capstone/arch/AArch64/AArch64InstPrinter.c

829 lines
25 KiB
C

//==-- AArch64InstPrinter.cpp - Convert AArch64 MCInst to assembly syntax --==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This class prints an AArch64 MCInst to a .s file.
//
//===----------------------------------------------------------------------===//
/* Capstone Disassembler Engine */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013> */
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include "AArch64InstPrinter.h"
#include "AArch64BaseInfo.h"
#include "../../MCInst.h"
#include "../../SStream.h"
#include "../../MCRegisterInfo.h"
#include "../../MathExtras.h"
#include "../../utils.h"
#include "mapping.h"
static char *getRegisterName(unsigned RegNo);
static void printOperand(MCInst *MI, unsigned OpNo, SStream *O);
// FIXME: make this status session's specific, not global like this
static bool doing_mem = false;
static void set_mem_access(MCInst *MI, bool status)
{
if (MI->detail != CS_OPT_ON)
return;
doing_mem = status;
if (doing_mem) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_MEM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].mem.base = ARM64_REG_INVALID;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].mem.index = ARM64_REG_INVALID;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].mem.disp = 0;
} else {
// done, create the next operand slot
MI->pub_insn.arm64.op_count++;
}
}
static int64_t unpackSignedImm(int BitWidth, uint64_t Value)
{
//assert(!(Value & ~((1ULL << BitWidth)-1)) && "immediate not n-bit");
if (Value & (1ULL << (BitWidth - 1)))
return (int64_t)Value - (1LL << BitWidth);
else
return Value;
}
static void printOffsetSImm9Operand(MCInst *MI, unsigned OpNum, SStream *O)
{
MCOperand *MOImm = MCInst_getOperand(MI, OpNum);
int32_t Imm = unpackSignedImm(9, MCOperand_getImm(MOImm));
if (Imm > HEX_THRESHOLD)
SStream_concat(O, "#0x%x", Imm);
else
SStream_concat(O, "#%u", Imm);
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = Imm;
MI->pub_insn.arm64.op_count++;
}
}
static void printAddrRegExtendOperand(MCInst *MI, unsigned OpNum,
SStream *O, unsigned MemSize, unsigned RmSize)
{
unsigned ExtImm = MCOperand_getImm(MCInst_getOperand(MI, OpNum));
unsigned OptionHi = ExtImm >> 1;
unsigned S = ExtImm & 1;
bool IsLSL = OptionHi == 1 && RmSize == 64;
char *Ext = 0;
switch (OptionHi) {
case 1:
if (RmSize == 32) {
Ext = "uxtw";
if (MI->detail)
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].ext = ARM64_EXT_UXTW;
} else {
Ext = "lsl";
}
break;
case 3:
if (RmSize == 32) {
Ext = "sxtw";
if (MI->detail)
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].ext = ARM64_EXT_SXTW;
} else {
Ext = "sxtx";
if (MI->detail)
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].ext = ARM64_EXT_SXTX;
}
break;
default:
break; //llvm_unreachable("Incorrect Option on load/store (reg offset)");
}
SStream_concat(O, Ext);
if (S) {
unsigned ShiftAmt = Log2_32(MemSize);
if (ShiftAmt > HEX_THRESHOLD)
SStream_concat(O, " #0x%x", ShiftAmt);
else
SStream_concat(O, " #%u", ShiftAmt);
if (MI->detail) {
if (doing_mem) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].shift.type = ARM64_SFT_LSL;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].shift.value = ShiftAmt;
} else {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].shift.type = ARM64_SFT_LSL;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].shift.value = ShiftAmt;
}
}
} else if (IsLSL) {
SStream_concat(O, " #0");
}
}
static void printAddSubImmLSL0Operand(MCInst *MI, unsigned OpNum, SStream *O)
{
MCOperand *Imm12Op = MCInst_getOperand(MI, OpNum);
if (MCOperand_isImm(Imm12Op)) {
int64_t Imm12 = MCOperand_getImm(Imm12Op);
//assert(Imm12 >= 0 && "Invalid immediate for add/sub imm");
if (Imm12 > HEX_THRESHOLD)
SStream_concat(O, "#0x%"PRIx64, Imm12);
else
SStream_concat(O, "#%u"PRIu64, Imm12);
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = Imm12;
MI->pub_insn.arm64.op_count++;
}
}
}
static void printAddSubImmLSL12Operand(MCInst *MI, unsigned OpNum, SStream *O)
{
printAddSubImmLSL0Operand(MI, OpNum, O);
SStream_concat(O, ", lsl #12");
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].shift.type = ARM64_SFT_LSL;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].shift.value = 12;
}
}
static void printBareImmOperand(MCInst *MI, unsigned OpNum, SStream *O)
{
MCOperand *MO = MCInst_getOperand(MI, OpNum);
uint64_t imm = MCOperand_getImm(MO);
if (imm > HEX_THRESHOLD)
SStream_concat(O, "0x%"PRIx64, imm);
else
SStream_concat(O, "%"PRIu64, imm);
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = imm;
MI->pub_insn.arm64.op_count++;
}
}
static void printBFILSBOperand(MCInst *MI, unsigned OpNum,
SStream *O, unsigned RegWidth)
{
MCOperand *ImmROp = MCInst_getOperand(MI, OpNum);
unsigned LSB = MCOperand_getImm(ImmROp) == 0 ? 0 : RegWidth - MCOperand_getImm(ImmROp);
if (LSB > HEX_THRESHOLD)
SStream_concat(O, "#0x%x", LSB);
else
SStream_concat(O, "#%u", LSB);
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = LSB;
MI->pub_insn.arm64.op_count++;
}
}
static void printBFIWidthOperand(MCInst *MI, unsigned OpNum, SStream *O)
{
MCOperand *ImmSOp = MCInst_getOperand(MI, OpNum);
unsigned Width = MCOperand_getImm(ImmSOp) + 1;
if (Width > HEX_THRESHOLD)
SStream_concat(O, "#0x%x", Width);
else
SStream_concat(O, "#%u", Width);
}
static void printBFXWidthOperand(MCInst *MI, unsigned OpNum, SStream *O)
{
MCOperand *ImmSOp = MCInst_getOperand(MI, OpNum);
MCOperand *ImmROp = MCInst_getOperand(MI, OpNum - 1);
unsigned ImmR = MCOperand_getImm(ImmROp);
unsigned ImmS = MCOperand_getImm(ImmSOp);
//assert(ImmS >= ImmR && "Invalid ImmR, ImmS combination for bitfield extract");
if (ImmS - ImmR + 1 > HEX_THRESHOLD)
SStream_concat(O, "#0x%x", (ImmS - ImmR + 1));
else
SStream_concat(O, "#%u", (ImmS - ImmR + 1));
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = ImmS - ImmR + 1;
MI->pub_insn.arm64.op_count++;
}
}
static void printCRxOperand(MCInst *MI, unsigned OpNum, SStream *O)
{
MCOperand *CRx = MCInst_getOperand(MI, OpNum);
SStream_concat(O, "c%"PRIu64, MCOperand_getImm(CRx));
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_CIMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = MCOperand_getImm(CRx);
MI->pub_insn.arm64.op_count++;
}
}
static void printCVTFixedPosOperand(MCInst *MI, unsigned OpNum, SStream *O)
{
MCOperand *ScaleOp = MCInst_getOperand(MI, OpNum);
if (64 - MCOperand_getImm(ScaleOp) > HEX_THRESHOLD)
SStream_concat(O, "#0x%x", 64 - MCOperand_getImm(ScaleOp));
else
SStream_concat(O, "#%u", 64 - MCOperand_getImm(ScaleOp));
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = 64 - MCOperand_getImm(ScaleOp);
MI->pub_insn.arm64.op_count++;
}
}
static void printFPImmOperand(MCInst *MI, unsigned OpNum, SStream *O)
{
MCOperand *MOImm8 = MCInst_getOperand(MI, OpNum);
//assert(MOImm8.isImm()
// && "Immediate operand required for floating-point immediate inst");
uint32_t Imm8 = MCOperand_getImm(MOImm8);
uint32_t Fraction = Imm8 & 0xf;
uint32_t Exponent = (Imm8 >> 4) & 0x7;
uint32_t Negative = (Imm8 >> 7) & 0x1;
float Val = 1.0f + Fraction / 16.0f;
// That is:
// 000 -> 2^1, 001 -> 2^2, 010 -> 2^3, 011 -> 2^4,
// 100 -> 2^-3, 101 -> 2^-2, 110 -> 2^-1, 111 -> 2^0
if (Exponent & 0x4) {
Val /= 1 << (7 - Exponent);
} else {
Val *= 1 << (Exponent + 1);
}
Val = Negative ? -Val : Val;
//o << '#' << format("%.8f", Val);
SStream_concat(O, "#%.8f", Val);
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_FP;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].fp = Val;
MI->pub_insn.arm64.op_count++;
}
}
static void printFPZeroOperand(MCInst *MI, unsigned OpNum, SStream *O)
{
SStream_concat(O, "#0.0");
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_FP;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].fp = 0;
MI->pub_insn.arm64.op_count++;
}
}
static void printCondCodeOperand(MCInst *MI, unsigned OpNum, SStream *O)
{
MCOperand *MO = MCInst_getOperand(MI, OpNum);
SStream_concat(O, A64CondCodeToString((A64CC_CondCodes)(MCOperand_getImm(MO))));
if (MI->detail)
MI->pub_insn.arm64.cc = MCOperand_getImm(MO) + 1;
}
static void printLabelOperand(MCInst *MI, unsigned OpNum,
SStream *O, unsigned field_width, unsigned scale)
{
MCOperand *MO = MCInst_getOperand(MI, OpNum);
if (!MCOperand_isImm(MO)) {
printOperand(MI, OpNum, O);
return;
}
// The immediate of LDR (lit) instructions is a signed 19-bit immediate, which
// is multiplied by 4 (because all A64 instructions are 32-bits wide).
uint64_t UImm = MCOperand_getImm(MO);
uint64_t Sign = UImm & (1LL << (field_width - 1));
int64_t SImm = scale * ((UImm & ~Sign) - Sign);
// this is a relative address, so add with the address
// of current instruction
SImm += MI->address;
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = SImm;
MI->pub_insn.arm64.op_count++;
}
if (SImm > HEX_THRESHOLD)
SStream_concat(O, "#0x%"PRIx64, SImm);
else
SStream_concat(O, "#%"PRIu64, SImm);
}
static void printLogicalImmOperand(MCInst *MI, unsigned OpNum,
SStream *O, unsigned RegWidth)
{
MCOperand *MO = MCInst_getOperand(MI, OpNum);
uint64_t Val;
A64Imms_isLogicalImmBits(RegWidth, MCOperand_getImm(MO), &Val);
if (Val > HEX_THRESHOLD)
SStream_concat(O, "#0x%"PRIx64, Val);
else
SStream_concat(O, "#%"PRIu64, Val);
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = Val;
MI->pub_insn.arm64.op_count++;
}
}
static void printOffsetUImm12Operand(MCInst *MI, unsigned OpNum,
SStream *O, int MemSize)
{
MCOperand *MOImm = MCInst_getOperand(MI, OpNum);
if (MCOperand_isImm(MOImm)) {
uint32_t Imm = MCOperand_getImm(MOImm) * MemSize;
if (Imm > HEX_THRESHOLD)
SStream_concat(O, "#0x%x", Imm);
else
SStream_concat(O, "#%u", Imm);
if (MI->detail) {
if (doing_mem) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].mem.disp = Imm;
} else {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = Imm;
MI->pub_insn.arm64.op_count++;
}
}
}
}
static void printShiftOperand(MCInst *MI, unsigned OpNum,
SStream *O, A64SE_ShiftExtSpecifiers Shift)
{
MCOperand *MO = MCInst_getOperand(MI, OpNum);
// LSL #0 is not printed
if (Shift == A64SE_LSL && MCOperand_isImm(MO) && MCOperand_getImm(MO) == 0)
return;
switch (Shift) {
case A64SE_LSL: SStream_concat(O, "lsl"); break;
case A64SE_LSR: SStream_concat(O, "lsr"); break;
case A64SE_ASR: SStream_concat(O, "asr"); break;
case A64SE_ROR: SStream_concat(O, "ror"); break;
default: break; // llvm_unreachable("Invalid shift specifier in logical instruction");
}
unsigned int imm = MCOperand_getImm(MO);
if (imm > HEX_THRESHOLD)
SStream_concat(O, " #0x%x", imm);
else
SStream_concat(O, " #%u", imm);
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].shift.type = Shift + 1;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].shift.value = imm;
}
}
static void printMoveWideImmOperand(MCInst *MI, unsigned OpNum, SStream *O)
{
MCOperand *UImm16MO = MCInst_getOperand(MI, OpNum);
MCOperand *ShiftMO = MCInst_getOperand(MI, OpNum + 1);
if (MCOperand_isImm(UImm16MO)) {
uint64_t imm = MCOperand_getImm(UImm16MO);
if (imm > HEX_THRESHOLD)
SStream_concat(O, "#0x%"PRIx64, imm);
else
SStream_concat(O, "#%"PRIu64, imm);
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = imm;
MI->pub_insn.arm64.op_count++;
}
if (MCOperand_getImm(ShiftMO) != 0) {
unsigned int shift = MCOperand_getImm(ShiftMO) * 16;
if (shift > HEX_THRESHOLD)
SStream_concat(O, ", lsl #0x%x", shift);
else
SStream_concat(O, ", lsl #%u", shift);
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].shift.type = ARM64_SFT_LSL;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].shift.value = shift;
}
}
return;
}
}
static void printNamedImmOperand(NamedImmMapper *Mapper,
MCInst *MI, unsigned OpNum, SStream *O)
{
bool ValidName;
MCOperand *MO = MCInst_getOperand(MI, OpNum);
char *Name = NamedImmMapper_toString(Mapper, MCOperand_getImm(MO), &ValidName);
if (ValidName)
SStream_concat(O, Name);
else {
uint64_t imm = MCOperand_getImm(MO);
if (imm > HEX_THRESHOLD)
SStream_concat(O, "#0x%"PRIx64, imm);
else
SStream_concat(O, "#%"PRIu64, imm);
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = imm;
MI->pub_insn.arm64.op_count++;
}
}
}
static void printSysRegOperand(SysRegMapper *Mapper,
MCInst *MI, unsigned OpNum, SStream *O)
{
bool ValidName;
char Name[128];
MCOperand *MO = MCInst_getOperand(MI, OpNum);
SysRegMapper_toString(Mapper, MCOperand_getImm(MO), &ValidName, Name);
if (ValidName) {
SStream_concat(O, Name);
}
}
#define GET_REGINFO_ENUM
#include "AArch64GenRegisterInfo.inc"
static inline bool isStackReg(unsigned RegNo)
{
return RegNo == AArch64_XSP || RegNo == AArch64_WSP;
}
static void printRegExtendOperand(MCInst *MI, unsigned OpNum, SStream *O,
A64SE_ShiftExtSpecifiers Ext)
{
// FIXME: In principle TableGen should be able to detect this itself far more
// easily. We will only accumulate more of these hacks.
unsigned Reg0 = MCOperand_getReg(MCInst_getOperand(MI, 0));
unsigned Reg1 = MCOperand_getReg(MCInst_getOperand(MI, 1));
if (isStackReg(Reg0) || isStackReg(Reg1)) {
A64SE_ShiftExtSpecifiers LSLEquiv;
if (Reg0 == AArch64_XSP || Reg1 == AArch64_XSP)
LSLEquiv = A64SE_UXTX;
else
LSLEquiv = A64SE_UXTW;
if (Ext == LSLEquiv) {
unsigned int shift = MCOperand_getImm(MCInst_getOperand(MI, OpNum));
if (shift > HEX_THRESHOLD)
SStream_concat(O, "lsl #0x%x", shift);
else
SStream_concat(O, "lsl #%u", shift);
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].shift.type = ARM64_SFT_LSL;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].shift.value = shift;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].ext = Ext - 4;
}
return;
}
}
switch (Ext) {
case A64SE_UXTB: SStream_concat(O, "uxtb"); break;
case A64SE_UXTH: SStream_concat(O, "uxth"); break;
case A64SE_UXTW: SStream_concat(O, "uxtw"); break;
case A64SE_UXTX: SStream_concat(O, "uxtx"); break;
case A64SE_SXTB: SStream_concat(O, "sxtb"); break;
case A64SE_SXTH: SStream_concat(O, "sxth"); break;
case A64SE_SXTW: SStream_concat(O, "sxtw"); break;
case A64SE_SXTX: SStream_concat(O, "sxtx"); break;
default: break; //llvm_unreachable("Unexpected shift type for printing");
}
if (MI->detail)
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].ext = Ext - 4;
MCOperand *MO = MCInst_getOperand(MI, OpNum);
if (MCOperand_getImm(MO) != 0) {
unsigned int shift = MCOperand_getImm(MO);
if (shift > HEX_THRESHOLD)
SStream_concat(O, " #0x%x", shift);
else
SStream_concat(O, " #%u", shift);
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].shift.type = ARM64_SFT_LSL;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].shift.value = shift;
}
}
}
static void printSImm7ScaledOperand(MCInst *MI, unsigned OpNum,
SStream *O, int MemScale)
{
MCOperand *MOImm = MCInst_getOperand(MI, OpNum);
int32_t Imm = unpackSignedImm(7, MCOperand_getImm(MOImm));
if (Imm * MemScale > HEX_THRESHOLD)
SStream_concat(O, "#0x%x", Imm * MemScale);
else
SStream_concat(O, "#%u", Imm * MemScale);
if (MI->detail) {
if (doing_mem) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].mem.disp = Imm * MemScale;
} else {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = Imm * MemScale;
MI->pub_insn.arm64.op_count++;
}
}
}
// TODO: handle this Vd register??
static void printVPRRegister(MCInst *MI, unsigned OpNo, SStream *O)
{
unsigned Reg = MCOperand_getReg(MCInst_getOperand(MI, OpNo));
char *Name = strdup(getRegisterName(Reg));
Name[0] = 'v';
SStream_concat(O, "%s", Name);
free(Name);
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_REG;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].reg = Reg;
MI->pub_insn.arm64.op_count++;
}
}
static void printOperand(MCInst *MI, unsigned OpNo, SStream *O)
{
MCOperand *Op = MCInst_getOperand(MI, OpNo);
if (MCOperand_isReg(Op)) {
unsigned Reg = MCOperand_getReg(Op);
SStream_concat(O, getRegisterName(Reg));
if (MI->detail) {
if (doing_mem) {
if (MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].mem.base == ARM64_REG_INVALID) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].mem.base = Reg;
} else {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].mem.index = Reg;
}
} else {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_REG;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].reg = Reg;
MI->pub_insn.arm64.op_count++;
}
}
} else if (MCOperand_isImm(Op)) {
int64_t imm = MCOperand_getImm(Op);
if (imm > HEX_THRESHOLD)
SStream_concat(O, "#0x%"PRIx64, imm);
else
SStream_concat(O, "#%"PRIu64, imm);
if (MI->detail) {
if (doing_mem) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].mem.disp = imm;
} else {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = imm;
MI->pub_insn.arm64.op_count++;
}
}
}
}
#define GET_INSTRINFO_ENUM
#include "AArch64GenInstrInfo.inc"
static void printNeonMovImmShiftOperand(MCInst *MI, unsigned OpNum,
SStream *O, A64SE_ShiftExtSpecifiers Ext, bool isHalf)
{
MCOperand *MO = MCInst_getOperand(MI, OpNum);
//assert(MO.isImm() &&
// "Immediate operand required for Neon vector immediate inst.");
bool IsLSL = false;
if (Ext == A64SE_LSL)
IsLSL = true;
else if (Ext != A64SE_MSL) {
//llvm_unreachable("Invalid shift specifier in movi instruction");
}
int64_t Imm = MCOperand_getImm(MO);
// MSL and LSLH accepts encoded shift amount 0 or 1.
if ((!IsLSL || (IsLSL && isHalf)) && Imm != 0 && Imm != 1) {
// llvm_unreachable("Invalid shift amount in movi instruction");
}
// LSH accepts encoded shift amount 0, 1, 2 or 3.
if (IsLSL && (Imm < 0 || Imm > 3)) {
//llvm_unreachable("Invalid shift amount in movi instruction");
}
// Print shift amount as multiple of 8 with MSL encoded shift amount
// 0 and 1 printed as 8 and 16.
if (!IsLSL)
Imm++;
Imm *= 8;
// LSL #0 is not printed
if (IsLSL) {
if (Imm == 0)
return;
SStream_concat(O, ", lsl");
if (MI->detail)
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].shift.type = ARM64_SFT_LSL;
} else {
SStream_concat(O, ", msl");
if (MI->detail)
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].shift.type = ARM64_SFT_MSL;
}
if (Imm > HEX_THRESHOLD)
SStream_concat(O, " #0x%"PRIx64, Imm);
else
SStream_concat(O, " #%"PRIu64, Imm);
if (MI->detail)
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count - 1].shift.value = Imm;
}
static void printNeonUImm0Operand(MCInst *MI, unsigned OpNum, SStream *O)
{
SStream_concat(O, "#0");
// FIXME: vector ZERO
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = 0;
MI->pub_insn.arm64.op_count++;
}
}
static void printUImmHexOperand(MCInst *MI, unsigned OpNum, SStream *O)
{
MCOperand *MOUImm = MCInst_getOperand(MI, OpNum);
//assert(MOUImm.isImm() &&
// "Immediate operand required for Neon vector immediate inst.");
unsigned Imm = MCOperand_getImm(MOUImm);
if (Imm > HEX_THRESHOLD)
SStream_concat(O, "#0x%x", Imm);
else
SStream_concat(O, "#%u", Imm);
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = Imm;
MI->pub_insn.arm64.op_count++;
}
}
static void printUImmBareOperand(MCInst *MI, unsigned OpNum, SStream *O)
{
MCOperand *MOUImm = MCInst_getOperand(MI, OpNum);
//assert(MOUImm.isImm()
// && "Immediate operand required for Neon vector immediate inst.");
unsigned Imm = MCOperand_getImm(MOUImm);
if (Imm > HEX_THRESHOLD)
SStream_concat(O, "0x%x", Imm);
else
SStream_concat(O, "%u", Imm);
if (MI->detail) {
if (doing_mem) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].mem.disp = Imm;
} else {
// FIXME: never has false branch??
}
}
}
static void printNeonUImm64MaskOperand(MCInst *MI, unsigned OpNum, SStream *O)
{
MCOperand *MOUImm8 = MCInst_getOperand(MI, OpNum);
//assert(MOUImm8.isImm() &&
// "Immediate operand required for Neon vector immediate bytemask inst.");
uint32_t UImm8 = MCOperand_getImm(MOUImm8);
uint64_t Mask = 0;
// Replicates 0x00 or 0xff byte in a 64-bit vector
unsigned ByteNum;
for (ByteNum = 0; ByteNum < 8; ++ByteNum) {
if ((UImm8 >> ByteNum) & 1)
Mask |= (uint64_t)0xff << (8 * ByteNum);
}
if (Mask > HEX_THRESHOLD)
SStream_concat(O, "#0x%"PRIx64, Mask);
else
SStream_concat(O, "#%"PRIu64, Mask);
if (MI->detail) {
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].type = ARM64_OP_IMM;
MI->pub_insn.arm64.operands[MI->pub_insn.arm64.op_count].imm = Mask;
MI->pub_insn.arm64.op_count++;
}
}
static void printMRSOperand(MCInst *MI, unsigned OpNum, SStream *O)
{
printSysRegOperand(&AArch64_MRSMapper, MI, OpNum, O);
}
static void printMSROperand(MCInst *MI, unsigned OpNum, SStream *O)
{
printSysRegOperand(&AArch64_MSRMapper, MI, OpNum, O);
}
// If Count > 1, there are two valid kinds of vector list:
// (1) {Vn.layout, Vn+1.layout, ... , Vm.layout}
// (2) {Vn.layout - Vm.layout}
// We choose the first kind as output.
static void printVectorList(MCInst *MI, unsigned OpNum,
SStream *O, MCRegisterInfo *MRI, A64Layout_VectorLayout Layout, unsigned Count)
{
//assert(Count >= 1 && Count <= 4 && "Invalid Number of Vectors");
unsigned Reg = MCOperand_getReg(MCInst_getOperand(MI, OpNum));
const char *LayoutStr = A64VectorLayoutToString(Layout);
SStream_concat(O, "{");
if (Count > 1) { // Print sub registers separately
bool IsVec64 = (Layout < A64Layout_VL_16B);
unsigned SubRegIdx = IsVec64 ? AArch64_dsub_0 : AArch64_qsub_0;
unsigned I;
for (I = 0; I < Count; I++) {
char *Name = strdup(getRegisterName(MCRegisterInfo_getSubReg(MRI, Reg, SubRegIdx++)));
Name[0] = 'v';
SStream_concat(O, "%s%s", Name, LayoutStr);
if (I != Count - 1)
SStream_concat(O, ", ");
free(Name);
}
} else { // Print the register directly when NumVecs is 1.
char *Name = strdup(getRegisterName(Reg));
Name[0] = 'v';
SStream_concat(O, "%s%s", Name, LayoutStr);
free(Name);
}
SStream_concat(O, "}");
}
#define PRINT_ALIAS_INSTR
#include "AArch64GenAsmWriter.inc"
void AArch64_post_printer(csh handle, cs_insn *pub_insn, char *insn_asm)
{
// check if this insn requests write-back
if (strrchr(insn_asm, '!') != NULL)
pub_insn->arm64.writeback = true;
}
void AArch64_printInst(MCInst *MI, SStream *O, void *Info)
{
if (printAliasInstr(MI, O, Info)) {
char *mnem = strdup(O->buffer);
char *tab = strchr(mnem, '\t');
if (tab) {
*tab = '\0';
}
// reflect the new insn name (alias) in the opcode
unsigned int id = AArch64_map_insn(mnem);
MCInst_setOpcode(MI, AArch64_get_insn_id2(id));
MCInst_setOpcodePub(MI, id);
free(mnem);
} else
AArch64InstPrinter_printInstruction(MI, O, Info);
}