diff options
-rw-r--r-- | lib/Target/ARM/ARMInstrInfo.td | 22 | ||||
-rw-r--r-- | lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 89 | ||||
-rw-r--r-- | test/MC/ARM/arm-memory-instructions.s | 14 | ||||
-rw-r--r-- | test/MC/ARM/diagnostics.s | 14 |
4 files changed, 132 insertions, 7 deletions
diff --git a/lib/Target/ARM/ARMInstrInfo.td b/lib/Target/ARM/ARMInstrInfo.td index 678ac2a..0aaf676 100644 --- a/lib/Target/ARM/ARMInstrInfo.td +++ b/lib/Target/ARM/ARMInstrInfo.td @@ -710,20 +710,26 @@ def am2offset_imm : Operand<i32>, // addrmode3 := reg +/- reg // addrmode3 := reg +/- imm8 // -//def AddrMode3AsmOperand : AsmOperandClass { let Name = "AddrMode3"; } +// FIXME: split into imm vs. reg versions. +def AddrMode3AsmOperand : AsmOperandClass { let Name = "AddrMode3"; } def addrmode3 : Operand<i32>, ComplexPattern<i32, 3, "SelectAddrMode3", []> { let EncoderMethod = "getAddrMode3OpValue"; let PrintMethod = "printAddrMode3Operand"; + let ParserMatchClass = AddrMode3AsmOperand; let MIOperandInfo = (ops GPR:$base, GPR:$offsreg, i32imm:$offsimm); } +// FIXME: split into imm vs. reg versions. +// FIXME: parser method to handle +/- register. +def AM3OffsetAsmOperand : AsmOperandClass { let Name = "AM3Offset"; } def am3offset : Operand<i32>, ComplexPattern<i32, 2, "SelectAddrMode3Offset", [], [SDNPWantRoot]> { let EncoderMethod = "getAddrMode3OffsetOpValue"; let DecoderMethod = "DecodeAddrMode3Offset"; let PrintMethod = "printAddrMode3OffsetOperand"; + let ParserMatchClass = AM3OffsetAsmOperand; let MIOperandInfo = (ops GPR, i32imm); } @@ -2030,20 +2036,22 @@ def LDRD_PRE : AI3ldstidx<0b1101, 0, 1, 1, (outs GPR:$Rt, GPR:$Rt2, GPR:$Rn_wb), let Inst{11-8} = addr{7-4}; // imm7_4/zero let Inst{3-0} = addr{3-0}; // imm3_0/Rm let DecoderMethod = "DecodeAddrMode3Instruction"; + let AsmMatchConverter = "cvtLdrdPre"; } def LDRD_POST: AI3ldstidx<0b1101, 0, 1, 0, (outs GPR:$Rt, GPR:$Rt2, GPR:$Rn_wb), - (ins GPR:$Rn, am3offset:$offset), IndexModePost, - LdMiscFrm, IIC_iLoad_d_ru, - "ldrd", "\t$Rt, $Rt2, [$Rn], $offset", - "$Rn = $Rn_wb", []> { + (ins addr_offset_none:$addr, am3offset:$offset), + IndexModePost, LdMiscFrm, IIC_iLoad_d_ru, + "ldrd", "\t$Rt, $Rt2, $addr, $offset", + "$addr.base = $Rn_wb", []> { bits<10> offset; - bits<4> Rn; + bits<4> addr; let Inst{23} = offset{8}; // U bit let Inst{22} = offset{9}; // 1 == imm8, 0 == Rm - let Inst{19-16} = Rn; + let Inst{19-16} = addr; let Inst{11-8} = offset{7-4}; // imm7_4/zero let Inst{3-0} = offset{3-0}; // imm3_0/Rm let DecoderMethod = "DecodeAddrMode3Instruction"; +// let AsmMatchConverter = "cvtLdrdPost"; } } // hasExtraDefRegAllocReq = 1 } // mayLoad = 1, neverHasSideEffects = 1 diff --git a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index 4bae824..8273ffc 100644 --- a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -128,6 +128,8 @@ class ARMAsmParser : public MCTargetAsmParser { const SmallVectorImpl<MCParsedAsmOperand*> &); bool cvtStExtTWriteBackReg(MCInst &Inst, unsigned Opcode, const SmallVectorImpl<MCParsedAsmOperand*> &); + bool cvtLdrdPre(MCInst &Inst, unsigned Opcode, + const SmallVectorImpl<MCParsedAsmOperand*> &); bool validateInstruction(MCInst &Inst, const SmallVectorImpl<MCParsedAsmOperand*> &Ops); @@ -534,6 +536,29 @@ public: int64_t Val = CE->getValue(); return Val > -4096 && Val < 4096; } + bool isAddrMode3() const { + if (Kind != Memory) + return false; + // No shifts are legal for AM3. + if (Mem.ShiftType != ARM_AM::no_shift) return false; + // Check for register offset. + if (Mem.OffsetRegNum) return true; + // Immediate offset in range [-255, 255]. + if (!Mem.OffsetImm) return true; + int64_t Val = Mem.OffsetImm->getValue(); + return Val > -256 && Val < 256; + } + bool isAM3Offset() const { + if (Kind != Immediate && Kind != PostIndexRegister) + return false; + if (Kind == PostIndexRegister) + return PostIdxReg.ShiftTy == ARM_AM::no_shift; + // Immediate offset in range [-255, 255]. + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); + if (!CE) return false; + int64_t Val = CE->getValue(); + return Val > -256 && Val < 256; + } bool isAddrMode5() const { if (Kind != Memory) return false; @@ -814,6 +839,46 @@ public: Inst.addOperand(MCOperand::CreateImm(Val)); } + void addAddrMode3Operands(MCInst &Inst, unsigned N) const { + assert(N == 3 && "Invalid number of operands!"); + int32_t Val = Mem.OffsetImm ? Mem.OffsetImm->getValue() : 0; + if (!Mem.OffsetRegNum) { + ARM_AM::AddrOpc AddSub = Val < 0 ? ARM_AM::sub : ARM_AM::add; + // Special case for #-0 + if (Val == INT32_MIN) Val = 0; + if (Val < 0) Val = -Val; + Val = ARM_AM::getAM3Opc(AddSub, Val); + } else { + // For register offset, we encode the shift type and negation flag + // here. + Val = ARM_AM::getAM3Opc(Mem.isNegative ? ARM_AM::sub : ARM_AM::add, 0); + } + Inst.addOperand(MCOperand::CreateReg(Mem.BaseRegNum)); + Inst.addOperand(MCOperand::CreateReg(Mem.OffsetRegNum)); + Inst.addOperand(MCOperand::CreateImm(Val)); + } + + void addAM3OffsetOperands(MCInst &Inst, unsigned N) const { + assert(N == 2 && "Invalid number of operands!"); + if (Kind == PostIndexRegister) { + int32_t Val = + ARM_AM::getAM3Opc(PostIdxReg.isAdd ? ARM_AM::add : ARM_AM::sub, 0); + Inst.addOperand(MCOperand::CreateReg(PostIdxReg.RegNum)); + Inst.addOperand(MCOperand::CreateImm(Val)); + } + + // Constant offset. + const MCConstantExpr *CE = static_cast<const MCConstantExpr*>(getImm()); + int32_t Val = CE->getValue(); + ARM_AM::AddrOpc AddSub = Val < 0 ? ARM_AM::sub : ARM_AM::add; + // Special case for #-0 + if (Val == INT32_MIN) Val = 0; + if (Val < 0) Val = -Val; + Val = ARM_AM::getAM2Opc(AddSub, Val, ARM_AM::no_shift); + Inst.addOperand(MCOperand::CreateReg(0)); + Inst.addOperand(MCOperand::CreateImm(Val)); + } + void addAddrMode5Operands(MCInst &Inst, unsigned N) const { assert(N == 2 && "Invalid number of operands!"); // The lower two bits are always zero and as such are not encoded. @@ -2046,6 +2111,24 @@ cvtStExtTWriteBackReg(MCInst &Inst, unsigned Opcode, return true; } +/// cvtLdrdPre - Convert parsed operands to MCInst. +/// Needed here because the Asm Gen Matcher can't handle properly tied operands +/// when they refer multiple MIOperands inside a single one. +bool ARMAsmParser:: +cvtLdrdPre(MCInst &Inst, unsigned Opcode, + const SmallVectorImpl<MCParsedAsmOperand*> &Operands) { + // Rt, Rt2 + ((ARMOperand*)Operands[2])->addRegOperands(Inst, 1); + ((ARMOperand*)Operands[3])->addRegOperands(Inst, 1); + // Create a writeback register dummy placeholder. + Inst.addOperand(MCOperand::CreateImm(0)); + // addr + ((ARMOperand*)Operands[4])->addAddrMode3Operands(Inst, 3); + // pred + ((ARMOperand*)Operands[1])->addCondCodeOperands(Inst, 2); + return true; +} + /// Parse an ARM memory expression, return false if successful else return true /// or an error. The first token must be a '[' when called. bool ARMAsmParser:: @@ -2645,6 +2728,9 @@ bool ARMAsmParser:: validateInstruction(MCInst &Inst, const SmallVectorImpl<MCParsedAsmOperand*> &Operands) { switch (Inst.getOpcode()) { + case ARM::LDRD: + case ARM::LDRD_PRE: + case ARM::LDRD_POST: case ARM::LDREXD: { // Rt2 must be Rt + 1. unsigned Rt = getARMRegisterNumbering(Inst.getOperand(0).getReg()); @@ -2654,6 +2740,9 @@ validateInstruction(MCInst &Inst, "destination operands must be sequential"); return false; } + case ARM::STRgD: + case ARM::STRgD_PRE: + case ARM::STRgD_POST: case ARM::STREXD: { // Rt2 must be Rt + 1. unsigned Rt = getARMRegisterNumbering(Inst.getOperand(1).getReg()); diff --git a/test/MC/ARM/arm-memory-instructions.s b/test/MC/ARM/arm-memory-instructions.s index 961f24f..a216a11 100644 --- a/test/MC/ARM/arm-memory-instructions.s +++ b/test/MC/ARM/arm-memory-instructions.s @@ -105,3 +105,17 @@ _func: @ CHECK: ldrbt r2, [r8], #-8 @ encoding: [0x08,0x20,0x78,0xe4] @ CHECK: ldrbt r8, [r7], r6 @ encoding: [0x06,0x80,0xf7,0xe6] @ CHECK: ldrbt r1, [r2], -r6, lsl #12 @ encoding: [0x06,0x16,0x72,0xe6] + + +@------------------------------------------------------------------------------ +@ LDRD (immediate) +@------------------------------------------------------------------------------ + ldrd r3, r4, [r5] + ldrd r7, r8, [r2, #15] + ldrd r1, r2, [r9, #32]! + ldrd r6, r7, [r1], #8 + +@ CHECK: ldrd r3, r4, [r5] @ encoding: [0xd0,0x30,0xc5,0xe1] +@ CHECK: ldrd r7, r8, [r2, #15] @ encoding: [0xdf,0x70,0xc2,0xe1] +@ CHECK: ldrd r1, r2, [r9, #32]! @ encoding: [0xd0,0x12,0xe9,0xe1] +@ CHECK: ldrd r6, r7, [r1], #8 @ encoding: [0xd8,0x60,0xc1,0xe0] diff --git a/test/MC/ARM/diagnostics.s b/test/MC/ARM/diagnostics.s index cc24416..da1b885 100644 --- a/test/MC/ARM/diagnostics.s +++ b/test/MC/ARM/diagnostics.s @@ -283,3 +283,17 @@ @ CHECK-ERRORS: error: bitfield width must be in range [1,32-lsb] @ CHECK-ERRORS: ubfxgt r4, r5, #16, #17 @ CHECK-ERRORS: ^ + + @ Out of order Rt/Rt2 operands for ldrd + ldrd r4, r3, [r8] + ldrd r4, r3, [r8, #8]! + ldrd r4, r3, [r8], #8 +@ CHECK-ERRORS: error: destination operands must be sequential +@ CHECK-ERRORS: ldrd r4, r3, [r8] +@ CHECK-ERRORS: ^ +@ CHECK-ERRORS: error: destination operands must be sequential +@ CHECK-ERRORS: ldrd r4, r3, [r8, #8]! +@ CHECK-ERRORS: ^ +@ CHECK-ERRORS: error: destination operands must be sequential +@ CHECK-ERRORS: ldrd r4, r3, [r8], #8 +@ CHECK-ERRORS: ^ |