perf(recompiler): reuse sorted sets for iteration selection merges

Seed each iteration-selection update from its first eligible successor
instead of rebuilding and intersecting the universe of block IDs.
Reuse the existing in-place intersection and sorted-insertion helpers
while preserving the original empty-successor and fixed-point behavior.
Keep a cache-neutral switch for same-binary output and timing comparisons.
Run the existing structurizer and control-flow fixtures in a separate
process with this optimization disabled as well as in their default mode.
This commit is contained in:
Celegans12 2026-10-11 16:06:57 +02:00
parent a11c8f7f70
commit 76fa2beceb
3 changed files with 26 additions and 6 deletions

View File

@ -1021,8 +1021,12 @@ if(BUILD_TESTING)
endif()
add_test(NAME agc_structurizer_intersection_off COMMAND agc_structurizer_tests)
set_tests_properties(agc_structurizer_intersection_off PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_PERF_DOM_INTERSECTION=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
add_test(NAME agc_structurizer_iteration_selection_off COMMAND agc_structurizer_tests)
set_tests_properties(agc_structurizer_iteration_selection_off PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_PERF_ITERATION_SELECTION=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
add_test(NAME agc_shader_control_flow_iteration_selection_off COMMAND agc_shader_control_flow_tests)
set_tests_properties(agc_shader_control_flow_iteration_selection_off PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_PERF_ITERATION_SELECTION=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(agc_structurizer_intersection_off PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
set_tests_properties(agc_structurizer_intersection_off agc_structurizer_iteration_selection_off agc_shader_control_flow_iteration_selection_off PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_driver_gated_dispatch_capture_short_timeout COMMAND agc_driver_gated_dispatch_capture_tests)
set_tests_properties(agc_driver_gated_dispatch_capture_short_timeout PROPERTIES

View File

@ -428,6 +428,7 @@ std::uint32_t mergeBesideReturns(const ControlFlowGraph& graph, const BasicBlock
std::uint32_t iterationSelectionMerge(const ControlFlowGraph& graph, const NaturalLoop& loop, const BasicBlock& header, std::uint32_t join) {
const auto count = static_cast<std::uint32_t>(graph.blocks.size());
static const bool reuse = std::getenv("APS5_NO_PERF_ITERATION_SELECTION") == nullptr;
const auto escapes = [&](std::uint32_t blockId) { return blockId == loop.continueBlock || blockId == loop.mergeBlock; };
std::vector<bool> joins(count, false);
std::vector<std::uint32_t> pending = {join};
@ -455,16 +456,30 @@ std::uint32_t iterationSelectionMerge(const ControlFlowGraph& graph, const Natur
if (!joins[block.id] || block.id == join) {
continue;
}
auto next = allBlockIds(count);
bool seeded = !reuse;
auto next = seeded ? allBlockIds(count) : std::vector<std::uint32_t>{};
for (const auto successor : block.successors) {
if (joins[successor]) {
next = intersectSorted(next, postDominators[successor]);
if (!seeded) {
next = postDominators[successor];
seeded = true;
} else if (reuse) {
intersectAnalysisSet(next, postDominators[successor]);
} else {
next = intersectSorted(next, postDominators[successor]);
}
}
}
addUnique(next, block.id);
sortUnique(next);
if (!seeded) next = allBlockIds(count);
if (reuse) {
addSortedUnique(next, block.id);
} else {
addUnique(next, block.id);
sortUnique(next);
}
if (next != postDominators[block.id]) {
postDominators[block.id] = std::move(next);
if (reuse) replaceAnalysisSet(postDominators[block.id], std::move(next));
else postDominators[block.id] = std::move(next);
changed = true;
}
}

View File

@ -671,6 +671,7 @@ constexpr std::string_view NeutralSwitches[] = {
"APS5_NO_FAILURE_MEMO",
"APS5_NO_PERF_FRONTEND_PAIR",
"APS5_NO_PERF_DOM_INTERSECTION",
"APS5_NO_PERF_ITERATION_SELECTION",
"APS5_NO_RESULT_MEMO",
};