refactor(joinir): Phase 260 P0.1 Step 4 - Extract type_propagation.rs (70 lines)

Extract propagate_value_type_for_inst to dedicated type_propagation module.
Handles type info flow from JoinIR→MIR merge (SSOT + fallback inference).

Changes:
- NEW: rewriter/type_propagation.rs - propagate_value_type_for_inst
- CHANGED: instruction_rewriter.rs - import from type_propagation (deleted local fn)
- CHANGED: rewriter/mod.rs - declare type_propagation module

Design:
- SSOT: Prefer type info from JoinIR source function metadata
- Fallback: Infer from instruction structure (Const/Copy/BinOp/Compare)
- LLVM: Ensures '+' stays numeric (not concat_hh_mixed)

Reduction:
- instruction_rewriter.rs: 1466 → 1396 lines (-70)

Next: Start terminator.rs extraction (Branch/Jump/Return remapping)
This commit is contained in:
2025-12-21 06:11:53 +09:00
parent e555b4ad31
commit d9ff3f60ff
3 changed files with 120 additions and 73 deletions

View File

@ -25,78 +25,8 @@ use std::collections::BTreeSet;
// Phase 260 P0.1 Step 3: Import from helpers module
use super::rewriter::helpers::is_skippable_continuation;
fn propagate_value_type_for_inst(
builder: &mut crate::mir::builder::MirBuilder,
source_func: &MirFunction,
old_inst: &MirInstruction,
new_inst: &MirInstruction,
) {
let Some(_) = builder.scope_ctx.current_function else {
return;
};
let (old_dst, new_dst) = match (old_inst, new_inst) {
(MirInstruction::Const { dst: o, .. }, MirInstruction::Const { dst: n, .. }) => {
(o, n)
}
(MirInstruction::BinOp { dst: o, .. }, MirInstruction::BinOp { dst: n, .. }) => (o, n),
(MirInstruction::UnaryOp { dst: o, .. }, MirInstruction::UnaryOp { dst: n, .. }) => (o, n),
(MirInstruction::Compare { dst: o, .. }, MirInstruction::Compare { dst: n, .. }) => (o, n),
(MirInstruction::Load { dst: o, .. }, MirInstruction::Load { dst: n, .. }) => (o, n),
(MirInstruction::Cast { dst: o, .. }, MirInstruction::Cast { dst: n, .. }) => (o, n),
(MirInstruction::TypeOp { dst: o, .. }, MirInstruction::TypeOp { dst: n, .. }) => (o, n),
(MirInstruction::ArrayGet { dst: o, .. }, MirInstruction::ArrayGet { dst: n, .. }) => (o, n),
(MirInstruction::Copy { dst: o, .. }, MirInstruction::Copy { dst: n, .. }) => (o, n),
(MirInstruction::NewBox { dst: o, .. }, MirInstruction::NewBox { dst: n, .. }) => (o, n),
(MirInstruction::TypeCheck { dst: o, .. }, MirInstruction::TypeCheck { dst: n, .. }) => (o, n),
(MirInstruction::Phi { dst: o, .. }, MirInstruction::Phi { dst: n, .. }) => (o, n),
_ => return,
};
// Prefer SSOT from the source MIR (JoinIR→MIR bridge may have hints).
if let Some(ty) = source_func.metadata.value_types.get(old_dst).cloned() {
builder.type_ctx.value_types.insert(*new_dst, ty);
return;
}
// Fallback inference from the rewritten instruction itself.
//
// This is important for the LLVM harness: it relies on `dst_type` hints to keep `+`
// as numeric add instead of `concat_hh_mixed` when types are otherwise unknown.
match new_inst {
MirInstruction::Const { dst, value } if dst == new_dst => {
let ty = match value {
ConstValue::Integer(_) => Some(MirType::Integer),
ConstValue::Float(_) => Some(MirType::Float),
ConstValue::Bool(_) => Some(MirType::Bool),
ConstValue::String(_) => Some(MirType::String),
ConstValue::Void => Some(MirType::Void),
_ => None,
};
if let Some(ty) = ty {
builder.type_ctx.value_types.insert(*dst, ty);
}
}
MirInstruction::Copy { dst, src } if dst == new_dst => {
if let Some(src_ty) = builder.type_ctx.value_types.get(src).cloned() {
builder.type_ctx.value_types.insert(*dst, src_ty);
}
}
MirInstruction::BinOp { dst, lhs, rhs, .. } if dst == new_dst => {
let lhs_ty = builder.type_ctx.value_types.get(lhs);
let rhs_ty = builder.type_ctx.value_types.get(rhs);
if matches!(lhs_ty, Some(MirType::Integer)) && matches!(rhs_ty, Some(MirType::Integer))
{
builder.type_ctx.value_types.insert(*dst, MirType::Integer);
}
}
MirInstruction::Compare { dst, .. } if dst == new_dst => {
builder.type_ctx.value_types.insert(*dst, MirType::Bool);
}
_ => {}
}
}
// Phase 260 P0.1 Step 4: Import from type_propagation module
use super::rewriter::type_propagation::propagate_value_type_for_inst;
/// Phase 4: Merge ALL functions and rewrite instructions
///

View File

@ -21,9 +21,9 @@
// Modules (extracted):
// - logging: DEBUG-177 style verbose logs ✅
// - helpers: Small pure functions (is_skippable_continuation) ✅
// - type_propagation: Type propagation (propagate_value_type_for_inst) ✅
// Future modules (pending):
// - type_propagation: propagate_value_type_for_inst
// - terminator: Branch/Jump/Return remapping
// - exit_line: ExitLine/exit-phi wiring
// - carriers: loop_invariants, exit_bindings
@ -32,6 +32,7 @@
pub(super) mod helpers; // Phase 260 P0.1 Step 3: Helpers extracted ✅
pub(super) mod logging; // Phase 260 P0.1 Step 2: Logging extracted ✅
pub(super) mod type_propagation; // Phase 260 P0.1 Step 4: Type propagation extracted ✅
// Re-export public API
pub(super) use helpers::is_skippable_continuation; // Phase 260 P0.1 Step 3: From helpers ✅

View File

@ -0,0 +1,116 @@
//! Type propagation utilities for instruction rewriter
//!
//! Phase 260 P0.1 Step 4: Extracted from instruction_rewriter.rs (lines 29-99)
//! Propagates type information from JoinIR source to HOST MIR during merge.
use crate::mir::types::ConstValue;
use crate::mir::{MirFunction, MirInstruction, MirType};
/// Propagate value type information from source instruction to remapped instruction
///
/// This function ensures type information flows correctly from JoinIR→MIR merge:
/// 1. **SSOT from source**: Prefer type info from JoinIR source function metadata
/// 2. **Fallback inference**: Infer from instruction structure if no source hint
///
/// ## Why This Matters
///
/// The LLVM harness relies on `dst_type` hints to keep `+` as numeric add
/// instead of `concat_hh_mixed` when types are otherwise unknown.
///
/// ## Supported Instructions
///
/// - Const, BinOp, UnaryOp, Compare, Load, Cast, TypeOp
/// - ArrayGet, Copy, NewBox, TypeCheck, Phi
///
/// ## Example
///
/// ```ignore
/// // Before remapping
/// let old_inst = MirInstruction::Const {
/// dst: ValueId(42), // JoinIR value
/// value: ConstValue::Integer(10),
/// };
///
/// // After remapping
/// let new_inst = MirInstruction::Const {
/// dst: ValueId(123), // HOST value
/// value: ConstValue::Integer(10),
/// };
///
/// // Propagate type: ValueId(123) → MirType::Integer
/// propagate_value_type_for_inst(builder, source_func, &old_inst, &new_inst);
/// ```
pub(in crate::mir::builder::control_flow::joinir::merge) fn propagate_value_type_for_inst(
builder: &mut crate::mir::builder::MirBuilder,
source_func: &MirFunction,
old_inst: &MirInstruction,
new_inst: &MirInstruction,
) {
let Some(_) = builder.scope_ctx.current_function else {
return;
};
let (old_dst, new_dst) = match (old_inst, new_inst) {
(MirInstruction::Const { dst: o, .. }, MirInstruction::Const { dst: n, .. }) => (o, n),
(MirInstruction::BinOp { dst: o, .. }, MirInstruction::BinOp { dst: n, .. }) => (o, n),
(MirInstruction::UnaryOp { dst: o, .. }, MirInstruction::UnaryOp { dst: n, .. }) => (o, n),
(MirInstruction::Compare { dst: o, .. }, MirInstruction::Compare { dst: n, .. }) => (o, n),
(MirInstruction::Load { dst: o, .. }, MirInstruction::Load { dst: n, .. }) => (o, n),
(MirInstruction::Cast { dst: o, .. }, MirInstruction::Cast { dst: n, .. }) => (o, n),
(MirInstruction::TypeOp { dst: o, .. }, MirInstruction::TypeOp { dst: n, .. }) => (o, n),
(MirInstruction::ArrayGet { dst: o, .. }, MirInstruction::ArrayGet { dst: n, .. }) => {
(o, n)
}
(MirInstruction::Copy { dst: o, .. }, MirInstruction::Copy { dst: n, .. }) => (o, n),
(MirInstruction::NewBox { dst: o, .. }, MirInstruction::NewBox { dst: n, .. }) => (o, n),
(MirInstruction::TypeCheck { dst: o, .. }, MirInstruction::TypeCheck { dst: n, .. }) => {
(o, n)
}
(MirInstruction::Phi { dst: o, .. }, MirInstruction::Phi { dst: n, .. }) => (o, n),
_ => return,
};
// Prefer SSOT from the source MIR (JoinIR→MIR bridge may have hints).
if let Some(ty) = source_func.metadata.value_types.get(old_dst).cloned() {
builder.type_ctx.value_types.insert(*new_dst, ty);
return;
}
// Fallback inference from the rewritten instruction itself.
//
// This is important for the LLVM harness: it relies on `dst_type` hints to keep `+`
// as numeric add instead of `concat_hh_mixed` when types are otherwise unknown.
match new_inst {
MirInstruction::Const { dst, value } if dst == new_dst => {
let ty = match value {
ConstValue::Integer(_) => Some(MirType::Integer),
ConstValue::Float(_) => Some(MirType::Float),
ConstValue::Bool(_) => Some(MirType::Bool),
ConstValue::String(_) => Some(MirType::String),
ConstValue::Void => Some(MirType::Void),
_ => None,
};
if let Some(ty) = ty {
builder.type_ctx.value_types.insert(*dst, ty);
}
}
MirInstruction::Copy { dst, src } if dst == new_dst => {
if let Some(src_ty) = builder.type_ctx.value_types.get(src).cloned() {
builder.type_ctx.value_types.insert(*dst, src_ty);
}
}
MirInstruction::BinOp { dst, lhs, rhs, .. } if dst == new_dst => {
let lhs_ty = builder.type_ctx.value_types.get(lhs);
let rhs_ty = builder.type_ctx.value_types.get(rhs);
if matches!(lhs_ty, Some(MirType::Integer))
&& matches!(rhs_ty, Some(MirType::Integer))
{
builder.type_ctx.value_types.insert(*dst, MirType::Integer);
}
}
MirInstruction::Compare { dst, .. } if dst == new_dst => {
builder.type_ctx.value_types.insert(*dst, MirType::Bool);
}
_ => {}
}
}