From 3df456675d3f001ff00db90a9f85e11cac0565f1 Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 9 Oct 2026 01:59:41 +0200 Subject: [PATCH] shader: own control flow on IR blocks with direct targets --- .../backend/spirv/spirvEmitterProgram.cpp | 86 ++--- .../frontend/translate/Translate.cpp | 80 +++-- .../frontend/translate/Translator.h | 3 +- src/graphics/shader/recompiler/ir/Block.h | 22 ++ src/graphics/shader/recompiler/ir/Program.cpp | 66 ++-- src/graphics/shader/recompiler/ir/ShaderIR.h | 10 - .../ir/passes/ResourceMaterialization.cpp | 40 +-- .../recompiler/ir/passes/ResourceTracking.cpp | 72 ++-- tests/ResourceMaterializationTests.cpp | 18 +- tests/ResourceTrackingTests.cpp | 331 ++++++++++-------- tests/ScalarProvenanceTests.cpp | 12 +- tests/ShaderRecompilerComputeTests.cpp | 2 - tests/shaderCfgTests.cpp | 207 +++++------ 13 files changed, 477 insertions(+), 472 deletions(-) diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp index ca10e541e..f7111c87b 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp @@ -83,44 +83,31 @@ void StoreDispatcherPhiEdge(ValueEmitContext& ctx, const DispatcherFunctionState } } -const IR::Block* TargetBlock(const IR::Program& program, uint32_t id) { - const auto found = std::ranges::find_if( - program.block_info, [&](const IR::BlockInfo& info) { return info.id == id; }); - if (found == program.block_info.end()) { - return nullptr; - } - return program.blocks[static_cast(found - program.block_info.begin())]; -} - void EmitReturn(ValueEmitContext& ctx) { EmitKillIfPixelValidMaskInactive(ctx.state); ctx.state.builder.AddFunction(spv::OpReturn); } -void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block, - const IR::BlockInfo& info) { - const auto& program = ctx.state.program; - const auto& term = info.terminator; +void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block) { + const auto& term = block->terminator; const auto emit_merge = [&]() { if (term.loop_header) { - const auto* merge = TargetBlock(program, term.merge_block); - const auto* cont = TargetBlock(program, term.continue_block); + const auto* merge = term.merge_block; + const auto* cont = term.continue_block; if (merge != nullptr && cont != nullptr) { ctx.state.builder.AddFunction(spv::OpLoopMerge, merge->Definition(), cont->Definition(), spv::LoopControlMaskNone); } } else if (term.kind == CFG::TerminatorKind::ConditionalBranch && - term.merge_block != UINT32_MAX) { - if (const auto* merge = TargetBlock(program, term.merge_block); merge != nullptr) { - ctx.state.builder.AddFunction(spv::OpSelectionMerge, merge->Definition(), - spv::SelectionControlMaskNone); - } + term.merge_block != nullptr) { + ctx.state.builder.AddFunction(spv::OpSelectionMerge, term.merge_block->Definition(), + spv::SelectionControlMaskNone); } }; switch (term.kind) { case CFG::TerminatorKind::Branch: { - const auto* target = TargetBlock(program, term.true_block); + const auto* target = term.true_block; if (target == nullptr) { EmitReturn(ctx); return; @@ -130,13 +117,13 @@ void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block, return; } case CFG::TerminatorKind::ConditionalBranch: { - const auto* true_block = TargetBlock(program, term.true_block); - const auto* false_block = TargetBlock(program, term.false_block); - if (true_block == nullptr || false_block == nullptr || info.condition.IsEmpty()) { + const auto* true_block = term.true_block; + const auto* false_block = term.false_block; + if (true_block == nullptr || false_block == nullptr || block->condition.IsEmpty()) { EmitReturn(ctx); return; } - const auto condition = ctx.Def(info.condition); + const auto condition = ctx.Def(block->condition); emit_merge(); ctx.state.builder.AddFunction(spv::OpBranchConditional, condition, true_block->Definition(), false_block->Definition()); @@ -147,8 +134,7 @@ void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block, } void EmitDispatcherTarget(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher, - const IR::Block* from, uint32_t target) { - const auto* block = TargetBlock(ctx.state.program, target); + const IR::Block* from, const IR::Block* block) { if (block != nullptr) { StoreDispatcherPhiEdge(ctx, dispatcher, from, block); if (ctx.other_half != nullptr) { @@ -158,41 +144,37 @@ void EmitDispatcherTarget(ValueEmitContext& ctx, const DispatcherFunctionState& } uint32_t EmitDispatcherNextPc(ValueEmitContext& ctx, const DispatcherFunctionState& dispatcher, - const IR::Block* block, const IR::BlockInfo& info) { - const auto& term = info.terminator; + const IR::Block* block) { + const auto& term = block->terminator; switch (term.kind) { case CFG::TerminatorKind::Branch: EmitDispatcherTarget(ctx, dispatcher, block, term.true_block); - return ConstantU32(ctx.state, term.true_block); + return ConstantU32(ctx.state, term.true_block->id); case CFG::TerminatorKind::ConditionalBranch: { EmitDispatcherTarget(ctx, dispatcher, block, term.true_block); EmitDispatcherTarget(ctx, dispatcher, block, term.false_block); const auto selected = ctx.state.builder.AllocateId(); - ctx.state.builder.AddFunction( - spv::OpSelect, TypeU32(ctx.state), selected, ctx.Def(info.condition), - ConstantU32(ctx.state, term.true_block), ConstantU32(ctx.state, term.false_block)); + ctx.state.builder.AddFunction(spv::OpSelect, TypeU32(ctx.state), selected, + ctx.Def(block->condition), + ConstantU32(ctx.state, term.true_block->id), + ConstantU32(ctx.state, term.false_block->id)); return selected; } case CFG::TerminatorKind::IndirectBranch: { - for (const auto target: term.indirect_targets) { + for (const auto* target: block->ImmSuccessors()) { EmitDispatcherTarget(ctx, dispatcher, block, target); } uint32_t selected = ConstantU32(ctx.state, UINT32_MAX); - if (!info.indirect_target.IsEmpty()) { - const auto selector = ctx.Def(info.indirect_target); - const auto& values = term.indirect_selector_code != UINT32_MAX - ? term.indirect_selector_values - : term.indirect_target_pcs; - const auto& targets = term.indirect_selector_code != UINT32_MAX - ? term.indirect_selector_targets - : term.indirect_targets; - for (size_t index = 0; index < std::min(values.size(), targets.size()); index++) { + if (!block->indirect_target.IsEmpty()) { + const auto selector = ctx.Def(block->indirect_target); + for (const auto& branch: term.cases) { const auto match = ctx.state.builder.AllocateId(); const auto next = ctx.state.builder.AllocateId(); ctx.state.builder.AddFunction(spv::OpIEqual, TypeBool(ctx.state), match, - selector, ConstantU32(ctx.state, values[index])); + selector, ConstantU32(ctx.state, branch.value)); ctx.state.builder.AddFunction(spv::OpSelect, TypeU32(ctx.state), next, match, - ConstantU32(ctx.state, targets[index]), selected); + ConstantU32(ctx.state, branch.target->id), + selected); selected = next; } } @@ -349,7 +331,7 @@ void EmitStructuredFunction(ValueEmitContext& ctx) { EmitStructuredInstruction(lane, structured, inst); }); structured.block_exit_labels.emplace(block, ctx.state.current_label); - EmitStructuredTerminator(ctx, block, program.block_info[index]); + EmitStructuredTerminator(ctx, block); } PatchStructuredPhis(ctx, structured); } @@ -361,8 +343,7 @@ void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState EmitBlock(ctx, entry, [&](ValueEmitContext& lane, const IR::Inst& inst) { EmitDispatcherInstruction(lane, dispatcher, inst); }); - const auto initial_pc = - EmitDispatcherNextPc(ctx, dispatcher, entry, state.program.block_info.front()); + const auto initial_pc = EmitDispatcherNextPc(ctx, dispatcher, entry); const auto initial_parent = state.current_label; state.builder.AddFunction(spv::OpBranch, dispatcher.header_label); @@ -384,7 +365,7 @@ void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState spv::SelectionControlMaskNone); std::vector words {spv::OpSwitch, pc, dispatcher.after_switch_label}; for (size_t index = 1; index < state.program.blocks.size(); index++) { - words.push_back(state.program.block_info[index].id); + words.push_back(state.program.blocks[index]->id); words.push_back(state.program.blocks[index]->Definition()); } state.builder.AddFunction(words); @@ -396,8 +377,7 @@ void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState [&](ValueEmitContext& lane, const IR::Inst& inst) { EmitDispatcherInstruction(lane, dispatcher, inst); }); - const auto selected = EmitDispatcherNextPc(ctx, dispatcher, state.program.blocks[index], - state.program.block_info[index]); + const auto selected = EmitDispatcherNextPc(ctx, dispatcher, state.program.blocks[index]); next_pc_words.push_back(selected); next_pc_words.push_back(state.current_label); state.builder.AddFunction(spv::OpBranch, dispatcher.after_switch_label); @@ -668,8 +648,8 @@ void EmitProgram(EmitterState& state) { } } for (size_t index = 0; index < program.blocks.size(); index++) { - mark_cross_block(program.block_info[index].condition, program.blocks[index]); - mark_cross_block(program.block_info[index].indirect_target, program.blocks[index]); + mark_cross_block(program.blocks[index]->condition, program.blocks[index]); + mark_cross_block(program.blocks[index]->indirect_target, program.blocks[index]); } dispatch.header_label = state.builder.AllocateId(); dispatch.select_label = state.builder.AllocateId(); diff --git a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp index c48170e6f..e77a04e66 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp @@ -735,7 +735,7 @@ void Translator::WriteCompareResult(const Decoder::Operand& operand, IR::U1 valu } void Translator::AddBranchCondition(const CFG::Graph& graph, const CFG::BasicBlock& source, - IR::BlockInfo& info) { + IR::Block& info) { const auto native_condition = [&](CFG::BranchCondition kind) -> IR::U1 { IR::U1 condition; switch (kind) { @@ -1023,45 +1023,67 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& result.block_storage.reserve(cfg.blocks.size() + 1u); result.blocks.reserve(cfg.blocks.size() + 1u); - result.block_info.reserve(cfg.blocks.size() + 1u); const auto max_id = std::ranges::max_element(cfg.blocks, {}, [](const CFG::BasicBlock& block) { return block.id; })->id; if (max_id == UINT32_MAX) { EXIT("cannot allocate a typed entry block id"); } - CFG::Terminator terminator; - terminator.kind = CFG::TerminatorKind::Branch; - terminator.true_block = cfg.blocks.front().id; result.block_storage.push_back(std::make_unique()); result.blocks.push_back(result.block_storage.back().get()); - result.block_info.push_back({max_id + 1u, cfg.blocks.front().start_pc, - cfg.blocks.front().start_pc, std::move(terminator)}); + auto& entry = *result.blocks.front(); + entry.id = max_id + 1u; + entry.start_pc = entry.end_pc = cfg.blocks.front().start_pc; + entry.terminator.kind = CFG::TerminatorKind::Branch; - std::unordered_map block_indices; - block_indices.reserve(cfg.blocks.size()); - for (const auto& source_block: cfg.blocks) { - if (!block_indices.emplace(source_block.id, result.blocks.size()).second) { - EXIT("CFG contains duplicate block id %u", source_block.id); + std::unordered_map blocks_by_id; + blocks_by_id.reserve(cfg.blocks.size()); + for (const auto& source: cfg.blocks) { + auto& block = *result.block_storage.emplace_back(std::make_unique()); + if (!blocks_by_id.emplace(source.id, &block).second) { + EXIT("CFG contains duplicate block id %u", source.id); } - result.block_storage.push_back(std::make_unique()); - result.blocks.push_back(result.block_storage.back().get()); - result.block_info.push_back( - {source_block.id, source_block.start_pc, source_block.end_pc, source_block.terminator}); + result.blocks.push_back(&block); + block.id = source.id; + block.start_pc = source.start_pc; + block.end_pc = source.end_pc; } - for (const auto& source_block: cfg.blocks) { - const auto source_index = block_indices.at(source_block.id); - for (const auto successor: source_block.successors) { - const auto target = block_indices.find(successor); - if (target == block_indices.end()) { - EXIT("CFG block %u has unknown successor %u", source_block.id, successor); + const auto target_block = [&](uint32_t id) -> IR::Block* { + return id == UINT32_MAX ? nullptr : blocks_by_id.at(id); + }; + entry.terminator.true_block = target_block(cfg.blocks.front().id); + for (const auto& source: cfg.blocks) { + auto& block = *blocks_by_id.at(source.id); + const auto& term = source.terminator; + block.terminator.kind = term.kind; + block.terminator.true_block = target_block(term.true_block); + block.terminator.false_block = target_block(term.false_block); + block.terminator.merge_block = target_block(term.merge_block); + block.terminator.continue_block = target_block(term.continue_block); + block.terminator.loop_header = term.loop_header; + block.terminator.indexed = term.indirect_selector_code != UINT32_MAX; + const auto& values = + block.terminator.indexed ? term.indirect_selector_values : term.indirect_target_pcs; + const auto& targets = + block.terminator.indexed ? term.indirect_selector_targets : term.indirect_targets; + if (values.size() != targets.size()) { + EXIT("CFG block %u has inconsistent indirect targets", source.id); + } + block.terminator.cases.reserve(values.size()); + for (size_t index = 0; index < values.size(); ++index) { + block.terminator.cases.push_back({values[index], target_block(targets[index])}); + } + for (const auto successor: source.successors) { + const auto target = blocks_by_id.find(successor); + if (target == blocks_by_id.end()) { + EXIT("CFG block %u has unknown successor %u", source.id, successor); } - result.blocks[source_index]->AddBranch(result.blocks[target->second]); + block.AddBranch(target->second); } } { - result.blocks.front()->AddBranch(result.blocks.at(block_indices.at(cfg.blocks.front().id))); - IR::IREmitter entry_ir(result.blocks.front()); + entry.AddBranch(entry.terminator.true_block); + IR::IREmitter entry_ir(&entry); const auto builtin = [&](IR::StageInputKind kind, uint32_t component = 0u) { return IR::U32( entry_ir.Emit(IR::ValueOpcode::GetBuiltin, @@ -1331,10 +1353,10 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& } } const bool flush_f32_inputs = options.stage == ShaderType::Compute && - (options.input_info.compute->float_mode & 0x10u) == 0; + (options.input_info.compute->float_mode & 0x10u) == 0; for (const auto& cfg_block: cfg.blocks) { - const auto typed_index = block_indices.at(cfg_block.id); - Translator translator(result, result.blocks[typed_index], vector_limit, flush_f32_inputs); + auto* block = blocks_by_id.at(cfg_block.id); + Translator translator(result, block, vector_limit, flush_f32_inputs); for (uint32_t index = cfg_block.inst_begin; index < cfg_block.inst_end; index++) { const auto& instruction = decoded.instructions[index]; if (IsCodeTableLoad(cfg, instruction.pc)) { @@ -1357,7 +1379,7 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& } translator.TranslateInstruction(instruction); } - translator.AddBranchCondition(cfg, cfg_block, result.block_info[typed_index]); + translator.AddBranchCondition(cfg, cfg_block, *block); } IR::ValidateProgram(result, false); return result; diff --git a/src/graphics/shader/recompiler/frontend/translate/Translator.h b/src/graphics/shader/recompiler/frontend/translate/Translator.h index e63442814..5d5b4f432 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translator.h +++ b/src/graphics/shader/recompiler/frontend/translate/Translator.h @@ -17,7 +17,8 @@ public: void TranslateInstruction(const Decoder::Instruction& inst); void TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32_t attribute, uint32_t component_count, const ShaderBufferResource& resource); - void AddBranchCondition(const CFG::Graph& graph, const CFG::BasicBlock& source, IR::BlockInfo& info); + void AddBranchCondition(const CFG::Graph& graph, const CFG::BasicBlock& source, + IR::Block& info); private: const Decoder::Operand& SourceAt(const Decoder::Instruction& inst, uint32_t index); diff --git a/src/graphics/shader/recompiler/ir/Block.h b/src/graphics/shader/recompiler/ir/Block.h index e6da6d32c..b05a27a70 100644 --- a/src/graphics/shader/recompiler/ir/Block.h +++ b/src/graphics/shader/recompiler/ir/Block.h @@ -1,5 +1,6 @@ #pragma once +#include "graphics/shader/recompiler/frontend/cfg/ShaderCFG.h" #include "graphics/shader/recompiler/ir/Reg.h" #include "graphics/shader/recompiler/ir/Value.h" @@ -49,6 +50,27 @@ public: const_iterator end() const; bool empty() const; + struct SwitchCase { + uint32_t value; + Block* target; + }; + struct Terminator { + CFG::TerminatorKind kind = CFG::TerminatorKind::Return; + Block* true_block = nullptr; + Block* false_block = nullptr; + Block* merge_block = nullptr; + Block* continue_block = nullptr; + std::vector cases; + bool indexed = false; + bool loop_header = false; + }; + uint32_t id = 0; + uint32_t start_pc = 0; + uint32_t end_pc = 0; + Terminator terminator; + Value condition; + Value indirect_target; + std::array ssa_sreg_values {}; std::array ssa_thread_bit_sreg_values {}; std::array ssa_sreg_mask_tags {}; diff --git a/src/graphics/shader/recompiler/ir/Program.cpp b/src/graphics/shader/recompiler/ir/Program.cpp index 0cdb4bba6..0405f41ae 100644 --- a/src/graphics/shader/recompiler/ir/Program.cpp +++ b/src/graphics/shader/recompiler/ir/Program.cpp @@ -230,12 +230,11 @@ bool HasShaderMemoryWrites(const Program& program) { } void ValidateProgram(const Program& program, bool require_ssa) { - if (program.blocks.size() != program.block_info.size() || - program.blocks.size() != program.block_storage.size()) { + if (program.blocks.size() != program.block_storage.size()) { return Fail("value IR block storage is inconsistent"); } std::unordered_map block_indices; - std::unordered_map blocks_by_id; + std::unordered_set block_ids; std::unordered_map instruction_positions; for (size_t block_index = 0; block_index < program.blocks.size(); block_index++) { const auto* block = program.blocks[block_index]; @@ -246,10 +245,10 @@ void ValidateProgram(const Program& program, bool require_ssa) { if (!block_indices.emplace(block, block_index).second) { return Fail("value IR block pointer is duplicated"); } - if (program.block_info[block_index].id == UINT32_MAX) { + if (block->id == UINT32_MAX) { return Fail("value IR block uses the reserved exit id"); } - if (!blocks_by_id.emplace(program.block_info[block_index].id, block).second) { + if (!block_ids.insert(block->id).second) { return Fail("value IR block id is duplicated"); } size_t position = 0; @@ -294,15 +293,14 @@ void ValidateProgram(const Program& program, bool require_ssa) { } std::unordered_set expected_successors; - const auto add_target = [&](uint32_t id) { - const auto found = blocks_by_id.find(id); - if (found == blocks_by_id.end()) { + const auto add_target = [&](const Block* target) { + if (!block_indices.contains(target)) { return false; } - expected_successors.insert(found->second); + expected_successors.insert(target); return true; }; - const auto& terminator = program.block_info[block_index].terminator; + const auto& terminator = block->terminator; const auto validate_control_value = [&](Value value, Type type) { if (value.IsEmpty() || value.GetType() != type) { return false; @@ -320,44 +318,32 @@ void ValidateProgram(const Program& program, bool require_ssa) { if (!add_target(terminator.true_block) || !add_target(terminator.false_block)) { return Fail("value IR conditional branch target is missing"); } - if (!validate_control_value(program.block_info[block_index].condition, Type::U1)) { + if (!validate_control_value(block->condition, Type::U1)) { return Fail("value IR conditional branch condition is invalid"); } break; case CFG::TerminatorKind::IndirectBranch: { - if (!validate_control_value(program.block_info[block_index].indirect_target, - Type::U32)) { + if (!validate_control_value(block->indirect_target, Type::U32)) { return Fail("value IR indirect branch selector is invalid"); } - std::unordered_set indirect_targets; - for (const auto target: terminator.indirect_targets) { - if (!indirect_targets.insert(target).second) { - return Fail("value IR indirect branch target is duplicated"); + std::unordered_set values; + for (const auto& branch: terminator.cases) { + if (!values.insert(branch.value).second) { + return Fail("value IR indirect selector value is duplicated"); } - if (!add_target(target)) { + if (!add_target(branch.target)) { return Fail("value IR indirect branch target is missing"); } } - if (terminator.indirect_selector_values.size() != - terminator.indirect_selector_targets.size()) { - return Fail("value IR indirect selector table is inconsistent"); - } - for (const auto target: terminator.indirect_selector_targets) { - const auto found = blocks_by_id.find(target); - if (found == blocks_by_id.end() || - !expected_successors.contains(found->second)) { - return Fail("value IR indirect selector target is not a CFG successor"); - } - } break; } case CFG::TerminatorKind::Return: case CFG::TerminatorKind::Unsupported: break; } - if ((terminator.merge_block != UINT32_MAX && - !blocks_by_id.contains(terminator.merge_block)) || - (terminator.continue_block != UINT32_MAX && - !blocks_by_id.contains(terminator.continue_block))) { + if ((terminator.merge_block != nullptr && + !block_indices.contains(terminator.merge_block)) || + (terminator.continue_block != nullptr && + !block_indices.contains(terminator.continue_block))) { return Fail("value IR structured control target is missing"); } if (successors != expected_successors) { @@ -664,7 +650,7 @@ void ValidateProgram(const Program& program, bool require_ssa) { } } } - const auto& info = program.block_info[block_index]; + const auto& info = *program.blocks[block_index]; if (!info.condition.IsEmpty() && !control_dominates(info.condition, block)) { return Fail("value IR branch condition definition does not dominate its use"); } @@ -675,9 +661,9 @@ void ValidateProgram(const Program& program, bool require_ssa) { } void ResolveControlFlowIdentities(Program& program) { - for (auto& info: program.block_info) { - info.condition = info.condition.Resolve(); - info.indirect_target = info.indirect_target.Resolve(); + for (auto* block: program.blocks) { + block->condition = block->condition.Resolve(); + block->indirect_target = block->indirect_target.Resolve(); } } @@ -712,9 +698,9 @@ std::string ProgramToString(const Program& program) { std::string text; for (size_t block_index = 0; block_index < program.blocks.size(); block_index++) { - text += fmt::format("Block ${} pc=0x{:08x}..0x{:08x}\n", block_index, - program.block_info[block_index].start_pc, - program.block_info[block_index].end_pc); + text += + fmt::format("Block ${} pc=0x{:08x}..0x{:08x}\n", block_index, + program.blocks[block_index]->start_pc, program.blocks[block_index]->end_pc); for (const auto& inst: *program.blocks[block_index]) { const auto type = inst.GetType(); if (type != Type::Void) { diff --git a/src/graphics/shader/recompiler/ir/ShaderIR.h b/src/graphics/shader/recompiler/ir/ShaderIR.h index 3b9556785..0e473f60b 100644 --- a/src/graphics/shader/recompiler/ir/ShaderIR.h +++ b/src/graphics/shader/recompiler/ir/ShaderIR.h @@ -493,15 +493,6 @@ struct ShaderInfo { bool operator==(const ShaderInfo& other) const = default; }; -struct BlockInfo { - uint32_t id = 0; - uint32_t start_pc = 0; - uint32_t end_pc = 0; - CFG::Terminator terminator; - Value condition; - Value indirect_target; -}; - struct DescriptorSource { struct IndirectDescriptor { struct SelectorRead { @@ -631,7 +622,6 @@ struct Program: ResourcePlan { bool dispatcher_fallback = false; CFG::FailureKind cfg_failure_kind = CFG::FailureKind::None; std::string fallback_reason; - std::vector block_info; struct ScalarWrite { uint32_t pc; ScalarReg reg; }; std::vector scalar_writes; // Typed memory and export instructions reference shader-local metadata by dense index. diff --git a/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp b/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp index 468f4e26a..cf467412b 100644 --- a/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp @@ -680,30 +680,29 @@ bool BuildSamplerPlan(const ShaderInfo& base, SamplerPlan& plan) { template static std::vector ResourceControlFlow(const Program& program, const Predicate& predicate) { // The renderer checks captured scalar reads against final buffer and image write ranges. - if (program.blocks.size() != program.block_info.size() || program.has_address_writes) { + if (program.has_address_writes) { return {}; } - std::unordered_map indices; - for (uint32_t i = 0; i < program.block_info.size(); i++) { - if (!indices.emplace(program.block_info[i].id, i).second) { + std::unordered_map indices; + for (uint32_t i = 0; i < program.blocks.size(); i++) { + if (!indices.emplace(program.blocks[i], i).second) { return {}; } } std::vector blocks(program.blocks.size()); for (uint32_t i = 0; i < blocks.size(); i++) { auto& block = blocks[i]; - const auto& info = program.block_info[i]; + const auto& info = *program.blocks[i]; const auto& terminator = info.terminator; - std::vector successors; + const std::array targets {terminator.true_block, terminator.false_block}; + std::span successors; switch (terminator.kind) { - case CFG::TerminatorKind::Branch: successors.push_back(terminator.true_block); break; + case CFG::TerminatorKind::Branch: successors = std::span(targets).first(1); break; case CFG::TerminatorKind::ConditionalBranch: - successors = {terminator.true_block, terminator.false_block}; + successors = targets; block.condition = info.condition; break; - case CFG::TerminatorKind::IndirectBranch: - successors = terminator.indirect_targets; - break; + case CFG::TerminatorKind::IndirectBranch: successors = info.ImmSuccessors(); break; case CFG::TerminatorKind::Return: break; default: return {}; } @@ -824,17 +823,16 @@ static std::optional> FillIndex(Value value, uint32_t ax } static UniformFillPlan AnalyzeUniformFill(const Program& program) { - if (program.stage != ShaderType::Compute || program.blocks.empty() || - program.blocks.size() != program.block_info.size() || program.info.uses_dma || + if (program.stage != ShaderType::Compute || program.blocks.empty() || program.info.uses_dma || !program.info.samplers.empty()) { return {}; } - std::unordered_set visited; - uint32_t index = 0; - const Inst* store = nullptr; + std::unordered_set visited; + const auto* block = program.blocks.front(); + const Inst* store = nullptr; for (;;) { - if (!visited.insert(index).second) return {}; - for (const auto& inst: *program.blocks[index]) { + if (block == nullptr || !visited.insert(block).second) return {}; + for (const auto& inst: *block) { if (AddressOpcodeInfoOf(inst.GetOpcode()).access != AddressAccess::None) return {}; if (!inst.MayHaveSideEffects()) continue; if (store != nullptr || (BufferAccessOf(inst.GetOpcode()) != BufferAccess::Write && @@ -842,12 +840,10 @@ static UniformFillPlan AnalyzeUniformFill(const Program& program) { return {}; store = &inst; } - const auto& term = program.block_info[index].terminator; + const auto& term = block->terminator; if (term.kind == CFG::TerminatorKind::Return) break; if (term.kind != CFG::TerminatorKind::Branch) return {}; - const auto next = std::ranges::find(program.block_info, term.true_block, &BlockInfo::id); - if (next == program.block_info.end()) return {}; - index = static_cast(next - program.block_info.begin()); + block = term.true_block; } if (store == nullptr || visited.size() != program.blocks.size()) return {}; for (const auto& buffer: program.info.buffers) { diff --git a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp index 59804cf18..472e3a02a 100644 --- a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp @@ -613,8 +613,7 @@ private: value = value.Resolve(); const auto* phi = value.TryInstruction(); if (m_shader_writes || phi == nullptr || phi->GetOpcode() != ValueOpcode::Phi || - phi->NumArgs() != 2u || phi->NumPhiBlocks() != 2u || phi->GetType() != Type::U32 || - m_program.blocks.size() != m_program.block_info.size()) { + phi->NumArgs() != 2u || phi->NumPhiBlocks() != 2u || phi->GetType() != Type::U32) { return value; } const auto* merge = phi->Parent(); @@ -637,7 +636,7 @@ private: if (branch == merge || branch->ImmSuccessors().size() != 2u) { return value; } - std::array target_ids; + std::array targets; for (uint32_t arm = 0; arm < 2; arm++) { const auto* incoming = phi->PhiBlock(arm); if (incoming == merge || @@ -648,29 +647,20 @@ private: incoming->ImmSuccessors()[0] != merge))) { return value; } - const auto* target = incoming == branch ? merge : incoming; - const auto it = std::ranges::find(m_program.blocks, target); - if (it == m_program.blocks.end()) { - return value; - } - target_ids[arm] = m_program.block_info[it - m_program.blocks.begin()].id; + targets[arm] = incoming == branch ? merge : incoming; } - const auto branch_it = std::ranges::find(m_program.blocks, branch); - if (branch_it == m_program.blocks.end()) { - return value; - } - const auto& info = m_program.block_info[branch_it - m_program.blocks.begin()]; + const auto& info = *branch; const auto& term = info.terminator; if (term.kind != CFG::TerminatorKind::ConditionalBranch || - !((term.true_block == target_ids[0] && term.false_block == target_ids[1]) || - (term.false_block == target_ids[0] && term.true_block == target_ids[1])) || + !((term.true_block == targets[0] && term.false_block == targets[1]) || + (term.false_block == targets[0] && term.true_block == targets[1])) || !ValidateRuntimeValue(m_program, info.condition, RuntimeValueType::Integer) || !ValidateRuntimeValue(m_program, phi->Arg(0)) || !ValidateRuntimeValue(m_program, phi->Arg(1))) { return value; } // Retain a host expression; replacing the GPU Phi would break SSA dominance. - const auto true_arg = term.true_block == target_ids[0] ? 0u : 1u; + const auto true_arg = term.true_block == targets[0] ? 0u : 1u; auto& selected = m_program.value_storage.emplace_back(ValueOpcode::SelectU32); selected.SetArg(0, info.condition); selected.SetArg(1, phi->Arg(true_arg)); @@ -852,9 +842,9 @@ private: if (entry.source.dwords[word].Resolve() == Value(read)) entry.source.dwords[word] = flat; } - for (auto& info: m_program.block_info) { - if (info.condition.Resolve() == Value(read)) info.condition = flat; - if (info.indirect_target.Resolve() == Value(read)) info.indirect_target = flat; + for (auto* block: m_program.blocks) { + if (block->condition.Resolve() == Value(read)) block->condition = flat; + if (block->indirect_target.Resolve() == Value(read)) block->indirect_target = flat; } if (keep) { if (resolved->planning_handle.IsEmpty()) { @@ -1022,9 +1012,6 @@ private: }; bool NonzeroOnEntry(Value value, const Block* block) const { - if (m_program.blocks.size() != m_program.block_info.size()) { - return false; - } // Each unique predecessor must execute before this use. Stop at joins: an // unrelated comparison is not a bound on FindILsb's zero-input sentinel. for (size_t depth = 0; block != nullptr && depth < m_program.blocks.size(); ++depth) { @@ -1098,15 +1085,11 @@ private: } std::optional ConditionalEdge(const Block* from, const Block* to) const { - const auto position = std::ranges::find(m_program.blocks, from); - const auto target = std::ranges::find(m_program.blocks, to); - if (position == m_program.blocks.end() || target == m_program.blocks.end()) return {}; - const auto& info = m_program.block_info[position - m_program.blocks.begin()]; - const auto& term = info.terminator; - const auto id = m_program.block_info[target - m_program.blocks.begin()].id; + const auto& term = from->terminator; if (term.kind != CFG::TerminatorKind::ConditionalBranch || - (term.true_block == id) == (term.false_block == id)) return {}; - EdgePredicate edge {info.condition, term.true_block == id}; + (term.true_block == to) == (term.false_block == to)) + return {}; + EdgePredicate edge {from->condition, term.true_block == to}; while (const auto* inst = edge.condition.Resolve().TryInstruction()) { if (inst->GetOpcode() == ValueOpcode::LogicalNot) { edge.positive = !edge.positive; @@ -1470,9 +1453,9 @@ private: const Inst* BoundedLoop(Value key, const Block* use, const auto& accepts_bound) const { const auto* phi = key.Resolve().TryInstruction(); - if (phi == nullptr || phi->GetOpcode() != ValueOpcode::Phi || - phi->GetType() != Type::U32 || phi->NumArgs() != 2u || phi->NumPhiBlocks() != 2u || - m_program.blocks.size() != m_program.block_info.size()) return {}; + if (phi == nullptr || phi->GetOpcode() != ValueOpcode::Phi || phi->GetType() != Type::U32 || + phi->NumArgs() != 2u || phi->NumPhiBlocks() != 2u) + return {}; const Block* increment_block = nullptr; uint32_t initial_arm = 0; for (uint32_t initial = 0; initial < 2u; ++initial) { @@ -1904,22 +1887,13 @@ private: } bool ImpossibleSwitchEdge(const Block* from, const Block* to) const { - const auto from_it = std::ranges::find(m_program.blocks, from); - const auto to_it = std::ranges::find(m_program.blocks, to); - if (from_it == m_program.blocks.end() || to_it == m_program.blocks.end() || - m_program.block_info.size() != m_program.blocks.size()) return false; - const auto& info = m_program.block_info[from_it - m_program.blocks.begin()]; - const auto& term = info.terminator; - if (term.kind != CFG::TerminatorKind::IndirectBranch || - term.indirect_selector_code == UINT32_MAX || - term.indirect_selector_values.empty() || - term.indirect_selector_values.size() != term.indirect_selector_targets.size()) return false; - const auto target = m_program.block_info[to_it - m_program.blocks.begin()].id; - const auto maximum = SelectorMaximum(info.indirect_target); + const auto& term = from->terminator; + if (term.kind != CFG::TerminatorKind::IndirectBranch || !term.indexed) return false; + const auto maximum = SelectorMaximum(from->indirect_target); bool found = false; - for (size_t i = 0; i < term.indirect_selector_values.size(); ++i) { - if (term.indirect_selector_targets[i] != target) continue; - if (term.indirect_selector_values[i] <= maximum) return false; + for (const auto& branch: term.cases) { + if (branch.target != to) continue; + if (branch.value <= maximum) return false; found = true; } return found; diff --git a/tests/ResourceMaterializationTests.cpp b/tests/ResourceMaterializationTests.cpp index bb38eb452..2688062ef 100644 --- a/tests/ResourceMaterializationTests.cpp +++ b/tests/ResourceMaterializationTests.cpp @@ -27,7 +27,7 @@ AddValueBlock(Libs::Graphics::ShaderRecompiler::IR::Program &program) { auto block = std::make_unique(); auto *result = block.get(); program.blocks.push_back(result); - program.block_info.push_back({.id = 0}); + result->id = static_cast(program.blocks.size() - 1u); program.block_storage.push_back(std::move(block)); return *result; } @@ -462,16 +462,16 @@ void TestWrittenDescriptorUsesStrictReaderOnce() { {Value(&offset), Value(4u)}); auto &store_block = AddValueBlock(program); AddValueBlock(program); - program.block_info[0].condition = Value(&condition); - program.block_info[0].terminator.kind = + program.blocks[0]->condition = Value(&condition); + program.blocks[0]->terminator.kind = Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch; - program.block_info[0].terminator.true_block = 1; - program.block_info[0].terminator.false_block = 2; - program.block_info[1].id = 1; - program.block_info[1].terminator.kind = + program.blocks[0]->terminator.true_block = program.blocks[1]; + program.blocks[0]->terminator.false_block = program.blocks[2]; + program.blocks[1]->id = 1; + program.blocks[1]->terminator.kind = Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return; - program.block_info[2].id = 2; - program.block_info[2].terminator.kind = + program.blocks[2]->id = 2; + program.blocks[2]->terminator.kind = Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return; program.memory_info.push_back({.kind = ResourceKind::Buffer, .resource = 0}); auto &output = store_block.AppendNewInst(ValueOpcode::GetBufferResource, diff --git a/tests/ResourceTrackingTests.cpp b/tests/ResourceTrackingTests.cpp index e29702704..d82a18d97 100644 --- a/tests/ResourceTrackingTests.cpp +++ b/tests/ResourceTrackingTests.cpp @@ -58,8 +58,8 @@ struct Fixture { auto *result = storage.get(); program.block_storage.push_back(std::move(storage)); program.blocks.push_back(result); - program.block_info.push_back( - {.id = static_cast(program.block_info.size())}); + program.blocks.back()->id = + static_cast(program.blocks.size() - 1u); return result; } @@ -126,8 +126,8 @@ struct Fixture { } void PlanAndTrack() { - for (size_t index = 0; index < program.block_info.size(); ++index) { - const auto condition = program.block_info[index].condition; + for (size_t index = 0; index < program.blocks.size(); ++index) { + const auto condition = program.blocks[index]->condition; if (!condition.IsEmpty()) Emit(ValueOpcode::Reference, {condition}, 0, program.blocks[index]); } @@ -475,9 +475,11 @@ void TestWaterfallImageTable() { {guard, fixture->Emit(ValueOpcode::IEqual32, {image_words[word], local})}); } } - fixture->program.block_info[0].condition = guard; - fixture->program.block_info[0].terminator = { - .kind = CFG::TerminatorKind::ConditionalBranch, .true_block = 1u, .false_block = 2u}; + fixture->program.blocks[0]->condition = guard; + fixture->program.blocks[0]->terminator = { + .kind = CFG::TerminatorKind::ConditionalBranch, + .true_block = fixture->program.blocks[1u], + .false_block = fixture->program.blocks[2u]}; const auto sample = [&](Value image, Block *block) { const auto sampler = fixture->Sampler({Value(0u), Value(0u), Value(0u), Value(0u)}); MemoryInfo memory; @@ -589,19 +591,20 @@ void TestGuardedDirectImageTable() { if (guard != Guard::Plain && guard != Guard::SccNonZero) { condition = fixture.Emit(ValueOpcode::ConditionRef, {condition}, kind); } - fixture.program.block_info[0].condition = condition; - fixture.program.block_info[0].terminator = { + fixture.program.blocks[0]->condition = condition; + fixture.program.blocks[0]->terminator = { .kind = CFG::TerminatorKind::ConditionalBranch, - .true_block = guard == Guard::Zero ? 1u : 4u, - .false_block = guard == Guard::Zero ? 4u : 1u}; + .true_block = fixture.program.blocks[guard == Guard::Zero ? 1u : 4u], + .false_block = fixture.program.blocks[guard == Guard::Zero ? 4u : 1u]}; for (uint32_t block = 1; block < 4; ++block) { - fixture.program.block_info[block].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = block + 1u}; + fixture.program.blocks[block]->terminator = { + .kind = CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[block + 1u]}; } - fixture.program.block_info[4].terminator = { + fixture.program.blocks[4]->terminator = { .kind = guard == Guard::Bypass ? CFG::TerminatorKind::Branch - : CFG::TerminatorKind::Return, - .true_block = 3u}; + : CFG::TerminatorKind::Return, + .true_block = fixture.program.blocks[3u]}; const auto srt = fixture.Address(fixture.UserData(0), fixture.UserData(1)); std::array pointer; for (uint32_t word = 0; word < pointer.size(); ++word) { @@ -767,27 +770,40 @@ void TestBoundedComputeImageLoop() { if (variant == Variant::ExitBypass) exit->AddBranch(body); if (variant == Variant::IncrementBypass || variant == Variant::PreviousBound) exit->AddBranch(latch); - fixture.program.block_info[0].terminator = { - .kind = variant == Variant::EntryBypass ? CFG::TerminatorKind::ConditionalBranch - : CFG::TerminatorKind::Branch, - .true_block = 1u, .false_block = 2u}; - fixture.program.block_info[1].terminator = { + fixture.program.blocks[0]->terminator = { + .kind = variant == Variant::EntryBypass + ? CFG::TerminatorKind::ConditionalBranch + : CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[1u], + .false_block = fixture.program.blocks[2u]}; + fixture.program.blocks[1]->terminator = { .kind = CFG::TerminatorKind::ConditionalBranch, - .true_block = variant == Variant::TrueEdge ? 2u : 4u, - .false_block = variant == Variant::TrueEdge ? 4u : 2u}; - fixture.program.block_info[2].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = 3u}; - fixture.program.block_info[3].terminator = { - .kind = variant == Variant::PreviousBound ? CFG::TerminatorKind::ConditionalBranch - : CFG::TerminatorKind::Branch, - .true_block = 1u, .false_block = 5u}; - fixture.program.block_info[4].terminator = { + .true_block = + fixture.program.blocks[variant == Variant::TrueEdge ? 2u : 4u], + .false_block = + fixture.program.blocks[variant == Variant::TrueEdge ? 4u : 2u]}; + fixture.program.blocks[2]->terminator = { + .kind = CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[3u]}; + fixture.program.blocks[3]->terminator = { + .kind = variant == Variant::PreviousBound + ? CFG::TerminatorKind::ConditionalBranch + : CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[1u], + .false_block = final_exit}; + fixture.program.blocks[4]->terminator = { .kind = final_exit != exit || variant == Variant::ExitBypass || variant == Variant::IncrementBypass - ? CFG::TerminatorKind::Branch : CFG::TerminatorKind::Return, - .true_block = final_exit != exit || variant == Variant::IncrementBypass ? 3u : 2u}; + ? CFG::TerminatorKind::Branch + : CFG::TerminatorKind::Return, + .true_block = + fixture.program.blocks[final_exit != exit || + variant == Variant::IncrementBypass + ? 3u + : 2u]}; if (final_exit != exit) - fixture.program.block_info[5].terminator.kind = CFG::TerminatorKind::Return; + fixture.program.blocks[5]->terminator.kind = + CFG::TerminatorKind::Return; } auto &phi = header->AppendNewInst(ValueOpcode::Phi, {}, @@ -859,14 +875,16 @@ void TestBoundedComputeImageLoop() { block->AddBranch(fixture.program.blocks[yes]); block->AddBranch(fixture.program.blocks[no]); const auto inverse = fixture.Emit(ValueOpcode::LogicalNot, {predicate}, 0, block); - fixture.program.block_info[index].condition = + fixture.program.blocks[index]->condition = fixture.Emit(ValueOpcode::ConditionRef, {inverse}, kind, block); - fixture.program.block_info[index].terminator = { + fixture.program.blocks[index]->terminator = { .kind = CFG::TerminatorKind::ConditionalBranch, - .true_block = yes, .false_block = no}; + .true_block = fixture.program.blocks[yes], + .false_block = fixture.program.blocks[no]}; }; - fixture.program.block_info[0].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = 1u}; + fixture.program.blocks[0]->terminator = { + .kind = CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[1u]}; branch(1u, 4u, 5u, active, CFG::BranchCondition::ExecZero); branch(5u, variant == Variant::GuardedDiamondBypass ? 7u : 4u, 6u, diamond ? execute : mask, @@ -877,13 +895,15 @@ void TestBoundedComputeImageLoop() { fixture.Emit(ValueOpcode::IEqual32, {local, Value(1u)}, 0, guard)}, 0, guard); branch(6u, 3u, 2u, image_guard, CFG::BranchCondition::ExecZero); body->AddBranch(latch); - fixture.program.block_info[2].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = 3u}; + fixture.program.blocks[2]->terminator = { + .kind = CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[3u]}; branch(3u, 4u, 7u, next_active, CFG::BranchCondition::ExecZero); increment->AddBranch(header); - fixture.program.block_info[7].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = 1u}; - fixture.program.block_info[4].terminator.kind = CFG::TerminatorKind::Return; + fixture.program.blocks[7]->terminator = { + .kind = CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[1u]}; + fixture.program.blocks[4]->terminator.kind = CFG::TerminatorKind::Return; } else { const auto active = initial_active; const auto allowed = fixture.Emit(variant == Variant::Disjunction @@ -899,14 +919,15 @@ void TestBoundedComputeImageLoop() { : CFG::BranchCondition::ExecZero, header); if (variant == Variant::Nonzero || variant == Variant::WrongPolarity) condition = fixture.Emit(ValueOpcode::LogicalNot, {condition}, 0, header); - fixture.program.block_info[1].condition = condition; + fixture.program.blocks[1]->condition = condition; if (variant == Variant::PreviousBound) { previous->AddPhiOperand(entry, Value(false)); previous->AddPhiOperand(latch, in_range); const auto continuing = fixture.Emit(ValueOpcode::LogicalOr, {in_range, Value(previous)}, 0, latch); - fixture.program.block_info[3].condition = fixture.Emit(ValueOpcode::ConditionRef, - {continuing}, CFG::BranchCondition::SccNonZero, latch); + fixture.program.blocks[3]->condition = + fixture.Emit(ValueOpcode::ConditionRef, {continuing}, + CFG::BranchCondition::SccNonZero, latch); } } const auto step = fixture.Emit(ValueOpcode::IAdd32, @@ -1067,21 +1088,27 @@ void TestUniformizedMaterialImageKeys() { choose->AddBranch(done); if (first_lane) { sample_entry->AddBranch(first_lane_loop ? sample_header : sample); - fixture.program.block_info[sample_entry_id].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = first_lane_loop ? 9u : 7u}; + fixture.program.blocks[sample_entry_id]->terminator = { + .kind = CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[first_lane_loop ? 9u : 7u]}; } if (first_lane_loop) { sample_header->AddBranch(sample); sample->AddBranch(sample_header); - fixture.program.block_info[9].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = 7u, - .merge_block = 8u, .continue_block = 7u, .loop_header = true}; + fixture.program.blocks[9]->terminator = { + .kind = CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[7u], + .merge_block = fixture.program.blocks[8u], + .continue_block = fixture.program.blocks[7u], + .loop_header = true}; } sample->AddBranch(done); - fixture.program.block_info[0].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = 1u}; - fixture.program.block_info[1].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = 2u}; + fixture.program.blocks[0]->terminator = { + .kind = CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[1u]}; + fixture.program.blocks[1]->terminator = { + .kind = CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[2u]}; const auto enabled = fixture.Emit( ValueOpcode::INotEqual32, {fixture.UserData(5u), Value(0u)}, 0, entry); const auto local = fixture.Emit(ValueOpcode::GetBuiltin, @@ -1097,23 +1124,26 @@ void TestUniformizedMaterialImageKeys() { ValueOpcode::Phi, {}, static_cast(Type::U32)); const auto mask = Value(&mask_phi); const auto active = Value(&active_phi); - fixture.program.block_info[2].condition = branch( - fixture.Emit(ValueOpcode::LogicalNot, {active}, 0, inactive), - variant == Variant::WrongExit ? CFG::BranchCondition::ExecNonZero - : CFG::BranchCondition::ExecZero, inactive); - fixture.program.block_info[2].terminator = { + fixture.program.blocks[2]->condition = + branch(fixture.Emit(ValueOpcode::LogicalNot, {active}, 0, inactive), + variant == Variant::WrongExit ? CFG::BranchCondition::ExecNonZero + : CFG::BranchCondition::ExecZero, + inactive); + fixture.program.blocks[2]->terminator = { .kind = CFG::TerminatorKind::ConditionalBranch, - .true_block = 5u, .false_block = 3u}; + .true_block = fixture.program.blocks[5u], + .false_block = fixture.program.blocks[3u]}; const auto nonzero = fixture.Emit( ValueOpcode::INotEqual32, {Value(0u), mask}, 0, sentinel); const auto bit_guard = fixture.Emit( ValueOpcode::LogicalAnd, {active, nonzero}, 0, sentinel); - fixture.program.block_info[3].condition = branch( - fixture.Emit(ValueOpcode::LogicalNot, {bit_guard}, 0, sentinel), - CFG::BranchCondition::ExecZero, sentinel); - fixture.program.block_info[3].terminator = { + fixture.program.blocks[3]->condition = + branch(fixture.Emit(ValueOpcode::LogicalNot, {bit_guard}, 0, sentinel), + CFG::BranchCondition::ExecZero, sentinel); + fixture.program.blocks[3]->terminator = { .kind = CFG::TerminatorKind::ConditionalBranch, - .true_block = 5u, .false_block = 4u}; + .true_block = fixture.program.blocks[5u], + .false_block = fixture.program.blocks[4u]}; const auto first = fixture.Emit(ValueOpcode::FindILsb32, {mask}, 0, bit); const auto position = fixture.Emit( ValueOpcode::BitwiseAnd32, {first, Value(31u)}, 0, bit); @@ -1130,11 +1160,12 @@ void TestUniformizedMaterialImageKeys() { {mask, removed}, 0, bit); const auto continuation = fixture.Emit( ValueOpcode::LogicalAnd, {bit_guard, active_on_entry}, 0, bit); - fixture.program.block_info[4].condition = branch( - continuation, CFG::BranchCondition::ExecNonZero, bit); - fixture.program.block_info[4].terminator = { + fixture.program.blocks[4]->condition = + branch(continuation, CFG::BranchCondition::ExecNonZero, bit); + fixture.program.blocks[4]->terminator = { .kind = CFG::TerminatorKind::ConditionalBranch, - .true_block = 1u, .false_block = 5u}; + .true_block = fixture.program.blocks[1u], + .false_block = fixture.program.blocks[5u]}; active_phi.AddPhiOperand(entry, active_on_entry); active_phi.AddPhiOperand(bit, continuation); mask_phi.AddPhiOperand(entry, fixture.UserData(4u)); @@ -1159,8 +1190,9 @@ void TestUniformizedMaterialImageKeys() { ValueOpcode::SGreaterThan32, {Value(32u), index}, 0, merge); const auto material_guard = fixture.Emit( ValueOpcode::LogicalAnd, {active_on_entry, below}, 0, merge); - fixture.program.block_info[5].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = 6u}; + fixture.program.blocks[5]->terminator = { + .kind = CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[6u]}; const auto scaled = fixture.Emit( ValueOpcode::ShiftLeftLogical32, {index, Value(4u)}, 0, choose); const auto selected_scale = fixture.Emit( @@ -1202,12 +1234,13 @@ void TestUniformizedMaterialImageKeys() { sample_active_phi->AddPhiOperand(sample_entry, initial); sample_active = Value(sample_active_phi); } - fixture.program.block_info[6].condition = branch( - variant == Variant::EmptyFirstEntry ? enabled : initial, - CFG::BranchCondition::ExecNonZero, choose); - fixture.program.block_info[6].terminator = { + fixture.program.blocks[6]->condition = + branch(variant == Variant::EmptyFirstEntry ? enabled : initial, + CFG::BranchCondition::ExecNonZero, choose); + fixture.program.blocks[6]->terminator = { .kind = CFG::TerminatorKind::ConditionalBranch, - .true_block = sample_entry_id, .false_block = 8u}; + .true_block = fixture.program.blocks[sample_entry_id], + .false_block = fixture.program.blocks[8u]}; } auto *key_block = first_lane ? sample : choose; const auto key = first_lane @@ -1219,12 +1252,13 @@ void TestUniformizedMaterialImageKeys() { const auto sample_guard = fixture.Emit( ValueOpcode::LogicalAnd, {sample_active, compared}, 0, key_block); if (!first_lane) { - fixture.program.block_info[6].condition = branch( + fixture.program.blocks[6]->condition = branch( fixture.Emit(ValueOpcode::LogicalNot, {sample_guard}, 0, choose), CFG::BranchCondition::ExecZero, choose); - fixture.program.block_info[6].terminator = { + fixture.program.blocks[6]->terminator = { .kind = CFG::TerminatorKind::ConditionalBranch, - .true_block = 8u, .false_block = 7u}; + .true_block = fixture.program.blocks[8u], + .false_block = fixture.program.blocks[7u]}; } const auto table_offset = fixture.Emit( ValueOpcode::IAdd32, @@ -1272,17 +1306,19 @@ void TestUniformizedMaterialImageKeys() { if (variant == Variant::WideningFirstBackedge) remaining = fixture.Emit(ValueOpcode::LogicalOr, {remaining, enabled}, 0, sample); sample_active_phi->AddPhiOperand(sample, remaining); - fixture.program.block_info[7].condition = branch( + fixture.program.blocks[7]->condition = branch( variant == Variant::WrongFirstBackedge ? sample_guard : remaining, CFG::BranchCondition::ExecNonZero, sample); - fixture.program.block_info[7].terminator = { + fixture.program.blocks[7]->terminator = { .kind = CFG::TerminatorKind::ConditionalBranch, - .true_block = 9u, .false_block = 8u}; + .true_block = fixture.program.blocks[9u], + .false_block = fixture.program.blocks[8u]}; } else { - fixture.program.block_info[7].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = 8u}; + fixture.program.blocks[7]->terminator = { + .kind = CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[8u]}; } - fixture.program.block_info[8].terminator.kind = CFG::TerminatorKind::Return; + fixture.program.blocks[8]->terminator.kind = CFG::TerminatorKind::Return; const auto output = fixture.Emit( ValueOpcode::GetBufferResource, {fixture.UserData(8u), fixture.UserData(9u), @@ -1556,10 +1592,10 @@ void TestComputeBufferFill() { }; const auto Run = [](Options options) { Fixture fixture; - fixture.program.block_info[0].terminator.kind = + fixture.program.blocks[0]->terminator.kind = Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return; if (options.branch) { - fixture.program.block_info[0].terminator.kind = Libs::Graphics:: + fixture.program.blocks[0]->terminator.kind = Libs::Graphics:: ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch; } const auto buffer = @@ -2369,16 +2405,20 @@ ResourcePlan ConditionalSamplerPlan(bool diamond, bool reverse, bool reverse_phi alternate->AddBranch(merge); if (diamond) { initial->AddBranch(merge); - fixture.program.block_info[1].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = merge_id}; + fixture.program.blocks[1]->terminator = { + .kind = CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[merge_id]}; } - fixture.program.block_info[0].terminator = { + fixture.program.blocks[0]->terminator = { .kind = CFG::TerminatorKind::ConditionalBranch, - .true_block = reverse ? alternate_id : initial_target, - .false_block = reverse ? initial_target : alternate_id}; - fixture.program.block_info[alternate_id].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = merge_id}; - fixture.program.block_info[merge_id].terminator.kind = + .true_block = + fixture.program.blocks[reverse ? alternate_id : initial_target], + .false_block = + fixture.program.blocks[reverse ? initial_target : alternate_id]}; + fixture.program.blocks[alternate_id]->terminator = { + .kind = CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[merge_id]}; + fixture.program.blocks[merge_id]->terminator.kind = CFG::TerminatorKind::Return; const auto control = fixture.Buffer({Value(0x2000u), Value(0u), Value(200u), Value(0u)}); @@ -2389,7 +2429,7 @@ ResourcePlan ConditionalSamplerPlan(bool diamond, bool reverse, bool reverse_phi if (nonuniform) { flag = fixture.Emit(ValueOpcode::LaneId); } - fixture.program.block_info[0].condition = + fixture.program.blocks[0]->condition = fixture.Emit(ValueOpcode::SGreaterThanEqual32, {flag, Value(0u)}); if (writable) { MemoryInfo memory; @@ -2590,22 +2630,21 @@ void TestFiniteImageBitScanSentinel() { {Value(uint32_t(StageInputKind::LocalInvocationIndex)), Value(0u)}); const auto condition = fixture.Emit(nonzero ? ValueOpcode::INotEqual32 : ValueOpcode::IEqual32, {mask, Value(0u)}); - auto &guard = fixture.program.block_info[0]; + auto &guard = *fixture.program.blocks[0]; guard.condition = condition; guard.terminator.kind = CFG::TerminatorKind::ConditionalBranch; - guard.terminator.true_block = 1; - guard.terminator.false_block = 4; + guard.terminator.true_block = dispatch; + guard.terminator.false_block = exit; fixture.block = dispatch; const auto first = fixture.Emit(ValueOpcode::FindILsb32, {mask}); const auto minimum = fixture.Emit(ValueOpcode::UMin32, {first, Value(32u)}); const auto selector = fixture.Emit(ValueOpcode::ShiftLeftLogical32, {minimum, Value(2u)}); fixture.Emit(ValueOpcode::ReferenceU32, {selector}); - auto &table = fixture.program.block_info[1]; + auto &table = *fixture.program.blocks[1]; table.indirect_target = selector; table.terminator.kind = CFG::TerminatorKind::IndirectBranch; - table.terminator.indirect_selector_code = 0; - table.terminator.indirect_selector_values = {0u, 128u}; - table.terminator.indirect_selector_targets = {2u, 3u}; + table.terminator.indexed = true; + table.terminator.cases = {{0u, load}, {128u, merge}}; std::array words; for (uint32_t word = 0; word < words.size(); ++word) { auto &phi = merge->AppendNewInst(ValueOpcode::Phi, {}, uint64_t(Type::U32)); @@ -2743,21 +2782,25 @@ void TestBoundedRelativeRegisterWrites() { header->AddBranch(body); header->AddBranch(exit); body->AddBranch(header); - fixture.program.block_info[0].terminator = { - .kind = variant == Variant::EntryBypass ? CFG::TerminatorKind::ConditionalBranch - : CFG::TerminatorKind::Branch, - .true_block = 1u, .false_block = 2u}; - fixture.program.block_info[1].terminator = { + fixture.program.blocks[0]->terminator = { + .kind = variant == Variant::EntryBypass + ? CFG::TerminatorKind::ConditionalBranch + : CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[1u], + .false_block = fixture.program.blocks[2u]}; + fixture.program.blocks[1]->terminator = { .kind = CFG::TerminatorKind::ConditionalBranch, - .true_block = 2u, .false_block = 3u}; - fixture.program.block_info[2].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = 1u}; - fixture.program.block_info[3].terminator.kind = CFG::TerminatorKind::Return; + .true_block = fixture.program.blocks[2u], + .false_block = fixture.program.blocks[3u]}; + fixture.program.blocks[2]->terminator = { + .kind = CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[1u]}; + fixture.program.blocks[3]->terminator.kind = CFG::TerminatorKind::Return; const auto lane = fixture.Emit(ValueOpcode::GetBuiltin, {Value(static_cast(StageInputKind::LocalInvocationId)), Value(0u)}); const auto enabled = fixture.Emit(ValueOpcode::ULessThan32, {lane, Value(32u)}); - fixture.program.block_info[0].condition = enabled; + fixture.program.blocks[0]->condition = enabled; const auto chunk = fixture.Emit(ValueOpcode::SelectU32, {enabled, Value(64u), Value(512u)}); const auto initial_bound = fixture.Emit(ValueOpcode::ShiftRightLogical32, @@ -2769,8 +2812,9 @@ void TestBoundedRelativeRegisterWrites() { auto &records = header->AppendNewInst(ValueOpcode::Phi, {}, uint64_t(Type::U32)); const auto in_range = fixture.Emit(ValueOpcode::ULessThan32, {Value(&counter), Value(&bound)}, 0, header); - fixture.program.block_info[1].condition = fixture.Emit( - ValueOpcode::ConditionRef, {in_range}, CFG::BranchCondition::VccNonZero, header); + fixture.program.blocks[1]->condition = + fixture.Emit(ValueOpcode::ConditionRef, {in_range}, + CFG::BranchCondition::VccNonZero, header); const auto m0 = fixture.Emit(ValueOpcode::BitwiseAnd32, {fixture.Emit(ValueOpcode::ShiftLeftLogical32, {Value(&counter), Value(1u)}, 0, body), Value(255u)}, 0, body); @@ -2953,26 +2997,27 @@ ResourcePlan ConditionalBufferPlan(ConditionalBufferUse use) { auto *done = fixture.AddBlock(); auto *condition_block = entry; uint32_t condition_index = 0; - fixture.program.block_info[0].id = 11; - fixture.program.block_info[1].id = 27; - fixture.program.block_info[2].id = 42; + fixture.program.blocks[0]->id = 11; + fixture.program.blocks[1]->id = 27; + fixture.program.blocks[2]->id = 42; if (use == ConditionalBufferUse::Loop) { condition_block = fixture.AddBlock(); condition_index = 3; - fixture.program.block_info[3].id = 55; - fixture.program.block_info[0].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = 55}; + fixture.program.blocks[3]->id = 55; + fixture.program.blocks[0]->terminator = { + .kind = CFG::TerminatorKind::Branch, .true_block = condition_block}; entry->AddBranch(condition_block); } condition_block->AddBranch(optional); condition_block->AddBranch(done); optional->AddBranch(use == ConditionalBufferUse::Loop ? condition_block : done); - fixture.program.block_info[condition_index].terminator = { + fixture.program.blocks[condition_index]->terminator = { .kind = CFG::TerminatorKind::ConditionalBranch, - .true_block = 27, .false_block = 42}; - fixture.program.block_info[1].terminator = { + .true_block = optional, + .false_block = done}; + fixture.program.blocks[1]->terminator = { .kind = CFG::TerminatorKind::Branch, - .true_block = use == ConditionalBufferUse::Loop ? 55u : 42u}; + .true_block = use == ConditionalBufferUse::Loop ? condition_block : done}; const auto control = fixture.Buffer( {fixture.UserData(0), fixture.UserData(1), fixture.UserData(2), @@ -2988,8 +3033,8 @@ ResourcePlan ConditionalBufferPlan(ConditionalBufferUse use) { phi.AddPhiOperand(optional, Value(1u)); flag = Value(&phi); } - fixture.program.block_info[condition_index].condition = - fixture.Emit(ValueOpcode::INotEqual32, {flag, Value(0u)}, 0, condition_block); + fixture.program.blocks[condition_index]->condition = fixture.Emit( + ValueOpcode::INotEqual32, {flag, Value(0u)}, 0, condition_block); const auto payload = fixture.Buffer( {fixture.UserData(4), fixture.UserData(5), fixture.UserData(6), @@ -3067,11 +3112,14 @@ void TestGuardedScalarDescriptorReads() { entry->AddBranch(optional); entry->AddBranch(done); optional->AddBranch(done); - fixture.program.block_info[0].terminator = { - .kind = CFG::TerminatorKind::ConditionalBranch, .true_block = 1, .false_block = 2}; - fixture.program.block_info[1].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = 2}; - fixture.program.block_info[2].terminator.kind = CFG::TerminatorKind::Return; + fixture.program.blocks[0]->terminator = { + .kind = CFG::TerminatorKind::ConditionalBranch, + .true_block = fixture.program.blocks[1], + .false_block = fixture.program.blocks[2]}; + fixture.program.blocks[1]->terminator = { + .kind = CFG::TerminatorKind::Branch, + .true_block = fixture.program.blocks[2]}; + fixture.program.blocks[2]->terminator.kind = CFG::TerminatorKind::Return; const auto control = fixture.Buffer({fixture.UserData(0), fixture.UserData(1), fixture.UserData(2), fixture.UserData(3)}); MemoryInfo scalar; @@ -3083,7 +3131,7 @@ void TestGuardedScalarDescriptorReads() { {Value(static_cast(StageInputKind::LocalInvocationId)), Value(0u)}); const auto varying = fixture.Emit(ValueOpcode::INotEqual32, {lane, Value(0u)}); const auto enabled = fixture.Emit(ValueOpcode::INotEqual32, {flag, Value(0u)}); - fixture.program.block_info[0].condition = + fixture.program.blocks[0]->condition = fixture.Emit(ValueOpcode::LogicalAnd, {varying, enabled}); const auto root = fixture.Address(fixture.UserData(4), Value(0u)); const auto Load = [&](Block *block) { @@ -3192,15 +3240,16 @@ void TestConditionalIndirectImageMaterialization() { body->AddBranch(done); const auto flag = fixture->Emit(ValueOpcode::GetUserData, {Value(static_cast(8))}, 0, entry); - fixture->program.block_info[1].condition = fixture->Emit( - ValueOpcode::INotEqual32, {flag, Value(0u)}, 0, entry); - fixture->program.block_info[1].terminator = { + fixture->program.blocks[1]->condition = + fixture->Emit(ValueOpcode::INotEqual32, {flag, Value(0u)}, 0, entry); + fixture->program.blocks[1]->terminator = { .kind = CFG::TerminatorKind::ConditionalBranch, - .true_block = 0, .false_block = 2}; - fixture->program.block_info[0].terminator = { - .kind = CFG::TerminatorKind::Branch, .true_block = 2}; + .true_block = fixture->program.blocks[0], + .false_block = fixture->program.blocks[2]}; + fixture->program.blocks[0]->terminator = {.kind = CFG::TerminatorKind::Branch, + .true_block = + fixture->program.blocks[2]}; std::swap(fixture->program.blocks[0], fixture->program.blocks[1]); - std::swap(fixture->program.block_info[0], fixture->program.block_info[1]); fixture->PlanAndTrack(); auto plan = ExtractResourcePlan(fixture->program); std::array user_data{ diff --git a/tests/ScalarProvenanceTests.cpp b/tests/ScalarProvenanceTests.cpp index 4e41a40b0..74600d916 100644 --- a/tests/ScalarProvenanceTests.cpp +++ b/tests/ScalarProvenanceTests.cpp @@ -46,7 +46,7 @@ struct Fixture { program.block_storage.push_back(std::make_unique()); auto *block = program.block_storage.back().get(); program.blocks.push_back(block); - program.block_info.push_back({.id = index}); + program.blocks.back()->id = index; } } @@ -624,14 +624,14 @@ void TestControlFlowValueSurvivesReadLaneFolding() { entry->AddBranch(taken); entry->AddBranch(other); - auto &entry_info = fixture.program.block_info[0]; + auto &entry_info = *fixture.program.blocks[0]; entry_info.terminator.kind = Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch; - entry_info.terminator.true_block = 1; - entry_info.terminator.false_block = 2; - fixture.program.block_info[1].terminator.kind = + entry_info.terminator.true_block = taken; + entry_info.terminator.false_block = other; + fixture.program.blocks[1]->terminator.kind = Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return; - fixture.program.block_info[2].terminator.kind = + fixture.program.blocks[2]->terminator.kind = Libs::Graphics::ShaderRecompiler::CFG::TerminatorKind::Return; const auto undef = fixture.Emit(ValueOpcode::UndefU32); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 80209494f..623209b60 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -34523,7 +34523,6 @@ void CheckIndirectBufferStore(VulkanHarness &vulkan) { program.block_storage.push_back(std::make_unique()); auto *block = program.block_storage.back().get(); program.blocks.push_back(block); - program.block_info.push_back({.id = 0}); auto &lane = block->AppendNewInst(ValueOpcode::LaneId); auto &selector = block->AppendNewInst(ValueOpcode::BitwiseAnd32, @@ -34629,7 +34628,6 @@ void CheckIndirectImageKeySwitch(VulkanHarness &vulkan) { program.block_storage.push_back(std::make_unique()); auto *block = program.block_storage.back().get(); program.blocks.push_back(block); - program.block_info.push_back({.id = 0}); auto &key = block->AppendNewInst(ValueOpcode::LaneId); auto &image = diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 67f7ee66a..e29c76c89 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -1929,13 +1929,14 @@ void TestSopkCompareImmediateExtension() { }; auto translated = TranslateProgram(shader, MakeCompileOptions(ShaderType::Vertex)); - const auto branch = std::ranges::find_if( - translated.program.block_info, [](const auto &block) { - return block.terminator.kind == CFG::TerminatorKind::ConditionalBranch; + const auto branch = + std::ranges::find_if(translated.program.blocks, [](const auto *block) { + return block->terminator.kind == + CFG::TerminatorKind::ConditionalBranch; }); - Check(branch != translated.program.block_info.end(), + Check(branch != translated.program.blocks.end(), "captured LUT parameter export branch was lost"); - const auto condition = branch->condition.Resolve(); + const auto condition = (*branch)->condition.Resolve(); Check(condition.IsImmediate() && condition.U1() == (counts != 6u), "captured LUT unsigned compare suppressed a live parameter export"); } @@ -11083,14 +11084,12 @@ void TestNewShaderRecompilerSetpcJumpTable() { Check((result.ir_dump.find("mode=dispatcher") != std::string::npos), "S_SETPC_B64 jump table did not select dispatcher fallback"); const auto jump = std::find_if( - result.program.block_info.begin(), - result.program.block_info.end(), [](const auto &block) { - return !block.terminator.indirect_targets.empty(); - }); - Check(jump != result.program.block_info.end() && - jump->terminator.indirect_targets.size() == 2, + result.program.blocks.begin(), result.program.blocks.end(), + [](const auto *block) { return !block->terminator.cases.empty(); }); + Check(jump != result.program.blocks.end() && + (*jump)->ImmSuccessors().size() == 2, "S_SETPC_B64 jump table did not retain both targets"); - Check(jump->terminator.indirect_selector_values.size() == 2, + Check((*jump)->terminator.cases.size() == 2, "S_SETPC_B64 jump table did not retain selector mapping"); Check( (result.ir_dump.find("SLoadDword") == std::string::npos), @@ -11187,31 +11186,21 @@ void TestNewShaderRecompilerSetpcDwordJumpTable() { Check((result.ir_dump.find("mode=dispatcher") != std::string::npos), "subtractive S_SETPC_B64 table did not select dispatcher fallback"); const auto jump = std::find_if( - result.program.block_info.begin(), - result.program.block_info.end(), [](const auto &block) { - return !block.terminator.indirect_targets.empty(); - }); - const auto block_pc = [&](uint32_t id) { - const auto block = - std::find_if(result.program.block_info.begin(), - result.program.block_info.end(), - [=](const auto &info) { return info.id == id; }); - return block != result.program.block_info.end() ? block->start_pc - : UINT32_MAX; - }; - Check(jump != result.program.block_info.end() && - jump->terminator.indirect_targets.size() == 2 && - jump->terminator.indirect_target_pcs == - std::vector({0x38u, 0x40u}) && - jump->terminator.indirect_selector_values == - std::vector({0u, 4u, 8u}) && - jump->terminator.indirect_selector_targets.size() == 3 && - block_pc(jump->terminator.indirect_selector_targets[0]) == 0x38u && - block_pc(jump->terminator.indirect_selector_targets[1]) == 0x40u && - block_pc(jump->terminator.indirect_selector_targets[2]) == 0x38u && + result.program.blocks.begin(), result.program.blocks.end(), + [](const auto *block) { return !block->terminator.cases.empty(); }); + Check(jump != result.program.blocks.end() && + (*jump)->ImmSuccessors().size() == 2 && + (*jump)->terminator.indexed && + (*jump)->terminator.cases.size() == 3 && + (*jump)->terminator.cases[0].value == 0u && + (*jump)->terminator.cases[0].target->start_pc == 0x38u && + (*jump)->terminator.cases[1].value == 4u && + (*jump)->terminator.cases[1].target->start_pc == 0x40u && + (*jump)->terminator.cases[2].value == 8u && + (*jump)->terminator.cases[2].target->start_pc == 0x38u && std::ranges::none_of( - result.program.block_info, - [](const auto &info) { return info.start_pc == 0x04u; }), + result.program.blocks, + [](const auto *block) { return block->start_pc == 0x04u; }), "subtractive S_SETPC_B64 table targets or selector mapping changed"); Check((result.ir_dump.find("SLoadDword") == std::string::npos), "subtractive S_SETPC_B64 table load reached normal IR"); @@ -11802,7 +11791,6 @@ void TestFinalSsaRejectsRegisterStatePseudos() { Program program; program.block_storage.push_back(std::make_unique()); program.blocks.push_back(program.block_storage.back().get()); - program.block_info.emplace_back(); IREmitter ir(program.blocks.front()); if (setter) { ir.SetScalarReg(ScalarReg{0}, U32(Value(1u))); @@ -11842,7 +11830,7 @@ void TestValuePhiValidation() { program.block_storage.push_back(std::make_unique()); auto *block = program.block_storage.back().get(); program.blocks.push_back(block); - program.block_info.push_back({.id = id}); + program.blocks.back()->id = id; return block; }; @@ -11850,17 +11838,17 @@ void TestValuePhiValidation() { auto *left = add_block(1); auto *right = add_block(2); auto *join = add_block(3); - program.block_info[0].terminator.kind = + program.blocks[0]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch; - program.block_info[0].terminator.true_block = 1; - program.block_info[0].terminator.false_block = 2; - program.block_info[0].condition = Value(true); - program.block_info[1].terminator.kind = + program.blocks[0]->terminator.true_block = program.blocks[1]; + program.blocks[0]->terminator.false_block = program.blocks[2]; + program.blocks[0]->condition = Value(true); + program.blocks[1]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Branch; - program.block_info[1].terminator.true_block = 3; - program.block_info[2].terminator.kind = + program.blocks[1]->terminator.true_block = program.blocks[3]; + program.blocks[2]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Branch; - program.block_info[2].terminator.true_block = 3; + program.blocks[2]->terminator.true_block = program.blocks[3]; entry->AddBranch(left); entry->AddBranch(right); left->AddBranch(join); @@ -11910,12 +11898,12 @@ void TestValuePhiValidation() { program.block_storage.push_back(std::make_unique()); auto *block = program.block_storage.back().get(); program.blocks.push_back(block); - program.block_info.push_back({.id = duplicate_id ? 0u : id}); + program.blocks.back()->id = duplicate_id ? 0u : id; } if (!duplicate_id) { - program.block_info[0].terminator.kind = + program.blocks[0]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Branch; - program.block_info[0].terminator.true_block = 1; + program.blocks[0]->terminator.true_block = program.blocks[1]; } expect_invalid(program, "malformed Value CFG did not terminate IR validation"); @@ -11928,14 +11916,14 @@ void TestValuePhiValidation() { for (uint32_t id = 0; id < 2; id++) { program.block_storage.push_back(std::make_unique()); program.blocks.push_back(program.block_storage.back().get()); - program.block_info.push_back({.id = id}); + program.blocks.back()->id = id; } - program.block_info[0].terminator.kind = + program.blocks[0]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Branch; - program.block_info[0].terminator.true_block = 1; - program.block_info[1].terminator.kind = + program.blocks[0]->terminator.true_block = program.blocks[1]; + program.blocks[1]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Branch; - program.block_info[1].terminator.true_block = 0; + program.blocks[1]->terminator.true_block = program.blocks[0]; program.blocks[0]->AddBranch(program.blocks[1]); program.blocks[1]->AddBranch(program.blocks[0]); expect_invalid(program, @@ -11946,17 +11934,15 @@ void TestValuePhiValidation() { for (uint32_t id = 0; id < 3; id++) { program.block_storage.push_back(std::make_unique()); program.blocks.push_back(program.block_storage.back().get()); - program.block_info.push_back({.id = id}); + program.blocks.back()->id = id; } - program.block_info[0].terminator.kind = + program.blocks[0]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Branch; - program.block_info[0].terminator.true_block = 1; - auto &indirect = program.block_info[1]; + program.blocks[0]->terminator.true_block = program.blocks[1]; + auto &indirect = *program.blocks[1]; indirect.terminator.kind = ShaderRecompiler::CFG::TerminatorKind::IndirectBranch; - indirect.terminator.indirect_targets = {1}; - indirect.terminator.indirect_selector_values = {0}; - indirect.terminator.indirect_selector_targets = {2}; + indirect.terminator.cases = {{0, program.blocks[2]}}; indirect.indirect_target = Value(0u); program.blocks[0]->AddBranch(program.blocks[1]); program.blocks[1]->AddBranch(program.blocks[1]); @@ -11968,7 +11954,7 @@ void TestValuePhiValidation() { Program program; program.block_storage.push_back(std::make_unique()); program.blocks.push_back(program.block_storage.back().get()); - program.block_info.push_back({.id = UINT32_MAX}); + program.blocks.back()->id = UINT32_MAX; expect_invalid(program, "reserved exit block id did not terminate IR validation"); } @@ -11977,27 +11963,29 @@ void TestValuePhiValidation() { for (uint32_t id = 0; id < 2; id++) { program.block_storage.push_back(std::make_unique()); program.blocks.push_back(program.block_storage.back().get()); - program.block_info.push_back({.id = id}); + program.blocks.back()->id = id; } - program.block_info[0].terminator.kind = + program.blocks[0]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Branch; - program.block_info[0].terminator.true_block = 1; - auto &indirect = program.block_info[1]; + program.blocks[0]->terminator.true_block = program.blocks[1]; + auto &indirect = *program.blocks[1]; indirect.terminator.kind = ShaderRecompiler::CFG::TerminatorKind::IndirectBranch; - indirect.terminator.indirect_targets = {1, 1}; + indirect.terminator.cases = {{0, program.blocks[1]}, + {0, program.blocks[1]}}; indirect.indirect_target = Value(0u); program.blocks[0]->AddBranch(program.blocks[1]); program.blocks[1]->AddBranch(program.blocks[1]); - expect_invalid(program, - "duplicate indirect targets did not terminate IR validation"); + expect_invalid( + program, + "duplicate indirect selector values did not terminate IR validation"); } { Program program; for (uint32_t id = 0; id < 2; id++) { program.block_storage.push_back(std::make_unique()); program.blocks.push_back(program.block_storage.back().get()); - program.block_info.push_back({.id = id}); + program.blocks.back()->id = id; } expect_invalid(program, "unreachable block did not terminate IR validation"); @@ -12007,7 +11995,6 @@ void TestValuePhiValidation() { program.block_storage.push_back(std::make_unique()); auto *block = program.block_storage.back().get(); program.blocks.push_back(block); - program.block_info.push_back({.id = 0}); auto &use = block->AppendNewInst(ValueOpcode::IAdd32, {Value(1u), Value(2u)}); auto &definition = @@ -12021,17 +12008,17 @@ void TestValuePhiValidation() { for (uint32_t id = 0; id < 4; id++) { program.block_storage.push_back(std::make_unique()); program.blocks.push_back(program.block_storage.back().get()); - program.block_info.push_back({.id = id}); + program.blocks.back()->id = id; } - program.block_info[0].terminator.kind = + program.blocks[0]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch; - program.block_info[0].terminator.true_block = 1; - program.block_info[0].terminator.false_block = 2; - program.block_info[0].condition = Value(true); + program.blocks[0]->terminator.true_block = program.blocks[1]; + program.blocks[0]->terminator.false_block = program.blocks[2]; + program.blocks[0]->condition = Value(true); for (uint32_t id : {1u, 2u}) { - program.block_info[id].terminator.kind = + program.blocks[id]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Branch; - program.block_info[id].terminator.true_block = 3; + program.blocks[id]->terminator.true_block = program.blocks[3]; } program.blocks[0]->AddBranch(program.blocks[1]); program.blocks[0]->AddBranch(program.blocks[2]); @@ -12049,17 +12036,17 @@ void TestValuePhiValidation() { for (uint32_t id = 0; id < 4; id++) { program.block_storage.push_back(std::make_unique()); program.blocks.push_back(program.block_storage.back().get()); - program.block_info.push_back({.id = id}); + program.blocks.back()->id = id; } - program.block_info[0].terminator.kind = + program.blocks[0]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch; - program.block_info[0].terminator.true_block = 1; - program.block_info[0].terminator.false_block = 2; - program.block_info[0].condition = Value(true); + program.blocks[0]->terminator.true_block = program.blocks[1]; + program.blocks[0]->terminator.false_block = program.blocks[2]; + program.blocks[0]->condition = Value(true); for (uint32_t id : {1u, 2u}) { - program.block_info[id].terminator.kind = + program.blocks[id]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Branch; - program.block_info[id].terminator.true_block = 3; + program.blocks[id]->terminator.true_block = program.blocks[3]; } program.blocks[0]->AddBranch(program.blocks[1]); program.blocks[0]->AddBranch(program.blocks[2]); @@ -12080,23 +12067,23 @@ void TestValuePhiValidation() { for (uint32_t id = 0; id < 5; id++) { program.block_storage.push_back(std::make_unique()); program.blocks.push_back(program.block_storage.back().get()); - program.block_info.push_back({.id = id}); + program.blocks.back()->id = id; } - program.block_info[0].terminator.kind = + program.blocks[0]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch; - program.block_info[0].terminator.true_block = 1; - program.block_info[0].terminator.false_block = 2; - program.block_info[0].condition = Value(true); - program.block_info[1].terminator.kind = + program.blocks[0]->terminator.true_block = program.blocks[1]; + program.blocks[0]->terminator.false_block = program.blocks[2]; + program.blocks[0]->condition = Value(true); + program.blocks[1]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Branch; - program.block_info[1].terminator.true_block = 3; - program.block_info[2].terminator.kind = + program.blocks[1]->terminator.true_block = program.blocks[3]; + program.blocks[2]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch; - program.block_info[2].terminator.true_block = 3; - program.block_info[2].terminator.false_block = 4; - program.block_info[3].terminator.kind = + program.blocks[2]->terminator.true_block = program.blocks[3]; + program.blocks[2]->terminator.false_block = program.blocks[4]; + program.blocks[3]->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Branch; - program.block_info[3].terminator.true_block = 4; + program.blocks[3]->terminator.true_block = program.blocks[4]; program.blocks[0]->AddBranch(program.blocks[1]); program.blocks[0]->AddBranch(program.blocks[2]); program.blocks[1]->AddBranch(program.blocks[3]); @@ -12105,7 +12092,7 @@ void TestValuePhiValidation() { program.blocks[3]->AddBranch(program.blocks[4]); auto &condition = program.blocks[1]->AppendNewInst(ValueOpcode::IEqual32, {Value(1u), Value(2u)}); - program.block_info[2].condition = Value(&condition); + program.blocks[2]->condition = Value(&condition); expect_invalid(program, "non-dominating branch condition did not terminate IR " "validation"); @@ -12122,19 +12109,22 @@ void TestWave32MaskProjection() { for (uint32_t id = 0; id < 3; ++id) { program.block_storage.push_back(std::make_unique()); program.blocks.push_back(program.block_storage.back().get()); - program.block_info.push_back({.id = id}); + program.blocks.back()->id = id; } auto *entry = program.blocks[0]; auto *loop = program.blocks[1]; entry->AddBranch(loop); loop->AddBranch(loop); loop->AddBranch(program.blocks[2]); - program.block_info[0].terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Branch; - program.block_info[0].terminator.true_block = 1; - program.block_info[1].terminator.kind = ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch; - program.block_info[1].terminator.true_block = 1; - program.block_info[1].terminator.false_block = 2; - program.block_info[2].terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Return; + program.blocks[0]->terminator.kind = + ShaderRecompiler::CFG::TerminatorKind::Branch; + program.blocks[0]->terminator.true_block = program.blocks[1]; + program.blocks[1]->terminator.kind = + ShaderRecompiler::CFG::TerminatorKind::ConditionalBranch; + program.blocks[1]->terminator.true_block = program.blocks[1]; + program.blocks[1]->terminator.false_block = program.blocks[2]; + program.blocks[2]->terminator.kind = + ShaderRecompiler::CFG::TerminatorKind::Return; IREmitter e(entry); const auto zero = U32(Value(0u)); const auto raw = e.GetUserData(static_cast(0)); @@ -12188,7 +12178,7 @@ void TestWave32MaskProjection() { mask.AddPhiOperand(loop, remaining); predicate.AddPhiOperand(entry, initial_predicate); predicate.AddPhiOperand(loop, remaining_predicate); - program.block_info[1].condition = remaining_predicate; + program.blocks[1]->condition = remaining_predicate; ConstantPropagationPass(program.blocks, wave_size); if (wave_size == 32u) { @@ -12237,7 +12227,6 @@ void TestU64ShiftConstantPropagation() { Program shifts; shifts.block_storage.push_back(std::make_unique()); shifts.blocks.push_back(shifts.block_storage.back().get()); - shifts.block_info.emplace_back(); IREmitter shift_ir(shifts.blocks.front()); struct ShiftCase { Value value; @@ -12309,9 +12298,7 @@ void TestNativeWideValueValidation() { Program program; program.block_storage.push_back(std::make_unique()); program.blocks.push_back(program.block_storage.back().get()); - program.block_info.emplace_back(); - program.block_info.front().id = 0; - program.block_info.front().terminator.kind = + program.blocks.front()->terminator.kind = ShaderRecompiler::CFG::TerminatorKind::Return; return program; };