mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-16 11:30:51 +00:00
a606d955de
MachineRegisterInfo doesn't have to confusingly allocate an extra entry. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@106296 91177308-0d34-0410-b5e6-96231b3b80d8
2249 lines
73 KiB
C++
2249 lines
73 KiB
C++
//===- ThumbDisassemblerCore.h - Thumb 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. It is to be included by
|
|
// ARMDisassemblerCore.cpp because it contains the static DisassembleThumbFrm()
|
|
// function which acts as the dispatcher to disassemble a Thumb instruction.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
///////////////////////////////
|
|
// //
|
|
// 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 ]
|
|
*/
|
|
|
|
// 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 bool getBitfieldInvMask(uint32_t insn, uint32_t &mask) {
|
|
uint32_t lsb = getImm3(insn) << 2 | getImm2(insn);
|
|
uint32_t msb = getMsb(insn);
|
|
uint32_t Val = 0;
|
|
if (msb < lsb) {
|
|
DEBUG(errs() << "Encoding error: msb < lsb\n");
|
|
return false;
|
|
}
|
|
for (uint32_t i = lsb; i <= msb; ++i)
|
|
Val |= (1 << i);
|
|
mask = ~Val;
|
|
return true;
|
|
}
|
|
|
|
// 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 && "Invalid imm5 argument");
|
|
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 && "Invalid imm12 argument");
|
|
|
|
// 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 SignExtend32<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 SignExtend32<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 SignExtend32<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 SignExtend32<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, BO B) {
|
|
|
|
const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo;
|
|
unsigned &OpIdx = NumOpsAdded;
|
|
|
|
OpIdx = 0;
|
|
|
|
assert(NumOps >= 2 && OpInfo[0].RegClass == ARM::tGPRRegClassID
|
|
&& "Invalid arguments");
|
|
|
|
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(B, 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() && "Optional def operand expected");
|
|
MI.addOperand(MCOperand::CreateReg(B->InITBlock() ? 0 : ARM::CPSR));
|
|
++OpIdx;
|
|
}
|
|
|
|
// Check whether the next operand to be added is a Thumb1 Register.
|
|
assert(OpIdx < NumOps && "More operands expected");
|
|
if (OpInfo[OpIdx].RegClass == ARM::tGPRRegClassID) {
|
|
// For UseRt, the reg operand is tied to the first reg operand.
|
|
MI.addOperand(MCOperand::CreateReg(
|
|
getRegisterEnum(B, 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(B, ARM::tGPRRegClassID,
|
|
getT1tRm(insn))));
|
|
} else {
|
|
assert(OpInfo[OpIdx].RegClass < 0 &&
|
|
!OpInfo[OpIdx].isPredicate() && !OpInfo[OpIdx].isOptionalDef()
|
|
&& "Pure imm operand expected");
|
|
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, BO B) {
|
|
|
|
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)
|
|
&& "Invalid arguments");
|
|
|
|
// Add the destination operand.
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, 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() && "Optional def operand expected");
|
|
MI.addOperand(MCOperand::CreateReg(B->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
|
|
&& "Thumb reg operand expected");
|
|
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(B, 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, BO B) {
|
|
|
|
// 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(B, 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(B, 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 && "More operands expected");
|
|
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 && "More operands expected");
|
|
RegClass = OpInfo[OpIdx].RegClass;
|
|
MI.addOperand(MCOperand::CreateReg(
|
|
getRegisterEnum(B, 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, BO B) {
|
|
|
|
const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo;
|
|
if (!OpInfo) return false;
|
|
|
|
assert(NumOps >= 2 && OpInfo[0].RegClass == ARM::tGPRRegClassID &&
|
|
(OpInfo[1].RegClass < 0 &&
|
|
!OpInfo[1].isPredicate() &&
|
|
!OpInfo[1].isOptionalDef())
|
|
&& "Invalid arguments");
|
|
|
|
// Add the destination operand.
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, 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, BO B) {
|
|
|
|
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
|
|
&& "Expect >= 2 operands and first two as thumb reg operands");
|
|
|
|
// Add the destination reg and the base reg.
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::tGPRRegClassID,
|
|
getT1tRd(insn))));
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, 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 && "More operands expected");
|
|
|
|
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
|
|
&& "Thumb reg operand expected");
|
|
MI.addOperand(MCOperand::CreateReg(
|
|
Imm5 ? 0
|
|
: getRegisterEnum(B, 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, BO B) {
|
|
|
|
assert((Opcode == ARM::tLDRspi || Opcode == ARM::tSTRspi)
|
|
&& "Unexpected opcode");
|
|
|
|
const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo;
|
|
if (!OpInfo) return false;
|
|
|
|
assert(NumOps >= 3 &&
|
|
OpInfo[0].RegClass == ARM::tGPRRegClassID &&
|
|
OpInfo[1].RegClass == ARM::GPRRegClassID &&
|
|
(OpInfo[2].RegClass < 0 &&
|
|
!OpInfo[2].isPredicate() &&
|
|
!OpInfo[2].isOptionalDef())
|
|
&& "Invalid arguments");
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, 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, BO B) {
|
|
|
|
assert(Opcode == ARM::tADDrPCi && "Unexpected opcode");
|
|
|
|
const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo;
|
|
if (!OpInfo) return false;
|
|
|
|
assert(NumOps >= 2 && OpInfo[0].RegClass == ARM::tGPRRegClassID &&
|
|
(OpInfo[1].RegClass < 0 &&
|
|
!OpInfo[1].isPredicate() &&
|
|
!OpInfo[1].isOptionalDef())
|
|
&& "Invalid arguments");
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, 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, BO B) {
|
|
|
|
assert(Opcode == ARM::tADDrSPi && "Unexpected opcode");
|
|
|
|
const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo;
|
|
if (!OpInfo) return false;
|
|
|
|
assert(NumOps >= 3 &&
|
|
OpInfo[0].RegClass == ARM::tGPRRegClassID &&
|
|
OpInfo[1].RegClass == ARM::GPRRegClassID &&
|
|
(OpInfo[2].RegClass < 0 &&
|
|
!OpInfo[2].isPredicate() &&
|
|
!OpInfo[2].isOptionalDef())
|
|
&& "Invalid arguments");
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, 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, BO B) {
|
|
|
|
assert((Opcode == ARM::tPUSH || Opcode == ARM::tPOP) && "Unexpected opcode");
|
|
|
|
unsigned &OpIdx = NumOpsAdded;
|
|
|
|
// Handling the two predicate operands before the reglist.
|
|
if (B->DoPredicateOperands(MI, Opcode, insn, NumOps))
|
|
OpIdx += 2;
|
|
else {
|
|
DEBUG(errs() << "Expected predicate operands not found.\n");
|
|
return false;
|
|
}
|
|
|
|
unsigned RegListBits = slice(insn, 8, 8) << (Opcode == ARM::tPUSH ? 14 : 15)
|
|
| slice(insn, 7, 0);
|
|
|
|
// Fill the variadic part of reglist.
|
|
for (unsigned i = 0; i < 16; ++i) {
|
|
if ((RegListBits >> i) & 1) {
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, 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, BO B) {
|
|
|
|
if (NumOps == 0)
|
|
return true;
|
|
|
|
if (Opcode == ARM::tPUSH || Opcode == ARM::tPOP)
|
|
return DisassembleThumb1PushPop(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
|
|
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)
|
|
&& "Expect >=2 operands");
|
|
|
|
// Add the destination operand.
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::tGPRRegClassID,
|
|
getT1tRd(insn))));
|
|
|
|
if (OpInfo[1].RegClass == ARM::tGPRRegClassID) {
|
|
// Two register instructions.
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::tGPRRegClassID,
|
|
getT1tRn(insn))));
|
|
} else {
|
|
// CBNZ, CBZ
|
|
assert((Opcode == ARM::tCBNZ || Opcode == ARM::tCBZ) &&"Unexpected opcode");
|
|
MI.addOperand(MCOperand::CreateImm(getT1Imm6(insn) * 2));
|
|
}
|
|
|
|
NumOpsAdded = 2;
|
|
|
|
return true;
|
|
}
|
|
|
|
// A8.6.53 LDM / LDMIA
|
|
// A8.6.189 STM / STMIA
|
|
//
|
|
// tLDM_UPD/tSTM_UPD: tRt tRt AM4ModeImm Pred-Imm Pred-CCR register_list
|
|
// tLDM: 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, BO B) {
|
|
|
|
assert((Opcode == ARM::tLDM || Opcode == ARM::tLDM_UPD ||
|
|
Opcode == ARM::tSTM_UPD) && "Unexpected opcode");
|
|
|
|
unsigned &OpIdx = NumOpsAdded;
|
|
|
|
unsigned tRt = getT1tRt(insn);
|
|
|
|
OpIdx = 0;
|
|
|
|
// WB register, if necessary.
|
|
if (Opcode == ARM::tLDM_UPD || Opcode == ARM::tSTM_UPD) {
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
tRt)));
|
|
++OpIdx;
|
|
}
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
tRt)));
|
|
++OpIdx;
|
|
|
|
// A8.6.53 LDM / LDMIA / LDMFD - Encoding T1
|
|
// A8.6.53 STM / STMIA / STMEA - Encoding T1
|
|
MI.addOperand(MCOperand::CreateImm(ARM_AM::getAM4ModeImm(ARM_AM::ia)));
|
|
++OpIdx;
|
|
|
|
// Handling the two predicate operands before the reglist.
|
|
if (B->DoPredicateOperands(MI, Opcode, insn, NumOps))
|
|
OpIdx += 2;
|
|
else {
|
|
DEBUG(errs() << "Expected predicate operands not found.\n");
|
|
return false;
|
|
}
|
|
|
|
unsigned RegListBits = slice(insn, 7, 0);
|
|
|
|
// Fill the variadic part of reglist.
|
|
for (unsigned i = 0; i < 8; ++i) {
|
|
if ((RegListBits >> i) & 1) {
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::tGPRRegClassID,
|
|
i)));
|
|
++OpIdx;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool DisassembleThumb1LdMul(MCInst &MI, unsigned Opcode, uint32_t insn,
|
|
unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
return DisassembleThumb1LdStMul(true, MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
B);
|
|
}
|
|
|
|
static bool DisassembleThumb1StMul(MCInst &MI, unsigned Opcode, uint32_t insn,
|
|
unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
return DisassembleThumb1LdStMul(false, MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
B);
|
|
}
|
|
|
|
// 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, BO) {
|
|
|
|
if (Opcode == ARM::tTRAP)
|
|
return true;
|
|
|
|
const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo;
|
|
if (!OpInfo) return false;
|
|
|
|
assert(NumOps == 3 && OpInfo[0].RegClass < 0 &&
|
|
OpInfo[1].isPredicate() && OpInfo[2].RegClass == ARM::CCRRegClassID
|
|
&& "Exactly 3 operands expected");
|
|
|
|
unsigned Imm8 = getT1Imm8(insn);
|
|
MI.addOperand(MCOperand::CreateImm(
|
|
Opcode == ARM::tBcc ? SignExtend32<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, BO) {
|
|
|
|
const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo;
|
|
if (!OpInfo) return false;
|
|
|
|
assert(NumOps == 1 && OpInfo[0].RegClass < 0 && "1 imm operand expected");
|
|
|
|
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(SignExtend32<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, BO B) {
|
|
|
|
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, B);
|
|
case 1:
|
|
switch (op2) {
|
|
case 0:
|
|
switch (op3) {
|
|
case 0:
|
|
// A6.2.2 Data-processing
|
|
return DisassembleThumb1DP(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
case 1:
|
|
// A6.2.3 Special data instructions and branch and exchange
|
|
return DisassembleThumb1Special(MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
B);
|
|
default:
|
|
// A8.6.59 LDR (literal)
|
|
return DisassembleThumb1LdPC(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
}
|
|
break;
|
|
default:
|
|
// A6.2.4 Load/store single data item
|
|
return DisassembleThumb1LdSt(opA, MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
B);
|
|
break;
|
|
}
|
|
break;
|
|
case 2:
|
|
switch (op2) {
|
|
case 0:
|
|
// A6.2.4 Load/store single data item
|
|
return DisassembleThumb1LdSt(opA, MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
B);
|
|
case 1:
|
|
// A6.2.4 Load/store single data item
|
|
return DisassembleThumb1LdStSP(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
case 2:
|
|
if (op3 <= 1) {
|
|
// A8.6.10 ADR
|
|
return DisassembleThumb1AddPCi(MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
B);
|
|
} else {
|
|
// A8.6.8 ADD (SP plus immediate)
|
|
return DisassembleThumb1AddSPi(MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
B);
|
|
}
|
|
default:
|
|
// A6.2.5 Miscellaneous 16-bit instructions
|
|
return DisassembleThumb1Misc(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
}
|
|
break;
|
|
case 3:
|
|
switch (op2) {
|
|
case 0:
|
|
if (op3 <= 1) {
|
|
// A8.6.189 STM / STMIA / STMEA
|
|
return DisassembleThumb1StMul(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
} else {
|
|
// A8.6.53 LDM / LDMIA / LDMFD
|
|
return DisassembleThumb1LdMul(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
}
|
|
case 1:
|
|
// A6.2.6 Conditional branch, and Supervisor Call
|
|
return DisassembleThumb1CondBr(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
case 2:
|
|
// Unconditional Branch, see B on page A8-44
|
|
return DisassembleThumb1Br(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
default:
|
|
assert(0 && "Unreachable code");
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
assert(0 && "Unreachable code");
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// //
|
|
// Thumb2 instruction disassembly functions. //
|
|
// //
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// //
|
|
// Note: the register naming follows the ARM convention! //
|
|
// //
|
|
///////////////////////////////////////////////////////////
|
|
|
|
static inline bool Thumb2SRSOpcode(unsigned Opcode) {
|
|
switch (Opcode) {
|
|
default:
|
|
return false;
|
|
case ARM::t2SRSDBW: case ARM::t2SRSDB:
|
|
case ARM::t2SRSIAW: case ARM::t2SRSIA:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static inline bool Thumb2RFEOpcode(unsigned Opcode) {
|
|
switch (Opcode) {
|
|
default:
|
|
return false;
|
|
case ARM::t2RFEDBW: case ARM::t2RFEDB:
|
|
case ARM::t2RFEIAW: case ARM::t2RFEIA:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// t2SRS[IA|DB]W/t2SRS[IA|DB]: mode_imm = Inst{4-0}
|
|
static bool DisassembleThumb2SRS(MCInst &MI, unsigned Opcode, uint32_t insn,
|
|
unsigned short NumOps, unsigned &NumOpsAdded) {
|
|
MI.addOperand(MCOperand::CreateImm(slice(insn, 4, 0)));
|
|
NumOpsAdded = 1;
|
|
return true;
|
|
}
|
|
|
|
// t2RFE[IA|DB]W/t2RFE[IA|DB]: Rn
|
|
static bool DisassembleThumb2RFE(MCInst &MI, unsigned Opcode, uint32_t insn,
|
|
unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRn(insn))));
|
|
NumOpsAdded = 1;
|
|
return true;
|
|
}
|
|
|
|
static bool DisassembleThumb2LdStMul(MCInst &MI, unsigned Opcode, uint32_t insn,
|
|
unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
|
|
if (Thumb2SRSOpcode(Opcode))
|
|
return DisassembleThumb2SRS(MI, Opcode, insn, NumOps, NumOpsAdded);
|
|
|
|
if (Thumb2RFEOpcode(Opcode))
|
|
return DisassembleThumb2RFE(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
|
|
assert((Opcode == ARM::t2LDM || Opcode == ARM::t2LDM_UPD ||
|
|
Opcode == ARM::t2STM || Opcode == ARM::t2STM_UPD)
|
|
&& "Unexpected opcode");
|
|
assert(NumOps >= 5 && "Thumb2 LdStMul expects NumOps >= 5");
|
|
|
|
unsigned &OpIdx = NumOpsAdded;
|
|
|
|
OpIdx = 0;
|
|
|
|
unsigned Base = getRegisterEnum(B, ARM::GPRRegClassID, decodeRn(insn));
|
|
|
|
// Writeback to base.
|
|
if (Opcode == ARM::t2LDM_UPD || Opcode == ARM::t2STM_UPD) {
|
|
MI.addOperand(MCOperand::CreateReg(Base));
|
|
++OpIdx;
|
|
}
|
|
|
|
MI.addOperand(MCOperand::CreateReg(Base));
|
|
++OpIdx;
|
|
|
|
ARM_AM::AMSubMode SubMode = getAMSubModeForBits(getPUBits(insn));
|
|
MI.addOperand(MCOperand::CreateImm(ARM_AM::getAM4ModeImm(SubMode)));
|
|
++OpIdx;
|
|
|
|
// Handling the two predicate operands before the reglist.
|
|
if (B->DoPredicateOperands(MI, Opcode, insn, NumOps))
|
|
OpIdx += 2;
|
|
else {
|
|
DEBUG(errs() << "Expected predicate operands not found.\n");
|
|
return false;
|
|
}
|
|
|
|
unsigned RegListBits = insn & ((1 << 16) - 1);
|
|
|
|
// Fill the variadic part of reglist.
|
|
for (unsigned i = 0; i < 16; ++i) {
|
|
if ((RegListBits >> i) & 1) {
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
i)));
|
|
++OpIdx;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// t2LDREX: Rd Rn
|
|
// t2LDREXD: Rd Rs Rn
|
|
// t2LDREXB, t2LDREXH: Rd Rn
|
|
// t2STREX: Rs Rd Rn
|
|
// t2STREXD: Rm Rd Rs Rn
|
|
// t2STREXB, t2STREXH: Rm Rd Rn
|
|
static bool DisassembleThumb2LdStEx(MCInst &MI, unsigned Opcode, uint32_t insn,
|
|
unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
|
|
const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo;
|
|
if (!OpInfo) return false;
|
|
|
|
unsigned &OpIdx = NumOpsAdded;
|
|
|
|
OpIdx = 0;
|
|
|
|
assert(NumOps >= 2
|
|
&& OpInfo[0].RegClass == ARM::GPRRegClassID
|
|
&& OpInfo[1].RegClass == ARM::GPRRegClassID
|
|
&& "Expect >=2 operands and first two as reg operands");
|
|
|
|
bool isStore = (ARM::t2STREX <= Opcode && Opcode <= ARM::t2STREXH);
|
|
bool isSW = (Opcode == ARM::t2LDREX || Opcode == ARM::t2STREX);
|
|
bool isDW = (Opcode == ARM::t2LDREXD || Opcode == ARM::t2STREXD);
|
|
|
|
// Add the destination operand for store.
|
|
if (isStore) {
|
|
MI.addOperand(MCOperand::CreateReg(
|
|
getRegisterEnum(B, ARM::GPRRegClassID,
|
|
isSW ? decodeRs(insn) : decodeRm(insn))));
|
|
++OpIdx;
|
|
}
|
|
|
|
// Source operand for store and destination operand for load.
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRd(insn))));
|
|
++OpIdx;
|
|
|
|
// Thumb2 doubleword complication: with an extra source/destination operand.
|
|
if (isDW) {
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRs(insn))));
|
|
++OpIdx;
|
|
}
|
|
|
|
// Finally add the pointer operand.
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRn(insn))));
|
|
++OpIdx;
|
|
|
|
return true;
|
|
}
|
|
|
|
// LLVM, as of Jan-05-2010, does not output <Rt2>, i.e., Rs, in the asm.
|
|
// Whereas the ARM Arch. Manual does not require that t2 = t+1 like in ARM ISA.
|
|
//
|
|
// t2LDRDi8: Rd Rs Rn imm8s4 (offset mode)
|
|
// t2LDRDpci: Rd Rs imm8s4 (Not decoded, prefer the generic t2LDRDi8 version)
|
|
// t2STRDi8: Rd Rs Rn imm8s4 (offset mode)
|
|
//
|
|
// Ditto for t2LDRD_PRE, t2LDRD_POST, t2STRD_PRE, t2STRD_POST, which are for
|
|
// disassembly only and do not have a tied_to writeback base register operand.
|
|
static bool DisassembleThumb2LdStDual(MCInst &MI, unsigned Opcode,
|
|
uint32_t insn, unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
|
|
const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo;
|
|
if (!OpInfo) return false;
|
|
|
|
assert(NumOps >= 4
|
|
&& OpInfo[0].RegClass == ARM::GPRRegClassID
|
|
&& OpInfo[1].RegClass == ARM::GPRRegClassID
|
|
&& OpInfo[2].RegClass == ARM::GPRRegClassID
|
|
&& OpInfo[3].RegClass < 0
|
|
&& "Expect >= 4 operands and first 3 as reg operands");
|
|
|
|
// Add the <Rt> <Rt2> operands.
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRd(insn))));
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRs(insn))));
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRn(insn))));
|
|
|
|
// Finally add (+/-)imm8*4, depending on the U bit.
|
|
int Offset = getImm8(insn) * 4;
|
|
if (getUBit(insn) == 0)
|
|
Offset = -Offset;
|
|
MI.addOperand(MCOperand::CreateImm(Offset));
|
|
NumOpsAdded = 4;
|
|
|
|
return true;
|
|
}
|
|
|
|
// PC-based defined for Codegen, which do not get decoded by design:
|
|
//
|
|
// t2TBB, t2TBH: Rm immDontCare immDontCare
|
|
//
|
|
// Generic version defined for disassembly:
|
|
//
|
|
// t2TBBgen, t2TBHgen: Rn Rm Pred-Imm Pred-CCR
|
|
static bool DisassembleThumb2TB(MCInst &MI, unsigned Opcode,
|
|
uint32_t insn, unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
|
|
assert(NumOps >= 2 && "Expect >= 2 operands");
|
|
|
|
// The generic version of TBB/TBH needs a base register.
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRn(insn))));
|
|
// Add the index register.
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRm(insn))));
|
|
NumOpsAdded = 2;
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline bool Thumb2ShiftOpcode(unsigned Opcode) {
|
|
switch (Opcode) {
|
|
default:
|
|
return false;
|
|
case ARM::t2MOVCClsl: case ARM::t2MOVCClsr:
|
|
case ARM::t2MOVCCasr: case ARM::t2MOVCCror:
|
|
case ARM::t2LSLri: case ARM::t2LSRri:
|
|
case ARM::t2ASRri: case ARM::t2RORri:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// A6.3.11 Data-processing (shifted register)
|
|
//
|
|
// Two register operands (Rn=0b1111 no 1st operand reg): Rs Rm
|
|
// Two register operands (Rs=0b1111 no dst operand reg): Rn Rm
|
|
// Three register operands: Rs Rn Rm
|
|
// Three register operands: (Rn=0b1111 Conditional Move) Rs Ro(TIED_TO) Rm
|
|
//
|
|
// Constant shifts t2_so_reg is a 2-operand unit corresponding to the Thumb2
|
|
// register with shift forms: (Rm, ConstantShiftSpecifier).
|
|
// Constant shift specifier: Imm = (ShOp | ShAmt<<3).
|
|
//
|
|
// There are special instructions, like t2MOVsra_flag and t2MOVsrl_flag, which
|
|
// only require two register operands: Rd, Rm in ARM Reference Manual terms, and
|
|
// nothing else, because the shift amount is already specified.
|
|
// Similar case holds for t2MOVrx, t2ADDrr, ..., etc.
|
|
static bool DisassembleThumb2DPSoReg(MCInst &MI, unsigned Opcode, uint32_t insn,
|
|
unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
|
|
const TargetInstrDesc &TID = ARMInsts[Opcode];
|
|
const TargetOperandInfo *OpInfo = TID.OpInfo;
|
|
unsigned &OpIdx = NumOpsAdded;
|
|
|
|
// Special case handling.
|
|
if (Opcode == ARM::t2BR_JT) {
|
|
assert(NumOps == 4
|
|
&& OpInfo[0].RegClass == ARM::GPRRegClassID
|
|
&& OpInfo[1].RegClass == ARM::GPRRegClassID
|
|
&& OpInfo[2].RegClass < 0
|
|
&& OpInfo[3].RegClass < 0
|
|
&& "Exactlt 4 operands expect and first two as reg operands");
|
|
// Only need to populate the src reg operand.
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRm(insn))));
|
|
MI.addOperand(MCOperand::CreateReg(0));
|
|
MI.addOperand(MCOperand::CreateImm(0));
|
|
MI.addOperand(MCOperand::CreateImm(0));
|
|
NumOpsAdded = 4;
|
|
return true;
|
|
}
|
|
|
|
OpIdx = 0;
|
|
|
|
assert(NumOps >= 2
|
|
&& OpInfo[0].RegClass == ARM::GPRRegClassID
|
|
&& OpInfo[1].RegClass == ARM::GPRRegClassID
|
|
&& "Expect >= 2 operands and first two as reg operands");
|
|
|
|
bool ThreeReg = (NumOps > 2 && OpInfo[2].RegClass == ARM::GPRRegClassID);
|
|
bool NoDstReg = (decodeRs(insn) == 0xF);
|
|
|
|
// Build the register operands, followed by the constant shift specifier.
|
|
|
|
MI.addOperand(MCOperand::CreateReg(
|
|
getRegisterEnum(B, ARM::GPRRegClassID,
|
|
NoDstReg ? decodeRn(insn) : decodeRs(insn))));
|
|
++OpIdx;
|
|
|
|
if (ThreeReg) {
|
|
int Idx;
|
|
if ((Idx = TID.getOperandConstraint(OpIdx, TOI::TIED_TO)) != -1) {
|
|
// Process tied_to operand constraint.
|
|
MI.addOperand(MI.getOperand(Idx));
|
|
++OpIdx;
|
|
} else if (!NoDstReg) {
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRn(insn))));
|
|
++OpIdx;
|
|
} else {
|
|
DEBUG(errs() << "Thumb2 encoding error: d==15 for three-reg operands.\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRm(insn))));
|
|
++OpIdx;
|
|
|
|
if (NumOps == OpIdx)
|
|
return true;
|
|
|
|
if (OpInfo[OpIdx].RegClass < 0 && !OpInfo[OpIdx].isPredicate()
|
|
&& !OpInfo[OpIdx].isOptionalDef()) {
|
|
|
|
if (Thumb2ShiftOpcode(Opcode))
|
|
MI.addOperand(MCOperand::CreateImm(getShiftAmtBits(insn)));
|
|
else {
|
|
// Build the constant shift specifier operand.
|
|
unsigned bits2 = getShiftTypeBits(insn);
|
|
unsigned imm5 = getShiftAmtBits(insn);
|
|
ARM_AM::ShiftOpc ShOp = ARM_AM::no_shift;
|
|
unsigned ShAmt = decodeImmShift(bits2, imm5, ShOp);
|
|
|
|
// PKHBT/PKHTB are special in that we need the decodeImmShift() call to
|
|
// decode the shift amount from raw imm5 and bits2, but we DO NOT need
|
|
// to encode the ShOp, as it's in the asm string already.
|
|
if (Opcode == ARM::t2PKHBT || Opcode == ARM::t2PKHTB)
|
|
MI.addOperand(MCOperand::CreateImm(ShAmt));
|
|
else
|
|
MI.addOperand(MCOperand::CreateImm(ARM_AM::getSORegOpc(ShOp, ShAmt)));
|
|
}
|
|
++OpIdx;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// A6.3.1 Data-processing (modified immediate)
|
|
//
|
|
// Two register operands: Rs Rn ModImm
|
|
// One register operands (Rs=0b1111 no explicit dest reg): Rn ModImm
|
|
// One register operands (Rn=0b1111 no explicit src reg): Rs ModImm - {t2MOVi, t2MVNi}
|
|
//
|
|
// ModImm = ThumbExpandImm(i:imm3:imm8)
|
|
static bool DisassembleThumb2DPModImm(MCInst &MI, unsigned Opcode,
|
|
uint32_t insn, unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
|
|
const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo;
|
|
unsigned &OpIdx = NumOpsAdded;
|
|
|
|
OpIdx = 0;
|
|
|
|
assert(NumOps >= 2 && OpInfo[0].RegClass == ARM::GPRRegClassID
|
|
&& "Expect >= 2 operands and first one as reg operand");
|
|
|
|
bool TwoReg = (OpInfo[1].RegClass == ARM::GPRRegClassID);
|
|
bool NoDstReg = (decodeRs(insn) == 0xF);
|
|
|
|
// Build the register operands, followed by the modified immediate.
|
|
|
|
MI.addOperand(MCOperand::CreateReg(
|
|
getRegisterEnum(B, ARM::GPRRegClassID,
|
|
NoDstReg ? decodeRn(insn) : decodeRs(insn))));
|
|
++OpIdx;
|
|
|
|
if (TwoReg) {
|
|
if (NoDstReg) {
|
|
DEBUG(errs()<<"Thumb2 encoding error: d==15 for DPModImm 2-reg instr.\n");
|
|
return false;
|
|
}
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRn(insn))));
|
|
++OpIdx;
|
|
}
|
|
|
|
// The modified immediate operand should come next.
|
|
assert(OpIdx < NumOps && OpInfo[OpIdx].RegClass < 0 &&
|
|
!OpInfo[OpIdx].isPredicate() && !OpInfo[OpIdx].isOptionalDef()
|
|
&& "Pure imm operand expected");
|
|
|
|
// i:imm3:imm8
|
|
// A6.3.2 Modified immediate constants in Thumb instructions
|
|
unsigned imm12 = getIImm3Imm8(insn);
|
|
MI.addOperand(MCOperand::CreateImm(ThumbExpandImm(imm12)));
|
|
++OpIdx;
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline bool Thumb2SaturateOpcode(unsigned Opcode) {
|
|
switch (Opcode) {
|
|
case ARM::t2SSATlsl: case ARM::t2SSATasr: case ARM::t2SSAT16:
|
|
case ARM::t2USATlsl: case ARM::t2USATasr: case ARM::t2USAT16:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline unsigned decodeThumb2SaturatePos(unsigned Opcode, uint32_t insn) {
|
|
switch (Opcode) {
|
|
case ARM::t2SSATlsl:
|
|
case ARM::t2SSATasr:
|
|
return slice(insn, 4, 0) + 1;
|
|
case ARM::t2SSAT16:
|
|
return slice(insn, 3, 0) + 1;
|
|
case ARM::t2USATlsl:
|
|
case ARM::t2USATasr:
|
|
return slice(insn, 4, 0);
|
|
case ARM::t2USAT16:
|
|
return slice(insn, 3, 0);
|
|
default:
|
|
assert(0 && "Unexpected opcode");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// A6.3.3 Data-processing (plain binary immediate)
|
|
//
|
|
// o t2ADDri12, t2SUBri12: Rs Rn imm12
|
|
// o t2LEApcrel (ADR): Rs imm12
|
|
// o t2BFC (BFC): Rs Ro(TIED_TO) bf_inv_mask_imm
|
|
// o t2BFI (BFI) (Currently not defined in LLVM as of Jan-07-2010)
|
|
// o t2MOVi16: Rs imm16
|
|
// o t2MOVTi16: Rs imm16
|
|
// o t2SBFX (SBFX): Rs Rn lsb width
|
|
// o t2UBFX (UBFX): Rs Rn lsb width
|
|
// o t2BFI (BFI): Rs Rn lsb width
|
|
//
|
|
// [Signed|Unsigned] Saturate [16]
|
|
//
|
|
// o t2SSAT[lsl|asr], t2USAT[lsl|asr]: Rs sat_pos Rn shamt
|
|
// o t2SSAT16, t2USAT16: Rs sat_pos Rn
|
|
static bool DisassembleThumb2DPBinImm(MCInst &MI, unsigned Opcode,
|
|
uint32_t insn, unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
|
|
const TargetInstrDesc &TID = ARMInsts[Opcode];
|
|
const TargetOperandInfo *OpInfo = TID.OpInfo;
|
|
unsigned &OpIdx = NumOpsAdded;
|
|
|
|
OpIdx = 0;
|
|
|
|
assert(NumOps >= 2 && OpInfo[0].RegClass == ARM::GPRRegClassID
|
|
&& "Expect >= 2 operands and first one as reg operand");
|
|
|
|
bool TwoReg = (OpInfo[1].RegClass == ARM::GPRRegClassID);
|
|
|
|
// Build the register operand(s), followed by the immediate(s).
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRs(insn))));
|
|
++OpIdx;
|
|
|
|
// t2SSAT/t2SSAT16/t2USAT/t2USAT16 has imm operand after Rd.
|
|
if (Thumb2SaturateOpcode(Opcode)) {
|
|
MI.addOperand(MCOperand::CreateImm(decodeThumb2SaturatePos(Opcode, insn)));
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRn(insn))));
|
|
|
|
if (Opcode == ARM::t2SSAT16 || Opcode == ARM::t2USAT16) {
|
|
OpIdx += 2;
|
|
return true;
|
|
}
|
|
|
|
// For SSAT operand reg (Rn) has been disassembled above.
|
|
// Now disassemble the shift amount.
|
|
|
|
// Inst{14-12:7-6} encodes the imm5 shift amount.
|
|
unsigned ShAmt = slice(insn, 14, 12) << 2 | slice(insn, 7, 6);
|
|
|
|
MI.addOperand(MCOperand::CreateImm(ShAmt));
|
|
|
|
OpIdx += 3;
|
|
return true;
|
|
}
|
|
|
|
if (TwoReg) {
|
|
assert(NumOps >= 3 && "Expect >= 3 operands");
|
|
int Idx;
|
|
if ((Idx = TID.getOperandConstraint(OpIdx, TOI::TIED_TO)) != -1) {
|
|
// Process tied_to operand constraint.
|
|
MI.addOperand(MI.getOperand(Idx));
|
|
} else {
|
|
// Add src reg operand.
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRn(insn))));
|
|
}
|
|
++OpIdx;
|
|
}
|
|
|
|
assert(OpInfo[OpIdx].RegClass < 0 && !OpInfo[OpIdx].isPredicate()
|
|
&& !OpInfo[OpIdx].isOptionalDef()
|
|
&& "Pure imm operand expected");
|
|
|
|
// Pre-increment OpIdx.
|
|
++OpIdx;
|
|
|
|
if (Opcode == ARM::t2ADDri12 || Opcode == ARM::t2SUBri12
|
|
|| Opcode == ARM::t2LEApcrel)
|
|
MI.addOperand(MCOperand::CreateImm(getIImm3Imm8(insn)));
|
|
else if (Opcode == ARM::t2MOVi16 || Opcode == ARM::t2MOVTi16)
|
|
MI.addOperand(MCOperand::CreateImm(getImm16(insn)));
|
|
else if (Opcode == ARM::t2BFC) {
|
|
uint32_t mask = 0;
|
|
if (getBitfieldInvMask(insn, mask))
|
|
MI.addOperand(MCOperand::CreateImm(mask));
|
|
else
|
|
return false;
|
|
} else {
|
|
// Handle the case of: lsb width
|
|
assert((Opcode == ARM::t2SBFX || Opcode == ARM::t2UBFX ||
|
|
Opcode == ARM::t2BFI) && "Unexpected opcode");
|
|
MI.addOperand(MCOperand::CreateImm(getLsb(insn)));
|
|
if (Opcode == ARM::t2BFI) {
|
|
if (getMsb(insn) < getLsb(insn)) {
|
|
DEBUG(errs() << "Encoding error: msb < lsb\n");
|
|
return false;
|
|
}
|
|
MI.addOperand(MCOperand::CreateImm(getMsb(insn) - getLsb(insn) + 1));
|
|
} else
|
|
MI.addOperand(MCOperand::CreateImm(getWidthMinus1(insn) + 1));
|
|
|
|
++OpIdx;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// A6.3.4 Table A6-15 Miscellaneous control instructions
|
|
// A8.6.41 DMB
|
|
// A8.6.42 DSB
|
|
// A8.6.49 ISB
|
|
static inline bool t2MiscCtrlInstr(uint32_t insn) {
|
|
if (slice(insn, 31, 20) == 0xf3b && slice(insn, 15, 14) == 2 &&
|
|
slice(insn, 12, 12) == 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
// A6.3.4 Branches and miscellaneous control
|
|
//
|
|
// A8.6.16 B
|
|
// Branches: t2B, t2Bcc -> imm operand
|
|
//
|
|
// Branches: t2TPsoft -> no operand
|
|
//
|
|
// A8.6.23 BL, BLX (immediate)
|
|
// Branches (defined in ARMInstrThumb.td): tBLr9, tBLXi_r9 -> imm operand
|
|
//
|
|
// A8.6.26
|
|
// t2BXJ -> Rn
|
|
//
|
|
// Miscellaneous control: t2Int_MemBarrierV7 (and its t2DMB variants),
|
|
// t2Int_SyncBarrierV7 (and its t2DSB varianst), t2ISBsy, t2CLREX
|
|
// -> no operand (except pred-imm pred-ccr for CLREX, memory barrier variants)
|
|
//
|
|
// Hint: t2NOP, t2YIELD, t2WFE, t2WFI, t2SEV
|
|
// -> no operand (except pred-imm pred-ccr)
|
|
//
|
|
// t2DBG -> imm4 = Inst{3-0}
|
|
//
|
|
// t2MRS/t2MRSsys -> Rs
|
|
// t2MSR/t2MSRsys -> Rn mask=Inst{11-8}
|
|
// t2SMC -> imm4 = Inst{19-16}
|
|
static bool DisassembleThumb2BrMiscCtrl(MCInst &MI, unsigned Opcode,
|
|
uint32_t insn, unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
|
|
if (NumOps == 0)
|
|
return true;
|
|
|
|
if (t2MiscCtrlInstr(insn))
|
|
return true;
|
|
|
|
switch (Opcode) {
|
|
case ARM::t2CLREX:
|
|
case ARM::t2NOP:
|
|
case ARM::t2YIELD:
|
|
case ARM::t2WFE:
|
|
case ARM::t2WFI:
|
|
case ARM::t2SEV:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// CPS has a singleton $opt operand that contains the following information:
|
|
// opt{4-0} = mode from Inst{4-0}
|
|
// opt{5} = changemode from Inst{8}
|
|
// opt{8-6} = AIF from Inst{7-5}
|
|
// opt{10-9} = imod from Inst{10-9} with 0b10 as enable and 0b11 as disable
|
|
if (Opcode == ARM::t2CPS) {
|
|
unsigned Option = slice(insn, 4, 0) | slice(insn, 8, 8) << 5 |
|
|
slice(insn, 7, 5) << 6 | slice(insn, 10, 9) << 9;
|
|
MI.addOperand(MCOperand::CreateImm(Option));
|
|
NumOpsAdded = 1;
|
|
return true;
|
|
}
|
|
|
|
// DBG has its option specified in Inst{3-0}.
|
|
if (Opcode == ARM::t2DBG) {
|
|
MI.addOperand(MCOperand::CreateImm(slice(insn, 3, 0)));
|
|
NumOpsAdded = 1;
|
|
return true;
|
|
}
|
|
|
|
// MRS and MRSsys take one GPR reg Rs.
|
|
if (Opcode == ARM::t2MRS || Opcode == ARM::t2MRSsys) {
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRs(insn))));
|
|
NumOpsAdded = 1;
|
|
return true;
|
|
}
|
|
// BXJ takes one GPR reg Rn.
|
|
if (Opcode == ARM::t2BXJ) {
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRn(insn))));
|
|
NumOpsAdded = 1;
|
|
return true;
|
|
}
|
|
// MSR and MSRsys take one GPR reg Rn, followed by the mask.
|
|
if (Opcode == ARM::t2MSR || Opcode == ARM::t2MSRsys || Opcode == ARM::t2BXJ) {
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRn(insn))));
|
|
MI.addOperand(MCOperand::CreateImm(slice(insn, 11, 8)));
|
|
NumOpsAdded = 2;
|
|
return true;
|
|
}
|
|
// SMC take imm4.
|
|
if (Opcode == ARM::t2SMC) {
|
|
MI.addOperand(MCOperand::CreateImm(slice(insn, 19, 16)));
|
|
NumOpsAdded = 1;
|
|
return true;
|
|
}
|
|
|
|
// Add the imm operand.
|
|
int Offset = 0;
|
|
|
|
switch (Opcode) {
|
|
default:
|
|
assert(0 && "Unexpected opcode");
|
|
return false;
|
|
case ARM::t2B:
|
|
Offset = decodeImm32_B_EncodingT4(insn);
|
|
break;
|
|
case ARM::t2Bcc:
|
|
Offset = decodeImm32_B_EncodingT3(insn);
|
|
break;
|
|
case ARM::tBLr9:
|
|
Offset = decodeImm32_BL(insn);
|
|
break;
|
|
case ARM::tBLXi_r9:
|
|
Offset = decodeImm32_BLX(insn);
|
|
break;
|
|
}
|
|
// 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(Offset + 4));
|
|
|
|
NumOpsAdded = 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline bool Thumb2PreloadOpcode(unsigned Opcode) {
|
|
switch (Opcode) {
|
|
default:
|
|
return false;
|
|
case ARM::t2PLDi12: case ARM::t2PLDi8: case ARM::t2PLDpci:
|
|
case ARM::t2PLDr: case ARM::t2PLDs:
|
|
case ARM::t2PLDWi12: case ARM::t2PLDWi8: case ARM::t2PLDWpci:
|
|
case ARM::t2PLDWr: case ARM::t2PLDWs:
|
|
case ARM::t2PLIi12: case ARM::t2PLIi8: case ARM::t2PLIpci:
|
|
case ARM::t2PLIr: case ARM::t2PLIs:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static bool DisassembleThumb2PreLoad(MCInst &MI, unsigned Opcode, uint32_t insn,
|
|
unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
|
|
// Preload Data/Instruction requires either 2 or 3 operands.
|
|
// t2PLDi12, t2PLDi8, t2PLDpci: Rn [+/-]imm12/imm8
|
|
// t2PLDr: Rn Rm
|
|
// t2PLDs: Rn Rm imm2=Inst{5-4}
|
|
// Same pattern applies for t2PLDW* and t2PLI*.
|
|
|
|
const TargetInstrDesc &TID = ARMInsts[Opcode];
|
|
const TargetOperandInfo *OpInfo = TID.OpInfo;
|
|
unsigned &OpIdx = NumOpsAdded;
|
|
|
|
OpIdx = 0;
|
|
|
|
assert(NumOps >= 2 &&
|
|
OpInfo[0].RegClass == ARM::GPRRegClassID &&
|
|
"Expect >= 2 operands and first one as reg operand");
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRn(insn))));
|
|
++OpIdx;
|
|
|
|
if (OpInfo[OpIdx].RegClass == ARM::GPRRegClassID) {
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRm(insn))));
|
|
} else {
|
|
assert(OpInfo[OpIdx].RegClass < 0 && !OpInfo[OpIdx].isPredicate()
|
|
&& !OpInfo[OpIdx].isOptionalDef()
|
|
&& "Pure imm operand expected");
|
|
int Offset = 0;
|
|
if (Opcode == ARM::t2PLDpci || Opcode == ARM::t2PLDWpci ||
|
|
Opcode == ARM::t2PLIpci) {
|
|
bool Negative = slice(insn, 23, 23) == 0;
|
|
unsigned Imm12 = getImm12(insn);
|
|
Offset = Negative ? -1 - Imm12 : 1 * Imm12;
|
|
} else if (Opcode == ARM::t2PLDi8 || Opcode == ARM::t2PLDWi8 ||
|
|
Opcode == ARM::t2PLIi8) {
|
|
// A8.6.117 Encoding T2: add = FALSE
|
|
unsigned Imm8 = getImm8(insn);
|
|
Offset = -1 - Imm8;
|
|
} else // The i12 forms. See, for example, A8.6.117 Encoding T1.
|
|
Offset = decodeImm12(insn);
|
|
MI.addOperand(MCOperand::CreateImm(Offset));
|
|
}
|
|
++OpIdx;
|
|
|
|
if (OpIdx < NumOps && OpInfo[OpIdx].RegClass < 0 &&
|
|
!OpInfo[OpIdx].isPredicate() && !OpInfo[OpIdx].isOptionalDef()) {
|
|
// Fills in the shift amount for t2PLDs, t2PLDWs, t2PLIs.
|
|
MI.addOperand(MCOperand::CreateImm(slice(insn, 5, 4)));
|
|
++OpIdx;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// A8.6.63 LDRB (literal)
|
|
// A8.6.79 LDRSB (literal)
|
|
// A8.6.75 LDRH (literal)
|
|
// A8.6.83 LDRSH (literal)
|
|
// A8.6.59 LDR (literal)
|
|
//
|
|
// These instrs calculate an address from the PC value and an immediate offset.
|
|
// Rd Rn=PC (+/-)imm12 (+ if Inst{23} == 0b1)
|
|
static bool DisassembleThumb2Ldpci(MCInst &MI, unsigned Opcode,
|
|
uint32_t insn, unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
|
|
const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo;
|
|
if (!OpInfo) return false;
|
|
|
|
assert(NumOps >= 2 &&
|
|
OpInfo[0].RegClass == ARM::GPRRegClassID &&
|
|
OpInfo[1].RegClass < 0 &&
|
|
"Expect >= 2 operands, first as reg, and second as imm operand");
|
|
|
|
// Build the register operand, followed by the (+/-)imm12 immediate.
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRd(insn))));
|
|
|
|
MI.addOperand(MCOperand::CreateImm(decodeImm12(insn)));
|
|
|
|
NumOpsAdded = 2;
|
|
|
|
return true;
|
|
}
|
|
|
|
// A6.3.10 Store single data item
|
|
// A6.3.9 Load byte, memory hints
|
|
// A6.3.8 Load halfword, memory hints
|
|
// A6.3.7 Load word
|
|
//
|
|
// For example,
|
|
//
|
|
// t2LDRi12: Rd Rn (+)imm12
|
|
// t2LDRi8: Rd Rn (+/-)imm8 (+ if Inst{9} == 0b1)
|
|
// t2LDRs: Rd Rn Rm ConstantShiftSpecifier (see also DisassembleThumb2DPSoReg)
|
|
// t2LDR_POST: Rd Rn Rn(TIED_TO) (+/-)imm8 (+ if Inst{9} == 0b1)
|
|
// t2LDR_PRE: Rd Rn Rn(TIED_TO) (+/-)imm8 (+ if Inst{9} == 0b1)
|
|
//
|
|
// t2STRi12: Rd Rn (+)imm12
|
|
// t2STRi8: Rd Rn (+/-)imm8 (+ if Inst{9} == 0b1)
|
|
// t2STRs: Rd Rn Rm ConstantShiftSpecifier (see also DisassembleThumb2DPSoReg)
|
|
// t2STR_POST: Rn Rd Rn(TIED_TO) (+/-)imm8 (+ if Inst{9} == 0b1)
|
|
// t2STR_PRE: Rn Rd Rn(TIED_TO) (+/-)imm8 (+ if Inst{9} == 0b1)
|
|
//
|
|
// Note that for indexed modes, the Rn(TIED_TO) operand needs to be populated
|
|
// correctly, as LLVM AsmPrinter depends on it. For indexed stores, the first
|
|
// operand is Rn; for all the other instructions, Rd is the first operand.
|
|
//
|
|
// Delegates to DisassembleThumb2PreLoad() for preload data/instruction.
|
|
// Delegates to DisassembleThumb2Ldpci() for load * literal operations.
|
|
static bool DisassembleThumb2LdSt(bool Load, MCInst &MI, unsigned Opcode,
|
|
uint32_t insn, unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
|
|
unsigned Rn = decodeRn(insn);
|
|
|
|
if (Thumb2PreloadOpcode(Opcode))
|
|
return DisassembleThumb2PreLoad(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
|
|
// See, for example, A6.3.7 Load word: Table A6-18 Load word.
|
|
if (Load && Rn == 15)
|
|
return DisassembleThumb2Ldpci(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
|
|
const TargetInstrDesc &TID = ARMInsts[Opcode];
|
|
const TargetOperandInfo *OpInfo = TID.OpInfo;
|
|
unsigned &OpIdx = NumOpsAdded;
|
|
|
|
OpIdx = 0;
|
|
|
|
assert(NumOps >= 3 &&
|
|
OpInfo[0].RegClass == ARM::GPRRegClassID &&
|
|
OpInfo[1].RegClass == ARM::GPRRegClassID &&
|
|
"Expect >= 3 operands and first two as reg operands");
|
|
|
|
bool ThreeReg = (OpInfo[2].RegClass == ARM::GPRRegClassID);
|
|
bool TIED_TO = ThreeReg && TID.getOperandConstraint(2, TOI::TIED_TO) != -1;
|
|
bool Imm12 = !ThreeReg && slice(insn, 23, 23) == 1; // ARMInstrThumb2.td
|
|
|
|
// Build the register operands, followed by the immediate.
|
|
unsigned R0, R1, R2 = 0;
|
|
unsigned Rd = decodeRd(insn);
|
|
int Imm = 0;
|
|
|
|
if (!Load && TIED_TO) {
|
|
R0 = Rn;
|
|
R1 = Rd;
|
|
} else {
|
|
R0 = Rd;
|
|
R1 = Rn;
|
|
}
|
|
if (ThreeReg) {
|
|
if (TIED_TO) {
|
|
R2 = Rn;
|
|
Imm = decodeImm8(insn);
|
|
} else {
|
|
R2 = decodeRm(insn);
|
|
// See, for example, A8.6.64 LDRB (register).
|
|
// And ARMAsmPrinter::printT2AddrModeSoRegOperand().
|
|
// LSL is the default shift opc, and LLVM does not expect it to be encoded
|
|
// as part of the immediate operand.
|
|
// Imm = ARM_AM::getSORegOpc(ARM_AM::lsl, slice(insn, 5, 4));
|
|
Imm = slice(insn, 5, 4);
|
|
}
|
|
} else {
|
|
if (Imm12)
|
|
Imm = getImm12(insn);
|
|
else
|
|
Imm = decodeImm8(insn);
|
|
}
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
R0)));
|
|
++OpIdx;
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
R1)));
|
|
++OpIdx;
|
|
|
|
if (ThreeReg) {
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
R2)));
|
|
++OpIdx;
|
|
}
|
|
|
|
assert(OpInfo[OpIdx].RegClass < 0 && !OpInfo[OpIdx].isPredicate()
|
|
&& !OpInfo[OpIdx].isOptionalDef()
|
|
&& "Pure imm operand expected");
|
|
|
|
MI.addOperand(MCOperand::CreateImm(Imm));
|
|
++OpIdx;
|
|
|
|
return true;
|
|
}
|
|
|
|
// A6.3.12 Data-processing (register)
|
|
//
|
|
// Two register operands [rotate]: Rs Rm [rotation(= (rotate:'000'))]
|
|
// Three register operands only: Rs Rn Rm
|
|
// Three register operands [rotate]: Rs Rn Rm [rotation(= (rotate:'000'))]
|
|
//
|
|
// Parallel addition and subtraction 32-bit Thumb instructions: Rs Rn Rm
|
|
//
|
|
// Miscellaneous operations: Rs [Rn] Rm
|
|
static bool DisassembleThumb2DPReg(MCInst &MI, unsigned Opcode, uint32_t insn,
|
|
unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
|
|
const TargetInstrDesc &TID = ARMInsts[Opcode];
|
|
const TargetOperandInfo *OpInfo = TID.OpInfo;
|
|
unsigned &OpIdx = NumOpsAdded;
|
|
|
|
OpIdx = 0;
|
|
|
|
assert(NumOps >= 2 &&
|
|
OpInfo[0].RegClass == ARM::GPRRegClassID &&
|
|
OpInfo[1].RegClass == ARM::GPRRegClassID &&
|
|
"Expect >= 2 operands and first two as reg operands");
|
|
|
|
// Build the register operands, followed by the optional rotation amount.
|
|
|
|
bool ThreeReg = NumOps > 2 && OpInfo[2].RegClass == ARM::GPRRegClassID;
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRs(insn))));
|
|
++OpIdx;
|
|
|
|
if (ThreeReg) {
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRn(insn))));
|
|
++OpIdx;
|
|
}
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRm(insn))));
|
|
++OpIdx;
|
|
|
|
if (OpIdx < NumOps && OpInfo[OpIdx].RegClass < 0
|
|
&& !OpInfo[OpIdx].isPredicate() && !OpInfo[OpIdx].isOptionalDef()) {
|
|
// Add the rotation amount immediate.
|
|
MI.addOperand(MCOperand::CreateImm(decodeRotate(insn)));
|
|
++OpIdx;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// A6.3.16 Multiply, multiply accumulate, and absolute difference
|
|
//
|
|
// t2MLA, t2MLS, t2SMMLA, t2SMMLS: Rs Rn Rm Ra=Inst{15-12}
|
|
// t2MUL, t2SMMUL: Rs Rn Rm
|
|
// t2SMLA[BB|BT|TB|TT|WB|WT]: Rs Rn Rm Ra=Inst{15-12}
|
|
// t2SMUL[BB|BT|TB|TT|WB|WT]: Rs Rn Rm
|
|
//
|
|
// Dual halfword multiply: t2SMUAD[X], t2SMUSD[X], t2SMLAD[X], t2SMLSD[X]:
|
|
// Rs Rn Rm Ra=Inst{15-12}
|
|
//
|
|
// Unsigned Sum of Absolute Differences [and Accumulate]
|
|
// Rs Rn Rm [Ra=Inst{15-12}]
|
|
static bool DisassembleThumb2Mul(MCInst &MI, unsigned Opcode, uint32_t insn,
|
|
unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
|
|
const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo;
|
|
|
|
assert(NumOps >= 3 &&
|
|
OpInfo[0].RegClass == ARM::GPRRegClassID &&
|
|
OpInfo[1].RegClass == ARM::GPRRegClassID &&
|
|
OpInfo[2].RegClass == ARM::GPRRegClassID &&
|
|
"Expect >= 3 operands and first three as reg operands");
|
|
|
|
// Build the register operands.
|
|
|
|
bool FourReg = NumOps > 3 && OpInfo[3].RegClass == ARM::GPRRegClassID;
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRs(insn))));
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRn(insn))));
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRm(insn))));
|
|
|
|
if (FourReg)
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRd(insn))));
|
|
|
|
NumOpsAdded = FourReg ? 4 : 3;
|
|
|
|
return true;
|
|
}
|
|
|
|
// A6.3.17 Long multiply, long multiply accumulate, and divide
|
|
//
|
|
// t2SMULL, t2UMULL, t2SMLAL, t2UMLAL, t2UMAAL: RdLo RdHi Rn Rm
|
|
// where RdLo = Inst{15-12} and RdHi = Inst{11-8}
|
|
//
|
|
// Halfword multiple accumulate long: t2SMLAL<x><y>: RdLo RdHi Rn Rm
|
|
// where RdLo = Inst{15-12} and RdHi = Inst{11-8}
|
|
//
|
|
// Dual halfword multiple: t2SMLALD[X], t2SMLSLD[X]: RdLo RdHi Rn Rm
|
|
// where RdLo = Inst{15-12} and RdHi = Inst{11-8}
|
|
//
|
|
// Signed/Unsigned divide: t2SDIV, t2UDIV: Rs Rn Rm
|
|
static bool DisassembleThumb2LongMul(MCInst &MI, unsigned Opcode, uint32_t insn,
|
|
unsigned short NumOps, unsigned &NumOpsAdded, BO B) {
|
|
|
|
const TargetOperandInfo *OpInfo = ARMInsts[Opcode].OpInfo;
|
|
|
|
assert(NumOps >= 3 &&
|
|
OpInfo[0].RegClass == ARM::GPRRegClassID &&
|
|
OpInfo[1].RegClass == ARM::GPRRegClassID &&
|
|
OpInfo[2].RegClass == ARM::GPRRegClassID &&
|
|
"Expect >= 3 operands and first three as reg operands");
|
|
|
|
bool FourReg = NumOps > 3 && OpInfo[3].RegClass == ARM::GPRRegClassID;
|
|
|
|
// Build the register operands.
|
|
|
|
if (FourReg)
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRd(insn))));
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRs(insn))));
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRn(insn))));
|
|
|
|
MI.addOperand(MCOperand::CreateReg(getRegisterEnum(B, ARM::GPRRegClassID,
|
|
decodeRm(insn))));
|
|
|
|
if (FourReg)
|
|
NumOpsAdded = 4;
|
|
else
|
|
NumOpsAdded = 3;
|
|
|
|
return true;
|
|
}
|
|
|
|
// See A6.3 32-bit Thumb instruction encoding for instruction classes
|
|
// corresponding to (op1, op2, op).
|
|
//
|
|
// Table A6-9 32-bit Thumb instruction encoding
|
|
// op1 op2 op Instruction class, see
|
|
// --- ------- -- ------------------------------------------------------------
|
|
// 01 00xx0xx - Load/store multiple on page A6-23
|
|
// 00xx1xx - Load/store dual, load/store exclusive, table branch on page A6-24
|
|
// 01xxxxx - Data-processing (shifted register) on page A6-31
|
|
// 1xxxxxx - Coprocessor instructions on page A6-40
|
|
// 10 x0xxxxx 0 Data-processing (modified immediate) on page A6-15
|
|
// x1xxxxx 0 Data-processing (plain binary immediate) on page A6-19
|
|
// - 1 Branches and miscellaneous control on page A6-20
|
|
// 11 000xxx0 - Store single data item on page A6-30
|
|
// 001xxx0 - Advanced SIMD element or structure load/store instructions on page A7-27
|
|
// 00xx001 - Load byte, memory hints on page A6-28
|
|
// 00xx011 - Load halfword, memory hints on page A6-26
|
|
// 00xx101 - Load word on page A6-25
|
|
// 00xx111 - UNDEFINED
|
|
// 010xxxx - Data-processing (register) on page A6-33
|
|
// 0110xxx - Multiply, multiply accumulate, and absolute difference on page A6-38
|
|
// 0111xxx - Long multiply, long multiply accumulate, and divide on page A6-39
|
|
// 1xxxxxx - Coprocessor instructions on page A6-40
|
|
//
|
|
static bool DisassembleThumb2(uint16_t op1, uint16_t op2, uint16_t op,
|
|
MCInst &MI, unsigned Opcode, uint32_t insn, unsigned short NumOps,
|
|
unsigned &NumOpsAdded, BO B) {
|
|
|
|
switch (op1) {
|
|
case 1:
|
|
if (slice(op2, 6, 5) == 0) {
|
|
if (slice(op2, 2, 2) == 0) {
|
|
// Load/store multiple.
|
|
return DisassembleThumb2LdStMul(MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
B);
|
|
}
|
|
|
|
// Load/store dual, load/store exclusive, table branch, otherwise.
|
|
assert(slice(op2, 2, 2) == 1 && "Thumb2 encoding error!");
|
|
if ((ARM::t2LDREX <= Opcode && Opcode <= ARM::t2LDREXH) ||
|
|
(ARM::t2STREX <= Opcode && Opcode <= ARM::t2STREXH)) {
|
|
// Load/store exclusive.
|
|
return DisassembleThumb2LdStEx(MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
B);
|
|
}
|
|
if (Opcode == ARM::t2LDRDi8 ||
|
|
Opcode == ARM::t2LDRD_PRE || Opcode == ARM::t2LDRD_POST ||
|
|
Opcode == ARM::t2STRDi8 ||
|
|
Opcode == ARM::t2STRD_PRE || Opcode == ARM::t2STRD_POST) {
|
|
// Load/store dual.
|
|
return DisassembleThumb2LdStDual(MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
B);
|
|
}
|
|
if (Opcode == ARM::t2TBBgen || Opcode == ARM::t2TBHgen) {
|
|
// Table branch.
|
|
return DisassembleThumb2TB(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
}
|
|
} else if (slice(op2, 6, 5) == 1) {
|
|
// Data-processing (shifted register).
|
|
return DisassembleThumb2DPSoReg(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
}
|
|
|
|
// FIXME: A6.3.18 Coprocessor instructions
|
|
// But see ThumbDisassembler::getInstruction().
|
|
|
|
break;
|
|
case 2:
|
|
if (op == 0) {
|
|
if (slice(op2, 5, 5) == 0) {
|
|
// Data-processing (modified immediate)
|
|
return DisassembleThumb2DPModImm(MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
B);
|
|
} else {
|
|
// Data-processing (plain binary immediate)
|
|
return DisassembleThumb2DPBinImm(MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
B);
|
|
}
|
|
} else {
|
|
// Branches and miscellaneous control on page A6-20.
|
|
return DisassembleThumb2BrMiscCtrl(MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
B);
|
|
}
|
|
|
|
break;
|
|
case 3:
|
|
switch (slice(op2, 6, 5)) {
|
|
case 0:
|
|
// Load/store instructions...
|
|
if (slice(op2, 0, 0) == 0) {
|
|
if (slice(op2, 4, 4) == 0) {
|
|
// Store single data item on page A6-30
|
|
return DisassembleThumb2LdSt(false, MI,Opcode,insn,NumOps,NumOpsAdded,
|
|
B);
|
|
} else {
|
|
// FIXME: Advanced SIMD element or structure load/store instructions.
|
|
// But see ThumbDisassembler::getInstruction().
|
|
;
|
|
}
|
|
} else {
|
|
// Table A6-9 32-bit Thumb instruction encoding: Load byte|halfword|word
|
|
return DisassembleThumb2LdSt(true, MI,Opcode,insn,NumOps,NumOpsAdded, B);
|
|
}
|
|
break;
|
|
case 1:
|
|
if (slice(op2, 4, 4) == 0) {
|
|
// A6.3.12 Data-processing (register)
|
|
return DisassembleThumb2DPReg(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
} else if (slice(op2, 3, 3) == 0) {
|
|
// A6.3.16 Multiply, multiply accumulate, and absolute difference
|
|
return DisassembleThumb2Mul(MI, Opcode, insn, NumOps, NumOpsAdded, B);
|
|
} else {
|
|
// A6.3.17 Long multiply, long multiply accumulate, and divide
|
|
return DisassembleThumb2LongMul(MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
B);
|
|
}
|
|
break;
|
|
default:
|
|
// FIXME: A6.3.18 Coprocessor instructions
|
|
// But see ThumbDisassembler::getInstruction().
|
|
;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
default:
|
|
assert(0 && "Thumb2 encoding error!");
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool DisassembleThumbFrm(MCInst &MI, unsigned Opcode, uint32_t insn,
|
|
unsigned short NumOps, unsigned &NumOpsAdded, BO Builder) {
|
|
|
|
uint16_t HalfWord = slice(insn, 31, 16);
|
|
|
|
if (HalfWord == 0) {
|
|
// A6.2 16-bit Thumb instruction encoding
|
|
// op = bits[15:10]
|
|
uint16_t op = slice(insn, 15, 10);
|
|
return DisassembleThumb1(op, MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
Builder);
|
|
}
|
|
|
|
unsigned bits15_11 = slice(HalfWord, 15, 11);
|
|
|
|
// A6.1 Thumb instruction set encoding
|
|
if (!(bits15_11 == 0x1D || bits15_11 == 0x1E || bits15_11 == 0x1F)) {
|
|
assert("Bits[15:11] first halfword of Thumb2 instruction is out of range");
|
|
return false;
|
|
}
|
|
|
|
// A6.3 32-bit Thumb instruction encoding
|
|
|
|
uint16_t op1 = slice(HalfWord, 12, 11);
|
|
uint16_t op2 = slice(HalfWord, 10, 4);
|
|
uint16_t op = slice(insn, 15, 15);
|
|
|
|
return DisassembleThumb2(op1, op2, op, MI, Opcode, insn, NumOps, NumOpsAdded,
|
|
Builder);
|
|
}
|