From a77295db19527503d6b290e4f34f273d0a789365 Mon Sep 17 00:00:00 2001 From: Jim Grosbach Date: Thu, 8 Sep 2011 22:07:06 +0000 Subject: [PATCH] Thumb2 assembly parsing and encoding for LDRD(immediate). Refactor operand handling for STRD as well. Tests for that forthcoming. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@139322 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/ARM/ARMCodeEmitter.cpp | 2 + lib/Target/ARM/ARMInstrFormats.td | 17 ++--- lib/Target/ARM/ARMInstrThumb2.td | 44 +++++++---- lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 71 +++++++++++++++++ .../ARM/Disassembler/ARMDisassembler.cpp | 76 +++++++++++++++++++ .../ARM/MCTargetDesc/ARMMCCodeEmitter.cpp | 41 ++++++++++ test/MC/ARM/basic-thumb2-instructions.s | 18 +++++ .../ARM/invalid-LDRD_PRE-thumb.txt | 1 - 8 files changed, 244 insertions(+), 26 deletions(-) diff --git a/lib/Target/ARM/ARMCodeEmitter.cpp b/lib/Target/ARM/ARMCodeEmitter.cpp index a94f40ae74a..d587eca3858 100644 --- a/lib/Target/ARM/ARMCodeEmitter.cpp +++ b/lib/Target/ARM/ARMCodeEmitter.cpp @@ -207,6 +207,8 @@ namespace { const { return 0; } unsigned getT2AddrModeImm8OpValue(const MachineInstr &MI, unsigned Op) const { return 0; } + unsigned getT2Imm8s4OpValue(const MachineInstr &MI, unsigned Op) + const { return 0; } unsigned getT2AddrModeImm8s4OpValue(const MachineInstr &MI, unsigned Op) const { return 0; } unsigned getT2AddrModeImm8OffsetOpValue(const MachineInstr &MI, unsigned Op) diff --git a/lib/Target/ARM/ARMInstrFormats.td b/lib/Target/ARM/ARMInstrFormats.td index 9387cf0ab12..67a74e04b1d 100644 --- a/lib/Target/ARM/ARMInstrFormats.td +++ b/lib/Target/ARM/ARMInstrFormats.td @@ -1100,8 +1100,8 @@ class T2Ipc pattern> : Thumb2I; class T2Ii8s4 pattern> - : Thumb2I pattern> + : Thumb2I { bits<4> Rt; bits<4> Rt2; @@ -1117,14 +1117,14 @@ class T2Ii8s4 pattern> - : Thumb2I pattern> + : Thumb2I { bits<4> Rt; bits<4> Rt2; - bits<4> base; + bits<4> addr; bits<9> imm; let Inst{31-25} = 0b1110100; let Inst{24} = P; @@ -1132,13 +1132,12 @@ class T2Ii8s4Tied pattern> : Thumb2sI; diff --git a/lib/Target/ARM/ARMInstrThumb2.td b/lib/Target/ARM/ARMInstrThumb2.td index 3e64c1905c0..c95d8aa571b 100644 --- a/lib/Target/ARM/ARMInstrThumb2.td +++ b/lib/Target/ARM/ARMInstrThumb2.td @@ -164,15 +164,19 @@ def t2am_imm8_offset : Operand, } // t2addrmode_imm8s4 := reg +/- (imm8 << 2) +def MemImm8s4OffsetAsmOperand : AsmOperandClass {let Name = "MemImm8s4Offset";} def t2addrmode_imm8s4 : Operand { let PrintMethod = "printT2AddrModeImm8s4Operand"; let EncoderMethod = "getT2AddrModeImm8s4OpValue"; let DecoderMethod = "DecodeT2AddrModeImm8s4"; + let ParserMatchClass = MemImm8s4OffsetAsmOperand; let MIOperandInfo = (ops GPR:$base, i32imm:$offsimm); } +def t2am_imm8s4_offset_asmoperand : AsmOperandClass { let Name = "Imm8s4"; } def t2am_imm8s4_offset : Operand { let PrintMethod = "printT2AddrModeImm8s4OffsetOperand"; + let EncoderMethod = "getT2Imm8s4OpValue"; let DecoderMethod = "DecodeT2Imm8S4"; } @@ -1193,7 +1197,7 @@ let mayLoad = 1, neverHasSideEffects = 1, hasExtraDefRegAllocReq = 1 in { // Load doubleword def t2LDRDi8 : T2Ii8s4<1, 0, 1, (outs rGPR:$Rt, rGPR:$Rt2), (ins t2addrmode_imm8s4:$addr), - IIC_iLoad_d_i, "ldrd", "\t$Rt, $Rt2, $addr", []>; + IIC_iLoad_d_i, "ldrd", "\t$Rt, $Rt2, $addr", "", []>; } // mayLoad = 1, neverHasSideEffects = 1, hasExtraDefRegAllocReq = 1 // zextload i1 -> zextload i8 @@ -1344,7 +1348,7 @@ defm t2STRH:T2I_st<0b01,"strh", IIC_iStore_bh_i, IIC_iStore_bh_si, let mayLoad = 1, neverHasSideEffects = 1, hasExtraSrcRegAllocReq = 1 in def t2STRDi8 : T2Ii8s4<1, 0, 0, (outs), (ins GPR:$Rt, GPR:$Rt2, t2addrmode_imm8s4:$addr), - IIC_iStore_d_r, "strd", "\t$Rt, $Rt2, $addr", []>; + IIC_iStore_d_r, "strd", "\t$Rt, $Rt2, $addr", "", []>; // Indexed stores def t2STR_PRE : T2Ipreldst<0, 0b10, 0, 1, (outs GPRnopc:$Rn_wb), @@ -1424,23 +1428,31 @@ def t2STRHT : T2IstT<0b01, "strht", IIC_iStore_bh_i>; // ldrd / strd pre / post variants // For disassembly only. -def t2LDRD_PRE : T2Ii8s4Tied<1, 1, 1, - (outs rGPR:$Rt, rGPR:$Rt2, GPR:$wb), - (ins GPR:$base, t2am_imm8s4_offset:$imm), IIC_iLoad_d_ru, - "ldrd", "\t$Rt, $Rt2, [$base, $imm]!", []>; +def t2LDRD_PRE : T2Ii8s4<1, 1, 1, (outs rGPR:$Rt, rGPR:$Rt2, GPR:$wb), + (ins t2addrmode_imm8s4:$addr), IIC_iLoad_d_ru, + "ldrd", "\t$Rt, $Rt2, $addr!", "$addr.base = $wb", []> { + let AsmMatchConverter = "cvtT2LdrdPre"; + let DecoderMethod = "DecodeT2LDRDPreInstruction"; +} -def t2LDRD_POST : T2Ii8s4Tied<0, 1, 1, - (outs rGPR:$Rt, rGPR:$Rt2, GPR:$wb), - (ins GPR:$base, t2am_imm8s4_offset:$imm), IIC_iLoad_d_ru, - "ldrd", "\t$Rt, $Rt2, [$base], $imm", []>; +def t2LDRD_POST : T2Ii8s4post<0, 1, 1, (outs rGPR:$Rt, rGPR:$Rt2, GPR:$wb), + (ins addr_offset_none:$addr, t2am_imm8s4_offset:$imm), + IIC_iLoad_d_ru, "ldrd", "\t$Rt, $Rt2, $addr, $imm", + "$addr.base = $wb", []>; -def t2STRD_PRE : T2Ii8s4Tied<1, 1, 0, (outs GPR:$wb), - (ins rGPR:$Rt, rGPR:$Rt2, GPR:$base, t2am_imm8s4_offset:$imm), - IIC_iStore_d_ru, "strd", "\t$Rt, $Rt2, [$base, $imm]!", []>; +def t2STRD_PRE : T2Ii8s4<1, 1, 0, (outs GPR:$wb), + (ins rGPR:$Rt, rGPR:$Rt2, t2addrmode_imm8s4:$addr), + IIC_iStore_d_ru, "strd", "\t$Rt, $Rt2, $addr!", + "$addr.base = $wb", []> { + let AsmMatchConverter = "cvtT2StrdPre"; + let DecoderMethod = "DecodeT2STRDPreInstruction"; +} -def t2STRD_POST : T2Ii8s4Tied<0, 1, 0, (outs GPR:$wb), - (ins rGPR:$Rt, rGPR:$Rt2, GPR:$base, t2am_imm8s4_offset:$imm), - IIC_iStore_d_ru, "strd", "\t$Rt, $Rt2, [$base], $imm", []>; +def t2STRD_POST : T2Ii8s4post<0, 1, 0, (outs GPR:$wb), + (ins rGPR:$Rt, rGPR:$Rt2, addr_offset_none:$addr, + t2am_imm8s4_offset:$imm), + IIC_iStore_d_ru, "strd", "\t$Rt, $Rt2, $addr, $imm", + "$addr.base = $wb", []>; // T2Ipl (Preload Data/Instruction) signals the memory system of possible future // data/instruction access. These are for disassembly only. diff --git a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index abe55050b38..afab484a17d 100644 --- a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -158,6 +158,10 @@ class ARMAsmParser : public MCTargetAsmParser { OperandMatchResultTy parseAM3Offset(SmallVectorImpl&); // Asm Match Converter Methods + bool cvtT2LdrdPre(MCInst &Inst, unsigned Opcode, + const SmallVectorImpl &); + bool cvtT2StrdPre(MCInst &Inst, unsigned Opcode, + const SmallVectorImpl &); bool cvtLdWriteBackRegT2AddrModeImm8(MCInst &Inst, unsigned Opcode, const SmallVectorImpl &); bool cvtLdWriteBackRegAddrMode2(MCInst &Inst, unsigned Opcode, @@ -463,6 +467,14 @@ public: bool isITMask() const { return Kind == ITCondMask; } bool isITCondCode() const { return Kind == CondCode; } bool isImm() const { return Kind == Immediate; } + bool isImm8s4() const { + if (Kind != Immediate) + return false; + const MCConstantExpr *CE = dyn_cast(getImm()); + if (!CE) return false; + int64_t Value = CE->getValue(); + return ((Value & 3) == 0) && Value >= -1020 && Value <= 1020; + } bool isImm0_1020s4() const { if (Kind != Immediate) return false; @@ -736,6 +748,14 @@ public: int64_t Val = Mem.OffsetImm->getValue(); return Val >= 0 && Val <= 1020 && (Val % 4) == 0; } + bool isMemImm8s4Offset() const { + if (Kind != Memory || Mem.OffsetRegNum != 0) + return false; + // Immediate offset a multiple of 4 in range [-1020, 1020]. + if (!Mem.OffsetImm) return true; + int64_t Val = Mem.OffsetImm->getValue(); + return Val >= -1020 && Val <= 1020 && (Val & 3) == 0; + } bool isMemImm8Offset() const { if (Kind != Memory || Mem.OffsetRegNum != 0) return false; @@ -908,6 +928,14 @@ public: addExpr(Inst, getImm()); } + void addImm8s4Operands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + // FIXME: We really want to scale the value here, but the LDRD/STRD + // instruction don't encode operands that way yet. + const MCConstantExpr *CE = dyn_cast(getImm()); + Inst.addOperand(MCOperand::CreateImm(CE->getValue())); + } + void addImm0_1020s4Operands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); // The immediate is scaled by four in the encoding and is stored @@ -1111,6 +1139,13 @@ public: Inst.addOperand(MCOperand::CreateImm(Val)); } + void addMemImm8s4OffsetOperands(MCInst &Inst, unsigned N) const { + assert(N == 2 && "Invalid number of operands!"); + int64_t Val = Mem.OffsetImm ? Mem.OffsetImm->getValue() : 0; + Inst.addOperand(MCOperand::CreateReg(Mem.BaseRegNum)); + Inst.addOperand(MCOperand::CreateImm(Val)); + } + void addMemImm8OffsetOperands(MCInst &Inst, unsigned N) const { assert(N == 2 && "Invalid number of operands!"); int64_t Val = Mem.OffsetImm ? Mem.OffsetImm->getValue() : 0; @@ -2400,6 +2435,42 @@ parseAM3Offset(SmallVectorImpl &Operands) { return MatchOperand_Success; } +/// cvtT2LdrdPre - 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:: +cvtT2LdrdPre(MCInst &Inst, unsigned Opcode, + const SmallVectorImpl &Operands) { + // Rt, Rt2 + ((ARMOperand*)Operands[2])->addRegOperands(Inst, 1); + ((ARMOperand*)Operands[3])->addRegOperands(Inst, 1); + // Create a writeback register dummy placeholder. + Inst.addOperand(MCOperand::CreateReg(0)); + // addr + ((ARMOperand*)Operands[4])->addMemImm8s4OffsetOperands(Inst, 2); + // pred + ((ARMOperand*)Operands[1])->addCondCodeOperands(Inst, 2); + return true; +} + +/// cvtT2StrdPre - 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:: +cvtT2StrdPre(MCInst &Inst, unsigned Opcode, + const SmallVectorImpl &Operands) { + // Create a writeback register dummy placeholder. + Inst.addOperand(MCOperand::CreateReg(0)); + // Rt, Rt2 + ((ARMOperand*)Operands[2])->addRegOperands(Inst, 1); + ((ARMOperand*)Operands[3])->addRegOperands(Inst, 1); + // addr + ((ARMOperand*)Operands[4])->addMemImm8s4OffsetOperands(Inst, 2); + // pred + ((ARMOperand*)Operands[1])->addCondCodeOperands(Inst, 2); + return true; +} + /// cvtLdWriteBackRegT2AddrModeImm8 - 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. diff --git a/lib/Target/ARM/Disassembler/ARMDisassembler.cpp b/lib/Target/ARM/Disassembler/ARMDisassembler.cpp index 66e64d3ffcb..fad7068fff9 100644 --- a/lib/Target/ARM/Disassembler/ARMDisassembler.cpp +++ b/lib/Target/ARM/Disassembler/ARMDisassembler.cpp @@ -293,6 +293,10 @@ static DecodeStatus DecodeThumbBLTargetOperand(llvm::MCInst &Inst, unsigned Val, uint64_t Address, const void *Decoder); static DecodeStatus DecodeIT(llvm::MCInst &Inst, unsigned Val, uint64_t Address, const void *Decoder); +static DecodeStatus DecodeT2LDRDPreInstruction(llvm::MCInst &Inst,unsigned Insn, + uint64_t Address, const void *Decoder); +static DecodeStatus DecodeT2STRDPreInstruction(llvm::MCInst &Inst,unsigned Insn, + uint64_t Address, const void *Decoder); #include "ARMGenDisassemblerTables.inc" #include "ARMGenInstrInfo.inc" @@ -3649,3 +3653,75 @@ static DecodeStatus DecodeIT(llvm::MCInst &Inst, unsigned Insn, Inst.addOperand(MCOperand::CreateImm(mask)); return S; } + +static DecodeStatus +DecodeT2LDRDPreInstruction(llvm::MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + unsigned Rt = fieldFromInstruction32(Insn, 12, 4); + unsigned Rt2 = fieldFromInstruction32(Insn, 8, 4); + unsigned Rn = fieldFromInstruction32(Insn, 16, 4); + unsigned addr = fieldFromInstruction32(Insn, 0, 8); + unsigned W = fieldFromInstruction32(Insn, 21, 1); + unsigned U = fieldFromInstruction32(Insn, 23, 1); + unsigned P = fieldFromInstruction32(Insn, 24, 1); + bool writeback = (W == 1) | (P == 0); + + addr |= (U << 8) | (Rn << 9); + + if (writeback && (Rn == Rt || Rn == Rt2)) + Check(S, MCDisassembler::SoftFail); + if (Rt == Rt2) + Check(S, MCDisassembler::SoftFail); + + // Rt + if (!Check(S, DecoderGPRRegisterClass(Inst, Rt, Address, Decoder))) + return MCDisassembler::Fail; + // Rt2 + if (!Check(S, DecoderGPRRegisterClass(Inst, Rt2, Address, Decoder))) + return MCDisassembler::Fail; + // Writeback operand + if (!Check(S, DecoderGPRRegisterClass(Inst, Rn, Address, Decoder))) + return MCDisassembler::Fail; + // addr + if (!Check(S, DecodeT2AddrModeImm8s4(Inst, addr, Address, Decoder))) + return MCDisassembler::Fail; + + return S; +} + +static DecodeStatus +DecodeT2STRDPreInstruction(llvm::MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + unsigned Rt = fieldFromInstruction32(Insn, 12, 4); + unsigned Rt2 = fieldFromInstruction32(Insn, 8, 4); + unsigned Rn = fieldFromInstruction32(Insn, 16, 4); + unsigned addr = fieldFromInstruction32(Insn, 0, 8); + unsigned W = fieldFromInstruction32(Insn, 21, 1); + unsigned U = fieldFromInstruction32(Insn, 23, 1); + unsigned P = fieldFromInstruction32(Insn, 24, 1); + bool writeback = (W == 1) | (P == 0); + + addr |= (U << 8) | (Rn << 9); + + if (writeback && (Rn == Rt || Rn == Rt2)) + Check(S, MCDisassembler::SoftFail); + + // Writeback operand + if (!Check(S, DecoderGPRRegisterClass(Inst, Rn, Address, Decoder))) + return MCDisassembler::Fail; + // Rt + if (!Check(S, DecoderGPRRegisterClass(Inst, Rt, Address, Decoder))) + return MCDisassembler::Fail; + // Rt2 + if (!Check(S, DecoderGPRRegisterClass(Inst, Rt2, Address, Decoder))) + return MCDisassembler::Fail; + // addr + if (!Check(S, DecodeT2AddrModeImm8s4(Inst, addr, Address, Decoder))) + return MCDisassembler::Fail; + + return S; +} diff --git a/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp b/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp index 69ec197af51..34b4e3d6b3f 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp @@ -144,6 +144,10 @@ public: /// operand. uint32_t getT2AddrModeImm8s4OpValue(const MCInst &MI, unsigned OpIdx, SmallVectorImpl &Fixups) const; + /// getT2Imm8s4OpValue - Return encoding info for '+/- imm8<<2' + /// operand. + uint32_t getT2Imm8s4OpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl &Fixups) const; /// getLdStSORegOpValue - Return encoding info for 'reg +/- reg shop imm' @@ -720,6 +724,37 @@ getAddrModeImm12OpValue(const MCInst &MI, unsigned OpIdx, return Binary; } +/// getT2Imm8s4OpValue - Return encoding info for +/// '+/- imm8<<2' operand. +uint32_t ARMMCCodeEmitter:: +getT2Imm8s4OpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl &Fixups) const { + // FIXME: The immediate operand should have already been encoded like this + // before ever getting here. The encoder method should just need to combine + // the MI operands for the register and the offset into a single + // representation for the complex operand in the .td file. This isn't just + // style, unfortunately. As-is, we can't represent the distinct encoding + // for #-0. + + // {8} = (U)nsigned (add == '1', sub == '0') + // {7-0} = imm8 + int32_t Imm8 = MI.getOperand(OpIdx).getImm(); + bool isAdd = Imm8 >= 0; + + // Immediate is always encoded as positive. The 'U' bit controls add vs sub. + if (Imm8 < 0) + Imm8 = -Imm8; + + // Scaled by 4. + Imm8 /= 4; + + uint32_t Binary = Imm8 & 0xff; + // Immediate is always encoded as positive. The 'U' bit controls add vs sub. + if (isAdd) + Binary |= (1 << 8); + return Binary; +} + /// getT2AddrModeImm8s4OpValue - Return encoding info for /// 'reg +/- imm8<<2' operand. uint32_t ARMMCCodeEmitter:: @@ -746,6 +781,12 @@ getT2AddrModeImm8s4OpValue(const MCInst &MI, unsigned OpIdx, } else isAdd = EncodeAddrModeOpValues(MI, OpIdx, Reg, Imm8, Fixups); + // FIXME: The immediate operand should have already been encoded like this + // before ever getting here. The encoder method should just need to combine + // the MI operands for the register and the offset into a single + // representation for the complex operand in the .td file. This isn't just + // style, unfortunately. As-is, we can't represent the distinct encoding + // for #-0. uint32_t Binary = (Imm8 >> 2) & 0xff; // Immediate is always encoded as positive. The 'U' bit controls add vs sub. if (isAdd) diff --git a/test/MC/ARM/basic-thumb2-instructions.s b/test/MC/ARM/basic-thumb2-instructions.s index ecf363f2460..99758258a6d 100644 --- a/test/MC/ARM/basic-thumb2-instructions.s +++ b/test/MC/ARM/basic-thumb2-instructions.s @@ -629,6 +629,24 @@ _func: @ CHECK: ldrbt r1, [r8, #255] @ encoding: [0x18,0xf8,0xff,0x1e] +@------------------------------------------------------------------------------ +@ LDRD(immediate) +@------------------------------------------------------------------------------ + ldrd r3, r5, [r6, #24] + ldrd r3, r5, [r6, #24]! + ldrd r3, r5, [r6], #4 + ldrd r3, r5, [r6], #-8 + ldrd r3, r5, [r6] + ldrd r8, r1, [r3, #0] + +@ CHECK: ldrd r3, r5, [r6, #24] @ encoding: [0xd6,0xe9,0x06,0x35] +@ CHECK: ldrd r3, r5, [r6, #24]! @ encoding: [0xf6,0xe9,0x06,0x35] +@ CHECK: ldrd r3, r5, [r6], #4 @ encoding: [0xf6,0xe8,0x01,0x35] +@ CHECK: ldrd r3, r5, [r6], #-8 @ encoding: [0x76,0xe8,0x02,0x35] +@ CHECK: ldrd r3, r5, [r6] @ encoding: [0xd6,0xe9,0x00,0x35] +@ CHECK: ldrd r8, r1, [r3] @ encoding: [0xd3,0xe9,0x00,0x81] + + @------------------------------------------------------------------------------ @ IT @------------------------------------------------------------------------------ diff --git a/test/MC/Disassembler/ARM/invalid-LDRD_PRE-thumb.txt b/test/MC/Disassembler/ARM/invalid-LDRD_PRE-thumb.txt index 5ad32808e01..7ea1b467957 100644 --- a/test/MC/Disassembler/ARM/invalid-LDRD_PRE-thumb.txt +++ b/test/MC/Disassembler/ARM/invalid-LDRD_PRE-thumb.txt @@ -1,5 +1,4 @@ # RUN: llvm-mc --disassemble %s -triple=thumbv7-apple-darwin9 |& grep {invalid instruction encoding} -# XFAIL: * # Opcode=1930 Name=t2LDRD_PRE Format=ARM_FORMAT_THUMBFRM(25) # 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0