1// Copyright 2014 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package asm67import (8 "fmt"9 "internal/abi"10 "strconv"11 "strings"12 "text/scanner"1314 "cmd/asm/internal/arch"15 "cmd/asm/internal/flags"16 "cmd/asm/internal/lex"17 "cmd/internal/obj"18 "cmd/internal/obj/arm64"19 "cmd/internal/obj/ppc64"20 "cmd/internal/obj/riscv"21 "cmd/internal/obj/x86"22 "cmd/internal/sys"23)2425// TODO: configure the architecture2627var testOut *strings.Builder // Gathers output when testing.2829// append adds the Prog to the end of the program-thus-far.30// If doLabel is set, it also defines the labels collect for this Prog.31func (p *Parser) append(prog *obj.Prog, cond string, doLabel bool) {32 if cond != "" {33 switch p.arch.Family {34 case sys.ARM:35 if !arch.ARMConditionCodes(prog, cond) {36 p.errorf("unrecognized condition code .%q", cond)37 return38 }3940 case sys.ARM64:41 if !arch.ARM64Suffix(prog, cond) {42 p.errorf("unrecognized suffix .%q", cond)43 return44 }4546 case sys.AMD64, sys.I386:47 if err := x86.ParseSuffix(prog, cond); err != nil {48 p.errorf("%v", err)49 return50 }51 case sys.RISCV64:52 if err := riscv.ParseSuffix(prog, cond); err != nil {53 p.errorf("unrecognized suffix .%q", cond)54 return55 }56 default:57 p.errorf("unrecognized suffix .%q", cond)58 return59 }60 }61 if p.firstProg == nil {62 p.firstProg = prog63 } else {64 p.lastProg.Link = prog65 }66 p.lastProg = prog67 if doLabel {68 p.pc++69 for _, label := range p.pendingLabels {70 if p.labels[label] != nil {71 p.errorf("label %q multiply defined", label)72 return73 }74 p.labels[label] = prog75 }76 p.pendingLabels = p.pendingLabels[0:0]77 }78 prog.Pc = p.pc79 if *flags.Debug {80 fmt.Println(p.lineNum, prog)81 }82 if testOut != nil {83 fmt.Fprintln(testOut, prog)84 }85}8687// validSymbol checks that addr represents a valid name for a pseudo-op.88func (p *Parser) validSymbol(pseudo string, addr *obj.Addr, offsetOk bool) bool {89 if addr.Sym == nil || addr.Name != obj.NAME_EXTERN && addr.Name != obj.NAME_STATIC || addr.Scale != 0 || addr.Reg != 0 {90 p.errorf("%s symbol %q must be a symbol(SB)", pseudo, symbolName(addr))91 return false92 }93 if !offsetOk && addr.Offset != 0 {94 p.errorf("%s symbol %q must not be offset from SB", pseudo, symbolName(addr))95 return false96 }97 return true98}99100// evalInteger evaluates an integer constant for a pseudo-op.101func (p *Parser) evalInteger(pseudo string, operands []lex.Token) int64 {102 addr := p.address(operands)103 return p.getConstantPseudo(pseudo, &addr)104}105106// validImmediate checks that addr represents an immediate constant.107func (p *Parser) validImmediate(pseudo string, addr *obj.Addr) bool {108 if addr.Type != obj.TYPE_CONST || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 {109 p.errorf("%s: expected immediate constant; found %s", pseudo, obj.Dconv(&emptyProg, addr))110 return false111 }112 return true113}114115// asmText assembles a TEXT pseudo-op.116// TEXT runtime·sigtramp(SB),4,$0-0117func (p *Parser) asmText(operands [][]lex.Token) {118 if len(operands) != 2 && len(operands) != 3 {119 p.errorf("expect two or three operands for TEXT")120 return121 }122123 // Labels are function scoped. Patch existing labels and124 // create a new label space for this TEXT.125 p.patch()126 p.labels = make(map[string]*obj.Prog)127128 // Operand 0 is the symbol name in the form foo(SB).129 // That means symbol plus indirect on SB and no offset.130 nameAddr := p.address(operands[0])131 if !p.validSymbol("TEXT", &nameAddr, false) {132 return133 }134 name := symbolName(&nameAddr)135 next := 1136137 // Next operand is the optional text flag, a literal integer.138 var flag = int64(0)139 if len(operands) == 3 {140 flag = p.evalInteger("TEXT", operands[1])141 next++142 }143144 // Issue an error if we see a function defined as ABIInternal145 // without NOSPLIT. In ABIInternal, obj needs to know the function146 // signature in order to construct the morestack path, so this147 // currently isn't supported for asm functions.148 if nameAddr.Sym.ABI() == obj.ABIInternal && flag&obj.NOSPLIT == 0 {149 p.errorf("TEXT %q: ABIInternal requires NOSPLIT", name)150 }151152 // Next operand is the frame and arg size.153 // Bizarre syntax: $frameSize-argSize is two words, not subtraction.154 // Both frameSize and argSize must be simple integers; only frameSize155 // can be negative.156 // The "-argSize" may be missing; if so, set it to objabi.ArgsSizeUnknown.157 // Parse left to right.158 op := operands[next]159 if len(op) < 2 || op[0].ScanToken != '$' {160 p.errorf("TEXT %s: frame size must be an immediate constant", name)161 return162 }163 op = op[1:]164 negative := false165 if op[0].ScanToken == '-' {166 negative = true167 op = op[1:]168 }169 if len(op) == 0 || op[0].ScanToken != scanner.Int {170 p.errorf("TEXT %s: frame size must be an immediate constant", name)171 return172 }173 frameSize := p.positiveAtoi(op[0].String())174 if negative {175 frameSize = -frameSize176 }177 op = op[1:]178 argSize := int64(abi.ArgsSizeUnknown)179 if len(op) > 0 {180 // There is an argument size. It must be a minus sign followed by a non-negative integer literal.181 if len(op) != 2 || op[0].ScanToken != '-' || op[1].ScanToken != scanner.Int {182 p.errorf("TEXT %s: argument size must be of form -integer", name)183 return184 }185 argSize = p.positiveAtoi(op[1].String())186 }187 p.ctxt.InitTextSym(nameAddr.Sym, int(flag), p.pos())188 prog := &obj.Prog{189 Ctxt: p.ctxt,190 As: obj.ATEXT,191 Pos: p.pos(),192 From: nameAddr,193 To: obj.Addr{194 Type: obj.TYPE_TEXTSIZE,195 Offset: frameSize,196 // Argsize set below.197 },198 }199 nameAddr.Sym.Func().Text = prog200 prog.To.Val = int32(argSize)201 p.append(prog, "", true)202}203204// asmData assembles a DATA pseudo-op.205// DATA masks<>+0x00(SB)/4, $0x00000000206func (p *Parser) asmData(operands [][]lex.Token) {207 if len(operands) != 2 {208 p.errorf("expect two operands for DATA")209 return210 }211212 // Operand 0 has the general form foo<>+0x04(SB)/4.213 op := operands[0]214 n := len(op)215 if n < 3 || op[n-2].ScanToken != '/' || op[n-1].ScanToken != scanner.Int {216 p.errorf("expect /size for DATA argument")217 return218 }219 szop := op[n-1].String()220 sz, err := strconv.Atoi(szop)221 if err != nil {222 p.errorf("bad size for DATA argument: %q", szop)223 }224 op = op[:n-2]225 nameAddr := p.address(op)226 if !p.validSymbol("DATA", &nameAddr, true) {227 return228 }229 name := symbolName(&nameAddr)230231 // Operand 1 is an immediate constant or address.232 valueAddr := p.address(operands[1])233 switch valueAddr.Type {234 case obj.TYPE_CONST, obj.TYPE_FCONST, obj.TYPE_SCONST, obj.TYPE_ADDR:235 // OK236 default:237 p.errorf("DATA value must be an immediate constant or address")238 return239 }240241 // The addresses must not overlap. Easiest test: require monotonicity.242 if lastAddr, ok := p.dataAddr[name]; ok && nameAddr.Offset < lastAddr {243 p.errorf("overlapping DATA entry for %s", name)244 return245 }246 p.dataAddr[name] = nameAddr.Offset + int64(sz)247248 switch valueAddr.Type {249 case obj.TYPE_CONST:250 switch sz {251 case 1, 2, 4, 8:252 nameAddr.Sym.WriteInt(p.ctxt, nameAddr.Offset, sz, valueAddr.Offset)253 default:254 p.errorf("bad int size for DATA argument: %d", sz)255 }256 case obj.TYPE_FCONST:257 switch sz {258 case 4:259 nameAddr.Sym.WriteFloat32(p.ctxt, nameAddr.Offset, float32(valueAddr.Val.(float64)))260 case 8:261 nameAddr.Sym.WriteFloat64(p.ctxt, nameAddr.Offset, valueAddr.Val.(float64))262 default:263 p.errorf("bad float size for DATA argument: %d", sz)264 }265 case obj.TYPE_SCONST:266 nameAddr.Sym.WriteString(p.ctxt, nameAddr.Offset, sz, valueAddr.Val.(string))267 case obj.TYPE_ADDR:268 if sz == p.arch.PtrSize {269 nameAddr.Sym.WriteAddr(p.ctxt, nameAddr.Offset, sz, valueAddr.Sym, valueAddr.Offset)270 } else {271 p.errorf("bad addr size for DATA argument: %d", sz)272 }273 }274}275276// asmGlobl assembles a GLOBL pseudo-op.277// GLOBL shifts<>(SB),8,$256278// GLOBL shifts<>(SB),$256279func (p *Parser) asmGlobl(operands [][]lex.Token) {280 if len(operands) != 2 && len(operands) != 3 {281 p.errorf("expect two or three operands for GLOBL")282 return283 }284285 // Operand 0 has the general form foo<>+0x04(SB).286 nameAddr := p.address(operands[0])287 if !p.validSymbol("GLOBL", &nameAddr, false) {288 return289 }290 next := 1291292 // Next operand is the optional flag, a literal integer.293 var flag = int64(0)294 if len(operands) == 3 {295 flag = p.evalInteger("GLOBL", operands[1])296 next++297 }298299 // Final operand is an immediate constant.300 addr := p.address(operands[next])301 if !p.validImmediate("GLOBL", &addr) {302 return303 }304305 // log.Printf("GLOBL %s %d, $%d", name, flag, size)306 p.ctxt.GloblPos(nameAddr.Sym, addr.Offset, int(flag), p.pos())307}308309// asmPCData assembles a PCDATA pseudo-op.310// PCDATA $2, $705311func (p *Parser) asmPCData(operands [][]lex.Token) {312 if len(operands) != 2 {313 p.errorf("expect two operands for PCDATA")314 return315 }316317 // Operand 0 must be an immediate constant.318 key := p.address(operands[0])319 if !p.validImmediate("PCDATA", &key) {320 return321 }322323 // Operand 1 must be an immediate constant.324 value := p.address(operands[1])325 if !p.validImmediate("PCDATA", &value) {326 return327 }328329 // log.Printf("PCDATA $%d, $%d", key.Offset, value.Offset)330 prog := &obj.Prog{331 Ctxt: p.ctxt,332 As: obj.APCDATA,333 Pos: p.pos(),334 From: key,335 To: value,336 }337 p.append(prog, "", true)338}339340// asmPCAlign assembles a PCALIGN pseudo-op.341// PCALIGN $16342func (p *Parser) asmPCAlign(operands [][]lex.Token) {343 if len(operands) != 1 {344 p.errorf("expect one operand for PCALIGN")345 return346 }347348 // Operand 0 must be an immediate constant.349 key := p.address(operands[0])350 if !p.validImmediate("PCALIGN", &key) {351 return352 }353354 prog := &obj.Prog{355 Ctxt: p.ctxt,356 As: obj.APCALIGN,357 Pos: p.pos(),358 From: key,359 }360 p.append(prog, "", true)361}362363// asmFuncData assembles a FUNCDATA pseudo-op.364// FUNCDATA $1, funcdata<>+4(SB)365func (p *Parser) asmFuncData(operands [][]lex.Token) {366 if len(operands) != 2 {367 p.errorf("expect two operands for FUNCDATA")368 return369 }370371 // Operand 0 must be an immediate constant.372 valueAddr := p.address(operands[0])373 if !p.validImmediate("FUNCDATA", &valueAddr) {374 return375 }376377 // Operand 1 is a symbol name in the form foo(SB).378 nameAddr := p.address(operands[1])379 if !p.validSymbol("FUNCDATA", &nameAddr, true) {380 return381 }382383 prog := &obj.Prog{384 Ctxt: p.ctxt,385 As: obj.AFUNCDATA,386 Pos: p.pos(),387 From: valueAddr,388 To: nameAddr,389 }390 p.append(prog, "", true)391}392393// asmJump assembles a jump instruction.394// JMP R1395// JMP exit396// JMP 3(PC)397func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) {398 var target *obj.Addr399 prog := &obj.Prog{400 Ctxt: p.ctxt,401 Pos: p.pos(),402 As: op,403 }404 targetAddr := &prog.To405 switch len(a) {406 case 0:407 if p.arch.Family == sys.Wasm {408 target = &obj.Addr{Type: obj.TYPE_NONE}409 break410 }411 p.errorf("wrong number of arguments to %s instruction", op)412 return413 case 1:414 target = &a[0]415 case 2:416 // Special 2-operand jumps.417 if p.arch.Family == sys.ARM64 && arch.IsARM64ADR(op) {418 // ADR label, R. Label is in From.419 target = &a[0]420 prog.To = a[1]421 targetAddr = &prog.From422 } else {423 target = &a[1]424 prog.From = a[0]425 }426 case 3:427 if p.arch.Family == sys.PPC64 {428 // Special 3-operand jumps.429 // a[1] is a register number expressed as a constant or register value430 target = &a[2]431 prog.From = a[0]432 if a[0].Type != obj.TYPE_CONST {433 // Legacy code may use a plain constant, accept it, and coerce434 // into a constant. E.g:435 // BC 4,...436 // into437 // BC $4,...438 prog.From = obj.Addr{439 Type: obj.TYPE_CONST,440 Offset: p.getConstant(prog, op, &a[0]),441 }442443 }444445 // Likewise, fixup usage like:446 // BC x,LT,...447 // BC x,foo+2,...448 // BC x,4449 // BC x,$5450 // into451 // BC x,CR0LT,...452 // BC x,CR0EQ,...453 // BC x,CR1LT,...454 // BC x,CR1GT,...455 // The first and second cases demonstrate a symbol name which is456 // effectively discarded. In these cases, the offset determines457 // the CR bit.458 prog.Reg = a[1].Reg459 if a[1].Type != obj.TYPE_REG {460 // The CR bit is represented as a constant 0-31. Convert it to a Reg.461 c := p.getConstant(prog, op, &a[1])462 reg, success := ppc64.ConstantToCRbit(c)463 if !success {464 p.errorf("invalid CR bit register number %d", c)465 }466 prog.Reg = reg467 }468 break469 }470 if p.arch.Family == sys.MIPS || p.arch.Family == sys.MIPS64 || p.arch.Family == sys.RISCV64 {471 // 3-operand jumps.472 // First two must be registers473 target = &a[2]474 prog.From = a[0]475 prog.Reg = p.getRegister(prog, op, &a[1])476 break477 }478 if p.arch.Family == sys.Loong64 {479 // 3-operand jumps.480 // First two must be registers481 target = &a[2]482 prog.From = a[0]483 prog.Reg = p.getRegister(prog, op, &a[1])484 break485 }486 if p.arch.Family == sys.S390X {487 // 3-operand jumps.488 target = &a[2]489 prog.From = a[0]490 if a[1].Reg != 0 {491 // Compare two registers and jump.492 prog.Reg = p.getRegister(prog, op, &a[1])493 } else {494 // Compare register with immediate and jump.495 prog.AddRestSource(a[1])496 }497 break498 }499 if p.arch.Family == sys.ARM64 {500 // Special 3-operand jumps.501 // a[0] must be immediate constant; a[1] is a register.502 if a[0].Type != obj.TYPE_CONST {503 p.errorf("%s: expected immediate constant; found %s", op, obj.Dconv(prog, &a[0]))504 return505 }506 prog.From = a[0]507 prog.Reg = p.getRegister(prog, op, &a[1])508 target = &a[2]509 break510 }511 p.errorf("wrong number of arguments to %s instruction", op)512 return513 case 4:514 if p.arch.Family == sys.S390X || p.arch.Family == sys.PPC64 {515 // 4-operand compare-and-branch.516 prog.From = a[0]517 prog.Reg = p.getRegister(prog, op, &a[1])518 prog.AddRestSource(a[2])519 target = &a[3]520 break521 }522 p.errorf("wrong number of arguments to %s instruction", op)523 return524 default:525 p.errorf("wrong number of arguments to %s instruction", op)526 return527 }528 switch {529 case target.Type == obj.TYPE_BRANCH:530 // JMP 4(PC)531 *targetAddr = obj.Addr{532 Type: obj.TYPE_BRANCH,533 Offset: p.pc + 1 + target.Offset, // +1 because p.pc is incremented in append, below.534 }535 case target.Type == obj.TYPE_REG:536 // JMP R1537 *targetAddr = *target538 case target.Type == obj.TYPE_MEM && (target.Name == obj.NAME_EXTERN || target.Name == obj.NAME_STATIC):539 // JMP main·morestack(SB)540 *targetAddr = *target541 case target.Type == obj.TYPE_INDIR && (target.Name == obj.NAME_EXTERN || target.Name == obj.NAME_STATIC):542 // JMP *main·morestack(SB)543 *targetAddr = *target544 targetAddr.Type = obj.TYPE_INDIR545 case target.Type == obj.TYPE_MEM && target.Reg == 0 && target.Offset == 0:546 // JMP exit547 if target.Sym == nil {548 // Parse error left name unset.549 return550 }551 targetProg := p.labels[target.Sym.Name]552 if targetProg == nil {553 p.toPatch = append(p.toPatch, Patch{targetAddr, target.Sym.Name})554 } else {555 p.branch(targetAddr, targetProg)556 }557 case target.Type == obj.TYPE_MEM && target.Name == obj.NAME_NONE:558 // JMP 4(R0)559 *targetAddr = *target560 // On the ppc64, 9a encodes BR (CTR) as BR CTR. We do the same.561 if p.arch.Family == sys.PPC64 && target.Offset == 0 {562 targetAddr.Type = obj.TYPE_REG563 }564 case target.Type == obj.TYPE_CONST:565 // JMP $4566 *targetAddr = a[0]567 case target.Type == obj.TYPE_NONE:568 // JMP569 default:570 p.errorf("cannot assemble jump %+v", target)571 return572 }573574 p.append(prog, cond, true)575}576577func (p *Parser) patch() {578 for _, patch := range p.toPatch {579 targetProg := p.labels[patch.label]580 if targetProg == nil {581 p.errorf("undefined label %s", patch.label)582 return583 }584 p.branch(patch.addr, targetProg)585 }586 p.toPatch = p.toPatch[:0]587}588589func (p *Parser) branch(addr *obj.Addr, target *obj.Prog) {590 *addr = obj.Addr{591 Type: obj.TYPE_BRANCH,592 Index: 0,593 }594 addr.Val = target595}596597func isARM64SVE(op obj.As) bool {598 return op > arm64.ASVESTART599}600601// asmInstruction assembles an instruction.602// MOVW R9, (R10)603func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {604 // fmt.Printf("%s %+v\n", op, a)605 prog := &obj.Prog{606 Ctxt: p.ctxt,607 Pos: p.pos(),608 As: op,609 }610 switch len(a) {611 case 0:612 // Nothing to do.613 case 1:614 if p.arch.UnaryDst[op] || op == obj.ARET || op == obj.AGETCALLERPC {615 // prog.From is no address.616 prog.To = a[0]617 } else {618 prog.From = a[0]619 // prog.To is no address.620 }621 if p.arch.Family == sys.PPC64 && arch.IsPPC64NEG(op) {622 // NEG: From and To are both a[0].623 prog.To = a[0]624 prog.From = a[0]625 break626 }627 case 2:628 if p.arch.Family == sys.ARM {629 if arch.IsARMCMP(op) {630 prog.From = a[0]631 prog.Reg = p.getRegister(prog, op, &a[1])632 break633 }634 // Strange special cases.635 if arch.IsARMFloatCmp(op) {636 prog.From = a[0]637 prog.Reg = p.getRegister(prog, op, &a[1])638 break639 }640 } else if p.arch.Family == sys.ARM64 && arch.IsARM64CMP(op) {641 prog.From = a[0]642 prog.Reg = p.getRegister(prog, op, &a[1])643 break644 } else if p.arch.Family == sys.MIPS || p.arch.Family == sys.MIPS64 {645 if arch.IsMIPSCMP(op) || arch.IsMIPSMUL(op) {646 prog.From = a[0]647 prog.Reg = p.getRegister(prog, op, &a[1])648 break649 }650 } else if p.arch.Family == sys.Loong64 {651 if arch.IsLoong64RDTIME(op) {652 // The Loong64 RDTIME family of instructions is a bit special,653 // in that both its register operands are outputs654 prog.To = a[0]655 if a[1].Type != obj.TYPE_REG {656 p.errorf("invalid addressing modes for 2nd operand to %s instruction, must be register", op)657 return658 }659 prog.RegTo2 = a[1].Reg660 break661 }662663 if arch.IsLoong64PRELD(op) {664 prog.From = a[0]665 prog.AddRestSource(a[1])666 break667 }668 } else if p.arch.Family == sys.RISCV64 {669 // RISCV64 pseudo instructions that reference CSRs with symbolic names.670 if isImm, specialIndex, ok := arch.IsRISCV64PseudoCSRO(op); ok {671 if a[0].Type != obj.TYPE_CONST && isImm {672 p.errorf("invalid value for first operand to %s instruction, must be a 5 bit unsigned immediate", op)673 return674 }675 if a[specialIndex].Type != obj.TYPE_SPECIAL {676 p.errorf("invalid value for first or second operand to %s instruction, must be a CSR name", op)677 return678 }679 prog.AddRestSourceArgs([]obj.Addr{a[specialIndex]})680 if specialIndex == 0 {681 prog.To = a[1]682 } else {683 prog.From = a[0]684 }685 break686 }687 }688 prog.From = a[0]689 prog.To = a[1]690 case 3:691 switch p.arch.Family {692 case sys.MIPS, sys.MIPS64:693 prog.From = a[0]694 prog.Reg = p.getRegister(prog, op, &a[1])695 prog.To = a[2]696 case sys.Loong64:697 switch {698 // Loong64 atomic instructions with one input and two outputs.699 case arch.IsLoong64AMO(op):700 prog.From = a[0]701 prog.To = a[1]702 prog.RegTo2 = a[2].Reg703704 case arch.IsLoong64PRELD(op):705 prog.From = a[0]706 prog.AddRestSourceArgs([]obj.Addr{a[1], a[2]})707708 default:709 prog.From = a[0]710 prog.Reg = p.getRegister(prog, op, &a[1])711 prog.To = a[2]712 }713 case sys.ARM:714 // Special cases.715 if arch.IsARMSTREX(op) {716 /*717 STREX x, (y), z718 from=(y) reg=x to=z719 */720 prog.From = a[1]721 prog.Reg = p.getRegister(prog, op, &a[0])722 prog.To = a[2]723 break724 }725 if arch.IsARMBFX(op) {726 // a[0] and a[1] must be constants, a[2] must be a register727 prog.From = a[0]728 prog.AddRestSource(a[1])729 prog.To = a[2]730 break731 }732 // Otherwise the 2nd operand (a[1]) must be a register.733 prog.From = a[0]734 prog.Reg = p.getRegister(prog, op, &a[1])735 prog.To = a[2]736 case sys.AMD64:737 prog.From = a[0]738 prog.AddRestSource(a[1])739 prog.To = a[2]740 case sys.ARM64:741 switch {742 case arch.IsARM64STLXR(op):743 // ARM64 instructions with one input and two outputs.744 prog.From = a[0]745 prog.To = a[1]746 if a[2].Type != obj.TYPE_REG {747 p.errorf("invalid addressing modes for third operand to %s instruction, must be register", op)748 return749 }750 prog.RegTo2 = a[2].Reg751 case arch.IsARM64TBL(op):752 // one of its inputs does not fit into prog.Reg.753 prog.From = a[0]754 prog.AddRestSource(a[1])755 prog.To = a[2]756 case arch.IsARM64CASP(op):757 prog.From = a[0]758 prog.To = a[1]759 // both 1st operand and 3rd operand are (Rs, Rs+1) register pair.760 // And the register pair must be contiguous.761 if (a[0].Type != obj.TYPE_REGREG) || (a[2].Type != obj.TYPE_REGREG) {762 p.errorf("invalid addressing modes for 1st or 3rd operand to %s instruction, must be register pair", op)763 return764 }765 // For ARM64 CASP-like instructions, its 2nd destination operand is register pair(Rt, Rt+1) that can766 // not fit into prog.RegTo2, so save it to the prog.RestArgs.767 prog.AddRestDest(a[2])768 case isARM64SVE(op):769 // SVE instructions, see arm64/goops_gen.go770 prog.From = a[0]771 prog.AddRestSource(a[1])772 prog.To = a[2]773 default:774 prog.From = a[0]775 prog.Reg = p.getRegister(prog, op, &a[1])776 prog.To = a[2]777 }778 case sys.I386:779 prog.From = a[0]780 prog.AddRestSource(a[1])781 prog.To = a[2]782 case sys.PPC64:783 if arch.IsPPC64CMP(op) {784 // CMPW etc.; third argument is a CR register that goes into prog.Reg.785 prog.From = a[0]786 prog.Reg = p.getRegister(prog, op, &a[2])787 prog.To = a[1]788 break789 }790791 prog.From = a[0]792 prog.To = a[2]793794 // If the second argument is not a register argument, it must be795 // passed RestArgs/AddRestSource796 switch a[1].Type {797 case obj.TYPE_REG:798 prog.Reg = p.getRegister(prog, op, &a[1])799 default:800 prog.AddRestSource(a[1])801 }802 case sys.RISCV64:803 // RISCV64 instructions with one input and two outputs.804 if arch.IsRISCV64AMO(op) {805 prog.From = a[0]806 prog.To = a[1]807 if a[2].Type != obj.TYPE_REG {808 p.errorf("invalid addressing modes for third operand to %s instruction, must be register", op)809 return810 }811 prog.RegTo2 = a[2].Reg812 break813 }814 // RISCV64 instructions that reference CSRs with symbolic names.815 if isImm, ok := arch.IsRISCV64CSRO(op); ok {816 if a[0].Type != obj.TYPE_CONST && isImm {817 p.errorf("invalid value for first operand to %s instruction, must be a 5 bit unsigned immediate", op)818 return819 }820 if a[1].Type != obj.TYPE_SPECIAL {821 p.errorf("invalid value for second operand to %s instruction, must be a CSR name", op)822 return823 }824 prog.AddRestSourceArgs([]obj.Addr{a[1]})825 prog.From = a[0]826 prog.To = a[2]827 break828 }829 prog.From = a[0]830 prog.Reg = p.getRegister(prog, op, &a[1])831 prog.To = a[2]832 case sys.S390X:833 prog.From = a[0]834 if a[1].Type == obj.TYPE_REG {835 prog.Reg = p.getRegister(prog, op, &a[1])836 } else {837 prog.AddRestSource(a[1])838 }839 prog.To = a[2]840 default:841 p.errorf("TODO: implement three-operand instructions for this architecture")842 return843 }844 case 4:845 if p.arch.Family == sys.ARM {846 if arch.IsARMBFX(op) {847 // a[0] and a[1] must be constants, a[2] and a[3] must be registers848 prog.From = a[0]849 prog.AddRestSource(a[1])850 prog.Reg = p.getRegister(prog, op, &a[2])851 prog.To = a[3]852 break853 }854 if arch.IsARMMULA(op) {855 // All must be registers.856 p.getRegister(prog, op, &a[0])857 r1 := p.getRegister(prog, op, &a[1])858 r2 := p.getRegister(prog, op, &a[2])859 p.getRegister(prog, op, &a[3])860 prog.From = a[0]861 prog.To = a[3]862 prog.To.Type = obj.TYPE_REGREG2863 prog.To.Offset = int64(r2)864 prog.Reg = r1865 break866 }867 }868 if p.arch.Family == sys.AMD64 {869 prog.From = a[0]870 prog.AddRestSourceArgs([]obj.Addr{a[1], a[2]})871 prog.To = a[3]872 break873 }874 if p.arch.Family == sys.ARM64 {875 if isARM64SVE(op) {876 // SVE instructions, see arm64/goops_gen.go877 prog.From = a[0]878 prog.AddRestSourceArgs([]obj.Addr{a[1], a[2]})879 prog.To = a[3]880 break881 }882 prog.From = a[0]883 prog.Reg = p.getRegister(prog, op, &a[1])884 prog.AddRestSource(a[2])885 prog.To = a[3]886 break887 }888 if p.arch.Family == sys.Loong64 {889 prog.From = a[0]890 prog.Reg = p.getRegister(prog, op, &a[1])891 prog.AddRestSource(a[2])892 prog.To = a[3]893 break894 }895 if p.arch.Family == sys.PPC64 {896 prog.From = a[0]897 prog.To = a[3]898 // If the second argument is not a register argument, it must be899 // passed RestArgs/AddRestSource900 if a[1].Type == obj.TYPE_REG {901 prog.Reg = p.getRegister(prog, op, &a[1])902 prog.AddRestSource(a[2])903 } else {904 // Don't set prog.Reg if a1 isn't a reg arg.905 prog.AddRestSourceArgs([]obj.Addr{a[1], a[2]})906 }907 break908 }909 if p.arch.Family == sys.RISCV64 {910 prog.From = a[0]911 prog.Reg = p.getRegister(prog, op, &a[1])912 prog.AddRestSource(a[2])913 prog.To = a[3]914 break915 }916 if p.arch.Family == sys.S390X {917 if a[1].Type != obj.TYPE_REG {918 p.errorf("second operand must be a register in %s instruction", op)919 return920 }921 prog.From = a[0]922 prog.Reg = p.getRegister(prog, op, &a[1])923 prog.AddRestSource(a[2])924 prog.To = a[3]925 break926 }927 p.errorf("can't handle %s instruction with 4 operands", op)928 return929 case 5:930 if p.arch.Family == sys.ARM64 && isARM64SVE(op) {931 // SVE instructions, see arm64/goops_gen.go932 prog.From = a[0]933 prog.AddRestSourceArgs([]obj.Addr{a[1], a[2], a[3]})934 prog.To = a[4]935 break936 }937 if p.arch.Family == sys.PPC64 {938 prog.From = a[0]939 // Second arg is always a register type on ppc64.940 prog.Reg = p.getRegister(prog, op, &a[1])941 prog.AddRestSourceArgs([]obj.Addr{a[2], a[3]})942 prog.To = a[4]943 break944 }945 if p.arch.Family == sys.AMD64 {946 prog.From = a[0]947 prog.AddRestSourceArgs([]obj.Addr{a[1], a[2], a[3]})948 prog.To = a[4]949 break950 }951 if p.arch.Family == sys.S390X {952 prog.From = a[0]953 prog.AddRestSourceArgs([]obj.Addr{a[1], a[2], a[3]})954 prog.To = a[4]955 break956 }957 p.errorf("can't handle %s instruction with 5 operands", op)958 return959 case 6:960 if p.arch.Family == sys.ARM64 && isARM64SVE(op) {961 // SVE instructions, see arm64/goops_gen.go962 prog.From = a[0]963 prog.AddRestSourceArgs([]obj.Addr{a[1], a[2], a[3], a[4]})964 prog.To = a[5]965 break966 }967 if p.arch.Family == sys.ARM && arch.IsARMMRC(op) {968 // Strange special case: MCR, MRC.969 prog.To.Type = obj.TYPE_CONST970 x0 := p.getConstant(prog, op, &a[0])971 x1 := p.getConstant(prog, op, &a[1])972 x2 := int64(p.getRegister(prog, op, &a[2]))973 x3 := int64(p.getRegister(prog, op, &a[3]))974 x4 := int64(p.getRegister(prog, op, &a[4]))975 x5 := p.getConstant(prog, op, &a[5])976 // Cond is handled specially for this instruction.977 offset, MRC, ok := arch.ARMMRCOffset(op, cond, x0, x1, x2, x3, x4, x5)978 if !ok {979 p.errorf("unrecognized condition code .%q", cond)980 }981 prog.To.Offset = offset982 cond = ""983 prog.As = MRC // Both instructions are coded as MRC.984 break985 }986 if p.arch.Family == sys.PPC64 {987 prog.From = a[0]988 // Second arg is always a register type on ppc64.989 prog.Reg = p.getRegister(prog, op, &a[1])990 prog.AddRestSourceArgs([]obj.Addr{a[2], a[3], a[4]})991 prog.To = a[5]992 break993 }994 if p.arch.Family == sys.RISCV64 && arch.IsRISCV64VTypeI(op) {995 prog.From = a[0]996 vsew := p.getSpecial(prog, op, &a[1])997 vlmul := p.getSpecial(prog, op, &a[2])998 vtail := p.getSpecial(prog, op, &a[3])999 vmask := p.getSpecial(prog, op, &a[4])1000 if err := arch.RISCV64ValidateVectorType(vsew, vlmul, vtail, vmask); err != nil {1001 p.errorf("invalid vtype: %v", err)1002 }1003 prog.AddRestSourceArgs([]obj.Addr{a[1], a[2], a[3], a[4]})1004 prog.To = a[5]1005 break1006 }1007 fallthrough1008 default:1009 p.errorf("can't handle %s instruction with %d operands", op, len(a))1010 return1011 }10121013 p.append(prog, cond, true)1014}10151016// symbolName returns the symbol name, or an error string if none is available.1017func symbolName(addr *obj.Addr) string {1018 if addr.Sym != nil {1019 return addr.Sym.Name1020 }1021 return "<erroneous symbol>"1022}10231024var emptyProg obj.Prog10251026// getConstantPseudo checks that addr represents a plain constant and returns its value.1027func (p *Parser) getConstantPseudo(pseudo string, addr *obj.Addr) int64 {1028 if addr.Type != obj.TYPE_MEM || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 {1029 p.errorf("%s: expected integer constant; found %s", pseudo, obj.Dconv(&emptyProg, addr))1030 }1031 return addr.Offset1032}10331034// getConstant checks that addr represents a plain constant and returns its value.1035func (p *Parser) getConstant(prog *obj.Prog, op obj.As, addr *obj.Addr) int64 {1036 if addr.Type != obj.TYPE_MEM || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 {1037 p.errorf("%s: expected integer constant; found %s", op, obj.Dconv(prog, addr))1038 }1039 return addr.Offset1040}10411042// getRegister checks that addr represents a register and returns its value.1043func (p *Parser) getRegister(prog *obj.Prog, op obj.As, addr *obj.Addr) int16 {1044 if addr.Type != obj.TYPE_REG || addr.Offset != 0 || addr.Name != 0 || addr.Index != 0 {1045 p.errorf("%s: expected register; found %s", op, obj.Dconv(prog, addr))1046 }1047 return addr.Reg1048}10491050// getSpecial checks that addr represents a special operand and returns its value.1051func (p *Parser) getSpecial(prog *obj.Prog, op obj.As, addr *obj.Addr) int64 {1052 if addr.Type != obj.TYPE_SPECIAL || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 {1053 p.errorf("%s: expected special operand; found %s", op, obj.Dconv(prog, addr))1054 }1055 return addr.Offset1056}
Findings
✓ No findings reported for this file.