diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp index ff82ee6bb..b3ea4c7cf 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp @@ -56,23 +56,6 @@ uint32_t EmitMulHigh(EmitterState& state, uint32_t lhs, uint32_t rhs, bool signe return signed_value ? Unary(state, spv::OpBitcast, TypeU32(state), high) : high; } -uint32_t EmitMinMax3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c, bool signed_value, - bool max_value) { - const auto ab = signed_value ? EmitMinMaxI32Value(state, a, b, max_value) - : EmitMinMaxU32Value(state, a, b, max_value); - return signed_value ? EmitMinMaxI32Value(state, ab, c, max_value) - : EmitMinMaxU32Value(state, ab, c, max_value); -} - -uint32_t EmitMed3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c, bool signed_value) { - const auto minimum = EmitMinMax3(state, a, b, c, signed_value, false); - const auto maximum = EmitMinMax3(state, a, b, c, signed_value, true); - const auto ab = Binary(state, spv::OpIAdd, TypeU32(state), a, b); - const auto abc = Binary(state, spv::OpIAdd, TypeU32(state), ab, c); - return Binary(state, spv::OpISub, TypeU32(state), - Binary(state, spv::OpISub, TypeU32(state), abc, minimum), maximum); -} - uint32_t EmitFMinMax3(EmitterState& state, uint32_t a, uint32_t b, uint32_t c, bool max_value) { return EmitMinMaxF32Value(state, EmitMinMaxF32Value(state, a, b, max_value), c, max_value); } @@ -246,14 +229,6 @@ uint32_t EmitUMulHi(EmitterState& state, uint32_t arg0, uint32_t arg1) { return EmitMulHigh(state, arg0, arg1, false); } -uint32_t EmitIAbs32(EmitterState& state, uint32_t arg0) { - const auto value = arg0; - const auto neg = Unary(state, spv::OpSNegate, TypeU32(state), value); - const auto negative = - Binary(state, spv::OpSLessThan, TypeBool(state), value, ConstantU32(state, 0)); - return Select(state, TypeU32(state), negative, neg, value); -} - uint32_t EmitBitCount64(EmitterState& state, uint32_t arg0) { // Vulkan bit-count operands remain 32-bit, including when counting a U64. const auto unpacked = Unary(state, spv::OpBitcast, TypeU32Vector(state, 2), arg0); @@ -262,54 +237,30 @@ uint32_t EmitBitCount64(EmitterState& state, uint32_t arg0) { return Binary(state, spv::OpIAdd, TypeU32(state), pair.low, pair.high); } -uint32_t EmitFindILsb32(EmitterState& state, uint32_t arg0) { - const auto value = EmitExt(state, TypeI32(state), GLSLstd450FindILsb, {arg0}); - return Unary(state, spv::OpBitcast, TypeU32(state), value); -} - -uint32_t EmitFindUMsb32(EmitterState& state, uint32_t arg0) { - const auto value = EmitExt(state, TypeI32(state), GLSLstd450FindUMsb, {arg0}); - return Unary(state, spv::OpBitcast, TypeU32(state), value); -} - -uint32_t EmitSMin32(EmitterState& state, uint32_t arg0, uint32_t arg1) { - return EmitMinMaxI32Value(state, arg0, arg1, false); -} - -uint32_t EmitSMax32(EmitterState& state, uint32_t arg0, uint32_t arg1) { - return EmitMinMaxI32Value(state, arg0, arg1, true); -} - -uint32_t EmitUMin32(EmitterState& state, uint32_t arg0, uint32_t arg1) { - return EmitMinMaxU32Value(state, arg0, arg1, false); -} - -uint32_t EmitUMax32(EmitterState& state, uint32_t arg0, uint32_t arg1) { - return EmitMinMaxU32Value(state, arg0, arg1, true); -} - uint32_t EmitSMinTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2) { - return EmitMinMax3(state, arg0, arg1, arg2, true, false); + return EmitSMin32(state, arg0, EmitSMin32(state, arg1, arg2)); } uint32_t EmitSMaxTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2) { - return EmitMinMax3(state, arg0, arg1, arg2, true, true); + return EmitSMax32(state, arg0, EmitSMax32(state, arg1, arg2)); } uint32_t EmitUMinTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2) { - return EmitMinMax3(state, arg0, arg1, arg2, false, false); + return EmitUMin32(state, arg0, EmitUMin32(state, arg1, arg2)); } uint32_t EmitUMaxTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2) { - return EmitMinMax3(state, arg0, arg1, arg2, false, true); + return EmitUMax32(state, arg0, EmitUMax32(state, arg1, arg2)); } uint32_t EmitSMedTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2) { - return EmitMed3(state, arg0, arg1, arg2, true); + const auto high_min = EmitSMin32(state, EmitSMax32(state, arg0, arg1), arg2); + return EmitSMax32(state, EmitSMin32(state, arg0, arg1), high_min); } uint32_t EmitUMedTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2) { - return EmitMed3(state, arg0, arg1, arg2, false); + const auto high_min = EmitUMin32(state, EmitUMax32(state, arg0, arg1), arg2); + return EmitUMax32(state, EmitUMin32(state, arg0, arg1), high_min); } uint32_t EmitFPIsNan32(EmitterState& state, uint32_t arg0) { diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAluHelpers.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAluHelpers.cpp index b35f8a8ec..02a6227e4 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAluHelpers.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAluHelpers.cpp @@ -82,24 +82,6 @@ uint32_t EmitF32ToF16RtzBits(EmitterState& state, uint32_t f32) { return EmitAndConstant(state, EmitSelectValueU32(state, exp_eq_255, special, finite2), 0xffffu); } -uint32_t EmitMinMaxU32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value) { - const auto cond = state.builder.AllocateId(); - const auto ret = state.builder.AllocateId(); - state.builder.AddFunction(max_value ? spv::OpUGreaterThan : spv::OpULessThan, TypeBool(state), - cond, lhs, rhs); - state.builder.AddFunction(spv::OpSelect, TypeU32(state), ret, cond, lhs, rhs); - return ret; -} - -uint32_t EmitMinMaxI32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value) { - const auto cond = state.builder.AllocateId(); - const auto ret = state.builder.AllocateId(); - state.builder.AddFunction(max_value ? spv::OpSGreaterThan : spv::OpSLessThan, TypeBool(state), - cond, lhs, rhs); - state.builder.AddFunction(spv::OpSelect, TypeU32(state), ret, cond, lhs, rhs); - return ret; -} - F32Class EmitClassifyF32Bits(EmitterState& state, uint32_t bits) { F32Class cls; cls.bits = bits; diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h index 96b67ac07..c358f643c 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h @@ -23,13 +23,8 @@ uint32_t EmitConvertF16F32(EmitterState& state, uint32_t arg0); inline constexpr auto EmitConvertF32F16 = EmitF16BitsToF32; uint32_t EmitConvertS32F32(EmitterState& state, uint32_t arg0); uint32_t EmitConvertU32F32(EmitterState& state, uint32_t arg0); -template -uint32_t EmitConvertSigned32ToFloat(EmitterState& state, uint32_t arg0) { - const auto signed_value = Unary(state, spv::OpBitcast, TypeI32(state), arg0); - return EmitNative(state, signed_value); -} -inline constexpr auto EmitConvertF32S32 = EmitConvertSigned32ToFloat; -inline constexpr auto EmitConvertF64S32 = EmitConvertSigned32ToFloat; +EMIT_NATIVE(ConvertF32S32, OpConvertSToF, F32, uint32_t) +EMIT_NATIVE(ConvertF64S32, OpConvertSToF, F64, uint32_t) uint32_t EmitConvertF32F64(EmitterState& state, uint32_t arg0); uint32_t EmitConvertF64F32(EmitterState& state, uint32_t arg0); EMIT_NATIVE(ConvertF32U32, OpConvertUToF, F32, uint32_t) @@ -69,7 +64,7 @@ EMIT_NATIVE(IMul64, OpIMul, U64, uint32_t, uint32_t) EMIT_NATIVE(IAddCarry32, OpIAddCarry, U32x2, uint32_t, uint32_t) uint32_t EmitSMulHi(EmitterState& state, uint32_t arg0, uint32_t arg1); uint32_t EmitUMulHi(EmitterState& state, uint32_t arg0, uint32_t arg1); -uint32_t EmitIAbs32(EmitterState& state, uint32_t arg0); +inline constexpr auto EmitIAbs32 = EmitGlsl; EMIT_NATIVE(ShiftLeftLogical32, OpShiftLeftLogical, U32, uint32_t, uint32_t) EMIT_NATIVE(ShiftRightLogical32, OpShiftRightLogical, U32, uint32_t, uint32_t) EMIT_NATIVE(ShiftRightArithmetic32, OpShiftRightArithmetic, U32, uint32_t, uint32_t) @@ -96,13 +91,13 @@ EMIT_NATIVE(BitwiseAnd64, OpBitwiseAnd, U64, uint32_t, uint32_t) EMIT_NATIVE(BitReverse32, OpBitReverse, U32, uint32_t) EMIT_NATIVE(BitCount32, OpBitCount, U32, uint32_t) uint32_t EmitBitCount64(EmitterState& state, uint32_t arg0); -uint32_t EmitFindILsb32(EmitterState& state, uint32_t arg0); -uint32_t EmitFindUMsb32(EmitterState& state, uint32_t arg0); +inline constexpr auto EmitFindILsb32 = EmitGlsl; +inline constexpr auto EmitFindUMsb32 = EmitGlsl; uint32_t EmitFindUMsb64(EmitterState& state, uint32_t arg0); -uint32_t EmitSMin32(EmitterState& state, uint32_t arg0, uint32_t arg1); -uint32_t EmitSMax32(EmitterState& state, uint32_t arg0, uint32_t arg1); -uint32_t EmitUMin32(EmitterState& state, uint32_t arg0, uint32_t arg1); -uint32_t EmitUMax32(EmitterState& state, uint32_t arg0, uint32_t arg1); +inline constexpr auto EmitSMin32 = EmitGlsl; +inline constexpr auto EmitSMax32 = EmitGlsl; +inline constexpr auto EmitUMin32 = EmitGlsl; +inline constexpr auto EmitUMax32 = EmitGlsl; uint32_t EmitSMinTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2); uint32_t EmitSMaxTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2); uint32_t EmitUMinTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2); diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h index ba02bf0fb..411b1ee25 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h @@ -471,10 +471,6 @@ uint32_t EmitSubConstantMinusU32(EmitterState& state, uint32_t constant, uint32_ uint32_t EmitF32ToF16RtzBits(EmitterState& state, uint32_t f32); -uint32_t EmitMinMaxU32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value); - -uint32_t EmitMinMaxI32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value); - inline constexpr auto EmitBitcastF32ToU32 = EmitNative; inline constexpr auto EmitBitcastU32ToF32 = EmitNative; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 623209b60..6b9ae9940 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -19729,27 +19729,34 @@ TestCase ScalarAbsI32UpdatesScc() { code.push_back(EncodeSop2(0x0a, dst_sgpr, InlineU32(1), InlineU32(0))); }; - code.push_back(EncodeSMovB32(0, InlineU32(0))); + AppendSmemLoadOpcode(&code, 0x08, 16, 0); set_scc(true); - code.push_back(EncodeSop1(0x34, 1, 0)); - capture_scc(2); + code.push_back(EncodeSop1(0x34, 17, 16)); + capture_scc(18); - AppendSMovLiteral(&code, 3, 0xfffffffbu); + AppendSmemLoadOpcode(&code, 0x08, 19, 4); set_scc(false); - code.push_back(EncodeSop1(0x34, 4, 3)); - capture_scc(5); + code.push_back(EncodeSop1(0x34, 20, 19)); + capture_scc(21); - AppendStoreSgpr(&code, 1, 0); - AppendStoreSgpr(&code, 2, 1); - AppendStoreSgpr(&code, 4, 2); - AppendStoreSgpr(&code, 5, 3); + AppendSmemLoadOpcode(&code, 0x08, 22, 8); + set_scc(false); + code.push_back(EncodeSop1(0x34, 23, 22)); + capture_scc(24); + + AppendStoreSgpr(&code, 17, 0); + AppendStoreSgpr(&code, 18, 1); + AppendStoreSgpr(&code, 20, 2); + AppendStoreSgpr(&code, 21, 3); + AppendStoreSgpr(&code, 23, 4); + AppendStoreSgpr(&code, 24, 5); AppendEnd(&code); return {"ScalarAbsI32UpdatesScc", code, - {}, - {0, 0, 5, 1}, - {O::S_MOV_B32, O::S_CMP_EQ_U32, O::S_ABS_I32, O::S_CSELECT_B32, + {0, 0xfffffffbu, 0x80000000u}, + {0, 0, 5, 1, 0x80000000u, 1}, + {O::S_BUFFER_LOAD_DWORD, O::S_CMP_EQ_U32, O::S_ABS_I32, O::S_CSELECT_B32, O::V_MOV_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; } @@ -21887,6 +21894,62 @@ TestCase VectorIntegerOps() { O::S_ENDPGM}}; } +TestCase VectorIntegerExtremaRuntimeEdges() { + using O = ShaderOpcode; + constexpr std::array, 8> inputs{{ + {0x80000000u, 0u, 0x7fffffffu}, + {0xffffffffu, 1u, 0x80000000u}, + {1u, 0x80000000u, 0xffffffffu}, + {1u, 0xffffffffu, 0x80000000u}, + {0x7fffffffu, 0x7fffffffu, 0x80000000u}, + {0x80000000u, 0x80000000u, 0xffffffffu}, + {0xffffffffu, 0xffffffffu, 0xffffffffu}, + {0u, 0u, 0u}, + }}; + TestCase test; + test.name = "VectorIntegerExtremaRuntimeEdges"; + for (const auto &values : inputs) + test.initial.insert(test.initial.end(), values.begin(), values.end()); + test.expected = test.initial; + for (u32 i = 0; i < inputs.size(); ++i) { + for (u32 reg = 0; reg < 3; ++reg) { + AppendVMovU32(&test.code, 30, (i * 3u + reg) * 4u); + AppendBufferLoadDword(&test.code, reg, 30); + } + for (u32 op = 0; op < 4; ++op) + test.code.push_back(EncodeVop2(0x11u + op, 4u + op, Vgpr(0), 1)); + constexpr std::array opcodes{0x152u, 0x155u, 0x158u, 0x153u, 0x156u, 0x159u}; + for (u32 op = 0; op < opcodes.size(); ++op) + AppendVop3(&test.code, opcodes[op], 8u + op, Vgpr(0), Vgpr(1), Vgpr(2)); + const auto output = static_cast(test.expected.size()); + for (u32 result = 0; result < 10; ++result) + AppendStoreVgpr(&test.code, 4u + result, output + result); + + auto unsigned_values = inputs[i]; + std::array signed_values; + for (u32 j = 0; j < 3; ++j) + signed_values[j] = std::bit_cast(unsigned_values[j]); + test.expected.insert(test.expected.end(), { + static_cast(std::min(signed_values[0], signed_values[1])), + static_cast(std::max(signed_values[0], signed_values[1])), + std::min(unsigned_values[0], unsigned_values[1]), + std::max(unsigned_values[0], unsigned_values[1])}); + std::sort(signed_values.begin(), signed_values.end()); + std::sort(unsigned_values.begin(), unsigned_values.end()); + test.expected.insert(test.expected.end(), { + static_cast(signed_values[0]), static_cast(signed_values[2]), + static_cast(signed_values[1]), unsigned_values[0], + unsigned_values[2], unsigned_values[1]}); + } + AppendEnd(&test.code); + test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_MIN_I32, O::V_MAX_I32, + O::V_MIN_U32, O::V_MAX_U32, O::V_MIN3_I32, O::V_MAX3_I32, + O::V_MED3_I32, O::V_MIN3_U32, O::V_MAX3_U32, O::V_MED3_U32, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.required_spirv = {" SMin ", " SMax ", " UMin ", " UMax "}; + return test; +} + TestCase VectorFfbhI32NativeAndVop3OnGpu() { using O = ShaderOpcode; @@ -22731,7 +22794,7 @@ TestCase Vop3Min3U16CapturedAndSelectors() { test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_MIN3_U16, O::S_MOV_B64, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; test.decoded_counts = {{"V_MIN3_U16", cases.size()}}; - test.required_spirv = {"OpULessThan"}; + test.required_spirv = {" UMin "}; return test; } @@ -22757,7 +22820,7 @@ TestCase Vop3Med3I16Captured() { test.decoded_counts = {{"0x00000020: V_MED3_I16 v3.sdwa(sel=5,sext=0), " "v5.opsel(lo=1,hi=0,neghi=0), s25, s27\n", 1}}; - test.required_spirv = {"OpSLessThan"}; + test.required_spirv = {" SMin ", " SMax "}; return test; } @@ -22783,7 +22846,7 @@ TestCase Vop2SdwaMinU32PreservesWordDestination() { test.expected = {0xa1b20007u, 0xa1b2001fu}; test.opcodes = {O::V_MOV_B32, O::V_MIN_U32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; - test.required_spirv = {"OpULessThan", "OpSelect"}; + test.required_spirv = {" UMin ", "OpSelect"}; return test; } @@ -36327,6 +36390,7 @@ std::vector MakeCases() { AddCase(VectorMoves); AddCase(VectorVop3MoveAppliesFloatSourceModifiers); AddCase(VectorIntegerOps); + AddCase(VectorIntegerExtremaRuntimeEdges); AddCase(VectorFfbhI32NativeAndVop3OnGpu); AddCase(Vop1SdwaBfrevSourceSelectors); AddCase(Vop1SdwaFfbhCapturedScalarLowWordSource);