Add TwoPassTraceContext as third tracing implementation#290
Draft
PhilippGrulich wants to merge 8 commits into
Draft
Add TwoPassTraceContext as third tracing implementation#290PhilippGrulich wants to merge 8 commits into
PhilippGrulich wants to merge 8 commits into
Conversation
Introduces a two-tier tracing approach where snapshot computation and tag-map lookups are only performed at "checkpoint" operations (CMP branches, CALL, RETURN, and first-op-in-block), while all other operations (arithmetic, constants, assignments, loads, stores) use a fast path that skips the expensive recordSnapshot() + checkTag() calls. Key changes: - ExecutionTrace: add NoTag variants of addOperationWithResult and addAssignmentOperation that skip tag-map registration - ExceptionBasedTraceContext/LazyTraceContext: add traceOperationFast() with dual fast/checkpoint lambdas, controlled by needsCheckpoint_ flag - processControlFlowMerge: skip tag-map updates for null-snapshot ops Status: Prototype. All interpreter tests pass (semantically correct traces). Trace-comparison tests fail (different structure). Some compiler tests fail (ifWithTernary) due to missed merge points when convergence happens mid-block rather than at block boundaries. https://claude.ai/code/session_0132qQrKYMcQvLjjs8NA12wH
Introduces a new trace context that uses inline checkTag() and processControlFlowMerge() (same as existing contexts) but without the LazyTraceContext's needsCheckpoint_ fast-path optimization. This provides a clean baseline for future deferred-merge experiments. Activated via engine option: engine.traceMode = "twoPass" Also adds a MergeDetectionPhase utility class for future use in deferred merge restructuring experiments. https://claude.ai/code/session_0132qQrKYMcQvLjjs8NA12wH
Adding twoPass to the global trace modes list caused the pre-existing ifWithTernary failure to surface in every CI job. The TwoPassTraceContext is still wired into LegacyCompiler and can be tested explicitly via engine.traceMode = "twoPass". https://claude.ai/code/session_0132qQrKYMcQvLjjs8NA12wH
The prototype commit (6d2f43d) modified ExceptionBasedTraceContext, LazyTraceContext, and ExecutionTrace with needsCheckpoint_ fast-path and NoTag variants. These changes broke trace reference tests across all CI jobs by producing different merge structures. Reverts these files to their main-branch versions. The TwoPassTraceContext is a standalone addition that doesn't modify existing contexts. https://claude.ai/code/session_0132qQrKYMcQvLjjs8NA12wH
Contributor
There was a problem hiding this comment.
Tracing Benchmark
Details
| Benchmark suite | Current: e8c9d0c | Previous: f9cd28f | Ratio |
|---|---|---|---|
tiered_compile_addOne |
49.3759 us (± 8.4582) |
55.1658 us (± 10.7264) |
0.90 |
single_compile_mlir_addOne |
3.26567 ms (± 43.7988) |
3.25749 ms (± 57.5499) |
1.00 |
single_compile_cpp_addOne |
24.7507 ms (± 1.42291) |
24.4391 ms (± 405.29) |
1.01 |
single_compile_bc_addOne |
56.1537 us (± 9.76231) |
60.6703 us (± 11.2707) |
0.93 |
tiered_compile_sumLoop |
77.4407 us (± 10.2814) |
79.6202 us (± 15.7788) |
0.97 |
single_compile_mlir_sumLoop |
5.37035 ms (± 436.433) |
5.35632 ms (± 118.42) |
1.00 |
single_compile_cpp_sumLoop |
25.2119 ms (± 222.384) |
26.0899 ms (± 453.281) |
0.97 |
single_compile_bc_sumLoop |
75.8018 us (± 7.12729) |
78.6934 us (± 10.1775) |
0.96 |
exec_bc_addOne |
35.3491 ns (± 4.98316) |
36.0837 ns (± 6.3975) |
0.98 |
exec_mlir_addOne |
283.396 ns (± 2.42984) |
258.154 ns (± 2.33301) |
1.10 |
exec_cpp_addOne |
3.95764 ns (± 0.686006) |
3.54853 ns (± 0.249363) |
1.12 |
exec_interpreted_addOne |
42.4819 ns (± 9.60566) |
37.1985 ns (± 2.33453) |
1.14 |
ir_add |
746.987 ns (± 42.9945) |
757.379 ns (± 38.5584) |
0.99 |
ir_ifThenElse |
1.53632 us (± 85.237) |
1.58461 us (± 136.165) |
0.97 |
ir_deeplyNestedIfElse |
3.3864 us (± 366.475) |
3.40826 us (± 363.019) |
0.99 |
ir_loop |
1.68884 us (± 252.19) |
1.63694 us (± 149.086) |
1.03 |
ir_ifInsideLoop |
2.80319 us (± 228.313) |
2.83439 us (± 293.378) |
0.99 |
ir_loopDirectCall |
1.81276 us (± 143.492) |
1.80584 us (± 177.074) |
1.00 |
ir_pointerLoop |
2.75931 us (± 834.9) |
1.97161 us (± 153.779) |
1.40 |
ir_staticLoop |
1.44306 us (± 138.838) |
1.44956 us (± 119.501) |
1.00 |
ir_fibonacci |
1.75446 us (± 139.103) |
1.72022 us (± 143.401) |
1.02 |
ir_gcd |
1.49416 us (± 113.641) |
1.4549 us (± 101.976) |
1.03 |
ir_nestedIf10 |
7.72282 us (± 906.796) |
7.70943 us (± 540.522) |
1.00 |
ir_nestedIf100 |
86.2408 us (± 7.23312) |
93.6887 us (± 16.2569) |
0.92 |
ir_chainedIf10 |
11.5643 us (± 1.05563) |
11.594 us (± 826.837) |
1.00 |
ir_chainedIf100 |
169.089 us (± 25.5692) |
169.138 us (± 8.3263) |
1.00 |
ssa_add |
183.305 ns (± 15.6324) |
178.473 ns (± 9.97434) |
1.03 |
ssa_ifThenElse |
461.93 ns (± 60.3973) |
459.53 ns (± 43.4269) |
1.01 |
ssa_deeplyNestedIfElse |
1.18174 us (± 145.176) |
1.16567 us (± 124.837) |
1.01 |
ssa_loop |
521.829 ns (± 48.2841) |
487.861 ns (± 38.2642) |
1.07 |
ssa_ifInsideLoop |
935.981 ns (± 62.5527) |
895.76 ns (± 72.8648) |
1.04 |
ssa_loopDirectCall |
495.808 ns (± 42.5412) |
491.118 ns (± 45.7827) |
1.01 |
ssa_pointerLoop |
608.753 ns (± 30.8363) |
583.073 ns (± 41.8959) |
1.04 |
ssa_staticLoop |
396.042 ns (± 30.1721) |
422.539 ns (± 47.7647) |
0.94 |
ssa_fibonacci |
531.697 ns (± 34.4653) |
503.737 ns (± 39.1986) |
1.06 |
ssa_gcd |
496.838 ns (± 43.5145) |
445.234 ns (± 36.8343) |
1.12 |
e2e_tiered_bc_to_mlir |
54.5016 us (± 6.45622) |
56.9167 us (± 7.69512) |
0.96 |
e2e_single_mlir |
5.60496 ms (± 568.424) |
5.52315 ms (± 41.2737) |
1.01 |
comp_mlir_add |
5.53034 ms (± 79.9952) |
5.55479 ms (± 56.4997) |
1.00 |
comp_mlir_ifThenElse |
6.12085 ms (± 43.6689) |
6.14317 ms (± 38.4483) |
1.00 |
comp_mlir_deeplyNestedIfElse |
5.04022 ms (± 158.378) |
5.06889 ms (± 99.0918) |
0.99 |
comp_mlir_loop |
7.12088 ms (± 31.8853) |
7.14743 ms (± 31.0585) |
1.00 |
comp_mlir_ifInsideLoop |
28.5585 ms (± 267.516) |
28.6125 ms (± 100.629) |
1.00 |
comp_mlir_loopDirectCall |
11.7593 ms (± 47.5567) |
11.792 ms (± 44.0495) |
1.00 |
comp_mlir_pointerLoop |
27.5973 ms (± 143.594) |
27.7179 ms (± 76.6326) |
1.00 |
comp_mlir_staticLoop |
4.99837 ms (± 41.019) |
5.03112 ms (± 38.6111) |
0.99 |
comp_mlir_fibonacci |
10.4271 ms (± 52.1502) |
10.4618 ms (± 76.5332) |
1.00 |
comp_mlir_gcd |
9.40532 ms (± 62.1999) |
9.43388 ms (± 42.8866) |
1.00 |
comp_mlir_nestedIf10 |
10.4621 ms (± 53.6469) |
10.5281 ms (± 45.0755) |
0.99 |
comp_mlir_nestedIf100 |
24.6081 ms (± 133.002) |
25.0116 ms (± 102.345) |
0.98 |
comp_mlir_chainedIf10 |
9.53695 ms (± 46.6553) |
9.56929 ms (± 46.3491) |
1.00 |
comp_mlir_chainedIf100 |
19.9952 ms (± 168.965) |
20.2061 ms (± 81.9884) |
0.99 |
comp_cpp_add |
24.5445 ms (± 242.431) |
24.3267 ms (± 189.47) |
1.01 |
comp_cpp_ifThenElse |
25.0362 ms (± 247.377) |
24.9638 ms (± 283.6) |
1.00 |
comp_cpp_deeplyNestedIfElse |
26.199 ms (± 444.539) |
26.0088 ms (± 233.598) |
1.01 |
comp_cpp_loop |
25.22 ms (± 224.563) |
25.0851 ms (± 291.998) |
1.01 |
comp_cpp_ifInsideLoop |
26.1548 ms (± 264.579) |
26.2072 ms (± 775.414) |
1.00 |
comp_cpp_loopDirectCall |
25.663 ms (± 271.565) |
25.3831 ms (± 531.136) |
1.01 |
comp_cpp_pointerLoop |
25.9858 ms (± 1.80081) |
25.4858 ms (± 203.254) |
1.02 |
comp_cpp_staticLoop |
25.0511 ms (± 642.902) |
24.7845 ms (± 176.103) |
1.01 |
comp_cpp_fibonacci |
25.2644 ms (± 258.403) |
25.2385 ms (± 205.167) |
1.00 |
comp_cpp_gcd |
25.0678 ms (± 272.827) |
25.0233 ms (± 436.288) |
1.00 |
comp_cpp_nestedIf10 |
28.0561 ms (± 253.934) |
28.0721 ms (± 363.361) |
1.00 |
comp_cpp_nestedIf100 |
61.964 ms (± 1.52933) |
61.7276 ms (± 707.638) |
1.00 |
comp_cpp_chainedIf10 |
31.7857 ms (± 2.72235) |
30.7822 ms (± 574.356) |
1.03 |
comp_cpp_chainedIf100 |
93.5768 ms (± 2.34167) |
91.9107 ms (± 685.096) |
1.02 |
comp_bc_add |
14.8058 us (± 2.59558) |
14.7773 us (± 1.91246) |
1.00 |
comp_bc_ifThenElse |
19.4385 us (± 4.24737) |
19.2429 us (± 3.53172) |
1.01 |
comp_bc_deeplyNestedIfElse |
22.7842 us (± 4.13866) |
23.0339 us (± 3.44593) |
0.99 |
comp_bc_loop |
19.0311 us (± 4.38883) |
18.9128 us (± 2.70016) |
1.01 |
comp_bc_ifInsideLoop |
21.5833 us (± 3.4477) |
21.5194 us (± 2.28647) |
1.00 |
comp_bc_loopDirectCall |
20.5191 us (± 5.35745) |
19.2878 us (± 2.03113) |
1.06 |
comp_bc_pointerLoop |
21.0134 us (± 5.17648) |
20.5351 us (± 2.66896) |
1.02 |
comp_bc_staticLoop |
17.3214 us (± 3.38925) |
17.2132 us (± 2.1155) |
1.01 |
comp_bc_fibonacci |
19.2063 us (± 3.61845) |
19.1709 us (± 2.93634) |
1.00 |
comp_bc_gcd |
18.3378 us (± 3.16889) |
18.5754 us (± 2.5724) |
0.99 |
comp_bc_nestedIf10 |
35.313 us (± 5.44198) |
35.6002 us (± 4.60432) |
0.99 |
comp_bc_nestedIf100 |
188.002 us (± 8.54544) |
198.41 us (± 10.198) |
0.95 |
comp_bc_chainedIf10 |
50.8488 us (± 10.4835) |
52.6073 us (± 9.11991) |
0.97 |
comp_bc_chainedIf100 |
296.808 us (± 23.1299) |
309.294 us (± 26.888) |
0.96 |
comp_asmjit_add |
22.3143 us (± 4.82514) |
22.1735 us (± 4.82406) |
1.01 |
comp_asmjit_ifThenElse |
34.1369 us (± 6.11381) |
33.4071 us (± 4.26649) |
1.02 |
comp_asmjit_deeplyNestedIfElse |
57.6219 us (± 10.2413) |
56.4176 us (± 5.25514) |
1.02 |
comp_asmjit_loop |
36.5097 us (± 5.11159) |
35.8631 us (± 4.35405) |
1.02 |
comp_asmjit_ifInsideLoop |
57.299 us (± 9.22632) |
57.5183 us (± 8.7641) |
1.00 |
comp_asmjit_loopDirectCall |
45.9998 us (± 9.74257) |
47.2559 us (± 9.00425) |
0.97 |
comp_asmjit_pointerLoop |
48.4943 us (± 8.37996) |
48.2785 us (± 6.56048) |
1.00 |
comp_asmjit_staticLoop |
29.1653 us (± 5.64879) |
28.6917 us (± 4.31543) |
1.02 |
comp_asmjit_fibonacci |
43.5165 us (± 7.87749) |
43.5157 us (± 6.92307) |
1.00 |
comp_asmjit_gcd |
36.1862 us (± 4.92882) |
35.3711 us (± 4.19816) |
1.02 |
comp_asmjit_nestedIf10 |
101.111 us (± 11.4672) |
103.255 us (± 10.7623) |
0.98 |
comp_asmjit_nestedIf100 |
1031.3600000000001 us (± 26340.3) |
1057.97 us (± 54427.5) |
0.97 |
comp_asmjit_chainedIf10 |
154.114 us (± 16.4685) |
153.183 us (± 14.6589) |
1.01 |
comp_asmjit_chainedIf100 |
2.19993 ms (± 45.7512) |
2.16653 ms (± 46.7063) |
1.02 |
exec_mlir_add |
10.3657 ns (± 0.893614) |
10.6651 ns (± 0.946586) |
0.97 |
exec_mlir_fibonacci |
13.2242 us (± 1.17463) |
13.4703 us (± 2.01788) |
0.98 |
exec_mlir_sum |
595.075 us (± 27.8987) |
527.422 us (± 21.2529) |
1.13 |
exec_cpp_add |
4.70498 ns (± 0.987826) |
4.69868 ns (± 0.745035) |
1.00 |
exec_cpp_fibonacci |
96.9705 us (± 9.64906) |
96.6361 us (± 9.26507) |
1.00 |
exec_cpp_sum |
36.0489 ms (± 802.063) |
35.9426 ms (± 115.287) |
1.00 |
exec_bc_add |
43.6795 ns (± 5.41778) |
44.8635 ns (± 8.015) |
0.97 |
exec_bc_fibonacci |
787.302 us (± 13.8159) |
859.028 us (± 13.0138) |
0.92 |
exec_bc_sum |
168.694 ms (± 590.151) |
186.067 ms (± 1.36597) |
0.91 |
exec_asmjit_add |
3.45826 ns (± 0.287882) |
3.48273 ns (± 0.424618) |
0.99 |
exec_asmjit_fibonacci |
20.4042 us (± 1.25159) |
20.8625 us (± 3.3694) |
0.98 |
exec_asmjit_sum |
4.84832 ms (± 40.6488) |
4.87965 ms (± 312.598) |
0.99 |
exec_bc_add_noRegAlloc |
43.1112 ns (± 4.85311) |
44.3118 ns (± 3.89068) |
0.97 |
exec_bc_add_regAlloc |
43.3876 ns (± 5.3221) |
44.599 ns (± 5.15383) |
0.97 |
exec_bc_fibonacci_noRegAlloc |
787.575 us (± 16.2729) |
852.767 us (± 11.3762) |
0.92 |
exec_bc_fibonacci_regAlloc |
796.169 us (± 42.6491) |
875.208 us (± 25.409) |
0.91 |
exec_bc_sum_noRegAlloc |
169.691 ms (± 3.70471) |
185.974 ms (± 222.438) |
0.91 |
exec_bc_sum_regAlloc |
168.577 ms (± 2.68024) |
185.982 ms (± 165.251) |
0.91 |
trace_add |
2.02407 us (± 193.769) |
2.27731 us (± 155.69) |
0.89 |
completing_trace_add |
2.13372 us (± 281.7) |
2.32405 us (± 143.855) |
0.92 |
trace_ifThenElse |
8.16499 us (± 1.08702) |
8.63915 us (± 776.354) |
0.95 |
completing_trace_ifThenElse |
3.7725 us (± 369.394) |
4.54071 us (± 401.003) |
0.83 |
trace_deeplyNestedIfElse |
23.9226 us (± 2.79848) |
25.5445 us (± 1.57172) |
0.94 |
completing_trace_deeplyNestedIfElse |
9.92996 us (± 1.13172) |
12.559 us (± 987.646) |
0.79 |
trace_loop |
7.89573 us (± 1.08286) |
8.80429 us (± 1.55155) |
0.90 |
completing_trace_loop |
4.0924 us (± 576.34) |
4.79104 us (± 376.911) |
0.85 |
trace_ifInsideLoop |
16.0887 us (± 2.7426) |
16.6688 us (± 1.56827) |
0.97 |
completing_trace_ifInsideLoop |
6.76618 us (± 951.604) |
8.40114 us (± 807.253) |
0.81 |
trace_loopDirectCall |
8.19934 us (± 1.19091) |
8.6864 us (± 697.439) |
0.94 |
completing_trace_loopDirectCall |
4.1868 us (± 535.012) |
4.86476 us (± 354.791) |
0.86 |
trace_pointerLoop |
12.4988 us (± 1.90697) |
14.0902 us (± 1.18648) |
0.89 |
completing_trace_pointerLoop |
8.14105 us (± 1.06249) |
10.1206 us (± 990.611) |
0.80 |
trace_staticLoop |
5.76164 us (± 560.41) |
7.21602 us (± 496.139) |
0.80 |
completing_trace_staticLoop |
5.7156 us (± 669.826) |
7.43532 us (± 991.516) |
0.77 |
trace_fibonacci |
9.21085 us (± 1.44412) |
10.0814 us (± 861.346) |
0.91 |
completing_trace_fibonacci |
4.93946 us (± 548.392) |
6.00031 us (± 426.095) |
0.82 |
trace_gcd |
7.73949 us (± 1.71352) |
7.79585 us (± 648.305) |
0.99 |
completing_trace_gcd |
3.54825 us (± 466.037) |
3.87126 us (± 272.452) |
0.92 |
trace_nestedIf10 |
31.3547 us (± 5.35581) |
38.0283 us (± 3.64798) |
0.82 |
completing_trace_nestedIf10 |
32.0398 us (± 5.73964) |
37.3711 us (± 3.21573) |
0.86 |
trace_nestedIf100 |
1.1313 ms (± 44.3488) |
1.35476 ms (± 22.6997) |
0.84 |
completing_trace_nestedIf100 |
1.13514 ms (± 49.1226) |
1.35268 ms (± 26.016) |
0.84 |
trace_chainedIf10 |
91.1809 us (± 7.86043) |
97.7583 us (± 4.73759) |
0.93 |
completing_trace_chainedIf10 |
35.7613 us (± 5.67618) |
48.3258 us (± 3.59147) |
0.74 |
trace_chainedIf100 |
4.19821 ms (± 50.8792) |
4.43042 ms (± 29.6138) |
0.95 |
completing_trace_chainedIf100 |
1.51039 ms (± 25.9136) |
2.24075 ms (± 31.9951) |
0.67 |
This comment was automatically generated by workflow using github-action-benchmark.
Three optimizations measured with benchmarks: 1. AliveVariableHash: replace unordered_map<uint32_t,uint32_t> with flat vector<uint32_t> indexed by ValueRef. ValueRef is monotonically increasing from 0, so direct indexing is O(1) with no hashing. Impact: ~20% speedup (chainedIf10: 50.1 -> 39.7 us) 2. ExecutionTrace: merge globalTagMap and localTagMap into a single tagMap. Both maps always contained the same entries (addTag writes to both), and checkTag searched both with identical handling. Eliminates one find + one insert per operation. Impact: ~8% speedup (chainedIf10: 39.7 -> 36.8 us) 3. TypedValueRefHolder: fix move constructor from const&& to && noexcept, skip allocateValRef on move (steal ref, null source). Skip freeValRef in destructor for moved-from objects (type == Type::v sentinel). Impact: within noise (moves are rare on the tracing hot path) Combined: ~27% faster tracing for control-flow-heavy functions. https://claude.ai/code/session_0132qQrKYMcQvLjjs8NA12wH
During follow mode (replaying previously recorded operations), val<T> constructors/destructors still call allocateValRef/freeValRef to maintain the alive-variable hash. This is wasted work since we're just replaying. Cache the alive hash in the Snapshot (new aliveHash field), then: - Set following_ flag at resume(), clear it on first RECORD operation - Skip allocateValRef/freeValRef when following_ is true - Restore alive hash from the CMP operation's cached snapshot at the follow-to-record transition in traceBool This eliminates all AliveVariableHash overhead during follow iterations. https://claude.ai/code/session_0132qQrKYMcQvLjjs8NA12wH
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
TwoPassTraceContext, a new trace context activated viaengine.traceMode = "twoPass"that provides a clean baseline for future deferred-merge experimentsMergeDetectionPhaseutility class for post-hoc merge restructuring (currently unused but available for future experimentation)ExecutionTest.hppandTracingTest.cpp) so it runs alongside the existing trace modesDesign Notes
The current implementation uses inline
checkTag()+processControlFlowMerge()(same semantics as LazyTraceContext) without theneedsCheckpoint_fast-path optimization. This produces identical traces to the existing contexts.Key finding from exploration: Deferred merge detection using Tag*-only or Tag*+staticHash keys produces false positives because Tags are not unique to a single code path — different branches can produce the same backtrace at structurally similar operations. The full Snapshot (Tag + staticHash + aliveHash) is required for correct merge detection. This rules out the "cheaper merge key" approach and motivates future work on a post-trace structural matching pass.
Architecture
Test plan
twoPassmode (1 pre-existingifWithTernaryfailure shared by all trace modes)https://claude.ai/code/session_0132qQrKYMcQvLjjs8NA12wH