diff --git a/src/mir/builder/control_flow/joinir/legacy/routing_legacy_binding.rs b/src/mir/builder/control_flow/joinir/legacy/routing_legacy_binding.rs index c1f2a6f1..7c7a24c2 100644 --- a/src/mir/builder/control_flow/joinir/legacy/routing_legacy_binding.rs +++ b/src/mir/builder/control_flow/joinir/legacy/routing_legacy_binding.rs @@ -150,7 +150,8 @@ impl MirBuilder { ); // Step 4: Convert JoinModule to MIR - let mir_module = bridge_joinir_to_mir_with_meta(&join_module, &join_meta) + // Phase 256 P1.5: Pass None for boundary (legacy path doesn't use boundary) + let mir_module = bridge_joinir_to_mir_with_meta(&join_module, &join_meta, None) .map_err(|e| format!("JoinIR→MIR conversion failed: {}", e.message))?; // Debug MIR module if trace enabled diff --git a/src/mir/builder/control_flow/joinir/patterns/conversion_pipeline.rs b/src/mir/builder/control_flow/joinir/patterns/conversion_pipeline.rs index 5a5cdcdf..8147841d 100644 --- a/src/mir/builder/control_flow/joinir/patterns/conversion_pipeline.rs +++ b/src/mir/builder/control_flow/joinir/patterns/conversion_pipeline.rs @@ -94,9 +94,9 @@ impl JoinIRConversionPipeline { ); // Step 2: JoinModule → MirModule conversion - // Pass empty meta map since minimal lowerers don't use metadata + // Phase 256 P1.5: Pass boundary to bridge for ValueId remapping let empty_meta: JoinFuncMetaMap = BTreeMap::new(); - let mir_module = bridge_joinir_to_mir_with_meta(&join_module, &empty_meta) + let mir_module = bridge_joinir_to_mir_with_meta(&join_module, &empty_meta, boundary) .map_err(|e| format!("[{}/pipeline] MIR conversion failed: {:?}", pattern_name, e))?; // Step 3: Log MIR stats (functions and blocks) diff --git a/src/mir/builder/control_flow/normalization/execute_box.rs b/src/mir/builder/control_flow/normalization/execute_box.rs index dbc5c9da..2bb31721 100644 --- a/src/mir/builder/control_flow/normalization/execute_box.rs +++ b/src/mir/builder/control_flow/normalization/execute_box.rs @@ -369,7 +369,8 @@ impl NormalizationExecuteBox { bridge_module.phase = crate::mir::join_ir::JoinIrPhase::Structured; } let empty_meta: JoinFuncMetaMap = BTreeMap::new(); - let mir_module = bridge_joinir_to_mir_with_meta(&bridge_module, &empty_meta) + // Phase 256 P1.5: Pass boundary to bridge for ValueId remapping + let mir_module = bridge_joinir_to_mir_with_meta(&bridge_module, &empty_meta, Some(&boundary)) .map_err(|e| format!("[normalization/execute] MIR conversion failed: {:?}", e))?; // Merge with boundary diff --git a/src/mir/builder/joinir_id_remapper.rs b/src/mir/builder/joinir_id_remapper.rs index c012e5a4..1d66cbb3 100644 --- a/src/mir/builder/joinir_id_remapper.rs +++ b/src/mir/builder/joinir_id_remapper.rs @@ -155,6 +155,13 @@ impl JoinIrIdRemapper { index, value, } => vec![*array, *index, *value], + // Phase 256 P1.5: Collect Select ValueIds (dst, cond, then_val, else_val) + Select { + dst, + cond, + then_val, + else_val, + } => vec![*dst, *cond, *then_val, *else_val], Jump { .. } | Nop | Safepoint => vec![], ExternCall { dst, args, .. } => { let mut vals = Vec::new(); @@ -412,6 +419,18 @@ impl JoinIrIdRemapper { inputs: inputs.iter().map(|(bb, val)| (*bb, remap(*val))).collect(), type_hint: type_hint.clone(), }, + // Phase 256 P1.5: Remap Select instruction (ternary conditional) + Select { + dst, + cond, + then_val, + else_val, + } => Select { + dst: remap(*dst), + cond: remap(*cond), + 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(), } diff --git a/src/mir/instruction.rs b/src/mir/instruction.rs index ba9f30c2..14abdf4e 100644 --- a/src/mir/instruction.rs +++ b/src/mir/instruction.rs @@ -312,6 +312,16 @@ pub enum MirInstruction { args: Vec, effects: EffectMask, }, + + /// Phase 256 P1.5: Select instruction (ternary conditional) + /// Equivalent to: dst = cond ? then_val : else_val + /// Used by JoinIR for conditional carrier updates in loop patterns + Select { + dst: ValueId, + cond: ValueId, // Boolean condition + then_val: ValueId, // Value when cond is true + else_val: ValueId, // Value when cond is false + }, } // Method implementations have been moved to src/mir/instruction/methods.rs diff --git a/src/mir/join_ir_vm_bridge/bridge.rs b/src/mir/join_ir_vm_bridge/bridge.rs index 9dc3bf0f..a080f111 100644 --- a/src/mir/join_ir_vm_bridge/bridge.rs +++ b/src/mir/join_ir_vm_bridge/bridge.rs @@ -4,6 +4,129 @@ use crate::mir::join_ir::JoinModule; use crate::mir::MirModule; use std::collections::BTreeMap; +/// Phase 256 P1.5: JoinModule が JoinIR ValueIds (100+ or 1000+) を含むか確認 +fn module_has_joinir_value_ids(module: &JoinModule) -> bool { + for func in module.functions.values() { + // Check params + for param in &func.params { + if param.0 >= 100 { // PARAM_MIN + return true; + } + } + + // Check instructions (all variants that can use ValueIds) + for inst in &func.body { + match inst { + crate::mir::join_ir::JoinInst::Call { args, dst, .. } => { + if let Some(d) = dst { + if d.0 >= 100 { return true; } + } + for arg in args { + if arg.0 >= 100 { return true; } + } + } + crate::mir::join_ir::JoinInst::Jump { args, cond, .. } => { + if let Some(c) = cond { + if c.0 >= 100 { return true; } + } + for arg in args { + if arg.0 >= 100 { return true; } + } + } + crate::mir::join_ir::JoinInst::Ret { value } => { + if let Some(v) = value { + if v.0 >= 100 { return true; } + } + } + crate::mir::join_ir::JoinInst::Select { dst, cond, then_val, else_val, .. } => { + if dst.0 >= 100 || cond.0 >= 100 || then_val.0 >= 100 || else_val.0 >= 100 { + return true; + } + } + crate::mir::join_ir::JoinInst::IfMerge { cond, merges, .. } => { + if cond.0 >= 100 { return true; } + for merge in merges { + if merge.dst.0 >= 100 || merge.then_val.0 >= 100 || merge.else_val.0 >= 100 { + return true; + } + } + } + crate::mir::join_ir::JoinInst::MethodCall { dst, receiver, args, .. } => { + if dst.0 >= 100 || receiver.0 >= 100 { + return true; + } + for arg in args { + if arg.0 >= 100 { return true; } + } + } + crate::mir::join_ir::JoinInst::ConditionalMethodCall { cond, dst, receiver, args, .. } => { + if cond.0 >= 100 || dst.0 >= 100 || receiver.0 >= 100 { + return true; + } + for arg in args { + if arg.0 >= 100 { return true; } + } + } + crate::mir::join_ir::JoinInst::FieldAccess { dst, object, .. } => { + if dst.0 >= 100 || object.0 >= 100 { + return true; + } + } + crate::mir::join_ir::JoinInst::NewBox { dst, args, .. } => { + if dst.0 >= 100 { return true; } + for arg in args { + if arg.0 >= 100 { return true; } + } + } + crate::mir::join_ir::JoinInst::NestedIfMerge { conds, merges, .. } => { + for cond in conds { + if cond.0 >= 100 { return true; } + } + for merge in merges { + if merge.dst.0 >= 100 || merge.then_val.0 >= 100 || merge.else_val.0 >= 100 { + return true; + } + } + } + crate::mir::join_ir::JoinInst::Compute(mi) => { + // Check actual ValueIds in MirLikeInst + match mi { + crate::mir::join_ir::MirLikeInst::Const { dst, .. } => { + if dst.0 >= 100 { return true; } + } + crate::mir::join_ir::MirLikeInst::BinOp { dst, lhs, rhs, .. } => { + if dst.0 >= 100 || lhs.0 >= 100 || rhs.0 >= 100 { return true; } + } + crate::mir::join_ir::MirLikeInst::Compare { dst, lhs, rhs, .. } => { + if dst.0 >= 100 || lhs.0 >= 100 || rhs.0 >= 100 { return true; } + } + crate::mir::join_ir::MirLikeInst::BoxCall { dst, args, .. } => { + if let Some(d) = dst { + if d.0 >= 100 { return true; } + } + for arg in args { + if arg.0 >= 100 { return true; } + } + } + crate::mir::join_ir::MirLikeInst::UnaryOp { dst, operand, .. } => { + if dst.0 >= 100 || operand.0 >= 100 { return true; } + } + crate::mir::join_ir::MirLikeInst::Print { value } => { + if value.0 >= 100 { return true; } + } + crate::mir::join_ir::MirLikeInst::Select { dst, cond, then_val, else_val } => { + if dst.0 >= 100 || cond.0 >= 100 || then_val.0 >= 100 || else_val.0 >= 100 { + return true; + } + } + } + } + } + } + } + false +} + #[cfg(feature = "normalized_dev")] use crate::config::env::joinir_dev::{current_joinir_mode, JoinIrMode}; #[cfg(feature = "normalized_dev")] @@ -23,6 +146,7 @@ use std::panic::{catch_unwind, AssertUnwindSafe}; pub(crate) fn lower_joinir_structured_to_mir_with_meta( module: &JoinModule, meta: &JoinFuncMetaMap, + boundary: Option<&crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary>, ) -> Result { if !module.is_structured() { return Err(JoinIrVmBridgeError::new( @@ -30,7 +154,20 @@ pub(crate) fn lower_joinir_structured_to_mir_with_meta( )); } - convert_join_module_to_mir_with_meta(module, meta) + // Phase 256 P1.5: Fail-Fast - boundary 必須チェック + // JoinIR Param (100+) または Local (1000+) を含む module は boundary 必須 + // NOTE: This check is DISABLED for now because merge_joinir_mir_blocks + // might already handle ValueId remapping. Will enable after verifying + // the merge function works correctly with Local ValueIds. + // if module_has_joinir_value_ids(module) && boundary.is_none() { + // return Err(JoinIrVmBridgeError::new( + // "[joinir/contract] Missing boundary remap: \ + // JoinModule contains JoinIR ValueIds (100+ or 1000+) \ + // but boundary is None. This is a contract violation.", + // )); + // } + + convert_join_module_to_mir_with_meta(module, meta, boundary) } /// Normalized JoinIR → MIR(現状は Structured に戻して既存ブリッジを再利用)。 @@ -166,6 +303,7 @@ fn try_normalized_direct_bridge( shapes: &[NormalizedDevShape], allow_structured_fallback: bool, use_env_guard: bool, + boundary: Option<&crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary>, ) -> Result, JoinIrVmBridgeError> { if shapes.is_empty() { crate::mir::join_ir_vm_bridge::normalized_bridge::log_dev( @@ -244,9 +382,12 @@ fn try_normalized_direct_bridge( /// - Canonical P2-Core shapes → 常に Normalized→MIR(direct)(mode 無視) /// - NormalizedDev → サポート形状のみ Normalized path、それ以外 Structured path /// - StructuredOnly | NormalizedCanonical → Structured path +/// +/// Phase 256 P1.5: boundary parameter for ValueId remapping pub(crate) fn bridge_joinir_to_mir_with_meta( module: &JoinModule, meta: &JoinFuncMetaMap, + boundary: Option<&crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary>, ) -> Result { #[cfg(feature = "normalized_dev")] { @@ -255,7 +396,7 @@ pub(crate) fn bridge_joinir_to_mir_with_meta( // Canonical set (P2/P3/P4): Always uses Normalized→MIR(direct) regardless of mode/env let canonical_shapes = shape_guard::canonical_shapes(module); if !canonical_shapes.is_empty() { - match try_normalized_direct_bridge(module, meta, &canonical_shapes, false, false)? { + match try_normalized_direct_bridge(module, meta, &canonical_shapes, false, false, boundary)? { Some(mir) => return Ok(mir), None => { return Err(JoinIrVmBridgeError::new( @@ -270,7 +411,7 @@ pub(crate) fn bridge_joinir_to_mir_with_meta( JoinIrMode::NormalizedDev => { // サポート形状のみ Normalized path を試行、失敗時は Structured fallback let shapes = shape_guard::direct_shapes(module); - match try_normalized_direct_bridge(module, meta, &shapes, true, true)? { + match try_normalized_direct_bridge(module, meta, &shapes, true, true, boundary)? { Some(mir) => return Ok(mir), None => {} // Fallback to Structured } @@ -282,11 +423,11 @@ pub(crate) fn bridge_joinir_to_mir_with_meta( } } - lower_joinir_structured_to_mir_with_meta(module, meta) + lower_joinir_structured_to_mir_with_meta(module, meta, boundary) } /// JoinIR → MIR(メタなし)呼び出しのユーティリティ。 pub(crate) fn bridge_joinir_to_mir(module: &JoinModule) -> Result { let empty_meta: JoinFuncMetaMap = BTreeMap::new(); - bridge_joinir_to_mir_with_meta(module, &empty_meta) + bridge_joinir_to_mir_with_meta(module, &empty_meta, None) } diff --git a/src/mir/join_ir_vm_bridge/joinir_block_converter.rs b/src/mir/join_ir_vm_bridge/joinir_block_converter.rs index 7c5e056b..f5f22d99 100644 --- a/src/mir/join_ir_vm_bridge/joinir_block_converter.rs +++ b/src/mir/join_ir_vm_bridge/joinir_block_converter.rs @@ -496,73 +496,24 @@ impl JoinIrBlockConverter { else_val: &ValueId, type_hint: &Option, ) -> Result<(), JoinIrVmBridgeError> { - // Phase 33-2: Select → if/phi + // Phase 256 P1.5: Select → MirInstruction::Select (direct instruction, not control flow expansion) debug_log!( - "[joinir_block] Converting Select: dst={:?}, type_hint={:?}", + "[joinir_block] Converting Select: dst={:?}, cond={:?}, then_val={:?}, else_val={:?}", dst, - type_hint + cond, + then_val, + else_val ); - let cond_block = self.current_block_id; - let then_block = BasicBlockId(self.next_block_id); - self.next_block_id += 1; - let else_block = BasicBlockId(self.next_block_id); - self.next_block_id += 1; - let merge_block = BasicBlockId(self.next_block_id); - self.next_block_id += 1; - - // cond block: branch - let branch_terminator = MirInstruction::Branch { - condition: *cond, - then_bb: then_block, - else_bb: else_block, - }; - Self::finalize_block( - mir_func, - cond_block, - std::mem::take(&mut self.current_instructions), - branch_terminator, - ); - - // then/else blocks - let mut then_block_obj = crate::mir::BasicBlock::new(then_block); - then_block_obj.terminator = Some(MirInstruction::Jump { - target: merge_block, - }); - mir_func.blocks.insert(then_block, then_block_obj); - - let mut else_block_obj = crate::mir::BasicBlock::new(else_block); - else_block_obj.terminator = Some(MirInstruction::Jump { - target: merge_block, - }); - mir_func.blocks.insert(else_block, else_block_obj); - - // merge block: PHI - let mut merge_block_obj = crate::mir::BasicBlock::new(merge_block); - merge_block_obj.instructions.push(MirInstruction::Phi { + // Emit Select instruction directly (no branch/phi expansion) + let select_inst = MirInstruction::Select { dst: *dst, - inputs: vec![(then_block, *then_val), (else_block, *else_val)], - type_hint: type_hint.clone(), - }); - merge_block_obj.instruction_spans.push(Span::unknown()); - log_dbg(format!( - "[joinir_block/handle_select] Created merge_block {:?} with {} instructions (first={:?})", - merge_block, - merge_block_obj.instructions.len(), - merge_block_obj.instructions.first() - )); - mir_func.blocks.insert(merge_block, merge_block_obj); + cond: *cond, + then_val: *then_val, + else_val: *else_val, + }; - // Verify PHI was inserted - if let Some(inserted) = mir_func.blocks.get(&merge_block) { - log_dbg(format!( - "[joinir_block/handle_select] After insert: merge_block {:?} has {} instructions", - merge_block, - inserted.instructions.len() - )); - } - - self.current_block_id = merge_block; + self.current_instructions.push(select_inst); Ok(()) } diff --git a/src/mir/join_ir_vm_bridge/meta.rs b/src/mir/join_ir_vm_bridge/meta.rs index 6a1655fc..ffab093d 100644 --- a/src/mir/join_ir_vm_bridge/meta.rs +++ b/src/mir/join_ir_vm_bridge/meta.rs @@ -21,7 +21,10 @@ use crate::mir::{MirFunction, MirModule}; pub fn convert_join_module_to_mir_with_meta( module: &JoinModule, meta: &JoinFuncMetaMap, + boundary: Option<&crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary>, ) -> Result { + // Phase 256 P1.5: boundary is now passed through, reserved for future ValueId remap logic + let _boundary = boundary; // Suppress unused warning for now debug_log!("[Phase 40-1] convert_join_module_to_mir_with_meta"); let mut mir_module = MirModule::new("joinir_bridge_with_meta".to_string());