diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2024-03-05 19:50:13 -0500 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2024-03-05 20:34:34 -0500 |
| commit | e093c21ed880ac3eb72119be15093ee04f8ce299 (patch) | |
| tree | 9f720ebdc0ae415734b1199ed341668c69710a94 /arch/arm64/il.cpp | |
| parent | 0609276712622908254065546102381466033141 (diff) | |
Move architecture modules into the API repo
Diffstat (limited to 'arch/arm64/il.cpp')
| -rw-r--r-- | arch/arm64/il.cpp | 2512 |
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; +} |
