diff --git a/lib/Target/ARM/ARMInstrInfo.td b/lib/Target/ARM/ARMInstrInfo.td index 3c1d77939b6..3fd27b0e305 100644 --- a/lib/Target/ARM/ARMInstrInfo.td +++ b/lib/Target/ARM/ARMInstrInfo.td @@ -412,14 +412,20 @@ def shift_imm : Operand { let ParserMatchClass = ShifterAsmOperand; } +def ShiftedRegAsmOperand : AsmOperandClass { + let Name = "ShiftedReg"; +} + // shifter_operand operands: so_reg and so_imm. def so_reg : Operand, // reg reg imm ComplexPattern { let EncoderMethod = "getSORegOpValue"; let PrintMethod = "printSORegOperand"; + let ParserMatchClass = ShiftedRegAsmOperand; let MIOperandInfo = (ops GPR, GPR, shift_imm); } +// FIXME: Does this need to be distinct from so_reg? def shift_so_reg : Operand, // reg reg imm ComplexPattern { diff --git a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index e90962ff0b3..035907dd0a5 100644 --- a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -164,6 +164,7 @@ class ARMOperand : public MCParsedAsmOperand { RegisterList, DPRRegisterList, SPRRegisterList, + ShiftedRegister, Shifter, Token } Kind; @@ -225,8 +226,14 @@ class ARMOperand : public MCParsedAsmOperand { struct { ARM_AM::ShiftOpc ShiftTy; - unsigned RegNum; + unsigned Imm; } Shift; + struct { + ARM_AM::ShiftOpc ShiftTy; + unsigned SrcReg; + unsigned ShiftReg; + unsigned ShiftImm; + } ShiftedReg; }; ARMOperand(KindTy K) : MCParsedAsmOperand(), Kind(K) {} @@ -273,6 +280,9 @@ public: case Shifter: Shift = o.Shift; break; + case ShiftedRegister: + ShiftedReg = o.ShiftedReg; + break; } } @@ -392,6 +402,7 @@ public: bool isMemBarrierOpt() const { return Kind == MemBarrierOpt; } bool isMemory() const { return Kind == Memory; } bool isShifter() const { return Kind == Shifter; } + bool isShiftedReg() const { return Kind == ShiftedRegister; } bool isMemMode2() const { if (getMemAddrMode() != ARMII::AddrMode2) return false; @@ -522,6 +533,18 @@ public: Inst.addOperand(MCOperand::CreateReg(getReg())); } + void addShiftedRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 3 && "Invalid number of operands!"); + assert(isShiftedReg() && "addShiftedRegOperands() on non ShiftedReg!"); + assert((ShiftedReg.ShiftReg == 0 || + ARM_AM::getSORegOffset(ShiftedReg.ShiftImm) == 0) && + "Invalid shifted register operand!"); + Inst.addOperand(MCOperand::CreateReg(ShiftedReg.SrcReg)); + Inst.addOperand(MCOperand::CreateReg(ShiftedReg.ShiftReg)); + Inst.addOperand(MCOperand::CreateImm( + ARM_AM::getSORegOpc(ShiftedReg.ShiftTy, ShiftedReg.ShiftImm))); + } + void addShifterOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); Inst.addOperand(MCOperand::CreateImm( @@ -743,6 +766,21 @@ public: return Op; } + static ARMOperand *CreateShiftedRegister(ARM_AM::ShiftOpc ShTy, + unsigned SrcReg, + unsigned ShiftReg, + unsigned ShiftImm, + SMLoc S, SMLoc E) { + ARMOperand *Op = new ARMOperand(ShiftedRegister); + Op->ShiftedReg.ShiftTy = ShTy; + Op->ShiftedReg.SrcReg = SrcReg; + Op->ShiftedReg.ShiftReg = ShiftReg; + Op->ShiftedReg.ShiftImm = ShiftImm; + Op->StartLoc = S; + Op->EndLoc = E; + return Op; + } + static ARMOperand *CreateShifter(ARM_AM::ShiftOpc ShTy, SMLoc S, SMLoc E) { ARMOperand *Op = new ARMOperand(Shifter); @@ -907,7 +945,15 @@ void ARMOperand::print(raw_ostream &OS) const { OS << ""; break; case Shifter: - OS << ""; + OS << ""; + break; + case ShiftedRegister: + OS << ""; break; case RegisterList: case DPRRegisterList: @@ -994,13 +1040,56 @@ bool ARMAsmParser::TryParseShiftRegister( if (ShiftTy == ARM_AM::no_shift) return true; - Parser.Lex(); // Eat shift-type operand; - int RegNum = TryParseRegister(); - if (RegNum == -1) - return Error(Parser.getTok().getLoc(), "register expected"); + Parser.Lex(); // Eat the operator. - Operands.push_back(ARMOperand::CreateReg(RegNum,S, Parser.getTok().getLoc())); - Operands.push_back(ARMOperand::CreateShifter(ShiftTy, + // The source register for the shift has already been added to the + // operand list, so we need to pop it off and combine it into the shifted + // register operand instead. + ARMOperand *PrevOp = (ARMOperand*)Operands.pop_back_val(); + if (!PrevOp->isReg()) + return Error(PrevOp->getStartLoc(), "shift must be of a register"); + int SrcReg = PrevOp->getReg(); + int64_t Imm = 0; + int ShiftReg = 0; + if (ShiftTy == ARM_AM::rrx) { + // RRX Doesn't have an explicit shift amount. The encoder expects + // the shift register to be the same as the source register. Seems odd, + // but OK. + ShiftReg = SrcReg; + } else { + // Figure out if this is shifted by a constant or a register (for non-RRX). + if (Parser.getTok().is(AsmToken::Hash)) { + Parser.Lex(); // Eat hash. + SMLoc ImmLoc = Parser.getTok().getLoc(); + const MCExpr *ShiftExpr = 0; + if (getParser().ParseExpression(ShiftExpr)) + return Error(ImmLoc, "invalid immediate shift value"); + // The expression must be evaluatable as an immediate. + const MCConstantExpr *CE = dyn_cast(ShiftExpr); + if (!CE) + return Error(ImmLoc, "invalid immediate shift value"); + // Range check the immediate. + // lsl, ror: 0 <= imm <= 31 + // lsr, asr: 0 <= imm <= 32 + Imm = CE->getValue(); + if (Imm < 0 || + ((ShiftTy == ARM_AM::lsl || ShiftTy == ARM_AM::ror) && Imm > 31) || + ((ShiftTy == ARM_AM::lsr || ShiftTy == ARM_AM::asr) && Imm > 32)) { + return Error(ImmLoc, "immediate shift value out of range"); + } + } else if (Parser.getTok().is(AsmToken::Identifier)) { + ShiftReg = TryParseRegister(); + SMLoc L = Parser.getTok().getLoc(); + if (ShiftReg == -1) + return Error (L, "expected immediate or register in shift operand"); + } else + return Error (Parser.getTok().getLoc(), + "expected immediate or register in shift operand"); + } + + + Operands.push_back(ARMOperand::CreateShiftedRegister(ShiftTy, SrcReg, + ShiftReg, Imm, S, Parser.getTok().getLoc())); return false; diff --git a/lib/Target/ARM/InstPrinter/ARMInstPrinter.cpp b/lib/Target/ARM/InstPrinter/ARMInstPrinter.cpp index b2f2f023650..78d3e477975 100644 --- a/lib/Target/ARM/InstPrinter/ARMInstPrinter.cpp +++ b/lib/Target/ARM/InstPrinter/ARMInstPrinter.cpp @@ -142,6 +142,8 @@ void ARMInstPrinter::printSORegOperand(const MCInst *MI, unsigned OpNum, // Print the shift opc. ARM_AM::ShiftOpc ShOpc = ARM_AM::getSORegShOp(MO3.getImm()); O << ", " << ARM_AM::getShiftOpcStr(ShOpc); + if (ShOpc == ARM_AM::rrx) + return; if (MO2.getReg()) { O << ' ' << getRegisterName(MO2.getReg()); assert(ARM_AM::getSORegOffset(MO3.getImm()) == 0); diff --git a/test/MC/ARM/basic-arm-instructions.s b/test/MC/ARM/basic-arm-instructions.s index 254d33d0a1e..756b59f10ec 100644 --- a/test/MC/ARM/basic-arm-instructions.s +++ b/test/MC/ARM/basic-arm-instructions.s @@ -1,6 +1,13 @@ @ RUN: llvm-mc -triple=armv7-apple-darwin -show-encoding < %s | FileCheck %s .syntax unified .globl _func + +@ Check that the assembler can handle the documented syntax from the ARM ARM. +@ For complex constructs like shifter operands, check more thoroughly for them +@ once then spot check that following instructions accept the form generally. +@ This gives us good coverage while keeping the overall size of the test +@ more reasonable. + _func: @ CHECK: _func @@ -31,3 +38,44 @@ _func: @ CHECK: adcs r1, r2, #3840 @ encoding: [0x0f,0x1c,0xb2,0xe2] @ CHECK: adcseq r1, r2, #3840 @ encoding: [0x0f,0x1c,0xb2,0x02] @ CHECK: adceq r1, r2, #3840 @ encoding: [0x0f,0x1c,0xa2,0x02] + +@ ADC (register) + adc r4, r5, r6 + @ Constant shifts + adc r4, r5, r6, lsl #1 + adc r4, r5, r6, lsl #31 + adc r4, r5, r6, lsr #1 + adc r4, r5, r6, lsr #31 + adc r4, r5, r6, lsr #32 + adc r4, r5, r6, asr #1 + adc r4, r5, r6, asr #31 + adc r4, r5, r6, asr #32 + adc r4, r5, r6, ror #1 + adc r4, r5, r6, ror #31 + + @ Register shifts + adc r6, r7, r8, lsl r9 + adc r6, r7, r8, lsr r9 + adc r6, r7, r8, asr r9 + adc r6, r7, r8, ror r9 + adc r4, r5, r6, rrx + +@ CHECK: adc r4, r5, r6 @ encoding: [0x06,0x40,0xa5,0xe0] + +@ CHECK: adc r4, r5, r6, lsl #1 @ encoding: [0x86,0x40,0xa5,0xe0] +@ CHECK: adc r4, r5, r6, lsl #31 @ encoding: [0x86,0x4f,0xa5,0xe0] +@ CHECK: adc r4, r5, r6, lsr #1 @ encoding: [0xa6,0x40,0xa5,0xe0] +@ CHECK: adc r4, r5, r6, lsr #31 @ encoding: [0xa6,0x4f,0xa5,0xe0] +@ CHECK: adc r4, r5, r6, lsr #32 @ encoding: [0x26,0x40,0xa5,0xe0] +@ CHECK: adc r4, r5, r6, asr #1 @ encoding: [0xc6,0x40,0xa5,0xe0] +@ CHECK: adc r4, r5, r6, asr #31 @ encoding: [0xc6,0x4f,0xa5,0xe0] +@ CHECK: adc r4, r5, r6, asr #32 @ encoding: [0x46,0x40,0xa5,0xe0] +@ CHECK: adc r4, r5, r6, ror #1 @ encoding: [0xe6,0x40,0xa5,0xe0] +@ CHECK: adc r4, r5, r6, ror #31 @ encoding: [0xe6,0x4f,0xa5,0xe0] + +@ CHECK: adc r6, r7, r8, lsl r9 @ encoding: [0x18,0x69,0xa7,0xe0] +@ CHECK: adc r6, r7, r8, lsr r9 @ encoding: [0x38,0x69,0xa7,0xe0] +@ CHECK: adc r6, r7, r8, asr r9 @ encoding: [0x58,0x69,0xa7,0xe0] +@ CHECK: adc r6, r7, r8, ror r9 @ encoding: [0x78,0x69,0xa7,0xe0] +@ CHECK: adc r4, r5, r6, rrx @ encoding: [0x66,0x40,0xa5,0xe0] +