From cbdbc601dc526a10d474eba1d486a17b9a2b60ce Mon Sep 17 00:00:00 2001 From: hamb3r <48532909+hamb3r@users.noreply.github.com> Date: Wed, 21 Oct 2026 18:11:13 +0300 Subject: [PATCH] fix(shader): keep undecoded OpExtInst and runtime logical guards in specialization --- .../prx/libSceAgcDriver/tests/SpirvModule.cpp | 11 --------- .../SpirvBackend/src/SpirvRuntimeBuffer.cpp | 8 +++---- .../SpirvBackend/src/SpirvSpecialization.cpp | 24 ++----------------- 3 files changed, 6 insertions(+), 37 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/SpirvModule.cpp b/core/libs/prx/libSceAgcDriver/tests/SpirvModule.cpp index d78cb62a9f..00796cd2b5 100644 --- a/core/libs/prx/libSceAgcDriver/tests/SpirvModule.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/SpirvModule.cpp @@ -322,8 +322,6 @@ void testPrivateCompositeTableLoadFolding() { const auto input = module.DefineGlobalVariable(typeInputPtr, spv::StorageClassInput); const auto table = module.DefineInitializedGlobalVariable(typeTablePtr, spv::StorageClassPrivate, initial); const auto output = module.DefineGlobalVariable(typePrivatePtr, spv::StorageClassPrivate); - const auto glsl = module.Import("GLSL.std.450"); - const auto customSet = module.Import("NonSemantic.Test"); const auto mainId = module.AllocateId(); const auto lane = module.AllocateId(); const auto rawCond = module.AllocateId(); @@ -341,11 +339,6 @@ void testPrivateCompositeTableLoadFolding() { module.AddFunction(accessOp, typePrivatePtr, writePtr, table, zero); module.AddFunction(spv::OpStore, writePtr, lane); } - const auto liveGlsl = module.AllocateId(); - module.AddFunction(spv::OpExtInst, typeU32, module.AllocateId(), glsl, 37u, lane, zero); - module.AddFunction(spv::OpExtInst, typeU32, liveGlsl, glsl, 37u, lane, one); - module.AddFunction(spv::OpStore, output, liveGlsl); - module.AddFunction(spv::OpExtInst, typeU32, module.AllocateId(), customSet, 1u, lane); module.AddFunction(spv::OpUGreaterThan, typeBool, rawCond, lane, zero); module.AddFunction(spv::OpLogicalOr, typeBool, cond, rawCond, falseVal); module.AddFunction(spv::OpIAdd, typeU32, baseIndex, one, one); @@ -364,16 +357,12 @@ void testPrivateCompositeTableLoadFolding() { check(!foldedCounts.contains(spv::OpLogicalOr), "a right-false OpLogicalOr was not simplified"); check(!foldedCounts.contains(spv::OpSelect), "an identical-arm OpSelect was not simplified"); check(foldedCounts.contains(spv::OpLoad) && foldedCounts.at(spv::OpLoad) == 1u, "a constant-index private composite table load was not folded"); - check(foldedCounts.contains(spv::OpExtInst) && foldedCounts.at(spv::OpExtInst) == 2u, "an unused GLSL.std.450 OpExtInst was not removed or a live/non-GLSL.std.450 OpExtInst was removed"); bool addedThirty = false; - bool keptCustomExtInst = false; for (std::size_t cursor = 5; cursor < folded.size(); cursor += folded[cursor] >> 16u) { if ((folded[cursor] >> 16u) == 0u) break; if ((folded[cursor] & 0xffffu) == spv::OpIAdd && folded[cursor + 4u] == expectedConstant) addedThirty = true; - if ((folded[cursor] & 0xffffu) == spv::OpExtInst && folded[cursor + 4u] == 1u) keptCustomExtInst = true; } check(addedThirty, "a constant-index private composite table load did not fold to its element constant"); - check(keptCustomExtInst, "a non-GLSL.std.450 OpExtInst was not preserved"); const auto [dynamic, unusedDynamic] = build(true, false); static_cast(unusedDynamic); diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvRuntimeBuffer.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvRuntimeBuffer.cpp index 433c58aad1..35d5e131dd 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvRuntimeBuffer.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvRuntimeBuffer.cpp @@ -446,7 +446,7 @@ std::uint32_t genericFormattedLoad(SpirvValueEmitContext& context, const IrValue const auto fromMemory = Binary(state, spv::OpUGreaterThanEqual, boolean, selectors[output], constant(4u)); required[component] = Binary(state, spv::OpLogicalOr, boolean, required[component], both(state, fromMemory, equal(state, selected[output], constant(component)))); } - required[component] = both(state, active[component], required[component]); + required[component] = Select(state, boolean, active[component], required[component], ConstantBool(state, false)); const auto componentOffset = field(state, byteOffsets, component * 8u, 8u); const auto bytes = Select(state, u32, packed, constant(4u), Binary(state, spv::OpShiftRightLogical, u32, bits[component], constant(3u))); const auto offset = Binary(state, spv::OpIAdd, u32, resource.offset != 0u ? resource.offset : Binary(state, spv::OpIAdd, u32, context.Arg(instruction, 2u), constant(memory.offset)), componentOffset); @@ -454,16 +454,16 @@ std::uint32_t genericFormattedLoad(SpirvValueEmitContext& context, const IrValue if (memory.gpuDescriptor) { const auto u64 = TypeScalarU64(state); const auto guest = Binary(state, spv::OpIAdd, u64, resource.base, Unary(state, spv::OpUConvert, u64, addresses[component].guest)); - const auto overflow = both(state, active[component], Binary(state, spv::OpULessThan, boolean, guest, resource.base)); + const auto overflow = Select(state, boolean, active[component], Binary(state, spv::OpULessThan, boolean, guest, resource.base), ConstantBool(state, false)); EmitIfCondition(state, overflow, [&] { RecordBdaFault(state, resource.base, constant(0u), BdaInstructionPc(state, instruction), BdaAbi::FaultReason::Overflow); }); StopBdaInvocationIf(state, overflow); addresses[component].guest = guest; } - inBounds = both(state, inBounds, Binary(state, spv::OpLogicalOr, boolean, Unary(state, spv::OpLogicalNot, boolean, required[component]), addresses[component].inBounds)); + inBounds = Select(state, boolean, required[component], both(state, inBounds, addresses[component].inBounds), inBounds); } std::array values{}; for (std::uint32_t component = 0; component < 4u; ++component) { - values[component] = EmitValueOrZeroIfCondition(state, both(state, inBounds, required[component]), [&] { + values[component] = EmitValueOrZeroIfCondition(state, Select(state, boolean, required[component], inBounds, ConstantBool(state, false)), [&] { const auto width = Select(state, u32, packed, constant(32u), bits[component]); const auto dword = equal(state, width, constant(32u)); const auto raw = memory.gpuDescriptor diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvSpecialization.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvSpecialization.cpp index 2b7b1eb792..b20fa14351 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvSpecialization.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvSpecialization.cpp @@ -100,7 +100,7 @@ private: std::vector present; }; -bool PureInstruction(const Instruction& instruction, const IdSet& glslExtInstSets = {}) { +bool PureInstruction(const Instruction& instruction) { switch (Opcode(instruction)) { case spv::OpVectorExtractDynamic: case spv::OpVectorInsertDynamic: @@ -219,8 +219,6 @@ bool PureInstruction(const Instruction& instruction, const IdSet& glslExtInstSet case spv::OpArrayLength: case spv::OpPhi: return true; - case spv::OpExtInst: - return instruction.size() >= 5u && glslExtInstSets.contains(instruction[3]); case spv::OpLoad: return instruction.size() == 4u || (instruction.size() == 5u && instruction[4] == spv::MemoryAccessMaskNone); default: @@ -233,9 +231,6 @@ bool VisitInputs(const Instruction& instruction, const TVisitor& visit) { std::size_t first = 3u; std::size_t end = instruction.size(); switch (Opcode(instruction)) { - case spv::OpExtInst: - first = 5u; - break; case spv::OpVectorShuffle: case spv::OpCompositeInsert: end = 5u; @@ -583,7 +578,6 @@ public: values.Reset(bound); resultTypes.Reset(bound); vectorTypes.Reset(bound); - glslExtInstSets.Reset(bound); removed.Reset(bound); aliases.Reset(bound); composites.Reset(bound); @@ -598,7 +592,6 @@ public: bool hasType = false; spv::HasResultAndType(op, &hasResult, &hasType); if (hasResult && hasType) resultTypes.emplace(instruction.at(2), instruction.at(1)); - if (op == spv::OpExtInstImport && instruction.size() == 6u && instruction[2] == 0x4c534c47u && instruction[3] == 0x6474732eu && instruction[4] == 0x3035342eu && instruction[5] == 0u) glslExtInstSets.insert(instruction.at(1)); if (op == spv::OpTypeBool) types.emplace(instruction.at(1), ScalarType{1u, true}); if (op == spv::OpTypeInt) types.emplace(instruction.at(1), ScalarType{instruction.at(2), false}); if (op == spv::OpTypeVector) vectorTypes.insert(instruction.at(1)); @@ -680,18 +673,6 @@ private: return pointer->second < elements.size() ? value(elements[pointer->second]) : std::nullopt; } const auto left = value(instruction[3]); - if (op == spv::OpLogicalAnd && instruction.size() == 5u) { - const auto right = value(instruction[4]); - if ((left && *left == 0u) || (right && *right == 0u)) return 0u; - if (left && right) return *left != 0u && *right != 0u; - return std::nullopt; - } - if (op == spv::OpLogicalOr && instruction.size() == 5u) { - const auto right = value(instruction[4]); - if ((left && *left != 0u) || (right && *right != 0u)) return 1u; - if (left && right) return *left != 0u || *right != 0u; - return std::nullopt; - } if (!left) return std::nullopt; if (op == spv::OpCopyObject) return left; if (op == spv::OpLogicalNot) return *left == 0u; @@ -1128,7 +1109,7 @@ private: } continue; } - if (PureInstruction(instruction, glslExtInstSets)) { + if (PureInstruction(instruction)) { definitions.emplace(Result(instruction), index); continue; } @@ -1165,7 +1146,6 @@ private: IdTable values; IdTable resultTypes; IdSet vectorTypes; - IdSet glslExtInstSets; IdSet removed; IdTable aliases; IdTable> composites;