From cda4e9cbf39811d7ab5c79ee5ddb58064352afde Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 23 Nov 2013 13:35:42 +0100 Subject: [PATCH] ARM emitter: Complete VLD1/VST1 for lanes and to-all-lanes. --- Common/ArmEmitter.cpp | 91 ++++++++++++++++++++++++++++++++----------- Common/ArmEmitter.h | 57 +++++++++++++++------------ 2 files changed, 100 insertions(+), 48 deletions(-) diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index 0546e0f6e0..750687899f 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -1177,6 +1177,21 @@ u32 EncodeVm(ARMReg Vm) } } +u32 encodedSize(u32 value) +{ + if (value & I_8) + return 0; + else if (value & I_16) + return 1; + else if ((value & I_32) || (value & F_32)) + return 2; + else if (value & I_64) + return 3; + else + _dbg_assert_msg_(JIT, false, "Passed invalid size to integer NEON instruction"); + return 0; +} + ARMReg SubBase(ARMReg Reg) { if (Reg >= S0) @@ -2369,43 +2384,72 @@ static int RegCountToType(int nRegs, NEONAlignment align) { } } - - -void ARMXEmitter::VLD1(u32 Size, ARMReg Vd, ARMReg Rn, int regCount, NEONAlignment align, ARMReg Rm) +void ARMXEmitter::VLDST1(bool load, u32 Size, ARMReg Vd, ARMReg Rn, int regCount, NEONAlignment align, ARMReg Rm) { u32 spacing = RegCountToType(regCount, align); // Only support loading to 1 reg // Gets encoded as a double register Vd = SubBase(Vd); - Write32((0xF4 << 24) | ((Vd & 0x10) << 18) | (1 << 21) | (Rn << 16) + Write32((0xF4 << 24) | ((Vd & 0x10) << 18) | (load << 21) | (Rn << 16) | ((Vd & 0xF) << 12) | (spacing << 8) | (encodedSize(Size) << 6) | (align << 4) | Rm); } -void ARMXEmitter::VST1(u32 Size, ARMReg Vd, ARMReg Rn, int regCount, NEONAlignment align, ARMReg Rm) +void ARMXEmitter::VLD1(u32 Size, ARMReg Vd, ARMReg Rn, int regCount, NEONAlignment align, ARMReg Rm) { + VLDST1(true, Size, Vd, Rn, regCount, align, Rm); +} + +void ARMXEmitter::VST1(u32 Size, ARMReg Vd, ARMReg Rn, int regCount, NEONAlignment align, ARMReg Rm) { + VLDST1(false, Size, Vd, Rn, regCount, align, Rm); +} + +void ARMXEmitter::VLDST1_lane(bool load, u32 Size, ARMReg Vd, ARMReg Rn, int lane, bool aligned, ARMReg Rm) { - u32 spacing = RegCountToType(regCount, align); // Only support loading to 1 reg - // Gets encoded as a double register + bool register_quad = Vd >= Q0; + + Vd = SubBase(Vd); + // Support quad lanes by converting to D lanes + if (register_quad && lane > 1) { + Vd = (ARMReg)((int)Vd + 1); + lane -= 2; + } + int encSize = encodedSize(Size); + int index_align = 0; + switch (encSize) { + case 0: index_align = lane << 1; break; + case 1: index_align = lane << 2; if (aligned) index_align |= 1; break; + case 2: index_align = lane << 3; if (aligned) index_align |= 3; break; + default: + break; + } + + Write32((0xF4 << 24) | (1 << 23) | ((Vd & 0x10) << 18) | (load << 21) | (Rn << 16) + | ((Vd & 0xF) << 12) | (encSize << 10) + | (index_align << 4) | Rm); +} + +void ARMXEmitter::VLD1_lane(u32 Size, ARMReg Vd, ARMReg Rn, int lane, bool aligned, ARMReg Rm) { + VLDST1_lane(true, Size, Vd, Rn, lane, aligned, Rm); +} + +void ARMXEmitter::VST1_lane(u32 Size, ARMReg Vd, ARMReg Rn, int lane, bool aligned, ARMReg Rm) { + VLDST1_lane(false, Size, Vd, Rn, lane, aligned, Rm); +} + +void ARMXEmitter::VLD1_all_lanes(u32 Size, ARMReg Vd, ARMReg Rn, bool aligned, ARMReg Rm) { + bool register_quad = Vd >= Q0; + Vd = SubBase(Vd); - Write32((0xF4 << 24) | ((Vd & 0x10) << 18) | (Rn << 16) - | ((Vd & 0xF) << 12) | (spacing << 8) | (encodedSize(Size) << 6) - | (align << 4) | Rm); + int T = register_quad; // two D registers + + Write32((0xF4 << 24) | (1 << 23) | ((Vd & 0x10) << 18) | (1 << 21) | (Rn << 16) + | ((Vd & 0xF) << 12) | (0xC << 8) | (encodedSize(Size) << 6) + | (T << 5) | (aligned << 4) | Rm); } -void ARMXEmitter::VLD1_lane(u32 Size, ARMReg Vd, ARMReg Rn, int lane, ARMReg Rm) { - _dbg_assert_msg_(JIT, false, "VLD1_lane not done yet"); - // TODO -} - -void ARMXEmitter::VST1_lane(u32 Size, ARMReg Vd, ARMReg Rn, int lane, ARMReg Rm) { - _dbg_assert_msg_(JIT, false, "VST1_lane not done yet"); - // TODO -} - - - -void ARMXEmitter::VLD2(u32 Size, ARMReg Vd, ARMReg Rn, NEONAlignment align, ARMReg Rm) +/* +void ARMXEmitter::VLD2(u32 Size, ARMReg Vd, ARMReg Rn, int regCount, NEONAlignment align, ARMReg Rm) { u32 spacing = 0x8; // Single spaced registers // Gets encoded as a double register @@ -2415,6 +2459,7 @@ void ARMXEmitter::VLD2(u32 Size, ARMReg Vd, ARMReg Rn, NEONAlignment align, ARMR | ((Vd & 0xF) << 12) | (spacing << 8) | (encodedSize(Size) << 6) | (align << 4) | Rm); } +*/ void ARMXEmitter::VREVX(u32 size, u32 Size, ARMReg Vd, ARMReg Vm) { diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index 6bcea2dfd5..a5125fb4bf 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -70,6 +70,9 @@ enum ARMReg // ASIMD Quad-Word registers Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15, + + // for NEON VLD/VST instructions + REG_UPDATE = R13, INVALID_REG = 0xFFFFFFFF }; @@ -360,6 +363,8 @@ u32 EncodeVd(ARMReg Vd); u32 EncodeVn(ARMReg Vn); u32 EncodeVm(ARMReg Vm); +u32 encodedSize(u32 value); + // Subtracts the base from the register to give us the real one ARMReg SubBase(ARMReg Reg); @@ -720,26 +725,44 @@ public: // Notes: // Rm == _PC is interpreted as no offset, otherwise, effective address is sum of Rn and Rm - // Rm == R13 is interpreted as VLD1, .... [Rn]! - + // Rm == R13 is interpreted as VLD1, .... [Rn]! Added a REG_UPDATE pseudo register. // Load/store multiple registers full of elements (a register is a D register) + // Specifying alignment when it can be guaranteed is documented to improve load/store performance. + // For example, when loading a set of four 64-bit registers that we know is 32-byte aligned, we should specify ALIGN_256. + void VLDST1(bool load, u32 Size, ARMReg Vd, ARMReg Rn, int regCount, NEONAlignment align, ARMReg Rm); void VLD1(u32 Size, ARMReg Vd, ARMReg Rn, int regCount, NEONAlignment align = ALIGN_NONE, ARMReg Rm = _PC); void VST1(u32 Size, ARMReg Vd, ARMReg Rn, int regCount, NEONAlignment align = ALIGN_NONE, ARMReg Rm = _PC); // Load/store single lanes of D registers // TODO - void VLD1_lane(u32 Size, ARMReg Vd, ARMReg Rn, int lane, ARMReg Rm = _PC); - void VST1_lane(u32 Size, ARMReg Vd, ARMReg Rn, int lane, ARMReg Rm = _PC); - - - // TODO: Make quad-oriented wrappers for the above. - + void VLDST1_lane(bool load, u32 Size, ARMReg Vd, ARMReg Rn, int lane, bool aligned, ARMReg Rm); + void VLD1_lane(u32 Size, ARMReg Vd, ARMReg Rn, int lane, bool aligned, ARMReg Rm = _PC); + void VST1_lane(u32 Size, ARMReg Vd, ARMReg Rn, int lane, bool aligned, ARMReg Rm = _PC); + // Load one value into all lanes of a D or a Q register (either supported, all formats should work). + void VLD1_all_lanes(u32 Size, ARMReg Vd, ARMReg Rn, bool aligned, ARMReg Rm = _PC); + /* // Deinterleave two loads... or something. TODO - void VLD2(u32 Size, ARMReg Vd, ARMReg Rn, NEONAlignment align = ALIGN_NONE, ARMReg Rm = _PC); + void VLD2(u32 Size, ARMReg Vd, ARMReg Rn, int regCount, NEONAlignment align = ALIGN_NONE, ARMReg Rm = _PC); + void VST2(u32 Size, ARMReg Vd, ARMReg Rn, int regCount, NEONAlignment align = ALIGN_NONE, ARMReg Rm = _PC); + void VLD2_lane(u32 Size, ARMReg Vd, ARMReg Rn, int lane, ARMReg Rm = _PC); + void VST2_lane(u32 Size, ARMReg Vd, ARMReg Rn, int lane, ARMReg Rm = _PC); + + void VLD3(u32 Size, ARMReg Vd, ARMReg Rn, int regCount, NEONAlignment align = ALIGN_NONE, ARMReg Rm = _PC); + void VST3(u32 Size, ARMReg Vd, ARMReg Rn, int regCount, NEONAlignment align = ALIGN_NONE, ARMReg Rm = _PC); + + void VLD3_lane(u32 Size, ARMReg Vd, ARMReg Rn, int lane, ARMReg Rm = _PC); + void VST3_lane(u32 Size, ARMReg Vd, ARMReg Rn, int lane, ARMReg Rm = _PC); + + void VLD4(u32 Size, ARMReg Vd, ARMReg Rn, int regCount, NEONAlignment align = ALIGN_NONE, ARMReg Rm = _PC); + void VST4(u32 Size, ARMReg Vd, ARMReg Rn, int regCount, NEONAlignment align = ALIGN_NONE, ARMReg Rm = _PC); + + void VLD4_lane(u32 Size, ARMReg Vd, ARMReg Rn, int lane, ARMReg Rm = _PC); + void VST4_lane(u32 Size, ARMReg Vd, ARMReg Rn, int lane, ARMReg Rm = _PC); + */ void VMRS_APSR(); void VMRS(ARMReg Rt); @@ -761,22 +784,6 @@ public: void CMPI2R(ARMReg rs, u32 val, ARMReg scratch); void TSTI2R(ARMReg rs, u32 val, ARMReg scratch); void ORI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch); - - inline u32 encodedSize(u32 value) - { - if (value & I_8) - return 0; - else if (value & I_16) - return 1; - else if ((value & I_32) || (value & F_32)) - return 2; - else if (value & I_64) - return 3; - else - _dbg_assert_msg_(JIT, false, "Passed invalid size to integer NEON instruction"); - return 0; - } - }; // class ARMXEmitter