Skip to content

Commit 5ccc2b3

Browse files
sfwclaude
andcommitted
Sprint 50: MIR Optimization Pass Phase 1 — constant fold, copy prop, dead blocks, peephole
Four-pass MIR optimizer with fixed-point iteration (max 3 rounds), validity checker, --no-optimize CLI flag, 24 unit tests, 3 CLI integration tests, .forma correctness test, and benchmark script. Documentation updated across reference, ai-reference, changelog, README, WHY_FORMA, and sprint plans. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9ba34c6 commit 5ccc2b3

13 files changed

Lines changed: 2402 additions & 6 deletions

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,36 @@ All notable changes to FORMA are documented in this file.
4242
- Added security warnings for `--allow-all` and capability flags to README, reference.md, and ai-reference.md.
4343
- Replaced predictable `/tmp/forma_*` paths with `mktemp` and `trap` cleanup in contract test script.
4444

45+
### MIR Optimization Pass (Phase 1)
46+
47+
- Added `src/mir/optimize.rs` with four optimization passes: constant folding, copy propagation, dead block elimination, and peephole optimizations.
48+
- Optimization runs by default between MIR lowering and interpretation/codegen; disable with `--no-optimize`.
49+
- Passes run in fixed-point rounds (max 3) so one pass can unlock another.
50+
- Constant folding evaluates compile-time arithmetic, comparisons, and boolean logic; simplifies constant-condition `If`/`Switch` to `Goto`.
51+
- Copy propagation eliminates redundant compiler temporaries with chain resolution.
52+
- Dead block elimination removes unreachable blocks with BlockId remapping; includes jump threading for empty goto chains.
53+
- Peephole optimizations: nop removal, identity ops (`x+0`, `x*1`), `x*0` folding, double negation elimination, redundant return-temp elimination.
54+
- Added MIR validity checker (`validate_mir()`) for post-optimization invariant checking.
55+
- Added `--no-optimize` CLI flag to `run`, `build`, and `compile` commands.
56+
- 24 unit tests covering all optimization passes and safety guards.
57+
- 3 CLI integration tests verifying output equivalence with and without optimization.
58+
- `.forma` correctness test (`test_optimization.forma`) and benchmark script (`scripts/bench_optimize.sh`).
59+
60+
### Contract Patterns Expansion
61+
62+
- Expanded named contract patterns from 12 to 35 across 6 categories (numeric, collection, set, sequence, ordering, state).
63+
- Added 8 runtime contract helpers (`set_equals`, `is_prefix`, `is_suffix`, `is_reversed`, `is_rotated`, `is_partitioned`, `stable`, `value_le`).
64+
- Added `pattern_to_english()` for human-readable pattern descriptions in explain output.
65+
- Fixed `@rotated` panic on negative k values (uses `rem_euclid`).
66+
- Fixed `@stable` correctness: now enforces permutation check + nondecreasing key order + stable relative ordering.
67+
- Fixed `is_partitioned` negative index guard.
68+
4569
### Test + Coverage Improvements
4670

4771
- Added CLI JSON failure-matrix tests for `run/check/build` across lex/parse/module/type failures.
4872
- Added capability matrix integration tests.
4973
- Expanded builtin coverage and added coverage enforcement support in CI.
74+
- Removed global `reset_type_var_counter()` calls from tests to fix parallel test flakes.
5075

5176
---
5277

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ FORMA is in **active development**. The core language and standard library are f
314314

315315
- [x] Lexer, parser, type checker
316316
- [x] Borrow checker (second-class references)
317-
- [x] MIR interpreter
317+
- [x] MIR interpreter with optimization pass (constant fold, copy propagation, dead block elimination, peephole)
318318
- [x] Generics with monomorphization
319319
- [x] Linear types and capability system
320320
- [x] Module system

docs/WHY_FORMA.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ Interpreted languages work well with AI — but they're not systems languages. Y
181181

182182
No. FORMA compiles to native code via LLVM — the same backend powering Rust, Clang, and Swift. The second-class reference model doesn't prevent optimization; it just changes how the compiler reasons about memory.
183183

184+
Before execution, FORMA runs a MIR optimization pass (constant folding, copy propagation, dead block elimination, peephole optimizations) that eliminates redundant temporaries and simplifies control flow. For LLVM builds, these MIR-level optimizations complement LLVM's own passes.
185+
184186
For the same algorithms, FORMA should produce comparable machine code to Rust. We're not trading performance for simplicity — we're trading *language complexity* for *AI compatibility*.
185187

186188
## Who Is FORMA For?

docs/ai-reference.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ f main()
429429
forma run <file> # run program
430430
forma run <file> --allow-all # run with all capabilities (DO NOT use on untrusted code)
431431
forma run <file> --no-check-contracts # disable contracts (enabled by default)
432+
forma run <file> --no-optimize # disable MIR optimization pass
432433
forma check <file> # type check only
433434
forma check <file> --error-format json # JSON errors
434435
forma explain <file> --format json # contract intent in JSON
@@ -441,6 +442,8 @@ forma verify <path> --report --max-steps 10000 --timeout 1000
441442
forma verify <path> --report --allow-side-effects
442443
forma grammar --format ebnf # export grammar
443444
forma grammar --format json # export grammar (JSON)
445+
forma build <file> # build native binary (LLVM)
446+
forma build <file> --no-optimize # build without MIR optimization
444447
forma fmt <file> # format code
445448
forma repl # interactive REPL
446449
forma typeof <file> --position L:C # type at position

docs/reference.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,7 @@ Capability-gated groups:
12391239
forma run <file> # Run a FORMA program
12401240
forma run <file> --dump-mir # Run with MIR dump
12411241
forma run <file> --no-check-contracts # Disable runtime contracts
1242+
forma run <file> --no-optimize # Disable MIR optimization pass
12421243
forma run <file> --allow-read # Allow file reads
12431244
forma run <file> --allow-write # Allow file writes
12441245
forma run <file> --allow-network # Allow networking
@@ -1249,6 +1250,7 @@ forma run <file> --allow-all # Allow all capabilities (see warning below)
12491250
forma check <file> # Type check without running
12501251
forma check <file> --partial # Partial checking
12511252
forma build <file> # Build native executable (LLVM feature)
1253+
forma build <file> --no-optimize # Build without MIR optimization
12521254
forma explain <file> # Explain contracts in plain English
12531255
forma explain <file> --examples=3 --seed 42 --format json
12541256
forma explain <file> --max-examples 3 --seed 42 --format json
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Sprint 50.1: MIR Copy Propagation Soundness Hotfix
2+
3+
## Goal
4+
5+
Fix the semantic unsoundness in MIR copy propagation introduced in Sprint 50 by enforcing reassignment and control-flow safety. Preserve optimization benefits while guaranteeing behavior equivalence with `--no-optimize`.
6+
7+
---
8+
9+
## Why This Sprint Exists
10+
11+
Current copy propagation in `src/mir/optimize.rs` builds a global substitution map and rewrites uses without proving safety across:
12+
13+
1. source local reassignments, and
14+
2. control-flow joins/branches.
15+
16+
This can rewrite a use to a newer value than the original copied value, producing incorrect runtime behavior.
17+
18+
---
19+
20+
## Scope
21+
22+
### 50.1.1 Make Copy Propagation Sound by Construction (P0)
23+
24+
**Files:** `src/mir/optimize.rs`
25+
26+
Replace current global substitution strategy with a conservative, semantics-safe approach.
27+
28+
### Required implementation constraints
29+
30+
Implement **block-local forward propagation only** for Phase 1:
31+
32+
1. Only propagate from `Assign(dest, Use(Copy(src)|Local(src)|Move(src)))`.
33+
2. `dest` must be a compiler temp (`LocalDecl.name == None`).
34+
3. Propagation candidates are valid **only within the same basic block**.
35+
4. Stop propagation when either `src` or `dest` is assigned again.
36+
5. Do not propagate through `Ref`, `Discriminant`, `EnumField`, or contract-sensitive contexts.
37+
6. Do not propagate into/through terminators if safety cannot be proven for block-local ordering.
38+
7. Never create/keep cyclic substitutions.
39+
40+
This is intentionally conservative. Correctness is mandatory; aggressiveness can return in Sprint 51 with data-flow analysis.
41+
42+
### Acceptance criteria
43+
44+
1. No behavior divergence between optimized and non-optimized execution on reassignment/control-flow edge cases.
45+
2. Copy propagation still optimizes trivial safe chains inside a block.
46+
3. Existing optimizer tests pass.
47+
48+
---
49+
50+
### 50.1.2 Add Regression Tests for the Bug Class (P0)
51+
52+
**Files:** `src/mir/optimize.rs` (test module), `tests/cli_tests.rs`, optionally `tests/forma/test_optimization.forma`
53+
54+
Add targeted tests that failed conceptually under old logic:
55+
56+
1. **Reassignment barrier test (unit):**
57+
- copy temp from `x`,
58+
- reassign `x`,
59+
- ensure return/use still points to copied temp semantics.
60+
2. **Branch/join test (unit):**
61+
- copy in one block,
62+
- reassign source in successor,
63+
- verify no illegal substitution through join.
64+
3. **CLI equivalence regression (integration):**
65+
- run fixture with and without `--no-optimize`,
66+
- assert identical stdout and exit code.
67+
68+
### Acceptance criteria
69+
70+
1. At least 3 new tests directly covering the unsoundness class.
71+
2. New tests fail on old algorithm and pass with fix.
72+
73+
---
74+
75+
### 50.1.3 Validate Optimizer Invariants Post-Pass (P1)
76+
77+
**Files:** `src/mir/optimize.rs`
78+
79+
After each optimization round (or in debug/test builds), run `validate_mir()` and fail fast in tests if invariants break.
80+
81+
### Acceptance criteria
82+
83+
1. Optimization tests assert `validate_mir(program).is_empty()`.
84+
2. Any malformed MIR from pass bugs is caught immediately.
85+
86+
---
87+
88+
### 50.1.4 Sprint-Report Accuracy Cleanup (P2)
89+
90+
**Files:** sprint notes/docs only as needed
91+
92+
Clarify .forma suite status from this environment:
93+
- `test_contract_errors.forma` is intentional negative.
94+
- `test_tcp.forma` may be environment-dependent and not introduced by Sprint 50.
95+
96+
Do not block merge on infra-dependent networking tests unless regression is proven against `--no-optimize`.
97+
98+
### Acceptance criteria
99+
100+
1. Sprint summary language is precise and non-misleading.
101+
102+
---
103+
104+
## Implementation Notes
105+
106+
Recommended minimal algorithm (safe):
107+
108+
1. For each basic block, scan statements top-to-bottom.
109+
2. Maintain a local map `dest -> src` valid for current scan window.
110+
3. On assignment to any local `l`, remove mappings where `dest == l` or `src == l`.
111+
4. Rewrite operands only using currently valid mappings.
112+
5. Do not carry mapping across block boundaries.
113+
114+
This gives deterministic safety without requiring dominance/use-def infrastructure.
115+
116+
---
117+
118+
## Verification Plan
119+
120+
```bash
121+
# Core
122+
cargo fmt --all -- --check
123+
cargo clippy --all-targets -- -D warnings
124+
cargo test --all
125+
126+
# Targeted optimizer tests
127+
cargo test mir::optimize::tests -- --nocapture
128+
129+
# Optimization equivalence check
130+
./target/release/forma run --allow-all tests/forma/test_optimization.forma > /tmp/opt_on.out
131+
./target/release/forma run --allow-all --no-optimize tests/forma/test_optimization.forma > /tmp/opt_off.out
132+
diff -u /tmp/opt_off.out /tmp/opt_on.out
133+
```
134+
135+
Optional reproduction fixture (for manual validation):
136+
137+
```forma
138+
f main() -> Int
139+
x := 1
140+
z := if true then x else 0
141+
x := 2
142+
if z == 1 then 0 else 99
143+
```
144+
145+
Expected exit code is identical with and without optimization.
146+
147+
---
148+
149+
## Out of Scope
150+
151+
1. Global data-flow copy propagation across blocks.
152+
2. Dominator tree construction.
153+
3. Dead store elimination.
154+
4. CSE/inlining/loop opts.
155+
156+
---
157+
158+
## Definition of Done
159+
160+
1. Copy propagation no longer performs unsound cross-assignment or cross-flow rewrites.
161+
2. New regression tests for reassignment + branch/join cases are present and passing.
162+
3. `cargo test --all` passes.
163+
4. CLI output/exit equivalence for optimization fixture remains identical (`opt` vs `--no-optimize`).
164+
5. MIR invariants are validated after optimization in tests.
165+

0 commit comments

Comments
 (0)