refactor(mir): phase260 p0 edge-args plumbing (strangler) + ssot api + docs
This commit is contained in:
14
src/mir/builder/control_flow/joinir/api/entry.rs
Normal file
14
src/mir/builder/control_flow/joinir/api/entry.rs
Normal file
@ -0,0 +1,14 @@
|
||||
//! Entry function helpers (SSOT)
|
||||
|
||||
use crate::mir::join_ir::{JoinFunction, JoinModule};
|
||||
|
||||
/// Extract entry function from JoinModule (SSOT).
|
||||
///
|
||||
/// Priority: `join_module.entry` → fallback to `"main"`.
|
||||
pub(in crate::mir::builder) fn get_entry_function<'a>(
|
||||
join_module: &'a JoinModule,
|
||||
context: &str,
|
||||
) -> Result<&'a JoinFunction, String> {
|
||||
// Re-exported from patterns/common as the SSOT entry point.
|
||||
super::super::patterns::common::get_entry_function(join_module, context)
|
||||
}
|
||||
14
src/mir/builder/control_flow/joinir/api/mod.rs
Normal file
14
src/mir/builder/control_flow/joinir/api/mod.rs
Normal file
@ -0,0 +1,14 @@
|
||||
//! JoinIR integration API (SSOT entry points)
|
||||
//!
|
||||
//! Purpose: provide a small, stable surface for pattern lowerers and merge code.
|
||||
//! This reduces "where should I call this from?" drift and avoids re-implementing
|
||||
//! contract logic (SSOT, fail-fast checks) in each pattern.
|
||||
//!
|
||||
//! Policy:
|
||||
//! - Prefer SSOT helpers over ad-hoc logic in patterns.
|
||||
//! - Avoid guessing (order/layout/name) in callers; callers pass explicit intent.
|
||||
//! - Keep this module thin: mostly wrappers/re-exports with clear naming.
|
||||
|
||||
pub(in crate::mir::builder) mod entry;
|
||||
pub(in crate::mir::builder) mod pipeline_contracts;
|
||||
pub(in crate::mir::builder) mod receiver;
|
||||
@ -0,0 +1,16 @@
|
||||
//! Pipeline contract helpers (SSOT)
|
||||
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
||||
use crate::mir::join_ir::JoinModule;
|
||||
|
||||
/// Run all JoinIR pipeline contract checks (SSOT).
|
||||
///
|
||||
/// This is the preferred entry point for patterns that need to validate boundary
|
||||
/// assumptions before bridge/merge.
|
||||
pub(in crate::mir::builder) fn run_all_pipeline_checks(
|
||||
join_module: &JoinModule,
|
||||
boundary: &JoinInlineBoundary,
|
||||
) -> Result<(), String> {
|
||||
super::super::merge::contract_checks::run_all_pipeline_checks(join_module, boundary)
|
||||
}
|
||||
|
||||
20
src/mir/builder/control_flow/joinir/api/receiver.rs
Normal file
20
src/mir/builder/control_flow/joinir/api/receiver.rs
Normal file
@ -0,0 +1,20 @@
|
||||
//! Receiver helpers (SSOT)
|
||||
//!
|
||||
//! Motivation: surface syntax uses `this` / `me`, but the MIR builder's `variable_map`
|
||||
//! registers the receiver under a specific key. Patterns must not guess that key.
|
||||
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::ValueId;
|
||||
|
||||
/// Return the host ValueId for `me` receiver (SSOT).
|
||||
///
|
||||
/// Patterns should use this instead of hardcoding `"me"` / `"this"`.
|
||||
pub(in crate::mir::builder) fn get_me_host_id(builder: &MirBuilder, context: &str) -> Result<ValueId, String> {
|
||||
builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.get("me")
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[{}] receiver 'me' not found in variable_map", context))
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ pub(super) fn verify_all_terminator_targets_exist(
|
||||
let Some(term) = &block.terminator else { continue };
|
||||
|
||||
match term {
|
||||
MirInstruction::Jump { target } => {
|
||||
MirInstruction::Jump { target, .. } => {
|
||||
if !func.blocks.contains_key(target)
|
||||
&& !contracts.allowed_missing_jump_targets.contains(target)
|
||||
{
|
||||
@ -749,13 +749,21 @@ mod tests {
|
||||
let bb2 = BasicBlockId(2);
|
||||
|
||||
let func = create_test_function(vec![
|
||||
(bb0, Some(MirInstruction::Jump { target: bb1 })),
|
||||
(
|
||||
bb0,
|
||||
Some(MirInstruction::Jump {
|
||||
target: bb1,
|
||||
edge_args: None,
|
||||
}),
|
||||
),
|
||||
(
|
||||
bb1,
|
||||
Some(MirInstruction::Branch {
|
||||
condition: ValueId(0),
|
||||
then_bb: bb2,
|
||||
else_bb: bb2,
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
}),
|
||||
),
|
||||
(bb2, Some(MirInstruction::Return { value: None })),
|
||||
@ -775,7 +783,13 @@ mod tests {
|
||||
let bb0 = BasicBlockId(0);
|
||||
let bb99 = BasicBlockId(99); // Missing block
|
||||
|
||||
let func = create_test_function(vec![(bb0, Some(MirInstruction::Jump { target: bb99 }))]);
|
||||
let func = create_test_function(vec![(
|
||||
bb0,
|
||||
Some(MirInstruction::Jump {
|
||||
target: bb99,
|
||||
edge_args: None,
|
||||
}),
|
||||
)]);
|
||||
|
||||
let contracts = MergeContracts {
|
||||
allowed_missing_jump_targets: vec![],
|
||||
@ -795,7 +809,13 @@ mod tests {
|
||||
let bb0 = BasicBlockId(0);
|
||||
let bb_exit = BasicBlockId(100); // Missing but allowed
|
||||
|
||||
let func = create_test_function(vec![(bb0, Some(MirInstruction::Jump { target: bb_exit }))]);
|
||||
let func = create_test_function(vec![(
|
||||
bb0,
|
||||
Some(MirInstruction::Jump {
|
||||
target: bb_exit,
|
||||
edge_args: None,
|
||||
}),
|
||||
)]);
|
||||
|
||||
let contracts = MergeContracts {
|
||||
allowed_missing_jump_targets: vec![bb_exit],
|
||||
|
||||
@ -391,7 +391,7 @@ pub(super) fn merge_and_rewrite(
|
||||
// We skip merging continuation functions, so any tail-call to k_exit must be
|
||||
// lowered as an exit jump to `exit_block_id` (and contribute exit values).
|
||||
let mut k_exit_lowering_decision: Option<LoweringDecision> = None;
|
||||
let mut k_exit_jump_args: Option<Vec<ValueId>> = None;
|
||||
let mut k_exit_edge_args: Option<crate::mir::EdgeArgs> = None;
|
||||
|
||||
// Phase 177-3: Check if this block is the loop header with PHI nodes
|
||||
let is_loop_header_with_phi =
|
||||
@ -488,7 +488,10 @@ pub(super) fn merge_and_rewrite(
|
||||
if let Some(decision) = tail_call_policy.classify_tail_call(callee_name, &remapped_args) {
|
||||
// This is a k_exit tail call - policy says normalize to exit jump
|
||||
k_exit_lowering_decision = Some(decision);
|
||||
k_exit_jump_args = old_block.jump_args.clone();
|
||||
if let Some(b) = boundary {
|
||||
k_exit_edge_args =
|
||||
old_block.legacy_edge_args_with_layout(b.jump_args_layout);
|
||||
}
|
||||
found_tail_call = true;
|
||||
if debug {
|
||||
log!(
|
||||
@ -554,6 +557,8 @@ pub(super) fn merge_and_rewrite(
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
then_edge_args,
|
||||
else_edge_args,
|
||||
} => {
|
||||
let remapped_then = skipped_entry_redirects
|
||||
.get(&then_bb)
|
||||
@ -569,6 +574,8 @@ pub(super) fn merge_and_rewrite(
|
||||
condition,
|
||||
then_bb: remapped_then,
|
||||
else_bb: remapped_else,
|
||||
then_edge_args,
|
||||
else_edge_args,
|
||||
}
|
||||
}
|
||||
MirInstruction::Phi { dst, inputs, type_hint } => {
|
||||
@ -950,8 +957,9 @@ pub(super) fn merge_and_rewrite(
|
||||
}
|
||||
};
|
||||
|
||||
new_block.terminator = Some(MirInstruction::Jump {
|
||||
new_block.set_terminator(MirInstruction::Jump {
|
||||
target: actual_target,
|
||||
edge_args: None,
|
||||
});
|
||||
|
||||
// DEBUG: Print final state after adding parameter bindings
|
||||
@ -979,7 +987,18 @@ pub(super) fn merge_and_rewrite(
|
||||
// Remap terminator (convert Return → Jump to exit) if not already set by tail call
|
||||
if !found_tail_call {
|
||||
if let Some(ref term) = old_block.terminator {
|
||||
let remapped_term = match term {
|
||||
let remap_edge_args = |edge_args: &Option<crate::mir::EdgeArgs>| {
|
||||
edge_args.as_ref().map(|args| crate::mir::EdgeArgs {
|
||||
layout: args.layout,
|
||||
values: args
|
||||
.values
|
||||
.iter()
|
||||
.map(|&v| remapper.remap_value(v))
|
||||
.collect(),
|
||||
})
|
||||
};
|
||||
let mut remapped_term: Option<MirInstruction> = None;
|
||||
match term {
|
||||
MirInstruction::Return { value } => {
|
||||
// Convert Return to Jump to exit block
|
||||
// All functions return to same exit block (Phase 189)
|
||||
@ -988,43 +1007,54 @@ pub(super) fn merge_and_rewrite(
|
||||
//
|
||||
// The JoinIR Jump instruction passes ALL carrier values in its args,
|
||||
// but the JoinIR→MIR conversion in joinir_block_converter only preserved
|
||||
// the first arg in the Return value. We now use the jump_args metadata
|
||||
// the first arg in the Return value. We now use the legacy jump_args metadata
|
||||
// to recover all the original Jump args.
|
||||
//
|
||||
if let Some(_ret_val) = value {
|
||||
// Phase 246-EX: Check if this block has jump_args metadata
|
||||
if let Some(ref jump_args) = old_block.jump_args {
|
||||
log!(
|
||||
verbose,
|
||||
"[DEBUG-177] Phase 246-EX: Block {:?} has jump_args metadata: {:?}",
|
||||
old_block.id, jump_args
|
||||
);
|
||||
let mut exit_edge_args: Option<crate::mir::EdgeArgs> = None;
|
||||
if value.is_some() {
|
||||
if let Some(b) = boundary {
|
||||
// Phase 246-EX: Check if this block has legacy jump_args metadata
|
||||
if let Some(edge_args) =
|
||||
old_block.legacy_edge_args_with_layout(b.jump_args_layout)
|
||||
{
|
||||
log!(
|
||||
verbose,
|
||||
"[DEBUG-177] Phase 246-EX: Block {:?} has legacy jump_args metadata: {:?}",
|
||||
old_block.id, edge_args.values
|
||||
);
|
||||
|
||||
// The jump_args are in JoinIR value space, remap them to HOST
|
||||
let remapped_args: Vec<ValueId> = jump_args
|
||||
.iter()
|
||||
.map(|&arg| remapper.remap_value(arg))
|
||||
.collect();
|
||||
// The jump_args are in JoinIR value space, remap them to HOST
|
||||
let remapped_args: Vec<ValueId> = edge_args
|
||||
.values
|
||||
.iter()
|
||||
.map(|&arg| remapper.remap_value(arg))
|
||||
.collect();
|
||||
|
||||
log!(
|
||||
verbose,
|
||||
"[DEBUG-177] Phase 246-EX: Remapped jump_args: {:?}",
|
||||
remapped_args
|
||||
);
|
||||
log!(
|
||||
verbose,
|
||||
"[DEBUG-177] Phase 246-EX: Remapped jump_args: {:?}",
|
||||
remapped_args
|
||||
);
|
||||
|
||||
// Phase 118 P2: Use ExitArgsCollectorBox to collect exit values
|
||||
let edge_args = crate::mir::EdgeArgs {
|
||||
layout: edge_args.layout,
|
||||
values: remapped_args,
|
||||
};
|
||||
exit_edge_args = Some(edge_args.clone());
|
||||
|
||||
// Phase 118 P2: Use ExitArgsCollectorBox to collect exit values
|
||||
if let Some(b) = boundary {
|
||||
let collector = ExitArgsCollectorBox::new();
|
||||
let collection_result = collector.collect(
|
||||
&b.exit_bindings,
|
||||
&remapped_args,
|
||||
&edge_args.values,
|
||||
new_block_id,
|
||||
strict_exit,
|
||||
b.jump_args_layout,
|
||||
edge_args.layout,
|
||||
)?;
|
||||
|
||||
// Add expr_result to exit_phi_inputs (if present)
|
||||
if let Some(expr_result_val) = collection_result.expr_result_value {
|
||||
if let Some(expr_result_val) =
|
||||
collection_result.expr_result_value
|
||||
{
|
||||
exit_phi_inputs.push((new_block_id, expr_result_val));
|
||||
log!(
|
||||
verbose,
|
||||
@ -1034,7 +1064,9 @@ pub(super) fn merge_and_rewrite(
|
||||
}
|
||||
|
||||
// Add carrier values to carrier_inputs
|
||||
for (carrier_name, (block_id, value_id)) in collection_result.carrier_values {
|
||||
for (carrier_name, (block_id, value_id)) in
|
||||
collection_result.carrier_values
|
||||
{
|
||||
carrier_inputs
|
||||
.entry(carrier_name.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
@ -1045,16 +1077,14 @@ pub(super) fn merge_and_rewrite(
|
||||
carrier_name, block_id, value_id
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Fallback: Use header PHI dst (old behavior for blocks without jump_args)
|
||||
log!(
|
||||
verbose,
|
||||
"[DEBUG-177] Phase 246-EX: Block {:?} has NO jump_args, using header PHI fallback",
|
||||
old_block.id
|
||||
);
|
||||
} else {
|
||||
// Fallback: Use header PHI dst (old behavior for blocks without jump_args)
|
||||
log!(
|
||||
verbose,
|
||||
"[DEBUG-177] Phase 246-EX: Block {:?} has NO jump_args, using header PHI fallback",
|
||||
old_block.id
|
||||
);
|
||||
|
||||
if let Some(b) = boundary {
|
||||
if let Some(loop_var_name) = &b.loop_var_name {
|
||||
if let Some(phi_dst) =
|
||||
loop_header_phi_info.get_carrier_phi(loop_var_name)
|
||||
@ -1102,11 +1132,16 @@ pub(super) fn merge_and_rewrite(
|
||||
}
|
||||
}
|
||||
|
||||
MirInstruction::Jump {
|
||||
target: exit_block_id,
|
||||
if let Some(edge_args) = exit_edge_args {
|
||||
new_block.set_jump_with_edge_args(exit_block_id, Some(edge_args));
|
||||
} else {
|
||||
new_block.set_terminator(MirInstruction::Jump {
|
||||
target: exit_block_id,
|
||||
edge_args: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
MirInstruction::Jump { target } => {
|
||||
MirInstruction::Jump { target, edge_args } => {
|
||||
// Phase 189 FIX: Remap block ID for Jump
|
||||
// Phase 259 P0 FIX: Check skipped_entry_redirects first (for k_exit blocks)
|
||||
let remapped_target = skipped_entry_redirects
|
||||
@ -1114,14 +1149,17 @@ pub(super) fn merge_and_rewrite(
|
||||
.or_else(|| local_block_map.get(target))
|
||||
.copied()
|
||||
.unwrap_or(*target);
|
||||
MirInstruction::Jump {
|
||||
remapped_term = Some(MirInstruction::Jump {
|
||||
target: remapped_target,
|
||||
}
|
||||
edge_args: remap_edge_args(edge_args),
|
||||
});
|
||||
}
|
||||
MirInstruction::Branch {
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
then_edge_args,
|
||||
else_edge_args,
|
||||
} => {
|
||||
// Phase 189 FIX: Remap block IDs AND condition ValueId for Branch
|
||||
// Phase 259 P0 FIX: Check skipped_entry_redirects first (for k_exit blocks)
|
||||
@ -1135,15 +1173,46 @@ pub(super) fn merge_and_rewrite(
|
||||
.or_else(|| local_block_map.get(else_bb))
|
||||
.copied()
|
||||
.unwrap_or(*else_bb);
|
||||
MirInstruction::Branch {
|
||||
remapped_term = Some(MirInstruction::Branch {
|
||||
condition: remapper.remap_value(*condition),
|
||||
then_bb: remapped_then,
|
||||
else_bb: remapped_else,
|
||||
}
|
||||
then_edge_args: remap_edge_args(then_edge_args),
|
||||
else_edge_args: remap_edge_args(else_edge_args),
|
||||
});
|
||||
}
|
||||
_ => remapper.remap_instruction(term),
|
||||
};
|
||||
new_block.terminator = Some(remapped_term);
|
||||
_ => remapped_term = Some(remapper.remap_instruction(term)),
|
||||
}
|
||||
if let Some(term) = remapped_term {
|
||||
match term {
|
||||
MirInstruction::Jump { target, edge_args } => {
|
||||
if edge_args.is_some() {
|
||||
new_block.set_jump_with_edge_args(target, edge_args);
|
||||
} else {
|
||||
new_block.set_terminator(MirInstruction::Jump {
|
||||
target,
|
||||
edge_args: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
MirInstruction::Branch {
|
||||
condition,
|
||||
then_bb,
|
||||
then_edge_args,
|
||||
else_bb,
|
||||
else_edge_args,
|
||||
} => {
|
||||
new_block.set_branch_with_edge_args(
|
||||
condition,
|
||||
then_bb,
|
||||
then_edge_args,
|
||||
else_bb,
|
||||
else_edge_args,
|
||||
);
|
||||
}
|
||||
_ => new_block.set_terminator(term),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1153,23 +1222,31 @@ pub(super) fn merge_and_rewrite(
|
||||
match decision {
|
||||
LoweringDecision::NormalizeToExitJump { args } => {
|
||||
// Collect exit values from k_exit arguments
|
||||
let mut exit_edge_args: Option<crate::mir::EdgeArgs> = None;
|
||||
if let Some(b) = boundary {
|
||||
let collector = ExitArgsCollectorBox::new();
|
||||
let exit_args: Vec<ValueId> = if let Some(ref jump_args) = k_exit_jump_args {
|
||||
jump_args
|
||||
.iter()
|
||||
.map(|&arg| remapper.remap_value(arg))
|
||||
.collect()
|
||||
} else {
|
||||
args.clone()
|
||||
let exit_values: Vec<ValueId> =
|
||||
if let Some(ref edge_args) = k_exit_edge_args {
|
||||
edge_args
|
||||
.values
|
||||
.iter()
|
||||
.map(|&arg| remapper.remap_value(arg))
|
||||
.collect()
|
||||
} else {
|
||||
args.clone()
|
||||
};
|
||||
let edge_args = crate::mir::EdgeArgs {
|
||||
layout: b.jump_args_layout,
|
||||
values: exit_values,
|
||||
};
|
||||
exit_edge_args = Some(edge_args.clone());
|
||||
let collection_result =
|
||||
collector.collect(
|
||||
&b.exit_bindings,
|
||||
&exit_args,
|
||||
&edge_args.values,
|
||||
new_block_id,
|
||||
strict_exit,
|
||||
b.jump_args_layout,
|
||||
edge_args.layout,
|
||||
)?;
|
||||
if let Some(expr_result_value) = collection_result.expr_result_value {
|
||||
exit_phi_inputs.push((collection_result.block_id, expr_result_value));
|
||||
@ -1189,8 +1266,17 @@ pub(super) fn merge_and_rewrite(
|
||||
}
|
||||
|
||||
// Generate exit jump using policy box
|
||||
let exit_jump = tail_call_policy.rewrite_to_exit_jump(exit_block_id);
|
||||
new_block.terminator = Some(exit_jump.clone());
|
||||
let exit_jump = if let Some(edge_args) = exit_edge_args {
|
||||
new_block.set_jump_with_edge_args(exit_block_id, Some(edge_args));
|
||||
new_block.terminator.clone().unwrap_or(MirInstruction::Jump {
|
||||
target: exit_block_id,
|
||||
edge_args: None,
|
||||
})
|
||||
} else {
|
||||
let exit_jump = tail_call_policy.rewrite_to_exit_jump(exit_block_id);
|
||||
new_block.set_terminator(exit_jump.clone());
|
||||
exit_jump
|
||||
};
|
||||
|
||||
// Strict mode: verify the generated terminator matches contract
|
||||
if strict_exit {
|
||||
|
||||
@ -361,7 +361,7 @@ impl LoopHeaderPhiBuilder {
|
||||
);
|
||||
}
|
||||
}
|
||||
crate::mir::MirInstruction::Jump { target } => {
|
||||
crate::mir::MirInstruction::Jump { target, .. } => {
|
||||
block.successors.insert(*target);
|
||||
if dev_debug {
|
||||
trace.stderr_if(
|
||||
|
||||
@ -96,6 +96,7 @@ impl TailCallLoweringPolicyBox {
|
||||
pub fn rewrite_to_exit_jump(&self, exit_block_id: BasicBlockId) -> MirInstruction {
|
||||
MirInstruction::Jump {
|
||||
target: exit_block_id,
|
||||
edge_args: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,8 +122,8 @@ impl TailCallLoweringPolicyBox {
|
||||
exit_block_id: BasicBlockId,
|
||||
) -> Result<(), String> {
|
||||
match terminator {
|
||||
MirInstruction::Jump { target } if *target == exit_block_id => Ok(()),
|
||||
MirInstruction::Jump { target } => Err(crate::mir::join_ir::lowering::error_tags::freeze_with_hint(
|
||||
MirInstruction::Jump { target, .. } if *target == exit_block_id => Ok(()),
|
||||
MirInstruction::Jump { target, .. } => Err(crate::mir::join_ir::lowering::error_tags::freeze_with_hint(
|
||||
"phase131/k_exit/wrong_jump_target",
|
||||
&format!(
|
||||
"skippable continuation tail call lowered to Jump {:?}, expected exit_block_id {:?}",
|
||||
@ -184,7 +185,7 @@ mod tests {
|
||||
let jump = policy.rewrite_to_exit_jump(exit_block);
|
||||
assert!(matches!(
|
||||
jump,
|
||||
MirInstruction::Jump { target } if target == exit_block
|
||||
MirInstruction::Jump { target, .. } if target == exit_block
|
||||
));
|
||||
}
|
||||
|
||||
@ -194,6 +195,7 @@ mod tests {
|
||||
let exit_block = BasicBlockId(42);
|
||||
let jump = MirInstruction::Jump {
|
||||
target: exit_block,
|
||||
edge_args: None,
|
||||
};
|
||||
|
||||
let result = policy.verify_exit_jump(&jump, exit_block);
|
||||
@ -206,6 +208,7 @@ mod tests {
|
||||
let exit_block = BasicBlockId(42);
|
||||
let wrong_jump = MirInstruction::Jump {
|
||||
target: BasicBlockId(99),
|
||||
edge_args: None,
|
||||
};
|
||||
|
||||
let result = policy.verify_exit_jump(&wrong_jump, exit_block);
|
||||
|
||||
@ -33,7 +33,7 @@ fn case_a_pure_k_exit_return_is_skippable() {
|
||||
let mut func = make_function("join_func_2");
|
||||
let block = func.blocks.get_mut(&func.entry_block).unwrap();
|
||||
block.instructions.clear();
|
||||
block.terminator = Some(MirInstruction::Return { value: None });
|
||||
block.set_terminator(MirInstruction::Return { value: None });
|
||||
assert!(is_skippable_continuation(&func));
|
||||
}
|
||||
|
||||
@ -52,8 +52,9 @@ fn case_b_k_exit_tailcall_post_k_is_not_skippable() {
|
||||
args: vec![],
|
||||
effects: EffectMask::CONTROL,
|
||||
});
|
||||
block.terminator = Some(MirInstruction::Jump {
|
||||
block.set_terminator(MirInstruction::Jump {
|
||||
target: BasicBlockId(1),
|
||||
edge_args: None,
|
||||
});
|
||||
assert!(!is_skippable_continuation(&func));
|
||||
}
|
||||
@ -67,14 +68,15 @@ fn case_c_multi_block_continuation_is_not_skippable() {
|
||||
// Add a second block
|
||||
let second_block_id = BasicBlockId(1);
|
||||
let mut second_block = crate::mir::BasicBlock::new(second_block_id);
|
||||
second_block.terminator = Some(MirInstruction::Return { value: None });
|
||||
second_block.set_terminator(MirInstruction::Return { value: None });
|
||||
func.blocks.insert(second_block_id, second_block);
|
||||
|
||||
// Entry block jumps to second block
|
||||
let entry_block = func.blocks.get_mut(&func.entry_block).unwrap();
|
||||
entry_block.instructions.clear();
|
||||
entry_block.terminator = Some(MirInstruction::Jump {
|
||||
entry_block.set_terminator(MirInstruction::Jump {
|
||||
target: second_block_id,
|
||||
edge_args: None,
|
||||
});
|
||||
|
||||
assert!(!is_skippable_continuation(&func));
|
||||
@ -91,6 +93,6 @@ fn case_d_continuation_with_instructions_is_not_skippable() {
|
||||
dst: ValueId(0),
|
||||
value: crate::mir::types::ConstValue::Integer(42),
|
||||
});
|
||||
block.terminator = Some(MirInstruction::Return { value: None });
|
||||
block.set_terminator(MirInstruction::Return { value: None });
|
||||
assert!(!is_skippable_continuation(&func));
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
//! - Control tree capability guard (control_tree_capability_guard.rs) ✅ Phase 112
|
||||
|
||||
pub(in crate::mir::builder) mod control_tree_capability_guard;
|
||||
pub(in crate::mir::builder) mod api;
|
||||
pub(in crate::mir::builder) mod legacy; // Phase 132-R0 Task 4: Legacy routing isolation
|
||||
pub(in crate::mir::builder) mod loop_context;
|
||||
pub(in crate::mir::builder) mod merge;
|
||||
|
||||
@ -18,6 +18,8 @@ pub fn emit_conditional(
|
||||
condition: cond,
|
||||
then_bb,
|
||||
else_bb,
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -28,6 +30,9 @@ pub fn emit_jump(b: &mut MirBuilder, target: BasicBlockId) -> Result<(), String>
|
||||
crate::mir::ssot::cf_common::set_jump(func, cur_bb, target);
|
||||
Ok(())
|
||||
} else {
|
||||
b.emit_instruction(MirInstruction::Jump { target })
|
||||
b.emit_instruction(MirInstruction::Jump {
|
||||
target,
|
||||
edge_args: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,7 +102,25 @@ impl JoinIrIdRemapper {
|
||||
vals.extend(args.iter().copied());
|
||||
vals
|
||||
}
|
||||
Branch { condition, .. } => vec![*condition],
|
||||
Branch {
|
||||
condition,
|
||||
then_edge_args,
|
||||
else_edge_args,
|
||||
..
|
||||
} => {
|
||||
let mut vals = vec![*condition];
|
||||
if let Some(args) = then_edge_args {
|
||||
vals.extend(args.values.iter().copied());
|
||||
}
|
||||
if let Some(args) = else_edge_args {
|
||||
vals.extend(args.values.iter().copied());
|
||||
}
|
||||
vals
|
||||
}
|
||||
Jump { edge_args, .. } => edge_args
|
||||
.as_ref()
|
||||
.map(|args| args.values.clone())
|
||||
.unwrap_or_default(),
|
||||
Return { value } => value.iter().copied().collect(),
|
||||
Phi { dst, inputs, .. } => {
|
||||
let mut vals = vec![*dst];
|
||||
@ -177,8 +195,15 @@ impl JoinIrIdRemapper {
|
||||
/// 命令を新しい ID空間にリマップ
|
||||
pub fn remap_instruction(&self, inst: &MirInstruction) -> MirInstruction {
|
||||
use crate::mir::MirInstruction::*;
|
||||
use crate::mir::EdgeArgs;
|
||||
|
||||
let remap = |v: ValueId| self.value_map.get(&v).copied().unwrap_or(v);
|
||||
let remap_edge_args = |edge_args: &Option<EdgeArgs>| {
|
||||
edge_args.as_ref().map(|args| EdgeArgs {
|
||||
layout: args.layout,
|
||||
values: args.values.iter().map(|&v| remap(v)).collect(),
|
||||
})
|
||||
};
|
||||
|
||||
match inst {
|
||||
Const { dst, value } => Const {
|
||||
@ -431,8 +456,28 @@ impl JoinIrIdRemapper {
|
||||
then_val: remap(*then_val),
|
||||
else_val: remap(*else_val),
|
||||
},
|
||||
// Pass through unchanged (Branch/Jump/Return handled separately)
|
||||
Branch { .. } | Jump { .. } | Return { .. } | Nop | Safepoint => inst.clone(),
|
||||
Branch {
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
then_edge_args,
|
||||
else_edge_args,
|
||||
} => Branch {
|
||||
condition: remap(*condition),
|
||||
then_bb: *then_bb,
|
||||
else_bb: *else_bb,
|
||||
then_edge_args: remap_edge_args(then_edge_args),
|
||||
else_edge_args: remap_edge_args(else_edge_args),
|
||||
},
|
||||
Jump { target, edge_args } => Jump {
|
||||
target: *target,
|
||||
edge_args: remap_edge_args(edge_args),
|
||||
},
|
||||
Return { value } => Return {
|
||||
value: value.map(remap),
|
||||
},
|
||||
// Pass through unchanged
|
||||
Nop | Safepoint => inst.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user