From d809958b2764dfa493e8ef23ae613bfc92e1274e Mon Sep 17 00:00:00 2001 From: Alex <270190108+CouchRiv@users.noreply.github.com> Date: Sun, 11 Oct 2026 04:23:07 -0400 Subject: [PATCH] fix(shader): fold BitReverse32 of an immediate A descriptor dword built by S_BREV_B32, V_BFREV_B32 or S_BREV_B64 from a constant stayed a runtime value, so the resource could not be resolved and the shader failed to prepare. The constant folder now reverses the bits of an immediate the way the hardware does (D[31-i] = S0[i]), like the existing BitCount32 fold. ConstantBitTest covers the fold. Co-Authored-By: Claude Haiku 5.5 --- .../libSceAgcDriver/tests/ConstantBitTest.cpp | 21 +++++++++++++++++++ .../Optimization/src/ConstantFolder.cpp | 13 ++++++++++++ 2 files changed, 34 insertions(+) diff --git a/core/libs/prx/libSceAgcDriver/tests/ConstantBitTest.cpp b/core/libs/prx/libSceAgcDriver/tests/ConstantBitTest.cpp index 92786750e..38131c019 100644 --- a/core/libs/prx/libSceAgcDriver/tests/ConstantBitTest.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/ConstantBitTest.cpp @@ -28,6 +28,22 @@ bool foldsTo(std::uint32_t word, bool expected, bool maskedShift) { return result->HasImmediate() && result->ImmediateBool() == expected; } +bool reversesTo(std::uint32_t word, std::uint32_t expected) { + IrProgram program; + auto& block = program.CreateBlock(); + program.SetEntryBlock(block); + program.BlockOrder().push_back(&block); + IrBuilder builder(program); + builder.SetInsertionPoint(block); + auto& reversed = builder.Emit(IrOpcode::BitReverse32, IrType::U32, {&builder.Constant(word)}); + auto& test = builder.Emit(IrOpcode::IEqual32, IrType::Bool, {&reversed, &builder.Constant(expected)}); + auto& user = builder.Emit(IrOpcode::Reference, IrType::Void, {&test}); + static_cast(builder.Emit(IrOpcode::Return, IrType::Void, {})); + ConstantFolder().Fold(program); + const IrValue* result = user.Argument(0)->Resolve(); + return result->HasImmediate() && result->ImmediateBool(); +} + bool run(const char* name, bool passed) { if (!passed) std::fprintf(stderr, "%s\n", name); return passed; @@ -42,6 +58,11 @@ int main() { passed &= run("an all-one word must fold the lane bit test to true", foldsTo(0xffffffffu, true, true)); passed &= run("a mixed word must keep the lane bit test", foldsTo(0x0000ffffu, false, true)); passed &= run("an unmasked lane number must keep the lane bit test", foldsTo(0xffffffffu, true, false)); + passed &= run("a reversed zero must fold to zero", reversesTo(0x00000000u, 0x00000000u)); + passed &= run("a reversed single bit must fold to the opposite end", reversesTo(0x00000001u, 0x80000000u)); + passed &= run("a reversed high bit must fold to bit zero", reversesTo(0x80000000u, 0x00000001u)); + passed &= run("a reversed mixed word must fold to its mirror image", reversesTo(0x12345678u, 0x1e6a2c48u)); + passed &= run("a reversed descriptor dword must fold to its mirror image", reversesTo(0xdeadbeefu, 0xf77db57bu)); return passed ? 0 : 1; } catch (const std::exception& error) { std::fprintf(stderr, "%s\n", error.what()); diff --git a/core/shader/recompiler/Optimization/src/ConstantFolder.cpp b/core/shader/recompiler/Optimization/src/ConstantFolder.cpp index a37056fc7..0020b87cb 100644 --- a/core/shader/recompiler/Optimization/src/ConstantFolder.cpp +++ b/core/shader/recompiler/Optimization/src/ConstantFolder.cpp @@ -623,6 +623,19 @@ bool ConstantFolder::tryFoldValue(IrProgram& program, IrValue& value) const { replaceWith(value, builder.Constant(static_cast(std::popcount(operand.ImmediateU64())))); return true; } + case IrOpcode::BitReverse32: { + auto& operand = resolveArg(value, 0); + if (!isImmediate(operand, IrType::U32)) { + return false; + } + const std::uint32_t word = operand.ImmediateU32(); + std::uint32_t reversed = 0; + for (std::uint32_t bit = 0; bit < 32; ++bit) { + reversed |= ((word >> bit) & 1u) << (31u - bit); + } + replaceWith(value, builder.Constant(reversed)); + return true; + } case IrOpcode::SMin32: case IrOpcode::SMax32: { const auto opcode = value.Opcode();