diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern2_steps/emit_joinir_step_box.rs b/src/mir/builder/control_flow/joinir/patterns/pattern2_steps/emit_joinir_step_box.rs index dcd5616a..c9ac54ab 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern2_steps/emit_joinir_step_box.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern2_steps/emit_joinir_step_box.rs @@ -52,6 +52,7 @@ impl EmitJoinIRStepBox { skeleton, condition_only_recipe: inputs.condition_only_recipe.as_ref(), body_local_derived_recipe: inputs.body_local_derived_recipe.as_ref(), + balanced_depth_scan_recipe: inputs.balanced_depth_scan_recipe.as_ref(), }; let (join_module, fragment_meta) = match lower_loop_with_break_minimal(lowering_inputs) { diff --git a/src/mir/join_ir/lowering/common/balanced_depth_scan_emitter.rs b/src/mir/join_ir/lowering/common/balanced_depth_scan_emitter.rs index 61be5272..49c2abc3 100644 --- a/src/mir/join_ir/lowering/common/balanced_depth_scan_emitter.rs +++ b/src/mir/join_ir/lowering/common/balanced_depth_scan_emitter.rs @@ -5,6 +5,11 @@ //! - `depth_delta`: per-iteration delta (-1/0/+1) //! - `depth_next`: depth + depth_delta (for break checks) +use crate::mir::join_ir::lowering::condition_env::ConditionEnv; +use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv; +use crate::mir::join_ir::{BinOpKind, CompareOp, ConstValue, JoinInst, MirLikeInst}; +use crate::mir::ValueId; + #[derive(Debug, Clone)] pub struct BalancedDepthScanRecipe { pub depth_var: String, @@ -15,3 +20,105 @@ pub struct BalancedDepthScanRecipe { pub depth_next_name: String, } +pub struct BalancedDepthScanEmitter; + +impl BalancedDepthScanEmitter { + pub fn emit_derived( + recipe: &BalancedDepthScanRecipe, + body_local_env: &mut LoopBodyLocalEnv, + condition_env: &ConditionEnv, + alloc_value: &mut dyn FnMut() -> ValueId, + instructions: &mut Vec, + ) -> Result<(), String> { + let ch_id = body_local_env.get(&recipe.ch_var).ok_or_else(|| { + format!( + "[phase107/balanced-depth] ch '{}' not found in LoopBodyLocalEnv", + recipe.ch_var + ) + })?; + let depth_id = condition_env.get(&recipe.depth_var).ok_or_else(|| { + format!( + "[phase107/balanced-depth] depth '{}' not found in ConditionEnv", + recipe.depth_var + ) + })?; + + let const_open = alloc_value(); + instructions.push(JoinInst::Compute(MirLikeInst::Const { + dst: const_open, + value: ConstValue::String(recipe.open.clone()), + })); + let const_close = alloc_value(); + instructions.push(JoinInst::Compute(MirLikeInst::Const { + dst: const_close, + value: ConstValue::String(recipe.close.clone()), + })); + + let is_open = alloc_value(); + instructions.push(JoinInst::Compute(MirLikeInst::Compare { + dst: is_open, + op: CompareOp::Eq, + lhs: ch_id, + rhs: const_open, + })); + let is_close = alloc_value(); + instructions.push(JoinInst::Compute(MirLikeInst::Compare { + dst: is_close, + op: CompareOp::Eq, + lhs: ch_id, + rhs: const_close, + })); + + let const_1 = alloc_value(); + instructions.push(JoinInst::Compute(MirLikeInst::Const { + dst: const_1, + value: ConstValue::Integer(1), + })); + let const_0 = alloc_value(); + instructions.push(JoinInst::Compute(MirLikeInst::Const { + dst: const_0, + value: ConstValue::Integer(0), + })); + let const_m1 = alloc_value(); + instructions.push(JoinInst::Compute(MirLikeInst::Const { + dst: const_m1, + value: ConstValue::Integer(-1), + })); + + let delta_open = alloc_value(); + instructions.push(JoinInst::Compute(MirLikeInst::Select { + dst: delta_open, + cond: is_open, + then_val: const_1, + else_val: const_0, + })); + + let delta_close = alloc_value(); + instructions.push(JoinInst::Compute(MirLikeInst::Select { + dst: delta_close, + cond: is_close, + then_val: const_m1, + else_val: const_0, + })); + + let depth_delta = alloc_value(); + instructions.push(JoinInst::Compute(MirLikeInst::BinOp { + dst: depth_delta, + op: BinOpKind::Add, + lhs: delta_open, + rhs: delta_close, + })); + + let depth_next = alloc_value(); + instructions.push(JoinInst::Compute(MirLikeInst::BinOp { + dst: depth_next, + op: BinOpKind::Add, + lhs: depth_id, + rhs: depth_delta, + })); + + body_local_env.insert(recipe.depth_delta_name.clone(), depth_delta); + body_local_env.insert(recipe.depth_next_name.clone(), depth_next); + Ok(()) + } +} diff --git a/src/mir/join_ir/lowering/loop_with_break_minimal.rs b/src/mir/join_ir/lowering/loop_with_break_minimal.rs index a5a67f62..dfac990b 100644 --- a/src/mir/join_ir/lowering/loop_with_break_minimal.rs +++ b/src/mir/join_ir/lowering/loop_with_break_minimal.rs @@ -68,6 +68,7 @@ use crate::mir::join_ir::lowering::carrier_update_emitter::{ // Phase 92 P2-1: Import ConditionalStep emitter from dedicated module use crate::mir::join_ir::lowering::common::conditional_step_emitter::emit_conditional_step_update; use crate::mir::join_ir::lowering::common::body_local_derived_emitter::BodyLocalDerivedRecipe; +use crate::mir::join_ir::lowering::common::balanced_depth_scan_emitter::{BalancedDepthScanEmitter, BalancedDepthScanRecipe}; use crate::mir::join_ir::lowering::condition_to_joinir::ConditionEnv; use crate::mir::loop_canonicalizer::UpdateKind; use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace; @@ -111,6 +112,8 @@ pub(crate) struct LoopWithBreakLoweringInputs<'a> { pub condition_only_recipe: Option<&'a crate::mir::join_ir::lowering::common::condition_only_emitter::ConditionOnlyRecipe>, /// Phase 94: BodyLocalDerived recipe (P5b escape `ch` reassignment + conditional counter). pub body_local_derived_recipe: Option<&'a BodyLocalDerivedRecipe>, + /// Phase 107: Balanced depth-scan recipe (find_balanced_* family). + pub balanced_depth_scan_recipe: Option<&'a BalancedDepthScanRecipe>, } /// Lower Pattern 2 (Loop with Conditional Break) to JoinIR @@ -189,6 +192,7 @@ pub(crate) fn lower_loop_with_break_minimal( skeleton, condition_only_recipe, body_local_derived_recipe, + balanced_depth_scan_recipe, } = inputs; let mut body_local_env = body_local_env; @@ -492,6 +496,17 @@ pub(crate) fn lower_loop_with_break_minimal( ) }); } + + // Phase 107: Balanced depth-scan derived vars (depth_delta/depth_next) + if let Some(recipe) = balanced_depth_scan_recipe { + BalancedDepthScanEmitter::emit_derived( + recipe, + body_env, + env, + &mut alloc_value, + &mut body_init_block, + )?; + } } // ------------------------------------------------------------------ diff --git a/src/mir/join_ir/lowering/loop_with_break_minimal/tests.rs b/src/mir/join_ir/lowering/loop_with_break_minimal/tests.rs index 53202056..6cc6cd1b 100644 --- a/src/mir/join_ir/lowering/loop_with_break_minimal/tests.rs +++ b/src/mir/join_ir/lowering/loop_with_break_minimal/tests.rs @@ -148,6 +148,7 @@ fn test_pattern2_header_condition_via_exprlowerer() { skeleton: None, // Phase 92 P0-3: skeleton=None for backward compatibility condition_only_recipe: None, // Phase 93 P0: None for normal loops body_local_derived_recipe: None, // Phase 94: None for normal loops + balanced_depth_scan_recipe: None, // Phase 107: None for normal loops }); assert!(result.is_ok(), "ExprLowerer header path should succeed"); diff --git a/src/mir/join_ir/normalized/fixtures.rs b/src/mir/join_ir/normalized/fixtures.rs index 54c7cc2a..5eb3f99c 100644 --- a/src/mir/join_ir/normalized/fixtures.rs +++ b/src/mir/join_ir/normalized/fixtures.rs @@ -137,6 +137,7 @@ pub fn build_pattern2_minimal_structured() -> JoinModule { skeleton: None, // Phase 92 P0-3: skeleton=None for backward compatibility condition_only_recipe: None, // Phase 93 P0: None for normal loops body_local_derived_recipe: None, // Phase 94: None for fixture + balanced_depth_scan_recipe: None, // Phase 107: None for fixture }, ) .expect("pattern2 minimal lowering should succeed");