refactor(joinir): Phase 27.9 - Modular separation of join_ir.rs into directory structure
Phase 27.9 で join_ir.rs (~1,336行) を以下のモジュール構造に分離:
## 新規ディレクトリ構造:
```
src/mir/join_ir/
├── mod.rs # 型定義・共通ユーティリティ (~330行)
└── lowering/
├── mod.rs # lowering インターフェース
├── min_loop.rs # lower_min_loop_to_joinir (~140行)
├── skip_ws.rs # skip_ws lowering 3関数 (~390行)
└── funcscanner_trim.rs # trim lowering (~480行)
```
## 技術的変更:
- **型定義統一**: JoinFuncId, JoinInst, JoinModule 等を mod.rs に集約
- **lowering 分離**: 3つの lowering 関数を個別モジュールに移動
- **後方互換性**: pub use で lowering 関数を re-export(既存コード影響なし)
- **削除**: src/mir/join_ir.rs (旧単一ファイル)
## テスト結果:
- **385 passed** (+1 from 384)
- **9 failed** (-1 from 10)
- **ビルド成功**: 0 errors, 18 warnings (変化なし)
## 効果:
- **保守性向上**: 1,336行 → 4ファイル(各300-500行)で可読性向上
- **モジュール境界明確化**: 型定義 vs lowering 実装の責務分離
- **将来の拡張容易**: 新 lowering 関数追加が簡単に
Phase 27.8 で実装した MIR 自動解析 lowering の基盤整備完了。
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -12,7 +12,7 @@
|
|||||||
- **25.x**: Stage0/Stage1/Stage‑B / Selfhost ラインのブートストラップと LoopForm v2 / LoopSSA v2 まわりの整備。
|
- **25.x**: Stage0/Stage1/Stage‑B / Selfhost ラインのブートストラップと LoopForm v2 / LoopSSA v2 まわりの整備。
|
||||||
- **25.1 系**: Stage‑B / Stage‑1 / selfhost 向けに、Rust MIR / LoopForm v2 / LoopSSA v2 を段階的に整える長期ライン。
|
- **25.1 系**: Stage‑B / Stage‑1 / selfhost 向けに、Rust MIR / LoopForm v2 / LoopSSA v2 を段階的に整える長期ライン。
|
||||||
- **26-F / 26-G**: Exit PHI / ExitLiveness 用の 4箱構成(LoopVarClassBox / LoopExitLivenessBox / BodyLocalPhiBuilder / PhiInvariantsBox)と MirScanExitLiveness の準備。
|
- **26-F / 26-G**: Exit PHI / ExitLiveness 用の 4箱構成(LoopVarClassBox / LoopExitLivenessBox / BodyLocalPhiBuilder / PhiInvariantsBox)と MirScanExitLiveness の準備。
|
||||||
- **26-H / 27.x(New)**: JoinIR 設計+ミニ実験フェーズ → minimal/skip_ws/FuncScanner.trim までを対象に、制御構造を関数呼び出しに正規化する IR とランナーを段階的に整備中(27.4 で Header φ を LoopHeaderShape 化、27.5 で Exit φ の意味を LoopExitShape として固定済み。次は 27.6 で ExitPhiBuilder 縮退に着手予定)。
|
- **26-H / 27.x(New)**: JoinIR 設計+ミニ実験フェーズ → minimal/skip_ws/FuncScanner.trim までを対象に、制御構造を関数呼び出しに正規化する IR とランナーを段階的に整備中(27.4 で Header φ を LoopHeaderShape 化、27.5 で Exit φ の意味を LoopExitShape として固定済み。27.6-1/2/3 で ExitPhiBuilder 側にトグル付きバイパスを入れて A/B 観測まで完了、seal_phis と Header φ バイパスの整合性は別フェーズで refinement 予定)。
|
||||||
- Rust 側:
|
- Rust 側:
|
||||||
- LoopForm v2 + ControlForm + Conservative PHI は、代表テスト(Stage‑1 UsingResolver / Stage‑B 最小ループ)ではほぼ安定。
|
- LoopForm v2 + ControlForm + Conservative PHI は、代表テスト(Stage‑1 UsingResolver / Stage‑B 最小ループ)ではほぼ安定。
|
||||||
- 静的メソッド呼び出し規約と `continue` 絡みの PHI は 25.1m までで根治済み。
|
- 静的メソッド呼び出し規約と `continue` 絡みの PHI は 25.1m までで根治済み。
|
||||||
|
|||||||
@ -41,6 +41,7 @@ SSOT(Single Source of Truth)
|
|||||||
- Bridge 側に `LoopPhiOps` 実装を追加し、`prepare/seal/exit` を直接呼ぶ。
|
- Bridge 側に `LoopPhiOps` 実装を追加し、`prepare/seal/exit` を直接呼ぶ。
|
||||||
- ループ形状の生成をユーティリティ化(builder/bridge 双方から共通呼び出し)。
|
- ループ形状の生成をユーティリティ化(builder/bridge 双方から共通呼び出し)。
|
||||||
- ExitLivenessProvider は 26-G 以降で MIR 命令列スキャン版に差し替える予定(現状の MirScanExitLiveness は header/exit_snapshots の union 近似)。
|
- ExitLivenessProvider は 26-G 以降で MIR 命令列スキャン版に差し替える予定(現状の MirScanExitLiveness は header/exit_snapshots の union 近似)。
|
||||||
|
- Header/Exit φ については、Phase 26-H / 27.x で導入した JoinIR(関数正規化IR)側の `LoopHeaderShape` / `LoopExitShape` と `loop_step` / `k_exit` 引数に最終的に吸収し、Rust 側では LoopVarClass / LoopExitLiveness / BodyLocalPhi / PhiInvariants による前処理+検証だけを残す方針。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
1335
src/mir/join_ir.rs
1335
src/mir/join_ir.rs
File diff suppressed because it is too large
Load Diff
497
src/mir/join_ir/lowering/funcscanner_trim.rs
Normal file
497
src/mir/join_ir/lowering/funcscanner_trim.rs
Normal file
@ -0,0 +1,497 @@
|
|||||||
|
//! Phase 27.1: FuncScannerBox.trim/1 の MIR → JoinIR 変換
|
||||||
|
//!
|
||||||
|
//! 目的: lang/src/compiler/entry/func_scanner.hako の trim メソッドを JoinIR に変換
|
||||||
|
//!
|
||||||
|
//! 期待される変換:
|
||||||
|
//! ```text
|
||||||
|
//! // MIR (元):
|
||||||
|
//! method trim(s) {
|
||||||
|
//! local e = n
|
||||||
|
//! loop(e > b) {
|
||||||
|
//! local ch = str.substring(e - 1, e)
|
||||||
|
//! if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" {
|
||||||
|
//! e = e - 1
|
||||||
|
//! } else {
|
||||||
|
//! break
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! return substring(b, e)
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // JoinIR (変換後):
|
||||||
|
//! fn trim_main(s_param, k_exit) {
|
||||||
|
//! str = "" + s_param
|
||||||
|
//! n = str.length()
|
||||||
|
//! b = skip_whitespace(str, 0)
|
||||||
|
//! e_init = n
|
||||||
|
//! loop_step(str, b, e_init, k_exit)
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn loop_step(str, b, e, k_exit) {
|
||||||
|
//! cond = (e > b)
|
||||||
|
//! if cond {
|
||||||
|
//! ch = str.substring(e - 1, e)
|
||||||
|
//! is_space = (ch == " " || ch == "\t" || ch == "\n" || ch == "\r")
|
||||||
|
//! if is_space {
|
||||||
|
//! e_next = e - 1
|
||||||
|
//! loop_step(str, b, e_next, k_exit)
|
||||||
|
//! } else {
|
||||||
|
//! k_exit(e)
|
||||||
|
//! }
|
||||||
|
//! } else {
|
||||||
|
//! k_exit(e)
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use crate::mir::ValueId;
|
||||||
|
use crate::mir::join_ir::{
|
||||||
|
BinOpKind, CompareOp, ConstValue, JoinContId, JoinFuncId, JoinFunction, JoinInst, JoinModule,
|
||||||
|
LoopExitShape, LoopHeaderShape, MirLikeInst,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn lower_funcscanner_trim_to_joinir(module: &crate::mir::MirModule) -> Option<JoinModule> {
|
||||||
|
// Step 1: "FuncScannerBox.trim/1" を探す
|
||||||
|
let target_func = module.functions.get("FuncScannerBox.trim/1")?;
|
||||||
|
|
||||||
|
eprintln!("[joinir/trim] Found FuncScannerBox.trim/1");
|
||||||
|
eprintln!("[joinir/trim] MIR blocks: {}", target_func.blocks.len());
|
||||||
|
|
||||||
|
let mut join_module = JoinModule::new();
|
||||||
|
|
||||||
|
// trim_main 関数: 前処理 + 先頭/末尾の空白を除去
|
||||||
|
let trim_main_id = JoinFuncId::new(0);
|
||||||
|
let s_param = ValueId(5000);
|
||||||
|
let mut trim_main_func = JoinFunction::new(trim_main_id, "trim_main".to_string(), vec![s_param]);
|
||||||
|
|
||||||
|
let str_val = ValueId(5001);
|
||||||
|
let n_val = ValueId(5002);
|
||||||
|
let b_val = ValueId(5003);
|
||||||
|
let e_init = ValueId(5004);
|
||||||
|
let const_empty = ValueId(5005);
|
||||||
|
let const_zero = ValueId(5006);
|
||||||
|
|
||||||
|
// str = "" + s_param (文字列化)
|
||||||
|
trim_main_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_empty,
|
||||||
|
value: ConstValue::String("".to_string()),
|
||||||
|
}));
|
||||||
|
trim_main_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: str_val,
|
||||||
|
lhs: const_empty,
|
||||||
|
rhs: s_param,
|
||||||
|
op: BinOpKind::Add,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// n = str.length()
|
||||||
|
trim_main_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||||
|
dst: Some(n_val),
|
||||||
|
box_name: "StringBox".to_string(),
|
||||||
|
method: "length".to_string(),
|
||||||
|
args: vec![str_val],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// const 0
|
||||||
|
trim_main_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_zero,
|
||||||
|
value: ConstValue::Integer(0),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// b = skip_leading_whitespace(str, 0, n)
|
||||||
|
let skip_leading_id = JoinFuncId::new(2);
|
||||||
|
trim_main_func.body.push(JoinInst::Call {
|
||||||
|
func: skip_leading_id,
|
||||||
|
args: vec![str_val, const_zero, n_val],
|
||||||
|
k_next: None,
|
||||||
|
dst: Some(b_val),
|
||||||
|
});
|
||||||
|
|
||||||
|
// e_init = n (コピー)
|
||||||
|
trim_main_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: e_init,
|
||||||
|
op: BinOpKind::Add,
|
||||||
|
lhs: n_val,
|
||||||
|
rhs: const_zero,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// loop_step(str, b, e_init) -> 戻り値をそのまま返す
|
||||||
|
let loop_step_id = JoinFuncId::new(1);
|
||||||
|
trim_main_func.body.push(JoinInst::Call {
|
||||||
|
func: loop_step_id,
|
||||||
|
args: vec![str_val, b_val, e_init],
|
||||||
|
k_next: None,
|
||||||
|
dst: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
join_module.entry = Some(trim_main_id);
|
||||||
|
join_module.add_function(trim_main_func);
|
||||||
|
|
||||||
|
// Phase 27.4-A: trim loop_step の Pinned/Carrier 構造を明示
|
||||||
|
// trim ループの場合:
|
||||||
|
// - Pinned: str (文字列), b (開始位置) - ループ中で不変
|
||||||
|
// - Carrier: e (終了位置) - ループで後ろから前へ更新される
|
||||||
|
let str_loop = ValueId(6000); // Pinned
|
||||||
|
let b_loop = ValueId(6001); // Pinned
|
||||||
|
let e_loop = ValueId(6002); // Carrier
|
||||||
|
|
||||||
|
let _header_shape = LoopHeaderShape::new_manual(
|
||||||
|
vec![str_loop, b_loop], // Pinned: str, b
|
||||||
|
vec![e_loop], // Carrier: e
|
||||||
|
);
|
||||||
|
// 将来: to_loop_step_params() で [str, b, e] (pinned..., carriers...) を生成する設計。
|
||||||
|
// 現在は既存 JoinIR テストとの互換性のため、手動で [str, b, e] の順を維持している。
|
||||||
|
|
||||||
|
// loop_step 関数: 末尾の空白を削り、最終的に substring(b, e) を返す
|
||||||
|
let mut loop_step_func = JoinFunction::new(
|
||||||
|
loop_step_id,
|
||||||
|
"loop_step".to_string(),
|
||||||
|
vec![str_loop, b_loop, e_loop],
|
||||||
|
);
|
||||||
|
|
||||||
|
// cond = (e > b)
|
||||||
|
let cond = ValueId(6003);
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cond,
|
||||||
|
lhs: e_loop,
|
||||||
|
rhs: b_loop,
|
||||||
|
op: CompareOp::Gt,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// bool false (共通)
|
||||||
|
let bool_false = ValueId(6019);
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: bool_false,
|
||||||
|
value: ConstValue::Bool(false),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// trimmed_base = str.substring(b, e)
|
||||||
|
let trimmed_base = ValueId(6004);
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||||
|
dst: Some(trimmed_base),
|
||||||
|
box_name: "StringBox".to_string(),
|
||||||
|
method: "substring".to_string(),
|
||||||
|
args: vec![str_loop, b_loop, e_loop],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// cond_is_false = (cond == false)
|
||||||
|
let cond_is_false = ValueId(6020);
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cond_is_false,
|
||||||
|
lhs: cond,
|
||||||
|
rhs: bool_false,
|
||||||
|
op: CompareOp::Eq,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Phase 27.5: Exit φ の意味を LoopExitShape で明示(Option A)
|
||||||
|
// trim のループ脱出時は e の値で substring(b, e) を計算済み
|
||||||
|
let _exit_shape_trim = LoopExitShape::new_manual(vec![e_loop]); // exit_args = [e] (Option A)
|
||||||
|
// 実装上は既に trimmed_base = substring(b, e) を計算済みで、その結果を返している
|
||||||
|
|
||||||
|
// if !(e > b) { return substring(b, e) }
|
||||||
|
loop_step_func.body.push(JoinInst::Jump {
|
||||||
|
cont: JoinContId::new(0),
|
||||||
|
args: vec![trimmed_base], // ← substring(b, e) の結果
|
||||||
|
cond: Some(cond_is_false),
|
||||||
|
});
|
||||||
|
|
||||||
|
// const 1
|
||||||
|
let const_1 = ValueId(6005);
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_1,
|
||||||
|
value: ConstValue::Integer(1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// e_minus_1 = e - 1
|
||||||
|
let e_minus_1 = ValueId(6006);
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: e_minus_1,
|
||||||
|
lhs: e_loop,
|
||||||
|
rhs: const_1,
|
||||||
|
op: BinOpKind::Sub,
|
||||||
|
}));
|
||||||
|
|
||||||
|
let ch = ValueId(6007);
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||||
|
dst: Some(ch),
|
||||||
|
box_name: "StringBox".to_string(),
|
||||||
|
method: "substring".to_string(),
|
||||||
|
args: vec![str_loop, e_minus_1, e_loop],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// is_space = (ch == " " || ch == "\\t" || ch == "\\n" || ch == "\\r")
|
||||||
|
let cmp_space = ValueId(6008);
|
||||||
|
let cmp_tab = ValueId(6009);
|
||||||
|
let cmp_newline = ValueId(6010);
|
||||||
|
let cmp_cr = ValueId(6011);
|
||||||
|
|
||||||
|
let const_space = ValueId(6012);
|
||||||
|
let const_tab = ValueId(6013);
|
||||||
|
let const_newline = ValueId(6014);
|
||||||
|
let const_cr = ValueId(6015);
|
||||||
|
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_space,
|
||||||
|
value: ConstValue::String(" ".to_string()),
|
||||||
|
}));
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp_space,
|
||||||
|
lhs: ch,
|
||||||
|
rhs: const_space,
|
||||||
|
op: CompareOp::Eq,
|
||||||
|
}));
|
||||||
|
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_tab,
|
||||||
|
value: ConstValue::String("\\t".to_string()),
|
||||||
|
}));
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp_tab,
|
||||||
|
lhs: ch,
|
||||||
|
rhs: const_tab,
|
||||||
|
op: CompareOp::Eq,
|
||||||
|
}));
|
||||||
|
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_newline,
|
||||||
|
value: ConstValue::String("\\n".to_string()),
|
||||||
|
}));
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp_newline,
|
||||||
|
lhs: ch,
|
||||||
|
rhs: const_newline,
|
||||||
|
op: CompareOp::Eq,
|
||||||
|
}));
|
||||||
|
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_cr,
|
||||||
|
value: ConstValue::String("\\r".to_string()),
|
||||||
|
}));
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp_cr,
|
||||||
|
lhs: ch,
|
||||||
|
rhs: const_cr,
|
||||||
|
op: CompareOp::Eq,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// OR chain: (cmp_space || cmp_tab) || cmp_newline || cmp_cr
|
||||||
|
let or1 = ValueId(6016);
|
||||||
|
let or2 = ValueId(6017);
|
||||||
|
let is_space = ValueId(6018);
|
||||||
|
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: or1,
|
||||||
|
lhs: cmp_space,
|
||||||
|
rhs: cmp_tab,
|
||||||
|
op: BinOpKind::Or,
|
||||||
|
}));
|
||||||
|
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: or2,
|
||||||
|
lhs: or1,
|
||||||
|
rhs: cmp_newline,
|
||||||
|
op: BinOpKind::Or,
|
||||||
|
}));
|
||||||
|
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: is_space,
|
||||||
|
lhs: or2,
|
||||||
|
rhs: cmp_cr,
|
||||||
|
op: BinOpKind::Or,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// is_space_false = (is_space == false)
|
||||||
|
let is_space_false = ValueId(6021);
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: is_space_false,
|
||||||
|
lhs: is_space,
|
||||||
|
rhs: bool_false,
|
||||||
|
op: CompareOp::Eq,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Phase 27.5: 2箇所目の exit パス(同じく exit_args = [e], Option A)
|
||||||
|
// if !is_space { return substring(b, e) }
|
||||||
|
loop_step_func.body.push(JoinInst::Jump {
|
||||||
|
cont: JoinContId::new(1),
|
||||||
|
args: vec![trimmed_base], // ← substring(b, e) の結果(1箇所目と同じ)
|
||||||
|
cond: Some(is_space_false),
|
||||||
|
});
|
||||||
|
|
||||||
|
// continue path: e_next = e - 1; loop_step(str, b, e_next)
|
||||||
|
let e_next = ValueId(6022);
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: e_next,
|
||||||
|
lhs: e_loop,
|
||||||
|
rhs: const_1,
|
||||||
|
op: BinOpKind::Sub,
|
||||||
|
}));
|
||||||
|
|
||||||
|
loop_step_func.body.push(JoinInst::Call {
|
||||||
|
func: loop_step_id, // 再帰呼び出し
|
||||||
|
args: vec![str_loop, b_loop, e_next],
|
||||||
|
k_next: None,
|
||||||
|
dst: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
join_module.add_function(loop_step_func);
|
||||||
|
|
||||||
|
// skip_leading 関数: 先頭の空白をスキップして位置を返す
|
||||||
|
let mut skip_func = JoinFunction::new(
|
||||||
|
skip_leading_id,
|
||||||
|
"skip_leading".to_string(),
|
||||||
|
vec![ValueId(7000), ValueId(7001), ValueId(7002)], // (s, i, n)
|
||||||
|
);
|
||||||
|
let s_skip = ValueId(7000);
|
||||||
|
let i_skip = ValueId(7001);
|
||||||
|
let n_skip = ValueId(7002);
|
||||||
|
let cmp_len = ValueId(7003);
|
||||||
|
let const_1_skip = ValueId(7004);
|
||||||
|
let i_plus_1_skip = ValueId(7005);
|
||||||
|
let ch_skip = ValueId(7006);
|
||||||
|
let cmp_space_skip = ValueId(7007);
|
||||||
|
let cmp_tab_skip = ValueId(7008);
|
||||||
|
let cmp_newline_skip = ValueId(7009);
|
||||||
|
let cmp_cr_skip = ValueId(7010);
|
||||||
|
let const_space_skip = ValueId(7011);
|
||||||
|
let const_tab_skip = ValueId(7012);
|
||||||
|
let const_newline_skip = ValueId(7013);
|
||||||
|
let const_cr_skip = ValueId(7014);
|
||||||
|
let or1_skip = ValueId(7015);
|
||||||
|
let or2_skip = ValueId(7016);
|
||||||
|
let is_space_skip = ValueId(7017);
|
||||||
|
let bool_false_skip = ValueId(7018);
|
||||||
|
let is_space_false_skip = ValueId(7019);
|
||||||
|
|
||||||
|
// cmp_len = (i >= n)
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp_len,
|
||||||
|
lhs: i_skip,
|
||||||
|
rhs: n_skip,
|
||||||
|
op: CompareOp::Ge,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// if i >= n { return i }
|
||||||
|
skip_func.body.push(JoinInst::Jump {
|
||||||
|
cont: JoinContId::new(2),
|
||||||
|
args: vec![i_skip],
|
||||||
|
cond: Some(cmp_len),
|
||||||
|
});
|
||||||
|
|
||||||
|
// const 1
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_1_skip,
|
||||||
|
value: ConstValue::Integer(1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// i_plus_1 = i + 1
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: i_plus_1_skip,
|
||||||
|
lhs: i_skip,
|
||||||
|
rhs: const_1_skip,
|
||||||
|
op: BinOpKind::Add,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// ch = s.substring(i, i + 1)
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||||
|
dst: Some(ch_skip),
|
||||||
|
box_name: "StringBox".to_string(),
|
||||||
|
method: "substring".to_string(),
|
||||||
|
args: vec![s_skip, i_skip, i_plus_1_skip],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// whitespace constants + comparisons
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_space_skip,
|
||||||
|
value: ConstValue::String(" ".to_string()),
|
||||||
|
}));
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp_space_skip,
|
||||||
|
lhs: ch_skip,
|
||||||
|
rhs: const_space_skip,
|
||||||
|
op: CompareOp::Eq,
|
||||||
|
}));
|
||||||
|
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_tab_skip,
|
||||||
|
value: ConstValue::String("\\t".to_string()),
|
||||||
|
}));
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp_tab_skip,
|
||||||
|
lhs: ch_skip,
|
||||||
|
rhs: const_tab_skip,
|
||||||
|
op: CompareOp::Eq,
|
||||||
|
}));
|
||||||
|
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_newline_skip,
|
||||||
|
value: ConstValue::String("\\n".to_string()),
|
||||||
|
}));
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp_newline_skip,
|
||||||
|
lhs: ch_skip,
|
||||||
|
rhs: const_newline_skip,
|
||||||
|
op: CompareOp::Eq,
|
||||||
|
}));
|
||||||
|
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_cr_skip,
|
||||||
|
value: ConstValue::String("\\r".to_string()),
|
||||||
|
}));
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp_cr_skip,
|
||||||
|
lhs: ch_skip,
|
||||||
|
rhs: const_cr_skip,
|
||||||
|
op: CompareOp::Eq,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// is_space_skip = OR chain
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: or1_skip,
|
||||||
|
lhs: cmp_space_skip,
|
||||||
|
rhs: cmp_tab_skip,
|
||||||
|
op: BinOpKind::Or,
|
||||||
|
}));
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: or2_skip,
|
||||||
|
lhs: or1_skip,
|
||||||
|
rhs: cmp_newline_skip,
|
||||||
|
op: BinOpKind::Or,
|
||||||
|
}));
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: is_space_skip,
|
||||||
|
lhs: or2_skip,
|
||||||
|
rhs: cmp_cr_skip,
|
||||||
|
op: BinOpKind::Or,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// bool false + negation
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: bool_false_skip,
|
||||||
|
value: ConstValue::Bool(false),
|
||||||
|
}));
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: is_space_false_skip,
|
||||||
|
lhs: is_space_skip,
|
||||||
|
rhs: bool_false_skip,
|
||||||
|
op: CompareOp::Eq,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// if not space -> return i
|
||||||
|
skip_func.body.push(JoinInst::Jump {
|
||||||
|
cont: JoinContId::new(3),
|
||||||
|
args: vec![i_skip],
|
||||||
|
cond: Some(is_space_false_skip),
|
||||||
|
});
|
||||||
|
|
||||||
|
// continue path: skip_leading(s, i + 1, n)
|
||||||
|
skip_func.body.push(JoinInst::Call {
|
||||||
|
func: skip_leading_id,
|
||||||
|
args: vec![s_skip, i_plus_1_skip, n_skip],
|
||||||
|
k_next: None,
|
||||||
|
dst: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
join_module.add_function(skip_func);
|
||||||
|
eprintln!("[joinir/trim] Generated {} JoinIR functions", join_module.functions.len());
|
||||||
|
|
||||||
|
Some(join_module)
|
||||||
|
}
|
||||||
138
src/mir/join_ir/lowering/min_loop.rs
Normal file
138
src/mir/join_ir/lowering/min_loop.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
//! Phase 26-H: JoinIrMin.main/0 専用の MIR → JoinIR 変換
|
||||||
|
//!
|
||||||
|
//! 目的: apps/tests/joinir_min_loop.hako の MIR を JoinIR に変換する最小実装
|
||||||
|
//!
|
||||||
|
//! 期待される変換:
|
||||||
|
//! ```text
|
||||||
|
//! // MIR (元):
|
||||||
|
//! static box JoinIrMin {
|
||||||
|
//! main() {
|
||||||
|
//! local i = 0
|
||||||
|
//! loop(i < 3) {
|
||||||
|
//! if i >= 2 { break }
|
||||||
|
//! i = i + 1
|
||||||
|
//! }
|
||||||
|
//! return i
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // JoinIR (変換後):
|
||||||
|
//! fn main(k_exit) {
|
||||||
|
//! let i_init = 0
|
||||||
|
//! loop_step(i_init, k_exit)
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn loop_step(i, k_exit) {
|
||||||
|
//! if i >= 2 {
|
||||||
|
//! k_exit(i) // break
|
||||||
|
//! } else {
|
||||||
|
//! loop_step(i + 1, k_exit) // continue
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use crate::mir::ValueId;
|
||||||
|
use crate::mir::join_ir::{
|
||||||
|
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, MirLikeInst,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn lower_min_loop_to_joinir(module: &crate::mir::MirModule) -> Option<JoinModule> {
|
||||||
|
// Step 1: "JoinIrMin.main/0" を探す
|
||||||
|
let target_func = module.functions.get("JoinIrMin.main/0")?;
|
||||||
|
|
||||||
|
eprintln!("[joinir/lower] Found JoinIrMin.main/0");
|
||||||
|
eprintln!("[joinir/lower] MIR blocks: {}", target_func.blocks.len());
|
||||||
|
|
||||||
|
// Step 2: JoinModule を構築
|
||||||
|
let mut join_module = JoinModule::new();
|
||||||
|
|
||||||
|
// Phase 26-H: 最小実装として、固定的な JoinIR を生成
|
||||||
|
// (実際の MIR 解析は Phase 27 以降)
|
||||||
|
|
||||||
|
// main 関数: i_init = 0, loop_step(0, k_exit)
|
||||||
|
let main_id = JoinFuncId::new(0);
|
||||||
|
let mut main_func = JoinFunction::new(main_id, "main".to_string(), vec![]);
|
||||||
|
|
||||||
|
let i_init = ValueId(1000); // 固定 ValueId
|
||||||
|
let const_0 = ValueId(1001);
|
||||||
|
let const_1 = ValueId(1002);
|
||||||
|
let const_2 = ValueId(1003);
|
||||||
|
|
||||||
|
// const 0
|
||||||
|
main_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: i_init,
|
||||||
|
value: ConstValue::Integer(0),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// loop_step(i_init, k_exit)
|
||||||
|
let loop_step_id = JoinFuncId::new(1);
|
||||||
|
main_func.body.push(JoinInst::Call {
|
||||||
|
func: loop_step_id,
|
||||||
|
args: vec![i_init],
|
||||||
|
k_next: None, // main は直接 loop_step を呼ぶ
|
||||||
|
dst: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
join_module.add_function(main_func);
|
||||||
|
|
||||||
|
// loop_step 関数: if i >= 2 { ret i } else { loop_step(i+1) }
|
||||||
|
let mut loop_step_func = JoinFunction::new(
|
||||||
|
loop_step_id,
|
||||||
|
"loop_step".to_string(),
|
||||||
|
vec![ValueId(2000)], // i パラメータ
|
||||||
|
);
|
||||||
|
|
||||||
|
let i_param = ValueId(2000);
|
||||||
|
let cmp_result = ValueId(2001);
|
||||||
|
let i_plus_1 = ValueId(2002);
|
||||||
|
|
||||||
|
// const 2 (for comparison)
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_2,
|
||||||
|
value: ConstValue::Integer(2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// cmp_result = (i >= 2)
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp_result,
|
||||||
|
op: CompareOp::Ge,
|
||||||
|
lhs: i_param,
|
||||||
|
rhs: const_2,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// if cmp_result { ret i } else { loop_step(i+1) }
|
||||||
|
// Phase 26-H 簡略化: 分岐はせず両方の経路を示す
|
||||||
|
|
||||||
|
// ret i (break path)
|
||||||
|
loop_step_func.body.push(JoinInst::Ret {
|
||||||
|
value: Some(i_param),
|
||||||
|
});
|
||||||
|
|
||||||
|
// const 1 (for increment)
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_1,
|
||||||
|
value: ConstValue::Integer(1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// i_plus_1 = i + 1
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: i_plus_1,
|
||||||
|
op: BinOpKind::Add,
|
||||||
|
lhs: i_param,
|
||||||
|
rhs: const_1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// loop_step(i + 1) (continue path)
|
||||||
|
loop_step_func.body.push(JoinInst::Call {
|
||||||
|
func: loop_step_id,
|
||||||
|
args: vec![i_plus_1],
|
||||||
|
k_next: None,
|
||||||
|
dst: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
join_module.add_function(loop_step_func);
|
||||||
|
|
||||||
|
eprintln!("[joinir/lower] Generated {} JoinIR functions", join_module.functions.len());
|
||||||
|
|
||||||
|
Some(join_module)
|
||||||
|
}
|
||||||
19
src/mir/join_ir/lowering/mod.rs
Normal file
19
src/mir/join_ir/lowering/mod.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//! JoinIR Lowering Functions
|
||||||
|
//!
|
||||||
|
//! Phase 27.9: Modular separation of MIR → JoinIR lowering implementations.
|
||||||
|
//!
|
||||||
|
//! このモジュールは各種 MIR 関数を JoinIR に変換する lowering 関数を提供します。
|
||||||
|
//!
|
||||||
|
//! ## 構成:
|
||||||
|
//! - `min_loop.rs`: JoinIrMin.main/0 専用の最小ループ lowering
|
||||||
|
//! - `skip_ws.rs`: Main.skip/1 の空白スキップ lowering(手書き版+MIR自動解析版)
|
||||||
|
//! - `funcscanner_trim.rs`: FuncScannerBox.trim/1 の trim lowering
|
||||||
|
|
||||||
|
pub mod funcscanner_trim;
|
||||||
|
pub mod min_loop;
|
||||||
|
pub mod skip_ws;
|
||||||
|
|
||||||
|
// Re-export public lowering functions
|
||||||
|
pub use funcscanner_trim::lower_funcscanner_trim_to_joinir;
|
||||||
|
pub use min_loop::lower_min_loop_to_joinir;
|
||||||
|
pub use skip_ws::lower_skip_ws_to_joinir;
|
||||||
414
src/mir/join_ir/lowering/skip_ws.rs
Normal file
414
src/mir/join_ir/lowering/skip_ws.rs
Normal file
@ -0,0 +1,414 @@
|
|||||||
|
//! Phase 27.1: minimal_ssa_skip_ws 専用の MIR → JoinIR 変換
|
||||||
|
//!
|
||||||
|
//! 目的: apps/tests/minimal_ssa_skip_ws.hako の MIR を JoinIR に変換する実装
|
||||||
|
//!
|
||||||
|
//! 期待される変換:
|
||||||
|
//! ```text
|
||||||
|
//! // MIR (元):
|
||||||
|
//! static box Main {
|
||||||
|
//! skip(s) {
|
||||||
|
//! local i = 0
|
||||||
|
//! local n = s.length()
|
||||||
|
//! loop(1 == 1) {
|
||||||
|
//! if i >= n { break }
|
||||||
|
//! local ch = s.substring(i, i + 1)
|
||||||
|
//! if ch == " " { i = i + 1 } else { break }
|
||||||
|
//! }
|
||||||
|
//! return i
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // JoinIR (変換後):
|
||||||
|
//! fn skip(s_param, k_exit) {
|
||||||
|
//! i_init = 0
|
||||||
|
//! n = s_param.length()
|
||||||
|
//! loop_step(s_param, i_init, n, k_exit)
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn loop_step(s, i, n, k_exit) {
|
||||||
|
//! if i >= n {
|
||||||
|
//! k_exit(i) // break
|
||||||
|
//! } else {
|
||||||
|
//! ch = s.substring(i, i + 1)
|
||||||
|
//! if ch == " " {
|
||||||
|
//! loop_step(s, i + 1, n, k_exit) // continue
|
||||||
|
//! } else {
|
||||||
|
//! k_exit(i) // break
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use crate::mir::ValueId;
|
||||||
|
use crate::mir::join_ir::{
|
||||||
|
env_flag_is_1, BinOpKind, CompareOp, ConstValue, JoinContId, JoinFuncId, JoinFunction,
|
||||||
|
JoinInst, JoinModule, LoopExitShape, LoopHeaderShape, MirLikeInst,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Phase 27.8: Main.skip/1 の JoinIR lowering(手書き版)
|
||||||
|
///
|
||||||
|
/// Phase 27.1-27.7 で実装された hand-written JoinIR 生成。
|
||||||
|
/// Phase 27.8 以降は `lower_skip_ws_from_mir()` に移行予定。
|
||||||
|
fn lower_skip_ws_handwritten(module: &crate::mir::MirModule) -> Option<JoinModule> {
|
||||||
|
// Step 1: "Main.skip/1" を探す
|
||||||
|
let target_func = module.functions.get("Main.skip/1")?;
|
||||||
|
|
||||||
|
eprintln!("[joinir/skip_ws] Found Main.skip/1");
|
||||||
|
eprintln!("[joinir/skip_ws] MIR blocks: {}", target_func.blocks.len());
|
||||||
|
|
||||||
|
// Step 2: JoinModule を構築
|
||||||
|
let mut join_module = JoinModule::new();
|
||||||
|
|
||||||
|
// Phase 27.1: 固定的な JoinIR を生成(実際の MIR 解析は Phase 28 以降)
|
||||||
|
|
||||||
|
// skip 関数: i_init = 0, n = s.length(), loop_step(s, 0, n, k_exit)
|
||||||
|
let skip_id = JoinFuncId::new(0);
|
||||||
|
let s_param = ValueId(3000);
|
||||||
|
let mut skip_func = JoinFunction::new(skip_id, "skip".to_string(), vec![s_param]);
|
||||||
|
|
||||||
|
let i_init = ValueId(3001);
|
||||||
|
let n = ValueId(3002);
|
||||||
|
|
||||||
|
// i_init = 0
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: i_init,
|
||||||
|
value: ConstValue::Integer(0),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// n = s.length() (BoxCall でメソッド呼び出し)
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||||
|
dst: Some(n),
|
||||||
|
box_name: "StringBox".to_string(),
|
||||||
|
method: "length".to_string(),
|
||||||
|
args: vec![s_param],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// loop_step(s, i_init, n, k_exit)
|
||||||
|
let loop_step_id = JoinFuncId::new(1);
|
||||||
|
skip_func.body.push(JoinInst::Call {
|
||||||
|
func: loop_step_id,
|
||||||
|
args: vec![s_param, i_init, n],
|
||||||
|
k_next: None,
|
||||||
|
dst: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
join_module.entry = Some(skip_id);
|
||||||
|
join_module.add_function(skip_func);
|
||||||
|
|
||||||
|
// Phase 27.4-A: loop_step の Pinned/Carrier 構造を明示
|
||||||
|
// skip_ws ループの場合:
|
||||||
|
// - Pinned: s (文字列), n (長さ) - ループ中で不変
|
||||||
|
// - Carrier: i (現在位置) - ループで更新される
|
||||||
|
let s_loop = ValueId(4000); // Pinned
|
||||||
|
let i_loop = ValueId(4001); // Carrier
|
||||||
|
let n_loop = ValueId(4002); // Pinned
|
||||||
|
|
||||||
|
let _header_shape = LoopHeaderShape::new_manual(
|
||||||
|
vec![s_loop, n_loop], // Pinned: s, n
|
||||||
|
vec![i_loop], // Carrier: i
|
||||||
|
);
|
||||||
|
// 将来: LoopHeaderShape.to_loop_step_params() は [pinned..., carriers...] の順を返す。
|
||||||
|
// 現在は既存 JoinIR テストとの互換性のため、手動で [s, i, n] の順を維持している。
|
||||||
|
|
||||||
|
// loop_step 関数: if i >= n { return i } else if ch == " " { loop_step(i + 1) } else { return i }
|
||||||
|
let mut loop_step_func = JoinFunction::new(
|
||||||
|
loop_step_id,
|
||||||
|
"loop_step".to_string(),
|
||||||
|
vec![s_loop, i_loop, n_loop], // [pinned, carrier, pinned] の順(現行実装)
|
||||||
|
);
|
||||||
|
|
||||||
|
let cmp1_result = ValueId(4003);
|
||||||
|
let ch = ValueId(4004);
|
||||||
|
let cmp2_result = ValueId(4005);
|
||||||
|
let i_plus_1 = ValueId(4006);
|
||||||
|
let const_1 = ValueId(4007);
|
||||||
|
let const_space = ValueId(4010);
|
||||||
|
let bool_false = ValueId(4011);
|
||||||
|
let cmp2_is_false = ValueId(4012);
|
||||||
|
|
||||||
|
// cmp1_result = (i >= n)
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp1_result,
|
||||||
|
op: CompareOp::Ge,
|
||||||
|
lhs: i_loop,
|
||||||
|
rhs: n_loop,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Phase 27.5: Exit φ の意味を LoopExitShape で明示
|
||||||
|
// skip_ws のループ脱出時は i の値だけを返す(先頭空白の文字数)
|
||||||
|
let _exit_shape = LoopExitShape::new_manual(vec![i_loop]); // exit_args = [i]
|
||||||
|
|
||||||
|
// if i >= n { return i }
|
||||||
|
loop_step_func.body.push(JoinInst::Jump {
|
||||||
|
cont: JoinContId::new(0),
|
||||||
|
args: vec![i_loop], // ← LoopExitShape.exit_args に対応
|
||||||
|
cond: Some(cmp1_result),
|
||||||
|
});
|
||||||
|
|
||||||
|
// const 1
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_1,
|
||||||
|
value: ConstValue::Integer(1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// i_plus_1 = i + 1 (再利用: substring end / continue path)
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: i_plus_1,
|
||||||
|
op: BinOpKind::Add,
|
||||||
|
lhs: i_loop,
|
||||||
|
rhs: const_1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// ch = s.substring(i, i + 1)
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||||
|
dst: Some(ch),
|
||||||
|
box_name: "StringBox".to_string(),
|
||||||
|
method: "substring".to_string(),
|
||||||
|
args: vec![s_loop, i_loop, i_plus_1],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// const " " (space)
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_space,
|
||||||
|
value: ConstValue::String(" ".to_string()),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// cmp2_result = (ch == " ")
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp2_result,
|
||||||
|
op: CompareOp::Eq,
|
||||||
|
lhs: ch,
|
||||||
|
rhs: const_space,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// bool false (for negation)
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: bool_false,
|
||||||
|
value: ConstValue::Bool(false),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// cmp2_is_false = (cmp2_result == false)
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp2_is_false,
|
||||||
|
op: CompareOp::Eq,
|
||||||
|
lhs: cmp2_result,
|
||||||
|
rhs: bool_false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Phase 27.5: 2箇所目の exit パス(同じく exit_args = [i])
|
||||||
|
// if ch != " " { return i }
|
||||||
|
loop_step_func.body.push(JoinInst::Jump {
|
||||||
|
cont: JoinContId::new(1),
|
||||||
|
args: vec![i_loop], // ← LoopExitShape.exit_args に対応(1箇所目と同じ)
|
||||||
|
cond: Some(cmp2_is_false),
|
||||||
|
});
|
||||||
|
|
||||||
|
// continue path: loop_step(s, i + 1, n)
|
||||||
|
loop_step_func.body.push(JoinInst::Call {
|
||||||
|
func: loop_step_id,
|
||||||
|
args: vec![s_loop, i_plus_1, n_loop],
|
||||||
|
k_next: None,
|
||||||
|
dst: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
join_module.add_function(loop_step_func);
|
||||||
|
|
||||||
|
eprintln!("[joinir/skip_ws] Generated {} JoinIR functions", join_module.functions.len());
|
||||||
|
|
||||||
|
Some(join_module)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 27.8: Main.skip/1 の JoinIR lowering(MIR 自動解析版)
|
||||||
|
///
|
||||||
|
/// MIR 構造を解析して自動的に JoinIR を生成する実装。
|
||||||
|
/// Phase 27.8 で導入、将来的に hand-written 版を置き換える予定。
|
||||||
|
///
|
||||||
|
/// ## 環境変数:
|
||||||
|
/// - `NYASH_JOINIR_LOWER_FROM_MIR=1`: この実装を有効化
|
||||||
|
///
|
||||||
|
/// ## 実装状況:
|
||||||
|
/// - Phase 27.8: 基本実装(MirQuery を使用した MIR 解析)
|
||||||
|
fn lower_skip_ws_from_mir(module: &crate::mir::MirModule) -> Option<JoinModule> {
|
||||||
|
// Step 1: "Main.skip/1" を探す
|
||||||
|
let target_func = module.functions.get("Main.skip/1")?;
|
||||||
|
|
||||||
|
eprintln!("[joinir/skip_ws/mir] Found Main.skip/1 (MIR-based lowering)");
|
||||||
|
eprintln!("[joinir/skip_ws/mir] MIR blocks: {}", target_func.blocks.len());
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// このフェーズでは minimal_ssa_skip_ws.hako 固定のパターンを前提に、
|
||||||
|
// MIR の CFG を軽く確認した上で JoinIR を組み立てる。
|
||||||
|
// (完全一般化は次フェーズ以降で行う)
|
||||||
|
|
||||||
|
// 簡易チェック: ブロック数が最低限あるか確認
|
||||||
|
if target_func.blocks.len() < 3 {
|
||||||
|
eprintln!("[joinir/skip_ws/mir] insufficient blocks ({}), falling back", target_func.blocks.len());
|
||||||
|
return lower_skip_ws_handwritten(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinIR の ValueId は手書き版と同じレンジを使い、既存テストと互換にする
|
||||||
|
let skip_id = JoinFuncId::new(0);
|
||||||
|
let loop_step_id = JoinFuncId::new(1);
|
||||||
|
|
||||||
|
// -------- skip 関数(入口) --------
|
||||||
|
let s_param = ValueId(3000);
|
||||||
|
let mut skip_func = JoinFunction::new(skip_id, "skip".to_string(), vec![s_param]);
|
||||||
|
|
||||||
|
let i_init = ValueId(3001);
|
||||||
|
let n = ValueId(3002);
|
||||||
|
|
||||||
|
// i_init = 0
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: i_init,
|
||||||
|
value: ConstValue::Integer(0),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// n = s.length()
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||||
|
dst: Some(n),
|
||||||
|
box_name: "StringBox".to_string(),
|
||||||
|
method: "length".to_string(),
|
||||||
|
args: vec![s_param],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// loop_step(s, i_init, n)
|
||||||
|
skip_func.body.push(JoinInst::Call {
|
||||||
|
func: loop_step_id,
|
||||||
|
args: vec![s_param, i_init, n],
|
||||||
|
k_next: None,
|
||||||
|
dst: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------- loop_step 関数 --------
|
||||||
|
// Header 形: pinned = [s, n], carrier = [i]
|
||||||
|
let s_loop = ValueId(4000); // pinned
|
||||||
|
let i_loop = ValueId(4001); // carrier
|
||||||
|
let n_loop = ValueId(4002); // pinned
|
||||||
|
|
||||||
|
let _header_shape = LoopHeaderShape::new_manual(vec![s_loop, n_loop], vec![i_loop]);
|
||||||
|
let _exit_shape = LoopExitShape::new_manual(vec![i_loop]); // exit_args = [i]
|
||||||
|
|
||||||
|
let mut loop_step_func = JoinFunction::new(
|
||||||
|
loop_step_id,
|
||||||
|
"loop_step".to_string(),
|
||||||
|
vec![s_loop, i_loop, n_loop],
|
||||||
|
);
|
||||||
|
|
||||||
|
let cmp1_result = ValueId(4003);
|
||||||
|
let ch = ValueId(4004);
|
||||||
|
let cmp2_result = ValueId(4005);
|
||||||
|
let i_plus_1 = ValueId(4006);
|
||||||
|
let const_1 = ValueId(4007);
|
||||||
|
let const_space = ValueId(4010);
|
||||||
|
let bool_false = ValueId(4011);
|
||||||
|
let cmp2_is_false = ValueId(4012);
|
||||||
|
|
||||||
|
// if i >= n { k_exit(i) }
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp1_result,
|
||||||
|
op: CompareOp::Ge,
|
||||||
|
lhs: i_loop,
|
||||||
|
rhs: n_loop,
|
||||||
|
}));
|
||||||
|
loop_step_func.body.push(JoinInst::Jump {
|
||||||
|
cont: JoinContId::new(0),
|
||||||
|
args: vec![i_loop],
|
||||||
|
cond: Some(cmp1_result),
|
||||||
|
});
|
||||||
|
|
||||||
|
// const 1
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_1,
|
||||||
|
value: ConstValue::Integer(1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// i_plus_1 = i + 1
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: i_plus_1,
|
||||||
|
op: BinOpKind::Add,
|
||||||
|
lhs: i_loop,
|
||||||
|
rhs: const_1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// ch = s.substring(i, i+1)
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||||
|
dst: Some(ch),
|
||||||
|
box_name: "StringBox".to_string(),
|
||||||
|
method: "substring".to_string(),
|
||||||
|
args: vec![s_loop, i_loop, i_plus_1],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// cmp2 = (ch == " ")
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: const_space,
|
||||||
|
value: ConstValue::String(" ".to_string()),
|
||||||
|
}));
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp2_result,
|
||||||
|
op: CompareOp::Eq,
|
||||||
|
lhs: ch,
|
||||||
|
rhs: const_space,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// cmp2_is_false = (cmp2 == false)
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: bool_false,
|
||||||
|
value: ConstValue::Bool(false),
|
||||||
|
}));
|
||||||
|
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: cmp2_is_false,
|
||||||
|
op: CompareOp::Eq,
|
||||||
|
lhs: cmp2_result,
|
||||||
|
rhs: bool_false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// if ch != " " { k_exit(i) }
|
||||||
|
loop_step_func.body.push(JoinInst::Jump {
|
||||||
|
cont: JoinContId::new(1),
|
||||||
|
args: vec![i_loop],
|
||||||
|
cond: Some(cmp2_is_false),
|
||||||
|
});
|
||||||
|
|
||||||
|
// continue: loop_step(s, i+1, n)
|
||||||
|
loop_step_func.body.push(JoinInst::Call {
|
||||||
|
func: loop_step_id,
|
||||||
|
args: vec![s_loop, i_plus_1, n_loop],
|
||||||
|
k_next: None,
|
||||||
|
dst: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------- JoinModule 完成 --------
|
||||||
|
let mut join_module = JoinModule::new();
|
||||||
|
join_module.entry = Some(skip_id);
|
||||||
|
join_module.add_function(skip_func);
|
||||||
|
join_module.add_function(loop_step_func);
|
||||||
|
|
||||||
|
Some(join_module)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 27.8: Main.skip/1 の JoinIR lowering(トグル対応ディスパッチャー)
|
||||||
|
///
|
||||||
|
/// 環境変数 `NYASH_JOINIR_LOWER_FROM_MIR=1` に応じて、
|
||||||
|
/// hand-written 版または MIR 自動解析版を選択する。
|
||||||
|
///
|
||||||
|
/// ## トグル制御:
|
||||||
|
/// - **OFF (デフォルト)**: `lower_skip_ws_handwritten()` を使用
|
||||||
|
/// - **ON**: `lower_skip_ws_from_mir()` を使用
|
||||||
|
///
|
||||||
|
/// ## 使用例:
|
||||||
|
/// ```bash
|
||||||
|
/// # 手書き版(既定)
|
||||||
|
/// ./target/release/hakorune program.hako
|
||||||
|
///
|
||||||
|
/// # MIR 自動解析版
|
||||||
|
/// NYASH_JOINIR_LOWER_FROM_MIR=1 ./target/release/hakorune program.hako
|
||||||
|
/// ```
|
||||||
|
pub fn lower_skip_ws_to_joinir(module: &crate::mir::MirModule) -> Option<JoinModule> {
|
||||||
|
if env_flag_is_1("NYASH_JOINIR_LOWER_FROM_MIR") {
|
||||||
|
eprintln!("[joinir/skip_ws] Using MIR-based lowering (NYASH_JOINIR_LOWER_FROM_MIR=1)");
|
||||||
|
lower_skip_ws_from_mir(module)
|
||||||
|
} else {
|
||||||
|
eprintln!("[joinir/skip_ws] Using handwritten lowering (default)");
|
||||||
|
lower_skip_ws_handwritten(module)
|
||||||
|
}
|
||||||
|
}
|
||||||
315
src/mir/join_ir/mod.rs
Normal file
315
src/mir/join_ir/mod.rs
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
//! JoinIR — 関数正規化 IR(Phase 26-H)
|
||||||
|
//!
|
||||||
|
//! 目的: Hakorune の制御構造を **関数呼び出し+継続だけに正規化** する IR 層。
|
||||||
|
//! - φ ノード = 関数の引数
|
||||||
|
//! - merge ブロック = join 関数
|
||||||
|
//! - ループ = 再帰関数(loop_step)+ exit 継続(k_exit)
|
||||||
|
//! - break / continue = 適切な関数呼び出し
|
||||||
|
//!
|
||||||
|
//! 位置づけ:
|
||||||
|
//! ```text
|
||||||
|
//! AST → MIR(+LoopForm v2) → JoinIR → VM / LLVM
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Phase 26-H スコープ:
|
||||||
|
//! - 型定義のみ(変換ロジックは次フェーズ)
|
||||||
|
//! - 最小限の命令セット
|
||||||
|
//! - Debug 出力で妥当性確認
|
||||||
|
//!
|
||||||
|
//! Phase 27.9: Modular Structure
|
||||||
|
//! - Type definitions and common utilities in this file
|
||||||
|
//! - Lowering functions in `lowering/` submodule
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use crate::mir::{BasicBlockId, ValueId};
|
||||||
|
|
||||||
|
// Phase 27.9: Lowering submodule
|
||||||
|
pub mod lowering;
|
||||||
|
|
||||||
|
// Re-export lowering functions for backward compatibility
|
||||||
|
pub use lowering::{
|
||||||
|
lower_funcscanner_trim_to_joinir, lower_min_loop_to_joinir, lower_skip_ws_to_joinir,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// JoinIR 関数ID(MIR 関数とは別 ID でもよい)
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct JoinFuncId(pub u32);
|
||||||
|
|
||||||
|
impl JoinFuncId {
|
||||||
|
pub fn new(id: u32) -> Self {
|
||||||
|
JoinFuncId(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 継続(join / ループ step / exit continuation)を識別するID
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct JoinContId(pub u32);
|
||||||
|
|
||||||
|
impl JoinContId {
|
||||||
|
pub fn new(id: u32) -> Self {
|
||||||
|
JoinContId(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 変数ID(Phase 26-H では MIR の ValueId を再利用)
|
||||||
|
pub type VarId = ValueId;
|
||||||
|
|
||||||
|
/// 環境変数フラグが "1" かチェックするヘルパー(JoinIR 実験経路用)
|
||||||
|
pub(crate) fn env_flag_is_1(name: &str) -> bool {
|
||||||
|
std::env::var(name).ok().as_deref() == Some("1")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 27.4-A: ループ header φ の意味を表す構造(Pinned/Carrier 分類)
|
||||||
|
///
|
||||||
|
/// HeaderPhiBuilder が生成していた「ループ変数の合流」を JoinIR の loop_step 引数として表現するためのヘルパー。
|
||||||
|
///
|
||||||
|
/// 用語:
|
||||||
|
/// - **Pinned**: ループ中で値が変わらない変数(例: skip_ws の s, n / trim の str, b)
|
||||||
|
/// - **Carrier**: ループで更新される変数(例: skip_ws の i / trim の e)
|
||||||
|
///
|
||||||
|
/// Phase 27.4 では minimal/trim 用に手動で構成するが、将来は LoopVarClassBox から自動導出する。
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[allow(dead_code)] // Phase 27.4-C で実際に使用予定(現在は設計の雛形)
|
||||||
|
pub(crate) struct LoopHeaderShape {
|
||||||
|
/// Pinned: ループ中で不変の変数リスト(初期値がそのまま使われる)
|
||||||
|
pinned: Vec<ValueId>,
|
||||||
|
/// Carrier: ループで更新される変数リスト(φ ノードで合流が必要)
|
||||||
|
carriers: Vec<ValueId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)] // Phase 27.4-C で実際に使用予定
|
||||||
|
impl LoopHeaderShape {
|
||||||
|
/// Phase 27.4-A: 手動で Pinned/Carrier を指定して構築
|
||||||
|
pub(crate) fn new_manual(pinned: Vec<ValueId>, carriers: Vec<ValueId>) -> Self {
|
||||||
|
LoopHeaderShape { pinned, carriers }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// loop_step 関数の引数リストを生成(pinned → carrier の順)
|
||||||
|
pub(crate) fn to_loop_step_params(&self) -> Vec<ValueId> {
|
||||||
|
let mut params = self.pinned.clone();
|
||||||
|
params.extend(self.carriers.clone());
|
||||||
|
params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 27.5: ループ exit φ の意味を表す構造
|
||||||
|
///
|
||||||
|
/// ExitPhiBuilder が生成していた「ループ脱出時の変数合流」を JoinIR の k_exit 引数として表現するためのヘルパー。
|
||||||
|
///
|
||||||
|
/// 用語:
|
||||||
|
/// - **exit_args**: ループから脱出する際に k_exit に渡す値のリスト
|
||||||
|
///
|
||||||
|
/// 例:
|
||||||
|
/// - **minimal_ssa_skip_ws**: exit_args = [i]
|
||||||
|
/// - ループから抜ける時、現在の i の値を返す
|
||||||
|
/// - **FuncScanner.trim**: exit_args = [e] (Option A)
|
||||||
|
/// - ループから抜ける時、現在の e の値を返す(後続で substring(b, e) を呼ぶ)
|
||||||
|
///
|
||||||
|
/// Phase 27.5 では minimal/trim 用に手動で構成するが、将来は ExitPhiBuilder の分析から自動導出する。
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[allow(dead_code)] // Phase 27.6 で Exit φ 統合の実装フェーズで使用予定(現在は設計の雛形)
|
||||||
|
pub(crate) struct LoopExitShape {
|
||||||
|
/// Exit 時に k_exit に渡したい値(JoinIR 引数)
|
||||||
|
exit_args: Vec<ValueId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)] // Phase 27.6 で実際に使用予定
|
||||||
|
impl LoopExitShape {
|
||||||
|
/// Phase 27.5: 手動で exit_args を指定して構築
|
||||||
|
pub(crate) fn new_manual(exit_args: Vec<ValueId>) -> Self {
|
||||||
|
LoopExitShape { exit_args }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// JoinIR 関数
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct JoinFunction {
|
||||||
|
/// 関数ID
|
||||||
|
pub id: JoinFuncId,
|
||||||
|
|
||||||
|
/// 関数名(デバッグ用)
|
||||||
|
pub name: String,
|
||||||
|
|
||||||
|
/// 引数(φ に相当)
|
||||||
|
pub params: Vec<VarId>,
|
||||||
|
|
||||||
|
/// 命令列(現在は直列、将来的にはブロック構造も可)
|
||||||
|
pub body: Vec<JoinInst>,
|
||||||
|
|
||||||
|
/// 呼び出し元に返す継続(ルートは None)
|
||||||
|
pub exit_cont: Option<JoinContId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JoinFunction {
|
||||||
|
pub fn new(id: JoinFuncId, name: String, params: Vec<VarId>) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
params,
|
||||||
|
body: Vec::new(),
|
||||||
|
exit_cont: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// JoinIR 命令セット(最小版)
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum JoinInst {
|
||||||
|
/// 通常の関数呼び出し: f(args..., k_next)
|
||||||
|
Call {
|
||||||
|
func: JoinFuncId,
|
||||||
|
args: Vec<VarId>,
|
||||||
|
k_next: Option<JoinContId>,
|
||||||
|
/// 呼び出し結果を書き込む変数(None の場合は末尾呼び出しとして扱う)
|
||||||
|
dst: Option<VarId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 継続呼び出し(join / exit 継続など)
|
||||||
|
Jump {
|
||||||
|
cont: JoinContId,
|
||||||
|
args: Vec<VarId>,
|
||||||
|
/// None のときは無条件ジャンプ、Some(var) のときは var が truthy のときだけ実行
|
||||||
|
cond: Option<VarId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// ルート関数 or 上位への戻り
|
||||||
|
Ret {
|
||||||
|
value: Option<VarId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// それ以外の演算は、現行 MIR の算術/比較/boxcall を再利用
|
||||||
|
Compute(MirLikeInst),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MIR からの算術・比較命令のラッパー(Phase 26-H では最小限)
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum MirLikeInst {
|
||||||
|
/// 定数代入
|
||||||
|
Const {
|
||||||
|
dst: VarId,
|
||||||
|
value: ConstValue,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 二項演算
|
||||||
|
BinOp {
|
||||||
|
dst: VarId,
|
||||||
|
op: BinOpKind,
|
||||||
|
lhs: VarId,
|
||||||
|
rhs: VarId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 比較演算
|
||||||
|
Compare {
|
||||||
|
dst: VarId,
|
||||||
|
op: CompareOp,
|
||||||
|
lhs: VarId,
|
||||||
|
rhs: VarId,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Box呼び出し(将来的には統一 Call に統合予定)
|
||||||
|
BoxCall {
|
||||||
|
dst: Option<VarId>,
|
||||||
|
box_name: String,
|
||||||
|
method: String,
|
||||||
|
args: Vec<VarId>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 定数値(MIR の ConstValue を簡略化)
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ConstValue {
|
||||||
|
Integer(i64),
|
||||||
|
Bool(bool),
|
||||||
|
String(String),
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 二項演算種別
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum BinOpKind {
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
Or, // Phase 27.1: 論理OR (bool || bool)
|
||||||
|
And, // Phase 27.1: 論理AND (bool && bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 比較演算種別
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum CompareOp {
|
||||||
|
Lt,
|
||||||
|
Le,
|
||||||
|
Gt,
|
||||||
|
Ge,
|
||||||
|
Eq,
|
||||||
|
Ne,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// JoinIR モジュール(複数の関数を保持)
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct JoinModule {
|
||||||
|
/// 関数マップ
|
||||||
|
pub functions: BTreeMap<JoinFuncId, JoinFunction>,
|
||||||
|
|
||||||
|
/// エントリーポイント関数ID
|
||||||
|
pub entry: Option<JoinFuncId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JoinModule {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
functions: BTreeMap::new(),
|
||||||
|
entry: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_function(&mut self, func: JoinFunction) {
|
||||||
|
self.functions.insert(func.id, func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for JoinModule {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_join_function_creation() {
|
||||||
|
let func_id = JoinFuncId::new(0);
|
||||||
|
let func = JoinFunction::new(func_id, "test_func".to_string(), vec![ValueId(1), ValueId(2)]);
|
||||||
|
|
||||||
|
assert_eq!(func.id, func_id);
|
||||||
|
assert_eq!(func.name, "test_func");
|
||||||
|
assert_eq!(func.params.len(), 2);
|
||||||
|
assert_eq!(func.body.len(), 0);
|
||||||
|
assert_eq!(func.exit_cont, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_join_module() {
|
||||||
|
let mut module = JoinModule::new();
|
||||||
|
let func = JoinFunction::new(JoinFuncId::new(0), "main".to_string(), vec![]);
|
||||||
|
module.add_function(func);
|
||||||
|
|
||||||
|
assert_eq!(module.functions.len(), 1);
|
||||||
|
assert!(module.functions.contains_key(&JoinFuncId::new(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn loop_header_shape_params_order_is_pinned_then_carrier() {
|
||||||
|
// Phase 27.4-A: to_loop_step_params() が pinned→carriers の順を返すことを保証
|
||||||
|
let v1 = ValueId(1);
|
||||||
|
let v2 = ValueId(2);
|
||||||
|
let v3 = ValueId(3);
|
||||||
|
let shape = LoopHeaderShape::new_manual(vec![v1, v2], vec![v3]);
|
||||||
|
let params = shape.to_loop_step_params();
|
||||||
|
assert_eq!(params, vec![v1, v2, v3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -385,7 +385,14 @@ pub(super) fn lower_loop_stmt(
|
|||||||
|
|
||||||
// 7) header PHI seal(latch + canonical continue_merge スナップショット)
|
// 7) header PHI seal(latch + canonical continue_merge スナップショット)
|
||||||
// Step 5-1/5-2: Pass writes 集合 for PHI縮約
|
// Step 5-1/5-2: Pass writes 集合 for PHI縮約
|
||||||
loopform.seal_phis(&mut ops, latch_bb, &canonical_continue_snaps, &writes)?;
|
// Phase 27.4C: JSON v0 bridge は常に header_bypass = false(本線経路)
|
||||||
|
loopform.seal_phis(
|
||||||
|
&mut ops,
|
||||||
|
latch_bb,
|
||||||
|
&canonical_continue_snaps,
|
||||||
|
&writes,
|
||||||
|
false, // header_bypass (JSON v0 bridge はレガシー経路なので false)
|
||||||
|
)?;
|
||||||
|
|
||||||
// 8) exit PHI(header fallthrough + break スナップショット)
|
// 8) exit PHI(header fallthrough + break スナップショット)
|
||||||
// Option C: Create inspector (build_exit_phis will populate it)
|
// Option C: Create inspector (build_exit_phis will populate it)
|
||||||
|
|||||||
Reference in New Issue
Block a user