Phase 34-7.5: Code organization improvements
- Added type conversion helpers (as_cont/as_func) in join_ir/mod.rs
- Enhanced docstrings for JoinCall/JoinJump with usage examples
- Improved error messages in join_ir_vm_bridge.rs
JoinIrFrontendTestRunner box implementation
- Created src/tests/helpers/joinir_frontend.rs (119 lines)
- Reduced test code by 83% (70 lines → 12 lines per test)
- 26% overall reduction in test file (284 → 209 lines)
Phase 34-8: Break/Continue pattern implementation
- Extended ast_lowerer.rs (+630 lines)
- lower_loop_break_pattern(): Break as Jump (early return)
- lower_loop_continue_pattern(): Continue as Select + Call
- Added Bool literal support in extract_value()
- Created 2 fixtures: loop_frontend_{break,continue}.program.json
- Added 2 A/B tests (all 6 Phase 34 tests PASS)
Technical achievements:
- Break = Jump (early return pattern)
- Continue = Select + Call (NOT Jump) - critical discovery
- 3-function structure sufficient (no k_continue needed)
- SSA-style re-assignment with natural var_map updates
Test results:
✅ joinir_frontend_if_select_simple_ab_test
✅ joinir_frontend_if_select_local_ab_test
✅ joinir_frontend_json_shape_read_value_ab_test
✅ joinir_frontend_loop_simple_ab_test
✅ joinir_frontend_loop_break_ab_test
✅ joinir_frontend_loop_continue_ab_test
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
684 lines
26 KiB
Rust
684 lines
26 KiB
Rust
//! Phase 27-shortterm S-4: JoinIR → Rust VM Bridge
|
||
//!
|
||
//! 目的: JoinIR(正規化された IR)を Rust VM で実行するブリッジ層
|
||
//!
|
||
//! ## Architecture
|
||
//! ```text
|
||
//! JoinIR (normalized) → MirModule → Rust VM → Result
|
||
//! ↑ ↑ ↑
|
||
//! PHI bugs VM input Execution
|
||
//! eliminated format (GC, plugins)
|
||
//! ```
|
||
//!
|
||
//! ## Design Principles
|
||
//! - JoinIR の正規化構造を保持したまま VM に渡す
|
||
//! - マッピングだけで済ませる(JoinIR でやった正規化は消えない)
|
||
//! - VM の機能(GC、プラグイン、エラーハンドリング)を活用
|
||
//!
|
||
//! ## Minimal Instruction Set (S-4.3)
|
||
//! - **Compute**: Const, BinOp, Compare
|
||
//! - **BoxCall**: StringBox メソッド呼び出し
|
||
//! - **Call/Jump/Ret**: 制御フロー
|
||
//!
|
||
//! Phase 27-shortterm scope: skip_ws で green 化できれば成功
|
||
|
||
use crate::backend::{MirInterpreter, VMError, VMValue};
|
||
use crate::config::env::joinir_vm_bridge_debug;
|
||
use crate::mir::join_ir::{
|
||
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinInst, JoinModule, MirLikeInst,
|
||
};
|
||
use crate::mir::join_ir_ops::JoinValue;
|
||
use crate::ast::Span;
|
||
use crate::mir::{
|
||
BasicBlockId, BinaryOp, CompareOp as MirCompareOp, ConstValue as MirConstValue, EffectMask,
|
||
FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId,
|
||
};
|
||
|
||
/// 条件付きデバッグ出力マクロ(NYASH_JOINIR_VM_BRIDGE_DEBUG=1 で有効)
|
||
macro_rules! debug_log {
|
||
($($arg:tt)*) => {
|
||
if joinir_vm_bridge_debug() {
|
||
eprintln!($($arg)*);
|
||
}
|
||
};
|
||
}
|
||
|
||
/// Phase 27-shortterm S-4 エラー型
|
||
#[derive(Debug, Clone)]
|
||
pub struct JoinIrVmBridgeError {
|
||
pub message: String,
|
||
}
|
||
|
||
impl JoinIrVmBridgeError {
|
||
pub fn new(msg: impl Into<String>) -> Self {
|
||
Self {
|
||
message: msg.into(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl From<VMError> for JoinIrVmBridgeError {
|
||
fn from(err: VMError) -> Self {
|
||
JoinIrVmBridgeError::new(format!("VM error: {:?}", err))
|
||
}
|
||
}
|
||
|
||
/// ブロックを確定する(instructions + spans + terminator を設定)
|
||
fn finalize_block(
|
||
mir_func: &mut MirFunction,
|
||
block_id: BasicBlockId,
|
||
instructions: Vec<MirInstruction>,
|
||
terminator: MirInstruction,
|
||
) {
|
||
if let Some(block) = mir_func.blocks.get_mut(&block_id) {
|
||
let inst_count = instructions.len();
|
||
block.instructions = instructions;
|
||
block.instruction_spans = vec![Span::unknown(); inst_count];
|
||
block.terminator = Some(terminator);
|
||
}
|
||
}
|
||
|
||
/// JoinFuncId から MIR 用の関数名を生成
|
||
fn join_func_name(id: JoinFuncId) -> String {
|
||
format!("join_func_{}", id.0)
|
||
}
|
||
|
||
/// Phase 27-shortterm S-4.3: JoinIR → VM 実行のエントリーポイント
|
||
///
|
||
/// ## Arguments
|
||
/// - `join_module`: JoinIR モジュール(正規化済み)
|
||
/// - `entry_func`: エントリーポイント関数ID
|
||
/// - `args`: 初期引数(JoinValue 形式)
|
||
///
|
||
/// ## Returns
|
||
/// - `Ok(JoinValue)`: 実行結果
|
||
/// - `Err(JoinIrVmBridgeError)`: 変換エラーまたは実行エラー
|
||
///
|
||
/// ## Example
|
||
/// ```ignore
|
||
/// let join_module = lower_skip_ws_to_joinir(&mir_module)?;
|
||
/// let result = run_joinir_via_vm(
|
||
/// &join_module,
|
||
/// JoinFuncId::new(0),
|
||
/// &[JoinValue::Str(" hello".to_string()), JoinValue::Int(7)]
|
||
/// )?;
|
||
/// assert_eq!(result, JoinValue::Int(2));
|
||
/// ```
|
||
pub fn run_joinir_via_vm(
|
||
join_module: &JoinModule,
|
||
entry_func: JoinFuncId,
|
||
args: &[JoinValue],
|
||
) -> Result<JoinValue, JoinIrVmBridgeError> {
|
||
debug_log!("[joinir_vm_bridge] Phase 27-shortterm S-4.3");
|
||
debug_log!("[joinir_vm_bridge] Converting JoinIR to MIR for VM execution");
|
||
|
||
// Step 1: JoinIR → MIR 変換
|
||
let mir_module = convert_joinir_to_mir(join_module)?;
|
||
|
||
debug_log!(
|
||
"[joinir_vm_bridge] Converted {} JoinIR functions to MIR",
|
||
join_module.functions.len()
|
||
);
|
||
|
||
// Step 2: VM 実行
|
||
let mut vm = MirInterpreter::new();
|
||
|
||
debug_log!(
|
||
"[joinir_vm_bridge] Executing via VM with {} arguments",
|
||
args.len()
|
||
);
|
||
|
||
// Convert JoinValue → VMValue (BoxRef 含む)
|
||
let vm_args: Vec<VMValue> = args.iter().cloned().map(|v| v.into_vm_value()).collect();
|
||
|
||
let entry_name = join_func_name(entry_func);
|
||
let result = vm.execute_function_with_args(&mir_module, &entry_name, &vm_args)?;
|
||
|
||
// Step 3: VMValue → JoinValue 変換
|
||
let join_result = JoinValue::from_vm_value(&result)
|
||
.map_err(|e| JoinIrVmBridgeError::new(format!("Result conversion error: {}", e.message)))?;
|
||
|
||
debug_log!("[joinir_vm_bridge] Execution succeeded: {:?}", join_result);
|
||
|
||
Ok(join_result)
|
||
}
|
||
|
||
/// Phase 30.x: JoinIR → MIR 変換器
|
||
///
|
||
/// Phase 32 L-2.2 Step-3: テストから呼び出し可能に `pub(crate)` 化
|
||
pub(crate) fn convert_joinir_to_mir(join_module: &JoinModule) -> Result<MirModule, JoinIrVmBridgeError> {
|
||
let mut mir_module = MirModule::new("joinir_bridge".to_string());
|
||
|
||
// Convert all JoinIR functions to MIR (entry function becomes "skip" or similar)
|
||
for (func_id, join_func) in &join_module.functions {
|
||
debug_log!(
|
||
"[joinir_vm_bridge] Converting JoinFunction {} ({})",
|
||
func_id.0, join_func.name
|
||
);
|
||
|
||
let mir_func = convert_join_function_to_mir(join_func)?;
|
||
|
||
// Use actual function name (not "main") since we'll create a wrapper
|
||
mir_module.functions.insert(join_func_name(*func_id), mir_func);
|
||
}
|
||
|
||
Ok(mir_module)
|
||
}
|
||
|
||
/// JoinFunction → MirFunction 変換
|
||
fn convert_join_function_to_mir(
|
||
join_func: &crate::mir::join_ir::JoinFunction,
|
||
) -> Result<MirFunction, JoinIrVmBridgeError> {
|
||
// Phase 27-shortterm S-4.4: skip_ws パターン対応版
|
||
// - Call (tail call): MIR Call に変換
|
||
// - Jump (conditional exit): Branch + Return に変換
|
||
let entry_block = BasicBlockId(0);
|
||
|
||
// Create minimal FunctionSignature for JoinIR function
|
||
// Phase 27-shortterm: すべて MirType::Unknown として扱う(型情報は JoinIR に無いため)
|
||
let param_types = join_func
|
||
.params
|
||
.iter()
|
||
.map(|_| MirType::Unknown)
|
||
.collect::<Vec<_>>();
|
||
|
||
let signature = FunctionSignature {
|
||
name: join_func.name.clone(),
|
||
params: param_types,
|
||
return_type: MirType::Unknown,
|
||
effects: EffectMask::PURE,
|
||
};
|
||
|
||
let mut mir_func = MirFunction::new(signature, entry_block);
|
||
|
||
// Phase 30.x: Set parameter ValueIds from JoinIR function
|
||
// JoinIR's VarId is an alias for ValueId, so direct copy works
|
||
mir_func.params = join_func.params.clone();
|
||
|
||
// Phase 27-shortterm S-4.4: Multi-block conversion for Jump instructions
|
||
// Strategy:
|
||
// - Accumulate Compute instructions in current block
|
||
// - On Jump: emit Branch + create exit block with Return
|
||
// - On Call: emit Call in current block
|
||
let mut current_block_id = entry_block;
|
||
let mut current_instructions = Vec::new();
|
||
let mut next_block_id = 1u32; // for creating new blocks
|
||
|
||
for join_inst in &join_func.body {
|
||
match join_inst {
|
||
JoinInst::Compute(mir_like) => {
|
||
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,
|
||
dst,
|
||
k_next,
|
||
} => {
|
||
// Phase 30.x: Support both tail calls and non-tail calls
|
||
// - dst=None, k_next=None: Tail call → call + return
|
||
// - dst=Some(id), k_next=None: Non-tail call → call + store + continue
|
||
// - k_next=Some: Not yet supported
|
||
|
||
if k_next.is_some() {
|
||
let call_target_name = join_func_name(*func);
|
||
return Err(JoinIrVmBridgeError::new(format!(
|
||
"Call with k_next is not yet supported\n\
|
||
\n\
|
||
Current function: {}\n\
|
||
Call target: {} (JoinFuncId: {:?})\n\
|
||
Arguments: {} args\n\
|
||
k_next: {:?}\n\
|
||
\n\
|
||
💡 Fix: Change `k_next: Some(...)` to `k_next: None`\n\
|
||
💡 Note: Phase 31 and Phase 34 both use k_next=None (bridge limitation)\n\
|
||
\n\
|
||
Example:\n\
|
||
Call {{\n\
|
||
func: loop_step_id,\n\
|
||
args: vec![i_next, acc_next, n],\n\
|
||
k_next: None, // ⚠️ Must be None\n\
|
||
dst: Some(result),\n\
|
||
}}",
|
||
join_func.name,
|
||
call_target_name,
|
||
func,
|
||
args.len(),
|
||
k_next,
|
||
)));
|
||
}
|
||
|
||
// Convert JoinFuncId to function name
|
||
let func_name = join_func_name(*func);
|
||
|
||
// Create temporary ValueId for function name
|
||
let func_name_id = ValueId(99990 + next_block_id);
|
||
next_block_id += 1;
|
||
|
||
// Add Const instruction for function name
|
||
current_instructions.push(MirInstruction::Const {
|
||
dst: func_name_id,
|
||
value: MirConstValue::String(func_name),
|
||
});
|
||
|
||
match dst {
|
||
Some(result_dst) => {
|
||
// Non-tail call: store result in dst and continue
|
||
current_instructions.push(MirInstruction::Call {
|
||
dst: Some(*result_dst),
|
||
func: func_name_id,
|
||
callee: None,
|
||
args: args.clone(),
|
||
effects: EffectMask::PURE,
|
||
});
|
||
// Continue to next instruction (no block termination)
|
||
}
|
||
None => {
|
||
// Tail call: call + return result
|
||
let call_result_id = ValueId(99991);
|
||
current_instructions.push(MirInstruction::Call {
|
||
dst: Some(call_result_id),
|
||
func: func_name_id,
|
||
callee: None,
|
||
args: args.clone(),
|
||
effects: EffectMask::PURE,
|
||
});
|
||
|
||
// Return the result of the tail call
|
||
let terminator = MirInstruction::Return { value: Some(call_result_id) };
|
||
finalize_block(&mut mir_func, current_block_id, current_instructions, terminator);
|
||
current_instructions = Vec::new();
|
||
}
|
||
}
|
||
}
|
||
JoinInst::Jump { cont, args, cond } => {
|
||
// Phase 27-shortterm S-4.4-A: Jump with condition → Branch + Return
|
||
// Jump represents an exit continuation (k_exit) in skip_ws pattern
|
||
|
||
debug_log!(
|
||
"[joinir_vm_bridge] Converting Jump to cont={:?}, args={:?}, cond={:?}",
|
||
cont, args, cond
|
||
);
|
||
|
||
match cond {
|
||
Some(cond_var) => {
|
||
// Conditional jump: Branch to exit block
|
||
let exit_block_id = BasicBlockId(next_block_id);
|
||
next_block_id += 1;
|
||
let continue_block_id = BasicBlockId(next_block_id);
|
||
next_block_id += 1;
|
||
|
||
// Phase 30.x: Branch terminator (separate from instructions)
|
||
let branch_terminator = MirInstruction::Branch {
|
||
condition: *cond_var,
|
||
then_bb: exit_block_id,
|
||
else_bb: continue_block_id,
|
||
};
|
||
|
||
// Finalize current block with Branch terminator
|
||
finalize_block(&mut mir_func, current_block_id, current_instructions, branch_terminator);
|
||
|
||
// Create exit block with Return terminator
|
||
let exit_value = args.first().copied();
|
||
let mut exit_block = crate::mir::BasicBlock::new(exit_block_id);
|
||
exit_block.terminator = Some(MirInstruction::Return { value: exit_value });
|
||
mir_func.blocks.insert(exit_block_id, exit_block);
|
||
|
||
// Create continue block (will be populated by subsequent instructions)
|
||
let continue_block = crate::mir::BasicBlock::new(continue_block_id);
|
||
mir_func.blocks.insert(continue_block_id, continue_block);
|
||
|
||
// Continue in the next block
|
||
current_block_id = continue_block_id;
|
||
current_instructions = Vec::new();
|
||
}
|
||
None => {
|
||
// Unconditional jump: direct Return terminator
|
||
let exit_value = args.first().copied();
|
||
let return_terminator = MirInstruction::Return { value: exit_value };
|
||
|
||
// Finalize current block with Return terminator
|
||
finalize_block(&mut mir_func, current_block_id, current_instructions, return_terminator);
|
||
|
||
// No continuation after unconditional return
|
||
current_instructions = Vec::new();
|
||
}
|
||
}
|
||
}
|
||
// Phase 33: Select instruction conversion to MIR
|
||
JoinInst::Select { dst, cond, then_val, else_val } => {
|
||
// Phase 33-2: Select を MIR の if/phi に変換
|
||
// 最小実装: cond/then/else/merge の 4 ブロック構造
|
||
|
||
debug_log!(
|
||
"[joinir_vm_bridge] Converting Select: dst={:?}, cond={:?}, then={:?}, else={:?}",
|
||
dst, cond, then_val, else_val
|
||
);
|
||
|
||
// 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 ブロック: dst = then_val; jump merge
|
||
let mut then_block_obj = crate::mir::BasicBlock::new(then_block);
|
||
then_block_obj.instructions.push(MirInstruction::Copy {
|
||
dst: *dst,
|
||
src: *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 ブロック: dst = else_val; jump merge
|
||
let mut else_block_obj = crate::mir::BasicBlock::new(else_block);
|
||
else_block_obj.instructions.push(MirInstruction::Copy {
|
||
dst: *dst,
|
||
src: *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();
|
||
}
|
||
// 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 };
|
||
|
||
// Finalize current block with Return terminator
|
||
finalize_block(&mut mir_func, current_block_id, current_instructions, return_terminator);
|
||
|
||
current_instructions = Vec::new();
|
||
}
|
||
}
|
||
}
|
||
|
||
// Finalize any remaining instructions in the last block
|
||
if !current_instructions.is_empty() {
|
||
debug_log!(
|
||
"[joinir_vm_bridge] Final block {:?} has {} remaining instructions",
|
||
current_block_id,
|
||
current_instructions.len()
|
||
);
|
||
if let Some(block) = mir_func.blocks.get_mut(¤t_block_id) {
|
||
// Phase 30.x: VM requires instruction_spans to match instructions length
|
||
let inst_count = current_instructions.len();
|
||
block.instructions = current_instructions;
|
||
block.instruction_spans = vec![Span::unknown(); inst_count];
|
||
}
|
||
}
|
||
|
||
// Debug: print all blocks and their instruction counts + terminators
|
||
debug_log!(
|
||
"[joinir_vm_bridge] Function '{}' has {} blocks:",
|
||
mir_func.signature.name,
|
||
mir_func.blocks.len()
|
||
);
|
||
for (block_id, block) in &mir_func.blocks {
|
||
debug_log!(
|
||
" Block {:?}: {} instructions, terminator={:?}",
|
||
block_id,
|
||
block.instructions.len(),
|
||
block.terminator
|
||
);
|
||
}
|
||
|
||
Ok(mir_func)
|
||
}
|
||
|
||
/// MirLikeInst → MirInstruction 変換
|
||
fn convert_mir_like_inst(mir_like: &MirLikeInst) -> Result<MirInstruction, JoinIrVmBridgeError> {
|
||
match mir_like {
|
||
MirLikeInst::Const { dst, value } => {
|
||
let mir_const = match value {
|
||
ConstValue::Integer(i) => MirConstValue::Integer(*i),
|
||
ConstValue::Bool(b) => MirConstValue::Bool(*b),
|
||
ConstValue::String(s) => MirConstValue::String(s.clone()),
|
||
ConstValue::Null => MirConstValue::Null,
|
||
};
|
||
Ok(MirInstruction::Const {
|
||
dst: *dst,
|
||
value: mir_const,
|
||
})
|
||
}
|
||
MirLikeInst::BinOp { dst, op, lhs, rhs } => {
|
||
let mir_op = match op {
|
||
BinOpKind::Add => BinaryOp::Add,
|
||
BinOpKind::Sub => BinaryOp::Sub,
|
||
BinOpKind::Mul => BinaryOp::Mul,
|
||
BinOpKind::Div => BinaryOp::Div,
|
||
BinOpKind::Or => BinaryOp::Or,
|
||
BinOpKind::And => BinaryOp::And,
|
||
};
|
||
Ok(MirInstruction::BinOp {
|
||
dst: *dst,
|
||
op: mir_op,
|
||
lhs: *lhs,
|
||
rhs: *rhs,
|
||
})
|
||
}
|
||
MirLikeInst::Compare { dst, op, lhs, rhs } => {
|
||
let mir_cmp = match op {
|
||
CompareOp::Lt => MirCompareOp::Lt,
|
||
CompareOp::Le => MirCompareOp::Le,
|
||
CompareOp::Gt => MirCompareOp::Gt,
|
||
CompareOp::Ge => MirCompareOp::Ge,
|
||
CompareOp::Eq => MirCompareOp::Eq,
|
||
CompareOp::Ne => MirCompareOp::Ne,
|
||
};
|
||
Ok(MirInstruction::Compare {
|
||
dst: *dst,
|
||
op: mir_cmp,
|
||
lhs: *lhs,
|
||
rhs: *rhs,
|
||
})
|
||
}
|
||
MirLikeInst::BoxCall {
|
||
dst,
|
||
box_name,
|
||
method,
|
||
args,
|
||
} => {
|
||
// Phase 27-shortterm S-4.3: BoxCall → MIR BoxCall
|
||
// box_name は JoinIR で保持しているが、MIR BoxCall には receiver ValueId のみ
|
||
// 暫定: args[0] を receiver として扱う
|
||
if args.is_empty() {
|
||
return Err(JoinIrVmBridgeError::new(format!(
|
||
"BoxCall requires at least one argument (receiver), got: box_name={}, method={}",
|
||
box_name, method
|
||
)));
|
||
}
|
||
|
||
let receiver = args[0];
|
||
let method_args = args[1..].to_vec();
|
||
|
||
Ok(MirInstruction::BoxCall {
|
||
dst: *dst,
|
||
box_val: receiver,
|
||
method: method.clone(),
|
||
method_id: None, // Phase 27-shortterm: no method ID resolution
|
||
args: method_args,
|
||
effects: EffectMask::PURE, // Phase 27-shortterm: assume pure
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_convert_const_inst() {
|
||
let join_const = MirLikeInst::Const {
|
||
dst: ValueId(10),
|
||
value: ConstValue::Integer(42),
|
||
};
|
||
|
||
let mir_inst = convert_mir_like_inst(&join_const).unwrap();
|
||
|
||
match mir_inst {
|
||
MirInstruction::Const { dst, value } => {
|
||
assert_eq!(dst, ValueId(10));
|
||
assert!(matches!(value, MirConstValue::Integer(42)));
|
||
}
|
||
_ => panic!("Expected Const instruction"),
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_convert_binop_inst() {
|
||
let join_binop = MirLikeInst::BinOp {
|
||
dst: ValueId(20),
|
||
op: BinOpKind::Add,
|
||
lhs: ValueId(10),
|
||
rhs: ValueId(11),
|
||
};
|
||
|
||
let mir_inst = convert_mir_like_inst(&join_binop).unwrap();
|
||
|
||
match mir_inst {
|
||
MirInstruction::BinOp { dst, op, lhs, rhs } => {
|
||
assert_eq!(dst, ValueId(20));
|
||
assert_eq!(op, BinaryOp::Add);
|
||
assert_eq!(lhs, ValueId(10));
|
||
assert_eq!(rhs, ValueId(11));
|
||
}
|
||
_ => panic!("Expected BinOp instruction"),
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_convert_compare_inst() {
|
||
let join_cmp = MirLikeInst::Compare {
|
||
dst: ValueId(30),
|
||
op: CompareOp::Ge,
|
||
lhs: ValueId(10),
|
||
rhs: ValueId(11),
|
||
};
|
||
|
||
let mir_inst = convert_mir_like_inst(&join_cmp).unwrap();
|
||
|
||
match mir_inst {
|
||
MirInstruction::Compare { dst, op, lhs, rhs } => {
|
||
assert_eq!(dst, ValueId(30));
|
||
assert_eq!(op, MirCompareOp::Ge);
|
||
assert_eq!(lhs, ValueId(10));
|
||
assert_eq!(rhs, ValueId(11));
|
||
}
|
||
_ => panic!("Expected Compare instruction"),
|
||
}
|
||
}
|
||
}
|