shader: validate the IR once, at SPIR-V emission

This commit is contained in:
nmzik committed 2026-10-30 10:12:37 +01:00
1 parent 216bd28bf6
commit 4919fdb6e6
7 files changed
+47 -18

No files matched your search

+3 -3
View File
@@ -319,7 +319,7 @@ void TestInvariantIndirectImageMaterialization(bool nested) {
fixture->PlanAndTrack();
auto plan = ExtractResourcePlan(fixture->program);
EliminateDeadCode(fixture->program.blocks);
ValidateProgram(fixture->program, true);
ValidateProgram(fixture->program);
const auto &source = *plan.descriptor_sources[plan.info.images[0].source].indirect_descriptor;
Check(!source.selector && fixture->program.info.buffers.size() == (nested ? 3u : 2u) &&
std::ranges::any_of(*fixture->block, [](const Inst &inst) {
@@ -7589,7 +7589,7 @@ ResourcePlan ConditionalSamplerPlan(bool diamond, bool reverse, bool reverse_phi
Check(sampler_words[0].ResolveInstruction()->GetOpcode() == ValueOpcode::Phi,
"host descriptor selection changed the GPU Phi");
EliminateDeadCode(fixture.program.blocks);
ValidateProgram(fixture.program, true);
ValidateProgram(fixture.program);
return ExtractResourcePlan(fixture.program);
}
@@ -7855,7 +7855,7 @@ void TestFiniteImageWaterfall() {
fixture->PlanAndTrack();
const auto plan = ExtractResourcePlan(fixture->program);
EliminateDeadCode(fixture->program.blocks);
ValidateProgram(fixture->program, true);
ValidateProgram(fixture->program);
const auto& finite = plan.descriptor_sources[plan.info.images[0].source].indirect_descriptor;
Check(finite && finite->sources.size() == 3u,
"waterfall image selection mixed descriptor words or lost a candidate");
+3 -3
View File
@@ -788,7 +788,7 @@ void TestCompleteWaveReduction() {
fixture.BlockAt().terminator.kind = Terminator::Branch;
fixture.BlockAt().terminator.true_block = &fixture.BlockAt(1);
}
ValidateProgram(fixture.program, true);
ValidateProgram(fixture.program);
// The pipeline folds full-EXEC predicates to true before eliminating reads.
const uint32_t wave_size = test == Case::Wave32 || rows ? 32u : 64u;
ConstantPropagationPass(fixture.program.blocks, wave_size);
@@ -812,7 +812,7 @@ void TestCompleteWaveReduction() {
}
Check(updates == 2 && preserved.Instruction()->Arg(0).Instruction()->HasUses(),
"wave reduction destroyed an independently consumed prefix or kept its dead suffix");
ValidateProgram(fixture.program, true);
ValidateProgram(fixture.program);
} else {
Check(use.Instruction()->Arg(0).Resolve() == result,
"rejected wave reduction proof changed its original result");
@@ -873,7 +873,7 @@ void TestControlFlowValueSurvivesReadLaneFolding() {
Check(entry_info.condition == Value(true),
"folded branch condition did not survive identity removal");
ValidateProgram(fixture.program, true);
ValidateProgram(fixture.program);
}
void TestDeadPhiCyclesAndPlanningRoots() {
+37 -7
View File
@@ -12778,7 +12778,7 @@ void TestTypedEntryStateIsMinimal() {
IR::RewriteToSsa(values.blocks);
IR::RemoveIdentities(values);
IR::EliminateDeadCode(values.blocks);
IR::ValidateProgram(values, true);
IR::ValidateProgram(values);
};
check(32u);
@@ -12799,13 +12799,42 @@ void TestFinalSsaRejectsRegisterStatePseudos() {
} else {
ir.Emit(ValueOpcode::ReferenceU32, {ir.GetScalarReg(ScalarReg{0})});
}
ExpectFatal([&] { ValidateProgram(program, true); },
ExpectFatal([&] { ValidateProgram(program); },
"final SSA accepted a register-state pseudo operation");
};
check_rejected(false);
check_rejected(true);
}
void TestEmitterValidatesFinalProgram() {
using namespace ShaderRecompiler;
const uint32_t shader[] = {
EncodeMubuf0(0x1c, 0, false), // buffer_store_dword v0
EncodeMubuf1(0, 12, 0),
0xbf810000u,
};
const auto options = MakeCompileOptions(ShaderType::Compute);
auto result = RecompileForTest(shader, options);
// Only emission validates, so a definition moved below its same-block user must be fatal.
bool moved = false;
for (auto *block : result.program.blocks) {
auto &list = block->Instructions();
for (auto user = list.begin(); !moved && user != list.end(); ++user) {
for (size_t arg = 0; !moved && arg < user->NumArgs(); ++arg) {
const auto *def = user->Arg(arg).TryInstruction();
if (def == nullptr || def->Parent() != block) continue;
const auto where = std::ranges::find_if(
list, [&](const IR::Inst &inst) { return &inst == def; });
list.splice(std::next(user), list, where);
moved = true;
}
}
}
Check(moved, "emitter fixture has no same-block definition");
ExpectFatal([&] { Spirv::EmitProgram(result.program, options.input_info); },
"SPIR-V emission accepted a use before its definition");
}
#endif
void TestSsaRewriteSealsFilledBlocks() {
@@ -12888,7 +12917,7 @@ void TestValuePhiValidation() {
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
const auto expect_invalid = [](const Program &program, const char *text) {
ExpectFatal([&] { ValidateProgram(program, true); }, text);
ExpectFatal([&] { ValidateProgram(program); }, text);
};
#endif
const auto validate = [&](InvalidPhi invalid) {
@@ -12940,7 +12969,7 @@ void TestValuePhiValidation() {
}
if (invalid == InvalidPhi::None) {
ValidateProgram(program, true);
ValidateProgram(program);
}
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
else {
@@ -13306,7 +13335,7 @@ void TestLaneMaskProjection() {
gated_raw_consumer.Resolve().TryInstruction()->Arg(0).Resolve() == gated_mask.Resolve(),
"mask projection changed a raw scalar mask consumer");
RemoveIdentities(program);
ValidateProgram(program, true);
ValidateProgram(program);
}
}
@@ -13376,7 +13405,7 @@ void TestLateConstantPropagation() {
"late constants did not reach Phi, nested mask patterns or new logical nodes");
RemoveIdentities(program);
Check(exit->condition == Value(true), "late constant lost its control-flow use");
ValidateProgram(program, true);
ValidateProgram(program);
}
}
@@ -13586,7 +13615,7 @@ void TestNativeWideValueValidation() {
}
};
const auto check_rejected = [](const Program &program, const char *text) {
ExpectFatal([&] { ValidateProgram(program, true); }, text);
ExpectFatal([&] { ValidateProgram(program); }, text);
};
{
@@ -17404,6 +17433,7 @@ int main() {
TestTypedEntryStateIsMinimal();
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
TestFinalSsaRejectsRegisterStatePseudos();
TestEmitterValidatesFinalProgram();
#endif
TestSsaRewriteSealsFilledBlocks();
TestValuePhiValidation();