Repository navigation
feat(recompiler): structure every control-flow graph the structurizer refuses - #4926
Merged
oneandonlydean merged 2 commits intoOct 14, 2026
Conversation
11 tasks done
This was referenced Oct 13, 2026
nikowolf123
force-pushed
the
feat/structurizer-complete-fallback
branch
from
October 13, 2026 08:53
8881609 to
504acb1
Compare
11 tasks done
nikowolf123
force-pushed
the
feat/structurizer-complete-fallback
branch
from
October 13, 2026 08:58
504acb1 to
3acb962
Compare
11 tasks done
11 tasks done
10 tasks done
nikowolf123
force-pushed
the
feat/structurizer-complete-fallback
branch
2 times, most recently
from
October 13, 2026 16:21
e59b624 to
395280c
Compare
This was referenced Oct 13, 2026
When every attempt of the structurizer ladder refuses a graph, the title aborts. Rebuild such a graph from a hierarchical decomposition into strongly connected components instead: each original block keeps one copy and runs behind a guard on its own reach flag, and each component becomes a loop with a single latch that repeats it while a back edge set its again flag. Every edge sets the reach flag of its target, so exactly one flag names the next original block and the blocks run in the input's order for every branch decision. Irreducible and non-terminating graphs are handled the same way, and no block is cloned. The ladder runs first and its output is unchanged. The fallback only runs when both attempt series refuse; if it cannot structure the graph, the ladder's error is reported as before. Blocks that start the top-level order unconditionally run unguarded, so the entry block's results reach later blocks without a stale path. APS5_FORCE_STRUCTURIZER_FALLBACK=1 uses the construction for every supported graph, for tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…zer fallback Structurize eight graphs the ladder refuses on main (a duplicate merge, a region that wraps around to its dominator, the merge-splitting budget, a conditional without a structured merge, an irreducible entry and three loops that never exit) and the boykopovar#2642 two-latch loop, which the ladder accepts. Each must give structured branches, every original block exactly once and the same executions as the input for every branch decision. Register agc_structurizer_forced_fallback and agc_shader_control_flow_forced_fallback, which run the in-tree graphs and RDNA programs, through Recompile and SPIR-V validation, with APS5_FORCE_STRUCTURIZER_FALLBACK=1. Under that switch the split-kind checks become checks that no block is cloned and a route variable is used, and the nested-selection check names blocks by their original instructions instead of their position. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nikowolf123
force-pushed
the
feat/structurizer-complete-fallback
branch
from
October 13, 2026 23:07
395280c to
22c0c51
Compare
11 tasks done
11 tasks done
|
Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.
|
Collaborator
|
Closing: no title is known to reach this. With the queue at 700+ open PRs, please hold off on new PRs that don't name a title needing the change until it's down. If a title stops on this on current main, reopen with the title ID and the log line. |
This was referenced Oct 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A structurizer refusal stops the title:
Recompilememoizes it per source and the dispatch or draw that needs the shader turns it into a sticky driver failure. The structurizer has grown a ladder of retries (two attempt series of up to four configurations) that each refuse some shapes. Measured onmainwith the oracle described under Tested, on 200,000 random graphs of 2 to 32 blocks: of the 82,743 reducible graphs whose every block reaches a return, 3,041 (3.7%) are refused. Irreducible graphs and graphs with a cycle that never exits are refused by design (114,667 in that corpus).This PR adds a construction that structures every graph built from branch, conditional branch and return blocks, and uses it only when the ladder refuses:
s_barrierkeeps one copy.APS5_FORCE_STRUCTURIZER_FALLBACK=1uses the construction for every graph; it is part of the shader cache key.This is the textbook state-variable construction (Böhm and Jacopini; Ashcroft and Manna) applied to the structurizer's own route variables. It costs about 8 times the input's blocks and one variable per block, which the technical debt entry records with the other limits below.
Limits:
main) it spends more than 10 s before it accepts or refuses, and this PR does not shorten that.No open PR changes
Structurizer.cpp. #2762 concerns ladder outputs that are invalid SPIR-V; this PR does not change ladder outputs.Tested
WSL2 Ubuntu 26.04, GCC 15.2.0, configured like CI (Release, SPIRV-Tools ON, lavapipe), and native Windows 11 with WinLibs GCC 15.2.0 on an RTX 5080 (NVIDIA 616.92). No title.
In-tree:
agc_structurizerstructure eight graphs thatmainrefuses (a duplicate merge, a region that wraps around to its dominator, the merge-splitting budget, a conditional without a structured merge, an irreducible entry and three loops that never exit) and the Structurizer: shared merge splitting never terminates for nested selections in a two-latch loop #2642 two-latch loop, whichmainaccepts. Each must give structured branches, every original block exactly once and the same executions as the input for every branch decision.agc_structurizer_forced_fallbackandagc_shader_control_flow_forced_fallbackrun the in-tree graphs and the 35 RDNA programs through the fallback, throughRecompileand SPIR-V validation before and after spirv-opt, for 32 and 64 lanes.ctestin the CI configuration onmaine51428b: 1014 tests, 0 failed.tools/check_skips.pyflags onlyguest_video_out_high_frame_rate, which skips because the WSL display has no 120 Hz mode, as it does onmain.ctest(ANYPS5_REQUIRE_VULKAN=1): 1030 tests, 0 failed, 7 skipped astools/check_skips.py --job windowsexpects.APS5_FORCE_STRUCTURIZER_FALLBACK=1 ANYPS5_NO_SHADER_CACHE=1, so that every shader of every GPU execution test is structured by the fallback and runs on the RTX 5080: 1028 of 1030 pass. The two failures check analysis output, not GPU results.agc_shader_memoryexpects a descriptor phi with two candidates and gets a third from a guarded path that never runs (the planning limit above).agc_scalar_select_maskruns programs through its own evaluator, which does not model route variables.Out of tree, with an oracle that shares no code with the structurizer (outside the repository; I can attach it). For every output it checks the graph contract (dense ids, exact successors and predecessors, reachability, dominator order, one latch per loop, unique merges, synthetic block shapes), runs SPIRV-Tools on a skeleton module emitted exactly as
SpirvFlowEmitterlays out blocks and merges, at SPIR-V 1.3, 1.4 and 1.6, and decides semantic equivalence exactly: a search over pairs of (input block, output block and live route-variable values) that compares the original block reached next for every branch decision, loops included. It was checked against 593,159 mutated outputs and an independent simulator, with no missed difference.main, 200,000 graphs: every one of the 81,072 accepted graphs is valid at 1.3 and 1.4 and equivalent; 30,220 of them contain a conditional with equal targets, which only 1.6 rejects (the fix(shader): never branch conditionally to one label twice #4580 topic). 117,708 graphs are refused, 3,041 of them reducible and terminating, and 1,220 run past 10 s.mainaccepts give byte-identical outputs. Every graphmainrefuses (117,708) gives a valid, equivalent output. No graph is refused.maincac4e96: fullctestin the CI configuration, with this PR and its follow-up on top, 1032 tests, 0 failed. Then onto 4bb2c7f, where only the technical debt file conflicted: the structurizer, control-flow and shader tests, 18 of 18.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code