Phase 33 NORM canon test: enforce normalized dev route for P1/P2/JP mini
This commit is contained in:
@ -5,9 +5,9 @@
|
||||
//!
|
||||
//! Phase 4 Extraction: Separated from merge_joinir_mir_blocks (lines 159-194)
|
||||
|
||||
use crate::mir::MirModule;
|
||||
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
|
||||
use super::super::trace;
|
||||
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
|
||||
use crate::mir::MirModule;
|
||||
|
||||
/// Phase 1: Allocate new block IDs for ALL functions (Phase 189)
|
||||
///
|
||||
@ -25,10 +25,16 @@ pub(super) fn allocate_blocks(
|
||||
// This exit_block_id will be returned and used by instruction_rewriter and exit_phi_builder
|
||||
let exit_block_id = builder.block_gen.next();
|
||||
|
||||
eprintln!("[cf_loop/joinir/block_allocator] Phase 177-3: Allocated exit_block_id = {:?}", exit_block_id);
|
||||
eprintln!(
|
||||
"[cf_loop/joinir/block_allocator] Phase 177-3: Allocated exit_block_id = {:?}",
|
||||
exit_block_id
|
||||
);
|
||||
|
||||
// Phase 195: Use unified trace
|
||||
trace::trace().blocks("allocator", "Phase 189: Allocating block IDs for all functions");
|
||||
trace::trace().blocks(
|
||||
"allocator",
|
||||
"Phase 189: Allocating block IDs for all functions",
|
||||
);
|
||||
|
||||
// DETERMINISM FIX: Sort functions by name to ensure consistent iteration order
|
||||
let mut functions: Vec<_> = mir_module.functions.iter().collect();
|
||||
@ -50,7 +56,10 @@ pub(super) fn allocate_blocks(
|
||||
// Phase 195: Use unified trace
|
||||
trace::trace().blocks(
|
||||
"allocator",
|
||||
&format!("Block remap: {}:{:?} → {:?}", func_name, old_block_id, new_block_id),
|
||||
&format!(
|
||||
"Block remap: {}:{:?} → {:?}",
|
||||
func_name, old_block_id, new_block_id
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use super::LoopHeaderPhiInfo;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
||||
use crate::mir::join_ir::lowering::join_value_space::{LOCAL_MAX, PARAM_MAX, PARAM_MIN};
|
||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||
use super::LoopHeaderPhiInfo;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub(super) fn verify_loop_header_phis(
|
||||
@ -99,10 +99,7 @@ pub(super) fn verify_exit_line(
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub(super) fn verify_valueid_regions(
|
||||
loop_info: &LoopHeaderPhiInfo,
|
||||
boundary: &JoinInlineBoundary,
|
||||
) {
|
||||
pub(super) fn verify_valueid_regions(loop_info: &LoopHeaderPhiInfo, boundary: &JoinInlineBoundary) {
|
||||
fn region_name(id: ValueId) -> &'static str {
|
||||
if id.0 < PARAM_MIN {
|
||||
"PHI Reserved"
|
||||
|
||||
@ -81,21 +81,25 @@ impl ExitMetaCollector {
|
||||
debug: bool,
|
||||
) -> Vec<LoopExitBinding> {
|
||||
let mut bindings = Vec::new();
|
||||
let verbose = debug || crate::config::env::joinir_dev_enabled();
|
||||
let dev_on = crate::config::env::joinir_dev_enabled();
|
||||
let verbose = debug || dev_on;
|
||||
let strict = crate::config::env::joinir_strict_enabled() || dev_on;
|
||||
|
||||
if debug {
|
||||
if verbose {
|
||||
eprintln!(
|
||||
"[cf_loop/exit_line] ExitMetaCollector: Collecting {} exit values",
|
||||
"[joinir/exit-line] ExitMetaCollector: Collecting {} exit values",
|
||||
exit_meta.exit_values.len()
|
||||
);
|
||||
}
|
||||
|
||||
// Iterate over ExitMeta entries and build bindings
|
||||
for (carrier_name, join_exit_value) in &exit_meta.exit_values {
|
||||
eprintln!(
|
||||
"[cf_loop/exit_line] ExitMetaCollector DEBUG: Checking carrier '{}' in variable_map",
|
||||
carrier_name
|
||||
);
|
||||
if verbose {
|
||||
eprintln!(
|
||||
"[joinir/exit-line] checking carrier '{}' in variable_map",
|
||||
carrier_name
|
||||
);
|
||||
}
|
||||
|
||||
// Look up host slot from variable_map
|
||||
if let Some(&host_slot) = builder.variable_map.get(carrier_name) {
|
||||
@ -103,7 +107,8 @@ impl ExitMetaCollector {
|
||||
|
||||
// Phase 228-8: Look up role from carrier_info if available
|
||||
let role = if let Some(ci) = carrier_info {
|
||||
ci.carriers.iter()
|
||||
ci.carriers
|
||||
.iter()
|
||||
.find(|c| c.name == *carrier_name)
|
||||
.map(|c| c.role)
|
||||
.unwrap_or(CarrierRole::LoopState)
|
||||
@ -118,18 +123,21 @@ impl ExitMetaCollector {
|
||||
role,
|
||||
};
|
||||
|
||||
eprintln!(
|
||||
"[cf_loop/exit_line] ExitMetaCollector: Collected '{}' JoinIR {:?} → HOST {:?}, role={:?}",
|
||||
carrier_name, join_exit_value, host_slot, role
|
||||
);
|
||||
if verbose {
|
||||
eprintln!(
|
||||
"[joinir/exit-line] collected '{}' JoinIR {:?} → HOST {:?}, role={:?}",
|
||||
carrier_name, join_exit_value, host_slot, role
|
||||
);
|
||||
}
|
||||
|
||||
bindings.push(binding);
|
||||
} else {
|
||||
// Phase 228-8: Check if this is a ConditionOnly carrier
|
||||
// Phase 247-EX: Also check if this is a FromHost carrier (e.g., digit_value)
|
||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierRole, CarrierInit};
|
||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole};
|
||||
let carrier_meta = if let Some(ci) = carrier_info {
|
||||
ci.carriers.iter()
|
||||
ci.carriers
|
||||
.iter()
|
||||
.find(|c| c.name == *carrier_name)
|
||||
.map(|c| (c.role, c.init))
|
||||
} else {
|
||||
@ -147,10 +155,12 @@ impl ExitMetaCollector {
|
||||
role: CarrierRole::ConditionOnly,
|
||||
};
|
||||
|
||||
eprintln!(
|
||||
"[cf_loop/exit_line] Phase 228-8: Collected ConditionOnly carrier '{}' JoinIR {:?} (not in variable_map)",
|
||||
carrier_name, join_exit_value
|
||||
);
|
||||
if verbose {
|
||||
eprintln!(
|
||||
"[joinir/exit-line] collected ConditionOnly carrier '{}' JoinIR {:?} (not in variable_map)",
|
||||
carrier_name, join_exit_value
|
||||
);
|
||||
}
|
||||
|
||||
bindings.push(binding);
|
||||
}
|
||||
@ -164,10 +174,12 @@ impl ExitMetaCollector {
|
||||
role: CarrierRole::LoopState,
|
||||
};
|
||||
|
||||
eprintln!(
|
||||
"[cf_loop/exit_line] Phase 247-EX: Collected FromHost carrier '{}' JoinIR {:?} (not in variable_map)",
|
||||
carrier_name, join_exit_value
|
||||
);
|
||||
if verbose {
|
||||
eprintln!(
|
||||
"[joinir/exit-line] collected FromHost carrier '{}' JoinIR {:?} (not in variable_map)",
|
||||
carrier_name, join_exit_value
|
||||
);
|
||||
}
|
||||
|
||||
bindings.push(binding);
|
||||
}
|
||||
@ -180,18 +192,25 @@ impl ExitMetaCollector {
|
||||
role: CarrierRole::LoopState,
|
||||
};
|
||||
|
||||
eprintln!(
|
||||
"[cf_loop/exit_line] Phase 247-EX: Collected loop-local carrier '{}' JoinIR {:?} (no host slot)",
|
||||
carrier_name, join_exit_value
|
||||
);
|
||||
if verbose {
|
||||
eprintln!(
|
||||
"[joinir/exit-line] collected loop-local carrier '{}' JoinIR {:?} (no host slot)",
|
||||
carrier_name, join_exit_value
|
||||
);
|
||||
}
|
||||
|
||||
bindings.push(binding);
|
||||
}
|
||||
_ => {
|
||||
eprintln!(
|
||||
"[cf_loop/exit_line] ExitMetaCollector DEBUG: Carrier '{}' not in variable_map and not ConditionOnly/FromHost (skip)",
|
||||
let msg = format!(
|
||||
"[joinir/exit-line] carrier '{}' not in variable_map and not ConditionOnly/FromHost (skip)",
|
||||
carrier_name
|
||||
);
|
||||
if strict {
|
||||
panic!("{}", msg);
|
||||
} else if verbose {
|
||||
eprintln!("{}", msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -199,7 +218,7 @@ impl ExitMetaCollector {
|
||||
|
||||
if verbose {
|
||||
eprintln!(
|
||||
"[cf_loop/exit_line] ExitMetaCollector: Collected {} bindings: {:?}",
|
||||
"[joinir/exit-line] ExitMetaCollector: collected {} bindings: {:?}",
|
||||
bindings.len(),
|
||||
bindings
|
||||
);
|
||||
@ -211,7 +230,6 @@ impl ExitMetaCollector {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_empty_exit_meta() {
|
||||
|
||||
@ -48,14 +48,14 @@
|
||||
//! ```
|
||||
//! No changes to exit_line module needed!
|
||||
|
||||
pub mod reconnector;
|
||||
pub mod meta_collector;
|
||||
pub mod reconnector;
|
||||
|
||||
pub use reconnector::ExitLineReconnector;
|
||||
pub use meta_collector::ExitMetaCollector;
|
||||
pub use reconnector::ExitLineReconnector;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// Phase 33-10-Refactor-P2: ExitLineOrchestrator facade
|
||||
///
|
||||
@ -84,9 +84,10 @@ impl ExitLineOrchestrator {
|
||||
carrier_phis: &BTreeMap<String, ValueId>,
|
||||
debug: bool,
|
||||
) -> Result<(), String> {
|
||||
if debug {
|
||||
let verbose = debug || crate::config::env::joinir_dev_enabled();
|
||||
if verbose {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir/exit_line] ExitLineOrchestrator: Starting Phase 6 reconnection with {} carrier PHIs",
|
||||
"[joinir/exit-line] orchestrator start: {} carrier PHIs",
|
||||
carrier_phis.len()
|
||||
);
|
||||
}
|
||||
@ -94,8 +95,8 @@ impl ExitLineOrchestrator {
|
||||
// Phase 33-13: Delegate to ExitLineReconnector with carrier_phis
|
||||
ExitLineReconnector::reconnect(builder, boundary, carrier_phis, debug)?;
|
||||
|
||||
if debug {
|
||||
eprintln!("[cf_loop/joinir/exit_line] ExitLineOrchestrator: Phase 6 complete");
|
||||
if verbose {
|
||||
eprintln!("[joinir/exit-line] orchestrator complete");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -79,26 +79,39 @@ impl ExitLineReconnector {
|
||||
carrier_phis: &BTreeMap<String, ValueId>,
|
||||
debug: bool,
|
||||
) -> Result<(), String> {
|
||||
let strict = crate::config::env::joinir_strict_enabled();
|
||||
let dev_on = crate::config::env::joinir_dev_enabled();
|
||||
let strict = crate::config::env::joinir_strict_enabled() || dev_on;
|
||||
let verbose = debug || dev_on;
|
||||
|
||||
// Phase 177-STRUCT: Always log for debugging
|
||||
eprintln!(
|
||||
"[DEBUG-177/reconnect] ExitLineReconnector: {} exit bindings, {} carrier PHIs",
|
||||
boundary.exit_bindings.len(),
|
||||
carrier_phis.len()
|
||||
);
|
||||
if verbose {
|
||||
eprintln!(
|
||||
"[joinir/exit-line] reconnect: {} exit bindings, {} carrier PHIs",
|
||||
boundary.exit_bindings.len(),
|
||||
carrier_phis.len()
|
||||
);
|
||||
if !boundary.exit_bindings.is_empty() {
|
||||
eprintln!(
|
||||
"[joinir/exit-line] bindings {:?}",
|
||||
boundary
|
||||
.exit_bindings
|
||||
.iter()
|
||||
.map(|b| (&b.carrier_name, b.role, b.join_exit_value))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Early return for empty exit_bindings
|
||||
if boundary.exit_bindings.is_empty() {
|
||||
if debug {
|
||||
eprintln!("[cf_loop/joinir/exit_line] ExitLineReconnector: No exit bindings, skipping reconnect");
|
||||
if verbose {
|
||||
eprintln!("[joinir/exit-line] reconnect: no exit bindings, skip");
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if debug {
|
||||
if verbose {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir/exit_line] ExitLineReconnector: Reconnecting {} exit bindings with {} carrier PHIs",
|
||||
"[joinir/exit-line] reconnecting {} exit bindings with {} carrier PHIs",
|
||||
boundary.exit_bindings.len(),
|
||||
carrier_phis.len()
|
||||
);
|
||||
@ -109,9 +122,9 @@ impl ExitLineReconnector {
|
||||
// Phase 228-8: Skip ConditionOnly carriers (no variable_map update needed)
|
||||
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
||||
if binding.role == CarrierRole::ConditionOnly {
|
||||
if debug {
|
||||
if verbose {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir/exit_line] Phase 228-8: Skipping ConditionOnly carrier '{}' (no variable_map update)",
|
||||
"[joinir/exit-line] skip ConditionOnly carrier '{}' (no variable_map update)",
|
||||
binding.carrier_name
|
||||
);
|
||||
}
|
||||
@ -121,9 +134,9 @@ impl ExitLineReconnector {
|
||||
// Phase 33-13: Look up the PHI dst for this carrier
|
||||
let phi_dst = carrier_phis.get(&binding.carrier_name);
|
||||
|
||||
if debug {
|
||||
if verbose {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir/exit_line] ExitLineReconnector: Carrier '{}' → phi_dst={:?}",
|
||||
"[joinir/exit-line] carrier '{}' → phi_dst={:?}",
|
||||
binding.carrier_name, phi_dst
|
||||
);
|
||||
}
|
||||
@ -132,33 +145,34 @@ impl ExitLineReconnector {
|
||||
if let Some(&phi_value) = phi_dst {
|
||||
if let Some(var_vid) = builder.variable_map.get_mut(&binding.carrier_name) {
|
||||
// Phase 177-STRUCT: Always log for debugging
|
||||
eprintln!(
|
||||
"[DEBUG-177/reconnect] Updated variable_map['{}'] {:?} → {:?}",
|
||||
binding.carrier_name, *var_vid, phi_value
|
||||
);
|
||||
if verbose {
|
||||
eprintln!(
|
||||
"[joinir/exit-line] variable_map['{}'] {:?} → {:?}",
|
||||
binding.carrier_name, *var_vid, phi_value
|
||||
);
|
||||
}
|
||||
*var_vid = phi_value;
|
||||
} else if debug {
|
||||
} else if verbose {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir/exit_line] ExitLineReconnector WARNING: Carrier '{}' not found in variable_map",
|
||||
"[joinir/exit-line] warning: carrier '{}' not found in variable_map",
|
||||
binding.carrier_name
|
||||
);
|
||||
} else if strict {
|
||||
return Err(format!(
|
||||
"[pattern2/exit_line] Missing variable_map entry for carrier '{}' (exit reconnection)",
|
||||
"[joinir/exit-line] missing variable_map entry for carrier '{}' (exit reconnection)",
|
||||
binding.carrier_name
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if strict && binding.role != CarrierRole::ConditionOnly {
|
||||
return Err(format!(
|
||||
"[pattern2/exit_line] Missing PHI dst for carrier '{}' ({} PHIs available)",
|
||||
"[joinir/exit-line] missing PHI dst for carrier '{}' ({} PHIs available)",
|
||||
binding.carrier_name,
|
||||
carrier_phis.len()
|
||||
));
|
||||
}
|
||||
if debug {
|
||||
} else if verbose {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir/exit_line] ExitLineReconnector WARNING: No PHI dst for carrier '{}' (may be condition-only variable)",
|
||||
"[joinir/exit-line] warning: No PHI dst for carrier '{}' (may be condition-only variable)",
|
||||
binding.carrier_name
|
||||
);
|
||||
}
|
||||
@ -169,7 +183,7 @@ impl ExitLineReconnector {
|
||||
#[allow(deprecated)]
|
||||
if !boundary.host_outputs.is_empty() && debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir/exit_line] WARNING: Using deprecated host_outputs. Migrate to exit_bindings."
|
||||
"[joinir/exit-line] WARNING: Using deprecated host_outputs. Migrate to exit_bindings."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -85,7 +85,8 @@ pub(super) fn build_exit_phi(
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Created exit block: {:?} with {} carrier PHIs",
|
||||
exit_block_id, carrier_phis.len()
|
||||
exit_block_id,
|
||||
carrier_phis.len()
|
||||
);
|
||||
}
|
||||
phi_result
|
||||
|
||||
@ -12,10 +12,10 @@
|
||||
//! 4. 非対応の場合 → remapper.get_value(expr_result) を返す
|
||||
//! 5. expr_result が None → None を返す
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use crate::mir::ValueId;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
|
||||
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub struct ExprResultResolver;
|
||||
|
||||
@ -60,7 +60,10 @@ impl ExprResultResolver {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 221: Resolving expr_result {:?}, exit_bindings={:?}",
|
||||
expr_result_id,
|
||||
exit_bindings.iter().map(|b| (b.carrier_name.as_str(), b.join_exit_value)).collect::<Vec<_>>()
|
||||
exit_bindings
|
||||
.iter()
|
||||
.map(|b| (b.carrier_name.as_str(), b.join_exit_value))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
@ -114,13 +117,7 @@ mod tests {
|
||||
let carrier_phis = BTreeMap::new();
|
||||
let remapper = JoinIrIdRemapper::new();
|
||||
|
||||
let result = ExprResultResolver::resolve(
|
||||
None,
|
||||
&[],
|
||||
&carrier_phis,
|
||||
&remapper,
|
||||
false,
|
||||
);
|
||||
let result = ExprResultResolver::resolve(None, &[], &carrier_phis, &remapper, false);
|
||||
|
||||
assert_eq!(result.unwrap(), None);
|
||||
}
|
||||
@ -156,13 +153,8 @@ mod tests {
|
||||
let mut remapper = JoinIrIdRemapper::new();
|
||||
remapper.set_value(ValueId(42), ValueId(200));
|
||||
|
||||
let result = ExprResultResolver::resolve(
|
||||
Some(ValueId(42)),
|
||||
&[],
|
||||
&carrier_phis,
|
||||
&remapper,
|
||||
false,
|
||||
);
|
||||
let result =
|
||||
ExprResultResolver::resolve(Some(ValueId(42)), &[], &carrier_phis, &remapper, false);
|
||||
|
||||
assert_eq!(result.unwrap(), Some(ValueId(200)));
|
||||
}
|
||||
@ -172,13 +164,8 @@ mod tests {
|
||||
let carrier_phis = BTreeMap::new();
|
||||
let remapper = JoinIrIdRemapper::new();
|
||||
|
||||
let result = ExprResultResolver::resolve(
|
||||
Some(ValueId(999)),
|
||||
&[],
|
||||
&carrier_phis,
|
||||
&remapper,
|
||||
false,
|
||||
);
|
||||
let result =
|
||||
ExprResultResolver::resolve(Some(ValueId(999)), &[], &carrier_phis, &remapper, false);
|
||||
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("not found in remapper"));
|
||||
|
||||
@ -61,7 +61,8 @@ pub(super) fn merge_and_rewrite(
|
||||
let boundary_input_set: std::collections::HashSet<ValueId> = boundary
|
||||
.map(|b| b.join_inputs.iter().copied().collect())
|
||||
.unwrap_or_default();
|
||||
let strict_exit = crate::config::env::joinir_strict_enabled();
|
||||
let strict_exit =
|
||||
crate::config::env::joinir_strict_enabled() || crate::config::env::joinir_dev_enabled();
|
||||
|
||||
// Phase 189-Fix: Collect return values from JoinIR functions for exit PHI
|
||||
let mut exit_phi_inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||
@ -614,7 +615,7 @@ pub(super) fn merge_and_rewrite(
|
||||
let expected_args = carrier_info.carriers.len() + 1; // loop_var + carriers
|
||||
if remapped_args.len() < expected_args {
|
||||
let msg = format!(
|
||||
"[pattern2/exit_line] jump_args length mismatch: expected at least {} (loop_var + carriers) but got {} in block {:?}",
|
||||
"[joinir/exit-line] jump_args length mismatch: expected at least {} (loop_var + carriers) but got {} in block {:?}",
|
||||
expected_args,
|
||||
remapped_args.len(),
|
||||
old_block.id
|
||||
@ -673,7 +674,7 @@ pub(super) fn merge_and_rewrite(
|
||||
);
|
||||
} else {
|
||||
let msg = format!(
|
||||
"[pattern2/exit_line] Missing jump_args entry for carrier '{}' at index {} in block {:?}",
|
||||
"[joinir/exit-line] Missing jump_args entry for carrier '{}' at index {} in block {:?}",
|
||||
carrier.name, jump_args_idx, old_block.id
|
||||
);
|
||||
if strict_exit {
|
||||
|
||||
@ -27,8 +27,8 @@
|
||||
//! Called from merge pipeline between Phase 3 (remap_values) and Phase 4
|
||||
//! (instruction_rewriter).
|
||||
|
||||
use crate::mir::{MirInstruction, ValueId, BasicBlockId};
|
||||
use super::loop_header_phi_info::{LoopHeaderPhiInfo, CarrierPhiEntry};
|
||||
use super::loop_header_phi_info::{CarrierPhiEntry, LoopHeaderPhiInfo};
|
||||
use crate::mir::{BasicBlockId, MirInstruction, ValueId};
|
||||
|
||||
/// Builder for loop header PHIs
|
||||
///
|
||||
@ -64,7 +64,12 @@ impl LoopHeaderPhiBuilder {
|
||||
entry_block: BasicBlockId,
|
||||
loop_var_name: &str,
|
||||
loop_var_init: ValueId,
|
||||
carriers: &[(String, ValueId, crate::mir::join_ir::lowering::carrier_info::CarrierInit, crate::mir::join_ir::lowering::carrier_info::CarrierRole)], // Phase 228: Added CarrierInit and CarrierRole
|
||||
carriers: &[(
|
||||
String,
|
||||
ValueId,
|
||||
crate::mir::join_ir::lowering::carrier_info::CarrierInit,
|
||||
crate::mir::join_ir::lowering::carrier_info::CarrierRole,
|
||||
)], // Phase 228: Added CarrierInit and CarrierRole
|
||||
expr_result_is_loop_var: bool,
|
||||
debug: bool,
|
||||
) -> Result<LoopHeaderPhiInfo, String> {
|
||||
@ -188,6 +193,7 @@ impl LoopHeaderPhiBuilder {
|
||||
info: &LoopHeaderPhiInfo,
|
||||
debug: bool,
|
||||
) -> Result<(), String> {
|
||||
let dev_debug = debug || crate::config::env::joinir_dev_enabled();
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 33-16: Finalizing header PHIs at {:?}",
|
||||
@ -206,16 +212,20 @@ impl LoopHeaderPhiBuilder {
|
||||
}
|
||||
|
||||
// Get the header block from current function
|
||||
let current_func = builder.current_function.as_mut().ok_or(
|
||||
"Phase 33-16: No current function when finalizing header PHIs"
|
||||
)?;
|
||||
let current_func = builder
|
||||
.current_function
|
||||
.as_mut()
|
||||
.ok_or("Phase 33-16: No current function when finalizing header PHIs")?;
|
||||
|
||||
let header_block = current_func.blocks.get_mut(&info.header_block).ok_or_else(|| {
|
||||
format!(
|
||||
"Phase 33-16: Header block {:?} not found in current function",
|
||||
info.header_block
|
||||
)
|
||||
})?;
|
||||
let header_block = current_func
|
||||
.blocks
|
||||
.get_mut(&info.header_block)
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Phase 33-16: Header block {:?} not found in current function",
|
||||
info.header_block
|
||||
)
|
||||
})?;
|
||||
|
||||
// Insert PHIs at the beginning of the header block (before other instructions)
|
||||
// Sorted by carrier name for determinism
|
||||
@ -233,11 +243,11 @@ impl LoopHeaderPhiBuilder {
|
||||
|
||||
phi_instructions.push(phi);
|
||||
|
||||
if debug {
|
||||
if dev_debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Finalized carrier '{}' PHI: {:?} = phi [({:?}, {:?}), ({:?}, {:?})]",
|
||||
name, entry.phi_dst, entry_block, entry_val, latch_block, latch_val
|
||||
);
|
||||
"[joinir/header-phi] Finalized carrier '{}' PHI: {:?} = phi [({:?}, {:?}), ({:?}, {:?})]",
|
||||
name, entry.phi_dst, entry_block, entry_val, latch_block, latch_val
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -253,9 +263,9 @@ impl LoopHeaderPhiBuilder {
|
||||
new_spans.append(&mut header_block.instruction_spans);
|
||||
header_block.instruction_spans = new_spans;
|
||||
|
||||
if debug {
|
||||
if dev_debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Header block now has {} instructions",
|
||||
"[joinir/header-phi] Header block now has {} instructions",
|
||||
header_block.instructions.len()
|
||||
);
|
||||
}
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
//! - exit_phi_builder: to reference current loop values
|
||||
//! - ExitLineReconnector: to update variable_map with final values
|
||||
|
||||
use crate::mir::{BasicBlockId, ValueId};
|
||||
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
||||
use crate::mir::{BasicBlockId, ValueId};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// Information about loop header PHIs
|
||||
@ -104,7 +104,9 @@ impl LoopHeaderPhiInfo {
|
||||
/// Check if all carriers have latch incoming set
|
||||
#[allow(dead_code)]
|
||||
pub fn all_latch_set(&self) -> bool {
|
||||
self.carrier_phis.values().all(|e| e.latch_incoming.is_some())
|
||||
self.carrier_phis
|
||||
.values()
|
||||
.all(|e| e.latch_incoming.is_some())
|
||||
}
|
||||
|
||||
/// Phase 201-A: Get reserved ValueIds (PHI dsts that must not be overwritten)
|
||||
|
||||
@ -38,7 +38,12 @@ impl MergeResult {
|
||||
|
||||
/// Add a carrier input
|
||||
#[allow(dead_code)]
|
||||
pub fn add_carrier_input(&mut self, carrier_name: String, from_block: BasicBlockId, value: ValueId) {
|
||||
pub fn add_carrier_input(
|
||||
&mut self,
|
||||
carrier_name: String,
|
||||
from_block: BasicBlockId,
|
||||
value: ValueId,
|
||||
) {
|
||||
self.carrier_inputs
|
||||
.entry(carrier_name)
|
||||
.or_insert_with(Vec::new)
|
||||
|
||||
@ -13,24 +13,24 @@
|
||||
//! Phase 4 Refactoring: Breaking down 714-line merge_joinir_mir_blocks() into focused modules
|
||||
|
||||
mod block_allocator;
|
||||
mod value_collector;
|
||||
mod instruction_rewriter;
|
||||
mod exit_phi_builder;
|
||||
pub mod exit_line;
|
||||
mod loop_header_phi_info;
|
||||
mod loop_header_phi_builder;
|
||||
mod tail_call_classifier;
|
||||
mod merge_result;
|
||||
mod expr_result_resolver;
|
||||
#[cfg(debug_assertions)]
|
||||
mod contract_checks;
|
||||
pub mod exit_line;
|
||||
mod exit_phi_builder;
|
||||
mod expr_result_resolver;
|
||||
mod instruction_rewriter;
|
||||
mod loop_header_phi_builder;
|
||||
mod loop_header_phi_info;
|
||||
mod merge_result;
|
||||
mod tail_call_classifier;
|
||||
mod value_collector;
|
||||
|
||||
// Phase 33-17: Re-export for use by other modules
|
||||
pub use loop_header_phi_info::LoopHeaderPhiInfo;
|
||||
pub use loop_header_phi_builder::LoopHeaderPhiBuilder;
|
||||
pub use loop_header_phi_info::LoopHeaderPhiInfo;
|
||||
|
||||
use crate::mir::{MirModule, ValueId};
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
||||
use crate::mir::{MirModule, ValueId};
|
||||
|
||||
/// Phase 49-3.2: Merge JoinIR-generated MIR blocks into current_function
|
||||
///
|
||||
@ -100,7 +100,12 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
let cond_summary: Vec<String> = boundary
|
||||
.condition_bindings
|
||||
.iter()
|
||||
.map(|b| format!("{}: host {:?} → join {:?}", b.name, b.host_value, b.join_value))
|
||||
.map(|b| {
|
||||
format!(
|
||||
"{}: host {:?} → join {:?}",
|
||||
b.name, b.host_value, b.join_value
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
eprintln!(
|
||||
@ -123,8 +128,7 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
let carriers: Vec<String> = ci.carriers.iter().map(|c| c.name.clone()).collect();
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Boundary carrier_info: loop_var='{}', carriers={:?}",
|
||||
ci.loop_var_name,
|
||||
carriers
|
||||
ci.loop_var_name, carriers
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@ -134,7 +138,8 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
|
||||
// Phase 1: Allocate block IDs for all functions
|
||||
// Phase 177-3: block_allocator now returns exit_block_id to avoid conflicts
|
||||
let (mut remapper, exit_block_id) = block_allocator::allocate_blocks(builder, mir_module, debug)?;
|
||||
let (mut remapper, exit_block_id) =
|
||||
block_allocator::allocate_blocks(builder, mir_module, debug)?;
|
||||
|
||||
// Phase 2: Collect values from all functions
|
||||
let (mut used_values, value_to_func_name, function_params) =
|
||||
@ -181,35 +186,55 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
.ok_or("JoinIR module has no functions (Phase 201-A)")?;
|
||||
let entry_block_remapped = remapper
|
||||
.get_block(entry_func_name, entry_func.entry_block)
|
||||
.ok_or_else(|| format!("Entry block not found for {} (Phase 201-A)", entry_func_name))?;
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Entry block not found for {} (Phase 201-A)",
|
||||
entry_func_name
|
||||
)
|
||||
})?;
|
||||
|
||||
// Get host's current block as the entry edge
|
||||
let host_entry_block = builder.current_block.ok_or(
|
||||
"Phase 201-A: No current block when building header PHIs"
|
||||
)?;
|
||||
let host_entry_block = builder
|
||||
.current_block
|
||||
.ok_or("Phase 201-A: No current block when building header PHIs")?;
|
||||
|
||||
// Get loop variable's initial value from HOST
|
||||
let loop_var_init = boundary.host_inputs.first().copied().ok_or(
|
||||
"Phase 201-A: No host_inputs in boundary for loop_var_init"
|
||||
)?;
|
||||
let loop_var_init = boundary
|
||||
.host_inputs
|
||||
.first()
|
||||
.copied()
|
||||
.ok_or("Phase 201-A: No host_inputs in boundary for loop_var_init")?;
|
||||
|
||||
// Phase 228-4: Extract carriers with their initialization strategy
|
||||
let other_carriers: Vec<(String, ValueId, crate::mir::join_ir::lowering::carrier_info::CarrierInit, crate::mir::join_ir::lowering::carrier_info::CarrierRole)> =
|
||||
if let Some(ref carrier_info) = boundary.carrier_info {
|
||||
// Use carrier_info if available (Phase 228)
|
||||
carrier_info.carriers
|
||||
.iter()
|
||||
.filter(|c| c.name != *loop_var_name)
|
||||
.map(|c| (c.name.clone(), c.host_id, c.init, c.role))
|
||||
.collect()
|
||||
} else {
|
||||
// Fallback: exit_bindings から取得(既存動作)
|
||||
boundary.exit_bindings
|
||||
.iter()
|
||||
.filter(|b| b.carrier_name != *loop_var_name)
|
||||
.map(|b| (b.carrier_name.clone(), b.host_slot, crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState))
|
||||
.collect()
|
||||
};
|
||||
let other_carriers: Vec<(
|
||||
String,
|
||||
ValueId,
|
||||
crate::mir::join_ir::lowering::carrier_info::CarrierInit,
|
||||
crate::mir::join_ir::lowering::carrier_info::CarrierRole,
|
||||
)> = if let Some(ref carrier_info) = boundary.carrier_info {
|
||||
// Use carrier_info if available (Phase 228)
|
||||
carrier_info
|
||||
.carriers
|
||||
.iter()
|
||||
.filter(|c| c.name != *loop_var_name)
|
||||
.map(|c| (c.name.clone(), c.host_id, c.init, c.role))
|
||||
.collect()
|
||||
} else {
|
||||
// Fallback: exit_bindings から取得(既存動作)
|
||||
boundary
|
||||
.exit_bindings
|
||||
.iter()
|
||||
.filter(|b| b.carrier_name != *loop_var_name)
|
||||
.map(|b| {
|
||||
(
|
||||
b.carrier_name.clone(),
|
||||
b.host_slot,
|
||||
crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost,
|
||||
crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
if debug {
|
||||
eprintln!(
|
||||
@ -219,7 +244,10 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] loop_var_init={:?}, carriers={:?}",
|
||||
loop_var_init,
|
||||
other_carriers.iter().map(|(n, _, _, _)| n.as_str()).collect::<Vec<_>>()
|
||||
other_carriers
|
||||
.iter()
|
||||
.map(|(n, _, _, _)| n.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
@ -235,33 +263,53 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
debug,
|
||||
)?
|
||||
} else {
|
||||
LoopHeaderPhiInfo::empty(remapper.get_block(
|
||||
mir_module.functions.iter().next().unwrap().0,
|
||||
mir_module.functions.iter().next().unwrap().1.entry_block
|
||||
).unwrap())
|
||||
LoopHeaderPhiInfo::empty(
|
||||
remapper
|
||||
.get_block(
|
||||
mir_module.functions.iter().next().unwrap().0,
|
||||
mir_module.functions.iter().next().unwrap().1.entry_block,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
LoopHeaderPhiInfo::empty(remapper.get_block(
|
||||
mir_module.functions.iter().next().unwrap().0,
|
||||
mir_module.functions.iter().next().unwrap().1.entry_block
|
||||
).unwrap())
|
||||
LoopHeaderPhiInfo::empty(
|
||||
remapper
|
||||
.get_block(
|
||||
mir_module.functions.iter().next().unwrap().0,
|
||||
mir_module.functions.iter().next().unwrap().1.entry_block,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
};
|
||||
|
||||
// Phase 201-A: Get reserved PHI dst ValueIds and set in MirBuilder
|
||||
let reserved_phi_dsts = loop_header_phi_info.reserved_value_ids();
|
||||
if debug && !reserved_phi_dsts.is_empty() {
|
||||
eprintln!("[cf_loop/joinir] Phase 201-A: Reserved PHI dsts: {:?}", reserved_phi_dsts);
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 201-A: Reserved PHI dsts: {:?}",
|
||||
reserved_phi_dsts
|
||||
);
|
||||
}
|
||||
|
||||
// Phase 201-A: Set reserved IDs in MirBuilder so next_value_id() skips them
|
||||
// This protects against carrier corruption when break conditions emit Const instructions
|
||||
builder.reserved_value_ids = reserved_phi_dsts.clone();
|
||||
if debug && !builder.reserved_value_ids.is_empty() {
|
||||
eprintln!("[cf_loop/joinir] Phase 201-A: Set builder.reserved_value_ids = {:?}", builder.reserved_value_ids);
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 201-A: Set builder.reserved_value_ids = {:?}",
|
||||
builder.reserved_value_ids
|
||||
);
|
||||
}
|
||||
|
||||
// Phase 3: Remap ValueIds (with reserved PHI dsts protection)
|
||||
remap_values(builder, &used_values, &mut remapper, &reserved_phi_dsts, debug)?;
|
||||
remap_values(
|
||||
builder,
|
||||
&used_values,
|
||||
&mut remapper,
|
||||
&reserved_phi_dsts,
|
||||
debug,
|
||||
)?;
|
||||
|
||||
// Phase 177-3 DEBUG: Verify remapper state after Phase 3
|
||||
eprintln!("[DEBUG-177] === Remapper state after Phase 3 ===");
|
||||
@ -372,12 +420,18 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
eprintln!(
|
||||
"[DEBUG-177] Phase 33-21: carrier_phis count: {}, names: {:?}",
|
||||
loop_header_phi_info.carrier_phis.len(),
|
||||
loop_header_phi_info.carrier_phis.iter().map(|(n, _)| n.as_str()).collect::<Vec<_>>()
|
||||
loop_header_phi_info
|
||||
.carrier_phis
|
||||
.iter()
|
||||
.map(|(n, _)| n.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
// Map main's parameters to header PHI dsts
|
||||
// main params: [i_init, carrier1_init, ...]
|
||||
// carrier_phis: [("i", entry), ("sum", entry), ...]
|
||||
for (idx, (carrier_name, entry)) in loop_header_phi_info.carrier_phis.iter().enumerate() {
|
||||
for (idx, (carrier_name, entry)) in
|
||||
loop_header_phi_info.carrier_phis.iter().enumerate()
|
||||
{
|
||||
if let Some(&main_param) = main_params.get(idx) {
|
||||
// Phase 177-3: Don't override condition_bindings
|
||||
if condition_binding_ids.contains(&main_param) {
|
||||
@ -401,7 +455,11 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
// They appear in condition_bindings (added by Phase 176-5) but need PHI remapping.
|
||||
for (carrier_name, entry) in &loop_header_phi_info.carrier_phis {
|
||||
// Check if this carrier has a condition_binding
|
||||
if let Some(binding) = boundary.condition_bindings.iter().find(|cb| cb.name == *carrier_name) {
|
||||
if let Some(binding) = boundary
|
||||
.condition_bindings
|
||||
.iter()
|
||||
.find(|cb| cb.name == *carrier_name)
|
||||
{
|
||||
// Skip if it's a true condition-only variable (already protected above)
|
||||
if condition_binding_ids.contains(&binding.join_value) {
|
||||
continue;
|
||||
@ -451,8 +509,11 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
// Find which carrier this param belongs to by matching join_value
|
||||
// Check if this param was already handled by Phase 177-3-B
|
||||
let already_mapped = boundary.condition_bindings.iter().any(|cb| {
|
||||
cb.join_value == *loop_step_param &&
|
||||
loop_header_phi_info.carrier_phis.iter().any(|(name, _)| name == &cb.name)
|
||||
cb.join_value == *loop_step_param
|
||||
&& loop_header_phi_info
|
||||
.carrier_phis
|
||||
.iter()
|
||||
.any(|(name, _)| name == &cb.name)
|
||||
});
|
||||
if already_mapped {
|
||||
eprintln!(
|
||||
@ -467,7 +528,9 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
// generates params in exit_bindings order.
|
||||
//
|
||||
// Solution: Use carrier_order (Vec<String>) which preserves insertion order.
|
||||
if let Some(param_idx) = loop_step_params.iter().position(|p| p == loop_step_param) {
|
||||
if let Some(param_idx) =
|
||||
loop_step_params.iter().position(|p| p == loop_step_param)
|
||||
{
|
||||
// Map params[i] to carrier_order[i]
|
||||
if let (Some(carrier_name), Some(entry)) = (
|
||||
loop_header_phi_info.get_carrier_at_index(param_idx),
|
||||
@ -483,7 +546,9 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
}
|
||||
}
|
||||
|
||||
if function_params.get(main_func_name).is_none() && function_params.get(loop_step_func_name).is_none() {
|
||||
if function_params.get(main_func_name).is_none()
|
||||
&& function_params.get(loop_step_func_name).is_none()
|
||||
{
|
||||
// Fallback: Use old behavior (ValueId(0), ValueId(1), ...)
|
||||
// This handles patterns that don't have loop_step function
|
||||
if let Some(phi_dst) = loop_header_phi_info.get_carrier_phi(loop_var_name) {
|
||||
@ -570,11 +635,7 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
loop_header_phi_info.carrier_phis.len()
|
||||
);
|
||||
}
|
||||
LoopHeaderPhiBuilder::finalize(
|
||||
builder,
|
||||
&loop_header_phi_info,
|
||||
debug,
|
||||
)?;
|
||||
LoopHeaderPhiBuilder::finalize(builder, &loop_header_phi_info, debug)?;
|
||||
}
|
||||
|
||||
// Phase 5: Build exit PHI (expr result only, not carrier PHIs)
|
||||
@ -633,12 +694,18 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
let entry_block = loop_header_phi_info.header_block;
|
||||
|
||||
if debug {
|
||||
eprintln!("[cf_loop/joinir] Entry block (from loop_header_phi_info): {:?}", entry_block);
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Entry block (from loop_header_phi_info): {:?}",
|
||||
entry_block
|
||||
);
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Current block before emit_jump: {:?}",
|
||||
builder.current_block
|
||||
);
|
||||
eprintln!("[cf_loop/joinir] Jumping to entry block: {:?}", entry_block);
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Jumping to entry block: {:?}",
|
||||
entry_block
|
||||
);
|
||||
}
|
||||
|
||||
crate::mir::builder::emission::branch::emit_jump(builder, entry_block)?;
|
||||
@ -684,7 +751,10 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
// Future loops will set their own reserved IDs
|
||||
if !builder.reserved_value_ids.is_empty() {
|
||||
if debug {
|
||||
eprintln!("[cf_loop/joinir] Phase 201-A: Clearing reserved_value_ids (was {:?})", builder.reserved_value_ids);
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 201-A: Clearing reserved_value_ids (was {:?})",
|
||||
builder.reserved_value_ids
|
||||
);
|
||||
}
|
||||
builder.reserved_value_ids.clear();
|
||||
}
|
||||
@ -698,7 +768,9 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
// Check if expr_result is the loop variable
|
||||
if let Some(loop_var_name) = &b.loop_var_name {
|
||||
// Find the exit binding for the loop variable
|
||||
let loop_var_binding = b.exit_bindings.iter()
|
||||
let loop_var_binding = b
|
||||
.exit_bindings
|
||||
.iter()
|
||||
.find(|binding| binding.carrier_name == *loop_var_name);
|
||||
|
||||
if let Some(binding) = loop_var_binding {
|
||||
@ -751,7 +823,10 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
// Return expr_result if present, otherwise fall back to exit_phi_result_id
|
||||
if let Some(resolved) = expr_result_value {
|
||||
if debug {
|
||||
eprintln!("[cf_loop/joinir] Phase 246-EX-FIX: Returning expr_result_value {:?}", resolved);
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 246-EX-FIX: Returning expr_result_value {:?}",
|
||||
resolved
|
||||
);
|
||||
}
|
||||
Ok(Some(resolved))
|
||||
} else {
|
||||
@ -779,8 +854,11 @@ fn remap_values(
|
||||
debug: bool,
|
||||
) -> Result<(), String> {
|
||||
if debug {
|
||||
eprintln!("[cf_loop/joinir] Phase 3: Remapping {} ValueIds (reserved: {})",
|
||||
used_values.len(), reserved_ids.len());
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 3: Remapping {} ValueIds (reserved: {})",
|
||||
used_values.len(),
|
||||
reserved_ids.len()
|
||||
);
|
||||
}
|
||||
|
||||
for old_value in used_values {
|
||||
@ -792,7 +870,10 @@ fn remap_values(
|
||||
}
|
||||
// Skip reserved ID - will try next one
|
||||
if debug {
|
||||
eprintln!("[cf_loop/joinir] Phase 201-A: Skipping reserved PHI dst {:?}", candidate);
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 201-A: Skipping reserved PHI dst {:?}",
|
||||
candidate
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -943,7 +1024,12 @@ fn verify_phi_inputs_defined(
|
||||
});
|
||||
|
||||
for instr in &header_block_data.instructions {
|
||||
if let crate::mir::MirInstruction::Phi { dst, inputs, type_hint: _ } = instr {
|
||||
if let crate::mir::MirInstruction::Phi {
|
||||
dst,
|
||||
inputs,
|
||||
type_hint: _,
|
||||
} = instr
|
||||
{
|
||||
for (value_id, pred_block) in inputs {
|
||||
// Conservative sanity check: ValueId should not be suspiciously large
|
||||
// Phase 201 JoinValueSpace uses regions:
|
||||
|
||||
@ -68,9 +68,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_classify_loop_entry() {
|
||||
let result = classify_tail_call(
|
||||
true, // is_entry_func_entry_block
|
||||
true, // has_loop_header_phis
|
||||
true, // has_boundary
|
||||
true, // is_entry_func_entry_block
|
||||
true, // has_loop_header_phis
|
||||
true, // has_boundary
|
||||
);
|
||||
assert_eq!(result, TailCallKind::LoopEntry);
|
||||
}
|
||||
|
||||
@ -5,10 +5,10 @@
|
||||
//!
|
||||
//! Phase 4 Extraction: Separated from merge_joinir_mir_blocks (lines 202-246)
|
||||
|
||||
use crate::mir::{MirInstruction, MirModule, ValueId};
|
||||
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
|
||||
use std::collections::BTreeSet; // Phase 222.5-E: HashMap → BTreeMap for determinism
|
||||
use std::collections::BTreeMap; // Phase 222.5-E: HashMap → BTreeMap for determinism
|
||||
use crate::mir::{MirInstruction, MirModule, ValueId};
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet; // Phase 222.5-E: HashMap → BTreeMap for determinism // Phase 222.5-E: HashMap → BTreeMap for determinism
|
||||
|
||||
/// Phase 2: Collect all ValueIds used across ALL functions (Phase 189)
|
||||
///
|
||||
@ -22,15 +22,13 @@ pub(super) fn collect_values(
|
||||
) -> Result<
|
||||
(
|
||||
BTreeSet<ValueId>,
|
||||
BTreeMap<ValueId, String>, // Phase 222.5-E: HashMap → BTreeMap for determinism
|
||||
BTreeMap<String, Vec<ValueId>>, // Phase 222.5-E: HashMap → BTreeMap for determinism
|
||||
BTreeMap<ValueId, String>, // Phase 222.5-E: HashMap → BTreeMap for determinism
|
||||
BTreeMap<String, Vec<ValueId>>, // Phase 222.5-E: HashMap → BTreeMap for determinism
|
||||
),
|
||||
String,
|
||||
> {
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 189: Collecting value IDs from all functions"
|
||||
);
|
||||
eprintln!("[cf_loop/joinir] Phase 189: Collecting value IDs from all functions");
|
||||
}
|
||||
|
||||
let mut used_values: BTreeSet<ValueId> = BTreeSet::new();
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
//! - MIR block merging (merge/) ✅ Phase 4
|
||||
//! - Unified tracing (trace.rs) ✅ Phase 195
|
||||
|
||||
pub(in crate::mir::builder) mod merge;
|
||||
pub(in crate::mir::builder) mod patterns;
|
||||
pub(in crate::mir::builder) mod routing;
|
||||
pub(in crate::mir::builder) mod routing_legacy_binding;
|
||||
pub(in crate::mir::builder) mod merge;
|
||||
pub(in crate::mir::builder) mod trace;
|
||||
|
||||
@ -27,13 +27,13 @@
|
||||
//! - **Testability**: Can test conversion independently
|
||||
//! - **Reduces duplication**: Eliminates 120 lines across Pattern 1-4
|
||||
|
||||
use crate::mir::ValueId;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::join_ir::JoinModule;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
||||
use crate::mir::join_ir::JoinModule;
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub struct JoinIRConversionPipeline;
|
||||
pub(crate) struct JoinIRConversionPipeline;
|
||||
|
||||
impl JoinIRConversionPipeline {
|
||||
/// Execute unified conversion pipeline
|
||||
@ -82,41 +82,28 @@ impl JoinIRConversionPipeline {
|
||||
pattern_name: &str,
|
||||
debug: bool,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta;
|
||||
use crate::mir::join_ir::frontend::JoinFuncMetaMap;
|
||||
use super::super::trace;
|
||||
use crate::mir::join_ir::frontend::JoinFuncMetaMap;
|
||||
use crate::mir::join_ir_vm_bridge::bridge_joinir_to_mir_with_meta;
|
||||
|
||||
// Step 1: Log JoinIR stats (functions and blocks)
|
||||
trace::trace().joinir_stats(
|
||||
pattern_name,
|
||||
join_module.functions.len(),
|
||||
join_module
|
||||
.functions
|
||||
.values()
|
||||
.map(|f| f.body.len())
|
||||
.sum(),
|
||||
join_module.functions.values().map(|f| f.body.len()).sum(),
|
||||
);
|
||||
|
||||
// Step 2: JoinModule → MirModule conversion
|
||||
// Pass empty meta map since minimal lowerers don't use metadata
|
||||
let empty_meta: JoinFuncMetaMap = BTreeMap::new();
|
||||
let mir_module = convert_join_module_to_mir_with_meta(&join_module, &empty_meta)
|
||||
.map_err(|e| {
|
||||
format!(
|
||||
"[{}/pipeline] MIR conversion failed: {:?}",
|
||||
pattern_name, e
|
||||
)
|
||||
})?;
|
||||
let mir_module = bridge_joinir_to_mir_with_meta(&join_module, &empty_meta)
|
||||
.map_err(|e| format!("[{}/pipeline] MIR conversion failed: {:?}", pattern_name, e))?;
|
||||
|
||||
// Step 3: Log MIR stats (functions and blocks)
|
||||
trace::trace().joinir_stats(
|
||||
pattern_name,
|
||||
mir_module.functions.len(),
|
||||
mir_module
|
||||
.functions
|
||||
.values()
|
||||
.map(|f| f.blocks.len())
|
||||
.sum(),
|
||||
mir_module.functions.values().map(|f| f.blocks.len()).sum(),
|
||||
);
|
||||
|
||||
// Step 4: Merge into current function
|
||||
|
||||
@ -10,17 +10,17 @@
|
||||
//!
|
||||
//! This orchestrator coordinates the three modules for a complete workflow.
|
||||
|
||||
use crate::mir::ValueId;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::{
|
||||
JoinInlineBoundary, LoopExitBinding,
|
||||
};
|
||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta};
|
||||
use crate::mir::join_ir::lowering::inline_boundary::{JoinInlineBoundary, LoopExitBinding};
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
|
||||
// Phase 222.5-C: Import modular components
|
||||
use super::exit_binding_validator::validate_exit_binding;
|
||||
use super::exit_binding_applicator::{
|
||||
apply_exit_bindings_to_boundary, create_loop_var_exit_binding,
|
||||
};
|
||||
use super::exit_binding_constructor::build_loop_exit_bindings;
|
||||
use super::exit_binding_applicator::{apply_exit_bindings_to_boundary, create_loop_var_exit_binding};
|
||||
use super::exit_binding_validator::validate_exit_binding;
|
||||
|
||||
/// Builder for generating loop exit bindings
|
||||
///
|
||||
@ -29,7 +29,7 @@ use super::exit_binding_applicator::{apply_exit_bindings_to_boundary, create_loo
|
||||
///
|
||||
/// Eliminates hardcoded variable names and ValueId plumbing scattered across lowerers.
|
||||
#[allow(dead_code)]
|
||||
pub struct ExitBindingBuilder<'a> {
|
||||
pub(crate) struct ExitBindingBuilder<'a> {
|
||||
carrier_info: &'a CarrierInfo,
|
||||
exit_meta: &'a ExitMeta,
|
||||
variable_map: &'a mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
@ -60,7 +60,7 @@ impl<'a> ExitBindingBuilder<'a> {
|
||||
///
|
||||
/// ExitBindingBuilder instance, or error if metadata is inconsistent
|
||||
#[allow(dead_code)]
|
||||
pub fn new(
|
||||
pub(crate) fn new(
|
||||
carrier_info: &'a CarrierInfo,
|
||||
exit_meta: &'a ExitMeta,
|
||||
variable_map: &'a mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
@ -86,7 +86,7 @@ impl<'a> ExitBindingBuilder<'a> {
|
||||
///
|
||||
/// Vec of LoopExitBinding, one per carrier, sorted by carrier name
|
||||
#[allow(dead_code)]
|
||||
pub fn build_loop_exit_bindings(&mut self) -> Result<Vec<LoopExitBinding>, String> {
|
||||
pub(crate) fn build_loop_exit_bindings(&mut self) -> Result<Vec<LoopExitBinding>, String> {
|
||||
// Phase 222.5-C: Delegate to constructor module
|
||||
build_loop_exit_bindings(self.carrier_info, self.exit_meta, self.variable_map)
|
||||
}
|
||||
@ -106,7 +106,7 @@ impl<'a> ExitBindingBuilder<'a> {
|
||||
///
|
||||
/// Success or error if boundary cannot be updated
|
||||
#[allow(dead_code)]
|
||||
pub fn apply_to_boundary(&self, boundary: &mut JoinInlineBoundary) -> Result<(), String> {
|
||||
pub(crate) fn apply_to_boundary(&self, boundary: &mut JoinInlineBoundary) -> Result<(), String> {
|
||||
// Phase 222.5-C: Delegate to applicator module
|
||||
apply_exit_bindings_to_boundary(
|
||||
self.carrier_info,
|
||||
@ -160,7 +160,9 @@ mod tests {
|
||||
let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map)
|
||||
.expect("Failed to create builder");
|
||||
|
||||
let bindings = builder.build_loop_exit_bindings().expect("Failed to build bindings");
|
||||
let bindings = builder
|
||||
.build_loop_exit_bindings()
|
||||
.expect("Failed to build bindings");
|
||||
|
||||
assert_eq!(bindings.len(), 1);
|
||||
assert_eq!(bindings[0].carrier_name, "sum");
|
||||
@ -213,7 +215,9 @@ mod tests {
|
||||
let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map)
|
||||
.expect("Failed to create builder");
|
||||
|
||||
let bindings = builder.build_loop_exit_bindings().expect("Failed to build bindings");
|
||||
let bindings = builder
|
||||
.build_loop_exit_bindings()
|
||||
.expect("Failed to build bindings");
|
||||
|
||||
assert_eq!(bindings.len(), 2);
|
||||
// Bindings should be sorted by carrier name
|
||||
@ -345,24 +349,27 @@ mod tests {
|
||||
let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map)
|
||||
.expect("Failed to create builder");
|
||||
|
||||
let _ = builder.build_loop_exit_bindings().expect("Failed to build bindings");
|
||||
let _ = builder
|
||||
.build_loop_exit_bindings()
|
||||
.expect("Failed to build bindings");
|
||||
|
||||
let mut boundary = JoinInlineBoundary {
|
||||
host_inputs: vec![],
|
||||
join_inputs: vec![],
|
||||
exit_bindings: vec![], // Phase 171: Add missing field
|
||||
exit_bindings: vec![], // Phase 171: Add missing field
|
||||
#[allow(deprecated)]
|
||||
host_outputs: vec![], // legacy, unused in new assertions
|
||||
host_outputs: vec![], // legacy, unused in new assertions
|
||||
join_outputs: vec![],
|
||||
#[allow(deprecated)]
|
||||
condition_inputs: vec![], // Phase 171: Add missing field
|
||||
condition_bindings: vec![], // Phase 171-fix: Add missing field
|
||||
expr_result: None, // Phase 33-14: Add missing field
|
||||
loop_var_name: None, // Phase 33-16: Add missing field
|
||||
carrier_info: None, // Phase 228: Add missing field
|
||||
condition_inputs: vec![], // Phase 171: Add missing field
|
||||
condition_bindings: vec![], // Phase 171-fix: Add missing field
|
||||
expr_result: None, // Phase 33-14: Add missing field
|
||||
loop_var_name: None, // Phase 33-16: Add missing field
|
||||
carrier_info: None, // Phase 228: Add missing field
|
||||
};
|
||||
|
||||
builder.apply_to_boundary(&mut boundary)
|
||||
builder
|
||||
.apply_to_boundary(&mut boundary)
|
||||
.expect("Failed to apply to boundary");
|
||||
|
||||
// Should have loop_var + sum carrier in exit_bindings
|
||||
|
||||
@ -38,7 +38,7 @@ use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||
use crate::mir::BasicBlockId;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
pub struct LoopScopeShapeBuilder;
|
||||
pub(crate) struct LoopScopeShapeBuilder;
|
||||
|
||||
impl LoopScopeShapeBuilder {
|
||||
/// Create LoopScopeShape with empty body_locals
|
||||
|
||||
@ -46,20 +46,20 @@ pub(in crate::mir::builder) mod common_init;
|
||||
pub(in crate::mir::builder) mod condition_env_builder;
|
||||
pub(in crate::mir::builder) mod conversion_pipeline;
|
||||
pub(in crate::mir::builder) mod exit_binding;
|
||||
pub(in crate::mir::builder) mod exit_binding_validator; // Phase 222.5-C
|
||||
pub(in crate::mir::builder) mod exit_binding_constructor; // Phase 222.5-C
|
||||
pub(in crate::mir::builder) mod exit_binding_applicator; // Phase 222.5-C
|
||||
pub(in crate::mir::builder) mod exit_binding_applicator; // Phase 222.5-C
|
||||
pub(in crate::mir::builder) mod exit_binding_constructor; // Phase 222.5-C
|
||||
pub(in crate::mir::builder) mod exit_binding_validator; // Phase 222.5-C
|
||||
pub(in crate::mir::builder) mod loop_scope_shape_builder;
|
||||
pub(in crate::mir::builder) mod pattern_pipeline;
|
||||
pub(in crate::mir::builder) mod pattern1_minimal;
|
||||
pub(in crate::mir::builder) mod pattern2_with_break;
|
||||
pub(in crate::mir::builder) mod pattern3_with_if_phi;
|
||||
pub(in crate::mir::builder) mod pattern4_carrier_analyzer;
|
||||
pub(in crate::mir::builder) mod pattern4_with_continue;
|
||||
pub(in crate::mir::builder) mod pattern_pipeline;
|
||||
pub(in crate::mir::builder) mod router;
|
||||
pub(in crate::mir::builder) mod trim_loop_lowering; // Phase 180: Dedicated Trim/P5 lowering module
|
||||
pub(in crate::mir::builder) mod trim_pattern_validator;
|
||||
pub(in crate::mir::builder) mod trim_pattern_lowerer;
|
||||
pub(in crate::mir::builder) mod trim_pattern_validator;
|
||||
|
||||
// Re-export router for convenience
|
||||
pub(in crate::mir::builder) use router::{route_loop_pattern, LoopPatternContext};
|
||||
|
||||
@ -5,10 +5,10 @@
|
||||
//! - Trim break condition generation
|
||||
//! - Carrier binding setup in ConditionEnv
|
||||
|
||||
use crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper;
|
||||
use crate::ast::{ASTNode, Span, UnaryOperator};
|
||||
use crate::mir::join_ir::lowering::condition_env::ConditionBinding;
|
||||
use crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper;
|
||||
use crate::mir::ValueId;
|
||||
use crate::ast::{ASTNode, UnaryOperator, Span};
|
||||
|
||||
pub(in crate::mir::builder::control_flow::joinir::patterns) struct TrimPatternLowerer;
|
||||
|
||||
@ -61,8 +61,12 @@ impl TrimPatternLowerer {
|
||||
let carrier_name = &trim_helper.carrier_name;
|
||||
|
||||
// Get host ValueId for carrier
|
||||
let host_value_id = get_host_value(carrier_name)
|
||||
.ok_or_else(|| format!("[TrimPatternLowerer] Carrier '{}' not in variable_map", carrier_name))?;
|
||||
let host_value_id = get_host_value(carrier_name).ok_or_else(|| {
|
||||
format!(
|
||||
"[TrimPatternLowerer] Carrier '{}' not in variable_map",
|
||||
carrier_name
|
||||
)
|
||||
})?;
|
||||
|
||||
// Allocate JoinIR ValueId
|
||||
let joinir_value_id = alloc_join_value();
|
||||
@ -94,7 +98,8 @@ impl TrimPatternLowerer {
|
||||
insert_to_env: impl FnOnce(String, ValueId),
|
||||
alloc_join_value: &mut dyn FnMut() -> ValueId,
|
||||
) -> Result<ConditionBinding, String> {
|
||||
let binding = Self::setup_trim_carrier_binding(trim_helper, get_host_value, alloc_join_value)?;
|
||||
let binding =
|
||||
Self::setup_trim_carrier_binding(trim_helper, get_host_value, alloc_join_value)?;
|
||||
|
||||
// Insert into env
|
||||
insert_to_env(binding.name.clone(), binding.join_value);
|
||||
@ -122,7 +127,9 @@ mod tests {
|
||||
|
||||
// Should be UnaryOp::Not
|
||||
match result {
|
||||
ASTNode::UnaryOp { operator, operand, .. } => {
|
||||
ASTNode::UnaryOp {
|
||||
operator, operand, ..
|
||||
} => {
|
||||
assert_eq!(operator, UnaryOperator::Not);
|
||||
// Operand should be Variable with carrier name
|
||||
match *operand {
|
||||
@ -151,11 +158,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let get_value = |name: &str| variable_map.get(name).copied();
|
||||
let result = TrimPatternLowerer::setup_trim_carrier_binding(
|
||||
&helper,
|
||||
get_value,
|
||||
&mut alloc,
|
||||
);
|
||||
let result = TrimPatternLowerer::setup_trim_carrier_binding(&helper, get_value, &mut alloc);
|
||||
|
||||
assert!(result.is_ok());
|
||||
let binding = result.unwrap();
|
||||
@ -178,11 +181,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let get_value = |name: &str| variable_map.get(name).copied();
|
||||
let result = TrimPatternLowerer::setup_trim_carrier_binding(
|
||||
&helper,
|
||||
get_value,
|
||||
&mut alloc,
|
||||
);
|
||||
let result = TrimPatternLowerer::setup_trim_carrier_binding(&helper, get_value, &mut alloc);
|
||||
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("not in variable_map"));
|
||||
@ -209,12 +208,8 @@ mod tests {
|
||||
env.insert(name, value);
|
||||
};
|
||||
|
||||
let result = TrimPatternLowerer::add_to_condition_env(
|
||||
&helper,
|
||||
get_value,
|
||||
insert,
|
||||
&mut alloc,
|
||||
);
|
||||
let result =
|
||||
TrimPatternLowerer::add_to_condition_env(&helper, get_value, insert, &mut alloc);
|
||||
|
||||
assert!(result.is_ok());
|
||||
let binding = result.unwrap();
|
||||
|
||||
@ -31,10 +31,10 @@ impl TrimPatternValidator {
|
||||
ch_value: ValueId,
|
||||
whitespace_chars: &[String],
|
||||
) -> Result<ValueId, String> {
|
||||
use crate::mir::builder::emission::constant::emit_string;
|
||||
use crate::mir::builder::emission::compare::emit_eq_to;
|
||||
use crate::mir::types::BinaryOp;
|
||||
use crate::mir::builder::emission::constant::emit_string;
|
||||
use crate::mir::instruction::MirInstruction;
|
||||
use crate::mir::types::BinaryOp;
|
||||
|
||||
if whitespace_chars.is_empty() {
|
||||
return Err("[emit_whitespace_check] Empty whitespace_chars".to_string());
|
||||
@ -89,18 +89,32 @@ impl TrimPatternValidator {
|
||||
) -> Option<(String, Box<ASTNode>)> {
|
||||
for stmt in loop_body {
|
||||
// Look for: local ch = ...
|
||||
if let ASTNode::Local { variables, initial_values, .. } = stmt {
|
||||
if let ASTNode::Local {
|
||||
variables,
|
||||
initial_values,
|
||||
..
|
||||
} = stmt
|
||||
{
|
||||
for (i, var) in variables.iter().enumerate() {
|
||||
if var == var_name {
|
||||
if let Some(Some(init_expr_box)) = initial_values.get(i) {
|
||||
// Check if it's a substring method call
|
||||
if let ASTNode::MethodCall { object, method, arguments, .. } = init_expr_box.as_ref() {
|
||||
if let ASTNode::MethodCall {
|
||||
object,
|
||||
method,
|
||||
arguments,
|
||||
..
|
||||
} = init_expr_box.as_ref()
|
||||
{
|
||||
if method == "substring" && arguments.len() == 2 {
|
||||
// Extract object name
|
||||
if let ASTNode::Variable { name, .. } = object.as_ref() {
|
||||
// Return object name and start expression
|
||||
// (We assume second arg is start+1, first arg is start)
|
||||
return Some((name.clone(), Box::new(arguments[0].clone())));
|
||||
return Some((
|
||||
name.clone(),
|
||||
Box::new(arguments[0].clone()),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -116,45 +130,41 @@ impl TrimPatternValidator {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ast::{LiteralValue, Span, BinaryOperator};
|
||||
use crate::ast::{BinaryOperator, LiteralValue, Span};
|
||||
|
||||
#[test]
|
||||
fn test_extract_substring_args_valid() {
|
||||
// Create: local ch = s.substring(start, start+1)
|
||||
let body = vec![
|
||||
ASTNode::Local {
|
||||
variables: vec!["ch".to_string()],
|
||||
initial_values: vec![
|
||||
Some(Box::new(ASTNode::MethodCall {
|
||||
object: Box::new(ASTNode::Variable {
|
||||
name: "s".to_string(),
|
||||
let body = vec![ASTNode::Local {
|
||||
variables: vec!["ch".to_string()],
|
||||
initial_values: vec![Some(Box::new(ASTNode::MethodCall {
|
||||
object: Box::new(ASTNode::Variable {
|
||||
name: "s".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
method: "substring".to_string(),
|
||||
arguments: vec![
|
||||
ASTNode::Variable {
|
||||
name: "start".to_string(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "start".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
method: "substring".to_string(),
|
||||
arguments: vec![
|
||||
ASTNode::Variable {
|
||||
name: "start".to_string(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "start".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
],
|
||||
span: Span::unknown(),
|
||||
})),
|
||||
},
|
||||
],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
];
|
||||
}))],
|
||||
span: Span::unknown(),
|
||||
}];
|
||||
|
||||
let result = TrimPatternValidator::extract_substring_args(&body, "ch");
|
||||
assert!(result.is_some());
|
||||
@ -173,32 +183,28 @@ mod tests {
|
||||
#[test]
|
||||
fn test_extract_substring_args_wrong_var() {
|
||||
// local other_var = s.substring(0, 1)
|
||||
let body = vec![
|
||||
ASTNode::Local {
|
||||
variables: vec!["other_var".to_string()],
|
||||
initial_values: vec![
|
||||
Some(Box::new(ASTNode::MethodCall {
|
||||
object: Box::new(ASTNode::Variable {
|
||||
name: "s".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
method: "substring".to_string(),
|
||||
arguments: vec![
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(0),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
],
|
||||
let body = vec![ASTNode::Local {
|
||||
variables: vec!["other_var".to_string()],
|
||||
initial_values: vec![Some(Box::new(ASTNode::MethodCall {
|
||||
object: Box::new(ASTNode::Variable {
|
||||
name: "s".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
method: "substring".to_string(),
|
||||
arguments: vec![
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(0),
|
||||
span: Span::unknown(),
|
||||
})),
|
||||
},
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
];
|
||||
}))],
|
||||
span: Span::unknown(),
|
||||
}];
|
||||
|
||||
let result = TrimPatternValidator::extract_substring_args(&body, "ch");
|
||||
assert!(result.is_none());
|
||||
@ -207,28 +213,22 @@ mod tests {
|
||||
#[test]
|
||||
fn test_extract_substring_args_wrong_method() {
|
||||
// local ch = s.charAt(0)
|
||||
let body = vec![
|
||||
ASTNode::Local {
|
||||
variables: vec!["ch".to_string()],
|
||||
initial_values: vec![
|
||||
Some(Box::new(ASTNode::MethodCall {
|
||||
object: Box::new(ASTNode::Variable {
|
||||
name: "s".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
method: "charAt".to_string(),
|
||||
arguments: vec![
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(0),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
],
|
||||
span: Span::unknown(),
|
||||
})),
|
||||
],
|
||||
let body = vec![ASTNode::Local {
|
||||
variables: vec!["ch".to_string()],
|
||||
initial_values: vec![Some(Box::new(ASTNode::MethodCall {
|
||||
object: Box::new(ASTNode::Variable {
|
||||
name: "s".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
method: "charAt".to_string(),
|
||||
arguments: vec![ASTNode::Literal {
|
||||
value: LiteralValue::Integer(0),
|
||||
span: Span::unknown(),
|
||||
}],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
];
|
||||
}))],
|
||||
span: Span::unknown(),
|
||||
}];
|
||||
|
||||
let result = TrimPatternValidator::extract_substring_args(&body, "ch");
|
||||
assert!(result.is_none());
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
//! JoinIR routing logic for loop lowering
|
||||
|
||||
use super::trace;
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::ValueId;
|
||||
use super::trace;
|
||||
|
||||
impl MirBuilder {
|
||||
/// Phase 49: Try JoinIR Frontend for mainline integration
|
||||
@ -42,16 +42,17 @@ impl MirBuilder {
|
||||
// Phase 196: Default to structure-first routing now that LoopBuilder is removed.
|
||||
// - Default: ON (structure_only = true) to allow JoinIR patterns to run for all loops.
|
||||
// - To revert to the previous whitelist-only behavior, set NYASH_JOINIR_STRUCTURE_ONLY=0.
|
||||
let structure_only = match std::env::var("NYASH_JOINIR_STRUCTURE_ONLY")
|
||||
.ok()
|
||||
.as_deref()
|
||||
{
|
||||
let structure_only = match std::env::var("NYASH_JOINIR_STRUCTURE_ONLY").ok().as_deref() {
|
||||
Some("0") | Some("off") => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
if structure_only {
|
||||
trace::trace().routing("router", &func_name, "Structure-only mode enabled, skipping whitelist");
|
||||
trace::trace().routing(
|
||||
"router",
|
||||
&func_name,
|
||||
"Structure-only mode enabled, skipping whitelist",
|
||||
);
|
||||
} else {
|
||||
// Phase 49-4 + Phase 80: Multi-target routing (legacy whitelist)
|
||||
// - JoinIR は常時 ON。legacy LoopBuilder は削除済み。
|
||||
@ -60,25 +61,25 @@ impl MirBuilder {
|
||||
// Phase 188: Add "main" routing for loop pattern expansion
|
||||
// Phase 170: Add JsonParserBox methods for selfhost validation
|
||||
let is_target = match func_name.as_str() {
|
||||
"main" => true, // Phase 188-Impl-1: Enable JoinIR for main function (Pattern 1)
|
||||
"JoinIrMin.main/0" => true, // Phase 188-Impl-2: Enable JoinIR for JoinIrMin.main/0 (Pattern 2)
|
||||
"main" => true, // Phase 188-Impl-1: Enable JoinIR for main function (Pattern 1)
|
||||
"JoinIrMin.main/0" => true, // Phase 188-Impl-2: Enable JoinIR for JoinIrMin.main/0 (Pattern 2)
|
||||
"JsonTokenizer.print_tokens/0" => true,
|
||||
"ArrayExtBox.filter/2" => true,
|
||||
// Phase 170-A-1: Enable JsonParserBox methods for JoinIR routing
|
||||
"JsonParserBox._trim/1" => true,
|
||||
"JsonParserBox._skip_whitespace/2" => true,
|
||||
"JsonParserBox._match_literal/3" => true, // Phase 182: Fixed arity (s, pos, literal)
|
||||
"JsonParserBox._match_literal/3" => true, // Phase 182: Fixed arity (s, pos, literal)
|
||||
"JsonParserBox._parse_string/2" => true,
|
||||
"JsonParserBox._parse_array/2" => true,
|
||||
"JsonParserBox._parse_object/2" => true,
|
||||
// Phase 182: Add simple loop methods
|
||||
"JsonParserBox._parse_number/2" => true, // P2 Break (s, pos)
|
||||
"JsonParserBox._atoi/1" => true, // P2 Break (s)
|
||||
"JsonParserBox._parse_number/2" => true, // P2 Break (s, pos)
|
||||
"JsonParserBox._atoi/1" => true, // P2 Break (s)
|
||||
// Phase 170-A-1: Test methods (simplified versions)
|
||||
"TrimTest.trim/1" => true,
|
||||
"Main.trim/1" => true, // Phase 171-fix: Main box variant
|
||||
"Main.trim_string_simple/1" => true, // Phase 33-13: Simple trim variant
|
||||
"TrimTest.main/0" => true, // Phase 170: TrimTest.main for loop pattern test
|
||||
"Main.trim/1" => true, // Phase 171-fix: Main box variant
|
||||
"Main.trim_string_simple/1" => true, // Phase 33-13: Simple trim variant
|
||||
"TrimTest.main/0" => true, // Phase 170: TrimTest.main for loop pattern test
|
||||
// Phase 173: JsonParser P5 expansion test
|
||||
"JsonParserTest._skip_whitespace/3" => true,
|
||||
"JsonParserTest.main/0" => true,
|
||||
@ -99,7 +100,11 @@ impl MirBuilder {
|
||||
// Debug log when routing through JoinIR Frontend
|
||||
// Phase 195: Check trace flags directly from JoinLoopTrace
|
||||
let debug = trace::trace().is_loopform_enabled() || trace::trace().is_mainline_enabled();
|
||||
trace::trace().routing("router", &func_name, "Routing through JoinIR Frontend mainline");
|
||||
trace::trace().routing(
|
||||
"router",
|
||||
&func_name,
|
||||
"Routing through JoinIR Frontend mainline",
|
||||
);
|
||||
|
||||
// Phase 49-3: Implement JoinIR Frontend integration
|
||||
self.cf_loop_joinir_impl(condition, body, &func_name, debug)
|
||||
@ -123,9 +128,20 @@ impl MirBuilder {
|
||||
// Phase 200-C: Pass fn_body_ast to LoopPatternContext if available
|
||||
// Clone fn_body_ast to avoid borrow checker issues
|
||||
let fn_body_clone = self.fn_body_ast.clone();
|
||||
eprintln!("[routing] fn_body_ast is {} for '{}'", if fn_body_clone.is_some() { "SOME" } else { "NONE" }, func_name);
|
||||
eprintln!(
|
||||
"[routing] fn_body_ast is {} for '{}'",
|
||||
if fn_body_clone.is_some() {
|
||||
"SOME"
|
||||
} else {
|
||||
"NONE"
|
||||
},
|
||||
func_name
|
||||
);
|
||||
let ctx = if let Some(ref fn_body) = fn_body_clone {
|
||||
eprintln!("[routing] Creating ctx with fn_body ({} nodes)", fn_body.len());
|
||||
eprintln!(
|
||||
"[routing] Creating ctx with fn_body ({} nodes)",
|
||||
fn_body.len()
|
||||
);
|
||||
LoopPatternContext::with_fn_body(condition, body, &func_name, debug, fn_body)
|
||||
} else {
|
||||
LoopPatternContext::new(condition, body, &func_name, debug)
|
||||
@ -137,7 +153,11 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
// Phase 187-2: Pattern router failed, try legacy whitelist
|
||||
trace::trace().routing("router", func_name, "Pattern router found no match, trying legacy whitelist");
|
||||
trace::trace().routing(
|
||||
"router",
|
||||
func_name,
|
||||
"Pattern router found no match, trying legacy whitelist",
|
||||
);
|
||||
|
||||
// Delegate to legacy binding path (routing_legacy_binding.rs)
|
||||
self.cf_loop_joinir_legacy_binding(condition, body, func_name, debug)
|
||||
|
||||
@ -7,10 +7,10 @@
|
||||
//! Phase 194+ uses the pattern-based router instead. This legacy path is
|
||||
//! kept for backward compatibility with existing whitelist entries.
|
||||
|
||||
use super::trace;
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::ValueId;
|
||||
use super::trace;
|
||||
|
||||
impl MirBuilder {
|
||||
/// Phase 49-3: Legacy JoinIR Frontend integration via LoopFrontendBinding
|
||||
@ -21,7 +21,7 @@ impl MirBuilder {
|
||||
/// # Pipeline
|
||||
/// 1. Build Loop AST → JSON v0 format (with "defs" array)
|
||||
/// 2. AstToJoinIrLowerer::lower_program_json() → JoinModule
|
||||
/// 3. convert_join_module_to_mir_with_meta() → MirModule
|
||||
/// 3. bridge_joinir_to_mir_with_meta() → MirModule
|
||||
/// 4. Merge MIR blocks into current_function
|
||||
pub(in crate::mir::builder) fn cf_loop_joinir_legacy_binding(
|
||||
&mut self,
|
||||
@ -32,7 +32,7 @@ impl MirBuilder {
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
use super::super::super::loop_frontend_binding::LoopFrontendBinding;
|
||||
use crate::mir::join_ir::frontend::{AstToJoinIrLowerer, JoinFuncMetaMap};
|
||||
use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta;
|
||||
use crate::mir::join_ir_vm_bridge::bridge_joinir_to_mir_with_meta;
|
||||
use crate::mir::types::ConstValue;
|
||||
use crate::mir::MirInstruction;
|
||||
use crate::r#macro::ast_json::ast_to_json;
|
||||
@ -42,7 +42,11 @@ impl MirBuilder {
|
||||
"JsonTokenizer.print_tokens/0" => LoopFrontendBinding::for_print_tokens(),
|
||||
"ArrayExtBox.filter/2" => LoopFrontendBinding::for_array_filter(),
|
||||
_ => {
|
||||
trace::trace().routing("router", func_name, "No legacy binding defined, falling back");
|
||||
trace::trace().routing(
|
||||
"router",
|
||||
func_name,
|
||||
"No legacy binding defined, falling back",
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
@ -79,7 +83,10 @@ impl MirBuilder {
|
||||
if ext_ref == "me" || ext_ref.starts_with("me.") {
|
||||
continue;
|
||||
}
|
||||
trace::trace().debug("router", &format!("Adding '{}' to params (external_ref)", ext_ref));
|
||||
trace::trace().debug(
|
||||
"router",
|
||||
&format!("Adding '{}' to params (external_ref)", ext_ref),
|
||||
);
|
||||
params.push(serde_json::json!(ext_ref));
|
||||
}
|
||||
|
||||
@ -157,15 +164,11 @@ impl MirBuilder {
|
||||
trace::trace().joinir_stats(
|
||||
"router",
|
||||
join_module.functions.len(),
|
||||
join_module
|
||||
.functions
|
||||
.values()
|
||||
.map(|f| f.body.len())
|
||||
.sum(),
|
||||
join_module.functions.values().map(|f| f.body.len()).sum(),
|
||||
);
|
||||
|
||||
// Step 4: Convert JoinModule to MIR
|
||||
let mir_module = convert_join_module_to_mir_with_meta(&join_module, &join_meta)
|
||||
let mir_module = bridge_joinir_to_mir_with_meta(&join_module, &join_meta)
|
||||
.map_err(|e| format!("JoinIR→MIR conversion failed: {}", e.message))?;
|
||||
|
||||
// Debug MIR module if trace enabled
|
||||
@ -187,7 +190,11 @@ impl MirBuilder {
|
||||
for (block_id, block) in &func.blocks {
|
||||
trace::trace().blocks(
|
||||
"router",
|
||||
&format!("Block {:?}: {} instructions", block_id, block.instructions.len()),
|
||||
&format!(
|
||||
"Block {:?}: {} instructions",
|
||||
block_id,
|
||||
block.instructions.len()
|
||||
),
|
||||
);
|
||||
for (i, inst) in block.instructions.iter().enumerate() {
|
||||
trace::trace().instructions("router", &format!("[{}] {:?}", i, inst));
|
||||
|
||||
@ -41,8 +41,8 @@
|
||||
//! NYASH_TRACE_VARMAP=1 NYASH_JOINIR_DEBUG=1 ./target/release/hakorune test.hako
|
||||
//! ```
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// Unified tracer for JoinIR loop operations.
|
||||
///
|
||||
@ -206,7 +206,10 @@ impl JoinLoopTrace {
|
||||
/// - `msg`: Human-readable message about the routing decision
|
||||
pub fn routing(&self, tag: &str, func_name: &str, msg: &str) {
|
||||
if self.joinir_enabled || self.mainline_enabled {
|
||||
eprintln!("[trace:routing] {}: function '{}' - {}", tag, func_name, msg);
|
||||
eprintln!(
|
||||
"[trace:routing] {}: function '{}' - {}",
|
||||
tag, func_name, msg
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -26,15 +26,14 @@ pub(in crate::mir::builder) fn extract_loop_variable_from_condition(
|
||||
condition: &ASTNode,
|
||||
) -> Result<String, String> {
|
||||
match condition {
|
||||
ASTNode::BinaryOp {
|
||||
operator, left, ..
|
||||
} if matches!(
|
||||
operator,
|
||||
BinaryOperator::Less
|
||||
| BinaryOperator::Greater
|
||||
| BinaryOperator::LessEqual
|
||||
| BinaryOperator::GreaterEqual
|
||||
) =>
|
||||
ASTNode::BinaryOp { operator, left, .. }
|
||||
if matches!(
|
||||
operator,
|
||||
BinaryOperator::Less
|
||||
| BinaryOperator::Greater
|
||||
| BinaryOperator::LessEqual
|
||||
| BinaryOperator::GreaterEqual
|
||||
) =>
|
||||
{
|
||||
// Binary comparison: extract variable from left side
|
||||
match &**left {
|
||||
|
||||
Reference in New Issue
Block a user