// asmInstruction assembles an instruction. // MOVW R9, (R10) func (p *Parser) asmInstruction(op int, cond string, a []obj.Addr) { // fmt.Printf("%s %+v\n", obj.Aconv(op), a) prog := &obj.Prog{ Ctxt: p.ctxt, Lineno: p.histLineNum, As: int16(op), } switch len(a) { case 0: // Nothing to do. case 1: if p.arch.UnaryDst[op] { // prog.From is no address. prog.To = a[0] } else { prog.From = a[0] // prog.To is no address. } if p.arch.Thechar == '9' && 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.Thechar == '5' { if arch.IsARMCMP(op) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) break } // Strange special cases. if arch.IsARMSTREX(op) { /* STREX x, (y) from=(y) reg=x to=x STREX (x), y from=(x) reg=y to=y */ if a[0].Type == obj.TYPE_REG && a[1].Type != obj.TYPE_REG { prog.From = a[1] prog.Reg = a[0].Reg prog.To = a[0] break } else if a[0].Type != obj.TYPE_REG && a[1].Type == obj.TYPE_REG { prog.From = a[0] prog.Reg = a[1].Reg prog.To = a[1] break } p.errorf("unrecognized addressing for %s", obj.Aconv(op)) return } if arch.IsARMFloatCmp(op) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) break } } else if p.arch.Thechar == '7' && arch.IsARM64CMP(op) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) break } prog.From = a[0] prog.To = a[1] case 3: switch p.arch.Thechar { case '5': // 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 } // 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 '7': // ARM64 instructions with one input and two outputs. if arch.IsARM64STLXR(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", obj.Aconv(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 '6', '8': prog.From = a[0] prog.From3 = newAddr(a[1]) prog.To = a[2] case '9': 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 } // Arithmetic. Choices are: // reg reg reg // imm reg reg // reg imm reg // If the immediate is the middle argument, use From3. switch a[1].Type { case obj.TYPE_REG: prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.To = a[2] case obj.TYPE_CONST: prog.From = a[0] prog.From3 = newAddr(a[1]) prog.To = a[2] default: p.errorf("invalid addressing modes for %s instruction", obj.Aconv(op)) return } default: p.errorf("TODO: implement three-operand instructions for this architecture") return } case 4: if p.arch.Thechar == '5' && arch.IsARMMULA(op) { // All must be registers. p.getRegister(prog, op, &a[0]) r1 := p.getRegister(prog, op, &a[1]) p.getRegister(prog, op, &a[2]) r3 := p.getRegister(prog, op, &a[3]) prog.From = a[0] prog.To = a[2] prog.To.Type = obj.TYPE_REGREG2 prog.To.Offset = int64(r3) prog.Reg = r1 break } if p.arch.Thechar == '7' { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.From3 = newAddr(a[2]) prog.To = a[3] break } if p.arch.Thechar == '9' && arch.IsPPC64RLD(op) { // 2nd operand must always be a register. // TODO: Do we need to guard this with the instruction type? // That is, are there 4-operand instructions without this property? prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.From3 = newAddr(a[2]) prog.To = a[3] break } p.errorf("can't handle %s instruction with 4 operands", obj.Aconv(op)) return case 5: if p.arch.Thechar == '9' && arch.IsPPC64RLD(op) { // Always reg, reg, con, con, reg. (con, con is a 'mask'). prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) mask1 := p.getConstant(prog, op, &a[2]) mask2 := p.getConstant(prog, op, &a[3]) var mask uint32 if mask1 < mask2 { mask = (^uint32(0) >> uint(mask1)) & (^uint32(0) << uint(31-mask2)) } else { mask = (^uint32(0) >> uint(mask2+1)) & (^uint32(0) << uint(31-(mask1-1))) } prog.From3 = &obj.Addr{ Type: obj.TYPE_CONST, Offset: int64(mask), } prog.To = a[4] break } p.errorf("can't handle %s instruction with 5 operands", obj.Aconv(op)) return case 6: if p.arch.Thechar == '5' && 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 } fallthrough default: p.errorf("can't handle %s instruction with %d operands", obj.Aconv(op), len(a)) return } p.append(prog, cond, true) }
// 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, Lineno: p.histLineNum, As: op, } switch len(a) { case 0: // Nothing to do. case 1: if p.arch.UnaryDst[op] { // 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.IsARMSTREX(op) { /* STREX x, (y) from=(y) reg=x to=x STREX (x), y from=(x) reg=y to=y */ if a[0].Type == obj.TYPE_REG && a[1].Type != obj.TYPE_REG { prog.From = a[1] prog.Reg = a[0].Reg prog.To = a[0] break } else if a[0].Type != obj.TYPE_REG && a[1].Type == obj.TYPE_REG { prog.From = a[0] prog.Reg = a[1].Reg prog.To = a[1] break } p.errorf("unrecognized addressing for %s", op) return } 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.MIPS64 { if arch.IsMIPS64CMP(op) || arch.IsMIPS64MUL(op) { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) break } } prog.From = a[0] prog.To = a[1] case 3: switch p.arch.Family { case sys.MIPS64: 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 } // 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: // Catch missing operand here, because we store immediate as part of From3, and can't distinguish // missing operand from legal value 0 in obj/x86/asm6. if arch.IsAMD4OP(op) { p.errorf("4 operands required, but only 3 are provided for %s instruction", op) } prog.From = a[0] prog.From3 = newAddr(a[1]) prog.To = a[2] case sys.ARM64: // ARM64 instructions with one input and two outputs. if arch.IsARM64STLXR(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.I386: prog.From = a[0] prog.From3 = newAddr(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 } // Arithmetic. Choices are: // reg reg reg // imm reg reg // reg imm reg // If the immediate is the middle argument, use From3. switch a[1].Type { case obj.TYPE_REG: prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.To = a[2] case obj.TYPE_CONST: prog.From = a[0] prog.From3 = newAddr(a[1]) prog.To = a[2] default: p.errorf("invalid addressing modes for %s instruction", op) return } case sys.S390X: if arch.IsS390xWithLength(op) || arch.IsS390xWithIndex(op) { prog.From = a[1] prog.From3 = newAddr(a[0]) } else { prog.Reg = p.getRegister(prog, op, &a[1]) prog.From = a[0] } prog.To = a[2] default: p.errorf("TODO: implement three-operand instructions for this architecture") return } case 4: if p.arch.Family == sys.ARM && arch.IsARMMULA(op) { // All must be registers. p.getRegister(prog, op, &a[0]) r1 := p.getRegister(prog, op, &a[1]) p.getRegister(prog, op, &a[2]) r3 := p.getRegister(prog, op, &a[3]) prog.From = a[0] prog.To = a[2] prog.To.Type = obj.TYPE_REGREG2 prog.To.Offset = int64(r3) prog.Reg = r1 break } if p.arch.Family == sys.AMD64 { // 4 operand instruction have form ymm1, ymm2, ymm3/m256, imm8 // So From3 is always just a register, so we store imm8 in Offset field, // to avoid increasing size of Prog. prog.From = a[1] prog.From3 = newAddr(a[2]) if a[0].Type != obj.TYPE_CONST { p.errorf("first operand must be an immediate in %s instruction", op) } if prog.From3.Type != obj.TYPE_REG { p.errorf("third operand must be a register in %s instruction", op) } prog.From3.Offset = int64(p.getImmediate(prog, op, &a[0])) prog.To = a[3] prog.RegTo2 = -1 break } if p.arch.Family == sys.ARM64 { prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.From3 = newAddr(a[2]) prog.To = a[3] break } if p.arch.Family == sys.PPC64 && arch.IsPPC64RLD(op) { // 2nd operand must always be a register. // TODO: Do we need to guard this with the instruction type? // That is, are there 4-operand instructions without this property? prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) prog.From3 = newAddr(a[2]) prog.To = a[3] break } if p.arch.Family == sys.S390X { prog.From = a[1] prog.Reg = p.getRegister(prog, op, &a[2]) prog.From3 = newAddr(a[0]) 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 && arch.IsPPC64RLD(op) { // Always reg, reg, con, con, reg. (con, con is a 'mask'). prog.From = a[0] prog.Reg = p.getRegister(prog, op, &a[1]) mask1 := p.getConstant(prog, op, &a[2]) mask2 := p.getConstant(prog, op, &a[3]) var mask uint32 if mask1 < mask2 { mask = (^uint32(0) >> uint(mask1)) & (^uint32(0) << uint(31-mask2)) } else { mask = (^uint32(0) >> uint(mask2+1)) & (^uint32(0) << uint(31-(mask1-1))) } prog.From3 = &obj.Addr{ Type: obj.TYPE_CONST, Offset: int64(mask), } 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 } fallthrough default: p.errorf("can't handle %s instruction with %d operands", op, len(a)) return } p.append(prog, cond, true) }