Skip to content

feat: de-thread copyable CFG values via non-local Dom edges - #2091

Draft
ss2165 wants to merge 1 commit into
ss2165-revert-962-poc-current-stackfrom
ss2165-cfg-dom-edge-dethreading
Draft

ss2165 wants to merge 1 commit into
ss2165-revert-962-poc-current-stackfrom
ss2165-cfg-dom-edge-dethreading

Conversation

@ss2165

@ss2165 ss2165 commented Jul 24, 2026

Copy link
Copy Markdown
Member

Stacked on #2090.

Copyable values live across CFG blocks but only used past a dominating block were threaded through every intervening block signature — O(W²) in the width of e.g. an if a or b or ...: chain, which blows up LLVM (SLP/SCCP) to ~90min compiles in the worst cases.

Emit a non-local Dom edge from the defining block instead, gated on a strict single-def-dominates-all-uses check (linear, multi-def and loop-carried values stay threaded). Block-signature width goes O(W²)→O(W); on a 16×if / 48-bool repro opt-2 compile drops 20s→2.2s.

Measurement is carved out — it's future-like and the Helios QIS lowers it to a linear type, which can't ride a non-local edge. Tracked in Quantinuum/tket2#1883.

Short-circuit boolean chains (`if b0 or b1 or ... or bN:`) previously
threaded every live copyable value through the Input/Output signature of
every CFG basic block, even blocks that neither read nor write it. For W
booleans across W DataflowBlocks this is O(W^2) total signature width,
which drives O(W^2) LLVM insertvalue/extractvalue packing and makes
superlinear LLVM passes blow up.

For a copyable scalar value whose single definition strictly dominates all
its uses, drop it from the signatures of intervening blocks and instead let
hugr wire a non-local Dom edge from the defining block directly to the use
blocks. Linear/non-copyable values stay threaded (can't be duplicated).

- cfg/analysis.py: add iterative dominators (`compute_dominators`),
  `reverse_postorder`, and natural-loop detection (`loop_blocks`).
- cfg_compiler.py: `compute_dethread_info` + `DethreadInfo`; strict
  single-def-dominates-all-uses gate; RPO block ordering so a def compiles
  before its uses; supply/store de-threaded wires in `compile_bb`.

`Measurement` is explicitly excluded: it is a future that guppy auto-copies
at the surface level but Hugr models as linear, so the Helios QIS lowering
rejects it on a non-local edge. Tracked as an edge case (TODO) rather than
a general type restriction.

Payoff on a bool or-chain (W=8/16/32/48): block-signature sum
28/120/496/1128 -> 0; insertvalue 21/105/465/1081 -> 0; extractvalue
35/135/527/1175 -> 0; alloca O(W^2) -> O(W). End-to-end opt-2 compile of a
16x if-chain over 48 measured bools: 20.0s -> 2.2s.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Projectguppylang
Branchss2165-cfg-dom-edge-dethreading
TestbedLinux

🚨 3 Alerts

BenchmarkMeasure
Units
ViewBenchmark Result
(Result Δ%)
Upper Boundary
(Limit %)
tests/benchmarks/test_queue_push_pop.py::test_queue_push_benchmark_compilehugr_nodes
nodes
📈 plot
🚷 threshold
🚨 alert (🔔)
315.00
(+1.61%)Baseline: 310.00
313.10
(100.61%)

tests/benchmarks/test_queue_push_pop.py::test_queue_push_benchmark_compilehugr_bytes
bytes x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
8.02 x 1e3
(+2.36%)Baseline: 7.84 x 1e3
7.92 x 1e3
(101.35%)

tests/benchmarks/test_queue_push_pop.py::test_queue_push_pop_benchmark_compilehugr_nodes
nodes
📈 plot
🚷 threshold
🚨 alert (🔔)
416.00
(+1.22%)Baseline: 411.00
415.11
(100.21%)

Click to view all benchmark results
Benchmarkhugr_bytesBenchmark Result
bytes x 1e3
(Result Δ%)
Upper Boundary
bytes x 1e3
(Limit %)
hugr_nodesBenchmark Result
nodes
(Result Δ%)
Upper Boundary
nodes
(Limit %)
tests/benchmarks/test_big_array.py::test_big_array_compile📈 view plot
🚷 view threshold
66.21 x 1e3
(-0.29%)Baseline: 66.41 x 1e3
67.07 x 1e3
(98.72%)
📈 view plot
🚷 view threshold
4,583.00
(-0.26%)Baseline: 4,595.00
4,640.95
(98.75%)
tests/benchmarks/test_ctrl_flow.py::test_many_ctrl_flow_compile📈 view plot
🚷 view threshold
28.62 x 1e3
(-0.52%)Baseline: 28.77 x 1e3
29.06 x 1e3
(98.50%)
📈 view plot
🚷 view threshold
1,297.00
(-0.31%)Baseline: 1,301.00
1,314.01
(98.71%)
tests/benchmarks/test_queue_push_pop.py::test_queue_push_benchmark_compile📈 view plot
🚷 view threshold
🚨 view alert (🔔)
8.02 x 1e3
(+2.36%)Baseline: 7.84 x 1e3
7.92 x 1e3
(101.35%)

📈 view plot
🚷 view threshold
🚨 view alert (🔔)
315.00
(+1.61%)Baseline: 310.00
313.10
(100.61%)

tests/benchmarks/test_queue_push_pop.py::test_queue_push_pop_benchmark_compile📈 view plot
🚷 view threshold
10.85 x 1e3
(+0.70%)Baseline: 10.77 x 1e3
10.88 x 1e3
(99.70%)
📈 view plot
🚷 view threshold
🚨 view alert (🔔)
416.00
(+1.22%)Baseline: 411.00
415.11
(100.21%)

🐰 View full continuous benchmarking report in Bencher

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.33333% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.22%. Comparing base (d463d85) to head (be64f84).

Files with missing lines Patch % Lines
...s/src/guppylang_internals/compiler/cfg_compiler.py 90.47% 12 Missing ⚠️
Additional details and impacted files
@@                           Coverage Diff                           @@
##           ss2165-revert-962-poc-current-stack    #2091      +/-   ##
=======================================================================
+ Coverage                                93.21%   93.22%   +0.01%     
=======================================================================
  Files                                      152      152              
  Lines                                    14676    14848     +172     
=======================================================================
+ Hits                                     13680    13842     +162     
- Misses                                     996     1006      +10     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants