feat(mir): Phase 279 P0 - Type propagation pipeline SSOT unification
Eliminate "2本のコンパイラ" problem by unifying type propagation into single SSOT entry. SSOT implementation: - src/mir/type_propagation/pipeline.rs - SSOT type propagation pipeline - TypePropagationPipeline::run() - Single entry point for all routes Pipeline steps (fixed order): 1. Copy propagation (initial) 2. BinOp re-propagation (numeric promotion: Int+Float→Float) 3. Copy propagation (propagate promoted types) 4. PHI type inference (private step - cannot bypass) Callers (both routes now use SSOT): - lifecycle.rs::finalize_module() - Builder lifecycle route - joinir_function_converter.rs::propagate_types() - JoinIR bridge route Fail-fast guard (structural guarantee): - PHI type inference is private step inside TypePropagationPipeline - lifecycle.rs and joinir_function_converter.rs cannot call PhiTypeResolver directly - Only public API: TypePropagationPipeline::run() - Order drift is structurally impossible (private encapsulation) Code reduction: - ~500 lines of duplicate BinOp re-propagation logic removed - 2 implementations consolidated into 1 SSOT Files changed: - New: src/mir/type_propagation/mod.rs - New: src/mir/type_propagation/pipeline.rs (~300 lines) - Modified: src/mir/mod.rs - Modified: src/mir/builder/lifecycle.rs (~400 lines removed) - Modified: src/mir/join_ir_vm_bridge/joinir_function_converter.rs (~100 lines removed) Regression testing: ✅ Lifecycle route (VM backend): Phase 275 fixture ✅ JoinIR route (VM backend): loop_min_while.hako ✅ LLVM harness: Phase 275 fixture 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -102,8 +102,9 @@ impl JoinIrFunctionConverter {
|
||||
let mut block_converter = JoinIrBlockConverter::new();
|
||||
block_converter.convert_function_body(&mut mir_func, &join_func.body)?;
|
||||
|
||||
// Phase 275 P0: Type propagation for JoinIR → MIR conversion
|
||||
Self::propagate_types(&mut mir_func);
|
||||
// Phase 279 P0: Type propagation for JoinIR → MIR conversion (SSOT)
|
||||
Self::propagate_types(&mut mir_func)
|
||||
.map_err(|e| JoinIrVmBridgeError::new(format!("Type propagation failed: {}", e)))?;
|
||||
|
||||
// Debug: print all blocks
|
||||
debug_log!(
|
||||
@ -153,8 +154,9 @@ impl JoinIrFunctionConverter {
|
||||
let mut block_converter = JoinIrBlockConverter::new_with_func_names(func_name_map);
|
||||
block_converter.convert_function_body(&mut mir_func, &join_func.body)?;
|
||||
|
||||
// Phase 275 P0: Type propagation for JoinIR → MIR conversion
|
||||
Self::propagate_types(&mut mir_func);
|
||||
// Phase 279 P0: Type propagation for JoinIR → MIR conversion (SSOT)
|
||||
Self::propagate_types(&mut mir_func)
|
||||
.map_err(|e| JoinIrVmBridgeError::new(format!("Type propagation failed: {}", e)))?;
|
||||
|
||||
debug_log!(
|
||||
"[joinir_vm_bridge] Function '{}' has {} blocks:",
|
||||
@ -173,149 +175,27 @@ impl JoinIrFunctionConverter {
|
||||
Ok(mir_func)
|
||||
}
|
||||
|
||||
/// Phase 275 P0: Type propagation for JoinIR-converted MIR
|
||||
/// Phase 279 P0: Type propagation for JoinIR-converted MIR
|
||||
///
|
||||
/// Runs Copy type propagation, BinOp type re-propagation, and PHI type inference after conversion.
|
||||
fn propagate_types(mir_func: &mut MirFunction) {
|
||||
if std::env::var("NYASH_PHI_RESOLVER_DEBUG").is_ok() {
|
||||
eprintln!("[phi_resolver] propagate_types START for function: {}", mir_func.signature.name);
|
||||
}
|
||||
/// SSOT 型伝播パイプラインを呼び出す。
|
||||
/// lifecycle.rs と同じ入口を使用することで、順序ドリフトを防止。
|
||||
fn propagate_types(mir_func: &mut MirFunction) -> Result<(), String> {
|
||||
use crate::mir::type_propagation::TypePropagationPipeline;
|
||||
|
||||
// Extract value_types to avoid borrow conflicts
|
||||
let mut value_types = std::mem::take(&mut mir_func.metadata.value_types);
|
||||
|
||||
// Step 1: Copy type propagation
|
||||
CopyTypePropagator::propagate(mir_func, &mut value_types);
|
||||
|
||||
// Step 2: BinOp type re-propagation (after Copy types are resolved)
|
||||
// This fixes Int+Float → Float promotion
|
||||
if std::env::var("NYASH_PHI_RESOLVER_DEBUG").is_ok() {
|
||||
eprintln!("[phi_resolver] Before repropagate_binop_types:");
|
||||
for vid in [9, 10, 11, 12].iter() {
|
||||
let v = crate::mir::ValueId(*vid);
|
||||
if let Some(ty) = value_types.get(&v) {
|
||||
eprintln!("[phi_resolver] {:?} = {:?}", v, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::repropagate_binop_types(mir_func, &mut value_types);
|
||||
if std::env::var("NYASH_PHI_RESOLVER_DEBUG").is_ok() {
|
||||
eprintln!("[phi_resolver] After repropagate_binop_types:");
|
||||
for vid in [9, 10, 11, 12].iter() {
|
||||
let v = crate::mir::ValueId(*vid);
|
||||
if let Some(ty) = value_types.get(&v) {
|
||||
eprintln!("[phi_resolver] {:?} = {:?}", v, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Another round of Copy propagation (for chains like BinOp→Copy→PHI)
|
||||
CopyTypePropagator::propagate(mir_func, &mut value_types);
|
||||
|
||||
// Step 4: PHI type inference from incoming values
|
||||
// Collect PHI dsts first
|
||||
let mut phi_dsts: Vec<crate::mir::ValueId> = Vec::new();
|
||||
for (_bid, bb) in mir_func.blocks.iter() {
|
||||
for inst in &bb.instructions {
|
||||
if let MirInstruction::Phi { dst, .. } = inst {
|
||||
phi_dsts.push(*dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Infer PHI types using PhiTypeResolver
|
||||
if !phi_dsts.is_empty() {
|
||||
// Debug: Check value_types before PHI resolution
|
||||
if std::env::var("NYASH_PHI_RESOLVER_DEBUG").is_ok() {
|
||||
eprintln!("[phi_resolver] value_types before PHI resolution:");
|
||||
for vid in [9, 10, 11, 12].iter() {
|
||||
let v = crate::mir::ValueId(*vid);
|
||||
if let Some(ty) = value_types.get(&v) {
|
||||
eprintln!("[phi_resolver] {:?} = {:?}", v, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let phi_resolver = PhiTypeResolver::new(mir_func, &value_types);
|
||||
let mut inferred_types: Vec<(crate::mir::ValueId, MirType)> = Vec::new();
|
||||
|
||||
for dst in phi_dsts {
|
||||
if let Some(mt) = phi_resolver.resolve(dst) {
|
||||
let existing = value_types.get(&dst);
|
||||
if existing.is_none() || existing != Some(&mt) {
|
||||
inferred_types.push((dst, mt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply inferred types
|
||||
for (dst, mt) in inferred_types {
|
||||
value_types.insert(dst, mt);
|
||||
}
|
||||
}
|
||||
// Phase 279 P0: Use SSOT type propagation pipeline
|
||||
// 順序固定: Copy → BinOp → Copy → PHI
|
||||
TypePropagationPipeline::run(mir_func, &mut value_types)?;
|
||||
|
||||
// Put value_types back
|
||||
mir_func.metadata.value_types = value_types;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Phase 275 P0: BinOp type re-propagation
|
||||
///
|
||||
/// Re-infers BinOp result types based on operand types.
|
||||
/// Handles Int+Float → Float promotion.
|
||||
fn repropagate_binop_types(
|
||||
mir_func: &MirFunction,
|
||||
value_types: &mut BTreeMap<crate::mir::ValueId, MirType>,
|
||||
) {
|
||||
use crate::mir::BinaryOp;
|
||||
|
||||
let mut updates: Vec<(crate::mir::ValueId, MirType)> = Vec::new();
|
||||
|
||||
for (_bid, bb) in mir_func.blocks.iter() {
|
||||
for inst in bb.instructions.iter() {
|
||||
if let MirInstruction::BinOp { dst, op, lhs, rhs } = inst {
|
||||
// Get operand types
|
||||
let lhs_type = value_types.get(lhs);
|
||||
let rhs_type = value_types.get(rhs);
|
||||
|
||||
let new_type = if matches!(op, BinaryOp::Add) {
|
||||
// Add can be numeric or string concat
|
||||
match (lhs_type, rhs_type) {
|
||||
(Some(MirType::Float), _) | (_, Some(MirType::Float)) => {
|
||||
Some(MirType::Float)
|
||||
}
|
||||
(Some(MirType::Integer), Some(MirType::Integer)) => {
|
||||
Some(MirType::Integer)
|
||||
}
|
||||
(Some(MirType::String), Some(MirType::String)) => {
|
||||
Some(MirType::Box("StringBox".to_string()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
// Other arithmetic ops: check for Float
|
||||
match (lhs_type, rhs_type) {
|
||||
(Some(MirType::Float), _) | (_, Some(MirType::Float)) => {
|
||||
Some(MirType::Float)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(new_ty) = new_type {
|
||||
let current = value_types.get(dst);
|
||||
if current.is_none() || current != Some(&new_ty) {
|
||||
updates.push((*dst, new_ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply updates
|
||||
for (dst, ty) in updates {
|
||||
value_types.insert(dst, ty);
|
||||
}
|
||||
}
|
||||
// Phase 279 P0: repropagate_binop_types() static method removed
|
||||
// Moved to TypePropagationPipeline (SSOT)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user