feat(joinir): Phase 189 Box 1・2 モジュール化実装
Box 1: joinir_id_remapper.rs (~380行) - ValueId/BlockId ID空間変換の独立化 - 決定性を重視した実装 - 全MIR命令型に対応(Await含む) Box 2: joinir_inline_boundary_injector.rs (~180行) - JoinInlineBoundary Copy命令注入 - Entry blockへのCopy instruction挿入 - SSA値空間の接続 統合: - src/mir/builder.rs に mod 宣言追加 - ビルド成功確認 - テスト実行確認(loop_min_while.hako) Phase 189 モジュール化の第一歩完了
This commit is contained in:
@ -32,6 +32,8 @@ mod fields; // field access/assignment lowering split
|
|||||||
mod if_form;
|
mod if_form;
|
||||||
mod lifecycle;
|
mod lifecycle;
|
||||||
mod loop_frontend_binding; // Phase 50: Loop Frontend Binding (JoinIR variable mapping)
|
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;
|
pub(crate) mod loops;
|
||||||
mod ops;
|
mod ops;
|
||||||
mod phi;
|
mod phi;
|
||||||
|
|||||||
392
src/mir/builder/joinir_id_remapper.rs
Normal file
392
src/mir/builder/joinir_id_remapper.rs
Normal file
@ -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<ValueId, ValueId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<BasicBlockId> {
|
||||||
|
self.block_map.get(&(func_name.to_string(), old_id)).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Value ID mapping を取得
|
||||||
|
pub fn get_value(&self, old_id: ValueId) -> Option<ValueId> {
|
||||||
|
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<ValueId> {
|
||||||
|
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<ValueId> {
|
||||||
|
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)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
202
src/mir/builder/joinir_inline_boundary_injector.rs
Normal file
202
src/mir/builder/joinir_inline_boundary_injector.rs
Normal file
@ -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<ValueId, ValueId>,
|
||||||
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user