refactor(joinir): Pattern 4 modularization with CarrierInfo/ExitMeta
Removes hardcoded "sum" and ValueId(15) from Pattern 4 lowerer by introducing CarrierInfo and ExitMeta structures. Changes: - New carrier_info.rs: CarrierInfo, CarrierVar, ExitMeta structs - loop_with_continue_minimal.rs: Returns (JoinModule, ExitMeta) - pattern4_with_continue.rs: Dynamic binding generation from metadata Design approach: "Thin meta on existing boxes" (ChatGPT proposal) - CarrierInfo: Built from variable_map, not AST re-analysis - ExitMeta: Carrier name + JoinIR ValueId pairs from lowerer - LoopExitBinding: Auto-generated from CarrierInfo + ExitMeta Test: loop_continue_pattern4.hako outputs 25 (unchanged) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -100,6 +100,7 @@ impl MirBuilder {
|
|||||||
_func_name: &str,
|
_func_name: &str,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
) -> Result<Option<ValueId>, String> {
|
) -> Result<Option<ValueId>, String> {
|
||||||
|
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, CarrierVar};
|
||||||
use crate::mir::join_ir::lowering::loop_with_continue_minimal::lower_loop_with_continue_minimal;
|
use crate::mir::join_ir::lowering::loop_with_continue_minimal::lower_loop_with_continue_minimal;
|
||||||
use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta;
|
use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta;
|
||||||
use crate::mir::BasicBlockId;
|
use crate::mir::BasicBlockId;
|
||||||
@ -121,14 +122,28 @@ impl MirBuilder {
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Get sum variable from variable_map
|
// Phase 196: Build CarrierInfo from variable_map
|
||||||
let sum_var_id = self
|
// Collect all non-loop-var variables as potential carriers
|
||||||
.variable_map
|
let mut carriers = Vec::new();
|
||||||
.get("sum")
|
for (var_name, &var_id) in &self.variable_map {
|
||||||
.copied()
|
if var_name != &loop_var_name {
|
||||||
.ok_or_else(|| {
|
carriers.push(CarrierVar {
|
||||||
format!("[cf_loop/pattern4] Sum variable 'sum' not found in variable_map")
|
name: var_name.clone(),
|
||||||
})?;
|
host_id: var_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let carrier_info = CarrierInfo {
|
||||||
|
loop_var_name: loop_var_name.clone(),
|
||||||
|
loop_var_id,
|
||||||
|
carriers: carriers.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
eprintln!("[pattern4] CarrierInfo: loop_var={}, carriers={:?}",
|
||||||
|
carrier_info.loop_var_name,
|
||||||
|
carrier_info.carriers.iter().map(|c| &c.name).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
|
||||||
// Phase 195: Use unified trace
|
// Phase 195: Use unified trace
|
||||||
trace::trace().varmap("pattern4_start", &self.variable_map);
|
trace::trace().varmap("pattern4_start", &self.variable_map);
|
||||||
@ -150,8 +165,8 @@ impl MirBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Call Pattern 4 lowerer
|
// Call Pattern 4 lowerer
|
||||||
let join_module = match lower_loop_with_continue_minimal(scope) {
|
let (join_module, exit_meta) = match lower_loop_with_continue_minimal(scope) {
|
||||||
Some(module) => module,
|
Some(result) => result,
|
||||||
None => {
|
None => {
|
||||||
// Phase 195: Use unified trace
|
// Phase 195: Use unified trace
|
||||||
trace::trace().debug("pattern4", "Pattern 4 lowerer returned None");
|
trace::trace().debug("pattern4", "Pattern 4 lowerer returned None");
|
||||||
@ -159,6 +174,11 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
eprintln!("[pattern4] ExitMeta: {} exit bindings", exit_meta.exit_values.len());
|
||||||
|
for (carrier_name, join_value) in &exit_meta.exit_values {
|
||||||
|
eprintln!("[pattern4] {} → ValueId({})", carrier_name, join_value.0);
|
||||||
|
}
|
||||||
|
|
||||||
// Phase 195: Use unified trace
|
// Phase 195: Use unified trace
|
||||||
trace::trace().joinir_stats(
|
trace::trace().joinir_stats(
|
||||||
"pattern4",
|
"pattern4",
|
||||||
@ -181,19 +201,49 @@ impl MirBuilder {
|
|||||||
mir_module.functions.values().map(|f| f.blocks.len()).sum(),
|
mir_module.functions.values().map(|f| f.blocks.len()).sum(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Phase 196: Dynamically generate exit_bindings from ExitMeta and CarrierInfo
|
||||||
|
let mut exit_bindings = Vec::new();
|
||||||
|
for (carrier_name, join_exit_value) in &exit_meta.exit_values {
|
||||||
|
// Find the host_slot for this carrier
|
||||||
|
let host_slot = carrier_info
|
||||||
|
.carriers
|
||||||
|
.iter()
|
||||||
|
.find(|c| &c.name == carrier_name)
|
||||||
|
.map(|c| c.host_id)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
format!(
|
||||||
|
"[cf_loop/pattern4] Carrier '{}' not found in CarrierInfo",
|
||||||
|
carrier_name
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
exit_bindings.push(
|
||||||
|
crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding {
|
||||||
|
carrier_name: carrier_name.clone(),
|
||||||
|
join_exit_value: *join_exit_value,
|
||||||
|
host_slot,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
eprintln!("[pattern4] Generated binding: {} → JoinIR={} Host={}",
|
||||||
|
carrier_name, join_exit_value.0, host_slot.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 196: Build host_inputs dynamically
|
||||||
|
// Order: [loop_var, carrier1, carrier2, ...]
|
||||||
|
let mut host_inputs = vec![carrier_info.loop_var_id];
|
||||||
|
for carrier in &carrier_info.carriers {
|
||||||
|
host_inputs.push(carrier.host_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!("[pattern4] host_inputs: {:?}", host_inputs.iter().map(|v| v.0).collect::<Vec<_>>());
|
||||||
|
|
||||||
// Merge JoinIR blocks into current function
|
// Merge JoinIR blocks into current function
|
||||||
// Phase 195 FIX: Use exit_bindings to connect k_exit's sum_exit to host's sum variable
|
// Phase 196: Use dynamically generated exit_bindings and host_inputs
|
||||||
// Pattern 4: k_exit(sum_exit) returns sum_exit, so we bind ValueId(15) to host's sum
|
|
||||||
let boundary = crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_with_exit_bindings(
|
let boundary = crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_with_exit_bindings(
|
||||||
vec![ValueId(0), ValueId(1)], // JoinIR's main() parameters (i_init, sum_init)
|
vec![ValueId(0), ValueId(1)], // JoinIR's main() parameters (i_init, sum_init)
|
||||||
vec![loop_var_id, sum_var_id], // Host's loop variables
|
host_inputs, // Host's loop variables (dynamic)
|
||||||
vec![
|
exit_bindings,
|
||||||
crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding {
|
|
||||||
carrier_name: "sum".to_string(),
|
|
||||||
join_exit_value: ValueId(15), // k_exit's parameter (sum_exit)
|
|
||||||
host_slot: sum_var_id, // variable_map["sum"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
// Phase 195: Capture exit PHI result (Pattern 4 returns sum)
|
// Phase 195: Capture exit PHI result (Pattern 4 returns sum)
|
||||||
let result_val = self.merge_joinir_mir_blocks(&mir_module, Some(&boundary), debug)?;
|
let result_val = self.merge_joinir_mir_blocks(&mir_module, Some(&boundary), debug)?;
|
||||||
|
|||||||
64
src/mir/join_ir/lowering/carrier_info.rs
Normal file
64
src/mir/join_ir/lowering/carrier_info.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
//! Carrier variable metadata for JoinIR loop lowering
|
||||||
|
//!
|
||||||
|
//! This module defines metadata structures for tracking carrier variables
|
||||||
|
//! in loop lowering. This enables dynamic generation of exit bindings
|
||||||
|
//! without hardcoded variable names or ValueIds.
|
||||||
|
|
||||||
|
use crate::mir::ValueId;
|
||||||
|
|
||||||
|
/// Information about a single carrier variable
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CarrierVar {
|
||||||
|
/// Variable name (e.g., "sum", "printed")
|
||||||
|
pub name: String,
|
||||||
|
/// Host ValueId for this variable
|
||||||
|
pub host_id: ValueId,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Complete carrier information for a loop
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CarrierInfo {
|
||||||
|
/// Loop control variable name (e.g., "i")
|
||||||
|
pub loop_var_name: String,
|
||||||
|
/// Loop control variable ValueId in host
|
||||||
|
pub loop_var_id: ValueId,
|
||||||
|
/// Additional carrier variables (e.g., sum, printed)
|
||||||
|
pub carriers: Vec<CarrierVar>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exit metadata returned by lowerers
|
||||||
|
///
|
||||||
|
/// This structure captures the mapping from JoinIR exit values to
|
||||||
|
/// carrier variable names, enabling dynamic binding generation.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ExitMeta {
|
||||||
|
/// Exit value bindings: (carrier_name, join_exit_value_id)
|
||||||
|
///
|
||||||
|
/// Example for Pattern 4:
|
||||||
|
/// ```
|
||||||
|
/// vec![("sum".to_string(), ValueId(15))]
|
||||||
|
/// ```
|
||||||
|
/// where ValueId(15) is the k_exit parameter in JoinIR-local space.
|
||||||
|
pub exit_values: Vec<(String, ValueId)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExitMeta {
|
||||||
|
/// Create new ExitMeta with no exit values
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
Self {
|
||||||
|
exit_values: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create ExitMeta with a single exit value
|
||||||
|
pub fn single(carrier_name: String, join_value: ValueId) -> Self {
|
||||||
|
Self {
|
||||||
|
exit_values: vec![(carrier_name, join_value)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create ExitMeta with multiple exit values
|
||||||
|
pub fn multiple(exit_values: Vec<(String, ValueId)>) -> Self {
|
||||||
|
Self { exit_values }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -68,6 +68,7 @@
|
|||||||
//!
|
//!
|
||||||
//! Following the "80/20 rule" from CLAUDE.md - get it working first, generalize later.
|
//! Following the "80/20 rule" from CLAUDE.md - get it working first, generalize later.
|
||||||
|
|
||||||
|
use crate::mir::join_ir::lowering::carrier_info::ExitMeta;
|
||||||
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||||
use crate::mir::join_ir::{
|
use crate::mir::join_ir::{
|
||||||
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
|
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
|
||||||
@ -101,7 +102,7 @@ use crate::mir::ValueId;
|
|||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// * `Some(JoinModule)` - Successfully lowered to JoinIR
|
/// * `Some((JoinModule, ExitMeta))` - Successfully lowered to JoinIR with exit metadata
|
||||||
/// * `None` - Pattern not matched (fallback to other lowerers)
|
/// * `None` - Pattern not matched (fallback to other lowerers)
|
||||||
///
|
///
|
||||||
/// # Boundary Contract
|
/// # Boundary Contract
|
||||||
@ -109,8 +110,9 @@ use crate::mir::ValueId;
|
|||||||
/// This function returns a JoinModule with:
|
/// This function returns a JoinModule with:
|
||||||
/// - **Input slots**: ValueId(0) = i_init, ValueId(1) = sum_init
|
/// - **Input slots**: ValueId(0) = i_init, ValueId(1) = sum_init
|
||||||
/// - **Output slot**: k_exit returns the final sum value
|
/// - **Output slot**: k_exit returns the final sum value
|
||||||
|
/// - **Exit metadata**: ExitMeta containing ("sum", ValueId(15)) binding
|
||||||
/// - **Caller responsibility**: Create JoinInlineBoundary to map ValueIds
|
/// - **Caller responsibility**: Create JoinInlineBoundary to map ValueIds
|
||||||
pub fn lower_loop_with_continue_minimal(_scope: LoopScopeShape) -> Option<JoinModule> {
|
pub fn lower_loop_with_continue_minimal(_scope: LoopScopeShape) -> Option<(JoinModule, ExitMeta)> {
|
||||||
// Phase 195: Use local ValueId allocator (sequential from 0)
|
// Phase 195: Use local ValueId allocator (sequential from 0)
|
||||||
// JoinIR has NO knowledge of host ValueIds - boundary handled separately
|
// JoinIR has NO knowledge of host ValueIds - boundary handled separately
|
||||||
let mut value_counter = 0u32;
|
let mut value_counter = 0u32;
|
||||||
@ -345,5 +347,11 @@ pub fn lower_loop_with_continue_minimal(_scope: LoopScopeShape) -> Option<JoinMo
|
|||||||
eprintln!("[joinir/pattern4] Continue: Jump(loop_step) to skip iteration");
|
eprintln!("[joinir/pattern4] Continue: Jump(loop_step) to skip iteration");
|
||||||
eprintln!("[joinir/pattern4] Carriers: i, sum");
|
eprintln!("[joinir/pattern4] Carriers: i, sum");
|
||||||
|
|
||||||
Some(join_module)
|
// Phase 196: Return ExitMeta with carrier bindings
|
||||||
|
// k_exit parameter sum_exit (ValueId(15)) should be bound to host's "sum" variable
|
||||||
|
let exit_meta = ExitMeta::single("sum".to_string(), sum_exit);
|
||||||
|
|
||||||
|
eprintln!("[joinir/pattern4] ExitMeta: sum → ValueId({})", sum_exit.0);
|
||||||
|
|
||||||
|
Some((join_module, exit_meta))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
//! - `if_select.rs`: Phase 33 If/Else → Select lowering
|
//! - `if_select.rs`: Phase 33 If/Else → Select lowering
|
||||||
//! - `if_dry_runner.rs`: Phase 33-10 If lowering dry-run スキャナー(箱化版)
|
//! - `if_dry_runner.rs`: Phase 33-10 If lowering dry-run スキャナー(箱化版)
|
||||||
|
|
||||||
|
pub mod carrier_info; // Phase 196: Carrier metadata for loop lowering
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod exit_args_resolver;
|
pub mod exit_args_resolver;
|
||||||
pub mod funcscanner_append_defs;
|
pub mod funcscanner_append_defs;
|
||||||
|
|||||||
Reference in New Issue
Block a user