00cc9309cb
de6e324bdseparate emu thread10d3daf86Roms List improvements95d202f37Let's make the rom list process on a separate thread so the emulator doesnt take ages to load.fc306967fWow the ROM Header was just completely busted. Game list view works nowbad1691eefuck this shit2b59e5f46game list in progressd26417b83remappable inputs in progressac4af8106inpute72abc240update readme430139dc9Qt6 frontend3080d4d45Fix this small bug too08cd13b85Cop0 unused functions do not actually pose a threat (as per manual). They don't do anything, so shall we.61bb4fb44make idle loop detection a little more specific with where the load goesb037de4c3SAZDFsdff12e81e73eneed to figure out why n64-systemtest loops indefinitely at some address that appears to be valid (i think it's me not invalidating the cache properly)204f0e13bidle skipping seems to work!cb8bb634asdkfjlasdf58e5c89c1Fix compilation issue on my machine (no idea)24fb2898eattempting more serious idle skipping214719577Place rsp.Step inside cached interpreter. Gains about 3 more fpsbb97dcc23mmmmm920b77d38wjkhasdfjhkasdf430ccdab4it's a start...4f42a673aCached interpreter plays Mario 64. Start looking into RSP as wellc9a030787idle skipping works!5fbda03cenew idea366637abaIdle skipping... maybe?609fa2fb0Cache instructions implemented but broken lmao. Commented out for nowe140a6d12- Stop using inheritance for CPU, instead use composition. - Introduce KAIZEN_JIT_ENABLED optional define instead of relying on __aarch64__ and the like. - More cache work68e613057prep cache impl811b4d809fix clang formatfda755f7didkd5024ebbfsmall MI refactor in preparation of (eventually) implementing the RDRAM interface properly694b45341Merge commit '206dcdedf195fb320913584180edb12c7731e396' as 'external/SDL'206dcdedfSquashed 'external/SDL/' content from commit 4d17b99d0a4d16e1cb4need to update sdl848b19920Fix compilation errordb61b5299Merge commit 'e94a94559f28e49678fbcf72199a5258137b0fe9' as 'external/imgui'e94a94559Squashed 'external/imgui/' content from commit 02e9b8cac52edb3757need to update imguic1a705e86Emulate weird JALR behaviour4b4c32f4bFix exception for "unusable COP1" in 4 instructions i missed accidentally (again)df5828142Bug putting 0s in the log everywheref8b580048Make isviewer a sink to file8241e9735Fix exception for "unusable COP1" in 4 instructions i missed accidentallyb29715f20small changesd9a620bc1make use of my new small utility library0d1aa938eAdd 'external/ircolib/' from commit 'ce3cd726c8df8388d554abf8bb55d55020eb4450'e64eb40b3Fuck git git-subtree-dir: external/ircolib git-subtree-split:de6e324bde
2590 lines
73 KiB
C
2590 lines
73 KiB
C
/* Capstone Disassembly Engine, http://www.capstone-engine.org */
|
|
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2022, */
|
|
/* Rot127 <unisono@quyllur.org> 2022-2023 */
|
|
/* Automatically translated source file from LLVM. */
|
|
|
|
/* LLVM-commit: <commit> */
|
|
/* LLVM-tag: <tag> */
|
|
|
|
/* Only small edits allowed. */
|
|
/* For multiple similar edits, please create a Patch for the translator. */
|
|
|
|
/* Capstone's C++ file translator: */
|
|
/* https://github.com/capstone-engine/capstone/tree/next/suite/auto-sync */
|
|
|
|
//==-- AArch64InstPrinter.cpp - Convert AArch64 MCInst to assembly syntax --==//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This class prints an AArch64 MCInst to a .s file.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <capstone/platform.h>
|
|
|
|
#include "../../Mapping.h"
|
|
#include "../../MCInst.h"
|
|
#include "../../MCInstPrinter.h"
|
|
#include "../../MCRegisterInfo.h"
|
|
#include "../../SStream.h"
|
|
#include "../../utils.h"
|
|
#include "AArch64AddressingModes.h"
|
|
#include "AArch64BaseInfo.h"
|
|
#include "AArch64DisassemblerExtension.h"
|
|
#include "AArch64InstPrinter.h"
|
|
#include "AArch64Linkage.h"
|
|
#include "AArch64Mapping.h"
|
|
|
|
#define GET_BANKEDREG_IMPL
|
|
#include "AArch64GenSystemOperands.inc"
|
|
|
|
#define CONCAT(a, b) CONCAT_(a, b)
|
|
#define CONCAT_(a, b) a##_##b
|
|
|
|
#define CONCATs(a, b) CONCATS(a, b)
|
|
#define CONCATS(a, b) a##b
|
|
|
|
#define DEBUG_TYPE "asm-printer"
|
|
|
|
// BEGIN Static declarations.
|
|
// These functions must be declared statically here, because they
|
|
// are also defined in the ARM module.
|
|
// If they are not static, we fail during linking.
|
|
|
|
static void printCustomAliasOperand(MCInst *MI, uint64_t Address,
|
|
unsigned OpIdx, unsigned PrintMethodIdx,
|
|
SStream *OS);
|
|
|
|
static void printFPImmOperand(MCInst *MI, unsigned OpNum, SStream *O);
|
|
|
|
#define DECLARE_printComplexRotationOp(Angle, Remainder) \
|
|
static void CONCAT(printComplexRotationOp, CONCAT(Angle, Remainder))( \
|
|
MCInst * MI, unsigned OpNo, SStream *O);
|
|
DECLARE_printComplexRotationOp(180, 90);
|
|
DECLARE_printComplexRotationOp(90, 0);
|
|
|
|
// END Static declarations.
|
|
|
|
#define GET_INSTRUCTION_NAME
|
|
#define PRINT_ALIAS_INSTR
|
|
#include "AArch64GenAsmWriter.inc"
|
|
|
|
void printRegName(SStream *OS, unsigned Reg)
|
|
{
|
|
SStream_concat(OS, "%s%s", markup("<reg:"),
|
|
getRegisterName(Reg, AArch64_NoRegAltName));
|
|
SStream_concat0(OS, markup(">"));
|
|
}
|
|
|
|
void printRegNameAlt(SStream *OS, unsigned Reg, unsigned AltIdx)
|
|
{
|
|
SStream_concat(OS, "%s%s", markup("<reg:"),
|
|
getRegisterName(Reg, AltIdx));
|
|
SStream_concat0(OS, markup(">"));
|
|
}
|
|
|
|
const char *getRegName(unsigned Reg)
|
|
{
|
|
return getRegisterName(Reg, AArch64_NoRegAltName);
|
|
}
|
|
|
|
void printInst(MCInst *MI, uint64_t Address, const char *Annot, SStream *O)
|
|
{
|
|
bool isAlias = false;
|
|
bool useAliasDetails = map_use_alias_details(MI);
|
|
map_set_fill_detail_ops(MI, useAliasDetails);
|
|
|
|
unsigned Opcode = MCInst_getOpcode(MI);
|
|
|
|
if (Opcode == AArch64_SYSxt) {
|
|
if (printSysAlias(MI, O)) {
|
|
isAlias = true;
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
if (useAliasDetails)
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (Opcode == AArch64_SYSPxt || Opcode == AArch64_SYSPxt_XZR) {
|
|
if (printSyspAlias(MI, O)) {
|
|
isAlias = true;
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
if (useAliasDetails)
|
|
return;
|
|
}
|
|
}
|
|
|
|
// RPRFM overlaps PRFM (reg), so try to print it as RPRFM here.
|
|
if ((Opcode == AArch64_PRFMroX) || (Opcode == AArch64_PRFMroW)) {
|
|
if (printRangePrefetchAlias(MI, O, Annot)) {
|
|
isAlias = true;
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
if (useAliasDetails)
|
|
return;
|
|
}
|
|
}
|
|
|
|
// SBFM/UBFM should print to a nicer aliased form if possible.
|
|
if (Opcode == AArch64_SBFMXri || Opcode == AArch64_SBFMWri ||
|
|
Opcode == AArch64_UBFMXri || Opcode == AArch64_UBFMWri) {
|
|
MCOperand *Op0 = MCInst_getOperand(MI, (0));
|
|
MCOperand *Op1 = MCInst_getOperand(MI, (1));
|
|
MCOperand *Op2 = MCInst_getOperand(MI, (2));
|
|
MCOperand *Op3 = MCInst_getOperand(MI, (3));
|
|
|
|
bool IsSigned = (Opcode == AArch64_SBFMXri ||
|
|
Opcode == AArch64_SBFMWri);
|
|
bool Is64Bit = (Opcode == AArch64_SBFMXri ||
|
|
Opcode == AArch64_UBFMXri);
|
|
if (MCOperand_isImm(Op2) && MCOperand_getImm(Op2) == 0 &&
|
|
MCOperand_isImm(Op3)) {
|
|
const char *AsmMnemonic = NULL;
|
|
|
|
switch (MCOperand_getImm(Op3)) {
|
|
default:
|
|
break;
|
|
case 7:
|
|
if (IsSigned)
|
|
AsmMnemonic = "sxtb";
|
|
else if (!Is64Bit)
|
|
AsmMnemonic = "uxtb";
|
|
break;
|
|
case 15:
|
|
if (IsSigned)
|
|
AsmMnemonic = "sxth";
|
|
else if (!Is64Bit)
|
|
AsmMnemonic = "uxth";
|
|
break;
|
|
case 31:
|
|
// *xtw is only valid for signed 64-bit operations.
|
|
if (Is64Bit && IsSigned)
|
|
AsmMnemonic = "sxtw";
|
|
break;
|
|
}
|
|
|
|
if (AsmMnemonic) {
|
|
SStream_concat(O, "%s", AsmMnemonic);
|
|
SStream_concat0(O, " ");
|
|
|
|
printRegName(O, MCOperand_getReg(Op0));
|
|
SStream_concat0(O, ", ");
|
|
printRegName(O, getWRegFromXReg(
|
|
MCOperand_getReg(Op1)));
|
|
if (detail_is_set(MI) && useAliasDetails) {
|
|
AArch64_set_detail_op_reg(
|
|
MI, 0, MCOperand_getReg(Op0));
|
|
AArch64_set_detail_op_reg(
|
|
MI, 1,
|
|
getWRegFromXReg(
|
|
MCOperand_getReg(Op1)));
|
|
if (strings_match(AsmMnemonic, "uxtb"))
|
|
AArch64_get_detail_op(MI, -1)
|
|
->ext =
|
|
AARCH64_EXT_UXTB;
|
|
else if (strings_match(AsmMnemonic,
|
|
"sxtb"))
|
|
AArch64_get_detail_op(MI, -1)
|
|
->ext =
|
|
AARCH64_EXT_SXTB;
|
|
else if (strings_match(AsmMnemonic,
|
|
"uxth"))
|
|
AArch64_get_detail_op(MI, -1)
|
|
->ext =
|
|
AARCH64_EXT_UXTH;
|
|
else if (strings_match(AsmMnemonic,
|
|
"sxth"))
|
|
AArch64_get_detail_op(MI, -1)
|
|
->ext =
|
|
AARCH64_EXT_SXTH;
|
|
else if (strings_match(AsmMnemonic,
|
|
"sxtw"))
|
|
AArch64_get_detail_op(MI, -1)
|
|
->ext =
|
|
AARCH64_EXT_SXTW;
|
|
else
|
|
AArch64_get_detail_op(MI, -1)
|
|
->ext =
|
|
AARCH64_EXT_INVALID;
|
|
}
|
|
isAlias = true;
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
if (useAliasDetails)
|
|
return;
|
|
else
|
|
goto add_real_detail;
|
|
}
|
|
}
|
|
|
|
// All immediate shifts are aliases, implemented using the Bitfield
|
|
// instruction. In all cases the immediate shift amount shift must be in
|
|
// the range 0 to (reg.size -1).
|
|
if (MCOperand_isImm(Op2) && MCOperand_isImm(Op3)) {
|
|
const char *AsmMnemonic = NULL;
|
|
int shift = 0;
|
|
int64_t immr = MCOperand_getImm(Op2);
|
|
int64_t imms = MCOperand_getImm(Op3);
|
|
if (Opcode == AArch64_UBFMWri && imms != 0x1F &&
|
|
((imms + 1) == immr)) {
|
|
AsmMnemonic = "lsl";
|
|
shift = 31 - imms;
|
|
} else if (Opcode == AArch64_UBFMXri && imms != 0x3f &&
|
|
((imms + 1 == immr))) {
|
|
AsmMnemonic = "lsl";
|
|
shift = 63 - imms;
|
|
} else if (Opcode == AArch64_UBFMWri && imms == 0x1f) {
|
|
AsmMnemonic = "lsr";
|
|
shift = immr;
|
|
} else if (Opcode == AArch64_UBFMXri && imms == 0x3f) {
|
|
AsmMnemonic = "lsr";
|
|
shift = immr;
|
|
} else if (Opcode == AArch64_SBFMWri && imms == 0x1f) {
|
|
AsmMnemonic = "asr";
|
|
shift = immr;
|
|
} else if (Opcode == AArch64_SBFMXri && imms == 0x3f) {
|
|
AsmMnemonic = "asr";
|
|
shift = immr;
|
|
}
|
|
if (AsmMnemonic) {
|
|
SStream_concat(O, "%s", AsmMnemonic);
|
|
SStream_concat0(O, " ");
|
|
|
|
printRegName(O, MCOperand_getReg(Op0));
|
|
SStream_concat0(O, ", ");
|
|
printRegName(O, MCOperand_getReg(Op1));
|
|
SStream_concat(O, "%s%s#%d", ", ",
|
|
markup("<imm:"), shift);
|
|
SStream_concat0(O, markup(">"));
|
|
if (detail_is_set(MI) && useAliasDetails) {
|
|
AArch64_set_detail_op_reg(
|
|
MI, 0, MCOperand_getReg(Op0));
|
|
AArch64_set_detail_op_reg(
|
|
MI, 1, MCOperand_getReg(Op1));
|
|
if (strings_match(AsmMnemonic, "lsl"))
|
|
AArch64_get_detail_op(MI, -1)
|
|
->shift.type =
|
|
AARCH64_SFT_LSL;
|
|
else if (strings_match(AsmMnemonic,
|
|
"lsr"))
|
|
AArch64_get_detail_op(MI, -1)
|
|
->shift.type =
|
|
AARCH64_SFT_LSR;
|
|
else if (strings_match(AsmMnemonic,
|
|
"asr"))
|
|
AArch64_get_detail_op(MI, -1)
|
|
->shift.type =
|
|
AARCH64_SFT_ASR;
|
|
else
|
|
AArch64_get_detail_op(MI, -1)
|
|
->shift.type =
|
|
AARCH64_SFT_INVALID;
|
|
AArch64_get_detail_op(MI, -1)
|
|
->shift.value = shift;
|
|
}
|
|
isAlias = true;
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
if (useAliasDetails)
|
|
return;
|
|
else
|
|
goto add_real_detail;
|
|
}
|
|
}
|
|
|
|
// SBFIZ/UBFIZ aliases
|
|
if (MCOperand_getImm(Op2) > MCOperand_getImm(Op3)) {
|
|
SStream_concat(O, "%s", (IsSigned ? "sbfiz" : "ubfiz"));
|
|
SStream_concat0(O, " ");
|
|
|
|
printRegName(O, MCOperand_getReg(Op0));
|
|
SStream_concat0(O, ", ");
|
|
printRegName(O, MCOperand_getReg(Op1));
|
|
SStream_concat(O, "%s%s", ", ", markup("<imm:"));
|
|
printUInt32Bang(O, (Is64Bit ? 64 : 32) -
|
|
MCOperand_getImm(Op2));
|
|
SStream_concat(O, "%s%s%s", markup(">"), ", ",
|
|
markup("<imm:"));
|
|
printInt64Bang(O, MCOperand_getImm(Op3) + 1);
|
|
SStream_concat0(O, markup(">"));
|
|
if (detail_is_set(MI) && useAliasDetails) {
|
|
AArch64_set_detail_op_reg(
|
|
MI, 0, MCOperand_getReg(Op0));
|
|
AArch64_set_detail_op_reg(
|
|
MI, 1, MCOperand_getReg(Op1));
|
|
AArch64_set_detail_op_imm(
|
|
MI, 2, AARCH64_OP_IMM,
|
|
(Is64Bit ? 64 : 32) -
|
|
MCOperand_getImm(Op2));
|
|
AArch64_set_detail_op_imm(
|
|
MI, 3, AARCH64_OP_IMM,
|
|
MCOperand_getImm(Op3) + 1);
|
|
}
|
|
isAlias = true;
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
if (useAliasDetails)
|
|
return;
|
|
else
|
|
goto add_real_detail;
|
|
}
|
|
|
|
// Otherwise SBFX/UBFX is the preferred form
|
|
SStream_concat(O, "%s", (IsSigned ? "sbfx" : "ubfx"));
|
|
SStream_concat0(O, " ");
|
|
|
|
printRegName(O, MCOperand_getReg(Op0));
|
|
SStream_concat0(O, ", ");
|
|
printRegName(O, MCOperand_getReg(Op1));
|
|
SStream_concat(O, "%s%s", ", ", markup("<imm:"));
|
|
printInt64Bang(O, MCOperand_getImm(Op2));
|
|
SStream_concat(O, "%s%s%s", markup(">"), ", ", markup("<imm:"));
|
|
printInt64Bang(O, MCOperand_getImm(Op3) -
|
|
MCOperand_getImm(Op2) + 1);
|
|
SStream_concat0(O, markup(">"));
|
|
if (detail_is_set(MI) && useAliasDetails) {
|
|
AArch64_set_detail_op_reg(MI, 0, MCOperand_getReg(Op0));
|
|
AArch64_set_detail_op_reg(MI, 1, MCOperand_getReg(Op1));
|
|
AArch64_set_detail_op_imm(MI, 2, AARCH64_OP_IMM,
|
|
MCOperand_getImm(Op2));
|
|
AArch64_set_detail_op_imm(
|
|
MI, 3, AARCH64_OP_IMM,
|
|
MCOperand_getImm(Op3) - MCOperand_getImm(Op2) +
|
|
1);
|
|
}
|
|
isAlias = true;
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
if (useAliasDetails)
|
|
return;
|
|
else
|
|
goto add_real_detail;
|
|
}
|
|
|
|
if (Opcode == AArch64_BFMXri || Opcode == AArch64_BFMWri) {
|
|
isAlias = true;
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
MCOperand *Op0 = MCInst_getOperand(MI, (0)); // Op1 == Op0
|
|
MCOperand *Op2 = MCInst_getOperand(MI, (2));
|
|
int ImmR = MCOperand_getImm(MCInst_getOperand(MI, (3)));
|
|
int ImmS = MCOperand_getImm(MCInst_getOperand(MI, (4)));
|
|
|
|
if ((MCOperand_getReg(Op2) == AArch64_WZR ||
|
|
MCOperand_getReg(Op2) == AArch64_XZR) &&
|
|
(ImmR == 0 || ImmS < ImmR) &&
|
|
(AArch64_getFeatureBits(MI->csh->mode,
|
|
AArch64_FeatureAll) ||
|
|
AArch64_getFeatureBits(MI->csh->mode,
|
|
AArch64_HasV8_2aOps))) {
|
|
// BFC takes precedence over its entire range, sligtly differently
|
|
// to BFI.
|
|
int BitWidth = Opcode == AArch64_BFMXri ? 64 : 32;
|
|
int LSB = (BitWidth - ImmR) % BitWidth;
|
|
int Width = ImmS + 1;
|
|
|
|
SStream_concat0(O, "bfc ");
|
|
printRegName(O, MCOperand_getReg(Op0));
|
|
SStream_concat(O, "%s%s#%d", ", ", markup("<imm:"),
|
|
LSB);
|
|
SStream_concat(O, "%s%s%s#%d", markup(">"), ", ",
|
|
markup("<imm:"), Width);
|
|
SStream_concat0(O, markup(">"));
|
|
if (detail_is_set(MI) && useAliasDetails) {
|
|
AArch64_set_detail_op_reg(
|
|
MI, 0, MCOperand_getReg(Op0));
|
|
AArch64_set_detail_op_imm(MI, 3, AARCH64_OP_IMM,
|
|
LSB);
|
|
AArch64_set_detail_op_imm(MI, 4, AARCH64_OP_IMM,
|
|
Width);
|
|
}
|
|
|
|
if (useAliasDetails)
|
|
return;
|
|
else
|
|
goto add_real_detail;
|
|
} else if (ImmS < ImmR) {
|
|
// BFI alias
|
|
int BitWidth = Opcode == AArch64_BFMXri ? 64 : 32;
|
|
int LSB = (BitWidth - ImmR) % BitWidth;
|
|
int Width = ImmS + 1;
|
|
|
|
SStream_concat0(O, "bfi ");
|
|
printRegName(O, MCOperand_getReg(Op0));
|
|
SStream_concat0(O, ", ");
|
|
printRegName(O, MCOperand_getReg(Op2));
|
|
SStream_concat(O, "%s%s#%d", ", ", markup("<imm:"),
|
|
LSB);
|
|
SStream_concat(O, "%s%s%s#%d", markup(">"), ", ",
|
|
markup("<imm:"), Width);
|
|
SStream_concat0(O, markup(">"));
|
|
if (detail_is_set(MI) && useAliasDetails) {
|
|
AArch64_set_detail_op_reg(
|
|
MI, 0, MCOperand_getReg(Op0));
|
|
AArch64_set_detail_op_reg(
|
|
MI, 2, MCOperand_getReg(Op2));
|
|
AArch64_set_detail_op_imm(MI, 3, AARCH64_OP_IMM,
|
|
LSB);
|
|
AArch64_set_detail_op_imm(MI, 4, AARCH64_OP_IMM,
|
|
Width);
|
|
}
|
|
if (useAliasDetails)
|
|
return;
|
|
else
|
|
goto add_real_detail;
|
|
}
|
|
|
|
int LSB = ImmR;
|
|
int Width = ImmS - ImmR + 1;
|
|
// Otherwise BFXIL the preferred form
|
|
SStream_concat0(O, "bfxil ");
|
|
printRegName(O, MCOperand_getReg(Op0));
|
|
SStream_concat0(O, ", ");
|
|
printRegName(O, MCOperand_getReg(Op2));
|
|
SStream_concat(O, "%s%s#%d", ", ", markup("<imm:"), LSB);
|
|
SStream_concat(O, "%s%s%s#%d", markup(">"), ", ",
|
|
markup("<imm:"), Width);
|
|
SStream_concat0(O, markup(">"));
|
|
if (detail_is_set(MI) && useAliasDetails) {
|
|
AArch64_set_detail_op_reg(MI, 0, MCOperand_getReg(Op0));
|
|
AArch64_set_detail_op_reg(MI, 2, MCOperand_getReg(Op2));
|
|
AArch64_set_detail_op_imm(MI, 3, AARCH64_OP_IMM, LSB);
|
|
AArch64_set_detail_op_imm(MI, 4, AARCH64_OP_IMM, Width);
|
|
}
|
|
if (useAliasDetails)
|
|
return;
|
|
}
|
|
|
|
// Symbolic operands for MOVZ, MOVN and MOVK already imply a shift
|
|
// (e.g. :gottprel_g1: is always going to be "lsl #16") so it should not be
|
|
// printed.
|
|
if ((Opcode == AArch64_MOVZXi || Opcode == AArch64_MOVZWi ||
|
|
Opcode == AArch64_MOVNXi || Opcode == AArch64_MOVNWi) &&
|
|
MCOperand_isExpr(MCInst_getOperand(MI, (1)))) {
|
|
printUInt64Bang(O, MCInst_getOpVal(MI, 1));
|
|
if (detail_is_set(MI) && useAliasDetails) {
|
|
AArch64_set_detail_op_imm(MI, 1, AARCH64_OP_IMM, MCInst_getOpVal(MI, 1));
|
|
}
|
|
}
|
|
|
|
if ((Opcode == AArch64_MOVKXi || Opcode == AArch64_MOVKWi) &&
|
|
MCOperand_isExpr(MCInst_getOperand(MI, (2)))) {
|
|
printUInt64Bang(O, MCInst_getOpVal(MI, 2));
|
|
if (detail_is_set(MI) && useAliasDetails) {
|
|
AArch64_set_detail_op_imm(MI, 2, AARCH64_OP_IMM, MCInst_getOpVal(MI, 2));
|
|
}
|
|
}
|
|
|
|
// MOVZ, MOVN and "ORR wzr, #imm" instructions are aliases for MOV, but
|
|
// their domains overlap so they need to be prioritized. The chain is "MOVZ
|
|
// lsl #0 > MOVZ lsl #N > MOVN lsl #0 > MOVN lsl #N > ORR". The highest
|
|
// instruction that can represent the move is the MOV alias, and the rest
|
|
// get printed normally.
|
|
if ((Opcode == AArch64_MOVZXi || Opcode == AArch64_MOVZWi) &&
|
|
MCOperand_isImm(MCInst_getOperand(MI, (1))) &&
|
|
MCOperand_isImm(MCInst_getOperand(MI, (2)))) {
|
|
int RegWidth = Opcode == AArch64_MOVZXi ? 64 : 32;
|
|
int Shift = MCOperand_getImm(MCInst_getOperand(MI, (2)));
|
|
uint64_t Value =
|
|
(uint64_t)MCOperand_getImm(MCInst_getOperand(MI, (1)))
|
|
<< Shift;
|
|
|
|
if (AArch64_AM_isMOVZMovAlias(
|
|
Value, Shift, Opcode == AArch64_MOVZXi ? 64 : 32)) {
|
|
isAlias = true;
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
SStream_concat0(O, "mov ");
|
|
printRegName(O, MCOperand_getReg(
|
|
MCInst_getOperand(MI, (0))));
|
|
SStream_concat(O, "%s%s", ", ", markup("<imm:"));
|
|
printInt64Bang(O, SignExtend64(Value, RegWidth));
|
|
SStream_concat0(O, markup(">"));
|
|
if (detail_is_set(MI) && useAliasDetails) {
|
|
AArch64_set_detail_op_reg(
|
|
MI, 0, MCInst_getOpVal(MI, 0));
|
|
AArch64_set_detail_op_imm(
|
|
MI, 1, AARCH64_OP_IMM,
|
|
SignExtend64(Value, RegWidth));
|
|
}
|
|
if (useAliasDetails)
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ((Opcode == AArch64_MOVNXi || Opcode == AArch64_MOVNWi) &&
|
|
MCOperand_isImm(MCInst_getOperand(MI, (1))) &&
|
|
MCOperand_isImm(MCInst_getOperand(MI, (2)))) {
|
|
int RegWidth = Opcode == AArch64_MOVNXi ? 64 : 32;
|
|
int Shift = MCOperand_getImm(MCInst_getOperand(MI, (2)));
|
|
uint64_t Value =
|
|
~((uint64_t)MCOperand_getImm(MCInst_getOperand(MI, (1)))
|
|
<< Shift);
|
|
if (RegWidth == 32)
|
|
Value = Value & 0xffffffff;
|
|
|
|
if (AArch64_AM_isMOVNMovAlias(Value, Shift, RegWidth)) {
|
|
isAlias = true;
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
SStream_concat0(O, "mov ");
|
|
printRegName(O, MCOperand_getReg(
|
|
MCInst_getOperand(MI, (0))));
|
|
SStream_concat(O, "%s%s", ", ", markup("<imm:"));
|
|
printInt64Bang(O, SignExtend64(Value, RegWidth));
|
|
SStream_concat0(O, markup(">"));
|
|
if (detail_is_set(MI) && useAliasDetails) {
|
|
AArch64_set_detail_op_reg(
|
|
MI, 0, MCInst_getOpVal(MI, 0));
|
|
AArch64_set_detail_op_imm(
|
|
MI, 1, AARCH64_OP_IMM,
|
|
SignExtend64(Value, RegWidth));
|
|
}
|
|
if (useAliasDetails)
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ((Opcode == AArch64_ORRXri || Opcode == AArch64_ORRWri) &&
|
|
(MCOperand_getReg(MCInst_getOperand(MI, (1))) == AArch64_XZR ||
|
|
MCOperand_getReg(MCInst_getOperand(MI, (1))) == AArch64_WZR) &&
|
|
MCOperand_isImm(MCInst_getOperand(MI, (2)))) {
|
|
int RegWidth = Opcode == AArch64_ORRXri ? 64 : 32;
|
|
uint64_t Value = AArch64_AM_decodeLogicalImmediate(
|
|
MCOperand_getImm(MCInst_getOperand(MI, (2))), RegWidth);
|
|
if (!AArch64_AM_isAnyMOVWMovAlias(Value, RegWidth)) {
|
|
isAlias = true;
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
SStream_concat0(O, "mov ");
|
|
printRegName(O, MCOperand_getReg(
|
|
MCInst_getOperand(MI, (0))));
|
|
SStream_concat(O, "%s%s", ", ", markup("<imm:"));
|
|
printInt64Bang(O, SignExtend64(Value, RegWidth));
|
|
SStream_concat0(O, markup(">"));
|
|
if (detail_is_set(MI) && useAliasDetails) {
|
|
AArch64_set_detail_op_reg(
|
|
MI, 0, MCInst_getOpVal(MI, 0));
|
|
AArch64_set_detail_op_imm(
|
|
MI, 2, AARCH64_OP_IMM,
|
|
SignExtend64(Value, RegWidth));
|
|
}
|
|
if (useAliasDetails)
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (Opcode == AArch64_SPACE) {
|
|
isAlias = true;
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
SStream_concat1(O, ' ');
|
|
SStream_concat(O, "%s", " SPACE ");
|
|
printInt64(O, MCOperand_getImm(MCInst_getOperand(MI, (1))));
|
|
if (detail_is_set(MI) && useAliasDetails) {
|
|
AArch64_set_detail_op_imm(MI, 1, AARCH64_OP_IMM,
|
|
MCInst_getOpVal(MI, 1));
|
|
}
|
|
if (useAliasDetails)
|
|
return;
|
|
}
|
|
|
|
if (!isAlias)
|
|
isAlias |= printAliasInstr(MI, Address, O);
|
|
|
|
add_real_detail:
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
|
|
if (!isAlias || !useAliasDetails) {
|
|
map_set_fill_detail_ops(MI, !(isAlias && useAliasDetails));
|
|
if (isAlias)
|
|
SStream_Close(O);
|
|
printInstruction(MI, Address, O);
|
|
if (isAlias)
|
|
SStream_Open(O);
|
|
}
|
|
}
|
|
|
|
bool printRangePrefetchAlias(MCInst *MI, SStream *O, const char *Annot)
|
|
{
|
|
unsigned Opcode = MCInst_getOpcode(MI);
|
|
|
|
#ifndef NDEBUG
|
|
|
|
#endif
|
|
|
|
unsigned PRFOp = MCOperand_getImm(MCInst_getOperand(MI, (0)));
|
|
unsigned Mask = 0x18; // 0b11000
|
|
if ((PRFOp & Mask) != Mask)
|
|
return false; // Rt != '11xxx', it's a PRFM instruction.
|
|
|
|
unsigned Rm = MCOperand_getReg(MCInst_getOperand(MI, (2)));
|
|
|
|
// "Rm" must be a 64-bit GPR for RPRFM.
|
|
if (MCRegisterInfo_getRegClass(MI->MRI, Rm))
|
|
Rm = MCRegisterInfo_getMatchingSuperReg(
|
|
MI->MRI, Rm, AArch64_sub_32,
|
|
MCRegisterInfo_getRegClass(MI->MRI, Rm));
|
|
|
|
unsigned SignExtend = MCOperand_getImm(
|
|
MCInst_getOperand(MI, (3))); // encoded in "option<2>".
|
|
unsigned Shift =
|
|
MCOperand_getImm(MCInst_getOperand(MI, (4))); // encoded in "S".
|
|
|
|
unsigned Option0 = (Opcode == AArch64_PRFMroX) ? 1 : 0;
|
|
|
|
// encoded in "option<2>:option<0>:S:Rt<2:0>".
|
|
unsigned RPRFOp = (SignExtend << 5) | (Option0 << 4) | (Shift << 3) |
|
|
(PRFOp & 0x7);
|
|
|
|
SStream_concat0(O, "rprfm ");
|
|
const AArch64RPRFM_RPRFM *RPRFM =
|
|
AArch64RPRFM_lookupRPRFMByEncoding(RPRFOp);
|
|
if (RPRFM) {
|
|
SStream_concat0(O, RPRFM->Name);
|
|
} else {
|
|
printUInt32Bang(O, RPRFOp);
|
|
SStream_concat(O, ", ");
|
|
}
|
|
SStream_concat0(O, getRegisterName(Rm, AArch64_NoRegAltName));
|
|
SStream_concat0(O, ", [");
|
|
printOperand(MI, 1, O); // "Rn".
|
|
SStream_concat0(O, "]");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool printSysAlias(MCInst *MI, SStream *O)
|
|
{
|
|
MCOperand *Op1 = MCInst_getOperand(MI, (0));
|
|
MCOperand *Cn = MCInst_getOperand(MI, (1));
|
|
MCOperand *Cm = MCInst_getOperand(MI, (2));
|
|
MCOperand *Op2 = MCInst_getOperand(MI, (3));
|
|
|
|
unsigned Op1Val = MCOperand_getImm(Op1);
|
|
unsigned CnVal = MCOperand_getImm(Cn);
|
|
unsigned CmVal = MCOperand_getImm(Cm);
|
|
unsigned Op2Val = MCOperand_getImm(Op2);
|
|
|
|
uint16_t Encoding = Op2Val;
|
|
Encoding |= CmVal << 3;
|
|
Encoding |= CnVal << 7;
|
|
Encoding |= Op1Val << 11;
|
|
|
|
bool NeedsReg;
|
|
const char *Ins;
|
|
const char *Name;
|
|
|
|
if (CnVal == 7) {
|
|
switch (CmVal) {
|
|
default:
|
|
return false;
|
|
// Maybe IC, maybe Prediction Restriction
|
|
case 1:
|
|
switch (Op1Val) {
|
|
default:
|
|
return false;
|
|
case 0:
|
|
goto Search_IC;
|
|
case 3:
|
|
goto Search_PRCTX;
|
|
}
|
|
// Prediction Restriction aliases
|
|
case 3: {
|
|
Search_PRCTX:
|
|
if (Op1Val != 3 || CnVal != 7 || CmVal != 3)
|
|
return false;
|
|
|
|
unsigned int Requires =
|
|
Op2Val == 6 ? AArch64_FeatureSPECRES2 :
|
|
AArch64_FeaturePredRes;
|
|
if (!(AArch64_getFeatureBits(MI->csh->mode,
|
|
AArch64_FeatureAll) ||
|
|
AArch64_getFeatureBits(MI->csh->mode, Requires)))
|
|
return false;
|
|
|
|
NeedsReg = true;
|
|
switch (Op2Val) {
|
|
default:
|
|
return false;
|
|
case 4:
|
|
Ins = "cfp ";
|
|
break;
|
|
case 5:
|
|
Ins = "dvp ";
|
|
break;
|
|
case 6:
|
|
Ins = "cosp ";
|
|
break;
|
|
case 7:
|
|
Ins = "cpp ";
|
|
break;
|
|
}
|
|
Name = "RCTX";
|
|
} break;
|
|
// IC aliases
|
|
case 5: {
|
|
Search_IC: {
|
|
const AArch64IC_IC *IC = AArch64IC_lookupICByEncoding(Encoding);
|
|
if (!IC ||
|
|
!AArch64_testFeatureList(MI->csh->mode, IC->FeaturesRequired))
|
|
return false;
|
|
if (detail_is_set(MI)) {
|
|
aarch64_sysop sysop = { 0 };
|
|
sysop.reg = IC->SysReg;
|
|
sysop.sub_type = AARCH64_OP_IC;
|
|
AArch64_get_detail_op(MI, 0)->type = AARCH64_OP_SYSREG;
|
|
AArch64_get_detail_op(MI, 0)->sysop = sysop;
|
|
AArch64_inc_op_count(MI);
|
|
}
|
|
|
|
NeedsReg = IC->NeedsReg;
|
|
Ins = "ic ";
|
|
Name = IC->Name;
|
|
}
|
|
} break;
|
|
// DC aliases
|
|
case 4:
|
|
case 6:
|
|
case 10:
|
|
case 11:
|
|
case 12:
|
|
case 13:
|
|
case 14: {
|
|
const AArch64DC_DC *DC =
|
|
AArch64DC_lookupDCByEncoding(Encoding);
|
|
if (!DC || !AArch64_testFeatureList(
|
|
MI->csh->mode, DC->FeaturesRequired))
|
|
return false;
|
|
if (detail_is_set(MI)) {
|
|
aarch64_sysop sysop = { 0 };
|
|
sysop.alias = DC->SysAlias;
|
|
sysop.sub_type = AARCH64_OP_DC;
|
|
AArch64_get_detail_op(MI, 0)->type =
|
|
AARCH64_OP_SYSALIAS;
|
|
AArch64_get_detail_op(MI, 0)->sysop = sysop;
|
|
AArch64_inc_op_count(MI);
|
|
}
|
|
|
|
NeedsReg = true;
|
|
Ins = "dc ";
|
|
Name = DC->Name;
|
|
} break;
|
|
// AT aliases
|
|
case 8:
|
|
case 9: {
|
|
const AArch64AT_AT *AT =
|
|
AArch64AT_lookupATByEncoding(Encoding);
|
|
if (!AT || !AArch64_testFeatureList(
|
|
MI->csh->mode, AT->FeaturesRequired))
|
|
return false;
|
|
|
|
if (detail_is_set(MI)) {
|
|
aarch64_sysop sysop = { 0 };
|
|
sysop.alias = AT->SysAlias;
|
|
sysop.sub_type = AARCH64_OP_AT;
|
|
AArch64_get_detail_op(MI, 0)->type =
|
|
AARCH64_OP_SYSALIAS;
|
|
AArch64_get_detail_op(MI, 0)->sysop = sysop;
|
|
AArch64_inc_op_count(MI);
|
|
}
|
|
NeedsReg = true;
|
|
Ins = "at ";
|
|
Name = AT->Name;
|
|
} break;
|
|
}
|
|
} else if (CnVal == 8 || CnVal == 9) {
|
|
// TLBI aliases
|
|
const AArch64TLBI_TLBI *TLBI =
|
|
AArch64TLBI_lookupTLBIByEncoding(Encoding);
|
|
if (!TLBI || !AArch64_testFeatureList(MI->csh->mode,
|
|
TLBI->FeaturesRequired))
|
|
return false;
|
|
|
|
if (detail_is_set(MI)) {
|
|
aarch64_sysop sysop = { 0 };
|
|
sysop.reg = TLBI->SysReg;
|
|
sysop.sub_type = AARCH64_OP_TLBI;
|
|
AArch64_get_detail_op(MI, 0)->type = AARCH64_OP_SYSREG;
|
|
AArch64_get_detail_op(MI, 0)->sysop = sysop;
|
|
AArch64_inc_op_count(MI);
|
|
}
|
|
NeedsReg = TLBI->NeedsReg;
|
|
Ins = "tlbi ";
|
|
Name = TLBI->Name;
|
|
} else
|
|
return false;
|
|
|
|
#define TMP_STR_LEN 32
|
|
char Str[TMP_STR_LEN] = { 0 };
|
|
append_to_str_lower(Str, TMP_STR_LEN, Ins);
|
|
append_to_str_lower(Str, TMP_STR_LEN, Name);
|
|
#undef TMP_STR_LEN
|
|
|
|
SStream_concat1(O, ' ');
|
|
SStream_concat0(O, Str);
|
|
if (NeedsReg) {
|
|
SStream_concat0(O, ", ");
|
|
printRegName(O, MCOperand_getReg(MCInst_getOperand(MI, (4))));
|
|
AArch64_set_detail_op_reg(MI, 4, MCInst_getOpVal(MI, 4));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool printSyspAlias(MCInst *MI, SStream *O)
|
|
{
|
|
MCOperand *Op1 = MCInst_getOperand(MI, (0));
|
|
MCOperand *Cn = MCInst_getOperand(MI, (1));
|
|
MCOperand *Cm = MCInst_getOperand(MI, (2));
|
|
MCOperand *Op2 = MCInst_getOperand(MI, (3));
|
|
|
|
unsigned Op1Val = MCOperand_getImm(Op1);
|
|
unsigned CnVal = MCOperand_getImm(Cn);
|
|
unsigned CmVal = MCOperand_getImm(Cm);
|
|
unsigned Op2Val = MCOperand_getImm(Op2);
|
|
|
|
uint16_t Encoding = Op2Val;
|
|
Encoding |= CmVal << 3;
|
|
Encoding |= CnVal << 7;
|
|
Encoding |= Op1Val << 11;
|
|
|
|
const char *Ins;
|
|
const char *Name;
|
|
|
|
if (CnVal == 8 || CnVal == 9) {
|
|
// TLBIP aliases
|
|
|
|
if (CnVal == 9) {
|
|
if (!AArch64_getFeatureBits(MI->csh->mode,
|
|
AArch64_FeatureAll) ||
|
|
!AArch64_getFeatureBits(MI->csh->mode,
|
|
AArch64_FeatureXS))
|
|
return false;
|
|
Encoding &= ~(1 << 7);
|
|
}
|
|
|
|
const AArch64TLBI_TLBI *TLBI =
|
|
AArch64TLBI_lookupTLBIByEncoding(Encoding);
|
|
if (!TLBI || !AArch64_testFeatureList(MI->csh->mode,
|
|
TLBI->FeaturesRequired))
|
|
return false;
|
|
|
|
if (detail_is_set(MI)) {
|
|
aarch64_sysop sysop = { 0 };
|
|
sysop.reg = TLBI->SysReg;
|
|
sysop.sub_type = AARCH64_OP_TLBI;
|
|
AArch64_get_detail_op(MI, 0)->type = AARCH64_OP_SYSREG;
|
|
AArch64_get_detail_op(MI, 0)->sysop = sysop;
|
|
AArch64_inc_op_count(MI);
|
|
}
|
|
Ins = "tlbip ";
|
|
Name = TLBI->Name;
|
|
} else
|
|
return false;
|
|
|
|
#define TMP_STR_LEN 32
|
|
char Str[TMP_STR_LEN] = { 0 };
|
|
append_to_str_lower(Str, TMP_STR_LEN, Ins);
|
|
append_to_str_lower(Str, TMP_STR_LEN, Name);
|
|
|
|
if (CnVal == 9) {
|
|
append_to_str_lower(Str, TMP_STR_LEN, "nxs");
|
|
}
|
|
#undef TMP_STR_LEN
|
|
|
|
SStream_concat1(O, ' ');
|
|
SStream_concat0(O, Str);
|
|
SStream_concat0(O, ", ");
|
|
if (MCOperand_getReg(MCInst_getOperand(MI, (4))) == AArch64_XZR)
|
|
printSyspXzrPair(MI, 4, O);
|
|
else
|
|
CONCAT(printGPRSeqPairsClassOperand, 64)(MI, 4, O);
|
|
|
|
return true;
|
|
}
|
|
|
|
#define DEFINE_printMatrix(EltSize) \
|
|
void CONCAT(printMatrix, EltSize)(MCInst * MI, unsigned OpNum, \
|
|
SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_1( \
|
|
MI, CONCAT(AArch64_OP_GROUP_Matrix, EltSize), OpNum, \
|
|
EltSize); \
|
|
MCOperand *RegOp = MCInst_getOperand(MI, (OpNum)); \
|
|
\
|
|
printRegName(O, MCOperand_getReg(RegOp)); \
|
|
switch (EltSize) { \
|
|
case 0: \
|
|
break; \
|
|
case 8: \
|
|
SStream_concat0(O, ".b"); \
|
|
break; \
|
|
case 16: \
|
|
SStream_concat0(O, ".h"); \
|
|
break; \
|
|
case 32: \
|
|
SStream_concat0(O, ".s"); \
|
|
break; \
|
|
case 64: \
|
|
SStream_concat0(O, ".d"); \
|
|
break; \
|
|
case 128: \
|
|
SStream_concat0(O, ".q"); \
|
|
break; \
|
|
default: \
|
|
CS_ASSERT_RET(0 && "Unsupported element size"); \
|
|
} \
|
|
}
|
|
DEFINE_printMatrix(64);
|
|
DEFINE_printMatrix(32);
|
|
DEFINE_printMatrix(16);
|
|
DEFINE_printMatrix(0);
|
|
|
|
#define DEFINE_printMatrixTileVector(IsVertical) \
|
|
void CONCAT(printMatrixTileVector, \
|
|
IsVertical)(MCInst * MI, unsigned OpNum, SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_1( \
|
|
MI, \
|
|
CONCAT(AArch64_OP_GROUP_MatrixTileVector, IsVertical), \
|
|
OpNum, IsVertical); \
|
|
MCOperand *RegOp = MCInst_getOperand(MI, (OpNum)); \
|
|
\
|
|
const char *RegName = getRegisterName(MCOperand_getReg(RegOp), \
|
|
AArch64_NoRegAltName); \
|
|
\
|
|
unsigned buf_len = strlen(RegName) + 1; \
|
|
char *Base = cs_mem_calloc(1, buf_len); \
|
|
memcpy(Base, RegName, buf_len); \
|
|
char *Dot = strchr(Base, '.'); \
|
|
if (!Dot) { \
|
|
SStream_concat0(O, RegName); \
|
|
return; \
|
|
} \
|
|
*Dot = '\0'; /* Split string */ \
|
|
char *Suffix = Dot + 1; \
|
|
SStream_concat(O, "%s%s", Base, (IsVertical ? "v" : "h")); \
|
|
SStream_concat1(O, '.'); \
|
|
SStream_concat0(O, Suffix); \
|
|
cs_mem_free(Base); \
|
|
}
|
|
DEFINE_printMatrixTileVector(0);
|
|
DEFINE_printMatrixTileVector(1);
|
|
|
|
void printMatrixTile(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_MatrixTile, OpNum);
|
|
MCOperand *RegOp = MCInst_getOperand(MI, (OpNum));
|
|
|
|
printRegName(O, MCOperand_getReg(RegOp));
|
|
}
|
|
|
|
void printSVCROp(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_SVCROp, OpNum);
|
|
MCOperand *MO = MCInst_getOperand(MI, (OpNum));
|
|
|
|
unsigned svcrop = MCOperand_getImm(MO);
|
|
const AArch64SVCR_SVCR *SVCR = AArch64SVCR_lookupSVCRByEncoding(svcrop);
|
|
|
|
SStream_concat0(O, SVCR->Name);
|
|
}
|
|
|
|
void printOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_Operand, OpNo);
|
|
MCOperand *Op = MCInst_getOperand(MI, (OpNo));
|
|
if (MCOperand_isReg(Op)) {
|
|
unsigned Reg = MCOperand_getReg(Op);
|
|
printRegName(O, Reg);
|
|
} else if (MCOperand_isImm(Op)) {
|
|
Op = MCInst_getOperand(MI, (OpNo));
|
|
SStream_concat(O, "%s", markup("<imm:"));
|
|
printInt64Bang(O, MCOperand_getImm(Op));
|
|
SStream_concat0(O, markup(">"));
|
|
} else {
|
|
printUInt64Bang(O, MCInst_getOpVal(MI, OpNo));
|
|
}
|
|
}
|
|
|
|
void printImm(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_Imm, OpNo);
|
|
MCOperand *Op = MCInst_getOperand(MI, (OpNo));
|
|
SStream_concat(O, "%s", markup("<imm:"));
|
|
printInt64Bang(O, MCOperand_getImm(Op));
|
|
SStream_concat0(O, markup(">"));
|
|
}
|
|
|
|
void printImmHex(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_ImmHex, OpNo);
|
|
MCOperand *Op = MCInst_getOperand(MI, (OpNo));
|
|
SStream_concat(O, "%s", markup("<imm:"));
|
|
printInt64Bang(O, MCOperand_getImm(Op));
|
|
SStream_concat0(O, markup(">"));
|
|
}
|
|
|
|
#define DEFINE_printSImm(Size) \
|
|
void CONCAT(printSImm, Size)(MCInst * MI, unsigned OpNo, SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_1( \
|
|
MI, CONCAT(AArch64_OP_GROUP_SImm, Size), OpNo, Size); \
|
|
MCOperand *Op = MCInst_getOperand(MI, (OpNo)); \
|
|
if (Size == 8) { \
|
|
SStream_concat(O, "%s", markup("<imm:")); \
|
|
printInt32Bang(O, MCOperand_getImm(Op)); \
|
|
SStream_concat0(O, markup(">")); \
|
|
} else if (Size == 16) { \
|
|
SStream_concat(O, "%s", markup("<imm:")); \
|
|
printInt32Bang(O, MCOperand_getImm(Op)); \
|
|
SStream_concat0(O, markup(">")); \
|
|
} else { \
|
|
SStream_concat(O, "%s", markup("<imm:")); \
|
|
printInt64Bang(O, MCOperand_getImm(Op)); \
|
|
SStream_concat0(O, markup(">")); \
|
|
} \
|
|
}
|
|
DEFINE_printSImm(16);
|
|
DEFINE_printSImm(8);
|
|
|
|
void printPostIncOperand(MCInst *MI, unsigned OpNo, unsigned Imm, SStream *O)
|
|
{
|
|
MCOperand *Op = MCInst_getOperand(MI, (OpNo));
|
|
if (MCOperand_isReg(Op)) {
|
|
unsigned Reg = MCOperand_getReg(Op);
|
|
if (Reg == AArch64_XZR) {
|
|
SStream_concat(O, "%s", markup("<imm:"));
|
|
printUInt64Bang(O, Imm);
|
|
SStream_concat0(O, markup(">"));
|
|
} else
|
|
printRegName(O, Reg);
|
|
} else
|
|
CS_ASSERT_RET(0 && "unknown operand kind in printPostIncOperand64");
|
|
}
|
|
|
|
void printVRegOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_VRegOperand, OpNo);
|
|
MCOperand *Op = MCInst_getOperand(MI, (OpNo));
|
|
|
|
unsigned Reg = MCOperand_getReg(Op);
|
|
printRegNameAlt(O, Reg, AArch64_vreg);
|
|
}
|
|
|
|
void printSysCROperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_SysCROperand, OpNo);
|
|
MCOperand *Op = MCInst_getOperand(MI, (OpNo));
|
|
|
|
SStream_concat(O, "%s", "c");
|
|
printUInt32(O, MCOperand_getImm(Op));
|
|
SStream_concat1(O, '\0');
|
|
}
|
|
|
|
void printAddSubImm(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_AddSubImm, OpNum);
|
|
MCOperand *MO = MCInst_getOperand(MI, (OpNum));
|
|
if (MCOperand_isImm(MO)) {
|
|
unsigned Val = (MCOperand_getImm(MO) & 0xfff);
|
|
|
|
unsigned Shift = AArch64_AM_getShiftValue(
|
|
MCOperand_getImm(MCInst_getOperand(MI, (OpNum + 1))));
|
|
SStream_concat(O, "%s", markup("<imm:"));
|
|
printUInt32Bang(O, (Val));
|
|
SStream_concat0(O, markup(">"));
|
|
if (Shift != 0) {
|
|
printShifter(MI, OpNum + 1, O);
|
|
}
|
|
} else {
|
|
printShifter(MI, OpNum + 1, O);
|
|
}
|
|
}
|
|
|
|
#define DEFINE_printLogicalImm(T) \
|
|
void CONCAT(printLogicalImm, T)(MCInst * MI, unsigned OpNum, \
|
|
SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_1( \
|
|
MI, CONCAT(AArch64_OP_GROUP_LogicalImm, T), OpNum, sizeof(T)); \
|
|
uint64_t Val = \
|
|
MCOperand_getImm(MCInst_getOperand(MI, (OpNum))); \
|
|
SStream_concat(O, "%s", markup("<imm:")); \
|
|
printUInt64Bang(O, (AArch64_AM_decodeLogicalImmediate( \
|
|
Val, 8 * sizeof(T)))); \
|
|
SStream_concat0(O, markup(">")); \
|
|
}
|
|
DEFINE_printLogicalImm(int64_t);
|
|
DEFINE_printLogicalImm(int32_t);
|
|
DEFINE_printLogicalImm(int8_t);
|
|
DEFINE_printLogicalImm(int16_t);
|
|
|
|
void printShifter(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_Shifter, OpNum);
|
|
unsigned Val = MCOperand_getImm(MCInst_getOperand(MI, (OpNum)));
|
|
// LSL #0 should not be printed.
|
|
if (AArch64_AM_getShiftType(Val) == AArch64_AM_LSL &&
|
|
AArch64_AM_getShiftValue(Val) == 0)
|
|
return;
|
|
SStream_concat(
|
|
O, "%s%s%s%s#%d", ", ",
|
|
AArch64_AM_getShiftExtendName(AArch64_AM_getShiftType(Val)),
|
|
" ", markup("<imm:"), AArch64_AM_getShiftValue(Val));
|
|
SStream_concat0(O, markup(">"));
|
|
}
|
|
|
|
void printShiftedRegister(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_ShiftedRegister, OpNum);
|
|
printRegName(O, MCOperand_getReg(MCInst_getOperand(MI, (OpNum))));
|
|
printShifter(MI, OpNum + 1, O);
|
|
}
|
|
|
|
void printExtendedRegister(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_ExtendedRegister, OpNum);
|
|
printRegName(O, MCOperand_getReg(MCInst_getOperand(MI, (OpNum))));
|
|
printArithExtend(MI, OpNum + 1, O);
|
|
}
|
|
|
|
void printArithExtend(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_ArithExtend, OpNum);
|
|
unsigned Val = MCOperand_getImm(MCInst_getOperand(MI, (OpNum)));
|
|
AArch64_AM_ShiftExtendType ExtType = AArch64_AM_getArithExtendType(Val);
|
|
unsigned ShiftVal = AArch64_AM_getArithShiftValue(Val);
|
|
|
|
// If the destination or first source register operand is [W]SP, print
|
|
// UXTW/UXTX as LSL, and if the shift amount is also zero, print nothing at
|
|
// all.
|
|
if (ExtType == AArch64_AM_UXTW || ExtType == AArch64_AM_UXTX) {
|
|
unsigned Dest = MCOperand_getReg(MCInst_getOperand(MI, (0)));
|
|
unsigned Src1 = MCOperand_getReg(MCInst_getOperand(MI, (1)));
|
|
if (((Dest == AArch64_SP || Src1 == AArch64_SP) &&
|
|
ExtType == AArch64_AM_UXTX) ||
|
|
((Dest == AArch64_WSP || Src1 == AArch64_WSP) &&
|
|
ExtType == AArch64_AM_UXTW)) {
|
|
if (ShiftVal != 0) {
|
|
SStream_concat(O, "%s%s", ", lsl ",
|
|
markup("<imm:"));
|
|
printUInt32Bang(O, ShiftVal);
|
|
SStream_concat0(O, markup(">"));
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
SStream_concat(O, "%s", ", ");
|
|
SStream_concat0(O, AArch64_AM_getShiftExtendName(ExtType));
|
|
if (ShiftVal != 0) {
|
|
SStream_concat(O, "%s%s#%d", " ", markup("<imm:"), ShiftVal);
|
|
SStream_concat0(O, markup(">"));
|
|
}
|
|
}
|
|
|
|
static void printMemExtendImpl(bool SignExtend, bool DoShift, unsigned Width,
|
|
char SrcRegKind, SStream *O, bool getUseMarkup)
|
|
{
|
|
// sxtw, sxtx, uxtw or lsl (== uxtx)
|
|
bool IsLSL = !SignExtend && SrcRegKind == 'x';
|
|
if (IsLSL)
|
|
SStream_concat0(O, "lsl");
|
|
else {
|
|
SStream_concat(O, "%c%s", (SignExtend ? 's' : 'u'), "xt");
|
|
SStream_concat1(O, SrcRegKind);
|
|
}
|
|
|
|
if (DoShift || IsLSL) {
|
|
SStream_concat0(O, " ");
|
|
if (getUseMarkup)
|
|
SStream_concat0(O, "<imm:");
|
|
unsigned ShiftAmount = DoShift ? Log2_32(Width / 8) : 0;
|
|
SStream_concat(O, "%s%d", "#", ShiftAmount);
|
|
if (getUseMarkup)
|
|
SStream_concat0(O, ">");
|
|
}
|
|
}
|
|
|
|
void printMemExtend(MCInst *MI, unsigned OpNum, SStream *O, char SrcRegKind,
|
|
unsigned Width)
|
|
{
|
|
bool SignExtend = MCOperand_getImm(MCInst_getOperand(MI, (OpNum)));
|
|
bool DoShift = MCOperand_getImm(MCInst_getOperand(MI, (OpNum + 1)));
|
|
printMemExtendImpl(SignExtend, DoShift, Width, SrcRegKind, O,
|
|
getUseMarkup());
|
|
}
|
|
|
|
#define DEFINE_printRegWithShiftExtend(SignExtend, ExtWidth, SrcRegKind, \
|
|
Suffix) \
|
|
void CONCAT(printRegWithShiftExtend, \
|
|
CONCAT(SignExtend, \
|
|
CONCAT(ExtWidth, CONCAT(SrcRegKind, Suffix))))( \
|
|
MCInst * MI, unsigned OpNum, SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_4( \
|
|
MI, \
|
|
CONCAT(CONCAT(CONCAT(CONCAT(AArch64_OP_GROUP_RegWithShiftExtend, \
|
|
SignExtend), \
|
|
ExtWidth), \
|
|
SrcRegKind), \
|
|
Suffix), \
|
|
OpNum, SignExtend, ExtWidth, CHAR(SrcRegKind), \
|
|
CHAR(Suffix)); \
|
|
printOperand(MI, OpNum, O); \
|
|
if (CHAR(Suffix) == 's' || CHAR(Suffix) == 'd') { \
|
|
SStream_concat1(O, '.'); \
|
|
SStream_concat1(O, CHAR(Suffix)); \
|
|
SStream_concat1(O, '\0'); \
|
|
} else \
|
|
CS_ASSERT_RET((CHAR(Suffix) == '0') && \
|
|
"Unsupported suffix size"); \
|
|
bool DoShift = ExtWidth != 8; \
|
|
if (SignExtend || DoShift || CHAR(SrcRegKind) == 'w') { \
|
|
SStream_concat0(O, ", "); \
|
|
printMemExtendImpl(SignExtend, DoShift, ExtWidth, \
|
|
CHAR(SrcRegKind), O, \
|
|
getUseMarkup()); \
|
|
} \
|
|
}
|
|
DEFINE_printRegWithShiftExtend(false, 8, x, d);
|
|
DEFINE_printRegWithShiftExtend(true, 8, w, d);
|
|
DEFINE_printRegWithShiftExtend(false, 8, w, d);
|
|
DEFINE_printRegWithShiftExtend(false, 8, x, 0);
|
|
DEFINE_printRegWithShiftExtend(true, 8, w, s);
|
|
DEFINE_printRegWithShiftExtend(false, 8, w, s);
|
|
DEFINE_printRegWithShiftExtend(false, 64, x, d);
|
|
DEFINE_printRegWithShiftExtend(true, 64, w, d);
|
|
DEFINE_printRegWithShiftExtend(false, 64, w, d);
|
|
DEFINE_printRegWithShiftExtend(false, 64, x, 0);
|
|
DEFINE_printRegWithShiftExtend(true, 64, w, s);
|
|
DEFINE_printRegWithShiftExtend(false, 64, w, s);
|
|
DEFINE_printRegWithShiftExtend(false, 16, x, d);
|
|
DEFINE_printRegWithShiftExtend(true, 16, w, d);
|
|
DEFINE_printRegWithShiftExtend(false, 16, w, d);
|
|
DEFINE_printRegWithShiftExtend(false, 16, x, 0);
|
|
DEFINE_printRegWithShiftExtend(true, 16, w, s);
|
|
DEFINE_printRegWithShiftExtend(false, 16, w, s);
|
|
DEFINE_printRegWithShiftExtend(false, 32, x, d);
|
|
DEFINE_printRegWithShiftExtend(true, 32, w, d);
|
|
DEFINE_printRegWithShiftExtend(false, 32, w, d);
|
|
DEFINE_printRegWithShiftExtend(false, 32, x, 0);
|
|
DEFINE_printRegWithShiftExtend(true, 32, w, s);
|
|
DEFINE_printRegWithShiftExtend(false, 32, w, s);
|
|
DEFINE_printRegWithShiftExtend(false, 8, x, s);
|
|
DEFINE_printRegWithShiftExtend(false, 16, x, s);
|
|
DEFINE_printRegWithShiftExtend(false, 32, x, s);
|
|
DEFINE_printRegWithShiftExtend(false, 64, x, s);
|
|
DEFINE_printRegWithShiftExtend(false, 128, x, 0);
|
|
|
|
#define DEFINE_printPredicateAsCounter(EltSize) \
|
|
void CONCAT(printPredicateAsCounter, \
|
|
EltSize)(MCInst * MI, unsigned OpNum, SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_1( \
|
|
MI, \
|
|
CONCAT(AArch64_OP_GROUP_PredicateAsCounter, EltSize), \
|
|
OpNum, EltSize); \
|
|
unsigned Reg = \
|
|
MCOperand_getReg(MCInst_getOperand(MI, (OpNum))); \
|
|
if (Reg < AArch64_PN0 || Reg > AArch64_PN15) \
|
|
CS_ASSERT_RET(0 && \
|
|
"Unsupported predicate-as-counter register"); \
|
|
SStream_concat(O, "%s", "pn"); \
|
|
printUInt32(O, (Reg - AArch64_PN0)); \
|
|
switch (EltSize) { \
|
|
case 0: \
|
|
break; \
|
|
case 8: \
|
|
SStream_concat0(O, ".b"); \
|
|
break; \
|
|
case 16: \
|
|
SStream_concat0(O, ".h"); \
|
|
break; \
|
|
case 32: \
|
|
SStream_concat0(O, ".s"); \
|
|
break; \
|
|
case 64: \
|
|
SStream_concat0(O, ".d"); \
|
|
break; \
|
|
default: \
|
|
CS_ASSERT_RET(0 && "Unsupported element size"); \
|
|
} \
|
|
}
|
|
DEFINE_printPredicateAsCounter(8);
|
|
DEFINE_printPredicateAsCounter(64);
|
|
DEFINE_printPredicateAsCounter(16);
|
|
DEFINE_printPredicateAsCounter(32);
|
|
DEFINE_printPredicateAsCounter(0);
|
|
|
|
void printCondCode(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_CondCode, OpNum);
|
|
AArch64CC_CondCode CC = (AArch64CC_CondCode)MCOperand_getImm(
|
|
MCInst_getOperand(MI, (OpNum)));
|
|
SStream_concat0(O, AArch64CC_getCondCodeName(CC));
|
|
}
|
|
|
|
void printInverseCondCode(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_InverseCondCode, OpNum);
|
|
AArch64CC_CondCode CC = (AArch64CC_CondCode)MCOperand_getImm(
|
|
MCInst_getOperand(MI, (OpNum)));
|
|
SStream_concat0(O, AArch64CC_getCondCodeName(
|
|
AArch64CC_getInvertedCondCode(CC)));
|
|
}
|
|
|
|
void printAMNoIndex(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_AMNoIndex, OpNum);
|
|
SStream_concat0(O, "[");
|
|
|
|
printRegName(O, MCOperand_getReg(MCInst_getOperand(MI, (OpNum))));
|
|
SStream_concat0(O, "]");
|
|
}
|
|
|
|
#define DEFINE_printImmScale(Scale) \
|
|
void CONCAT(printImmScale, Scale)(MCInst * MI, unsigned OpNum, \
|
|
SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_1( \
|
|
MI, CONCAT(AArch64_OP_GROUP_ImmScale, Scale), OpNum, \
|
|
Scale); \
|
|
SStream_concat(O, "%s", markup("<imm:")); \
|
|
printInt32Bang(O, Scale *MCOperand_getImm( \
|
|
MCInst_getOperand(MI, (OpNum)))); \
|
|
SStream_concat0(O, markup(">")); \
|
|
}
|
|
DEFINE_printImmScale(8);
|
|
DEFINE_printImmScale(2);
|
|
DEFINE_printImmScale(4);
|
|
DEFINE_printImmScale(16);
|
|
DEFINE_printImmScale(32);
|
|
DEFINE_printImmScale(3);
|
|
|
|
#define DEFINE_printImmRangeScale(Scale, Offset) \
|
|
void CONCAT(printImmRangeScale, CONCAT(Scale, Offset))( \
|
|
MCInst * MI, unsigned OpNum, SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_2( \
|
|
MI, \
|
|
CONCAT(CONCAT(AArch64_OP_GROUP_ImmRangeScale, Scale), \
|
|
Offset), \
|
|
OpNum, Scale, Offset); \
|
|
unsigned FirstImm = \
|
|
Scale * \
|
|
MCOperand_getImm(MCInst_getOperand(MI, (OpNum))); \
|
|
printUInt32(O, (FirstImm)); \
|
|
SStream_concat(O, "%s", ":"); \
|
|
printUInt32(O, (FirstImm + Offset)); \
|
|
SStream_concat1(O, '\0'); \
|
|
}
|
|
DEFINE_printImmRangeScale(2, 1);
|
|
DEFINE_printImmRangeScale(4, 3);
|
|
|
|
void printUImm12Offset(MCInst *MI, unsigned OpNum, unsigned Scale, SStream *O)
|
|
{
|
|
MCOperand *MO = MCInst_getOperand(MI, (OpNum));
|
|
if (MCOperand_isImm(MO)) {
|
|
SStream_concat(O, "%s", markup("<imm:"));
|
|
printUInt32Bang(O, (MCOperand_getImm(MO) * Scale));
|
|
SStream_concat0(O, markup(">"));
|
|
} else {
|
|
printUInt64Bang(O, MCOperand_getImm(MO));
|
|
}
|
|
}
|
|
|
|
void printAMIndexedWB(MCInst *MI, unsigned OpNum, unsigned Scale, SStream *O)
|
|
{
|
|
MCOperand *MO1 = MCInst_getOperand(MI, (OpNum + 1));
|
|
SStream_concat0(O, "[");
|
|
|
|
printRegName(O, MCOperand_getReg(MCInst_getOperand(MI, (OpNum))));
|
|
if (MCOperand_isImm(MO1)) {
|
|
SStream_concat(O, "%s%s", ", ", markup("<imm:"));
|
|
printUInt32Bang(O, MCOperand_getImm(MO1) * Scale);
|
|
SStream_concat0(O, markup(">"));
|
|
} else {
|
|
printUInt64Bang(O, MCOperand_getImm(MO1));
|
|
}
|
|
SStream_concat0(O, "]");
|
|
}
|
|
|
|
void printRPRFMOperand(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_RPRFMOperand, OpNum);
|
|
unsigned prfop = MCOperand_getImm(MCInst_getOperand(MI, (OpNum)));
|
|
const AArch64PRFM_PRFM *PRFM =
|
|
AArch64RPRFM_lookupRPRFMByEncoding(prfop);
|
|
if (PRFM) {
|
|
SStream_concat0(O, PRFM->Name);
|
|
return;
|
|
}
|
|
|
|
printUInt32Bang(O, (prfop));
|
|
SStream_concat1(O, '\0');
|
|
}
|
|
|
|
#define DEFINE_printPrefetchOp(IsSVEPrefetch) \
|
|
void CONCAT(printPrefetchOp, \
|
|
IsSVEPrefetch)(MCInst * MI, unsigned OpNum, SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_1(MI, \
|
|
CONCAT(AArch64_OP_GROUP_PrefetchOp, \
|
|
IsSVEPrefetch), \
|
|
OpNum, IsSVEPrefetch); \
|
|
unsigned prfop = \
|
|
MCOperand_getImm(MCInst_getOperand(MI, (OpNum))); \
|
|
if (IsSVEPrefetch) { \
|
|
const AArch64SVEPRFM_SVEPRFM *PRFM = \
|
|
AArch64SVEPRFM_lookupSVEPRFMByEncoding(prfop); \
|
|
if (PRFM) { \
|
|
SStream_concat0(O, PRFM->Name); \
|
|
return; \
|
|
} \
|
|
} else { \
|
|
const AArch64PRFM_PRFM *PRFM = \
|
|
AArch64PRFM_lookupPRFMByEncoding(prfop); \
|
|
if (PRFM && \
|
|
AArch64_testFeatureList(MI->csh->mode, \
|
|
PRFM->FeaturesRequired)) { \
|
|
SStream_concat0(O, PRFM->Name); \
|
|
return; \
|
|
} \
|
|
} \
|
|
\
|
|
SStream_concat(O, "%s", markup("<imm:")); \
|
|
printUInt32Bang(O, (prfop)); \
|
|
SStream_concat0(O, markup(">")); \
|
|
}
|
|
DEFINE_printPrefetchOp(false);
|
|
DEFINE_printPrefetchOp(true);
|
|
|
|
void printPSBHintOp(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_PSBHintOp, OpNum);
|
|
unsigned psbhintop = MCOperand_getImm(MCInst_getOperand(MI, (OpNum)));
|
|
const AArch64PSBHint_PSB *PSB =
|
|
AArch64PSBHint_lookupPSBByEncoding(psbhintop);
|
|
if (PSB)
|
|
SStream_concat0(O, PSB->Name);
|
|
else {
|
|
SStream_concat(O, "%s", markup("<imm:"));
|
|
SStream_concat1(O, '#');
|
|
printUInt32Bang(O, (psbhintop));
|
|
SStream_concat0(O, markup(">"));
|
|
}
|
|
}
|
|
|
|
void printBTIHintOp(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_BTIHintOp, OpNum);
|
|
unsigned btihintop = MCOperand_getImm(MCInst_getOperand(MI, (OpNum))) ^
|
|
32;
|
|
const AArch64BTIHint_BTI *BTI =
|
|
AArch64BTIHint_lookupBTIByEncoding(btihintop);
|
|
if (BTI)
|
|
SStream_concat0(O, BTI->Name);
|
|
else {
|
|
SStream_concat(O, "%s", markup("<imm:"));
|
|
printUInt32Bang(O, (btihintop));
|
|
SStream_concat0(O, markup(">"));
|
|
}
|
|
}
|
|
|
|
static void printFPImmOperand(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_FPImmOperand, OpNum);
|
|
MCOperand *MO = MCInst_getOperand(MI, (OpNum));
|
|
float FPImm = MCOperand_isDFPImm(MO) ?
|
|
BitsToDouble(MCOperand_getImm(MO)) :
|
|
AArch64_AM_getFPImmFloat(MCOperand_getImm(MO));
|
|
|
|
// 8 decimal places are enough to perfectly represent permitted floats.
|
|
SStream_concat(O, "%s", markup("<imm:"));
|
|
SStream_concat(O, "#%.8f", FPImm);
|
|
SStream_concat0(O, markup(">"));
|
|
}
|
|
|
|
static unsigned getNextVectorRegister(unsigned Reg, unsigned Stride /* = 1 */)
|
|
{
|
|
while (Stride--) {
|
|
switch (Reg) {
|
|
default:
|
|
CS_ASSERT_RET_VAL(0 && "Vector register expected!", 0);
|
|
case AArch64_Q0:
|
|
Reg = AArch64_Q1;
|
|
break;
|
|
case AArch64_Q1:
|
|
Reg = AArch64_Q2;
|
|
break;
|
|
case AArch64_Q2:
|
|
Reg = AArch64_Q3;
|
|
break;
|
|
case AArch64_Q3:
|
|
Reg = AArch64_Q4;
|
|
break;
|
|
case AArch64_Q4:
|
|
Reg = AArch64_Q5;
|
|
break;
|
|
case AArch64_Q5:
|
|
Reg = AArch64_Q6;
|
|
break;
|
|
case AArch64_Q6:
|
|
Reg = AArch64_Q7;
|
|
break;
|
|
case AArch64_Q7:
|
|
Reg = AArch64_Q8;
|
|
break;
|
|
case AArch64_Q8:
|
|
Reg = AArch64_Q9;
|
|
break;
|
|
case AArch64_Q9:
|
|
Reg = AArch64_Q10;
|
|
break;
|
|
case AArch64_Q10:
|
|
Reg = AArch64_Q11;
|
|
break;
|
|
case AArch64_Q11:
|
|
Reg = AArch64_Q12;
|
|
break;
|
|
case AArch64_Q12:
|
|
Reg = AArch64_Q13;
|
|
break;
|
|
case AArch64_Q13:
|
|
Reg = AArch64_Q14;
|
|
break;
|
|
case AArch64_Q14:
|
|
Reg = AArch64_Q15;
|
|
break;
|
|
case AArch64_Q15:
|
|
Reg = AArch64_Q16;
|
|
break;
|
|
case AArch64_Q16:
|
|
Reg = AArch64_Q17;
|
|
break;
|
|
case AArch64_Q17:
|
|
Reg = AArch64_Q18;
|
|
break;
|
|
case AArch64_Q18:
|
|
Reg = AArch64_Q19;
|
|
break;
|
|
case AArch64_Q19:
|
|
Reg = AArch64_Q20;
|
|
break;
|
|
case AArch64_Q20:
|
|
Reg = AArch64_Q21;
|
|
break;
|
|
case AArch64_Q21:
|
|
Reg = AArch64_Q22;
|
|
break;
|
|
case AArch64_Q22:
|
|
Reg = AArch64_Q23;
|
|
break;
|
|
case AArch64_Q23:
|
|
Reg = AArch64_Q24;
|
|
break;
|
|
case AArch64_Q24:
|
|
Reg = AArch64_Q25;
|
|
break;
|
|
case AArch64_Q25:
|
|
Reg = AArch64_Q26;
|
|
break;
|
|
case AArch64_Q26:
|
|
Reg = AArch64_Q27;
|
|
break;
|
|
case AArch64_Q27:
|
|
Reg = AArch64_Q28;
|
|
break;
|
|
case AArch64_Q28:
|
|
Reg = AArch64_Q29;
|
|
break;
|
|
case AArch64_Q29:
|
|
Reg = AArch64_Q30;
|
|
break;
|
|
case AArch64_Q30:
|
|
Reg = AArch64_Q31;
|
|
break;
|
|
// Vector lists can wrap around.
|
|
case AArch64_Q31:
|
|
Reg = AArch64_Q0;
|
|
break;
|
|
case AArch64_Z0:
|
|
Reg = AArch64_Z1;
|
|
break;
|
|
case AArch64_Z1:
|
|
Reg = AArch64_Z2;
|
|
break;
|
|
case AArch64_Z2:
|
|
Reg = AArch64_Z3;
|
|
break;
|
|
case AArch64_Z3:
|
|
Reg = AArch64_Z4;
|
|
break;
|
|
case AArch64_Z4:
|
|
Reg = AArch64_Z5;
|
|
break;
|
|
case AArch64_Z5:
|
|
Reg = AArch64_Z6;
|
|
break;
|
|
case AArch64_Z6:
|
|
Reg = AArch64_Z7;
|
|
break;
|
|
case AArch64_Z7:
|
|
Reg = AArch64_Z8;
|
|
break;
|
|
case AArch64_Z8:
|
|
Reg = AArch64_Z9;
|
|
break;
|
|
case AArch64_Z9:
|
|
Reg = AArch64_Z10;
|
|
break;
|
|
case AArch64_Z10:
|
|
Reg = AArch64_Z11;
|
|
break;
|
|
case AArch64_Z11:
|
|
Reg = AArch64_Z12;
|
|
break;
|
|
case AArch64_Z12:
|
|
Reg = AArch64_Z13;
|
|
break;
|
|
case AArch64_Z13:
|
|
Reg = AArch64_Z14;
|
|
break;
|
|
case AArch64_Z14:
|
|
Reg = AArch64_Z15;
|
|
break;
|
|
case AArch64_Z15:
|
|
Reg = AArch64_Z16;
|
|
break;
|
|
case AArch64_Z16:
|
|
Reg = AArch64_Z17;
|
|
break;
|
|
case AArch64_Z17:
|
|
Reg = AArch64_Z18;
|
|
break;
|
|
case AArch64_Z18:
|
|
Reg = AArch64_Z19;
|
|
break;
|
|
case AArch64_Z19:
|
|
Reg = AArch64_Z20;
|
|
break;
|
|
case AArch64_Z20:
|
|
Reg = AArch64_Z21;
|
|
break;
|
|
case AArch64_Z21:
|
|
Reg = AArch64_Z22;
|
|
break;
|
|
case AArch64_Z22:
|
|
Reg = AArch64_Z23;
|
|
break;
|
|
case AArch64_Z23:
|
|
Reg = AArch64_Z24;
|
|
break;
|
|
case AArch64_Z24:
|
|
Reg = AArch64_Z25;
|
|
break;
|
|
case AArch64_Z25:
|
|
Reg = AArch64_Z26;
|
|
break;
|
|
case AArch64_Z26:
|
|
Reg = AArch64_Z27;
|
|
break;
|
|
case AArch64_Z27:
|
|
Reg = AArch64_Z28;
|
|
break;
|
|
case AArch64_Z28:
|
|
Reg = AArch64_Z29;
|
|
break;
|
|
case AArch64_Z29:
|
|
Reg = AArch64_Z30;
|
|
break;
|
|
case AArch64_Z30:
|
|
Reg = AArch64_Z31;
|
|
break;
|
|
// Vector lists can wrap around.
|
|
case AArch64_Z31:
|
|
Reg = AArch64_Z0;
|
|
break;
|
|
case AArch64_P0:
|
|
Reg = AArch64_P1;
|
|
break;
|
|
case AArch64_P1:
|
|
Reg = AArch64_P2;
|
|
break;
|
|
case AArch64_P2:
|
|
Reg = AArch64_P3;
|
|
break;
|
|
case AArch64_P3:
|
|
Reg = AArch64_P4;
|
|
break;
|
|
case AArch64_P4:
|
|
Reg = AArch64_P5;
|
|
break;
|
|
case AArch64_P5:
|
|
Reg = AArch64_P6;
|
|
break;
|
|
case AArch64_P6:
|
|
Reg = AArch64_P7;
|
|
break;
|
|
case AArch64_P7:
|
|
Reg = AArch64_P8;
|
|
break;
|
|
case AArch64_P8:
|
|
Reg = AArch64_P9;
|
|
break;
|
|
case AArch64_P9:
|
|
Reg = AArch64_P10;
|
|
break;
|
|
case AArch64_P10:
|
|
Reg = AArch64_P11;
|
|
break;
|
|
case AArch64_P11:
|
|
Reg = AArch64_P12;
|
|
break;
|
|
case AArch64_P12:
|
|
Reg = AArch64_P13;
|
|
break;
|
|
case AArch64_P13:
|
|
Reg = AArch64_P14;
|
|
break;
|
|
case AArch64_P14:
|
|
Reg = AArch64_P15;
|
|
break;
|
|
// Vector lists can wrap around.
|
|
case AArch64_P15:
|
|
Reg = AArch64_P0;
|
|
break;
|
|
}
|
|
}
|
|
return Reg;
|
|
}
|
|
|
|
#define DEFINE_printGPRSeqPairsClassOperand(size) \
|
|
void CONCAT(printGPRSeqPairsClassOperand, \
|
|
size)(MCInst * MI, unsigned OpNum, SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_1( \
|
|
MI, \
|
|
CONCAT(AArch64_OP_GROUP_GPRSeqPairsClassOperand, \
|
|
size), \
|
|
OpNum, size); \
|
|
CS_ASSERT_RET((size == 64 || size == 32) && \
|
|
"Template parameter must be either 32 or 64"); \
|
|
unsigned Reg = \
|
|
MCOperand_getReg(MCInst_getOperand(MI, (OpNum))); \
|
|
\
|
|
unsigned Sube = (size == 32) ? AArch64_sube32 : \
|
|
AArch64_sube64; \
|
|
unsigned Subo = (size == 32) ? AArch64_subo32 : \
|
|
AArch64_subo64; \
|
|
\
|
|
unsigned Even = MCRegisterInfo_getSubReg(MI->MRI, Reg, Sube); \
|
|
unsigned Odd = MCRegisterInfo_getSubReg(MI->MRI, Reg, Subo); \
|
|
printRegName(O, Even); \
|
|
SStream_concat0(O, ", "); \
|
|
printRegName(O, Odd); \
|
|
}
|
|
DEFINE_printGPRSeqPairsClassOperand(32);
|
|
DEFINE_printGPRSeqPairsClassOperand(64);
|
|
|
|
#define DEFINE_printMatrixIndex(Scale) \
|
|
void CONCAT(printMatrixIndex, Scale)(MCInst * MI, unsigned OpNum, \
|
|
SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_1(MI, CONCAT(AArch64_OP_GROUP_MatrixIndex, Scale), \
|
|
OpNum, Scale); \
|
|
printInt64(O, Scale *MCOperand_getImm( \
|
|
MCInst_getOperand(MI, (OpNum)))); \
|
|
}
|
|
DEFINE_printMatrixIndex(8);
|
|
DEFINE_printMatrixIndex(0);
|
|
DEFINE_printMatrixIndex(1);
|
|
|
|
void printMatrixTileList(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_MatrixTileList, OpNum);
|
|
unsigned MaxRegs = 8;
|
|
unsigned RegMask = MCOperand_getImm(MCInst_getOperand(MI, (OpNum)));
|
|
|
|
unsigned NumRegs = 0;
|
|
for (unsigned I = 0; I < MaxRegs; ++I)
|
|
if ((RegMask & (1 << I)) != 0)
|
|
++NumRegs;
|
|
|
|
SStream_concat0(O, "{");
|
|
unsigned Printed = 0;
|
|
for (unsigned I = 0; I < MaxRegs; ++I) {
|
|
unsigned Reg = RegMask & (1 << I);
|
|
if (Reg == 0)
|
|
continue;
|
|
printRegName(O, AArch64_ZAD0 + I);
|
|
if (Printed + 1 != NumRegs)
|
|
SStream_concat0(O, ", ");
|
|
++Printed;
|
|
}
|
|
SStream_concat0(O, "}");
|
|
}
|
|
|
|
void printVectorList(MCInst *MI, unsigned OpNum, SStream *O,
|
|
const char *LayoutSuffix)
|
|
{
|
|
unsigned Reg = MCOperand_getReg(MCInst_getOperand(MI, (OpNum)));
|
|
|
|
SStream_concat0(O, "{ ");
|
|
|
|
// Work out how many registers there are in the list (if there is an actual
|
|
// list).
|
|
unsigned NumRegs = 1;
|
|
if (MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(MI->MRI, AArch64_DDRegClassID),
|
|
Reg) ||
|
|
MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(MI->MRI, AArch64_ZPR2RegClassID),
|
|
Reg) ||
|
|
MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(MI->MRI, AArch64_QQRegClassID),
|
|
Reg) ||
|
|
MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(MI->MRI, AArch64_PPR2RegClassID),
|
|
Reg) ||
|
|
MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(MI->MRI,
|
|
AArch64_ZPR2StridedRegClassID),
|
|
Reg))
|
|
NumRegs = 2;
|
|
else if (MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(MI->MRI,
|
|
AArch64_DDDRegClassID),
|
|
Reg) ||
|
|
MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(MI->MRI,
|
|
AArch64_ZPR3RegClassID),
|
|
Reg) ||
|
|
MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(MI->MRI,
|
|
AArch64_QQQRegClassID),
|
|
Reg))
|
|
NumRegs = 3;
|
|
else if (MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(MI->MRI,
|
|
AArch64_DDDDRegClassID),
|
|
Reg) ||
|
|
MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(MI->MRI,
|
|
AArch64_ZPR4RegClassID),
|
|
Reg) ||
|
|
MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(MI->MRI,
|
|
AArch64_QQQQRegClassID),
|
|
Reg) ||
|
|
MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(
|
|
MI->MRI, AArch64_ZPR4StridedRegClassID),
|
|
Reg))
|
|
NumRegs = 4;
|
|
|
|
unsigned Stride = 1;
|
|
if (MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(MI->MRI,
|
|
AArch64_ZPR2StridedRegClassID),
|
|
Reg))
|
|
Stride = 8;
|
|
else if (MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(
|
|
MI->MRI, AArch64_ZPR4StridedRegClassID),
|
|
Reg))
|
|
Stride = 4;
|
|
|
|
// Now forget about the list and find out what the first register is.
|
|
if (MCRegisterInfo_getSubReg(MI->MRI, Reg, AArch64_dsub0))
|
|
Reg = MCRegisterInfo_getSubReg(MI->MRI, Reg, AArch64_dsub0);
|
|
else if (MCRegisterInfo_getSubReg(MI->MRI, Reg, AArch64_qsub0))
|
|
Reg = MCRegisterInfo_getSubReg(MI->MRI, Reg, AArch64_qsub0);
|
|
else if (MCRegisterInfo_getSubReg(MI->MRI, Reg, AArch64_zsub0))
|
|
Reg = MCRegisterInfo_getSubReg(MI->MRI, Reg, AArch64_zsub0);
|
|
else if (MCRegisterInfo_getSubReg(MI->MRI, Reg, AArch64_psub0))
|
|
Reg = MCRegisterInfo_getSubReg(MI->MRI, Reg, AArch64_psub0);
|
|
|
|
// If it's a D-reg, we need to promote it to the equivalent Q-reg before
|
|
// printing (otherwise getRegisterName fails).
|
|
if (MCRegisterClass_contains(MCRegisterInfo_getRegClass(
|
|
MI->MRI, AArch64_FPR64RegClassID),
|
|
Reg)) {
|
|
const MCRegisterClass *FPR128RC = MCRegisterInfo_getRegClass(
|
|
MI->MRI, AArch64_FPR128RegClassID);
|
|
Reg = MCRegisterInfo_getMatchingSuperReg(
|
|
MI->MRI, Reg, AArch64_dsub, FPR128RC);
|
|
}
|
|
|
|
if ((MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(MI->MRI, AArch64_ZPRRegClassID),
|
|
Reg) ||
|
|
MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(MI->MRI, AArch64_PPRRegClassID),
|
|
Reg)) &&
|
|
NumRegs > 1 && Stride == 1 &&
|
|
// Do not print the range when the last register is lower than the
|
|
// first. Because it is a wrap-around register.
|
|
Reg < getNextVectorRegister(Reg, NumRegs - 1)) {
|
|
printRegName(O, Reg);
|
|
SStream_concat0(O, LayoutSuffix);
|
|
if (NumRegs > 1) {
|
|
// Set of two sve registers should be separated by ','
|
|
const char *split_char = NumRegs == 2 ? ", " : " - ";
|
|
SStream_concat0(O, split_char);
|
|
printRegName(O,
|
|
(getNextVectorRegister(Reg, NumRegs - 1)));
|
|
SStream_concat0(O, LayoutSuffix);
|
|
}
|
|
} else {
|
|
for (unsigned i = 0; i < NumRegs;
|
|
++i, Reg = getNextVectorRegister(Reg, Stride)) {
|
|
// wrap-around sve register
|
|
if (MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(
|
|
MI->MRI, AArch64_ZPRRegClassID),
|
|
Reg) ||
|
|
MCRegisterClass_contains(
|
|
MCRegisterInfo_getRegClass(
|
|
MI->MRI, AArch64_PPRRegClassID),
|
|
Reg))
|
|
printRegName(O, Reg);
|
|
else
|
|
printRegNameAlt(O, Reg, AArch64_vreg);
|
|
SStream_concat0(O, LayoutSuffix);
|
|
if (i + 1 != NumRegs)
|
|
SStream_concat0(O, ", ");
|
|
}
|
|
}
|
|
SStream_concat0(O, " }");
|
|
}
|
|
|
|
void printImplicitlyTypedVectorList(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_ImplicitlyTypedVectorList,
|
|
OpNum);
|
|
printVectorList(MI, OpNum, O, "");
|
|
}
|
|
|
|
#define DEFINE_printTypedVectorList(NumLanes, LaneKind) \
|
|
void CONCAT(printTypedVectorList, CONCAT(NumLanes, LaneKind))( \
|
|
MCInst * MI, unsigned OpNum, SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_2( \
|
|
MI, \
|
|
CONCAT(CONCAT(AArch64_OP_GROUP_TypedVectorList, \
|
|
NumLanes), \
|
|
LaneKind), \
|
|
OpNum, NumLanes, CHAR(LaneKind)); \
|
|
if (CHAR(LaneKind) == '0') { \
|
|
printVectorList(MI, OpNum, O, ""); \
|
|
return; \
|
|
} \
|
|
char Suffix[32]; \
|
|
if (NumLanes) \
|
|
cs_snprintf(Suffix, sizeof(Suffix), ".%u%c", NumLanes, \
|
|
CHAR(LaneKind)); \
|
|
else \
|
|
cs_snprintf(Suffix, sizeof(Suffix), ".%c", \
|
|
CHAR(LaneKind)); \
|
|
\
|
|
printVectorList(MI, OpNum, O, ((const char *)&Suffix)); \
|
|
}
|
|
DEFINE_printTypedVectorList(0, b);
|
|
DEFINE_printTypedVectorList(0, d);
|
|
DEFINE_printTypedVectorList(0, h);
|
|
DEFINE_printTypedVectorList(0, s);
|
|
DEFINE_printTypedVectorList(0, q);
|
|
DEFINE_printTypedVectorList(16, b);
|
|
DEFINE_printTypedVectorList(1, d);
|
|
DEFINE_printTypedVectorList(2, d);
|
|
DEFINE_printTypedVectorList(2, s);
|
|
DEFINE_printTypedVectorList(4, h);
|
|
DEFINE_printTypedVectorList(4, s);
|
|
DEFINE_printTypedVectorList(8, b);
|
|
DEFINE_printTypedVectorList(8, h);
|
|
DEFINE_printTypedVectorList(0, 0);
|
|
|
|
#define DEFINE_printVectorIndex(Scale) \
|
|
void CONCAT(printVectorIndex, Scale)(MCInst * MI, unsigned OpNum, \
|
|
SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_1( \
|
|
MI, CONCAT(AArch64_OP_GROUP_VectorIndex, Scale), \
|
|
OpNum, Scale); \
|
|
SStream_concat(O, "%s", "["); \
|
|
printUInt64(O, Scale *MCOperand_getImm( \
|
|
MCInst_getOperand(MI, (OpNum)))); \
|
|
SStream_concat0(O, "]"); \
|
|
}
|
|
DEFINE_printVectorIndex(1);
|
|
DEFINE_printVectorIndex(8);
|
|
|
|
void printAlignedLabel(MCInst *MI, uint64_t Address, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_AlignedLabel, OpNum);
|
|
MCOperand *Op = MCInst_getOperand(MI, (OpNum));
|
|
|
|
// If the label has already been resolved to an immediate offset (say, when
|
|
// we're running the disassembler), just print the immediate.
|
|
if (MCOperand_isImm(Op)) {
|
|
SStream_concat0(O, markup("<imm:"));
|
|
int64_t Offset = MCOperand_getImm(Op) * 4;
|
|
if (MI->csh->PrintBranchImmAsAddress)
|
|
printUInt64(O, (Address + Offset));
|
|
else {
|
|
printUInt64Bang(O, (Offset));
|
|
}
|
|
SStream_concat0(O, markup(">"));
|
|
return;
|
|
}
|
|
|
|
printUInt64Bang(O, MCOperand_getImm(Op));
|
|
}
|
|
|
|
void printAdrLabel(MCInst *MI, uint64_t Address, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_AdrLabel, OpNum);
|
|
MCOperand *Op = MCInst_getOperand(MI, (OpNum));
|
|
|
|
// If the label has already been resolved to an immediate offset (say, when
|
|
// we're running the disassembler), just print the immediate.
|
|
if (MCOperand_isImm(Op)) {
|
|
const int64_t Offset = MCOperand_getImm(Op);
|
|
SStream_concat0(O, markup("<imm:"));
|
|
if (MI->csh->PrintBranchImmAsAddress)
|
|
printUInt64(O, ((Address & -4) + Offset));
|
|
else {
|
|
printUInt64Bang(O, Offset);
|
|
}
|
|
SStream_concat0(O, markup(">"));
|
|
return;
|
|
}
|
|
|
|
printUInt64Bang(O, MCOperand_getImm(Op));
|
|
}
|
|
|
|
void printAdrpLabel(MCInst *MI, uint64_t Address, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_AdrpLabel, OpNum);
|
|
MCOperand *Op = MCInst_getOperand(MI, (OpNum));
|
|
|
|
// If the label has already been resolved to an immediate offset (say, when
|
|
// we're running the disassembler), just print the immediate.
|
|
if (MCOperand_isImm(Op)) {
|
|
const int64_t Offset = MCOperand_getImm(Op) * 4096;
|
|
SStream_concat0(O, markup("<imm:"));
|
|
if (MI->csh->PrintBranchImmAsAddress)
|
|
printUInt64(O, ((Address & -4096) + Offset));
|
|
else {
|
|
printUInt64Bang(O, Offset);
|
|
}
|
|
SStream_concat0(O, markup(">"));
|
|
return;
|
|
}
|
|
|
|
printUInt64Bang(O, MCOperand_getImm(Op));
|
|
}
|
|
|
|
void printAdrAdrpLabel(MCInst *MI, uint64_t Address, unsigned OpNum, SStream *O) {
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_AdrAdrpLabel, OpNum);
|
|
MCOperand *Op = MCInst_getOperand(MI, (OpNum));
|
|
|
|
// If the label has already been resolved to an immediate offset (say, when
|
|
// we're running the disassembler), just print the immediate.
|
|
if (MCOperand_isImm(Op)) {
|
|
int64_t Offset = MCOperand_getImm(Op);
|
|
if (MCInst_getOpcode(MI) == AArch64_ADRP) {
|
|
Offset = Offset * 4096;
|
|
Address = Address & -4096;
|
|
}
|
|
SStream_concat0(O, markup(">"));
|
|
if (MI->csh->PrintBranchImmAsAddress)
|
|
printUInt64(O, (Address + Offset));
|
|
else {
|
|
printUInt64Bang(O, Offset);
|
|
}
|
|
SStream_concat0(O, markup(">"));
|
|
return;
|
|
}
|
|
|
|
printUInt64Bang(O, MCOperand_getImm(Op));
|
|
}
|
|
|
|
/// Not part of upstream LLVM.
|
|
/// Just prints the barrier options as documented in
|
|
/// https://github.com/AsahiLinux/docs/blob/main/docs/hw/cpu/apple-instructions.md
|
|
void printAppleSysBarrierOption(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_AppleSysBarrierOption, OpNo);
|
|
unsigned Val = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
switch (Val) {
|
|
default:
|
|
SStream_concat0(O, "<undefined>");
|
|
break;
|
|
case 0:
|
|
SStream_concat0(O, "osh");
|
|
break;
|
|
case 1:
|
|
SStream_concat0(O, "nsh");
|
|
break;
|
|
case 2:
|
|
SStream_concat0(O, "ish");
|
|
break;
|
|
case 3:
|
|
SStream_concat0(O, "sy");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void printBarrierOption(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_BarrierOption, OpNo);
|
|
unsigned Val = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
unsigned Opcode = MCInst_getOpcode(MI);
|
|
|
|
const char *Name;
|
|
if (Opcode == AArch64_ISB) {
|
|
const AArch64ISB_ISB *ISB = AArch64ISB_lookupISBByEncoding(Val);
|
|
Name = ISB ? ISB->Name : "";
|
|
} else if (Opcode == AArch64_TSB) {
|
|
const AArch64TSB_TSB *TSB = AArch64TSB_lookupTSBByEncoding(Val);
|
|
Name = TSB ? TSB->Name : "";
|
|
} else {
|
|
const AArch64DB_DB *DB = AArch64DB_lookupDBByEncoding(Val);
|
|
Name = DB ? DB->Name : "";
|
|
}
|
|
if (Name[0] != '\0')
|
|
SStream_concat0(O, Name);
|
|
else {
|
|
SStream_concat(O, "%s", markup("<imm:"));
|
|
printUInt32Bang(O, Val);
|
|
SStream_concat0(O, markup(">"));
|
|
}
|
|
}
|
|
|
|
void printBarriernXSOption(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_BarriernXSOption, OpNo);
|
|
unsigned Val = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
const char *Name;
|
|
const AArch64DBnXS_DBnXS *DB = AArch64DBnXS_lookupDBnXSByEncoding(Val);
|
|
Name = DB ? DB->Name : "";
|
|
|
|
if (Name[0] != '\0')
|
|
SStream_concat0(O, Name);
|
|
else {
|
|
SStream_concat(O, "%s%s%s", markup("<imm:"), "#", Val);
|
|
SStream_concat0(O, markup(">"));
|
|
}
|
|
}
|
|
|
|
static bool isValidSysReg(const AArch64SysReg_SysReg *Reg, bool Read,
|
|
unsigned mode)
|
|
{
|
|
return (Reg && (Read ? Reg->Readable : Reg->Writeable) &&
|
|
AArch64_testFeatureList(mode, Reg->FeaturesRequired));
|
|
}
|
|
|
|
// Looks up a system register either by encoding or by name. Some system
|
|
// registers share the same encoding between different architectures,
|
|
// therefore a tablegen lookup by encoding will return an entry regardless
|
|
// of the register's predication on a specific subtarget feature. To work
|
|
// around this problem we keep an alternative name for such registers and
|
|
// look them up by that name if the first lookup was unsuccessful.
|
|
static const AArch64SysReg_SysReg *lookupSysReg(unsigned Val, bool Read,
|
|
unsigned mode)
|
|
{
|
|
const AArch64SysReg_SysReg *Reg =
|
|
AArch64SysReg_lookupSysRegByEncoding(Val);
|
|
|
|
if (Reg && !isValidSysReg(Reg, Read, mode))
|
|
Reg = AArch64SysReg_lookupSysRegByName(Reg->AltName);
|
|
|
|
return Reg;
|
|
}
|
|
|
|
void printMRSSystemRegister(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_MRSSystemRegister, OpNo);
|
|
unsigned Val = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
// Horrible hack for the one register that has identical encodings but
|
|
// different names in MSR and MRS. Because of this, one of MRS and MSR is
|
|
// going to get the wrong entry
|
|
if (Val == AARCH64_SYSREG_DBGDTRRX_EL0) {
|
|
SStream_concat0(O, "DBGDTRRX_EL0");
|
|
return;
|
|
}
|
|
|
|
// Horrible hack for two different registers having the same encoding.
|
|
if (Val == AARCH64_SYSREG_TRCEXTINSELR) {
|
|
SStream_concat0(O, "TRCEXTINSELR");
|
|
return;
|
|
}
|
|
|
|
const AArch64SysReg_SysReg *Reg =
|
|
lookupSysReg(Val, true /*Read*/, MI->csh->mode);
|
|
|
|
if (isValidSysReg(Reg, true /*Read*/, MI->csh->mode))
|
|
SStream_concat0(O, Reg->Name);
|
|
else {
|
|
char result[AARCH64_GRS_LEN + 1] = { 0 };
|
|
AArch64SysReg_genericRegisterString(Val, result);
|
|
SStream_concat0(O, result);
|
|
}
|
|
}
|
|
|
|
void printMSRSystemRegister(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_MSRSystemRegister, OpNo);
|
|
unsigned Val = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
// Horrible hack for the one register that has identical encodings but
|
|
// different names in MSR and MRS. Because of this, one of MRS and MSR is
|
|
// going to get the wrong entry
|
|
if (Val == AARCH64_SYSREG_DBGDTRTX_EL0) {
|
|
SStream_concat0(O, "DBGDTRTX_EL0");
|
|
return;
|
|
}
|
|
|
|
// Horrible hack for two different registers having the same encoding.
|
|
if (Val == AARCH64_SYSREG_TRCEXTINSELR) {
|
|
SStream_concat0(O, "TRCEXTINSELR");
|
|
return;
|
|
}
|
|
|
|
const AArch64SysReg_SysReg *Reg =
|
|
lookupSysReg(Val, false /*Read*/, MI->csh->mode);
|
|
|
|
if (isValidSysReg(Reg, false /*Read*/, MI->csh->mode))
|
|
SStream_concat0(O, Reg->Name);
|
|
else {
|
|
char result[AARCH64_GRS_LEN + 1] = { 0 };
|
|
AArch64SysReg_genericRegisterString(Val, result);
|
|
SStream_concat0(O, result);
|
|
}
|
|
}
|
|
|
|
void printSystemPStateField(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_SystemPStateField, OpNo);
|
|
unsigned Val = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
const AArch64PState_PStateImm0_15 *PStateImm15 =
|
|
AArch64PState_lookupPStateImm0_15ByEncoding(Val);
|
|
const AArch64PState_PStateImm0_1 *PStateImm1 =
|
|
AArch64PState_lookupPStateImm0_1ByEncoding(Val);
|
|
if (PStateImm15 &&
|
|
AArch64_testFeatureList(MI->csh->mode,
|
|
PStateImm15->FeaturesRequired))
|
|
SStream_concat0(O, PStateImm15->Name);
|
|
else if (PStateImm1 &&
|
|
AArch64_testFeatureList(MI->csh->mode,
|
|
PStateImm1->FeaturesRequired))
|
|
SStream_concat0(O, PStateImm1->Name);
|
|
else {
|
|
printUInt32Bang(O, (Val));
|
|
SStream_concat1(O, '\0');
|
|
}
|
|
}
|
|
|
|
void printSIMDType10Operand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_SIMDType10Operand, OpNo);
|
|
unsigned RawVal = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
uint64_t Val = AArch64_AM_decodeAdvSIMDModImmType10(RawVal);
|
|
SStream_concat(O, "%s#%#016llx", markup("<imm:"), Val);
|
|
SStream_concat0(O, markup(">"));
|
|
}
|
|
|
|
#define DEFINE_printComplexRotationOp(Angle, Remainder) \
|
|
static void CONCAT(printComplexRotationOp, CONCAT(Angle, Remainder))( \
|
|
MCInst * MI, unsigned OpNo, SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_2( \
|
|
MI, \
|
|
CONCAT(CONCAT(AArch64_OP_GROUP_ComplexRotationOp, \
|
|
Angle), \
|
|
Remainder), \
|
|
OpNo, Angle, Remainder); \
|
|
unsigned Val = \
|
|
MCOperand_getImm(MCInst_getOperand(MI, (OpNo))); \
|
|
SStream_concat(O, "%s", markup("<imm:")); \
|
|
SStream_concat(O, "#%d", (Val * Angle) + Remainder); \
|
|
SStream_concat0(O, markup(">")); \
|
|
}
|
|
DEFINE_printComplexRotationOp(180, 90);
|
|
DEFINE_printComplexRotationOp(90, 0);
|
|
|
|
void printSVEPattern(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_SVEPattern, OpNum);
|
|
unsigned Val = MCOperand_getImm(MCInst_getOperand(MI, (OpNum)));
|
|
const AArch64SVEPredPattern_SVEPREDPAT *Pat =
|
|
AArch64SVEPredPattern_lookupSVEPREDPATByEncoding(Val);
|
|
if (Pat)
|
|
SStream_concat0(O, Pat->Name);
|
|
else
|
|
printUInt32Bang(O, Val);
|
|
}
|
|
|
|
void printSVEVecLenSpecifier(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_SVEVecLenSpecifier, OpNum);
|
|
unsigned Val = MCOperand_getImm(MCInst_getOperand(MI, (OpNum)));
|
|
// Pattern has only 1 bit
|
|
if (Val > 1)
|
|
CS_ASSERT_RET(0 && "Invalid vector length specifier");
|
|
const AArch64SVEVecLenSpecifier_SVEVECLENSPECIFIER *Pat =
|
|
AArch64SVEVecLenSpecifier_lookupSVEVECLENSPECIFIERByEncoding(
|
|
Val);
|
|
if (Pat)
|
|
SStream_concat0(O, Pat->Name);
|
|
}
|
|
|
|
#define DEFINE_printSVERegOp(suffix) \
|
|
void CONCAT(printSVERegOp, suffix)(MCInst * MI, unsigned OpNum, \
|
|
SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_1( \
|
|
MI, CONCAT(AArch64_OP_GROUP_SVERegOp, suffix), OpNum, \
|
|
CHAR(suffix)); \
|
|
switch (CHAR(suffix)) { \
|
|
case '0': \
|
|
case 'b': \
|
|
case 'h': \
|
|
case 's': \
|
|
case 'd': \
|
|
case 'q': \
|
|
break; \
|
|
default: \
|
|
CS_ASSERT_RET(0 && "Invalid kind specifier."); \
|
|
} \
|
|
\
|
|
unsigned Reg = \
|
|
MCOperand_getReg(MCInst_getOperand(MI, (OpNum))); \
|
|
printRegName(O, Reg); \
|
|
if (CHAR(suffix) != '0') { \
|
|
SStream_concat1(O, '.'); \
|
|
SStream_concat1(O, CHAR(suffix)); \
|
|
} \
|
|
}
|
|
DEFINE_printSVERegOp(b);
|
|
DEFINE_printSVERegOp(d);
|
|
DEFINE_printSVERegOp(h);
|
|
DEFINE_printSVERegOp(s);
|
|
DEFINE_printSVERegOp(0);
|
|
DEFINE_printSVERegOp(q);
|
|
|
|
#define DECLARE_printImmSVE_S32(T) \
|
|
void CONCAT(printImmSVE, T)(T Val, SStream * O) \
|
|
{ \
|
|
printInt32Bang(O, Val); \
|
|
}
|
|
DECLARE_printImmSVE_S32(int16_t);
|
|
DECLARE_printImmSVE_S32(int8_t);
|
|
DECLARE_printImmSVE_S32(int32_t);
|
|
|
|
#define DECLARE_printImmSVE_U32(T) \
|
|
void CONCAT(printImmSVE, T)(T Val, SStream * O) \
|
|
{ \
|
|
printUInt32Bang(O, Val); \
|
|
}
|
|
DECLARE_printImmSVE_U32(uint16_t);
|
|
DECLARE_printImmSVE_U32(uint8_t);
|
|
DECLARE_printImmSVE_U32(uint32_t);
|
|
|
|
#define DECLARE_printImmSVE_S64(T) \
|
|
void CONCAT(printImmSVE, T)(T Val, SStream * O) \
|
|
{ \
|
|
printInt64Bang(O, Val); \
|
|
}
|
|
DECLARE_printImmSVE_S64(int64_t);
|
|
|
|
#define DECLARE_printImmSVE_U64(T) \
|
|
void CONCAT(printImmSVE, T)(T Val, SStream * O) \
|
|
{ \
|
|
printUInt64Bang(O, Val); \
|
|
}
|
|
DECLARE_printImmSVE_U64(uint64_t);
|
|
|
|
#define DEFINE_isSignedType(T) \
|
|
static inline bool CONCAT(isSignedType, T)() \
|
|
{ \
|
|
return CHAR(T) == 'i'; \
|
|
}
|
|
DEFINE_isSignedType(int8_t);
|
|
DEFINE_isSignedType(int16_t);
|
|
DEFINE_isSignedType(int32_t);
|
|
DEFINE_isSignedType(int64_t);
|
|
DEFINE_isSignedType(uint8_t);
|
|
DEFINE_isSignedType(uint16_t);
|
|
DEFINE_isSignedType(uint32_t);
|
|
DEFINE_isSignedType(uint64_t);
|
|
|
|
#define DEFINE_printImm8OptLsl(T) \
|
|
void CONCAT(printImm8OptLsl, T)(MCInst * MI, unsigned OpNum, \
|
|
SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_1( \
|
|
MI, CONCAT(AArch64_OP_GROUP_Imm8OptLsl, T), OpNum, sizeof(T)); \
|
|
unsigned UnscaledVal = \
|
|
MCOperand_getImm(MCInst_getOperand(MI, (OpNum))); \
|
|
unsigned Shift = \
|
|
MCOperand_getImm(MCInst_getOperand(MI, (OpNum + 1))); \
|
|
\
|
|
if ((UnscaledVal == 0) && \
|
|
(AArch64_AM_getShiftValue(Shift) != 0)) { \
|
|
SStream_concat(O, "%s", markup("<imm:")); \
|
|
SStream_concat1(O, '#'); \
|
|
printUInt64(O, (UnscaledVal)); \
|
|
SStream_concat0(O, markup(">")); \
|
|
printShifter(MI, OpNum + 1, O); \
|
|
return; \
|
|
} \
|
|
\
|
|
T Val; \
|
|
if (CONCAT(isSignedType, T)()) \
|
|
Val = (int8_t)UnscaledVal * \
|
|
(1 << AArch64_AM_getShiftValue(Shift)); \
|
|
else \
|
|
Val = (uint8_t)UnscaledVal * \
|
|
(1 << AArch64_AM_getShiftValue(Shift)); \
|
|
\
|
|
CONCAT(printImmSVE, T)(Val, O); \
|
|
}
|
|
DEFINE_printImm8OptLsl(int16_t);
|
|
DEFINE_printImm8OptLsl(int8_t);
|
|
DEFINE_printImm8OptLsl(int64_t);
|
|
DEFINE_printImm8OptLsl(int32_t);
|
|
DEFINE_printImm8OptLsl(uint16_t);
|
|
DEFINE_printImm8OptLsl(uint8_t);
|
|
DEFINE_printImm8OptLsl(uint64_t);
|
|
DEFINE_printImm8OptLsl(uint32_t);
|
|
|
|
#define DEFINE_printSVELogicalImm(T) \
|
|
void CONCAT(printSVELogicalImm, T)(MCInst * MI, unsigned OpNum, \
|
|
SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_1( \
|
|
MI, CONCAT(AArch64_OP_GROUP_SVELogicalImm, T), OpNum, \
|
|
sizeof(T)); \
|
|
typedef T SignedT; \
|
|
typedef CONCATS(u, T) UnsignedT; \
|
|
\
|
|
uint64_t Val = \
|
|
MCOperand_getImm(MCInst_getOperand(MI, (OpNum))); \
|
|
UnsignedT PrintVal = \
|
|
AArch64_AM_decodeLogicalImmediate(Val, 64); \
|
|
\
|
|
if ((int16_t)PrintVal == (SignedT)PrintVal) \
|
|
CONCAT(printImmSVE, T)((T)PrintVal, O); \
|
|
else if ((uint16_t)PrintVal == PrintVal) \
|
|
CONCAT(printImmSVE, T)(PrintVal, O); \
|
|
else { \
|
|
SStream_concat(O, "%s", markup("<imm:")); \
|
|
printUInt64Bang(O, ((uint64_t)PrintVal)); \
|
|
SStream_concat0(O, markup(">")); \
|
|
} \
|
|
}
|
|
DEFINE_printSVELogicalImm(int16_t);
|
|
DEFINE_printSVELogicalImm(int32_t);
|
|
DEFINE_printSVELogicalImm(int64_t);
|
|
|
|
#define DEFINE_printZPRasFPR(Width) \
|
|
void CONCAT(printZPRasFPR, Width)(MCInst * MI, unsigned OpNum, \
|
|
SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_1( \
|
|
MI, CONCAT(AArch64_OP_GROUP_ZPRasFPR, Width), OpNum, \
|
|
Width); \
|
|
unsigned Base; \
|
|
switch (Width) { \
|
|
case 8: \
|
|
Base = AArch64_B0; \
|
|
break; \
|
|
case 16: \
|
|
Base = AArch64_H0; \
|
|
break; \
|
|
case 32: \
|
|
Base = AArch64_S0; \
|
|
break; \
|
|
case 64: \
|
|
Base = AArch64_D0; \
|
|
break; \
|
|
case 128: \
|
|
Base = AArch64_Q0; \
|
|
break; \
|
|
default: \
|
|
CS_ASSERT_RET(0 && "Unsupported width"); \
|
|
} \
|
|
unsigned Reg = \
|
|
MCOperand_getReg(MCInst_getOperand(MI, (OpNum))); \
|
|
printRegName(O, Reg - AArch64_Z0 + Base); \
|
|
}
|
|
DEFINE_printZPRasFPR(8);
|
|
DEFINE_printZPRasFPR(64);
|
|
DEFINE_printZPRasFPR(16);
|
|
DEFINE_printZPRasFPR(32);
|
|
DEFINE_printZPRasFPR(128);
|
|
|
|
#define DEFINE_printExactFPImm(ImmIs0, ImmIs1) \
|
|
void CONCAT(printExactFPImm, CONCAT(ImmIs0, ImmIs1))( \
|
|
MCInst * MI, unsigned OpNum, SStream *O) \
|
|
{ \
|
|
AArch64_add_cs_detail_2( \
|
|
MI, \
|
|
CONCAT(CONCAT(AArch64_OP_GROUP_ExactFPImm, ImmIs0), \
|
|
ImmIs1), \
|
|
OpNum, ImmIs0, ImmIs1); \
|
|
const AArch64ExactFPImm_ExactFPImm *Imm0Desc = \
|
|
AArch64ExactFPImm_lookupExactFPImmByEnum(ImmIs0); \
|
|
const AArch64ExactFPImm_ExactFPImm *Imm1Desc = \
|
|
AArch64ExactFPImm_lookupExactFPImmByEnum(ImmIs1); \
|
|
unsigned Val = \
|
|
MCOperand_getImm(MCInst_getOperand(MI, (OpNum))); \
|
|
SStream_concat(O, "%s%s%s", markup("<imm:"), "#", \
|
|
(Val ? Imm1Desc->Repr : Imm0Desc->Repr)); \
|
|
SStream_concat0(O, markup(">")); \
|
|
}
|
|
DEFINE_printExactFPImm(AArch64ExactFPImm_half, AArch64ExactFPImm_one);
|
|
DEFINE_printExactFPImm(AArch64ExactFPImm_zero, AArch64ExactFPImm_one);
|
|
DEFINE_printExactFPImm(AArch64ExactFPImm_half, AArch64ExactFPImm_two);
|
|
|
|
void printGPR64as32(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_GPR64as32, OpNum);
|
|
unsigned Reg = MCOperand_getReg(MCInst_getOperand(MI, (OpNum)));
|
|
printRegName(O, getWRegFromXReg(Reg));
|
|
}
|
|
|
|
void printGPR64x8(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_GPR64x8, OpNum);
|
|
unsigned Reg = MCOperand_getReg(MCInst_getOperand(MI, (OpNum)));
|
|
printRegName(O,
|
|
MCRegisterInfo_getSubReg(MI->MRI, Reg, AArch64_x8sub_0));
|
|
}
|
|
|
|
void printSyspXzrPair(MCInst *MI, unsigned OpNum, SStream *O)
|
|
{
|
|
AArch64_add_cs_detail_0(MI, AArch64_OP_GROUP_SyspXzrPair, OpNum);
|
|
unsigned Reg = MCOperand_getReg(MCInst_getOperand(MI, (OpNum)));
|
|
|
|
SStream_concat(O, "%s%s", getRegisterName(Reg, AArch64_NoRegAltName),
|
|
", ");
|
|
SStream_concat0(O, getRegisterName(Reg, AArch64_NoRegAltName));
|
|
}
|
|
|
|
const char *AArch64_LLVM_getRegisterName(unsigned RegNo, unsigned AltIdx)
|
|
{
|
|
return getRegisterName(RegNo, AltIdx);
|
|
}
|
|
|
|
void AArch64_LLVM_printInstruction(MCInst *MI, SStream *O,
|
|
void * /* MCRegisterInfo* */ info)
|
|
{
|
|
printInst(MI, MI->address, "", O);
|
|
}
|