diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index 9274118ca0..23839daab2 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -2723,7 +2723,22 @@ void ARMXEmitter::VSHL(u32 Size, ARMReg Vd, ARMReg Vm, int shiftAmount) { } void ARMXEmitter::VSHLL(u32 Size, ARMReg Vd, ARMReg Vm, int shiftAmount) { - EncodeShiftByImm((Size & ~I_UNSIGNED), Vd, Vm, shiftAmount, 0xA, false, false, false); + if (shiftAmount == (8 * (Size & 0xF))) { + // Entirely different encoding (A2) for size == shift! Bleh. + int sz = 0; + switch (Size & 0xF) { + case I_8: sz = 0; break; + case I_16: sz = 1; break; + case I_32: sz = 2; break; + case I_64: + _dbg_assert_msg_(JIT, false, "Cannot VSHLL 64-bit elements"); + } + int imm6 = 0x32 | (sz << 2); + u32 value = (0xF3 << 24) | (1 << 23) | (imm6 << 16) | EncodeVd(Vd) | (0x3 << 8) | EncodeVm(Vm); + Write32(value); + } else { + EncodeShiftByImm((Size & ~I_UNSIGNED), Vd, Vm, shiftAmount, 0xA, false, false, false); + } } void ARMXEmitter::VSHR(u32 Size, ARMReg Vd, ARMReg Vm, int shiftAmount) { diff --git a/ext/disarm.cpp b/ext/disarm.cpp index ea91cc01f1..26e0ba5b6c 100644 --- a/ext/disarm.cpp +++ b/ext/disarm.cpp @@ -476,10 +476,10 @@ static bool DisasmNeon2Op(uint32_t op, char *text) { if (op & (1 << 16)) opname = "NEG"; - int type = (op >> 6) & 0xF; + int opcode = (op >> 6) & 0xF; int sz = (op >> 18) & 3; const char *size = "f32"; - switch (type) { + switch (opcode) { case 0xE: opname = "NEG"; size = GetISizeString(sz); @@ -499,13 +499,24 @@ static bool DisasmNeon2Op(uint32_t op, char *text) { quadD = false; doubleD = true; break; + case 0xC: + opname = "SHLL"; // widen and shift + size = GetISizeString(sz); + quad = false; + quadD = true; + doubleD = true; + break; } int Vd = GetVd(op, quadD, doubleD); int Vm = GetVm(op, quad, false); char cD = quadD ? 'q' : 'd'; char c = quad ? 'q' : 'd'; - sprintf(text, "V%s%s%s %c%i, %c%i", opname, strlen(size) ? "." : "", size, cD, Vd, c, Vm); + if (opcode == 0xC) { + sprintf(text, "V%s%s%s %c%i, %c%i, #%d", opname, strlen(size) ? "." : "", size, cD, Vd, c, Vm, 8 << sz); + } else { + sprintf(text, "V%s%s%s %c%i, %c%i", opname, strlen(size) ? "." : "", size, cD, Vd, c, Vm); + } return true; } @@ -605,6 +616,7 @@ to64: return "i32"; } +// What a horror show! static bool DisasmNeon2RegShiftImm(uint32_t op, char *text) { bool U = (op >> 24) & 1; bool quadDest = false; @@ -617,7 +629,7 @@ static bool DisasmNeon2RegShiftImm(uint32_t op, char *text) { bool sign = false; switch (opcode) { case 0x5: opname = "VSHL"; quadDest = quadSrc = ((op >> 6) & 1); break; - case 0xA: opname = "VSHLL"; quadDest = true; quadSrc = false; break; + case 0xA: opname = "VSHLL"; quadDest = true; quadSrc = false; sign = true; break; case 0x0: opname = "VSHR"; sign = true; quadDest = quadSrc = ((op >> 6) & 1); inverse = true; break; case 0x8: opname = "VSHRN"; quadDest = false; quadSrc = true; inverse = true; incSize = true; break; default: @@ -632,8 +644,23 @@ static bool DisasmNeon2RegShiftImm(uint32_t op, char *text) { char c2 = quadSrc ? 'q' : 'd'; int imm7 = ((op >> 16) & 0x3f) | ((op & 0x80) >> 1); int shift; - const char *size = DecodeSizeAndShiftImm7(U, sign, inverse, imm7, incSize, &shift); - sprintf(text, "%s.%s %c%i, %c%i, #%i", opname, size, c1, Vd, c2, Vm, shift); + + const char *size; + if (opcode == 0xA) { + if (imm7 & 0x40) { + sprintf(text, "neon2regshiftimm undefined %08x", op); + return true; + } + } + + size = DecodeSizeAndShiftImm7(U, sign, inverse, imm7, incSize, &shift); + + if (opcode == 0xA && shift == 0) { + opname = "VMOVL"; + sprintf(text, "%s.%s %c%i, %c%i", opname, size, c1, Vd, c2, Vm); + } else { + sprintf(text, "%s.%s %c%i, %c%i, #%i", opname, size, c1, Vd, c2, Vm, shift); + } return true; } diff --git a/unittest/TestArmEmitter.cpp b/unittest/TestArmEmitter.cpp index 3d0127d442..a149f47704 100644 --- a/unittest/TestArmEmitter.cpp +++ b/unittest/TestArmEmitter.cpp @@ -32,6 +32,16 @@ bool TestArmEmitter() { u32 code[512]; ARMXEmitter emitter((u8 *)code); + emitter.VSHLL(I_16, Q0, D0, 16); + RET(CheckLast(emitter, "f3b60300 VSHLL.i16 q0, d0, #16")); + emitter.VSHLL(I_8, Q0, D0, 4); + RET(CheckLast(emitter, "f28c0a10 VSHLL.s8 q0, d0, #4")); + emitter.VSHLL(I_8, Q0, D0, 8); + RET(CheckLast(emitter, "f3b20300 VSHLL.i8 q0, d0, #8")); + emitter.VMOVL(I_16 | I_UNSIGNED, Q0, D0); + RET(CheckLast(emitter, "f3900a10 VMOVL.u16 q0, d0")); + emitter.VMOVL(I_32 | I_SIGNED, Q0, D0); + RET(CheckLast(emitter, "f2a00a10 VMOVL.s32 q0, d0")); emitter.VSHRN(I_32, D0, Q0, 16); RET(CheckLast(emitter, "f2900810 VSHRN.i32 d0, q0, #16")); emitter.VSHRN(I_64, D1, Q2, 24); @@ -53,7 +63,7 @@ bool TestArmEmitter() { emitter.VSHL(I_8, D1, D2, 7); RET(CheckLast(emitter, "f28f1512 VSHL.i8 d1, d2, #7")); emitter.VSHLL(I_32, Q1, D2, 17); - RET(CheckLast(emitter, "f2b12a12 VSHLL.i32 q1, d2, #17")); + RET(CheckLast(emitter, "f2b12a12 VSHLL.s32 q1, d2, #17")); emitter.LDR(R3, R7); RET(CheckLast(emitter, "e5973000 LDR r3, [r7, #0]")); emitter.VLDR(S3, R8, 48);