summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Rowe <mark@vector35.com>2026-06-03 21:25:55 -0700
committerMark Rowe <mark@vector35.com>2026-06-04 14:31:09 -0700
commit144cd0aa1f13baebac43d5c877dc7cec8e07b389 (patch)
tree97272bd36d7c65f4f85467bc903a7f7a8bd7b6b8
parentd592ed6dafbb134553bf3a0b8ee70ff7f2049aba (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.cpp20
-rwxr-xr-xarch/arm64/arm64test.py30
-rw-r--r--arch/arm64/il.cpp72
-rw-r--r--arch/arm64/il.h5
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,