mirror of
https://github.com/hedge-dev/XenonRecomp.git
synced 2025-06-06 18:31:03 +00:00
2555 lines
72 KiB
C
2555 lines
72 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;
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
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) \
|
|
{ \
|
|
add_cs_detail(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: \
|
|
assert(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) \
|
|
{ \
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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) \
|
|
{ \
|
|
add_cs_detail(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
|
|
assert(0 && "unknown operand kind in printPostIncOperand64");
|
|
}
|
|
|
|
void printVRegOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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) \
|
|
{ \
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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) \
|
|
{ \
|
|
add_cs_detail( \
|
|
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 \
|
|
assert((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) \
|
|
{ \
|
|
add_cs_detail(MI, \
|
|
CONCAT(AArch64_OP_GROUP_PredicateAsCounter, \
|
|
EltSize), \
|
|
OpNum, EltSize); \
|
|
unsigned Reg = \
|
|
MCOperand_getReg(MCInst_getOperand(MI, (OpNum))); \
|
|
if (Reg < AArch64_PN0 || Reg > AArch64_PN15) \
|
|
assert(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: \
|
|
assert(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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) \
|
|
{ \
|
|
add_cs_detail(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) \
|
|
{ \
|
|
add_cs_detail( \
|
|
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)
|
|
{
|
|
add_cs_detail(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) \
|
|
{ \
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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:
|
|
assert(0 && "Vector register expected!");
|
|
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) \
|
|
{ \
|
|
add_cs_detail(MI, \
|
|
CONCAT(AArch64_OP_GROUP_GPRSeqPairsClassOperand, \
|
|
size), \
|
|
OpNum, size); \
|
|
assert((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) \
|
|
{ \
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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) \
|
|
{ \
|
|
add_cs_detail(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) \
|
|
{ \
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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->PrintBranchImmNotAsAddress)
|
|
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)
|
|
{
|
|
add_cs_detail(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->PrintBranchImmNotAsAddress)
|
|
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)
|
|
{
|
|
add_cs_detail(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->PrintBranchImmNotAsAddress)
|
|
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) {
|
|
add_cs_detail(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->PrintBranchImmNotAsAddress)
|
|
printUInt64(O, (Address + Offset));
|
|
else {
|
|
printUInt64Bang(O, Offset);
|
|
}
|
|
SStream_concat0(O, markup(">"));
|
|
return;
|
|
}
|
|
|
|
printUInt64Bang(O, MCOperand_getImm(Op));
|
|
}
|
|
|
|
void printBarrierOption(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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) \
|
|
{ \
|
|
add_cs_detail( \
|
|
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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(MI, AArch64_OP_GROUP_SVEVecLenSpecifier, OpNum);
|
|
unsigned Val = MCOperand_getImm(MCInst_getOperand(MI, (OpNum)));
|
|
// Pattern has only 1 bit
|
|
if (Val > 1)
|
|
assert(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) \
|
|
{ \
|
|
add_cs_detail(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: \
|
|
assert(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) \
|
|
{ \
|
|
add_cs_detail(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) \
|
|
{ \
|
|
add_cs_detail(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) \
|
|
{ \
|
|
add_cs_detail(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: \
|
|
assert(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) \
|
|
{ \
|
|
add_cs_detail( \
|
|
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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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)
|
|
{
|
|
add_cs_detail(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);
|
|
}
|