mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-23 04:16:57 +02:00
shader: canonicalize CFG loop headers
This commit is contained in:
@@ -1264,6 +1264,11 @@ bool IsInnermostLoopControlConditional(const Graph& graph, const BasicBlock& blo
|
||||
};
|
||||
return is_repeat_target(true_target) && is_repeat_target(false_target);
|
||||
}
|
||||
const bool true_in_body = Contains(loop->body_blocks, true_target);
|
||||
const bool false_in_body = Contains(loop->body_blocks, false_target);
|
||||
if (true_in_body != false_in_body) {
|
||||
return true;
|
||||
}
|
||||
const auto is_control_target = [&](uint32_t target) {
|
||||
return target == loop->merge || target == loop->continue_block;
|
||||
};
|
||||
@@ -1342,6 +1347,37 @@ bool CanonicalizeNaturalLoops(Graph& graph, std::string* error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsolateSemanticLoopHeaders(Graph& graph, std::string* error) {
|
||||
const auto isolation_budget = graph.natural_loops.size() + 1u;
|
||||
for (size_t isolation = 0; isolation < isolation_budget; isolation++) {
|
||||
const auto loop = std::find_if(graph.natural_loops.begin(), graph.natural_loops.end(),
|
||||
[&](const auto& value) {
|
||||
const auto* header = graph.FindBlock(value.header);
|
||||
return header != nullptr &&
|
||||
header->inst_begin != header->inst_end;
|
||||
});
|
||||
if (loop == graph.natural_loops.end()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// SPIR-V requires OpLoopMerge and its branch to remain in the loop header's
|
||||
// physical block. Keep that header as a dedicated control node, exactly like
|
||||
// the separate Loop node in shadPS4's structured AST. Guest instructions live
|
||||
// in the body so later lowering may introduce bounds/EXEC control flow without
|
||||
// displacing OpLoopMerge into a helper-created block.
|
||||
if (!IsolateSemanticLoopHeader(graph, loop->header)) {
|
||||
SetFailure(graph, FailureKind::StructuredControlFlow, loop->header,
|
||||
fmt::format("failed to isolate semantic loop header {}", loop->header),
|
||||
error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SetFailure(graph, FailureKind::StructuredControlFlow, graph.entry_block,
|
||||
"CFG semantic loop-header isolation exceeded rewrite budget", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SplitSharedMergeBlock(Graph& graph, uint32_t merge,
|
||||
const std::vector<uint32_t>& construct_blocks,
|
||||
bool force_split = false) {
|
||||
@@ -2113,6 +2149,9 @@ bool StructurizeImpl(Graph& graph, std::string* error) {
|
||||
if (!SplitSharedMergeBlocks(graph, error)) {
|
||||
return false;
|
||||
}
|
||||
if (!IsolateSemanticLoopHeaders(graph, error)) {
|
||||
return false;
|
||||
}
|
||||
ClearStructuredTerminators(graph);
|
||||
|
||||
std::map<uint32_t, uint32_t> merge_headers;
|
||||
@@ -2139,6 +2178,14 @@ bool StructurizeImpl(Graph& graph, std::string* error) {
|
||||
error);
|
||||
return false;
|
||||
}
|
||||
if (header->inst_begin != header->inst_end ||
|
||||
header->terminator.kind != TerminatorKind::Branch) {
|
||||
SetFailure(graph, FailureKind::StructuredControlFlow, loop.header,
|
||||
fmt::format("loop header {} is not a dedicated empty control block",
|
||||
loop.header),
|
||||
error);
|
||||
return false;
|
||||
}
|
||||
if (!reserve_merge_block(loop.header, loop.merge)) {
|
||||
return false;
|
||||
}
|
||||
@@ -2207,31 +2254,6 @@ bool Structurize(Graph& graph, std::string* error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsolateLoopHeader(Graph& graph, uint32_t header_id, std::string* error) {
|
||||
const auto loop =
|
||||
std::find_if(graph.natural_loops.begin(), graph.natural_loops.end(),
|
||||
[header_id](const auto& value) { return value.header == header_id; });
|
||||
if (loop == graph.natural_loops.end()) {
|
||||
SetFailure(graph, FailureKind::StructuredControlFlow, header_id,
|
||||
fmt::format("block {} is not a natural loop header", header_id), error);
|
||||
return false;
|
||||
}
|
||||
|
||||
ClearStructuredTerminators(graph);
|
||||
if (!IsolateSemanticLoopHeader(graph, header_id)) {
|
||||
SetFailure(graph, FailureKind::StructuredControlFlow, header_id,
|
||||
fmt::format("loop header block {} has no semantic instructions", header_id),
|
||||
error);
|
||||
return false;
|
||||
}
|
||||
|
||||
graph.unsupported = false;
|
||||
graph.failure_kind = FailureKind::None;
|
||||
graph.failure_block = UINT32_MAX;
|
||||
graph.unsupported_reason.clear();
|
||||
return Structurize(graph, error);
|
||||
}
|
||||
|
||||
std::string BranchConditionToString(BranchCondition condition) {
|
||||
switch (condition) {
|
||||
case BranchCondition::Always: return "always";
|
||||
|
||||
Reference in New Issue
Block a user