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:
@ -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
|
||||
///
|
||||
|
||||
@ -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 ✅
|
||||
|
||||
@ -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);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user