From ae113a398a8af568c51aa14125e8a28aa49d5130 Mon Sep 17 00:00:00 2001 From: Brandon Miller Date: Mon, 10 Aug 2026 12:58:18 -0400 Subject: [PATCH] Add ARMv7 vabs and fix vcvt lifting Lift scalar VABS directly when the lane and register widths match, and use intrinsics for packed ARM and Thumb forms Correct Thumb F32/F64 VCVT result sizing, fix scalar fixed-point rounding and extension, and add an intrinsic for lane-wise fixed-point conversions --- arch/armv7/arch_armv7.cpp | 17 +++ arch/armv7/il.cpp | 154 +++++++++++++++-------- arch/armv7/il.h | 2 + arch/armv7/test_lift.py | 60 ++++++++- arch/armv7/thumb2_disasm/arch_thumb2.cpp | 17 +++ arch/armv7/thumb2_disasm/il_thumb2.cpp | 81 ++++++++++-- 6 files changed, 268 insertions(+), 63 deletions(-) diff --git a/arch/armv7/arch_armv7.cpp b/arch/armv7/arch_armv7.cpp index c7909eea7..c29d5b7c8 100644 --- a/arch/armv7/arch_armv7.cpp +++ b/arch/armv7/arch_armv7.cpp @@ -1601,6 +1601,10 @@ class Armv7Architecture: public ArmCommonArchitecture return "__vrhadd"; case ARMV7_INTRIN_VRECPE: return "__vrecpe"; + case ARMV7_INTRIN_VABS: + return "__vabs"; + case ARMV7_INTRIN_VCVT_FIXED: + return "__vcvt_fixed"; case ARMV7_INTRIN_VQSHL: return "__vqshl"; case ARMV7_INTRIN_VQRSHL: @@ -1866,6 +1870,8 @@ class Armv7Architecture: public ArmCommonArchitecture ARMV7_INTRIN_VHADD, ARMV7_INTRIN_VRHADD, ARMV7_INTRIN_VRECPE, + ARMV7_INTRIN_VABS, + ARMV7_INTRIN_VCVT_FIXED, ARMV7_INTRIN_VQSHL, ARMV7_INTRIN_VQRSHL, ARMV7_INTRIN_VQSHRN, @@ -2196,11 +2202,20 @@ class Armv7Architecture: public ArmCommonArchitecture NameAndType("source2", Type::IntegerType(8, false)), }; case ARMV7_INTRIN_VRECPE: + case ARMV7_INTRIN_VABS: return { NameAndType("size", Type::IntegerType(1, false)), NameAndType("is_float", Type::BoolType()), NameAndType("source", Type::IntegerType(8, false)), }; + case ARMV7_INTRIN_VCVT_FIXED: + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("fractional_bits", Type::IntegerType(1, false)), + NameAndType("to_fixed", Type::BoolType()), + NameAndType("is_unsigned", Type::BoolType()), + NameAndType("source", Type::IntegerType(8, false)), + }; case ARMV7_INTRIN_VREV16: case ARMV7_INTRIN_VREV32: case ARMV7_INTRIN_VREV64: @@ -2550,6 +2565,8 @@ class Armv7Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VHADD: case ARMV7_INTRIN_VRHADD: case ARMV7_INTRIN_VRECPE: + case ARMV7_INTRIN_VABS: + case ARMV7_INTRIN_VCVT_FIXED: case ARMV7_INTRIN_VQSHL: case ARMV7_INTRIN_VQRSHL: case ARMV7_INTRIN_VQSHRN: diff --git a/arch/armv7/il.cpp b/arch/armv7/il.cpp index f5fda63e1..1855c3a0b 100644 --- a/arch/armv7/il.cpp +++ b/arch/armv7/il.cpp @@ -4542,64 +4542,70 @@ bool GetLowLevelILForArmInstruction(Architecture* arch, uint64_t addr, LowLevelI if (op3.cls == IMM) { size_t destSize = get_register_size(op1.reg); - size_t fixedSize = GetDataTypeSize(instr.dataType); - if (!fixedSize) - fixedSize = destSize; - size_t sourceSize = GetDataTypeSize(instr.dataType2); - if (!sourceSize) - sourceSize = get_register_size(op2.reg); - auto source = [&]() { - ExprId value = il.Register(get_register_size(op2.reg), op2.reg); - if (sourceSize < get_register_size(op2.reg)) - value = il.LowPart(sourceSize, value); - return value; - }; - switch (instr.dataType) + size_t sourceRegisterSize = get_register_size(op2.reg); + bool destIsFloat = instr.dataType == DT_F32 || instr.dataType == DT_F64; + bool sourceIsFloat = instr.dataType2 == DT_F32 || instr.dataType2 == DT_F64; + bool toFixed = !destIsFloat && sourceIsFloat; + bool fromFixed = destIsFloat && !sourceIsFloat; + DataType fixedType = toFixed ? instr.dataType : instr.dataType2; + DataType floatType = toFixed ? instr.dataType2 : instr.dataType; + size_t fixedSize = GetDataTypeSize(fixedType); + size_t floatSize = GetDataTypeSize(floatType); + bool validFixedType = fixedType == DT_S16 || fixedType == DT_U16 + || fixedType == DT_S32 || fixedType == DT_U32; + bool isUnsigned = fixedType == DT_U16 || fixedType == DT_U32; + + if ((!toFixed && !fromFixed) || !validFixedType || fixedSize == 0 || floatSize == 0 + || destSize == 0 || sourceRegisterSize == 0 || destSize != sourceRegisterSize) { - case DT_S16: - case DT_S32: - ConditionExecute(il, instr.cond, il.SetRegister(destSize, op1.reg, - il.SignExtend(destSize, - il.FloatToInt(fixedSize, - il.RoundToInt(sourceSize, - il.FloatMult(sourceSize, - source(), - FixedPointScale(il, sourceSize, op3.imm))))))); - break; - case DT_U16: - case DT_U32: - ConditionExecute(il, instr.cond, il.SetRegister(destSize, op1.reg, - il.ZeroExtend(destSize, - il.FloatToInt(fixedSize, - il.RoundToInt(sourceSize, - il.FloatMult(sourceSize, - source(), - FixedPointScale(il, sourceSize, op3.imm))))))); + ConditionExecute(il, instr.cond, il.Unimplemented()); break; - case DT_F32: - case DT_F64: - switch (instr.dataType2) + } + + if (destSize != floatSize) + { + if (floatSize != 4 || fixedSize != 4 || (destSize != 8 && destSize != 16)) { - case DT_S16: - case DT_S32: - ConditionExecute(il, instr.cond, il.SetRegister(destSize, op1.reg, - il.FloatDiv(destSize, - il.IntToFloat(destSize, il.SignExtend(destSize, source())), - FixedPointScale(il, destSize, op3.imm)))); - break; - case DT_U16: - case DT_U32: - ConditionExecute(il, instr.cond, il.SetRegister(destSize, op1.reg, - il.FloatDiv(destSize, - il.IntToFloat(destSize, il.ZeroExtend(destSize, source())), - FixedPointScale(il, destSize, op3.imm)))); - break; - default: + ConditionExecute(il, instr.cond, il.Unimplemented()); break; } + + ConditionExecute(il, instr.cond, + il.Intrinsic( + { RegisterOrFlag::Register(op1.reg) }, + ARMV7_INTRIN_VCVT_FIXED, + { + il.Const(1, floatSize * 8), + il.Const(1, op3.imm), + il.Const(1, toFixed ? 1 : 0), + il.Const(1, isUnsigned ? 1 : 0), + il.Register(sourceRegisterSize, op2.reg), + })); break; - default: - break; + } + + if (toFixed) + { + ExprId scaled = il.FloatMult(floatSize, + il.Register(sourceRegisterSize, op2.reg), + FixedPointScale(il, floatSize, op3.imm)); + ExprId converted = il.FloatToInt(fixedSize, il.FloatTrunc(floatSize, scaled)); + converted = isUnsigned + ? il.ZeroExtend(destSize, converted) + : il.SignExtend(destSize, converted); + ConditionExecute(il, instr.cond, il.SetRegister(destSize, op1.reg, converted)); + } + else + { + ExprId source = il.Register(sourceRegisterSize, op2.reg); + if (fixedSize < sourceRegisterSize) + source = il.LowPart(fixedSize, source); + ExprId converted = isUnsigned + ? il.IntToFloat(floatSize, il.ZeroExtend(floatSize, source)) + : il.IntToFloat(floatSize, il.SignExtend(floatSize, source)); + ConditionExecute(il, instr.cond, + il.SetRegister(destSize, op1.reg, + il.FloatDiv(floatSize, converted, FixedPointScale(il, floatSize, op3.imm)))); } break; } @@ -4651,6 +4657,12 @@ bool GetLowLevelILForArmInstruction(Architecture* arch, uint64_t addr, LowLevelI il.ZeroExtend(get_register_size(op1.reg), il.Register(get_register_size(op2.reg), op2.reg))))); break; + case DT_F32: + case DT_F64: + ConditionExecute(il, instr.cond, il.SetRegister(get_register_size(op1.reg), op1.reg, + il.FloatConvert(get_register_size(op1.reg), + il.Register(get_register_size(op2.reg), op2.reg)))); + break; default: break; } @@ -4659,6 +4671,44 @@ bool GetLowLevelILForArmInstruction(Architecture* arch, uint64_t addr, LowLevelI break; } break; + case ARMV7_VABS: + if (op1.cls != REG || op2.cls != REG || op3.cls != NONE) + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + + { + size_t elementSize = GetDataTypeSize(instr.dataType); + size_t destSize = get_register_size(op1.reg); + size_t sourceSize = get_register_size(op2.reg); + bool isFloat = (instr.dataType == DT_F32) || (instr.dataType == DT_F64); + if (elementSize == 0 || destSize == 0 || sourceSize == 0 || destSize != sourceSize) + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + + if (isFloat && elementSize == destSize) + { + ConditionExecute(il, instr.cond, + il.SetRegister(destSize, op1.reg, + il.FloatAbs(destSize, il.Register(sourceSize, op2.reg)))); + } + else + { + ConditionExecute(il, instr.cond, + il.Intrinsic( + { RegisterOrFlag::Register(op1.reg) }, + ARMV7_INTRIN_VABS, + { + il.Const(1, elementSize * 8), + il.Const(1, isFloat ? 1 : 0), + il.Register(sourceSize, op2.reg), + })); + } + } + break; case ARMV7_VADD: if (op1.cls != REG || op2.cls != REG || op3.cls != REG) { diff --git a/arch/armv7/il.h b/arch/armv7/il.h index 9b46e96d7..7aaee4d49 100644 --- a/arch/armv7/il.h +++ b/arch/armv7/il.h @@ -201,6 +201,8 @@ enum Armv7Intrinsic : uint32_t ARMV7_INTRIN_VSUB, ARMV7_INTRIN_VRHADD, ARMV7_INTRIN_VRECPE, + ARMV7_INTRIN_VABS, + ARMV7_INTRIN_VCVT_FIXED, }; enum ArmFakeRegister: uint32_t diff --git a/arch/armv7/test_lift.py b/arch/armv7/test_lift.py index 79fd5aba7..1c09f359b 100755 --- a/arch/armv7/test_lift.py +++ b/arch/armv7/test_lift.py @@ -19,6 +19,14 @@ def vector_unary_intrinsic_expected(dst, intrinsic, size, modifier, src): src_size = 'o' if src.startswith('q') else 'q' return f'LLIL_INTRINSIC([{dst}],__{intrinsic},[LLIL_CONST.b(0x{size:X}),LLIL_CONST.b(0x{modifier:X}),LLIL_REG.{src_size}({src})])' +def vector_fixed_convert_intrinsic_expected(dst, size, fractional_bits, to_fixed, unsigned, src): + src_size = 'o' if src.startswith('q') else 'q' + return ( + f'LLIL_INTRINSIC([{dst}],__vcvt_fixed,[LLIL_CONST.b(0x{size:X}),' + f'LLIL_CONST.b(0x{fractional_bits:X}),LLIL_CONST.b(0x{to_fixed:X}),' + f'LLIL_CONST.b(0x{unsigned:X}),LLIL_REG.{src_size}({src})])' + ) + def saturating_scalar_expected(dst, src1, src2, intrinsic): return scalar_q_intrinsic_expected(dst, intrinsic, src1, src2) @@ -652,6 +660,26 @@ def vmlal_expected(size, unsigned): ('A', b'\x44\xa4\xfb\xf3', vector_unary_intrinsic_expected('q13', 'vrecpe', 32, 0, 'q2')), # vrecpe.u32 q13, q2 ('T', b'\xfb\xff\x44\xa4', vector_unary_intrinsic_expected('q13', 'vrecpe', 32, 0, 'q2')), + # vabs.f32 s0, s1 + ('A', b'\xe0\x0a\xb0\xee', 'LLIL_SET_REG.d(s0,LLIL_FABS.d(LLIL_REG.d(s1)))'), + # vabs.f64 d1, d2 + ('A', b'\xc2\x1b\xb0\xee', 'LLIL_SET_REG.q(d1,LLIL_FABS.q(LLIL_REG.q(d2)))'), + # vabs.f32 d0, d1 + ('A', b'\x01\x07\xb9\xf3', vector_unary_intrinsic_expected('d0', 'vabs', 32, 1, 'd1')), + # vabs.f32 q1, q2 + ('A', b'\x44\x27\xb9\xf3', vector_unary_intrinsic_expected('q1', 'vabs', 32, 1, 'q2')), + # vabs.s16 d0, d1 + ('A', b'\x01\x03\xb5\xf3', vector_unary_intrinsic_expected('d0', 'vabs', 16, 0, 'd1')), + # vabs.f32 s0, s1 + ('T', b'\xb0\xee\xe0\x0a', 'LLIL_SET_REG.d(s0,LLIL_FABS.d(LLIL_REG.d(s1)))'), + # vabs.f64 d1, d2 + ('T', b'\xb0\xee\xc2\x1b', 'LLIL_SET_REG.q(d1,LLIL_FABS.q(LLIL_REG.q(d2)))'), + # vabs.f32 d0, d1 + ('T', b'\xb9\xff\x01\x07', vector_unary_intrinsic_expected('d0', 'vabs', 32, 1, 'd1')), + # vabs.f32 q1, q2 + ('T', b'\xb9\xff\x44\x27', vector_unary_intrinsic_expected('q1', 'vabs', 32, 1, 'q2')), + # vabs.s16 d0, d1 + ('T', b'\xb5\xff\x01\x03', vector_unary_intrinsic_expected('d0', 'vabs', 16, 0, 'd1')), # vceq.s16 d16, d0, d13 ('T', b'\x50\xff\x1d\x08', vector_intrinsic_expected('d16', 'vceq', 16, 0, 'd0', 'd13')), # vcgt.s32 d0, d19, #0 @@ -692,10 +720,38 @@ def vmlal_expected(size, unsigned): ('T', b'\xba\xee\xef\x7a', 'LLIL_SET_REG.d(s14,LLIL_FDIV.d(LLIL_INT_TO_FLOAT.d(LLIL_SX.d(LLIL_REG.d(s14))),LLIL_FLOAT_CONST.d(2.0)))'), # vcvt.f64.u32 d7, d7, #1 ('T', b'\xbb\xee\xef\x7b', 'LLIL_SET_REG.q(d7,LLIL_FDIV.q(LLIL_INT_TO_FLOAT.q(LLIL_ZX.q(LLIL_LOW_PART.d(LLIL_REG.q(d7)))),LLIL_FLOAT_CONST.q(2.0)))'), + # vcvt.s16.f32 s0, s0, #8 + ('A', b'\x44\x0a\xbe\xee', 'LLIL_SET_REG.d(s0,LLIL_SX.d(LLIL_FLOAT_TO_INT.w(LLIL_FTRUNC.d(LLIL_FMUL.d(LLIL_REG.d(s0),LLIL_FLOAT_CONST.d(256.0))))))'), + # vcvt.f32.s16 s1, s1, #8 + ('A', b'\x44\x0a\xfa\xee', 'LLIL_SET_REG.d(s1,LLIL_FDIV.d(LLIL_INT_TO_FLOAT.d(LLIL_SX.d(LLIL_LOW_PART.w(LLIL_REG.d(s1)))),LLIL_FLOAT_CONST.d(256.0)))'), + # vcvt.u16.f64 d2, d2, #12 + ('T', b'\xbf\xee\x42\x2b', 'LLIL_SET_REG.q(d2,LLIL_ZX.q(LLIL_FLOAT_TO_INT.w(LLIL_FTRUNC.q(LLIL_FMUL.q(LLIL_REG.q(d2),LLIL_FLOAT_CONST.q(4096.0))))))'), + # vcvt.f64.u16 d3, d3, #12 + ('T', b'\xbb\xee\x42\x3b', 'LLIL_SET_REG.q(d3,LLIL_FDIV.q(LLIL_INT_TO_FLOAT.q(LLIL_ZX.q(LLIL_LOW_PART.w(LLIL_REG.q(d3)))),LLIL_FLOAT_CONST.q(4096.0)))'), + # vcvt.s32.f32 d0, d1, #16 + ('A', b'\x11\x0f\xb0\xf2', vector_fixed_convert_intrinsic_expected('d0', 32, 16, 1, 0, 'd1')), + # vcvt.u32.f32 q1, q2, #8 + ('A', b'\x54\x2f\xb8\xf3', vector_fixed_convert_intrinsic_expected('q1', 32, 8, 1, 1, 'q2')), + # vcvt.f32.s32 d2, d3, #16 + ('A', b'\x13\x2e\xb0\xf2', vector_fixed_convert_intrinsic_expected('d2', 32, 16, 0, 0, 'd3')), + # vcvt.f32.u32 q3, q4, #8 + ('A', b'\x58\x6e\xb8\xf3', vector_fixed_convert_intrinsic_expected('q3', 32, 8, 0, 1, 'q4')), + # vcvt.s32.f32 d0, d1, #16 + ('T', b'\xb0\xef\x11\x0f', vector_fixed_convert_intrinsic_expected('d0', 32, 16, 1, 0, 'd1')), + # vcvt.u32.f32 q1, q2, #8 + ('T', b'\xb8\xff\x54\x2f', vector_fixed_convert_intrinsic_expected('q1', 32, 8, 1, 1, 'q2')), + # vcvt.f32.s32 d2, d3, #16 + ('T', b'\xb0\xef\x13\x2e', vector_fixed_convert_intrinsic_expected('d2', 32, 16, 0, 0, 'd3')), + # vcvt.f32.u32 q3, q4, #8 + ('T', b'\xb8\xff\x58\x6e', vector_fixed_convert_intrinsic_expected('q3', 32, 8, 0, 1, 'q4')), + # vcvt.f64.f32 d3, s4 + ('T', b'\xb7\xee\xc2\x3a', 'LLIL_SET_REG.q(d3,LLIL_FLOAT_CONV.q(LLIL_REG.d(s4)))'), + # vcvt.f32.f64 s5, d4 + ('T', b'\xf7\xee\xc4\x2b', 'LLIL_SET_REG.d(s5,LLIL_FLOAT_CONV.d(LLIL_REG.q(d4)))'), # vcvt.s32.f64 d2, d2, #0x20 - ('A', b'\xc0\x2b\xbe\xee', 'LLIL_SET_REG.q(d2,LLIL_SX.q(LLIL_FLOAT_TO_INT.d(LLIL_ROUND_TO_INT.q(LLIL_FMUL.q(LLIL_REG.q(d2),LLIL_FLOAT_CONST.q(4294967296.0))))))'), + ('A', b'\xc0\x2b\xbe\xee', 'LLIL_SET_REG.q(d2,LLIL_SX.q(LLIL_FLOAT_TO_INT.d(LLIL_FTRUNC.q(LLIL_FMUL.q(LLIL_REG.q(d2),LLIL_FLOAT_CONST.q(4294967296.0))))))'), # vcvt.u32.f64 d20, d20, #0x20 - ('A', b'\xc0\x4b\xff\xee', 'LLIL_SET_REG.q(d20,LLIL_ZX.q(LLIL_FLOAT_TO_INT.d(LLIL_ROUND_TO_INT.q(LLIL_FMUL.q(LLIL_REG.q(d20),LLIL_FLOAT_CONST.q(4294967296.0))))))'), + ('A', b'\xc0\x4b\xff\xee', 'LLIL_SET_REG.q(d20,LLIL_ZX.q(LLIL_FLOAT_TO_INT.d(LLIL_FTRUNC.q(LLIL_FMUL.q(LLIL_REG.q(d20),LLIL_FLOAT_CONST.q(4294967296.0))))))'), # vmaxnm.f64 d11, d11, d7 ('T', b'\x8b\xfe\x07\xbb', 'LLIL_INTRINSIC([d11],__vmaxnm,[LLIL_REG.q(d11),LLIL_REG.q(d7)])'), # vminnm.f64 d8, d8, d9 diff --git a/arch/armv7/thumb2_disasm/arch_thumb2.cpp b/arch/armv7/thumb2_disasm/arch_thumb2.cpp index 09e505f84..94a6c80aa 100644 --- a/arch/armv7/thumb2_disasm/arch_thumb2.cpp +++ b/arch/armv7/thumb2_disasm/arch_thumb2.cpp @@ -1811,6 +1811,10 @@ class Thumb2Architecture: public ArmCommonArchitecture return "__vrhadd"; case ARMV7_INTRIN_VRECPE: return "__vrecpe"; + case ARMV7_INTRIN_VABS: + return "__vabs"; + case ARMV7_INTRIN_VCVT_FIXED: + return "__vcvt_fixed"; case ARMV7_INTRIN_VQSHL: return "__vqshl"; case ARMV7_INTRIN_VQRSHL: @@ -2121,6 +2125,8 @@ class Thumb2Architecture: public ArmCommonArchitecture ARMV7_INTRIN_VHADD, ARMV7_INTRIN_VRHADD, ARMV7_INTRIN_VRECPE, + ARMV7_INTRIN_VABS, + ARMV7_INTRIN_VCVT_FIXED, ARMV7_INTRIN_VQSHL, ARMV7_INTRIN_VQRSHL, ARMV7_INTRIN_VQSHRN, @@ -2427,11 +2433,20 @@ class Thumb2Architecture: public ArmCommonArchitecture NameAndType("source2", Type::IntegerType(8, false)), }; case ARMV7_INTRIN_VRECPE: + case ARMV7_INTRIN_VABS: return { NameAndType("size", Type::IntegerType(1, false)), NameAndType("is_float", Type::BoolType()), NameAndType("source", Type::IntegerType(8, false)), }; + case ARMV7_INTRIN_VCVT_FIXED: + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("fractional_bits", Type::IntegerType(1, false)), + NameAndType("to_fixed", Type::BoolType()), + NameAndType("is_unsigned", Type::BoolType()), + NameAndType("source", Type::IntegerType(8, false)), + }; case ARMV7_INTRIN_VREV16: case ARMV7_INTRIN_VREV32: case ARMV7_INTRIN_VREV64: @@ -2841,6 +2856,8 @@ class Thumb2Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VHADD: case ARMV7_INTRIN_VRHADD: case ARMV7_INTRIN_VRECPE: + case ARMV7_INTRIN_VABS: + case ARMV7_INTRIN_VCVT_FIXED: case ARMV7_INTRIN_VQSHL: case ARMV7_INTRIN_VQRSHL: case ARMV7_INTRIN_VQSHRN: diff --git a/arch/armv7/thumb2_disasm/il_thumb2.cpp b/arch/armv7/thumb2_disasm/il_thumb2.cpp index 54cb7e719..e283b1532 100644 --- a/arch/armv7/thumb2_disasm/il_thumb2.cpp +++ b/arch/armv7/thumb2_disasm/il_thumb2.cpp @@ -3939,16 +3939,54 @@ bool GetLowLevelILForNEONInstruction(Architecture* arch, LowLevelILFunction& il, switch (instr->mnem) { case armv7::ARMV7_VABS: - if (instr->format->operationFlags & (INSTR_FORMAT_FLAG_F32 | INSTR_FORMAT_FLAG_F64)) + { + uint32_t dest = GetRegisterOperand(instr, 0); + uint32_t source = GetRegisterOperand(instr, 1); + size_t destSize = GetRegisterSize(instr, 0); + size_t sourceSize = GetRegisterSize(instr, 1); + size_t elementSize = 0; + bool isFloat = false; + + if (instr->format->operationFlags & INSTR_FORMAT_FLAG_F32) { - il.AddInstruction( - WriteILOperand(il, instr, 0, il.FloatAbs(GetRegisterSize(instr, 0), ReadILOperand(il, instr, 1)))); + elementSize = 4; + isFloat = true; } - else + else if (instr->format->operationFlags & INSTR_FORMAT_FLAG_F64) + { + elementSize = 8; + isFloat = true; + } + else if (IS_FIELD_PRESENT(instr, FIELD_esize)) + { + elementSize = instr->fields[FIELD_esize] / 8; + isFloat = IS_FIELD_PRESENT(instr, FIELD_floating_point) + && instr->fields[FIELD_floating_point] != 0; + } + + if (dest == armv7::REG_INVALID || source == armv7::REG_INVALID || elementSize == 0 + || destSize == 0 || sourceSize == 0 || destSize != sourceSize) { il.AddInstruction(il.Unimplemented()); } + else if (isFloat && elementSize == destSize) + { + il.AddInstruction(WriteILOperand( + il, instr, 0, il.FloatAbs(destSize, ReadILOperand(il, instr, 1, sourceSize)))); + } + else + { + il.AddInstruction(il.Intrinsic( + { RegisterOrFlag::Register(dest) }, + ARMV7_INTRIN_VABS, + { + il.Const(1, elementSize * 8), + il.Const(1, isFloat ? 1 : 0), + ReadILOperand(il, instr, 1, sourceSize), + })); + } break; + } case armv7::ARMV7_VADD: if (instr->format->operationFlags & (INSTR_FORMAT_FLAG_F32 | INSTR_FORMAT_FLAG_F64)) { @@ -4386,9 +4424,10 @@ bool GetLowLevelILForNEONInstruction(Architecture* arch, LowLevelILFunction& il, ExprId scaled = il.FloatMult(floatSize, ReadILOperand(il, instr, 1, floatSize), FixedPointScale(il, floatSize, fractionalBits)); - ExprId converted = il.FloatToInt(floatSize, il.RoundToInt(floatSize, scaled)); - if (isUnsigned) - converted = il.ZeroExtend(floatSize, converted); + ExprId converted = il.FloatToInt(fixedSize, il.FloatTrunc(floatSize, scaled)); + converted = isUnsigned + ? il.ZeroExtend(floatSize, converted) + : il.SignExtend(floatSize, converted); il.AddInstruction(WriteILOperand(il, instr, 0, converted, floatSize)); } else @@ -4405,7 +4444,31 @@ bool GetLowLevelILForNEONInstruction(Architecture* arch, LowLevelILFunction& il, // VCVT (between floating-point and fixed-point, Advanced SIMD) /* VCVT.
,,# */ /* VCVT.
,,# */ - // TODO: vector and fixed-point unsupported. + uint32_t dest = GetRegisterOperand(instr, 0); + uint32_t source = GetRegisterOperand(instr, 1); + size_t destSize = GetRegisterSize(instr, 0); + size_t sourceSize = GetRegisterSize(instr, 1); + size_t elementSize = IS_FIELD_PRESENT(instr, FIELD_esize) + ? instr->fields[FIELD_esize] / 8 + : 0; + if (dest == armv7::REG_INVALID || source == armv7::REG_INVALID || elementSize != 4 + || destSize != sourceSize || (destSize != 8 && destSize != 16)) + { + il.AddInstruction(il.Unimplemented()); + } + else + { + il.AddInstruction(il.Intrinsic( + { RegisterOrFlag::Register(dest) }, + ARMV7_INTRIN_VCVT_FIXED, + { + il.Const(1, elementSize * 8), + il.Const(1, instr->fields[FIELD_fbits]), + il.Const(1, instr->fields[FIELD_to_fixed] ? 1 : 0), + il.Const(1, instr->fields[FIELD_unsigned] ? 1 : 0), + ReadILOperand(il, instr, 1, sourceSize), + })); + } } } else if (IS_FIELD_PRESENT(instr, FIELD_half_to_single)) @@ -4422,7 +4485,7 @@ bool GetLowLevelILForNEONInstruction(Architecture* arch, LowLevelILFunction& il, /* VCVT.F64.F32
, */ /* VCVT.F32.F64 , */ il.AddInstruction(WriteILOperand( - il, instr, 0, il.FloatConvert(GetRegisterSize(instr, 1), ReadILOperand(il, instr, 1)))); + il, instr, 0, il.FloatConvert(GetRegisterSize(instr, 0), ReadILOperand(il, instr, 1)))); break; } else if (IS_FIELD_PRESENT(instr, FIELD_to_integer))