LoopToJoinLowerer API cleanup: 1. Rename methods to reflect their purpose as Case-A helpers: - lower_minimal_skip_ws_case_a → lower_case_a_for_skip_ws - lower_minimal_trim_case_a → lower_case_a_for_trim - lower_minimal_append_defs_case_a → lower_case_a_for_append_defs - lower_minimal_stage1_case_a → lower_case_a_for_stage1_resolver 2. Update comments to clarify these are thin wrappers over `lower()` - "Case-A 汎用 lowerer の薄いラッパー" - Actual logic is centralized in `lower` method 3. Update all 4 call sites: - skip_ws.rs - funcscanner_trim.rs - funcscanner_append_defs.rs - stage1_using_resolver.rs Test results: standalone (2/2), JSON snapshot (6/6) PASS 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
397 lines
14 KiB
Rust
397 lines
14 KiB
Rust
//! Phase 27.14: FuncScannerBox._append_defs loop の JoinIR lowering
|
||
//!
|
||
//! 目的: FuncScanner の最も簡単な配列結合ループを JoinIR に変換
|
||
//!
|
||
//! ## 対象ループ
|
||
//! - ファイル: `lang/src/compiler/entry/func_scanner.hako`
|
||
//! - 関数: `FuncScannerBox._append_defs(dst, defs_box)`
|
||
//! - 行数: 293-300
|
||
//!
|
||
//! ## ループ構造
|
||
//! ```hako
|
||
//! method _append_defs(dst, defs_box) {
|
||
//! if defs_box == null { return }
|
||
//! local i = 0
|
||
//! loop(i < defs_box.length()) {
|
||
//! dst.push(defs_box.get(i))
|
||
//! i = i + 1
|
||
//! }
|
||
//! }
|
||
//! ```
|
||
//!
|
||
//! ## LoopForm ケース: Case A (動的条件 `i < defs_box.length()`)
|
||
//!
|
||
//! ## Pinned / Carrier / Exit
|
||
//! - **Pinned**: `dst` (ArrayBox), `defs_box` (ArrayBox), `n` (Integer = defs_box.length())
|
||
//! - **Carrier**: `i` (Integer)
|
||
//! - **Exit**: none (void return, dst は破壊的変更)
|
||
//!
|
||
//! ## 想定 JoinIR 構造
|
||
//! ```text
|
||
//! fn append_defs_entry(dst, defs_box, n) -> void {
|
||
//! let i_init = 0;
|
||
//! loop_step(dst, defs_box, n, i_init)
|
||
//! }
|
||
//!
|
||
//! fn loop_step(dst, defs_box, n, i) -> void {
|
||
//! if i >= n { return }
|
||
//! let item = defs_box.get(i)
|
||
//! dst.push(item)
|
||
//! let next_i = i + 1
|
||
//! loop_step(dst, defs_box, n, next_i)
|
||
//! }
|
||
//! ```
|
||
|
||
use crate::mir::join_ir::lowering::common::{
|
||
dispatch_lowering, ensure_entry_has_succs, has_array_method, has_const_int, has_loop_increment,
|
||
log_fallback,
|
||
};
|
||
use crate::mir::join_ir::lowering::value_id_ranges::funcscanner_append_defs as vid;
|
||
use crate::mir::join_ir::JoinModule;
|
||
use crate::mir::loop_form::LoopForm;
|
||
use crate::mir::query::{MirQuery, MirQueryBox};
|
||
|
||
/// Phase 27.14: FuncScannerBox._append_defs の JoinIR lowering(public dispatcher)
|
||
///
|
||
/// 環境変数 `NYASH_JOINIR_LOWER_FROM_MIR=1` に応じて、
|
||
/// MIR-based 版または handwritten 版を選択する。
|
||
///
|
||
/// ## トグル制御:
|
||
/// - **OFF (デフォルト)**: `lower_handwritten()` を使用
|
||
/// - **ON**: `lower_from_mir()` を使用
|
||
///
|
||
/// ## Shared Builder Pattern
|
||
/// 両方の実装が `build_funcscanner_append_defs_joinir()` を呼び出す共通パターン。
|
||
pub fn lower_funcscanner_append_defs_to_joinir(
|
||
module: &crate::mir::MirModule,
|
||
) -> Option<JoinModule> {
|
||
dispatch_lowering(
|
||
"funcscanner_append_defs",
|
||
module,
|
||
lower_from_mir,
|
||
lower_handwritten,
|
||
)
|
||
}
|
||
|
||
/// Phase 27.14: Common JoinIR builder for FuncScannerBox._append_defs
|
||
///
|
||
/// This function generates the JoinIR for the append_defs loop, shared by both:
|
||
/// - lower_handwritten (always uses this)
|
||
/// - lower_from_mir (uses this after CFG sanity checks pass)
|
||
///
|
||
/// ## 簡略化方針
|
||
/// Phase 27.14 の最小実装として、最も単純な JoinIR を生成する:
|
||
/// - ArrayBox.length() → dst.push(item) の基本パターン
|
||
/// - null チェックは省略(MIR 側で既に処理済み前提)
|
||
fn build_funcscanner_append_defs_joinir(module: &crate::mir::MirModule) -> Option<JoinModule> {
|
||
use crate::mir::join_ir::*;
|
||
|
||
// Phase 27.14: ターゲット関数が存在するかチェック
|
||
let _target_func = module.functions.get("FuncScannerBox._append_defs/2")?;
|
||
|
||
eprintln!("[joinir/funcscanner_append_defs/build] Phase 27.14 implementation");
|
||
eprintln!("[joinir/funcscanner_append_defs/build] Generating JoinIR for _append_defs loop");
|
||
eprintln!("[joinir/funcscanner_append_defs/build] Using ValueId range: 9000-10999 (via value_id_ranges)");
|
||
|
||
// Step 1: JoinModule を構築
|
||
let mut join_module = JoinModule::new();
|
||
|
||
// append_defs_entry 関数(entry):
|
||
// fn append_defs_entry(dst, defs_box, n) -> void {
|
||
// let i_init = 0;
|
||
// loop_step(dst, defs_box, n, i_init)
|
||
// }
|
||
let entry_id = JoinFuncId::new(0);
|
||
let dst_param = vid::entry(0); // 9000
|
||
let defs_box_param = vid::entry(1); // 9001
|
||
let n_param = vid::entry(2); // 9002
|
||
|
||
let mut entry_func = JoinFunction::new(
|
||
entry_id,
|
||
"append_defs_entry".to_string(),
|
||
vec![dst_param, defs_box_param, n_param],
|
||
);
|
||
|
||
let i_init = vid::entry(10); // 9010
|
||
|
||
// i_init = 0
|
||
entry_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||
dst: i_init,
|
||
value: ConstValue::Integer(0),
|
||
}));
|
||
|
||
// loop_step(dst, defs_box, n, i_init)
|
||
let loop_step_id = JoinFuncId::new(1);
|
||
entry_func.body.push(JoinInst::Call {
|
||
func: loop_step_id,
|
||
args: vec![dst_param, defs_box_param, n_param, i_init],
|
||
k_next: None,
|
||
dst: None,
|
||
});
|
||
|
||
join_module.entry = Some(entry_id);
|
||
join_module.add_function(entry_func);
|
||
|
||
// Phase 27.14: loop_step の Pinned/Carrier 構造を明示
|
||
// FuncScanner _append_defs ループの場合:
|
||
// - Pinned: dst (ArrayBox), defs_box (ArrayBox), n (Integer)
|
||
// - Carrier: i (Integer)
|
||
// - Exit: none (void return)
|
||
let dst_loop = vid::loop_step(0); // 10000 - Pinned
|
||
let defs_box_loop = vid::loop_step(1); // 10001 - Pinned
|
||
let n_loop = vid::loop_step(2); // 10002 - Pinned
|
||
let i_loop = vid::loop_step(3); // 10003 - Carrier
|
||
|
||
let _header_shape = LoopHeaderShape::new_manual(
|
||
vec![dst_loop, defs_box_loop, n_loop], // Pinned
|
||
vec![i_loop], // Carrier
|
||
);
|
||
|
||
// loop_step 関数:
|
||
// fn loop_step(dst, defs_box, n, i) -> void {
|
||
// if i >= n { return }
|
||
// let item = defs_box.get(i)
|
||
// dst.push(item)
|
||
// let next_i = i + 1
|
||
// loop_step(dst, defs_box, n, next_i)
|
||
// }
|
||
let mut loop_step_func = JoinFunction::new(
|
||
loop_step_id,
|
||
"loop_step".to_string(),
|
||
vec![dst_loop, defs_box_loop, n_loop, i_loop],
|
||
);
|
||
|
||
let cmp_result = vid::loop_step(10); // 10010
|
||
let item_value = vid::loop_step(11); // 10011
|
||
let next_i = vid::loop_step(12); // 10012
|
||
let const_1 = vid::loop_step(13); // 10013
|
||
|
||
// cmp_result = (i >= n)
|
||
loop_step_func
|
||
.body
|
||
.push(JoinInst::Compute(MirLikeInst::Compare {
|
||
dst: cmp_result,
|
||
op: CompareOp::Ge,
|
||
lhs: i_loop,
|
||
rhs: n_loop,
|
||
}));
|
||
|
||
// Phase 27.14: Exit φ の意味を LoopExitShape で明示
|
||
// FuncScanner _append_defs ループ脱出時は void 返却(dst は破壊的変更済み)
|
||
let _exit_shape = LoopExitShape::new_manual(vec![]); // exit_args = [] (void)
|
||
|
||
// if i >= n { return } (void)
|
||
loop_step_func.body.push(JoinInst::Jump {
|
||
cont: JoinContId::new(0),
|
||
args: vec![], // ← LoopExitShape.exit_args に対応 (void)
|
||
cond: Some(cmp_result),
|
||
});
|
||
|
||
// item = defs_box.get(i)
|
||
loop_step_func
|
||
.body
|
||
.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||
dst: Some(item_value),
|
||
box_name: "ArrayBox".to_string(),
|
||
method: "get".to_string(),
|
||
args: vec![defs_box_loop, i_loop],
|
||
}));
|
||
|
||
// dst.push(item) - 破壊的変更(戻り値なし)
|
||
loop_step_func
|
||
.body
|
||
.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||
dst: None, // push は戻り値なし
|
||
box_name: "ArrayBox".to_string(),
|
||
method: "push".to_string(),
|
||
args: vec![dst_loop, item_value],
|
||
}));
|
||
|
||
// const_1 = 1
|
||
loop_step_func
|
||
.body
|
||
.push(JoinInst::Compute(MirLikeInst::Const {
|
||
dst: const_1,
|
||
value: ConstValue::Integer(1),
|
||
}));
|
||
|
||
// next_i = i + 1
|
||
loop_step_func
|
||
.body
|
||
.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||
dst: next_i,
|
||
op: BinOpKind::Add,
|
||
lhs: i_loop,
|
||
rhs: const_1,
|
||
}));
|
||
|
||
// loop_step(dst, defs_box, n, next_i) - tail recursion
|
||
loop_step_func.body.push(JoinInst::Call {
|
||
func: loop_step_id,
|
||
args: vec![dst_loop, defs_box_loop, n_loop, next_i],
|
||
k_next: None,
|
||
dst: None,
|
||
});
|
||
|
||
join_module.add_function(loop_step_func);
|
||
|
||
eprintln!("[joinir/funcscanner_append_defs/build] ✅ JoinIR construction completed");
|
||
eprintln!(
|
||
"[joinir/funcscanner_append_defs/build] Functions: {}",
|
||
join_module.functions.len()
|
||
);
|
||
|
||
Some(join_module)
|
||
}
|
||
|
||
/// Phase 27.14: MIR-based lowering for FuncScannerBox._append_defs
|
||
///
|
||
/// CFG sanity checks + MIR パターンマッチング → 成功なら `build_funcscanner_append_defs_joinir()` 呼び出し
|
||
///
|
||
/// ## CFG Sanity Checks (軽量パターンマッチ):
|
||
/// 1. Entry block に後続がある
|
||
/// 2. Entry block 付近に以下の命令がある:
|
||
/// - `Const { value: Integer(0) }` (初期 i = 0)
|
||
/// - `BoxCall { box_name: "ArrayBox", method: "length" }` (n = defs_box.length())
|
||
/// 3. ループ本体付近に:
|
||
/// - `BoxCall { box_name: "ArrayBox", method: "get" }` (defs_box.get(i))
|
||
/// - `BoxCall { box_name: "ArrayBox", method: "push" }` (dst.push(item))
|
||
/// - `BinOp { op: Add }` (next_i = i + 1)
|
||
///
|
||
/// ## Graceful Degradation
|
||
/// 上記パターンが検出できない場合は `log_fallback()` → `lower_handwritten()` に戻る。
|
||
fn lower_from_mir(module: &crate::mir::MirModule) -> Option<JoinModule> {
|
||
eprintln!("[joinir/funcscanner_append_defs/mir] Starting MIR-based lowering");
|
||
|
||
// Step 1: FuncScannerBox._append_defs/2 を探す
|
||
let target_func = module.functions.get("FuncScannerBox._append_defs/2")?;
|
||
|
||
eprintln!("[joinir/funcscanner_append_defs/mir] Found FuncScannerBox._append_defs/2");
|
||
eprintln!(
|
||
"[joinir/funcscanner_append_defs/mir] MIR blocks: {}",
|
||
target_func.blocks.len()
|
||
);
|
||
|
||
// Step 2: MirQueryBox を作成
|
||
let query = MirQueryBox::new(target_func);
|
||
let entry = target_func.entry_block;
|
||
|
||
// CFG Check 1: Entry block has successors
|
||
if !ensure_entry_has_succs(&query, entry) {
|
||
log_fallback("funcscanner_append_defs", "entry block has no successors");
|
||
return lower_handwritten(module);
|
||
}
|
||
|
||
// CFG Check 2: Entry block contains expected patterns
|
||
// Pattern 1: i = 0 (初期化)
|
||
if !has_const_int(&query, entry, 0) {
|
||
log_fallback(
|
||
"funcscanner_append_defs",
|
||
"Const(0) not found in entry block",
|
||
);
|
||
return lower_handwritten(module);
|
||
}
|
||
|
||
// Pattern 2: defs_box.length() の検出
|
||
// Check entry block and its immediate successors for length() call
|
||
let has_length_call = has_array_method(&query, entry, "length")
|
||
|| query
|
||
.succs(entry)
|
||
.iter()
|
||
.any(|&succ| has_array_method(&query, succ, "length"));
|
||
|
||
if !has_length_call {
|
||
log_fallback(
|
||
"funcscanner_append_defs",
|
||
"ArrayBox.length() not found in entry or successors",
|
||
);
|
||
return lower_handwritten(module);
|
||
}
|
||
|
||
// Pattern 3: ループ本体での配列操作検出
|
||
// Check all blocks for array operations (get/push) and loop increment
|
||
let all_blocks: Vec<_> = target_func.blocks.keys().copied().collect();
|
||
|
||
let has_get_call = all_blocks
|
||
.iter()
|
||
.any(|&bb| has_array_method(&query, bb, "get"));
|
||
if !has_get_call {
|
||
log_fallback(
|
||
"funcscanner_append_defs",
|
||
"ArrayBox.get() not found in function body",
|
||
);
|
||
return lower_handwritten(module);
|
||
}
|
||
|
||
let has_push_call = all_blocks
|
||
.iter()
|
||
.any(|&bb| has_array_method(&query, bb, "push"));
|
||
if !has_push_call {
|
||
log_fallback(
|
||
"funcscanner_append_defs",
|
||
"ArrayBox.push() not found in function body",
|
||
);
|
||
return lower_handwritten(module);
|
||
}
|
||
|
||
let has_increment = all_blocks.iter().any(|&bb| has_loop_increment(&query, bb));
|
||
if !has_increment {
|
||
log_fallback(
|
||
"funcscanner_append_defs",
|
||
"loop increment (i + 1) not found in function body",
|
||
);
|
||
return lower_handwritten(module);
|
||
}
|
||
|
||
eprintln!("[joinir/funcscanner_append_defs/mir] CFG sanity checks passed ✅");
|
||
eprintln!("[joinir/funcscanner_append_defs/mir] Found: length(), get(), push(), i+1");
|
||
|
||
// Phase 31: LoopToJoinLowerer 統一箱経由に移行
|
||
if crate::mir::join_ir::env_flag_is_1("NYASH_JOINIR_LOWER_GENERIC") {
|
||
use crate::mir::join_ir::lowering::loop_to_join::LoopToJoinLowerer;
|
||
|
||
let header = query.succs(entry).get(0).copied().unwrap_or(entry);
|
||
let succs_header = query.succs(header);
|
||
let body = succs_header.get(0).copied().unwrap_or(header);
|
||
let exit = succs_header.get(1).copied().unwrap_or(header);
|
||
let loop_form = LoopForm {
|
||
preheader: entry,
|
||
header,
|
||
body,
|
||
latch: body,
|
||
exit,
|
||
continue_targets: vec![body],
|
||
break_targets: vec![exit],
|
||
};
|
||
if crate::mir::join_ir::lowering::common::case_a::is_simple_case_a_loop(&loop_form) {
|
||
eprintln!(
|
||
"[joinir/funcscanner_append_defs/generic-hook] detected simple Case A loop (LoopToJoinLowerer)"
|
||
);
|
||
let lowerer = LoopToJoinLowerer::new();
|
||
if let Some(jm) = lowerer.lower_case_a_for_append_defs(target_func, &loop_form) {
|
||
eprintln!(
|
||
"[joinir/funcscanner_append_defs/generic-hook] LoopToJoinLowerer produced JoinIR, returning early"
|
||
);
|
||
return Some(jm);
|
||
}
|
||
eprintln!(
|
||
"[joinir/funcscanner_append_defs/generic-hook] LoopToJoinLowerer returned None, falling back to handwritten/MIR path"
|
||
);
|
||
}
|
||
}
|
||
|
||
// Phase 27.14: Generate JoinIR using shared builder
|
||
// CFG checks passed, so we can use build_funcscanner_append_defs_joinir() directly
|
||
eprintln!("[joinir/funcscanner_append_defs/mir] Calling build_funcscanner_append_defs_joinir() after CFG validation");
|
||
build_funcscanner_append_defs_joinir(module)
|
||
}
|
||
|
||
/// Phase 27.14: Handwritten lowering wrapper for FuncScannerBox._append_defs
|
||
///
|
||
/// This is a thin wrapper that calls the shared build_funcscanner_append_defs_joinir() function.
|
||
/// Maintains the handwritten lowering path as the baseline reference.
|
||
fn lower_handwritten(module: &crate::mir::MirModule) -> Option<JoinModule> {
|
||
eprintln!("[joinir/funcscanner_append_defs/handwritten] Using handwritten lowering path");
|
||
build_funcscanner_append_defs_joinir(module)
|
||
}
|