summaryrefslogtreecommitdiff
path: root/arch/arm64/il.cpp
diff options
context:
space:
mode:
authorRusty Wagner <rusty.wagner@gmail.com>2024-03-05 19:50:13 -0500
committerRusty Wagner <rusty.wagner@gmail.com>2024-03-05 20:34:34 -0500
commite093c21ed880ac3eb72119be15093ee04f8ce299 (patch)
tree9f720ebdc0ae415734b1199ed341668c69710a94 /arch/arm64/il.cpp
parent0609276712622908254065546102381466033141 (diff)
Move architecture modules into the API repo
Diffstat (limited to 'arch/arm64/il.cpp')
-rw-r--r--arch/arm64/il.cpp2512
1 files changed, 2512 insertions, 0 deletions
diff --git a/arch/arm64/il.cpp b/arch/arm64/il.cpp
new file mode 100644
index 00000000..d3c3e943
--- /dev/null
+++ b/arch/arm64/il.cpp
@@ -0,0 +1,2512 @@
+#include "lowlevelilinstruction.h"
+#include <cstring>
+#include <inttypes.h>
+#include <stdarg.h>
+
+#include "il.h"
+#include "neon_intrinsics.h"
+#include "sysregs.h"
+
+using namespace BinaryNinja;
+
+#include "il_macros.h"
+
+static uint32_t GetFlagWriteTypeForEffect(FlagEffect e) {
+ switch (e) {
+ case FLAGEFFECT_SETS:
+ case FLAGEFFECT_SETS_NORMAL:
+ return IL_FLAG_WRITE_ALL;
+ case FLAGEFFECT_SETS_FLOAT:
+ return IL_FLAG_WRITE_ALL_FLOAT;
+ case FLAGEFFECT_NONE:
+ default:
+ return 0;
+ }
+}
+
+static ExprId GetCondition(LowLevelILFunction& il, Condition cond)
+{
+ switch (cond)
+ {
+ case COND_EQ:
+ return il.FlagGroup(IL_FLAG_GROUP_EQ);
+ case COND_NE:
+ return il.FlagGroup(IL_FLAG_GROUP_NE);
+ case COND_CS:
+ return il.FlagGroup(IL_FLAG_GROUP_CS);
+ case COND_CC:
+ return il.FlagGroup(IL_FLAG_GROUP_CC);
+ case COND_MI:
+ return il.FlagGroup(IL_FLAG_GROUP_MI);
+ case COND_PL:
+ return il.FlagGroup(IL_FLAG_GROUP_PL);
+ case COND_VS:
+ return il.FlagGroup(IL_FLAG_GROUP_VS);
+ case COND_VC:
+ return il.FlagGroup(IL_FLAG_GROUP_VC);
+ case COND_HI:
+ return il.FlagGroup(IL_FLAG_GROUP_HI);
+ case COND_LS:
+ return il.FlagGroup(IL_FLAG_GROUP_LS);
+ case COND_GE:
+ return il.FlagGroup(IL_FLAG_GROUP_GE);
+ case COND_LT:
+ return il.FlagGroup(IL_FLAG_GROUP_LT);
+ case COND_GT:
+ return il.FlagGroup(IL_FLAG_GROUP_GT);
+ case COND_LE:
+ return il.FlagGroup(IL_FLAG_GROUP_LE);
+ case COND_AL:
+ return il.Const(0, 1); // Always branch
+ case COND_NV:
+ default:
+ return il.Const(0, 0); // Never branch
+ }
+}
+
+static void GenIfElse(LowLevelILFunction& il, ExprId clause, ExprId trueCase, ExprId falseCase)
+{
+ if (falseCase)
+ {
+ LowLevelILLabel trueCode, falseCode, done;
+ il.AddInstruction(il.If(clause, trueCode, falseCode));
+ il.MarkLabel(trueCode);
+ il.AddInstruction(trueCase);
+ il.AddInstruction(il.Goto(done));
+ il.MarkLabel(falseCode);
+ il.AddInstruction(falseCase);
+ il.AddInstruction(il.Goto(done));
+ il.MarkLabel(done);
+ }
+ else
+ {
+ LowLevelILLabel trueCode, done;
+ il.AddInstruction(il.If(clause, trueCode, done));
+ il.MarkLabel(trueCode);
+ il.AddInstruction(trueCase);
+ il.MarkLabel(done);
+ }
+ return;
+}
+
+ExprId ExtractImmediate(LowLevelILFunction& il, InstructionOperand& operand, int sizeof_imm)
+{
+ if (operand.operandClass != IMM32 && operand.operandClass != IMM64)
+ return il.Unimplemented();
+
+ uint64_t imm = operand.immediate;
+
+ if (operand.shiftValueUsed)
+ {
+ switch (operand.shiftType)
+ {
+ case ShiftType_LSL:
+ imm = imm << operand.shiftValue;
+ break;
+ case ShiftType_LSR:
+ imm = imm >> operand.shiftValue;
+ break;
+ case ShiftType_MSL:
+ imm = (imm << operand.shiftValue) | ONES(operand.shiftValue);
+ break;
+ case ShiftType_ASR:
+ case ShiftType_ROR:
+ case ShiftType_UXTW:
+ case ShiftType_SXTW:
+ case ShiftType_SXTX:
+ case ShiftType_UXTX:
+ case ShiftType_SXTB:
+ case ShiftType_SXTH:
+ case ShiftType_UXTH:
+ case ShiftType_UXTB:
+ case ShiftType_END:
+ default:
+ return il.Unimplemented();
+ }
+ }
+
+ return ILCONST(sizeof_imm, imm & ONES(sizeof_imm * 8));
+}
+
+// extractSize can be smaller than the register, generating an LLIL_LOWPART
+// resultSize can be larger than the register, generating sign or zero extension
+ExprId ExtractRegister(LowLevelILFunction& il, InstructionOperand& operand, size_t regNum,
+ size_t extractSize, bool signExtend, size_t resultSize)
+{
+ size_t opsz = get_register_size(operand.reg[regNum]);
+
+ if (IS_ZERO_REG(operand.reg[regNum]))
+ return il.Const(resultSize, 0);
+
+ ExprId res = 0;
+
+ switch (operand.operandClass)
+ {
+ case SYS_REG:
+ res = il.Register(opsz, operand.sysreg);
+ break;
+ case REG:
+ default:
+ res = il.Register(opsz, operand.reg[regNum]);
+ break;
+ }
+
+ if (extractSize < opsz)
+ res = il.LowPart(extractSize, res);
+
+ if (extractSize < resultSize || opsz < extractSize)
+ {
+ if (signExtend)
+ res = il.SignExtend(resultSize, res);
+ else
+ res = il.ZeroExtend(resultSize, res);
+ }
+
+ return res;
+}
+
+static ExprId GetFloat(LowLevelILFunction& il, InstructionOperand& operand, int float_sz)
+{
+ if (operand.operandClass == FIMM32)
+ {
+ switch (float_sz)
+ {
+ case 2:
+ return il.FloatConstRaw(2, operand.immediate);
+ case 4:
+ return il.FloatConstSingle(*(float*)&(operand.immediate));
+ case 8:
+ return il.FloatConstDouble(*(float*)&(operand.immediate));
+ default:
+ break;
+ }
+ }
+ else if (operand.operandClass == REG)
+ {
+ return il.FloatConvert(
+ float_sz, ExtractRegister(il, operand, 0, REGSZ_O(operand), false, REGSZ_O(operand)));
+ }
+
+ return il.Unimplemented();
+}
+
+static ExprId GetShiftedRegister(
+ LowLevelILFunction& il, InstructionOperand& operand, size_t regNum, size_t resultSize)
+{
+ ExprId res;
+
+ // peel off the variants that return early
+ switch (operand.shiftType)
+ {
+ case ShiftType_NONE:
+ res = ExtractRegister(il, operand, regNum, REGSZ_O(operand), false, resultSize);
+ return res;
+ case ShiftType_ASR:
+ res = ExtractRegister(il, operand, regNum, REGSZ_O(operand), false, resultSize);
+ if (operand.shiftValue)
+ res = il.ArithShiftRight(resultSize, res, il.Const(1, operand.shiftValue));
+ return res;
+ case ShiftType_LSR:
+ res = ExtractRegister(il, operand, regNum, REGSZ_O(operand), false, resultSize);
+ if (operand.shiftValue)
+ res = il.LogicalShiftRight(resultSize, res, il.Const(1, operand.shiftValue));
+ return res;
+ case ShiftType_ROR:
+ res = ExtractRegister(il, operand, regNum, REGSZ_O(operand), false, resultSize);
+ if (operand.shiftValue)
+ res = il.RotateRight(resultSize, res, il.Const(1, operand.shiftValue));
+ return res;
+ default:
+ break;
+ }
+
+ // everything else falls through to maybe be left shifted
+ switch (operand.shiftType)
+ {
+ case ShiftType_LSL:
+ res = ExtractRegister(il, operand, regNum, REGSZ_O(operand), false, resultSize);
+ break;
+ case ShiftType_SXTB:
+ res = ExtractRegister(il, operand, regNum, 1, true, resultSize);
+ break;
+ case ShiftType_SXTH:
+ res = ExtractRegister(il, operand, regNum, 2, true, resultSize);
+ break;
+ case ShiftType_SXTW:
+ res = ExtractRegister(il, operand, regNum, 4, true, resultSize);
+ break;
+ case ShiftType_SXTX:
+ res = ExtractRegister(il, operand, regNum, 8, true, resultSize);
+ break;
+ case ShiftType_UXTB:
+ res = ExtractRegister(il, operand, regNum, 1, false, resultSize);
+ break;
+ case ShiftType_UXTH:
+ res = ExtractRegister(il, operand, regNum, 2, false, resultSize);
+ break;
+ case ShiftType_UXTW:
+ res = ExtractRegister(il, operand, regNum, 4, false, resultSize);
+ break;
+ case ShiftType_UXTX:
+ res = ExtractRegister(il, operand, regNum, 8, false, resultSize);
+ break;
+ default:
+ il.AddInstruction(il.Unimplemented());
+ return il.Unimplemented();
+ }
+
+ if (operand.shiftValue)
+ res = il.ShiftLeft(resultSize, res, il.Const(1, operand.shiftValue));
+
+ return res;
+}
+
+static ExprId GetILOperandPreOrPostIndex(LowLevelILFunction& il, InstructionOperand& operand)
+{
+ if (operand.operandClass != MEM_PRE_IDX && operand.operandClass != MEM_POST_IDX)
+ return 0;
+
+ if (operand.reg[1] == REG_NONE)
+ {
+ // ..., [Xn], #imm
+ if (IMM_O(operand) == 0)
+ return 0;
+
+ return ILSETREG_O(operand, ILADDREG_O(operand, il.Const(REGSZ_O(operand), IMM_O(operand))));
+ }
+ else
+ {
+ // ..., [Xn], <Xm>
+ return ILSETREG_O(operand, ILADDREG_O(operand, il.Register(8, operand.reg[1])));
+ }
+}
+
+/* Returns an expression that does any pre-incrementing on an operand, if it exists */
+static ExprId GetILOperandPreIndex(LowLevelILFunction& il, InstructionOperand& operand)
+{
+ if (operand.operandClass != MEM_PRE_IDX)
+ return 0;
+
+ return GetILOperandPreOrPostIndex(il, operand);
+}
+
+/* Returns an expression that does any post-incrementing on an operand, if it exists */
+static ExprId GetILOperandPostIndex(LowLevelILFunction& il, InstructionOperand& operand)
+{
+ if (operand.operandClass != MEM_POST_IDX)
+ return 0;
+
+ return GetILOperandPreOrPostIndex(il, operand);
+}
+
+/* Returns an IL expression that reads (and only reads) from the operand.
+ It accounts for, but does not generate IL that executes, pre and post indexing.
+ The operand class can be overridden.
+ An additional offset can be applied, convenient for calculating sequential loads and stores. */
+static ExprId GetILOperandEffectiveAddress(LowLevelILFunction& il, InstructionOperand& operand,
+ size_t addrSize, OperandClass oclass, size_t extra_offset)
+{
+ ExprId addr = 0;
+ if (oclass == NONE)
+ oclass = operand.operandClass;
+ switch (oclass)
+ {
+ case MEM_REG: // ldr x0, [x1]
+ case MEM_POST_IDX: // ldr w0, [x1], #4
+ addr = ILREG_O(operand);
+ if (extra_offset)
+ addr = il.Add(addrSize, addr, il.Const(addrSize, extra_offset));
+ break;
+ case MEM_OFFSET: // ldr w0, [x1, #4]
+ case MEM_PRE_IDX: // ldr w0, [x1, #4]!
+ addr = il.Add(addrSize, ILREG_O(operand), il.Const(addrSize, operand.immediate + extra_offset));
+ break;
+ case MEM_EXTENDED:
+ if (operand.shiftType == ShiftType_NONE)
+ {
+ addr =
+ il.Add(addrSize, ILREG_O(operand), il.Const(addrSize, operand.immediate + extra_offset));
+ }
+ else if (operand.shiftType == ShiftType_LSL)
+ {
+ if (extra_offset)
+ {
+ addr = il.Add(addrSize, ILREG_O(operand),
+ il.Add(addrSize,
+ il.ShiftLeft(addrSize, il.Const(addrSize, operand.immediate),
+ il.Const(1, operand.shiftValue)),
+ il.Const(addrSize, extra_offset)));
+ }
+ else
+ {
+ addr = il.Add(addrSize, ILREG_O(operand),
+ il.ShiftLeft(
+ addrSize, il.Const(addrSize, operand.immediate), il.Const(1, operand.shiftValue)));
+ }
+ }
+ else
+ {
+ // printf("ERROR: dunno how to handle MEM_EXTENDED shiftType %d\n", operand.shiftType);
+ ABORT_LIFT;
+ }
+ break;
+ default:
+ // printf("ERROR: dunno how to handle operand class %d\n", oclass);
+ ABORT_LIFT;
+ }
+ return addr;
+}
+
+
+static size_t ReadILOperand(LowLevelILFunction& il, InstructionOperand& operand, size_t resultSize)
+{
+ switch (operand.operandClass)
+ {
+ case IMM32:
+ case IMM64:
+ if (operand.shiftType != ShiftType_NONE && operand.shiftValue)
+ return il.Const(resultSize, operand.immediate << operand.shiftValue);
+ else
+ return il.Const(resultSize, operand.immediate);
+ case LABEL:
+ return il.ConstPointer(8, operand.immediate);
+ case REG:
+ if (IS_ZERO_REG(operand.reg[0]))
+ return il.Const(resultSize, 0);
+ return GetShiftedRegister(il, operand, 0, resultSize);
+ case MEM_REG:
+ return il.Load(resultSize, il.Register(8, operand.reg[0]));
+ case MEM_OFFSET:
+ if (operand.immediate != 0)
+ return il.Load(
+ resultSize, il.Add(8, il.Register(8, operand.reg[0]), il.Const(8, operand.immediate)));
+ else
+ return il.Load(resultSize, il.Register(8, operand.reg[0]));
+ case MEM_EXTENDED:
+ return il.Load(resultSize, GetILOperandEffectiveAddress(il, operand, resultSize, NONE, 0));
+ case MEM_PRE_IDX:
+ case MEM_POST_IDX:
+ case MULTI_REG:
+ case FIMM32:
+ return GetFloat(il, operand, resultSize);
+ case NONE:
+ default:
+ return il.Unimplemented();
+ }
+}
+
+unsigned v_unpack_lookup_sz[15] = {0, 1, 2, 4, 8, 16, 1, 2, 4, 8, 1, 2, 4, 1, 1};
+
+extern "C" Register* v_unpack_lookup[15][32];
+
+Register v_consolidate_lookup[32][15] = {
+ // NONE .q .2d .4s .8h .16b .d .2s .4h .8b .s .2h .4b .h .b
+ {REG_V0, REG_V0, REG_V0, REG_V0, REG_V0, REG_V0, REG_V0_D0, REG_V0_D0, REG_V0_D0, REG_V0_D0,
+ REG_V0_S0, REG_V0_S0, REG_V0_S0, REG_V0_H0, REG_V0_B0},
+ {REG_V1, REG_V1, REG_V1, REG_V1, REG_V1, REG_V1, REG_V1_D0, REG_V1_D0, REG_V1_D0, REG_V1_D0,
+ REG_V1_S0, REG_V1_S0, REG_V1_S0, REG_V1_H0, REG_V1_B0},
+ {REG_V2, REG_V2, REG_V2, REG_V2, REG_V2, REG_V2, REG_V2_D0, REG_V2_D0, REG_V2_D0, REG_V2_D0,
+ REG_V2_S0, REG_V2_S0, REG_V2_S0, REG_V2_H0, REG_V2_B0},
+ {REG_V3, REG_V3, REG_V3, REG_V3, REG_V3, REG_V3, REG_V3_D0, REG_V3_D0, REG_V3_D0, REG_V3_D0,
+ REG_V3_S0, REG_V3_S0, REG_V3_S0, REG_V3_H0, REG_V3_B0},
+ {REG_V4, REG_V4, REG_V4, REG_V4, REG_V4, REG_V4, REG_V4_D0, REG_V4_D0, REG_V4_D0, REG_V4_D0,
+ REG_V4_S0, REG_V4_S0, REG_V4_S0, REG_V4_H0, REG_V4_B0},
+ {REG_V5, REG_V5, REG_V5, REG_V5, REG_V5, REG_V5, REG_V5_D0, REG_V5_D0, REG_V5_D0, REG_V5_D0,
+ REG_V5_S0, REG_V5_S0, REG_V5_S0, REG_V5_H0, REG_V5_B0},
+ {REG_V6, REG_V6, REG_V6, REG_V6, REG_V6, REG_V6, REG_V6_D0, REG_V6_D0, REG_V6_D0, REG_V6_D0,
+ REG_V6_S0, REG_V6_S0, REG_V6_S0, REG_V6_H0, REG_V6_B0},
+ {REG_V7, REG_V7, REG_V7, REG_V7, REG_V7, REG_V7, REG_V7_D0, REG_V7_D0, REG_V7_D0, REG_V7_D0,
+ REG_V7_S0, REG_V7_S0, REG_V7_S0, REG_V7_H0, REG_V7_B0},
+ {REG_V8, REG_V8, REG_V8, REG_V8, REG_V8, REG_V8, REG_V8_D0, REG_V8_D0, REG_V8_D0, REG_V8_D0,
+ REG_V8_S0, REG_V8_S0, REG_V8_S0, REG_V8_H0, REG_V8_B0},
+ {REG_V9, REG_V9, REG_V9, REG_V9, REG_V9, REG_V9, REG_V9_D0, REG_V9_D0, REG_V9_D0, REG_V9_D0,
+ REG_V9_S0, REG_V9_S0, REG_V9_S0, REG_V9_H0, REG_V9_B0},
+ {REG_V10, REG_V10, REG_V10, REG_V10, REG_V10, REG_V10, REG_V10_D0, REG_V10_D0, REG_V10_D0,
+ REG_V10_D0, REG_V10_S0, REG_V10_S0, REG_V10_S0, REG_V10_H0, REG_V10_B0},
+ {REG_V11, REG_V11, REG_V11, REG_V11, REG_V11, REG_V11, REG_V11_D0, REG_V11_D0, REG_V11_D0,
+ REG_V11_D0, REG_V11_S0, REG_V11_S0, REG_V11_S0, REG_V11_H0, REG_V11_B0},
+ {REG_V12, REG_V12, REG_V12, REG_V12, REG_V12, REG_V12, REG_V12_D0, REG_V12_D0, REG_V12_D0,
+ REG_V12_D0, REG_V12_S0, REG_V12_S0, REG_V12_S0, REG_V12_H0, REG_V12_B0},
+ {REG_V13, REG_V13, REG_V13, REG_V13, REG_V13, REG_V13, REG_V13_D0, REG_V13_D0, REG_V13_D0,
+ REG_V13_D0, REG_V13_S0, REG_V13_S0, REG_V13_S0, REG_V13_H0, REG_V13_B0},
+ {REG_V14, REG_V14, REG_V14, REG_V14, REG_V14, REG_V14, REG_V14_D0, REG_V14_D0, REG_V14_D0,
+ REG_V14_D0, REG_V14_S0, REG_V14_S0, REG_V14_S0, REG_V14_H0, REG_V14_B0},
+ {REG_V15, REG_V15, REG_V15, REG_V15, REG_V15, REG_V15, REG_V15_D0, REG_V15_D0, REG_V15_D0,
+ REG_V15_D0, REG_V15_S0, REG_V15_S0, REG_V15_S0, REG_V15_H0, REG_V15_B0},
+ {REG_V16, REG_V16, REG_V16, REG_V16, REG_V16, REG_V16, REG_V16_D0, REG_V16_D0, REG_V16_D0,
+ REG_V16_D0, REG_V16_S0, REG_V16_S0, REG_V16_S0, REG_V16_H0, REG_V16_B0},
+ {REG_V17, REG_V17, REG_V17, REG_V17, REG_V17, REG_V17, REG_V17_D0, REG_V17_D0, REG_V17_D0,
+ REG_V17_D0, REG_V17_S0, REG_V17_S0, REG_V17_S0, REG_V17_H0, REG_V17_B0},
+ {REG_V18, REG_V18, REG_V18, REG_V18, REG_V18, REG_V18, REG_V18_D0, REG_V18_D0, REG_V18_D0,
+ REG_V18_D0, REG_V18_S0, REG_V18_S0, REG_V18_S0, REG_V18_H0, REG_V18_B0},
+ {REG_V19, REG_V19, REG_V19, REG_V19, REG_V19, REG_V19, REG_V19_D0, REG_V19_D0, REG_V19_D0,
+ REG_V19_D0, REG_V19_S0, REG_V19_S0, REG_V19_S0, REG_V19_H0, REG_V19_B0},
+ {REG_V20, REG_V20, REG_V20, REG_V20, REG_V20, REG_V20, REG_V20_D0, REG_V20_D0, REG_V20_D0,
+ REG_V20_D0, REG_V20_S0, REG_V20_S0, REG_V20_S0, REG_V20_H0, REG_V20_B0},
+ {REG_V21, REG_V21, REG_V21, REG_V21, REG_V21, REG_V21, REG_V21_D0, REG_V21_D0, REG_V21_D0,
+ REG_V21_D0, REG_V21_S0, REG_V21_S0, REG_V21_S0, REG_V21_H0, REG_V21_B0},
+ {REG_V22, REG_V22, REG_V22, REG_V22, REG_V22, REG_V22, REG_V22_D0, REG_V22_D0, REG_V22_D0,
+ REG_V22_D0, REG_V22_S0, REG_V22_S0, REG_V22_S0, REG_V22_H0, REG_V22_B0},
+ {REG_V23, REG_V23, REG_V23, REG_V23, REG_V23, REG_V23, REG_V23_D0, REG_V23_D0, REG_V23_D0,
+ REG_V23_D0, REG_V23_S0, REG_V23_S0, REG_V23_S0, REG_V23_H0, REG_V23_B0},
+ {REG_V24, REG_V24, REG_V24, REG_V24, REG_V24, REG_V24, REG_V24_D0, REG_V24_D0, REG_V24_D0,
+ REG_V24_D0, REG_V24_S0, REG_V24_S0, REG_V24_S0, REG_V24_H0, REG_V24_B0},
+ {REG_V25, REG_V25, REG_V25, REG_V25, REG_V25, REG_V25, REG_V25_D0, REG_V25_D0, REG_V25_D0,
+ REG_V25_D0, REG_V25_S0, REG_V25_S0, REG_V25_S0, REG_V25_H0, REG_V25_B0},
+ {REG_V26, REG_V26, REG_V26, REG_V26, REG_V26, REG_V26, REG_V26_D0, REG_V26_D0, REG_V26_D0,
+ REG_V26_D0, REG_V26_S0, REG_V26_S0, REG_V26_S0, REG_V26_H0, REG_V26_B0},
+ {REG_V27, REG_V27, REG_V27, REG_V27, REG_V27, REG_V27, REG_V27_D0, REG_V27_D0, REG_V27_D0,
+ REG_V27_D0, REG_V27_S0, REG_V27_S0, REG_V27_S0, REG_V27_H0, REG_V27_B0},
+ {REG_V28, REG_V28, REG_V28, REG_V28, REG_V28, REG_V28, REG_V28_D0, REG_V28_D0, REG_V28_D0,
+ REG_V28_D0, REG_V28_S0, REG_V28_S0, REG_V28_S0, REG_V28_H0, REG_V28_B0},
+ {REG_V29, REG_V29, REG_V29, REG_V29, REG_V29, REG_V29, REG_V29_D0, REG_V29_D0, REG_V29_D0,
+ REG_V29_D0, REG_V29_S0, REG_V29_S0, REG_V29_S0, REG_V29_H0, REG_V29_B0},
+ {REG_V30, REG_V30, REG_V30, REG_V30, REG_V30, REG_V30, REG_V30_D0, REG_V30_D0, REG_V30_D0,
+ REG_V30_D0, REG_V30_S0, REG_V30_S0, REG_V30_S0, REG_V30_H0, REG_V30_B0},
+ {REG_V31, REG_V31, REG_V31, REG_V31, REG_V31, REG_V31, REG_V31_D0, REG_V31_D0, REG_V31_D0,
+ REG_V31_D0, REG_V31_S0, REG_V31_S0, REG_V31_S0, REG_V31_H0, REG_V31_B0},
+};
+
+/* v28.d[1] -> REG_V0_D1 */
+static Register vector_reg_minimize(InstructionOperand& oper)
+{
+ if (!IS_ASIMD_O(oper))
+ return REG_NONE;
+
+ if (oper.arrSpec == ARRSPEC_NONE)
+ {
+ if (oper.laneUsed)
+ return REG_NONE; // cannot have lane without an arrangement spec
+ return oper.reg[0];
+ }
+
+ int vidx = oper.reg[0] - REG_V0;
+ if (vidx < 0 || vidx > 31)
+ return REG_NONE;
+
+ if (oper.laneUsed)
+ {
+ switch (oper.arrSpec)
+ {
+ case ARRSPEC_FULL:
+ return oper.reg[0];
+ case ARRSPEC_1DOUBLE:
+ case ARRSPEC_2DOUBLES:
+ if (oper.lane >= 2)
+ return REG_NONE;
+ return v_unpack_lookup[ARRSPEC_2DOUBLES][vidx][oper.lane];
+ case ARRSPEC_1SINGLE:
+ case ARRSPEC_2SINGLES:
+ case ARRSPEC_4SINGLES:
+ if (oper.lane >= 4)
+ return REG_NONE;
+ return v_unpack_lookup[ARRSPEC_4SINGLES][vidx][oper.lane];
+ case ARRSPEC_1HALF:
+ case ARRSPEC_2HALVES:
+ case ARRSPEC_4HALVES:
+ case ARRSPEC_8HALVES:
+ if (oper.lane >= 8)
+ return REG_NONE;
+ return v_unpack_lookup[ARRSPEC_8HALVES][vidx][oper.lane];
+ case ARRSPEC_1BYTE:
+ case ARRSPEC_4BYTES:
+ case ARRSPEC_8BYTES:
+ case ARRSPEC_16BYTES:
+ if (oper.lane >= 16)
+ return REG_NONE;
+ return v_unpack_lookup[ARRSPEC_16BYTES][vidx][oper.lane];
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch (oper.arrSpec)
+ {
+ case ARRSPEC_FULL:
+ case ARRSPEC_2DOUBLES:
+ case ARRSPEC_4SINGLES:
+ case ARRSPEC_8HALVES:
+ case ARRSPEC_16BYTES:
+ return oper.reg[0];
+ case ARRSPEC_1DOUBLE:
+ case ARRSPEC_2SINGLES:
+ case ARRSPEC_4HALVES:
+ case ARRSPEC_8BYTES:
+ return v_unpack_lookup[ARRSPEC_2DOUBLES][vidx][0];
+ case ARRSPEC_1SINGLE:
+ case ARRSPEC_2HALVES:
+ case ARRSPEC_4BYTES:
+ return v_unpack_lookup[ARRSPEC_4SINGLES][vidx][0];
+ case ARRSPEC_1HALF:
+ // case ARRSPEC_2BYTE
+ return v_unpack_lookup[ARRSPEC_8HALVES][vidx][0];
+ case ARRSPEC_1BYTE:
+ return v_unpack_lookup[ARRSPEC_16BYTES][vidx][0];
+ default:
+ break;
+ }
+ }
+
+ return REG_NONE;
+}
+
+/* "promote" the spec to full width so lane can select any */
+static ArrangementSpec promote_spec(ArrangementSpec spec)
+{
+ switch (spec)
+ {
+ case ARRSPEC_1DOUBLE:
+ return ARRSPEC_2DOUBLES;
+ case ARRSPEC_1SINGLE:
+ case ARRSPEC_2SINGLES:
+ return ARRSPEC_4SINGLES;
+ case ARRSPEC_1HALF:
+ case ARRSPEC_2HALVES:
+ case ARRSPEC_4HALVES:
+ return ARRSPEC_8HALVES;
+ case ARRSPEC_1BYTE:
+ case ARRSPEC_4BYTES:
+ case ARRSPEC_8BYTES:
+ return ARRSPEC_16BYTES;
+ default:
+ return spec;
+ }
+}
+
+static int unpack_vector(InstructionOperand& oper, Register* result)
+{
+ if (oper.operandClass == REG)
+ {
+ /* register without an arrangement specification is just a register
+ examples: "d18", "d6", "v7" */
+ if (oper.arrSpec == ARRSPEC_NONE)
+ {
+ result[0] = oper.reg[0];
+ return 1;
+ }
+
+ /* require V register with valid arrangement spec
+ examples: "v17.2s", "v8.4h", "v21.8b" */
+ if (oper.reg[0] < REG_V0 || oper.reg[0] > REG_V31)
+ return 0;
+ if (oper.arrSpec <= ARRSPEC_NONE || oper.arrSpec > ARRSPEC_1BYTE)
+ return 0;
+
+ /* lookup, copy result */
+ if (oper.laneUsed)
+ {
+ ArrangementSpec spec = promote_spec(oper.arrSpec);
+
+ int n_lanes = v_unpack_lookup_sz[spec];
+
+ if (oper.lane >= n_lanes)
+ return 0;
+
+ // int n = v_unpack_lookup_sz[spec];
+ // for (int i = 0; i < n; ++i)
+ result[0] = v_unpack_lookup[spec][oper.reg[0] - REG_V0][oper.lane];
+
+ return 1;
+ }
+
+ int n = v_unpack_lookup_sz[oper.arrSpec];
+ for (int i = 0; i < n; ++i)
+ result[i] = v_unpack_lookup[oper.arrSpec][oper.reg[0] - REG_V0][i];
+ return n;
+ }
+ else if (oper.operandClass == MULTI_REG)
+ {
+ if (oper.laneUsed)
+ {
+ /* multireg with a lane
+ examples: "ld2 {v17.d, v18.d}[1], [x20]" */
+
+ ArrangementSpec spec = promote_spec(oper.arrSpec);
+
+ int n = 0;
+ for (int i = 0; i < 4 && oper.reg[i] != REG_NONE; i++)
+ {
+ int n_lanes = v_unpack_lookup_sz[spec];
+ if (oper.lane >= n_lanes)
+ return 0;
+ result[i] = v_unpack_lookup[spec][oper.reg[i] - REG_V0][oper.lane];
+ n += 1;
+ }
+ return n;
+ }
+ else
+ {
+ /* multireg without a lane
+ examples: "{v0.8b, v1.8b}", "{v8.2s, v9.2s}" */
+ if (oper.arrSpec < ARRSPEC_NONE || oper.arrSpec > ARRSPEC_1BYTE)
+ return 0;
+
+ int n = 0;
+ for (int i = 0; i < 4 && oper.reg[i] != REG_NONE; i++)
+ {
+ result[i] = v_consolidate_lookup[oper.reg[i] - REG_V0][oper.arrSpec];
+ n += 1;
+ }
+ return n;
+ }
+ }
+
+ return 0;
+}
+
+/* if we have two operands that have the same arrangement spec, instead of treating them as
+ distinct sets of registers, see if we can consolidate the set of registers into a single
+ larger register. This allows us to easily lift things like 'mov v0.16b, v1.16b' as
+ 'mov v0, v1' */
+static int consolidate_vector(
+ InstructionOperand& operand1,
+ InstructionOperand& operand2,
+ Register *result)
+{
+ /* make sure both our operand classes are single regs */
+ if (operand1.operandClass != REG || operand2.operandClass != REG)
+ return 0;
+
+ /* make sure our arrSpec's match. We need this to deal with cases where the arrSpec might
+ have different sizes, e.g. 'uxtl v2.2d, v8.2s'.*/
+ if (operand1.arrSpec != operand2.arrSpec)
+ return 0;
+
+ result[0] = v_consolidate_lookup[operand1.reg[0]-REG_V0][operand1.arrSpec];
+ result[1] = v_consolidate_lookup[operand2.reg[0]-REG_V0][operand2.arrSpec];
+
+ return 1;
+}
+
+static void LoadStoreOperandPair(LowLevelILFunction& il, bool load, InstructionOperand& operand1,
+ InstructionOperand& operand2, InstructionOperand& operand3)
+{
+ unsigned sz = REGSZ_O(operand1);
+
+ /* do pre-indexing */
+ ExprId tmp = GetILOperandPreIndex(il, operand3);
+ if (tmp)
+ il.AddInstruction(tmp);
+
+ /* compute addresses */
+ OperandClass oclass = (operand3.operandClass == MEM_PRE_IDX) ? MEM_REG : operand3.operandClass;
+ ExprId addr0 = GetILOperandEffectiveAddress(il, operand3, 8, oclass, 0);
+ ExprId addr1 = GetILOperandEffectiveAddress(il, operand3, 8, oclass, sz);
+
+ /* load/store */
+ if (load)
+ {
+ il.AddInstruction(ILSETREG_O(operand1, il.Load(sz, addr0)));
+ il.AddInstruction(ILSETREG_O(operand2, il.Load(sz, addr1)));
+ }
+ else
+ {
+ il.AddInstruction(il.Store(sz, addr0, ILREG_O(operand1)));
+ il.AddInstruction(il.Store(sz, addr1, ILREG_O(operand2)));
+ }
+
+ /* do post-indexing */
+ tmp = GetILOperandPostIndex(il, operand3);
+ if (tmp)
+ il.AddInstruction(tmp);
+}
+
+static void LoadStoreOperandPairSize(LowLevelILFunction& il, bool load, size_t load_size, InstructionOperand& operand1,
+ InstructionOperand& operand2, InstructionOperand& operand3)
+{
+ /* do pre-indexing */
+ ExprId tmp = GetILOperandPreIndex(il, operand3);
+ if (tmp)
+ il.AddInstruction(tmp);
+
+ /* compute addresses */
+ OperandClass oclass = (operand3.operandClass == MEM_PRE_IDX) ? MEM_REG : operand3.operandClass;
+ ExprId addr0 = GetILOperandEffectiveAddress(il, operand3, 8, oclass, 0);
+ ExprId addr1 = GetILOperandEffectiveAddress(il, operand3, 8, oclass, load_size);
+
+ /* load/store */
+ if (load)
+ {
+ il.AddInstruction(ILSETREG_O(operand1, il.Load(load_size, addr0)));
+ il.AddInstruction(ILSETREG_O(operand2, il.Load(load_size, addr1)));
+ }
+ else
+ {
+ il.AddInstruction(il.Store(load_size, addr0, ILREG_O(operand1)));
+ il.AddInstruction(il.Store(load_size, addr1, ILREG_O(operand2)));
+ }
+
+ /* do post-indexing */
+ tmp = GetILOperandPostIndex(il, operand3);
+ if (tmp)
+ il.AddInstruction(tmp);
+}
+
+
+static void LoadStoreVector(
+ LowLevelILFunction& il, bool is_load, InstructionOperand& oper0, InstructionOperand& oper1)
+{
+ /* do pre-indexing */
+ ExprId tmp = GetILOperandPreIndex(il, oper1);
+ if (tmp)
+ il.AddInstruction(tmp);
+
+ Register regs[16];
+ int regs_n = unpack_vector(oper0, regs);
+
+ /* if we pre-indexed, base sequential effective addresses off the base register */
+ OperandClass oclass = (oper1.operandClass == MEM_PRE_IDX) ? MEM_REG : oper1.operandClass;
+
+ int offset = 0;
+ for (int i = 0; i < regs_n; ++i)
+ {
+ int rsize = get_register_size(regs[i]);
+ ExprId eaddr = GetILOperandEffectiveAddress(il, oper1, 8, oclass, offset);
+
+ if (is_load)
+ il.AddInstruction(il.SetRegister(rsize, regs[i], il.Load(rsize, eaddr)));
+ else
+ il.AddInstruction(il.Store(rsize, eaddr, il.Register(rsize, regs[i])));
+
+ offset += rsize;
+ }
+
+ /* do post-indexing */
+ tmp = GetILOperandPostIndex(il, oper1);
+ if (tmp)
+ il.AddInstruction(tmp);
+}
+
+static void LoadStoreOperand(LowLevelILFunction& il, bool load,
+ InstructionOperand& operand1, /* register that gets read/written */
+ InstructionOperand& operand2, /* location the read/write occurs */
+ int load_store_sz)
+{
+ if (!load_store_sz)
+ load_store_sz = REGSZ_O(operand1);
+
+ ExprId tmp;
+ if (load)
+ {
+ switch (operand2.operandClass)
+ {
+ case MEM_REG:
+ // operand1.reg = [operand2.reg]
+ il.AddInstruction(
+ ILSETREG_O(operand1, il.Operand(1, il.Load(load_store_sz, ILREG_O(operand2)))));
+ break;
+ case MEM_OFFSET:
+ if (!load_store_sz)
+ load_store_sz = REGSZ_O(operand1);
+
+ // operand1.reg = [operand2.reg + operand2.imm]
+ if (IMM_O(operand2) == 0)
+ tmp = ILREG_O(operand2);
+ else
+ tmp = ILADDREG_O(operand2, il.Const(REGSZ_O(operand2), IMM_O(operand2)));
+
+ il.AddInstruction(ILSETREG_O(operand1, il.Operand(1, il.Load(load_store_sz, tmp))));
+ break;
+ case MEM_PRE_IDX:
+ // operand2.reg += operand2.imm
+ if (IMM_O(operand2) != 0)
+ il.AddInstruction(ILSETREG_O(operand2, il.Add(REGSZ_O(operand2), ILREG_O(operand2),
+ il.Const(REGSZ_O(operand2), IMM_O(operand2)))));
+ // operand1.reg = [operand2.reg]
+ il.AddInstruction(
+ ILSETREG_O(operand1, il.Operand(1, il.Load(load_store_sz, ILREG_O(operand2)))));
+ break;
+ case MEM_POST_IDX:
+ // operand1.reg = [operand2.reg]
+ il.AddInstruction(
+ ILSETREG_O(operand1, il.Operand(1, il.Load(load_store_sz, ILREG_O(operand2)))));
+ // operand2.reg += operand2.imm
+ if (IMM_O(operand2) != 0)
+ il.AddInstruction(ILSETREG_O(operand2, il.Add(REGSZ_O(operand2), ILREG_O(operand2),
+ il.Const(REGSZ_O(operand2), IMM_O(operand2)))));
+ break;
+ case MEM_EXTENDED:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.Operand(1, il.Load(load_store_sz,
+ il.Add(REGSZ_O(operand2), ILREG_O(operand2),
+ GetShiftedRegister(il, operand2, 1, REGSZ_O(operand2)))))));
+ break;
+ case LABEL:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.Operand(1, il.Load(load_store_sz, il.ConstPointer(8, IMM_O(operand2))))));
+ break;
+ case IMM32:
+ case IMM64:
+ il.AddInstruction(ILSETREG_O(operand1, il.Const(REGSZ_O(operand1), IMM_O(operand2))));
+ break;
+ default:
+ il.AddInstruction(il.Unimplemented());
+ break;
+ }
+ }
+ else // store
+ {
+ switch (operand2.operandClass)
+ {
+ case MEM_REG:
+ il.AddInstruction(
+ il.Operand(1, il.Store(load_store_sz, ILREG_O(operand2), ILREG_O(operand1))));
+ break;
+ case MEM_OFFSET:
+ //[operand2.reg + operand2.immediate] = operand1.reg
+ if (IMM_O(operand2) == 0)
+ tmp = ILREG_O(operand2);
+ else
+ tmp = ILADDREG_O(operand2, il.Const(REGSZ_O(operand2), IMM_O(operand2)));
+
+ il.AddInstruction(il.Operand(1, il.Store(load_store_sz, tmp, ILREG_O(operand1))));
+ break;
+ case MEM_PRE_IDX:
+ // operand2.reg = operand2.reg + operand2.immediate
+ if (IMM_O(operand2) != 0)
+ il.AddInstruction(ILSETREG_O(
+ operand2, ILADDREG_O(operand2, il.Const(REGSZ_O(operand2), IMM_O(operand2)))));
+ //[operand2.reg] = operand1.reg
+ il.AddInstruction(
+ il.Operand(1, il.Store(load_store_sz, ILREG_O(operand2), ILREG_O(operand1))));
+ break;
+ case MEM_POST_IDX:
+ //[operand2.reg] = operand1.reg
+ il.AddInstruction(
+ il.Operand(1, il.Store(load_store_sz, ILREG_O(operand2), ILREG_O(operand1))));
+ // operand2.reg = operand2.reg + operand2.immediate
+ if (IMM_O(operand2) != 0)
+ il.AddInstruction(ILSETREG_O(
+ operand2, ILADDREG_O(operand2, il.Const(REGSZ_O(operand2), IMM_O(operand2)))));
+ break;
+ case MEM_EXTENDED:
+ il.AddInstruction(il.Operand(
+ 1, il.Store(load_store_sz,
+ il.Add(REGSZ_O(operand2), il.Register(REGSZ_O(operand2), operand2.reg[0]),
+ GetShiftedRegister(il, operand2, 1, REGSZ_O(operand2))),
+ ILREG_O(operand1))));
+ break;
+ default:
+ il.AddInstruction(il.Unimplemented());
+ break;
+ }
+ }
+}
+
+static void LoadStoreOperandSize(LowLevelILFunction& il, bool load, bool sign_extend, size_t size,
+ InstructionOperand& operand1, InstructionOperand& operand2)
+{
+ ExprId tmp;
+ if (load)
+ {
+ switch (operand2.operandClass)
+ {
+ case MEM_REG:
+ // operand1.reg = [operand2.reg]
+ tmp = il.Operand(1, il.Load(size, ILREG_O(operand2)));
+
+ if (sign_extend)
+ tmp = il.SignExtend(REGSZ_O(operand1), tmp);
+ else
+ tmp = il.ZeroExtend(REGSZ_O(operand1), tmp);
+
+ il.AddInstruction(ILSETREG_O(operand1, tmp));
+ break;
+ case MEM_OFFSET:
+ // operand1.reg = [operand2.reg + operand2.imm]
+ if (IMM_O(operand2) == 0)
+ tmp = ILREG_O(operand2);
+ else
+ tmp = ILADDREG_O(operand2, il.Const(REGSZ_O(operand2), IMM_O(operand2)));
+
+ tmp = il.Operand(1, il.Load(size, tmp));
+
+ if (sign_extend)
+ tmp = il.SignExtend(REGSZ_O(operand1), tmp);
+ else
+ tmp = il.ZeroExtend(REGSZ_O(operand1), tmp);
+
+ il.AddInstruction(ILSETREG_O(operand1, tmp));
+ break;
+ case MEM_PRE_IDX:
+ // operand2.reg += operand2.imm
+ if (IMM_O(operand2) != 0)
+ il.AddInstruction(ILSETREG_O(operand2, il.Add(REGSZ_O(operand2), ILREG_O(operand2),
+ il.Const(REGSZ_O(operand2), IMM_O(operand2)))));
+ // operand1.reg = [operand2.reg]
+ tmp = il.Operand(1, il.Load(size, ILREG_O(operand2)));
+
+ if (sign_extend)
+ tmp = il.SignExtend(REGSZ_O(operand1), tmp);
+ else
+ tmp = il.ZeroExtend(REGSZ_O(operand1), tmp);
+
+ il.AddInstruction(ILSETREG_O(operand1, tmp));
+ break;
+ case MEM_POST_IDX:
+ // operand1.reg = [operand2.reg]
+ tmp = il.Operand(1, il.Load(size, ILREG_O(operand2)));
+
+ if (sign_extend)
+ tmp = il.SignExtend(REGSZ_O(operand1), tmp);
+ else
+ tmp = il.ZeroExtend(REGSZ_O(operand1), tmp);
+
+ il.AddInstruction(ILSETREG_O(operand1, tmp));
+ // operand2.reg += operand2.imm
+ if (IMM_O(operand2) != 0)
+ il.AddInstruction(ILSETREG_O(operand2, il.Add(REGSZ_O(operand2), ILREG_O(operand2),
+ il.Const(REGSZ_O(operand2), IMM_O(operand2)))));
+ break;
+ case MEM_EXTENDED:
+ tmp =
+ il.Operand(1, il.Load(size, il.Add(REGSZ_O(operand2), ILREG_O(operand2),
+ GetShiftedRegister(il, operand2, 1, REGSZ_O(operand2)))));
+
+ if (sign_extend)
+ tmp = il.SignExtend(REGSZ_O(operand1), tmp);
+ else
+ tmp = il.ZeroExtend(REGSZ_O(operand1), tmp);
+
+ il.AddInstruction(ILSETREG_O(operand1, tmp));
+ break;
+ case LABEL:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.Operand(1, il.Load(size, il.ConstPointer(8, IMM_O(operand2))))));
+ break;
+ default:
+ il.AddInstruction(il.Unimplemented());
+ break;
+ }
+ }
+ else // store
+ {
+ ExprId valToStore = il.Operand(0, ILREG_O(operand1));
+
+ if (size < REGSZ_O(operand1))
+ valToStore = il.LowPart(size, valToStore);
+
+ switch (operand2.operandClass)
+ {
+ case MEM_REG:
+ il.AddInstruction(il.Operand(1, il.Store(size, ILREG_O(operand2), valToStore)));
+ break;
+ case MEM_OFFSET:
+ //[operand2.reg + operand2.immediate] = operand1.reg
+ if (IMM_O(operand2) == 0)
+ tmp = il.Store(size, ILREG_O(operand2), valToStore);
+ else
+ tmp = il.Store(
+ size, ILADDREG_O(operand2, il.Const(REGSZ_O(operand2), IMM_O(operand2))), valToStore);
+ il.AddInstruction(il.Operand(1, tmp));
+ break;
+ case MEM_PRE_IDX:
+ // operand2.reg = operand2.reg + operand2.immediate
+ if (IMM_O(operand2) != 0)
+ il.AddInstruction(ILSETREG_O(
+ operand2, ILADDREG_O(operand2, il.Const(REGSZ_O(operand2), IMM_O(operand2)))));
+ //[operand2.reg] = operand1.reg
+ il.AddInstruction(il.Operand(1, il.Store(size, ILREG_O(operand2), valToStore)));
+ break;
+ case MEM_POST_IDX:
+ //[operand2.reg] = operand1.reg
+ il.AddInstruction(il.Operand(1, il.Store(size, ILREG_O(operand2), valToStore)));
+ // operand2.reg = operand2.reg + operand2.immediate
+ if (IMM_O(operand2) != 0)
+ il.AddInstruction(ILSETREG_O(
+ operand2, ILADDREG_O(operand2, il.Const(REGSZ_O(operand2), IMM_O(operand2)))));
+ break;
+ case MEM_EXTENDED:
+ il.AddInstruction(il.Operand(
+ 1, il.Store(size,
+ il.Add(REGSZ_O(operand2), il.Register(REGSZ_O(operand2), operand2.reg[0]),
+ GetShiftedRegister(il, operand2, 1, REGSZ_O(operand2))),
+ valToStore)));
+ break;
+ default:
+ il.AddInstruction(il.Unimplemented());
+ break;
+ }
+ }
+}
+
+
+static size_t DirectJump(
+ Architecture* arch, LowLevelILFunction& il, uint64_t target, size_t addrSize)
+{
+ BNLowLevelILLabel* label = il.GetLabelForAddress(arch, target);
+ if (label)
+ return il.Goto(*label);
+ else
+ return il.Jump(il.ConstPointer(addrSize, target));
+
+ return 0;
+}
+
+
+static ExprId ExtractBits(
+ LowLevelILFunction& il, InstructionOperand& reg, size_t nbits, size_t rightMostBit)
+{
+// Get N set bits at offset O
+#define BITMASK(N, O) (((1LL << nbits) - 1) << O)
+ return il.And(REGSZ_O(reg), ILREG_O(reg), il.Const(REGSZ_O(reg), BITMASK(nbits, rightMostBit)));
+}
+
+static ExprId ExtractBit(LowLevelILFunction& il, InstructionOperand& reg, size_t bit)
+{
+ return il.And(REGSZ_O(reg), ILREG_O(reg), il.Const(REGSZ_O(reg), (1 << bit)));
+}
+
+static void ConditionalJump(Architecture* arch, LowLevelILFunction& il, size_t cond,
+ size_t addrSize, uint64_t t, uint64_t f)
+{
+ BNLowLevelILLabel* trueLabel = il.GetLabelForAddress(arch, t);
+ BNLowLevelILLabel* falseLabel = il.GetLabelForAddress(arch, f);
+
+ if (trueLabel && falseLabel)
+ {
+ il.AddInstruction(il.If(cond, *trueLabel, *falseLabel));
+ return;
+ }
+
+ LowLevelILLabel trueCode, falseCode;
+
+ if (trueLabel)
+ {
+ il.AddInstruction(il.If(cond, *trueLabel, falseCode));
+ il.MarkLabel(falseCode);
+ il.AddInstruction(il.Jump(il.ConstPointer(addrSize, f)));
+ return;
+ }
+
+ if (falseLabel)
+ {
+ il.AddInstruction(il.If(cond, trueCode, *falseLabel));
+ il.MarkLabel(trueCode);
+ il.AddInstruction(il.Jump(il.ConstPointer(addrSize, t)));
+ return;
+ }
+
+ il.AddInstruction(il.If(cond, trueCode, falseCode));
+ il.MarkLabel(trueCode);
+ il.AddInstruction(il.Jump(il.ConstPointer(addrSize, t)));
+ il.MarkLabel(falseCode);
+ il.AddInstruction(il.Jump(il.ConstPointer(addrSize, f)));
+}
+
+
+static void ApplyAttributeToLastInstruction(LowLevelILFunction& il, uint32_t attributes)
+{
+ size_t instrId = il.GetInstructionCount()-1;
+ ExprId expr = il.GetIndexForInstruction(instrId);
+ il.SetExprAttributes(expr, attributes);
+}
+
+
+enum Arm64Intrinsic operation_to_intrinsic(int operation)
+{
+ switch (operation)
+ {
+ case ARM64_AUTDA:
+ case ARM64_AUTDZA:
+ return ARM64_INTRIN_AUTDA;
+ case ARM64_AUTDB:
+ case ARM64_AUTDZB:
+ return ARM64_INTRIN_AUTDB;
+ case ARM64_AUTIA:
+ case ARM64_AUTIA1716:
+ case ARM64_AUTIASP:
+ case ARM64_AUTIAZ:
+ case ARM64_AUTIZA:
+ return ARM64_INTRIN_AUTIA;
+ case ARM64_AUTIB:
+ case ARM64_AUTIB1716:
+ case ARM64_AUTIBSP:
+ case ARM64_AUTIBZ:
+ case ARM64_AUTIZB:
+ return ARM64_INTRIN_AUTIB;
+ case ARM64_PACDA:
+ case ARM64_PACDZA:
+ return ARM64_INTRIN_PACDA;
+ case ARM64_PACDB:
+ case ARM64_PACDZB:
+ return ARM64_INTRIN_PACDB;
+ case ARM64_PACGA:
+ return ARM64_INTRIN_PACGA;
+ case ARM64_PACIA:
+ case ARM64_PACIA1716:
+ case ARM64_PACIASP:
+ case ARM64_PACIAZ:
+ case ARM64_PACIZA:
+ return ARM64_INTRIN_PACIA;
+ case ARM64_PACIB:
+ case ARM64_PACIB1716:
+ case ARM64_PACIBSP:
+ case ARM64_PACIBZ:
+ case ARM64_PACIZB:
+ return ARM64_INTRIN_PACIB;
+ case ARM64_XPACD:
+ return ARM64_INTRIN_XPACD;
+ case ARM64_XPACI:
+ case ARM64_XPACLRI:
+ return ARM64_INTRIN_XPACI;
+ default:
+ return ARM64_INTRIN_INVALID;
+ }
+}
+
+
+bool GetLowLevelILForInstruction(
+ Architecture* arch, uint64_t addr, LowLevelILFunction& il, Instruction& instr, size_t addrSize, bool requireAlignment)
+{
+ bool SetPacAttr = false;
+
+ InstructionOperand& operand1 = instr.operands[0];
+ InstructionOperand& operand2 = instr.operands[1];
+ InstructionOperand& operand3 = instr.operands[2];
+ InstructionOperand& operand4 = instr.operands[3];
+
+ if (requireAlignment && (addr % 4 != 0)) {
+ return false;
+ }
+
+ int n_instrs_before = il.GetInstructionCount();
+
+ // printf("%s() operation:%d encoding:%d\n", __func__, instr.operation, instr.encoding);
+
+ LowLevelILLabel trueLabel, falseLabel;
+ switch (instr.operation)
+ {
+ case ARM64_ADD:
+ case ARM64_ADDS:
+ il.AddInstruction(
+ ILSETREG_O(operand1, il.Add(REGSZ_O(operand1), ILREG_O(operand2),
+ ReadILOperand(il, operand3, REGSZ_O(operand1)), SETFLAGS)));
+ break;
+ case ARM64_ADC:
+ case ARM64_ADCS:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.AddCarry(REGSZ_O(operand1), ILREG_O(operand2),
+ ReadILOperand(il, operand3, REGSZ_O(operand1)), il.Flag(IL_FLAG_C), SETFLAGS)));
+ break;
+ case ARM64_AND:
+ case ARM64_ANDS:
+ il.AddInstruction(
+ ILSETREG_O(operand1, il.And(REGSZ_O(operand1), ILREG_O(operand2),
+ ReadILOperand(il, operand3, REGSZ_O(operand1)), SETFLAGS)));
+ break;
+ case ARM64_ADR:
+ case ARM64_ADRP:
+ il.AddInstruction(ILSETREG_O(operand1, il.ConstPointer(REGSZ_O(operand1), IMM_O(operand2))));
+ break;
+ case ARM64_ASR:
+ il.AddInstruction(ILSETREG_O(operand1, il.ArithShiftRight(REGSZ_O(operand2), ILREG_O(operand2),
+ ReadILOperand(il, operand3, REGSZ_O(operand2)))));
+ break;
+ case ARM64_AESD:
+ il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(REG_O(operand1))}, ARM64_INTRIN_AESD,
+ {ILREG_O(operand1), ILREG_O(operand2)}));
+ break;
+ case ARM64_AESE:
+ il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(REG_O(operand1))}, ARM64_INTRIN_AESE,
+ {ILREG_O(operand1), ILREG_O(operand2)}));
+ break;
+ case ARM64_BTI:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_HINT_BTI, {}));
+ break;
+ case ARM64_B:
+ il.AddInstruction(DirectJump(arch, il, IMM_O(operand1), addrSize));
+ break;
+ case ARM64_B_NE:
+ ConditionalJump(arch, il, il.FlagGroup(IL_FLAG_GROUP_NE), addrSize, IMM_O(operand1), addr + 4);
+ return false;
+ case ARM64_B_EQ:
+ ConditionalJump(arch, il, il.FlagGroup(IL_FLAG_GROUP_EQ), addrSize, IMM_O(operand1), addr + 4);
+ return false;
+ case ARM64_B_CS:
+ ConditionalJump(arch, il, il.FlagGroup(IL_FLAG_GROUP_CS), addrSize, IMM_O(operand1), addr + 4);
+ return false;
+ case ARM64_B_CC:
+ ConditionalJump(arch, il, il.FlagGroup(IL_FLAG_GROUP_CC), addrSize, IMM_O(operand1), addr + 4);
+ return false;
+ case ARM64_B_MI:
+ ConditionalJump(arch, il, il.FlagGroup(IL_FLAG_GROUP_MI), addrSize, IMM_O(operand1), addr + 4);
+ return false;
+ case ARM64_B_PL:
+ ConditionalJump(arch, il, il.FlagGroup(IL_FLAG_GROUP_PL), addrSize, IMM_O(operand1), addr + 4);
+ return false;
+ case ARM64_B_VS:
+ ConditionalJump(arch, il, il.FlagGroup(IL_FLAG_GROUP_VS), addrSize, IMM_O(operand1), addr + 4);
+ return false;
+ case ARM64_B_VC:
+ ConditionalJump(arch, il, il.FlagGroup(IL_FLAG_GROUP_VC), addrSize, IMM_O(operand1), addr + 4);
+ return false;
+ case ARM64_B_HI:
+ ConditionalJump(arch, il, il.FlagGroup(IL_FLAG_GROUP_HI), addrSize, IMM_O(operand1), addr + 4);
+ return false;
+ case ARM64_B_LS:
+ ConditionalJump(arch, il, il.FlagGroup(IL_FLAG_GROUP_LS), addrSize, IMM_O(operand1), addr + 4);
+ return false;
+ case ARM64_B_GE:
+ ConditionalJump(arch, il, il.FlagGroup(IL_FLAG_GROUP_GE), addrSize, IMM_O(operand1), addr + 4);
+ return false;
+ case ARM64_B_LT:
+ ConditionalJump(arch, il, il.FlagGroup(IL_FLAG_GROUP_LT), addrSize, IMM_O(operand1), addr + 4);
+ return false;
+ case ARM64_B_GT:
+ ConditionalJump(arch, il, il.FlagGroup(IL_FLAG_GROUP_GT), addrSize, IMM_O(operand1), addr + 4);
+ return false;
+ case ARM64_B_LE:
+ ConditionalJump(arch, il, il.FlagGroup(IL_FLAG_GROUP_LE), addrSize, IMM_O(operand1), addr + 4);
+ return false;
+ case ARM64_BL:
+ il.AddInstruction(il.Call(il.ConstPointer(addrSize, IMM_O(operand1))));
+ break;
+ case ARM64_BLRAA:
+ case ARM64_BLRAAZ:
+ case ARM64_BLRAB:
+ case ARM64_BLRABZ:
+ SetPacAttr = true;
+ case ARM64_BLR:
+ il.AddInstruction(il.Call(ILREG_O(operand1)));
+ if (SetPacAttr)
+ ApplyAttributeToLastInstruction(il, SrcInstructionUsesPointerAuth);
+ break;
+ case ARM64_BFC:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.And(REGSZ_O(operand1),
+ il.Const(REGSZ_O(operand1), ~(ONES(IMM_O(operand3)) << IMM_O(operand2))),
+ ILREG_O(operand1))));
+ break;
+ case ARM64_BFI:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.Or(REGSZ_O(operand1),
+ il.And(REGSZ_O(operand1),
+ il.Const(REGSZ_O(operand1), ~(ONES(IMM_O(operand4)) << IMM_O(operand3))),
+ ILREG_O(operand1)),
+ il.ShiftLeft(REGSZ_O(operand1),
+ il.And(REGSZ_O(operand1), il.Const(REGSZ_O(operand1), ONES(IMM_O(operand4))),
+ ILREG_O(operand2)),
+ il.Const(1, IMM_O(operand3))))));
+ break;
+ case ARM64_BFXIL:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.Or(REGSZ_O(operand1),
+ il.And(REGSZ_O(operand1), ILREG_O(operand1),
+ il.Const(REGSZ_O(operand1), ~ONES(IMM_O(operand4)))),
+ il.LogicalShiftRight(REGSZ_O(operand1),
+ il.And(REGSZ_O(operand1), ILREG_O(operand2),
+ il.Const(REGSZ_O(operand1), ONES(IMM_O(operand4)) << IMM_O(operand3))),
+ il.Const(1, IMM_O(operand3))))));
+ break;
+ case ARM64_BRAA:
+ case ARM64_BRAAZ:
+ case ARM64_BRAB:
+ case ARM64_BRABZ:
+ SetPacAttr = true;
+ case ARM64_BR:
+ il.AddInstruction(il.Jump(ILREG_O(operand1)));
+ if (SetPacAttr)
+ ApplyAttributeToLastInstruction(il, SrcInstructionUsesPointerAuth);
+ return false;
+ case ARM64_BIC:
+ case ARM64_BICS:
+ switch (instr.encoding) {
+ case ENC_BIC_ASIMDIMM_L_HL:
+ case ENC_BIC_ASIMDIMM_L_SL:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.And(REGSZ_O(operand1), ILREG_O(operand1),
+ il.Not(REGSZ_O(operand2), ReadILOperand(il, operand2, REGSZ_O(operand2))), SETFLAGS)));
+ break;
+ default:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.And(REGSZ_O(operand2), ILREG_O(operand2),
+ il.Not(REGSZ_O(operand2), ReadILOperand(il, operand3, REGSZ_O(operand2))), SETFLAGS)));
+ }
+ break;
+ case ARM64_CAS: // these compare-and-swaps can be 32 or 64 bit
+ case ARM64_CASA:
+ case ARM64_CASAL:
+ case ARM64_CASL:
+ il.AddInstruction(il.SetRegister(REGSZ_O(operand1), LLIL_TEMP(0), il.Load(REGSZ_O(operand1), ILREG_O(operand3))));
+
+ GenIfElse(il,
+ il.CompareEqual(REGSZ_O(operand1), ILREG_O(operand1), il.Register(REGSZ_O(operand1), LLIL_TEMP(0))),
+ il.Store(REGSZ_O(operand1), ILREG_O(operand3), ILREG_O(operand2)),
+ 0);
+
+ il.AddInstruction(ILSETREG_O(operand1, il.Register(REGSZ_O(operand1), LLIL_TEMP(0))));
+ break;
+ case ARM64_CASAH: // these compare-and-swaps are 16 bit
+ case ARM64_CASALH:
+ case ARM64_CASH:
+ case ARM64_CASLH:
+ il.AddInstruction(il.SetRegister(2, LLIL_TEMP(0), il.Load(2, ILREG_O(operand3))));
+
+ GenIfElse(il,
+ il.CompareEqual(REGSZ_O(operand1), ExtractRegister(il, operand1, 0, 2, false, 2), LLIL_TEMP(0)),
+ il.Store(2, ILREG_O(operand3), ExtractRegister(il, operand2, 0, 2, false, 2)),
+ 0);
+
+ il.AddInstruction(ILSETREG_O(operand1, il.Register(2, LLIL_TEMP(0))));
+ break;
+ case ARM64_CASAB: // these compare-and-swaps are 8 bit
+ case ARM64_CASALB:
+ case ARM64_CASB:
+ case ARM64_CASLB:
+ il.AddInstruction(il.SetRegister(1, LLIL_TEMP(0), il.Load(1, ILREG_O(operand3))));
+
+ GenIfElse(il,
+ il.CompareEqual(REGSZ_O(operand1), ExtractRegister(il, operand1, 0, 1, false, 1), LLIL_TEMP(0)),
+ il.Store(1, ILREG_O(operand3), ExtractRegister(il, operand2, 0, 1, false, 1)),
+ 0);
+
+ il.AddInstruction(ILSETREG_O(operand1, il.Register(1, LLIL_TEMP(0))));
+ break;
+ case ARM64_CBNZ:
+ ConditionalJump(arch, il,
+ il.CompareNotEqual(REGSZ_O(operand1), ILREG_O(operand1), il.Const(REGSZ_O(operand1), 0)),
+ addrSize, IMM_O(operand2), addr + 4);
+ return false;
+ case ARM64_CBZ:
+ ConditionalJump(arch, il,
+ il.CompareEqual(REGSZ_O(operand1), ILREG_O(operand1), il.Const(REGSZ_O(operand1), 0)),
+ addrSize, IMM_O(operand2), addr + 4);
+ return false;
+ case ARM64_CMN:
+ il.AddInstruction(il.Add(REGSZ_O(operand1), ILREG_O(operand1),
+ ReadILOperand(il, operand2, REGSZ_O(operand1)), SETFLAGS));
+ break;
+ case ARM64_CCMN:
+ {
+ LowLevelILLabel trueCode, falseCode, done;
+
+ il.AddInstruction(il.If(GetCondition(il, operand4.cond), trueCode, falseCode));
+
+ il.MarkLabel(trueCode);
+ il.AddInstruction(il.Add(REGSZ_O(operand1), ILREG_O(operand1),
+ ReadILOperand(il, operand2, REGSZ_O(operand1)), SETFLAGS));
+ il.AddInstruction(il.Goto(done));
+
+ il.MarkLabel(falseCode);
+ il.AddInstruction(il.SetFlag(IL_FLAG_N, il.Const(0, (IMM_O(operand3) >> 3) & 1)));
+ il.AddInstruction(il.SetFlag(IL_FLAG_Z, il.Const(0, (IMM_O(operand3) >> 2) & 1)));
+ il.AddInstruction(il.SetFlag(IL_FLAG_C, il.Const(0, (IMM_O(operand3) >> 1) & 1)));
+ il.AddInstruction(il.SetFlag(IL_FLAG_V, il.Const(0, (IMM_O(operand3) >> 0) & 1)));
+
+ il.AddInstruction(il.Goto(done));
+
+ il.MarkLabel(done);
+ }
+ break;
+ case ARM64_CMP:
+ il.AddInstruction(il.Sub(REGSZ_O(operand1), ILREG_O(operand1),
+ ReadILOperand(il, operand2, REGSZ_O(operand1)), SETFLAGS));
+ break;
+ case ARM64_CCMP:
+ {
+ LowLevelILLabel trueCode, falseCode, done;
+
+ il.AddInstruction(il.If(GetCondition(il, operand4.cond), trueCode, falseCode));
+
+ il.MarkLabel(trueCode);
+ il.AddInstruction(il.Sub(REGSZ_O(operand1), ILREG_O(operand1),
+ ReadILOperand(il, operand2, REGSZ_O(operand1)), SETFLAGS));
+ il.AddInstruction(il.Goto(done));
+
+ il.MarkLabel(falseCode);
+ il.AddInstruction(il.SetFlag(IL_FLAG_N, il.Const(0, (IMM_O(operand3) >> 3) & 1)));
+ il.AddInstruction(il.SetFlag(IL_FLAG_Z, il.Const(0, (IMM_O(operand3) >> 2) & 1)));
+ il.AddInstruction(il.SetFlag(IL_FLAG_C, il.Const(0, (IMM_O(operand3) >> 1) & 1)));
+ il.AddInstruction(il.SetFlag(IL_FLAG_V, il.Const(0, (IMM_O(operand3) >> 0) & 1)));
+
+ il.AddInstruction(il.Goto(done));
+
+ il.MarkLabel(done);
+ }
+ break;
+ case ARM64_CLREX:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_CLREX, {}));
+ break;
+ case ARM64_CSEL:
+ case ARM64_FCSEL:
+ GenIfElse(il, GetCondition(il, operand4.cond), ILSETREG_O(operand1, ILREG_O(operand2)),
+ ILSETREG_O(operand1, ILREG_O(operand3)));
+ break;
+ case ARM64_CSINC:
+ GenIfElse(il, GetCondition(il, operand4.cond), ILSETREG_O(operand1, ILREG_O(operand2)),
+ ILSETREG_O(operand1, ILADDREG_O(operand3, il.Const(REGSZ_O(operand1), 1))));
+ break;
+ case ARM64_CSINV:
+ GenIfElse(il, GetCondition(il, operand4.cond), ILSETREG_O(operand1, ILREG_O(operand2)),
+ ILSETREG_O(operand1, il.Not(REGSZ_O(operand1), ILREG_O(operand3))));
+ break;
+ case ARM64_CSNEG:
+ GenIfElse(il, GetCondition(il, operand4.cond), ILSETREG_O(operand1, ILREG_O(operand2)),
+ ILSETREG_O(operand1, il.Neg(REGSZ_O(operand1), ILREG_O(operand3))));
+ break;
+ case ARM64_CSET:
+ GenIfElse(il, GetCondition(il, operand2.cond),
+ ILSETREG_O(operand1, il.Const(REGSZ_O(operand1), 1)),
+ ILSETREG_O(operand1, il.Const(REGSZ_O(operand1), 0)));
+ break;
+ case ARM64_CSETM:
+ GenIfElse(il, GetCondition(il, operand2.cond),
+ ILSETREG_O(operand1, il.Const(REGSZ_O(operand1), -1)),
+ ILSETREG_O(operand1, il.Const(REGSZ_O(operand1), 0)));
+ break;
+ case ARM64_CINC:
+ GenIfElse(il, GetCondition(il, operand3.cond),
+ ILSETREG_O(operand1, ILADDREG_O(operand2, il.Const(REGSZ_O(operand1), 1))),
+ ILSETREG_O(operand1, ILREG_O(operand2)));
+ break;
+ case ARM64_CINV:
+ GenIfElse(il, GetCondition(il, operand3.cond),
+ ILSETREG_O(operand1, il.Not(REGSZ_O(operand1), ILREG_O(operand2))),
+ ILSETREG_O(operand1, ILREG_O(operand2)));
+ break;
+ case ARM64_CNEG:
+ GenIfElse(il, GetCondition(il, operand3.cond),
+ ILSETREG_O(operand1, il.Neg(REGSZ_O(operand1), ILREG_O(operand2))),
+ ILSETREG_O(operand1, ILREG_O(operand2)));
+ break;
+ case ARM64_CLZ:
+ il.AddInstruction(il.Intrinsic(
+ {RegisterOrFlag::Register(REG_O(operand1))}, ARM64_INTRIN_CLZ, {ILREG_O(operand2)}));
+ break;
+ case ARM64_DC:
+ il.AddInstruction(
+ il.Intrinsic({}, ARM64_INTRIN_DC, {ILREG_O(operand2)})); /* operand1 is <dc_op> */
+ break;
+ case ARM64_DMB:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_DMB, {}));
+ break;
+ case ARM64_DSB:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_DSB, {}));
+ break;
+ case ARM64_EON:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.Xor(REGSZ_O(operand1), ILREG_O(operand2),
+ il.Not(REGSZ_O(operand1), ReadILOperand(il, operand3, REGSZ_O(operand1))))));
+ break;
+ case ARM64_EOR:
+ il.AddInstruction(ILSETREG_O(operand1, il.Xor(REGSZ_O(operand1), ILREG_O(operand2),
+ ReadILOperand(il, operand3, REGSZ_O(operand1)))));
+ break;
+ case ARM64_ESB:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_ESB, {}));
+ break;
+ case ARM64_EXTR:
+ il.AddInstruction(
+ ILSETREG_O(operand1, il.LogicalShiftRight(REGSZ_O(operand1) * 2,
+ il.Or(REGSZ_O(operand1) * 2,
+ il.ShiftLeft(REGSZ_O(operand1) * 2, ILREG_O(operand2),
+ il.Const(1, REGSZ_O(operand1) * 8)),
+ ILREG_O(operand3)),
+ il.Const(1, IMM_O(operand4)))));
+ break;
+ case ARM64_FADD:
+ switch (instr.encoding)
+ {
+ case ENC_FADD_H_FLOATDP2:
+ case ENC_FADD_S_FLOATDP2:
+ case ENC_FADD_D_FLOATDP2:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.FloatAdd(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3))));
+ break;
+ case ENC_FADD_ASIMDSAME_ONLY:
+ case ENC_FADD_ASIMDSAMEFP16_ONLY:
+ {
+ Register srcs1[16], srcs2[16], dsts[16];
+ int dst_n = unpack_vector(operand1, dsts);
+ int src1_n = unpack_vector(operand2, srcs1);
+ int src2_n = unpack_vector(operand3, srcs2);
+ if ((dst_n != src1_n) || (src1_n != src2_n) || dst_n == 0)
+ ABORT_LIFT;
+
+ int rsize = get_register_size(dsts[0]);
+ for (int i = 0; i < dst_n; ++i)
+ il.AddInstruction(ILSETREG(
+ dsts[i], il.FloatAdd(rsize, ILREG(srcs1[i]), ILREG(srcs2[i]))));
+ }
+ break;
+ default:
+ il.AddInstruction(il.Unimplemented());
+ }
+ break;
+ case ARM64_FCCMP:
+ case ARM64_FCCMPE:
+ {
+ LowLevelILLabel trueCode, falseCode, done;
+
+ il.AddInstruction(il.If(GetCondition(il, operand4.cond), trueCode, falseCode));
+
+ il.MarkLabel(trueCode);
+ il.AddInstruction(il.FloatSub(REGSZ_O(operand1), ILREG_O(operand1),
+ ReadILOperand(il, operand2, REGSZ_O(operand1)), SETFLAGS));
+ il.AddInstruction(il.Goto(done));
+
+ il.MarkLabel(falseCode);
+ il.AddInstruction(il.SetFlag(IL_FLAG_N, il.Const(0, (IMM_O(operand3) >> 3) & 1)));
+ il.AddInstruction(il.SetFlag(IL_FLAG_Z, il.Const(0, (IMM_O(operand3) >> 2) & 1)));
+ il.AddInstruction(il.SetFlag(IL_FLAG_C, il.Const(0, (IMM_O(operand3) >> 1) & 1)));
+ il.AddInstruction(il.SetFlag(IL_FLAG_V, il.Const(0, (IMM_O(operand3) >> 0) & 1)));
+
+ il.AddInstruction(il.Goto(done));
+
+ il.MarkLabel(done);
+ }
+ break;
+ case ARM64_FCMP:
+ case ARM64_FCMPE:
+ il.AddInstruction(il.FloatSub(REGSZ_O(operand1), ILREG_O(operand1),
+ ReadILOperand(il, operand2, REGSZ_O(operand1)), SETFLAGS));
+ break;
+ case ARM64_FSUB:
+ switch (instr.encoding)
+ {
+ case ENC_FSUB_H_FLOATDP2:
+ case ENC_FSUB_S_FLOATDP2:
+ case ENC_FSUB_D_FLOATDP2:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.FloatSub(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3))));
+ break;
+ case ENC_FSUB_ASIMDSAME_ONLY:
+ case ENC_FSUB_ASIMDSAMEFP16_ONLY:
+ {
+ Register srcs[16], dsts[16];
+ int dst_n = unpack_vector(operand1, dsts);
+ int src_n = unpack_vector(operand2, srcs);
+ if ((dst_n != src_n) || dst_n == 0)
+ ABORT_LIFT;
+
+ int rsize = get_register_size(dsts[0]);
+ for (int i = 0; i < dst_n; ++i)
+ il.AddInstruction(il.FloatSub(rsize, ILREG(dsts[i]), ILREG(srcs[i])));
+ }
+ break;
+ default:
+ il.AddInstruction(il.Unimplemented());
+ }
+ break;
+ case ARM64_FCVT:
+ {
+ int float_sz = 0;
+ switch (instr.encoding)
+ {
+ /* non-SVE is straight register-to-register */
+ case ENC_FCVT_HS_FLOATDP1: // convert to half (2-byte)
+ case ENC_FCVT_HD_FLOATDP1:
+ float_sz = 2;
+ case ENC_FCVT_SH_FLOATDP1: // convert to single (4-byte)
+ case ENC_FCVT_SD_FLOATDP1:
+ if (!float_sz)
+ float_sz = 4;
+ case ENC_FCVT_DH_FLOATDP1: // convert to double (8-byte)
+ case ENC_FCVT_DS_FLOATDP1:
+ if (!float_sz)
+ float_sz = 8;
+ il.AddInstruction(ILSETREG_O(operand1, GetFloat(il, operand2, float_sz)));
+ break;
+ /* future: support SVE versions with predicated execution and z register file */
+ default:
+ ABORT_LIFT;
+ }
+ break;
+ }
+ case ARM64_FDIV:
+ switch (instr.encoding)
+ {
+ case ENC_FDIV_H_FLOATDP2:
+ case ENC_FDIV_S_FLOATDP2:
+ case ENC_FDIV_D_FLOATDP2:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.FloatDiv(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3))));
+ break;
+ default:
+ il.AddInstruction(il.Unimplemented());
+ }
+ break;
+ case ARM64_FMOV:
+ switch (instr.encoding)
+ {
+ case ENC_FMOV_64VX_FLOAT2INT:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.FloatToInt(REGSZ_O(operand1), ILREG(vector_reg_minimize(instr.operands[1])))));
+ break;
+ case ENC_FMOV_V64I_FLOAT2INT:
+ {
+ Register minreg = vector_reg_minimize(instr.operands[0]);
+ il.AddInstruction(il.SetRegister(get_register_size(minreg), minreg,
+ il.FloatToInt(REGSZ_O(operand1), ILREG_O(instr.operands[1]))));
+ break;
+ }
+ case ENC_FMOV_32H_FLOAT2INT:
+ case ENC_FMOV_32S_FLOAT2INT:
+ case ENC_FMOV_64H_FLOAT2INT:
+ case ENC_FMOV_64D_FLOAT2INT:
+ case ENC_FMOV_D64_FLOAT2INT:
+ case ENC_FMOV_H32_FLOAT2INT:
+ case ENC_FMOV_H64_FLOAT2INT:
+ case ENC_FMOV_S32_FLOAT2INT:
+ il.AddInstruction(
+ ILSETREG_O(operand1, il.FloatToInt(REGSZ_O(operand1), ILREG_O(instr.operands[1]))));
+ break;
+ case ENC_FMOV_H_FLOATIMM:
+ case ENC_FMOV_S_FLOATIMM:
+ case ENC_FMOV_D_FLOATIMM:
+ {
+ int float_sz = 2;
+ if (instr.encoding == ENC_FMOV_S_FLOATIMM)
+ float_sz = 4;
+ if (instr.encoding == ENC_FMOV_D_FLOATIMM)
+ float_sz = 8;
+ il.AddInstruction(ILSETREG_O(operand1, GetFloat(il, operand2, float_sz)));
+ break;
+ }
+ case ENC_FMOV_H_FLOATDP1:
+ case ENC_FMOV_S_FLOATDP1:
+ case ENC_FMOV_D_FLOATDP1:
+ il.AddInstruction(ILSETREG_O(operand1, ILREG_O(operand2)));
+ break;
+ case ENC_FMOV_ASIMDIMM_D2_D:
+ case ENC_FMOV_ASIMDIMM_H_H:
+ case ENC_FMOV_ASIMDIMM_S_S:
+ {
+ int float_sz = 2;
+ if (instr.encoding == ENC_FMOV_ASIMDIMM_S_S)
+ float_sz = 4;
+ if (instr.encoding == ENC_FMOV_ASIMDIMM_D2_D)
+ float_sz = 8;
+
+ Register regs[16];
+ int dst_n = unpack_vector(operand1, regs);
+ for (int i = 0; i < dst_n; ++i)
+ il.AddInstruction(ILSETREG(regs[i], GetFloat(il, operand2, float_sz)));
+ break;
+ }
+ default:
+ il.AddInstruction(il.Unimplemented());
+ }
+ break;
+ case ARM64_FMUL:
+ switch (instr.encoding)
+ {
+ case ENC_FMUL_H_FLOATDP2:
+ case ENC_FMUL_S_FLOATDP2:
+ case ENC_FMUL_D_FLOATDP2:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.FloatMult(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3))));
+ break;
+ case ENC_FMUL_ASIMDSAME_ONLY:
+ case ENC_FMUL_ASIMDSAMEFP16_ONLY:
+ {
+ Register srcs1[16], srcs2[16], dsts[16];
+ int dst_n = unpack_vector(operand1, dsts);
+ int src1_n = unpack_vector(operand2, srcs1);
+ int src2_n = unpack_vector(operand3, srcs2);
+ if ((dst_n != src1_n) || (src1_n != src2_n) || dst_n == 0)
+ ABORT_LIFT;
+ int rsize = get_register_size(dsts[0]);
+ for (int i = 0; i < dst_n; ++i)
+ il.AddInstruction(ILSETREG(
+ dsts[i], il.FloatMult(rsize, ILREG(srcs1[i]), ILREG(srcs2[i]))));
+ }
+ break;
+ case ENC_FMUL_ASIMDELEM_RH_H:
+ case ENC_FMUL_ASIMDELEM_R_SD:
+ case ENC_FMUL_ASISDELEM_RH_H:
+ case ENC_FMUL_ASISDELEM_R_SD:
+ {
+ Register srcs1[16], srcs2[16], dsts[16];
+ int dst_n = unpack_vector(operand1, dsts);
+ int src1_n = unpack_vector(operand2, srcs1);
+ int src2_n = unpack_vector(operand3, srcs2);
+ if ((dst_n != src1_n) || dst_n == 0 || src2_n != 1)
+ ABORT_LIFT;
+ int rsize = get_register_size(dsts[0]);
+ for (int i = 0; i < dst_n; ++i)
+ il.AddInstruction(ILSETREG(
+ dsts[i], il.FloatMult(rsize, ILREG(srcs1[i]), ILREG(srcs2[0]))));
+ }
+ break;
+ default:
+ il.AddInstruction(il.Unimplemented());
+ }
+ break;
+ case ARM64_ERET:
+ case ARM64_ERETAA:
+ case ARM64_ERETAB:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_ERET, {}));
+ il.AddInstruction(il.Trap(0));
+ return false;
+ case ARM64_ISB:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_ISB, {}));
+ break;
+ case ARM64_LDAR:
+
+ case ARM64_LDAPR:
+ case ARM64_LDAPUR:
+ LoadStoreOperand(il, true, instr.operands[0], instr.operands[1], 0);
+ break;
+ case ARM64_LDARB:
+ case ARM64_LDAPRB:
+ case ARM64_LDAPURB:
+ LoadStoreOperandSize(il, true, false, 1, instr.operands[0], instr.operands[1]);
+ break;
+ case ARM64_LDARH:
+ case ARM64_LDAPRH:
+ case ARM64_LDAPURH:
+ LoadStoreOperandSize(il, true, false, 2, instr.operands[0], instr.operands[1]);
+ break;
+ case ARM64_LDP:
+ case ARM64_LDNP:
+ LoadStoreOperandPair(il, true, instr.operands[0], instr.operands[1], instr.operands[2]);
+ break;
+ case ARM64_LDPSW:
+ LoadStoreOperandPairSize(il, true, 4, instr.operands[0], instr.operands[1], instr.operands[2]);
+ break;
+ case ARM64_LDRAA:
+ case ARM64_LDRAB:
+ SetPacAttr = true;
+ case ARM64_LDR:
+ case ARM64_LDUR:
+ LoadStoreOperand(il, true, instr.operands[0], instr.operands[1], 0);
+ if (SetPacAttr)
+ ApplyAttributeToLastInstruction(il, SrcInstructionUsesPointerAuth);
+ break;
+ case ARM64_LDRB:
+ case ARM64_LDURB:
+ LoadStoreOperandSize(il, true, false, 1, instr.operands[0], instr.operands[1]);
+ break;
+ case ARM64_LDRH:
+ case ARM64_LDURH:
+ LoadStoreOperandSize(il, true, false, 2, instr.operands[0], instr.operands[1]);
+ break;
+ case ARM64_LDRSB:
+ case ARM64_LDURSB:
+ case ARM64_LDAPURSB:
+ LoadStoreOperandSize(il, true, true, 1, instr.operands[0], instr.operands[1]);
+ break;
+ case ARM64_LDRSH:
+ case ARM64_LDURSH:
+ case ARM64_LDAPURSH:
+ LoadStoreOperandSize(il, true, true, 2, instr.operands[0], instr.operands[1]);
+ break;
+ case ARM64_LDRSW:
+ case ARM64_LDURSW:
+ case ARM64_LDAPURSW:
+ LoadStoreOperandSize(il, true, true, 4, instr.operands[0], instr.operands[1]);
+ break;
+ case ARM64_LDXR:
+ il.AddInstruction(il.Intrinsic({ RegisterOrFlag::Register(REG_O(operand1)) }, ARM64_INTRIN_LDXR, { ILREG_O(operand2) }));
+ break;
+ case ARM64_LDXRB:
+ il.AddInstruction(il.Intrinsic({ RegisterOrFlag::Register(REG_O(operand1)) }, ARM64_INTRIN_LDXRB, { ILREG_O(operand2) }));
+ break;
+ case ARM64_LDXRH:
+ il.AddInstruction(il.Intrinsic({ RegisterOrFlag::Register(REG_O(operand1)) }, ARM64_INTRIN_LDXRH, { ILREG_O(operand2) }));
+ // We don't have a way to specify intrinsic register size, so we explicitly embed the size in the intrinsic name.
+ case ARM64_LDAXR:
+ il.AddInstruction(il.Intrinsic({ RegisterOrFlag::Register(REG_O(operand1)) }, ARM64_INTRIN_LDAXR, { ILREG_O(operand2) }));
+ break;
+ case ARM64_LDAXRB:
+ il.AddInstruction(il.Intrinsic({ RegisterOrFlag::Register(REG_O(operand1)) }, ARM64_INTRIN_LDAXRB, { ILREG_O(operand2) }));
+ break;
+ case ARM64_LDAXRH:
+ il.AddInstruction(il.Intrinsic({ RegisterOrFlag::Register(REG_O(operand1)) }, ARM64_INTRIN_LDAXRH, { ILREG_O(operand2) }));
+ break;
+ case ARM64_STXR:
+ il.AddInstruction(il.Intrinsic({ RegisterOrFlag::Register(REG_O(operand1)) }, ARM64_INTRIN_STXR, { ILREG_O(operand2), ILREG_O(operand3) }));
+ break;
+ case ARM64_STXRB:
+ il.AddInstruction(il.Intrinsic({ RegisterOrFlag::Register(REG_O(operand1)) }, ARM64_INTRIN_STXRB, { ILREG_O(operand2), ILREG_O(operand3) }));
+ break;
+ case ARM64_STXRH:
+ il.AddInstruction(il.Intrinsic({ RegisterOrFlag::Register(REG_O(operand1)) }, ARM64_INTRIN_STXRH, { ILREG_O(operand2), ILREG_O(operand3) }));
+ break;
+ case ARM64_STLXR:
+ il.AddInstruction(il.Intrinsic({ RegisterOrFlag::Register(REG_O(operand1)) }, ARM64_INTRIN_STLXR, { ILREG_O(operand2), ILREG_O(operand3) }));
+ break;
+ case ARM64_STLXRB:
+ il.AddInstruction(il.Intrinsic({ RegisterOrFlag::Register(REG_O(operand1)) }, ARM64_INTRIN_STLXRB, { ILREG_O(operand2), ILREG_O(operand3) }));
+ break;
+ case ARM64_STLXRH:
+ il.AddInstruction(il.Intrinsic({ RegisterOrFlag::Register(REG_O(operand1)) }, ARM64_INTRIN_STLXRH, { ILREG_O(operand2), ILREG_O(operand3) }));
+ break;
+ case ARM64_LD1:
+ LoadStoreVector(il, true, instr.operands[0], instr.operands[1]);
+ break;
+ case ARM64_LDADD:
+ case ARM64_LDADDA:
+ case ARM64_LDADDL:
+ case ARM64_LDADDAL:
+ LoadStoreOperand(il, true, operand2, operand3, 0);
+ il.AddInstruction(il.Store(REGSZ_O(operand3), ILREG_O(operand3),
+ il.Add(REGSZ_O(operand1), ILREG_O(operand1), ILREG_O(operand2))));
+ break;
+ case ARM64_LDADDB:
+ case ARM64_LDADDAB:
+ case ARM64_LDADDLB:
+ case ARM64_LDADDALB:
+ LoadStoreOperand(il, true, operand2, operand3, 1);
+ il.AddInstruction(il.Store(REGSZ_O(operand3), ILREG_O(operand3),
+ il.Add(1, il.LowPart(1, ILREG_O(operand1)), il.LowPart(1, ILREG_O(operand2)))));
+ break;
+ case ARM64_LDADDH:
+ case ARM64_LDADDAH:
+ case ARM64_LDADDLH:
+ case ARM64_LDADDALH:
+ LoadStoreOperand(il, true, operand2, operand3, 2);
+ il.AddInstruction(il.Store(REGSZ_O(operand3), ILREG_O(operand3),
+ il.Add(2, il.LowPart(2, ILREG_O(operand1)), il.LowPart(2, ILREG_O(operand2)))));
+ break;
+ case ARM64_LSL:
+ il.AddInstruction(ILSETREG_O(operand1, il.ShiftLeft(REGSZ_O(operand2), ILREG_O(operand2),
+ ReadILOperand(il, operand3, REGSZ_O(operand2)))));
+ break;
+ case ARM64_LSR:
+ il.AddInstruction(
+ ILSETREG_O(operand1, il.LogicalShiftRight(REGSZ_O(operand2), ILREG_O(operand2),
+ ReadILOperand(il, operand3, REGSZ_O(operand2)))));
+ break;
+ case ARM64_MOV:
+ {
+ // Small hack... it doesn't seem the lifter ever see the ENC_DUP_ASISDONE_ONLY,
+ // but instead ENC_MOV_DUP_ASISDONE_ONLY
+ if (instr.encoding == ENC_MOV_DUP_ASISDONE_ONLY &&
+ instr.operands[1].laneUsed)
+ // Specific use case. e.g: [mov/dup] h16, v19.h[7].
+ // We let the Neon intrinsic lifter take care of this case.
+ break;
+
+ Register regs[16];
+ int n = unpack_vector(operand1, regs);
+
+ if (n == 1) {
+ il.AddInstruction(ILSETREG(regs[0], ReadILOperand(il, operand2, get_register_size(regs[0]))));
+ } else {
+ Register cregs[2];
+ if (consolidate_vector(operand1, operand2, cregs))
+ il.AddInstruction(ILSETREG(cregs[0], ILREG(cregs[1])));
+ else
+ ABORT_LIFT;
+ }
+
+ break;
+ }
+ case ARM64_MOVI:
+ {
+ Register regs[16];
+ int n = unpack_vector(operand1, regs);
+ for (int i = 0; i < n; ++i)
+ il.AddInstruction(ILSETREG(regs[i], ILCONST_O(get_register_size(regs[i]), operand2)));
+ break;
+ }
+ case ARM64_MVN:
+ case ARM64_MVNI:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.Not(REGSZ_O(operand1), ReadILOperand(il, operand2, REGSZ_O(operand1)))));
+ break;
+ case ARM64_MOVK:
+ // zero the underling register slice
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.And(REGSZ_O(operand1), ILREG_O(operand1),
+ il.Not(REGSZ_O(operand1),
+ il.Const(REGSZ_O(operand1), 0xffffULL << operand2.shiftValue)))));
+ // mov the immediate into it
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.Or(REGSZ_O(operand1), ILREG_O(operand1),
+ il.Const(REGSZ_O(operand1), IMM_O(operand2) << operand2.shiftValue))));
+ break;
+ case ARM64_MOVZ:
+ il.AddInstruction(
+ ILSETREG_O(operand1, il.Const(REGSZ_O(operand1), IMM_O(operand2) << operand2.shiftValue)));
+ break;
+ case ARM64_MUL:
+ il.AddInstruction(
+ ILSETREG_O(operand1, il.Mult(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3))));
+ break;
+ case ARM64_MADD:
+ il.AddInstruction(ILSETREG_O(operand1,
+ ILADDREG_O(operand4, il.Mult(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3)))));
+ break;
+ case ARM64_MRS:
+ {
+ ExprId reg = ILREG_O(operand2);
+ const char* name = get_system_register_name((SystemReg)operand2.sysreg);
+
+ if (strlen(name) == 0)
+ {
+ LogWarn("Unknown system register %d @ 0x%" PRIx64
+ ": S%d_%d_c%d_c%d_%d, using generic system register instead\n",
+ operand2.sysreg, addr, operand2.implspec[0], operand2.implspec[1], operand2.implspec[2],
+ operand2.implspec[3], operand2.implspec[4]);
+ reg = il.Register(8, FAKEREG_SYSREG_UNKNOWN);
+ }
+
+ il.AddInstruction(
+ il.Intrinsic({RegisterOrFlag::Register(REG_O(operand1))}, ARM64_INTRIN_MRS, {reg}));
+ break;
+ }
+ case ARM64_MSUB:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.Sub(REGSZ_O(operand1), ILREG_O(operand4),
+ il.Mult(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3)))));
+ break;
+ case ARM64_MNEG:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.Sub(REGSZ_O(operand1), il.Const(8, 0),
+ il.Mult(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3)))));
+ break;
+ case ARM64_MSR:
+ {
+ uint32_t dst = operand1.sysreg;
+ const char* name = get_system_register_name((SystemReg)dst);
+
+ if (strlen(name) == 0)
+ {
+ LogWarn("Unknown system register %d @ 0x%" PRIx64
+ ": S%d_%d_c%d_c%d_%d, using generic system register instead\n",
+ dst, addr, operand1.implspec[0], operand1.implspec[1], operand1.implspec[2],
+ operand1.implspec[3], operand1.implspec[4]);
+ dst = FAKEREG_SYSREG_UNKNOWN;
+ }
+
+ switch (operand2.operandClass)
+ {
+ case IMM32:
+ il.AddInstruction(il.Intrinsic(
+ {RegisterOrFlag::Register(dst)}, ARM64_INTRIN_MSR, {il.Const(4, IMM_O(operand2))}));
+ break;
+ case REG:
+ il.AddInstruction(
+ il.Intrinsic({RegisterOrFlag::Register(dst)}, ARM64_INTRIN_MSR, {ILREG_O(operand2)}));
+ break;
+ default:
+ LogError("unknown MSR operand class: %x\n", operand2.operandClass);
+ break;
+ }
+ break;
+ }
+ case ARM64_NEG:
+ case ARM64_NEGS:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.Neg(REGSZ_O(operand1), ReadILOperand(il, instr.operands[1], REGSZ_O(operand1)),
+ SETFLAGS)));
+ break;
+ case ARM64_NGC:
+ case ARM64_NGCS:
+ il.AddInstruction(ILSETREG_O(operand1, il.SubBorrow(REGSZ_O(operand1), il.Const(REGSZ_O(operand1), 0),
+ ReadILOperand(il, operand2, REGSZ_O(operand1)),
+ il.Not(0, il.Flag(IL_FLAG_C)), SETFLAGS)));
+ break;
+ case ARM64_NOP:
+ il.AddInstruction(il.Nop());
+ break;
+
+#ifdef LIFT_PAC_AS_INTRINSIC
+ case ARM64_AUTDA:
+ case ARM64_AUTDB:
+ case ARM64_AUTIA:
+ case ARM64_AUTIB:
+ case ARM64_PACDA:
+ case ARM64_PACDB:
+ case ARM64_PACIA:
+ case ARM64_PACIB:
+ // <Xd> is address, <Xn> is modifier
+ il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(REG_O(operand1))},
+ operation_to_intrinsic(instr.operation), {ILREG_O(operand1), ILREG_O(operand2)}));
+ break;
+ case ARM64_PACGA:
+ // <Xd> is address, <Xn>, <Xm> are modifiers, keys
+ il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(REG_O(operand1))},
+ operation_to_intrinsic(instr.operation), {ILREG_O(operand2), ILREG_O(operand3)}));
+ break;
+ case ARM64_AUTIA1716:
+ case ARM64_AUTIB1716:
+ case ARM64_PACIA1716:
+ case ARM64_PACIB1716:
+ // x17 is address, x16 is modifier
+ il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(REG_X17)},
+ operation_to_intrinsic(instr.operation), {il.Register(8, REG_X17), il.Register(8, REG_X16)}));
+ break;
+ case ARM64_AUTDZA:
+ case ARM64_AUTDZB:
+ case ARM64_AUTIZA:
+ case ARM64_AUTIZB:
+ case ARM64_PACDZA:
+ case ARM64_PACDZB:
+ case ARM64_PACIZA:
+ case ARM64_PACIZB:
+ // <Xd> is address, modifier is 0
+ il.AddInstruction(il.Intrinsic(
+ {RegisterOrFlag::Register(REG_O(operand1))}, operation_to_intrinsic(instr.operation), {ILREG_O(operand1), il.Const(8, 0)}));
+ break;
+ case ARM64_XPACI:
+ case ARM64_XPACD:
+ // <Xd> is address
+ il.AddInstruction(il.Intrinsic(
+ {RegisterOrFlag::Register(REG_O(operand1))}, operation_to_intrinsic(instr.operation), {ILREG_O(operand1)}));
+ break;
+ case ARM64_AUTIAZ:
+ case ARM64_AUTIBZ:
+ case ARM64_PACIAZ:
+ case ARM64_PACIBZ:
+ // x30 is address, modifier is 0
+ il.AddInstruction(il.Intrinsic(
+ {RegisterOrFlag::Register(REG_X30)}, operation_to_intrinsic(instr.operation), {il.Register(8, REG_X30), il.Const(8, 0)}));
+ break;
+ case ARM64_XPACLRI:
+ // x30 is address
+ il.AddInstruction(il.Intrinsic(
+ {RegisterOrFlag::Register(REG_X30)}, operation_to_intrinsic(instr.operation), {il.Register(8, REG_X30)}));
+ break;
+ case ARM64_AUTIASP:
+ case ARM64_AUTIBSP:
+ case ARM64_PACIASP:
+ case ARM64_PACIBSP:
+ // x30 is address, sp is modifier
+ il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(REG_X30)},
+ operation_to_intrinsic(instr.operation), {il.Register(8, REG_X30), il.Register(8, REG_SP)}));
+ break;
+#else
+ case ARM64_AUTDA:
+ case ARM64_AUTDB:
+ case ARM64_AUTIA:
+ case ARM64_AUTIB:
+ case ARM64_PACDA:
+ case ARM64_PACDB:
+ case ARM64_PACIA:
+ case ARM64_PACIB:
+ case ARM64_PACGA:
+ case ARM64_AUTIA1716:
+ case ARM64_AUTIB1716:
+ case ARM64_PACIA1716:
+ case ARM64_PACIB1716:
+ case ARM64_AUTDZA:
+ case ARM64_AUTDZB:
+ case ARM64_AUTIZA:
+ case ARM64_AUTIZB:
+ case ARM64_PACDZA:
+ case ARM64_PACDZB:
+ case ARM64_PACIZA:
+ case ARM64_PACIZB:
+ case ARM64_XPACI:
+ case ARM64_XPACD:
+ case ARM64_AUTIAZ:
+ case ARM64_AUTIBZ:
+ case ARM64_PACIAZ:
+ case ARM64_PACIBZ:
+ case ARM64_XPACLRI:
+ case ARM64_AUTIASP:
+ case ARM64_AUTIBSP:
+ case ARM64_PACIASP:
+ case ARM64_PACIBSP:
+ il.AddInstruction(il.Nop());
+ ApplyAttributeToLastInstruction(il, SrcInstructionUsesPointerAuth);
+ break;
+#endif
+ case ARM64_PRFUM:
+ case ARM64_PRFM:
+ // TODO use the PRFM types when we have a better option than defining 18 different intrinsics to
+ // account for:
+ // - 3 types {PLD, PLI, PST}
+ // - 3 targets {L1, L2, L3}
+ // - 2 policies {KEEP, STM}
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_PRFM, {ReadILOperand(il, operand2, 8)}));
+ break;
+ case ARM64_ORN:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.Or(REGSZ_O(operand1), ILREG_O(operand2),
+ il.Not(REGSZ_O(operand1), ReadILOperand(il, operand3, REGSZ_O(operand1))))));
+ break;
+ case ARM64_ORR:
+ case ARM64_ORRS:
+ il.AddInstruction(
+ ILSETREG_O(operand1, il.Or(REGSZ_O(operand1), ILREG_O(operand2),
+ ReadILOperand(il, operand3, REGSZ_O(operand1)), SETFLAGS)));
+ break;
+ case ARM64_PSB:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_PSBCSYNC, {}));
+ break;
+ case ARM64_RETAA:
+ case ARM64_RETAB:
+ SetPacAttr = true;
+ case ARM64_RET:
+ {
+ ExprId reg = (operand1.operandClass == REG) ? ILREG_O(operand1) : il.Register(8, REG_X30);
+ il.AddInstruction(il.Return(reg));
+ if (SetPacAttr)
+ ApplyAttributeToLastInstruction(il, SrcInstructionUsesPointerAuth);
+ }
+ break;
+ case ARM64_REVB: // SVE only
+ case ARM64_REVH:
+ case ARM64_REVW:
+ il.AddInstruction(il.Unimplemented());
+ break;
+ case ARM64_REV16:
+ case ARM64_REV32:
+ case ARM64_REV64:
+ case ARM64_REV:
+ if (IS_SVE_O(operand1))
+ {
+ il.AddInstruction(il.Unimplemented());
+ break;
+ }
+ // if LLIL_BSWAP ever gets added, replace
+ il.AddInstruction(il.Intrinsic(
+ {RegisterOrFlag::Register(REG_O(operand1))}, ARM64_INTRIN_REV, {ILREG_O(operand2)}));
+ break;
+ case ARM64_RBIT:
+ il.AddInstruction(il.Intrinsic(
+ {RegisterOrFlag::Register(REG_O(operand1))}, ARM64_INTRIN_RBIT, {ILREG_O(operand2)}));
+ break;
+ case ARM64_ROR:
+ il.AddInstruction(ILSETREG_O(operand1, il.RotateRight(REGSZ_O(operand2), ILREG_O(operand2),
+ ReadILOperand(il, operand3, REGSZ_O(operand2)))));
+ break;
+ case ARM64_SBC:
+ case ARM64_SBCS:
+ il.AddInstruction(ILSETREG_O(operand1, il.SubBorrow(REGSZ_O(operand1), ILREG_O(operand2),
+ ReadILOperand(il, operand3, REGSZ_O(operand1)),
+ il.Not(0, il.Flag(IL_FLAG_C)), SETFLAGS)));
+ break;
+ case ARM64_SBFIZ:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.ArithShiftRight(REGSZ_O(operand1),
+ il.ShiftLeft(REGSZ_O(operand1), ExtractBits(il, operand2, IMM_O(operand4), 0),
+ il.Const(1, (REGSZ_O(operand1) * 8) - IMM_O(operand4))),
+ il.Const(1, (REGSZ_O(operand1) * 8) - IMM_O(operand3) - IMM_O(operand4)))));
+ break;
+ case ARM64_SBFX:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.ArithShiftRight(REGSZ_O(operand1),
+ il.ShiftLeft(REGSZ_O(operand1),
+ ExtractBits(il, operand2, IMM_O(operand4), IMM_O(operand3)),
+ il.Const(1, (REGSZ_O(operand1) * 8) - IMM_O(operand4) - IMM_O(operand3))),
+ il.Const(1, (REGSZ_O(operand1) * 8) - IMM_O(operand4)))));
+ break;
+ case ARM64_SDIV:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.DivSigned(REGSZ_O(operand2), ILREG_O(operand2), ILREG_O(operand3))));
+ break;
+ case ARM64_SEV:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_SEV, {}));
+ break;
+ case ARM64_SEVL:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_SEVL, {}));
+ break;
+ case ARM64_SHL:
+ {
+ Register srcs[16], dsts[16];
+ int dst_n = unpack_vector(operand1, dsts);
+ int src_n = unpack_vector(operand2, srcs);
+
+ if ((dst_n != src_n) || dst_n == 0)
+ ABORT_LIFT;
+
+ int rsize = get_register_size(dsts[0]);
+ for (int i = 0; i < dst_n; ++i)
+ {
+ il.AddInstruction(il.SetRegister(rsize, dsts[i],
+ il.ShiftLeft(rsize, il.Register(rsize, srcs[i]), il.Const(1, IMM_O(operand3)))));
+ }
+
+ break;
+ }
+ case ARM64_ST1:
+ LoadStoreVector(il, false, instr.operands[0], instr.operands[1]);
+ break;
+ case ARM64_STP:
+ case ARM64_STNP:
+ LoadStoreOperandPair(il, false, instr.operands[0], instr.operands[1], instr.operands[2]);
+ break;
+ case ARM64_STR:
+ case ARM64_STLR:
+ case ARM64_STUR:
+ case ARM64_STLUR:
+ LoadStoreOperand(il, false, instr.operands[0], instr.operands[1], 0);
+ break;
+ case ARM64_STRB:
+ case ARM64_STLRB:
+ case ARM64_STURB:
+ case ARM64_STLURB:
+ LoadStoreOperandSize(il, false, false, 1, instr.operands[0], instr.operands[1]);
+ break;
+ case ARM64_STRH:
+ case ARM64_STLRH:
+ case ARM64_STURH:
+ case ARM64_STLURH:
+ LoadStoreOperandSize(il, false, false, 2, instr.operands[0], instr.operands[1]);
+ break;
+ case ARM64_SUB:
+ case ARM64_SUBS:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.Sub(REGSZ_O(operand1), ILREG_O(operand2),
+ ReadILOperand(il, instr.operands[2], REGSZ_O(operand1)), SETFLAGS)));
+ break;
+ case ARM64_SVC:
+ case ARM64_HVC:
+ case ARM64_SMC:
+ {
+ /* b31,b30==xx of fake register mark transition to ELxx */
+ uint32_t el_mark = 0;
+ if (instr.operation == ARM64_SVC)
+ el_mark = 0x40000000;
+ else if (instr.operation == ARM64_HVC)
+ el_mark = 0x80000000;
+ else if (instr.operation == ARM64_SMC)
+ el_mark = 0xC0000000;
+ /* b15..b0 of fake register still holds syscall number */
+ il.AddInstruction(
+ il.SetRegister(4, FAKEREG_SYSCALL_INFO, il.Const(4, el_mark | IMM_O(operand1))));
+ il.AddInstruction(il.SystemCall());
+ break;
+ }
+ case ARM64_SWP: /* word (4) or doubleword (8) */
+ case ARM64_SWPA:
+ case ARM64_SWPL:
+ case ARM64_SWPAL:
+ LoadStoreOperand(il, true, operand2, operand3, 0);
+ LoadStoreOperand(il, false, operand1, operand3, 0);
+ break;
+ case ARM64_SWPB: /* byte (1) */
+ case ARM64_SWPAB:
+ case ARM64_SWPLB:
+ case ARM64_SWPALB:
+ LoadStoreOperand(il, true, operand2, operand3, 1);
+ il.AddInstruction(il.Store(1, ILREG_O(operand3), il.LowPart(1, ILREG_O(operand1))));
+ break;
+ case ARM64_SWPH: /* half-word (2) */
+ case ARM64_SWPAH:
+ case ARM64_SWPLH:
+ case ARM64_SWPALH:
+ LoadStoreOperand(il, true, operand2, operand3, 2);
+ il.AddInstruction(il.Store(2, ILREG_O(operand3), il.LowPart(2, ILREG_O(operand1))));
+ break;
+ case ARM64_SXTB:
+ il.AddInstruction(
+ ILSETREG_O(operand1, ExtractRegister(il, operand2, 0, 1, true, REGSZ_O(operand1))));
+ break;
+ case ARM64_SXTH:
+ il.AddInstruction(
+ ILSETREG_O(operand1, ExtractRegister(il, operand2, 0, 2, true, REGSZ_O(operand1))));
+ break;
+ case ARM64_SXTW:
+ il.AddInstruction(
+ ILSETREG_O(operand1, ExtractRegister(il, operand2, 0, 4, true, REGSZ_O(operand1))));
+ break;
+ case ARM64_TBNZ:
+ ConditionalJump(arch, il,
+ il.CompareNotEqual(REGSZ_O(operand1), ExtractBit(il, operand1, IMM_O(operand2)),
+ il.Const(REGSZ_O(operand1), 0)),
+ addrSize, IMM_O(operand3), addr + 4);
+ return false;
+ case ARM64_TBZ:
+ ConditionalJump(arch, il,
+ il.CompareEqual(REGSZ_O(operand1), ExtractBit(il, operand1, IMM_O(operand2)),
+ il.Const(REGSZ_O(operand1), 0)),
+ addrSize, IMM_O(operand3), addr + 4);
+ return false;
+ case ARM64_TST:
+ il.AddInstruction(il.And(REGSZ_O(operand1), ILREG_O(operand1),
+ ReadILOperand(il, operand2, REGSZ_O(operand1)), SETFLAGS));
+ break;
+ case ARM64_UMADDL:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.Add(REGSZ_O(operand1), ILREG_O(operand4),
+ il.MultDoublePrecUnsigned(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3)))));
+ break;
+ case ARM64_UMULL:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.MultDoublePrecUnsigned(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3))));
+ break;
+ case ARM64_UMSUBL:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.Sub(REGSZ_O(operand1), ILREG_O(operand4),
+ il.MultDoublePrecUnsigned(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3)))));
+ break;
+ case ARM64_UMNEGL:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.Sub(REGSZ_O(operand1), il.Const(8, 0),
+ il.MultDoublePrecUnsigned(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3)))));
+ break;
+ case ARM64_UXTL:
+ case ARM64_UXTL2:
+ {
+ Register srcs[16], dsts[16];
+ int dst_n = unpack_vector(operand1, dsts);
+ int src_n = unpack_vector(operand2, srcs);
+
+ if (src_n == 0 || dst_n == 0)
+ ABORT_LIFT;
+ if (instr.operation == ARM64_UXTL && (src_n != dst_n))
+ ABORT_LIFT;
+ if (instr.operation == ARM64_UXTL2 && (src_n != 2 * dst_n))
+ ABORT_LIFT;
+
+ for (int i = 0; i < dst_n; ++i)
+ {
+ if (instr.operation == ARM64_UXTL)
+ il.AddInstruction(ILSETREG(dsts[i], ILREG(srcs[i])));
+ else
+ il.AddInstruction(ILSETREG(dsts[i], ILREG(srcs[i + src_n / 2])));
+ }
+
+ break;
+ }
+ case ARM64_SMADDL:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.Add(REGSZ_O(operand1), ILREG_O(operand4),
+ il.MultDoublePrecSigned(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3)))));
+ break;
+ case ARM64_USHR:
+ {
+ Register srcs[16], dsts[16];
+ int dst_n = unpack_vector(operand1, dsts);
+ int src_n = unpack_vector(operand2, srcs);
+
+ if ((dst_n != src_n) || dst_n == 0)
+ ABORT_LIFT;
+
+ int rsize = get_register_size(dsts[0]);
+ for (int i = 0; i < dst_n; ++i)
+ {
+ il.AddInstruction(il.SetRegister(rsize, dsts[i],
+ il.LogicalShiftRight(rsize, il.Register(rsize, srcs[i]), il.Const(1, IMM_O(operand3)))));
+ }
+
+ break;
+ }
+ case ARM64_SMULL:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.MultDoublePrecSigned(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3))));
+ break;
+ case ARM64_SMSUBL:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.Sub(REGSZ_O(operand1), ILREG_O(operand4),
+ il.MultDoublePrecSigned(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3)))));
+ break;
+ case ARM64_SMNEGL:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.Sub(REGSZ_O(operand1), il.Const(8, 0),
+ il.MultDoublePrecSigned(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3)))));
+ break;
+ case ARM64_UMULH:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.LowPart(8,
+ il.LogicalShiftRight(16,
+ il.MultDoublePrecUnsigned(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3)),
+ il.Const(1, 64)))));
+ break;
+ case ARM64_SMULH:
+ il.AddInstruction(ILSETREG_O(operand1,
+ il.LowPart(8,
+ il.LogicalShiftRight(16,
+ il.MultDoublePrecSigned(REGSZ_O(operand1), ILREG_O(operand2), ILREG_O(operand3)),
+ il.Const(1, 64)))));
+ break;
+ case ARM64_UDIV:
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.DivUnsigned(REGSZ_O(operand2), ILREG_O(operand2), ILREG_O(operand3))));
+ break;
+ case ARM64_UBFIZ:
+ il.AddInstruction(
+ ILSETREG_O(operand1, il.ZeroExtend(REGSZ_O(operand1),
+ il.ShiftLeft(REGSZ_O(operand2),
+ il.And(REGSZ_O(operand2), ILREG_O(operand2),
+ il.Const(REGSZ_O(operand2), (1LL << IMM_O(operand4)) - 1)),
+ il.Const(1, IMM_O(operand3))))));
+ break;
+ case ARM64_UBFX:
+ {
+ // ubfx <dst>, <src>, <src_lsb>, <src_len>
+ int src_lsb = IMM_O(operand3);
+ int src_len = IMM_O(operand4);
+ if (src_lsb == 0 && (src_len == 8 || src_len == 16 || src_len == 32 || src_len == 64))
+ {
+ il.AddInstruction(ILSETREG_O(operand1, il.LowPart(src_len / 8, ILREG_O(operand2))));
+ }
+ else
+ {
+ il.AddInstruction(ILSETREG_O(
+ operand1, il.ZeroExtend(REGSZ_O(operand1),
+ il.And(REGSZ_O(operand2),
+ il.LogicalShiftRight(
+ REGSZ_O(operand2), ILREG_O(operand2), il.Const(1, IMM_O(operand3))),
+ il.Const(REGSZ_O(operand2), (1LL << IMM_O(operand4)) - 1)))));
+ }
+ break;
+ }
+ case ARM64_UXTB:
+ il.AddInstruction(
+ ILSETREG_O(operand1, ExtractRegister(il, operand2, 0, 1, false, REGSZ_O(operand1))));
+ break;
+ case ARM64_UXTH:
+ il.AddInstruction(
+ ILSETREG_O(operand1, ExtractRegister(il, operand2, 0, 2, false, REGSZ_O(operand1))));
+ break;
+ case ARM64_WFE:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_WFE, {}));
+ break;
+ case ARM64_WFI:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_WFI, {}));
+ break;
+ case ARM64_BRK:
+ il.AddInstruction(
+ il.Trap(IMM_O(operand1))); // FIXME Breakpoint may need a parameter (IMM_O(operand1)));
+ return false;
+ case ARM64_DUP:
+ {
+ if (instr.encoding != ENC_DUP_ASIMDINS_DR_R)
+ break; // Abort lifting and let the neon intrinsic lifter take over.
+ Register regs[16];
+ int regs_n = unpack_vector(operand1, regs);
+ if (regs_n <= 0)
+ ABORT_LIFT;
+ int lane_sz = REGSZ(regs[0]);
+ for (int i = 0; i < regs_n; ++i)
+ il.AddInstruction(ILSETREG(regs[i], ExtractRegister(il, operand2, 0, lane_sz, 0, lane_sz)));
+ }
+ break;
+ case ARM64_DGH:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_HINT_DGH, {}));
+ break;
+ case ARM64_TSB:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_HINT_TSB, {}));
+ break;
+ case ARM64_CSDB:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_HINT_CSDB, {}));
+ break;
+ case ARM64_HINT:
+ if ((IMM_O(operand1) & ~0b110) == 0b100000)
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_HINT_BTI, {}));
+ else
+ LogWarn("unknown hint operand: 0x%" PRIx64 "\n", IMM_O(operand1));
+ break;
+ case ARM64_HLT:
+ il.AddInstruction(il.Trap(IMM_O(operand1)));
+ return false;
+ case ARM64_UDF:
+ il.AddInstruction(il.Trap(IMM_O(operand1)));
+ return false;
+ case ARM64_YIELD:
+ il.AddInstruction(il.Intrinsic({}, ARM64_INTRIN_YIELD, {}));
+ break;
+ default:
+ break;
+ }
+
+ if (il.GetInstructionCount() > n_instrs_before)
+ return true;
+
+ NeonGetLowLevelILForInstruction(arch, addr, il, instr, addrSize);
+ if (il.GetInstructionCount() > n_instrs_before)
+ return true;
+
+ il.AddInstruction(il.Unimplemented());
+ return true;
+}