feat(mir): Phase 63-6-1/2 MIR Phi type_hint field & JoinIR propagation
Phase 63-6-1: MirInstruction::Phi に type_hint フィールド追加 - Added `type_hint: Option<MirType>` field to Phi instruction - Updated 21 files with type_hint initialization (all set to None for legacy paths) - Pattern matching updated across codebase (11 files) - Test code updated (basic_block.rs) Phase 63-6-2: JoinIR→MIR Bridge で型ヒント伝播実装 - Modified convert.rs: Select → MIR now creates PHI with type_hint - Removed Copy instructions from then/else blocks - PHI instruction at merge block receives type_hint from JoinIR Select - Test verification: ✅ Type hint propagation successful (Some(Integer)) Modified files: - instruction.rs: Added type_hint field definition - join_ir_vm_bridge/convert.rs: Select lowering with PHI + type_hint - 19 other files: type_hint field initialization Test results: - ✅ test_type_hint_propagation_simple: Type hint = Some(Integer) confirmed - ✅ 7/8 if_select tests passing (1 race condition, passes individually) Next: Phase 63-6-3 (lifecycle.rs で型ヒント使用) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -172,7 +172,7 @@ impl MirInterpreter {
|
||||
self.last_block = Some(block.id);
|
||||
self.last_inst_index = Some(idx);
|
||||
self.last_inst = Some(inst.clone());
|
||||
if let MirInstruction::Phi { dst, inputs } = inst {
|
||||
if let MirInstruction::Phi { dst, inputs, .. } = inst {
|
||||
let dst_id = *dst;
|
||||
if trace_phi {
|
||||
let in_preds: Vec<_> = inputs.iter().map(|(bb, _)| *bb).collect();
|
||||
|
||||
@ -324,7 +324,7 @@ impl BasicBlock {
|
||||
incoming: (BasicBlockId, ValueId),
|
||||
) -> Result<(), String> {
|
||||
for inst in &mut self.instructions {
|
||||
if let MirInstruction::Phi { dst, inputs } = inst {
|
||||
if let MirInstruction::Phi { dst, inputs, .. } = inst {
|
||||
if *dst == phi_dst {
|
||||
inputs.push(incoming);
|
||||
return Ok(());
|
||||
@ -580,6 +580,7 @@ mod tests {
|
||||
let phi_inst = MirInstruction::Phi {
|
||||
dst: ValueId::new(0),
|
||||
inputs: vec![(BasicBlockId::new(1), ValueId::new(1))],
|
||||
type_hint: None, // Phase 63-6: Test code, no type hint
|
||||
};
|
||||
bb.add_instruction(phi_inst);
|
||||
|
||||
|
||||
@ -604,7 +604,7 @@ impl MirBuilder {
|
||||
.map(|f| f.signature.name.clone());
|
||||
let _dbg_region_id = self.debug_current_region_id();
|
||||
// P0: PHI の軽量補強と観測は、関数ブロック取得前に実施して借用競合を避ける
|
||||
if let MirInstruction::Phi { dst, inputs } = &instruction {
|
||||
if let MirInstruction::Phi { dst, inputs, .. } = &instruction {
|
||||
origin::phi::propagate_phi_meta(self, *dst, inputs);
|
||||
observe::ssa::emit_phi(self, *dst, inputs);
|
||||
}
|
||||
@ -815,7 +815,7 @@ impl MirBuilder {
|
||||
if let Some(block_data) = function.get_block_mut(block) {
|
||||
// Find PHI instruction with matching dst
|
||||
for inst in &mut block_data.instructions {
|
||||
if let MirInstruction::Phi { dst, inputs } = inst {
|
||||
if let MirInstruction::Phi { dst, inputs, .. } = inst {
|
||||
if *dst == phi_id {
|
||||
*inputs = new_inputs;
|
||||
return Ok(());
|
||||
|
||||
@ -542,7 +542,7 @@ impl super::MirBuilder {
|
||||
values.insert(*v);
|
||||
}
|
||||
}
|
||||
MirInstruction::Phi { dst, inputs } => {
|
||||
MirInstruction::Phi { dst, inputs, type_hint: None } => {
|
||||
values.insert(*dst);
|
||||
for (_, val) in inputs {
|
||||
values.insert(*val);
|
||||
@ -651,12 +651,13 @@ impl super::MirBuilder {
|
||||
MirInstruction::Return { value } => MirInstruction::Return {
|
||||
value: value.map(remap_value),
|
||||
},
|
||||
MirInstruction::Phi { dst, inputs } => MirInstruction::Phi {
|
||||
MirInstruction::Phi { dst, inputs, type_hint: None } => MirInstruction::Phi {
|
||||
dst: remap_value(*dst),
|
||||
inputs: inputs
|
||||
.iter()
|
||||
.map(|(bb, val)| (remap_block(*bb), remap_value(*val)))
|
||||
.collect(),
|
||||
type_hint: None, // Phase 63-6: Preserve no type hint during remapping
|
||||
},
|
||||
MirInstruction::Copy { dst, src } => MirInstruction::Copy {
|
||||
dst: remap_value(*dst),
|
||||
|
||||
@ -130,6 +130,7 @@ impl super::MirBuilder {
|
||||
self.emit_instruction(super::MirInstruction::Phi {
|
||||
dst: result_val,
|
||||
inputs: phi_inputs,
|
||||
type_hint: None, // Phase 63-6: Legacy path, no type hint
|
||||
})?;
|
||||
}
|
||||
Ok(result_val)
|
||||
|
||||
@ -32,7 +32,7 @@ impl<'a> PhiBuilderOps for ToplevelOps<'a> {
|
||||
self.0.current_span,
|
||||
);
|
||||
} else {
|
||||
self.0.emit_instruction(MirInstruction::Phi { dst, inputs })?;
|
||||
self.0.emit_instruction(MirInstruction::Phi { dst, inputs, type_hint: None })?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -91,6 +91,7 @@ impl MirBuilder {
|
||||
self.emit_instruction(MirInstruction::Phi {
|
||||
dst: merged,
|
||||
inputs,
|
||||
type_hint: None, // Phase 63-6: Legacy path, no type hint
|
||||
})?;
|
||||
}
|
||||
self.variable_map.insert(pin_name.clone(), merged);
|
||||
|
||||
@ -129,9 +129,17 @@ pub enum MirInstruction {
|
||||
// === SSA Phi Function ===
|
||||
/// SSA phi function for merging values from different paths
|
||||
/// `%dst = phi [%val1 from %bb1, %val2 from %bb2, ...]`
|
||||
///
|
||||
/// # Phase 63-6: Type Hint Support
|
||||
///
|
||||
/// `type_hint` field stores type information from JoinIR (Select/IfMerge)
|
||||
/// to enable type inference without scanning PHI inputs.
|
||||
/// - `Some(MirType)`: Type is known from JoinIR (P1 cases: IfSelectTest.simple/local)
|
||||
/// - `None`: Type must be inferred from PHI inputs (legacy behavior)
|
||||
Phi {
|
||||
dst: ValueId,
|
||||
inputs: Vec<(super::BasicBlockId, ValueId)>,
|
||||
type_hint: Option<super::MirType>, // Phase 63-6: JoinIR type hint
|
||||
},
|
||||
|
||||
// === Box Operations ===
|
||||
|
||||
@ -541,7 +541,7 @@ inst_meta! {
|
||||
inst_meta! {
|
||||
pub struct PhiInst { dst: ValueId, inputs: Vec<(BasicBlockId, ValueId)> }
|
||||
=> {
|
||||
from_mir = |i| match i { MirInstruction::Phi { dst, inputs } => Some(PhiInst { dst: *dst, inputs: inputs.clone() }), _ => None };
|
||||
from_mir = |i| match i { MirInstruction::Phi { dst, inputs, .. } => Some(PhiInst { dst: *dst, inputs: inputs.clone() }), _ => None };
|
||||
effects = |_: &Self| EffectMask::PURE;
|
||||
dst = |s: &Self| Some(s.dst);
|
||||
used = |s: &Self| s.inputs.iter().map(|(_, v)| *v).collect();
|
||||
|
||||
@ -94,7 +94,7 @@ pub(crate) fn intake_loop_form(
|
||||
let mut carrier_hint: Vec<String> = Vec::new();
|
||||
|
||||
for inst in query.insts_in_block(loop_form.header) {
|
||||
if let MirInstruction::Phi { dst, inputs } = inst {
|
||||
if let MirInstruction::Phi { dst, inputs, .. } = inst {
|
||||
let name = inputs
|
||||
.iter()
|
||||
.find_map(|(_, v)| value_to_name.get(v).cloned())
|
||||
|
||||
@ -491,17 +491,18 @@ Call {{\n\
|
||||
cond,
|
||||
then_val,
|
||||
else_val,
|
||||
type_hint: _, // Phase 63-3: MIR変換では現時点で未使用
|
||||
type_hint, // Phase 63-6: Type hint propagated to MIR Phi
|
||||
} => {
|
||||
// Phase 33-2: Select を MIR の if/phi に変換
|
||||
// 最小実装: cond/then/else/merge の 4 ブロック構造
|
||||
// Phase 63-6: PHI instruction with type hint from JoinIR
|
||||
|
||||
debug_log!(
|
||||
"[joinir_vm_bridge] Converting Select: dst={:?}, cond={:?}, then={:?}, else={:?}",
|
||||
"[joinir_vm_bridge] Converting Select: dst={:?}, cond={:?}, then={:?}, else={:?}, type_hint={:?}",
|
||||
dst,
|
||||
cond,
|
||||
then_val,
|
||||
else_val
|
||||
else_val,
|
||||
type_hint
|
||||
);
|
||||
|
||||
// 1. cond ブロック(現在のブロック)
|
||||
@ -532,32 +533,29 @@ Call {{\n\
|
||||
branch_terminator,
|
||||
);
|
||||
|
||||
// 6. then ブロック: dst = then_val; jump merge
|
||||
// 6. then ブロック: jump merge (no Copy, PHI will handle it)
|
||||
let mut then_block_obj = crate::mir::BasicBlock::new(then_block);
|
||||
then_block_obj.instructions.push(MirInstruction::Copy {
|
||||
dst: *dst,
|
||||
src: *then_val,
|
||||
});
|
||||
then_block_obj.instruction_spans.push(Span::unknown());
|
||||
then_block_obj.terminator = Some(MirInstruction::Jump {
|
||||
target: merge_block,
|
||||
});
|
||||
mir_func.blocks.insert(then_block, then_block_obj);
|
||||
|
||||
// 7. else ブロック: dst = else_val; jump merge
|
||||
// 7. else ブロック: jump merge (no Copy, PHI will handle it)
|
||||
let mut else_block_obj = crate::mir::BasicBlock::new(else_block);
|
||||
else_block_obj.instructions.push(MirInstruction::Copy {
|
||||
dst: *dst,
|
||||
src: *else_val,
|
||||
});
|
||||
else_block_obj.instruction_spans.push(Span::unknown());
|
||||
else_block_obj.terminator = Some(MirInstruction::Jump {
|
||||
target: merge_block,
|
||||
});
|
||||
mir_func.blocks.insert(else_block, else_block_obj);
|
||||
|
||||
// 8. merge ブロック作成(空)
|
||||
let merge_block_obj = crate::mir::BasicBlock::new(merge_block);
|
||||
// 8. merge ブロック: PHI instruction with type hint
|
||||
let mut merge_block_obj = crate::mir::BasicBlock::new(merge_block);
|
||||
// Phase 63-6: Create PHI with type hint from JoinIR Select
|
||||
merge_block_obj.instructions.push(MirInstruction::Phi {
|
||||
dst: *dst,
|
||||
inputs: vec![(then_block, *then_val), (else_block, *else_val)],
|
||||
type_hint: type_hint.clone(), // Phase 63-6: Propagate type hint from JoinIR
|
||||
});
|
||||
merge_block_obj.instruction_spans.push(Span::unknown());
|
||||
mir_func.blocks.insert(merge_block, merge_block_obj);
|
||||
|
||||
// 9. merge ブロックに移動
|
||||
|
||||
@ -395,6 +395,7 @@ impl<'a> LoopBuilder<'a> {
|
||||
merge_block.add_instruction(MirInstruction::Phi {
|
||||
dst: phi_id,
|
||||
inputs: final_inputs,
|
||||
type_hint: None, // Phase 63-6
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ impl<'a> LoopBuilder<'a> {
|
||||
MirInstruction::Phi {
|
||||
dst: d,
|
||||
inputs: ins,
|
||||
.. // Phase 63-6: Ignore type_hint in pattern matching
|
||||
} if *d == dst => {
|
||||
*ins = inputs.clone();
|
||||
if block.instruction_spans.len() <= idx {
|
||||
@ -58,6 +59,7 @@ impl<'a> LoopBuilder<'a> {
|
||||
let phi_inst = MirInstruction::Phi {
|
||||
dst,
|
||||
inputs: inputs.clone(),
|
||||
type_hint: None, // Phase 63-6: Legacy path, no type hint
|
||||
};
|
||||
block.instructions.insert(0, phi_inst);
|
||||
block.instruction_spans.insert(0, span);
|
||||
|
||||
@ -136,7 +136,8 @@ pub fn infer_type_from_phi(
|
||||
) -> Option<MirType> {
|
||||
for (_bid, bb) in function.blocks.iter() {
|
||||
for inst in bb.instructions.iter() {
|
||||
if let MirInstruction::Phi { dst, inputs } = inst {
|
||||
if let MirInstruction::Phi { dst, inputs, .. } = inst {
|
||||
// Phase 63-6: type_hint is ignored here, will be used in lifecycle.rs
|
||||
if *dst == ret_val {
|
||||
let mut it = inputs.iter().filter_map(|(_, v)| types.get(v));
|
||||
if let Some(first) = it.next() {
|
||||
|
||||
@ -248,7 +248,7 @@ pub fn format_instruction(
|
||||
}
|
||||
}
|
||||
|
||||
MirInstruction::Phi { dst, inputs } => {
|
||||
MirInstruction::Phi { dst, inputs, .. } => {
|
||||
let inputs_str = inputs
|
||||
.iter()
|
||||
.map(|(bb, val)| format!("[{}, {}]", val, bb))
|
||||
|
||||
@ -72,7 +72,7 @@ pub fn insert_phi_at_head_spanned(
|
||||
inputs.sort_by_key(|(bb, _)| bb.0);
|
||||
if let Some(bb) = f.get_block_mut(bb_id) {
|
||||
bb.insert_spanned_after_phis(SpannedInstruction {
|
||||
inst: MirInstruction::Phi { dst, inputs },
|
||||
inst: MirInstruction::Phi { dst, inputs, type_hint: None },
|
||||
span,
|
||||
});
|
||||
}
|
||||
|
||||
@ -84,6 +84,7 @@ impl MirBuilder {
|
||||
self.emit_instruction(MirInstruction::Phi {
|
||||
dst: phi_val,
|
||||
inputs,
|
||||
type_hint: None, // Phase 63-6: Legacy path, no type hint
|
||||
})?;
|
||||
}
|
||||
|
||||
@ -146,7 +147,11 @@ impl MirBuilder {
|
||||
);
|
||||
} else {
|
||||
// フォールバック: 直接emit(主にテストや特殊ケース用)
|
||||
self.emit_instruction(MirInstruction::Phi { dst, inputs })?;
|
||||
self.emit_instruction(MirInstruction::Phi {
|
||||
dst,
|
||||
inputs,
|
||||
type_hint: None, // Phase 63-6: Legacy path, no type hint
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -163,6 +163,7 @@ impl LoopFormOps for LoopFormJsonOps<'_> {
|
||||
if let MirInstruction::Phi {
|
||||
dst,
|
||||
inputs: phi_inputs,
|
||||
.. // Phase 63-6: Ignore type_hint
|
||||
} = inst
|
||||
{
|
||||
if *dst == phi_id {
|
||||
|
||||
@ -273,6 +273,7 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
block_ref.add_instruction(MirInstruction::Phi {
|
||||
dst: ValueId::new(dst),
|
||||
inputs: pairs,
|
||||
type_hint: None, // Phase 63-6
|
||||
});
|
||||
max_value_id = max_value_id.max(dst + 1);
|
||||
}
|
||||
|
||||
@ -227,7 +227,7 @@ pub fn emit_mir_json_for_harness(
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if let I::Phi { dst, inputs } = inst {
|
||||
if let I::Phi { dst, inputs, .. } = inst {
|
||||
let incoming: Vec<_> = inputs
|
||||
.iter()
|
||||
.map(|(b, v)| json!([v.as_u32(), b.as_u32()]))
|
||||
@ -642,7 +642,7 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
let mut emitted_defs: std::collections::HashSet<u32> =
|
||||
std::collections::HashSet::new();
|
||||
for inst in &bb.instructions {
|
||||
if let I::Phi { dst, inputs } = inst {
|
||||
if let I::Phi { dst, inputs, .. } = inst {
|
||||
let incoming: Vec<_> = inputs
|
||||
.iter()
|
||||
.map(|(b, v)| json!([v.as_u32(), b.as_u32()]))
|
||||
|
||||
@ -179,6 +179,7 @@ pub fn parse_mir_v0_to_module(json: &str) -> Result<MirModule, String> {
|
||||
block_ref.add_instruction(MirInstruction::Phi {
|
||||
dst: ValueId::new(dst),
|
||||
inputs: pairs,
|
||||
type_hint: None, // Phase 63-6
|
||||
});
|
||||
max_value_id = max_value_id.max(dst + 1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user