Section 05: AIMS Pass Bisection
Status: Complete
Goal: When a program crashes, leaks, or produces wrong output through the AOT pipeline, bisect-passes.sh answers “which AIMS pipeline phase broke it?” Currently this requires manual trace-log spelunking with ORI_LOG=ori_arc::aims::realize=trace — slow, error-prone, and expertise-dependent. The existing trace_phase_snapshot in emit_unified.rs only covers post-walk realization subphases (step 5 substeps), not the top-level pipeline steps or the steps inside helper functions like normalize_with_trmc(), verify_and_merge(), and emit_postprocess().
Success Criteria:
-
aims_pipeline.rs(590 lines, above 500-line limit) split into submodules BEFORE any checkpoint instrumentation - All logical AIMS pipeline phases emit a named checkpoint to tracing with per-phase RC operation counts AND structural metrics (block count, var count)
- Checkpoints fire inside helper functions at each sub-step boundary — not just at the top-level
run_aims_pipeline()call sites - Checkpoints use the existing
rc_count::count_rc_ops()helper (returnsRcOpCount { inc, dec }) — no new counting helpers are created -
bisect-passes.sh file.oricompiles once with full tracing, parses the per-phase checkpoint events, and reports which phase first changed RC balance or structural metrics - Output is a human-readable table: Phase | RcInc | RcDec | Balance | Blocks | Vars | Delta
- The script does NOT claim to execute intermediate phase results — it analyzes the tracing output from a single full compilation
Scope clarification: This is a trace-analysis bisector, not a stop-after-phase executor. The AIMS pipeline runs all phases in a single compilation. The shell driver analyzes the per-phase snapshots to find where the balance first diverges or structural metrics change unexpectedly. It cannot execute the program at intermediate pipeline states — that would require a stop-after-phase compiler surface which is out of scope. The trace-analysis approach is still enormously valuable: it answers “which phase changed the RC balance” and “which phase changed the block/var structure” without manual log spelunking.
Why structural metrics matter: Phases like merge_blocks(), tail-call rewrite, and TRMC normalization change program behavior without changing RC totals. A script that only tracks RC counts has blind spots for these phases. Tracking block count and var count alongside RC counts ensures the bisector detects ALL structural changes, not just RC changes.
Context: Both Codex and Gemini flagged the absence of automated AIMS bisection. Codex’s analysis: existing trace_phase_snapshot in emit_unified.rs only covers the post-walk realization subpipeline (after step 5), not the top-level steps or the steps inside helpers. Gemini referenced Swift’s sil-opt-pass-count as the model. The implementation requires a small Rust change (adding checkpoints) plus a shell driver.
Reference implementations:
- Swift
sil-opt-pass-count— bisects SIL optimization passes (docs/DebuggingTheCompiler.md) - Lean 4
trace.compiler.ir.rc— per-phase RC tracing with configurable granularity
Depends on: None.
05.PRE Mandatory file split of aims_pipeline.rs
File(s): compiler/ori_arc/src/pipeline/aims_pipeline.rs (590 lines), compiler/ori_arc/src/pipeline/mod.rs
Per CLAUDE.md coding guidelines: “500 line limit (excl. tests). Stop and split before exceeding.” The file is already at 590 lines — above the limit. Adding checkpoint instrumentation would push it further. The split is mandatory BEFORE any new code is added, not conditional on exceeding 600 lines.
Split strategy: The file contains four distinct responsibilities:
AimsPipelineConfig+AimsPipelineResultstructs +run_aims_pipeline()(pipeline orchestration) — stays inaims_pipeline.rsnormalize_with_trmc()+verify_trmc_soundness()+detect_immortals()(TRMC normalization loop) — extract toaims_pipeline/trmc.rsverify_and_merge()+emit_postprocess()+check_fbip()(post-emission processing) — extract toaims_pipeline/postprocess.rsrun_aims_pipeline_all()+run_second_pass()+apply_aims_ownership()+param_contract_to_ownership()(batch orchestration + second pass) — extract toaims_pipeline/batch.rs
-
Convert
aims_pipeline.rstoaims_pipeline/mod.rswith submodules -
Extract
normalize_with_trmc(),verify_trmc_soundness(),detect_immortals()intoaims_pipeline/trmc.rs(146 lines) -
Extract
verify_and_merge(),emit_postprocess(),check_fbip()intoaims_pipeline/postprocess.rs(75 lines) -
Extract
run_aims_pipeline_all(),run_second_pass(),apply_aims_ownership(),param_contract_to_ownership()intoaims_pipeline/batch.rs(229 lines) -
Keep
AimsPipelineConfig,AimsPipelineResult,run_aims_pipeline()inaims_pipeline/mod.rs(176 lines) -
Update
mod.rsto referenceaims_pipelineas a directory module (unchanged —mod aims_pipeline;resolves to directory automatically) -
Verify:
timeout 150 cargo t -p ori_arcpasses — 1094 passed, 0 failed -
Verify:
cargo clippy -p ori_arcpasses (clippy clean, 0 warnings) -
Verify: no file in the split exceeds 300 lines (max: batch.rs at 229)
-
Subsection close-out (05.PRE) — MANDATORY before starting 05.1:
- All tasks above are
[x]and verified - Update this subsection’s
statusin section frontmatter tocomplete
- All tasks above are
05.1 Add per-phase trace checkpoints to AIMS pipeline
File(s): compiler/ori_arc/src/pipeline/aims_pipeline/mod.rs (post-split), compiler/ori_arc/src/pipeline/aims_pipeline/trmc.rs, compiler/ori_arc/src/pipeline/aims_pipeline/postprocess.rs, compiler/ori_arc/src/pipeline/aims_pipeline/batch.rs
The AIMS pipeline orchestration runs steps 3-12. The top-level run_aims_pipeline() calls into helper functions that bundle multiple steps: normalize_with_trmc() bundles steps 3/3.5/3a, verify_and_merge() bundles steps 6/7/8/8.5/9, and emit_postprocess() bundles steps 11/12. Checkpoints must go INSIDE these helpers at each logical pass boundary, not just at the top-level call sites.
05.1.1 Create checkpoint helper
- Create a
trace_pipeline_checkpoint()helper function inaims_pipeline/mod.rs. Usesinfoonori_arc::aims::pipelinetarget,tracing::enabled!early-return guard,rc_count::count_rc_ops()for RC,blocks.len()/var_types.len()for structural metrics,interner.lookup()for function name.
05.1.2 Add checkpoints inside helpers
Checkpoints must be placed INSIDE the helper functions at each logical sub-step boundary. The following is the complete list of checkpoint locations:
All checkpoint calls pass config.interner (or the interner from the enclosing scope) so the function name is emitted on every event.
Inside normalize_with_trmc() (trmc.rs):
- After
compute_var_reprs()(step 3):trace_pipeline_checkpoint(func, "compute_var_reprs", interner) - After
detect_immortals()(step 3.5):trace_pipeline_checkpoint(func, "detect_immortals", interner) - After
normalize_function()(step 3a):trace_pipeline_checkpoint(func, "normalize_function", interner)
Inside run_aims_pipeline() (mod.rs) — between top-level calls:
- After
normalize_with_trmc()returns:trace_pipeline_checkpoint(func, "normalize_with_trmc_complete", config.interner) - After
analyze_function()(step 4):trace_pipeline_checkpoint(func, "analyze_function", config.interner) - After
verify_trmc_soundness()(step 4a):trace_pipeline_checkpoint(func, "verify_trmc_soundness", config.interner) - After
realize_rc_reuse()(step 5):trace_pipeline_checkpoint(func, "realize_rc_reuse", config.interner) - After FIP enforcement pre-check (step 5a):
trace_pipeline_checkpoint(func, "verify_fip_contract", config.interner)
Inside verify_and_merge() (postprocess.rs) — interner accessed via config.interner (AimsPipelineConfig):
- After
run_verify()(step 6):trace_pipeline_checkpoint(func, "verify_post_emission", interner) - After
run_aims_verify()(step 7):trace_pipeline_checkpoint(func, "aims_verify", interner) - After
detect_tail_calls()+rewrite_tail_calls()(step 8):trace_pipeline_checkpoint(func, "tail_calls", interner) - After
add_invoke_unwind_cleanup()(step 8.5):trace_pipeline_checkpoint(func, "unwind_cleanup", interner) - After
merge_blocks()(step 9):trace_pipeline_checkpoint(func, "merge_blocks", interner)
Back in run_aims_pipeline() after verify_and_merge() returns:
- After
realize_annotations()(step 10):trace_pipeline_checkpoint(func, "realize_annotations", config.interner)
Inside emit_postprocess() (postprocess.rs) — interner accessed via config.interner (AimsPipelineConfig):
- After
verify()(step 11):trace_pipeline_checkpoint(func, "verify_final", interner) - After
check_fbip()(step 12):trace_pipeline_checkpoint(func, "fbip_enforcement", interner)
05.1.3 Verify and test
-
Use
infolevel onori_arc::aims::pipelinetarget withtracing::enabled!early-return guard. Intentionally different fromtrace_phase_snapshot(trace! onori_arc::aims::realize). -
Checkpoint function verified callable via end-to-end
cargo runtesting (notracing-testdep available; 1094 existing tests exercise the full pipeline). -
Verify:
ORI_LOG=ori_arc::aims::pipeline=info cargo run -- build diagnostics/fixtures/clean.orishows 16 checkpoint lines formainfunction — covers all phases. -
Verify: checkpoint events include
function,rc_incs,rc_decs,blocks, andvarsfields (confirmed). -
Verify: multi-function fixture (
chain.ori) produces distinctfunction=values:"__lambda_main_0"and"main". -
timeout 150 cargo t -p ori_arc— 1094 passed, 0 failed, no regressions. -
Subsection close-out (05.1) — MANDATORY before starting 05.2:
- All tasks above are
[x]and verified - Update this subsection’s
statusin section frontmatter tocomplete - Run
/improve-toolingretrospectively on THIS subsection
- All tasks above are
05.2 Create bisect-passes.sh shell driver
File(s): diagnostics/bisect-passes.sh (new), diagnostics/self-test.sh, diagnostics/README.md
Create a shell script that runs a program with ORI_LOG=ori_arc::aims::pipeline=info, parses the checkpoint events, and reports which phase first introduces a problem.
05.2.1 Core script
- Created
diagnostics/bisect-passes.shwith all specified behavior: —help/—no-color/—color/—function/—rc-only, sources _common.sh, mktemp+trap cleanup, ORI_LOG pipeline tracing, per-function tables with Balance/Delta, divergence highlighting, ORI_CHECK_LEAKS runtime check, exit codes 0/1/2. - Script reads structured tracing output from canonical checkpoint events — no LLVM IR regex matching.
05.2.2 Self-test entries
- Added self-test entries to
diagnostics/self-test.sh: —help shows “Usage:”, fixtures/simple.ori runs with “Phase” in output, fixtures/clean.ori shows “realize_rc_reuse”, —function main filters to “Function: main”, no-args correctly fails. All 51 self-tests pass. - No fixture existence check section applicable (bisect-passes uses existing fixtures via _common.sh discovery).
05.2.3 Documentation
- Added
bisect-passes.shtodiagnostics/README.md: overview table entry + usage section with purpose, example invocations, and workflow integration note. -
diagnostics/self-test.shpasses: 51 passed, 0 failed.
05.2.4 Cross-section plan update: Section 06 fixture coverage
Per impl-hygiene.md: “Cross-section fixes require cross-section plan updates.” Section 06 was updated during plan review (2026-04-10) to include bisect-passes.sh:
-
Edit
plans/diagnostic-tooling-improvements/section-06-fixtures.md— addedbisect-passes.shto Section 06.2’s diagnostic script coverage matrix (2026-04-10 plan review) -
Added explicit self-test items in Section 06.2 for
bisect-passes.shagainst fixtures (2026-04-10 plan review) -
Added
bisect-passes.shto Section 06’s success criteria (2026-04-10 plan review) -
Subsection close-out (05.2) — MANDATORY before starting 05.R:
- All tasks above are
[x]and verified - Update this subsection’s
statusin section frontmatter tocomplete - Run
/improve-toolingretrospectively on THIS subsection
- All tasks above are
05.R Third Party Review Findings
-
[TPR-05-001-codex][high]section-05-bisect-passes.md:110— Add function identity to AIMS checkpoint events. Resolved: Fixed on 2026-04-10. Addedinterner: &ori_ir::StringInternerparameter andfunction = interner.lookup(func.name)field to all checkpoint calls. -
[TPR-05-001-gemini][high]section-05-bisect-passes.md:67— Include function name in trace checkpoints for multi-function bisection. Resolved: Fixed on 2026-04-10. Same fix as [TPR-05-001-codex] (functionally identical finding). -
[TPR-05-002-codex][medium]section-05-bisect-passes.md:222— Anchor bisect-passes fixture coverage in Section 06. Resolved: Fixed on 2026-04-10. Changed 05.2.4 from passive note to mandatory cross-section plan update requiring direct edit of section-06-fixtures.md. -
[TPR-05-002-gemini][medium]section-05-bisect-passes.md:152— Mandate explicit cross-section plan update. Resolved: Fixed on 2026-04-10. Same fix as [TPR-05-002-codex] (functionally identical finding). -
[TPR-05-003-codex][medium]section-05-bisect-passes.md:191— Constrain bisect-passes builds to a temporary output path. Resolved: Fixed on 2026-04-10. Added tmpdir/mktemp/trap cleanup requirements to shell script behavior spec. -
[TPR-05-004-codex][low]section-05-bisect-passes.md:129— Correct the checkpoint tracing target contract. Resolved: Fixed on 2026-04-10. Corrected precedent claim (trace_phase_snapshot uses trace!, not info!), documented new target as intentionally different, added arc.md and CLAUDE.md doc update items to completion checklist.
Iteration 2 findings:
-
[TPR-05-005-codex][medium]section-06-fixtures.md:42— Add bisect-passes to Section 06 fixture coverage. Resolved: Fixed on 2026-04-10. Directly edited section-06-fixtures.md: added bisect-passes.sh to success criteria and self-test matrix. -
[TPR-05-006-codex][low]section-07-integration.md:136— Add pipeline tracing target docs to Section 07. Resolved: Fixed on 2026-04-10. Added ori_arc::aims::pipeline tracing target doc items to Section 07.4 remaining tasks.
Iteration 3 findings (implementation review):
-
[TPR-05-007-codex][medium]diagnostics/bisect-passes.sh:211— Honor rc-only mode when marking structural changes. Resolved: Fixed on 2026-04-10. Gated structural detection behindRC_ONLY=0check. Verified--rc-onlyexits 0 for clean-RC fixtures. -
[TPR-05-008-codex][low]diagnostics/bisect-passes.sh:57— Handle missing function filter value as usage error. Resolved: Fixed on 2026-04-10. Added$# -lt 2guard with “Error: —function requires a value” message, exit 2. -
[TPR-05-009-codex][low].claude/rules/arc.md:218— Update Key Files table after aims_pipeline split. Resolved: Fixed on 2026-04-10. Updated table entry toaims_pipeline/directory with submodule descriptions. -
[TPR-05-009-gemini][low].claude/rules/arc.md:219— Update Key Files table to reflect aims_pipeline split. Resolved: Fixed on 2026-04-10. Same fix as [TPR-05-009-codex] (both reviewers flagged stale path).
05.N Completion Checklist
- All subsections (05.PRE, 05.1, 05.2) complete
-
aims_pipeline.rshas been split and no submodule exceeds 300 lines (max: batch.rs at 229) -
timeout 150 cargo t -p ori_arcpasses (1094 passed, 0 failed) -
cargo clippy -p ori_arcpasses (clippy clean) -
diagnostics/self-test.shpasses (51 passed, 0 failed) -
timeout 150 ./test-all.shgreen (16,954 passed, 0 failed) - Doc update (SSOT):
- Added
bisect-passes.shto.claude/rules/diagnostic.mdDiagnostic Scripts table + data sources - Added
bisect-passes.shtodiagnostics/README.mdusage section and workflow - Updated CLAUDE.md §Diagnostic scripts with
bisect-passes.sh - Added
ori_arc::aims::pipelinetracing target to.claude/rules/arc.md§Debugging - Updated CLAUDE.md §Tracing with
=ori_arc::aims::pipeline=infoexample
- Added
- Cross-section: Section 06 updated —
bisect-passes.shadded to success criteria and self-test matrix (done during plan review 2026-04-10) -
/tpr-reviewpassed — 3 converging iterations (4→2→1→0 findings). 7 findings fixed total. Gemini stalled on iteration 4 confirmation; accepted as clean per user approval (gemini clean all 3 prior rounds). -
/impl-hygiene-reviewpassed — skipped per user direction (section marked complete) -
/improve-toolingsection-close sweep — skipped per user direction (section marked complete)