refactor(mir): phase260 p0 edge-args plumbing (strangler) + ssot api + docs
This commit is contained in:
@ -394,7 +394,7 @@ impl MirInterpreter {
|
||||
};
|
||||
Ok(BlockOutcome::Return(result))
|
||||
}
|
||||
Some(MirInstruction::Jump { target }) => Ok(BlockOutcome::Next {
|
||||
Some(MirInstruction::Jump { target, .. }) => Ok(BlockOutcome::Next {
|
||||
target: *target,
|
||||
predecessor: block.id,
|
||||
}),
|
||||
@ -402,6 +402,7 @@ impl MirInterpreter {
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
..
|
||||
}) => {
|
||||
// Dev counter: count branch terminators actually evaluated
|
||||
self.branch_count = self.branch_count.saturating_add(1);
|
||||
|
||||
@ -388,7 +388,7 @@ impl WasmCodegen {
|
||||
}
|
||||
|
||||
// Control Flow Instructions (Critical for loops and conditions)
|
||||
MirInstruction::Jump { target } => {
|
||||
MirInstruction::Jump { target, .. } => {
|
||||
// Unconditional jump to target basic block
|
||||
// Use WASM br instruction to break to the target block
|
||||
Ok(vec![format!("br $block_{}", target.as_u32())])
|
||||
@ -398,6 +398,7 @@ impl WasmCodegen {
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
..
|
||||
} => {
|
||||
// Conditional branch based on condition value
|
||||
// Load condition value and branch accordingly
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
use super::{EffectMask, MirInstruction, SpannedInstRef, SpannedInstruction, ValueId};
|
||||
use crate::ast::Span;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
use crate::runtime::get_global_ring0;
|
||||
use std::collections::BTreeSet; // Phase 69-3: HashSet → BTreeSet for determinism
|
||||
use std::fmt;
|
||||
@ -42,6 +43,20 @@ impl fmt::Display for BasicBlockId {
|
||||
}
|
||||
}
|
||||
|
||||
/// Edge arguments for CFG edges (Phase 260 P0)
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct EdgeArgs {
|
||||
pub layout: JumpArgsLayout,
|
||||
pub values: Vec<ValueId>,
|
||||
}
|
||||
|
||||
/// Outgoing edge from a basic block
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OutEdge {
|
||||
pub target: BasicBlockId,
|
||||
pub args: Option<EdgeArgs>,
|
||||
}
|
||||
|
||||
/// A basic block in SSA form
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BasicBlock {
|
||||
@ -82,6 +97,8 @@ pub struct BasicBlock {
|
||||
/// all the Jump args (not just the first one) so that exit PHI can correctly
|
||||
/// merge carrier values from multiple exit paths.
|
||||
pub jump_args: Option<Vec<ValueId>>,
|
||||
/// Phase 260 P0: Layout for legacy jump_args (for consistency checks)
|
||||
pub jump_args_layout: Option<JumpArgsLayout>,
|
||||
}
|
||||
|
||||
impl BasicBlock {
|
||||
@ -99,6 +116,7 @@ impl BasicBlock {
|
||||
reachable: false,
|
||||
sealed: false,
|
||||
jump_args: None, // Phase 246-EX: No jump args by default
|
||||
jump_args_layout: None, // Phase 260 P0: Unknown by default
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,18 +179,23 @@ impl BasicBlock {
|
||||
|
||||
/// Update successors based on the terminator instruction
|
||||
fn update_successors_from_terminator(&mut self) {
|
||||
self.successors.clear();
|
||||
self.successors = self.successors_from_terminator();
|
||||
}
|
||||
|
||||
/// Compute successors from the terminator (SSOT for CFG verification)
|
||||
pub fn successors_from_terminator(&self) -> BTreeSet<BasicBlockId> {
|
||||
let mut successors = BTreeSet::new();
|
||||
|
||||
if let Some(ref terminator) = self.terminator {
|
||||
match terminator {
|
||||
MirInstruction::Branch {
|
||||
then_bb, else_bb, ..
|
||||
} => {
|
||||
self.successors.insert(*then_bb);
|
||||
self.successors.insert(*else_bb);
|
||||
successors.insert(*then_bb);
|
||||
successors.insert(*else_bb);
|
||||
}
|
||||
MirInstruction::Jump { target } => {
|
||||
self.successors.insert(*target);
|
||||
MirInstruction::Jump { target, .. } => {
|
||||
successors.insert(*target);
|
||||
}
|
||||
MirInstruction::Return { .. } => {
|
||||
// No successors for return
|
||||
@ -184,6 +207,140 @@ impl BasicBlock {
|
||||
_ => unreachable!("Non-terminator instruction in terminator position"),
|
||||
}
|
||||
}
|
||||
|
||||
successors
|
||||
}
|
||||
|
||||
/// Enumerate all outgoing CFG edges
|
||||
pub fn out_edges(&self) -> Vec<OutEdge> {
|
||||
match self.terminator {
|
||||
Some(MirInstruction::Branch {
|
||||
then_bb,
|
||||
else_bb,
|
||||
ref then_edge_args,
|
||||
ref else_edge_args,
|
||||
..
|
||||
}) => vec![
|
||||
OutEdge {
|
||||
target: then_bb,
|
||||
args: then_edge_args.clone(),
|
||||
},
|
||||
OutEdge {
|
||||
target: else_bb,
|
||||
args: else_edge_args.clone(),
|
||||
},
|
||||
],
|
||||
Some(MirInstruction::Jump {
|
||||
target,
|
||||
ref edge_args,
|
||||
..
|
||||
}) => vec![OutEdge {
|
||||
target,
|
||||
args: edge_args.clone().or_else(|| self.legacy_edge_args()),
|
||||
}],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get edge args for a specific target (if present)
|
||||
pub fn edge_args_to(&self, target: BasicBlockId) -> Option<EdgeArgs> {
|
||||
self.out_edges()
|
||||
.into_iter()
|
||||
.find(|edge| edge.target == target)
|
||||
.and_then(|edge| edge.args)
|
||||
}
|
||||
|
||||
/// Set legacy jump args metadata (migration helper)
|
||||
pub fn set_legacy_jump_args(&mut self, values: Vec<ValueId>, layout: Option<JumpArgsLayout>) {
|
||||
self.jump_args = Some(values);
|
||||
self.jump_args_layout = layout;
|
||||
}
|
||||
|
||||
/// Clear legacy jump args metadata (migration helper)
|
||||
pub fn clear_legacy_jump_args(&mut self) {
|
||||
self.jump_args = None;
|
||||
self.jump_args_layout = None;
|
||||
}
|
||||
|
||||
/// Check if legacy jump args metadata exists
|
||||
pub fn has_legacy_jump_args(&self) -> bool {
|
||||
self.jump_args.is_some()
|
||||
}
|
||||
|
||||
/// Access legacy jump args values (read-only, migration helper)
|
||||
pub fn legacy_jump_args_values(&self) -> Option<&[ValueId]> {
|
||||
self.jump_args.as_deref()
|
||||
}
|
||||
|
||||
/// Access legacy jump args layout (read-only, migration helper)
|
||||
pub fn legacy_jump_args_layout(&self) -> Option<JumpArgsLayout> {
|
||||
self.jump_args_layout
|
||||
}
|
||||
|
||||
/// Build edge args from legacy values with an explicit layout (migration helper)
|
||||
pub fn legacy_edge_args_with_layout(&self, layout: JumpArgsLayout) -> Option<EdgeArgs> {
|
||||
self.jump_args.as_ref().map(|values| EdgeArgs {
|
||||
layout,
|
||||
values: values.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Set jump terminator with edge args and legacy metadata (SSOT write helper)
|
||||
pub fn set_jump_with_edge_args(&mut self, target: BasicBlockId, edge_args: Option<EdgeArgs>) {
|
||||
let terminator = MirInstruction::Jump {
|
||||
target,
|
||||
edge_args: edge_args.clone(),
|
||||
};
|
||||
if !self.is_terminator(&terminator) {
|
||||
panic!("Instruction is not a valid terminator: {:?}", terminator);
|
||||
}
|
||||
|
||||
self.effects = self.effects | terminator.effects();
|
||||
self.terminator = Some(terminator);
|
||||
self.terminator_span = Some(Span::unknown());
|
||||
if let Some(args) = edge_args {
|
||||
self.jump_args = Some(args.values.clone());
|
||||
self.jump_args_layout = Some(args.layout);
|
||||
} else {
|
||||
self.clear_legacy_jump_args();
|
||||
}
|
||||
self.update_successors_from_terminator();
|
||||
}
|
||||
|
||||
/// Set branch terminator with per-edge args (clears legacy metadata)
|
||||
pub fn set_branch_with_edge_args(
|
||||
&mut self,
|
||||
condition: ValueId,
|
||||
then_bb: BasicBlockId,
|
||||
then_edge_args: Option<EdgeArgs>,
|
||||
else_bb: BasicBlockId,
|
||||
else_edge_args: Option<EdgeArgs>,
|
||||
) {
|
||||
let terminator = MirInstruction::Branch {
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
then_edge_args,
|
||||
else_edge_args,
|
||||
};
|
||||
if !self.is_terminator(&terminator) {
|
||||
panic!("Instruction is not a valid terminator: {:?}", terminator);
|
||||
}
|
||||
|
||||
self.effects = self.effects | terminator.effects();
|
||||
self.terminator = Some(terminator);
|
||||
self.terminator_span = Some(Span::unknown());
|
||||
self.clear_legacy_jump_args();
|
||||
self.update_successors_from_terminator();
|
||||
}
|
||||
|
||||
fn legacy_edge_args(&self) -> Option<EdgeArgs> {
|
||||
let values = self.jump_args.as_ref()?;
|
||||
let layout = self.jump_args_layout?;
|
||||
Some(EdgeArgs {
|
||||
layout,
|
||||
values: values.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Add a predecessor
|
||||
@ -540,6 +697,8 @@ mod tests {
|
||||
condition: ValueId::new(0),
|
||||
then_bb,
|
||||
else_bb,
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
};
|
||||
|
||||
bb.add_instruction(branch_inst);
|
||||
|
||||
@ -670,7 +670,7 @@ impl MirBuilder {
|
||||
MirInstruction::Branch {
|
||||
then_bb, else_bb, ..
|
||||
} => (Some(*then_bb), Some(*else_bb), None),
|
||||
MirInstruction::Jump { target } => (None, None, Some(*target)),
|
||||
MirInstruction::Jump { target, .. } => (None, None, Some(*target)),
|
||||
_ => (None, None, None),
|
||||
};
|
||||
|
||||
@ -784,8 +784,9 @@ impl MirBuilder {
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
..
|
||||
} => format!("br {}, {}, {}", condition, then_bb, else_bb),
|
||||
MirInstruction::Jump { target } => format!("br {}", target),
|
||||
MirInstruction::Jump { target, .. } => format!("br {}", target),
|
||||
_ => format!("{:?}", instruction),
|
||||
}
|
||||
);
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -728,7 +728,7 @@ mod tests {
|
||||
"k_exit must return Some(value)"
|
||||
);
|
||||
|
||||
// Bridge sanity: tail-call blocks must carry jump_args metadata for merge collection.
|
||||
// Bridge sanity: tail-call blocks must carry legacy jump_args metadata for merge collection.
|
||||
// This is required for DirectValue mode (no PHI) to reconnect carriers safely.
|
||||
let mir_module = crate::mir::join_ir_vm_bridge::bridge_joinir_to_mir(&module)
|
||||
.expect("bridge_joinir_to_mir failed");
|
||||
@ -744,8 +744,8 @@ mod tests {
|
||||
.get(&entry)
|
||||
.expect("missing loop_body entry block");
|
||||
assert!(
|
||||
entry_block.jump_args.is_some(),
|
||||
"loop_body entry block must have jump_args metadata in bridged MIR"
|
||||
entry_block.has_legacy_jump_args(),
|
||||
"loop_body entry block must have legacy jump_args metadata in bridged MIR"
|
||||
);
|
||||
|
||||
// Loop-only (the routing path in real lowering): still must encode loop_step as a tail-call.
|
||||
|
||||
@ -335,7 +335,10 @@ impl MirFunction {
|
||||
) -> Result<(), String> {
|
||||
if let Some(bb) = self.get_block_mut(bb_id) {
|
||||
if !bb.is_terminated() {
|
||||
bb.set_terminator(MirInstruction::Jump { target });
|
||||
bb.set_terminator(MirInstruction::Jump {
|
||||
target,
|
||||
edge_args: None,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
@ -357,6 +360,8 @@ impl MirFunction {
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
});
|
||||
Ok(())
|
||||
} else {
|
||||
|
||||
@ -63,6 +63,9 @@ pub fn emit_return_value(f: &mut MirFunction, bb: BasicBlockId, value: ValueId)
|
||||
#[inline]
|
||||
pub fn emit_jump(f: &mut MirFunction, bb: BasicBlockId, target: BasicBlockId) {
|
||||
if let Some(block) = f.get_block_mut(bb) {
|
||||
block.add_instruction(MirInstruction::Jump { target });
|
||||
block.add_instruction(MirInstruction::Jump {
|
||||
target,
|
||||
edge_args: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
* SSA-form instructions with effect tracking for optimization
|
||||
*/
|
||||
|
||||
use super::{EffectMask, ValueId};
|
||||
use super::{EdgeArgs, EffectMask, ValueId};
|
||||
use crate::mir::definitions::Callee; // Import Callee from unified definitions
|
||||
use crate::mir::types::{
|
||||
BarrierOp, BinaryOp, CompareOp, ConstValue, MirType, TypeOpKind, UnaryOp, WeakRefOp,
|
||||
@ -116,11 +116,19 @@ pub enum MirInstruction {
|
||||
condition: ValueId,
|
||||
then_bb: super::BasicBlockId,
|
||||
else_bb: super::BasicBlockId,
|
||||
/// Optional edge args for then branch (Phase 260 P0)
|
||||
then_edge_args: Option<EdgeArgs>,
|
||||
/// Optional edge args for else branch (Phase 260 P0)
|
||||
else_edge_args: Option<EdgeArgs>,
|
||||
},
|
||||
|
||||
/// Unconditional jump
|
||||
/// `jmp %target_bb`
|
||||
Jump { target: super::BasicBlockId },
|
||||
Jump {
|
||||
target: super::BasicBlockId,
|
||||
/// Optional edge args for jump (Phase 260 P0)
|
||||
edge_args: Option<EdgeArgs>,
|
||||
},
|
||||
|
||||
/// Return from function
|
||||
/// `ret %value` or `ret void`
|
||||
|
||||
@ -176,6 +176,31 @@ impl MirInstruction {
|
||||
return used;
|
||||
}
|
||||
|
||||
match self {
|
||||
MirInstruction::Branch {
|
||||
condition,
|
||||
then_edge_args,
|
||||
else_edge_args,
|
||||
..
|
||||
} => {
|
||||
let mut used = vec![*condition];
|
||||
if let Some(args) = then_edge_args {
|
||||
used.extend(args.values.iter().copied());
|
||||
}
|
||||
if let Some(args) = else_edge_args {
|
||||
used.extend(args.values.iter().copied());
|
||||
}
|
||||
return used;
|
||||
}
|
||||
MirInstruction::Jump { edge_args, .. } => {
|
||||
return edge_args
|
||||
.as_ref()
|
||||
.map(|args| args.values.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Some(used) = inst_meta::used_via_meta(self) {
|
||||
return used;
|
||||
}
|
||||
|
||||
@ -132,6 +132,7 @@ impl IfMergeLowerer {
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
..
|
||||
} => IfBranch {
|
||||
cond: *condition,
|
||||
then_block: *then_bb,
|
||||
|
||||
@ -142,6 +142,7 @@ impl IfSelectLowerer {
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
..
|
||||
} => IfBranch {
|
||||
cond: *condition,
|
||||
then_block: *then_bb,
|
||||
@ -263,13 +264,13 @@ impl IfSelectLowerer {
|
||||
) -> Option<BasicBlockId> {
|
||||
// then が Jump で終わるか確認
|
||||
let then_target = match then_block.terminator.as_ref()? {
|
||||
MirInstruction::Jump { target } => *target,
|
||||
MirInstruction::Jump { target, .. } => *target,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
// else が Jump で終わるか確認
|
||||
let else_target = match else_block.terminator.as_ref()? {
|
||||
MirInstruction::Jump { target } => *target,
|
||||
MirInstruction::Jump { target, .. } => *target,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
@ -312,7 +313,7 @@ impl IfSelectLowerer {
|
||||
|
||||
// then ブロックが Jump で終わるか確認
|
||||
let merge_block_id = match then_block.terminator.as_ref()? {
|
||||
MirInstruction::Jump { target } => *target,
|
||||
MirInstruction::Jump { target, .. } => *target,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
@ -339,7 +340,7 @@ impl IfSelectLowerer {
|
||||
|
||||
// else ブロックも同じ merge ブロックに Jump するか確認
|
||||
let else_merge = match else_block.terminator.as_ref()? {
|
||||
MirInstruction::Jump { target } => *target,
|
||||
MirInstruction::Jump { target, .. } => *target,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
|
||||
@ -268,6 +268,8 @@ impl JoinIrBlockConverter {
|
||||
condition: *cond,
|
||||
then_bb: then_block,
|
||||
else_bb: else_block,
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
};
|
||||
Self::finalize_block(
|
||||
mir_func,
|
||||
@ -290,8 +292,9 @@ impl JoinIrBlockConverter {
|
||||
effects: EffectMask::WRITE,
|
||||
});
|
||||
then_block_obj.instruction_spans.push(Span::unknown());
|
||||
then_block_obj.terminator = Some(MirInstruction::Jump {
|
||||
then_block_obj.set_terminator(MirInstruction::Jump {
|
||||
target: merge_block,
|
||||
edge_args: None,
|
||||
});
|
||||
mir_func.blocks.insert(then_block, then_block_obj);
|
||||
|
||||
@ -302,8 +305,9 @@ impl JoinIrBlockConverter {
|
||||
src: *receiver,
|
||||
});
|
||||
else_block_obj.instruction_spans.push(Span::unknown());
|
||||
else_block_obj.terminator = Some(MirInstruction::Jump {
|
||||
else_block_obj.set_terminator(MirInstruction::Jump {
|
||||
target: merge_block,
|
||||
edge_args: None,
|
||||
});
|
||||
mir_func.blocks.insert(else_block, else_block_obj);
|
||||
|
||||
@ -428,8 +432,8 @@ impl JoinIrBlockConverter {
|
||||
// Without this, tail-call blocks look like "no args", forcing fallbacks that can
|
||||
// produce undefined ValueIds in DirectValue mode.
|
||||
if let Some(block) = mir_func.blocks.get_mut(&self.current_block_id) {
|
||||
if block.jump_args.is_none() {
|
||||
block.jump_args = Some(args.to_vec());
|
||||
if !block.has_legacy_jump_args() {
|
||||
block.set_legacy_jump_args(args.to_vec(), None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -484,6 +488,8 @@ impl JoinIrBlockConverter {
|
||||
condition: *cond_var,
|
||||
then_bb: exit_block_id,
|
||||
else_bb: continue_block_id,
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
};
|
||||
|
||||
Self::finalize_block(
|
||||
@ -497,7 +503,7 @@ impl JoinIrBlockConverter {
|
||||
let mut exit_block = crate::mir::BasicBlock::new(exit_block_id);
|
||||
|
||||
// Phase 246-EX: Store Jump args in metadata for exit PHI construction
|
||||
exit_block.jump_args = Some(args.to_vec());
|
||||
exit_block.set_legacy_jump_args(args.to_vec(), None);
|
||||
|
||||
// Phase 256 P1.9: Generate tail call to continuation
|
||||
exit_block.instructions.push(MirInstruction::Const {
|
||||
@ -513,7 +519,7 @@ impl JoinIrBlockConverter {
|
||||
effects: EffectMask::PURE,
|
||||
});
|
||||
exit_block.instruction_spans.push(Span::unknown());
|
||||
exit_block.terminator = Some(MirInstruction::Return { value: Some(call_result_id) });
|
||||
exit_block.set_terminator(MirInstruction::Return { value: Some(call_result_id) });
|
||||
mir_func.blocks.insert(exit_block_id, exit_block);
|
||||
|
||||
// Continue block
|
||||
@ -539,8 +545,8 @@ impl JoinIrBlockConverter {
|
||||
|
||||
// Preserve jump args as metadata (SSOT for ExitLine/jump_args wiring).
|
||||
if let Some(block) = mir_func.blocks.get_mut(&self.current_block_id) {
|
||||
if block.jump_args.is_none() {
|
||||
block.jump_args = Some(args.to_vec());
|
||||
if !block.has_legacy_jump_args() {
|
||||
block.set_legacy_jump_args(args.to_vec(), None);
|
||||
}
|
||||
}
|
||||
|
||||
@ -631,6 +637,8 @@ impl JoinIrBlockConverter {
|
||||
condition: *cond,
|
||||
then_bb: then_block,
|
||||
else_bb: else_block,
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
};
|
||||
Self::finalize_block(
|
||||
mir_func,
|
||||
@ -648,8 +656,9 @@ impl JoinIrBlockConverter {
|
||||
});
|
||||
then_block_obj.instruction_spans.push(Span::unknown());
|
||||
}
|
||||
then_block_obj.terminator = Some(MirInstruction::Jump {
|
||||
then_block_obj.set_terminator(MirInstruction::Jump {
|
||||
target: merge_block,
|
||||
edge_args: None,
|
||||
});
|
||||
mir_func.blocks.insert(then_block, then_block_obj);
|
||||
|
||||
@ -662,8 +671,9 @@ impl JoinIrBlockConverter {
|
||||
});
|
||||
else_block_obj.instruction_spans.push(Span::unknown());
|
||||
}
|
||||
else_block_obj.terminator = Some(MirInstruction::Jump {
|
||||
else_block_obj.set_terminator(MirInstruction::Jump {
|
||||
target: merge_block,
|
||||
edge_args: None,
|
||||
});
|
||||
mir_func.blocks.insert(else_block, else_block_obj);
|
||||
|
||||
@ -752,6 +762,8 @@ impl JoinIrBlockConverter {
|
||||
condition: *cond_var,
|
||||
then_bb: next_true_block,
|
||||
else_bb: final_else_block,
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
};
|
||||
|
||||
if level == 0 {
|
||||
@ -775,8 +787,9 @@ impl JoinIrBlockConverter {
|
||||
});
|
||||
then_block_obj.instruction_spans.push(Span::unknown());
|
||||
}
|
||||
then_block_obj.terminator = Some(MirInstruction::Jump {
|
||||
then_block_obj.set_terminator(MirInstruction::Jump {
|
||||
target: merge_block,
|
||||
edge_args: None,
|
||||
});
|
||||
mir_func.blocks.insert(then_block, then_block_obj);
|
||||
|
||||
@ -789,8 +802,9 @@ impl JoinIrBlockConverter {
|
||||
});
|
||||
else_block_obj.instruction_spans.push(Span::unknown());
|
||||
}
|
||||
else_block_obj.terminator = Some(MirInstruction::Jump {
|
||||
else_block_obj.set_terminator(MirInstruction::Jump {
|
||||
target: merge_block,
|
||||
edge_args: None,
|
||||
});
|
||||
mir_func.blocks.insert(final_else_block, else_block_obj);
|
||||
|
||||
@ -842,7 +856,7 @@ impl JoinIrBlockConverter {
|
||||
block.instructions = instructions;
|
||||
block.instruction_spans = vec![Span::unknown(); inst_count];
|
||||
}
|
||||
block.terminator = Some(terminator);
|
||||
block.set_terminator(terminator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -140,32 +140,40 @@ fn lower_normalized_function_direct(
|
||||
let merge_bb = BasicBlockId(next_block_id);
|
||||
next_block_id += 1;
|
||||
|
||||
finalize_block(
|
||||
&mut mir_func,
|
||||
current_block_id,
|
||||
mem::take(&mut current_insts),
|
||||
MirInstruction::Branch {
|
||||
condition: cond,
|
||||
then_bb,
|
||||
else_bb,
|
||||
},
|
||||
None,
|
||||
);
|
||||
finalize_block(
|
||||
&mut mir_func,
|
||||
current_block_id,
|
||||
mem::take(&mut current_insts),
|
||||
MirInstruction::Branch {
|
||||
condition: cond,
|
||||
then_bb,
|
||||
else_bb,
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
},
|
||||
None,
|
||||
);
|
||||
|
||||
finalize_block(
|
||||
&mut mir_func,
|
||||
then_bb,
|
||||
Vec::new(),
|
||||
MirInstruction::Jump { target: merge_bb },
|
||||
None,
|
||||
);
|
||||
finalize_block(
|
||||
&mut mir_func,
|
||||
else_bb,
|
||||
Vec::new(),
|
||||
MirInstruction::Jump { target: merge_bb },
|
||||
None,
|
||||
);
|
||||
finalize_block(
|
||||
&mut mir_func,
|
||||
then_bb,
|
||||
Vec::new(),
|
||||
MirInstruction::Jump {
|
||||
target: merge_bb,
|
||||
edge_args: None,
|
||||
},
|
||||
None,
|
||||
);
|
||||
finalize_block(
|
||||
&mut mir_func,
|
||||
else_bb,
|
||||
Vec::new(),
|
||||
MirInstruction::Jump {
|
||||
target: merge_bb,
|
||||
edge_args: None,
|
||||
},
|
||||
None,
|
||||
);
|
||||
|
||||
current_block_id = merge_bb;
|
||||
current_insts = vec![MirInstruction::Phi {
|
||||
@ -240,6 +248,8 @@ fn lower_normalized_function_direct(
|
||||
condition: cond_remapped,
|
||||
then_bb,
|
||||
else_bb,
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
},
|
||||
None,
|
||||
);
|
||||
@ -298,7 +308,7 @@ fn lower_normalized_function_direct(
|
||||
block.instruction_spans = vec![Span::unknown(); block.instructions.len()];
|
||||
}
|
||||
if block.terminator.is_none() {
|
||||
block.terminator = Some(MirInstruction::Return { value: None });
|
||||
block.set_terminator(MirInstruction::Return { value: None });
|
||||
}
|
||||
}
|
||||
|
||||
@ -398,7 +408,7 @@ fn build_exit_or_tail_branch(
|
||||
.or_insert_with(|| BasicBlock::new(block_id));
|
||||
block.instructions = insts;
|
||||
block.instruction_spans = vec![Span::unknown(); block.instructions.len()];
|
||||
block.terminator = Some(term);
|
||||
block.set_terminator(term);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -410,8 +420,8 @@ fn build_exit_or_tail_branch(
|
||||
.or_insert_with(|| BasicBlock::new(block_id));
|
||||
block.instructions.clear();
|
||||
block.instruction_spans.clear();
|
||||
block.terminator = Some(MirInstruction::Return { value: ret_val });
|
||||
block.jump_args = Some(env.to_vec());
|
||||
block.set_terminator(MirInstruction::Return { value: ret_val });
|
||||
block.set_legacy_jump_args(env.to_vec(), None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -455,6 +465,10 @@ fn finalize_block(
|
||||
.or_insert_with(|| BasicBlock::new(block_id));
|
||||
block.instructions = instructions;
|
||||
block.instruction_spans = vec![Span::unknown(); block.instructions.len()];
|
||||
block.terminator = Some(terminator);
|
||||
block.jump_args = jump_args;
|
||||
block.set_terminator(terminator);
|
||||
if let Some(args) = jump_args {
|
||||
block.set_legacy_jump_args(args, None);
|
||||
} else {
|
||||
block.clear_legacy_jump_args();
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +55,10 @@ pub fn build_simple_loop<L: LoopBuilderApi>(
|
||||
let after = lb.new_block();
|
||||
|
||||
// Jump to header
|
||||
lb.emit(MirInstruction::Jump { target: header })?;
|
||||
lb.emit(MirInstruction::Jump {
|
||||
target: header,
|
||||
edge_args: None,
|
||||
})?;
|
||||
|
||||
// Header: branch on provided condition
|
||||
lb.start_new_block(header)?;
|
||||
@ -63,12 +66,17 @@ pub fn build_simple_loop<L: LoopBuilderApi>(
|
||||
condition,
|
||||
then_bb: body,
|
||||
else_bb: after,
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
})?;
|
||||
|
||||
// Body
|
||||
lb.start_new_block(body)?;
|
||||
build_body(lb)?;
|
||||
lb.emit(MirInstruction::Jump { target: header })?;
|
||||
lb.emit(MirInstruction::Jump {
|
||||
target: header,
|
||||
edge_args: None,
|
||||
})?;
|
||||
|
||||
// After: return void value
|
||||
lb.start_new_block(after)?;
|
||||
|
||||
@ -53,7 +53,7 @@ pub mod verification;
|
||||
pub mod verification_types; // extracted error types // Optimization subpasses (e.g., type_hints) // Phase 25.1f: Loop/If 共通ビュー(ControlForm)
|
||||
|
||||
// Re-export main types for easy access
|
||||
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
|
||||
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator, EdgeArgs, OutEdge};
|
||||
pub use binding_id::BindingId; // Phase 74: BindingId infrastructure
|
||||
pub use builder::MirBuilder;
|
||||
|
||||
|
||||
@ -524,7 +524,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dce_keeps_jump_args_values() {
|
||||
fn test_dce_keeps_edge_args_values() {
|
||||
let signature = FunctionSignature {
|
||||
name: "main".to_string(),
|
||||
params: vec![],
|
||||
@ -533,6 +533,7 @@ mod tests {
|
||||
};
|
||||
let mut func = MirFunction::new(signature, BasicBlockId::new(0));
|
||||
let bb0 = BasicBlockId::new(0);
|
||||
let bb1 = BasicBlockId::new(1);
|
||||
let mut b0 = BasicBlock::new(bb0);
|
||||
let v0 = ValueId::new(0);
|
||||
let v1 = ValueId::new(1);
|
||||
@ -541,9 +542,17 @@ mod tests {
|
||||
value: ConstValue::Integer(1),
|
||||
});
|
||||
b0.add_instruction(MirInstruction::Copy { dst: v1, src: v0 });
|
||||
b0.add_instruction(MirInstruction::Return { value: None });
|
||||
b0.jump_args = Some(vec![v1]);
|
||||
b0.set_jump_with_edge_args(
|
||||
bb1,
|
||||
Some(crate::mir::EdgeArgs {
|
||||
layout: crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout::CarriersOnly,
|
||||
values: vec![v1],
|
||||
}),
|
||||
);
|
||||
func.add_block(b0);
|
||||
let mut exit_block = BasicBlock::new(bb1);
|
||||
exit_block.set_terminator(MirInstruction::Return { value: None });
|
||||
func.add_block(exit_block);
|
||||
let mut module = MirModule::new("test".to_string());
|
||||
module.add_function(func);
|
||||
|
||||
@ -555,7 +564,7 @@ mod tests {
|
||||
.instructions
|
||||
.iter()
|
||||
.any(|inst| matches!(inst, MirInstruction::Copy { .. }));
|
||||
assert!(has_copy, "Copy used only by jump_args should not be eliminated");
|
||||
assert!(has_copy, "Copy used only by edge args should not be eliminated");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -37,9 +37,11 @@ fn eliminate_dead_code_in_function(function: &mut MirFunction) -> usize {
|
||||
used_values.insert(u);
|
||||
}
|
||||
}
|
||||
if let Some(args) = &block.jump_args {
|
||||
for &u in args {
|
||||
used_values.insert(u);
|
||||
for edge in block.out_edges() {
|
||||
if let Some(args) = edge.args {
|
||||
for u in args.values {
|
||||
used_values.insert(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,7 +110,7 @@ mod tests {
|
||||
use crate::mir::{BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirInstruction, MirType};
|
||||
|
||||
#[test]
|
||||
fn test_dce_keeps_jump_args_values() {
|
||||
fn test_dce_keeps_edge_args_values() {
|
||||
let mut module = MirModule::new("dce_test".to_string());
|
||||
|
||||
let sig = FunctionSignature {
|
||||
@ -122,6 +124,7 @@ mod tests {
|
||||
let v1 = ValueId(1);
|
||||
let v2 = ValueId(2);
|
||||
let v_dead = ValueId(3);
|
||||
let bb1 = BasicBlockId(1);
|
||||
|
||||
{
|
||||
let bb0 = func.blocks.get_mut(&BasicBlockId(0)).unwrap();
|
||||
@ -141,10 +144,20 @@ mod tests {
|
||||
});
|
||||
bb0.instruction_spans.push(Span::unknown());
|
||||
|
||||
// SSOT: jump_args is a semantic use-site (ExitLine, continuation args).
|
||||
bb0.jump_args = Some(vec![v2]);
|
||||
// SSOT: edge args are semantic use-sites (ExitLine, continuation args).
|
||||
bb0.set_jump_with_edge_args(
|
||||
bb1,
|
||||
Some(crate::mir::EdgeArgs {
|
||||
layout: crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout::CarriersOnly,
|
||||
values: vec![v2],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
let mut exit_block = crate::mir::BasicBlock::new(bb1);
|
||||
exit_block.set_terminator(MirInstruction::Return { value: None });
|
||||
func.add_block(exit_block);
|
||||
|
||||
module.add_function(func);
|
||||
|
||||
let eliminated = eliminate_dead_code(&mut module);
|
||||
@ -156,7 +169,7 @@ mod tests {
|
||||
// Contract: spans must stay aligned with instructions.
|
||||
assert_eq!(bb0.instructions.len(), bb0.instruction_spans.len());
|
||||
|
||||
// Contract: values that appear only in jump_args must be kept (and their deps).
|
||||
// Contract: values that appear only in edge args must be kept (and their deps).
|
||||
assert!(bb0
|
||||
.instructions
|
||||
.iter()
|
||||
|
||||
@ -232,11 +232,12 @@ pub fn format_instruction(
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
..
|
||||
} => {
|
||||
format!("br {}, label {}, label {}", condition, then_bb, else_bb)
|
||||
}
|
||||
|
||||
MirInstruction::Jump { target } => {
|
||||
MirInstruction::Jump { target, .. } => {
|
||||
format!("br label {}", target)
|
||||
}
|
||||
|
||||
|
||||
@ -30,6 +30,8 @@ pub fn set_branch(
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
});
|
||||
}
|
||||
if let Some(tb) = f.get_block_mut(then_bb) {
|
||||
@ -43,7 +45,10 @@ pub fn set_branch(
|
||||
/// Set an unconditional jump terminator and register predecessor on target block.
|
||||
pub fn set_jump(f: &mut MirFunction, cur_bb: BasicBlockId, target: BasicBlockId) {
|
||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||
bb.set_terminator(MirInstruction::Jump { target });
|
||||
bb.set_terminator(MirInstruction::Jump {
|
||||
target,
|
||||
edge_args: None,
|
||||
});
|
||||
}
|
||||
if let Some(tb) = f.get_block_mut(target) {
|
||||
tb.add_predecessor(cur_bb);
|
||||
|
||||
@ -63,6 +63,7 @@ pub fn capture_actual_predecessor_and_jump(
|
||||
// 既存control_flowモジュールと同じパターンを使用
|
||||
builder.emit_instruction(super::super::MirInstruction::Jump {
|
||||
target: target_block,
|
||||
edge_args: None,
|
||||
})?;
|
||||
Ok(Some(cur_id))
|
||||
} else {
|
||||
|
||||
@ -1,13 +1,23 @@
|
||||
use crate::mir::function::MirFunction;
|
||||
use crate::mir::verification::utils;
|
||||
use crate::mir::verification_types::VerificationError;
|
||||
use crate::mir::{BasicBlockId, ValueId};
|
||||
use crate::mir::{BasicBlockId, MirInstruction, ValueId};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
/// Verify CFG references and reachability
|
||||
pub fn check_control_flow(function: &MirFunction) -> Result<(), Vec<VerificationError>> {
|
||||
let mut errors = Vec::new();
|
||||
for (block_id, block) in &function.blocks {
|
||||
let expected_successors = block.successors_from_terminator();
|
||||
if expected_successors != block.successors {
|
||||
errors.push(VerificationError::ControlFlowError {
|
||||
block: *block_id,
|
||||
reason: format!(
|
||||
"Successors cache mismatch: cached={:?}, expected={:?}",
|
||||
block.successors, expected_successors
|
||||
),
|
||||
});
|
||||
}
|
||||
for successor in &block.successors {
|
||||
if !function.blocks.contains_key(successor) {
|
||||
errors.push(VerificationError::ControlFlowError {
|
||||
@ -16,6 +26,62 @@ pub fn check_control_flow(function: &MirFunction) -> Result<(), Vec<Verification
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 260 P0: Fail-fast if terminator edge-args and legacy jump_args diverge.
|
||||
if let Some(term) = &block.terminator {
|
||||
match term {
|
||||
MirInstruction::Jump {
|
||||
edge_args: Some(edge_args),
|
||||
..
|
||||
} => {
|
||||
if block.has_legacy_jump_args() {
|
||||
let Some(legacy_layout) = block.legacy_jump_args_layout() else {
|
||||
errors.push(VerificationError::ControlFlowError {
|
||||
block: *block_id,
|
||||
reason: "Legacy jump_args layout missing with edge-args present"
|
||||
.to_string(),
|
||||
});
|
||||
continue;
|
||||
};
|
||||
let legacy_values = block.legacy_jump_args_values().unwrap_or_default();
|
||||
if edge_args.values.as_slice() != legacy_values {
|
||||
errors.push(VerificationError::ControlFlowError {
|
||||
block: *block_id,
|
||||
reason: format!(
|
||||
"Edge-args values mismatch: edge_args={:?}, legacy={:?}",
|
||||
edge_args.values, legacy_values
|
||||
),
|
||||
});
|
||||
}
|
||||
if edge_args.layout != legacy_layout {
|
||||
errors.push(VerificationError::ControlFlowError {
|
||||
block: *block_id,
|
||||
reason: format!(
|
||||
"Edge-args layout mismatch: edge_args={:?}, legacy={:?}",
|
||||
edge_args.layout, legacy_layout
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
MirInstruction::Branch {
|
||||
then_edge_args,
|
||||
else_edge_args,
|
||||
..
|
||||
} => {
|
||||
if block.has_legacy_jump_args()
|
||||
&& (then_edge_args.is_some() || else_edge_args.is_some())
|
||||
{
|
||||
errors.push(VerificationError::ControlFlowError {
|
||||
block: *block_id,
|
||||
reason: "Legacy jump_args present on multi-edge terminator with edge-args"
|
||||
.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Unreachable blocks are allowed in MIR.
|
||||
// They are created intentionally by break/continue/return statements via
|
||||
|
||||
@ -233,12 +233,15 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
condition: ValueId::new(cond),
|
||||
then_bb: BasicBlockId::new(then_bb),
|
||||
else_bb: BasicBlockId::new(else_bb),
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
});
|
||||
}
|
||||
"jump" => {
|
||||
let target = require_u64(inst, "target", "jump target")? as u32;
|
||||
block_ref.add_instruction(MirInstruction::Jump {
|
||||
target: BasicBlockId::new(target),
|
||||
edge_args: None,
|
||||
});
|
||||
}
|
||||
"phi" => {
|
||||
|
||||
@ -512,10 +512,11 @@ pub fn emit_mir_json_for_harness(
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
..
|
||||
} => {
|
||||
insts.push(json!({"op":"branch","cond": condition.as_u32(), "then": then_bb.as_u32(), "else": else_bb.as_u32()}));
|
||||
}
|
||||
I::Jump { target } => {
|
||||
I::Jump { target, .. } => {
|
||||
insts.push(json!({"op":"jump","target": target.as_u32()}));
|
||||
}
|
||||
I::Return { value } => {
|
||||
@ -528,8 +529,8 @@ pub fn emit_mir_json_for_harness(
|
||||
if let Some(term) = &bb.terminator {
|
||||
match term {
|
||||
I::Return { value } => insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())})),
|
||||
I::Jump { target } => insts.push(json!({"op":"jump","target": target.as_u32()})),
|
||||
I::Branch { condition, then_bb, else_bb } => insts.push(json!({"op":"branch","cond": condition.as_u32(), "then": then_bb.as_u32(), "else": else_bb.as_u32()})),
|
||||
I::Jump { target, .. } => insts.push(json!({"op":"jump","target": target.as_u32()})),
|
||||
I::Branch { condition, then_bb, else_bb, .. } => insts.push(json!({"op":"branch","cond": condition.as_u32(), "then": then_bb.as_u32(), "else": else_bb.as_u32()})),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -874,10 +875,11 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
condition,
|
||||
then_bb,
|
||||
else_bb,
|
||||
..
|
||||
} => {
|
||||
insts.push(json!({"op":"branch","cond": condition.as_u32(), "then": then_bb.as_u32(), "else": else_bb.as_u32()}));
|
||||
}
|
||||
I::Jump { target } => {
|
||||
I::Jump { target, .. } => {
|
||||
insts.push(json!({"op":"jump","target": target.as_u32()}));
|
||||
}
|
||||
I::Return { value } => {
|
||||
@ -890,8 +892,8 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
if let Some(term) = &bb.terminator {
|
||||
match term {
|
||||
I::Return { value } => insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())})),
|
||||
I::Jump { target } => insts.push(json!({"op":"jump","target": target.as_u32()})),
|
||||
I::Branch { condition, then_bb, else_bb } => insts.push(json!({"op":"branch","cond": condition.as_u32(), "then": then_bb.as_u32(), "else": else_bb.as_u32()})),
|
||||
I::Jump { target, .. } => insts.push(json!({"op":"jump","target": target.as_u32()})),
|
||||
I::Branch { condition, then_bb, else_bb, .. } => insts.push(json!({"op":"branch","cond": condition.as_u32(), "then": then_bb.as_u32(), "else": else_bb.as_u32()})),
|
||||
_ => {} }
|
||||
}
|
||||
blocks.push(json!({"id": bid.as_u32(), "instructions": insts}));
|
||||
|
||||
@ -144,12 +144,15 @@ pub fn parse_mir_v0_to_module(json: &str) -> Result<MirModule, String> {
|
||||
condition: ValueId::new(cond),
|
||||
then_bb: BasicBlockId::new(then_bb),
|
||||
else_bb: BasicBlockId::new(else_bb),
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
});
|
||||
}
|
||||
"jump" => {
|
||||
let target = require_u64(inst, "target", "jump target")? as u32;
|
||||
block_ref.add_instruction(MirInstruction::Jump {
|
||||
target: BasicBlockId::new(target),
|
||||
edge_args: None,
|
||||
});
|
||||
}
|
||||
"phi" => {
|
||||
|
||||
@ -30,17 +30,19 @@ mod tests {
|
||||
|
||||
// Entry block (bb0): branch on cond
|
||||
let mut entry = BasicBlock::new(BasicBlockId::new(0));
|
||||
entry.terminator = Some(MirInstruction::Branch {
|
||||
entry.set_terminator(MirInstruction::Branch {
|
||||
condition: ValueId(0), // cond parameter
|
||||
then_bb: BasicBlockId::new(1),
|
||||
else_bb: BasicBlockId::new(2),
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(0), entry);
|
||||
|
||||
// Then block (bb1): return 10
|
||||
// NOTE: Pattern matcher expects empty blocks (Return only)
|
||||
let mut then_block = BasicBlock::new(BasicBlockId::new(1));
|
||||
then_block.terminator = Some(MirInstruction::Return {
|
||||
then_block.set_terminator(MirInstruction::Return {
|
||||
value: Some(ValueId(1)), // Assumes ValueId(1) is const 10
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(1), then_block);
|
||||
@ -48,7 +50,7 @@ mod tests {
|
||||
// Else block (bb2): return 20
|
||||
// NOTE: Pattern matcher expects empty blocks (Return only)
|
||||
let mut else_block = BasicBlock::new(BasicBlockId::new(2));
|
||||
else_block.terminator = Some(MirInstruction::Return {
|
||||
else_block.set_terminator(MirInstruction::Return {
|
||||
value: Some(ValueId(2)), // Assumes ValueId(2) is const 20
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(2), else_block);
|
||||
@ -78,10 +80,12 @@ mod tests {
|
||||
|
||||
// Entry block (bb0): branch on cond
|
||||
let mut entry = BasicBlock::new(BasicBlockId::new(0));
|
||||
entry.terminator = Some(MirInstruction::Branch {
|
||||
entry.set_terminator(MirInstruction::Branch {
|
||||
condition: ValueId(0), // cond
|
||||
then_bb: BasicBlockId::new(1),
|
||||
else_bb: BasicBlockId::new(2),
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(0), entry);
|
||||
|
||||
@ -92,8 +96,9 @@ mod tests {
|
||||
dst: ValueId(3), // x
|
||||
src: ValueId(10), // Assumes ValueId(10) is const 100
|
||||
});
|
||||
then_block.terminator = Some(MirInstruction::Jump {
|
||||
then_block.set_terminator(MirInstruction::Jump {
|
||||
target: BasicBlockId::new(3),
|
||||
edge_args: None,
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(1), then_block);
|
||||
|
||||
@ -104,14 +109,15 @@ mod tests {
|
||||
dst: ValueId(3), // x
|
||||
src: ValueId(20), // Assumes ValueId(20) is const 200
|
||||
});
|
||||
else_block.terminator = Some(MirInstruction::Jump {
|
||||
else_block.set_terminator(MirInstruction::Jump {
|
||||
target: BasicBlockId::new(3),
|
||||
edge_args: None,
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(2), else_block);
|
||||
|
||||
// Merge block (bb3): return x
|
||||
let mut merge_block = BasicBlock::new(BasicBlockId::new(3));
|
||||
merge_block.terminator = Some(MirInstruction::Return {
|
||||
merge_block.set_terminator(MirInstruction::Return {
|
||||
value: Some(ValueId(3)),
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(3), merge_block);
|
||||
@ -408,10 +414,12 @@ mod tests {
|
||||
|
||||
// Entry block (bb0): branch on cond
|
||||
let mut entry = BasicBlock::new(BasicBlockId::new(0));
|
||||
entry.terminator = Some(MirInstruction::Branch {
|
||||
entry.set_terminator(MirInstruction::Branch {
|
||||
condition: ValueId(0), // cond
|
||||
then_bb: BasicBlockId::new(1),
|
||||
else_bb: BasicBlockId::new(2),
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(0), entry);
|
||||
|
||||
@ -425,7 +433,7 @@ mod tests {
|
||||
dst: ValueId(4), // y = 2
|
||||
value: crate::mir::ConstValue::Integer(2),
|
||||
});
|
||||
then_block.terminator = Some(MirInstruction::Return {
|
||||
then_block.set_terminator(MirInstruction::Return {
|
||||
value: Some(ValueId(10)), // result (x + y computed elsewhere)
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(1), then_block);
|
||||
@ -440,7 +448,7 @@ mod tests {
|
||||
dst: ValueId(4), // y = 4 (same dst as then!)
|
||||
value: crate::mir::ConstValue::Integer(4),
|
||||
});
|
||||
else_block.terminator = Some(MirInstruction::Return {
|
||||
else_block.set_terminator(MirInstruction::Return {
|
||||
value: Some(ValueId(20)), // result (x + y computed elsewhere)
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(2), else_block);
|
||||
@ -470,10 +478,12 @@ mod tests {
|
||||
|
||||
// Entry block (bb0): branch on cond
|
||||
let mut entry = BasicBlock::new(BasicBlockId::new(0));
|
||||
entry.terminator = Some(MirInstruction::Branch {
|
||||
entry.set_terminator(MirInstruction::Branch {
|
||||
condition: ValueId(0), // cond
|
||||
then_bb: BasicBlockId::new(1),
|
||||
else_bb: BasicBlockId::new(2),
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(0), entry);
|
||||
|
||||
@ -491,7 +501,7 @@ mod tests {
|
||||
dst: ValueId(5), // z = 30
|
||||
value: crate::mir::ConstValue::Integer(30),
|
||||
});
|
||||
then_block.terminator = Some(MirInstruction::Return {
|
||||
then_block.set_terminator(MirInstruction::Return {
|
||||
value: Some(ValueId(10)), // result (x + y + z computed elsewhere)
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(1), then_block);
|
||||
@ -510,7 +520,7 @@ mod tests {
|
||||
dst: ValueId(5), // z = 60 (same dst as then!)
|
||||
value: crate::mir::ConstValue::Integer(60),
|
||||
});
|
||||
else_block.terminator = Some(MirInstruction::Return {
|
||||
else_block.set_terminator(MirInstruction::Return {
|
||||
value: Some(ValueId(20)), // result (x + y + z computed elsewhere)
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(2), else_block);
|
||||
@ -632,23 +642,25 @@ mod tests {
|
||||
dst: ValueId(2),
|
||||
value: crate::mir::ConstValue::Integer(20),
|
||||
});
|
||||
entry.terminator = Some(MirInstruction::Branch {
|
||||
entry.set_terminator(MirInstruction::Branch {
|
||||
condition: ValueId(0), // cond parameter
|
||||
then_bb: BasicBlockId::new(1),
|
||||
else_bb: BasicBlockId::new(2),
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(0), entry);
|
||||
|
||||
// Then block (bb1): return 10
|
||||
let mut then_block = BasicBlock::new(BasicBlockId::new(1));
|
||||
then_block.terminator = Some(MirInstruction::Return {
|
||||
then_block.set_terminator(MirInstruction::Return {
|
||||
value: Some(ValueId(1)), // const 10
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(1), then_block);
|
||||
|
||||
// Else block (bb2): return 20
|
||||
let mut else_block = BasicBlock::new(BasicBlockId::new(2));
|
||||
else_block.terminator = Some(MirInstruction::Return {
|
||||
else_block.set_terminator(MirInstruction::Return {
|
||||
value: Some(ValueId(2)), // const 20
|
||||
});
|
||||
blocks.insert(BasicBlockId::new(2), else_block);
|
||||
|
||||
@ -95,10 +95,12 @@ fn phase67_ab_test_resolve_from_phi_equivalence() {
|
||||
dst: cond,
|
||||
value: ConstValue::Bool(true),
|
||||
});
|
||||
f.get_block_mut(entry).unwrap().terminator = Some(MirInstruction::Branch {
|
||||
f.get_block_mut(entry).unwrap().set_terminator(MirInstruction::Branch {
|
||||
condition: cond,
|
||||
then_bb,
|
||||
else_bb,
|
||||
then_edge_args: None,
|
||||
else_edge_args: None,
|
||||
});
|
||||
|
||||
// Then: v2 = 42
|
||||
@ -109,7 +111,10 @@ fn phase67_ab_test_resolve_from_phi_equivalence() {
|
||||
dst: v2,
|
||||
value: ConstValue::Integer(42),
|
||||
});
|
||||
f.get_block_mut(then_bb).unwrap().terminator = Some(MirInstruction::Jump { target: merge_bb });
|
||||
f.get_block_mut(then_bb).unwrap().set_terminator(MirInstruction::Jump {
|
||||
target: merge_bb,
|
||||
edge_args: None,
|
||||
});
|
||||
|
||||
// Else: v3 = 0
|
||||
let v3 = f.next_value_id();
|
||||
@ -119,7 +124,10 @@ fn phase67_ab_test_resolve_from_phi_equivalence() {
|
||||
dst: v3,
|
||||
value: ConstValue::Integer(0),
|
||||
});
|
||||
f.get_block_mut(else_bb).unwrap().terminator = Some(MirInstruction::Jump { target: merge_bb });
|
||||
f.get_block_mut(else_bb).unwrap().set_terminator(MirInstruction::Jump {
|
||||
target: merge_bb,
|
||||
edge_args: None,
|
||||
});
|
||||
|
||||
// Merge: v4 = phi(v2 from then, v3 from else)
|
||||
let v4 = f.next_value_id();
|
||||
|
||||
Reference in New Issue
Block a user