//! Phase 273 P3: PlanLowerer - CorePlan → MIR 生成 (SSOT) //! //! # Responsibilities //! //! - Receive CorePlan from PlanNormalizer //! - Emit MIR instructions using pre-allocated ValueIds //! - No pattern-specific knowledge (pattern-agnostic) //! //! # Key Design Decision //! //! Lowerer processes CorePlan ONLY. It does not know about scan, split, or //! any other pattern-specific semantics. All pattern knowledge is in Normalizer. //! //! # Phase 273 P3: SSOT Finalization //! //! - Generalized fields (block_effects/phis/frag/final_values) are now REQUIRED //! - Legacy fallback has been removed (Fail-Fast on missing fields) //! - Pattern-specific emission functions (emit_scan_with_init_edgecfg) no longer used use super::{CoreEffectPlan, CoreExitPlan, CoreIfPlan, CoreLoopPlan, CorePlan}; use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext; use crate::mir::builder::MirBuilder; use crate::mir::{MirInstruction, ValueId}; /// Phase 273 P1: PlanLowerer - CorePlan → MIR 生成 (SSOT) pub(in crate::mir::builder) struct PlanLowerer; impl PlanLowerer { /// CorePlan を受け取り、MIR を生成 /// /// # Arguments /// /// * `builder` - MIR builder (mutable access for instruction emission) /// * `plan` - CorePlan from Normalizer (pre-allocated ValueIds) /// * `ctx` - Loop pattern context for debug/func_name pub(in crate::mir::builder) fn lower( builder: &mut MirBuilder, plan: CorePlan, ctx: &LoopPatternContext, ) -> Result, String> { match plan { CorePlan::Seq(plans) => Self::lower_seq(builder, plans, ctx), CorePlan::Loop(loop_plan) => Self::lower_loop(builder, loop_plan, ctx), CorePlan::If(if_plan) => Self::lower_if(builder, if_plan, ctx), CorePlan::Effect(effect) => { Self::emit_effect(builder, &effect)?; Ok(None) } CorePlan::Exit(exit) => Self::lower_exit(builder, exit), } } /// Seq: process plans in order fn lower_seq( builder: &mut MirBuilder, plans: Vec, ctx: &LoopPatternContext, ) -> Result, String> { let mut result = None; for plan in plans { result = Self::lower(builder, plan, ctx)?; } Ok(result) } /// Loop: emit blocks, effects, PHI, and edge CFG /// /// This is pattern-agnostic. All pattern knowledge is in Normalizer. /// Phase 273 P3: Generalized fields are now REQUIRED (Fail-Fast). fn lower_loop( builder: &mut MirBuilder, loop_plan: CoreLoopPlan, ctx: &LoopPatternContext, ) -> Result, String> { use crate::mir::builder::control_flow::joinir::trace; let trace_logger = trace::trace(); let debug = ctx.debug; if debug { trace_logger.debug( "lowerer/loop", &format!( "Phase 273 P3: Lowering CoreLoopPlan for {}", ctx.func_name ), ); } // Phase 273 P3: Generalized fields are now struct fields (not Option) // No validation needed - type system guarantees presence if debug { trace_logger.debug("lowerer/loop", "Processing generalized fields (SSOT)"); } Self::lower_loop_generalized(builder, loop_plan, ctx) } /// If: emit Branch and lower then/else plans (standalone) fn lower_if( builder: &mut MirBuilder, if_plan: CoreIfPlan, ctx: &LoopPatternContext, ) -> Result, String> { use crate::mir::builder::emission::branch::{emit_conditional, emit_jump}; let _pre_branch_bb = builder .current_block .ok_or_else(|| "[lowerer] No current block for CorePlan::If".to_string())?; let then_bb = builder.next_block_id(); let else_bb = builder.next_block_id(); let merge_bb = builder.next_block_id(); builder.ensure_block_exists(then_bb)?; builder.ensure_block_exists(else_bb)?; builder.ensure_block_exists(merge_bb)?; let mut condition_val = if_plan.condition; crate::mir::builder::ssa::local::finalize_branch_cond(builder, &mut condition_val); emit_conditional(builder, condition_val, then_bb, else_bb)?; // then builder.start_new_block(then_bb)?; for plan in if_plan.then_plans { Self::lower(builder, plan, ctx)?; } let then_reaches_merge = !builder.is_current_block_terminated(); if then_reaches_merge { emit_jump(builder, merge_bb)?; } // else builder.start_new_block(else_bb)?; if let Some(else_plans) = if_plan.else_plans { for plan in else_plans { Self::lower(builder, plan, ctx)?; } } let else_reaches_merge = !builder.is_current_block_terminated(); if else_reaches_merge { emit_jump(builder, merge_bb)?; } // merge (may be unreachable if both branches terminate) builder.start_new_block(merge_bb)?; Ok(None) } /// Exit: emit Return (standalone); Break/Continue require loop context fn lower_exit( builder: &mut MirBuilder, exit: CoreExitPlan, ) -> Result, String> { match exit { CoreExitPlan::Return(opt_val) => { builder.emit_instruction(MirInstruction::Return { value: opt_val })?; Ok(opt_val) } CoreExitPlan::Break => Err("[lowerer] CorePlan::Exit::Break requires loop context".to_string()), CoreExitPlan::Continue => { Err("[lowerer] CorePlan::Exit::Continue requires loop context".to_string()) } } } /// Phase 273 P3: Generalized loop lowering (SSOT) fn lower_loop_generalized( builder: &mut MirBuilder, loop_plan: CoreLoopPlan, ctx: &LoopPatternContext, ) -> Result, String> { use crate::mir::builder::control_flow::joinir::trace; use crate::mir::builder::control_flow::edgecfg::api::emit_frag; let trace_logger = trace::trace(); let debug = ctx.debug; // Phase 273 P3: Direct access (not Option - type system guarantees presence) let block_effects = &loop_plan.block_effects; let phis = &loop_plan.phis; let frag = &loop_plan.frag; let final_values = &loop_plan.final_values; // Step 1: Emit Jump from current block to loop entry if builder.current_block.is_some() { builder.emit_instruction(MirInstruction::Jump { target: frag.entry, edge_args: None, })?; } // Step 2: Emit block effects in SSOT order (preheader, header, body, step) // Note: Body effects are handled separately via body CorePlan for (block_id, effects) in block_effects { builder.start_new_block(*block_id)?; // Special handling for body block: emit body CorePlan instead of effects if *block_id == loop_plan.body_bb { for plan in &loop_plan.body { Self::emit_body_plan(builder, plan)?; } } else { // Normal block: emit effects for effect in effects { Self::emit_effect(builder, effect)?; } } } if debug { trace_logger.debug( "lowerer/loop_generalized", &format!("Block effects emitted: {} blocks", block_effects.len()), ); } // Step 3: Ensure non-effect blocks exist (after_bb, found_bb, etc.) builder.ensure_block_exists(loop_plan.after_bb)?; builder.ensure_block_exists(loop_plan.found_bb)?; // Step 4: Insert PHIs use crate::mir::builder::emission::phi::insert_loop_phi; for phi in phis { insert_loop_phi( builder, phi.block, phi.dst, phi.inputs.clone(), &phi.tag, )?; } if debug { trace_logger.debug( "lowerer/loop_generalized", &format!("PHI inserted: {} PHIs", phis.len()), ); } // Step 5: Emit Frag (terminators) if let Some(ref mut func) = builder.scope_ctx.current_function { emit_frag(func, frag)?; } else { return Err("[lowerer] current_function is None".to_string()); } if debug { trace_logger.debug("lowerer/loop_generalized", "Frag emitted"); } // Step 6: Update variable_map for final values for (name, value_id) in final_values { builder .variable_ctx .variable_map .insert(name.clone(), *value_id); } // Step 7: Setup after_bb for subsequent AST lowering builder.start_new_block(loop_plan.after_bb)?; // Step 8: Return Void (pattern applied successfully) use crate::mir::builder::emission::constant::emit_void; let void_val = emit_void(builder); if debug { trace_logger.debug( "lowerer/loop_generalized", &format!("Loop complete, returning Void {:?}", void_val), ); } Ok(Some(void_val)) } // Phase 273 P3: lower_loop_legacy() has been REMOVED // All patterns must use generalized fields (block_effects/phis/frag/final_values) // Pattern-specific emission functions (emit_scan_with_init_edgecfg) are no longer used /// Emit a single CoreEffectPlan as MirInstruction fn emit_effect(builder: &mut MirBuilder, effect: &CoreEffectPlan) -> Result<(), String> { match effect { CoreEffectPlan::Const { dst, value } => { builder.emit_instruction(MirInstruction::Const { dst: *dst, value: value.clone(), })?; } CoreEffectPlan::MethodCall { dst, object, method, args, effects } => { // P2: dst and effects are now specified by Normalizer builder.emit_instruction(MirInstruction::BoxCall { dst: *dst, box_val: *object, method: method.clone(), method_id: None, args: args.clone(), effects: *effects, })?; } CoreEffectPlan::BinOp { dst, lhs, op, rhs } => { builder.emit_instruction(MirInstruction::BinOp { dst: *dst, lhs: *lhs, op: *op, rhs: *rhs, })?; } CoreEffectPlan::Compare { dst, lhs, op, rhs } => { builder.emit_instruction(MirInstruction::Compare { dst: *dst, lhs: *lhs, op: *op, rhs: *rhs, })?; } } Ok(()) } fn emit_body_plan(builder: &mut MirBuilder, plan: &CorePlan) -> Result<(), String> { match plan { CorePlan::Effect(effect) => Self::emit_effect(builder, effect), CorePlan::Seq(plans) => { for nested in plans { Self::emit_body_plan(builder, nested)?; } Ok(()) } _ => Err("[lowerer] Non-Effect plan in Loop body not yet supported".to_string()), } } } #[cfg(test)] mod tests { use super::*; use crate::ast::{ASTNode, LiteralValue, Span}; use crate::mir::builder::control_flow::edgecfg::api::Frag; use crate::mir::{ConstValue, MirInstruction}; fn make_ctx<'a>(condition: &'a ASTNode, body: &'a [ASTNode]) -> LoopPatternContext<'a> { LoopPatternContext::new(condition, body, "test_coreplan", false, false) } #[test] fn test_lower_exit_return_sets_terminator() { let mut builder = MirBuilder::new(); builder.enter_function_for_test("test_exit".to_string()); let ret_val = builder.alloc_value_for_test(); builder .emit_for_test(MirInstruction::Const { dst: ret_val, value: ConstValue::Integer(1), }) .expect("emit const"); let cond = ASTNode::Literal { value: LiteralValue::Bool(true), span: Span::unknown(), }; let body: Vec = vec![]; let ctx = make_ctx(&cond, &body); let plan = CorePlan::Exit(CoreExitPlan::Return(Some(ret_val))); let result = PlanLowerer::lower(&mut builder, plan, &ctx); assert!(result.is_ok()); let entry = builder.current_block_for_test().expect("entry block"); let func = builder.scope_ctx.current_function.as_ref().expect("function"); let block = func.get_block(entry).expect("block"); assert!( matches!(block.terminator, Some(MirInstruction::Return { value: Some(v) }) if v == ret_val), "expected Return terminator" ); } #[test] fn test_lower_if_emits_branch() { let mut builder = MirBuilder::new(); builder.enter_function_for_test("test_if".to_string()); let entry = builder.current_block_for_test().expect("entry block"); let cond_val = builder.alloc_value_for_test(); builder .emit_for_test(MirInstruction::Const { dst: cond_val, value: ConstValue::Bool(true), }) .expect("emit const"); let then_val = builder.alloc_value_for_test(); let if_plan = CoreIfPlan { condition: cond_val, then_plans: vec![CorePlan::Effect(CoreEffectPlan::Const { dst: then_val, value: ConstValue::Integer(2), })], else_plans: None, }; let cond = ASTNode::Literal { value: LiteralValue::Bool(true), span: Span::unknown(), }; let body: Vec = vec![]; let ctx = make_ctx(&cond, &body); let result = PlanLowerer::lower(&mut builder, CorePlan::If(if_plan), &ctx); assert!(result.is_ok()); let func = builder.scope_ctx.current_function.as_ref().expect("function"); let block = func.get_block(entry).expect("entry block"); assert!( matches!(block.terminator, Some(MirInstruction::Branch { .. })), "expected Branch terminator" ); } #[test] fn test_lower_loop_body_seq_flattens() { let mut builder = MirBuilder::new(); builder.enter_function_for_test("test_loop_body_seq".to_string()); let preheader_bb = builder.next_block_id(); let header_bb = builder.next_block_id(); let body_bb = builder.next_block_id(); let step_bb = builder.next_block_id(); let after_bb = builder.next_block_id(); let found_bb = builder.next_block_id(); let eff1 = CoreEffectPlan::Const { dst: builder.alloc_value_for_test(), value: ConstValue::Integer(1), }; let eff2 = CoreEffectPlan::Const { dst: builder.alloc_value_for_test(), value: ConstValue::Integer(2), }; let eff3 = CoreEffectPlan::Const { dst: builder.alloc_value_for_test(), value: ConstValue::Integer(3), }; let loop_plan = CoreLoopPlan { preheader_bb, header_bb, body_bb, step_bb, after_bb, found_bb, body: vec![CorePlan::Seq(vec![ CorePlan::Effect(eff1), CorePlan::Seq(vec![CorePlan::Effect(eff2)]), CorePlan::Effect(eff3), ])], cond_loop: builder.alloc_value_for_test(), cond_match: builder.alloc_value_for_test(), block_effects: vec![ (preheader_bb, vec![]), (header_bb, vec![]), (body_bb, vec![]), (step_bb, vec![]), ], phis: vec![], frag: Frag::new(header_bb), final_values: vec![], }; let cond = ASTNode::Literal { value: LiteralValue::Bool(true), span: Span::unknown(), }; let body: Vec = vec![]; let ctx = make_ctx(&cond, &body); let result = PlanLowerer::lower(&mut builder, CorePlan::Loop(loop_plan), &ctx); assert!(result.is_ok()); let func = builder.scope_ctx.current_function.as_ref().expect("function"); let block = func.get_block(body_bb).expect("body block"); let const_count = block .instructions .iter() .filter(|inst| matches!(inst, MirInstruction::Const { .. })) .count(); assert_eq!(const_count, 3, "expected 3 Const effects in body"); } }