بنقرة واحدة
reduce-ssa-repro
// Minimize an SSA file that triggers a bug in the noir-ssa pipeline, producing the smallest possible reproduction case. Use after bisecting to identify which SSA passes cause the issue.
// Minimize an SSA file that triggers a bug in the noir-ssa pipeline, producing the smallest possible reproduction case. Use after bisecting to identify which SSA passes cause the issue.
Workflow for debugging SSA pass semantic preservation using the noir-ssa CLI. Use when a program's behavior changes incorrectly during the SSA pipeline - bisects passes to identify which one breaks semantics. The `pass_vs_prev` fuzzer finds such issues automatically.
End-to-end workflow for debugging SSA fuzzer failures from CI. Extracts a reproduction case from GitHub Actions logs, then bisects SSA passes to identify the bug. Use when a `pass_vs_prev` or similar fuzzer test fails in CI.
Extract a Noir reproduction project from fuzzer failure logs in GitHub Actions. Use when a CI fuzzer test fails and you need to create a local reproduction.
Workflow for measuring and optimizing the ACIR circuit size of a constrained Noir program. Use when asked to optimize a Noir program's gate count or circuit size.
Guide for writing noirc_frontend unit tests. Use when adding, writing, or reviewing frontend tests — regression tests, reproduction tests, error-checking tests, or should_panic tests in the compiler frontend.
Guidelines for writing idiomatic, efficient Noir programs. Use when writing or reviewing Noir code.
| name | reduce-ssa-repro |
| description | Minimize an SSA file that triggers a bug in the noir-ssa pipeline, producing the smallest possible reproduction case. Use after bisecting to identify which SSA passes cause the issue. |
Minimize an SSA file that triggers a bug in the noir-ssa pipeline. Use after the bisect-ssa-pass skill has identified which passes cause the issue.
Build the noir-ssa CLI as a debug binary:
cargo build -p noir_ssa_cli
Always use debug builds (target/debug/noir-ssa). Many SSA invariant checks are #[cfg(debug_assertions)]-guarded and compiled out of release builds.
Set SKILL_DIR to this skill's directory (for script references):
SKILL_DIR=<path-to-repo>/.claude/skills/reduce-ssa-repro
Gather from bisection:
input.ssa)Reduce the number of passes needed to trigger the bug. For each non-detection pass in the pipeline, try removing it:
# If the pipeline is: A → B → C → Detection
# Try removing each non-detection pass:
noir-ssa transform --source-path input.ssa --ssa-pass "B" --ssa-pass "C" --ssa-pass "Detection" -o /dev/null
noir-ssa transform --source-path input.ssa --ssa-pass "A" --ssa-pass "C" --ssa-pass "Detection" -o /dev/null
noir-ssa transform --source-path input.ssa --ssa-pass "A" --ssa-pass "B" --ssa-pass "Detection" -o /dev/null
When a pass produces valid output (not the buggy pass), bake it into the input to shrink both input and pipeline:
noir-ssa transform --source-path input.ssa --ssa-pass "A" -o after_A.ssa
# Verify crash reproduces without A:
noir-ssa transform --source-path after_A.ssa --ssa-pass "B" --ssa-pass "Detection" -o /dev/null
# If it still crashes:
cp after_A.ssa input.ssa
Repeat until no more passes can be removed.
Use $SKILL_DIR/scripts/reproduce_crash.sh. It has two modes depending on whether SSA_PASSES is set:
Multi-pass mode — when corruption passes precede the detection pass:
SSA_PASSES="<pass>" DETECTION_PASS="<detection-pass>" $SKILL_DIR/scripts/reproduce_crash.sh input.ssa
SSA_PASSES="<pass-1>:<pass-2>" DETECTION_PASS="<detection-pass>" $SKILL_DIR/scripts/reproduce_crash.sh input.ssa
Validates: (1) input parses, (2) detection pass alone succeeds, (3) full pipeline crashes.
Single-pass mode — when a single pass crashes directly on the input (all intermediate passes were removed in step 1):
DETECTION_PASS="<crashing-pass>" $SKILL_DIR/scripts/reproduce_crash.sh input.ssa
Validates: (1) input parses, (2) the pass crashes.
The detection pass is appended after the buggy passes. noir-ssa transform calls normalize_ids when formatting output after every pass, so any pass works for detecting corruption caught by normalize_ids (e.g., "Unmapped value" panics). Simplifying or Inlining Brillig Calls are lightweight choices.
The script defaults to target/debug/noir-ssa relative to the repo root. Override with NOIR_SSA=/path/to/noir-ssa.
Run reproduce_crash.sh after every change to input.ssa to verify the crash still reproduces.
Run reducer scripts from the same directory as input.ssa. Both --passes and --error-pattern are required. Pass all passes including the detection pass.
python3 $SKILL_DIR/scripts/reduce_instructions.py --passes "<pass>" "<detection-pass>" --error-pattern "<error text>"
Iterates over instructions, removes those whose results aren't referenced elsewhere, keeps only removals where the crash still reproduces.
python3 $SKILL_DIR/scripts/reduce_branches.py --passes "<pass>" "<detection-pass>" --error-pattern "<error text>"
Converts jmpif to unconditional jmp (tries both targets). Re-runs Phase 1 after each successful collapse.
Apply simplification passes one at a time. After each, verify the same error pattern still triggers:
noir-ssa transform --source-path input.ssa --ssa-pass "Simplifying" -o candidate.ssa
# Then verify:
SSA_PASSES="<pass>" DETECTION_PASS="<detection-pass>" $SKILL_DIR/scripts/reproduce_crash.sh candidate.ssa
# If same error, replace:
cp candidate.ssa input.ssa
Try these passes in order:
Simplifying — collapses trivial block chainsMem2Reg — eliminates store/load pairsDead Instruction Elimination — removes dead instructionsIf a cleanup pass triggers a different error: save that SSA separately as a potential second bug, but do not use it as input.ssa.
Re-run phases 1–3 until no more reductions are possible.
Draw a CFG diagram first. Write an ASCII diagram of the control flow graph to a file (e.g., cfg_diagram.txt). This makes the meaningful structure visible — nested diamonds, loop nesting, jmp chains vs. actual branch points — and guides which simplifications preserve the structure that triggers the bug vs. which just remove padding. Update the diagram after significant reductions.
Try each change one at a time, running reproduce_crash.sh after each:
bA → bB → bC are all unconditional jumps with no instructions/params, redirect bA → bC and remove bB. This is the single most effective manual reduction for CFG-heavy inputsjmpif to the same target (e.g., jmpif v0 then: bX, else: bX). This tests whether the branch matters or just the block countgN definitions not referenced in any function bodyu128 671967...) with small values (u128 1)f0 just calls f1, try return in f0; or inline f1 into f0lt vN, u32 3 → lt vN, u32 1. Test one change at a time — reductions that work individually may not composejmpif conditions, loop bounds) are less likely to be removablereturn vN with return, update the function signature and callerspredicate_pure and other attributesReduction is done when:
After reduction, the minimized SSA reveals the structural trigger:
normalize_value_ids panicking with "Unmapped value".store, leaving a load that reads uninitialized memory. Detected by "loaded before it was first stored".noir-ssa check: parses SSA, normalizes IDs, removes unreachable blocks, prints canonical form to stdout (not -o). Use to clean up after manual edits: noir-ssa check --source-path input.ssa > normalized.ssa.--ssa-pass uses substring matching (contains()), always matching the first pass with that name. Passes appearing multiple times may have different implementations at different pipeline positions.noir-ssa transform -o file.ssa then re-read — isolates whether corruption is in DFG state or logical SSA structure.