feat(joinir): Phase 256 P1.5-P1.7 contracts and naming SSOT
This commit is contained in:
@ -93,12 +93,32 @@ impl JoinIRConversionPipeline {
|
||||
join_module.functions.values().map(|f| f.body.len()).sum(),
|
||||
);
|
||||
|
||||
// Step 1.5: Run all pipeline contract checks (Phase 256 P1.5-DBG + P1.6)
|
||||
if let Some(boundary) = boundary {
|
||||
use super::super::merge::contract_checks::run_all_pipeline_checks;
|
||||
run_all_pipeline_checks(&join_module, boundary)?;
|
||||
}
|
||||
|
||||
// Step 2: JoinModule → MirModule conversion
|
||||
// Phase 256 P1.5: Pass boundary to bridge for ValueId remapping
|
||||
let empty_meta: JoinFuncMetaMap = BTreeMap::new();
|
||||
let mir_module = bridge_joinir_to_mir_with_meta(&join_module, &empty_meta, boundary)
|
||||
.map_err(|e| format!("[{}/pipeline] MIR conversion failed: {:?}", pattern_name, e))?;
|
||||
|
||||
// Task 3.1-2: Dump bridge output for diagnosis (dev-only)
|
||||
if crate::config::env::is_joinir_debug() {
|
||||
use crate::mir::printer::MirPrinter;
|
||||
use std::io::Write;
|
||||
|
||||
let mir_text = MirPrinter::new().print_module(&mir_module);
|
||||
if let Ok(mut file) = std::fs::File::create("/tmp/joinir_bridge_split.mir") {
|
||||
let _ = writeln!(file, "; Bridge output for {}", pattern_name);
|
||||
let _ = writeln!(file, "; JoinIR → MIR conversion (before merge)\n");
|
||||
let _ = write!(file, "{}", mir_text);
|
||||
eprintln!("[trace:bridge] Dumped bridge MIR to /tmp/joinir_bridge_split.mir");
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Log MIR stats (functions and blocks)
|
||||
trace::trace().joinir_stats(
|
||||
pattern_name,
|
||||
|
||||
@ -383,8 +383,9 @@ mod tests {
|
||||
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
|
||||
loop_invariants: vec![], // Phase 255 P2: Add missing field
|
||||
continuation_func_ids: std::collections::BTreeSet::from([
|
||||
crate::mir::join_ir::JoinFuncId::new(2),
|
||||
"k_exit".to_string(), // Phase 256 P1.7: Use String instead of JoinFuncId
|
||||
]),
|
||||
exit_reconnect_mode: crate::mir::join_ir::lowering::carrier_info::ExitReconnectMode::default(), // Phase 131 P1.5
|
||||
};
|
||||
|
||||
@ -137,8 +137,9 @@ mod tests {
|
||||
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
|
||||
loop_invariants: vec![], // Phase 255 P2: Add missing field
|
||||
continuation_func_ids: std::collections::BTreeSet::from([
|
||||
crate::mir::join_ir::JoinFuncId::new(2),
|
||||
"k_exit".to_string(), // Phase 256 P1.7: Use String instead of JoinFuncId
|
||||
]),
|
||||
exit_reconnect_mode: crate::mir::join_ir::lowering::carrier_info::ExitReconnectMode::default(), // Phase 131 P1.5
|
||||
};
|
||||
|
||||
@ -425,10 +425,13 @@ impl MirBuilder {
|
||||
vec![], // Empty carriers - only loop_var
|
||||
);
|
||||
|
||||
// Phase 255 P2: Create loop_invariants for s and ch
|
||||
// Phase 255 P2: Create loop_invariants for ch and s
|
||||
// CRITICAL: Order MUST match JoinModule loop_step params: [i, ch, s]
|
||||
// carrier_order is built as: [loop_var] + loop_invariants
|
||||
// So loop_invariants order determines param-to-PHI mapping for invariants!
|
||||
let loop_invariants = vec![
|
||||
(parts.haystack.clone(), s_host), // s: haystack
|
||||
(parts.needle.clone(), ch_host), // ch: needle
|
||||
(parts.needle.clone(), ch_host), // ch: needle (JoinIR param 1)
|
||||
(parts.haystack.clone(), s_host), // s: haystack (JoinIR param 2)
|
||||
];
|
||||
|
||||
if debug {
|
||||
@ -457,7 +460,10 @@ impl MirBuilder {
|
||||
// Loop invariants (s, ch) do NOT need exit bindings
|
||||
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
|
||||
|
||||
let k_exit_func = join_module.require_function("k_exit", "Pattern 6");
|
||||
let k_exit_func = join_module.require_function(
|
||||
crate::mir::join_ir::lowering::canonical_names::K_EXIT,
|
||||
"Pattern 6",
|
||||
);
|
||||
let join_exit_value_i = k_exit_func
|
||||
.params
|
||||
.first()
|
||||
@ -563,7 +569,7 @@ impl MirBuilder {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ast::{ASTNode, BinaryOperator, Literal, Span};
|
||||
use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span};
|
||||
|
||||
#[test]
|
||||
fn test_contains_methodcall_positive() {
|
||||
@ -620,7 +626,7 @@ mod tests {
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: Literal::Integer(1),
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
@ -639,7 +645,7 @@ mod tests {
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: Literal::Integer(1),
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
|
||||
@ -471,12 +471,15 @@ impl MirBuilder {
|
||||
)],
|
||||
);
|
||||
|
||||
// Phase 255 P2: Create loop_invariants for s, sep, result
|
||||
// Phase 255 P2: Create loop_invariants for result, s, sep
|
||||
// CRITICAL: Order MUST match JoinModule loop_step params: [i, start, result, s, sep]
|
||||
// carrier_order is built as: [loop_var (i), carriers (start)] + loop_invariants
|
||||
// So loop_invariants order must be [result, s, sep] to match param indices 2, 3, 4!
|
||||
// Phase 256 P1.5: result needs to be in BOTH loop_invariants (for initial value) AND exit_bindings (for return)
|
||||
let loop_invariants = vec![
|
||||
(parts.s_var.clone(), s_host), // s: haystack (read-only)
|
||||
(parts.sep_var.clone(), sep_host), // sep: separator (read-only)
|
||||
(parts.result_var.clone(), result_host), // result: also needs initial value for post-loop code
|
||||
(parts.result_var.clone(), result_host), // result: JoinIR param 2
|
||||
(parts.s_var.clone(), s_host), // s: JoinIR param 3 (haystack, read-only)
|
||||
(parts.sep_var.clone(), sep_host), // sep: JoinIR param 4 (separator, read-only)
|
||||
];
|
||||
|
||||
if debug {
|
||||
@ -573,6 +576,7 @@ impl MirBuilder {
|
||||
|
||||
// Step 6: Build boundary with carrier_info and loop_invariants
|
||||
// Phase 256 P1.5: Set expr_result to result_exit_param so the loop expression returns the result
|
||||
// Phase 256 P1.7: Register k_exit as continuation function for proper merging
|
||||
let boundary = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(join_inputs, host_inputs)
|
||||
.with_loop_invariants(loop_invariants) // Phase 255 P2: Add loop invariants
|
||||
@ -580,6 +584,7 @@ impl MirBuilder {
|
||||
.with_expr_result(Some(join_exit_value_result)) // Phase 256 P1.5: Loop expression returns result
|
||||
.with_loop_var_name(Some(parts.i_var.clone()))
|
||||
.with_carrier_info(carrier_info.clone()) // ✅ Key: carrier_info for multi-PHI
|
||||
.with_k_exit_continuation() // Phase 256 P1.7: Convenience API for k_exit registration
|
||||
.build();
|
||||
|
||||
if debug {
|
||||
|
||||
Reference in New Issue
Block a user