feat(joinir): Phase 256 P1.5 - JoinInst::Select 命令サポート(根治)

## 変更内容

Task 1(既完了): boundary パラメータを bridge 経路全体に伝播
- conversion_pipeline.rs, bridge.rs(複数箇所), meta.rs, routing_legacy_binding.rs, execute_box.rs

Task 2-A~2-D: Select 命令の実装
- 2-A: MirInstruction::Select バリアント追加
- 2-B: JoinIR Select → MIR Select 直接変換(branch+phi展開廃止)
- 2-C:  joinir_id_remapper に Select remap case(ValueId変換の根治)
- 2-D: value_collector に Select case

## 根本原因解決

Pattern7 の JoinInst::Select が JoinIR→MIR で未対応
→ ValueId(1002) → ValueId(57) のリマップが行われず
→ "use of undefined value ValueId(57)" エラー

## 現在地

 cargo check: 0 errors
 Pattern7/6 VM test(Task 2-E LLVM実装後)

🧠 Generated with Claude Code
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-20 03:11:58 +09:00
parent 64f679354a
commit bfd324d7b9
8 changed files with 196 additions and 70 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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(),
}

View File

@ -312,6 +312,16 @@ pub enum MirInstruction {
args: Vec<ValueId>,
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

View File

@ -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<MirModule, JoinIrVmBridgeError> {
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<Option<MirModule>, 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<MirModule, JoinIrVmBridgeError> {
#[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<MirModule, JoinIrVmBridgeError> {
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)
}

View File

@ -496,73 +496,24 @@ impl JoinIrBlockConverter {
else_val: &ValueId,
type_hint: &Option<crate::mir::MirType>,
) -> 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(())
}

View File

@ -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<MirModule, JoinIrVmBridgeError> {
// 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());