diff --git a/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp b/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp index 8729678f3..860fd2291 100644 --- a/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp +++ b/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include namespace Libs::Graphics::ShaderRecompiler::CFG { @@ -568,9 +567,8 @@ bool ResolveSetpcTargets(const Decoder::Program& program, uint32_t setpc_index, return false; } -bool IsValidTarget(uint32_t target, const std::set& instruction_pcs, uint32_t first_pc, - uint32_t end_pc) { - return target == end_pc || (target >= first_pc && instruction_pcs.contains(target)); +bool IsValidTarget(uint32_t target, std::span instructions, uint32_t end_pc) { + return target == end_pc || std::ranges::binary_search(instructions, target, {}, &Instruction::pc); } std::vector AllBlockIds(uint32_t count) { @@ -1455,9 +1453,7 @@ Graph BuildGraph(const Decoder::Program& program) { const auto first_pc = program.instructions.front().pc; const auto end_pc = ProgramEndPc(program); - std::set instruction_pcs; for (const auto& inst: program.instructions) { - instruction_pcs.insert(inst.pc); if (inst.opcode == Opcode::UNSUPPORTED) { ExitBuildFailure( graph, FailureKind::UnsupportedInstruction, UINT32_MAX, @@ -1466,23 +1462,21 @@ Graph BuildGraph(const Decoder::Program& program) { } } - std::set labels; - labels.insert(first_pc); - labels.insert(end_pc); + std::vector labels {first_pc, end_pc}; std::map setpc_targets; for (uint32_t i = 0; i < program.instructions.size(); i++) { const auto& inst = program.instructions[i]; const auto next_pc = InstructionEndPc(inst); if (Decoder::IsDirectBranch(inst.opcode)) { - if (!IsValidTarget(inst.branch_target, instruction_pcs, first_pc, end_pc)) { + if (!IsValidTarget(inst.branch_target, program.instructions, end_pc)) { ExitBuildFailure(graph, FailureKind::InvalidBranchTarget, UINT32_MAX, fmt::format("branch at pc 0x{:08x} targets invalid pc 0x{:08x}", inst.pc, inst.branch_target)); } - labels.insert(inst.branch_target); + labels.push_back(inst.branch_target); if (next_pc <= end_pc) { - labels.insert(next_pc); + labels.push_back(next_pc); } } else if (inst.opcode == Opcode::S_SETPC_B64) { SetpcTargetInfo target_info; @@ -1495,30 +1489,32 @@ Graph BuildGraph(const Decoder::Program& program) { ? std::span(target_info.target_pcs) : std::span(&target_info.target, 1); for (const auto target: target_pcs) { - if (!IsValidTarget(target, instruction_pcs, first_pc, end_pc)) { + if (!IsValidTarget(target, program.instructions, end_pc)) { ExitBuildFailure( graph, FailureKind::InvalidBranchTarget, UINT32_MAX, fmt::format("S_SETPC_B64 at pc 0x{:08x} targets invalid pc 0x{:08x}", inst.pc, target)); } - labels.insert(target); + labels.push_back(target); } setpc_targets.emplace(inst.pc, std::move(target_info)); if (next_pc <= end_pc) { - labels.insert(next_pc); + labels.push_back(next_pc); } } else if (inst.opcode == Opcode::S_ENDPGM) { - labels.insert(next_pc); + labels.push_back(next_pc); } } - std::vector sorted_labels(labels.begin(), labels.end()); - for (uint32_t i = 0; i < sorted_labels.size(); i++) { - const auto start = sorted_labels[i]; + SortUnique(labels); + graph.blocks.reserve(labels.size()); + for (uint32_t i = 0; i < labels.size(); i++) { + const auto start = labels[i]; if (start > end_pc) { continue; } - if (start != end_pc && !instruction_pcs.contains(start)) { + const auto begin = std::ranges::lower_bound(program.instructions, start, {}, &Instruction::pc); + if (start != end_pc && (begin == program.instructions.end() || begin->pc != start)) { ExitBuildFailure( graph, FailureKind::InvalidLabel, UINT32_MAX, fmt::format("CFG label does not start on an instruction: 0x{:08x}", start)); @@ -1527,11 +1523,8 @@ Graph BuildGraph(const Decoder::Program& program) { BasicBlock block; block.id = static_cast(graph.blocks.size()); block.start_pc = start; - block.end_pc = i + 1u < sorted_labels.size() ? sorted_labels[i + 1u] : end_pc; - block.inst_begin = static_cast( - std::lower_bound(program.instructions.begin(), program.instructions.end(), start, - [](const Instruction& inst, uint32_t pc) { return inst.pc < pc; }) - - program.instructions.begin()); + block.end_pc = i + 1u < labels.size() ? labels[i + 1u] : end_pc; + block.inst_begin = static_cast(begin - program.instructions.begin()); block.inst_end = static_cast( std::lower_bound(program.instructions.begin(), program.instructions.end(), block.end_pc, [](const Instruction& inst, uint32_t pc) { return inst.pc < pc; }) - @@ -1541,10 +1534,10 @@ Graph BuildGraph(const Decoder::Program& program) { graph.entry_block = 0; - std::map pc_to_block; - for (const auto& block: graph.blocks) { - pc_to_block.emplace(block.start_pc, block.id); - } + const auto block_at_pc = [&](uint32_t pc) { + const auto found = std::ranges::lower_bound(graph.blocks, pc, {}, &BasicBlock::start_pc); + return found != graph.blocks.end() && found->start_pc == pc ? found->id : UINT32_MAX; + }; for (auto& block: graph.blocks) { block.terminator = {}; @@ -1566,7 +1559,7 @@ Graph BuildGraph(const Decoder::Program& program) { block.terminator.indirect_selector_code = target_info.selector_code; for (const auto target_pc: target_info.target_pcs) { block.terminator.indirect_target_pcs.push_back(target_pc); - block.terminator.indirect_targets.push_back(pc_to_block.at(target_pc)); + block.terminator.indirect_targets.push_back(block_at_pc(target_pc)); } const auto selector_count = std::min(target_info.selector_values.size(), target_info.selector_target_pcs.size()); @@ -1574,35 +1567,35 @@ Graph BuildGraph(const Decoder::Program& program) { block.terminator.indirect_selector_values.push_back( target_info.selector_values[i]); block.terminator.indirect_selector_targets.push_back( - pc_to_block.at(target_info.selector_target_pcs[i])); + block_at_pc(target_info.selector_target_pcs[i])); } } else { block.terminator.kind = TerminatorKind::Branch; block.terminator.condition = BranchCondition::Always; - block.terminator.true_block = pc_to_block.at(target_info.target); + block.terminator.true_block = block_at_pc(target_info.target); } } else if (last.opcode == Opcode::S_BRANCH) { block.terminator.kind = TerminatorKind::Branch; block.terminator.condition = BranchCondition::Always; - block.terminator.true_block = pc_to_block.at(last.branch_target); + block.terminator.true_block = block_at_pc(last.branch_target); } else if (Decoder::IsConditionalBranch(last.opcode)) { block.terminator.kind = TerminatorKind::ConditionalBranch; block.terminator.condition = ConditionForOpcode(last.opcode); - block.terminator.true_block = pc_to_block.at(last.branch_target); - const auto fallthrough = pc_to_block.find(next_pc); - if (fallthrough == pc_to_block.end()) { + block.terminator.true_block = block_at_pc(last.branch_target); + const auto fallthrough = block_at_pc(next_pc); + if (fallthrough == UINT32_MAX) { ExitBuildFailure( graph, FailureKind::MissingFallthrough, block.id, fmt::format("conditional branch at pc 0x{:08x} has no fallthrough block", last.pc)); } - block.terminator.false_block = fallthrough->second; + block.terminator.false_block = fallthrough; } else { - auto next = pc_to_block.find(block.end_pc); - if (next != pc_to_block.end() && block.end_pc != block.start_pc) { + const auto next = block_at_pc(block.end_pc); + if (next != UINT32_MAX && block.end_pc != block.start_pc) { block.terminator.kind = TerminatorKind::Branch; block.terminator.condition = BranchCondition::Always; - block.terminator.true_block = next->second; + block.terminator.true_block = next; } else { block.terminator.kind = TerminatorKind::Return; }