diff --git a/src/mir/builder.rs b/src/mir/builder.rs index 02db8ce6..b423a3fd 100644 --- a/src/mir/builder.rs +++ b/src/mir/builder.rs @@ -32,6 +32,8 @@ mod fields; // field access/assignment lowering split mod if_form; mod lifecycle; mod loop_frontend_binding; // Phase 50: Loop Frontend Binding (JoinIR variable mapping) +mod joinir_id_remapper; // Phase 189: JoinIR ID remapping (ValueId/BlockId translation) +mod joinir_inline_boundary_injector; // Phase 189: JoinInlineBoundary Copy instruction injector pub(crate) mod loops; mod ops; mod phi; diff --git a/src/mir/builder/joinir_id_remapper.rs b/src/mir/builder/joinir_id_remapper.rs new file mode 100644 index 00000000..1196571b --- /dev/null +++ b/src/mir/builder/joinir_id_remapper.rs @@ -0,0 +1,392 @@ +//! Phase 189: JoinIR ID Remapper - ValueId/BlockId ID空間変換の独立化 +//! +//! 責務: +//! - ValueId/BlockId の ID割り当て +//! - JoinIR fragment → host MIR への ID変換 +//! - 決定性を重視した実装 + +use std::collections::HashMap; +use crate::mir::{BasicBlock, BasicBlockId, MirInstruction, ValueId}; + +/// JoinIR ID space を host MIR ID space に変換する +pub struct JoinIrIdRemapper { + /// (func_name, old_block_id) → new_block_id のマッピング + block_map: HashMap<(String, BasicBlockId), BasicBlockId>, + /// old_value_id → new_value_id のマッピング + value_map: HashMap, +} + +impl JoinIrIdRemapper { + pub fn new() -> Self { + Self { + block_map: HashMap::new(), + value_map: HashMap::new(), + } + } + + /// Block ID mapping を取得 + pub fn get_block(&self, func_name: &str, old_id: BasicBlockId) -> Option { + self.block_map.get(&(func_name.to_string(), old_id)).copied() + } + + /// Value ID mapping を取得 + pub fn get_value(&self, old_id: ValueId) -> Option { + self.value_map.get(&old_id).copied() + } + + /// Block mapping を設定 + pub fn set_block(&mut self, func_name: String, old_id: BasicBlockId, new_id: BasicBlockId) { + self.block_map.insert((func_name, old_id), new_id); + } + + /// Value mapping を設定 + pub fn set_value(&mut self, old_id: ValueId, new_id: ValueId) { + self.value_map.insert(old_id, new_id); + } + + /// Block 内の ValueId を収集 + pub fn collect_values_in_block(&self, block: &BasicBlock) -> Vec { + let mut values = Vec::new(); + for inst in &block.instructions { + values.extend(self.collect_values_in_instruction(inst)); + } + if let Some(ref term) = block.terminator { + values.extend(self.collect_values_in_instruction(term)); + } + values + } + + /// 命令内の ValueId を収集 + pub fn collect_values_in_instruction(&self, inst: &MirInstruction) -> Vec { + use crate::mir::MirInstruction::*; + + match inst { + Const { dst, .. } => vec![*dst], + UnaryOp { dst, operand, .. } => vec![*dst, *operand], + BinOp { dst, lhs, rhs, .. } => vec![*dst, *lhs, *rhs], + Compare { dst, lhs, rhs, .. } => vec![*dst, *lhs, *rhs], + Load { dst, ptr } => vec![*dst, *ptr], + Store { value, ptr } => vec![*value, *ptr], + Call { dst, func, args, .. } => { + let mut vals = vec![*func]; + if let Some(d) = dst { + vals.push(*d); + } + vals.extend(args.iter().copied()); + vals + } + BoxCall { dst, box_val, args, .. } => { + let mut vals = vec![*box_val]; + if let Some(d) = dst { + vals.push(*d); + } + vals.extend(args.iter().copied()); + vals + } + PluginInvoke { dst, box_val, args, .. } => { + let mut vals = vec![*box_val]; + if let Some(d) = dst { + vals.push(*d); + } + vals.extend(args.iter().copied()); + vals + } + Branch { condition, .. } => vec![*condition], + Return { value } => value.iter().copied().collect(), + Phi { dst, inputs, .. } => { + let mut vals = vec![*dst]; + vals.extend(inputs.iter().map(|(_, v)| *v)); + vals + } + Copy { dst, src } => vec![*dst, *src], + NewBox { dst, args, .. } => { + let mut vals = vec![*dst]; + vals.extend(args.iter().copied()); + vals + } + NewClosure { dst, captures, me, .. } => { + let mut vals = vec![*dst]; + vals.extend(captures.iter().map(|(_, v)| *v)); + if let Some(m) = me { + vals.push(*m); + } + vals + } + Print { value, .. } => vec![*value], + Debug { value, .. } => vec![*value], + DebugLog { values, .. } => values.clone(), + Throw { exception, .. } => vec![*exception], + Catch { exception_value, .. } => vec![*exception_value], + RefNew { dst, box_val } => vec![*dst, *box_val], + RefGet { dst, reference, .. } => vec![*dst, *reference], + RefSet { reference, value, .. } => vec![*reference, *value], + WeakNew { dst, box_val } => vec![*dst, *box_val], + WeakLoad { dst, weak_ref } => vec![*dst, *weak_ref], + WeakRef { dst, value, .. } => vec![*dst, *value], + BarrierRead { ptr } => vec![*ptr], + BarrierWrite { ptr } => vec![*ptr], + Barrier { ptr, .. } => vec![*ptr], + FutureNew { dst, value } => vec![*dst, *value], + FutureSet { future, value } => vec![*future, *value], + Await { dst, future } => vec![*dst, *future], + TypeCheck { dst, value, .. } => vec![*dst, *value], + Cast { dst, value, .. } => vec![*dst, *value], + TypeOp { dst, value, .. } => vec![*dst, *value], + ArrayGet { dst, array, index } => vec![*dst, *array, *index], + ArraySet { array, index, value } => vec![*array, *index, *value], + Jump { .. } | Nop | Safepoint => vec![], + ExternCall { dst, args, .. } => { + let mut vals = Vec::new(); + if let Some(d) = dst { + vals.push(*d); + } + vals.extend(args.iter().copied()); + vals + } + } + } + + /// 命令を新しい ID空間にリマップ + pub fn remap_instruction(&self, inst: &MirInstruction) -> MirInstruction { + use crate::mir::MirInstruction::*; + + let remap = |v: ValueId| self.value_map.get(&v).copied().unwrap_or(v); + + match inst { + Const { dst, value } => Const { + dst: remap(*dst), + value: value.clone(), + }, + UnaryOp { dst, op, operand } => UnaryOp { + dst: remap(*dst), + op: *op, + operand: remap(*operand), + }, + BinOp { dst, op, lhs, rhs } => BinOp { + dst: remap(*dst), + op: *op, + lhs: remap(*lhs), + rhs: remap(*rhs), + }, + Compare { dst, op, lhs, rhs } => Compare { + dst: remap(*dst), + op: *op, + lhs: remap(*lhs), + rhs: remap(*rhs), + }, + Load { dst, ptr } => Load { + dst: remap(*dst), + ptr: remap(*ptr), + }, + Store { value, ptr } => Store { + value: remap(*value), + ptr: remap(*ptr), + }, + Call { dst, func, callee, args, effects } => Call { + dst: dst.map(remap), + func: remap(*func), + callee: callee.clone(), + args: args.iter().map(|&a| remap(a)).collect(), + effects: *effects, + }, + BoxCall { dst, box_val, method, method_id, args, effects } => BoxCall { + dst: dst.map(remap), + box_val: remap(*box_val), + method: method.clone(), + method_id: *method_id, + args: args.iter().map(|&a| remap(a)).collect(), + effects: *effects, + }, + PluginInvoke { dst, box_val, method, args, effects } => PluginInvoke { + dst: dst.map(remap), + box_val: remap(*box_val), + method: method.clone(), + args: args.iter().map(|&a| remap(a)).collect(), + effects: *effects, + }, + Copy { dst, src } => Copy { + dst: remap(*dst), + src: remap(*src), + }, + NewBox { dst, box_type, args } => NewBox { + dst: remap(*dst), + box_type: box_type.clone(), + args: args.iter().map(|&a| remap(a)).collect(), + }, + NewClosure { dst, params, body, captures, me } => NewClosure { + dst: remap(*dst), + params: params.clone(), + body: body.clone(), + captures: captures.iter().map(|(n, v)| (n.clone(), remap(*v))).collect(), + me: me.map(remap), + }, + Print { value, effects } => Print { + value: remap(*value), + effects: *effects, + }, + Debug { value, message } => Debug { + value: remap(*value), + message: message.clone(), + }, + DebugLog { message, values } => DebugLog { + message: message.clone(), + values: values.iter().map(|&v| remap(v)).collect(), + }, + Throw { exception, effects } => Throw { + exception: remap(*exception), + effects: *effects, + }, + Catch { exception_type, exception_value, handler_bb } => Catch { + exception_type: exception_type.clone(), + exception_value: remap(*exception_value), + handler_bb: *handler_bb, + }, + RefNew { dst, box_val } => RefNew { + dst: remap(*dst), + box_val: remap(*box_val), + }, + RefGet { dst, reference, field } => RefGet { + dst: remap(*dst), + reference: remap(*reference), + field: field.clone(), + }, + RefSet { reference, field, value } => RefSet { + reference: remap(*reference), + field: field.clone(), + value: remap(*value), + }, + WeakNew { dst, box_val } => WeakNew { + dst: remap(*dst), + box_val: remap(*box_val), + }, + WeakLoad { dst, weak_ref } => WeakLoad { + dst: remap(*dst), + weak_ref: remap(*weak_ref), + }, + WeakRef { dst, op, value } => WeakRef { + dst: remap(*dst), + op: *op, + value: remap(*value), + }, + BarrierRead { ptr } => BarrierRead { + ptr: remap(*ptr), + }, + BarrierWrite { ptr } => BarrierWrite { + ptr: remap(*ptr), + }, + Barrier { op, ptr } => Barrier { + op: *op, + ptr: remap(*ptr), + }, + FutureNew { dst, value } => FutureNew { + dst: remap(*dst), + value: remap(*value), + }, + FutureSet { future, value } => FutureSet { + future: remap(*future), + value: remap(*value), + }, + Await { dst, future } => Await { + dst: remap(*dst), + future: remap(*future), + }, + TypeCheck { dst, value, expected_type } => TypeCheck { + dst: remap(*dst), + value: remap(*value), + expected_type: expected_type.clone(), + }, + Cast { dst, value, target_type } => Cast { + dst: remap(*dst), + value: remap(*value), + target_type: target_type.clone(), + }, + TypeOp { dst, op, value, ty } => TypeOp { + dst: remap(*dst), + op: *op, + value: remap(*value), + ty: ty.clone(), + }, + ArrayGet { dst, array, index } => ArrayGet { + dst: remap(*dst), + array: remap(*array), + index: remap(*index), + }, + ArraySet { array, index, value } => ArraySet { + array: remap(*array), + index: remap(*index), + value: remap(*value), + }, + ExternCall { dst, iface_name, method_name, args, effects } => ExternCall { + dst: dst.map(remap), + iface_name: iface_name.clone(), + method_name: method_name.clone(), + args: args.iter().map(|&a| remap(a)).collect(), + effects: *effects, + }, + // Pass through unchanged + Branch { .. } | Jump { .. } | Return { .. } | Phi { .. } | Nop | Safepoint => inst.clone(), + } + } + + /// Value ID をリマップ + pub fn remap_value(&self, v: ValueId) -> ValueId { + self.value_map.get(&v).copied().unwrap_or(v) + } + + /// Block ID をリマップ + pub fn remap_block(&self, func_name: &str, b: BasicBlockId) -> BasicBlockId { + self.block_map + .get(&(func_name.to_string(), b)) + .copied() + .unwrap_or(b) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_remapper_new() { + let remapper = JoinIrIdRemapper::new(); + assert_eq!(remapper.get_value(ValueId(0)), None); + } + + #[test] + fn test_remapper_set_and_get_value() { + let mut remapper = JoinIrIdRemapper::new(); + remapper.set_value(ValueId(0), ValueId(10)); + assert_eq!(remapper.get_value(ValueId(0)), Some(ValueId(10))); + } + + #[test] + fn test_remapper_set_and_get_block() { + let mut remapper = JoinIrIdRemapper::new(); + remapper.set_block("main".to_string(), BasicBlockId(0), BasicBlockId(100)); + assert_eq!( + remapper.get_block("main", BasicBlockId(0)), + Some(BasicBlockId(100)) + ); + } + + #[test] + fn test_remap_value() { + let mut remapper = JoinIrIdRemapper::new(); + remapper.set_value(ValueId(5), ValueId(50)); + assert_eq!(remapper.remap_value(ValueId(5)), ValueId(50)); + assert_eq!(remapper.remap_value(ValueId(99)), ValueId(99)); // Unmapped returns original + } + + #[test] + fn test_collect_values_simple() { + let remapper = JoinIrIdRemapper::new(); + let inst = MirInstruction::BinOp { + dst: ValueId(1), + op: crate::mir::types::BinaryOp::Add, + lhs: ValueId(2), + rhs: ValueId(3), + }; + let values = remapper.collect_values_in_instruction(&inst); + assert_eq!(values, vec![ValueId(1), ValueId(2), ValueId(3)]); + } +} diff --git a/src/mir/builder/joinir_inline_boundary_injector.rs b/src/mir/builder/joinir_inline_boundary_injector.rs new file mode 100644 index 00000000..6fedc187 --- /dev/null +++ b/src/mir/builder/joinir_inline_boundary_injector.rs @@ -0,0 +1,202 @@ +//! Phase 188-Impl-3: JoinInlineBoundary Copy Instruction Injector +//! +//! 責務: +//! - JoinInlineBoundary で指定された入出力の Copy instruction 生成 +//! - Entry block への Copy instruction 挿入 +//! - SSA 値空間の接続 + +use std::collections::HashMap; +use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId}; +use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary; + +pub struct BoundaryInjector; + +impl BoundaryInjector { + /// JoinInlineBoundary で指定された入力を entry block に Copy instruction として挿入 + /// + /// # Arguments + /// + /// * `func` - 対象の MirFunction + /// * `entry_block_id` - entry block の ID + /// * `boundary` - JoinInlineBoundary(入力マッピング情報) + /// * `value_map` - ValueId リマッピング情報 + /// * `debug` - デバッグログ出力 + /// + /// # Returns + /// + /// * `Ok(())` - 成功 + /// * `Err(String)` - エラー(ブロックが見つからない等) + pub fn inject_boundary_copies( + func: &mut MirFunction, + entry_block_id: BasicBlockId, + boundary: &JoinInlineBoundary, + value_map: &HashMap, + debug: bool, + ) -> Result<(), String> { + // Boundary が空の場合はスキップ + if boundary.join_inputs.is_empty() { + return Ok(()); + } + + if debug { + eprintln!( + "[BoundaryInjector] Injecting {} Copy instructions at entry block {:?}", + boundary.join_inputs.len(), + entry_block_id + ); + } + + // Entry block を取得 + let entry_block = func + .get_block_mut(entry_block_id) + .ok_or(format!("Entry block {:?} not found", entry_block_id))?; + + // Copy instructions を生成して挿入 + let mut copy_instructions = Vec::new(); + + for (join_input, host_input) in boundary + .join_inputs + .iter() + .zip(boundary.host_inputs.iter()) + { + // リマップ後の ValueId を取得 + let remapped_join = value_map.get(join_input).copied().unwrap_or(*join_input); + let remapped_host = *host_input; // host_input is already in host space + + // Copy instruction: remapped_join = Copy remapped_host + let copy_inst = MirInstruction::Copy { + dst: remapped_join, + src: remapped_host, + }; + + copy_instructions.push(copy_inst); + + if debug { + eprintln!( + "[BoundaryInjector] Copy {:?} = Copy {:?}", + remapped_join, remapped_host + ); + } + } + + // Entry block の先頭に Copy instructions を挿入 + // Reverse order to preserve original order when inserting at position 0 + for inst in copy_instructions.into_iter().rev() { + entry_block.instructions.insert(0, inst); + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mir::{BasicBlock, MirModule}; + + #[test] + fn test_injector_empty_boundary() { + // 空の boundary で何もしない + let boundary = JoinInlineBoundary::new_inputs_only(vec![], vec![]); + let mut module = MirModule::new(); + let mut func = module.define_function("test".to_string(), vec![]); + let entry_block = func.create_block(); + let value_map = HashMap::new(); + + let result = BoundaryInjector::inject_boundary_copies( + &mut func, + entry_block, + &boundary, + &value_map, + false, + ); + + assert!(result.is_ok()); + } + + #[test] + fn test_injector_single_copy() { + // 単一の Copy instruction を挿入 + let boundary = JoinInlineBoundary::new_inputs_only( + vec![ValueId(0)], + vec![ValueId(10)], + ); + + let mut module = MirModule::new(); + let mut func = module.define_function("test".to_string(), vec![]); + let entry_block = func.create_block(); + + let mut value_map = HashMap::new(); + value_map.insert(ValueId(0), ValueId(100)); // JoinIR ValueId(0) remapped to ValueId(100) + + let result = BoundaryInjector::inject_boundary_copies( + &mut func, + entry_block, + &boundary, + &value_map, + false, + ); + + assert!(result.is_ok()); + + // Copy instruction が挿入されたことを確認 + let block = func.get_block(entry_block).unwrap(); + assert!(!block.instructions.is_empty()); + + // First instruction should be Copy + match &block.instructions[0] { + MirInstruction::Copy { dst, src } => { + assert_eq!(*dst, ValueId(100)); // Remapped join input + assert_eq!(*src, ValueId(10)); // Host input + } + _ => panic!("Expected Copy instruction"), + } + } + + #[test] + fn test_injector_multiple_copies() { + // 複数の Copy instruction を挿入 + let boundary = JoinInlineBoundary::new_inputs_only( + vec![ValueId(0), ValueId(1)], + vec![ValueId(10), ValueId(20)], + ); + + let mut module = MirModule::new(); + let mut func = module.define_function("test".to_string(), vec![]); + let entry_block = func.create_block(); + + let mut value_map = HashMap::new(); + value_map.insert(ValueId(0), ValueId(100)); + value_map.insert(ValueId(1), ValueId(101)); + + let result = BoundaryInjector::inject_boundary_copies( + &mut func, + entry_block, + &boundary, + &value_map, + false, + ); + + assert!(result.is_ok()); + + let block = func.get_block(entry_block).unwrap(); + assert_eq!(block.instructions.len(), 2); + + // Check both copy instructions + match &block.instructions[0] { + MirInstruction::Copy { dst, src } => { + assert_eq!(*dst, ValueId(100)); + assert_eq!(*src, ValueId(10)); + } + _ => panic!("Expected Copy instruction"), + } + + match &block.instructions[1] { + MirInstruction::Copy { dst, src } => { + assert_eq!(*dst, ValueId(101)); + assert_eq!(*src, ValueId(20)); + } + _ => panic!("Expected Copy instruction"), + } + } +}