feat(joinir): Phase 92完了 - ConditionalStep + body-local変数サポート

## Phase 92全体の成果

**Phase 92 P0-P2**: ConditionalStep JoinIR生成とbody-local変数サポート
- ConditionalStep(条件付きキャリア更新)のJoinIR生成実装
- Body-local変数(ch等)の条件式での参照サポート
- 変数解決優先度: ConditionEnv → LoopBodyLocalEnv

**Phase 92 P3**: BodyLocalPolicyBox + 安全ガード
- BodyLocalPolicyDecision実装(Accept/Reject判定)
- BodyLocalSlot + DualValueRewriter(JoinIR/MIR二重書き込み)
- Fail-Fast契約(Cannot promote LoopBodyLocal検出)

**Phase 92 P4**: E2E固定+回帰最小化 (本コミット)
- Unit test 3本追加(body-local変数解決検証)
- Integration smoke追加(phase92_pattern2_baseline.sh、2ケースPASS)
- P4-E2E-PLAN.md、P4-COMPLETION.md作成

## 主要な実装

### ConditionalStep(条件付きキャリア更新)
- `conditional_step_emitter.rs`: JoinIR Select命令生成
- `loop_with_break_minimal.rs`: ConditionalStep検出と統合
- `loop_with_continue_minimal.rs`: Pattern4対応

### Body-local変数サポート
- `condition_lowerer.rs`: body-local変数解決機能
  - `lower_condition_to_joinir`: body_local_env パラメータ追加
  - 変数解決優先度実装(ConditionEnv優先)
  - Unit test 3本追加: 変数解決/優先度/エラー
- `header_break_lowering.rs`: break条件でbody-local変数参照
- 7ファイルで後方互換ラッパー(lower_condition_to_joinir_no_body_locals)

### Body-local Policy & Safety
- `body_local_policy.rs`: BodyLocalPolicyDecision(Accept/Reject)
- `body_local_slot.rs`: JoinIR/MIR二重書き込み
- `dual_value_rewriter.rs`: ValueId書き換えヘルパー

## テスト体制

### Unit Tests (+3)
- `test_body_local_variable_resolution`: body-local変数解決
- `test_variable_resolution_priority`: 変数解決優先度(ConditionEnv優先)
- `test_undefined_variable_error`: 未定義変数エラー
- 全7テストPASS(cargo test --release condition_lowerer::tests)

### Integration Smoke (+1)
- `phase92_pattern2_baseline.sh`:
  - Case A: loop_min_while.hako (Pattern2 baseline)
  - Case B: phase92_conditional_step_minimal.hako (条件付きインクリメント)
  - 両ケースPASS、integration profileで発見可能

### 退行確認
-  既存Pattern2Breakテスト正常(退行なし)
-  Phase 135 smoke正常(MIR検証PASS)

## アーキテクチャ設計

### 変数解決メカニズム
```rust
// Priority 1: ConditionEnv (loop params, captured)
if let Some(value_id) = env.get(name) { return Ok(value_id); }
// Priority 2: LoopBodyLocalEnv (body-local like `ch`)
if let Some(body_env) = body_local_env {
    if let Some(value_id) = body_env.get(name) { return Ok(value_id); }
}
```

### Fail-Fast契約
- Delta equality check (conditional_step_emitter.rs)
- Variable resolution error messages (ConditionEnv)
- Body-local promotion rejection (BodyLocalPolicyDecision::Reject)

## ドキュメント

- `P4-E2E-PLAN.md`: 3レベルテスト戦略(Level 1-2完了、Level 3延期)
- `P4-COMPLETION.md`: Phase 92完了報告
- `README.md`: Phase 92全体のまとめ

## 将来の拡張(Phase 92スコープ外)

- Body-local promotionシステム拡張
- P5bパターン認識の汎化(flagベース条件サポート)
- 完全なP5b E2Eテスト(body-local promotion実装後)

🎯 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-16 21:37:07 +09:00
parent 568619df89
commit d2972c1437
43 changed files with 2615 additions and 980 deletions

View File

@ -145,13 +145,17 @@ impl<'a> LoopProcessingContext<'a> {
return Err(msg);
} else {
// Debug mode: log only
eprintln!("{}", msg);
crate::mir::builder::control_flow::joinir::trace::trace()
.dev("loop_canonicalizer/parity", &msg);
}
} else {
// Patterns match - success!
eprintln!(
"[loop_canonicalizer/PARITY] OK: canonical and actual agree on {:?}",
canonical_pattern
crate::mir::builder::control_flow::joinir::trace::trace().dev(
"loop_canonicalizer/parity",
&format!(
"[loop_canonicalizer/PARITY] OK: canonical and actual agree on {:?}",
canonical_pattern
),
);
}

View File

@ -25,9 +25,12 @@ 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.next_block_id();
eprintln!(
"[cf_loop/joinir/block_allocator] Phase 177-3: Allocated exit_block_id = {:?}",
exit_block_id
trace::trace().dev(
"cf_loop/joinir/block_allocator",
&format!(
"Phase 177-3: Allocated exit_block_id = {:?}",
exit_block_id
),
);
// Phase 195: Use unified trace

View File

@ -53,13 +53,17 @@ pub fn init_value(
name: &str,
debug: bool,
) -> ValueId {
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
match init {
CarrierInit::FromHost => {
// Use host variable's ValueId directly (no const emission needed)
if debug {
eprintln!(
"[carrier_init_builder] '{}': FromHost -> ValueId({})",
name, host_id.0
trace.stderr_if(
&format!(
"[carrier_init_builder] '{}': FromHost -> ValueId({})",
name, host_id.0
),
true,
);
}
host_id
@ -72,9 +76,12 @@ pub fn init_value(
value: ConstValue::Bool(*val),
});
if debug {
eprintln!(
"[carrier_init_builder] '{}': BoolConst({}) -> ValueId({})",
name, val, const_id.0
trace.stderr_if(
&format!(
"[carrier_init_builder] '{}': BoolConst({}) -> ValueId({})",
name, val, const_id.0
),
true,
);
}
const_id
@ -87,9 +94,12 @@ pub fn init_value(
value: ConstValue::Integer(0),
});
if debug {
eprintln!(
"[carrier_init_builder] '{}': LoopLocalZero -> ValueId({})",
name, const_id.0
trace.stderr_if(
&format!(
"[carrier_init_builder] '{}': LoopLocalZero -> ValueId({})",
name, const_id.0
),
true,
);
}
const_id

View File

@ -84,20 +84,31 @@ impl ExitMetaCollector {
let dev_on = crate::config::env::joinir_dev_enabled();
let verbose = debug || dev_on;
let strict = crate::config::env::joinir_strict_enabled() || dev_on;
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
if verbose {
eprintln!(
"[joinir/exit-line] ExitMetaCollector: Collecting {} exit values",
exit_meta.exit_values.len()
trace.emit_if(
"exit-line",
"collector",
&format!(
"Collecting {} exit values",
exit_meta.exit_values.len()
),
true,
);
}
// Iterate over ExitMeta entries and build bindings
for (carrier_name, join_exit_value) in &exit_meta.exit_values {
if verbose {
eprintln!(
"[joinir/exit-line] checking carrier '{}' in variable_ctx.variable_map",
carrier_name
trace.emit_if(
"exit-line",
"collector",
&format!(
"checking carrier '{}' in variable_ctx.variable_map",
carrier_name
),
true,
);
}
@ -124,9 +135,14 @@ impl ExitMetaCollector {
};
if verbose {
eprintln!(
"[joinir/exit-line] collected '{}' JoinIR {:?} → HOST {:?}, role={:?}",
carrier_name, join_exit_value, host_slot, role
trace.emit_if(
"exit-line",
"collector",
&format!(
"collected '{}' JoinIR {:?} → HOST {:?}, role={:?}",
carrier_name, join_exit_value, host_slot, role
),
true,
);
}
@ -156,9 +172,14 @@ impl ExitMetaCollector {
};
if verbose {
eprintln!(
"[joinir/exit-line] collected ConditionOnly carrier '{}' JoinIR {:?} (not in variable_ctx.variable_map)",
carrier_name, join_exit_value
trace.emit_if(
"exit-line",
"collector",
&format!(
"collected ConditionOnly carrier '{}' JoinIR {:?} (not in variable_ctx.variable_map)",
carrier_name, join_exit_value
),
true,
);
}
@ -175,9 +196,14 @@ impl ExitMetaCollector {
};
if verbose {
eprintln!(
"[joinir/exit-line] collected FromHost carrier '{}' JoinIR {:?} (not in variable_ctx.variable_map)",
carrier_name, join_exit_value
trace.emit_if(
"exit-line",
"collector",
&format!(
"collected FromHost carrier '{}' JoinIR {:?} (not in variable_ctx.variable_map)",
carrier_name, join_exit_value
),
true,
);
}
@ -193,9 +219,14 @@ impl ExitMetaCollector {
};
if verbose {
eprintln!(
"[joinir/exit-line] collected loop-local carrier '{}' JoinIR {:?} (no host slot)",
carrier_name, join_exit_value
trace.emit_if(
"exit-line",
"collector",
&format!(
"collected loop-local carrier '{}' JoinIR {:?} (no host slot)",
carrier_name, join_exit_value
),
true,
);
}
@ -209,7 +240,7 @@ impl ExitMetaCollector {
if strict {
panic!("{}", msg);
} else if verbose {
eprintln!("{}", msg);
trace.emit_if("exit-line", "collector", &msg, true);
}
}
}
@ -217,10 +248,11 @@ impl ExitMetaCollector {
}
if verbose {
eprintln!(
"[joinir/exit-line] ExitMetaCollector: collected {} bindings: {:?}",
bindings.len(),
bindings
trace.emit_if(
"exit-line",
"collector",
&format!("collected {} bindings: {:?}", bindings.len(), bindings),
true,
);
}

View File

@ -84,11 +84,15 @@ impl ExitLineOrchestrator {
carrier_phis: &BTreeMap<String, ValueId>,
debug: bool,
) -> Result<(), String> {
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
let verbose = debug || crate::config::env::joinir_dev_enabled();
if verbose {
eprintln!(
"[joinir/exit-line] orchestrator start: {} carrier PHIs",
carrier_phis.len()
trace.stderr_if(
&format!(
"[joinir/exit-line] orchestrator start: {} carrier PHIs",
carrier_phis.len()
),
true,
);
}
@ -96,7 +100,7 @@ impl ExitLineOrchestrator {
ExitLineReconnector::reconnect(builder, boundary, carrier_phis, debug)?;
if verbose {
eprintln!("[joinir/exit-line] orchestrator complete");
trace.stderr_if("[joinir/exit-line] orchestrator complete", true);
}
Ok(())

View File

@ -79,24 +79,31 @@ impl ExitLineReconnector {
carrier_phis: &BTreeMap<String, ValueId>,
debug: bool,
) -> Result<(), String> {
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
let dev_on = crate::config::env::joinir_dev_enabled();
let strict = crate::config::env::joinir_strict_enabled() || dev_on;
let verbose = debug || dev_on;
if verbose {
eprintln!(
"[joinir/exit-line] reconnect: {} exit bindings, {} carrier PHIs",
boundary.exit_bindings.len(),
carrier_phis.len()
trace.stderr_if(
&format!(
"[joinir/exit-line] reconnect: {} exit bindings, {} carrier PHIs",
boundary.exit_bindings.len(),
carrier_phis.len()
),
true,
);
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<_>>()
trace.stderr_if(
&format!(
"[joinir/exit-line] bindings {:?}",
boundary
.exit_bindings
.iter()
.map(|b| (&b.carrier_name, b.role, b.join_exit_value))
.collect::<Vec<_>>()
),
true,
);
}
}
@ -104,16 +111,19 @@ impl ExitLineReconnector {
// Early return for empty exit_bindings
if boundary.exit_bindings.is_empty() {
if verbose {
eprintln!("[joinir/exit-line] reconnect: no exit bindings, skip");
trace.stderr_if("[joinir/exit-line] reconnect: no exit bindings, skip", true);
}
return Ok(());
}
if verbose {
eprintln!(
"[joinir/exit-line] reconnecting {} exit bindings with {} carrier PHIs",
boundary.exit_bindings.len(),
carrier_phis.len()
trace.stderr_if(
&format!(
"[joinir/exit-line] reconnecting {} exit bindings with {} carrier PHIs",
boundary.exit_bindings.len(),
carrier_phis.len()
),
true,
);
}
@ -123,9 +133,12 @@ impl ExitLineReconnector {
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
if binding.role == CarrierRole::ConditionOnly {
if verbose {
eprintln!(
"[joinir/exit-line] skip ConditionOnly carrier '{}' (no variable_ctx.variable_map update)",
binding.carrier_name
trace.stderr_if(
&format!(
"[joinir/exit-line] skip ConditionOnly carrier '{}' (no variable_ctx.variable_map update)",
binding.carrier_name
),
true,
);
}
continue;
@ -135,9 +148,12 @@ impl ExitLineReconnector {
let phi_dst = carrier_phis.get(&binding.carrier_name);
if verbose {
eprintln!(
"[joinir/exit-line] carrier '{}' → phi_dst={:?}",
binding.carrier_name, phi_dst
trace.stderr_if(
&format!(
"[joinir/exit-line] carrier '{}' → phi_dst={:?}",
binding.carrier_name, phi_dst
),
true,
);
}
@ -150,16 +166,22 @@ impl ExitLineReconnector {
{
// Phase 177-STRUCT: Always log for debugging
if verbose {
eprintln!(
"[joinir/exit-line] variable_ctx.variable_map['{}'] {:?}{:?}",
binding.carrier_name, *var_vid, phi_value
trace.stderr_if(
&format!(
"[joinir/exit-line] variable_ctx.variable_map['{}'] {:?}{:?}",
binding.carrier_name, *var_vid, phi_value
),
true,
);
}
*var_vid = phi_value;
} else if verbose {
eprintln!(
"[joinir/exit-line] warning: carrier '{}' not found in variable_ctx.variable_map",
binding.carrier_name
trace.stderr_if(
&format!(
"[joinir/exit-line] warning: carrier '{}' not found in variable_ctx.variable_map",
binding.carrier_name
),
true,
);
} else if strict {
return Err(format!(
@ -175,9 +197,12 @@ impl ExitLineReconnector {
carrier_phis.len()
));
} else if verbose {
eprintln!(
"[joinir/exit-line] warning: No PHI dst for carrier '{}' (may be condition-only variable)",
binding.carrier_name
trace.stderr_if(
&format!(
"[joinir/exit-line] warning: No PHI dst for carrier '{}' (may be condition-only variable)",
binding.carrier_name
),
true,
);
}
}
@ -186,8 +211,9 @@ impl ExitLineReconnector {
// Backward compatibility warning for deprecated host_outputs
#[allow(deprecated)]
if !boundary.host_outputs.is_empty() && debug {
eprintln!(
"[joinir/exit-line] WARNING: Using deprecated host_outputs. Migrate to exit_bindings."
trace.stderr_if(
"[joinir/exit-line] WARNING: Using deprecated host_outputs. Migrate to exit_bindings.",
true,
);
}
@ -220,13 +246,17 @@ impl ExitLineReconnector {
variable_map: &BTreeMap<String, ValueId>,
) {
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
for binding in &boundary.exit_bindings {
// Phase 228-8: Skip ConditionOnly carriers (not in variable_ctx.variable_map by design)
if binding.role == CarrierRole::ConditionOnly {
eprintln!(
"[JoinIR/ExitLine/Contract] Phase 228-8: Skipping ConditionOnly carrier '{}' (not in variable_ctx.variable_map)",
binding.carrier_name
trace.stderr_if(
&format!(
"[JoinIR/ExitLine/Contract] Phase 228-8: Skipping ConditionOnly carrier '{}' (not in variable_ctx.variable_map)",
binding.carrier_name
),
true,
);
continue;
}
@ -236,9 +266,12 @@ impl ExitLineReconnector {
if phi_dst.is_none() {
// Skip loop variable (it's handled separately in loop_header_phi)
// Only check carriers that have exit_bindings
eprintln!(
"[JoinIR/ExitLine/Contract] WARNING: Carrier '{}' has exit_binding but no PHI in carrier_phis",
binding.carrier_name
trace.stderr_if(
&format!(
"[JoinIR/ExitLine/Contract] WARNING: Carrier '{}' has exit_binding but no PHI in carrier_phis",
binding.carrier_name
),
true,
);
// Don't panic for now - loop variable might not be in carrier_phis
// Future: Distinguish loop_var from carriers in exit_bindings

View File

@ -24,6 +24,8 @@ pub(super) fn build_exit_phi(
carrier_inputs: &BTreeMap<String, Vec<(BasicBlockId, ValueId)>>,
debug: bool,
) -> Result<(Option<ValueId>, BTreeMap<String, ValueId>), String> {
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
let verbose = debug || crate::config::env::joinir_dev_enabled();
let mut carrier_phis: BTreeMap<String, ValueId> = BTreeMap::new();
let exit_phi_result_id = if let Some(ref mut func) = builder.scope_ctx.current_function {
@ -45,9 +47,12 @@ pub(super) fn build_exit_phi(
.instruction_spans
.push(crate::ast::Span::unknown());
if debug {
eprintln!(
"[cf_loop/joinir] Exit block PHI (expr result): {:?} = phi {:?}",
phi_dst, exit_phi_inputs
trace.stderr_if(
&format!(
"[cf_loop/joinir] Exit block PHI (expr result): {:?} = phi {:?}",
phi_dst, exit_phi_inputs
),
true,
);
}
Some(phi_dst)
@ -78,19 +83,25 @@ pub(super) fn build_exit_phi(
carrier_phis.insert(carrier_name.clone(), phi_dst);
// DEBUG-177: Always log exit block PHI creation for carrier debugging
eprintln!(
"[DEBUG-177] Exit block PHI (carrier '{}'): {:?} = phi {:?}",
carrier_name, phi_dst, inputs
// DEBUG-177: Exit block PHI creation for carrier debugging
trace.stderr_if(
&format!(
"[DEBUG-177] Exit block PHI (carrier '{}'): {:?} = phi {:?}",
carrier_name, phi_dst, inputs
),
verbose,
);
}
func.add_block(exit_block);
if debug {
eprintln!(
"[cf_loop/joinir] Created exit block: {:?} with {} carrier PHIs",
exit_block_id,
carrier_phis.len()
trace.stderr_if(
&format!(
"[cf_loop/joinir] Created exit block: {:?} with {} carrier PHIs",
exit_block_id,
carrier_phis.len()
),
true,
);
}
phi_result

View File

@ -45,25 +45,33 @@ impl ExprResultResolver {
remapper: &JoinIrIdRemapper,
debug: bool,
) -> Result<Option<ValueId>, String> {
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
// Step 1: Check if expr_result exists
let expr_result_id = match expr_result {
Some(id) => id,
None => {
if debug {
eprintln!("[cf_loop/joinir] Phase 221: expr_result is None, returning None");
trace.stderr_if(
"[cf_loop/joinir] Phase 221: expr_result is None, returning None",
true,
);
}
return Ok(None);
}
};
if debug {
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<_>>()
trace.stderr_if(
&format!(
"[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<_>>()
),
true,
);
}
@ -74,9 +82,12 @@ impl ExprResultResolver {
// expr_result is a carrier! Use the carrier PHI dst
if let Some(&carrier_phi_dst) = carrier_phis.get(&binding.carrier_name) {
if debug {
eprintln!(
"[cf_loop/joinir] Phase 221: expr_result {:?} is carrier '{}', returning PHI dst {:?}",
expr_result_id, binding.carrier_name, carrier_phi_dst
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 221: expr_result {:?} is carrier '{}', returning PHI dst {:?}",
expr_result_id, binding.carrier_name, carrier_phi_dst
),
true,
);
}
return Ok(Some(carrier_phi_dst));
@ -92,9 +103,12 @@ impl ExprResultResolver {
// Step 3: expr_result is NOT a carrier - use remapped value
if let Some(remapped_expr) = remapper.get_value(expr_result_id) {
if debug {
eprintln!(
"[cf_loop/joinir] Phase 221: Returning non-carrier expr_result: JoinIR {:?} → Host {:?}",
expr_result_id, remapped_expr
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 221: Returning non-carrier expr_result: JoinIR {:?} → Host {:?}",
expr_result_id, remapped_expr
),
true,
);
}
Ok(Some(remapped_expr))

View File

@ -10,6 +10,7 @@
use super::loop_header_phi_info::LoopHeaderPhiInfo;
use super::merge_result::MergeResult;
use super::tail_call_classifier::{classify_tail_call, TailCallKind};
use super::super::trace;
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
use crate::mir::{BasicBlock, BasicBlockId, MirInstruction, MirModule, ValueId};
@ -51,8 +52,17 @@ pub(super) fn merge_and_rewrite(
exit_block_id: BasicBlockId,
debug: bool,
) -> Result<MergeResult, String> {
let trace = trace::trace();
let verbose = debug || crate::config::env::joinir_dev_enabled();
macro_rules! log {
($enabled:expr, $($arg:tt)*) => {
trace.stderr_if(&format!($($arg)*), $enabled);
};
}
// Phase 177-3: exit_block_id is now passed in from block_allocator
eprintln!(
log!(
verbose,
"[cf_loop/joinir/instruction_rewriter] Phase 177-3: Using exit_block_id = {:?}",
exit_block_id
);
@ -84,7 +94,8 @@ pub(super) fn merge_and_rewrite(
// DETERMINISM FIX: Sort functions by name to ensure consistent iteration order
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Phase 189: Merging {} functions",
mir_module.functions.len()
);
@ -103,7 +114,8 @@ pub(super) fn merge_and_rewrite(
let is_continuation_func = func_name == K_EXIT_FUNC_NAME || func_name.ends_with("k_exit");
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Merging function '{}' with {} blocks, entry={:?} (is_continuation={})",
func_name,
func.blocks.len(),
@ -118,7 +130,8 @@ pub(super) fn merge_and_rewrite(
// Processing continuation functions would add undefined ValueIds to PHI.
if is_continuation_func {
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Phase 33-15: Skipping continuation function '{}' blocks",
func_name
);
@ -169,18 +182,21 @@ pub(super) fn merge_and_rewrite(
// DEBUG: Print block being processed
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] === Processing block {:?} (from func '{}') ===",
old_block_id, func_name
);
eprintln!(
log!(
true,
"[cf_loop/joinir] Original block has {} instructions:",
old_block.instructions.len()
);
for (idx, inst) in old_block.instructions.iter().enumerate() {
eprintln!("[cf_loop/joinir] [{}] {:?}", idx, inst);
log!(true, "[cf_loop/joinir] [{}] {:?}", idx, inst);
}
eprintln!(
log!(
true,
"[cf_loop/joinir] Original block terminator: {:?}",
old_block.terminator
);
@ -195,7 +211,8 @@ pub(super) fn merge_and_rewrite(
is_loop_entry_point && !loop_header_phi_info.carrier_phis.is_empty();
if is_loop_entry_point {
eprintln!(
log!(
verbose,
"[cf_loop/joinir] Phase 177-3 DEBUG: is_loop_entry_point={}, carrier_phis.len()={}, is_loop_header_with_phi={}",
is_loop_entry_point,
loop_header_phi_info.carrier_phis.len(),
@ -217,7 +234,8 @@ pub(super) fn merge_and_rewrite(
};
if is_loop_header_with_phi && !phi_dst_ids_for_block.is_empty() {
eprintln!(
log!(
verbose,
"[cf_loop/joinir] Phase 177-3: Loop header with {} PHI dsts to protect: {:?}",
phi_dst_ids_for_block.len(),
phi_dst_ids_for_block
@ -232,12 +250,14 @@ pub(super) fn merge_and_rewrite(
if let MirInstruction::Copy { dst, src } = inst {
// Check if this copy's dst is a PHI dst (after remapping)
let dst_remapped = remapper.get_value(*dst).unwrap_or(*dst);
eprintln!(
log!(
verbose,
"[cf_loop/joinir] Phase 177-3 DEBUG: Copy {:?} = {:?}, dst_remapped = {:?}, in phi_dsts = {}",
dst, src, dst_remapped, phi_dst_ids_for_block.contains(&dst_remapped)
);
if phi_dst_ids_for_block.contains(&dst_remapped) {
eprintln!(
log!(
verbose,
"[cf_loop/joinir] Phase 177-3: ✅ Skipping loop header Copy to PHI dst {:?} (original {:?})",
dst_remapped, dst
);
@ -251,10 +271,7 @@ pub(super) fn merge_and_rewrite(
if let crate::mir::types::ConstValue::String(_) = value {
if value_to_func_name.contains_key(dst) {
if debug {
eprintln!(
"[cf_loop/joinir] Skipping function name const: {:?}",
inst
);
log!(true, "[cf_loop/joinir] Skipping function name const: {:?}", inst);
}
continue; // Skip this instruction
}
@ -263,7 +280,7 @@ pub(super) fn merge_and_rewrite(
// that initialize boundary inputs. BoundaryInjector provides these values via Copy.
if is_loop_entry_point && boundary_input_set.contains(dst) {
if debug {
eprintln!("[cf_loop/joinir] Skipping boundary input const (replaced by BoundaryInjector Copy): {:?}", inst);
log!(true, "[cf_loop/joinir] Skipping boundary input const (replaced by BoundaryInjector Copy): {:?}", inst);
}
continue; // Skip - BoundaryInjector will provide the value
}
@ -282,7 +299,8 @@ pub(super) fn merge_and_rewrite(
found_tail_call = true;
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Detected tail call to '{}' (args={:?}), will convert to Jump",
func_name, args
);
@ -306,7 +324,8 @@ pub(super) fn merge_and_rewrite(
if is_header_phi_dst {
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Phase 33-20: Skipping Copy that would overwrite header PHI dst {:?}",
remapped_dst
);
@ -354,7 +373,8 @@ pub(super) fn merge_and_rewrite(
if debug {
match inst {
MirInstruction::BoxCall { .. } => {
eprintln!(
log!(
true,
"[cf_loop/joinir] Adding BoxCall to block {:?}: {:?}",
new_block_id, inst
);
@ -368,12 +388,13 @@ pub(super) fn merge_and_rewrite(
// DEBUG: Print what was added to the block after first pass
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] After first pass, new_block has {} instructions",
new_block.instructions.len()
);
for (idx, inst) in new_block.instructions.iter().enumerate() {
eprintln!("[cf_loop/joinir] [{}] {:?}", idx, inst);
log!(true, "[cf_loop/joinir] [{}] {:?}", idx, inst);
}
}
@ -381,7 +402,8 @@ pub(super) fn merge_and_rewrite(
// Phase 188-Impl-3: Use actual parameter ValueIds from target function
if let Some((target_block, args)) = tail_call_target {
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Inserting param bindings for tail call to {:?}",
target_block
);
@ -412,7 +434,8 @@ pub(super) fn merge_and_rewrite(
// %phi_dst = copy %undefined ← ❌ This overwrites the PHI!
if is_loop_entry_point {
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Phase 33-21: Skip param bindings in header block (PHIs define carriers)"
);
}
@ -434,7 +457,8 @@ pub(super) fn merge_and_rewrite(
if is_header_phi_dst {
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Phase 131-6: Skip param binding to PHI dst {:?} (PHI receives value via incoming edge)",
param_val_remapped
);
@ -449,7 +473,8 @@ pub(super) fn merge_and_rewrite(
});
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Param binding: arg {:?} → param {:?}",
arg_val_remapped, param_val_remapped
);
@ -479,7 +504,8 @@ pub(super) fn merge_and_rewrite(
);
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Phase 33-16: Set latch incoming for '{}': block={:?}, value={:?}",
loop_var_name, new_block_id, latch_value
);
@ -497,7 +523,8 @@ pub(super) fn merge_and_rewrite(
if let Some(ref loop_var) = b.loop_var_name {
if &binding.carrier_name == loop_var {
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Phase 176-4: Skipping loop variable '{}' in exit_bindings (handled separately)",
binding.carrier_name
);
@ -516,14 +543,16 @@ pub(super) fn merge_and_rewrite(
);
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Phase 176-4: Set latch incoming for carrier '{}': block={:?}, value={:?} (arg[{}])",
binding.carrier_name, new_block_id, latch_value, carrier_arg_idx
);
}
carrier_arg_idx += 1;
} else if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Phase 33-20 WARNING: No arg for carrier '{}' at index {}",
binding.carrier_name, carrier_arg_idx
);
@ -547,7 +576,8 @@ pub(super) fn merge_and_rewrite(
TailCallKind::BackEdge => {
// Back edge: redirect to header block where PHIs are
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Phase 33-16: BackEdge detected, redirecting from {:?} to header {:?}",
target_block, loop_header_phi_info.header_block
);
@ -557,7 +587,8 @@ pub(super) fn merge_and_rewrite(
TailCallKind::LoopEntry => {
// Loop entry: no redirect (entry block IS the header)
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Phase 33-16: LoopEntry detected, using direct target {:?}",
target_block
);
@ -576,19 +607,20 @@ pub(super) fn merge_and_rewrite(
// DEBUG: Print final state after adding parameter bindings
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] After adding param bindings, new_block has {} instructions",
new_block.instructions.len()
);
for (idx, inst) in new_block.instructions.iter().enumerate() {
eprintln!("[cf_loop/joinir] [{}] {:?}", idx, inst);
log!(true, "[cf_loop/joinir] [{}] {:?}", idx, inst);
}
}
}
new_block.instruction_spans = old_block.instruction_spans.clone();
if debug {
eprintln!("[cf_loop/joinir] Span sync: new_block.instructions.len()={}, old_block.instruction_spans.len()={}, new_block.instruction_spans.len()={}",
log!(true, "[cf_loop/joinir] Span sync: new_block.instructions.len()={}, old_block.instruction_spans.len()={}, new_block.instruction_spans.len()={}",
new_block.instructions.len(),
old_block.instruction_spans.len(),
new_block.instruction_spans.len()
@ -613,7 +645,8 @@ pub(super) fn merge_and_rewrite(
if let Some(_ret_val) = value {
// Phase 246-EX: Check if this block has jump_args metadata
if let Some(ref jump_args) = old_block.jump_args {
eprintln!(
log!(
verbose,
"[DEBUG-177] Phase 246-EX: Block {:?} has jump_args metadata: {:?}",
old_block.id, jump_args
);
@ -624,7 +657,8 @@ pub(super) fn merge_and_rewrite(
.map(|&arg| remapper.remap_value(arg))
.collect();
eprintln!(
log!(
verbose,
"[DEBUG-177] Phase 246-EX: Remapped jump_args: {:?}",
remapped_args
);
@ -643,7 +677,7 @@ pub(super) fn merge_and_rewrite(
if strict_exit {
return Err(msg);
} else {
eprintln!("[DEBUG-177] {}", msg);
log!(verbose, "[DEBUG-177] {}", msg);
}
}
}
@ -651,7 +685,8 @@ pub(super) fn merge_and_rewrite(
// First arg is the loop variable (expr_result)
if let Some(&loop_var_exit) = remapped_args.first() {
exit_phi_inputs.push((new_block_id, loop_var_exit));
eprintln!(
log!(
verbose,
"[DEBUG-177] Phase 246-EX: exit_phi_inputs from jump_args[0]: ({:?}, {:?})",
new_block_id, loop_var_exit
);
@ -663,7 +698,8 @@ pub(super) fn merge_and_rewrite(
.entry(loop_var_name.clone())
.or_insert_with(Vec::new)
.push((new_block_id, loop_var_exit));
eprintln!(
log!(
verbose,
"[DEBUG-177] Phase 246-EX-P5: Added loop_var '{}' to carrier_inputs: ({:?}, {:?})",
loop_var_name, new_block_id, loop_var_exit
);
@ -685,7 +721,8 @@ pub(super) fn merge_and_rewrite(
{
// Phase 227: Skip ConditionOnly carriers
if carrier.role == crate::mir::join_ir::lowering::carrier_info::CarrierRole::ConditionOnly {
eprintln!(
log!(
verbose,
"[DEBUG-177] Phase 227: Skipping ConditionOnly carrier '{}' from exit PHI",
carrier.name
);
@ -701,7 +738,8 @@ pub(super) fn merge_and_rewrite(
.entry(carrier.name.clone())
.or_insert_with(Vec::new)
.push((new_block_id, carrier_exit));
eprintln!(
log!(
verbose,
"[DEBUG-177] Phase 246-EX-FIX: Collecting carrier '{}': from {:?} using jump_args[{}] = {:?}",
carrier.name, new_block_id, jump_args_idx, carrier_exit
);
@ -713,7 +751,8 @@ pub(super) fn merge_and_rewrite(
if strict_exit {
return Err(msg);
} else {
eprintln!(
log!(
verbose,
"[DEBUG-177] Phase 246-EX WARNING: No jump_args entry for carrier '{}' at index {}",
carrier.name, jump_args_idx
);
@ -721,12 +760,16 @@ pub(super) fn merge_and_rewrite(
}
}
} else {
eprintln!("[DEBUG-177] Phase 246-EX WARNING: No carrier_info in boundary!");
log!(
verbose,
"[DEBUG-177] Phase 246-EX WARNING: No carrier_info in boundary!"
);
}
}
} else {
// Fallback: Use header PHI dst (old behavior for blocks without jump_args)
eprintln!(
log!(
verbose,
"[DEBUG-177] Phase 246-EX: Block {:?} has NO jump_args, using header PHI fallback",
old_block.id
);
@ -738,7 +781,8 @@ pub(super) fn merge_and_rewrite(
{
exit_phi_inputs.push((new_block_id, phi_dst));
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Phase 246-EX fallback: Using header PHI dst {:?} for exit (loop_var='{}')",
phi_dst, loop_var_name
);
@ -850,7 +894,8 @@ pub(super) fn merge_and_rewrite(
if let Some(remapped) = remapper.get_value(binding.join_value) {
value_map_for_injector.insert(binding.join_value, remapped);
if debug {
eprintln!(
log!(
true,
"[cf_loop/joinir] Phase 171-fix: Condition binding '{}': JoinIR {:?} → remapped {:?} (HOST {:?})",
binding.name, binding.join_value, remapped, binding.host_value
);

View File

@ -28,6 +28,7 @@
//! (instruction_rewriter).
use super::loop_header_phi_info::{CarrierPhiEntry, LoopHeaderPhiInfo};
use super::super::trace;
use crate::mir::{BasicBlockId, MirInstruction, ValueId};
/// Builder for loop header PHIs
@ -73,14 +74,21 @@ impl LoopHeaderPhiBuilder {
expr_result_is_loop_var: bool,
debug: bool,
) -> Result<LoopHeaderPhiInfo, String> {
let trace = trace::trace();
if debug {
eprintln!(
"[cf_loop/joinir] Phase 33-16: Building header PHIs at {:?}",
header_block
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 33-16: Building header PHIs at {:?}",
header_block
),
true,
);
eprintln!(
"[cf_loop/joinir] Loop var '{}' init={:?}, entry_block={:?}",
loop_var_name, loop_var_init, entry_block
trace.stderr_if(
&format!(
"[cf_loop/joinir] Loop var '{}' init={:?}, entry_block={:?}",
loop_var_name, loop_var_init, entry_block
),
true,
);
}
@ -101,14 +109,15 @@ impl LoopHeaderPhiBuilder {
.value_types
.insert(loop_var_phi_dst, init_type.clone());
if debug || std::env::var("NYASH_CARRIER_PHI_DEBUG").ok().as_deref() == Some("1") {
eprintln!(
trace.stderr_if(
&format!(
"[carrier/phi] Loop var '{}': dst=%{} entry_type={:?} (backedge ignored)",
loop_var_name,
loop_var_phi_dst.as_u32(),
init_type
);
}
),
debug || std::env::var("NYASH_CARRIER_PHI_DEBUG").ok().as_deref() == Some("1"),
);
}
info.carrier_phis.insert(
@ -124,9 +133,12 @@ impl LoopHeaderPhiBuilder {
info.carrier_order.push(loop_var_name.to_string());
if debug {
eprintln!(
"[cf_loop/joinir] Loop var PHI: {:?} = phi [(from {:?}, {:?}), (latch TBD)]",
loop_var_phi_dst, entry_block, loop_var_init
trace.stderr_if(
&format!(
"[cf_loop/joinir] Loop var PHI: {:?} = phi [(from {:?}, {:?}), (latch TBD)]",
loop_var_phi_dst, entry_block, loop_var_init
),
true,
);
}
@ -150,14 +162,15 @@ impl LoopHeaderPhiBuilder {
.value_types
.insert(phi_dst, init_type.clone());
if debug || std::env::var("NYASH_CARRIER_PHI_DEBUG").ok().as_deref() == Some("1") {
eprintln!(
trace.stderr_if(
&format!(
"[carrier/phi] Carrier '{}': dst=%{} entry_type={:?} (backedge ignored)",
name,
phi_dst.as_u32(),
init_type
);
}
),
debug || std::env::var("NYASH_CARRIER_PHI_DEBUG").ok().as_deref() == Some("1"),
);
}
info.carrier_phis.insert(
@ -173,9 +186,12 @@ impl LoopHeaderPhiBuilder {
info.carrier_order.push(name.clone());
if debug {
eprintln!(
"[cf_loop/joinir] Carrier '{}' PHI: {:?} = phi [(from {:?}, {:?}), (latch TBD)], role={:?}",
name, phi_dst, entry_block, init_value, role
trace.stderr_if(
&format!(
"[cf_loop/joinir] Carrier '{}' PHI: {:?} = phi [(from {:?}, {:?}), (latch TBD)], role={:?}",
name, phi_dst, entry_block, init_value, role
),
true,
);
}
}
@ -184,9 +200,12 @@ impl LoopHeaderPhiBuilder {
if expr_result_is_loop_var {
info.expr_result_phi = Some(loop_var_phi_dst);
if debug {
eprintln!(
"[cf_loop/joinir] expr_result = {:?} (loop var PHI)",
loop_var_phi_dst
trace.stderr_if(
&format!(
"[cf_loop/joinir] expr_result = {:?} (loop var PHI)",
loop_var_phi_dst
),
true,
);
}
}
@ -208,11 +227,15 @@ impl LoopHeaderPhiBuilder {
info: &LoopHeaderPhiInfo,
debug: bool,
) -> Result<(), String> {
let trace = trace::trace();
let dev_debug = debug || crate::config::env::joinir_dev_enabled();
if debug {
eprintln!(
"[cf_loop/joinir] Phase 33-16: Finalizing header PHIs at {:?}",
info.header_block
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 33-16: Finalizing header PHIs at {:?}",
info.header_block
),
true,
);
}
@ -260,10 +283,13 @@ impl LoopHeaderPhiBuilder {
phi_instructions.push(phi);
if dev_debug {
eprintln!(
"[joinir/header-phi] Finalized carrier '{}' PHI: {:?} = phi [({:?}, {:?}), ({:?}, {:?})]",
name, entry.phi_dst, entry_block, entry_val, latch_block, latch_val
);
trace.stderr_if(
&format!(
"[joinir/header-phi] Finalized carrier '{}' PHI: {:?} = phi [({:?}, {:?}), ({:?}, {:?})]",
name, entry.phi_dst, entry_block, entry_val, latch_block, latch_val
),
true,
);
}
}
@ -280,9 +306,12 @@ impl LoopHeaderPhiBuilder {
header_block.instruction_spans = new_spans;
if dev_debug {
eprintln!(
"[joinir/header-phi] Header block now has {} instructions",
header_block.instructions.len()
trace.stderr_if(
&format!(
"[joinir/header-phi] Header block now has {} instructions",
header_block.instructions.len()
),
true,
);
}

View File

@ -30,6 +30,7 @@ mod value_collector;
pub use loop_header_phi_builder::LoopHeaderPhiBuilder;
pub use loop_header_phi_info::LoopHeaderPhiInfo;
use super::trace;
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
use crate::mir::{MirModule, ValueId};
@ -77,13 +78,15 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
debug: bool,
) -> Result<Option<ValueId>, String> {
let verbose = debug || crate::config::env::joinir_dev_enabled();
let trace = trace::trace();
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] merge_joinir_mir_blocks called with {} functions",
mir_module.functions.len()
);
}
),
debug,
);
if verbose {
if let Some(boundary) = boundary {
@ -109,31 +112,43 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
})
.collect();
eprintln!(
"[cf_loop/joinir] Boundary join_inputs={:?} host_inputs={:?}",
boundary.join_inputs, boundary.host_inputs
trace.stderr_if(
&format!(
"[cf_loop/joinir] Boundary join_inputs={:?} host_inputs={:?}",
boundary.join_inputs, boundary.host_inputs
),
true,
);
eprintln!(
"[cf_loop/joinir] Boundary exit_bindings ({}): {}",
boundary.exit_bindings.len(),
exit_summary.join(", ")
trace.stderr_if(
&format!(
"[cf_loop/joinir] Boundary exit_bindings ({}): {}",
boundary.exit_bindings.len(),
exit_summary.join(", ")
),
true,
);
if !cond_summary.is_empty() {
eprintln!(
"[cf_loop/joinir] Boundary condition_bindings ({}): {}",
cond_summary.len(),
cond_summary.join(", ")
trace.stderr_if(
&format!(
"[cf_loop/joinir] Boundary condition_bindings ({}): {}",
cond_summary.len(),
cond_summary.join(", ")
),
true,
);
}
if let Some(ci) = &boundary.carrier_info {
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
trace.stderr_if(
&format!(
"[cf_loop/joinir] Boundary carrier_info: loop_var='{}', carriers={:?}",
ci.loop_var_name, carriers
),
true,
);
}
} else {
eprintln!("[cf_loop/joinir] No boundary provided");
trace.stderr_if("[cf_loop/joinir] No boundary provided", true);
}
}
@ -149,23 +164,25 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Phase 171-fix: Add condition_bindings' join_values to used_values for remapping
if let Some(boundary) = boundary {
for binding in &boundary.condition_bindings {
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 171-fix: Adding condition binding '{}' JoinIR {:?} to used_values",
binding.name, binding.join_value
);
}
),
debug,
);
used_values.insert(binding.join_value);
}
// Phase 172-3: Add exit_bindings' join_exit_values to used_values for remapping
for binding in &boundary.exit_bindings {
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 172-3: Adding exit binding '{}' JoinIR {:?} to used_values",
binding.carrier_name, binding.join_exit_value
);
}
),
debug,
);
used_values.insert(binding.join_exit_value);
}
}
@ -237,20 +254,24 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
.collect()
};
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 201-A: Pre-building header PHIs for loop_var='{}' at {:?}",
loop_var_name, entry_block_remapped
);
eprintln!(
),
debug,
);
trace.stderr_if(
&format!(
"[cf_loop/joinir] loop_var_init={:?}, carriers={:?}",
loop_var_init,
other_carriers
.iter()
.map(|(n, _, _, _)| n.as_str())
.collect::<Vec<_>>()
);
}
),
debug,
);
// Build PHI info (this allocates PHI dst ValueIds)
LoopHeaderPhiBuilder::build(
@ -286,22 +307,24 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// 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!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 201-A: Reserved PHI dsts: {:?}",
reserved_phi_dsts
);
}
),
debug && !reserved_phi_dsts.is_empty(),
);
// 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.comp_ctx.reserved_value_ids = reserved_phi_dsts.clone();
if debug && !builder.comp_ctx.reserved_value_ids.is_empty() {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 201-A: Set builder.comp_ctx.reserved_value_ids = {:?}",
builder.comp_ctx.reserved_value_ids
);
}
),
debug && !builder.comp_ctx.reserved_value_ids.is_empty(),
);
// Phase 3: Remap ValueIds (with reserved PHI dsts protection)
remap_values(
@ -313,28 +336,40 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
)?;
// Phase 177-3 DEBUG: Verify remapper state after Phase 3
eprintln!("[DEBUG-177] === Remapper state after Phase 3 ===");
eprintln!("[DEBUG-177] used_values count: {}", used_values.len());
trace.stderr_if("[DEBUG-177] === Remapper state after Phase 3 ===", verbose);
trace.stderr_if(
&format!("[DEBUG-177] used_values count: {}", used_values.len()),
verbose,
);
for value_id in &used_values {
if let Some(remapped) = remapper.get_value(*value_id) {
eprintln!("[DEBUG-177] JoinIR {:?} → Host {:?}", value_id, remapped);
trace.stderr_if(
&format!("[DEBUG-177] JoinIR {:?} → Host {:?}", value_id, remapped),
verbose,
);
} else {
eprintln!("[DEBUG-177] JoinIR {:?} → NOT FOUND ❌", value_id);
trace.stderr_if(
&format!("[DEBUG-177] JoinIR {:?} → NOT FOUND ❌", value_id),
verbose,
);
}
}
// Check condition_bindings specifically
if let Some(boundary) = boundary {
eprintln!("[DEBUG-177] === Condition bindings check ===");
trace.stderr_if("[DEBUG-177] === Condition bindings check ===", verbose);
for binding in &boundary.condition_bindings {
let lookup_result = remapper.get_value(binding.join_value);
eprintln!(
"[DEBUG-177] '{}': JoinIR {:?}{:?}",
binding.name, binding.join_value, lookup_result
trace.stderr_if(
&format!(
"[DEBUG-177] '{}': JoinIR {:?}{:?}",
binding.name, binding.join_value, lookup_result
),
verbose,
);
}
}
eprintln!("[DEBUG-177] ==============================");
trace.stderr_if("[DEBUG-177] ==============================", verbose);
// Phase 3.5: Override remapper for function parameters to use PHI dsts
//
@ -390,15 +425,21 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
.collect();
if !condition_binding_ids.is_empty() {
eprintln!(
"[cf_loop/joinir] Phase 177-3: Protected ValueIds (condition-only, not carriers): {:?}",
condition_binding_ids
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 177-3: Protected ValueIds (condition-only, not carriers): {:?}",
condition_binding_ids
),
verbose,
);
for cb in &boundary.condition_bindings {
let is_carrier = carrier_names.contains(cb.name.as_str());
eprintln!(
"[cf_loop/joinir] Phase 177-3: '{}': JoinIR {:?} (carrier={})",
cb.name, cb.join_value, is_carrier
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 177-3: '{}': JoinIR {:?} (carrier={})",
cb.name, cb.join_value, is_carrier
),
verbose,
);
}
}
@ -407,25 +448,34 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
let loop_step_func_name = "join_func_1";
if function_params.get(main_func_name).is_none() {
eprintln!(
"[cf_loop/joinir] WARNING: function_params.get('{}') returned None. Available keys: {:?}",
main_func_name,
function_params.keys().collect::<Vec<_>>()
trace.stderr_if(
&format!(
"[cf_loop/joinir] WARNING: function_params.get('{}') returned None. Available keys: {:?}",
main_func_name,
function_params.keys().collect::<Vec<_>>()
),
verbose,
);
}
if let Some(main_params) = function_params.get(main_func_name) {
eprintln!(
"[DEBUG-177] Phase 33-21: main ({}) params: {:?}",
main_func_name, main_params
trace.stderr_if(
&format!(
"[DEBUG-177] Phase 33-21: main ({}) params: {:?}",
main_func_name, main_params
),
verbose,
);
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<_>>()
trace.stderr_if(
&format!(
"[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<_>>()
),
verbose,
);
// Map main's parameters to header PHI dsts
// main params: [i_init, carrier1_init, ...]
@ -436,15 +486,21 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
if let Some(&main_param) = main_params.get(idx) {
// Phase 177-3: Don't override condition_bindings
if condition_binding_ids.contains(&main_param) {
eprintln!(
"[cf_loop/joinir] Phase 177-3: Skipping override for condition_binding {:?} ('{}')",
main_param, carrier_name
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 177-3: Skipping override for condition_binding {:?} ('{}')",
main_param, carrier_name
),
verbose,
);
continue;
}
eprintln!(
"[DEBUG-177] Phase 33-21: REMAP main param[{}] {:?}{:?} ('{}')",
idx, main_param, entry.phi_dst, carrier_name
trace.stderr_if(
&format!(
"[DEBUG-177] Phase 33-21: REMAP main param[{}] {:?}{:?} ('{}')",
idx, main_param, entry.phi_dst, carrier_name
),
verbose,
);
remapper.set_value(main_param, entry.phi_dst);
}
@ -466,9 +522,12 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
continue;
}
// This is a body-only carrier - remap it to PHI dst
eprintln!(
"[cf_loop/joinir] Phase 177-3-B: Body-only carrier '{}': JoinIR {:?} → PHI {:?}",
carrier_name, binding.join_value, entry.phi_dst
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 177-3-B: Body-only carrier '{}': JoinIR {:?} → PHI {:?}",
carrier_name, binding.join_value, entry.phi_dst
),
verbose,
);
remapper.set_value(binding.join_value, entry.phi_dst);
}
@ -476,22 +535,31 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Map loop_step's parameters
// DEBUG-177: Always log function_params keys to diagnose multi-carrier issue
eprintln!(
"[DEBUG-177] Phase 33-21: function_params keys: {:?}",
function_params.keys().collect::<Vec<_>>()
trace.stderr_if(
&format!(
"[DEBUG-177] Phase 33-21: function_params keys: {:?}",
function_params.keys().collect::<Vec<_>>()
),
verbose,
);
if function_params.get(loop_step_func_name).is_none() {
eprintln!(
"[cf_loop/joinir] WARNING: function_params.get('{}') returned None. Available keys: {:?}",
loop_step_func_name,
function_params.keys().collect::<Vec<_>>()
trace.stderr_if(
&format!(
"[cf_loop/joinir] WARNING: function_params.get('{}') returned None. Available keys: {:?}",
loop_step_func_name,
function_params.keys().collect::<Vec<_>>()
),
verbose,
);
}
if let Some(loop_step_params) = function_params.get(loop_step_func_name) {
// DEBUG-177: Always log loop_step params
eprintln!(
"[DEBUG-177] Phase 33-21: loop_step ({}) params: {:?}",
loop_step_func_name, loop_step_params
trace.stderr_if(
&format!(
"[DEBUG-177] Phase 33-21: loop_step ({}) params: {:?}",
loop_step_func_name, loop_step_params
),
verbose,
);
// Phase 177-FIX: Process loop_step params but skip if already mapped
//
@ -501,9 +569,12 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
for loop_step_param in loop_step_params {
// Phase 177-3: Don't override condition_bindings
if condition_binding_ids.contains(loop_step_param) {
eprintln!(
"[DEBUG-177] Phase 177-FIX: Skipping condition_binding {:?}",
loop_step_param
trace.stderr_if(
&format!(
"[DEBUG-177] Phase 177-FIX: Skipping condition_binding {:?}",
loop_step_param
),
verbose,
);
continue;
}
@ -517,9 +588,12 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
.any(|(name, _)| name == &cb.name)
});
if already_mapped {
eprintln!(
"[DEBUG-177] Phase 177-FIX: Skipping {:?} (already mapped by Phase 177-3-B)",
loop_step_param
trace.stderr_if(
&format!(
"[DEBUG-177] Phase 177-FIX: Skipping {:?} (already mapped by Phase 177-3-B)",
loop_step_param
),
verbose,
);
continue;
}
@ -537,9 +611,12 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
loop_header_phi_info.get_carrier_at_index(param_idx),
loop_header_phi_info.get_entry_at_index(param_idx),
) {
eprintln!(
"[DEBUG-177] Phase 177-STRUCT-2: REMAP loop_step param[{}] {:?}{:?} (carrier '{}')",
param_idx, loop_step_param, entry.phi_dst, carrier_name
trace.stderr_if(
&format!(
"[DEBUG-177] Phase 177-STRUCT-2: REMAP loop_step param[{}] {:?}{:?} (carrier '{}')",
param_idx, loop_step_param, entry.phi_dst, carrier_name
),
verbose,
);
remapper.set_value(*loop_step_param, entry.phi_dst);
}
@ -556,15 +633,17 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Phase 177-3: Don't override condition_bindings
if !condition_binding_ids.contains(&ValueId(0)) {
remapper.set_value(ValueId(0), phi_dst);
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 33-16 fallback: Override remap ValueId(0) → {:?} (PHI dst)",
phi_dst
);
}
),
debug,
);
} else {
eprintln!(
"[cf_loop/joinir] Phase 177-3 fallback: Skipping override for condition_binding ValueId(0)"
trace.stderr_if(
"[cf_loop/joinir] Phase 177-3 fallback: Skipping override for condition_binding ValueId(0)",
verbose,
);
}
}
@ -581,28 +660,35 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Phase 177-3: Don't override condition_bindings
if !condition_binding_ids.contains(&join_value_id) {
remapper.set_value(join_value_id, entry.phi_dst);
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 33-20 fallback: Override remap {:?}{:?} (carrier '{}' PHI dst)",
join_value_id, entry.phi_dst, carrier_name
);
}
),
debug,
);
} else {
eprintln!(
"[cf_loop/joinir] Phase 177-3 fallback: Skipping override for condition_binding {:?} ('{}')",
join_value_id, carrier_name
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 177-3 fallback: Skipping override for condition_binding {:?} ('{}')",
join_value_id, carrier_name
),
verbose,
);
}
}
}
// Phase 177-3 DEBUG: Check remapper after Phase 33-21 overrides
eprintln!("[DEBUG-177] === Remapper state after Phase 33-21 ===");
trace.stderr_if("[DEBUG-177] === Remapper state after Phase 33-21 ===", verbose);
for binding in &boundary.condition_bindings {
let lookup_result = remapper.get_value(binding.join_value);
eprintln!(
"[DEBUG-177] '{}': JoinIR {:?}{:?} (after 33-21)",
binding.name, binding.join_value, lookup_result
trace.stderr_if(
&format!(
"[DEBUG-177] '{}': JoinIR {:?}{:?} (after 33-21)",
binding.name, binding.join_value, lookup_result
),
verbose,
);
}
@ -630,12 +716,13 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// By now, instruction_rewriter has set latch_incoming for all carriers.
// We can finalize the PHIs and insert them into the header block.
if !loop_header_phi_info.carrier_phis.is_empty() {
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 4.5: Finalizing {} header PHIs",
loop_header_phi_info.carrier_phis.len()
);
}
),
debug,
);
LoopHeaderPhiBuilder::finalize(builder, &loop_header_phi_info, debug)?;
}
@ -673,12 +760,16 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// The exit PHI correctly merges values from both exit paths, giving us the final result.
let carrier_phis = &exit_carrier_phis;
if debug && !carrier_phis.is_empty() {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 246-EX: Using EXIT PHI dsts for variable_map (not header): {:?}",
carrier_phis.iter().map(|(n, v)| (n.as_str(), v)).collect::<Vec<_>>()
);
}
carrier_phis
.iter()
.map(|(n, v)| (n.as_str(), v))
.collect::<Vec<_>>()
),
debug && !carrier_phis.is_empty(),
);
// Phase 6: Reconnect boundary (if specified)
// Phase 197-B: Pass remapper to enable per-carrier exit value lookup
@ -694,40 +785,49 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// The header_block in loop_header_phi_info is the remapped entry block
let entry_block = loop_header_phi_info.header_block;
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Entry block (from loop_header_phi_info): {:?}",
entry_block
);
eprintln!(
),
debug,
);
trace.stderr_if(
&format!(
"[cf_loop/joinir] Current block before emit_jump: {:?}",
builder.current_block
);
eprintln!(
),
debug,
);
trace.stderr_if(
&format!(
"[cf_loop/joinir] Jumping to entry block: {:?}",
entry_block
);
}
),
debug,
);
crate::mir::builder::emission::branch::emit_jump(builder, entry_block)?;
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] After emit_jump, current_block: {:?}",
builder.current_block
);
}
),
debug,
);
// Switch to exit block for subsequent code
builder.start_new_block(exit_block_id)?;
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 189: Merge complete: {} functions merged, continuing from {:?}",
mir_module.functions.len(),
exit_block_id
);
}
),
debug,
);
// Phase 200-3: Verify JoinIR contracts (debug only)
#[cfg(debug_assertions)]
@ -742,21 +842,23 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
boundary,
);
}
if debug {
eprintln!("[cf_loop/joinir] Phase 200-3: Contract verification passed");
}
trace.stderr_if(
"[cf_loop/joinir] Phase 200-3: Contract verification passed",
debug,
);
}
}
// Phase 201-A: Clear reserved ValueIds after merge completes
// Future loops will set their own reserved IDs
if !builder.comp_ctx.reserved_value_ids.is_empty() {
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 201-A: Clearing reserved_value_ids (was {:?})",
builder.comp_ctx.reserved_value_ids
);
}
),
debug,
);
builder.comp_ctx.reserved_value_ids.clear();
}
@ -777,12 +879,13 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
if let Some(binding) = loop_var_binding {
if binding.join_exit_value == expr_result_id {
// expr_result is the loop variable! Use exit_phi_result_id
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 246-EX-FIX: expr_result {:?} is loop variable '{}', using exit_phi_result_id {:?}",
expr_result_id, loop_var_name, exit_phi_result_id
);
}
),
debug,
);
exit_phi_result_id
} else {
// expr_result is not the loop variable, resolve as carrier
@ -823,21 +926,23 @@ 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!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 246-EX-FIX: Returning expr_result_value {:?}",
resolved
);
}
),
debug,
);
Ok(Some(resolved))
} else {
// Fallback: return exit_phi_result_id (for legacy patterns or carrier-only loops)
if debug && exit_phi_result_id.is_some() {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 221-R: Returning exit_phi_result_id (fallback): {:?}",
exit_phi_result_id
);
}
),
debug && exit_phi_result_id.is_some(),
);
Ok(exit_phi_result_id)
}
}
@ -854,13 +959,15 @@ fn remap_values(
reserved_ids: &std::collections::HashSet<ValueId>,
debug: bool,
) -> Result<(), String> {
if debug {
eprintln!(
let trace = trace::trace();
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 3: Remapping {} ValueIds (reserved: {})",
used_values.len(),
reserved_ids.len()
);
}
),
debug,
);
for old_value in used_values {
// Phase 201-A: Allocate new ValueId, skipping reserved PHI dsts
@ -870,21 +977,23 @@ fn remap_values(
break candidate;
}
// Skip reserved ID - will try next one
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 201-A: Skipping reserved PHI dst {:?}",
candidate
);
}
),
debug,
);
};
remapper.set_value(*old_value, new_value);
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Value remap: {:?}{:?}",
old_value, new_value
);
}
),
debug,
);
}
Ok(())

View File

@ -27,8 +27,12 @@ pub(super) fn collect_values(
),
String,
> {
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
if debug {
eprintln!("[cf_loop/joinir] Phase 189: Collecting value IDs from all functions");
trace.stderr_if(
"[cf_loop/joinir] Phase 189: Collecting value IDs from all functions",
true,
);
}
let mut used_values: BTreeSet<ValueId> = BTreeSet::new();
@ -63,9 +67,12 @@ pub(super) fn collect_values(
// Without this, subsequent instructions referencing dst will fail
used_values.insert(*dst);
if debug {
eprintln!(
"[cf_loop/joinir] Found function name constant: {:?} = '{}'",
dst, s
trace.stderr_if(
&format!(
"[cf_loop/joinir] Found function name constant: {:?} = '{}'",
dst, s
),
true,
);
}
}
@ -95,9 +102,12 @@ pub(super) fn collect_values(
}
if debug {
eprintln!(
"[cf_loop/joinir] Collected {} unique values",
used_values.len()
trace.stderr_if(
&format!(
"[cf_loop/joinir] Collected {} unique values",
used_values.len()
),
true,
);
}

View File

@ -62,25 +62,30 @@ impl MirBuilder {
Err(msg)
} else {
// Debug mode: log only
eprintln!("{}", msg);
super::trace::trace().dev("loop_canonicalizer/parity", &msg);
Ok(())
}
} else {
// Patterns match - success!
eprintln!(
"[loop_canonicalizer/PARITY] OK in function '{}': \
canonical and actual agree on {:?}",
func_name, canonical_pattern
super::trace::trace().dev(
"loop_canonicalizer/parity",
&format!(
"[loop_canonicalizer/PARITY] OK in function '{}': canonical and actual agree on {:?}",
func_name, canonical_pattern
),
);
Ok(())
}
} else {
// Canonicalizer failed (Fail-Fast)
// Log but don't error - router might still handle it
eprintln!(
"[loop_canonicalizer/PARITY] Canonicalizer failed for '{}': {}",
func_name,
decision.notes.join("; ")
super::trace::trace().dev(
"loop_canonicalizer/parity",
&format!(
"[loop_canonicalizer/PARITY] Canonicalizer failed for '{}': {}",
func_name,
decision.notes.join("; ")
),
);
Ok(())
};

View File

@ -0,0 +1,92 @@
//! Phase 92 P3: BodyLocal policy routing (Box)
//!
//! Purpose: make the "promotion vs read-only slot vs reject" decision explicit,
//! so Pattern2 code does not look like it "falls back" after failure.
use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
use crate::mir::join_ir::lowering::common::body_local_slot::{
ReadOnlyBodyLocalSlot, ReadOnlyBodyLocalSlotBox,
};
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
use crate::mir::loop_pattern_detection::loop_body_cond_promoter::{
ConditionPromotionRequest, ConditionPromotionResult, LoopBodyCondPromoter,
};
use crate::mir::loop_pattern_detection::loop_condition_scope::{CondVarScope, LoopConditionScope};
/// Explicit routing policy for LoopBodyLocal variables used in Pattern2 conditions.
///
/// This is a "route" decision (not a fallback): we choose exactly one of the supported
/// strategies and reject otherwise.
pub enum BodyLocalPolicyDecision {
UsePromotion {
promoted_carrier: CarrierInfo,
promoted_var: String,
carrier_name: String,
},
UseReadOnlySlot(ReadOnlyBodyLocalSlot),
Reject { reason: String, vars: Vec<String> },
}
pub fn classify_for_pattern2(
_builder: &MirBuilder,
loop_var_name: &str,
scope: &LoopScopeShape,
break_condition_node: &ASTNode,
cond_scope: &LoopConditionScope,
body: &[ASTNode],
) -> BodyLocalPolicyDecision {
let vars: Vec<String> = cond_scope
.vars
.iter()
.filter(|v| v.scope == CondVarScope::LoopBodyLocal)
.map(|v| v.name.clone())
.collect();
let promotion_req = ConditionPromotionRequest {
loop_param_name: loop_var_name,
cond_scope,
scope_shape: Some(scope),
break_cond: Some(break_condition_node),
continue_cond: None,
loop_body: body,
#[cfg(feature = "normalized_dev")]
binding_map: Some(&_builder.binding_map),
};
match LoopBodyCondPromoter::try_promote_for_condition(promotion_req) {
ConditionPromotionResult::Promoted {
carrier_info: promoted_carrier,
promoted_var,
carrier_name,
} => BodyLocalPolicyDecision::UsePromotion {
promoted_carrier,
promoted_var,
carrier_name,
},
ConditionPromotionResult::CannotPromote { reason, .. } => {
match extract_body_local_inits_for_conditions(&vars, body) {
Ok(Some(slot)) => BodyLocalPolicyDecision::UseReadOnlySlot(slot),
Ok(None) => BodyLocalPolicyDecision::Reject { reason, vars },
Err(slot_err) => BodyLocalPolicyDecision::Reject {
reason: format!("{reason}; read-only-slot rejected: {slot_err}"),
vars,
},
}
}
}
}
fn extract_body_local_inits_for_conditions(
body_local_names_in_conditions: &[String],
body: &[ASTNode],
) -> Result<Option<ReadOnlyBodyLocalSlot>, String> {
if body_local_names_in_conditions.is_empty() {
return Ok(None);
}
Ok(Some(ReadOnlyBodyLocalSlotBox::extract_single(
body_local_names_in_conditions,
body,
)?))
}

View File

@ -200,7 +200,10 @@ impl CommonPatternInitializer {
}
UpdateRhs::Other => {
// Phase 188: Complex update (method call, nested BinOp) - reject
eprintln!("[common_init/check_carriers] Phase 188: Complex update detected (UpdateRhs::Other), rejecting pattern");
crate::mir::builder::control_flow::joinir::trace::trace().dev(
"common_init/check_carriers",
"Phase 188: Complex update detected (UpdateRhs::Other), rejecting pattern",
);
return false;
}
}

View File

@ -175,16 +175,14 @@ impl ConditionEnvBuilder {
variable_map: &BTreeMap<String, ValueId>,
space: &mut JoinValueSpace,
) -> (ConditionEnv, ValueId) {
use std::env;
let debug = env::var("NYASH_CAPTURE_DEBUG").is_ok();
if debug {
eprintln!(
"[capture/env_builder] Building ConditionEnv with {} captured vars",
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
trace.capture(
"env_builder",
&format!(
"Building ConditionEnv with {} captured vars",
captured.vars.len()
);
}
),
);
// Step 1: Build base ConditionEnv with loop params using v2 API (Phase 222.5-B)
let (mut env, loop_var_join_id) = Self::build_loop_param_only_v2(loop_var_name, space);
@ -195,9 +193,13 @@ impl ConditionEnvBuilder {
let host_id = match variable_map.get(&var.name) {
Some(&id) => id,
None => {
if debug {
eprintln!("[capture/env_builder] WARNING: Captured var '{}' not found in variable_map, skipping", var.name);
}
trace.capture(
"env_builder",
&format!(
"WARNING: Captured var '{}' not found in variable_map, skipping",
var.name
),
);
continue;
}
};
@ -217,12 +219,13 @@ impl ConditionEnvBuilder {
// 2d: Add to ConditionEnv.captured map
env.captured.insert(var.name.clone(), join_id);
if debug {
eprintln!(
"[capture/env_builder] Added captured var '{}': host={:?}, join={:?}",
trace.capture(
"env_builder",
&format!(
"Added captured var '{}': host={:?}, join={:?}",
var.name, host_id, join_id
);
}
),
);
}
// Step 3: Debug guard - Condition params must NOT be in PHI candidates
@ -236,14 +239,15 @@ impl ConditionEnvBuilder {
}
}
if debug {
let param_count = env.iter().count();
eprintln!(
"[capture/env_builder] Final ConditionEnv: {} params, {} captured",
let param_count = env.iter().count();
trace.capture(
"env_builder",
&format!(
"Final ConditionEnv: {} params, {} captured",
param_count,
env.captured.len()
);
}
),
);
(env, loop_var_join_id)
}

View File

@ -46,6 +46,7 @@
//! - Extracted from ast_feature_extractor for improved modularity
pub(in crate::mir::builder) mod ast_feature_extractor;
pub(in crate::mir::builder) mod body_local_policy; // Phase 92 P3: promotion vs slot routing
pub(in crate::mir::builder) mod escape_pattern_recognizer; // Phase 91 P5b
pub(in crate::mir::builder) mod common_init;
pub(in crate::mir::builder) mod condition_env_builder;

View File

@ -5,18 +5,36 @@ use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, CarrierInit};
use crate::mir::join_ir::lowering::condition_env::{ConditionBinding, ConditionEnv};
use super::body_local_policy::{classify_for_pattern2, BodyLocalPolicyDecision};
use crate::mir::join_ir::lowering::common::body_local_slot::ReadOnlyBodyLocalSlot;
use crate::mir::join_ir::lowering::debug_output_box::DebugOutputBox;
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
use crate::mir::join_ir::lowering::loop_with_break_minimal::LoopWithBreakLoweringInputs;
use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
use crate::mir::loop_pattern_detection::error_messages;
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
use crate::mir::ValueId;
use std::collections::BTreeMap;
fn log_pattern2(verbose: bool, tag: &str, message: impl AsRef<str>) {
if verbose {
eprintln!("[joinir/pattern2/{tag}] {}", message.as_ref());
struct Pattern2DebugLog {
verbose: bool,
debug: DebugOutputBox,
}
impl Pattern2DebugLog {
fn new(verbose: bool) -> Self {
Self {
verbose,
debug: DebugOutputBox::new_with_enabled("joinir/pattern2", verbose),
}
}
fn log(&self, tag: &str, message: impl AsRef<str>) {
if self.verbose {
self.debug.log(tag, message.as_ref());
}
}
}
@ -30,6 +48,11 @@ struct Pattern2Inputs {
env: ConditionEnv,
condition_bindings: Vec<ConditionBinding>,
body_local_env: LoopBodyLocalEnv,
/// Phase 92 P3: Allow-list of LoopBodyLocal variable names permitted in conditions.
/// This must stay minimal (1 variable) and is validated by ReadOnlyBodyLocalSlotBox.
allowed_body_locals_for_conditions: Vec<String>,
/// Phase 92 P3: Diagnostics / debug metadata for the allow-listed variable.
read_only_body_local_slot: Option<ReadOnlyBodyLocalSlot>,
break_condition_node: ASTNode,
}
@ -41,6 +64,7 @@ fn prepare_pattern2_inputs(
ctx: &super::pattern_pipeline::PatternPipelineContext,
verbose: bool,
) -> Result<Pattern2Inputs, String> {
let log = Pattern2DebugLog::new(verbose);
use super::condition_env_builder::ConditionEnvBuilder;
use crate::mir::loop_pattern_detection::function_scope_capture::{
analyze_captured_vars_v2, CapturedEnv,
@ -51,8 +75,7 @@ fn prepare_pattern2_inputs(
let carrier_info = ctx.carrier_info.clone();
let scope = ctx.loop_scope.clone();
log_pattern2(
verbose,
log.log(
"init",
format!(
"PatternPipelineContext: loop_var='{}', loop_var_id={:?}, carriers={}",
@ -63,8 +86,7 @@ fn prepare_pattern2_inputs(
);
// Capture analysis
log_pattern2(
verbose,
log.log(
"phase200c",
format!(
"fn_body is {}",
@ -72,23 +94,20 @@ fn prepare_pattern2_inputs(
),
);
let captured_env = if let Some(fn_body_ref) = fn_body {
log_pattern2(
verbose,
log.log(
"phase200c",
format!("fn_body has {} nodes", fn_body_ref.len()),
);
analyze_captured_vars_v2(fn_body_ref, condition, body, &scope)
} else {
log_pattern2(
verbose,
log.log(
"phase200c",
"fn_body is None, using empty CapturedEnv",
);
CapturedEnv::new()
};
if verbose {
log_pattern2(
verbose,
log.log(
"capture",
format!(
"Phase 200-C: Captured {} variables",
@ -96,8 +115,7 @@ fn prepare_pattern2_inputs(
),
);
for var in &captured_env.vars {
log_pattern2(
verbose,
log.log(
"capture",
format!(
" '{}': host_id={:?}, immutable={}",
@ -123,8 +141,7 @@ fn prepare_pattern2_inputs(
// Phase 136 Step 4/7: Use binding_ctx for lookup
if let Some(loop_var_bid) = builder.binding_ctx.lookup(&loop_var_name) {
env.register_loop_var_binding(loop_var_bid, _loop_var_join_id);
log_pattern2(
verbose,
log.log(
"phase79",
format!(
"Registered loop var BindingId: '{}' BindingId({}) → ValueId({})",
@ -133,8 +150,7 @@ fn prepare_pattern2_inputs(
);
}
log_pattern2(
verbose,
log.log(
"phase201",
format!(
"Using JoinValueSpace: loop_var '{}' → {:?}",
@ -153,8 +169,7 @@ fn prepare_pattern2_inputs(
host_value: host_id,
join_value: join_id,
});
log_pattern2(
verbose,
log.log(
"capture",
format!(
"Phase 201: Added captured '{}': host={:?}, join={:?}",
@ -165,8 +180,7 @@ fn prepare_pattern2_inputs(
}
let body_local_env = LoopBodyLocalEnv::new();
log_pattern2(
verbose,
log.log(
"body-local",
format!(
"Phase 201: Created empty body-local environment (param_count={})",
@ -174,13 +188,11 @@ fn prepare_pattern2_inputs(
),
);
if verbose {
log_pattern2(
verbose,
log.log(
"cond-env",
format!("Phase 201: ConditionEnv contains {} variables:", env.len()),
);
log_pattern2(
verbose,
log.log(
"cond-env",
format!(
" Loop param '{}' → JoinIR {:?}",
@ -189,14 +201,12 @@ fn prepare_pattern2_inputs(
),
);
if !condition_bindings.is_empty() {
log_pattern2(
verbose,
log.log(
"cond-env",
format!(" {} condition-only bindings:", condition_bindings.len()),
);
for binding in &condition_bindings {
log_pattern2(
verbose,
log.log(
"cond-env",
format!(
" '{}': HOST {:?} → JoinIR {:?}",
@ -205,7 +215,7 @@ fn prepare_pattern2_inputs(
);
}
} else {
log_pattern2(verbose, "cond-env", " No condition-only variables");
log.log("cond-env", " No condition-only variables");
}
}
@ -238,6 +248,8 @@ fn prepare_pattern2_inputs(
env,
condition_bindings,
body_local_env,
allowed_body_locals_for_conditions: Vec::new(),
read_only_body_local_slot: None,
break_condition_node,
})
}
@ -251,9 +263,6 @@ fn promote_and_prepare_carriers(
verbose: bool,
) -> Result<(), String> {
use crate::mir::join_ir::lowering::digitpos_condition_normalizer::DigitPosConditionNormalizer;
use crate::mir::loop_pattern_detection::loop_body_cond_promoter::{
ConditionPromotionRequest, ConditionPromotionResult, LoopBodyCondPromoter,
};
use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScopeBox;
let cond_scope = LoopConditionScopeBox::analyze(
@ -262,23 +271,20 @@ fn promote_and_prepare_carriers(
Some(&inputs.scope),
);
let log = Pattern2DebugLog::new(verbose);
let mut promoted_pairs: Vec<(String, String)> = Vec::new();
if cond_scope.has_loop_body_local() {
let promotion_req = ConditionPromotionRequest {
loop_param_name: &inputs.loop_var_name,
cond_scope: &cond_scope,
scope_shape: Some(&inputs.scope),
break_cond: Some(&inputs.break_condition_node),
continue_cond: None,
loop_body: body,
#[cfg(feature = "normalized_dev")]
binding_map: Some(&builder.binding_map),
};
match LoopBodyCondPromoter::try_promote_for_condition(promotion_req) {
ConditionPromotionResult::Promoted {
carrier_info: promoted_carrier,
match classify_for_pattern2(
builder,
&inputs.loop_var_name,
&inputs.scope,
&inputs.break_condition_node,
&cond_scope,
body,
) {
BodyLocalPolicyDecision::UsePromotion {
promoted_carrier,
promoted_var,
carrier_name,
} => {
@ -312,8 +318,7 @@ fn promote_and_prepare_carriers(
inputs.carrier_info.merge_from(&promoted_carrier);
}
log_pattern2(
verbose,
log.log(
"cond_promoter",
format!(
"LoopBodyLocal '{}' promoted to carrier '{}'",
@ -326,8 +331,7 @@ fn promote_and_prepare_carriers(
.promoted_loopbodylocals
.push(promoted_var.clone());
log_pattern2(
verbose,
log.log(
"cond_promoter",
format!(
"Merged carrier '{}' into CarrierInfo (total carriers: {})",
@ -335,8 +339,7 @@ fn promote_and_prepare_carriers(
inputs.carrier_info.carrier_count()
),
);
log_pattern2(
verbose,
log.log(
"cond_promoter",
format!(
"Phase 224: Recorded promoted variable '{}' in carrier_info.promoted_loopbodylocals",
@ -346,17 +349,15 @@ fn promote_and_prepare_carriers(
if let Some(helper) = inputs.carrier_info.trim_helper() {
if helper.is_safe_trim() {
log_pattern2(verbose, "cond_promoter", "Safe Trim pattern detected");
log_pattern2(
verbose,
log.log("cond_promoter", "Safe Trim pattern detected");
log.log(
"cond_promoter",
format!(
"Carrier: '{}', original var: '{}', whitespace chars: {:?}",
helper.carrier_name, helper.original_var, helper.whitespace_chars
),
);
log_pattern2(
verbose,
log.log(
"phase133",
format!(
"Phase 133 P1: Trim promotion deferred to TrimLoopLowerer (SSOT for env/join_id)"
@ -379,8 +380,7 @@ fn promote_and_prepare_carriers(
&carrier_name,
);
log_pattern2(
verbose,
log.log(
"phase224e",
format!(
"Normalized break condition for promoted variable '{}' → carrier '{}'",
@ -389,7 +389,18 @@ fn promote_and_prepare_carriers(
);
}
}
ConditionPromotionResult::CannotPromote { reason, vars } => {
BodyLocalPolicyDecision::UseReadOnlySlot(slot) => {
log.log(
"body_local_slot",
format!(
"Phase 92 P3: BodyLocalPolicy=UseReadOnlySlot var='{}' (decl@{}, break_if@{}, init={:?})",
slot.name, slot.decl_stmt_index, slot.break_guard_stmt_index, &slot.init_expr
),
);
inputs.allowed_body_locals_for_conditions = vec![slot.name.clone()];
inputs.read_only_body_local_slot = Some(slot);
}
BodyLocalPolicyDecision::Reject { reason, vars } => {
return Err(error_messages::format_error_pattern2_promotion_failed(
&vars, &reason,
));
@ -397,8 +408,7 @@ fn promote_and_prepare_carriers(
}
}
log_pattern2(
verbose,
log.log(
"phase224d",
format!(
"Allocating join_ids for {} carriers",
@ -417,8 +427,7 @@ fn promote_and_prepare_carriers(
inputs
.env
.register_condition_binding(binding_id, carrier_join_id);
log_pattern2(
verbose,
log.log(
"phase79",
format!(
"Registered condition-only carrier '{}' BindingId({}) → ValueId({})",
@ -430,8 +439,7 @@ fn promote_and_prepare_carriers(
inputs
.env
.register_carrier_binding(binding_id, carrier_join_id);
log_pattern2(
verbose,
log.log(
"phase79",
format!(
"Registered loop-state carrier '{}' BindingId({}) → ValueId({})",
@ -441,8 +449,7 @@ fn promote_and_prepare_carriers(
}
}
}
log_pattern2(
verbose,
log.log(
"phase224d",
format!(
"Allocated carrier '{}' param ID: {:?}",
@ -463,8 +470,7 @@ fn promote_and_prepare_carriers(
)
})?;
inputs.env.insert(promoted_var.clone(), join_id);
log_pattern2(
verbose,
log.log(
"phase229",
format!(
"Resolved promoted '{}' → carrier '{}' (join_id={:?})",
@ -492,25 +498,13 @@ fn promote_and_prepare_carriers(
.lower(&inputs.break_condition_node)
{
Ok(_value_id) => {
log_pattern2(
verbose,
"phase231",
"ExprLowerer successfully validated break condition",
);
log.log("phase231", "ExprLowerer successfully validated break condition");
}
Err(ExprLoweringError::UnsupportedNode(msg)) => {
log_pattern2(
verbose,
"phase231",
format!("ExprLowerer fallback (unsupported): {}", msg),
);
log.log("phase231", format!("ExprLowerer fallback (unsupported): {}", msg));
}
Err(e) => {
log_pattern2(
verbose,
"phase231",
format!("ExprLowerer validation error: {}", e),
);
log.log("phase231", format!("ExprLowerer validation error: {}", e));
}
}
}
@ -525,6 +519,7 @@ fn apply_trim_and_normalize(
inputs: &mut Pattern2Inputs,
verbose: bool,
) -> Result<(ASTNode, Option<Vec<ASTNode>>), String> {
let log = Pattern2DebugLog::new(verbose);
let mut alloc_join_value = || inputs.join_value_space.alloc_param();
let effective_break_condition = if let Some(trim_result) =
@ -538,11 +533,7 @@ fn apply_trim_and_normalize(
&mut inputs.carrier_info,
&mut alloc_join_value,
)? {
log_pattern2(
verbose,
"trim",
"TrimLoopLowerer processed Trim pattern successfully",
);
log.log("trim", "TrimLoopLowerer processed Trim pattern successfully");
inputs.carrier_info = trim_result.carrier_info;
inputs
.condition_bindings
@ -550,8 +541,7 @@ fn apply_trim_and_normalize(
for binding in &trim_result.condition_bindings {
inputs.env.insert(binding.name.clone(), binding.join_value);
}
log_pattern2(
verbose,
log.log(
"trim",
format!(
"Extended condition_bindings with {} Trim bindings",
@ -577,8 +567,7 @@ fn apply_trim_and_normalize(
new_assign,
temp_name,
} => {
log_pattern2(
verbose,
log.log(
"phase192",
format!(
"Normalized complex addend: temp='{}' inserted before update",
@ -625,17 +614,14 @@ fn collect_body_local_variables(
) -> Vec<(String, ValueId)> {
let mut locals = Vec::new();
let verbose = crate::config::env::joinir_dev_enabled();
let log = Pattern2DebugLog::new(verbose);
for node in body {
if let ASTNode::Local { variables, .. } = node {
// Local declaration can have multiple variables (e.g., local a, b, c)
for name in variables {
let value_id = alloc_join_value();
locals.push((name.clone(), value_id));
log_pattern2(
verbose,
"body-local",
format!("Collected local '{}' → {:?}", name, value_id),
);
log.log("body-local", format!("Collected local '{}' → {:?}", name, value_id));
}
}
}
@ -791,6 +777,7 @@ impl MirBuilder {
use crate::mir::join_ir::lowering::loop_with_break_minimal::lower_loop_with_break_minimal;
let verbose = debug || crate::config::env::joinir_dev_enabled();
let log = Pattern2DebugLog::new(verbose);
// Phase 195: Use unified trace
trace::trace().debug("pattern2", "Calling Pattern 2 minimal lowerer");
@ -814,8 +801,7 @@ impl MirBuilder {
&inputs.carrier_info.carriers,
);
log_pattern2(
verbose,
log.log(
"updates",
format!(
"Phase 176-3: Analyzed {} carrier updates",
@ -826,8 +812,7 @@ impl MirBuilder {
let original_carrier_count = inputs.carrier_info.carriers.len();
filter_carriers_for_updates(&mut inputs.carrier_info, &carrier_updates);
log_pattern2(
verbose,
log.log(
"updates",
format!(
"Phase 176-4: Filtered carriers: {}{} (kept only carriers with updates/condition-only/loop-local-zero)",
@ -851,8 +836,7 @@ impl MirBuilder {
join_value,
});
} else {
log_pattern2(
verbose,
log.log(
"updates",
format!(
"Phase 247-EX: Skipping host binding for loop-local carrier '{}' (init=LoopLocalZero)",
@ -861,8 +845,7 @@ impl MirBuilder {
);
}
log_pattern2(
verbose,
log.log(
"updates",
format!(
"Phase 176-5: Added body-only carrier '{}' to ConditionEnv: HOST {:?} → JoinIR {:?}",
@ -872,8 +855,7 @@ impl MirBuilder {
}
}
log_pattern2(
verbose,
log.log(
"before_lowerer",
format!(
"About to call lower_loop_with_break_minimal with carrier_info.loop_var_name='{}'",
@ -881,22 +863,31 @@ impl MirBuilder {
),
);
let (join_module, fragment_meta) = match lower_loop_with_break_minimal(
inputs.scope,
condition,
&effective_break_condition,
&inputs.env,
&inputs.carrier_info,
&carrier_updates,
analysis_body, // Phase 191/192: Pass normalized body AST for init lowering
Some(&mut inputs.body_local_env), // Phase 191: Pass mutable body-local environment
&mut inputs.join_value_space, // Phase 201: Unified ValueId allocation (Local region)
skeleton, // Phase 92 P0-3: Pass skeleton for ConditionalStep support
) {
Ok((module, meta)) => (module, meta),
Err(e) => {
// Phase 195: Use unified trace
trace::trace().debug("pattern2", &format!("Pattern 2 lowerer failed: {}", e));
let body_local_env = &mut inputs.body_local_env;
let join_value_space = &mut inputs.join_value_space;
let lowering_inputs = LoopWithBreakLoweringInputs {
scope: inputs.scope,
condition,
break_condition: &effective_break_condition,
env: &inputs.env,
carrier_info: &inputs.carrier_info,
carrier_updates: &carrier_updates,
body_ast: analysis_body,
body_local_env: Some(body_local_env),
allowed_body_locals_for_conditions: if inputs.allowed_body_locals_for_conditions.is_empty() {
None
} else {
Some(inputs.allowed_body_locals_for_conditions.as_slice())
},
join_value_space,
skeleton,
};
let (join_module, fragment_meta) = match lower_loop_with_break_minimal(lowering_inputs) {
Ok((module, meta)) => (module, meta),
Err(e) => {
// Phase 195: Use unified trace
trace::trace().debug("pattern2", &format!("Pattern 2 lowerer failed: {}", e));
return Err(format!("[cf_loop/pattern2] Lowering failed: {}", e));
}
};
@ -921,8 +912,7 @@ impl MirBuilder {
host_input_values.push(carrier.host_id);
}
log_pattern2(
verbose,
log.log(
"boundary",
format!(
"Phase 176-3: Boundary inputs - {} JoinIR slots, {} host values",

View File

@ -137,9 +137,14 @@ impl MirBuilder {
if let Some(bid) = self.binding_ctx.lookup(&loop_var_name) {
cond_env.register_loop_var_binding(bid, _loop_var_join_id);
if debug {
eprintln!(
"[phase80/p3] Registered loop var '{}' BindingId({}) -> ValueId({})",
loop_var_name, bid.0, _loop_var_join_id.0
super::super::trace::trace().emit_if(
"phase80",
"p3",
&format!(
"Registered loop var '{}' BindingId({}) -> ValueId({})",
loop_var_name, bid.0, _loop_var_join_id.0
),
true,
);
}
}
@ -152,9 +157,14 @@ impl MirBuilder {
if let Some(bid) = self.binding_ctx.lookup(&binding.name) {
cond_env.register_condition_binding(bid, binding.join_value);
if debug {
eprintln!(
"[phase80/p3] Registered condition binding '{}' BindingId({}) -> ValueId({})",
binding.name, bid.0, binding.join_value.0
super::super::trace::trace().emit_if(
"phase80",
"p3",
&format!(
"Registered condition binding '{}' BindingId({}) -> ValueId({})",
binding.name, bid.0, binding.join_value.0
),
true,
);
}
}
@ -202,7 +212,8 @@ impl MirBuilder {
&ctx.carrier_info,
&condition_binding_names,
) {
eprintln!("[phase64/ownership] Consistency check failed: {}", e);
super::super::trace::trace()
.dev("phase64/ownership", &format!("Consistency check failed: {}", e));
return Err(e);
}
@ -217,9 +228,9 @@ impl MirBuilder {
);
}
Err(e) => {
eprintln!(
"[phase64/ownership] Analysis failed (continuing with legacy): {}",
e
super::super::trace::trace().dev(
"phase64/ownership",
&format!("Analysis failed (continuing with legacy): {}", e),
);
// Don't fail - analysis is optional in Phase 64
}

View File

@ -171,6 +171,9 @@ impl TrimLoopLowerer {
carrier_info: &mut CarrierInfo,
alloc_join_value: &mut dyn FnMut() -> ValueId,
) -> Result<Option<TrimLoweringResult>, String> {
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
let verbose = crate::config::env::joinir_dev_enabled() || trace.is_joinir_enabled();
// Phase 180-2: Skeleton implementation
// TODO: Phase 180-3 will implement full logic from Pattern2
@ -178,18 +181,33 @@ impl TrimLoopLowerer {
let cond_scope =
LoopConditionScopeBox::analyze(loop_var_name, &[loop_cond, break_cond], Some(scope));
eprintln!(
"[TrimLoopLowerer] Analyzing condition scope: {} variables",
cond_scope.vars.len()
trace.emit_if(
"trim",
"scope",
&format!(
"Analyzing condition scope: {} variables",
cond_scope.vars.len()
),
verbose,
);
if !cond_scope.has_loop_body_local() {
// Not a Trim pattern - normal loop
eprintln!("[TrimLoopLowerer] No LoopBodyLocal detected, skipping Trim lowering");
trace.emit_if(
"trim",
"scope",
"No LoopBodyLocal detected, skipping Trim lowering",
verbose,
);
return Ok(None);
}
eprintln!("[TrimLoopLowerer] LoopBodyLocal detected in condition scope");
trace.emit_if(
"trim",
"scope",
"LoopBodyLocal detected in condition scope",
verbose,
);
// Phase 183-2: Filter to only condition LoopBodyLocal (skip body-only)
use crate::mir::loop_pattern_detection::loop_condition_scope::CondVarScope;
@ -205,19 +223,27 @@ impl TrimLoopLowerer {
if condition_body_locals.is_empty() {
// All LoopBodyLocal are body-only (not in conditions) → Not a Trim pattern
eprintln!(
"[TrimLoopLowerer] Phase 183: All LoopBodyLocal are body-only (not in conditions), skipping Trim lowering"
trace.emit_if(
"trim",
"phase183",
"All LoopBodyLocal are body-only (not in conditions), skipping Trim lowering",
verbose,
);
return Ok(None);
}
eprintln!(
"[TrimLoopLowerer] Phase 183: Found {} condition LoopBodyLocal variables: {:?}",
condition_body_locals.len(),
condition_body_locals
.iter()
.map(|v| &v.name)
.collect::<Vec<_>>()
trace.emit_if(
"trim",
"phase183",
&format!(
"Found {} condition LoopBodyLocal variables: {:?}",
condition_body_locals.len(),
condition_body_locals
.iter()
.map(|v| &v.name)
.collect::<Vec<_>>()
),
verbose,
);
// Step 2: Try promotion via LoopBodyCarrierPromoter
@ -233,9 +259,14 @@ impl TrimLoopLowerer {
match LoopBodyCarrierPromoter::try_promote(&request) {
PromotionResult::Promoted { trim_info } => {
eprintln!(
"[TrimLoopLowerer] LoopBodyLocal '{}' promoted to carrier '{}'",
trim_info.var_name, trim_info.carrier_name
trace.emit_if(
"trim",
"promote",
&format!(
"LoopBodyLocal '{}' promoted to carrier '{}'",
trim_info.var_name, trim_info.carrier_name
),
verbose,
);
// Step 3: Convert to CarrierInfo and merge
@ -247,10 +278,15 @@ impl TrimLoopLowerer {
let promoted_carrier = trim_info.to_carrier_info();
carrier_info.merge_from(&promoted_carrier);
eprintln!(
"[TrimLoopLowerer] Merged carrier '{}' into CarrierInfo (total carriers: {})",
trim_info.carrier_name,
carrier_info.carrier_count()
trace.emit_if(
"trim",
"promote",
&format!(
"Merged carrier '{}' into CarrierInfo (total carriers: {})",
trim_info.carrier_name,
carrier_info.carrier_count()
),
verbose,
);
// Step 4: Safety check via TrimLoopHelper
@ -269,37 +305,62 @@ impl TrimLoopLowerer {
));
}
eprintln!("[TrimLoopLowerer] Safe Trim pattern detected, implementing lowering");
eprintln!(
"[TrimLoopLowerer] Carrier: '{}', original var: '{}', whitespace chars: {:?}",
trim_helper.carrier_name,
trim_helper.original_var,
trim_helper.whitespace_chars
trace.emit_if(
"trim",
"safe",
"Safe Trim pattern detected, implementing lowering",
verbose,
);
trace.emit_if(
"trim",
"safe",
&format!(
"Carrier: '{}', original var: '{}', whitespace chars: {:?}",
trim_helper.carrier_name,
trim_helper.original_var,
trim_helper.whitespace_chars
),
verbose,
);
// Step 5: Generate carrier initialization code
Self::generate_carrier_initialization(builder, body, trim_helper)?;
eprintln!(
"[TrimLoopLowerer] Registered carrier '{}' in variable_ctx.variable_map",
trim_helper.carrier_name
trace.emit_if(
"trim",
"init",
&format!(
"Registered carrier '{}' in variable_ctx.variable_map",
trim_helper.carrier_name
),
verbose,
);
// Step 6: Generate Trim break condition
let trim_break_condition = Self::generate_trim_break_condition(trim_helper);
eprintln!(
"[TrimLoopLowerer] Replaced break condition with !{}",
trim_helper.carrier_name
trace.emit_if(
"trim",
"cond",
&format!(
"Replaced break condition with !{}",
trim_helper.carrier_name
),
verbose,
);
// Step 7: Setup ConditionEnv bindings
let condition_bindings =
Self::setup_condition_env_bindings(builder, trim_helper, alloc_join_value)?;
eprintln!(
"[TrimLoopLowerer] Added {} condition bindings",
condition_bindings.len()
trace.emit_if(
"trim",
"cond",
&format!(
"Added {} condition bindings",
condition_bindings.len()
),
verbose,
);
// Step 8: Return result with all updates
@ -313,9 +374,14 @@ impl TrimLoopLowerer {
// Phase 196: Treat non-trim loops as normal loops.
// If promotion fails, simply skip Trim lowering and let the caller
// continue with the original break condition.
eprintln!(
"[TrimLoopLowerer] Cannot promote LoopBodyLocal variables {:?}: {}; skipping Trim lowering",
vars, reason
trace.emit_if(
"trim",
"reject",
&format!(
"Cannot promote LoopBodyLocal variables {:?}: {}; skipping Trim lowering",
vars, reason
),
verbose,
);
Ok(None)
}
@ -336,6 +402,8 @@ impl TrimLoopLowerer {
trim_helper: &crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper,
) -> Result<(), String> {
use crate::mir::builder::control_flow::joinir::patterns::trim_pattern_validator::TrimPatternValidator;
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
let verbose = crate::config::env::joinir_dev_enabled() || trace.is_joinir_enabled();
// Extract substring pattern from body
let (s_name, start_expr) =
@ -347,9 +415,11 @@ impl TrimLoopLowerer {
)
})?;
eprintln!(
"[TrimLoopLowerer] Extracted substring pattern: s='{}', start={:?}",
s_name, start_expr
trace.emit_if(
"trim",
"init",
&format!("Extracted substring pattern: s='{}', start={:?}", s_name, start_expr),
verbose,
);
// Get ValueIds for string and start
@ -387,9 +457,11 @@ impl TrimLoopLowerer {
vec![start_id, start_plus_1],
)?;
eprintln!(
"[TrimLoopLowerer] Generated initial substring call: ch0 = {:?}",
ch0
trace.emit_if(
"trim",
"init",
&format!("Generated initial substring call: ch0 = {:?}", ch0),
verbose,
);
// Generate: is_ch_match0 = (ch0 == " " || ch0 == "\t" || ...)
@ -399,9 +471,14 @@ impl TrimLoopLowerer {
&trim_helper.whitespace_chars,
)?;
eprintln!(
"[TrimLoopLowerer] Generated initial whitespace check: is_ch_match0 = {:?}",
is_ch_match0
trace.emit_if(
"trim",
"init",
&format!(
"Generated initial whitespace check: is_ch_match0 = {:?}",
is_ch_match0
),
verbose,
);
// Register carrier in variable_ctx.variable_map
@ -438,6 +515,8 @@ impl TrimLoopLowerer {
alloc_join_value: &mut dyn FnMut() -> ValueId,
) -> Result<Vec<ConditionBinding>, String> {
use crate::mir::builder::control_flow::joinir::patterns::trim_pattern_lowerer::TrimPatternLowerer;
let trace = crate::mir::builder::control_flow::joinir::trace::trace();
let verbose = crate::config::env::joinir_dev_enabled() || trace.is_joinir_enabled();
let mut bindings = Vec::new();
@ -454,9 +533,14 @@ impl TrimLoopLowerer {
alloc_join_value,
)?;
eprintln!(
"[TrimLoopLowerer] Added carrier '{}' to ConditionEnv: HOST {:?} → JoinIR {:?}",
trim_helper.carrier_name, binding.host_value, binding.join_value
trace.emit_if(
"trim",
"cond-env",
&format!(
"Added carrier '{}' to ConditionEnv: HOST {:?} → JoinIR {:?}",
trim_helper.carrier_name, binding.host_value, binding.join_value
),
verbose,
);
bindings.push(binding.clone());
@ -470,9 +554,14 @@ impl TrimLoopLowerer {
join_value: binding.join_value,
};
eprintln!(
"[TrimLoopLowerer] Phase 176-6: Also mapped original var '{}' → JoinIR {:?}",
trim_helper.original_var, binding.join_value
trace.emit_if(
"trim",
"cond-env",
&format!(
"Phase 176-6: Also mapped original var '{}' → JoinIR {:?}",
trim_helper.original_var, binding.join_value
),
verbose,
);
bindings.push(original_binding);

View File

@ -54,13 +54,16 @@ pub(in crate::mir::builder) fn choose_pattern_kind(
panic!("{}", msg);
} else {
// debug mode: ログのみ
eprintln!("{}", msg);
trace::trace().dev("choose_pattern_kind/parity", &msg);
}
} else {
// Patterns match - success!
eprintln!(
"[choose_pattern_kind/PARITY] OK: canonical and actual agree on {:?}",
canonical_choice
trace::trace().dev(
"choose_pattern_kind/parity",
&format!(
"[choose_pattern_kind/PARITY] OK: canonical and actual agree on {:?}",
canonical_choice
),
);
}
@ -218,38 +221,47 @@ impl MirBuilder {
match canonicalize_loop_expr(&loop_ast) {
Ok((skeleton, decision)) => {
eprintln!("[loop_canonicalizer] Function: {}", func_name);
eprintln!(
"[loop_canonicalizer] Skeleton steps: {}",
skeleton.steps.len()
trace::trace().dev(
"loop_canonicalizer",
&format!("Function: {}", func_name),
);
eprintln!(
"[loop_canonicalizer] Carriers: {}",
skeleton.carriers.len()
trace::trace().dev(
"loop_canonicalizer",
&format!(" Skeleton steps: {}", skeleton.steps.len()),
);
eprintln!(
"[loop_canonicalizer] Has exits: {}",
skeleton.exits.has_any_exit()
trace::trace().dev(
"loop_canonicalizer",
&format!(" Carriers: {}", skeleton.carriers.len()),
);
eprintln!(
"[loop_canonicalizer] Decision: {}",
if decision.is_success() {
"SUCCESS"
} else {
"FAIL_FAST"
}
trace::trace().dev(
"loop_canonicalizer",
&format!(" Has exits: {}", skeleton.exits.has_any_exit()),
);
trace::trace().dev(
"loop_canonicalizer",
&format!(
" Decision: {}",
if decision.is_success() {
"SUCCESS"
} else {
"FAIL_FAST"
}
),
);
if let Some(pattern) = decision.chosen {
eprintln!("[loop_canonicalizer] Chosen pattern: {:?}", pattern);
trace::trace().dev(
"loop_canonicalizer",
&format!(" Chosen pattern: {:?}", pattern),
);
}
eprintln!(
"[loop_canonicalizer] Missing caps: {:?}",
decision.missing_caps
trace::trace().dev(
"loop_canonicalizer",
&format!(" Missing caps: {:?}", decision.missing_caps),
);
if decision.is_fail_fast() {
eprintln!(
"[loop_canonicalizer] Reason: {}",
decision.notes.join("; ")
trace::trace().dev(
"loop_canonicalizer",
&format!(" Reason: {}", decision.notes.join("; ")),
);
}
@ -268,8 +280,8 @@ impl MirBuilder {
}
}
Err(e) => {
eprintln!("[loop_canonicalizer] Function: {}", func_name);
eprintln!("[loop_canonicalizer] Error: {}", e);
trace::trace().dev("loop_canonicalizer", &format!("Function: {}", func_name));
trace::trace().dev("loop_canonicalizer", &format!(" Error: {}", e));
}
}
}

View File

@ -59,6 +59,10 @@ pub struct JoinLoopTrace {
mainline_enabled: bool,
/// Whether LoopForm debug is enabled (NYASH_LOOPFORM_DEBUG)
loopform_enabled: bool,
/// Whether JoinIR dev mode is enabled (NYASH_JOINIR_DEV)
dev_enabled: bool,
/// Whether capture/ConditionEnv construction debug is enabled (NYASH_CAPTURE_DEBUG)
capture_enabled: bool,
}
impl JoinLoopTrace {
@ -71,6 +75,8 @@ impl JoinLoopTrace {
phi_enabled: std::env::var("NYASH_OPTION_C_DEBUG").is_ok(),
mainline_enabled: std::env::var("NYASH_JOINIR_MAINLINE_DEBUG").is_ok(),
loopform_enabled: std::env::var("NYASH_LOOPFORM_DEBUG").is_ok(),
dev_enabled: crate::config::env::joinir_dev_enabled(),
capture_enabled: std::env::var("NYASH_CAPTURE_DEBUG").is_ok(),
}
}
@ -81,6 +87,8 @@ impl JoinLoopTrace {
|| self.phi_enabled
|| self.mainline_enabled
|| self.loopform_enabled
|| self.dev_enabled
|| self.capture_enabled
}
/// Check if varmap tracing is enabled
@ -199,6 +207,43 @@ impl JoinLoopTrace {
}
}
/// Dev-only trace message (NYASH_JOINIR_DEV=1).
///
/// This is for diagnostics that should never appear in default runs, but are
/// useful while developing JoinIR lowering.
pub fn dev(&self, tag: &str, msg: &str) {
if self.dev_enabled {
eprintln!("[trace:dev] {}: {}", tag, msg);
}
}
/// Capture/debug output (NYASH_CAPTURE_DEBUG=1).
pub fn capture(&self, tag: &str, msg: &str) {
if self.capture_enabled {
eprintln!("[trace:capture] {}: {}", tag, msg);
}
}
/// Emit a message when the caller explicitly enables it (no env checks).
///
/// This is useful for routing `debug: bool` parameters through a single formatting point,
/// instead of scattering ad-hoc `eprintln!`.
pub fn emit_if(&self, channel: &str, tag: &str, msg: &str, enabled: bool) {
if enabled {
eprintln!("[trace:{}] {}: {}", channel, tag, msg);
}
}
/// Emit a raw line to stderr when enabled (no formatting).
///
/// Use this to preserve existing log formats while consolidating the actual `eprintln!`
/// call sites into this tracer.
pub fn stderr_if(&self, msg: &str, enabled: bool) {
if enabled {
eprintln!("{}", msg);
}
}
/// Trace function routing decisions
///
/// # Arguments