mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-26 21:06:53 +01:00
shader: build CFG labels and targets from ordered storage
This commit is contained in:
@@ -8,7 +8,6 @@
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
|
||||||
#include <span>
|
#include <span>
|
||||||
|
|
||||||
namespace Libs::Graphics::ShaderRecompiler::CFG {
|
namespace Libs::Graphics::ShaderRecompiler::CFG {
|
||||||
@@ -568,9 +567,8 @@ bool ResolveSetpcTargets(const Decoder::Program& program, uint32_t setpc_index,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValidTarget(uint32_t target, const std::set<uint32_t>& instruction_pcs, uint32_t first_pc,
|
bool IsValidTarget(uint32_t target, std::span<const Instruction> instructions, uint32_t end_pc) {
|
||||||
uint32_t end_pc) {
|
return target == end_pc || std::ranges::binary_search(instructions, target, {}, &Instruction::pc);
|
||||||
return target == end_pc || (target >= first_pc && instruction_pcs.contains(target));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint32_t> AllBlockIds(uint32_t count) {
|
std::vector<uint32_t> AllBlockIds(uint32_t count) {
|
||||||
@@ -1455,9 +1453,7 @@ Graph BuildGraph(const Decoder::Program& program) {
|
|||||||
const auto first_pc = program.instructions.front().pc;
|
const auto first_pc = program.instructions.front().pc;
|
||||||
const auto end_pc = ProgramEndPc(program);
|
const auto end_pc = ProgramEndPc(program);
|
||||||
|
|
||||||
std::set<uint32_t> instruction_pcs;
|
|
||||||
for (const auto& inst: program.instructions) {
|
for (const auto& inst: program.instructions) {
|
||||||
instruction_pcs.insert(inst.pc);
|
|
||||||
if (inst.opcode == Opcode::UNSUPPORTED) {
|
if (inst.opcode == Opcode::UNSUPPORTED) {
|
||||||
ExitBuildFailure(
|
ExitBuildFailure(
|
||||||
graph, FailureKind::UnsupportedInstruction, UINT32_MAX,
|
graph, FailureKind::UnsupportedInstruction, UINT32_MAX,
|
||||||
@@ -1466,23 +1462,21 @@ Graph BuildGraph(const Decoder::Program& program) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<uint32_t> labels;
|
std::vector<uint32_t> labels {first_pc, end_pc};
|
||||||
labels.insert(first_pc);
|
|
||||||
labels.insert(end_pc);
|
|
||||||
|
|
||||||
std::map<uint32_t, SetpcTargetInfo> setpc_targets;
|
std::map<uint32_t, SetpcTargetInfo> setpc_targets;
|
||||||
for (uint32_t i = 0; i < program.instructions.size(); i++) {
|
for (uint32_t i = 0; i < program.instructions.size(); i++) {
|
||||||
const auto& inst = program.instructions[i];
|
const auto& inst = program.instructions[i];
|
||||||
const auto next_pc = InstructionEndPc(inst);
|
const auto next_pc = InstructionEndPc(inst);
|
||||||
if (Decoder::IsDirectBranch(inst.opcode)) {
|
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,
|
ExitBuildFailure(graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
|
||||||
fmt::format("branch at pc 0x{:08x} targets invalid pc 0x{:08x}",
|
fmt::format("branch at pc 0x{:08x} targets invalid pc 0x{:08x}",
|
||||||
inst.pc, inst.branch_target));
|
inst.pc, inst.branch_target));
|
||||||
}
|
}
|
||||||
labels.insert(inst.branch_target);
|
labels.push_back(inst.branch_target);
|
||||||
if (next_pc <= end_pc) {
|
if (next_pc <= end_pc) {
|
||||||
labels.insert(next_pc);
|
labels.push_back(next_pc);
|
||||||
}
|
}
|
||||||
} else if (inst.opcode == Opcode::S_SETPC_B64) {
|
} else if (inst.opcode == Opcode::S_SETPC_B64) {
|
||||||
SetpcTargetInfo target_info;
|
SetpcTargetInfo target_info;
|
||||||
@@ -1495,30 +1489,32 @@ Graph BuildGraph(const Decoder::Program& program) {
|
|||||||
? std::span<const uint32_t>(target_info.target_pcs)
|
? std::span<const uint32_t>(target_info.target_pcs)
|
||||||
: std::span<const uint32_t>(&target_info.target, 1);
|
: std::span<const uint32_t>(&target_info.target, 1);
|
||||||
for (const auto target: target_pcs) {
|
for (const auto target: target_pcs) {
|
||||||
if (!IsValidTarget(target, instruction_pcs, first_pc, end_pc)) {
|
if (!IsValidTarget(target, program.instructions, end_pc)) {
|
||||||
ExitBuildFailure(
|
ExitBuildFailure(
|
||||||
graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
|
graph, FailureKind::InvalidBranchTarget, UINT32_MAX,
|
||||||
fmt::format("S_SETPC_B64 at pc 0x{:08x} targets invalid pc 0x{:08x}",
|
fmt::format("S_SETPC_B64 at pc 0x{:08x} targets invalid pc 0x{:08x}",
|
||||||
inst.pc, target));
|
inst.pc, target));
|
||||||
}
|
}
|
||||||
labels.insert(target);
|
labels.push_back(target);
|
||||||
}
|
}
|
||||||
setpc_targets.emplace(inst.pc, std::move(target_info));
|
setpc_targets.emplace(inst.pc, std::move(target_info));
|
||||||
if (next_pc <= end_pc) {
|
if (next_pc <= end_pc) {
|
||||||
labels.insert(next_pc);
|
labels.push_back(next_pc);
|
||||||
}
|
}
|
||||||
} else if (inst.opcode == Opcode::S_ENDPGM) {
|
} else if (inst.opcode == Opcode::S_ENDPGM) {
|
||||||
labels.insert(next_pc);
|
labels.push_back(next_pc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint32_t> sorted_labels(labels.begin(), labels.end());
|
SortUnique(labels);
|
||||||
for (uint32_t i = 0; i < sorted_labels.size(); i++) {
|
graph.blocks.reserve(labels.size());
|
||||||
const auto start = sorted_labels[i];
|
for (uint32_t i = 0; i < labels.size(); i++) {
|
||||||
|
const auto start = labels[i];
|
||||||
if (start > end_pc) {
|
if (start > end_pc) {
|
||||||
continue;
|
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(
|
ExitBuildFailure(
|
||||||
graph, FailureKind::InvalidLabel, UINT32_MAX,
|
graph, FailureKind::InvalidLabel, UINT32_MAX,
|
||||||
fmt::format("CFG label does not start on an instruction: 0x{:08x}", start));
|
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;
|
BasicBlock block;
|
||||||
block.id = static_cast<uint32_t>(graph.blocks.size());
|
block.id = static_cast<uint32_t>(graph.blocks.size());
|
||||||
block.start_pc = start;
|
block.start_pc = start;
|
||||||
block.end_pc = i + 1u < sorted_labels.size() ? sorted_labels[i + 1u] : end_pc;
|
block.end_pc = i + 1u < labels.size() ? labels[i + 1u] : end_pc;
|
||||||
block.inst_begin = static_cast<uint32_t>(
|
block.inst_begin = static_cast<uint32_t>(begin - program.instructions.begin());
|
||||||
std::lower_bound(program.instructions.begin(), program.instructions.end(), start,
|
|
||||||
[](const Instruction& inst, uint32_t pc) { return inst.pc < pc; }) -
|
|
||||||
program.instructions.begin());
|
|
||||||
block.inst_end = static_cast<uint32_t>(
|
block.inst_end = static_cast<uint32_t>(
|
||||||
std::lower_bound(program.instructions.begin(), program.instructions.end(), block.end_pc,
|
std::lower_bound(program.instructions.begin(), program.instructions.end(), block.end_pc,
|
||||||
[](const Instruction& inst, uint32_t pc) { return inst.pc < 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;
|
graph.entry_block = 0;
|
||||||
|
|
||||||
std::map<uint32_t, uint32_t> pc_to_block;
|
const auto block_at_pc = [&](uint32_t pc) {
|
||||||
for (const auto& block: graph.blocks) {
|
const auto found = std::ranges::lower_bound(graph.blocks, pc, {}, &BasicBlock::start_pc);
|
||||||
pc_to_block.emplace(block.start_pc, block.id);
|
return found != graph.blocks.end() && found->start_pc == pc ? found->id : UINT32_MAX;
|
||||||
}
|
};
|
||||||
|
|
||||||
for (auto& block: graph.blocks) {
|
for (auto& block: graph.blocks) {
|
||||||
block.terminator = {};
|
block.terminator = {};
|
||||||
@@ -1566,7 +1559,7 @@ Graph BuildGraph(const Decoder::Program& program) {
|
|||||||
block.terminator.indirect_selector_code = target_info.selector_code;
|
block.terminator.indirect_selector_code = target_info.selector_code;
|
||||||
for (const auto target_pc: target_info.target_pcs) {
|
for (const auto target_pc: target_info.target_pcs) {
|
||||||
block.terminator.indirect_target_pcs.push_back(target_pc);
|
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(),
|
const auto selector_count = std::min(target_info.selector_values.size(),
|
||||||
target_info.selector_target_pcs.size());
|
target_info.selector_target_pcs.size());
|
||||||
@@ -1574,35 +1567,35 @@ Graph BuildGraph(const Decoder::Program& program) {
|
|||||||
block.terminator.indirect_selector_values.push_back(
|
block.terminator.indirect_selector_values.push_back(
|
||||||
target_info.selector_values[i]);
|
target_info.selector_values[i]);
|
||||||
block.terminator.indirect_selector_targets.push_back(
|
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 {
|
} else {
|
||||||
block.terminator.kind = TerminatorKind::Branch;
|
block.terminator.kind = TerminatorKind::Branch;
|
||||||
block.terminator.condition = BranchCondition::Always;
|
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) {
|
} else if (last.opcode == Opcode::S_BRANCH) {
|
||||||
block.terminator.kind = TerminatorKind::Branch;
|
block.terminator.kind = TerminatorKind::Branch;
|
||||||
block.terminator.condition = BranchCondition::Always;
|
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)) {
|
} else if (Decoder::IsConditionalBranch(last.opcode)) {
|
||||||
block.terminator.kind = TerminatorKind::ConditionalBranch;
|
block.terminator.kind = TerminatorKind::ConditionalBranch;
|
||||||
block.terminator.condition = ConditionForOpcode(last.opcode);
|
block.terminator.condition = ConditionForOpcode(last.opcode);
|
||||||
block.terminator.true_block = pc_to_block.at(last.branch_target);
|
block.terminator.true_block = block_at_pc(last.branch_target);
|
||||||
const auto fallthrough = pc_to_block.find(next_pc);
|
const auto fallthrough = block_at_pc(next_pc);
|
||||||
if (fallthrough == pc_to_block.end()) {
|
if (fallthrough == UINT32_MAX) {
|
||||||
ExitBuildFailure(
|
ExitBuildFailure(
|
||||||
graph, FailureKind::MissingFallthrough, block.id,
|
graph, FailureKind::MissingFallthrough, block.id,
|
||||||
fmt::format("conditional branch at pc 0x{:08x} has no fallthrough block",
|
fmt::format("conditional branch at pc 0x{:08x} has no fallthrough block",
|
||||||
last.pc));
|
last.pc));
|
||||||
}
|
}
|
||||||
block.terminator.false_block = fallthrough->second;
|
block.terminator.false_block = fallthrough;
|
||||||
} else {
|
} else {
|
||||||
auto next = pc_to_block.find(block.end_pc);
|
const auto next = block_at_pc(block.end_pc);
|
||||||
if (next != pc_to_block.end() && block.end_pc != block.start_pc) {
|
if (next != UINT32_MAX && block.end_pc != block.start_pc) {
|
||||||
block.terminator.kind = TerminatorKind::Branch;
|
block.terminator.kind = TerminatorKind::Branch;
|
||||||
block.terminator.condition = BranchCondition::Always;
|
block.terminator.condition = BranchCondition::Always;
|
||||||
block.terminator.true_block = next->second;
|
block.terminator.true_block = next;
|
||||||
} else {
|
} else {
|
||||||
block.terminator.kind = TerminatorKind::Return;
|
block.terminator.kind = TerminatorKind::Return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user