// Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package asm import ( "fmt" "internal/abi" "strconv" "strings" "text/scanner" "cmd/asm/internal/arch" "cmd/asm/internal/flags" "cmd/asm/internal/lex" "cmd/internal/obj" "cmd/internal/obj/ppc64" "cmd/internal/obj/x86" "cmd/internal/sys" ) // TODO: configure the architecture var testOut *strings.Builder // Gathers output when testing. // append adds the Prog to the end of the program-thus-far. // If doLabel is set, it also defines the labels collect for this Prog. func (p *Parser) append(prog *obj.Prog, cond string, doLabel bool) { if cond != "" { switch p.arch.Family { case sys.ARM: if !arch.ARMConditionCodes(prog, cond) { p.errorf("unrecognized condition code .%q", cond) return } case sys.ARM64: if !arch.ARM64Suffix(prog, cond) { p.errorf("unrecognized suffix .%q", cond) return } case sys.AMD64, sys.I386: if err := x86.ParseSuffix(prog, cond); err != nil { p.errorf("%v", err) return } default: p.errorf("unrecognized suffix .%q", cond) return } } if p.firstProg == nil { p.firstProg = prog } else { p.lastProg.Link = prog } p.lastProg = prog if doLabel { p.pc++ for _, label := range p.pendingLabels { if p.labels[label] != nil { p.errorf("label %q multiply defined", label) return } p.labels[label] = prog } p.pendingLabels = p.pendingLabels[0:0] } prog.Pc = p.pc if *flags.Debug { fmt.Println(p.lineNum, prog) } if testOut != nil { fmt.Fprintln(testOut, prog) } } // validSymbol checks that addr represents a valid name for a pseudo-op. func (p *Parser) validSymbol(pseudo string, addr *obj.Addr, offsetOk bool) bool { if addr.Sym == nil || addr.Name != obj.NAME_EXTERN && addr.Name != obj.NAME_STATIC || addr.Scale != 0 || addr.Reg != 0 { p.errorf("%s symbol %q must be a symbol(SB)", pseudo, symbolName(addr)) return false } if !offsetOk && addr.Offset != 0 { p.errorf("%s symbol %q must not be offset from SB", pseudo, symbolName(addr)) return false } return true } // evalInteger evaluates an integer constant for a pseudo-op. func (p *Parser) evalInteger(pseudo string, operands []lex.Token) int64 { addr := p.address(operands) return p.getConstantPseudo(pseudo, &addr) } // validImmediate checks that addr represents an immediate constant. func (p *Parser) validImmediate(pseudo string, addr *obj.Addr) bool { if addr.Type != obj.TYPE_CONST || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 { p.errorf("%s: expected immediate constant; found %s", pseudo, obj.Dconv(&emptyProg, addr)) return false } return true } // asmText assembles a TEXT pseudo-op. // TEXT runtime·sigtramp(SB),4,$0-0 func (p *Parser) asmText(operands [][]lex.Token) { if len(operands) != 2 && len(operands) != 3 { p.errorf("expect two or three operands for TEXT") return } // Labels are function scoped. Patch existing labels and // create a new label space for this TEXT. p.patch() p.labels = make(map[string]*obj.Prog) // Operand 0 is the symbol name in the form foo(SB). // That means symbol plus indirect on SB and no offset. nameAddr := p.address(operands[0]) if !p.validSymbol("TEXT", &nameAddr, false) { return } name := symbolName(&nameAddr) next := 1 // Next operand is the optional text flag, a literal integer. var flag = int64(0) if len(operands) == 3 { flag = p.evalInteger("TEXT", operands[1]) next++ } // Issue an error if we see a function defined as ABIInternal // without NOSPLIT. In ABIInternal, obj needs to know the function // signature in order to construct the morestack path, so this // currently isn't supported for asm functions. if nameAddr.Sym.ABI() == obj.ABIInternal && flag&obj.NOSPLIT == 0 { p.errorf("TEXT %q: ABIInternal requires NOSPLIT", name) } // Next operand is the frame and arg size. // Bizarre syntax: $frameSize-argSize is two words, not subtraction. // Both frameSize and argSize must be simple integers; only frameSize // can be negative. // The "-argSize" may be missing; if so, set it to objabi.ArgsSizeUnknown. // Parse left to right. op := operands[next] if len(op) < 2 || op[0].ScanToken != '$' { p.errorf("TEXT %s: frame size must be an immediate constant", name) return } op = op[1:] negative := false if op[0].ScanToken == '-' { negative = true op = op[1:] } if len(op) == 0 || op[0].ScanToken != scanner.Int { p.errorf("TEXT %s: frame size must be an immediate constant", name) return } frameSize := p.positiveAtoi(op[0].String()) if negative { frameSize = -frameSize } op = op[1:] argSize := int64(abi.ArgsSizeUnknown) if len(op) > 0 { // There is an argument size. It must be a minus sign followed by a non-negative integer literal. if len(op) != 2 || op[0].ScanToken != '-' || op[1].ScanToken != scanner.Int { p.errorf("TEXT %s: argument size must be of form -integer", name) return } argSize = p.positiveAtoi(op[1].String()) } p.ctxt.InitTextSym(nameAddr.Sym, int(flag), p.pos()) prog := &obj.Prog{ Ctxt: p.ctxt, As: obj.ATEXT, Pos: p.pos(), From: nameAddr, To: obj.Addr{ Type: obj.TYPE_TEXTSIZE, Offset: frameSize, // Argsize set below. }, } nameAddr.Sym.Func().Text = prog prog.To.Val = int32(argSize) p.append(prog, "", true) } // asmData assembles a DATA pseudo-op. // DATA masks<>+0x00(SB)/4, $0x00000000 func (p *Parser) asmData(operands [][]lex.Token) { if len(operands) != 2 { p.errorf("expect two operands for DATA") return } // Operand 0 has the general form foo<>+0x04(SB)/4. op := operands[0] n := len(op) if n < 3 || op[n-2].ScanToken != '/' || op[n-1].ScanToken != scanner.Int { p.errorf("expect /size for DATA argument") return } szop := op[n-1].String() sz, err := strconv.Atoi(szop) if err != nil { p.errorf("bad size for DATA argument: %q", szop) } op = op[:n-2] nameAddr := p.address(op) if !p.validSymbol("DATA", &nameAddr, true) { return } name := symbolName(&nameAddr) // Operand 1 is an immediate constant or address. valueAddr := p.address(operands[1]) switch valueAddr.Type { case obj.TYPE_CONST, obj.TYPE_FCONST, obj.TYPE_SCONST, obj.TYPE_ADDR: // OK default: p.errorf("DATA value must be an immediate constant or address") return } // The addresses must not overlap. Easiest test: require monotonicity. if lastAddr, ok := p.dataAddr[name]; ok && nameAddr.Offset < lastAddr { p.errorf("overlapping DATA entry for %s", name) return } p.dataAddr[name] = nameAddr.Offset + int64(sz) switch valueAddr.Type { case obj.TYPE_CONST: switch sz { case 1, 2, 4, 8: nameAddr.Sym.WriteInt(p.ctxt, nameAddr.Offset, int(sz), valueAddr.Offset) default: p.errorf("bad int size for DATA argument: %d", sz) } case obj.TYPE_FCONST: switch sz { case 4: nameAddr.Sym.WriteFloat32(p.ctxt, nameAddr.Offset, float32(valueAddr.Val.(float64))) case 8: nameAddr.Sym.WriteFloat64(p.ctxt, nameAddr.Offset, valueAddr.Val.(float64)) default: p.errorf("bad float size for DATA argument: %d", sz) } case obj.TYPE_SCONST: nameAddr.Sym.WriteString(p.ctxt, nameAddr.Offset, int(sz), valueAddr.Val.(string)) case obj.TYPE_ADDR: if sz == p.arch.PtrSize { nameAddr.Sym.WriteAddr(p.ctxt, nameAddr.Offset, int(sz), valueAddr.Sym, valueAddr.Offset) } else { p.errorf("bad addr size for DATA argument: %d", sz) } } } // asmGlobl assembles a GLOBL pseudo-op. // GLOBL shifts<>(SB),8,$256 // GLOBL shifts<>(SB),$256 func (p *Parser) asmGlobl(operands [][]lex.Token) { if len(operands) != 2 && len(operands) != 3 { p.errorf("expect two or three operands for GLOBL") return } // Operand 0 has the general form foo<>+0x04(SB). nameAddr := p.address(operands[0]) if !p.validSymbol("GLOBL", &nameAddr, false) { return } next := 1 // Next operand is the optional flag, a literal integer. var flag = int64(0) if len(operands) == 3 { flag = p.evalInteger("GLOBL", operands[1]) next++ } // Final operand is an immediate constant. addr := p.address(operands[next]) if !p.validImmediate("GLOBL", &addr) { return } // log.Printf("GLOBL %s %d, $%d", name, flag, size) p.ctxt.GloblPos(nameAddr.Sym, addr.Offset, int(flag), p.pos()) } // asmPCData assembles a PCDATA pseudo-op. // PCDATA $2, $705 func (p *Parser) asmPCData(operands [][]lex.Token) { if len(operands) != 2 { p.errorf("expect two operands for PCDATA") return } // Operand 0 must be an immediate constant. key := p.address(operands[0]) if !p.validImmediate("PCDATA", &key) { return } // Operand 1 must be an immediate constant. value := p.address(operands[1]) if !p.validImmediate("PCDATA", &value) { return } // log.Printf("PCDATA $%d, $%d", key.Offset, value.Offset) prog := &obj.Prog{ Ctxt: p.ctxt, As: obj.APCDATA, Pos: p.pos(), From: key, To: value, } p.append(prog, "", true) } // asmPCAlign assembles a PCALIGN pseudo-op. // PCALIGN $16 func (p *Parser) asmPCAlign(operands [][]lex.Token) { if len(operands) != 1 { p.errorf("expect one operand for PCALIGN") return } // Operand 0 must be an immediate constant. key := p.address(operands[0]) if !p.validImmediate("PCALIGN", &key) { return } prog := &obj.Prog{ Ctxt: p.ctxt, As: obj.APCALIGN, From: key, } p.append(prog, "", true) } // asmFuncData assembles a FUNCDATA pseudo-op. // FUNCDATA $1, funcdata<>+4(SB) func (p *Parser) asmFuncData(operands [][]lex.Token) { if len(operands) != 2 { p.errorf("expect two operands for FUNCDATA") return } // Operand 0 must be an immediate constant. valueAddr := p.address(operands[0]) if !p.validImmediate("FUNCDATA", &valueAddr) { return } // Operand 1 is a symbol name in the form foo(SB). nameAddr := p.address(operands[1]) if !p.validSymbol("FUNCDATA", &nameAddr, true) { return } prog := &obj.Prog{ Ctxt: p.ctxt, As: obj.AFUNCDATA, Pos: p.pos(), From: valueAddr, To: nameAddr, } p.append(prog, "", true) } // asmJump assembles a jump instruction. // JMP R1 // JMP exit // JMP 3(PC) func (p *Parser) asmJump(op obj.As, cond string, a []obj.Addr) { var target *obj.Addr prog := &obj.Prog{ Ctxt: p.ctxt, Pos: p.pos(), As: op, } targetAddr := &prog.To switch len(a) { case 0: if p.arch.Family == sys.Wasm { target = &obj.Addr{Type: obj.TYPE_NONE} break } p.errorf("wrong number of arguments to %s instruction", op) return case 1: target = &a[0] case 2: // Special 2-operand jumps. if p.arch.Family == sys.ARM64 && arch.IsARM64ADR(op) { // ADR label, R. Label is in From. target = &a[0] prog.To = a[1] targetAddr = &prog.From } else { target = &a[1] prog.From = a[0] } case 3: if p.arch.Family == sys.PPC64 { // Special 3-operand jumps. // a[1] is a register number expressed as a constant or register value target = &a[2] prog.From = a[0] if a[0].Type != obj.TYPE_CONST { // Legacy code may use a plain constant, accept it, and coerce // into a constant. E.g: // BC 4,... // into // BC $4,... prog.From = obj.Addr{ Type: obj.TYPE_CONST, Offset: p.getConstant(prog, op, &a[0]), } } // Likewise, fixup usage like: // BC x,LT,... // BC x,foo+2,... // BC x,4 // BC x,$5 // into // BC x,CR0LT,... // BC x,CR0EQ,... // BC x,CR1LT,... // BC x,CR1GT,... // The first and second cases demonstrate a symbol name which is // effectively discarded. In these cases, the offset determines // the CR bit. prog.Reg = a[1].Reg if a[1].Type != obj.TYPE_REG { // The CR bit is represented as a constant 0-31. Convert it to a Reg. c := p.getConstant(prog, op, &a[1]) reg, success := ppc64.ConstantToCRbit(c) if !success { p.errorf("invalid CR bit register number %d", c) } prog.Reg = reg } break } if p.arch.Family == sys.MIPS || p.arch.Family == sys.MIPS64 || p.arch.Family == sys.RISCV64 { // 3-operand jumps. // First two must be registers target = &a[2] prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) break } if p.arch.Family == sys.Loong64 { // 3-operand jumps. // First two must be registers target = &a[2] prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) break } if p.arch.Family == sys.S390X { // 3-operand jumps. target = &a[2] prog.From = a[0] if a[1].Reg != 0 { // Compare two registers and jump. prog.Reg = p.getRegister(prog, op, &a[1]) } else { // Compare register with immediate and jump. prog.AddRestSource(a[1]) } break } if p.arch.Family == sys.ARM64 { // Special 3-operand jumps. // a[0] must be immediate constant; a[1] is a register. if a[0].Type != obj.TYPE_CONST { p.errorf("%s: expected immediate constant; found %s", op, obj.Dconv(prog, &a[0])) return } prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) target = &a[2] break } p.errorf("wrong number of arguments to %s instruction", op) return case 4: if p.arch.Family == sys.S390X || p.arch.Family == sys.PPC64 { // 4-operand compare-and-branch. prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.AddRestSource(a[2]) target = &a[3] break } p.errorf("wrong number of arguments to %s instruction", op) return default: p.errorf("wrong number of arguments to %s instruction", op) return } switch { case target.Type == obj.TYPE_BRANCH: // JMP 4(PC) *targetAddr = obj.Addr{ Type: obj.TYPE_BRANCH, Offset: p.pc + 1 + target.Offset, // +1 because p.pc is incremented in append, below. } case target.Type == obj.TYPE_REG: // JMP R1 *targetAddr = *target case target.Type == obj.TYPE_MEM && (target.Name == obj.NAME_EXTERN || target.Name == obj.NAME_STATIC): // JMP main·morestack(SB) *targetAddr = *target case target.Type == obj.TYPE_INDIR && (target.Name == obj.NAME_EXTERN || target.Name == obj.NAME_STATIC): // JMP *main·morestack(SB) *targetAddr = *target targetAddr.Type = obj.TYPE_INDIR case target.Type == obj.TYPE_MEM && target.Reg == 0 && target.Offset == 0: // JMP exit if target.Sym == nil { // Parse error left name unset. return } targetProg := p.labels[target.Sym.Name] if targetProg == nil { p.toPatch = append(p.toPatch, Patch{targetAddr, target.Sym.Name}) } else { p.branch(targetAddr, targetProg) } case target.Type == obj.TYPE_MEM && target.Name == obj.NAME_NONE: // JMP 4(R0) *targetAddr = *target // On the ppc64, 9a encodes BR (CTR) as BR CTR. We do the same. if p.arch.Family == sys.PPC64 && target.Offset == 0 { targetAddr.Type = obj.TYPE_REG } case target.Type == obj.TYPE_CONST: // JMP $4 *targetAddr = a[0] case target.Type == obj.TYPE_NONE: // JMP default: p.errorf("cannot assemble jump %+v", target) return } p.append(prog, cond, true) } func (p *Parser) patch() { for _, patch := range p.toPatch { targetProg := p.labels[patch.label] if targetProg == nil { p.errorf("undefined label %s", patch.label) return } p.branch(patch.addr, targetProg) } p.toPatch = p.toPatch[:0] } func (p *Parser) branch(addr *obj.Addr, target *obj.Prog) { *addr = obj.Addr{ Type: obj.TYPE_BRANCH, Index: 0, } addr.Val = target } // asmInstruction assembles an instruction. // MOVW R9, (R10) func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { // fmt.Printf("%s %+v\n", op, a) prog := &obj.Prog{ Ctxt: p.ctxt, Pos: p.pos(), As: op, } switch len(a) { case 0: // Nothing to do. case 1: if p.arch.UnaryDst[op] || op == obj.ARET || op == obj.AGETCALLERPC { // prog.From is no address. prog.To = a[0] } else { prog.From = a[0] // prog.To is no address. } if p.arch.Family == sys.PPC64 && arch.IsPPC64NEG(op) { // NEG: From and To are both a[0]. prog.To = a[0] prog.From = a[0] break } case 2: if p.arch.Family == sys.ARM { if arch.IsARMCMP(op) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) break } // Strange special cases. if arch.IsARMFloatCmp(op) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) break } } else if p.arch.Family == sys.ARM64 && arch.IsARM64CMP(op) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) break } else if p.arch.Family == sys.MIPS || p.arch.Family == sys.MIPS64 { if arch.IsMIPSCMP(op) || arch.IsMIPSMUL(op) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) break } } else if p.arch.Family == sys.Loong64 { if arch.IsLoong64CMP(op) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) break } if arch.IsLoong64RDTIME(op) { // The Loong64 RDTIME family of instructions is a bit special, // in that both its register operands are outputs prog.To = a[0] if a[1].Type != obj.TYPE_REG { p.errorf("invalid addressing modes for 2nd operand to %s instruction, must be register", op) return } prog.RegTo2 = a[1].Reg break } } prog.From = a[0] prog.To = a[1] case 3: switch p.arch.Family { case sys.MIPS, sys.MIPS64: prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.To = a[2] case sys.Loong64: prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.To = a[2] case sys.ARM: // Special cases. if arch.IsARMSTREX(op) { /* STREX x, (y), z from=(y) reg=x to=z */ prog.From = a[1] prog.Reg = p.getRegister(prog, op, &a[0]) prog.To = a[2] break } if arch.IsARMBFX(op) { // a[0] and a[1] must be constants, a[2] must be a register prog.From = a[0] prog.AddRestSource(a[1]) prog.To = a[2] break } // Otherwise the 2nd operand (a[1]) must be a register. prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.To = a[2] case sys.AMD64: prog.From = a[0] prog.AddRestSource(a[1]) prog.To = a[2] case sys.ARM64: switch { case arch.IsARM64STLXR(op): // ARM64 instructions with one input and two outputs. prog.From = a[0] prog.To = a[1] if a[2].Type != obj.TYPE_REG { p.errorf("invalid addressing modes for third operand to %s instruction, must be register", op) return } prog.RegTo2 = a[2].Reg case arch.IsARM64TBL(op): // one of its inputs does not fit into prog.Reg. prog.From = a[0] prog.AddRestSource(a[1]) prog.To = a[2] case arch.IsARM64CASP(op): prog.From = a[0] prog.To = a[1] // both 1st operand and 3rd operand are (Rs, Rs+1) register pair. // And the register pair must be contiguous. if (a[0].Type != obj.TYPE_REGREG) || (a[2].Type != obj.TYPE_REGREG) { p.errorf("invalid addressing modes for 1st or 3rd operand to %s instruction, must be register pair", op) return } // For ARM64 CASP-like instructions, its 2nd destination operand is register pair(Rt, Rt+1) that can // not fit into prog.RegTo2, so save it to the prog.RestArgs. prog.AddRestDest(a[2]) default: prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.To = a[2] } case sys.I386: prog.From = a[0] prog.AddRestSource(a[1]) prog.To = a[2] case sys.PPC64: if arch.IsPPC64CMP(op) { // CMPW etc.; third argument is a CR register that goes into prog.Reg. prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[2]) prog.To = a[1] break } prog.From = a[0] prog.To = a[2] // If the second argument is not a register argument, it must be // passed RestArgs/AddRestSource switch a[1].Type { case obj.TYPE_REG: prog.Reg = p.getRegister(prog, op, &a[1]) default: prog.AddRestSource(a[1]) } case sys.RISCV64: // RISCV64 instructions with one input and two outputs. if arch.IsRISCV64AMO(op) { prog.From = a[0] prog.To = a[1] if a[2].Type != obj.TYPE_REG { p.errorf("invalid addressing modes for third operand to %s instruction, must be register", op) return } prog.RegTo2 = a[2].Reg break } prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.To = a[2] case sys.S390X: prog.From = a[0] if a[1].Type == obj.TYPE_REG { prog.Reg = p.getRegister(prog, op, &a[1]) } else { prog.AddRestSource(a[1]) } prog.To = a[2] default: p.errorf("TODO: implement three-operand instructions for this architecture") return } case 4: if p.arch.Family == sys.ARM { if arch.IsARMBFX(op) { // a[0] and a[1] must be constants, a[2] and a[3] must be registers prog.From = a[0] prog.AddRestSource(a[1]) prog.Reg = p.getRegister(prog, op, &a[2]) prog.To = a[3] break } if arch.IsARMMULA(op) { // All must be registers. p.getRegister(prog, op, &a[0]) r1 := p.getRegister(prog, op, &a[1]) r2 := p.getRegister(prog, op, &a[2]) p.getRegister(prog, op, &a[3]) prog.From = a[0] prog.To = a[3] prog.To.Type = obj.TYPE_REGREG2 prog.To.Offset = int64(r2) prog.Reg = r1 break } } if p.arch.Family == sys.AMD64 { prog.From = a[0] prog.AddRestSourceArgs([]obj.Addr{a[1], a[2]}) prog.To = a[3] break } if p.arch.Family == sys.ARM64 { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.AddRestSource(a[2]) prog.To = a[3] break } if p.arch.Family == sys.PPC64 { prog.From = a[0] prog.To = a[3] // If the second argument is not a register argument, it must be // passed RestArgs/AddRestSource if a[1].Type == obj.TYPE_REG { prog.Reg = p.getRegister(prog, op, &a[1]) prog.AddRestSource(a[2]) } else { // Don't set prog.Reg if a1 isn't a reg arg. prog.AddRestSourceArgs([]obj.Addr{a[1], a[2]}) } break } if p.arch.Family == sys.RISCV64 { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.AddRestSource(a[2]) prog.To = a[3] break } if p.arch.Family == sys.S390X { if a[1].Type != obj.TYPE_REG { p.errorf("second operand must be a register in %s instruction", op) return } prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.AddRestSource(a[2]) prog.To = a[3] break } p.errorf("can't handle %s instruction with 4 operands", op) return case 5: if p.arch.Family == sys.PPC64 { prog.From = a[0] // Second arg is always a register type on ppc64. prog.Reg = p.getRegister(prog, op, &a[1]) prog.AddRestSourceArgs([]obj.Addr{a[2], a[3]}) prog.To = a[4] break } if p.arch.Family == sys.AMD64 { prog.From = a[0] prog.AddRestSourceArgs([]obj.Addr{a[1], a[2], a[3]}) prog.To = a[4] break } if p.arch.Family == sys.S390X { prog.From = a[0] prog.AddRestSourceArgs([]obj.Addr{a[1], a[2], a[3]}) prog.To = a[4] break } p.errorf("can't handle %s instruction with 5 operands", op) return case 6: if p.arch.Family == sys.ARM && arch.IsARMMRC(op) { // Strange special case: MCR, MRC. prog.To.Type = obj.TYPE_CONST x0 := p.getConstant(prog, op, &a[0]) x1 := p.getConstant(prog, op, &a[1]) x2 := int64(p.getRegister(prog, op, &a[2])) x3 := int64(p.getRegister(prog, op, &a[3])) x4 := int64(p.getRegister(prog, op, &a[4])) x5 := p.getConstant(prog, op, &a[5]) // Cond is handled specially for this instruction. offset, MRC, ok := arch.ARMMRCOffset(op, cond, x0, x1, x2, x3, x4, x5) if !ok { p.errorf("unrecognized condition code .%q", cond) } prog.To.Offset = offset cond = "" prog.As = MRC // Both instructions are coded as MRC. break } if p.arch.Family == sys.PPC64 { prog.From = a[0] // Second arg is always a register type on ppc64. prog.Reg = p.getRegister(prog, op, &a[1]) prog.AddRestSourceArgs([]obj.Addr{a[2], a[3], a[4]}) prog.To = a[5] break } fallthrough default: p.errorf("can't handle %s instruction with %d operands", op, len(a)) return } p.append(prog, cond, true) } // symbolName returns the symbol name, or an error string if none is available. func symbolName(addr *obj.Addr) string { if addr.Sym != nil { return addr.Sym.Name } return "" } var emptyProg obj.Prog // getConstantPseudo checks that addr represents a plain constant and returns its value. func (p *Parser) getConstantPseudo(pseudo string, addr *obj.Addr) int64 { if addr.Type != obj.TYPE_MEM || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 { p.errorf("%s: expected integer constant; found %s", pseudo, obj.Dconv(&emptyProg, addr)) } return addr.Offset } // getConstant checks that addr represents a plain constant and returns its value. func (p *Parser) getConstant(prog *obj.Prog, op obj.As, addr *obj.Addr) int64 { if addr.Type != obj.TYPE_MEM || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 { p.errorf("%s: expected integer constant; found %s", op, obj.Dconv(prog, addr)) } return addr.Offset } // getImmediate checks that addr represents an immediate constant and returns its value. func (p *Parser) getImmediate(prog *obj.Prog, op obj.As, addr *obj.Addr) int64 { if addr.Type != obj.TYPE_CONST || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 { p.errorf("%s: expected immediate constant; found %s", op, obj.Dconv(prog, addr)) } return addr.Offset } // getRegister checks that addr represents a register and returns its value. func (p *Parser) getRegister(prog *obj.Prog, op obj.As, addr *obj.Addr) int16 { if addr.Type != obj.TYPE_REG || addr.Offset != 0 || addr.Name != 0 || addr.Index != 0 { p.errorf("%s: expected register; found %s", op, obj.Dconv(prog, addr)) } return addr.Reg }