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:
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(),
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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(())
|
||||
}
|
||||
|
||||
|
||||
@ -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());
|
||||
|
||||
Reference in New Issue
Block a user