Tag-scoped alive-vars contribution for branch snapshots#286
Draft
PhilippGrulich wants to merge 5 commits into
Draft
Tag-scoped alive-vars contribution for branch snapshots#286PhilippGrulich wants to merge 5 commits into
PhilippGrulich wants to merge 5 commits into
Conversation
The pathExplosion_baseline_threeCallsNoBranch fixture chains three inlined
calls to a 3-return leaf with no post-call branches. Its docstring
documents the intended outcome ("Expected: 3 RETURNs ... fan in at the call
boundary instead of forking the trace"), but the actual raw trace produces
27 RETURNs because aliveVars.hash() in recordSnapshot mixes upstream-path
ValueRef ids into the snapshot of in-callee CMPs, fanning every internal
branch out per upstream arm.
Introduce a ScopeFrameStack that mirrors the prefix of the current Tag's
parent chain and a recordBranchSnapshot() (used only by traceBool) that
computes the alive-vars term as an XOR delta against the innermost scope
frame's entry hash. Pair it with checkBranchTag in ExecutionTrace, which
inserts an ASSIGN matchedOperand currentOperand in the predecessor before
firing the CFM so the merged CMP's operand is defined on every incoming
edge.
Gated behind engine.callsiteScopedBranchSnapshots (default off) so that
existing test reference traces are untouched. With the option on,
baseline_threeCallsNoBranch's raw trace drops from 27 RETURNs to 3 (3
RETURN-bearing blocks fanning in via explicit ControlFlowMerge blocks);
non-CMP path-local ValueRefs still need follow-up SSA alignment work
before the option can become default.
198/198 tests pass with default options.
Activates the new branch-snapshot scoping introduced in the previous commit. baseline_threeCallsNoBranch's raw trace now drops from 27 RETURNs to 3 as the fixture's docstring expected, and the trace fans in at the call boundary via explicit ControlFlowMerge blocks. 197/198 tests still pass. The remaining failure is "Path Explosion Trace Test" — the four chained-inline fixtures (baseline_threeCallsNoBranch, postCallBranch_1..3) trip the SSA verifier's "Wrong number of arguments" check because non-CMP path-local ValueRefs (the per-arm fresh val<int32_t> a/b/c declared at the user's `val<T> a = peLeaf(v)` call site) remain unaligned at CFM time. alignCmpOperandInCurrentBlock handles the CMP's operand specifically; downstream uses (e.g. `if (a < 10)` in the next inlined arm) still need block-arg threading. The follow-up is to extend the CFM alignment to every divergent ValueRef referenced by moved ops, not just the CMP operand. Once that lands the path-explosion reference traces can be regenerated and the test will turn green.
This reverts commit 08c56f1.
Contributor
There was a problem hiding this comment.
Tracing Benchmark
Details
| Benchmark suite | Current: 4b475c7 | Previous: f9cd28f | Ratio |
|---|---|---|---|
e2e_tiered_bc_to_mlir |
54.113 us (± 7.45545) |
56.9167 us (± 7.69512) |
0.95 |
e2e_single_mlir |
5.54937 ms (± 426.035) |
5.52315 ms (± 41.2737) |
1.00 |
trace_add |
4.07248 us (± 319.595) |
2.27731 us (± 155.69) |
1.79 |
completing_trace_add |
2.28061 us (± 207.745) |
2.32405 us (± 143.855) |
0.98 |
trace_ifThenElse |
8.66975 us (± 824.97) |
8.63915 us (± 776.354) |
1.00 |
completing_trace_ifThenElse |
4.73609 us (± 443.187) |
4.54071 us (± 401.003) |
1.04 |
trace_deeplyNestedIfElse |
26.1046 us (± 3.00469) |
25.5445 us (± 1.57172) |
1.02 |
completing_trace_deeplyNestedIfElse |
12.8054 us (± 1.11765) |
12.559 us (± 987.646) |
1.02 |
trace_loop |
8.61724 us (± 810.206) |
8.80429 us (± 1.55155) |
0.98 |
completing_trace_loop |
4.74316 us (± 414.017) |
4.79104 us (± 376.911) |
0.99 |
trace_ifInsideLoop |
17.0259 us (± 2.74998) |
16.6688 us (± 1.56827) |
1.02 |
completing_trace_ifInsideLoop |
8.62858 us (± 1.43292) |
8.40114 us (± 807.253) |
1.03 |
trace_loopDirectCall |
8.79643 us (± 918.847) |
8.6864 us (± 697.439) |
1.01 |
completing_trace_loopDirectCall |
4.79646 us (± 550.874) |
4.86476 us (± 354.791) |
0.99 |
trace_pointerLoop |
14.1543 us (± 1.47552) |
14.0902 us (± 1.18648) |
1.00 |
completing_trace_pointerLoop |
10.3877 us (± 1.5768) |
10.1206 us (± 990.611) |
1.03 |
trace_staticLoop |
7.18533 us (± 578.196) |
7.21602 us (± 496.139) |
1.00 |
completing_trace_staticLoop |
7.18445 us (± 473.472) |
7.43532 us (± 991.516) |
0.97 |
trace_fibonacci |
10.075 us (± 1.57224) |
10.0814 us (± 861.346) |
1.00 |
completing_trace_fibonacci |
6.14659 us (± 568.389) |
6.00031 us (± 426.095) |
1.02 |
trace_gcd |
7.9762 us (± 1.34593) |
7.79585 us (± 648.305) |
1.02 |
completing_trace_gcd |
4.18252 us (± 711.519) |
3.87126 us (± 272.452) |
1.08 |
trace_nestedIf10 |
38.6581 us (± 6.04038) |
38.0283 us (± 3.64798) |
1.02 |
completing_trace_nestedIf10 |
40.641 us (± 5.80709) |
37.3711 us (± 3.21573) |
1.09 |
trace_nestedIf100 |
1.36724 ms (± 19.8987) |
1.35476 ms (± 22.6997) |
1.01 |
completing_trace_nestedIf100 |
1.37781 ms (± 27.1255) |
1.35268 ms (± 26.016) |
1.02 |
trace_chainedIf10 |
99.6056 us (± 7.86015) |
97.7583 us (± 4.73759) |
1.02 |
completing_trace_chainedIf10 |
50.3516 us (± 4.81571) |
48.3258 us (± 3.59147) |
1.04 |
trace_chainedIf100 |
4.44425 ms (± 29.0219) |
4.43042 ms (± 29.6138) |
1.00 |
completing_trace_chainedIf100 |
2.31749 ms (± 28.8255) |
2.24075 ms (± 31.9951) |
1.03 |
ssa_add |
180.113 ns (± 13.2104) |
178.473 ns (± 9.97434) |
1.01 |
ssa_ifThenElse |
500.445 ns (± 37.6302) |
459.53 ns (± 43.4269) |
1.09 |
ssa_deeplyNestedIfElse |
1.21541 us (± 94.7708) |
1.16567 us (± 124.837) |
1.04 |
ssa_loop |
518.587 ns (± 23.8382) |
487.861 ns (± 38.2642) |
1.06 |
ssa_ifInsideLoop |
950.194 ns (± 76.9495) |
895.76 ns (± 72.8648) |
1.06 |
ssa_loopDirectCall |
530.224 ns (± 30.9166) |
491.118 ns (± 45.7827) |
1.08 |
ssa_pointerLoop |
628.654 ns (± 48.6706) |
583.073 ns (± 41.8959) |
1.08 |
ssa_staticLoop |
473.788 ns (± 31.3643) |
422.539 ns (± 47.7647) |
1.12 |
ssa_fibonacci |
555.651 ns (± 40.7217) |
503.737 ns (± 39.1986) |
1.10 |
ssa_gcd |
491.254 ns (± 36.7624) |
445.234 ns (± 36.8343) |
1.10 |
exec_bc_addOne |
35.5674 ns (± 3.29859) |
36.0837 ns (± 6.3975) |
0.99 |
exec_mlir_addOne |
275.292 ns (± 6.39703) |
258.154 ns (± 2.33301) |
1.07 |
exec_cpp_addOne |
3.95238 ns (± 0.496238) |
3.54853 ns (± 0.249363) |
1.11 |
exec_interpreted_addOne |
36.8899 ns (± 2.5494) |
37.1985 ns (± 2.33453) |
0.99 |
ir_add |
770.203 ns (± 54.753) |
757.379 ns (± 38.5584) |
1.02 |
ir_ifThenElse |
1.54204 us (± 175.729) |
1.58461 us (± 136.165) |
0.97 |
ir_deeplyNestedIfElse |
3.51585 us (± 509.286) |
3.40826 us (± 363.019) |
1.03 |
ir_loop |
1.646 us (± 157.725) |
1.63694 us (± 149.086) |
1.01 |
ir_ifInsideLoop |
2.76985 us (± 156.51) |
2.83439 us (± 293.378) |
0.98 |
ir_loopDirectCall |
1.78745 us (± 142.551) |
1.80584 us (± 177.074) |
0.99 |
ir_pointerLoop |
1.95884 us (± 146.518) |
1.97161 us (± 153.779) |
0.99 |
ir_staticLoop |
1.47381 us (± 136.776) |
1.44956 us (± 119.501) |
1.02 |
ir_fibonacci |
1.7415 us (± 171.986) |
1.72022 us (± 143.401) |
1.01 |
ir_gcd |
1.58738 us (± 423.617) |
1.4549 us (± 101.976) |
1.09 |
ir_nestedIf10 |
7.83673 us (± 761.876) |
7.70943 us (± 540.522) |
1.02 |
ir_nestedIf100 |
91.0254 us (± 4.9978) |
93.6887 us (± 16.2569) |
0.97 |
ir_chainedIf10 |
11.8546 us (± 1.25154) |
11.594 us (± 826.837) |
1.02 |
ir_chainedIf100 |
171.393 us (± 9.67101) |
169.138 us (± 8.3263) |
1.01 |
tiered_compile_addOne |
53.3527 us (± 6.9883) |
55.1658 us (± 10.7264) |
0.97 |
single_compile_mlir_addOne |
3.18018 ms (± 90.6188) |
3.25749 ms (± 57.5499) |
0.98 |
single_compile_cpp_addOne |
24.3344 ms (± 407.264) |
24.4391 ms (± 405.29) |
1.00 |
single_compile_bc_addOne |
54.6204 us (± 9.01589) |
60.6703 us (± 11.2707) |
0.90 |
tiered_compile_sumLoop |
75.6064 us (± 14.2942) |
79.6202 us (± 15.7788) |
0.95 |
single_compile_mlir_sumLoop |
5.13648 ms (± 115.892) |
5.35632 ms (± 118.42) |
0.96 |
single_compile_cpp_sumLoop |
25.4223 ms (± 470.206) |
26.0899 ms (± 453.281) |
0.97 |
single_compile_bc_sumLoop |
76.4716 us (± 9.07028) |
78.6934 us (± 10.1775) |
0.97 |
comp_mlir_add |
5.42246 ms (± 76.7166) |
5.55479 ms (± 56.4997) |
0.98 |
comp_mlir_ifThenElse |
5.95749 ms (± 132.164) |
6.14317 ms (± 38.4483) |
0.97 |
comp_mlir_deeplyNestedIfElse |
4.91528 ms (± 112.895) |
5.06889 ms (± 99.0918) |
0.97 |
comp_mlir_loop |
6.92542 ms (± 140.224) |
7.14743 ms (± 31.0585) |
0.97 |
comp_mlir_ifInsideLoop |
27.9622 ms (± 300.156) |
28.6125 ms (± 100.629) |
0.98 |
comp_mlir_loopDirectCall |
11.5615 ms (± 494.564) |
11.792 ms (± 44.0495) |
0.98 |
comp_mlir_pointerLoop |
27.1203 ms (± 284.425) |
27.7179 ms (± 76.6326) |
0.98 |
comp_mlir_staticLoop |
4.89959 ms (± 121.533) |
5.03112 ms (± 38.6111) |
0.97 |
comp_mlir_fibonacci |
10.1802 ms (± 111.61) |
10.4618 ms (± 76.5332) |
0.97 |
comp_mlir_gcd |
9.22702 ms (± 446.562) |
9.43388 ms (± 42.8866) |
0.98 |
comp_mlir_nestedIf10 |
10.2166 ms (± 109.988) |
10.5281 ms (± 45.0755) |
0.97 |
comp_mlir_nestedIf100 |
24.8654 ms (± 361.677) |
25.0116 ms (± 102.345) |
0.99 |
comp_mlir_chainedIf10 |
9.30746 ms (± 112.527) |
9.56929 ms (± 46.3491) |
0.97 |
comp_mlir_chainedIf100 |
19.9102 ms (± 197.675) |
20.2061 ms (± 81.9884) |
0.99 |
comp_cpp_add |
24.4327 ms (± 474.887) |
24.3267 ms (± 189.47) |
1.00 |
comp_cpp_ifThenElse |
24.9472 ms (± 397.586) |
24.9638 ms (± 283.6) |
1.00 |
comp_cpp_deeplyNestedIfElse |
26.05 ms (± 462.863) |
26.0088 ms (± 233.598) |
1.00 |
comp_cpp_loop |
25.0272 ms (± 343.336) |
25.0851 ms (± 291.998) |
1.00 |
comp_cpp_ifInsideLoop |
25.9377 ms (± 366.204) |
26.2072 ms (± 775.414) |
0.99 |
comp_cpp_loopDirectCall |
25.3427 ms (± 302.822) |
25.3831 ms (± 531.136) |
1.00 |
comp_cpp_pointerLoop |
25.4718 ms (± 352.492) |
25.4858 ms (± 203.254) |
1.00 |
comp_cpp_staticLoop |
25.1073 ms (± 612.747) |
24.7845 ms (± 176.103) |
1.01 |
comp_cpp_fibonacci |
25.8078 ms (± 585.253) |
25.2385 ms (± 205.167) |
1.02 |
comp_cpp_gcd |
25.3428 ms (± 859.604) |
25.0233 ms (± 436.288) |
1.01 |
comp_cpp_nestedIf10 |
28.3586 ms (± 521.779) |
28.0721 ms (± 363.361) |
1.01 |
comp_cpp_nestedIf100 |
61.9037 ms (± 684.947) |
61.7276 ms (± 707.638) |
1.00 |
comp_cpp_chainedIf10 |
30.5341 ms (± 355.383) |
30.7822 ms (± 574.356) |
0.99 |
comp_cpp_chainedIf100 |
91.6268 ms (± 838.253) |
91.9107 ms (± 685.096) |
1.00 |
comp_bc_add |
15.3072 us (± 2.95827) |
14.7773 us (± 1.91246) |
1.04 |
comp_bc_ifThenElse |
18.7253 us (± 3.23789) |
19.2429 us (± 3.53172) |
0.97 |
comp_bc_deeplyNestedIfElse |
22.8498 us (± 3.51048) |
23.0339 us (± 3.44593) |
0.99 |
comp_bc_loop |
18.7883 us (± 3.6606) |
18.9128 us (± 2.70016) |
0.99 |
comp_bc_ifInsideLoop |
21.6726 us (± 2.4675) |
21.5194 us (± 2.28647) |
1.01 |
comp_bc_loopDirectCall |
19.5765 us (± 2.93716) |
19.2878 us (± 2.03113) |
1.01 |
comp_bc_pointerLoop |
20.5971 us (± 3.25702) |
20.5351 us (± 2.66896) |
1.00 |
comp_bc_staticLoop |
17.0099 us (± 2.2341) |
17.2132 us (± 2.1155) |
0.99 |
comp_bc_fibonacci |
19.1791 us (± 3.03381) |
19.1709 us (± 2.93634) |
1.00 |
comp_bc_gcd |
18.7806 us (± 4.32212) |
18.5754 us (± 2.5724) |
1.01 |
comp_bc_nestedIf10 |
36.7799 us (± 4.18545) |
35.6002 us (± 4.60432) |
1.03 |
comp_bc_nestedIf100 |
201.53 us (± 12.2262) |
198.41 us (± 10.198) |
1.02 |
comp_bc_chainedIf10 |
52.0644 us (± 7.77225) |
52.6073 us (± 9.11991) |
0.99 |
comp_bc_chainedIf100 |
310.096 us (± 12.0225) |
309.294 us (± 26.888) |
1.00 |
comp_asmjit_add |
21.0908 us (± 3.95538) |
22.1735 us (± 4.82406) |
0.95 |
comp_asmjit_ifThenElse |
32.2842 us (± 3.72629) |
33.4071 us (± 4.26649) |
0.97 |
comp_asmjit_deeplyNestedIfElse |
56.9345 us (± 9.51601) |
56.4176 us (± 5.25514) |
1.01 |
comp_asmjit_loop |
34.5205 us (± 3.80188) |
35.8631 us (± 4.35405) |
0.96 |
comp_asmjit_ifInsideLoop |
56.7593 us (± 10.4411) |
57.5183 us (± 8.7641) |
0.99 |
comp_asmjit_loopDirectCall |
44.8799 us (± 7.51965) |
47.2559 us (± 9.00425) |
0.95 |
comp_asmjit_pointerLoop |
49.0256 us (± 10.1432) |
48.2785 us (± 6.56048) |
1.02 |
comp_asmjit_staticLoop |
28.1698 us (± 4.46578) |
28.6917 us (± 4.31543) |
0.98 |
comp_asmjit_fibonacci |
42.8934 us (± 7.32126) |
43.5157 us (± 6.92307) |
0.99 |
comp_asmjit_gcd |
34.4511 us (± 3.82449) |
35.3711 us (± 4.19816) |
0.97 |
comp_asmjit_nestedIf10 |
101.806 us (± 10.5981) |
103.255 us (± 10.7623) |
0.99 |
comp_asmjit_nestedIf100 |
1026.1100000000001 us (± 18006) |
1057.97 us (± 54427.5) |
0.97 |
comp_asmjit_chainedIf10 |
164.071 us (± 35.4405) |
153.183 us (± 14.6589) |
1.07 |
comp_asmjit_chainedIf100 |
2.20319 ms (± 223.684) |
2.16653 ms (± 46.7063) |
1.02 |
exec_mlir_add |
11.0716 ns (± 0.985272) |
10.6651 ns (± 0.946586) |
1.04 |
exec_mlir_fibonacci |
13.9709 us (± 1.54464) |
13.4703 us (± 2.01788) |
1.04 |
exec_mlir_sum |
536.426 us (± 31.34) |
527.422 us (± 21.2529) |
1.02 |
exec_cpp_add |
4.76141 ns (± 0.856455) |
4.69868 ns (± 0.745035) |
1.01 |
exec_cpp_fibonacci |
96.2419 us (± 9.84059) |
96.6361 us (± 9.26507) |
1.00 |
exec_cpp_sum |
35.894 ms (± 84.8136) |
35.9426 ms (± 115.287) |
1.00 |
exec_bc_add |
43.6175 ns (± 6.92386) |
44.8635 ns (± 8.015) |
0.97 |
exec_bc_fibonacci |
750.915 us (± 9.98364) |
859.028 us (± 13.0138) |
0.87 |
exec_bc_sum |
163.07 ms (± 1.24625) |
186.067 ms (± 1.36597) |
0.88 |
exec_asmjit_add |
3.48912 ns (± 0.389821) |
3.48273 ns (± 0.424618) |
1.00 |
exec_asmjit_fibonacci |
20.4898 us (± 1.65734) |
20.8625 us (± 3.3694) |
0.98 |
exec_asmjit_sum |
4.8532 ms (± 40.8937) |
4.87965 ms (± 312.598) |
0.99 |
exec_bc_add_noRegAlloc |
43.3481 ns (± 5.30779) |
44.3118 ns (± 3.89068) |
0.98 |
exec_bc_add_regAlloc |
43.404 ns (± 3.28639) |
44.599 ns (± 5.15383) |
0.97 |
exec_bc_fibonacci_noRegAlloc |
754.168 us (± 25.2842) |
852.767 us (± 11.3762) |
0.88 |
exec_bc_fibonacci_regAlloc |
749.164 us (± 9.1131) |
875.208 us (± 25.409) |
0.86 |
exec_bc_sum_noRegAlloc |
162.976 ms (± 329.211) |
185.974 ms (± 222.438) |
0.88 |
exec_bc_sum_regAlloc |
162.88 ms (± 108.795) |
185.982 ms (± 165.251) |
0.88 |
This comment was automatically generated by workflow using github-action-benchmark.
Following commits enabled engine.callsiteScopedBranchSnapshots and reverted it because the SSA pass leaked path-local ValueRefs (the val<T> r/a/b/c defined in only one of the merged arms) up to the root, failing the argument-count check. Generalise alignCmpOperandInCurrentBlock from "align the matched CMP's operand" to "walk the data-flow DAG of the matched CMP's operand and emit ASSIGN aliases for every ValueRef pair encountered." At each step we look up the producer of the reference-path operand and the producer of the current-path operand (transitively walking the predecessor chain when the local prefix is empty, as cascade CFMs leave behind), and when the producers agree on op type we recurse into their inputs. Visited pairs are memoised on the (refRef, curRef) tuple so the walk terminates. This identifies user-visible val<T> variables even when they don't appear as ASSIGN targets in the trace (peLeaf's `return 1` inlines into a bare CONST without an intervening ASSIGN), and supplies SSA with enough predecessor-local definitions to thread block arguments cleanly. Result with the option on by default: pathExplosion_baseline_threeCallsNoBranch: 27 -> 3 RETURNs (9x) pathExplosion_postCallBranch_1: 6 -> 2 RETURNs (3x) pathExplosion_postCallBranch_2: ~18 -> 2 RETURNs (9x) pathExplosion_postCallBranch_3: ~54 -> 2 RETURNs (27x) 198/198 tests pass. Path-explosion reference traces regenerated to reflect the collapsed structure. https://claude.ai/code/session_01UeMJ7hcqhyqwYGiRewSsyk
…CUIT_BOOL The DAG-walk alignment introduced in 71cc889 has a soundness gap: it emits ASSIGN aliases for ValueRef pairs whose producer ops match by op-kind without checking that their operand subtrees compute the same value. Under SHORT_CIRCUIT_BOOL the `(a && b) || (c && d)` branch tree has two CMPs at the same source line (e.g. `value > 15` from B2's `!a` arm and from B4's `a && !b` arm) that my snapshot collapse merges, but the operand DAGs of the two paths reach distinct CONST literals (B1's CONST 10 for `value < 10` vs B2's CONST 5 for `value > 5`) and the alignment emits an ASSIGN aliasing them. The compiled code then reads the wrong literal on one of the paths and `multipleConditions(0)` returns 1 instead of 0. Reverts default for engine.callsiteScopedBranchSnapshots back to false and restores the path-explosion reference traces to their option-off shape. The alignment implementation in ExecutionTrace.cpp is preserved as the opt-in foundation; a follow-up needs to gate ASSIGN emission on full structural-equivalence of producer subtrees (CONST literal match, recursive input check) before this can flip on by default. 198/198 tests pass with default off. https://claude.ai/code/session_01UeMJ7hcqhyqwYGiRewSsyk
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.
Summary
pathExplosion_baseline_threeCallsNoBranchchains three inlined calls to a 3-return leaf with no post-call branches. Its docstring says "Expected: 3 RETURNs ... fan in at the call boundary instead of forking the trace", but the actual trace produces 27 RETURNs (3³). The cause is inrecordSnapshot:aliveVars.hash()mixes the concreteValueRefids of every alive variable into the snapshot. When three inlined arms of call 1 each produce a differenta-id, the same source-location CMP inside call 2 sees three different snapshots andSymbolicExecutionContext::tagMapexplores each as its own path. The Tag itself already encodes call-site (via__builtin_return_addresstrie), so the alive-vars term only needs to disambiguate two snapshots at the same Tag — across different Tags it's pure noise.This change makes the alive-vars term scope-relative at branch points only:
ScopeFrameStackmirrors the prefix of the current Tag's parent chain. Each frame recordsaliveVars.hash()at the moment the chain first reached that depth.recordBranchSnapshot()(used only bytraceBool) replaces the alive-vars term with the XOR delta againstframes.back().aliveHashOnEntry. Value-creating ops (CONST/ASSIGN/EQ/binary) keep the original formula so their CFM machinery's ValueRef-alignment invariant is undisturbed.ExecutionTrace::checkBranchTagis a branch-awarecheckTagthat, when a CFM fires, emits anASSIGN matchedOperand currentOperandin the predecessor so every incoming edge of the merge block defines the CMP operand.Gated behind
engine.callsiteScopedBranchSnapshots(defaultfalse). With the option on,baseline_threeCallsNoBranch's raw trace drops from 27 RETURNs to 3 (B5/B13/B14), with explicitControlFlowMergeblocks fanning calls 1/2/3 into the next call's body — exactly what the fixture's docstring promised.Not in this change
Non-CMP path-local ValueRefs (e.g. the
val<int32_t> adeclared at the call site, which gets a fresh ValueRef per inlined arm) still trip the SSA verifier's "Wrong number of arguments" check when the option is on. That requires either deeper alignment work inprocessControlFlowMergeor relaxing the SSA verifier — both intentionally deferred. The plumbing here makes the follow-up tractable. This change is the focused TraceContext-level lever the broader sub-trace memoization direction needs as a foundation.Test plan
ctest --test-dir build/nautiluswith default options: 198/198 pass, no regressionspathExplosion_baseline_threeCallsNoBranchtrace shows 3 RETURNs, 17 blocks total, threeControlFlowMergeblocks fanning in the call-1 arms; calls 2 and 3 likewise — matches the fixture docstring's expectationpostCallBranch_*tests unchanged (option off path)https://claude.ai/code/session_01UeMJ7hcqhyqwYGiRewSsyk
Generated by Claude Code