feat(llvm): Phase 131-13/14 - MIR JSON順序修正 & 2パスsnapshot解決
## Phase 131-13: MIR JSON 命令順序修正 - copy 遅延ロジック削除(~80行) - MIR の def→use 順序をそのまま出力(SSOT) - PHI 先頭集約のみ維持 ## Phase 131-14: jump-only block 2パス snapshot 解決 - Pass A: jump-only block はメタ記録のみ - Pass B: resolve_jump_only_snapshots() で CFG ベース解決 - path compression で連鎖を効率的に解決 - サイクル検出で Fail-Fast ## 結果 - ✅ STRICT モードでエラーなし - ✅ bb7 が bb5 の snapshot を正しく継承 - ✅ ループが正しく動作(1, 2 出力確認) - ⚠️ print/concat で segfault(別問題、次Phase) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -0,0 +1,189 @@
|
|||||||
|
# Phase 131-13: MIR JSON Instruction Order Fix - COMPLETED ✅
|
||||||
|
|
||||||
|
Status: **RESOLVED** (2025-12-14)
|
||||||
|
Scope: Rust 側の `MIR → JSON (harness)` 出力で、ブロック内の命令順序が崩れて Python LLVM backend が Fail-Fast する問題。
|
||||||
|
Related:
|
||||||
|
- SSOT (LLVM棚卸し): `docs/development/current/main/phase131-3-llvm-lowering-inventory.md`
|
||||||
|
- Case C: `docs/development/current/main/phase131-11-case-c-summary.md`
|
||||||
|
|
||||||
|
## Problem Summary
|
||||||
|
|
||||||
|
**Issue**: LLVM Stage-3 Case C (loop control) failed with undefined value error
|
||||||
|
- Test: `apps/tests/llvm_stage3_loop_only.hako`
|
||||||
|
- Symptom: `binop dst=19 lhs=17 rhs=18` used undefined v17 and v18
|
||||||
|
- Root cause: JSON emitter reordered instructions, breaking def-use chain
|
||||||
|
|
||||||
|
### Original JSON Order (Broken)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"instructions": [
|
||||||
|
{"op": "const", "dst": 16},
|
||||||
|
{"op": "binop", "dst": 19, "lhs": 17, "rhs": 18}, // ❌ Use v17, v18 before def
|
||||||
|
{"op": "copy", "dst": 17, "src": 16}, // ⚠️ Define v17 AFTER use
|
||||||
|
{"op": "copy", "dst": 18, "src": 1}, // ⚠️ Define v18 AFTER use
|
||||||
|
{"op": "copy", "dst": 20, "src": 19}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Root Cause Analysis
|
||||||
|
|
||||||
|
`src/runner/mir_json_emit.rs` が "use-before-def copy 回避" を目的に **copy の遅延/再配置**を行っており、
|
||||||
|
依存(copy dst を参照する binop 等)まで含めた正しいスケジューリングになっていなかった。
|
||||||
|
|
||||||
|
この層(JSON emitter)は optimizer/scheduler ではないため、順序修正を試みるより **Fail-Fast で upstream を炙り出す**のが筋。
|
||||||
|
|
||||||
|
### Responsible Code (Lines 193-266, 652-710)
|
||||||
|
```rust
|
||||||
|
// Pre-scan: collect values defined anywhere in this block (to delay use-before-def copies)
|
||||||
|
let mut block_defines: std::collections::HashSet<u32> = ...;
|
||||||
|
let mut emitted_defs: std::collections::HashSet<u32> = ...;
|
||||||
|
let mut delayed_copies: Vec<(u32, u32)> = Vec::new();
|
||||||
|
|
||||||
|
// Delay copies if source will be defined later
|
||||||
|
if block_defines.contains(&s) && !emitted_defs.contains(&s) {
|
||||||
|
delayed_copies.push((d, s)); // ❌ Reordering!
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit delayed copies after sources should be available
|
||||||
|
for (d, s) in delayed_copies {
|
||||||
|
insts.push(json!({"op":"copy","dst": d, "src": s}));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Solution: SSOT Principle ✅
|
||||||
|
|
||||||
|
**Box Theory Diagnosis**: Responsibility boundary violation
|
||||||
|
- **JSON Emitter responsibility**: Output MIR instructions in original order
|
||||||
|
- **Builder/Optimizer/Verifier responsibility**: Ensure correct def-use order upstream
|
||||||
|
|
||||||
|
### SSOT: 「順序を直す場所」を固定する
|
||||||
|
|
||||||
|
- ✅ JSON emitter は "順序を変えない"(MIR の命令列をそのまま出力する)
|
||||||
|
- ✅ もし MIR 自体に use-before-def があるなら、builder/optimizer/verifier 側で直す(またはそこで Fail-Fast)
|
||||||
|
|
||||||
|
### Fix Implementation
|
||||||
|
|
||||||
|
**P0-1: Remove all reordering logic** (except PHI consolidation)
|
||||||
|
```rust
|
||||||
|
// Phase 131-13: Emit all instructions in MIR order (SSOT principle)
|
||||||
|
// No reordering except PHI consolidation at block start (LLVM constraint)
|
||||||
|
|
||||||
|
// Step 1: Emit all PHI instructions first (LLVM requirement)
|
||||||
|
for inst in &bb.instructions {
|
||||||
|
if let I::Phi { ... } => { insts.push(phi_inst); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Emit all non-PHI instructions in MIR order (no reordering!)
|
||||||
|
for inst in &bb.instructions {
|
||||||
|
match inst {
|
||||||
|
I::Phi { .. } => continue, // Already emitted
|
||||||
|
I::Copy { dst, src } => {
|
||||||
|
insts.push(json!({"op":"copy","dst": dst.as_u32(), "src": src.as_u32()}));
|
||||||
|
}
|
||||||
|
// ... other instructions in order
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Emit 規約 (Updated)
|
||||||
|
|
||||||
|
- ✅ PHI は block の先頭(LLVM 制約)。JSON 側で先頭に集約済み。
|
||||||
|
- ✅ 非PHIは MIR の順序を保持して出力する(並べ替えない)。
|
||||||
|
- ✅ Terminator は最後。
|
||||||
|
|
||||||
|
**Changes**:
|
||||||
|
- ✅ Removed `block_defines` HashSet
|
||||||
|
- ✅ Removed `emitted_defs` HashSet
|
||||||
|
- ✅ Removed `delayed_copies` Vec
|
||||||
|
- ✅ Removed all `emitted_defs.insert()` calls
|
||||||
|
- ✅ Removed delayed copy emission loop
|
||||||
|
- ✅ Applied fix to both `emit_mir_json_for_harness` (lib) and `emit_mir_json_for_harness_bin` (bin)
|
||||||
|
|
||||||
|
### Fixed JSON Order ✅
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"instructions": [
|
||||||
|
{"op": "phi", "dst": 1},
|
||||||
|
{"op": "const", "dst": 16},
|
||||||
|
{"op": "copy", "dst": 17, "src": 16}, // ✅ Define v17 first
|
||||||
|
{"op": "copy", "dst": 18, "src": 1}, // ✅ Define v18 second
|
||||||
|
{"op": "binop", "dst": 19, "lhs": 17, "rhs": 18}, // ✅ Use v17, v18 after def
|
||||||
|
{"op": "copy", "dst": 20, "src": 19},
|
||||||
|
{"op": "mir_call", ...},
|
||||||
|
{"op": "const", "dst": 21},
|
||||||
|
{"op": "ret"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verification ✅
|
||||||
|
|
||||||
|
### Test Execution
|
||||||
|
```bash
|
||||||
|
# Generate fixed JSON
|
||||||
|
NYASH_DISABLE_PLUGINS=1 ./target/release/hakorune --backend mir \
|
||||||
|
--emit-mir-json /tmp/fixed.json apps/tests/llvm_stage3_loop_only.hako
|
||||||
|
# Output: MIR JSON written: /tmp/fixed.json
|
||||||
|
|
||||||
|
# Verify instruction order
|
||||||
|
cat /tmp/fixed.json | jq '.functions[] | select(.name == "main") | .blocks[1].instructions'
|
||||||
|
# ✅ Correct: copy dst=17, copy dst=18, binop lhs=17 rhs=18
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build Status
|
||||||
|
```bash
|
||||||
|
cargo build --release
|
||||||
|
# ✅ 0 errors, 0 warnings
|
||||||
|
```
|
||||||
|
|
||||||
|
## Done 条件 ✅
|
||||||
|
|
||||||
|
- ✅ Case C の JSON 出力で def→use 順序が正しい
|
||||||
|
- ✅ LLVM harness が正しく型解決できる(use-before-def エラーなし)
|
||||||
|
- ✅ ビルド成功(0 errors, 0 warnings)
|
||||||
|
|
||||||
|
## Fail-Fast チェック(Future Work)
|
||||||
|
|
||||||
|
JSON emit 時に、同一ブロック内の use-before-def を検出したら明示エラーにする("直そうとしない")。
|
||||||
|
|
||||||
|
**P1: Add use-before-def detector** (NYASH_MIR_STRICT mode) - TODO
|
||||||
|
```rust
|
||||||
|
fn check_use_before_def(block: &BasicBlock) -> Result<(), String> {
|
||||||
|
let mut defined: HashSet<ValueId> = HashSet::new();
|
||||||
|
for inst in &block.instructions {
|
||||||
|
// Check all operands are defined
|
||||||
|
for operand in inst.operands() {
|
||||||
|
if !defined.contains(&operand) && !is_phi(inst) && !is_block_param(operand) {
|
||||||
|
if strict_mode() {
|
||||||
|
return Err(format!("Use-before-def: v{}", operand.as_u32()));
|
||||||
|
} else {
|
||||||
|
eprintln!("[WARN] Use-before-def: v{}", operand.as_u32());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(dst) = inst.dst() {
|
||||||
|
defined.insert(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Files Modified
|
||||||
|
|
||||||
|
- `src/runner/mir_json_emit.rs`: Core fix (removed ~80 lines of reordering logic)
|
||||||
|
|
||||||
|
## Box Theory Insights
|
||||||
|
|
||||||
|
**Fail-Fast Principle**: Instead of hiding errors with reordering, expose them early
|
||||||
|
- ❌ Old approach: Emitter tries to fix broken MIR (scheduler role)
|
||||||
|
- ✅ New approach: Emitter outputs MIR as-is, builder ensures correctness
|
||||||
|
|
||||||
|
**SSOT Boundary**:
|
||||||
|
- **Upstream** (builder.rs): Responsible for def-use order
|
||||||
|
- **Emitter** (mir_json_emit.rs): Responsible for faithful output
|
||||||
|
- **Downstream** (LLVM harness): Expects correct order from JSON
|
||||||
|
|
||||||
@ -211,6 +211,11 @@ VM and MIR look correct, but LLVM output does not match expected result for Case
|
|||||||
- `return counter` (no string concat)
|
- `return counter` (no string concat)
|
||||||
- `print(counter)` (no `"Result: " + ...`)
|
- `print(counter)` (no `"Result: " + ...`)
|
||||||
- Compare with VM and inspect the IR use-sites.
|
- Compare with VM and inspect the IR use-sites.
|
||||||
|
|
||||||
|
**Update (Phase 131-13)**:
|
||||||
|
- snapshot-only + strict resolver により、Case C の不一致が “LLVM の値解決バグ” ではなく
|
||||||
|
“Rust の MIR→JSON emit が block 内命令順序を崩している” 問題として顕在化した。
|
||||||
|
- Investigation note: `docs/development/current/main/investigations/phase131-13-mir-json-instruction-order.md`
|
||||||
- Add `is_infinite_loop: bool` feature to `LoopFeatures` (detect `loop(true)`).
|
- Add `is_infinite_loop: bool` feature to `LoopFeatures` (detect `loop(true)`).
|
||||||
- Fix classification so `has_break && has_continue` does not route to Pattern 4.
|
- Fix classification so `has_break && has_continue` does not route to Pattern 4.
|
||||||
- Introduce a dedicated pattern kind + lowerer for **infinite loop + early-exit (+ optional continue)**:
|
- Introduce a dedicated pattern kind + lowerer for **infinite loop + early-exit (+ optional continue)**:
|
||||||
|
|||||||
@ -1,9 +1,51 @@
|
|||||||
from typing import Dict, Any, List, Tuple, NamedTuple
|
from typing import Dict, Any, List, Tuple, NamedTuple
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
from llvmlite import ir
|
from llvmlite import ir
|
||||||
from trace import debug as trace_debug
|
from trace import debug as trace_debug
|
||||||
from trace import phi_json as trace_phi_json
|
from trace import phi_json as trace_phi_json
|
||||||
|
|
||||||
|
|
||||||
|
def is_jump_only_block(block_info: Dict) -> bool:
|
||||||
|
"""Phase 131-14-B: Detect pure jump-only blocks (trampoline blocks).
|
||||||
|
|
||||||
|
A pure jump-only block has:
|
||||||
|
- NO PHI instructions (PHI blocks do meaningful work - value merging)
|
||||||
|
- NO other instructions except a single terminator (jump/branch/ret)
|
||||||
|
- Acts as a pure trampoline/routing block
|
||||||
|
|
||||||
|
Blocks with PHI instructions are NOT jump-only because they perform value
|
||||||
|
merging and must compute their own snapshots.
|
||||||
|
"""
|
||||||
|
instructions = block_info.get("instructions", [])
|
||||||
|
|
||||||
|
# Check if block has any PHI instructions
|
||||||
|
has_phi = any(i.get("op") == "phi" for i in instructions)
|
||||||
|
if has_phi:
|
||||||
|
# PHI blocks are NOT jump-only - they do value merging
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Check if block has only terminator instructions
|
||||||
|
non_term = [
|
||||||
|
i for i in instructions
|
||||||
|
if i.get("op") not in ("ret", "jump", "branch")
|
||||||
|
]
|
||||||
|
return len(non_term) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def get_predecessors(bid: int, preds: Dict[int, List[int]]) -> List[int]:
|
||||||
|
"""Phase 131-14 P0-3: Get predecessors for a block.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
bid: Block ID
|
||||||
|
preds: Predecessor map (bid -> [predecessor_bids])
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of predecessor block IDs
|
||||||
|
"""
|
||||||
|
return preds.get(bid, [])
|
||||||
|
|
||||||
|
|
||||||
class DeferredTerminator(NamedTuple):
|
class DeferredTerminator(NamedTuple):
|
||||||
"""Phase 131-12-P1: Deferred terminator with vmap snapshot.
|
"""Phase 131-12-P1: Deferred terminator with vmap snapshot.
|
||||||
|
|
||||||
@ -15,12 +57,138 @@ class DeferredTerminator(NamedTuple):
|
|||||||
vmap_snapshot: Dict[int, ir.Value]
|
vmap_snapshot: Dict[int, ir.Value]
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_jump_only_snapshots(builder, block_by_id: Dict[int, Dict[str, Any]]):
|
||||||
|
"""Phase 131-14-B P0-2: Resolve jump-only block snapshots (Pass B).
|
||||||
|
|
||||||
|
This function runs AFTER all blocks have been lowered (Pass A) but BEFORE
|
||||||
|
PHI finalization. It resolves snapshots for jump-only blocks by following
|
||||||
|
the CFG to find the nearest non-jump-only predecessor.
|
||||||
|
|
||||||
|
Uses path compression to efficiently handle chains of jump-only blocks.
|
||||||
|
|
||||||
|
SSOT: Snapshots are based on CFG structure, not processing order.
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
|
||||||
|
strict_mode = os.environ.get('NYASH_LLVM_STRICT') == '1'
|
||||||
|
trace_vmap = os.environ.get('NYASH_LLVM_TRACE_VMAP') == '1'
|
||||||
|
|
||||||
|
jump_only = getattr(builder, '_jump_only_blocks', {})
|
||||||
|
if not jump_only:
|
||||||
|
if trace_vmap:
|
||||||
|
print("[vmap/resolve/passB] No jump-only blocks to resolve", file=sys.stderr)
|
||||||
|
return
|
||||||
|
|
||||||
|
if trace_vmap:
|
||||||
|
print(f"[vmap/resolve/passB] Resolving {len(jump_only)} jump-only blocks: {sorted(jump_only.keys())}", file=sys.stderr)
|
||||||
|
|
||||||
|
resolved = {} # bid -> snapshot dict
|
||||||
|
|
||||||
|
def resolve(bid: int, visited: set | None = None) -> Dict[int, Any]:
|
||||||
|
"""Recursively resolve snapshot for a block, with cycle detection."""
|
||||||
|
if visited is None:
|
||||||
|
visited = set()
|
||||||
|
|
||||||
|
# Cycle detection
|
||||||
|
if bid in visited:
|
||||||
|
if strict_mode:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"[LLVM_PY/STRICT] Phase 131-14-B: Cycle detected in jump-only chain: "
|
||||||
|
f"{visited} -> {bid}"
|
||||||
|
)
|
||||||
|
if trace_vmap:
|
||||||
|
print(f"[vmap/resolve/passB] WARNING: Cycle at bb{bid}, returning empty", file=sys.stderr)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
visited.add(bid)
|
||||||
|
|
||||||
|
# Already resolved (path compression cache)
|
||||||
|
if bid in resolved:
|
||||||
|
if trace_vmap:
|
||||||
|
print(f"[vmap/resolve/passB] bb{bid} already resolved (cached)", file=sys.stderr)
|
||||||
|
return resolved[bid]
|
||||||
|
|
||||||
|
# Normal block - already has snapshot from Pass A
|
||||||
|
if bid in builder.block_end_values:
|
||||||
|
snapshot = builder.block_end_values[bid]
|
||||||
|
if trace_vmap:
|
||||||
|
print(
|
||||||
|
f"[vmap/resolve/passB] bb{bid} is normal block with snapshot "
|
||||||
|
f"({len(snapshot)} values)",
|
||||||
|
file=sys.stderr
|
||||||
|
)
|
||||||
|
return snapshot
|
||||||
|
|
||||||
|
# Jump-only block - resolve from predecessor
|
||||||
|
if bid in jump_only:
|
||||||
|
pred_bid = jump_only[bid]
|
||||||
|
if trace_vmap:
|
||||||
|
print(f"[vmap/resolve/passB] bb{bid} is jump-only, resolving from pred bb{pred_bid}", file=sys.stderr)
|
||||||
|
|
||||||
|
# Recursively resolve predecessor
|
||||||
|
pred_snapshot = resolve(pred_bid, visited)
|
||||||
|
|
||||||
|
if not pred_snapshot:
|
||||||
|
if strict_mode:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"[LLVM_PY/STRICT] Phase 131-14-B: jump-only block bb{bid} "
|
||||||
|
f"cannot resolve snapshot from predecessor bb{pred_bid} "
|
||||||
|
f"(predecessor has no snapshot)"
|
||||||
|
)
|
||||||
|
if trace_vmap:
|
||||||
|
print(
|
||||||
|
f"[vmap/resolve/passB] WARNING: bb{bid} pred bb{pred_bid} has no snapshot, "
|
||||||
|
f"using empty dict",
|
||||||
|
file=sys.stderr
|
||||||
|
)
|
||||||
|
pred_snapshot = {}
|
||||||
|
|
||||||
|
# Cache the result (path compression)
|
||||||
|
resolved[bid] = dict(pred_snapshot)
|
||||||
|
if trace_vmap:
|
||||||
|
print(
|
||||||
|
f"[vmap/resolve/passB] bb{bid} resolved from bb{pred_bid}: "
|
||||||
|
f"{len(resolved[bid])} values",
|
||||||
|
file=sys.stderr
|
||||||
|
)
|
||||||
|
return resolved[bid]
|
||||||
|
|
||||||
|
# Unknown block (should not happen if Pass A worked correctly)
|
||||||
|
if strict_mode:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"[LLVM_PY/STRICT] Phase 131-14-B: block bb{bid} is neither normal "
|
||||||
|
f"nor jump-only (invalid state)"
|
||||||
|
)
|
||||||
|
|
||||||
|
if trace_vmap:
|
||||||
|
print(f"[vmap/resolve/passB] WARNING: bb{bid} unknown state, returning empty", file=sys.stderr)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# Resolve all jump-only blocks
|
||||||
|
for bid in sorted(jump_only.keys()):
|
||||||
|
snapshot = resolve(bid)
|
||||||
|
builder.block_end_values[bid] = snapshot
|
||||||
|
|
||||||
|
if trace_vmap:
|
||||||
|
print(
|
||||||
|
f"[vmap/resolve/passB] ✅ bb{bid} final snapshot: "
|
||||||
|
f"{len(snapshot)} values, keys={sorted(snapshot.keys())[:10]}",
|
||||||
|
file=sys.stderr
|
||||||
|
)
|
||||||
|
|
||||||
|
if trace_vmap:
|
||||||
|
print(f"[vmap/resolve/passB] Pass B complete: resolved {len(jump_only)} jump-only blocks", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
def lower_blocks(builder, func: ir.Function, block_by_id: Dict[int, Dict[str, Any]], order: List[int], loop_plan: Dict[str, Any] | None):
|
def lower_blocks(builder, func: ir.Function, block_by_id: Dict[int, Dict[str, Any]], order: List[int], loop_plan: Dict[str, Any] | None):
|
||||||
"""Lower blocks in multi-pass to ensure PHIs are always before terminators.
|
"""Lower blocks in multi-pass to ensure PHIs are always before terminators.
|
||||||
|
|
||||||
Phase 131-4: Multi-pass block lowering architecture
|
Phase 131-4: Multi-pass block lowering architecture
|
||||||
|
Phase 131-14-B: Two-pass snapshot resolution
|
||||||
- Pass A: Lower non-terminator instructions only (terminators deferred)
|
- Pass A: Lower non-terminator instructions only (terminators deferred)
|
||||||
|
- jump-only blocks: record metadata only, NO snapshot resolution
|
||||||
- Pass B: PHI finalization happens in function_lower.py
|
- Pass B: PHI finalization happens in function_lower.py
|
||||||
|
- resolve_jump_only_snapshots() called BEFORE PHI finalization
|
||||||
- Pass C: Lower terminators (happens after PHI finalization)
|
- Pass C: Lower terminators (happens after PHI finalization)
|
||||||
|
|
||||||
This ensures LLVM IR invariant: PHI nodes must be at block head before any
|
This ensures LLVM IR invariant: PHI nodes must be at block head before any
|
||||||
@ -278,16 +446,94 @@ def lower_blocks(builder, func: ir.Function, block_by_id: Dict[int, Dict[str, An
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
# End-of-block snapshot
|
# End-of-block snapshot
|
||||||
snap = dict(vmap_cur)
|
# Phase 131-14-B P0-1: Jump-only blocks - record metadata only (Pass A)
|
||||||
try:
|
strict_mode = os.environ.get('NYASH_LLVM_STRICT') == '1'
|
||||||
keys = sorted(list(snap.keys()))
|
trace_vmap = os.environ.get('NYASH_LLVM_TRACE_VMAP') == '1'
|
||||||
except Exception:
|
|
||||||
keys = list(snap.keys())
|
# Initialize jump_only_blocks dict if not exists
|
||||||
trace_phi_json({"phi": "snapshot", "block": int(bid), "keys": [int(k) for k in keys[:20]]})
|
if not hasattr(builder, '_jump_only_blocks'):
|
||||||
for vid in created_ids:
|
builder._jump_only_blocks = {}
|
||||||
if vid in vmap_cur:
|
|
||||||
builder.def_blocks.setdefault(vid, set()).add(block_data.get("id", 0))
|
is_jump_only = is_jump_only_block(block_data)
|
||||||
builder.block_end_values[bid] = snap
|
if trace_vmap:
|
||||||
|
print(
|
||||||
|
f"[vmap/snapshot] bb{bid} is_jump_only={is_jump_only} "
|
||||||
|
f"instructions={[i.get('op') for i in block_data.get('instructions', [])]}",
|
||||||
|
file=sys.stderr
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_jump_only:
|
||||||
|
# Phase 131-14-B: Jump-only blocks - record metadata, defer snapshot resolution to Pass B
|
||||||
|
preds_list = get_predecessors(bid, builder.preds)
|
||||||
|
|
||||||
|
if len(preds_list) == 0:
|
||||||
|
# No predecessors - error in STRICT mode
|
||||||
|
if strict_mode:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"[LLVM_PY/STRICT] Phase 131-14-B: jump-only block bb{bid} "
|
||||||
|
f"has no predecessors (orphan trampoline)"
|
||||||
|
)
|
||||||
|
# Non-STRICT: use current vmap_cur (defensive fallback)
|
||||||
|
snap = dict(vmap_cur)
|
||||||
|
if trace_vmap:
|
||||||
|
print(
|
||||||
|
f"[vmap/snapshot] bb{bid} jump-only with 0 preds: "
|
||||||
|
f"using vmap_cur keys={sorted(snap.keys())}",
|
||||||
|
file=sys.stderr
|
||||||
|
)
|
||||||
|
elif len(preds_list) == 1:
|
||||||
|
# Single predecessor - record metadata for Pass B resolution
|
||||||
|
pred_bid = preds_list[0]
|
||||||
|
builder._jump_only_blocks[bid] = pred_bid
|
||||||
|
|
||||||
|
# DO NOT create snapshot here - will be resolved in Pass B
|
||||||
|
# Set snap to None to indicate "skip storing in block_end_values"
|
||||||
|
snap = None
|
||||||
|
|
||||||
|
if trace_vmap:
|
||||||
|
print(
|
||||||
|
f"[vmap/snapshot/passA] bb{bid} jump-only: recorded pred=bb{pred_bid}, "
|
||||||
|
f"snapshot deferred to Pass B",
|
||||||
|
file=sys.stderr
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Multiple predecessors - error in STRICT mode (merge rules not yet defined)
|
||||||
|
if strict_mode:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"[LLVM_PY/STRICT] Phase 131-14-B: jump-only block bb{bid} "
|
||||||
|
f"has multiple predecessors: {preds_list} "
|
||||||
|
f"(merge propagation not implemented)"
|
||||||
|
)
|
||||||
|
# Non-STRICT: use current vmap_cur (defensive fallback)
|
||||||
|
snap = dict(vmap_cur)
|
||||||
|
if trace_vmap:
|
||||||
|
print(
|
||||||
|
f"[vmap/snapshot] bb{bid} jump-only with multiple preds {preds_list}: "
|
||||||
|
f"using vmap_cur keys={sorted(snap.keys())}",
|
||||||
|
file=sys.stderr
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Normal block: use its own vmap_cur
|
||||||
|
snap = dict(vmap_cur)
|
||||||
|
|
||||||
|
# Phase 131-14-B: Only store snapshot if not deferred (snap is not None)
|
||||||
|
if snap is not None:
|
||||||
|
try:
|
||||||
|
keys = sorted(list(snap.keys()))
|
||||||
|
except Exception:
|
||||||
|
keys = list(snap.keys())
|
||||||
|
trace_phi_json({"phi": "snapshot", "block": int(bid), "keys": [int(k) for k in keys[:20]]})
|
||||||
|
for vid in created_ids:
|
||||||
|
if vid in vmap_cur:
|
||||||
|
builder.def_blocks.setdefault(vid, set()).add(block_data.get("id", 0))
|
||||||
|
builder.block_end_values[bid] = snap
|
||||||
|
else:
|
||||||
|
# Jump-only block with deferred snapshot - don't store yet
|
||||||
|
if trace_vmap:
|
||||||
|
print(
|
||||||
|
f"[vmap/snapshot/passA] bb{bid} snapshot deferred (not stored in block_end_values)",
|
||||||
|
file=sys.stderr
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
delattr(builder, '_current_vmap')
|
delattr(builder, '_current_vmap')
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@ -279,6 +279,10 @@ def lower_function(builder, func_data: Dict[str, Any]):
|
|||||||
from builders.block_lower import lower_blocks as _lower_blocks
|
from builders.block_lower import lower_blocks as _lower_blocks
|
||||||
_lower_blocks(builder, func, block_by_id, order, loop_plan)
|
_lower_blocks(builder, func, block_by_id, order, loop_plan)
|
||||||
|
|
||||||
|
# Phase 131-14-B Pass B: Resolve jump-only block snapshots (BEFORE PHI finalization)
|
||||||
|
from builders.block_lower import resolve_jump_only_snapshots as _resolve_jump_only_snapshots
|
||||||
|
_resolve_jump_only_snapshots(builder, block_by_id)
|
||||||
|
|
||||||
# Optional: capture lowering ctx for downstream helpers
|
# Optional: capture lowering ctx for downstream helpers
|
||||||
try:
|
try:
|
||||||
builder.ctx = dict(
|
builder.ctx = dict(
|
||||||
@ -300,7 +304,7 @@ def lower_function(builder, func_data: Dict[str, Any]):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Phase 131-4 Pass B: Finalize PHIs (wires incoming edges)
|
# Phase 131-4 Pass B (now Pass B2): Finalize PHIs (wires incoming edges)
|
||||||
_finalize_phis(builder)
|
_finalize_phis(builder)
|
||||||
|
|
||||||
# Phase 131-4 Pass C: Lower deferred terminators (after PHIs are placed)
|
# Phase 131-4 Pass C: Lower deferred terminators (after PHIs are placed)
|
||||||
|
|||||||
@ -202,9 +202,8 @@ def wire_incomings(builder, block_id: int, dst_vid: int, incoming: List[Tuple[in
|
|||||||
if original_vs != vs:
|
if original_vs != vs:
|
||||||
trace({"phi": "wire_replaced_src", "original": original_vs, "replaced": vs})
|
trace({"phi": "wire_replaced_src", "original": original_vs, "replaced": vs})
|
||||||
try:
|
try:
|
||||||
val = builder.resolver._value_at_end_i64(
|
# P0-4: Use resolve_incoming for PHI incoming values
|
||||||
vs, pred_match, builder.preds, builder.block_end_values, builder.vmap, builder.bb_map
|
val = builder.resolver.resolve_incoming(pred_match, vs)
|
||||||
)
|
|
||||||
trace({"phi": "wire_resolved", "vs": vs, "pred": pred_match, "val_type": type(val).__name__})
|
trace({"phi": "wire_resolved", "vs": vs, "pred": pred_match, "val_type": type(val).__name__})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
trace({"phi": "wire_resolve_fail", "vs": vs, "pred": pred_match, "error": str(e)})
|
trace({"phi": "wire_resolve_fail", "vs": vs, "pred": pred_match, "error": str(e)})
|
||||||
|
|||||||
@ -3,7 +3,7 @@ Resolver API (Python version)
|
|||||||
Based on src/backend/llvm/compiler/codegen/instructions/resolver.rs
|
Based on src/backend/llvm/compiler/codegen/instructions/resolver.rs
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Dict, Optional, Any, Tuple
|
from typing import Dict, Optional, Any, Tuple, Set
|
||||||
import os
|
import os
|
||||||
from trace import phi as trace_phi
|
from trace import phi as trace_phi
|
||||||
from trace import values as trace_values
|
from trace import values as trace_values
|
||||||
@ -63,6 +63,8 @@ class Resolver:
|
|||||||
self.block_phi_incomings = {}
|
self.block_phi_incomings = {}
|
||||||
# P0-1: SSOT for end-of-block values (snapshots)
|
# P0-1: SSOT for end-of-block values (snapshots)
|
||||||
self.block_end_values = {}
|
self.block_end_values = {}
|
||||||
|
# P0-3: Circular reference detection (hang prevention)
|
||||||
|
self._visited: Set[Tuple[int, int]] = set()
|
||||||
|
|
||||||
def mark_string(self, value_id: int) -> None:
|
def mark_string(self, value_id: int) -> None:
|
||||||
try:
|
try:
|
||||||
@ -75,7 +77,81 @@ class Resolver:
|
|||||||
return int(value_id) in self.string_ids
|
return int(value_id) in self.string_ids
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _check_cycle(self, block_id: int, value_id: int):
|
||||||
|
"""P0-3: Circular reference detection (hang prevention)"""
|
||||||
|
key = (block_id, value_id)
|
||||||
|
if key in self._visited:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"[LLVM_PY] Circular reference detected: bb{block_id} v{value_id}"
|
||||||
|
)
|
||||||
|
self._visited.add(key)
|
||||||
|
|
||||||
|
def resolve_cur(self, block_id: int, value_id: int, vmap_cur: Dict[int, ir.Value]) -> ir.Value:
|
||||||
|
"""P0-1: Same-block instruction lowering (vmap_cur as primary source)
|
||||||
|
|
||||||
|
Used for lowering instructions within the same basic block where the value
|
||||||
|
is defined and used. Checks vmap_cur first, then applies fail-fast checks.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
block_id: Current basic block ID
|
||||||
|
value_id: Value ID to resolve
|
||||||
|
vmap_cur: Current block's value map (def->use tracking)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
LLVM IR value (i64)
|
||||||
|
"""
|
||||||
|
# 1. Check vmap_cur first
|
||||||
|
val = vmap_cur.get(value_id)
|
||||||
|
if val is not None:
|
||||||
|
return val
|
||||||
|
|
||||||
|
# 2. Fail-Fast: def_blocks has bb but vmap_cur doesn't → lowerer bug
|
||||||
|
if value_id in self.def_blocks and block_id in self.def_blocks[value_id]:
|
||||||
|
if os.environ.get('NYASH_LLVM_STRICT') == '1':
|
||||||
|
raise RuntimeError(
|
||||||
|
f"[LLVM_PY/STRICT] resolve_cur: v{value_id} defined in bb{block_id} "
|
||||||
|
f"but not in vmap_cur. Lowerer order bug?"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 3. vmap_cur miss → undefined error
|
||||||
|
if os.environ.get('NYASH_LLVM_STRICT') == '1':
|
||||||
|
raise RuntimeError(
|
||||||
|
f"[LLVM_PY/STRICT] resolve_cur: v{value_id} not found in bb{block_id} vmap_cur. "
|
||||||
|
f"Available: {sorted(vmap_cur.keys())}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Non-STRICT: fallback to 0
|
||||||
|
return ir.Constant(ir.IntType(64), 0)
|
||||||
|
|
||||||
|
def resolve_incoming(self, pred_block_id: int, value_id: int) -> ir.Value:
|
||||||
|
"""P0-2: PHI incoming resolution (snapshot-only reference)
|
||||||
|
|
||||||
|
Used for resolving PHI incoming values from predecessor blocks.
|
||||||
|
Only looks at block_end_values snapshot, never vmap_cur.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pred_block_id: Predecessor block ID
|
||||||
|
value_id: Value ID to resolve from predecessor
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
LLVM IR value (i64)
|
||||||
|
"""
|
||||||
|
snapshot = self.block_end_values.get(pred_block_id, {})
|
||||||
|
val = snapshot.get(value_id)
|
||||||
|
if val is not None:
|
||||||
|
return val
|
||||||
|
|
||||||
|
# Fail-Fast: snapshot miss → structural bug
|
||||||
|
if os.environ.get('NYASH_LLVM_STRICT') == '1':
|
||||||
|
raise RuntimeError(
|
||||||
|
f"[LLVM_PY/STRICT] resolve_incoming: v{value_id} not in bb{pred_block_id} snapshot. "
|
||||||
|
f"Available: {sorted(snapshot.keys())}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Non-STRICT: fallback to 0
|
||||||
|
return ir.Constant(ir.IntType(64), 0)
|
||||||
|
|
||||||
def resolve_i64(
|
def resolve_i64(
|
||||||
self,
|
self,
|
||||||
value_id: int,
|
value_id: int,
|
||||||
|
|||||||
@ -190,43 +190,11 @@ pub fn emit_mir_json_for_harness(
|
|||||||
for bid in ids {
|
for bid in ids {
|
||||||
if let Some(bb) = f.blocks.get(&bid) {
|
if let Some(bb) = f.blocks.get(&bid) {
|
||||||
let mut insts = Vec::new();
|
let mut insts = Vec::new();
|
||||||
// Pre-scan: collect values defined anywhere in this block (to delay use-before-def copies)
|
// Phase 131-13: Emit all instructions in MIR order (SSOT principle)
|
||||||
let mut block_defines: std::collections::HashSet<u32> =
|
// No reordering except PHI consolidation at block start (LLVM constraint)
|
||||||
std::collections::HashSet::new();
|
|
||||||
|
// Step 1: Emit all PHI instructions first (LLVM requirement)
|
||||||
for inst in &bb.instructions {
|
for inst in &bb.instructions {
|
||||||
match inst {
|
|
||||||
I::UnaryOp { dst, .. }
|
|
||||||
| I::Const { dst, .. }
|
|
||||||
| I::BinOp { dst, .. }
|
|
||||||
| I::Compare { dst, .. }
|
|
||||||
| I::Call { dst: Some(dst), .. }
|
|
||||||
| I::ExternCall { dst: Some(dst), .. }
|
|
||||||
| I::BoxCall { dst: Some(dst), .. }
|
|
||||||
| I::NewBox { dst, .. }
|
|
||||||
| I::Phi { dst, .. } => {
|
|
||||||
block_defines.insert(dst.as_u32());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Track which values have been emitted (to order copies after their sources)
|
|
||||||
let mut emitted_defs: std::collections::HashSet<u32> =
|
|
||||||
std::collections::HashSet::new();
|
|
||||||
// PHI first(オプション)
|
|
||||||
for inst in &bb.instructions {
|
|
||||||
if let I::Copy { dst, src } = inst {
|
|
||||||
// For copies whose source will be defined later in this block, delay emission
|
|
||||||
let s = src.as_u32();
|
|
||||||
if block_defines.contains(&s) && !emitted_defs.contains(&s) {
|
|
||||||
// delayed; will be emitted after non-PHI pass
|
|
||||||
} else {
|
|
||||||
insts.push(
|
|
||||||
json!({"op":"copy","dst": dst.as_u32(), "src": src.as_u32()}),
|
|
||||||
);
|
|
||||||
emitted_defs.insert(dst.as_u32());
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let I::Phi { dst, inputs, .. } = inst {
|
if let I::Phi { dst, inputs, .. } = inst {
|
||||||
let incoming: Vec<_> = inputs
|
let incoming: Vec<_> = inputs
|
||||||
.iter()
|
.iter()
|
||||||
@ -250,20 +218,16 @@ pub fn emit_mir_json_for_harness(
|
|||||||
insts.push(phi_inst);
|
insts.push(phi_inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Non-PHI
|
|
||||||
// Non-PHI
|
// Step 2: Emit all non-PHI instructions in MIR order (no reordering!)
|
||||||
let mut delayed_copies: Vec<(u32, u32)> = Vec::new();
|
|
||||||
for inst in &bb.instructions {
|
for inst in &bb.instructions {
|
||||||
match inst {
|
match inst {
|
||||||
|
I::Phi { .. } => {
|
||||||
|
// Already emitted in step 1
|
||||||
|
continue;
|
||||||
|
}
|
||||||
I::Copy { dst, src } => {
|
I::Copy { dst, src } => {
|
||||||
let d = dst.as_u32();
|
insts.push(json!({"op":"copy","dst": dst.as_u32(), "src": src.as_u32()}));
|
||||||
let s = src.as_u32();
|
|
||||||
if block_defines.contains(&s) && !emitted_defs.contains(&s) {
|
|
||||||
delayed_copies.push((d, s));
|
|
||||||
} else {
|
|
||||||
insts.push(json!({"op":"copy","dst": d, "src": s}));
|
|
||||||
emitted_defs.insert(d);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
I::UnaryOp { dst, op, operand } => {
|
I::UnaryOp { dst, op, operand } => {
|
||||||
let kind = match op {
|
let kind = match op {
|
||||||
@ -322,7 +286,6 @@ pub fn emit_mir_json_for_harness(
|
|||||||
"dst": dst.as_u32(),
|
"dst": dst.as_u32(),
|
||||||
"target_type": ty_s,
|
"target_type": ty_s,
|
||||||
}));
|
}));
|
||||||
emitted_defs.insert(dst.as_u32());
|
|
||||||
}
|
}
|
||||||
I::BinOp { dst, op, lhs, rhs } => {
|
I::BinOp { dst, op, lhs, rhs } => {
|
||||||
let op_s = match op {
|
let op_s = match op {
|
||||||
@ -456,7 +419,6 @@ pub fn emit_mir_json_for_harness(
|
|||||||
}
|
}
|
||||||
insts.push(obj);
|
insts.push(obj);
|
||||||
if let Some(d) = dst.map(|v| v.as_u32()) {
|
if let Some(d) = dst.map(|v| v.as_u32()) {
|
||||||
emitted_defs.insert(d);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -531,7 +493,6 @@ pub fn emit_mir_json_for_harness(
|
|||||||
}
|
}
|
||||||
insts.push(obj);
|
insts.push(obj);
|
||||||
if let Some(d) = dst.map(|v| v.as_u32()) {
|
if let Some(d) = dst.map(|v| v.as_u32()) {
|
||||||
emitted_defs.insert(d);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
I::NewBox {
|
I::NewBox {
|
||||||
@ -541,7 +502,6 @@ pub fn emit_mir_json_for_harness(
|
|||||||
} => {
|
} => {
|
||||||
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
|
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
|
||||||
insts.push(json!({"op":"newbox","type": box_type, "args": args_a, "dst": dst.as_u32()}));
|
insts.push(json!({"op":"newbox","type": box_type, "args": args_a, "dst": dst.as_u32()}));
|
||||||
emitted_defs.insert(dst.as_u32());
|
|
||||||
}
|
}
|
||||||
I::Branch {
|
I::Branch {
|
||||||
condition,
|
condition,
|
||||||
@ -559,10 +519,7 @@ pub fn emit_mir_json_for_harness(
|
|||||||
_ => { /* skip non-essential ops for initial harness */ }
|
_ => { /* skip non-essential ops for initial harness */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Emit delayed copies now (sources should be available)
|
// Phase 131-13: Terminator emitted inline (no delayed copies)
|
||||||
for (d, s) in delayed_copies {
|
|
||||||
insts.push(json!({"op":"copy","dst": d, "src": s}));
|
|
||||||
}
|
|
||||||
if let Some(term) = &bb.terminator {
|
if let Some(term) = &bb.terminator {
|
||||||
match term {
|
match term {
|
||||||
I::Return { value } => insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())})),
|
I::Return { value } => insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())})),
|
||||||
@ -650,27 +607,10 @@ pub fn emit_mir_json_for_harness_bin(
|
|||||||
for bid in ids {
|
for bid in ids {
|
||||||
if let Some(bb) = f.blocks.get(&bid) {
|
if let Some(bb) = f.blocks.get(&bid) {
|
||||||
let mut insts = Vec::new();
|
let mut insts = Vec::new();
|
||||||
// Pre-scan to collect values defined in this block
|
// Phase 131-13: Emit all instructions in MIR order (SSOT principle)
|
||||||
let mut block_defines: std::collections::HashSet<u32> =
|
// No reordering except PHI consolidation at block start (LLVM constraint)
|
||||||
std::collections::HashSet::new();
|
|
||||||
for inst in &bb.instructions {
|
// Step 1: Emit all PHI instructions first (LLVM requirement)
|
||||||
match inst {
|
|
||||||
I::Copy { dst, .. }
|
|
||||||
| I::Const { dst, .. }
|
|
||||||
| I::BinOp { dst, .. }
|
|
||||||
| I::Compare { dst, .. }
|
|
||||||
| I::Call { dst: Some(dst), .. }
|
|
||||||
| I::ExternCall { dst: Some(dst), .. }
|
|
||||||
| I::BoxCall { dst: Some(dst), .. }
|
|
||||||
| I::NewBox { dst, .. }
|
|
||||||
| I::Phi { dst, .. } => {
|
|
||||||
block_defines.insert(dst.as_u32());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut emitted_defs: std::collections::HashSet<u32> =
|
|
||||||
std::collections::HashSet::new();
|
|
||||||
for inst in &bb.instructions {
|
for inst in &bb.instructions {
|
||||||
if let I::Phi { dst, inputs, .. } = inst {
|
if let I::Phi { dst, inputs, .. } = inst {
|
||||||
let incoming: Vec<_> = inputs
|
let incoming: Vec<_> = inputs
|
||||||
@ -693,21 +633,18 @@ pub fn emit_mir_json_for_harness_bin(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
insts.push(phi_inst);
|
insts.push(phi_inst);
|
||||||
emitted_defs.insert(dst.as_u32());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut delayed_copies: Vec<(u32, u32)> = Vec::new();
|
|
||||||
|
// Step 2: Emit all non-PHI instructions in MIR order (no reordering!)
|
||||||
for inst in &bb.instructions {
|
for inst in &bb.instructions {
|
||||||
match inst {
|
match inst {
|
||||||
|
I::Phi { .. } => {
|
||||||
|
// Already emitted in step 1
|
||||||
|
continue;
|
||||||
|
}
|
||||||
I::Copy { dst, src } => {
|
I::Copy { dst, src } => {
|
||||||
let d = dst.as_u32();
|
insts.push(json!({"op":"copy","dst": dst.as_u32(), "src": src.as_u32()}));
|
||||||
let s = src.as_u32();
|
|
||||||
if block_defines.contains(&s) && !emitted_defs.contains(&s) {
|
|
||||||
delayed_copies.push((d, s));
|
|
||||||
} else {
|
|
||||||
insts.push(json!({"op":"copy","dst": d, "src": s}));
|
|
||||||
emitted_defs.insert(d);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
I::Const { dst, value } => {
|
I::Const { dst, value } => {
|
||||||
match value {
|
match value {
|
||||||
@ -734,7 +671,6 @@ pub fn emit_mir_json_for_harness_bin(
|
|||||||
insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "void", "value": 0}}));
|
insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "void", "value": 0}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emitted_defs.insert(dst.as_u32());
|
|
||||||
}
|
}
|
||||||
I::BinOp { dst, op, lhs, rhs } => {
|
I::BinOp { dst, op, lhs, rhs } => {
|
||||||
let op_s = match op {
|
let op_s = match op {
|
||||||
@ -772,7 +708,6 @@ pub fn emit_mir_json_for_harness_bin(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
insts.push(obj);
|
insts.push(obj);
|
||||||
emitted_defs.insert(dst.as_u32());
|
|
||||||
}
|
}
|
||||||
I::Compare { dst, op, lhs, rhs } => {
|
I::Compare { dst, op, lhs, rhs } => {
|
||||||
let op_s = match op {
|
let op_s = match op {
|
||||||
@ -784,7 +719,6 @@ pub fn emit_mir_json_for_harness_bin(
|
|||||||
C::Ge => ">=",
|
C::Ge => ">=",
|
||||||
};
|
};
|
||||||
insts.push(json!({"op":"compare","operation": op_s, "lhs": lhs.as_u32(), "rhs": rhs.as_u32(), "dst": dst.as_u32()}));
|
insts.push(json!({"op":"compare","operation": op_s, "lhs": lhs.as_u32(), "rhs": rhs.as_u32(), "dst": dst.as_u32()}));
|
||||||
emitted_defs.insert(dst.as_u32());
|
|
||||||
}
|
}
|
||||||
I::Call {
|
I::Call {
|
||||||
dst,
|
dst,
|
||||||
@ -853,7 +787,6 @@ pub fn emit_mir_json_for_harness_bin(
|
|||||||
}
|
}
|
||||||
insts.push(obj);
|
insts.push(obj);
|
||||||
if let Some(d) = dst.map(|v| v.as_u32()) {
|
if let Some(d) = dst.map(|v| v.as_u32()) {
|
||||||
emitted_defs.insert(d);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -889,7 +822,6 @@ pub fn emit_mir_json_for_harness_bin(
|
|||||||
}
|
}
|
||||||
insts.push(obj);
|
insts.push(obj);
|
||||||
if let Some(d) = dst.map(|v| v.as_u32()) {
|
if let Some(d) = dst.map(|v| v.as_u32()) {
|
||||||
emitted_defs.insert(d);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
I::BoxCall {
|
I::BoxCall {
|
||||||
@ -921,7 +853,6 @@ pub fn emit_mir_json_for_harness_bin(
|
|||||||
}
|
}
|
||||||
insts.push(obj);
|
insts.push(obj);
|
||||||
if let Some(d) = dst.map(|v| v.as_u32()) {
|
if let Some(d) = dst.map(|v| v.as_u32()) {
|
||||||
emitted_defs.insert(d);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
I::NewBox {
|
I::NewBox {
|
||||||
@ -931,7 +862,6 @@ pub fn emit_mir_json_for_harness_bin(
|
|||||||
} => {
|
} => {
|
||||||
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
|
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
|
||||||
insts.push(json!({"op":"newbox","type": box_type, "args": args_a, "dst": dst.as_u32()}));
|
insts.push(json!({"op":"newbox","type": box_type, "args": args_a, "dst": dst.as_u32()}));
|
||||||
emitted_defs.insert(dst.as_u32());
|
|
||||||
}
|
}
|
||||||
I::Branch {
|
I::Branch {
|
||||||
condition,
|
condition,
|
||||||
@ -949,10 +879,7 @@ pub fn emit_mir_json_for_harness_bin(
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Append delayed copies after their sources
|
// Phase 131-13: Terminator emitted inline (no delayed copies)
|
||||||
for (d, s) in delayed_copies {
|
|
||||||
insts.push(json!({"op":"copy","dst": d, "src": s}));
|
|
||||||
}
|
|
||||||
if let Some(term) = &bb.terminator {
|
if let Some(term) = &bb.terminator {
|
||||||
match term {
|
match term {
|
||||||
I::Return { value } => insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())})),
|
I::Return { value } => insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())})),
|
||||||
|
|||||||
Reference in New Issue
Block a user