diff options
author | Johnny Chen <johnny.chen@apple.com> | 2010-03-16 16:36:54 +0000 |
---|---|---|
committer | Johnny Chen <johnny.chen@apple.com> | 2010-03-16 16:36:54 +0000 |
commit | d30a98e43ae18e1fc70a7dc748edf669d809c685 (patch) | |
tree | 808136df95741812c7297abfb604a5696499dad0 /lib/Target/ARM/Disassembler/ThumbDisassemblerCore.cpp.inc | |
parent | ea7f22c31d0d12923eaab6840322431cc0222ae9 (diff) |
Initial ARM/Thumb disassembler check-in. It consists of a tablgen backend
(RISCDisassemblerEmitter) which emits the decoder functions for ARM and Thumb,
and the disassembler core which invokes the decoder function and builds up the
MCInst based on the decoded Opcode.
Added sub-formats to the NeonI/NeonXI instructions to further refine the NEONFrm
instructions to help disassembly.
We also changed the output of the addressing modes to omit the '+' from the
assembler syntax #+/-<imm> or +/-<Rm>. See, for example, A8.6.57/58/60.
And modified test cases to not expect '+' in +reg or #+num. For example,
; CHECK: ldr.w r9, [r7, #28]
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@98637 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Target/ARM/Disassembler/ThumbDisassemblerCore.cpp.inc')
-rw-r--r-- | lib/Target/ARM/Disassembler/ThumbDisassemblerCore.cpp.inc | 2158 |
1 files changed, 2158 insertions, 0 deletions
diff --git a/lib/Target/ARM/Disassembler/ThumbDisassemblerCore.cpp.inc b/lib/Target/ARM/Disassembler/ThumbDisassemblerCore.cpp.inc new file mode 100644 index 0000000000..928c403b7e --- /dev/null +++ b/lib/Target/ARM/Disassembler/ThumbDisassemblerCore.cpp.inc @@ -0,0 +1,2158 @@ +//===- ThumbDisassemblerCore.cpp - ARM disassembler helpers ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is part of the ARM Disassembler. +// It contains code for disassembling a Thumb instr. +// +//===----------------------------------------------------------------------===// + +/////////////////////////////// +// // +// Utility Functions // +// // +/////////////////////////////// + +// Utilities for 16-bit Thumb instructions. +/* +15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + [ tRt ] + [ tRm ] [ tRn ] [ tRd ] + D [ Rm ] [ Rd ] + + [ imm3] + [ imm5 ] + i [ imm5 ] + [ imm7 ] + [ imm8 ] + [ imm11 ] + + [ cond ] +*/ + +static bool InITBlock() { + return ARMBasicMCBuilder::InITBlock(); +} + +// Extract tRt: Inst{10-8}. +static inline unsigned getT1tRt(uint32_t insn) { + return slice(insn, 10, 8); +} + +// Extract tRm: Inst{8-6}. +static inline unsigned getT1tRm(uint32_t insn) { + return slice(insn, 8, 6); +} + +// Extract tRn: Inst{5-3}. +static inline unsigned getT1tRn(uint32_t insn) { + return slice(insn, 5, 3); +} + +// Extract tRd: Inst{2-0}. +static inline unsigned getT1tRd(uint32_t insn) { + return slice(insn, 2, 0); +} + +// Extract [D:Rd]: Inst{7:2-0}. +static inline unsigned getT1Rd(uint32_t insn) { + return slice(insn, 7, 7) << 3 | slice(insn, 2, 0); +} + +// Extract Rm: Inst{6-3}. +static inline unsigned getT1Rm(uint32_t insn) { + return slice(insn, 6, 3); +} + +// Extract imm3: Inst{8-6}. +static inline unsigned getT1Imm3(uint32_t insn) { + return slice(insn, 8, 6); +} + +// Extract imm5: Inst{10-6}. +static inline unsigned getT1Imm5(uint32_t insn) { + return slice(insn, 10, 6); +} + +// Extract i:imm5: Inst{9:7-3}. +static inline unsigned getT1Imm6(uint32_t insn) { + return slice(insn, 9, 9) << 5 | slice(insn, 7, 3); +} + +// Extract imm7: Inst{6-0}. +static inline unsigned getT1Imm7(uint32_t insn) { + return slice(insn, 6, 0); +} + +// Extract imm8: Inst{7-0}. +static inline unsigned getT1Imm8(uint32_t insn) { + return slice(insn, 7, 0); +} + +// Extract imm11: Inst{10-0}. +static inline unsigned getT1Imm11(uint32_t insn) { + return slice(insn, 10, 0); +} + +// Extract cond: Inst{11-8}. +static inline unsigned getT1Cond(uint32_t insn) { + return slice(insn, 11, 8); +} + +static inline bool IsGPR(unsigned RegClass) { + return RegClass == ARM::GPRRegClassID; +} + +// Utilities for 32-bit Thumb instructions. + +// Extract imm4: Inst{19-16}. +static inline unsigned getImm4(uint32_t insn) { + return slice(insn, 19, 16); +} + +// Extract imm3: Inst{14-12}. +static inline unsigned getImm3(uint32_t insn) { + return slice(insn, 14, 12); +} + +// Extract imm8: Inst{7-0}. +static inline unsigned getImm8(uint32_t insn) { + return slice(insn, 7, 0); +} + +// A8.6.61 LDRB (immediate, Thumb) and friends +// +/-: Inst{9} +// imm8: Inst{7-0} +static inline int decodeImm8(uint32_t insn) { + int Offset = getImm8(insn); + return slice(insn, 9, 9) ? Offset : -Offset; +} + +// Extract imm12: Inst{11-0}. +static inline unsigned getImm12(uint32_t insn) { + return slice(insn, 11, 0); +} + +// A8.6.63 LDRB (literal) and friends +// +/-: Inst{23} +// imm12: Inst{11-0} +static inline int decodeImm12(uint32_t insn) { + int Offset = getImm12(insn); + return slice(insn, 23, 23) ? Offset : -Offset; +} + +// Extract imm2: Inst{7-6}. +static inline unsigned getImm2(uint32_t insn) { + return slice(insn, 7, 6); +} + +// For BFI, BFC, t2SBFX, and t2UBFX. +// Extract lsb: Inst{14-12:7-6}. +static inline unsigned getLsb(uint32_t insn) { + return getImm3(insn) << 2 | getImm2(insn); +} + +// For BFI and BFC. +// Extract msb: Inst{4-0}. +static inline unsigned getMsb(uint32_t insn) { + return slice(insn, 4, 0); +} + +// For t2SBFX and t2UBFX. +// Extract widthminus1: Inst{4-0}. +static inline unsigned getWidthMinus1(uint32_t insn) { + return slice(insn, 4, 0); +} + +// For t2ADDri12 and t2SUBri12. +// imm12 = i:imm3:imm8; +static inline unsigned getIImm3Imm8(uint32_t insn) { + return slice(insn, 26, 26) << 11 | getImm3(insn) << 8 | getImm8(insn); +} + +// For t2MOVi16 and t2MOVTi16. +// imm16 = imm4:i:imm3:imm8; +static inline unsigned getImm16(uint32_t insn) { + return getImm4(insn) << 12 | slice(insn, 26, 26) << 11 | + getImm3(insn) << 8 | getImm8(insn); +} + +// Inst{5-4} encodes the shift type. +static inline unsigned getShiftTypeBits(uint32_t insn) { + return slice(insn, 5, 4); +} + +// Inst{14-12}:Inst{7-6} encodes the imm5 shift amount. +static inline unsigned getShiftAmtBits(uint32_t insn) { + return getImm3(insn) << 2 | getImm2(insn); +} + +// A8.6.17 BFC +// Encoding T1 ARMv6T2, ARMv7 +// LLVM-specific encoding for #<lsb> and #<width> +static inline uint32_t getBitfieldInvMask(uint32_t insn) { + uint32_t lsb = getImm3(insn) << 2 | getImm2(insn); + uint32_t msb = getMsb(insn); + uint32_t Val = 0; + assert(lsb <= msb && "Encoding error: lsb > msb"); + for (uint32_t i = lsb; i <= msb; ++i) + Val |= (1 << i); + return ~Val; +} + +// A8.4 Shifts applied to a register +// A8.4.1 Constant shifts +// A8.4.3 Pseudocode details of instruction-specified shifts and rotates +// +// decodeImmShift() returns the shift amount and the the shift opcode. +// Note that, as of Jan-06-2010, LLVM does not support rrx shifted operands yet. +static inline unsigned decodeImmShift(unsigned bits2, unsigned imm5, + ARM_AM::ShiftOpc &ShOp) { + + assert(imm5 < 32); + switch (bits2) { + default: assert(0 && "No such value"); + case 0: + ShOp = ARM_AM::lsl; + return imm5; + case 1: + ShOp = ARM_AM::lsr; + return (imm5 == 0 ? 32 : imm5); + case 2: + ShOp = ARM_AM::asr; + return (imm5 == 0 ? 32 : imm5); + case 3: + ShOp = (imm5 == 0 ? ARM_AM::rrx : ARM_AM::ror); + return (imm5 == 0 ? 1 : imm5); + } +} + +// A6.3.2 Modified immediate constants in Thumb instructions +// +// ThumbExpandImm() returns the modified immediate constant given an imm12 for +// Thumb data-processing instructions with modified immediate. +// See also A6.3.1 Data-processing (modified immediate). +static inline unsigned ThumbExpandImm(unsigned imm12) { + assert(imm12 <= 0xFFF); + + // If the leading two bits is 0b00, the modified immediate constant is + // obtained by splatting the low 8 bits into the first byte, every other byte, + // or every byte of a 32-bit value. + // + // Otherwise, a rotate right of '1':imm12<6:0> by the amount imm12<11:7> is + // performed. + + if (slice(imm12, 11, 10) == 0) { + unsigned short control = slice(imm12, 9, 8); + unsigned imm8 = slice(imm12, 7, 0); + switch (control) { + default: + assert(0 && "No such value"); + return 0; + case 0: + return imm8; + case 1: + return imm8 << 16 | imm8; + case 2: + return imm8 << 24 | imm8 << 8; + case 3: + return imm8 << 24 | imm8 << 16 | imm8 << 8 | imm8; + } + } else { + // A rotate is required. + unsigned Val = 1 << 7 | slice(imm12, 6, 0); + unsigned Amt = slice(imm12, 11, 7); + return ARM_AM::rotr32(Val, Amt); + } +} + +static inline int decodeImm32_B_EncodingT3(uint32_t insn) { + bool S = slice(insn, 26, 26); + bool J1 = slice(insn, 13, 13); + bool J2 = slice(insn, 11, 11); + unsigned Imm21 = slice(insn, 21, 16) << 12 | slice(insn, 10, 0) << 1; + if (S) Imm21 |= 1 << 20; + if (J2) Imm21 |= 1 << 19; + if (J1) Imm21 |= 1 << 18; + + return signextend<signed int, 21>(Imm21); +} + +static inline int decodeImm32_B_EncodingT4(uint32_t insn) { + unsigned S = slice(insn, 26, 26); + bool I1 = slice(insn, 13, 13) == S; + bool I2 = slice(insn, 11, 11) == S; + unsigned Imm25 = slice(insn, 25, 16) << 12 | slice(insn, 10, 0) << 1; + if (S) Imm25 |= 1 << 24; + if (I1) Imm25 |= 1 << 23; + if (I2) Imm25 |= 1 << 22; + + return signextend<signed int, 25>(Imm25); +} + +static inline int decodeImm32_BL(uint32_t insn) { + unsigned S = slice(insn, 26, 26); + bool I1 = slice(insn, 13, 13) == S; + bool I2 = slice(insn, 11, 11) == S; + unsigned Imm25 = slice(insn, 25, 16) << 12 | slice(insn, 10, 0) << 1; + if (S) Imm25 |= 1 << 24; + if (I1) Imm25 |= 1 << 23; + if (I2) Imm25 |= 1 << 22; + + return signextend<signed int, 25>(Imm25); +} + +static inline int decodeImm32_BLX(uint32_t insn) { + unsigned S = slice(insn, 26, 26); + bool I1 = slice(insn, 13, 13) == S; + bool I2 = slice(insn, 11, 11) == S; + unsigned Imm25 = slice(insn, 25, 16) << 12 | slice(insn, 10, 1) << 2; + if (S) Imm25 |= 1 << 24; + if (I1) Imm25 |= 1 << 23; + if (I2) Imm25 |= 1 << 22; + + return signextend<signed int, 25>(Imm25); +} + +// See, for example, A8.6.221 SXTAB16. +static inline unsigned decodeRotate(uint32_t insn) { + unsigned rotate = slice(insn, 5, 4); + return rotate << 3; +} + +/////////////////////////////////////////////// +// // +// Thumb1 instruction disassembly functions. // +// // +/////////////////////////////////////////////// + +// See "Utilities for 16-bit Thumb instructions" for register naming convention. + +// A6.2.1 Shift (immediate), add, subtract, move, and compare +// +// shift immediate: tRd CPSR tRn imm5 +// add/sub register: tRd CPSR tRn tRm +// add/sub 3-bit immediate: tRd CPSR tRn imm3 +// add/sub 8-bit immediate: tRt CPSR tRt(TIED_TO) imm8 +// mov/cmp immediate: tRt [CPSR] imm8 (CPSR present for mov) +// +// Special case: +// tMOVSr: tRd tRn +static bool DisassembleThumb1General(MCInst &MI, unsigned Opcode, uint32_t insn, + unsigned short NumOps, unsigned &NumOpsAdded) { + + const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo; + unsigned &OpIdx = NumOpsAdded; + + OpIdx = 0; + + assert(NumOps >= 2 && OpInfo[0].RegClass == ARM::tGPRRegClassID); + + bool Imm3 = (Opcode == ARM::tADDi3 || Opcode == ARM::tSUBi3); + + // Use Rt implies use imm8. + bool UseRt = (Opcode == ARM::tADDi8 || Opcode == ARM::tSUBi8 || + Opcode == ARM::tMOVi8 || Opcode == ARM::tCMPi8); + + // Add the destination operand. + MI.addOperand(MCOperand::CreateReg( + getRegisterEnum(ARM::tGPRRegClassID, + UseRt ? getT1tRt(insn) : getT1tRd(insn)))); + ++OpIdx; + + // Check whether the next operand to be added is a CCR Register. + if (OpInfo[OpIdx].RegClass == ARM::CCRRegClassID) { + assert(OpInfo[OpIdx].isOptionalDef()); + MI.addOperand(MCOperand::CreateReg(InITBlock() ? 0 : ARM::CPSR)); + ++OpIdx; + } + + // Check whether the next operand to be added is a Thumb1 Register. + assert(OpIdx < NumOps); + if (OpInfo[OpIdx].RegClass == ARM::tGPRRegClassID) { + // For UseRt, the reg operand is tied to the first reg operand. + MI.addOperand(MCOperand::CreateReg( + getRegisterEnum(ARM::tGPRRegClassID, + UseRt ? getT1tRt(insn) : getT1tRn(insn)))); + ++OpIdx; + } + + // Special case for tMOVSr. + if (OpIdx == NumOps) + return true; + + // The next available operand is either a reg operand or an imm operand. + if (OpInfo[OpIdx].RegClass == ARM::tGPRRegClassID) { + // Three register operand instructions. + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::tGPRRegClassID, + getT1tRm(insn)))); + } else { + assert(OpInfo[OpIdx].RegClass == 0 && + !OpInfo[OpIdx].isPredicate() && !OpInfo[OpIdx].isOptionalDef()); + MI.addOperand(MCOperand::CreateImm(UseRt ? getT1Imm8(insn) + : (Imm3 ? getT1Imm3(insn) + : getT1Imm5(insn)))); + } + ++OpIdx; + + return true; +} + +// A6.2.2 Data-processing +// +// tCMPr, tTST, tCMN: tRd tRn +// tMVN, tRSB: tRd CPSR tRn +// Others: tRd CPSR tRd(TIED_TO) tRn +static bool DisassembleThumb1DP(MCInst &MI, unsigned Opcode, uint32_t insn, + unsigned short NumOps, unsigned &NumOpsAdded) { + + const TargetInstrDesc &TID = ARMInsts[Opcode]; + const TargetOperandInfo *OpInfo = TID.OpInfo; + unsigned &OpIdx = NumOpsAdded; + + OpIdx = 0; + + assert(NumOps >= 2 && OpInfo[0].RegClass == ARM::tGPRRegClassID && + (OpInfo[1].RegClass == ARM::CCRRegClassID + || OpInfo[1].RegClass == ARM::tGPRRegClassID)); + + // Add the destination operand. + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::tGPRRegClassID, + getT1tRd(insn)))); + ++OpIdx; + + // Check whether the next operand to be added is a CCR Register. + if (OpInfo[OpIdx].RegClass == ARM::CCRRegClassID) { + assert(OpInfo[OpIdx].isOptionalDef()); + MI.addOperand(MCOperand::CreateReg(InITBlock() ? 0 : ARM::CPSR)); + ++OpIdx; + } + + // We have either { tRd(TIED_TO), tRn } or { tRn } remaining. + // Process the TIED_TO operand first. + + assert(OpIdx < NumOps && OpInfo[OpIdx].RegClass == ARM::tGPRRegClassID); + int Idx; + if ((Idx = TID.getOperandConstraint(OpIdx, TOI::TIED_TO)) != -1) { + // The reg operand is tied to the first reg operand. + MI.addOperand(MI.getOperand(Idx)); + ++OpIdx; + } + + // Process possible next reg operand. + if (OpIdx < NumOps && OpInfo[OpIdx].RegClass == ARM::tGPRRegClassID) { + // Add tRn operand. + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::tGPRRegClassID, + getT1tRn(insn)))); + ++OpIdx; + } + + return true; +} + +// A6.2.3 Special data instructions and branch and exchange +// +// tADDhirr: Rd Rd(TIED_TO) Rm +// tCMPhir: Rd Rm +// tMOVr, tMOVgpr2gpr, tMOVgpr2tgpr, tMOVtgpr2gpr: Rd|tRd Rm|tRn +// tBX_RET: 0 operand +// tBX_RET_vararg: Rm +// tBLXr_r9: Rm +static bool DisassembleThumb1Special(MCInst &MI, unsigned Opcode, uint32_t insn, + unsigned short NumOps, unsigned &NumOpsAdded) { + + // tBX_RET has 0 operand. + if (NumOps == 0) + return true; + + // BX/BLX has 1 reg operand: Rm. + if (NumOps == 1) { + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::GPRRegClassID, + getT1Rm(insn)))); + NumOpsAdded = 1; + return true; + } + + const TargetInstrDesc &TID = ARMInsts[Opcode]; + const TargetOperandInfo *OpInfo = TID.OpInfo; + unsigned &OpIdx = NumOpsAdded; + + OpIdx = 0; + + // Add the destination operand. + unsigned RegClass = OpInfo[OpIdx].RegClass; + MI.addOperand(MCOperand::CreateReg( + getRegisterEnum(RegClass, + IsGPR(RegClass) ? getT1Rd(insn) + : getT1tRd(insn)))); + ++OpIdx; + + // We have either { Rd(TIED_TO), Rm } or { Rm|tRn } remaining. + // Process the TIED_TO operand first. + + assert(OpIdx < NumOps); + int Idx; + if ((Idx = TID.getOperandConstraint(OpIdx, TOI::TIED_TO)) != -1) { + // The reg operand is tied to the first reg operand. + MI.addOperand(MI.getOperand(Idx)); + ++OpIdx; + } + + // The next reg operand is either Rm or tRn. + assert(OpIdx < NumOps); + RegClass = OpInfo[OpIdx].RegClass; + MI.addOperand(MCOperand::CreateReg( + getRegisterEnum(RegClass, + IsGPR(RegClass) ? getT1Rm(insn) + : getT1tRn(insn)))); + ++OpIdx; + + return true; +} + +// A8.6.59 LDR (literal) +// +// tLDRpci: tRt imm8*4 +static bool DisassembleThumb1LdPC(MCInst &MI, unsigned Opcode, uint32_t insn, + unsigned short NumOps, unsigned &NumOpsAdded) { + + const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo; + + assert(NumOps >= 2 && OpInfo[0].RegClass == ARM::tGPRRegClassID && + (OpInfo[1].RegClass == 0 && + !OpInfo[1].isPredicate() && + !OpInfo[1].isOptionalDef())); + + // Add the destination operand. + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::tGPRRegClassID, + getT1tRt(insn)))); + + // And the (imm8 << 2) operand. + MI.addOperand(MCOperand::CreateImm(getT1Imm8(insn) << 2)); + + NumOpsAdded = 2; + + return true; +} + +// Thumb specific addressing modes (see ARMInstrThumb.td): +// +// t_addrmode_rr := reg + reg +// +// t_addrmode_s4 := reg + reg +// reg + imm5 * 4 +// +// t_addrmode_s2 := reg + reg +// reg + imm5 * 2 +// +// t_addrmode_s1 := reg + reg +// reg + imm5 +// +// t_addrmode_sp := sp + imm8 * 4 +// + +// A6.2.4 Load/store single data item +// +// Load/Store Register (reg|imm): tRd tRn imm5 tRm +// Load Register Signed Byte|Halfword: tRd tRn tRm +static bool DisassembleThumb1LdSt(unsigned opA, MCInst &MI, unsigned Opcode, + uint32_t insn, unsigned short NumOps, unsigned &NumOpsAdded) { + + const TargetInstrDesc &TID = ARMInsts[Opcode]; + const TargetOperandInfo *OpInfo = TID.OpInfo; + unsigned &OpIdx = NumOpsAdded; + + // Table A6-5 16-bit Thumb Load/store instructions + // opA = 0b0101 for STR/LDR (register) and friends. + // Otherwise, we have STR/LDR (immediate) and friends. + bool Imm5 = (opA != 5); + + assert(NumOps >= 2 + && OpInfo[0].RegClass == ARM::tGPRRegClassID + && OpInfo[1].RegClass == ARM::tGPRRegClassID); + + // Add the destination reg and the base reg. + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::tGPRRegClassID, + getT1tRd(insn)))); + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::tGPRRegClassID, + getT1tRn(insn)))); + OpIdx = 2; + + // We have either { imm5, tRm } or { tRm } remaining. + // Process the imm5 first. Note that STR/LDR (register) should skip the imm5 + // offset operand for t_addrmode_s[1|2|4]. + + assert(OpIdx < NumOps); + + if (OpInfo[OpIdx].RegClass == 0 && !OpInfo[OpIdx].isPredicate() && + !OpInfo[OpIdx].isOptionalDef()) { + + MI.addOperand(MCOperand::CreateImm(Imm5 ? getT1Imm5(insn) : 0)); + ++OpIdx; + } + + // The next reg operand is tRm, the offset. + assert(OpIdx < NumOps && OpInfo[OpIdx].RegClass == ARM::tGPRRegClassID); + MI.addOperand(MCOperand::CreateReg(Imm5 ? 0 + : getRegisterEnum(ARM::tGPRRegClassID, + getT1tRm(insn)))); + ++OpIdx; + + return true; +} + +// A6.2.4 Load/store single data item +// +// Load/Store Register SP relative: tRt ARM::SP imm8 +static bool DisassembleThumb1LdStSP(MCInst &MI, unsigned Opcode, uint32_t insn, + unsigned short NumOps, unsigned &NumOpsAdded) { + + assert(Opcode == ARM::tLDRspi || Opcode == ARM::tSTRspi); + + const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo; + + assert(NumOps >= 3 && + OpInfo[0].RegClass == ARM::tGPRRegClassID && + OpInfo[1].RegClass == ARM::GPRRegClassID && + (OpInfo[2].RegClass == 0 && + !OpInfo[2].isPredicate() && + !OpInfo[2].isOptionalDef())); + + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::tGPRRegClassID, + getT1tRt(insn)))); + MI.addOperand(MCOperand::CreateReg(ARM::SP)); + MI.addOperand(MCOperand::CreateImm(getT1Imm8(insn))); + NumOpsAdded = 3; + return true; +} + +// Table A6-1 16-bit Thumb instruction encoding +// A8.6.10 ADR +// +// tADDrPCi: tRt imm8 +static bool DisassembleThumb1AddPCi(MCInst &MI, unsigned Opcode, uint32_t insn, + unsigned short NumOps, unsigned &NumOpsAdded) { + + assert(Opcode == ARM::tADDrPCi); + + const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo; + + assert(NumOps >= 2 && OpInfo[0].RegClass == ARM::tGPRRegClassID && + (OpInfo[1].RegClass == 0 && + !OpInfo[1].isPredicate() && + !OpInfo[1].isOptionalDef())); + + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::tGPRRegClassID, + getT1tRt(insn)))); + MI.addOperand(MCOperand::CreateImm(getT1Imm8(insn))); + NumOpsAdded = 2; + return true; +} + +// Table A6-1 16-bit Thumb instruction encoding +// A8.6.8 ADD (SP plus immediate) +// +// tADDrSPi: tRt ARM::SP imm8 +static bool DisassembleThumb1AddSPi(MCInst &MI, unsigned Opcode, uint32_t insn, + unsigned short NumOps, unsigned &NumOpsAdded) { + + assert(Opcode == ARM::tADDrSPi); + + const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo; + + assert(NumOps >= 3 && + OpInfo[0].RegClass == ARM::tGPRRegClassID && + OpInfo[1].RegClass == ARM::GPRRegClassID && + (OpInfo[2].RegClass == 0 && + !OpInfo[2].isPredicate() && + !OpInfo[2].isOptionalDef())); + + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::tGPRRegClassID, + getT1tRt(insn)))); + MI.addOperand(MCOperand::CreateReg(ARM::SP)); + MI.addOperand(MCOperand::CreateImm(getT1Imm8(insn))); + NumOpsAdded = 3; + return true; +} + +// tPUSH, tPOP: Pred-Imm Pred-CCR register_list +// +// where register_list = low registers + [lr] for PUSH or +// low registers + [pc] for POP +// +// "low registers" is specified by Inst{7-0} +// lr|pc is specified by Inst{8} +static bool DisassembleThumb1PushPop(MCInst &MI, unsigned Opcode, uint32_t insn, + unsigned short NumOps, unsigned &NumOpsAdded) { + + assert(Opcode == ARM::tPUSH || Opcode == ARM::tPOP); + + unsigned &OpIdx = NumOpsAdded; + + // Handling the two predicate operands before the reglist. + MI.addOperand(MCOperand::CreateImm(ARMCC::AL)); + MI.addOperand(MCOperand::CreateReg(ARM::CPSR)); + OpIdx = 2; + + // Fill the variadic part of reglist. + unsigned RegListBits = slice(insn, 8, 8) << (Opcode == ARM::tPUSH ? 14 : 15) + | slice(insn, 7, 0); + for (unsigned i = 0; i < 16; ++i) { + if ((RegListBits >> i) & 1) { + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::GPRRegClassID, + i))); + ++OpIdx; + } + } + + return true; +} + +// A6.2.5 Miscellaneous 16-bit instructions +// Delegate to DisassembleThumb1PushPop() for tPUSH & tPOP. +// +// tADDspi, tSUBspi: ARM::SP ARM::SP(TIED_TO) imm7 +// t2IT: firstcond=Inst{7-4} mask=Inst{3-0} +// tCBNZ, tCBZ: tRd imm6*2 +// tBKPT: imm8 +// tNOP, tSEV, tYIELD, tWFE, tWFI: +// no operand (except predicate pair) +// tSETENDBE, tSETENDLE, : +// no operand +// Others: tRd tRn +static bool DisassembleThumb1Misc(MCInst &MI, unsigned Opcode, uint32_t insn, + unsigned short NumOps, unsigned &NumOpsAdded) { + + if (NumOps == 0) + return true; + + if (Opcode == ARM::tPUSH || Opcode == ARM::tPOP) + return DisassembleThumb1PushPop(MI, Opcode, insn, NumOps, NumOpsAdded); + + const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo; + + // Predicate operands are handled elsewhere. + if (NumOps == 2 && + OpInfo[0].isPredicate() && OpInfo[1].isPredicate() && + OpInfo[0].RegClass == 0 && OpInfo[1].RegClass == ARM::CCRRegClassID) { + return true; + } + + if (Opcode == ARM::tADDspi || Opcode == ARM::tSUBspi) { + // Special case handling for tADDspi and tSUBspi. + // A8.6.8 ADD (SP plus immediate) & A8.6.215 SUB (SP minus immediate) + MI.addOperand(MCOperand::CreateReg(ARM::SP)); + MI.addOperand(MCOperand::CreateReg(ARM::SP)); + MI.addOperand(MCOperand::CreateImm(getT1Imm7(insn))); + NumOpsAdded = 3; + return true; + } + + if (Opcode == ARM::t2IT) { + // Special case handling for If-Then. + // A8.6.50 IT + // Tag the (firstcond[0] bit << 4) along with mask. + + // firstcond + MI.addOperand(MCOperand::CreateImm(slice(insn, 7, 4))); + + // firstcond[0] and mask + MI.addOperand(MCOperand::CreateImm(slice(insn, 4, 0))); + NumOpsAdded = 2; + return true; + } + + if (Opcode == ARM::tBKPT) { + MI.addOperand(MCOperand::CreateImm(getT1Imm8(insn))); // breakpoint value + NumOpsAdded = 1; + return true; + } + + // CPS has a singleton $opt operand that contains the following information: + // opt{4-0} = don't care + // opt{5} = 0 (false) + // opt{8-6} = AIF from Inst{2-0} + // opt{10-9} = 1:imod from Inst{4} with 0b10 as enable and 0b11 as disable + if (Opcode == ARM::tCPS) { + unsigned Option = slice(insn, 2, 0) << 6 | slice(insn, 4, 4) << 9 | 1 << 10; + MI.addOperand(MCOperand::CreateImm(Option)); + NumOpsAdded = 1; + return true; + } + + assert(NumOps >= 2 && OpInfo[0].RegClass == ARM::tGPRRegClassID && + (OpInfo[1].RegClass==0 || OpInfo[1].RegClass==ARM::tGPRRegClassID)); + + // Add the destination operand. + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::tGPRRegClassID, + getT1tRd(insn)))); + + if (OpInfo[1].RegClass == ARM::tGPRRegClassID) { + // Two register instructions. + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::tGPRRegClassID, + getT1tRn(insn)))); + } else { + // CBNZ, CBZ + assert(Opcode == ARM::tCBNZ || Opcode == ARM::tCBZ); + MI.addOperand(MCOperand::CreateImm(getT1Imm6(insn) * 2)); + } + + NumOpsAdded = 2; + + return true; +} + +// A8.6.53 LDM / LDMIA +// A8.6.189 STM / STMIA +// +// tRt AM4ModeImm Pred-Imm Pred-CCR register_list +static bool DisassembleThumb1LdStMul(bool Ld, MCInst &MI, unsigned Opcode, + uint32_t insn, unsigned short NumOps, unsigned &NumOpsAdded) { + + assert(Opcode == ARM::tLDM || Opcode == ARM::tSTM_UPD); + + unsigned &OpIdx = NumOpsAdded; + + unsigned tRt = getT1tRt(insn); + unsigned RegListBits = slice(insn, 7, 0); + + OpIdx = 0; + + // For STM, WB is always true. + if (Opcode == ARM::tSTM_UPD) { + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::GPRRegClassID, + tRt))); + ++OpIdx; + } + + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::GPRRegClassID, + tRt))); + ++OpIdx; + + // A8.6.53 LDM / LDMIA / LDMFD - Encoding T1 + // WB is true if tRt is not specified as a member of the register list. + // For STM, WB is always true. + bool WB = Ld ? ((RegListBits >> tRt) & 1) == 0 : true; + MI.addOperand(MCOperand::CreateImm(ARM_AM::getAM4ModeImm(ARM_AM::ia, WB))); + ++OpIdx; + + // Handling the two predicate operands before the reglist. + MI.addOperand(MCOperand::CreateImm(ARMCC::AL)); + MI.addOperand(MCOperand::CreateReg(ARM::CPSR)); + OpIdx += 2; + + // Fill the variadic part of reglist. + for (unsigned i = 0; i < 8; ++i) { + if ((RegListBits >> i) & 1) { + MI.addOperand(MCOperand::CreateReg(getRegisterEnum(ARM::tGPRRegClassID, + i))); + ++OpIdx; + } + } + + return true; +} + +static bool DisassembleThumb1LdMul(MCInst &MI, unsigned Opcode, uint32_t insn, + unsigned short NumOps, unsigned &NumOpsAdded) { + return DisassembleThumb1LdStMul(true, MI, Opcode, insn, NumOps, NumOpsAdded); +} + +static bool DisassembleThumb1StMul(MCInst &MI, unsigned Opcode, uint32_t insn, + unsigned short NumOps, unsigned &NumOpsAdded) { + return DisassembleThumb1LdStMul(false, MI, Opcode, insn, NumOps, NumOpsAdded); +} + +// A8.6.16 B Encoding T1 +// cond = Inst{11-8} & imm8 = Inst{7-0} +// imm32 = SignExtend(imm8:'0', 32) +// +// tBcc: offset Pred-Imm Pred-CCR +// tSVC: imm8 Pred-Imm Pred-CCR +// tTRAP: 0 operand (early return) +static bool DisassembleThumb1CondBr(MCInst &MI, unsigned Opcode, uint32_t insn, + unsigned short NumOps, unsigned &NumOpsAdded) { + + if (Opcode == ARM::tTRAP) + return true; + + const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo; + assert(NumOps == 3 && OpInfo[0].RegClass == 0 && + OpInfo[1].isPredicate() && OpInfo[2].RegClass == ARM::CCRRegClassID); + + unsigned Imm8 = getT1Imm8(insn); + MI.addOperand(MCOperand::CreateImm( + Opcode == ARM::tBcc ? signextend<signed int, 9>(Imm8 << 1) + 4 + : (int)Imm8)); + + // Predicate operands by ARMBasicMCBuilder::TryPredicateAndSBitModifier(). + NumOpsAdded = 1; + + return true; +} + +// A8.6.16 B Encoding T2 +// imm11 = Inst{10-0} +// imm32 = SignExtend(imm11:'0', 32) +// +// tB: offset +static bool DisassembleThumb1Br(MCInst &MI, unsigned Opcode, uint32_t insn, + unsigned short NumOps, unsigned &NumOpsAdded) { + + const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo; + assert(NumOps == 1 && OpInfo[0].RegClass == 0); + + unsigned Imm11 = getT1Imm11(insn); + + // When executing a Thumb instruction, PC reads as the address of the current + // instruction plus 4. The assembler subtracts 4 from the difference between + // the branch instruction and the target address, disassembler has to add 4 to + // to compensate. + MI.addOperand(MCOperand::CreateImm( + signextend<signed int, 12>(Imm11 << 1) + 4)); + + NumOpsAdded = 1; + + return true; + +} + +// See A6.2 16-bit Thumb instruction encoding for instruction classes +// corresponding to op. +// +// Table A6-1 16-bit Thumb instruction encoding (abridged) +// op Instruction or instruction class +// ------ -------------------------------------------------------------------- +// 00xxxx Shift (immediate), add, subtract, move, and compare on page A6-7 +// 010000 Data-processing on page A6-8 +// 010001 Special data instructions and branch and exchange on page A6-9 +// 01001x Load from Literal Pool, see LDR (literal) on page A8-122 +// 0101xx Load/store single data item on page A6-10 +// 011xxx +// 100xxx +// 10100x Generate PC-relative address, see ADR on page A8-32 +// 10101x Generate SP-relative address, see ADD (SP plus immediate) on page A8-28 +// 1011xx Miscellaneous 16-bit instructions on page A6-11 +// 11000x Store multiple registers, see STM / STMIA / STMEA on page A8-374 +// 11001x Load multiple registers, see LDM / LDMIA / LDMFD on page A8-110 a +// 1101xx Conditional branch, and Supervisor Call on page A6-13 +// 11100x Unconditional Branch, see B on page A8-44 +// +static bool DisassembleThumb1(uint16_t op, + MCInst &MI, unsigned Opcode, uint32_t insn, + unsigned short NumOps, unsigned &NumOpsAdded) { + + unsigned op1 = slice(op, 5, 4); + unsigned op2 = slice(op, 3, 2); + unsigned op3 = slice(op, 1, 0); + unsigned opA = slice(op, 5, 2); + switch (op1) { + case 0: + // A6.2.1 Shift (immediate), add, subtract, move, and compare + return DisassembleThumb1General(MI, Opcode, insn, NumOps, NumOpsAdded); + case 1: + switch (op2) { + case 0: + switch (op3) { + case 0: + // A6.2.2 Data-processing + return DisassembleThumb1DP(MI, Opcode, insn, NumOps, NumOpsAdded); + case 1: + // A6.2.3 Special data instructions and branch and exchange + return DisassembleThumb1Special(MI, Opcode, insn, NumOps, NumOpsAdded); + default: + // A8.6.59 LDR (literal) + return DisassembleThumb1LdPC(MI, Opcode, insn, NumOps, NumOpsAdded); + } + break; + default: + // A6.2.4 Load/store single data item + return DisassembleThumb1LdSt(opA, MI, Opcode, insn, NumOps, NumOpsAdded); + break; + } + break; + case 2: + switch (op2) { + case 0: + // A6.2.4 Load/store single data item + return DisassembleThumb1LdSt(opA, MI, Opcode, insn, NumOps, NumOpsAdded); + case 1: + // A6.2.4 Load/store single data item + return DisassembleThumb1LdStSP(MI, Opcode, insn, NumOps, NumOpsAdded); + case 2: + if (op3 <= 1) { + // A8.6.10 ADR + return DisassembleThumb1AddPCi(MI, Opcode, insn, NumOps, NumOpsAdded); + } else { + // A8.6.8 ADD (SP plus immediate) + return DisassembleThumb1AddSPi(MI, Opcode, insn, NumOps, NumOpsAdded); + } + default: |