Files
hakorune/src/mir/join_ir_runner.rs
nyash-codex 8f736358c4 feat(joinir): Phase 63-4 infer_type_from_phi degradation design
Phase 63-4: infer_type_from_phi を『JoinIR 型ヒント優先+従来ロジックフォールバック』に縮退する仕様を設計(実装は Phase 63-5+)

## Changes

### Documentation Updates
- **README.md**: Added complete Phase 63-4 design (63-4.1 through 63-4.5)
  - 63-4.1: Current state analysis (definition location, callsites, role, JoinIR preparation)
  - 63-4.2: Degradation spec (type_hint priority + fallback pattern)
  - 63-4.3: Representative cases and A/B testing strategy (P1/P2/P3)
  - 63-4.4: Deletion conditions (5 conditions, current: 2/5 = 40%)
  - 63-4.5: Phase 63-5 handoff (infer_type_from_phi_with_hint() implementation tasks)

- **PHI_BOX_INVENTORY.md**: Updated if_phi.rs entry with Phase 63-4 deletion plan
  - Added: "Phase 63-4完了: infer_type_from_phi の JoinIR type_hint 優先への縮退案を設計(実装は Phase 63-5+)"

- **CURRENT_TASK.md**: Added Phase 63-4 section with summary of design work

## Design Highlights

### Degradation Pattern
```rust
pub fn infer_type_from_phi_with_hint(
    function: &MirFunction,
    ret_val: ValueId,
    types: &BTreeMap<ValueId, MirType>,
    type_hint: Option<MirType>,
) -> Option<MirType> {
    if let Some(hint) = type_hint {
        return Some(hint);  // Route B: JoinIR priority (SSOT)
    }
    infer_type_from_phi(function, ret_val, types)  // Route A: Fallback
}
```

### Representative Cases
- **P1**: IfSelectTest.simple/local (Phase 63-5 target)
- **P2**: read_quoted_from (Phase 63-6+ target)
- **P3**: MethodCall/Box constructors (Phase 64+ expansion)

### Deletion Conditions (2/5 achieved)
1.  JoinIR has type_hint field (Phase 63-3)
2.  Type hints populated for representative cases (Phase 63-2)
3.  Degraded to type_hint priority (Phase 63-5)
4.  P1 cases determined by type_hint only (Phase 63-5)
5.  All functions use type hints (Phase 64+)

## Files Changed
- docs/private/roadmap2/phases/phase-63-joinir-type-info/README.md
- docs/private/roadmap2/phases/phase-30-final-joinir-world/PHI_BOX_INVENTORY.md
- CURRENT_TASK.md

## Next Steps
Phase 63-5: Implement degradation for P1 cases (IfSelectTest.simple/local)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 17:58:06 +09:00

829 lines
29 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! JoinIR Runner - Development Harness (Structure Validation Only)
//!
//! # Two Routes
//!
//! ## Route A: JoinIR→MIR→VM (Recommended SSOT)
//! - Full semantic validation via MIR lowering pipeline
//! - Tests should use `JoinIrFrontendTestRunner` or `run_joinir_via_vm`
//! - Examples: Phase 34 tests (IfSelect, Loop, Break, Continue)
//! - **Use this route for ALL semantic tests**
//!
//! ## Route B: Direct JoinIR Runner (Structure Validation)
//! - For structure-only validation of JoinIR constructs
//! - Use `run_joinir_function` only when Route A is insufficient
//! - Examples: Handwritten JoinIR module tests, low-level instruction tests
//! - Note: Some operations (e.g., MethodCall) may be unimplemented in Runner
//!
//! # Phase 35-4 Unification Strategy
//! All semantic tests migrated to Route A. Route B kept only for fundamental
//! structure validation that cannot be verified through MIR→VM path.
//!
//! # Original Purpose (Phase 27.2)
//! hand-written / minimal JoinIR を VM と A/B 比較するための軽量ランナー。
//! - 対応値: i64 / bool / String / Unit
//! - 対応命令: Const / BinOp / Compare / BoxCall(StringBox: length, substring) /
//! Call / Jump / Ret
//!
//! Phase 27.8: ops box 統合
//! - JoinValue / JoinIrOpError は join_ir_ops から再エクスポート
//! - eval_binop() / eval_compare() を使用(実装を一箇所に集約)
use std::collections::HashMap;
use crate::mir::join_ir::{ConstValue, JoinFuncId, JoinInst, JoinModule, MirLikeInst, VarId};
// Phase 27.8: ops box からの再エクスポート
pub use crate::mir::join_ir_ops::{JoinIrOpError, JoinValue};
// Phase 27.8: 互換性のため JoinRuntimeError を JoinIrOpError の別名として保持
pub type JoinRuntimeError = JoinIrOpError;
pub fn run_joinir_function(
vm: &mut crate::backend::mir_interpreter::MirInterpreter,
module: &JoinModule,
entry: JoinFuncId,
args: &[JoinValue],
) -> Result<JoinValue, JoinRuntimeError> {
execute_function(vm, module, entry, args.to_vec())
}
fn execute_function(
vm: &mut crate::backend::mir_interpreter::MirInterpreter,
module: &JoinModule,
mut current_func: JoinFuncId,
mut current_args: Vec<JoinValue>,
) -> Result<JoinValue, JoinRuntimeError> {
'exec: loop {
let func = module.functions.get(&current_func).ok_or_else(|| {
JoinRuntimeError::new(format!("Function {:?} not found", current_func))
})?;
if func.params.len() != current_args.len() {
return Err(JoinRuntimeError::new(format!(
"Arity mismatch for {:?}: expected {}, got {}",
func.id,
func.params.len(),
current_args.len()
)));
}
let mut locals: HashMap<VarId, JoinValue> = HashMap::new();
for (param, arg) in func.params.iter().zip(current_args.iter()) {
locals.insert(*param, arg.clone());
}
let mut ip = 0usize;
while ip < func.body.len() {
match &func.body[ip] {
JoinInst::Compute(inst) => {
eval_compute(vm, inst, &mut locals)?;
ip += 1;
}
JoinInst::Call {
func: target,
args,
k_next,
dst,
} => {
if k_next.is_some() {
return Err(JoinRuntimeError::new(
"Join continuation (k_next) is not supported in the experimental runner",
));
}
let resolved_args = materialize_args(args, &locals)?;
if let Some(dst_var) = dst {
let value = execute_function(vm, module, *target, resolved_args)?;
locals.insert(*dst_var, value);
ip += 1;
} else {
current_func = *target;
current_args = resolved_args;
continue 'exec;
}
}
JoinInst::Jump {
cont: _,
args,
cond,
} => {
let should_jump = match cond {
Some(var) => as_bool(&read_var(&locals, *var)?)?,
None => true,
};
if should_jump {
let ret = if let Some(first) = args.first() {
read_var(&locals, *first)?
} else {
JoinValue::Unit
};
return Ok(ret);
}
ip += 1;
}
JoinInst::Ret { value } => {
let ret = match value {
Some(var) => read_var(&locals, *var)?,
None => JoinValue::Unit,
};
return Ok(ret);
}
// Phase 33: Select instruction execution
JoinInst::Select {
dst,
cond,
then_val,
else_val,
type_hint: _, // Phase 63-3: 実行時は未使用
} => {
// 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
_ => {
return Err(JoinRuntimeError::new(format!(
"Select: cond must be Bool or Int, got {:?}",
cond_value
)))
}
};
// 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)"
));
}
// Phase 56: ConditionalMethodCall instruction execution
JoinInst::ConditionalMethodCall { .. } => {
// Phase 56: ConditionalMethodCall は JoinIR Runner では未対応
// JoinIR → MIR 変換経由で VM が実行する
return Err(JoinRuntimeError::new(
"ConditionalMethodCall is not supported in JoinIR Runner (use JoinIR→MIR→VM bridge instead)"
));
}
// Phase 41-4: NestedIfMerge instruction execution
JoinInst::NestedIfMerge { .. } => {
// Phase 41-4: NestedIfMerge は JoinIR Runner では未対応
// JoinIR → MIR 変換経由で VM が実行する
return Err(JoinRuntimeError::new(
"NestedIfMerge is not supported in JoinIR Runner (use JoinIR→MIR→VM bridge instead)"
));
}
// Phase 51: FieldAccess instruction execution
JoinInst::FieldAccess { .. } => {
// Phase 51: FieldAccess は JoinIR Runner では未対応
// JoinIR → MIR 変換経由で VM が実行する
return Err(JoinRuntimeError::new(
"FieldAccess is not supported in JoinIR Runner (use JoinIR→MIR→VM bridge instead)"
));
}
// Phase 51: NewBox instruction execution
JoinInst::NewBox { .. } => {
// Phase 51: NewBox は JoinIR Runner では未対応
// JoinIR → MIR 変換経由で VM が実行する
return Err(JoinRuntimeError::new(
"NewBox is not supported in JoinIR Runner (use JoinIR→MIR→VM bridge instead)"
));
}
}
}
// fallthrough without explicit return
return Ok(JoinValue::Unit);
}
}
fn eval_compute(
vm: &mut crate::backend::mir_interpreter::MirInterpreter,
inst: &MirLikeInst,
locals: &mut HashMap<VarId, JoinValue>,
) -> Result<(), JoinRuntimeError> {
match inst {
MirLikeInst::Const { dst, value } => {
let v = match value {
ConstValue::Integer(i) => JoinValue::Int(*i),
ConstValue::Bool(b) => JoinValue::Bool(*b),
ConstValue::String(s) => JoinValue::Str(s.clone()),
ConstValue::Null => JoinValue::Unit,
};
locals.insert(*dst, v);
}
MirLikeInst::BinOp { dst, op, lhs, rhs } => {
// Phase 27.8: ops box の eval_binop() を使用
let l = read_var(locals, *lhs)?;
let r = read_var(locals, *rhs)?;
let v = crate::mir::join_ir_ops::eval_binop(*op, &l, &r)?;
locals.insert(*dst, v);
}
MirLikeInst::Compare { dst, op, lhs, rhs } => {
// Phase 27.8: ops box の eval_compare() を使用
let l = read_var(locals, *lhs)?;
let r = read_var(locals, *rhs)?;
let v = crate::mir::join_ir_ops::eval_compare(*op, &l, &r)?;
locals.insert(*dst, v);
}
// S-5.2-improved: BoxCall → VM execute_box_call ラッパー経由
// - 制御フロー: JoinIR Runner が担当
// - Box/Plugin 実装: Rust VM に完全委譲VM 2号機を避ける
// - VM の完全な BoxCall 意味論を使用:
// * Void guards (Void.length() → 0)
// * PluginBox サポート (FileBox, NetBox)
// * InstanceBox policy checks
// * object_fields handling
// * Method re-routing (toString→str)
MirLikeInst::BoxCall {
dst,
box_name: _, // box_name は VM が内部で判定するため不要
method,
args,
} => {
// First argument is the receiver (box instance)
if args.is_empty() {
return Err(JoinRuntimeError::new(
"BoxCall requires at least a receiver argument",
));
}
// Convert receiver to VMValue
let receiver_jv = read_var(locals, args[0])?;
let receiver_vm = receiver_jv.to_vm_value();
// Convert remaining arguments to VMValue
let method_args_vm: Vec<crate::backend::VMValue> = args[1..]
.iter()
.map(|&var_id| read_var(locals, var_id).map(|jv| jv.to_vm_value()))
.collect::<Result<Vec<_>, _>>()?;
// Invoke VM's execute_box_call for complete semantics
let result_vm = vm
.execute_box_call(receiver_vm, method, method_args_vm)
.map_err(|e| JoinRuntimeError::new(format!("BoxCall failed: {}", e)))?;
// Convert result back to JoinValue
let result_jv = crate::mir::join_ir_ops::JoinValue::from_vm_value(&result_vm)?;
// Store result if destination is specified
if let Some(dst_var) = dst {
locals.insert(*dst_var, result_jv);
}
}
// Phase 56: UnaryOp
MirLikeInst::UnaryOp { dst, op, operand } => {
let operand_val = read_var(locals, *operand)?;
let result = match op {
crate::mir::join_ir::UnaryOp::Not => match operand_val {
JoinValue::Bool(b) => JoinValue::Bool(!b),
JoinValue::Int(i) => JoinValue::Bool(i == 0),
_ => {
return Err(JoinRuntimeError::new(format!(
"Cannot apply 'not' to {:?}",
operand_val
)))
}
},
crate::mir::join_ir::UnaryOp::Neg => match operand_val {
JoinValue::Int(i) => JoinValue::Int(-i),
_ => {
return Err(JoinRuntimeError::new(format!(
"Cannot apply '-' to {:?}",
operand_val
)))
}
},
};
locals.insert(*dst, result);
}
}
Ok(())
}
fn read_var(locals: &HashMap<VarId, JoinValue>, var: VarId) -> Result<JoinValue, JoinRuntimeError> {
locals
.get(&var)
.cloned()
.ok_or_else(|| JoinRuntimeError::new(format!("Variable {:?} not bound", var)))
}
fn materialize_args(
args: &[VarId],
locals: &HashMap<VarId, JoinValue>,
) -> Result<Vec<JoinValue>, JoinRuntimeError> {
args.iter().map(|v| read_var(locals, *v)).collect()
}
fn as_bool(value: &JoinValue) -> Result<bool, JoinRuntimeError> {
match value {
JoinValue::Bool(b) => Ok(*b),
JoinValue::Int(i) => Ok(*i != 0),
JoinValue::Unit => Ok(false),
other => Err(JoinRuntimeError::new(format!(
"Expected bool-compatible value, got {:?}",
other
))),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::backend::mir_interpreter::MirInterpreter;
use crate::mir::join_ir::{ConstValue, JoinFunction, JoinModule};
use crate::mir::ValueId;
#[test]
fn test_select_true() {
// let result = if true { 1 } else { 2 }
// expected: result == 1
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 = ValueId(2);
let v_else = ValueId(3);
let v_result = ValueId(4);
// const v1 = true
func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: v_cond,
value: ConstValue::Bool(true),
}));
// const v2 = 1
func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: v_then,
value: ConstValue::Integer(1),
}));
// const v3 = 2
func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: v_else,
value: ConstValue::Integer(2),
}));
// select v4 = v1 ? v2 : v3
func.body.push(JoinInst::Select {
dst: v_result,
cond: v_cond,
then_val: v_then,
else_val: v_else,
type_hint: None, // Phase 63-3
});
// return v4
func.body.push(JoinInst::Ret {
value: Some(v_result),
});
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(1));
}
#[test]
fn test_select_false() {
// let result = if false { 1 } else { 2 }
// expected: result == 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 = ValueId(2);
let v_else = ValueId(3);
let v_result = ValueId(4);
// const v1 = false
func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: v_cond,
value: ConstValue::Bool(false),
}));
// const v2 = 1
func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: v_then,
value: ConstValue::Integer(1),
}));
// const v3 = 2
func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: v_else,
value: ConstValue::Integer(2),
}));
// select v4 = v1 ? v2 : v3
func.body.push(JoinInst::Select {
dst: v_result,
cond: v_cond,
then_val: v_then,
else_val: v_else,
type_hint: None, // Phase 63-3
});
// return v4
func.body.push(JoinInst::Ret {
value: Some(v_result),
});
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(2));
}
#[test]
fn test_select_int_cond() {
// cond=Int(0) → false、Int(1) → true
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 = ValueId(2);
let v_else = ValueId(3);
let v_result = ValueId(4);
// const v1 = 0 (treated as false)
func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: v_cond,
value: ConstValue::Integer(0),
}));
// const v2 = 100
func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: v_then,
value: ConstValue::Integer(100),
}));
// const v3 = 200
func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: v_else,
value: ConstValue::Integer(200),
}));
// select v4 = v1 ? v2 : v3
func.body.push(JoinInst::Select {
dst: v_result,
cond: v_cond,
then_val: v_then,
else_val: v_else,
type_hint: None, // Phase 63-3
});
// return v4
func.body.push(JoinInst::Ret {
value: Some(v_result),
});
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(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,
type_hint: None, // Phase 63-3
},
crate::mir::join_ir::MergePair {
dst: v_result_y,
then_val: v_then_y,
else_val: v_else_y,
type_hint: None, // Phase 63-3
},
],
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,
type_hint: None, // Phase 63-3
},
crate::mir::join_ir::MergePair {
dst: v_result_y,
then_val: v_then_y,
else_val: v_else_y,
type_hint: None, // Phase 63-3
},
],
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,
type_hint: None, // Phase 63-3
},
crate::mir::join_ir::MergePair {
dst: v_result_y,
then_val: v_then_y,
else_val: v_else_y,
type_hint: None, // Phase 63-3
},
crate::mir::join_ir::MergePair {
dst: v_result_z,
then_val: v_then_z,
else_val: v_else_z,
type_hint: None, // Phase 63-3
},
],
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
}
}