From ddcaace8164bd5655311393a661d371d3e796117 Mon Sep 17 00:00:00 2001 From: Brandon Miller Date: Fri, 18 Sep 2026 12:39:36 -0400 Subject: [PATCH] [ARMv7] Expand ARM and Thumb SIMD and VFP lifting Add lifting for vector comparisons, bitwise operations, widening and narrowing moves, multiply-long, negation, pairwise addition, shifts, swaps, transposes, tests, and interleaving operations. Use dedicated intrinsics for lane-wise operations and explicit IL for bitwise operations, register swaps, and scalar floating-point negation. Correct SIMD decoding, signedness, immediate types, D/Q operand widths, and reserved encodings. Support ARMv8 VMULL.P64 polynomial products. Add FLDMDBX/FLDMIAX and FSTMDBX/FSTMIAX lifting, accounting for the unused trailing word in addressing and writeback. Correct Thumb decoding of these forms, including SP-based transfers. --- arch/armv7/arch_armv7.cpp | 173 ++++++- arch/armv7/armv7_disasm/armv7.c | 44 +- arch/armv7/il.cpp | 524 +++++++++++++++++---- arch/armv7/il.h | 48 ++ arch/armv7/test_lift.py | 277 ++++++++++- arch/armv7/thumb2_disasm/arch_thumb2.cpp | 173 ++++++- arch/armv7/thumb2_disasm/il_thumb2.cpp | 569 ++++++++++++++++++----- arch/armv7/thumb2_disasm/spec.cpp | 390 ++++++++++++++-- arch/armv7/thumb2_disasm/spec.txt | 63 ++- 9 files changed, 1992 insertions(+), 269 deletions(-) diff --git a/arch/armv7/arch_armv7.cpp b/arch/armv7/arch_armv7.cpp index e24ee0ac9f..ac6391ced0 100644 --- a/arch/armv7/arch_armv7.cpp +++ b/arch/armv7/arch_armv7.cpp @@ -1521,6 +1521,8 @@ class Armv7Architecture: public ArmCommonArchitecture return "__vmax"; case ARMV7_INTRIN_VMIN: return "__vmin"; + case ARMV7_INTRIN_VPADD: + return "__vpadd"; case ARMV7_INTRIN_VPMAX: return "__vpmax"; case ARMV7_INTRIN_VPMIN: @@ -1535,6 +1537,16 @@ class Armv7Architecture: public ArmCommonArchitecture return "__vext"; case ARMV7_INTRIN_VCGT: return "__vcgt"; + case ARMV7_INTRIN_VCGT_Q: + return "__vcgt_q"; + case ARMV7_INTRIN_VCLT: + return "__vclt"; + case ARMV7_INTRIN_VCLT_Q: + return "__vclt_q"; + case ARMV7_INTRIN_VCGE: + return "__vcge"; + case ARMV7_INTRIN_VCGE_Q: + return "__vcge_q"; case ARMV7_INTRIN_VCEQ: return "__vceq"; case ARMV7_INTRIN_VTBL: @@ -1581,12 +1593,42 @@ class Armv7Architecture: public ArmCommonArchitecture return "__vst2"; case ARMV7_INTRIN_VST4: return "__vst4"; + case ARMV7_INTRIN_VSHL_Q: + return "__vshl_q"; + case ARMV7_INTRIN_VSHL_IMM: + return "__vshl_imm"; + case ARMV7_INTRIN_VSHL_IMM_Q: + return "__vshl_imm_q"; + case ARMV7_INTRIN_VSHR_Q: + return "__vshr_q"; + case ARMV7_INTRIN_VSHRN: + return "__vshrn"; + case ARMV7_INTRIN_VTRN: + return "__vtrn"; + case ARMV7_INTRIN_VTRN_Q: + return "__vtrn_q"; + case ARMV7_INTRIN_VUZP: + return "__vuzp"; + case ARMV7_INTRIN_VUZP_Q: + return "__vuzp_q"; + case ARMV7_INTRIN_VZIP: + return "__vzip"; + case ARMV7_INTRIN_VZIP_Q: + return "__vzip_q"; + case ARMV7_INTRIN_VTST: + return "__vtst"; + case ARMV7_INTRIN_VTST_Q: + return "__vtst_q"; case ARMV7_INTRIN_VSHL: return "__vshl"; case ARMV7_INTRIN_VSHR: return "__vshr"; case ARMV7_INTRIN_VSHLL: return "__vshll"; + case ARMV7_INTRIN_VMOVL: + return "__vmovl"; + case ARMV7_INTRIN_VMOVN: + return "__vmovn"; case ARMV7_INTRIN_VBIF: return "__vbif"; case ARMV7_INTRIN_VBIT: @@ -1603,6 +1645,10 @@ class Armv7Architecture: public ArmCommonArchitecture return "__vrecpe"; case ARMV7_INTRIN_VABS: return "__vabs"; + case ARMV7_INTRIN_VNEG: + return "__vneg"; + case ARMV7_INTRIN_VNEG_Q: + return "__vneg_q"; case ARMV7_INTRIN_VCVT_FIXED: return "__vcvt_fixed"; case ARMV7_INTRIN_VABS_Q: @@ -1635,6 +1681,8 @@ class Armv7Architecture: public ArmCommonArchitecture return "__vmlsl"; case ARMV7_INTRIN_VMUL: return "__vmul"; + case ARMV7_INTRIN_VMULL: + return "__vmull"; case ARMV7_INTRIN_VQDMULL: return "__vqdmull"; case ARMV7_INTRIN_SSAT: @@ -1836,6 +1884,7 @@ class Armv7Architecture: public ArmCommonArchitecture ARMV7_INTRIN_VMINNM, ARMV7_INTRIN_VMAX, ARMV7_INTRIN_VMIN, + ARMV7_INTRIN_VPADD, ARMV7_INTRIN_VPMAX, ARMV7_INTRIN_VPMIN, ARMV7_INTRIN_VREV16, @@ -1843,6 +1892,11 @@ class Armv7Architecture: public ArmCommonArchitecture ARMV7_INTRIN_VREV64, ARMV7_INTRIN_VEXT, ARMV7_INTRIN_VCGT, + ARMV7_INTRIN_VCGT_Q, + ARMV7_INTRIN_VCLT, + ARMV7_INTRIN_VCLT_Q, + ARMV7_INTRIN_VCGE, + ARMV7_INTRIN_VCGE_Q, ARMV7_INTRIN_VCEQ, ARMV7_INTRIN_VTBL, ARMV7_INTRIN_VTBX, @@ -1865,8 +1919,23 @@ class Armv7Architecture: public ArmCommonArchitecture ARMV7_INTRIN_VST2, ARMV7_INTRIN_VST4, ARMV7_INTRIN_VSHL, + ARMV7_INTRIN_VSHL_Q, + ARMV7_INTRIN_VSHL_IMM, + ARMV7_INTRIN_VSHL_IMM_Q, + ARMV7_INTRIN_VSHR_Q, + ARMV7_INTRIN_VSHRN, + ARMV7_INTRIN_VTRN, + ARMV7_INTRIN_VTRN_Q, + ARMV7_INTRIN_VUZP, + ARMV7_INTRIN_VUZP_Q, + ARMV7_INTRIN_VZIP, + ARMV7_INTRIN_VZIP_Q, + ARMV7_INTRIN_VTST, + ARMV7_INTRIN_VTST_Q, ARMV7_INTRIN_VSHR, ARMV7_INTRIN_VSHLL, + ARMV7_INTRIN_VMOVL, + ARMV7_INTRIN_VMOVN, ARMV7_INTRIN_VBIF, ARMV7_INTRIN_VBIT, ARMV7_INTRIN_VBSL, @@ -1875,6 +1944,8 @@ class Armv7Architecture: public ArmCommonArchitecture ARMV7_INTRIN_VRHADD, ARMV7_INTRIN_VRECPE, ARMV7_INTRIN_VABS, + ARMV7_INTRIN_VNEG, + ARMV7_INTRIN_VNEG_Q, ARMV7_INTRIN_VCVT_FIXED, ARMV7_INTRIN_VABS_Q, ARMV7_INTRIN_VCVT_FIXED_Q, @@ -1891,6 +1962,7 @@ class Armv7Architecture: public ArmCommonArchitecture ARMV7_INTRIN_VMLAL, ARMV7_INTRIN_VMLSL, ARMV7_INTRIN_VMUL, + ARMV7_INTRIN_VMULL, ARMV7_INTRIN_VQDMULL, ARMV7_INTRIN_SSAT, ARMV7_INTRIN_SSAT16, @@ -2191,7 +2263,6 @@ class Armv7Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VMIN: case ARMV7_INTRIN_VPMAX: case ARMV7_INTRIN_VPMIN: - case ARMV7_INTRIN_VCGT: case ARMV7_INTRIN_VHADD: case ARMV7_INTRIN_VRHADD: return { @@ -2200,6 +2271,24 @@ class Armv7Architecture: public ArmCommonArchitecture NameAndType("source1", Type::IntegerType(8, false)), NameAndType("source2", Type::IntegerType(8, false)), }; + case ARMV7_INTRIN_VCGT: + case ARMV7_INTRIN_VCGT_Q: + case ARMV7_INTRIN_VCGE: + case ARMV7_INTRIN_VCGE_Q: + case ARMV7_INTRIN_VCLT: + case ARMV7_INTRIN_VCLT_Q: + { + size_t vectorSize = intrinsic == ARMV7_INTRIN_VCGE_Q || intrinsic == ARMV7_INTRIN_VCGT_Q + || intrinsic == ARMV7_INTRIN_VCLT_Q ? 16 : 8; + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("is_unsigned", Type::BoolType()), + NameAndType("is_float", Type::BoolType()), + NameAndType("source1", Type::IntegerType(vectorSize, false)), + NameAndType("source2", Type::IntegerType(vectorSize, false)), + }; + } + case ARMV7_INTRIN_VPADD: case ARMV7_INTRIN_VCEQ: return { NameAndType("size", Type::IntegerType(1, false)), @@ -2208,13 +2297,15 @@ class Armv7Architecture: public ArmCommonArchitecture NameAndType("source2", Type::IntegerType(8, false)), }; case ARMV7_INTRIN_VRECPE: + case ARMV7_INTRIN_VNEG: case ARMV7_INTRIN_VABS: case ARMV7_INTRIN_VABS_Q: + case ARMV7_INTRIN_VNEG_Q: return { NameAndType("size", Type::IntegerType(1, false)), NameAndType("is_float", Type::BoolType()), NameAndType("source", Type::IntegerType( - intrinsic == ARMV7_INTRIN_VABS_Q ? 16 : 8, false)), + (intrinsic == ARMV7_INTRIN_VABS_Q || intrinsic == ARMV7_INTRIN_VNEG_Q) ? 16 : 8, false)), }; case ARMV7_INTRIN_VCVT_FIXED: case ARMV7_INTRIN_VCVT_FIXED_Q: @@ -2319,6 +2410,39 @@ class Armv7Architecture: public ArmCommonArchitecture NameAndType("source1", Type::IntegerType(16, false)), NameAndType("source2", Type::IntegerType(16, false)), }; + case ARMV7_INTRIN_VSHL_Q: + case ARMV7_INTRIN_VSHR_Q: + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("is_unsigned", Type::BoolType()), + NameAndType("source", Type::IntegerType(16, false)), + NameAndType("shift", Type::IntegerType(intrinsic == ARMV7_INTRIN_VSHL_Q ? 16 : 8, false)), + }; + case ARMV7_INTRIN_VTST: + case ARMV7_INTRIN_VTST_Q: + case ARMV7_INTRIN_VZIP: + case ARMV7_INTRIN_VZIP_Q: + case ARMV7_INTRIN_VUZP: + case ARMV7_INTRIN_VUZP_Q: + case ARMV7_INTRIN_VTRN: + case ARMV7_INTRIN_VTRN_Q: + { + size_t size = (intrinsic == ARMV7_INTRIN_VTRN_Q || intrinsic == ARMV7_INTRIN_VTST_Q + || intrinsic == ARMV7_INTRIN_VUZP_Q || intrinsic == ARMV7_INTRIN_VZIP_Q) ? 16 : 8; + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("source1", Type::IntegerType(size, false)), + NameAndType("source2", Type::IntegerType(size, false)), + }; + } + case ARMV7_INTRIN_VSHL_IMM: + case ARMV7_INTRIN_VSHL_IMM_Q: + case ARMV7_INTRIN_VSHRN: + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("source", Type::IntegerType(intrinsic == ARMV7_INTRIN_VSHL_IMM ? 8 : 16, false)), + NameAndType("shift", Type::IntegerType(1, false)), + }; case ARMV7_INTRIN_VRSHR: case ARMV7_INTRIN_VRSHL: case ARMV7_INTRIN_VSHL: @@ -2330,6 +2454,17 @@ class Armv7Architecture: public ArmCommonArchitecture NameAndType("source", Type::IntegerType(8, false)), NameAndType("shift", Type::IntegerType(8, false)), }; + case ARMV7_INTRIN_VMOVL: + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("is_unsigned", Type::BoolType()), + NameAndType("source", Type::IntegerType(8, false)), + }; + case ARMV7_INTRIN_VMOVN: + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("source", Type::IntegerType(16, false)), + }; case ARMV7_INTRIN_VBIF: case ARMV7_INTRIN_VBIT: case ARMV7_INTRIN_VBSL: @@ -2424,6 +2559,15 @@ class Armv7Architecture: public ArmCommonArchitecture NameAndType("scalar", Type::IntegerType(8, false)), NameAndType("index", Type::IntegerType(1, false)), }; + case ARMV7_INTRIN_VMULL: + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("is_unsigned", Type::BoolType()), + NameAndType("is_polynomial", Type::BoolType()), + NameAndType("source1", Type::IntegerType(8, false)), + NameAndType("source2", Type::IntegerType(8, false)), + NameAndType("index", Type::IntegerType(1, false)), + }; case ARMV7_INTRIN_VMUL: case ARMV7_INTRIN_VQDMULL: return { @@ -2541,6 +2685,13 @@ class Armv7Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_SMLALD: case ARMV7_INTRIN_SMLALDX: return { Type::IntegerType(4, false), Type::IntegerType(4, false) }; + case ARMV7_INTRIN_VZIP_Q: + case ARMV7_INTRIN_VUZP_Q: + case ARMV7_INTRIN_VTRN_Q: + return { Type::IntegerType(16, false), Type::IntegerType(16, false) }; + case ARMV7_INTRIN_VZIP: + case ARMV7_INTRIN_VUZP: + case ARMV7_INTRIN_VTRN: case ARMV7_INTRIN_VLD2: return { Type::IntegerType(8, false), Type::IntegerType(8, false) }; case ARMV7_INTRIN_VLD4: @@ -2557,10 +2708,14 @@ class Armv7Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VSRI: case ARMV7_INTRIN_VSLI: case ARMV7_INTRIN_VRADDHN: + case ARMV7_INTRIN_VTST: + case ARMV7_INTRIN_VSHL_IMM: + case ARMV7_INTRIN_VSHRN: case ARMV7_INTRIN_VSHL: case ARMV7_INTRIN_VSHR: case ARMV7_INTRIN_VMAX: case ARMV7_INTRIN_VMIN: + case ARMV7_INTRIN_VPADD: case ARMV7_INTRIN_VPMAX: case ARMV7_INTRIN_VPMIN: case ARMV7_INTRIN_VREV16: @@ -2568,6 +2723,8 @@ class Armv7Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VREV64: case ARMV7_INTRIN_VEXT: case ARMV7_INTRIN_VCGT: + case ARMV7_INTRIN_VCGE: + case ARMV7_INTRIN_VCLT: case ARMV7_INTRIN_VCEQ: case ARMV7_INTRIN_VADD: case ARMV7_INTRIN_VSUB: @@ -2575,6 +2732,7 @@ class Armv7Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VHADD: case ARMV7_INTRIN_VRHADD: case ARMV7_INTRIN_VRECPE: + case ARMV7_INTRIN_VNEG: case ARMV7_INTRIN_VABS: case ARMV7_INTRIN_VCVT_FIXED: case ARMV7_INTRIN_VQSHL: @@ -2585,6 +2743,7 @@ class Armv7Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VQRSHRUN: case ARMV7_INTRIN_VQMOVN: case ARMV7_INTRIN_VQMOVUN: + case ARMV7_INTRIN_VMOVN: case ARMV7_INTRIN_VMLA: case ARMV7_INTRIN_VMLS: case ARMV7_INTRIN_VMUL: @@ -2592,8 +2751,16 @@ class Armv7Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VBIT: case ARMV7_INTRIN_VBSL: return { Type::IntegerType(8, false) }; + case ARMV7_INTRIN_VTST_Q: + case ARMV7_INTRIN_VSHL_Q: + case ARMV7_INTRIN_VSHL_IMM_Q: + case ARMV7_INTRIN_VSHR_Q: case ARMV7_INTRIN_VABS_Q: + case ARMV7_INTRIN_VNEG_Q: case ARMV7_INTRIN_VCVT_FIXED_Q: + case ARMV7_INTRIN_VCGE_Q: + case ARMV7_INTRIN_VCGT_Q: + case ARMV7_INTRIN_VCLT_Q: return { Type::IntegerType(16, false) }; case ARMV7_INTRIN_VABAL: case ARMV7_INTRIN_VABDL: @@ -2603,6 +2770,8 @@ class Armv7Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VMLAL: case ARMV7_INTRIN_VMLSL: case ARMV7_INTRIN_VQDMULL: + case ARMV7_INTRIN_VMOVL: + case ARMV7_INTRIN_VMULL: return { Type::IntegerType(16, false) }; default: return vector>>(); diff --git a/arch/armv7/armv7_disasm/armv7.c b/arch/armv7/armv7_disasm/armv7.c index 6784e94b6f..622fabb2e8 100644 --- a/arch/armv7/armv7_disasm/armv7.c +++ b/arch/armv7/armv7_disasm/armv7.c @@ -4670,8 +4670,9 @@ uint32_t armv7_three_register_same(uint32_t instructionValue, Instruction* restr instruction->operation = operation[decode.com.b][decode.com.u]; if (instruction->operation == ARMV7_VTST) { + if (decode.vh.size == 3 || (decode.vh.q && ((decode.vh.vd | decode.vh.vn | decode.vh.vm) & 1))) + return 1; instruction->dataType = (DataType)(DT_8 + decode.vh.size); - // checkV0 = decode.vh.q; } else { @@ -4792,6 +4793,14 @@ uint32_t armv7_three_register_same(uint32_t instructionValue, Instruction* restr } } + if (instruction->operation == ARMV7_VSHL && decode.vh.q + && ((decode.vh.vd | decode.vh.vn | decode.vh.vm) & 1)) + return 1; + + if (instruction->operation == ARMV7_VPADD + && (decode.vh.q || (instruction->dataType == DT_F32 ? (decode.vh.size & 1) : decode.vh.size == 3))) + return 1; + instruction->operands[0].cls = REG; instruction->operands[0].reg = (Register)(regMap[decode.vh.q] + ((decode.vh.d << 4 | decode.vh.vd) >> decode.vh.q)); instruction->operands[src1].cls = REG; @@ -4997,6 +5006,8 @@ uint32_t armv7_three_register_different(uint32_t instructionValue, Instruction* break; case 12: //op = 0 { + if (decode.vcom.vd & 1) + return 1; static DataType dataType[2][4] = { {DT_S8, DT_S16, DT_S32, DT_NONE}, {DT_U8, DT_U16, DT_U32, DT_NONE} @@ -5027,9 +5038,11 @@ uint32_t armv7_three_register_different(uint32_t instructionValue, Instruction* break; case 14: //op = 1 { - static DataType dataType[4] = {DT_P8, DT_P16, DT_P32, DT_NONE}; + // ARMv8 crypto uses size=2 for a single 64x64 polynomial product. + if (decode.vcom.u != 0 || (decode.vcom.size != 0 && decode.vcom.size != 2) || (decode.vcom.vd & 1)) + return 1; instruction->operation = ARMV7_VMULL; - instruction->dataType = dataType[decode.vcom.size]; + instruction->dataType = decode.vcom.size == 2 ? DT_P64 : DT_P8; instruction->operands[0].cls = REG; instruction->operands[0].reg = (Register)(REG_Q0 + (((decode.vcom.d << 4) | decode.vcom.vd) >> 1)); instruction->operands[1].cls = REG; @@ -5199,13 +5212,18 @@ uint32_t armv7_two_register_scalar(uint32_t instructionValue, Instruction* restr break; case 10: { - static DataType dtMap[4] = {DT_NONE, DT_S16, DT_S32, DT_NONE}; + static DataType dtMap[2][4] = { + {DT_NONE, DT_S16, DT_S32, DT_NONE}, + {DT_NONE, DT_U16, DT_U32, DT_NONE} + }; + if (decode.vcom.vd & 1) + return 1; instruction->operation = ARMV7_VMULL; - instruction->dataType = dtMap[decode.vcom.size]; + instruction->dataType = dtMap[decode.com.u][decode.vcom.size]; instruction->operands[0].cls = REG; instruction->operands[0].reg = (Register)(REG_Q0 + (((decode.vcom.d << 4) | decode.vcom.vd) >> 1)); instruction->operands[1].cls = REG; - instruction->operands[1].reg = (Register)(REG_D0 + (((decode.vcom.n << 4) | decode.vcom.vn) >> decode.vcom.q)); + instruction->operands[1].reg = (Register)(REG_D0 + ((decode.vcom.n << 4) | decode.vcom.vn)); instruction->operands[2].cls = REG; instruction->operands[2].flags.hasElements = 1; if (decode.vcom.size == 1) @@ -5678,6 +5696,12 @@ uint32_t armv7_two_register_and_shift(uint32_t instructionValue, Instruction* re } break; } + if ((instruction->operation == ARMV7_VSHL || instruction->operation == ARMV7_VSHR) + && decode.vshr.q && ((decode.vshr.vd | decode.vshr.vm) & 1)) + return 1; + if (instruction->operation == ARMV7_VSHRN && (decode.vshr.vm & 1)) + return 1; + return instruction->operation == ARMV7_UNDEFINED; } @@ -5993,8 +6017,8 @@ uint32_t armv7_two_register_misc(uint32_t instructionValue, Instruction* restric { case 0: case 1: - if (decode.vcgt.size == 3 || - (decode.vcgt.q == 0 && decode.vcgt.size == 2)) + if (decode.vcgt.size != 0 || + (decode.vcgt.q && ((decode.vcgt.vd | decode.vcgt.vm) & 1))) return 1; instruction->operation = ARMV7_VSWP; break; @@ -6009,7 +6033,7 @@ uint32_t armv7_two_register_misc(uint32_t instructionValue, Instruction* restric break; case 4: case 5: - if (decode.vcgt.size == 3 || + if (decode.vcgt.size == 3 || (decode.vcgt.q == 0 && decode.vcgt.size == 2) || (decode.vcgt.q == 1 && ((decode.vcgt.vd & 1) == 1 || (decode.vcgt.vm & 1) == 1))) return 1; @@ -6018,7 +6042,7 @@ uint32_t armv7_two_register_misc(uint32_t instructionValue, Instruction* restric break; case 6: case 7: - if (decode.vcgt.size == 3 || + if (decode.vcgt.size == 3 || (decode.vcgt.q == 0 && decode.vcgt.size == 2) || (decode.vcgt.q == 1 && ((decode.vcgt.vd & 1) == 1 || (decode.vcgt.vm & 1) == 1))) return 1; diff --git a/arch/armv7/il.cpp b/arch/armv7/il.cpp index 0e9e49d0eb..8d8161bda3 100644 --- a/arch/armv7/il.cpp +++ b/arch/armv7/il.cpp @@ -947,6 +947,46 @@ static ExprId VectorWideningAdd(LowLevelILFunction& il, Instruction& instr, uint }); } +static ExprId VectorMoveLong(LowLevelILFunction& il, Instruction& instr) +{ + InstructionOperand& dst = instr.operands[0]; + InstructionOperand& src = instr.operands[1]; + size_t elementSize = GetDataTypeSize(instr.dataType); + if (dst.cls != REG || src.cls != REG || instr.operands[2].cls != NONE + || get_register_size(dst.reg) != 16 || get_register_size(src.reg) != 8 + || (elementSize != 1 && elementSize != 2 && elementSize != 4) + || (!IsSignedDataType(instr.dataType) && !IsUnsignedDataType(instr.dataType))) + return il.Unimplemented(); + + return il.Intrinsic( + { RegisterOrFlag::Register(dst.reg) }, + ARMV7_INTRIN_VMOVL, + { + il.Const(1, elementSize * 8), + il.Const(1, IsUnsignedDataType(instr.dataType) ? 1 : 0), + il.Register(8, src.reg), + }); +} + +static ExprId VectorMoveNarrow(LowLevelILFunction& il, Instruction& instr) +{ + InstructionOperand& dst = instr.operands[0]; + InstructionOperand& src = instr.operands[1]; + size_t elementSize = GetDataTypeSize(instr.dataType); + if (dst.cls != REG || src.cls != REG || instr.operands[2].cls != NONE + || get_register_size(dst.reg) != 8 || get_register_size(src.reg) != 16 + || (instr.dataType != DT_I16 && instr.dataType != DT_I32 && instr.dataType != DT_I64)) + return il.Unimplemented(); + + return il.Intrinsic( + { RegisterOrFlag::Register(dst.reg) }, + ARMV7_INTRIN_VMOVN, + { + il.Const(1, elementSize * 8), + il.Register(16, src.reg), + }); +} + static ExprId VectorRoundingAddNarrow(LowLevelILFunction& il, Instruction& instr) { InstructionOperand& dst = instr.operands[0]; @@ -1000,6 +1040,39 @@ static ExprId VectorMultiply(LowLevelILFunction& il, Instruction& instr) }); } +static ExprId VectorMultiplyLong(LowLevelILFunction& il, Instruction& instr) +{ + InstructionOperand& dst = instr.operands[0]; + InstructionOperand& src1 = instr.operands[1]; + InstructionOperand& src2 = instr.operands[2]; + size_t elementSize = GetDataTypeSize(instr.dataType); + bool polynomial = instr.dataType == DT_P8 || instr.dataType == DT_P64; + if (dst.cls != REG || src1.cls != REG || src2.cls != REG || instr.operands[3].cls != NONE + || get_register_size(dst.reg) != 16 || get_register_size(src1.reg) != 8 || get_register_size(src2.reg) != 8 + || (polynomial ? (elementSize != 1 && elementSize != 8) : (elementSize != 1 && elementSize != 2 && elementSize != 4)) + || (!polynomial && !IsSignedDataType(instr.dataType) && !IsUnsignedDataType(instr.dataType))) + return il.Unimplemented(); + + uint32_t index = 0xff; + if (src2.flags.hasElements) + { + if (polynomial || elementSize == 1 || src2.imm >= 8 / elementSize) + return il.Unimplemented(); + index = src2.imm; + } + return il.Intrinsic( + { RegisterOrFlag::Register(dst.reg) }, + ARMV7_INTRIN_VMULL, + { + il.Const(1, elementSize * 8), + il.Const(1, IsUnsignedDataType(instr.dataType) ? 1 : 0), + il.Const(1, polynomial ? 1 : 0), + il.Register(8, src1.reg), + il.Register(8, src2.reg), + il.Const(1, index), + }); +} + static ExprId VectorMultiplyAccumulateIntrinsic(LowLevelILFunction& il, Instruction& instr, uint32_t intrinsic) { InstructionOperand& dst = instr.operands[0]; @@ -1088,32 +1161,33 @@ static void VectorCompareEqual(LowLevelILFunction& il, Instruction& instr) })); } -static ExprId VectorCompareGreaterThan(LowLevelILFunction& il, Instruction& instr) +static ExprId VectorCompareOrdered(LowLevelILFunction& il, Instruction& instr, uint32_t intrinsic, uint32_t wideIntrinsic) { InstructionOperand& dst = instr.operands[0]; InstructionOperand& src1 = instr.operands[1]; InstructionOperand& src2 = instr.operands[2]; - - if (dst.cls != REG || src1.cls != REG || (src2.cls != REG && src2.cls != IMM)) + if (dst.cls != REG || src1.cls != REG || (src2.cls != REG && (src2.cls != IMM || src2.imm != 0))) return il.Unimplemented(); - size_t elementSize = GetDataTypeSize(instr.dataType); size_t regSize = get_register_size(dst.reg); - if (elementSize == 0 || regSize == 0) - return il.Unimplemented(); - - if (src2.cls == IMM && src2.imm != 0) + size_t elementSize = GetDataTypeSize(instr.dataType); + bool isFloat = instr.dataType == DT_F32; + bool isUnsigned = IsUnsignedDataType(instr.dataType); + if ((regSize != 8 && regSize != 16) || (elementSize != 1 && elementSize != 2 && elementSize != 4) + || (!isFloat && !isUnsigned && !IsSignedDataType(instr.dataType)) + || get_register_size(src1.reg) != regSize || (src2.cls == REG && get_register_size(src2.reg) != regSize)) return il.Unimplemented(); - ExprId rhs = (src2.cls == IMM) ? il.Const(regSize, 0) : il.Register(get_register_size(src2.reg), src2.reg); - bool isUnsigned = !IsSignedDataType(instr.dataType) && instr.dataType != DT_F32 && instr.dataType != DT_F64; + ExprId lhs = il.Register(regSize, src1.reg); + ExprId rhs = src2.cls == IMM ? il.Const(regSize, 0) : il.Register(regSize, src2.reg); return il.Intrinsic( { RegisterOrFlag::Register(dst.reg) }, - ARMV7_INTRIN_VCGT, + regSize == 16 ? wideIntrinsic : intrinsic, { il.Const(1, elementSize * 8), il.Const(1, isUnsigned ? 1 : 0), - il.Register(get_register_size(src1.reg), src1.reg), + il.Const(1, isFloat ? 1 : 0), + lhs, rhs, }); } @@ -1787,7 +1861,7 @@ static void LoadVpop(LowLevelILFunction& il, Instruction& instr, size_t addr) } static void VfpLoadStoreMultiple(LowLevelILFunction& il, InstructionOperand& base, InstructionOperand& regs, bool load, - bool decrementBefore) + bool decrementBefore, size_t addr, bool extraWord = false) { uint32_t regMask = (uint32_t)regs.reg; Register baseReg; @@ -1821,8 +1895,11 @@ static void VfpLoadStoreMultiple(LowLevelILFunction& il, InstructionOperand& bas if (((regMask >> i) & 1) == 1) count++; } - size_t totalSize = count * regSize; - ExprId start = decrementBefore ? il.Sub(4, il.Register(4, base.reg), il.Const(4, totalSize)) : il.Register(4, base.reg); + // ARM DDI 0406C.d A8.8.51: FLDM*X/FSTM*X includes a trailing word in + // the address span, but the VLDM/VSTM pseudocode does not access it. + size_t totalSize = count * regSize + (extraWord ? 4 : 0); + ExprId baseAddress = ReadRegisterOrPointer(il, base, addr); + ExprId start = decrementBefore ? il.Sub(4, baseAddress, il.Const(4, totalSize)) : baseAddress; uint32_t index = 0; for (uint32_t i = 0; i < 32; i++) @@ -1841,8 +1918,8 @@ static void VfpLoadStoreMultiple(LowLevelILFunction& il, InstructionOperand& bas if (base.flags.wb) { - ExprId newBase = decrementBefore ? il.Sub(4, il.Register(4, base.reg), il.Const(4, totalSize)) - : il.Add(4, il.Register(4, base.reg), il.Const(4, totalSize)); + ExprId newBase = decrementBefore ? il.Sub(4, baseAddress, il.Const(4, totalSize)) + : il.Add(4, baseAddress, il.Const(4, totalSize)); il.AddInstruction(il.SetRegister(4, base.reg, newBase)); } } @@ -2983,23 +3060,31 @@ bool GetLowLevelILForArmInstruction(Architecture* arch, uint64_t addr, LowLevelI case ARMV7_VSTM: case ARMV7_VSTMIA: case ARMV7_VSTMDB: + case ARMV7_FSTMDBX: + case ARMV7_FSTMIAX: ConditionExecute(addrSize, instr.cond, instr, il, [&](size_t addrSize, Instruction& instr, LowLevelILFunction& il) { (void) addrSize; (void) instr; - VfpLoadStoreMultiple(il, op1, op2, false, instr.operation == ARMV7_VSTMDB); + VfpLoadStoreMultiple(il, op1, op2, false, + instr.operation == ARMV7_VSTMDB || instr.operation == ARMV7_FSTMDBX, addr, + instr.operation == ARMV7_FSTMDBX || instr.operation == ARMV7_FSTMIAX); }); break; case ARMV7_VLDM: case ARMV7_VLDMIA: case ARMV7_VLDMDB: + case ARMV7_FLDMDBX: + case ARMV7_FLDMIAX: ConditionExecute(addrSize, instr.cond, instr, il, [&](size_t addrSize, Instruction& instr, LowLevelILFunction& il) { (void) addrSize; (void) instr; - VfpLoadStoreMultiple(il, op1, op2, true, instr.operation == ARMV7_VLDMDB); + VfpLoadStoreMultiple(il, op1, op2, true, + instr.operation == ARMV7_VLDMDB || instr.operation == ARMV7_FLDMDBX, addr, + instr.operation == ARMV7_FLDMDBX || instr.operation == ARMV7_FLDMIAX); }); break; case ARMV7_QADD: @@ -4715,6 +4800,22 @@ bool GetLowLevelILForArmInstruction(Architecture* arch, uint64_t addr, LowLevelI } } break; + case ARMV7_VPADD: + { + bool isFloat = instr.dataType == DT_F32; + if (op1.cls != REG || op2.cls != REG || op3.cls != REG || instr.operands[3].cls != NONE + || get_register_size(op1.reg) != 8 || get_register_size(op2.reg) != 8 || get_register_size(op3.reg) != 8 + || (!isFloat && instr.dataType != DT_I8 && instr.dataType != DT_I16 && instr.dataType != DT_I32)) + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + ConditionExecute(il, instr.cond, il.Intrinsic( + { RegisterOrFlag::Register(op1.reg) }, ARMV7_INTRIN_VPADD, + { il.Const(1, GetDataTypeSize(instr.dataType) * 8), il.Const(1, isFloat ? 1 : 0), + il.Register(8, op2.reg), il.Register(8, op3.reg) })); + break; + } case ARMV7_VADD: if (op1.cls != REG || op2.cls != REG || op3.cls != REG) { @@ -4756,7 +4857,13 @@ bool GetLowLevelILForArmInstruction(Architecture* arch, uint64_t addr, LowLevelI }); break; case ARMV7_VCGT: - ConditionExecute(il, instr.cond, VectorCompareGreaterThan(il, instr)); + ConditionExecute(il, instr.cond, VectorCompareOrdered(il, instr, ARMV7_INTRIN_VCGT, ARMV7_INTRIN_VCGT_Q)); + break; + case ARMV7_VCGE: + ConditionExecute(il, instr.cond, VectorCompareOrdered(il, instr, ARMV7_INTRIN_VCGE, ARMV7_INTRIN_VCGE_Q)); + break; + case ARMV7_VCLT: + ConditionExecute(il, instr.cond, VectorCompareOrdered(il, instr, ARMV7_INTRIN_VCLT, ARMV7_INTRIN_VCLT_Q)); break; case ARMV7_VAND: if (op1.cls != REG || op2.cls != REG || op3.cls != REG) @@ -4771,19 +4878,231 @@ bool GetLowLevelILForArmInstruction(Architecture* arch, uint64_t addr, LowLevelI il.Register(get_register_size(op3.reg), op3.reg)), flagOperation[instr.setsFlags])); break; - case ARMV7_VORR: - if (op1.cls != REG || op2.cls != REG || op3.cls != REG) + case ARMV7_VBIC: + { + size_t size = get_register_size(op1.reg); + if (op1.cls != REG || (size != 8 && size != 16)) + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + + ExprId source, mask; + if (op2.cls == IMM && op3.cls == NONE) + { + source = il.Register(size, op1.reg); + mask = il.Const(8, op2.imm64); + if (size == 16) + { + mask = il.ZeroExtend(16, mask); + mask = il.Or(16, mask, il.ShiftLeft(16, mask, il.Const(1, 64))); + } + } + else if (op2.cls == REG && op3.cls == REG) + { + source = il.Register(size, op2.reg); + mask = il.Register(size, op3.reg); + } + else { ConditionExecute(il, instr.cond, il.Unimplemented()); break; } ConditionExecute(il, instr.cond, - SetRegisterOrBranch(il, op1.reg, - il.Or(get_register_size(op1.reg), - il.Register(get_register_size(op2.reg), op2.reg), - il.Register(get_register_size(op3.reg), op3.reg)), - flagOperation[instr.setsFlags])); + il.SetRegister(size, op1.reg, il.And(size, source, il.Not(size, mask)))); + break; + } + case ARMV7_VBIF: + case ARMV7_VBIT: + case ARMV7_VBSL: + { + size_t size = get_register_size(op1.reg); + if (op1.cls != REG || op2.cls != REG || op3.cls != REG || (size != 8 && size != 16)) + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + + ExprId destination = il.Register(size, op1.reg); + ExprId source1 = il.Register(size, op2.reg); + ExprId source2 = il.Register(size, op3.reg); + ExprId setValue = source1; + ExprId clearValue = destination; + ExprId mask = source2; + if (instr.operation == ARMV7_VBIF) + { + setValue = destination; + clearValue = source1; + } + else if (instr.operation == ARMV7_VBSL) + { + mask = destination; + clearValue = source2; + } + ConditionExecute(il, instr.cond, + il.SetRegister(size, op1.reg, + il.Or(size, il.And(size, setValue, mask), il.And(size, clearValue, il.Not(size, mask))))); + break; + } + case ARMV7_VEOR: + { + size_t size = get_register_size(op1.reg); + if (op1.cls != REG || op2.cls != REG || op3.cls != REG || (size != 8 && size != 16) + || get_register_size(op2.reg) != size || get_register_size(op3.reg) != size) + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + ConditionExecute(il, instr.cond, + il.SetRegister(size, op1.reg, il.Xor(size, il.Register(size, op2.reg), il.Register(size, op3.reg)))); + break; + } + case ARMV7_VMVN: + { + size_t size = get_register_size(op1.reg); + if (op1.cls != REG || op3.cls != NONE || (size != 8 && size != 16)) + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + + ExprId value; + if (op2.cls == IMM) + { + value = il.Const(8, ~op2.imm64); + if (size == 16) + { + value = il.ZeroExtend(16, value); + value = il.Or(16, value, il.ShiftLeft(16, value, il.Const(1, 64))); + } + } + else if (op2.cls == REG && get_register_size(op2.reg) == size) + { + value = il.Not(size, il.Register(size, op2.reg)); + } + else + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + ConditionExecute(il, instr.cond, il.SetRegister(size, op1.reg, value)); + break; + } + case ARMV7_VTST: + { + size_t size = get_register_size(op1.reg); + size_t elementBits = GetDataTypeSize(instr.dataType) * 8; + if (op1.cls != REG || op2.cls != REG || op3.cls != REG || instr.operands[3].cls != NONE + || (size != 8 && size != 16) || get_register_size(op2.reg) != size + || get_register_size(op3.reg) != size + || (elementBits != 8 && elementBits != 16 && elementBits != 32)) + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + ConditionExecute(il, instr.cond, il.Intrinsic( + { RegisterOrFlag::Register(op1.reg) }, size == 16 ? ARMV7_INTRIN_VTST_Q : ARMV7_INTRIN_VTST, + { il.Const(1, elementBits), il.Register(size, op2.reg), il.Register(size, op3.reg) })); + break; + } + case ARMV7_VTRN: + case ARMV7_VUZP: + case ARMV7_VZIP: + { + size_t size = get_register_size(op1.reg); + size_t elementBits = GetDataTypeSize(instr.dataType) * 8; + if (op1.cls != REG || op2.cls != REG || op3.cls != NONE + || (size != 8 && size != 16) || get_register_size(op2.reg) != size + || (elementBits != 8 && elementBits != 16 && elementBits != 32) + || (instr.operation != ARMV7_VTRN && size == 8 && elementBits == 32)) + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + if (op1.reg == op2.reg) + { + // The ISA specifies UNKNOWN contents for identical operands. + ConditionExecute(il, instr.cond, il.SetRegister(size, op1.reg, il.Undefined())); + break; + } + uint32_t intrinsic = size == 16 ? ARMV7_INTRIN_VTRN_Q : ARMV7_INTRIN_VTRN; + if (instr.operation == ARMV7_VUZP) + intrinsic = size == 16 ? ARMV7_INTRIN_VUZP_Q : ARMV7_INTRIN_VUZP; + else if (instr.operation == ARMV7_VZIP) + intrinsic = size == 16 ? ARMV7_INTRIN_VZIP_Q : ARMV7_INTRIN_VZIP; + ConditionExecute(il, instr.cond, il.Intrinsic( + { RegisterOrFlag::Register(op1.reg), RegisterOrFlag::Register(op2.reg) }, + intrinsic, + { il.Const(1, elementBits), il.Register(size, op1.reg), il.Register(size, op2.reg) })); + break; + } + case ARMV7_VSWP: + { + size_t size = get_register_size(op1.reg); + if (op1.cls != REG || op2.cls != REG || op3.cls != NONE + || (size != 8 && size != 16) || get_register_size(op2.reg) != size) + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + ConditionExecute(addrSize, instr.cond, instr, il, + [&](size_t, Instruction&, LowLevelILFunction& il) + { + il.AddInstruction(il.SetRegister(size, LLIL_TEMP(0), il.Register(size, op1.reg))); + il.AddInstruction(il.SetRegister(size, op1.reg, il.Register(size, op2.reg))); + il.AddInstruction(il.SetRegister(size, op2.reg, il.Register(size, LLIL_TEMP(0)))); + }); + break; + } + case ARMV7_VORN: + { + size_t size = get_register_size(op1.reg); + if (op1.cls != REG || op2.cls != REG || op3.cls != REG || instr.operands[3].cls != NONE + || (size != 8 && size != 16) + || get_register_size(op2.reg) != size || get_register_size(op3.reg) != size) + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + ConditionExecute(il, instr.cond, + il.SetRegister(size, op1.reg, + il.Or(size, il.Register(size, op2.reg), il.Not(size, il.Register(size, op3.reg))))); + break; + } + case ARMV7_VORR: + { + size_t size = get_register_size(op1.reg); + if (op1.cls != REG || (size != 8 && size != 16)) + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + + ExprId source, mask; + if (op2.cls == IMM && op3.cls == NONE) + { + source = il.Register(size, op1.reg); + mask = il.Const(8, op2.imm64); + if (size == 16) + { + mask = il.ZeroExtend(16, mask); + mask = il.Or(16, mask, il.ShiftLeft(16, mask, il.Const(1, 64))); + } + } + else if (op2.cls == REG && op3.cls == REG && instr.operands[3].cls == NONE + && get_register_size(op2.reg) == size && get_register_size(op3.reg) == size) + { + source = il.Register(size, op2.reg); + mask = il.Register(size, op3.reg); + } + else + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + ConditionExecute(il, instr.cond, il.SetRegister(size, op1.reg, il.Or(size, source, mask))); break; + } case ARMV7_VDUP: { ConditionExecute(il, instr.cond, VectorDuplicate(il, instr)); @@ -4933,6 +5252,12 @@ bool GetLowLevelILForArmInstruction(Architecture* arch, uint64_t addr, LowLevelI ConditionExecute(il, instr.cond, VectorWideningAdd(il, instr, ARMV7_INTRIN_VADDW)); break; + case ARMV7_VMOVL: + ConditionExecute(il, instr.cond, VectorMoveLong(il, instr)); + break; + case ARMV7_VMOVN: + ConditionExecute(il, instr.cond, VectorMoveNarrow(il, instr)); + break; case ARMV7_VRADDHN: ConditionExecute(il, instr.cond, VectorRoundingAddNarrow(il, instr)); break; @@ -5109,6 +5434,9 @@ bool GetLowLevelILForArmInstruction(Architecture* arch, uint64_t addr, LowLevelI ConditionExecute(il, instr.cond, il.Unimplemented()); } break; + case ARMV7_VMULL: + ConditionExecute(il, instr.cond, VectorMultiplyLong(il, instr)); + break; case ARMV7_VQDMULL: ConditionExecute(il, instr.cond, VectorSaturatingDoublingMultiplyLongIntrinsic(il, instr)); break; @@ -5131,6 +5459,37 @@ bool GetLowLevelILForArmInstruction(Architecture* arch, uint64_t addr, LowLevelI ) ); break; + case ARMV7_VNEG: + { + if ((instr.dataType == DT_S8 || instr.dataType == DT_S16 || instr.dataType == DT_S32 + || instr.dataType == DT_F32) + && op1.cls == REG && op2.cls == REG && op3.cls == NONE + && (get_register_size(op1.reg) == 8 || get_register_size(op1.reg) == 16) + && get_register_size(op1.reg) == get_register_size(op2.reg)) + { + size_t size = get_register_size(op1.reg); + ConditionExecute(il, instr.cond, + il.Intrinsic( + { RegisterOrFlag::Register(op1.reg) }, + size == 16 ? ARMV7_INTRIN_VNEG_Q : ARMV7_INTRIN_VNEG, + { il.Const(1, GetDataTypeSize(instr.dataType) * 8), il.Const(1, instr.dataType == DT_F32), + il.Register(size, op2.reg) })); + break; + } + + size_t size = GetDataTypeSize(instr.dataType); + if (op1.cls != REG || op2.cls != REG || op3.cls != NONE + || (instr.dataType != DT_F32 && instr.dataType != DT_F64) + || get_register_size(op1.reg) != size || get_register_size(op2.reg) != size) + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + + ConditionExecute(il, instr.cond, + il.SetRegister(size, op1.reg, il.FloatNeg(size, il.Register(size, op2.reg)))); + break; + } case ARMV7_VNMUL: if((instr.dataType != DT_F32) && (instr.dataType != DT_F64)) break; @@ -5203,70 +5562,77 @@ bool GetLowLevelILForArmInstruction(Architecture* arch, uint64_t addr, LowLevelI LoadVld1(il, instr, addr); }); break; - case ARMV7_VSHR: - { - size_t elementSize = GetDataTypeSize(instr.dataType); - size_t size = get_register_size(op1.reg); - if (op1.cls != REG || op2.cls != REG || op3.cls != IMM || elementSize != size) - { - ConditionExecute(il, instr.cond, il.Unimplemented()); - break; - } - - ExprId value = IsSignedDataType(instr.dataType) - ? il.ArithShiftRight(size, il.Register(size, op2.reg), il.Const(1, op3.imm)) - : il.LogicalShiftRight(size, il.Register(size, op2.reg), il.Const(1, op3.imm)); - ConditionExecute(il, instr.cond, - SetRegisterOrBranch(il, op1.reg, value, flagOperation[instr.setsFlags])); - break; - } case ARMV7_VSHL: + case ARMV7_VSHR: + case ARMV7_VSHRN: { - size_t elementSize = GetDataTypeSize(instr.dataType); + size_t elementBits = GetDataTypeSize(instr.dataType) * 8; size_t size = get_register_size(op1.reg); - if (op1.cls != REG || op2.cls != REG || elementSize != size) + bool narrow = instr.operation == ARMV7_VSHRN; + bool right = instr.operation == ARMV7_VSHR; + bool immediate = op3.cls == IMM; + bool signedType = IsSignedDataType(instr.dataType); + bool unsignedType = IsUnsignedDataType(instr.dataType); + bool integerType = instr.dataType == DT_I8 || instr.dataType == DT_I16 + || instr.dataType == DT_I32 || instr.dataType == DT_I64; + if (op1.cls != REG || op2.cls != REG || instr.operands[3].cls != NONE + || (size != 8 && size != 16) || get_register_size(op2.reg) != (narrow ? 16 : size) + || (elementBits != 8 && elementBits != 16 && elementBits != 32 && elementBits != 64) + || (narrow && (size != 8 || elementBits == 8))) { ConditionExecute(il, instr.cond, il.Unimplemented()); break; } - if (op3.cls == IMM) + uint32_t intrinsic; + std::vector inputs = { il.Const(1, elementBits) }; + if (immediate) { - ConditionExecute(il, instr.cond, - SetRegisterOrBranch(il, op1.reg, - il.ShiftLeft(size, il.Register(size, op2.reg), il.Const(1, op3.imm)), - flagOperation[instr.setsFlags])); - break; + if ((right ? (!signedType && !unsignedType) : !integerType) + || ((right || narrow) ? (op3.imm == 0 || op3.imm > elementBits / (narrow ? 2 : 1)) + : op3.imm >= elementBits)) + { + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + if (!narrow && size == 8 && elementBits == 64) + { + // A single D64 lane is a scalar shift; keep it visible to constant propagation. + ExprId source = il.Register(8, op2.reg); + ExprId value; + if (!right) + value = il.ShiftLeft(8, source, il.Const(1, op3.imm)); + else if (signedType) + // ASR by 64 sign-fills, but LLIL masks 64-bit shift counts. + value = il.ArithShiftRight(8, source, il.Const(1, op3.imm == 64 ? 63 : op3.imm)); + else + value = op3.imm == 64 ? il.Const(8, 0) + : il.LogicalShiftRight(8, source, il.Const(1, op3.imm)); + ConditionExecute(il, instr.cond, il.SetRegister(8, op1.reg, value)); + break; + } + if (right) + inputs.push_back(il.Const(1, unsignedType ? 1 : 0)); + inputs.push_back(il.Register(narrow ? 16 : size, op2.reg)); + inputs.push_back(il.Const(right ? 8 : 1, op3.imm)); + intrinsic = narrow ? ARMV7_INTRIN_VSHRN : right + ? (size == 16 ? ARMV7_INTRIN_VSHR_Q : ARMV7_INTRIN_VSHR) + : (size == 16 ? ARMV7_INTRIN_VSHL_IMM_Q : ARMV7_INTRIN_VSHL_IMM); } - - if (op3.cls != REG || get_register_size(op3.reg) != size) + else { - ConditionExecute(il, instr.cond, il.Unimplemented()); - break; - } - - ConditionExecute(addrSize, instr.cond, instr, il, - [&](size_t addrSize, Instruction& instr, LowLevelILFunction& il) + if (right || narrow || op3.cls != REG || get_register_size(op3.reg) != size + || (!signedType && !unsignedType)) { - (void) addrSize; - (void) instr; - LowLevelILLabel rightShift, leftShift, done; - il.AddInstruction(il.If( - il.CompareSignedLessThan(size, il.Register(size, op3.reg), il.Const(size, 0)), - rightShift, leftShift)); - il.MarkLabel(rightShift); - il.AddInstruction(SetRegisterOrBranch(il, op1.reg, - IsSignedDataType(instr.dataType) - ? il.ArithShiftRight(size, il.Register(size, op2.reg), il.Neg(size, il.Register(size, op3.reg))) - : il.LogicalShiftRight(size, il.Register(size, op2.reg), il.Neg(size, il.Register(size, op3.reg))), - flagOperation[instr.setsFlags])); - il.AddInstruction(il.Goto(done)); - il.MarkLabel(leftShift); - il.AddInstruction(SetRegisterOrBranch(il, op1.reg, - il.ShiftLeft(size, il.Register(size, op2.reg), il.Register(size, op3.reg)), - flagOperation[instr.setsFlags])); - il.MarkLabel(done); - }); + ConditionExecute(il, instr.cond, il.Unimplemented()); + break; + } + intrinsic = size == 16 ? ARMV7_INTRIN_VSHL_Q : ARMV7_INTRIN_VSHL; + inputs.push_back(il.Const(1, unsignedType ? 1 : 0)); + inputs.push_back(il.Register(size, op2.reg)); + inputs.push_back(il.Register(size, op3.reg)); + } + ConditionExecute(il, instr.cond, il.Intrinsic({ RegisterOrFlag::Register(op1.reg) }, intrinsic, inputs)); break; } case ARMV7_VSUB: diff --git a/arch/armv7/il.h b/arch/armv7/il.h index 9764c261e9..e579eadcc9 100644 --- a/arch/armv7/il.h +++ b/arch/armv7/il.h @@ -205,6 +205,54 @@ enum Armv7Intrinsic : uint32_t ARMV7_INTRIN_VCVT_FIXED, ARMV7_INTRIN_VABS_Q, ARMV7_INTRIN_VCVT_FIXED_Q, + // Lane-wise >=; matching lanes produce all ones, otherwise all zeros. + ARMV7_INTRIN_VCGE, + ARMV7_INTRIN_VCGE_Q, + ARMV7_INTRIN_VCGT_Q, + // Lane-wise < comparison, with the original source operand order. + ARMV7_INTRIN_VCLT, + ARMV7_INTRIN_VCLT_Q, + // Widen each D-register lane to twice its size in a Q register. + ARMV7_INTRIN_VMOVL, + // Keep the low half of each Q-register lane in a D register. + ARMV7_INTRIN_VMOVN, + // Widening products from D sources to Q; index 0xff selects vector-by-vector multiplication. + // Polynomial size=64 is ARMv8 VMULL.P64: one carryless 64x64 product with a 128-bit result. + ARMV7_INTRIN_VMULL, + // Lane-wise negation; integer results wrap to the element size without saturation. + ARMV7_INTRIN_VNEG, + ARMV7_INTRIN_VNEG_Q, + // Adjacent pair sums from source1 fill the low half, then source2 the high half. + // Integer sums wrap to the element size; floating sums use standard SIMD FP behavior. + ARMV7_INTRIN_VPADD, + // Register VSHL uses the signed low byte of each count lane: negative shifts right. + // Signedness controls right-shift fill; large counts zero or sign-fill each lane. + ARMV7_INTRIN_VSHL_Q, + // Immediate VSHL applies one count to every lane, without saturation. + ARMV7_INTRIN_VSHL_IMM, + ARMV7_INTRIN_VSHL_IMM_Q, + ARMV7_INTRIN_VSHR_Q, + // Logical right shift of Q source lanes, then truncate each to half its width in D. + // The size parameter is the source lane width (16, 32, or 64 bits). + ARMV7_INTRIN_VSHRN, + // Transpose adjacent lane pairs, reading both sources before writing either result. + // Result 1 interleaves even lanes of source1/source2; result 2 interleaves odd lanes. + // The size parameter is the lane width in bits (8, 16, or 32). + ARMV7_INTRIN_VTRN, + ARMV7_INTRIN_VTRN_Q, + // For each 8/16/32-bit lane, return all ones if (source1 & source2) != 0, else zero. + ARMV7_INTRIN_VTST, + ARMV7_INTRIN_VTST_Q, + // De-interleave the concatenation of source1 (low) and source2 (high). + // Result 1 contains even lanes, result 2 odd lanes, in their original order. + // Lane width is 8/16 bits for D and 8/16/32 bits for Q; both inputs are read before either write. + ARMV7_INTRIN_VUZP, + ARMV7_INTRIN_VUZP_Q, + // Interleave source1/source2 lanes, preserving their order, then split the result. + // Result 1 is the low half, result 2 the high half; read both inputs before either write. + // Lane width is 8/16 bits for D and 8/16/32 bits for Q. + ARMV7_INTRIN_VZIP, + ARMV7_INTRIN_VZIP_Q, }; enum ArmFakeRegister: uint32_t diff --git a/arch/armv7/test_lift.py b/arch/armv7/test_lift.py index 1db163c28a..290ec0f979 100755 --- a/arch/armv7/test_lift.py +++ b/arch/armv7/test_lift.py @@ -39,6 +39,61 @@ def vmlal_expected(size, unsigned): test_cases = \ [ + # vzip.8 d0, d1 + ('A', b'\x81\x01\xb2\xf3', 'LLIL_INTRINSIC([d0,d1],__vzip,[LLIL_CONST.b(0x8),LLIL_REG.q(d0),LLIL_REG.q(d1)])'), + # it eq; vzipeq.16 q15, q14 + ('T', b'\x08\xbf\xf6\xff\xec\xe1', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_E,None),1,3); LLIL_INTRINSIC([q15,q14],__vzip_q,[LLIL_CONST.b(0x10),LLIL_REG.o(q15),LLIL_REG.o(q14)]); LLIL_GOTO(3)'), + # vzip.16 d31, d16 + ('T', b'\xf6\xff\xa0\xf1', 'LLIL_INTRINSIC([d31,d16],__vzip,[LLIL_CONST.b(0x10),LLIL_REG.q(d31),LLIL_REG.q(d16)])'), + # vzip.32 q0, q1 + ('A', b'\xc2\x01\xba\xf3', 'LLIL_INTRINSIC([q0,q1],__vzip_q,[LLIL_CONST.b(0x20),LLIL_REG.o(q0),LLIL_REG.o(q1)])'), + # Undefined VZIP: D-register .32 must use the VTRN encoding + ('A', b'\x81\x01\xba\xf3', 'LLIL_UNDEF()'), + + # vuzp.8 d0, d1 + ('A', b'\x01\x01\xb2\xf3', 'LLIL_INTRINSIC([d0,d1],__vuzp,[LLIL_CONST.b(0x8),LLIL_REG.q(d0),LLIL_REG.q(d1)])'), + # vuzp.16 d31, d16 + ('T', b'\xf6\xff\x20\xf1', 'LLIL_INTRINSIC([d31,d16],__vuzp,[LLIL_CONST.b(0x10),LLIL_REG.q(d31),LLIL_REG.q(d16)])'), + # it eq; vuzpeq.16 q15, q14 + ('T', b'\x08\xbf\xf6\xff\x6c\xe1', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_E,None),1,3); LLIL_INTRINSIC([q15,q14],__vuzp_q,[LLIL_CONST.b(0x10),LLIL_REG.o(q15),LLIL_REG.o(q14)]); LLIL_GOTO(3)'), + # vuzp.32 q0, q1 + ('A', b'\x42\x01\xba\xf3', 'LLIL_INTRINSIC([q0,q1],__vuzp_q,[LLIL_CONST.b(0x20),LLIL_REG.o(q0),LLIL_REG.o(q1)])'), + # Undefined VUZP: D-register .32 must use the VTRN encoding + ('A', b'\x01\x01\xba\xf3', 'LLIL_UNDEF()'), + + # vtst.8 d0, d1, d2 + ('A', b'\x12\x08\x01\xf2', 'LLIL_INTRINSIC([d0],__vtst,[LLIL_CONST.b(0x8),LLIL_REG.q(d1),LLIL_REG.q(d2)])'), + # vtst.16 d31, d30, d29 + ('T', b'\x5e\xef\xbd\xf8', 'LLIL_INTRINSIC([d31],__vtst,[LLIL_CONST.b(0x10),LLIL_REG.q(d30),LLIL_REG.q(d29)])'), + # vtst.32 q15, q14, q13 + ('A', b'\xfa\xe8\x6c\xf2', 'LLIL_INTRINSIC([q15],__vtst_q,[LLIL_CONST.b(0x20),LLIL_REG.o(q14),LLIL_REG.o(q13)])'), + # it ne; vtstne.32 q15, q14, q13 + ('T', b'\x18\xbf\x6c\xef\xfa\xe8', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_NE,None),1,3); LLIL_INTRINSIC([q15],__vtst_q,[LLIL_CONST.b(0x20),LLIL_REG.o(q14),LLIL_REG.o(q13)]); LLIL_GOTO(3)'), + # Undefined VTST: reserved size=3, Q=0 + ('A', b'\x12\x08\x31\xf2', 'LLIL_UNDEF()'), + + # vtrn.32 d16, d31 -- also encodes the D-register VUZP/VZIP aliases + ('A', b'\xaf\x00\xfa\xf3', 'LLIL_INTRINSIC([d16,d31],__vtrn,[LLIL_CONST.b(0x20),LLIL_REG.q(d16),LLIL_REG.q(d31)])'), + # vtrn.8 d0, d1 + ('T', b'\xb2\xff\x81\x00', 'LLIL_INTRINSIC([d0,d1],__vtrn,[LLIL_CONST.b(0x8),LLIL_REG.q(d0),LLIL_REG.q(d1)])'), + # vtrn.16 q15, q15 + ('T', b'\xf6\xff\xee\xe0', 'LLIL_SET_REG.o(q15,LLIL_UNDEF())'), + # it ne; vtrnne.16 q15, q8 + ('T', b'\x18\xbf\xf6\xff\xe0\xe0', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_NE,None),1,3); LLIL_INTRINSIC([q15,q8],__vtrn_q,[LLIL_CONST.b(0x10),LLIL_REG.o(q15),LLIL_REG.o(q8)]); LLIL_GOTO(3)'), + # vtrn.32 q0, q1 + ('A', b'\xc2\x00\xba\xf3', 'LLIL_INTRINSIC([q0,q1],__vtrn_q,[LLIL_CONST.b(0x20),LLIL_REG.o(q0),LLIL_REG.o(q1)])'), + + # vswp d0, d1 + ('A', b'\x01\x00\xb2\xf3', 'LLIL_SET_REG.q(temp0,LLIL_REG.q(d0)); LLIL_SET_REG.q(d0,LLIL_REG.q(d1)); LLIL_SET_REG.q(d1,LLIL_REG.q(temp0))'), + # vswp d31, d31 + ('A', b'\x2f\xf0\xf2\xf3', 'LLIL_SET_REG.q(temp0,LLIL_REG.q(d31)); LLIL_SET_REG.q(d31,LLIL_REG.q(d31)); LLIL_SET_REG.q(d31,LLIL_REG.q(temp0))'), + # vswp q15, q8 + ('T', b'\xf2\xff\x60\xe0', 'LLIL_SET_REG.o(temp0,LLIL_REG.o(q15)); LLIL_SET_REG.o(q15,LLIL_REG.o(q8)); LLIL_SET_REG.o(q8,LLIL_REG.o(temp0))'), + # it eq; vswpeq q15, q8 + ('T', b'\x08\xbf\xf2\xff\x60\xe0', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_E,None),1,5); LLIL_SET_REG.o(temp0,LLIL_REG.o(q15)); LLIL_SET_REG.o(q15,LLIL_REG.o(q8)); LLIL_SET_REG.o(q8,LLIL_REG.o(temp0)); LLIL_GOTO(5)'), + # Undefined VSWP: reserved size=1 with Q=0 + ('A', b'\x01\x00\xb6\xf3', 'LLIL_UNDEF()'), + # Post-Indexed addressing (normal) # with register offset # ldr r0, [r1], r2 @@ -436,8 +491,84 @@ def vmlal_expected(size, unsigned): ('T', b'\xff\xff\x01\x0c', 'LLIL_INTRINSIC([d16],__vdup,[LLIL_CONST.b(0x8),LLIL_REG.q(d1),LLIL_CONST.b(0x7)])'), # vorr d8, d17, d16 ('A', b'\xb0\x81\x21\xf2', 'LLIL_SET_REG.q(d8,LLIL_OR.q(LLIL_REG.q(d17),LLIL_REG.q(d16)))'), + # vorr.i32 q15, #0xa5000000 -- replicate into the high half without sign extension + ('A', b'\x55\xe7\xc2\xf3', 'LLIL_SET_REG.o(q15,LLIL_OR.o(LLIL_REG.o(q15),LLIL_OR.o(LLIL_ZX.o(LLIL_CONST.q(0xA5000000A5000000)),LLIL_LSL.o(LLIL_ZX.o(LLIL_CONST.q(0xA5000000A5000000)),LLIL_CONST.b(0x40)))))'), + # vorr.i16 d16, #0xa500 -- element size is independent of the high register bit + ('T', b'\xc2\xff\x15\x0b', 'LLIL_SET_REG.q(d16,LLIL_OR.q(LLIL_REG.q(d16),LLIL_CONST.q(0xA500A500A500A500)))'), + # vorr q0, q1, q2 + ('T', b'\x22\xef\x54\x01', 'LLIL_SET_REG.o(q0,LLIL_OR.o(LLIL_REG.o(q1),LLIL_REG.o(q2)))'), + # it eq; vorreq.i16 q8, #0xa500 + ('T', b'\x08\xbf\xc2\xff\x55\x0b', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_E,None),1,3); LLIL_SET_REG.o(q8,LLIL_OR.o(LLIL_REG.o(q8),LLIL_OR.o(LLIL_ZX.o(LLIL_CONST.q(0xA500A500A500A500)),LLIL_LSL.o(LLIL_ZX.o(LLIL_CONST.q(0xA500A500A500A500)),LLIL_CONST.b(0x40))))); LLIL_GOTO(3)'), + # vorn d0, d1, d2 + ('A', b'\x12\x01\x31\xf2', 'LLIL_SET_REG.q(d0,LLIL_OR.q(LLIL_REG.q(d1),LLIL_NOT.q(LLIL_REG.q(d2))))'), + # vorn q15, q8, q14 + ('T', b'\x70\xef\xfc\xe1', 'LLIL_SET_REG.o(q15,LLIL_OR.o(LLIL_REG.o(q8),LLIL_NOT.o(LLIL_REG.o(q14))))'), + # vorn d0, d1, d0 + ('T', b'\x31\xef\x10\x01', 'LLIL_SET_REG.q(d0,LLIL_OR.q(LLIL_REG.q(d1),LLIL_NOT.q(LLIL_REG.q(d0))))'), + # it ne; vornne q15, q8, q14 + ('T', b'\x18\xbf\x70\xef\xfc\xe1', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_NE,None),1,3); LLIL_SET_REG.o(q15,LLIL_OR.o(LLIL_REG.o(q8),LLIL_NOT.o(LLIL_REG.o(q14)))); LLIL_GOTO(3)'), + # veor d0, d1, d2 + ('A', b'\x12\x01\x01\xf3', 'LLIL_SET_REG.q(d0,LLIL_XOR.q(LLIL_REG.q(d1),LLIL_REG.q(d2)))'), + # veor q15, q8, q14 -- all 128 bits participate in the XOR + ('T', b'\x40\xff\xfc\xe1', 'LLIL_SET_REG.o(q15,LLIL_XOR.o(LLIL_REG.o(q8),LLIL_REG.o(q14)))'), + # veor d0, d0, d0 -- common register-zeroing idiom + ('A', b'\x10\x01\x00\xf3', 'LLIL_SET_REG.q(d0,LLIL_XOR.q(LLIL_REG.q(d0),LLIL_REG.q(d0)))'), + # it ne; veorne q15, q8, q14 + ('T', b'\x18\xbf\x40\xff\xfc\xe1', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_NE,None),1,3); LLIL_SET_REG.o(q15,LLIL_XOR.o(LLIL_REG.o(q8),LLIL_REG.o(q14))); LLIL_GOTO(3)'), + # vbic.i32 d0, #0xa5 + ('A', b'\x35\x01\x82\xf3', 'LLIL_SET_REG.q(d0,LLIL_AND.q(LLIL_REG.q(d0),LLIL_NOT.q(LLIL_CONST.q(0xA5000000A5))))'), + # vbic.i32 q15, #0xa5000000 -- replicate into the high half without sign extension + ('T', b'\xc2\xff\x75\xe7', 'LLIL_SET_REG.o(q15,LLIL_AND.o(LLIL_REG.o(q15),LLIL_NOT.o(LLIL_OR.o(LLIL_ZX.o(LLIL_CONST.q(0xA5000000A5000000)),LLIL_LSL.o(LLIL_ZX.o(LLIL_CONST.q(0xA5000000A5000000)),LLIL_CONST.b(0x40))))))'), + # vbic.i16 d16, #0xa500 -- element size is independent of the high register bit + ('T', b'\xc2\xff\x35\x0b', 'LLIL_SET_REG.q(d16,LLIL_AND.q(LLIL_REG.q(d16),LLIL_NOT.q(LLIL_CONST.q(0xA500A500A500A500))))'), + # vbic d8, d17, d16 -- register sources are distinct from the destination + ('A', b'\xb0\x81\x11\xf2', 'LLIL_SET_REG.q(d8,LLIL_AND.q(LLIL_REG.q(d17),LLIL_NOT.q(LLIL_REG.q(d16))))'), + # vbic q15, q8, q7 + ('T', b'\x50\xef\xde\xe1', 'LLIL_SET_REG.o(q15,LLIL_AND.o(LLIL_REG.o(q8),LLIL_NOT.o(LLIL_REG.o(q7))))'), + # vbif d0, d1, d2 + ('A', b'\x12\x01\x31\xf3', 'LLIL_SET_REG.q(d0,LLIL_OR.q(LLIL_AND.q(LLIL_REG.q(d0),LLIL_REG.q(d2)),LLIL_AND.q(LLIL_REG.q(d1),LLIL_NOT.q(LLIL_REG.q(d2)))))'), + # vbif q15, q8, q14 -- all 128 bits participate in the selection + ('T', b'\x70\xff\xfc\xe1', 'LLIL_SET_REG.o(q15,LLIL_OR.o(LLIL_AND.o(LLIL_REG.o(q15),LLIL_REG.o(q14)),LLIL_AND.o(LLIL_REG.o(q8),LLIL_NOT.o(LLIL_REG.o(q14)))))'), + # vbif d0, d1, d0 -- the original destination is also the mask + ('A', b'\x10\x01\x31\xf3', 'LLIL_SET_REG.q(d0,LLIL_OR.q(LLIL_AND.q(LLIL_REG.q(d0),LLIL_REG.q(d0)),LLIL_AND.q(LLIL_REG.q(d1),LLIL_NOT.q(LLIL_REG.q(d0)))))'), + # it ne; vbifne q15, q8, q14 + ('T', b'\x18\xbf\x70\xff\xfc\xe1', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_NE,None),1,3); LLIL_SET_REG.o(q15,LLIL_OR.o(LLIL_AND.o(LLIL_REG.o(q15),LLIL_REG.o(q14)),LLIL_AND.o(LLIL_REG.o(q8),LLIL_NOT.o(LLIL_REG.o(q14))))); LLIL_GOTO(3)'), + # vbit d0, d1, d2 + ('A', b'\x12\x01\x21\xf3', 'LLIL_SET_REG.q(d0,LLIL_OR.q(LLIL_AND.q(LLIL_REG.q(d1),LLIL_REG.q(d2)),LLIL_AND.q(LLIL_REG.q(d0),LLIL_NOT.q(LLIL_REG.q(d2)))))'), + # vbit q15, q8, q14 -- full-width selection in high Q registers + ('T', b'\x60\xff\xfc\xe1', 'LLIL_SET_REG.o(q15,LLIL_OR.o(LLIL_AND.o(LLIL_REG.o(q8),LLIL_REG.o(q14)),LLIL_AND.o(LLIL_REG.o(q15),LLIL_NOT.o(LLIL_REG.o(q14)))))'), + # vbit d0, d1, d0 -- the original destination is also the mask + ('A', b'\x10\x01\x21\xf3', 'LLIL_SET_REG.q(d0,LLIL_OR.q(LLIL_AND.q(LLIL_REG.q(d1),LLIL_REG.q(d0)),LLIL_AND.q(LLIL_REG.q(d0),LLIL_NOT.q(LLIL_REG.q(d0)))))'), + # it ne; vbitne q15, q8, q14 + ('T', b'\x18\xbf\x60\xff\xfc\xe1', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_NE,None),1,3); LLIL_SET_REG.o(q15,LLIL_OR.o(LLIL_AND.o(LLIL_REG.o(q8),LLIL_REG.o(q14)),LLIL_AND.o(LLIL_REG.o(q15),LLIL_NOT.o(LLIL_REG.o(q14))))); LLIL_GOTO(3)'), + # vbsl d0, d1, d2 + ('A', b'\x12\x01\x11\xf3', 'LLIL_SET_REG.q(d0,LLIL_OR.q(LLIL_AND.q(LLIL_REG.q(d1),LLIL_REG.q(d0)),LLIL_AND.q(LLIL_REG.q(d2),LLIL_NOT.q(LLIL_REG.q(d0)))))'), + # vbsl q15, q8, q14 -- full-width selection in high Q registers + ('T', b'\x50\xff\xfc\xe1', 'LLIL_SET_REG.o(q15,LLIL_OR.o(LLIL_AND.o(LLIL_REG.o(q8),LLIL_REG.o(q15)),LLIL_AND.o(LLIL_REG.o(q14),LLIL_NOT.o(LLIL_REG.o(q15)))))'), + # vbsl d0, d1, d0 -- source2 aliases the destination/mask + ('A', b'\x10\x01\x11\xf3', 'LLIL_SET_REG.q(d0,LLIL_OR.q(LLIL_AND.q(LLIL_REG.q(d1),LLIL_REG.q(d0)),LLIL_AND.q(LLIL_REG.q(d0),LLIL_NOT.q(LLIL_REG.q(d0)))))'), + # it ne; vbslne q15, q8, q14 + ('T', b'\x18\xbf\x50\xff\xfc\xe1', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_NE,None),1,3); LLIL_SET_REG.o(q15,LLIL_OR.o(LLIL_AND.o(LLIL_REG.o(q8),LLIL_REG.o(q15)),LLIL_AND.o(LLIL_REG.o(q14),LLIL_NOT.o(LLIL_REG.o(q15))))); LLIL_GOTO(3)'), # vand d0, d16, d6 ('T', b'\x00\xef\x96\x01', 'LLIL_SET_REG.q(d0,LLIL_AND.q(LLIL_REG.q(d16),LLIL_REG.q(d6)))'), + # vshl.i64 d31, d31, #0 -- a single D64 lane retains direct shift IL + ('T', b'\xc0\xef\xbf\xf5', 'LLIL_SET_REG.q(d31,LLIL_LSL.q(LLIL_REG.q(d31),LLIL_CONST.b(0x0)))'), + # vshr.u64 d31, d31, #64 -- zero rather than a masked shift by zero + ('T', b'\xc0\xff\xbf\xf0', 'LLIL_SET_REG.q(d31,LLIL_CONST.q(0x0))'), + # vshl.s32 q15, q8, q14 + ('A', b'\xe0\xe4\x6c\xf2', 'LLIL_INTRINSIC([q15],__vshl_q,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_REG.o(q8),LLIL_REG.o(q14)])'), + # vshrn.i16 d0, q15, #1 + ('A', b'\x3e\x08\x8f\xf2', 'LLIL_INTRINSIC([d0],__vshrn,[LLIL_CONST.b(0x10),LLIL_REG.o(q15),LLIL_CONST.b(0x1)])'), + # vshrn.i32 d0, q15, #16 + ('T', b'\x90\xef\x3e\x08', 'LLIL_INTRINSIC([d0],__vshrn,[LLIL_CONST.b(0x20),LLIL_REG.o(q15),LLIL_CONST.b(0x10)])'), + # it eq; vshleq.i32 q15, q8, #31 + ('T', b'\x08\xbf\xff\xef\x70\xe5', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_E,None),1,3); LLIL_INTRINSIC([q15],__vshl_imm_q,[LLIL_CONST.b(0x20),LLIL_REG.o(q8),LLIL_CONST.b(0x1F)]); LLIL_GOTO(3)'), + # it ne; vshrne.s64 d31, d31, #64 -- sign-fill, equivalent to ASR by 63 + ('T', b'\x18\xbf\xc0\xef\xbf\xf0', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_NE,None),1,3); LLIL_SET_REG.q(d31,LLIL_ASR.q(LLIL_REG.q(d31),LLIL_CONST.b(0x3F))); LLIL_GOTO(3)'), + # it ne; vshrnne.i64 d31, q15, #32 + ('T', b'\x18\xbf\xe0\xef\x3e\xf8', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_NE,None),1,3); LLIL_INTRINSIC([d31],__vshrn,[LLIL_CONST.b(0x40),LLIL_REG.o(q15),LLIL_CONST.b(0x20)]); LLIL_GOTO(3)'), + # vshrn.i64: odd Q source + ('A', b'\x13\x08\xbf\xf2', 'LLIL_UNDEF()'), # vshr.u64 d16, d16, #0x20 ('A', b'\xb0\x00\xe0\xf3', 'LLIL_SET_REG.q(d16,LLIL_LSR.q(LLIL_REG.q(d16),LLIL_CONST.b(0x20)))'), # vshr.s64 d8, d8, #0x20 @@ -460,8 +591,8 @@ def vmlal_expected(size, unsigned): ('T', b'\xf0\xff\x30\x04', 'LLIL_INTRINSIC([d16],__vsri,[LLIL_CONST.b(0x20),LLIL_REG.q(d16),LLIL_REG.q(d16),LLIL_CONST.q(0x10)])'), # vsli.32 d16, d23, #0x10 ('T', b'\xf0\xff\x37\x05', 'LLIL_INTRINSIC([d16],__vsli,[LLIL_CONST.b(0x20),LLIL_REG.q(d16),LLIL_REG.q(d23),LLIL_CONST.q(0x10)])'), - # vshl.i64 d17, d18, #0x7 - ('A', b'\xb2\x15\xc7\xf2', 'LLIL_SET_REG.q(d17,LLIL_LSL.q(LLIL_REG.q(d18),LLIL_CONST.b(0x7)))'), + # vshl.i64 d17, d18, #63 -- maximum immediate left shift + ('A', b'\xb2\x15\xff\xf2', 'LLIL_SET_REG.q(d17,LLIL_LSL.q(LLIL_REG.q(d18),LLIL_CONST.b(0x3F)))'), # vshl.i64 d29, d25, #0x20 ('T', b'\xe0\xef\xb9\xd5', 'LLIL_SET_REG.q(d29,LLIL_LSL.q(LLIL_REG.q(d25),LLIL_CONST.b(0x20)))'), # vshll.u32 q8, d25, #0x10 @@ -498,10 +629,46 @@ def vmlal_expected(size, unsigned): ('A', b'\x60\x04\xf8\xf2', 'LLIL_INTRINSIC([q8],__vext,[LLIL_CONST.b(0x8),LLIL_REG.o(q4),LLIL_REG.o(q8),LLIL_CONST.b(0x4)])'), # vext.8 d16, d8, d16, #4 ('T', b'\xf8\xef\x20\x04', 'LLIL_INTRINSIC([d16],__vext,[LLIL_CONST.b(0x8),LLIL_REG.q(d8),LLIL_REG.q(d16),LLIL_CONST.b(0x4)])'), + # vpadd.i8 d0, d1, d2 + ('A', b'\x12\x0b\x01\xf2', 'LLIL_INTRINSIC([d0],__vpadd,[LLIL_CONST.b(0x8),LLIL_CONST.b(0x0),LLIL_REG.q(d1),LLIL_REG.q(d2)])'), + # vpadd.i16 d0, d1, d2 + ('T', b'\x11\xef\x12\x0b', 'LLIL_INTRINSIC([d0],__vpadd,[LLIL_CONST.b(0x10),LLIL_CONST.b(0x0),LLIL_REG.q(d1),LLIL_REG.q(d2)])'), + # vpadd.i32 d0, d1, d2 + ('A', b'\x12\x0b\x21\xf2', 'LLIL_INTRINSIC([d0],__vpadd,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_REG.q(d1),LLIL_REG.q(d2)])'), + # it ne; vpaddne.f32 d31, d16, d30 + ('T', b'\x18\xbf\x40\xff\xae\xfd', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_NE,None),1,3); LLIL_INTRINSIC([d31],__vpadd,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x1),LLIL_REG.q(d16),LLIL_REG.q(d30)]); LLIL_GOTO(3)'), + # VPADD.I32 with Q=1 + ('A', b'\x54\x0b\x22\xf2', 'LLIL_UNDEF()'), # vpmin.f32 d4, d1, d19 ('T', b'\x21\xff\x23\x4f', 'LLIL_INTRINSIC([d4],__vpmin,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_REG.q(d1),LLIL_REG.q(d19)])'), - # vshl.u64 d16, d16, d17 - ('A', b'\xa0\x04\x71\xf3', 'LLIL_IF(LLIL_CMP_SLT.q(LLIL_REG.q(d17),LLIL_CONST.q(0x0)),1,3); LLIL_SET_REG.q(d16,LLIL_LSR.q(LLIL_REG.q(d16),LLIL_NEG.q(LLIL_REG.q(d17)))); LLIL_GOTO(5); LLIL_SET_REG.q(d16,LLIL_LSL.q(LLIL_REG.q(d16),LLIL_REG.q(d17))); LLIL_GOTO(5)'), + # vmovn.i16 d0, q1 + ('A', b'\x02\x02\xb2\xf3', 'LLIL_INTRINSIC([d0],__vmovn,[LLIL_CONST.b(0x10),LLIL_REG.o(q1)])'), + # vmovn.i32 d0, q1 + ('T', b'\xb6\xff\x02\x02', 'LLIL_INTRINSIC([d0],__vmovn,[LLIL_CONST.b(0x20),LLIL_REG.o(q1)])'), + # vmovn.i64 d31, q15 + ('T', b'\xfa\xff\x2e\xf2', 'LLIL_INTRINSIC([d31],__vmovn,[LLIL_CONST.b(0x40),LLIL_REG.o(q15)])'), + # it eq; vmovneq.i16 d0, q1 + ('T', b'\x08\xbf\xb2\xff\x02\x02', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_E,None),1,3); LLIL_INTRINSIC([d0],__vmovn,[LLIL_CONST.b(0x10),LLIL_REG.o(q1)]); LLIL_GOTO(3)'), + # vmovl.s8 q1, d0 + ('A', b'\x10\x2a\x88\xf2', 'LLIL_INTRINSIC([q1],__vmovl,[LLIL_CONST.b(0x8),LLIL_CONST.b(0x0),LLIL_REG.q(d0)])'), + # vmovl.s16 q1, d0 + ('T', b'\x90\xef\x10\x2a', 'LLIL_INTRINSIC([q1],__vmovl,[LLIL_CONST.b(0x10),LLIL_CONST.b(0x0),LLIL_REG.q(d0)])'), + # vmovl.u32 q1, d0 + ('T', b'\xa0\xff\x10\x2a', 'LLIL_INTRINSIC([q1],__vmovl,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x1),LLIL_REG.q(d0)])'), + # vmovl.s8 q15, d31 -- high registers with source/destination overlap + ('T', b'\xc8\xef\x3f\xea', 'LLIL_INTRINSIC([q15],__vmovl,[LLIL_CONST.b(0x8),LLIL_CONST.b(0x0),LLIL_REG.q(d31)])'), + # vmovl.u16 q0, d1 -- source is the high half of the destination + ('A', b'\x11\x0a\x90\xf3', 'LLIL_INTRINSIC([q0],__vmovl,[LLIL_CONST.b(0x10),LLIL_CONST.b(0x1),LLIL_REG.q(d1)])'), + # vmvn.i32 q15, #0xa5000000 + ('A', b'\x75\xe6\xc2\xf3', 'LLIL_SET_REG.o(q15,LLIL_OR.o(LLIL_ZX.o(LLIL_CONST.q(0x5AFFFFFF5AFFFFFF)),LLIL_LSL.o(LLIL_ZX.o(LLIL_CONST.q(0x5AFFFFFF5AFFFFFF)),LLIL_CONST.b(0x40))))'), + # vmvn.i16 d16, #0xa500 + ('T', b'\xc2\xff\x35\x0a', 'LLIL_SET_REG.q(d16,LLIL_CONST.q(0x5AFF5AFF5AFF5AFF))'), + # vmvn.i32 q15, #0xa5ffff + ('T', b'\xc2\xff\x75\xed', 'LLIL_SET_REG.o(q15,LLIL_OR.o(LLIL_ZX.o(LLIL_CONST.q(0xFF5A0000FF5A0000)),LLIL_LSL.o(LLIL_ZX.o(LLIL_CONST.q(0xFF5A0000FF5A0000)),LLIL_CONST.b(0x40))))'), + # vmvn d0, d1 + ('T', b'\xb0\xff\x81\x05', 'LLIL_SET_REG.q(d0,LLIL_NOT.q(LLIL_REG.q(d1)))'), + # vmvn q15, q8 + ('A', b'\xe0\xe5\xf0\xf3', 'LLIL_SET_REG.o(q15,LLIL_NOT.o(LLIL_REG.o(q8)))'), # vmov.i32 d16, #0 ('A', b'\x10\x00\xc0\xf2', 'LLIL_SET_REG.q(d16,LLIL_CONST.q(0x0))'), # vmov.i32 q8, #0 @@ -526,6 +693,48 @@ def vmlal_expected(size, unsigned): ('A', b'\x04\x0a\x90\xec', 'LLIL_SET_REG.d(s0,LLIL_LOAD.d(LLIL_REG.d(r0))); LLIL_SET_REG.d(s1,LLIL_LOAD.d(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x4)))); LLIL_SET_REG.d(s2,LLIL_LOAD.d(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x8)))); LLIL_SET_REG.d(s3,LLIL_LOAD.d(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0xC))))'), # vstmia r0, {s0, s1, s2, s3} ('A', b'\x04\x0a\x80\xec', 'LLIL_STORE.d(LLIL_REG.d(r0),LLIL_REG.d(s0)); LLIL_STORE.d(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x4)),LLIL_REG.d(s1)); LLIL_STORE.d(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x8)),LLIL_REG.d(s2)); LLIL_STORE.d(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0xC)),LLIL_REG.d(s3))'), + # fstmiax r0, {d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15} + ('A', b'\x21\x0b\x80\xec', 'LLIL_STORE.q(LLIL_REG.d(r0),LLIL_REG.q(d0)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x8)),LLIL_REG.q(d1)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x10)),LLIL_REG.q(d2)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x18)),LLIL_REG.q(d3)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x20)),LLIL_REG.q(d4)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x28)),LLIL_REG.q(d5)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x30)),LLIL_REG.q(d6)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x38)),LLIL_REG.q(d7)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x40)),LLIL_REG.q(d8)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x48)),LLIL_REG.q(d9)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x50)),LLIL_REG.q(d10)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x58)),LLIL_REG.q(d11)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x60)),LLIL_REG.q(d12)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x68)),LLIL_REG.q(d13)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x70)),LLIL_REG.q(d14)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x78)),LLIL_REG.q(d15))'), + # fstmiax r1!, {d2, d3} + ('T', b'\xa1\xec\x05\x2b', 'LLIL_STORE.q(LLIL_REG.d(r1),LLIL_REG.q(d2)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r1),LLIL_CONST.d(0x8)),LLIL_REG.q(d3)); LLIL_SET_REG.d(r1,LLIL_ADD.d(LLIL_REG.d(r1),LLIL_CONST.d(0x14)))'), + # fstmdbx r2!, {d4, d5} + ('A', b'\x05\x4b\x22\xed', 'LLIL_STORE.q(LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x14)),LLIL_REG.q(d4)); LLIL_STORE.q(LLIL_ADD.d(LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x14)),LLIL_CONST.d(0x8)),LLIL_REG.q(d5)); LLIL_SET_REG.d(r2,LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x14)))'), + # it eq; fstmdbxeq r2!, {d4, d5} + ('T', b'\x08\xbf\x22\xed\x05\x4b', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_E,None),1,5); LLIL_STORE.q(LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x14)),LLIL_REG.q(d4)); LLIL_STORE.q(LLIL_ADD.d(LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x14)),LLIL_CONST.d(0x8)),LLIL_REG.q(d5)); LLIL_SET_REG.d(r2,LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x14))); LLIL_GOTO(5)'), + # fstmiax sp!, {d8, d9} + ('T', b'\xad\xec\x05\x8b', 'LLIL_STORE.q(LLIL_REG.d(sp),LLIL_REG.q(d8)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(sp),LLIL_CONST.d(0x8)),LLIL_REG.q(d9)); LLIL_SET_REG.d(sp,LLIL_ADD.d(LLIL_REG.d(sp),LLIL_CONST.d(0x14)))'), + # fstmdbx sp!, {d8, d9} + ('T', b'\x2d\xed\x05\x8b', 'LLIL_STORE.q(LLIL_SUB.d(LLIL_REG.d(sp),LLIL_CONST.d(0x14)),LLIL_REG.q(d8)); LLIL_STORE.q(LLIL_ADD.d(LLIL_SUB.d(LLIL_REG.d(sp),LLIL_CONST.d(0x14)),LLIL_CONST.d(0x8)),LLIL_REG.q(d9)); LLIL_SET_REG.d(sp,LLIL_SUB.d(LLIL_REG.d(sp),LLIL_CONST.d(0x14)))'), + # fstmdbx r4!, {d15} + ('A', b'\x03\xfb\x24\xed', 'LLIL_STORE.q(LLIL_SUB.d(LLIL_REG.d(r4),LLIL_CONST.d(0xC)),LLIL_REG.q(d15)); LLIL_SET_REG.d(r4,LLIL_SUB.d(LLIL_REG.d(r4),LLIL_CONST.d(0xC)))'), + # vstmia r1!, {d2, d3} + ('T', b'\xa1\xec\x04\x2b', 'LLIL_STORE.q(LLIL_REG.d(r1),LLIL_REG.q(d2)); LLIL_STORE.q(LLIL_ADD.d(LLIL_REG.d(r1),LLIL_CONST.d(0x8)),LLIL_REG.q(d3)); LLIL_SET_REG.d(r1,LLIL_ADD.d(LLIL_REG.d(r1),LLIL_CONST.d(0x10)))'), + # vstmdb r2!, {d4, d5} + ('T', b'\x22\xed\x04\x4b', 'LLIL_STORE.q(LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x10)),LLIL_REG.q(d4)); LLIL_STORE.q(LLIL_ADD.d(LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x10)),LLIL_CONST.d(0x8)),LLIL_REG.q(d5)); LLIL_SET_REG.d(r2,LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x10)))'), + # fstmiax pc, {d0} -- A32 permits PC without writeback + ('A', b'\x03\x0b\x8f\xec', 'LLIL_STORE.q(LLIL_CONST.d(0x8),LLIL_REG.q(d0))'), + # fldmiax r0, {d0, d1} -- no writeback + ('A', b'\x05\x0b\x90\xec', 'LLIL_SET_REG.q(d0,LLIL_LOAD.q(LLIL_REG.d(r0))); LLIL_SET_REG.q(d1,LLIL_LOAD.q(LLIL_ADD.d(LLIL_REG.d(r0),LLIL_CONST.d(0x8))))'), + # fldmiax r1!, {d2, d3} -- writeback is 20 bytes, not 16 + ('T', b'\xb1\xec\x05\x2b', 'LLIL_SET_REG.q(d2,LLIL_LOAD.q(LLIL_REG.d(r1))); LLIL_SET_REG.q(d3,LLIL_LOAD.q(LLIL_ADD.d(LLIL_REG.d(r1),LLIL_CONST.d(0x8)))); LLIL_SET_REG.d(r1,LLIL_ADD.d(LLIL_REG.d(r1),LLIL_CONST.d(0x14)))'), + # fldmdbx r2!, {d4, d5} -- loads start 20 bytes below the original base + ('A', b'\x05\x4b\x32\xed', 'LLIL_SET_REG.q(d4,LLIL_LOAD.q(LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x14)))); LLIL_SET_REG.q(d5,LLIL_LOAD.q(LLIL_ADD.d(LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x14)),LLIL_CONST.d(0x8)))); LLIL_SET_REG.d(r2,LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x14)))'), + # fldmiax sp!, {d8, d9} -- the odd immediate must not decode as VPOP + ('T', b'\xbd\xec\x05\x8b', 'LLIL_SET_REG.q(d8,LLIL_LOAD.q(LLIL_REG.d(sp))); LLIL_SET_REG.q(d9,LLIL_LOAD.q(LLIL_ADD.d(LLIL_REG.d(sp),LLIL_CONST.d(0x8)))); LLIL_SET_REG.d(sp,LLIL_ADD.d(LLIL_REG.d(sp),LLIL_CONST.d(0x14)))'), + # fldmdbx sp!, {d8, d9} + ('T', b'\x3d\xed\x05\x8b', 'LLIL_SET_REG.q(d8,LLIL_LOAD.q(LLIL_SUB.d(LLIL_REG.d(sp),LLIL_CONST.d(0x14)))); LLIL_SET_REG.q(d9,LLIL_LOAD.q(LLIL_ADD.d(LLIL_SUB.d(LLIL_REG.d(sp),LLIL_CONST.d(0x14)),LLIL_CONST.d(0x8)))); LLIL_SET_REG.d(sp,LLIL_SUB.d(LLIL_REG.d(sp),LLIL_CONST.d(0x14)))'), + # fldmdbx r4!, {d15} + ('A', b'\x03\xfb\x34\xed', 'LLIL_SET_REG.q(d15,LLIL_LOAD.q(LLIL_SUB.d(LLIL_REG.d(r4),LLIL_CONST.d(0xC)))); LLIL_SET_REG.d(r4,LLIL_SUB.d(LLIL_REG.d(r4),LLIL_CONST.d(0xC)))'), + # fldmiax pc, {d0} -- A32 permits PC without writeback + ('A', b'\x03\x0b\x9f\xec', 'LLIL_SET_REG.q(d0,LLIL_LOAD.q(LLIL_CONST.d(0x8)))'), + # it eq; fldmdbxeq r2!, {d4, d5} + ('T', b'\x08\xbf\x32\xed\x05\x4b', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_E,None),1,5); LLIL_SET_REG.q(d4,LLIL_LOAD.q(LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x14)))); LLIL_SET_REG.q(d5,LLIL_LOAD.q(LLIL_ADD.d(LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x14)),LLIL_CONST.d(0x8)))); LLIL_SET_REG.d(r2,LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x14))); LLIL_GOTO(5)'), + # vldmia r1!, {d2, d3} + ('T', b'\xb1\xec\x04\x2b', 'LLIL_SET_REG.q(d2,LLIL_LOAD.q(LLIL_REG.d(r1))); LLIL_SET_REG.q(d3,LLIL_LOAD.q(LLIL_ADD.d(LLIL_REG.d(r1),LLIL_CONST.d(0x8)))); LLIL_SET_REG.d(r1,LLIL_ADD.d(LLIL_REG.d(r1),LLIL_CONST.d(0x10)))'), + # vldmdb r2!, {d4, d5} + ('T', b'\x32\xed\x04\x4b', 'LLIL_SET_REG.q(d4,LLIL_LOAD.q(LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x10)))); LLIL_SET_REG.q(d5,LLIL_LOAD.q(LLIL_ADD.d(LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x10)),LLIL_CONST.d(0x8)))); LLIL_SET_REG.d(r2,LLIL_SUB.d(LLIL_REG.d(r2),LLIL_CONST.d(0x10)))'), + # vpop {d8, d9} + ('T', b'\xbd\xec\x04\x8b', 'LLIL_SET_REG.q(d8,LLIL_POP.q()); LLIL_SET_REG.q(d9,LLIL_POP.q())'), # orr r0, r1, r3, lsl r4 ('A', b'\x13\x04\x81\xe1', 'LLIL_SET_REG.d(r0,LLIL_OR.d(LLIL_REG.d(r1),LLIL_LSL.d(LLIL_REG.d(r3),LLIL_AND.d(LLIL_REG.d(r4),LLIL_CONST.d(0xFF)))))'), @@ -603,6 +812,16 @@ def vmlal_expected(size, unsigned): ('T', b'\xe9\xff\x41\x06', 'LLIL_INTRINSIC([q8],__vmlsl,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x1),LLIL_REG.o(q8),LLIL_REG.q(d9),LLIL_REG.q(d1),LLIL_CONST.b(0x0)])'), # vmul.p8 q12, q9, q10 ('T', b'\x42\xff\xf4\x89', 'LLIL_INTRINSIC([q12],__vmul,[LLIL_CONST.b(0x8),LLIL_CONST.b(0x0),LLIL_REG.o(q9),LLIL_REG.o(q10)])'), + # vmull.p64 q2, d25, d7 -- ARMv8 polynomial product, previously decoded as .p32 + ('A', b'\x87\x4e\xa9\xf2', 'LLIL_INTRINSIC([q2],__vmull,[LLIL_CONST.b(0x40),LLIL_CONST.b(0x0),LLIL_CONST.b(0x1),LLIL_REG.q(d25),LLIL_REG.q(d7),LLIL_CONST.b(0xFF)])'), + # vmull.p64 q4, d24, d14 -- Thumb encoding from gcm_ghash_v8 + ('T', b'\xa8\xef\x8e\x8e', 'LLIL_INTRINSIC([q4],__vmull,[LLIL_CONST.b(0x40),LLIL_CONST.b(0x0),LLIL_CONST.b(0x1),LLIL_REG.q(d24),LLIL_REG.q(d14),LLIL_CONST.b(0xFF)])'), + # vmull.p8 q2, d25, d7 + ('A', b'\x87\x4e\x89\xf2', 'LLIL_INTRINSIC([q2],__vmull,[LLIL_CONST.b(0x8),LLIL_CONST.b(0x0),LLIL_CONST.b(0x1),LLIL_REG.q(d25),LLIL_REG.q(d7),LLIL_CONST.b(0xFF)])'), + # vmull.s16 q15, d31, d7[3] + ('A', b'\xef\xea\xdf\xf2', 'LLIL_INTRINSIC([q15],__vmull,[LLIL_CONST.b(0x10),LLIL_CONST.b(0x0),LLIL_CONST.b(0x0),LLIL_REG.q(d31),LLIL_REG.q(d7),LLIL_CONST.b(0x3)])'), + # vmull.u32 q14, d29, d15[1] + ('T', b'\xed\xff\xef\xca', 'LLIL_INTRINSIC([q14],__vmull,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x1),LLIL_CONST.b(0x0),LLIL_REG.q(d29),LLIL_REG.q(d15),LLIL_CONST.b(0x1)])'), # vqdmull.s32 q8, d0, d1 ('A', b'\x01\x0d\xe0\xf2', 'LLIL_INTRINSIC([q8],__vqdmull,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_REG.q(d0),LLIL_REG.q(d1)])'), # vqdmull.s32 q8, d0, d1 @@ -669,6 +888,28 @@ 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')), + # vneg.s8 d0, d1 + ('A', b'\x81\x03\xb1\xf3', 'LLIL_INTRINSIC([d0],__vneg,[LLIL_CONST.b(0x8),LLIL_CONST.b(0x0),LLIL_REG.q(d1)])'), + # vneg.s16 d0, d1 + ('A', b'\x81\x03\xb5\xf3', 'LLIL_INTRINSIC([d0],__vneg,[LLIL_CONST.b(0x10),LLIL_CONST.b(0x0),LLIL_REG.q(d1)])'), + # vneg.f32 q0, q1 + ('A', b'\xc2\x07\xb9\xf3', 'LLIL_INTRINSIC([q0],__vneg_q,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x1),LLIL_REG.o(q1)])'), + # vneg.s8 q0, q1 + ('T', b'\xb1\xff\xc2\x03', 'LLIL_INTRINSIC([q0],__vneg_q,[LLIL_CONST.b(0x8),LLIL_CONST.b(0x0),LLIL_REG.o(q1)])'), + # vneg.s16 q0, q1 + ('T', b'\xb5\xff\xc2\x03', 'LLIL_INTRINSIC([q0],__vneg_q,[LLIL_CONST.b(0x10),LLIL_CONST.b(0x0),LLIL_REG.o(q1)])'), + # vneg.f32 d0, d1 + ('T', b'\xb9\xff\x81\x07', 'LLIL_INTRINSIC([d0],__vneg,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x1),LLIL_REG.q(d1)])'), + # vneg.s32 d0, d1 + ('A', b'\x81\x03\xb9\xf3', 'LLIL_INTRINSIC([d0],__vneg,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_REG.q(d1)])'), + # vneg.s32 q15, q8 + ('T', b'\xf9\xff\xe0\xe3', 'LLIL_INTRINSIC([q15],__vneg_q,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_REG.o(q8)])'), + # vneg.f32 s0, s1 + ('A', b'\x60\x0a\xb1\xee', 'LLIL_SET_REG.d(s0,LLIL_FNEG.d(LLIL_REG.d(s1)))'), + # vneglt.f64 d1, d2 + ('A', b'\x42\x1b\xb1\xbe', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_SLT,None),1,3); LLIL_SET_REG.q(d1,LLIL_FNEG.q(LLIL_REG.q(d2))); LLIL_GOTO(3)'), + # it ne; vnegne.f64 d1, d2 + ('T', b'\x18\xbf\xb1\xee\x42\x1b', 'LLIL_IF(LLIL_FLAG_COND(LowLevelILFlagCondition.LLFC_NE,None),1,3); LLIL_SET_REG.q(d1,LLIL_FNEG.q(LLIL_REG.q(d2))); LLIL_GOTO(3)'), # 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 @@ -692,9 +933,33 @@ def vmlal_expected(size, unsigned): # 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 - ('T', b'\xb9\xff\x23\x00', 'LLIL_INTRINSIC([d0],__vcgt,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_REG.q(d19),LLIL_CONST.q(0x0)])'), + ('T', b'\xb9\xff\x23\x00', 'LLIL_INTRINSIC([d0],__vcgt,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_CONST.b(0x0),LLIL_REG.q(d19),LLIL_CONST.q(0x0)])'), # vcgt.u32 d10, d1, d18 - ('T', b'\x21\xff\x22\xa3', 'LLIL_INTRINSIC([d10],__vcgt,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x1),LLIL_REG.q(d1),LLIL_REG.q(d18)])'), + ('T', b'\x21\xff\x22\xa3', 'LLIL_INTRINSIC([d10],__vcgt,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x1),LLIL_CONST.b(0x0),LLIL_REG.q(d1),LLIL_REG.q(d18)])'), + # vclt.s8 d31, d16, d30 + ('A', b'\xa0\xf3\x4e\xf2', 'LLIL_INTRINSIC([d31],__vcgt,[LLIL_CONST.b(0x8),LLIL_CONST.b(0x0),LLIL_CONST.b(0x0),LLIL_REG.q(d30),LLIL_REG.q(d16)])'), + # vclt.u16 q15, q8, q14 + ('T', b'\x5c\xff\xe0\xe3', 'LLIL_INTRINSIC([q15],__vcgt_q,[LLIL_CONST.b(0x10),LLIL_CONST.b(0x1),LLIL_CONST.b(0x0),LLIL_REG.o(q14),LLIL_REG.o(q8)])'), + # vclt.f32 d31, d16, d30 -- the canonical encoding is vcgt.f32 d31, d30, d16 + ('A', b'\xa0\xfe\x6e\xf3', 'LLIL_INTRINSIC([d31],__vcgt,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_CONST.b(0x1),LLIL_REG.q(d30),LLIL_REG.q(d16)])'), + # vclt.s32 d0, d1, #0 + ('A', b'\x01\x02\xb9\xf3', 'LLIL_INTRINSIC([d0],__vclt,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_CONST.b(0x0),LLIL_REG.q(d1),LLIL_CONST.q(0x0)])'), + # vclt.f32 q0, q1, #0 + ('T', b'\xb9\xff\x42\x06', 'LLIL_INTRINSIC([q0],__vclt_q,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_CONST.b(0x1),LLIL_REG.o(q1),LLIL_CONST.o(0x0)])'), + # vcgt.f32 d0, d1, #0 -- positive comparisons retain their source order + ('A', b'\x01\x04\xb9\xf3', 'LLIL_INTRINSIC([d0],__vcgt,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_CONST.b(0x1),LLIL_REG.q(d1),LLIL_CONST.q(0x0)])'), + # vcgt.f32 q0, q1, #0 + ('T', b'\xb9\xff\x42\x04', 'LLIL_INTRINSIC([q0],__vcgt_q,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_CONST.b(0x1),LLIL_REG.o(q1),LLIL_CONST.o(0x0)])'), + # vcge.s8 d31, d16, d30 + ('A', b'\xbe\xf3\x40\xf2', 'LLIL_INTRINSIC([d31],__vcge,[LLIL_CONST.b(0x8),LLIL_CONST.b(0x0),LLIL_CONST.b(0x0),LLIL_REG.q(d16),LLIL_REG.q(d30)])'), + # vcge.u16 q15, q8, q14 + ('T', b'\x50\xff\xfc\xe3', 'LLIL_INTRINSIC([q15],__vcge_q,[LLIL_CONST.b(0x10),LLIL_CONST.b(0x1),LLIL_CONST.b(0x0),LLIL_REG.o(q8),LLIL_REG.o(q14)])'), + # vcge.f32 d31, d16, d30 -- floating-point comparisons must be distinct from signed integers + ('A', b'\xae\xfe\x40\xf3', 'LLIL_INTRINSIC([d31],__vcge,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_CONST.b(0x1),LLIL_REG.q(d16),LLIL_REG.q(d30)])'), + # vcge.s32 d0, d1, #0 + ('A', b'\x81\x00\xb9\xf3', 'LLIL_INTRINSIC([d0],__vcge,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_CONST.b(0x0),LLIL_REG.q(d1),LLIL_CONST.q(0x0)])'), + # vcge.f32 q0, q1, #0 + ('T', b'\xb9\xff\xc2\x04', 'LLIL_INTRINSIC([q0],__vcge_q,[LLIL_CONST.b(0x20),LLIL_CONST.b(0x0),LLIL_CONST.b(0x1),LLIL_REG.o(q1),LLIL_CONST.o(0x0)])'), # vtbl.8 d0, {d5}, d4 ('T', b'\xb5\xff\x04\x08', 'LLIL_INTRINSIC([d0],__vtbl,[LLIL_CONST.b(0x1),LLIL_REG.q(d5),LLIL_CONST.q(0x0),LLIL_CONST.q(0x0),LLIL_CONST.q(0x0),LLIL_REG.q(d4)])'), # vshl.u16 d0, d0, d1 diff --git a/arch/armv7/thumb2_disasm/arch_thumb2.cpp b/arch/armv7/thumb2_disasm/arch_thumb2.cpp index 744a423d68..712e5b3761 100644 --- a/arch/armv7/thumb2_disasm/arch_thumb2.cpp +++ b/arch/armv7/thumb2_disasm/arch_thumb2.cpp @@ -1731,6 +1731,8 @@ class Thumb2Architecture: public ArmCommonArchitecture return "__vmax"; case ARMV7_INTRIN_VMIN: return "__vmin"; + case ARMV7_INTRIN_VPADD: + return "__vpadd"; case ARMV7_INTRIN_VPMAX: return "__vpmax"; case ARMV7_INTRIN_VPMIN: @@ -1745,6 +1747,16 @@ class Thumb2Architecture: public ArmCommonArchitecture return "__vext"; case ARMV7_INTRIN_VCGT: return "__vcgt"; + case ARMV7_INTRIN_VCGT_Q: + return "__vcgt_q"; + case ARMV7_INTRIN_VCLT: + return "__vclt"; + case ARMV7_INTRIN_VCLT_Q: + return "__vclt_q"; + case ARMV7_INTRIN_VCGE: + return "__vcge"; + case ARMV7_INTRIN_VCGE_Q: + return "__vcge_q"; case ARMV7_INTRIN_VCEQ: return "__vceq"; case ARMV7_INTRIN_VTBL: @@ -1791,12 +1803,42 @@ class Thumb2Architecture: public ArmCommonArchitecture return "__vst2"; case ARMV7_INTRIN_VST4: return "__vst4"; + case ARMV7_INTRIN_VSHL_Q: + return "__vshl_q"; + case ARMV7_INTRIN_VSHL_IMM: + return "__vshl_imm"; + case ARMV7_INTRIN_VSHL_IMM_Q: + return "__vshl_imm_q"; + case ARMV7_INTRIN_VSHR_Q: + return "__vshr_q"; + case ARMV7_INTRIN_VSHRN: + return "__vshrn"; + case ARMV7_INTRIN_VTRN: + return "__vtrn"; + case ARMV7_INTRIN_VTRN_Q: + return "__vtrn_q"; + case ARMV7_INTRIN_VUZP: + return "__vuzp"; + case ARMV7_INTRIN_VUZP_Q: + return "__vuzp_q"; + case ARMV7_INTRIN_VZIP: + return "__vzip"; + case ARMV7_INTRIN_VZIP_Q: + return "__vzip_q"; + case ARMV7_INTRIN_VTST: + return "__vtst"; + case ARMV7_INTRIN_VTST_Q: + return "__vtst_q"; case ARMV7_INTRIN_VSHL: return "__vshl"; case ARMV7_INTRIN_VSHR: return "__vshr"; case ARMV7_INTRIN_VSHLL: return "__vshll"; + case ARMV7_INTRIN_VMOVL: + return "__vmovl"; + case ARMV7_INTRIN_VMOVN: + return "__vmovn"; case ARMV7_INTRIN_VBIF: return "__vbif"; case ARMV7_INTRIN_VBIT: @@ -1813,6 +1855,10 @@ class Thumb2Architecture: public ArmCommonArchitecture return "__vrecpe"; case ARMV7_INTRIN_VABS: return "__vabs"; + case ARMV7_INTRIN_VNEG: + return "__vneg"; + case ARMV7_INTRIN_VNEG_Q: + return "__vneg_q"; case ARMV7_INTRIN_VCVT_FIXED: return "__vcvt_fixed"; case ARMV7_INTRIN_VABS_Q: @@ -1845,6 +1891,8 @@ class Thumb2Architecture: public ArmCommonArchitecture return "__vmlsl"; case ARMV7_INTRIN_VMUL: return "__vmul"; + case ARMV7_INTRIN_VMULL: + return "__vmull"; case ARMV7_INTRIN_VQDMULL: return "__vqdmull"; case ARMV7_INTRIN_SSAT: @@ -2091,6 +2139,7 @@ class Thumb2Architecture: public ArmCommonArchitecture ARMV7_INTRIN_VMINNM, ARMV7_INTRIN_VMAX, ARMV7_INTRIN_VMIN, + ARMV7_INTRIN_VPADD, ARMV7_INTRIN_VPMAX, ARMV7_INTRIN_VPMIN, ARMV7_INTRIN_VREV16, @@ -2098,6 +2147,11 @@ class Thumb2Architecture: public ArmCommonArchitecture ARMV7_INTRIN_VREV64, ARMV7_INTRIN_VEXT, ARMV7_INTRIN_VCGT, + ARMV7_INTRIN_VCGT_Q, + ARMV7_INTRIN_VCLT, + ARMV7_INTRIN_VCLT_Q, + ARMV7_INTRIN_VCGE, + ARMV7_INTRIN_VCGE_Q, ARMV7_INTRIN_VCEQ, ARMV7_INTRIN_VTBL, ARMV7_INTRIN_VTBX, @@ -2120,8 +2174,23 @@ class Thumb2Architecture: public ArmCommonArchitecture ARMV7_INTRIN_VST2, ARMV7_INTRIN_VST4, ARMV7_INTRIN_VSHL, + ARMV7_INTRIN_VSHL_Q, + ARMV7_INTRIN_VSHL_IMM, + ARMV7_INTRIN_VSHL_IMM_Q, + ARMV7_INTRIN_VSHR_Q, + ARMV7_INTRIN_VSHRN, + ARMV7_INTRIN_VTRN, + ARMV7_INTRIN_VTRN_Q, + ARMV7_INTRIN_VUZP, + ARMV7_INTRIN_VUZP_Q, + ARMV7_INTRIN_VZIP, + ARMV7_INTRIN_VZIP_Q, + ARMV7_INTRIN_VTST, + ARMV7_INTRIN_VTST_Q, ARMV7_INTRIN_VSHR, ARMV7_INTRIN_VSHLL, + ARMV7_INTRIN_VMOVL, + ARMV7_INTRIN_VMOVN, ARMV7_INTRIN_VBIF, ARMV7_INTRIN_VBIT, ARMV7_INTRIN_VBSL, @@ -2130,6 +2199,8 @@ class Thumb2Architecture: public ArmCommonArchitecture ARMV7_INTRIN_VRHADD, ARMV7_INTRIN_VRECPE, ARMV7_INTRIN_VABS, + ARMV7_INTRIN_VNEG, + ARMV7_INTRIN_VNEG_Q, ARMV7_INTRIN_VCVT_FIXED, ARMV7_INTRIN_VABS_Q, ARMV7_INTRIN_VCVT_FIXED_Q, @@ -2146,6 +2217,7 @@ class Thumb2Architecture: public ArmCommonArchitecture ARMV7_INTRIN_VMLAL, ARMV7_INTRIN_VMLSL, ARMV7_INTRIN_VMUL, + ARMV7_INTRIN_VMULL, ARMV7_INTRIN_VQDMULL, ARMV7_INTRIN_SSAT, ARMV7_INTRIN_SSAT16, @@ -2422,7 +2494,6 @@ class Thumb2Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VMIN: case ARMV7_INTRIN_VPMAX: case ARMV7_INTRIN_VPMIN: - case ARMV7_INTRIN_VCGT: case ARMV7_INTRIN_VHADD: case ARMV7_INTRIN_VRHADD: return { @@ -2431,6 +2502,24 @@ class Thumb2Architecture: public ArmCommonArchitecture NameAndType("source1", Type::IntegerType(8, false)), NameAndType("source2", Type::IntegerType(8, false)), }; + case ARMV7_INTRIN_VCGT: + case ARMV7_INTRIN_VCGT_Q: + case ARMV7_INTRIN_VCGE: + case ARMV7_INTRIN_VCGE_Q: + case ARMV7_INTRIN_VCLT: + case ARMV7_INTRIN_VCLT_Q: + { + size_t vectorSize = intrinsic == ARMV7_INTRIN_VCGE_Q || intrinsic == ARMV7_INTRIN_VCGT_Q + || intrinsic == ARMV7_INTRIN_VCLT_Q ? 16 : 8; + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("is_unsigned", Type::BoolType()), + NameAndType("is_float", Type::BoolType()), + NameAndType("source1", Type::IntegerType(vectorSize, false)), + NameAndType("source2", Type::IntegerType(vectorSize, false)), + }; + } + case ARMV7_INTRIN_VPADD: case ARMV7_INTRIN_VCEQ: return { NameAndType("size", Type::IntegerType(1, false)), @@ -2439,13 +2528,15 @@ class Thumb2Architecture: public ArmCommonArchitecture NameAndType("source2", Type::IntegerType(8, false)), }; case ARMV7_INTRIN_VRECPE: + case ARMV7_INTRIN_VNEG: case ARMV7_INTRIN_VABS: case ARMV7_INTRIN_VABS_Q: + case ARMV7_INTRIN_VNEG_Q: return { NameAndType("size", Type::IntegerType(1, false)), NameAndType("is_float", Type::BoolType()), NameAndType("source", Type::IntegerType( - intrinsic == ARMV7_INTRIN_VABS_Q ? 16 : 8, false)), + (intrinsic == ARMV7_INTRIN_VABS_Q || intrinsic == ARMV7_INTRIN_VNEG_Q) ? 16 : 8, false)), }; case ARMV7_INTRIN_VCVT_FIXED: case ARMV7_INTRIN_VCVT_FIXED_Q: @@ -2550,6 +2641,39 @@ class Thumb2Architecture: public ArmCommonArchitecture NameAndType("source1", Type::IntegerType(16, false)), NameAndType("source2", Type::IntegerType(16, false)), }; + case ARMV7_INTRIN_VSHL_Q: + case ARMV7_INTRIN_VSHR_Q: + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("is_unsigned", Type::BoolType()), + NameAndType("source", Type::IntegerType(16, false)), + NameAndType("shift", Type::IntegerType(intrinsic == ARMV7_INTRIN_VSHL_Q ? 16 : 8, false)), + }; + case ARMV7_INTRIN_VTST: + case ARMV7_INTRIN_VTST_Q: + case ARMV7_INTRIN_VZIP: + case ARMV7_INTRIN_VZIP_Q: + case ARMV7_INTRIN_VUZP: + case ARMV7_INTRIN_VUZP_Q: + case ARMV7_INTRIN_VTRN: + case ARMV7_INTRIN_VTRN_Q: + { + size_t size = (intrinsic == ARMV7_INTRIN_VTRN_Q || intrinsic == ARMV7_INTRIN_VTST_Q + || intrinsic == ARMV7_INTRIN_VUZP_Q || intrinsic == ARMV7_INTRIN_VZIP_Q) ? 16 : 8; + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("source1", Type::IntegerType(size, false)), + NameAndType("source2", Type::IntegerType(size, false)), + }; + } + case ARMV7_INTRIN_VSHL_IMM: + case ARMV7_INTRIN_VSHL_IMM_Q: + case ARMV7_INTRIN_VSHRN: + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("source", Type::IntegerType(intrinsic == ARMV7_INTRIN_VSHL_IMM ? 8 : 16, false)), + NameAndType("shift", Type::IntegerType(1, false)), + }; case ARMV7_INTRIN_VRSHR: case ARMV7_INTRIN_VRSHL: case ARMV7_INTRIN_VSHL: @@ -2561,6 +2685,17 @@ class Thumb2Architecture: public ArmCommonArchitecture NameAndType("source", Type::IntegerType(8, false)), NameAndType("shift", Type::IntegerType(8, false)), }; + case ARMV7_INTRIN_VMOVL: + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("is_unsigned", Type::BoolType()), + NameAndType("source", Type::IntegerType(8, false)), + }; + case ARMV7_INTRIN_VMOVN: + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("source", Type::IntegerType(16, false)), + }; case ARMV7_INTRIN_VBIF: case ARMV7_INTRIN_VBIT: case ARMV7_INTRIN_VBSL: @@ -2655,6 +2790,15 @@ class Thumb2Architecture: public ArmCommonArchitecture NameAndType("scalar", Type::IntegerType(8, false)), NameAndType("index", Type::IntegerType(1, false)), }; + case ARMV7_INTRIN_VMULL: + return { + NameAndType("size", Type::IntegerType(1, false)), + NameAndType("is_unsigned", Type::BoolType()), + NameAndType("is_polynomial", Type::BoolType()), + NameAndType("source1", Type::IntegerType(8, false)), + NameAndType("source2", Type::IntegerType(8, false)), + NameAndType("index", Type::IntegerType(1, false)), + }; case ARMV7_INTRIN_VMUL: case ARMV7_INTRIN_VQDMULL: return { @@ -2832,6 +2976,13 @@ class Thumb2Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_SMLALD: case ARMV7_INTRIN_SMLALDX: return {Type::IntegerType(4, false), Type::IntegerType(4, false)}; + case ARMV7_INTRIN_VZIP_Q: + case ARMV7_INTRIN_VUZP_Q: + case ARMV7_INTRIN_VTRN_Q: + return {Type::IntegerType(16, false), Type::IntegerType(16, false)}; + case ARMV7_INTRIN_VZIP: + case ARMV7_INTRIN_VUZP: + case ARMV7_INTRIN_VTRN: case ARMV7_INTRIN_VLD2: return {Type::IntegerType(8, false), Type::IntegerType(8, false)}; case ARMV7_INTRIN_VLD4: @@ -2848,10 +2999,14 @@ class Thumb2Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VSRI: case ARMV7_INTRIN_VSLI: case ARMV7_INTRIN_VRADDHN: + case ARMV7_INTRIN_VTST: + case ARMV7_INTRIN_VSHL_IMM: + case ARMV7_INTRIN_VSHRN: case ARMV7_INTRIN_VSHL: case ARMV7_INTRIN_VSHR: case ARMV7_INTRIN_VMAX: case ARMV7_INTRIN_VMIN: + case ARMV7_INTRIN_VPADD: case ARMV7_INTRIN_VPMAX: case ARMV7_INTRIN_VPMIN: case ARMV7_INTRIN_VREV16: @@ -2859,6 +3014,8 @@ class Thumb2Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VREV64: case ARMV7_INTRIN_VEXT: case ARMV7_INTRIN_VCGT: + case ARMV7_INTRIN_VCGE: + case ARMV7_INTRIN_VCLT: case ARMV7_INTRIN_VCEQ: case ARMV7_INTRIN_VADD: case ARMV7_INTRIN_VSUB: @@ -2866,6 +3023,7 @@ class Thumb2Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VHADD: case ARMV7_INTRIN_VRHADD: case ARMV7_INTRIN_VRECPE: + case ARMV7_INTRIN_VNEG: case ARMV7_INTRIN_VABS: case ARMV7_INTRIN_VCVT_FIXED: case ARMV7_INTRIN_VQSHL: @@ -2876,6 +3034,7 @@ class Thumb2Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VQRSHRUN: case ARMV7_INTRIN_VQMOVN: case ARMV7_INTRIN_VQMOVUN: + case ARMV7_INTRIN_VMOVN: case ARMV7_INTRIN_VMLA: case ARMV7_INTRIN_VMLS: case ARMV7_INTRIN_VMUL: @@ -2883,8 +3042,16 @@ class Thumb2Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VBIT: case ARMV7_INTRIN_VBSL: return {Type::IntegerType(8, false)}; + case ARMV7_INTRIN_VTST_Q: + case ARMV7_INTRIN_VSHL_Q: + case ARMV7_INTRIN_VSHL_IMM_Q: + case ARMV7_INTRIN_VSHR_Q: case ARMV7_INTRIN_VABS_Q: + case ARMV7_INTRIN_VNEG_Q: case ARMV7_INTRIN_VCVT_FIXED_Q: + case ARMV7_INTRIN_VCGE_Q: + case ARMV7_INTRIN_VCGT_Q: + case ARMV7_INTRIN_VCLT_Q: return {Type::IntegerType(16, false)}; case ARMV7_INTRIN_VABAL: case ARMV7_INTRIN_VABDL: @@ -2894,6 +3061,8 @@ class Thumb2Architecture: public ArmCommonArchitecture case ARMV7_INTRIN_VMLAL: case ARMV7_INTRIN_VMLSL: case ARMV7_INTRIN_VQDMULL: + case ARMV7_INTRIN_VMOVL: + case ARMV7_INTRIN_VMULL: return {Type::IntegerType(16, false)}; case ARMV7_INTRIN_MSR: // return {Type::IntegerType(4, false)}; diff --git a/arch/armv7/thumb2_disasm/il_thumb2.cpp b/arch/armv7/thumb2_disasm/il_thumb2.cpp index a4d9980b36..5111324cbe 100644 --- a/arch/armv7/thumb2_disasm/il_thumb2.cpp +++ b/arch/armv7/thumb2_disasm/il_thumb2.cpp @@ -719,6 +719,50 @@ static bool VectorMultiplyIntrinsic(LowLevelILFunction& il, decomp_result* instr return true; } +static bool VectorMultiplyLong(LowLevelILFunction& il, decomp_result* instr) +{ + if (instr->format->operandCount != 3 || !IS_FIELD_PRESENT(instr, FIELD_esize) + || !IS_FIELD_PRESENT(instr, FIELD_unsigned)) + return false; + + uint32_t dst = GetRegisterOperand(instr, 0); + uint32_t src1 = GetRegisterOperand(instr, 1); + const instruction_operand_format& src2 = instr->format->operands[2]; + bool scalar = src2.type == OPERAND_FORMAT_REG_INDEX; + if (dst == REG_INVALID || src1 == REG_INVALID || GetRegisterSize(instr, 0) != 16 + || GetRegisterSize(instr, 1) != 8 || RegisterSizeFromPrefix(src2.prefix) != 8 + || (!scalar && src2.type != OPERAND_FORMAT_REG_FP) || !IS_FIELD_PRESENT(instr, src2.field0)) + return false; + + uint32_t elementSize = instr->fields[FIELD_esize]; + bool polynomial = !scalar && IS_FIELD_PRESENT(instr, FIELD_op) && instr->fields[FIELD_op]; + if (polynomial ? (elementSize != 8 && elementSize != 64) : (elementSize != 8 && elementSize != 16 && elementSize != 32)) + return false; + + uint32_t index = 0xff; + if (scalar) + { + if (elementSize == 8 || !IS_FIELD_PRESENT(instr, src2.field1)) + return false; + index = instr->fields[src2.field1]; + if (index >= 64 / elementSize) + return false; + } + uint32_t src2Reg = GetRegisterByIndex(instr->fields[src2.field0], src2.prefix); + il.AddInstruction(il.Intrinsic( + { RegisterOrFlag::Register(dst) }, + ARMV7_INTRIN_VMULL, + { + il.Const(1, elementSize), + il.Const(1, instr->fields[FIELD_unsigned] ? 1 : 0), + il.Const(1, polynomial ? 1 : 0), + il.Register(8, src1), + il.Register(8, src2Reg), + il.Const(1, index), + })); + return true; +} + static bool VectorSaturatingDoublingMultiplyLongIntrinsic(LowLevelILFunction& il, decomp_result* instr) { if (!IS_FIELD_PRESENT(instr, FIELD_esize) || instr->format->operandCount < 3) @@ -786,131 +830,118 @@ static void VectorTableLookup(LowLevelILFunction& il, decomp_result* instr) inputs)); } -static void VectorShiftLeft(LowLevelILFunction& il, decomp_result* instr) +static void VectorShift(LowLevelILFunction& il, decomp_result* instr) { - if (!IS_FIELD_PRESENT(instr, FIELD_esize)) + bool narrow = instr->mnem == ARMV7_VSHRN; + bool right = instr->mnem == ARMV7_VSHR; + if (instr->format->operandCount != 3 || !IS_FIELD_PRESENT(instr, FIELD_esize)) { il.AddInstruction(il.Unimplemented()); return; } - uint32_t dest = GetRegisterOperand(instr, 0); - uint32_t source = GetRegisterOperand(instr, 1); - if (dest == armv7::REG_INVALID || source == armv7::REG_INVALID) - { - il.AddInstruction(il.Unimplemented()); - return; - } - - size_t regSize = GetRegisterSize(instr, 0); - size_t elementSize = instr->fields[FIELD_esize] / 8; - if (regSize == 0 || elementSize == 0 || elementSize > regSize || instr->format->operandCount < 3) + size_t size = GetRegisterSize(instr, 0); + // VSHRN's decoded esize is the destination width; intrinsic size is the source width. + uint32_t elementBits = instr->fields[FIELD_esize] * (narrow ? 2 : 1); + if (dest == REG_INVALID || GetRegisterOperand(instr, 1) == REG_INVALID + || (size != 8 && size != 16) || GetRegisterSize(instr, 1) != (narrow ? 16 : size) + || (elementBits != 8 && elementBits != 16 && elementBits != 32 && elementBits != 64) + || (narrow && (size != 8 || elementBits == 8))) { il.AddInstruction(il.Unimplemented()); return; } + uint32_t intrinsic; + std::vector inputs = { il.Const(1, elementBits) }; if (instr->format->operands[2].type == OPERAND_FORMAT_IMM) { - uint64_t shift = instr->fields[instr->format->operands[2].field0]; - if (elementSize == regSize) + uint32_t shift = instr->fields[instr->format->operands[2].field0]; + if ((right || narrow) ? (shift == 0 || shift > elementBits / (narrow ? 2 : 1)) : shift >= elementBits) { - il.AddInstruction(WriteILOperand(il, instr, 0, - il.ShiftLeft(regSize, il.Register(regSize, source), il.Const(1, shift)), regSize)); + il.AddInstruction(il.Unimplemented()); return; } - - il.AddInstruction(il.Intrinsic( - { RegisterOrFlag::Register(dest) }, - ARMV7_INTRIN_VSHL, + if (right) + { + if (!IS_FIELD_PRESENT(instr, FIELD_type)) { - il.Const(1, instr->fields[FIELD_esize]), - il.Const(1, (IS_FIELD_PRESENT(instr, FIELD_unsigned) && instr->fields[FIELD_unsigned]) ? 1 : 0), - ReadILOperand(il, instr, 1, regSize), - il.Const(regSize, shift), - })); - return; + il.AddInstruction(il.Unimplemented()); + return; + } + inputs.push_back(il.Const(1, instr->fields[FIELD_type] == 3 ? 1 : 0)); + } + if (!narrow && size == 8 && elementBits == 64) + { + // A single D64 lane is a scalar shift; keep it visible to constant propagation. + ExprId source = ReadILOperand(il, instr, 1, 8); + ExprId value; + if (!right) + value = il.ShiftLeft(8, source, il.Const(1, shift)); + else if (instr->fields[FIELD_type] != 3) + // ASR by 64 sign-fills, but LLIL masks 64-bit shift counts. + value = il.ArithShiftRight(8, source, il.Const(1, shift == 64 ? 63 : shift)); + else + value = shift == 64 ? il.Const(8, 0) : il.LogicalShiftRight(8, source, il.Const(1, shift)); + il.AddInstruction(il.SetRegister(8, dest, value)); + return; + } + inputs.push_back(ReadILOperand(il, instr, 1, narrow ? 16 : size)); + inputs.push_back(il.Const(right ? 8 : 1, shift)); + intrinsic = narrow ? ARMV7_INTRIN_VSHRN : right + ? (size == 16 ? ARMV7_INTRIN_VSHR_Q : ARMV7_INTRIN_VSHR) + : (size == 16 ? ARMV7_INTRIN_VSHL_IMM_Q : ARMV7_INTRIN_VSHL_IMM); } - - if (!IS_FIELD_PRESENT(instr, FIELD_unsigned)) + else { - il.AddInstruction(il.Unimplemented()); - return; - } - - il.AddInstruction(il.Intrinsic( - { RegisterOrFlag::Register(dest) }, - ARMV7_INTRIN_VSHL, + if (right || narrow || !IS_FIELD_PRESENT(instr, FIELD_unsigned) + || GetRegisterOperand(instr, 2) == REG_INVALID || GetRegisterSize(instr, 2) != size) { - il.Const(1, instr->fields[FIELD_esize]), - il.Const(1, instr->fields[FIELD_unsigned] ? 1 : 0), - ReadILOperand(il, instr, 1, regSize), - ReadILOperand(il, instr, 2, regSize), - })); + il.AddInstruction(il.Unimplemented()); + return; + } + intrinsic = size == 16 ? ARMV7_INTRIN_VSHL_Q : ARMV7_INTRIN_VSHL; + inputs.push_back(il.Const(1, instr->fields[FIELD_unsigned] ? 1 : 0)); + inputs.push_back(ReadILOperand(il, instr, 1, size)); + inputs.push_back(ReadILOperand(il, instr, 2, size)); + } + il.AddInstruction(il.Intrinsic({ RegisterOrFlag::Register(dest) }, intrinsic, inputs)); } -static void VectorShiftRight(LowLevelILFunction& il, decomp_result* instr) +static void VectorBitSelect(LowLevelILFunction& il, decomp_result* instr) { - if (!IS_FIELD_PRESENT(instr, FIELD_esize) || (!IS_FIELD_PRESENT(instr, FIELD_unsigned) && !IS_FIELD_PRESENT(instr, FIELD_type))) - { - il.AddInstruction(il.Unimplemented()); - return; - } - uint32_t dest = GetRegisterOperand(instr, 0); - if (dest == armv7::REG_INVALID || GetRegisterOperand(instr, 1) == armv7::REG_INVALID) + if (dest == armv7::REG_INVALID || instr->format->operandCount < 3) { il.AddInstruction(il.Unimplemented()); return; } size_t regSize = GetRegisterSize(instr, 0); - size_t elementSize = instr->fields[FIELD_esize] / 8; - if (regSize == 0 || elementSize == 0 || elementSize > regSize || instr->format->operandCount < 3) + if (regSize != 8 && regSize != 16) { il.AddInstruction(il.Unimplemented()); return; } - uint64_t shift = instr->fields[instr->format->operands[2].field0]; - bool isUnsigned = IS_FIELD_PRESENT(instr, FIELD_unsigned) - ? instr->fields[FIELD_unsigned] != 0 - : instr->fields[FIELD_type] != 2; - il.AddInstruction(il.Intrinsic( - { RegisterOrFlag::Register(dest) }, - ARMV7_INTRIN_VSHR, - { - il.Const(1, instr->fields[FIELD_esize]), - il.Const(1, isUnsigned ? 1 : 0), - ReadILOperand(il, instr, 1, regSize), - il.Const(regSize, shift), - })); -} - -static void VectorBitSelect(LowLevelILFunction& il, decomp_result* instr, uint32_t intrinsic) -{ - uint32_t dest = GetRegisterOperand(instr, 0); - if (dest == armv7::REG_INVALID || instr->format->operandCount < 3) + ExprId destination = il.Register(regSize, dest); + ExprId source1 = ReadILOperand(il, instr, 1, regSize); + ExprId source2 = ReadILOperand(il, instr, 2, regSize); + ExprId setValue = source1; + ExprId clearValue = destination; + ExprId mask = source2; + if (instr->mnem == ARMV7_VBIF) { - il.AddInstruction(il.Unimplemented()); - return; + setValue = destination; + clearValue = source1; } - - size_t regSize = GetRegisterSize(instr, 0); - if (regSize == 0) + else if (instr->mnem == ARMV7_VBSL) { - il.AddInstruction(il.Unimplemented()); - return; + mask = destination; + clearValue = source2; } - - il.AddInstruction(il.Intrinsic( - { RegisterOrFlag::Register(dest) }, - intrinsic, - { - il.Register(regSize, dest), - ReadILOperand(il, instr, 1, regSize), - ReadILOperand(il, instr, 2, regSize), - })); + il.AddInstruction(il.SetRegister(regSize, dest, + il.Or(regSize, il.And(regSize, setValue, mask), il.And(regSize, clearValue, il.Not(regSize, mask))))); } static void RoundedVectorShift(LowLevelILFunction& il, decomp_result* instr, uint32_t intrinsic) @@ -1354,6 +1385,63 @@ static void VectorWideningAdd(LowLevelILFunction& il, decomp_result* instr, uint })); } +static void VectorMoveLong(LowLevelILFunction& il, decomp_result* instr) +{ + if (instr->format->operandCount != 2 || !IS_FIELD_PRESENT(instr, FIELD_esize) + || !IS_FIELD_PRESENT(instr, FIELD_unsigned)) + { + il.AddInstruction(il.Unimplemented()); + return; + } + + uint32_t dst = GetRegisterOperand(instr, 0); + uint32_t src = GetRegisterOperand(instr, 1); + size_t elementSize = instr->fields[FIELD_esize]; + if (dst == REG_INVALID || src == REG_INVALID || GetRegisterSize(instr, 0) != 16 + || GetRegisterSize(instr, 1) != 8 || (elementSize != 8 && elementSize != 16 && elementSize != 32)) + { + il.AddInstruction(il.Unimplemented()); + return; + } + + il.AddInstruction(il.Intrinsic( + { RegisterOrFlag::Register(dst) }, + ARMV7_INTRIN_VMOVL, + { + il.Const(1, elementSize), + il.Const(1, instr->fields[FIELD_unsigned] ? 1 : 0), + il.Register(8, src), + })); +} + +static void VectorMoveNarrow(LowLevelILFunction& il, decomp_result* instr) +{ + if (instr->format->operandCount != 2 || !IS_FIELD_PRESENT(instr, FIELD_esize)) + { + il.AddInstruction(il.Unimplemented()); + return; + } + + uint32_t dst = GetRegisterOperand(instr, 0); + uint32_t src = GetRegisterOperand(instr, 1); + // The decoder's esize describes the narrowed lanes; the intrinsic takes the source lane size. + size_t elementSize = instr->fields[FIELD_esize] * 2; + if (dst == REG_INVALID || src == REG_INVALID || GetRegisterSize(instr, 0) != 8 + || GetRegisterSize(instr, 1) != 16 || (elementSize != 16 && elementSize != 32 && elementSize != 64)) + { + il.AddInstruction(il.Unimplemented()); + return; + } + + il.AddInstruction(il.Intrinsic( + { RegisterOrFlag::Register(dst) }, + ARMV7_INTRIN_VMOVN, + { + il.Const(1, elementSize), + il.Register(16, src), + })); +} + static void VectorRoundingAddNarrow(LowLevelILFunction& il, decomp_result* instr) { if (!IS_FIELD_PRESENT(instr, FIELD_esize) || instr->format->operandCount < 3) @@ -1668,9 +1756,9 @@ static void VectorCompareEqual(LowLevelILFunction& il, decomp_result* instr) })); } -static void VectorCompareGreaterThan(LowLevelILFunction& il, decomp_result* instr) +static void VectorCompareOrdered(LowLevelILFunction& il, decomp_result* instr, uint32_t intrinsic, uint32_t wideIntrinsic) { - if (instr->format->operandCount < 3 || !IS_FIELD_PRESENT(instr, FIELD_esize)) + if (instr->format->operandCount != 3 || !IS_FIELD_PRESENT(instr, FIELD_esize)) { il.AddInstruction(il.Unimplemented()); return; @@ -1678,36 +1766,30 @@ static void VectorCompareGreaterThan(LowLevelILFunction& il, decomp_result* inst size_t regSize = GetRegisterSize(instr, 0); size_t elementSize = instr->fields[FIELD_esize] / 8; - if (regSize == 0 || elementSize == 0) + uint32_t dst = GetRegisterOperand(instr, 0); + uint32_t src1 = GetRegisterOperand(instr, 1); + bool compareZero = instr->format->operands[2].type == OPERAND_FORMAT_ZERO; + if ((regSize != 8 && regSize != 16) || (elementSize != 1 && elementSize != 2 && elementSize != 4) + || dst == REG_INVALID || src1 == REG_INVALID || GetRegisterSize(instr, 1) != regSize + || (!compareZero && (GetRegisterOperand(instr, 2) == REG_INVALID || GetRegisterSize(instr, 2) != regSize))) { il.AddInstruction(il.Unimplemented()); return; } - ExprId rhs; - if (instr->format->operands[2].type == OPERAND_FORMAT_ZERO) - { - rhs = il.Const(regSize, 0); - } - else - { - size_t rhsSize = GetRegisterSize(instr, 2); - if (rhsSize == 0) - { - il.AddInstruction(il.Unimplemented()); - return; - } - rhs = il.Register(rhsSize, GetRegisterOperand(instr, 2)); - } - - bool isUnsigned = IS_FIELD_PRESENT(instr, FIELD_unsigned) && instr->fields[FIELD_unsigned] != 0; + bool isFloat = (instr->format->operationFlags & INSTR_FORMAT_FLAG_F32) + || (IS_FIELD_PRESENT(instr, FIELD_F) && instr->fields[FIELD_F]); + bool isUnsigned = IS_FIELD_PRESENT(instr, FIELD_unsigned) && instr->fields[FIELD_unsigned]; + ExprId lhs = il.Register(regSize, src1); + ExprId rhs = compareZero ? il.Const(regSize, 0) : ReadILOperand(il, instr, 2, regSize); il.AddInstruction(il.Intrinsic( - { RegisterOrFlag::Register(GetRegisterOperand(instr, 0)) }, - ARMV7_INTRIN_VCGT, + { RegisterOrFlag::Register(dst) }, + regSize == 16 ? wideIntrinsic : intrinsic, { il.Const(1, elementSize * 8), il.Const(1, isUnsigned ? 1 : 0), - il.Register(GetRegisterSize(instr, 1), GetRegisterOperand(instr, 1)), + il.Const(1, isFloat ? 1 : 0), + lhs, rhs, })); } @@ -1795,7 +1877,8 @@ static void VfpLoadStoreMultiple(LowLevelILFunction& il, decomp_result* instr, b uint32_t regs = instr->fields[FIELD_regs]; bool increment = IS_FIELD_PRESENT(instr, FIELD_add) ? instr->fields[FIELD_add] != 0 : ((instr->mnem == armv7::ARMV7_VLDMIA) || (instr->mnem == armv7::ARMV7_VSTMIA)); - size_t totalSize = regs * regSize; + // ARM DDI 0406C.d A8.8.51: include the unused trailing word for FLDM*X/FSTM*X. + size_t totalSize = instr->fields[FIELD_imm32]; ExprId base = il.Register(4, baseReg); ExprId start = increment ? base : il.Sub(4, base, il.Const(4, totalSize)); @@ -3990,6 +4073,24 @@ bool GetLowLevelILForNEONInstruction(Architecture* arch, LowLevelILFunction& il, } break; } + case armv7::ARMV7_VPADD: + { + bool isFloat = instr->format->operationFlags & INSTR_FORMAT_FLAG_F32; + uint32_t elementSize = IS_FIELD_PRESENT(instr, FIELD_esize) ? instr->fields[FIELD_esize] : 0; + if (instr->format->operandCount != 3 || GetRegisterOperand(instr, 0) == REG_INVALID + || GetRegisterOperand(instr, 1) == REG_INVALID || GetRegisterOperand(instr, 2) == REG_INVALID + || GetRegisterSize(instr, 0) != 8 || GetRegisterSize(instr, 1) != 8 || GetRegisterSize(instr, 2) != 8 + || (isFloat ? elementSize != 32 : (elementSize != 8 && elementSize != 16 && elementSize != 32))) + { + il.AddInstruction(il.Unimplemented()); + break; + } + il.AddInstruction(il.Intrinsic( + { RegisterOrFlag::Register(GetRegisterOperand(instr, 0)) }, ARMV7_INTRIN_VPADD, + { il.Const(1, elementSize), il.Const(1, isFloat ? 1 : 0), + ReadILOperand(il, instr, 1, 8), ReadILOperand(il, instr, 2, 8) })); + break; + } case armv7::ARMV7_VADD: if (instr->format->operationFlags & (INSTR_FORMAT_FLAG_F32 | INSTR_FORMAT_FLAG_F64)) { @@ -4002,19 +4103,21 @@ bool GetLowLevelILForNEONInstruction(Architecture* arch, LowLevelILFunction& il, } break; case armv7::ARMV7_VBIF: - VectorBitSelect(il, instr, ARMV7_INTRIN_VBIF); - break; case armv7::ARMV7_VBIT: - VectorBitSelect(il, instr, ARMV7_INTRIN_VBIT); - break; case armv7::ARMV7_VBSL: - VectorBitSelect(il, instr, ARMV7_INTRIN_VBSL); + VectorBitSelect(il, instr); break; case armv7::ARMV7_VCEQ: VectorCompareEqual(il, instr); break; case armv7::ARMV7_VCGT: - VectorCompareGreaterThan(il, instr); + VectorCompareOrdered(il, instr, ARMV7_INTRIN_VCGT, ARMV7_INTRIN_VCGT_Q); + break; + case armv7::ARMV7_VCGE: + VectorCompareOrdered(il, instr, ARMV7_INTRIN_VCGE, ARMV7_INTRIN_VCGE_Q); + break; + case armv7::ARMV7_VCLT: + VectorCompareOrdered(il, instr, ARMV7_INTRIN_VCLT, ARMV7_INTRIN_VCLT_Q); break; case armv7::ARMV7_VDUP: VectorDuplicate(il, instr); @@ -4023,14 +4126,209 @@ bool GetLowLevelILForNEONInstruction(Architecture* arch, LowLevelILFunction& il, il.AddInstruction(WriteArithOperand( il, instr, il.And(GetRegisterSize(instr, 0), ReadILOperand(il, instr, 1), ReadILOperand(il, instr, 2)))); break; + case armv7::ARMV7_VBIC: + { + size_t size = GetRegisterSize(instr, 0); + ExprId source, mask; + if (instr->format->operandCount == 2 && instr->format->operands[1].type == OPERAND_FORMAT_IMM64) + { + // The decoder stores the element immediate for display; replicate it across 64 bits. + uint64_t imm64 = instr->fields[FIELD_imm64l]; + if (instr->fields[FIELD_dt] == VFP_DATA_SIZE_I16) + imm64 |= imm64 << 16; + imm64 |= imm64 << 32; + source = ReadILOperand(il, instr, 0); + mask = il.Const(8, imm64); + if (size == 16) + { + mask = il.ZeroExtend(16, mask); + mask = il.Or(16, mask, il.ShiftLeft(16, mask, il.Const(1, 64))); + } + } + else if (instr->format->operandCount == 3) + { + source = ReadILOperand(il, instr, 1); + mask = ReadILOperand(il, instr, 2); + } + else + { + il.AddInstruction(il.Unimplemented()); + break; + } + il.AddInstruction(WriteILOperand(il, instr, 0, il.And(size, source, il.Not(size, mask)))); + break; + } case armv7::ARMV7_VEOR: il.AddInstruction(WriteArithOperand( il, instr, il.Xor(GetRegisterSize(instr, 0), ReadILOperand(il, instr, 1), ReadILOperand(il, instr, 2)))); break; + case armv7::ARMV7_VMVN: + { + size_t size = GetRegisterSize(instr, 0); + if (instr->format->operandCount != 2 || GetRegisterOperand(instr, 0) == REG_INVALID + || (size != 8 && size != 16)) + { + il.AddInstruction(il.Unimplemented()); + break; + } + + ExprId value; + if (instr->format->operands[1].type == OPERAND_FORMAT_IMM64) + { + // The decoder stores the element immediate for display; replicate before complementing. + uint64_t imm64 = instr->fields[FIELD_imm64l]; + if (instr->fields[FIELD_dt] == VFP_DATA_SIZE_I16) + imm64 |= imm64 << 16; + imm64 |= imm64 << 32; + value = il.Const(8, ~imm64); + if (size == 16) + { + value = il.ZeroExtend(16, value); + value = il.Or(16, value, il.ShiftLeft(16, value, il.Const(1, 64))); + } + } + else if (instr->format->operands[1].type == OPERAND_FORMAT_REG_FP && GetRegisterSize(instr, 1) == size) + { + value = il.Not(size, ReadILOperand(il, instr, 1, size)); + } + else + { + il.AddInstruction(il.Unimplemented()); + break; + } + il.AddInstruction(WriteILOperand(il, instr, 0, value)); + break; + } + case armv7::ARMV7_VTST: + { + size_t size = GetRegisterSize(instr, 0); + if (instr->format->operandCount != 3 || !IS_FIELD_PRESENT(instr, FIELD_esize) + || (size != 8 && size != 16) || GetRegisterSize(instr, 1) != size || GetRegisterSize(instr, 2) != size + || GetRegisterOperand(instr, 0) == REG_INVALID || GetRegisterOperand(instr, 1) == REG_INVALID + || GetRegisterOperand(instr, 2) == REG_INVALID) + { + il.AddInstruction(il.Unimplemented()); + break; + } + uint32_t elementBits = instr->fields[FIELD_esize]; + if (elementBits != 8 && elementBits != 16 && elementBits != 32) + { + il.AddInstruction(il.Unimplemented()); + break; + } + il.AddInstruction(il.Intrinsic( + { RegisterOrFlag::Register(GetRegisterOperand(instr, 0)) }, size == 16 ? ARMV7_INTRIN_VTST_Q : ARMV7_INTRIN_VTST, + { il.Const(1, elementBits), ReadILOperand(il, instr, 1, size), ReadILOperand(il, instr, 2, size) })); + break; + } + case armv7::ARMV7_VTRN: + case armv7::ARMV7_VUZP: + case armv7::ARMV7_VZIP: + { + size_t size = GetRegisterSize(instr, 0); + uint32_t first = GetRegisterOperand(instr, 0); + uint32_t second = GetRegisterOperand(instr, 1); + if (instr->format->operandCount != 2 || !IS_FIELD_PRESENT(instr, FIELD_esize) + || (size != 8 && size != 16) || GetRegisterSize(instr, 1) != size + || first == REG_INVALID || second == REG_INVALID) + { + il.AddInstruction(il.Unimplemented()); + break; + } + uint32_t elementBits = instr->fields[FIELD_esize]; + if ((elementBits != 8 && elementBits != 16 && elementBits != 32) + || (instr->mnem != ARMV7_VTRN && size == 8 && elementBits == 32)) + { + il.AddInstruction(il.Unimplemented()); + break; + } + if (first == second) + { + // The ISA specifies UNKNOWN contents for identical operands. + il.AddInstruction(il.SetRegister(size, first, il.Undefined())); + break; + } + uint32_t intrinsic = size == 16 ? ARMV7_INTRIN_VTRN_Q : ARMV7_INTRIN_VTRN; + if (instr->mnem == ARMV7_VUZP) + intrinsic = size == 16 ? ARMV7_INTRIN_VUZP_Q : ARMV7_INTRIN_VUZP; + else if (instr->mnem == ARMV7_VZIP) + intrinsic = size == 16 ? ARMV7_INTRIN_VZIP_Q : ARMV7_INTRIN_VZIP; + il.AddInstruction(il.Intrinsic( + { RegisterOrFlag::Register(first), RegisterOrFlag::Register(second) }, + intrinsic, + { il.Const(1, elementBits), ReadILOperand(il, instr, 0, size), ReadILOperand(il, instr, 1, size) })); + break; + } + case armv7::ARMV7_VSWP: + { + size_t size = GetRegisterSize(instr, 0); + if (instr->format->operandCount != 2 || (size != 8 && size != 16) + || GetRegisterOperand(instr, 0) == REG_INVALID || GetRegisterOperand(instr, 1) == REG_INVALID + || GetRegisterSize(instr, 1) != size) + { + il.AddInstruction(il.Unimplemented()); + break; + } + il.AddInstruction(il.SetRegister(size, LLIL_TEMP(0), ReadILOperand(il, instr, 0, size))); + il.AddInstruction(WriteILOperand(il, instr, 0, ReadILOperand(il, instr, 1, size))); + il.AddInstruction(WriteILOperand(il, instr, 1, il.Register(size, LLIL_TEMP(0)))); + break; + } + case armv7::ARMV7_VORN: + { + size_t size = GetRegisterSize(instr, 0); + if (instr->format->operandCount != 3 || (size != 8 && size != 16) + || GetRegisterOperand(instr, 0) == REG_INVALID || GetRegisterOperand(instr, 1) == REG_INVALID + || GetRegisterOperand(instr, 2) == REG_INVALID + || GetRegisterSize(instr, 1) != size || GetRegisterSize(instr, 2) != size) + { + il.AddInstruction(il.Unimplemented()); + break; + } + il.AddInstruction(WriteILOperand(il, instr, 0, + il.Or(size, ReadILOperand(il, instr, 1, size), il.Not(size, ReadILOperand(il, instr, 2, size))))); + break; + } case armv7::ARMV7_VORR: - il.AddInstruction(WriteArithOperand( - il, instr, il.Or(GetRegisterSize(instr, 0), ReadILOperand(il, instr, 1), ReadILOperand(il, instr, 2)))); + { + size_t size = GetRegisterSize(instr, 0); + if (GetRegisterOperand(instr, 0) == REG_INVALID || (size != 8 && size != 16)) + { + il.AddInstruction(il.Unimplemented()); + break; + } + + ExprId source, mask; + if (instr->format->operandCount == 2 && instr->format->operands[1].type == OPERAND_FORMAT_IMM64) + { + // The decoder stores the element immediate for display; replicate across the vector. + uint64_t imm64 = instr->fields[FIELD_imm64l]; + if (instr->fields[FIELD_dt] == VFP_DATA_SIZE_I16) + imm64 |= imm64 << 16; + imm64 |= imm64 << 32; + source = ReadILOperand(il, instr, 0, size); + mask = il.Const(8, imm64); + if (size == 16) + { + mask = il.ZeroExtend(16, mask); + mask = il.Or(16, mask, il.ShiftLeft(16, mask, il.Const(1, 64))); + } + } + else if (instr->format->operandCount == 3 && GetRegisterOperand(instr, 1) != REG_INVALID + && GetRegisterOperand(instr, 2) != REG_INVALID + && GetRegisterSize(instr, 1) == size && GetRegisterSize(instr, 2) == size) + { + source = ReadILOperand(il, instr, 1, size); + mask = ReadILOperand(il, instr, 2, size); + } + else + { + il.AddInstruction(il.Unimplemented()); + break; + } + il.AddInstruction(WriteILOperand(il, instr, 0, il.Or(size, source, mask))); break; + } case armv7::ARMV7_VQADD: SaturatingVectorAdd(il, instr); break; @@ -4052,6 +4350,12 @@ bool GetLowLevelILForNEONInstruction(Architecture* arch, LowLevelILFunction& il, case armv7::ARMV7_VADDW: VectorWideningAdd(il, instr, ARMV7_INTRIN_VADDW); break; + case armv7::ARMV7_VMOVL: + VectorMoveLong(il, instr); + break; + case armv7::ARMV7_VMOVN: + VectorMoveNarrow(il, instr); + break; case armv7::ARMV7_VRADDHN: VectorRoundingAddNarrow(il, instr); break; @@ -4099,13 +4403,14 @@ bool GetLowLevelILForNEONInstruction(Architecture* arch, LowLevelILFunction& il, ShiftRightAccumulateOrInsert(il, instr, ARMV7_INTRIN_VSLI); break; case armv7::ARMV7_VSHL: - VectorShiftLeft(il, instr); + case armv7::ARMV7_VSHRN: + VectorShift(il, instr); break; case armv7::ARMV7_VSHLL: VectorShiftLeftLong(il, instr); break; case armv7::ARMV7_VSHR: - VectorShiftRight(il, instr); + VectorShift(il, instr); break; case armv7::ARMV7_VTBL: case armv7::ARMV7_VTBX: @@ -4272,6 +4577,10 @@ bool GetLowLevelILForNEONInstruction(Architecture* arch, LowLevelILFunction& il, if (!VectorSaturatingDoublingMultiplyLongIntrinsic(il, instr)) il.AddInstruction(il.Unimplemented()); break; + case armv7::ARMV7_VMULL: + if (!VectorMultiplyLong(il, instr)) + il.AddInstruction(il.Unimplemented()); + break; case armv7::ARMV7_VNMUL: if (instr->format->operationFlags & (INSTR_FORMAT_FLAG_F32 | INSTR_FORMAT_FLAG_F64)) { @@ -4306,9 +4615,23 @@ bool GetLowLevelILForNEONInstruction(Architecture* arch, LowLevelILFunction& il, il.AddInstruction( WriteArithOperand(il, instr, il.FloatNeg(GetRegisterSize(instr, 0), ReadILOperand(il, instr, 1)))); } + else if (IS_FIELD_PRESENT(instr, FIELD_esize) && IS_FIELD_PRESENT(instr, FIELD_floating_point) + && (instr->fields[FIELD_esize] == 32 || (!instr->fields[FIELD_floating_point] + && (instr->fields[FIELD_esize] == 8 || instr->fields[FIELD_esize] == 16))) + && instr->format->operandCount == 2 && GetRegisterOperand(instr, 0) != REG_INVALID + && GetRegisterOperand(instr, 1) != REG_INVALID + && (GetRegisterSize(instr, 0) == 8 || GetRegisterSize(instr, 0) == 16) + && GetRegisterSize(instr, 0) == GetRegisterSize(instr, 1)) + { + size_t size = GetRegisterSize(instr, 0); + il.AddInstruction(il.Intrinsic( + { RegisterOrFlag::Register(GetRegisterOperand(instr, 0)) }, + size == 16 ? ARMV7_INTRIN_VNEG_Q : ARMV7_INTRIN_VNEG, + { il.Const(1, instr->fields[FIELD_esize]), il.Const(1, instr->fields[FIELD_floating_point]), + ReadILOperand(il, instr, 1, size) })); + } else { - // Non scalar unsupported. il.AddInstruction(il.Unimplemented()); } break; @@ -4663,6 +4986,8 @@ bool GetLowLevelILForNEONInstruction(Architecture* arch, LowLevelILFunction& il, case armv7::ARMV7_VSTM: case armv7::ARMV7_VSTMDB: case armv7::ARMV7_VSTMIA: + case armv7::ARMV7_FSTMDBX: + case armv7::ARMV7_FSTMIAX: { VfpLoadStoreMultiple(il, instr, false); break; @@ -4811,6 +5136,8 @@ bool GetLowLevelILForNEONInstruction(Architecture* arch, LowLevelILFunction& il, case armv7::ARMV7_VLDM: case armv7::ARMV7_VLDMDB: case armv7::ARMV7_VLDMIA: + case armv7::ARMV7_FLDMDBX: + case armv7::ARMV7_FLDMIAX: { VfpLoadStoreMultiple(il, instr, true); break; diff --git a/arch/armv7/thumb2_disasm/spec.cpp b/arch/armv7/thumb2_disasm/spec.cpp index 1079b96649..eeaa8538d5 100644 --- a/arch/armv7/thumb2_disasm/spec.cpp +++ b/arch/armv7/thumb2_disasm/spec.cpp @@ -77,6 +77,8 @@ int enterx_leavex(struct decomp_request *req, struct decomp_result *res); int eor_immediate(struct decomp_request *req, struct decomp_result *res); int eor_register(struct decomp_request *req, struct decomp_result *res); int extension_reg_load_store(struct decomp_request *req, struct decomp_result *res); +int fldmx(struct decomp_request *req, struct decomp_result *res); +int fstmx(struct decomp_request *req, struct decomp_result *res); int hint_undoc(struct decomp_request *req, struct decomp_result *res); int if_then_hints(struct decomp_request *req, struct decomp_result *res); int isb(struct decomp_request *req, struct decomp_result *res); @@ -5937,6 +5939,252 @@ int extension_reg_load_store(struct decomp_request *req, struct decomp_result *r return undefined(req, res); } +// gen_crc: 36EA20CB +int fldmx(struct decomp_request *req, struct decomp_result *res) +{ + int rc = -1; + + res->group = INSN_GROUP_UNKNOWN; + res->group = INSN_GROUP_NEON; + /* Encoding T1 */ + /* pattern="1110,110,P.1,U.1,D.1,W.1,1,Rn.4,Vd.4,1011,imm8.8" width=32 stringency=16 */ + { + uint32_t instr = req->instr_word32; + if(((instr & 0xFE100F00)==0xEC100B00)) { + res->instrSize = 32; + if(!(req->arch & ARCH_VFPv2) && !(req->arch & ARCH_VFPv3) && !(req->arch & ARCH_ADVSIMD)) { + res->status |= STATUS_ARCH_UNSUPPORTED; + } + res->fields[FIELD_cond] = COND_AL; + res->fields_mask[FIELD_cond >> 6] |= 1LL << (FIELD_cond & 63); + res->fields[FIELD_P] = (instr & 0x1000000)>>24; + res->fields_mask[FIELD_P >> 6] |= 1LL << (FIELD_P & 63); + char P_width = 1; + res->fields[FIELD_U] = (instr & 0x800000)>>23; + res->fields_mask[FIELD_U >> 6] |= 1LL << (FIELD_U & 63); + char U_width = 1; + res->fields[FIELD_D] = (instr & 0x400000)>>22; + res->fields_mask[FIELD_D >> 6] |= 1LL << (FIELD_D & 63); + char D_width = 1; + res->fields[FIELD_W] = (instr & 0x200000)>>21; + res->fields_mask[FIELD_W >> 6] |= 1LL << (FIELD_W & 63); + char W_width = 1; + res->fields[FIELD_Rn] = (instr & 0xF0000)>>16; + res->fields_mask[FIELD_Rn >> 6] |= 1LL << (FIELD_Rn & 63); + char Rn_width = 4; + res->fields[FIELD_Vd] = (instr & 0xF000)>>12; + res->fields_mask[FIELD_Vd >> 6] |= 1LL << (FIELD_Vd & 63); + char Vd_width = 4; + res->fields[FIELD_imm8] = instr & 0xFF; + res->fields_mask[FIELD_imm8 >> 6] |= 1LL << (FIELD_imm8 & 63); + char imm8_width = 8; + + static const instruction_format instr_formats[] = + { + { /* FLDMDBX !, */ + "fldmdbx", /* .operation (const char *) */ + 0|INSTR_FORMAT_FLAG_CONDITIONAL, /* .operationFlags (uint32_t) */ + {/* .operands (instruction_operand_format) */ + {OPERAND_FORMAT_REG,FIELD_Rn,FIELD_UNINIT,"","",WRITEBACK_YES}, + {OPERAND_FORMAT_REGISTERS,FIELD_registers,FIELD_UNINIT,"","",WRITEBACK_NO}, + {OPERAND_FORMAT_END,FIELD_UNINIT,FIELD_UNINIT,"","",WRITEBACK_NO}, + }, + 2 /* .operandCount */ + }, + { /* FLDMIAX {!}, */ + "fldmiax", /* .operation (const char *) */ + 0|INSTR_FORMAT_FLAG_CONDITIONAL, /* .operationFlags (uint32_t) */ + {/* .operands (instruction_operand_format) */ + {OPERAND_FORMAT_REG,FIELD_Rn,FIELD_UNINIT,"","",WRITEBACK_OPTIONAL}, + {OPERAND_FORMAT_REGISTERS,FIELD_registers,FIELD_UNINIT,"","",WRITEBACK_NO}, + {OPERAND_FORMAT_END,FIELD_UNINIT,FIELD_UNINIT,"","",WRITEBACK_NO}, + }, + 2 /* .operandCount */ + }, + }; /* ENDS instruction_format array */ + + res->formats = instr_formats; + res->formatCount = 2; + res->mnem = armv7::ARMV7_FLDMDBX; + + /* pcode: if (P == '0' && U == '0' && W == '0') then SEE xfer_64_core_ext_regs */ + if(((((res->fields[FIELD_P]) == (0x0)) && ((res->fields[FIELD_U]) == (0x0))) && ((res->fields[FIELD_W]) == (0x0)))) { + + return xfer_64_core_ext_regs(req, res); + } + /* pcode: if (P == '1' && W == '0') then SEE vldr */ + if((((res->fields[FIELD_P]) == (0x1)) && ((res->fields[FIELD_W]) == (0x0)))) { + + return vldr(req, res); + } + /* pcode: if (P == U && W == '1') then UNDEFINED */ + if((((res->fields[FIELD_P]) == (res->fields[FIELD_U])) && ((res->fields[FIELD_W]) == (0x1)))) { + res->status |= STATUS_UNDEFINED; + } + /* pcode: single_regs = FALSE */ + res->fields[FIELD_single_regs] = 0; + res->fields_mask[FIELD_single_regs >> 6] |= 1LL << (FIELD_single_regs & 63); + /* pcode: add = (U == '1') */ + res->fields[FIELD_add] = ((res->fields[FIELD_U]) == (0x1)); + res->fields_mask[FIELD_add >> 6] |= 1LL << (FIELD_add & 63); + /* pcode: wback = (W == '1') */ + res->fields[FIELD_wback] = ((res->fields[FIELD_W]) == (0x1)); + res->fields_mask[FIELD_wback >> 6] |= 1LL << (FIELD_wback & 63); + /* pcode: d = UInt(D:Vd) */ + res->fields[FIELD_d] = ((res->fields[FIELD_D]<fields[FIELD_Vd])); + res->fields_mask[FIELD_d >> 6] |= 1LL << (FIELD_d & 63); + /* pcode: n = UInt(Rn) */ + res->fields[FIELD_n] = (res->fields[FIELD_Rn]); + res->fields_mask[FIELD_n >> 6] |= 1LL << (FIELD_n & 63); + /* pcode: imm32 = ZeroExtend(imm8:'00', 32) */ + res->fields[FIELD_imm32] = (res->fields[FIELD_imm8]<<2)|(0x0); + res->fields_mask[FIELD_imm32 >> 6] |= 1LL << (FIELD_imm32 & 63); + /* pcode: regs = UInt(imm8) DIV 2 */ + res->fields[FIELD_regs] = ((2) ? (((res->fields[FIELD_imm8])) / (2)) : 0); + res->fields_mask[FIELD_regs >> 6] |= 1LL << (FIELD_regs & 63); + /* pcode: if n == 15 then UNPREDICTABLE */ + if((res->fields[FIELD_n]) == (15)) { + res->flags |= FLAG_UNPREDICTABLE; + } + /* pcode: if (regs == 0 || regs > 16 || (d+regs) > 16) then UNPREDICTABLE */ + if(((((res->fields[FIELD_regs]) == (0)) || ((res->fields[FIELD_regs]) > (16))) || ((((res->fields[FIELD_d]) + (res->fields[FIELD_regs]))) > (16)))) { + res->flags |= FLAG_UNPREDICTABLE; + } + /* pcode: fmt_idx = U */ + res->fields[FIELD_fmt_idx] = res->fields[FIELD_U]; + res->fields_mask[FIELD_fmt_idx >> 6] |= 1LL << (FIELD_fmt_idx & 63); + + return success(); + } /* ENDS if() ... */ + } /* ENDS single encoding block */ + + /* if fall-thru here, no encoding block matched */ + return undefined(req, res); +} + +// gen_crc: 9D37C9CA +int fstmx(struct decomp_request *req, struct decomp_result *res) +{ + int rc = -1; + + res->group = INSN_GROUP_UNKNOWN; + res->group = INSN_GROUP_NEON; + /* Encoding T1 */ + /* pattern="1110,110,P.1,U.1,D.1,W.1,0,Rn.4,Vd.4,1011,imm8.8" width=32 stringency=16 */ + { + uint32_t instr = req->instr_word32; + if(((instr & 0xFE100F00)==0xEC000B00)) { + res->instrSize = 32; + if(!(req->arch & ARCH_VFPv2) && !(req->arch & ARCH_VFPv3) && !(req->arch & ARCH_ADVSIMD)) { + res->status |= STATUS_ARCH_UNSUPPORTED; + } + res->fields[FIELD_cond] = COND_AL; + res->fields_mask[FIELD_cond >> 6] |= 1LL << (FIELD_cond & 63); + res->fields[FIELD_P] = (instr & 0x1000000)>>24; + res->fields_mask[FIELD_P >> 6] |= 1LL << (FIELD_P & 63); + char P_width = 1; + res->fields[FIELD_U] = (instr & 0x800000)>>23; + res->fields_mask[FIELD_U >> 6] |= 1LL << (FIELD_U & 63); + char U_width = 1; + res->fields[FIELD_D] = (instr & 0x400000)>>22; + res->fields_mask[FIELD_D >> 6] |= 1LL << (FIELD_D & 63); + char D_width = 1; + res->fields[FIELD_W] = (instr & 0x200000)>>21; + res->fields_mask[FIELD_W >> 6] |= 1LL << (FIELD_W & 63); + char W_width = 1; + res->fields[FIELD_Rn] = (instr & 0xF0000)>>16; + res->fields_mask[FIELD_Rn >> 6] |= 1LL << (FIELD_Rn & 63); + char Rn_width = 4; + res->fields[FIELD_Vd] = (instr & 0xF000)>>12; + res->fields_mask[FIELD_Vd >> 6] |= 1LL << (FIELD_Vd & 63); + char Vd_width = 4; + res->fields[FIELD_imm8] = instr & 0xFF; + res->fields_mask[FIELD_imm8 >> 6] |= 1LL << (FIELD_imm8 & 63); + char imm8_width = 8; + + static const instruction_format instr_formats[] = + { + { /* FSTMDBX !, */ + "fstmdbx", /* .operation (const char *) */ + 0|INSTR_FORMAT_FLAG_CONDITIONAL, /* .operationFlags (uint32_t) */ + {/* .operands (instruction_operand_format) */ + {OPERAND_FORMAT_REG,FIELD_Rn,FIELD_UNINIT,"","",WRITEBACK_YES}, + {OPERAND_FORMAT_REGISTERS,FIELD_registers,FIELD_UNINIT,"","",WRITEBACK_NO}, + {OPERAND_FORMAT_END,FIELD_UNINIT,FIELD_UNINIT,"","",WRITEBACK_NO}, + }, + 2 /* .operandCount */ + }, + { /* FSTMIAX {!}, */ + "fstmiax", /* .operation (const char *) */ + 0|INSTR_FORMAT_FLAG_CONDITIONAL, /* .operationFlags (uint32_t) */ + {/* .operands (instruction_operand_format) */ + {OPERAND_FORMAT_REG,FIELD_Rn,FIELD_UNINIT,"","",WRITEBACK_OPTIONAL}, + {OPERAND_FORMAT_REGISTERS,FIELD_registers,FIELD_UNINIT,"","",WRITEBACK_NO}, + {OPERAND_FORMAT_END,FIELD_UNINIT,FIELD_UNINIT,"","",WRITEBACK_NO}, + }, + 2 /* .operandCount */ + }, + }; /* ENDS instruction_format array */ + + res->formats = instr_formats; + res->formatCount = 2; + res->mnem = armv7::ARMV7_FSTMDBX; + + /* pcode: if (P == '0' && U == '0' && W == '0') then SEE xfer_64_core_ext_regs */ + if(((((res->fields[FIELD_P]) == (0x0)) && ((res->fields[FIELD_U]) == (0x0))) && ((res->fields[FIELD_W]) == (0x0)))) { + + return xfer_64_core_ext_regs(req, res); + } + /* pcode: if (P == '1' && W == '0') then SEE vstr */ + if((((res->fields[FIELD_P]) == (0x1)) && ((res->fields[FIELD_W]) == (0x0)))) { + + return vstr(req, res); + } + /* pcode: if (P == U && W == '1') then UNDEFINED */ + if((((res->fields[FIELD_P]) == (res->fields[FIELD_U])) && ((res->fields[FIELD_W]) == (0x1)))) { + res->status |= STATUS_UNDEFINED; + } + /* pcode: single_regs = FALSE */ + res->fields[FIELD_single_regs] = 0; + res->fields_mask[FIELD_single_regs >> 6] |= 1LL << (FIELD_single_regs & 63); + /* pcode: add = (U == '1') */ + res->fields[FIELD_add] = ((res->fields[FIELD_U]) == (0x1)); + res->fields_mask[FIELD_add >> 6] |= 1LL << (FIELD_add & 63); + /* pcode: wback = (W == '1') */ + res->fields[FIELD_wback] = ((res->fields[FIELD_W]) == (0x1)); + res->fields_mask[FIELD_wback >> 6] |= 1LL << (FIELD_wback & 63); + /* pcode: d = UInt(D:Vd) */ + res->fields[FIELD_d] = ((res->fields[FIELD_D]<fields[FIELD_Vd])); + res->fields_mask[FIELD_d >> 6] |= 1LL << (FIELD_d & 63); + /* pcode: n = UInt(Rn) */ + res->fields[FIELD_n] = (res->fields[FIELD_Rn]); + res->fields_mask[FIELD_n >> 6] |= 1LL << (FIELD_n & 63); + /* pcode: imm32 = ZeroExtend(imm8:'00', 32) */ + res->fields[FIELD_imm32] = (res->fields[FIELD_imm8]<<2)|(0x0); + res->fields_mask[FIELD_imm32 >> 6] |= 1LL << (FIELD_imm32 & 63); + /* pcode: regs = UInt(imm8) DIV 2 */ + res->fields[FIELD_regs] = ((2) ? (((res->fields[FIELD_imm8])) / (2)) : 0); + res->fields_mask[FIELD_regs >> 6] |= 1LL << (FIELD_regs & 63); + /* pcode: if n == 15 then UNPREDICTABLE */ + if((res->fields[FIELD_n]) == (15)) { + res->flags |= FLAG_UNPREDICTABLE; + } + /* pcode: if (regs == 0 || regs > 16 || (d+regs) > 16) then UNPREDICTABLE */ + if(((((res->fields[FIELD_regs]) == (0)) || ((res->fields[FIELD_regs]) > (16))) || ((((res->fields[FIELD_d]) + (res->fields[FIELD_regs]))) > (16)))) { + res->flags |= FLAG_UNPREDICTABLE; + } + /* pcode: fmt_idx = U */ + res->fields[FIELD_fmt_idx] = res->fields[FIELD_U]; + res->fields_mask[FIELD_fmt_idx >> 6] |= 1LL << (FIELD_fmt_idx & 63); + + return success(); + } /* ENDS if() ... */ + } /* ENDS single encoding block */ + + /* if fall-thru here, no encoding block matched */ + return undefined(req, res); +} + // gen_crc: 82DAE323 int hint_undoc(struct decomp_request *req, struct decomp_result *res) { @@ -31595,7 +31843,7 @@ int vand(struct decomp_request *req, struct decomp_result *res) return undefined(req, res); } -// gen_crc: A489C601 +// gen_crc: DA4A2203 int vbic_immediate(struct decomp_request *req, struct decomp_result *res) { int rc = -1; @@ -31695,9 +31943,15 @@ int vbic_immediate(struct decomp_request *req, struct decomp_result *res) /* pcode: fmt_idx = (Q == '1') */ res->fields[FIELD_fmt_idx] = ((res->fields[FIELD_Q]) == (0x1)); res->fields_mask[FIELD_fmt_idx >> 6] |= 1LL << (FIELD_fmt_idx & 63); - /* pcode: dt = (D == '1') + 1 */ - res->fields[FIELD_dt] = (((res->fields[FIELD_D]) == (0x1))) + (1); - res->fields_mask[FIELD_dt >> 6] |= 1LL << (FIELD_dt & 63); + /* pcode: dt = if cmode<3> == '1' then 1 else 2 */ + if((((res->fields[FIELD_cmode] >> 3) & 1)) == (0x1)) { + res->fields[FIELD_dt] = 1; + res->fields_mask[FIELD_dt >> 6] |= 1LL << (FIELD_dt & 63); + } + else { + res->fields[FIELD_dt] = 2; + res->fields_mask[FIELD_dt >> 6] |= 1LL << (FIELD_dt & 63); + } return success(); } /* ENDS if() ... */ @@ -32467,7 +32721,7 @@ int vceq_register(struct decomp_request *req, struct decomp_result *res) return undefined(req, res); } -// gen_crc: C247D48E +// gen_crc: 39879EA1 int vcge_immediate(struct decomp_request *req, struct decomp_result *res) { int rc = -1; @@ -32569,8 +32823,8 @@ int vcge_immediate(struct decomp_request *req, struct decomp_result *res) /* pcode: fmt_idx = (Q == '1') */ res->fields[FIELD_fmt_idx] = ((res->fields[FIELD_Q]) == (0x1)); res->fields_mask[FIELD_fmt_idx >> 6] |= 1LL << (FIELD_fmt_idx & 63); - /* pcode: dt = size */ - res->fields[FIELD_dt] = res->fields[FIELD_size]; + /* pcode: dt = size + F */ + res->fields[FIELD_dt] = (res->fields[FIELD_size]) + (res->fields[FIELD_F]); res->fields_mask[FIELD_dt >> 6] |= 1LL << (FIELD_dt & 63); return success(); @@ -38785,7 +39039,7 @@ int vld4_single_4elem_nlanes(struct decomp_request *req, struct decomp_result *r return undefined(req, res); } -// gen_crc: 9373B587 +// gen_crc: 70B8FD1D int vldm(struct decomp_request *req, struct decomp_result *res) { int rc = -1; @@ -38975,19 +39229,20 @@ int vldm(struct decomp_request *req, struct decomp_result *res) return xfer_64_core_ext_regs(req, res); } - /* pcode: if (P == '0' && U == '1' && W == '1' && Rn == '1101') then SEE vpop */ - if((((((res->fields[FIELD_P]) == (0x0)) && ((res->fields[FIELD_U]) == (0x1))) && ((res->fields[FIELD_W]) == (0x1))) && ((res->fields[FIELD_Rn]) == (0xD)))) { - - return vpop(req, res); - } /* pcode: if (P == '1' && W == '0') then SEE vldr */ if((((res->fields[FIELD_P]) == (0x1)) && ((res->fields[FIELD_W]) == (0x0)))) { return vldr(req, res); } - /* pcode: if (imm8<0> == '1') then UNDEFINED */ + /* pcode: if (imm8<0> == '1') then SEE fldmx */ if((((res->fields[FIELD_imm8] & 1)) == (0x1))) { - res->status |= STATUS_UNDEFINED; + + return fldmx(req, res); + } + /* pcode: if (P == '0' && U == '1' && W == '1' && Rn == '1101') then SEE vpop */ + if((((((res->fields[FIELD_P]) == (0x0)) && ((res->fields[FIELD_U]) == (0x1))) && ((res->fields[FIELD_W]) == (0x1))) && ((res->fields[FIELD_Rn]) == (0xD)))) { + + return vpop(req, res); } /* pcode: if (P == U && W == '1') then UNDEFINED */ if((((res->fields[FIELD_P]) == (res->fields[FIELD_U])) && ((res->fields[FIELD_W]) == (0x1)))) { @@ -41903,7 +42158,7 @@ int vmov_scalar_core(struct decomp_request *req, struct decomp_result *res) return undefined(req, res); } -// gen_crc: 8E858F6E +// gen_crc: 394DE743 int vmovl(struct decomp_request *req, struct decomp_result *res) { int rc = -1; @@ -41987,9 +42242,6 @@ int vmovl(struct decomp_request *req, struct decomp_result *res) /* pcode: m = UInt(M:Vm) */ res->fields[FIELD_m] = ((res->fields[FIELD_M]<fields[FIELD_Vm])); res->fields_mask[FIELD_m >> 6] |= 1LL << (FIELD_m & 63); - /* pcode: unsigned = TRUE */ - res->fields[FIELD_unsigned] = 1; - res->fields_mask[FIELD_unsigned >> 6] |= 1LL << (FIELD_unsigned & 63); /* pcode: dt = UInt(U:imm3) */ res->fields[FIELD_dt] = ((res->fields[FIELD_U]<<3)|(res->fields[FIELD_imm3])); res->fields_mask[FIELD_dt >> 6] |= 1LL << (FIELD_dt & 63); @@ -42456,7 +42708,7 @@ int vmul_float(struct decomp_request *req, struct decomp_result *res) return undefined(req, res); } -// gen_crc: A5D959D0 +// gen_crc: 32A6AAF5 int vmul_integer(struct decomp_request *req, struct decomp_result *res) { int rc = -1; @@ -42638,10 +42890,21 @@ int vmul_integer(struct decomp_request *req, struct decomp_result *res) }, 3 /* .operandCount */ }, + { /* VMULL.P64 ,, */ + "vmull.p64", /* .operation (const char *) */ + 0|INSTR_FORMAT_FLAG_CONDITIONAL, /* .operationFlags (uint32_t) */ + {/* .operands (instruction_operand_format) */ + {OPERAND_FORMAT_REG_FP,FIELD_d,FIELD_UNINIT,"q","",WRITEBACK_OPTIONAL}, + {OPERAND_FORMAT_REG_FP,FIELD_n,FIELD_UNINIT,"d","",WRITEBACK_OPTIONAL}, + {OPERAND_FORMAT_REG_FP,FIELD_m,FIELD_UNINIT,"d","",WRITEBACK_OPTIONAL}, + {OPERAND_FORMAT_END,FIELD_UNINIT,FIELD_UNINIT,"","",WRITEBACK_NO}, + }, + 3 /* .operandCount */ + }, }; /* ENDS instruction_format array */ res->formats = instr_formats; - res->formatCount = 2; + res->formatCount = 3; res->mnem = armv7::ARMV7_VMULL; /* pcode: if size == '11' then SEE advanced_simd_data_proc */ @@ -42649,8 +42912,8 @@ int vmul_integer(struct decomp_request *req, struct decomp_result *res) return advanced_simd_data_proc(req, res); } - /* pcode: if (op == '1' && (U != '0' || size != '00')) then UNDEFINED */ - if((((res->fields[FIELD_op]) == (0x1)) && ((((res->fields[FIELD_U]) != (0x0)) || ((res->fields[FIELD_size]) != (0x0)))))) { + /* pcode: if (op == '1' && (U != '0' || size == '01')) then UNDEFINED */ + if((((res->fields[FIELD_op]) == (0x1)) && ((((res->fields[FIELD_U]) != (0x0)) || ((res->fields[FIELD_size]) == (0x1)))))) { res->status |= STATUS_UNDEFINED; } /* pcode: if Vd<0> == '1' then UNDEFINED */ @@ -42684,6 +42947,25 @@ int vmul_integer(struct decomp_request *req, struct decomp_result *res) /* pcode: fmt_idx = (op == '1') */ res->fields[FIELD_fmt_idx] = ((res->fields[FIELD_op]) == (0x1)); res->fields_mask[FIELD_fmt_idx >> 6] |= 1LL << (FIELD_fmt_idx & 63); + /* pcode: if (op == '1' && size == '10' && InITBlock()) then UNPREDICTABLE */ + if(((((res->fields[FIELD_op]) == (0x1)) && ((res->fields[FIELD_size]) == (0x2))) && (req->inIfThen == IFTHEN_YES))) { + res->flags |= FLAG_UNPREDICTABLE; + } + /* pcode: if (op == '1' && size == '10') then esize = 64 */ + if((((res->fields[FIELD_op]) == (0x1)) && ((res->fields[FIELD_size]) == (0x2)))) { + res->fields[FIELD_esize] = 64; + res->fields_mask[FIELD_esize >> 6] |= 1LL << (FIELD_esize & 63); + } + /* pcode: if (op == '1' && size == '10') then elements = 1 */ + if((((res->fields[FIELD_op]) == (0x1)) && ((res->fields[FIELD_size]) == (0x2)))) { + res->fields[FIELD_elements] = 1; + res->fields_mask[FIELD_elements >> 6] |= 1LL << (FIELD_elements & 63); + } + /* pcode: if (op == '1' && size == '10') then fmt_idx = 2 */ + if((((res->fields[FIELD_op]) == (0x1)) && ((res->fields[FIELD_size]) == (0x2)))) { + res->fields[FIELD_fmt_idx] = 2; + res->fields_mask[FIELD_fmt_idx >> 6] |= 1LL << (FIELD_fmt_idx & 63); + } /* pcode: dt = UInt(U:size) */ res->fields[FIELD_dt] = ((res->fields[FIELD_U]<fields[FIELD_size])); res->fields_mask[FIELD_dt >> 6] |= 1LL << (FIELD_dt & 63); @@ -43203,7 +43485,7 @@ int vmvn(struct decomp_request *req, struct decomp_result *res) return undefined(req, res); } -// gen_crc: B335A3A5 +// gen_crc: E65D45C4 int vmvn_immediate(struct decomp_request *req, struct decomp_result *res) { int rc = -1; @@ -43296,9 +43578,18 @@ int vmvn_immediate(struct decomp_request *req, struct decomp_result *res) /* pcode: fmt_idx = (Q == '1') */ res->fields[FIELD_fmt_idx] = ((res->fields[FIELD_Q]) == (0x1)); res->fields_mask[FIELD_fmt_idx >> 6] |= 1LL << (FIELD_fmt_idx & 63); - /* pcode: dt = UInt(imm3:i) */ - res->fields[FIELD_dt] = ((res->fields[FIELD_imm3]<fields[FIELD_i])); - res->fields_mask[FIELD_dt >> 6] |= 1LL << (FIELD_dt & 63); + /* pcode: iword = TRUE */ + res->fields[FIELD_iword] = 1; + res->fields_mask[FIELD_iword >> 6] |= 1LL << (FIELD_iword & 63); + /* pcode: dt = if cmode<3:2> == '10' then 1 else 2 */ + if((((res->fields[FIELD_cmode] >> 2) & 0x3)) == (0x2)) { + res->fields[FIELD_dt] = 1; + res->fields_mask[FIELD_dt >> 6] |= 1LL << (FIELD_dt & 63); + } + else { + res->fields[FIELD_dt] = 2; + res->fields_mask[FIELD_dt >> 6] |= 1LL << (FIELD_dt & 63); + } return success(); } /* ENDS if() ... */ @@ -43857,7 +44148,7 @@ int vorn_register(struct decomp_request *req, struct decomp_result *res) return undefined(req, res); } -// gen_crc: 5FE7F2EF +// gen_crc: 47DC7B6A int vorr_immediate(struct decomp_request *req, struct decomp_result *res) { int rc = -1; @@ -43957,9 +44248,15 @@ int vorr_immediate(struct decomp_request *req, struct decomp_result *res) /* pcode: fmt_idx = (Q == '1') */ res->fields[FIELD_fmt_idx] = ((res->fields[FIELD_Q]) == (0x1)); res->fields_mask[FIELD_fmt_idx >> 6] |= 1LL << (FIELD_fmt_idx & 63); - /* pcode: dt = 1 */ - res->fields[FIELD_dt] = 1; - res->fields_mask[FIELD_dt >> 6] |= 1LL << (FIELD_dt & 63); + /* pcode: dt = if cmode<3> == '1' then 1 else 2 */ + if((((res->fields[FIELD_cmode] >> 3) & 1)) == (0x1)) { + res->fields[FIELD_dt] = 1; + res->fields_mask[FIELD_dt >> 6] |= 1LL << (FIELD_dt & 63); + } + else { + res->fields[FIELD_dt] = 2; + res->fields_mask[FIELD_dt >> 6] |= 1LL << (FIELD_dt & 63); + } return success(); } /* ENDS if() ... */ @@ -44716,7 +45013,7 @@ int vpmax_integer(struct decomp_request *req, struct decomp_result *res) return undefined(req, res); } -// gen_crc: 1A1F516C +// gen_crc: 8E9AABD2 int vpop(struct decomp_request *req, struct decomp_result *res) { int rc = -1; @@ -44762,6 +45059,11 @@ int vpop(struct decomp_request *req, struct decomp_result *res) res->formatCount = 1; res->mnem = armv7::ARMV7_VPOP; + /* pcode: if imm8<0> == '1' then SEE fldmx */ + if(((res->fields[FIELD_imm8] & 1)) == (0x1)) { + + return fldmx(req, res); + } /* pcode: single_regs = FALSE */ res->fields[FIELD_single_regs] = 0; res->fields_mask[FIELD_single_regs >> 6] |= 1LL << (FIELD_single_regs & 63); @@ -44847,7 +45149,7 @@ int vpop(struct decomp_request *req, struct decomp_result *res) return undefined(req, res); } -// gen_crc: 524C58C7 +// gen_crc: 3FF841A9 int vpush(struct decomp_request *req, struct decomp_result *res) { int rc = -1; @@ -44893,6 +45195,11 @@ int vpush(struct decomp_request *req, struct decomp_result *res) res->formatCount = 1; res->mnem = armv7::ARMV7_VPUSH; + /* pcode: if imm8<0> == '1' then SEE fstmx */ + if(((res->fields[FIELD_imm8] & 1)) == (0x1)) { + + return fstmx(req, res); + } /* pcode: single_regs = FALSE */ res->fields[FIELD_single_regs] = 0; res->fields_mask[FIELD_single_regs >> 6] |= 1LL << (FIELD_single_regs & 63); @@ -52549,7 +52856,7 @@ int vst4_single_4elem(struct decomp_request *req, struct decomp_result *res) return undefined(req, res); } -// gen_crc: A180DDD2 +// gen_crc: FBD41951 int vstm(struct decomp_request *req, struct decomp_result *res) { int rc = -1; @@ -52739,16 +53046,21 @@ int vstm(struct decomp_request *req, struct decomp_result *res) return xfer_64_core_ext_regs(req, res); } - /* pcode: if P == '1' && U == '0' && W == '1' && Rn == '1101' then SEE vpush */ - if(((((res->fields[FIELD_P]) == (0x1)) && ((res->fields[FIELD_U]) == (0x0))) && ((res->fields[FIELD_W]) == (0x1))) && ((res->fields[FIELD_Rn]) == (0xD))) { - - return vpush(req, res); - } /* pcode: if P == '1' && W == '0' then SEE vstr */ if(((res->fields[FIELD_P]) == (0x1)) && ((res->fields[FIELD_W]) == (0x0))) { return vstr(req, res); } + /* pcode: if imm8<0> == '1' then SEE fstmx */ + if(((res->fields[FIELD_imm8] & 1)) == (0x1)) { + + return fstmx(req, res); + } + /* pcode: if P == '1' && U == '0' && W == '1' && Rn == '1101' then SEE vpush */ + if(((((res->fields[FIELD_P]) == (0x1)) && ((res->fields[FIELD_U]) == (0x0))) && ((res->fields[FIELD_W]) == (0x1))) && ((res->fields[FIELD_Rn]) == (0xD))) { + + return vpush(req, res); + } /* pcode: if P == U && W == '1' then UNDEFINED */ if(((res->fields[FIELD_P]) == (res->fields[FIELD_U])) && ((res->fields[FIELD_W]) == (0x1))) { res->status |= STATUS_UNDEFINED; diff --git a/arch/armv7/thumb2_disasm/spec.txt b/arch/armv7/thumb2_disasm/spec.txt index 34b04e6549..5b139801cf 100644 --- a/arch/armv7/thumb2_disasm/spec.txt +++ b/arch/armv7/thumb2_disasm/spec.txt @@ -3009,6 +3009,7 @@ group NEON Encoding T1 VFPv2, VFPv3, ADVSIMD fmt VPUSH extract32 cond.4,110,1,0,D.1,1,0,1101,Vd.4,1011,imm8.8 +pcode if imm8<0> == '1' then SEE fstmx; pcode single_regs = FALSE; d = UInt(D:Vd); imm32 = ZeroExtend(imm8:'00', 32); regs = UInt(imm8) / 2; if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; Encoding T2 VFPv2, VFPv3 fmt VPUSH @@ -3020,6 +3021,7 @@ group NEON Encoding T1 VFPv2, VFPv3, ADVSIMD fmt VPOP extract32 cond.4,110,0,1,D.1,1,1,1101,Vd.4,1011,imm8.8 +pcode if imm8<0> == '1' then SEE fldmx; pcode single_regs = FALSE; d = UInt(D:Vd); imm32 = ZeroExtend(imm8:'00', 32); regs = UInt(imm8) / 2; if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; Encoding T2 VFPv2, VFPv3 fmt VPOP @@ -3353,7 +3355,7 @@ imm64h = AdvSIMDExpandImm('0', cmode, i:imm3:imm4, 0); d = UInt(D:Vd); regs = if Q == '0' then 1 else 2; iword = TRUE; fmt_idx = (Q == '1'); -dt = 1; +dt = if cmode<3> == '1' then 1 else 2; pcode_end vbic_register: @@ -3380,7 +3382,7 @@ imm64h = AdvSIMDExpandImm('1', cmode, i:imm3:imm4, 0); d = UInt(D:Vd); regs = if Q == '0' then 1 else 2; iword = TRUE; fmt_idx = (Q == '1'); -dt = (D == '1') + 1; +dt = if cmode<3> == '1' then 1 else 2; pcode_end vorn_register: @@ -3594,7 +3596,6 @@ if Vd<0> == '1' then UNDEFINED; esize = 8 * UInt(imm3); unsigned = (U == '1'); elements = 64 DIV esize; d = UInt(D:Vd); m = UInt(M:Vm); -unsigned = TRUE; dt = UInt(U:imm3) dt = dt DIV 2 pcode_end @@ -3612,7 +3613,8 @@ imm64l = AdvSIMDExpandImm('1', cmode, i:imm3:imm4, 1); imm64h = AdvSIMDExpandImm('1', cmode, i:imm3:imm4, 0); d = UInt(D:Vd); regs = if Q == '0' then 1 else 2; fmt_idx = (Q == '1'); -dt = UInt(imm3:i); +iword = TRUE; +dt = if cmode<3:2> == '10' then 1 else 2; pcode_end vmovn: @@ -4668,7 +4670,7 @@ if (Q == '1' && (Vd<0> == '1' || Vm<0> == '1')) then UNDEFINED; esize = 8 << UInt(size); elements = 64 DIV esize; d = UInt(D:Vd); m = UInt(M:Vm); regs = if Q == '0' then 1 else 2; fmt_idx = (Q == '1'); -dt = size; +dt = size + F; pcode_end vcge_register: @@ -5161,15 +5163,20 @@ pcode_end Encoding T2 ADVSIMD fmt VMULL.
,, fmt VMULL.P8 ,, +fmt VMULL.P64 ,, extract32 111,U.1,1111,1,D.1,size.2,Vn.4,Vd.4,11,op.1,0,N.1,0,M.1,0,Vm.4 pcode_start if size == '11' then SEE advanced_simd_data_proc; -if (op == '1' && (U != '0' || size != '00')) then UNDEFINED; +if (op == '1' && (U != '0' || size == '01')) then UNDEFINED; if Vd<0> == '1' then UNDEFINED; long_destination = TRUE; unsigned = (U == '1'); esize = 8 << UInt(size); elements = 64 DIV esize; d = UInt(D:Vd); n = UInt(N:Vn); m = UInt(M:Vm); regs = 1; fmt_idx = (op == '1'); +if (op == '1' && size == '10' && InITBlock()) then UNPREDICTABLE; +if (op == '1' && size == '10') then esize = 64; +if (op == '1' && size == '10') then elements = 1; +if (op == '1' && size == '10') then fmt_idx = 2; dt = UInt(U:size); pcode_end Encoding T1 ADVSIMD @@ -6144,6 +6151,24 @@ if size == 3 then size = 2; if d4 > 31 then UNPREDICTABLE; pcode_end +fldmx: +group NEON +Encoding T1 VFPv2, VFPv3, ADVSIMD +fmt FLDMDBX !, +fmt FLDMIAX {!}, +extract32 1110,110,P.1,U.1,D.1,W.1,1,Rn.4,Vd.4,1011,imm8.8 +pcode_start +if (P == '0' && U == '0' && W == '0') then SEE xfer_64_core_ext_regs; +if (P == '1' && W == '0') then SEE vldr; +if (P == U && W == '1') then UNDEFINED; +single_regs = FALSE; add = (U == '1'); wback = (W == '1'); +d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:'00', 32); +regs = UInt(imm8) DIV 2; +if n == 15 then UNPREDICTABLE; +if (regs == 0 || regs > 16 || (d+regs) > 16) then UNPREDICTABLE; +fmt_idx = U; +pcode_end + vldm: group NEON Encoding T2 VFPv2, VFPv3 @@ -6167,9 +6192,9 @@ fmt VLDMIA {!}, extract32 1110,110,P.1,U.1,D.1,W.1,1,Rn.4,Vd.4,1011,imm8.8 pcode_start if (P == '0' && U == '0' && W == '0') then SEE xfer_64_core_ext_regs; -if (P == '0' && U == '1' && W == '1' && Rn == '1101') then SEE vpop; if (P == '1' && W == '0') then SEE vldr; -if (imm8<0> == '1') then UNDEFINED; +if (imm8<0> == '1') then SEE fldmx; +if (P == '0' && U == '1' && W == '1' && Rn == '1101') then SEE vpop; if (P == U && W == '1') then UNDEFINED; single_regs = FALSE; add = (U == '1'); wback = (W == '1'); d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:'00', 32); @@ -6180,6 +6205,24 @@ if (d+regs) > 16 then UNPREDICTABLE; fmt_idx = U; pcode_end +fstmx: +group NEON +Encoding T1 VFPv2, VFPv3, ADVSIMD +fmt FSTMDBX !, +fmt FSTMIAX {!}, +extract32 1110,110,P.1,U.1,D.1,W.1,0,Rn.4,Vd.4,1011,imm8.8 +pcode_start +if (P == '0' && U == '0' && W == '0') then SEE xfer_64_core_ext_regs; +if (P == '1' && W == '0') then SEE vstr; +if (P == U && W == '1') then UNDEFINED; +single_regs = FALSE; add = (U == '1'); wback = (W == '1'); +d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:'00', 32); +regs = UInt(imm8) DIV 2; +if n == 15 then UNPREDICTABLE; +if (regs == 0 || regs > 16 || (d+regs) > 16) then UNPREDICTABLE; +fmt_idx = U; +pcode_end + vstm: group NEON Encoding T2 VFPv2, VFPv3 @@ -6203,8 +6246,9 @@ fmt VSTMIA {!}, extract32 1110,110,P.1,U.1,D.1,W.1,0,Rn.4,Vd.4,1011,imm8.8 pcode_start if P == '0' && U == '0' && W == '0' then SEE xfer_64_core_ext_regs; -if P == '1' && U == '0' && W == '1' && Rn == '1101' then SEE vpush; if P == '1' && W == '0' then SEE vstr; +if imm8<0> == '1' then SEE fstmx; +if P == '1' && U == '0' && W == '1' && Rn == '1101' then SEE vpush; if P == U && W == '1' then UNDEFINED; single_regs = FALSE; add = (U == '1'); wback = (W == '1'); d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:'00', 32); @@ -6534,4 +6578,3 @@ Encoding T1 ARMv4T, ARMv5T, ARMv6, ARMv7 fmt UNPREDICTABLE extract32 (0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0)(0) pcode UNPREDICTABLE; -