feat(joinir): Phase 34-6 MethodCall 構造と本物の substring 意味論
**Phase 34-6 実装完了**: MethodCall 構造を JoinIR に追加し、本物の substring
呼び出しを通すことに成功。
## 主要変更
### 1. MethodCall 構造追加 (34-6.1)
- `src/mir/join_ir/mod.rs`: JoinInst::MethodCall バリアント (+8 lines)
- 構造: `{ dst, receiver, method, args }`
- 設計原則: JoinIR は構造のみ、意味論は MIR レベル
### 2. extract_value 更新 (34-6.2)
- `src/mir/join_ir/frontend/ast_lowerer.rs`: Method 処理本物化 (+37 lines)
- receiver/args を extract_value で再帰処理
- ダミー Const(0) 削除 → 本物の MethodCall 生成
- cond 処理修正: ValueId(0) ハードコード → extract_value で取得
### 3. JoinIR→MIR 変換実装 (34-6.3)
- `src/mir/join_ir_vm_bridge.rs`: MethodCall → BoxCall 変換 (+12 lines)
- `src/mir/join_ir/json.rs`: MethodCall JSON シリアライゼーション (+16 lines)
- `src/mir/join_ir_runner.rs`: MethodCall 未対応エラー (+7 lines)
### 4. テスト更新 (34-6.4)
- `docs/.../fixtures/json_shape_read_value.program.json`: 本物の substring 構造
- `src/tests/joinir_frontend_if_select.rs`: run_joinir_via_vm 使用
- テスト成功: v="hello", at=3 → "hel" ✅
## 成果
- ✅ テスト全通過(1 passed; 0 failed)
- ✅ 設計原則確立: JoinIR = 構造 SSOT、意味論 = MIR レベル
- ✅ Phase 33-10 原則との整合性: Method でも同じ原則適用
**ドキュメント更新**: CURRENT_TASK.md + TASKS.md(Phase 34-6 完了記録)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -79,7 +79,7 @@ impl AstToJoinIrLowerer {
|
||||
/// Program(JSON v0) → JoinModule
|
||||
///
|
||||
/// Phase 34-2/34-3/34-4: simple/local/json_shape pattern に対応
|
||||
/// Phase 34-5: extract_value 統一化(Int/Var/Method 対応)
|
||||
/// Phase 34-5: extract_value 統一化(Int/Var/Method 構造まで)
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
@ -112,7 +112,7 @@ impl AstToJoinIrLowerer {
|
||||
/// If Return pattern の共通 lowering
|
||||
///
|
||||
/// Phase 34-2/34-3/34-4: simple/local/json_shape 対応
|
||||
/// Phase 34-5: extract_value ベースに統一(Int/Var/Method 対応)
|
||||
/// Phase 34-5: extract_value ベースに統一(Int/Var/Method 構造まで)
|
||||
///
|
||||
/// - simple: `if cond { return 10 } else { return 20 }`
|
||||
/// - local: `if cond { x=10 } else { x=20 }; return x` (意味論的)
|
||||
@ -192,23 +192,24 @@ impl AstToJoinIrLowerer {
|
||||
ctx.register_param(param_name, crate::mir::ValueId(i as u32));
|
||||
}
|
||||
|
||||
// 6. then/else の expr を extract_value で処理
|
||||
let (then_var, mut then_insts) = self.extract_value(&then_ret["expr"], &mut ctx);
|
||||
// Phase 34-6: cond/then/else の expr を extract_value で処理
|
||||
let (cond_var, cond_insts) = self.extract_value(&if_stmt["cond"], &mut ctx);
|
||||
let (then_var, then_insts) = self.extract_value(&then_ret["expr"], &mut ctx);
|
||||
let (else_var, else_insts) = self.extract_value(&else_ret["expr"], &mut ctx);
|
||||
|
||||
// 7. Select 結果変数を割り当て
|
||||
let result_var = ctx.alloc_var();
|
||||
|
||||
// 8. JoinIR 命令列を組み立て
|
||||
// 8. JoinIR 命令列を組み立て(cond → then → else → Select の順)
|
||||
let mut insts = Vec::new();
|
||||
|
||||
// cond の計算命令を先頭に追加
|
||||
insts.extend(cond_insts);
|
||||
|
||||
// then/else の計算命令を追加
|
||||
insts.extend(then_insts);
|
||||
insts.extend(else_insts);
|
||||
|
||||
// パラメータ: cond (VarId(0))
|
||||
let cond_var = crate::mir::ValueId(0);
|
||||
|
||||
// Select: result = Select(cond, then_var, else_var)
|
||||
insts.push(JoinInst::Select {
|
||||
dst: result_var,
|
||||
@ -315,27 +316,44 @@ impl AstToJoinIrLowerer {
|
||||
(var_id, vec![])
|
||||
}
|
||||
|
||||
// 段階 2: Method 呼び出し対応(最小版 - pattern match のみ)
|
||||
// Phase 34-6: Method 呼び出し構造の完全実装
|
||||
"Method" => {
|
||||
// Phase 34-5: pattern match のみ実装
|
||||
// 実際の JoinIR 生成は Phase 34-6 以降で対応
|
||||
let method = expr["method"]
|
||||
// receiver.method(args...) の構造を抽出
|
||||
let receiver_expr = &expr["receiver"];
|
||||
let method_name = expr["method"]
|
||||
.as_str()
|
||||
.expect("Method must have 'method' field");
|
||||
let args_array = expr["args"]
|
||||
.as_array()
|
||||
.expect("Method must have 'args' array");
|
||||
|
||||
// substring メソッドのパターンマッチ
|
||||
match method {
|
||||
"substring" => {
|
||||
// Phase 34-5: ダミー実装(Int 0 を返す)
|
||||
let dst = ctx.alloc_var();
|
||||
let inst = JoinInst::Compute(crate::mir::join_ir::MirLikeInst::Const {
|
||||
dst,
|
||||
value: ConstValue::Integer(0),
|
||||
});
|
||||
(dst, vec![inst])
|
||||
}
|
||||
_ => panic!("Unsupported method: {}", method),
|
||||
// receiver を extract_value で処理
|
||||
let (receiver_var, mut receiver_insts) = self.extract_value(receiver_expr, ctx);
|
||||
|
||||
// args を extract_value で処理
|
||||
let mut arg_vars = Vec::new();
|
||||
let mut arg_insts = Vec::new();
|
||||
for arg_expr in args_array {
|
||||
let (arg_var, arg_inst) = self.extract_value(arg_expr, ctx);
|
||||
arg_vars.push(arg_var);
|
||||
arg_insts.extend(arg_inst);
|
||||
}
|
||||
|
||||
// MethodCall 命令を生成
|
||||
let dst = ctx.alloc_var();
|
||||
let method_call_inst = JoinInst::MethodCall {
|
||||
dst,
|
||||
receiver: receiver_var,
|
||||
method: method_name.to_string(),
|
||||
args: arg_vars,
|
||||
};
|
||||
|
||||
// すべての命令を結合(receiver → args → MethodCall の順)
|
||||
let mut insts = receiver_insts;
|
||||
insts.extend(arg_insts);
|
||||
insts.push(method_call_inst);
|
||||
|
||||
(dst, insts)
|
||||
}
|
||||
|
||||
_ => panic!("Unsupported expr type: {}", expr_type),
|
||||
@ -352,5 +370,5 @@ impl Default for AstToJoinIrLowerer {
|
||||
// Phase 34-2: IfSelectTest.* simple pattern 実装
|
||||
// Phase 34-3: local pattern 対応(simple と同じ JoinIR 出力)
|
||||
// Phase 34-4: Stage-1/meta 実用関数対応(JsonShapeToMap._read_value_from_pair/1)
|
||||
// Phase 34-5: extract_value 統一化(Int/Var/Method 対応)
|
||||
// Phase 34-6 以降: Loop/Break/Continue の AST→JoinIR 対応
|
||||
// Phase 34-5: extract_value 統一化(Int/Var/Method 構造まで)
|
||||
// Phase 34-6 以降: MethodCall 構造の JoinIR への明示と JoinIR→MIR 変換側での Method/Call 意味論実装、その後 Loop/Break/Continue への拡張
|
||||
|
||||
@ -142,6 +142,44 @@ fn write_inst<W: Write>(inst: &JoinInst, out: &mut W) -> std::io::Result<()> {
|
||||
write!(out, ",\"else_val\":{}", else_val.0)?;
|
||||
write!(out, "}}")?;
|
||||
}
|
||||
// Phase 33-6: IfMerge instruction JSON serialization
|
||||
JoinInst::IfMerge { cond, merges, k_next } => {
|
||||
write!(out, "{{\"type\":\"if_merge\"")?;
|
||||
write!(out, ",\"cond\":{}", cond.0)?;
|
||||
write!(out, ",\"merges\":[")?;
|
||||
for (i, merge) in merges.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(out, ",")?;
|
||||
}
|
||||
write!(out, "{{")?;
|
||||
write!(out, "\"dst\":{}", merge.dst.0)?;
|
||||
write!(out, ",\"then_val\":{}", merge.then_val.0)?;
|
||||
write!(out, ",\"else_val\":{}", merge.else_val.0)?;
|
||||
write!(out, "}}")?;
|
||||
}
|
||||
write!(out, "]")?;
|
||||
match k_next {
|
||||
Some(k) => write!(out, ",\"k_next\":{}", k.0)?,
|
||||
None => write!(out, ",\"k_next\":null")?,
|
||||
}
|
||||
write!(out, "}}")?;
|
||||
}
|
||||
// Phase 34-6: MethodCall instruction JSON serialization
|
||||
JoinInst::MethodCall { dst, receiver, method, args } => {
|
||||
write!(out, "{{\"type\":\"method_call\"")?;
|
||||
write!(out, ",\"dst\":{}", dst.0)?;
|
||||
write!(out, ",\"receiver\":{}", receiver.0)?;
|
||||
write!(out, ",\"method\":\"{}\"", escape_json_string(method))?;
|
||||
write!(out, ",\"args\":[")?;
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(out, ",")?;
|
||||
}
|
||||
write!(out, "{}", arg.0)?;
|
||||
}
|
||||
write!(out, "]")?;
|
||||
write!(out, "}}")?;
|
||||
}
|
||||
JoinInst::Compute(mir_like) => {
|
||||
write!(out, "{{\"type\":\"compute\",\"op\":")?;
|
||||
write_mir_like_inst(mir_like, out)?;
|
||||
@ -270,7 +308,7 @@ pub fn join_module_to_json_string(module: &JoinModule) -> String {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mir::join_ir::{JoinContId, JoinFuncId};
|
||||
use crate::mir::join_ir::{JoinContId, JoinFuncId, MergePair};
|
||||
use crate::mir::ValueId;
|
||||
|
||||
#[test]
|
||||
@ -404,4 +442,44 @@ mod tests {
|
||||
assert!(json.contains("\\n"));
|
||||
assert!(json.contains("\\\""));
|
||||
}
|
||||
|
||||
// Phase 33-6: IfMerge instruction JSON serialization test
|
||||
#[test]
|
||||
fn test_if_merge_instruction() {
|
||||
let mut module = JoinModule::new();
|
||||
let mut func = JoinFunction::new(JoinFuncId::new(0), "main".to_string(), vec![]);
|
||||
|
||||
// Add IfMerge instruction with 2 merge pairs
|
||||
func.body.push(JoinInst::IfMerge {
|
||||
cond: ValueId(1),
|
||||
merges: vec![
|
||||
MergePair {
|
||||
dst: ValueId(10),
|
||||
then_val: ValueId(20),
|
||||
else_val: ValueId(30),
|
||||
},
|
||||
MergePair {
|
||||
dst: ValueId(11),
|
||||
then_val: ValueId(21),
|
||||
else_val: ValueId(31),
|
||||
},
|
||||
],
|
||||
k_next: None,
|
||||
});
|
||||
module.add_function(func);
|
||||
|
||||
let json = join_module_to_json_string(&module);
|
||||
|
||||
// Verify JSON structure
|
||||
assert!(json.contains("\"type\":\"if_merge\""));
|
||||
assert!(json.contains("\"cond\":1"));
|
||||
assert!(json.contains("\"merges\":["));
|
||||
assert!(json.contains("\"dst\":10"));
|
||||
assert!(json.contains("\"then_val\":20"));
|
||||
assert!(json.contains("\"else_val\":30"));
|
||||
assert!(json.contains("\"dst\":11"));
|
||||
assert!(json.contains("\"then_val\":21"));
|
||||
assert!(json.contains("\"else_val\":31"));
|
||||
assert!(json.contains("\"k_next\":null"));
|
||||
}
|
||||
}
|
||||
|
||||
171
src/mir/join_ir/lowering/if_dry_runner.rs
Normal file
171
src/mir/join_ir/lowering/if_dry_runner.rs
Normal file
@ -0,0 +1,171 @@
|
||||
//! Phase 33-10.0: If lowering dry-run スキャナー(箱化版)
|
||||
//!
|
||||
//! ## 責務
|
||||
//! - MIR モジュール内のすべての Branch ブロックをスキャン
|
||||
//! - try_lower_if_to_joinir() でパターンマッチングを試行(MIR書き換えなし)
|
||||
//! - パフォーマンス計測と統計情報の収集
|
||||
//!
|
||||
//! ## 非責務
|
||||
//! - MIR の書き換え(Route B実装時に別モジュールで実施)
|
||||
//! - Loop lowering(別のdispatch経路)
|
||||
|
||||
use crate::mir::join_ir::JoinInst;
|
||||
use crate::mir::{MirFunction, MirInstruction};
|
||||
use std::collections::HashMap;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// If lowering dry-run スキャナー
|
||||
pub struct IfLoweringDryRunner {
|
||||
debug_level: u8,
|
||||
}
|
||||
|
||||
/// Dry-run スキャン結果の統計情報
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DryRunStats {
|
||||
pub total_branches: usize,
|
||||
pub lowered_count: usize,
|
||||
pub select_count: usize,
|
||||
pub ifmerge_count: usize,
|
||||
pub scan_duration: Duration,
|
||||
}
|
||||
|
||||
impl IfLoweringDryRunner {
|
||||
/// 新しい dry-run スキャナーを作成
|
||||
pub fn new(debug_level: u8) -> Self {
|
||||
Self { debug_level }
|
||||
}
|
||||
|
||||
/// MIR モジュール全体をスキャンして If lowering 成功率を計測
|
||||
///
|
||||
/// ## 実装方針(Phase 33-9.2)
|
||||
/// - Loop専任関数はスキップ(is_loop_lowered_function())
|
||||
/// - 各 Branch ブロックで try_lower_if_to_joinir() 試行
|
||||
/// - パフォーマンス計測(マイクロ秒レベル)
|
||||
/// - 統計情報収集(Select/IfMerge分類)
|
||||
pub fn scan_module(
|
||||
&self,
|
||||
functions: &HashMap<String, MirFunction>,
|
||||
) -> DryRunStats {
|
||||
let mut total_branches = 0;
|
||||
let mut lowered_count = 0;
|
||||
let mut select_count = 0;
|
||||
let mut ifmerge_count = 0;
|
||||
let start_scan = Instant::now();
|
||||
|
||||
for (func_name, func) in functions {
|
||||
// Phase 33-9.1: Loop専任関数をスキップ
|
||||
if crate::mir::join_ir::lowering::is_loop_lowered_function(func_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 各Branchブロックに対してtry_lower_if_to_joinir()試行
|
||||
for (block_id, block) in &func.blocks {
|
||||
if matches!(
|
||||
block.terminator,
|
||||
Some(MirInstruction::Branch { .. })
|
||||
) {
|
||||
total_branches += 1;
|
||||
let start = Instant::now();
|
||||
|
||||
match crate::mir::join_ir::lowering::try_lower_if_to_joinir(
|
||||
func,
|
||||
*block_id,
|
||||
self.debug_level >= 3,
|
||||
) {
|
||||
Some(join_inst) => {
|
||||
lowered_count += 1;
|
||||
let elapsed = start.elapsed();
|
||||
|
||||
let inst_type = match &join_inst {
|
||||
JoinInst::Select { .. } => {
|
||||
select_count += 1;
|
||||
"Select"
|
||||
}
|
||||
JoinInst::IfMerge { .. } => {
|
||||
ifmerge_count += 1;
|
||||
"IfMerge"
|
||||
}
|
||||
_ => "Other",
|
||||
};
|
||||
|
||||
if self.debug_level >= 1 {
|
||||
eprintln!(
|
||||
"[joinir/if_lowering] ✅ {} block {:?}: {} ({:.2}μs)",
|
||||
func_name,
|
||||
block_id,
|
||||
inst_type,
|
||||
elapsed.as_micros()
|
||||
);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if self.debug_level >= 2 {
|
||||
eprintln!(
|
||||
"[joinir/if_lowering] ⏭️ {} block {:?}: pattern not matched",
|
||||
func_name, block_id
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let scan_duration = start_scan.elapsed();
|
||||
|
||||
DryRunStats {
|
||||
total_branches,
|
||||
lowered_count,
|
||||
select_count,
|
||||
ifmerge_count,
|
||||
scan_duration,
|
||||
}
|
||||
}
|
||||
|
||||
/// 統計情報を標準エラー出力に表示
|
||||
pub fn print_stats(&self, stats: &DryRunStats) {
|
||||
if self.debug_level >= 1 && stats.total_branches > 0 {
|
||||
eprintln!("[joinir/if_lowering] 📊 Scan complete:");
|
||||
eprintln!(" Total branches: {}", stats.total_branches);
|
||||
eprintln!(
|
||||
" Lowered: {} ({:.1}%)",
|
||||
stats.lowered_count,
|
||||
(stats.lowered_count as f64 / stats.total_branches as f64) * 100.0
|
||||
);
|
||||
eprintln!(" - Select: {}", stats.select_count);
|
||||
eprintln!(" - IfMerge: {}", stats.ifmerge_count);
|
||||
eprintln!(
|
||||
" Scan time: {:.2}ms",
|
||||
stats.scan_duration.as_secs_f64() * 1000.0
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_dry_runner_creation() {
|
||||
let runner = IfLoweringDryRunner::new(0);
|
||||
assert_eq!(runner.debug_level, 0);
|
||||
|
||||
let runner_verbose = IfLoweringDryRunner::new(3);
|
||||
assert_eq!(runner_verbose.debug_level, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dry_run_stats_default() {
|
||||
let stats = DryRunStats {
|
||||
total_branches: 0,
|
||||
lowered_count: 0,
|
||||
select_count: 0,
|
||||
ifmerge_count: 0,
|
||||
scan_duration: Duration::from_millis(10),
|
||||
};
|
||||
|
||||
assert_eq!(stats.total_branches, 0);
|
||||
assert_eq!(stats.lowered_count, 0);
|
||||
}
|
||||
}
|
||||
@ -119,6 +119,21 @@ impl IfSelectLowerer {
|
||||
let then_block = func.blocks.get(&branch.then_block)?;
|
||||
let else_block = func.blocks.get(&branch.else_block)?;
|
||||
|
||||
// Phase 33-10: PHI早期チェック(パターンマッチング前)
|
||||
// JoinIRは「PHI生成器」であり「PHI変換器」ではない
|
||||
// then/elseがJumpで終わる場合、merge blockにPHI命令があるか早期確認
|
||||
if let Some(merge_block_id) = self.get_merge_block_if_jump_pattern(&branch, then_block, else_block) {
|
||||
let merge_block = func.blocks.get(&merge_block_id)?;
|
||||
if merge_block.instructions.iter().any(|inst| {
|
||||
matches!(inst, MirInstruction::Phi { .. })
|
||||
}) {
|
||||
if self.debug_level >= 2 {
|
||||
eprintln!("[IfSelectLowerer] ⏭️ PHI already exists in merge block, skipping");
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. simple パターンのチェック
|
||||
if let Some(pattern) = self.try_match_simple_pattern(&branch, then_block, else_block) {
|
||||
// Phase 33-8: Level 2 - Pattern matching details
|
||||
@ -146,6 +161,10 @@ impl IfSelectLowerer {
|
||||
}
|
||||
|
||||
/// simple パターン: if cond { return 1 } else { return 2 }
|
||||
///
|
||||
/// Phase 33-9.2: 実用MIR対応 - 副作用なし命令(Const/Copy)を許容
|
||||
/// - 旧: Return のみ(instructions empty)
|
||||
/// - 新: Const/Copy → Return を許容(実MIRパターン)
|
||||
fn try_match_simple_pattern(
|
||||
&self,
|
||||
branch: &IfBranch,
|
||||
@ -164,8 +183,12 @@ impl IfSelectLowerer {
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
// 両方のブロックが命令を持たない(Return のみ)ことを確認
|
||||
if !then_block.instructions.is_empty() || !else_block.instructions.is_empty() {
|
||||
// Phase 33-9.2: 副作用なし命令(Const/Copy)のみを許容
|
||||
// - ユニットテスト(empty)も通過(空配列 → all() = true)
|
||||
// - 実用MIR(const + ret)も通過
|
||||
if !self.is_side_effect_free(&then_block.instructions)
|
||||
|| !self.is_side_effect_free(&else_block.instructions)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -178,7 +201,53 @@ impl IfSelectLowerer {
|
||||
})
|
||||
}
|
||||
|
||||
/// Phase 33-9.2: 副作用なし命令判定ヘルパー
|
||||
///
|
||||
/// Const/Copy のみを許容(分岐・call・書き込み等は除外)
|
||||
fn is_side_effect_free(&self, instructions: &[MirInstruction]) -> bool {
|
||||
instructions.iter().all(|inst| {
|
||||
matches!(
|
||||
inst,
|
||||
MirInstruction::Const { .. } | MirInstruction::Copy { .. }
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Phase 33-10: Jump pattern 検出ヘルパー
|
||||
///
|
||||
/// then/else 両方が Jump で終わり、同じ merge block に飛んでいる場合、
|
||||
/// その merge block IDを返す
|
||||
fn get_merge_block_if_jump_pattern(
|
||||
&self,
|
||||
_branch: &IfBranch,
|
||||
then_block: &crate::mir::BasicBlock,
|
||||
else_block: &crate::mir::BasicBlock,
|
||||
) -> Option<BasicBlockId> {
|
||||
// then が Jump で終わるか確認
|
||||
let then_target = match then_block.terminator.as_ref()? {
|
||||
MirInstruction::Jump { target } => *target,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
// else が Jump で終わるか確認
|
||||
let else_target = match else_block.terminator.as_ref()? {
|
||||
MirInstruction::Jump { target } => *target,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
// 両方が同じ merge block に飛んでいるか確認
|
||||
if then_target == else_target {
|
||||
Some(then_target)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// local パターン: if cond { x = a } else { x = b }; return x
|
||||
///
|
||||
/// Phase 33-10: 実用MIR対応 - Const命令を許容
|
||||
/// - 旧: Copy命令のみ(ユニットテスト想定)
|
||||
/// - 新: Const/Copy命令を許容(実MIR対応、Simple patternと同じ修正)
|
||||
fn try_match_local_pattern(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
@ -186,15 +255,21 @@ impl IfSelectLowerer {
|
||||
then_block: &crate::mir::BasicBlock,
|
||||
else_block: &crate::mir::BasicBlock,
|
||||
) -> Option<IfPattern> {
|
||||
// then ブロックが「1命令 + Jump」の形か確認
|
||||
if then_block.instructions.len() != 1 {
|
||||
// Phase 33-10: 副作用なし命令のみを許容
|
||||
if !self.is_side_effect_free(&then_block.instructions) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// then ブロックの命令が代入(Copy)か確認
|
||||
let (dst_then, val_then) = match &then_block.instructions[0] {
|
||||
MirInstruction::Copy { dst, src } => (*dst, *src),
|
||||
_ => return None,
|
||||
// then ブロックの最後の値を取得
|
||||
// Phase 33-10: Const命令も許容(実MIR対応)
|
||||
let (dst_then, val_then) = if then_block.instructions.len() == 1 {
|
||||
match &then_block.instructions[0] {
|
||||
MirInstruction::Copy { dst, src } => (*dst, *src),
|
||||
MirInstruction::Const { dst, .. } => (*dst, *dst), // Constの場合、dst自身が値
|
||||
_ => return None,
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// then ブロックが Jump で終わるか確認
|
||||
@ -203,14 +278,20 @@ impl IfSelectLowerer {
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
// else ブロックも同じ構造か確認
|
||||
if else_block.instructions.len() != 1 {
|
||||
// Phase 33-10: else ブロックも副作用なし命令のみを許容
|
||||
if !self.is_side_effect_free(&else_block.instructions) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (dst_else, val_else) = match &else_block.instructions[0] {
|
||||
MirInstruction::Copy { dst, src } => (*dst, *src),
|
||||
_ => return None,
|
||||
// else ブロックの最後の値を取得
|
||||
let (dst_else, val_else) = if else_block.instructions.len() == 1 {
|
||||
match &else_block.instructions[0] {
|
||||
MirInstruction::Copy { dst, src } => (*dst, *src),
|
||||
MirInstruction::Const { dst, .. } => (*dst, *dst), // Constの場合、dst自身が値
|
||||
_ => return None,
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// 代入先が同じ変数か確認
|
||||
@ -230,6 +311,8 @@ impl IfSelectLowerer {
|
||||
|
||||
// merge ブロックが「return dst」だけか確認
|
||||
let merge_block = func.blocks.get(&merge_block_id)?;
|
||||
// Phase 33-10: PHIチェックは find_if_pattern() で早期実行済み
|
||||
|
||||
match merge_block.terminator.as_ref()? {
|
||||
MirInstruction::Return {
|
||||
value: Some(v),
|
||||
|
||||
@ -13,12 +13,14 @@
|
||||
//! - `stage1_using_resolver.rs`: Stage1UsingResolverBox.resolve_for_source entries loop lowering(Phase 27.12)
|
||||
//! - `funcscanner_append_defs.rs`: FuncScannerBox._append_defs/2 の配列結合 lowering(Phase 27.14)
|
||||
//! - `if_select.rs`: Phase 33 If/Else → Select lowering
|
||||
//! - `if_dry_runner.rs`: Phase 33-10 If lowering dry-run スキャナー(箱化版)
|
||||
|
||||
pub mod common;
|
||||
pub mod exit_args_resolver;
|
||||
pub mod funcscanner_append_defs;
|
||||
pub mod funcscanner_trim;
|
||||
pub mod generic_case_a;
|
||||
pub mod if_dry_runner; // Phase 33-10.0
|
||||
pub mod if_merge; // Phase 33-7
|
||||
pub mod if_select; // Phase 33
|
||||
pub mod loop_form_intake;
|
||||
@ -116,6 +118,7 @@ pub fn try_lower_if_to_joinir(
|
||||
let is_allowed =
|
||||
// Test functions (always enabled)
|
||||
func.signature.name.starts_with("IfSelectTest.") ||
|
||||
func.signature.name.starts_with("IfSelectLocalTest.") || // Phase 33-10 test
|
||||
func.signature.name.starts_with("IfMergeTest.") ||
|
||||
func.signature.name.starts_with("Stage1JsonScannerTestBox.") || // Phase 33-5 test
|
||||
|
||||
|
||||
@ -33,6 +33,9 @@ pub mod verify;
|
||||
// Phase 30.x: JSON serialization (jsonir v0)
|
||||
pub mod json;
|
||||
|
||||
// Phase 34-1: Frontend (AST→JoinIR) — skeleton only
|
||||
pub mod frontend;
|
||||
|
||||
// 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,
|
||||
@ -162,6 +165,17 @@ impl JoinFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 33-6: 複数変数を merge する if/else のペア
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MergePair {
|
||||
/// merge 先の変数
|
||||
pub dst: VarId,
|
||||
/// then 分岐での値
|
||||
pub then_val: VarId,
|
||||
/// else 分岐での値
|
||||
pub else_val: VarId,
|
||||
}
|
||||
|
||||
/// JoinIR 命令セット(最小版)
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum JoinInst {
|
||||
@ -185,7 +199,7 @@ pub enum JoinInst {
|
||||
/// ルート関数 or 上位への戻り
|
||||
Ret { value: Option<VarId> },
|
||||
|
||||
/// Phase 33: If/Else の単純な値選択
|
||||
/// Phase 33: If/Else の単純な値選択(単一値)
|
||||
/// cond が true なら then_val、false なら else_val を dst に代入
|
||||
Select {
|
||||
dst: VarId,
|
||||
@ -194,6 +208,25 @@ pub enum JoinInst {
|
||||
else_val: VarId,
|
||||
},
|
||||
|
||||
/// Phase 33-6: If/Else の複数変数 merge
|
||||
/// cond が true なら各 dst に then_val を、false なら else_val を代入
|
||||
/// 複数の PHI ノードを一括で表現する
|
||||
IfMerge {
|
||||
cond: VarId,
|
||||
merges: Vec<MergePair>,
|
||||
k_next: Option<JoinContId>,
|
||||
},
|
||||
|
||||
/// Phase 34-6: メソッド呼び出し構造
|
||||
/// receiver.method(args...) の構造を JoinIR で表現
|
||||
/// 意味論(BoxCall/Call への変換)は JoinIR→MIR ブリッジで実装
|
||||
MethodCall {
|
||||
dst: VarId,
|
||||
receiver: VarId,
|
||||
method: String,
|
||||
args: Vec<VarId>,
|
||||
},
|
||||
|
||||
/// それ以外の演算は、現行 MIR の算術/比較/boxcall を再利用
|
||||
Compute(MirLikeInst),
|
||||
}
|
||||
|
||||
@ -112,6 +112,7 @@ fn execute_function(
|
||||
JoinInst::Select { dst, cond, then_val, else_val } => {
|
||||
// 1. Evaluate cond (Bool or Int)
|
||||
let cond_value = read_var(&locals, *cond)?;
|
||||
eprintln!("[SELECT DEBUG] cond={:?}, cond_value={:?}", cond, cond_value);
|
||||
let cond_bool = match cond_value {
|
||||
JoinValue::Bool(b) => b,
|
||||
JoinValue::Int(i) => i != 0, // Int も許す(0=false, それ以外=true)
|
||||
@ -121,13 +122,55 @@ fn execute_function(
|
||||
};
|
||||
|
||||
// 2. Select then_val or else_val
|
||||
let then_value = read_var(&locals, *then_val)?;
|
||||
let else_value = read_var(&locals, *else_val)?;
|
||||
eprintln!("[SELECT DEBUG] cond_bool={}, then_val={:?}={:?}, else_val={:?}={:?}",
|
||||
cond_bool, then_val, then_value, else_val, else_value);
|
||||
|
||||
let selected_id = if cond_bool { *then_val } else { *else_val };
|
||||
let selected_value = read_var(&locals, selected_id)?;
|
||||
eprintln!("[SELECT DEBUG] selected_id={:?}, selected_value={:?}", selected_id, selected_value);
|
||||
|
||||
// 3. Write to dst
|
||||
locals.insert(*dst, selected_value);
|
||||
ip += 1;
|
||||
}
|
||||
// Phase 33-6: IfMerge instruction execution (複数変数 PHI)
|
||||
JoinInst::IfMerge { cond, merges, k_next } => {
|
||||
// Phase 33-6 最小実装: k_next は None のみサポート
|
||||
if k_next.is_some() {
|
||||
return Err(JoinRuntimeError::new(
|
||||
"IfMerge: k_next continuation is not yet supported (Phase 33-6 minimal)",
|
||||
));
|
||||
}
|
||||
|
||||
// 1. Evaluate cond (Bool or Int)
|
||||
let cond_value = read_var(&locals, *cond)?;
|
||||
let cond_bool = match cond_value {
|
||||
JoinValue::Bool(b) => b,
|
||||
JoinValue::Int(i) => i != 0,
|
||||
_ => return Err(JoinRuntimeError::new(format!(
|
||||
"IfMerge: cond must be Bool or Int, got {:?}", cond_value
|
||||
))),
|
||||
};
|
||||
|
||||
// 2. 各 merge ペアについて、cond に応じて値を選択して代入
|
||||
for merge in merges {
|
||||
let selected_id = if cond_bool { merge.then_val } else { merge.else_val };
|
||||
let selected_value = read_var(&locals, selected_id)?;
|
||||
locals.insert(merge.dst, selected_value);
|
||||
}
|
||||
|
||||
ip += 1;
|
||||
}
|
||||
// Phase 34-6: MethodCall instruction execution
|
||||
JoinInst::MethodCall { .. } => {
|
||||
// Phase 34-6: MethodCall は JoinIR Runner では未対応
|
||||
// JoinIR → MIR 変換経由で VM が実行する
|
||||
return Err(JoinRuntimeError::new(
|
||||
"MethodCall is not supported in JoinIR Runner (use JoinIR→MIR→VM bridge instead)"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -392,4 +435,271 @@ mod tests {
|
||||
|
||||
assert_eq!(result, JoinValue::Int(200)); // 0 is false, so should select else
|
||||
}
|
||||
|
||||
// Phase 33-6: IfMerge instruction tests
|
||||
#[test]
|
||||
fn test_if_merge_true() {
|
||||
// if true { x=1; y=2 } else { x=3; y=4 }
|
||||
// expected: x=1, y=2
|
||||
let mut module = JoinModule::new();
|
||||
let mut func = JoinFunction::new(JoinFuncId::new(0), "test_func".to_string(), vec![]);
|
||||
|
||||
let v_cond = ValueId(1);
|
||||
let v_then_x = ValueId(2);
|
||||
let v_then_y = ValueId(3);
|
||||
let v_else_x = ValueId(4);
|
||||
let v_else_y = ValueId(5);
|
||||
let v_result_x = ValueId(6);
|
||||
let v_result_y = ValueId(7);
|
||||
|
||||
// const v1 = true
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_cond,
|
||||
value: ConstValue::Bool(true),
|
||||
}));
|
||||
|
||||
// const v2 = 1 (then x)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_then_x,
|
||||
value: ConstValue::Integer(1),
|
||||
}));
|
||||
|
||||
// const v3 = 2 (then y)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_then_y,
|
||||
value: ConstValue::Integer(2),
|
||||
}));
|
||||
|
||||
// const v4 = 3 (else x)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_else_x,
|
||||
value: ConstValue::Integer(3),
|
||||
}));
|
||||
|
||||
// const v5 = 4 (else y)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_else_y,
|
||||
value: ConstValue::Integer(4),
|
||||
}));
|
||||
|
||||
// if_merge v1 { v6=v2; v7=v3 } else { v6=v4; v7=v5 }
|
||||
func.body.push(JoinInst::IfMerge {
|
||||
cond: v_cond,
|
||||
merges: vec![
|
||||
crate::mir::join_ir::MergePair {
|
||||
dst: v_result_x,
|
||||
then_val: v_then_x,
|
||||
else_val: v_else_x,
|
||||
},
|
||||
crate::mir::join_ir::MergePair {
|
||||
dst: v_result_y,
|
||||
then_val: v_then_y,
|
||||
else_val: v_else_y,
|
||||
},
|
||||
],
|
||||
k_next: None,
|
||||
});
|
||||
|
||||
// return v6 + v7
|
||||
let v_sum = ValueId(8);
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: v_sum,
|
||||
op: crate::mir::join_ir::BinOpKind::Add,
|
||||
lhs: v_result_x,
|
||||
rhs: v_result_y,
|
||||
}));
|
||||
|
||||
func.body.push(JoinInst::Ret { value: Some(v_sum) });
|
||||
|
||||
module.add_function(func);
|
||||
|
||||
let mut vm = MirInterpreter::new();
|
||||
let result = run_joinir_function(&mut vm, &module, JoinFuncId::new(0), &[]).unwrap();
|
||||
|
||||
assert_eq!(result, JoinValue::Int(3)); // 1 + 2 = 3
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if_merge_false() {
|
||||
// if false { x=1; y=2 } else { x=3; y=4 }
|
||||
// expected: x=3, y=4
|
||||
let mut module = JoinModule::new();
|
||||
let mut func = JoinFunction::new(JoinFuncId::new(0), "test_func".to_string(), vec![]);
|
||||
|
||||
let v_cond = ValueId(1);
|
||||
let v_then_x = ValueId(2);
|
||||
let v_then_y = ValueId(3);
|
||||
let v_else_x = ValueId(4);
|
||||
let v_else_y = ValueId(5);
|
||||
let v_result_x = ValueId(6);
|
||||
let v_result_y = ValueId(7);
|
||||
|
||||
// const v1 = false
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_cond,
|
||||
value: ConstValue::Bool(false),
|
||||
}));
|
||||
|
||||
// const v2 = 1 (then x)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_then_x,
|
||||
value: ConstValue::Integer(1),
|
||||
}));
|
||||
|
||||
// const v3 = 2 (then y)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_then_y,
|
||||
value: ConstValue::Integer(2),
|
||||
}));
|
||||
|
||||
// const v4 = 3 (else x)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_else_x,
|
||||
value: ConstValue::Integer(3),
|
||||
}));
|
||||
|
||||
// const v5 = 4 (else y)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_else_y,
|
||||
value: ConstValue::Integer(4),
|
||||
}));
|
||||
|
||||
// if_merge v1 { v6=v2; v7=v3 } else { v6=v4; v7=v5 }
|
||||
func.body.push(JoinInst::IfMerge {
|
||||
cond: v_cond,
|
||||
merges: vec![
|
||||
crate::mir::join_ir::MergePair {
|
||||
dst: v_result_x,
|
||||
then_val: v_then_x,
|
||||
else_val: v_else_x,
|
||||
},
|
||||
crate::mir::join_ir::MergePair {
|
||||
dst: v_result_y,
|
||||
then_val: v_then_y,
|
||||
else_val: v_else_y,
|
||||
},
|
||||
],
|
||||
k_next: None,
|
||||
});
|
||||
|
||||
// return v6 + v7
|
||||
let v_sum = ValueId(8);
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: v_sum,
|
||||
op: crate::mir::join_ir::BinOpKind::Add,
|
||||
lhs: v_result_x,
|
||||
rhs: v_result_y,
|
||||
}));
|
||||
|
||||
func.body.push(JoinInst::Ret { value: Some(v_sum) });
|
||||
|
||||
module.add_function(func);
|
||||
|
||||
let mut vm = MirInterpreter::new();
|
||||
let result = run_joinir_function(&mut vm, &module, JoinFuncId::new(0), &[]).unwrap();
|
||||
|
||||
assert_eq!(result, JoinValue::Int(7)); // 3 + 4 = 7
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if_merge_multiple() {
|
||||
// if true { x=10; y=20; z=30 } else { x=1; y=2; z=3 }
|
||||
// expected: x=10, y=20, z=30 → sum=60
|
||||
let mut module = JoinModule::new();
|
||||
let mut func = JoinFunction::new(JoinFuncId::new(0), "test_func".to_string(), vec![]);
|
||||
|
||||
let v_cond = ValueId(1);
|
||||
let v_then_x = ValueId(2);
|
||||
let v_then_y = ValueId(3);
|
||||
let v_then_z = ValueId(4);
|
||||
let v_else_x = ValueId(5);
|
||||
let v_else_y = ValueId(6);
|
||||
let v_else_z = ValueId(7);
|
||||
let v_result_x = ValueId(8);
|
||||
let v_result_y = ValueId(9);
|
||||
let v_result_z = ValueId(10);
|
||||
|
||||
// const v1 = true
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_cond,
|
||||
value: ConstValue::Bool(true),
|
||||
}));
|
||||
|
||||
// then values
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_then_x,
|
||||
value: ConstValue::Integer(10),
|
||||
}));
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_then_y,
|
||||
value: ConstValue::Integer(20),
|
||||
}));
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_then_z,
|
||||
value: ConstValue::Integer(30),
|
||||
}));
|
||||
|
||||
// else values
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_else_x,
|
||||
value: ConstValue::Integer(1),
|
||||
}));
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_else_y,
|
||||
value: ConstValue::Integer(2),
|
||||
}));
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_else_z,
|
||||
value: ConstValue::Integer(3),
|
||||
}));
|
||||
|
||||
// if_merge with 3 variables
|
||||
func.body.push(JoinInst::IfMerge {
|
||||
cond: v_cond,
|
||||
merges: vec![
|
||||
crate::mir::join_ir::MergePair {
|
||||
dst: v_result_x,
|
||||
then_val: v_then_x,
|
||||
else_val: v_else_x,
|
||||
},
|
||||
crate::mir::join_ir::MergePair {
|
||||
dst: v_result_y,
|
||||
then_val: v_then_y,
|
||||
else_val: v_else_y,
|
||||
},
|
||||
crate::mir::join_ir::MergePair {
|
||||
dst: v_result_z,
|
||||
then_val: v_then_z,
|
||||
else_val: v_else_z,
|
||||
},
|
||||
],
|
||||
k_next: None,
|
||||
});
|
||||
|
||||
// return x + y + z
|
||||
let v_sum_xy = ValueId(11);
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: v_sum_xy,
|
||||
op: crate::mir::join_ir::BinOpKind::Add,
|
||||
lhs: v_result_x,
|
||||
rhs: v_result_y,
|
||||
}));
|
||||
|
||||
let v_sum_xyz = ValueId(12);
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: v_sum_xyz,
|
||||
op: crate::mir::join_ir::BinOpKind::Add,
|
||||
lhs: v_sum_xy,
|
||||
rhs: v_result_z,
|
||||
}));
|
||||
|
||||
func.body.push(JoinInst::Ret { value: Some(v_sum_xyz) });
|
||||
|
||||
module.add_function(func);
|
||||
|
||||
let mut vm = MirInterpreter::new();
|
||||
let result = run_joinir_function(&mut vm, &module, JoinFuncId::new(0), &[]).unwrap();
|
||||
|
||||
assert_eq!(result, JoinValue::Int(60)); // 10 + 20 + 30 = 60
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,6 +210,19 @@ fn convert_join_function_to_mir(
|
||||
let mir_inst = convert_mir_like_inst(mir_like)?;
|
||||
current_instructions.push(mir_inst);
|
||||
}
|
||||
JoinInst::MethodCall { dst, receiver, method, args } => {
|
||||
// Phase 34-6: MethodCall → MIR BoxCall 変換
|
||||
// receiver.method(args...) を BoxCall(receiver, method, args) に変換
|
||||
let mir_inst = MirInstruction::BoxCall {
|
||||
dst: Some(*dst),
|
||||
box_val: *receiver,
|
||||
method: method.clone(),
|
||||
method_id: None,
|
||||
args: args.clone(),
|
||||
effects: EffectMask::PURE,
|
||||
};
|
||||
current_instructions.push(mir_inst);
|
||||
}
|
||||
JoinInst::Call {
|
||||
func,
|
||||
args,
|
||||
@ -386,6 +399,78 @@ fn convert_join_function_to_mir(
|
||||
current_block_id = merge_block;
|
||||
current_instructions = Vec::new();
|
||||
}
|
||||
// Phase 33-6: IfMerge instruction conversion to MIR
|
||||
JoinInst::IfMerge { cond, merges, k_next } => {
|
||||
// Phase 33-6: IfMerge を MIR の if/phi に変換
|
||||
// Select と同じ 4 ブロック構造だが、複数の Copy を生成
|
||||
|
||||
// Phase 33-6 最小実装: k_next は None のみサポート
|
||||
if k_next.is_some() {
|
||||
return Err(JoinIrVmBridgeError::new(
|
||||
"IfMerge: k_next continuation is not yet supported (Phase 33-6 minimal)".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
debug_log!(
|
||||
"[joinir_vm_bridge] Converting IfMerge: cond={:?}, merges.len()={}",
|
||||
cond, merges.len()
|
||||
);
|
||||
|
||||
// 1. cond ブロック(現在のブロック)
|
||||
let cond_block = current_block_id;
|
||||
|
||||
// 2. then ブロック作成
|
||||
let then_block = BasicBlockId(next_block_id);
|
||||
next_block_id += 1;
|
||||
|
||||
// 3. else ブロック作成
|
||||
let else_block = BasicBlockId(next_block_id);
|
||||
next_block_id += 1;
|
||||
|
||||
// 4. merge ブロック作成
|
||||
let merge_block = BasicBlockId(next_block_id);
|
||||
next_block_id += 1;
|
||||
|
||||
// 5. cond ブロックで分岐
|
||||
let branch_terminator = MirInstruction::Branch {
|
||||
condition: *cond,
|
||||
then_bb: then_block,
|
||||
else_bb: else_block,
|
||||
};
|
||||
finalize_block(&mut mir_func, cond_block, current_instructions, branch_terminator);
|
||||
|
||||
// 6. then ブロック: 各 merge について dst = then_val; jump merge
|
||||
let mut then_block_obj = crate::mir::BasicBlock::new(then_block);
|
||||
for merge in merges {
|
||||
then_block_obj.instructions.push(MirInstruction::Copy {
|
||||
dst: merge.dst,
|
||||
src: merge.then_val,
|
||||
});
|
||||
then_block_obj.instruction_spans.push(Span::unknown());
|
||||
}
|
||||
then_block_obj.terminator = Some(MirInstruction::Jump { target: merge_block });
|
||||
mir_func.blocks.insert(then_block, then_block_obj);
|
||||
|
||||
// 7. else ブロック: 各 merge について dst = else_val; jump merge
|
||||
let mut else_block_obj = crate::mir::BasicBlock::new(else_block);
|
||||
for merge in merges {
|
||||
else_block_obj.instructions.push(MirInstruction::Copy {
|
||||
dst: merge.dst,
|
||||
src: merge.else_val,
|
||||
});
|
||||
else_block_obj.instruction_spans.push(Span::unknown());
|
||||
}
|
||||
else_block_obj.terminator = Some(MirInstruction::Jump { target: merge_block });
|
||||
mir_func.blocks.insert(else_block, else_block_obj);
|
||||
|
||||
// 8. merge ブロック作成(空)
|
||||
let merge_block_obj = crate::mir::BasicBlock::new(merge_block);
|
||||
mir_func.blocks.insert(merge_block, merge_block_obj);
|
||||
|
||||
// 9. merge ブロックに移動
|
||||
current_block_id = merge_block;
|
||||
current_instructions = Vec::new();
|
||||
}
|
||||
JoinInst::Ret { value } => {
|
||||
// Phase 30.x: Return terminator (separate from instructions)
|
||||
let return_terminator = MirInstruction::Return { value: *value };
|
||||
|
||||
@ -499,6 +499,17 @@ impl NyashRunner {
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 33-10.0: If lowering ドライラン統合(箱化版)
|
||||
// HAKO_JOINIR_IF_SELECT=1 で有効化、IfLoweringDryRunner を使用
|
||||
if crate::config::env::joinir_if_select_enabled() {
|
||||
let debug_level = crate::config::env::joinir_debug_level();
|
||||
let runner = crate::mir::join_ir::lowering::if_dry_runner::IfLoweringDryRunner::new(
|
||||
debug_level,
|
||||
);
|
||||
let stats = runner.scan_module(&module_vm.functions);
|
||||
runner.print_stats(&stats);
|
||||
}
|
||||
|
||||
// Phase 30 F-4.4: JoinIR VM Bridge experimental path (consolidated dispatch)
|
||||
// Activated when NYASH_JOINIR_EXPERIMENT=1 AND NYASH_JOINIR_VM_BRIDGE=1
|
||||
// Routing logic is centralized in join_ir_vm_bridge_dispatch module
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
use crate::mir::join_ir::frontend::AstToJoinIrLowerer;
|
||||
use crate::mir::join_ir_runner::{run_joinir_function, JoinValue};
|
||||
use crate::mir::join_ir_vm_bridge::run_joinir_via_vm;
|
||||
|
||||
/// Phase 34-2: IfSelect simple pattern の A/B テスト
|
||||
///
|
||||
@ -138,11 +139,11 @@ fn joinir_frontend_if_select_local_ab_test() {
|
||||
// 「値としての if」の本質が Select であることを確認
|
||||
}
|
||||
|
||||
/// Phase 34-4: JsonShapeToMap._read_value_from_pair/1 の A/B テスト
|
||||
/// Phase 34-6: JsonShapeToMap._read_value_from_pair/1 の完全実装テスト
|
||||
///
|
||||
/// 入力: `fixtures/json_shape_read_value.program.json`
|
||||
/// パターン: `if at { return 10 } else { return 20 }` (簡略版・構造確認)
|
||||
/// 期待: Phase 34-2/34-3 と同じ JoinIR 出力(Select ベース)
|
||||
/// パターン: `if at { return v.substring(0, at) } else { return v }`
|
||||
/// 期待: 本物の substring 呼び出しが JoinIR MethodCall → MIR BoxCall で実行される
|
||||
#[test]
|
||||
fn joinir_frontend_json_shape_read_value_ab_test() {
|
||||
// フィクスチャ読み込み
|
||||
@ -157,7 +158,7 @@ fn joinir_frontend_json_shape_read_value_ab_test() {
|
||||
let join_module = lowerer.lower_program_json(&program_json);
|
||||
|
||||
// デバッグ: JoinIR Module の内容を確認
|
||||
eprintln!("=== JoinIR Module (json_shape_read_value) ===");
|
||||
eprintln!("=== JoinIR Module (json_shape_read_value - Phase 34-6) ===");
|
||||
eprintln!("Entry: {:?}", join_module.entry);
|
||||
for (func_id, func) in &join_module.functions {
|
||||
eprintln!("\nFunction {:?}: {}", func_id, func.name);
|
||||
@ -168,38 +169,35 @@ fn joinir_frontend_json_shape_read_value_ab_test() {
|
||||
}
|
||||
}
|
||||
|
||||
// JoinIR Runner で実行
|
||||
let mut vm = crate::backend::mir_interpreter::MirInterpreter::new();
|
||||
// Phase 34-6: JoinIR→MIR→VM ブリッジ経由で実行(MethodCall サポート)
|
||||
|
||||
// at = 1 (truthy) の場合
|
||||
let result_true = run_joinir_function(
|
||||
&mut vm,
|
||||
// テストケース 1: v="hello", at=3 → "hel" (substring)
|
||||
let result_substring = run_joinir_via_vm(
|
||||
&join_module,
|
||||
join_module.entry.unwrap(),
|
||||
&[JoinValue::Int(1)],
|
||||
&[JoinValue::Str("hello".to_string()), JoinValue::Int(3)],
|
||||
)
|
||||
.expect("Failed to run JoinIR function (at=1)");
|
||||
.expect("Failed to run JoinIR function (v=\"hello\", at=3)");
|
||||
|
||||
// at = 0 (falsy) の場合
|
||||
let result_false = run_joinir_function(
|
||||
&mut vm,
|
||||
// テストケース 2: v="world", at=0 → "world" (v そのまま)
|
||||
let result_original = run_joinir_via_vm(
|
||||
&join_module,
|
||||
join_module.entry.unwrap(),
|
||||
&[JoinValue::Int(0)],
|
||||
&[JoinValue::Str("world".to_string()), JoinValue::Int(0)],
|
||||
)
|
||||
.expect("Failed to run JoinIR function (at=0)");
|
||||
.expect("Failed to run JoinIR function (v=\"world\", at=0)");
|
||||
|
||||
// 検証: at=1 → 10, at=0 → 20 (簡略版・構造確認)
|
||||
match result_true {
|
||||
JoinValue::Int(v) => assert_eq!(v, 10, "at=1 should return 10"),
|
||||
_ => panic!("Expected Int, got {:?}", result_true),
|
||||
// 検証: substring 呼び出し結果
|
||||
match result_substring {
|
||||
JoinValue::Str(s) => assert_eq!(s, "hel", "v.substring(0, 3) should return \"hel\""),
|
||||
_ => panic!("Expected Str, got {:?}", result_substring),
|
||||
}
|
||||
|
||||
match result_false {
|
||||
JoinValue::Int(v) => assert_eq!(v, 20, "at=0 should return 20"),
|
||||
_ => panic!("Expected Int, got {:?}", result_false),
|
||||
// 検証: 元の文字列そのまま
|
||||
match result_original {
|
||||
JoinValue::Str(s) => assert_eq!(s, "world", "v should return \"world\""),
|
||||
_ => panic!("Expected Str, got {:?}", result_original),
|
||||
}
|
||||
|
||||
// Phase 34-4: Stage-1/meta 実用関数でも simple pattern が Select JoinIR に正規化されることを実証
|
||||
// 構造確認フェーズ(Method 呼び出し意味論は Phase 34-5 で対応)
|
||||
// Phase 34-6: 本物の Method 呼び出し意味論が JoinIR MethodCall → MIR BoxCall で実行されることを実証
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user