diff options
| author | Mark Rowe <mark@vector35.com> | 2026-06-04 17:01:20 -0700 |
|---|---|---|
| committer | Mark Rowe <mark@vector35.com> | 2026-06-05 13:01:46 -0700 |
| commit | 0c42e5b2491d890ae7bda0570ab57470c4a48940 (patch) | |
| tree | 40134d5e81ce53d454c5b8bd07700d0d3d36360c | |
| parent | fde1241ce928d38c2031c827ae0ddee5c6f5af0f (diff) | |
[aarch64] Emit abs, min, and max instructions during lifting
| -rwxr-xr-x | arch/arm64/arm64test.py | 49 | ||||
| -rw-r--r-- | arch/arm64/il.cpp | 29 |
2 files changed, 65 insertions, 13 deletions
diff --git a/arch/arm64/arm64test.py b/arch/arm64/arm64test.py index a130794f..eadab3f3 100755 --- a/arch/arm64/arm64test.py +++ b/arch/arm64/arm64test.py @@ -12725,7 +12725,56 @@ tests_grab_bag = [ (b'\x1F\x20\x03\xD5', 'LLIL_NOP()'), # nop, gets optimized from function ] +# FEAT_CSSC integer min/max and absolute value (lifted to dedicated LLIL ops) +tests_cssc = [ + # smax w0, w1, w2 + (b'\x20\x60\xc2\x1a', 'LLIL_SET_REG.d(w0,LLIL_MAXS.d(LLIL_REG.d(w1),LLIL_REG.d(w2)))'), + # smax x0, x1, x2 + (b'\x20\x60\xc2\x9a', 'LLIL_SET_REG.q(x0,LLIL_MAXS.q(LLIL_REG.q(x1),LLIL_REG.q(x2)))'), + # umax w0, w1, w2 + (b'\x20\x64\xc2\x1a', 'LLIL_SET_REG.d(w0,LLIL_MAXU.d(LLIL_REG.d(w1),LLIL_REG.d(w2)))'), + # umax x0, x1, x2 + (b'\x20\x64\xc2\x9a', 'LLIL_SET_REG.q(x0,LLIL_MAXU.q(LLIL_REG.q(x1),LLIL_REG.q(x2)))'), + # smin w0, w1, w2 + (b'\x20\x68\xc2\x1a', 'LLIL_SET_REG.d(w0,LLIL_MINS.d(LLIL_REG.d(w1),LLIL_REG.d(w2)))'), + # smin x0, x1, x2 + (b'\x20\x68\xc2\x9a', 'LLIL_SET_REG.q(x0,LLIL_MINS.q(LLIL_REG.q(x1),LLIL_REG.q(x2)))'), + # umin w0, w1, w2 + (b'\x20\x6c\xc2\x1a', 'LLIL_SET_REG.d(w0,LLIL_MINU.d(LLIL_REG.d(w1),LLIL_REG.d(w2)))'), + # umin x0, x1, x2 + (b'\x20\x6c\xc2\x9a', 'LLIL_SET_REG.q(x0,LLIL_MINU.q(LLIL_REG.q(x1),LLIL_REG.q(x2)))'), + # abs w0, w1 + (b'\x20\x20\xc0\x5a', 'LLIL_SET_REG.d(w0,LLIL_ABS.d(LLIL_REG.d(w1)))'), + # abs x0, x1 + (b'\x20\x20\xc0\xda', 'LLIL_SET_REG.q(x0,LLIL_ABS.q(LLIL_REG.q(x1)))'), + # smax w0, w1, #0x5 + (b'\x20\x14\xc0\x11', 'LLIL_SET_REG.d(w0,LLIL_MAXS.d(LLIL_REG.d(w1),LLIL_CONST.d(0x5)))'), + # umin w0, w1, #0x7 + (b'\x20\x1c\xcc\x11', 'LLIL_SET_REG.d(w0,LLIL_MINU.d(LLIL_REG.d(w1),LLIL_CONST.d(0x7)))'), + # ctz w0, w1 + (b'\x20\x18\xc0\x5a', 'LLIL_SET_REG.d(w0,LLIL_CTZ.d(LLIL_REG.d(w1)))'), + # ctz x0, x1 + (b'\x20\x18\xc0\xda', 'LLIL_SET_REG.q(x0,LLIL_CTZ.q(LLIL_REG.q(x1)))'), + # cnt w0, w1 (FEAT_CSSC scalar population count) + (b'\x20\x1c\xc0\x5a', 'LLIL_SET_REG.d(w0,LLIL_POPCNT.d(LLIL_REG.d(w1)))'), + # cnt x0, x1 (FEAT_CSSC scalar population count) + (b'\x20\x1c\xc0\xda', 'LLIL_SET_REG.q(x0,LLIL_POPCNT.q(LLIL_REG.q(x1)))'), + + # Vector/SVE forms of these mnemonics are not FEAT_CSSC scalar ops and must not be lifted + # as whole-register scalar operations. The NEON cnt has a per-element intrinsic; the others + # have no native scalar representation and are left unimplemented. + # cnt v0.8b, v1.8b + (b'\x20\x58\x20\x0e', 'LLIL_INTRINSIC([v0],_PopulationCount,[LLIL_REG.o(v1)])'), + # cnt v0.16b, v1.16b + (b'\x20\x58\x20\x4e', 'LLIL_INTRINSIC([v0],_PopulationCount,[LLIL_REG.o(v1)])'), + # abs v0.8b, v1.8b + (b'\x20\xb8\x20\x0e', 'LLIL_UNIMPL()'), + # abs v0.2d, v1.2d + (b'\x20\xb8\xe0\x4e', 'LLIL_UNIMPL()'), +] + test_cases = \ + tests_cssc + \ tests_shll + \ tests_udf + \ tests_pac + \ diff --git a/arch/arm64/il.cpp b/arch/arm64/il.cpp index 2cac5f04..c0ff2c71 100644 --- a/arch/arm64/il.cpp +++ b/arch/arm64/il.cpp @@ -1329,12 +1329,19 @@ bool GetLowLevelILForInstruction( switch (instr.operation) { case ARM64_ABS: - { - ExprId src = ILREG_O(operand2); - GenIfElse(il, il.CompareSignedLessThan(REGSZ_O(operand2), src, il.Const(REGSZ_O(operand2), 0)), - ILSETREG_O(operand1, il.Neg(REGSZ_O(operand2), src)), ILSETREG_O(operand1, src)); + switch (instr.encoding) + { + case ENC_ABS_32_DP_1SRC: + case ENC_ABS_64_DP_1SRC: + // FEAT_CSSC scalar absolute value on a general-purpose register + il.AddInstruction(ILSETREG_O(operand1, il.AbsoluteValue(REGSZ_O(operand2), ILREG_O(operand2)))); + break; + default: + // The NEON and SVE forms are per-element absolute values, which have no native scalar + // representation + il.AddInstruction(il.Unimplemented()); + } break; - } case ARM64_ADD: switch (instr.encoding) { @@ -4004,8 +4011,7 @@ bool GetLowLevelILForInstruction( return true; } - GenIfElse(il, il.CompareSignedGreaterThan(REGSZ_O(operand2), op2, op3), ILSETREG_O(operand1, op2), - ILSETREG_O(operand1, op3)); + il.AddInstruction(ILSETREG_O(operand1, il.MaxSigned(REGSZ_O(operand2), op2, op3))); break; } case ARM64_SMIN: @@ -4028,8 +4034,7 @@ bool GetLowLevelILForInstruction( return true; } - GenIfElse(il, il.CompareSignedLessThan(REGSZ_O(operand2), op2, op3), ILSETREG_O(operand1, op2), - ILSETREG_O(operand1, op3)); + il.AddInstruction(ILSETREG_O(operand1, il.MinSigned(REGSZ_O(operand2), op2, op3))); break; } case ARM64_UDIV: @@ -4064,8 +4069,7 @@ bool GetLowLevelILForInstruction( return true; } - GenIfElse(il, il.CompareUnsignedGreaterThan(REGSZ_O(operand2), op2, op3), ILSETREG_O(operand1, op2), - ILSETREG_O(operand1, op3)); + il.AddInstruction(ILSETREG_O(operand1, il.MaxUnsigned(REGSZ_O(operand2), op2, op3))); break; } case ARM64_UMIN: @@ -4088,8 +4092,7 @@ bool GetLowLevelILForInstruction( return true; } - GenIfElse(il, il.CompareUnsignedLessThan(REGSZ_O(operand2), op2, op3), ILSETREG_O(operand1, op2), - ILSETREG_O(operand1, op3)); + il.AddInstruction(ILSETREG_O(operand1, il.MinUnsigned(REGSZ_O(operand2), op2, op3))); break; } case ARM64_UBFIZ: |
