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 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;
|
||||
|
||||
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