diff options
| author | Mark Rowe <mark@vector35.com> | 2026-06-03 21:25:55 -0700 |
|---|---|---|
| committer | Mark Rowe <mark@vector35.com> | 2026-06-04 14:31:09 -0700 |
| commit | 144cd0aa1f13baebac43d5c877dc7cec8e07b389 (patch) | |
| tree | 97272bd36d7c65f4f85467bc903a7f7a8bd7b6b8 | |
| parent | d592ed6dafbb134553bf3a0b8ee70ff7f2049aba (diff) | |
[aarch64] Emit bswap, popcnt, clz, ctz, cls, and rbit instructions during lifting
Intrinsics are still used when lfiting vector instructions, and for
64-bit `rev16` instructions that are not easily composed from the new
instructions.
| -rw-r--r-- | arch/arm64/arch_arm64.cpp | 20 | ||||
| -rwxr-xr-x | arch/arm64/arm64test.py | 30 | ||||
| -rw-r--r-- | arch/arm64/il.cpp | 72 | ||||
| -rw-r--r-- | arch/arm64/il.h | 5 |
4 files changed, 79 insertions, 48 deletions
diff --git a/arch/arm64/arch_arm64.cpp b/arch/arm64/arch_arm64.cpp index 20b00e8a..8b1fbc55 100644 --- a/arch/arm64/arch_arm64.cpp +++ b/arch/arm64/arch_arm64.cpp @@ -994,18 +994,12 @@ class Arm64Architecture : public Architecture return "__xpaci"; case ARM64_INTRIN_ERET: return "_eret"; - case ARM64_INTRIN_CLZ: - return "_CountLeadingZeros"; case ARM64_INTRIN_CNT: return "_PopulationCount"; - case ARM64_INTRIN_CTZ: - return "_CountTrailingZeros"; case ARM64_INTRIN_CLREX: return "__clrex"; - case ARM64_INTRIN_REV: - return "_byteswap"; - case ARM64_INTRIN_RBIT: - return "__rbit"; + case ARM64_INTRIN_REV16: + return "__rev16"; case ARM64_INTRIN_AESD: return "__aesd"; case ARM64_INTRIN_AESE: @@ -1125,12 +1119,9 @@ class Arm64Architecture : public Architecture NameAndType(Type::IntegerType(8, false)) }; break; - case ARM64_INTRIN_CLZ: // reads <Xn> case ARM64_INTRIN_CNT: // reads <Xn> - case ARM64_INTRIN_CTZ: // reads <Xn> case ARM64_INTRIN_PRFM: - case ARM64_INTRIN_REV: // reads <Xn> - case ARM64_INTRIN_RBIT: // reads <Xn> + case ARM64_INTRIN_REV16: // reads <Xn> return {NameAndType(Type::IntegerType(8, false))}; case ARM64_INTRIN_AUTDA: // reads <Xd>, <Xn|SP> case ARM64_INTRIN_AUTDB: // reads <Xd>, <Xn|SP> @@ -1190,11 +1181,8 @@ class Arm64Architecture : public Architecture case ARM64_INTRIN_PACIB: // writes <Xd> case ARM64_INTRIN_XPACD: // writes <Xd> case ARM64_INTRIN_XPACI: // writes <Xd> - case ARM64_INTRIN_CLZ: // writes <Xd> case ARM64_INTRIN_CNT: // writes <Xd> - case ARM64_INTRIN_CTZ: // writes <Xd> - case ARM64_INTRIN_REV: // writes <Xd> - case ARM64_INTRIN_RBIT: // writes <Xd> + case ARM64_INTRIN_REV16: // writes <Xd> return {Type::IntegerType(8, false)}; case ARM64_INTRIN_AESD: case ARM64_INTRIN_AESE: diff --git a/arch/arm64/arm64test.py b/arch/arm64/arm64test.py index 7ad7332a..a130794f 100755 --- a/arch/arm64/arm64test.py +++ b/arch/arm64/arm64test.py @@ -417,11 +417,11 @@ tests_msr = [ # mrs x26, s2_3_c8_c14_1 MRS_RS_systemmove (b'\x3A\x8E\x33\xD5', 'LLIL_INTRINSIC([x26],_ReadMSR,[LLIL_CONST.d(0x9C71)])'), # msr spsel, #0x9 MSR_SI_pstate - (b'\xBF\x49\x00\xD5', 'LLIL_INTRINSIC([],_WriteMSR,[LLIL_CONST.d(0xFF13),LLIL_CONST.d(0x9)])'), + (b'\xBF\x49\x00\xD5', 'LLIL_INTRINSIC([],_WriteMSR,[LLIL_CONST.d(0x205),LLIL_CONST.d(0x9)])'), # msr s0_0_c4_c5_4, xzr MSR_SI_pstate (b'\x9F\x45\x00\xD5', 'LLIL_INTRINSIC([],_WriteMSR,[LLIL_CONST.d(0xC213),LLIL_CONST.d(0x5)])'), # msr daifset, #0xe MSR_SI_pstate - (b'\xDF\x4E\x03\xD5', 'LLIL_INTRINSIC([],_WriteMSR,[LLIL_CONST.d(0xDA11),LLIL_CONST.d(0xE)])'), + (b'\xDF\x4E\x03\xD5', 'LLIL_INTRINSIC([],_WriteMSR,[LLIL_CONST.d(0x1A06),LLIL_CONST.d(0xE)])'), # msr dit, #0x5 MSR_SI_pstate (b'\x5F\x45\x03\xD5', 'LLIL_INTRINSIC([],_WriteMSR,[LLIL_CONST.d(0xDA15),LLIL_CONST.d(0x5)])'), # msr s3_3_c1_c9_1, x11 MSR_SR_systemmove @@ -6144,21 +6144,21 @@ tests_sha = [ tests_rev = [ (b'\x49\x29\xC8\x9A', 'LLIL_SET_REG.q(x9,LLIL_ASR.q(LLIL_REG.q(x10),LLIL_REG.q(x8)))'), # asr x9, x10, x8 # rev16 w25, w2 - (b'\x59\x04\xC0\x5A', 'LLIL_INTRINSIC([w25],_byteswap,[LLIL_REG.d(w2)])'), + (b'\x59\x04\xC0\x5A', 'LLIL_SET_REG.d(w25,LLIL_ROR.d(LLIL_BSWAP.d(LLIL_REG.d(w2)),LLIL_CONST.b(0x10)))'), # rev16 w25, w6 - (b'\xD9\x04\xC0\x5A', 'LLIL_INTRINSIC([w25],_byteswap,[LLIL_REG.d(w6)])'), + (b'\xD9\x04\xC0\x5A', 'LLIL_SET_REG.d(w25,LLIL_ROR.d(LLIL_BSWAP.d(LLIL_REG.d(w6)),LLIL_CONST.b(0x10)))'), # rev16 x23, x3 - (b'\x77\x04\xC0\xDA', 'LLIL_INTRINSIC([x23],_byteswap,[LLIL_REG.q(x3)])'), + (b'\x77\x04\xC0\xDA', 'LLIL_INTRINSIC([x23],__rev16,[LLIL_REG.q(x3)])'), # rev16 x14, x17 - (b'\x2E\x06\xC0\xDA', 'LLIL_INTRINSIC([x14],_byteswap,[LLIL_REG.q(x17)])'), + (b'\x2E\x06\xC0\xDA', 'LLIL_INTRINSIC([x14],__rev16,[LLIL_REG.q(x17)])'), # rev16 v8.16b, v26.16b (b'\x48\x1B\x20\x4E', 'LLIL_INTRINSIC([v8],vrev16q_s8,[LLIL_REG.o(v26)])'), # rev16 v4.8b, v27.8b (b'\x64\x1B\x20\x0E', 'LLIL_INTRINSIC([v4],vrev16_s8,[LLIL_REG.o(v27)])'), # rev32 x29, x8 - (b'\x1D\x09\xC0\xDA', 'LLIL_INTRINSIC([fp],_byteswap,[LLIL_REG.q(x8)])'), + (b'\x1D\x09\xC0\xDA', 'LLIL_SET_REG.q(fp,LLIL_ROR.q(LLIL_BSWAP.q(LLIL_REG.q(x8)),LLIL_CONST.b(0x20)))'), # rev32 x18, x26 - (b'\x52\x0B\xC0\xDA', 'LLIL_INTRINSIC([x18],_byteswap,[LLIL_REG.q(x26)])'), + (b'\x52\x0B\xC0\xDA', 'LLIL_SET_REG.q(x18,LLIL_ROR.q(LLIL_BSWAP.q(LLIL_REG.q(x26)),LLIL_CONST.b(0x20)))'), # rev32 v18.4h, v15.4h (b'\xF2\x09\x60\x2E', 'LLIL_INTRINSIC([v18],vrev32_s16,[LLIL_REG.o(v15)])'), # rev32 v20.8h, v26.8h @@ -6168,13 +6168,13 @@ tests_rev = [ # rev64 v17.16b, v18.16b (b'\x51\x0A\x20\x4E', 'LLIL_INTRINSIC([v17],vrev64q_s8,[LLIL_REG.o(v18)])'), # rev w14, w21 - (b'\xAE\x0A\xC0\x5A', 'LLIL_INTRINSIC([w14],_byteswap,[LLIL_REG.d(w21)])'), + (b'\xAE\x0A\xC0\x5A', 'LLIL_SET_REG.d(w14,LLIL_BSWAP.d(LLIL_REG.d(w21)))'), # rev w23, w3 - (b'\x77\x08\xC0\x5A', 'LLIL_INTRINSIC([w23],_byteswap,[LLIL_REG.d(w3)])'), + (b'\x77\x08\xC0\x5A', 'LLIL_SET_REG.d(w23,LLIL_BSWAP.d(LLIL_REG.d(w3)))'), # rev x11, x6 - (b'\xCB\x0C\xC0\xDA', 'LLIL_INTRINSIC([x11],_byteswap,[LLIL_REG.q(x6)])'), + (b'\xCB\x0C\xC0\xDA', 'LLIL_SET_REG.q(x11,LLIL_BSWAP.q(LLIL_REG.q(x6)))'), # rev x18, x0 - (b'\x12\x0C\xC0\xDA', 'LLIL_INTRINSIC([x18],_byteswap,[LLIL_REG.q(x0)])'), + (b'\x12\x0C\xC0\xDA', 'LLIL_SET_REG.q(x18,LLIL_BSWAP.q(LLIL_REG.q(x0)))'), # rev p1.s, p8.s (b'\x01\x41\xB4\x05', 'LLIL_UNIMPL()'), # rev p15.d, p11.d @@ -12302,9 +12302,9 @@ tests_grab_bag = [ (b'\x20\x00\x02\xBA', 'LLIL_SET_REG.q(x0,LLIL_ADC.q{*}(LLIL_REG.q(x1),LLIL_REG.q(x2),LLIL_FLAG(c)))'), # adcs x0, x1, x2 (b'\x08\x75\x93\x13', 'LLIL_SET_REG.d(w8,LLIL_LSR.q(LLIL_OR.q(LLIL_LSL.q(LLIL_REG.d(w8),LLIL_CONST.b(0x20)),LLIL_REG.d(w19)),LLIL_CONST.b(0x1D)))'), # extr w8, w8, w19, #0x1d (b'\x20\x28\xC2\x93', 'LLIL_SET_REG.q(x0,LLIL_LSR.o(LLIL_OR.o(LLIL_LSL.o(LLIL_REG.q(x1),LLIL_CONST.b(0x40)),LLIL_REG.q(x2)),LLIL_CONST.b(0xA)))'), # extr x0, x1, x2, #10 - (b'\xC6\x0C\xC0\xDA', 'LLIL_INTRINSIC([x6],_byteswap,[LLIL_REG.q(x6)])'), # rev x6, x6 - (b'\xCB\x10\xC0\xDA', 'LLIL_INTRINSIC([x11],_CountLeadingZeros,[LLIL_REG.q(x6)])'), # clz x11, x6 - (b'\x63\x00\xC0\xDA', 'LLIL_INTRINSIC([x3],__rbit,[LLIL_REG.q(x3)])'), # rbit x3, x3 + (b'\xC6\x0C\xC0\xDA', 'LLIL_SET_REG.q(x6,LLIL_BSWAP.q(LLIL_REG.q(x6)))'), # rev x6, x6 + (b'\xCB\x10\xC0\xDA', 'LLIL_SET_REG.q(x11,LLIL_CLZ.q(LLIL_REG.q(x6)))'), # clz x11, x6 + (b'\x63\x00\xC0\xDA', 'LLIL_SET_REG.q(x3,LLIL_RBIT.q(LLIL_REG.q(x3)))'), # rbit x3, x3 # Unknown system register # (b'\x41\x00\x1B\xD5', 'LLIL_INTRINSIC([sysreg_unknown],_WriteStatusReg,[LLIL_REG.q(x1)])'), # msr s3_3_c0_c0_2, x1 # (b'\x43\x00\x3B\xD5', 'LLIL_INTRINSIC([x3],_ReadStatusReg,[LLIL_REG.q(sysreg_unknown)])'), # mrs x3, s3_3_c0_c0_2 diff --git a/arch/arm64/il.cpp b/arch/arm64/il.cpp index f02cdd2d..734e06d6 100644 --- a/arch/arm64/il.cpp +++ b/arch/arm64/il.cpp @@ -1767,17 +1767,28 @@ bool GetLowLevelILForInstruction( ILSETREG_O(operand1, il.Neg(REGSZ_O(operand1), ILREG_O(operand2))), ILSETREG_O(operand1, ILREG_O(operand2))); break; + case ARM64_CLS: + il.AddInstruction(ILSETREG_O(operand1, il.CountLeadingSigns(REGSZ_O(operand2), ILREG_O(operand2)))); + break; case ARM64_CLZ: - il.AddInstruction(il.Intrinsic( - {RegisterOrFlag::Register(REG_O(operand1))}, ARM64_INTRIN_CLZ, {ILREG_O(operand2)})); + il.AddInstruction(ILSETREG_O(operand1, il.CountLeadingZeros(REGSZ_O(operand2), ILREG_O(operand2)))); break; case ARM64_CNT: - il.AddInstruction( - il.Intrinsic({RegisterOrFlag::Register(REG_O(operand1))}, ARM64_INTRIN_CNT, {ILREG_O(operand2)})); + switch (instr.encoding) { + case ENC_CNT_32_DP_1SRC: + case ENC_CNT_64_DP_1SRC: + // FEAT_CSSC scalar population count on a general-purpose register + il.AddInstruction(ILSETREG_O(operand1, il.PopulationCount(REGSZ_O(operand2), ILREG_O(operand2)))); + break; + default: + // The NEON and SVE forms are per-element population counts, which have no native scalar + // representation and are lifted as an intrinsic + il.AddInstruction( + il.Intrinsic({RegisterOrFlag::Register(REG_O(operand1))}, ARM64_INTRIN_CNT, {ILREG_O(operand2)})); + } break; case ARM64_CTZ: - il.AddInstruction( - il.Intrinsic({RegisterOrFlag::Register(REG_O(operand1))}, ARM64_INTRIN_CTZ, {ILREG_O(operand2)})); + il.AddInstruction(ILSETREG_O(operand1, il.CountTrailingZeros(REGSZ_O(operand2), ILREG_O(operand2)))); break; case ARM64_DC: // il.AddInstruction( @@ -3159,12 +3170,50 @@ bool GetLowLevelILForInstruction( il.AddInstruction(il.Unimplemented()); break; case ARM64_REV16: + switch (instr.encoding) { + case ENC_REV16_ASIMDMISC_R: + break; + default: + if (IS_SVE_O(operand1)) + { + il.AddInstruction(il.Unimplemented()); + break; + } + if (REGSZ_O(operand1) == 4) + { + // A 32-bit register holds two 16-bit lanes, so reversing the bytes within each lane is + // a full byte reversal rotated by one halfword + il.AddInstruction(ILSETREG_O(operand1, + il.RotateRight(4, il.ByteSwap(4, ILREG_O(operand2)), il.Const(1, 16)))); + } + else + { + // A 64-bit register holds four 16-bit lanes; reversing the bytes within each lane has + // no native representation and is lifted as an intrinsic + il.AddInstruction(il.Intrinsic( + {RegisterOrFlag::Register(REG_O(operand1))}, ARM64_INTRIN_REV16, {ILREG_O(operand2)})); + } + } + break; case ARM64_REV32: + switch (instr.encoding) { + case ENC_REV32_ASIMDMISC_R: + break; + default: + if (IS_SVE_O(operand1)) + { + il.AddInstruction(il.Unimplemented()); + break; + } + // REV32 reverses bytes within each 32-bit lane; a 64-bit register holds two such lanes, + // so it is a full byte reversal rotated by one word + il.AddInstruction(ILSETREG_O(operand1, + il.RotateRight(8, il.ByteSwap(8, ILREG_O(operand2)), il.Const(1, 32)))); + } + break; case ARM64_REV64: case ARM64_REV: switch (instr.encoding) { - case ENC_REV16_ASIMDMISC_R: - case ENC_REV32_ASIMDMISC_R: case ENC_REV64_ASIMDMISC_R: break; default: @@ -3173,9 +3222,7 @@ bool GetLowLevelILForInstruction( 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)})); + il.AddInstruction(ILSETREG_O(operand1, il.ByteSwap(REGSZ_O(operand2), ILREG_O(operand2)))); } break; case ARM64_RBIT: @@ -3183,8 +3230,7 @@ bool GetLowLevelILForInstruction( case ENC_RBIT_ASIMDMISC_R: break; default: - il.AddInstruction(il.Intrinsic( - {RegisterOrFlag::Register(REG_O(operand1))}, ARM64_INTRIN_RBIT, {ILREG_O(operand2)})); + il.AddInstruction(ILSETREG_O(operand1, il.ReverseBits(REGSZ_O(operand2), ILREG_O(operand2)))); } break; case ARM64_ROR: diff --git a/arch/arm64/il.h b/arch/arm64/il.h index d5531197..1e182318 100644 --- a/arch/arm64/il.h +++ b/arch/arm64/il.h @@ -72,12 +72,9 @@ enum Arm64Intrinsic : uint32_t ARM64_INTRIN_XPACI, ARM64_INTRIN_YIELD, ARM64_INTRIN_ERET, - ARM64_INTRIN_CLZ, ARM64_INTRIN_CNT, - ARM64_INTRIN_CTZ, ARM64_INTRIN_CLREX, - ARM64_INTRIN_REV, - ARM64_INTRIN_RBIT, + ARM64_INTRIN_REV16, ARM64_INTRIN_AESD, ARM64_INTRIN_AESE, ARM64_INTRIN_AESIMC, |
