2025-12-05 20:48:31 +09:00
|
|
|
|
//! JoinIR routing logic for loop lowering
|
|
|
|
|
|
|
2025-12-11 20:54:33 +09:00
|
|
|
|
use super::trace;
|
2025-12-05 20:48:31 +09:00
|
|
|
|
use crate::ast::ASTNode;
|
|
|
|
|
|
use crate::mir::builder::MirBuilder;
|
|
|
|
|
|
use crate::mir::ValueId;
|
|
|
|
|
|
|
|
|
|
|
|
impl MirBuilder {
|
|
|
|
|
|
/// Phase 49: Try JoinIR Frontend for mainline integration
|
|
|
|
|
|
///
|
2025-12-08 18:36:13 +09:00
|
|
|
|
/// Returns `Ok(Some(value))` if the loop is successfully lowered via JoinIR,
|
|
|
|
|
|
/// `Ok(None)` if no JoinIR pattern matched (unsupported loop structure).
|
|
|
|
|
|
/// Phase 187-2: Legacy LoopBuilder removed - all loops must use JoinIR.
|
2025-12-05 20:48:31 +09:00
|
|
|
|
///
|
|
|
|
|
|
/// # Phase 49-4: Multi-target support
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Targets are enabled via separate dev flags:
|
|
|
|
|
|
/// - `HAKO_JOINIR_PRINT_TOKENS_MAIN=1`: JsonTokenizer.print_tokens/0
|
|
|
|
|
|
/// - `HAKO_JOINIR_ARRAY_FILTER_MAIN=1`: ArrayExtBox.filter/2
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Note: Arity in function names does NOT include implicit `me` receiver.
|
|
|
|
|
|
/// - Instance method `print_tokens()` → `/0` (no explicit params)
|
|
|
|
|
|
/// - Static method `filter(arr, pred)` → `/2` (two params)
|
|
|
|
|
|
pub(in crate::mir::builder) fn try_cf_loop_joinir(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
condition: &ASTNode,
|
|
|
|
|
|
body: &[ASTNode],
|
|
|
|
|
|
) -> Result<Option<ValueId>, String> {
|
|
|
|
|
|
// Get current function name
|
|
|
|
|
|
let func_name = self
|
2025-12-16 03:33:56 +09:00
|
|
|
|
.scope_ctx.current_function
|
2025-12-05 20:48:31 +09:00
|
|
|
|
.as_ref()
|
|
|
|
|
|
.map(|f| f.signature.name.clone())
|
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
feat(joinir): Phase 195 - Unified JoinLoopTrace for all JoinIR debug output
Created centralized tracing module for JoinIR loop lowering operations,
consolidating scattered eprintln! calls into a single SSOT interface.
# Implementation
1. **Created trace.rs module** (~233 lines)
- JoinLoopTrace struct with env var controls
- Unified API: pattern(), varmap(), joinir_stats(), phi(), merge(), etc.
- Global singleton via trace() function
- Supports 5 env vars: NYASH_TRACE_VARMAP, NYASH_JOINIR_DEBUG,
NYASH_OPTION_C_DEBUG, NYASH_JOINIR_MAINLINE_DEBUG, NYASH_LOOPFORM_DEBUG
2. **Updated debug.rs** - Delegates trace_varmap() to JoinLoopTrace
3. **Updated routing.rs** - All eprintln! replaced with trace calls (10 instances)
4. **Updated pattern routers** - All 3 patterns now use unified trace
- pattern1_minimal.rs: 6 replacements
- pattern2_with_break.rs: 6 replacements
- pattern3_with_if_phi.rs: 6 replacements
- router.rs: 2 replacements
5. **Updated merge/block_allocator.rs** - 6 eprintln! → trace calls
# Benefits
- **Single Source of Truth**: All trace control through environment variables
- **Consistent Format**: Unified [trace:*] prefix for easy filtering
- **Zero Overhead**: No output when env vars unset
- **Easy Extension**: Add new trace points via existing API
- **Better Debug Experience**: Structured output with clear categories
# Testing
✅ cargo build --release - Success
✅ NYASH_TRACE_VARMAP=1 - Shows varmap traces only
✅ NYASH_JOINIR_DEBUG=1 - Shows joinir + blocks + routing traces
✅ No env vars - No debug output
✅ apps/tests/loop_min_while.hako - All tests pass
# Related
- Phase 191-194 groundwork (modular merge structure)
- NYASH_TRACE_VARMAP added today for variable_map debugging
- Consolidates ~80 scattered eprintln! calls across JoinIR
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 22:23:51 +09:00
|
|
|
|
// Phase 195: Use unified trace
|
|
|
|
|
|
trace::trace().routing("router", &func_name, "try_cf_loop_joinir called");
|
2025-12-05 20:48:31 +09:00
|
|
|
|
|
2025-12-07 13:03:43 +09:00
|
|
|
|
// Phase 170-4: Structure-based routing option
|
|
|
|
|
|
// When NYASH_JOINIR_STRUCTURE_ONLY=1, skip function name whitelist
|
|
|
|
|
|
// and route purely based on loop structure analysis
|
2025-12-08 23:43:26 +09:00
|
|
|
|
// Phase 196: Default to structure-first routing now that LoopBuilder is removed.
|
|
|
|
|
|
// - Default: ON (structure_only = true) to allow JoinIR patterns to run for all loops.
|
|
|
|
|
|
// - To revert to the previous whitelist-only behavior, set NYASH_JOINIR_STRUCTURE_ONLY=0.
|
2025-12-11 20:54:33 +09:00
|
|
|
|
let structure_only = match std::env::var("NYASH_JOINIR_STRUCTURE_ONLY").ok().as_deref() {
|
2025-12-08 23:43:26 +09:00
|
|
|
|
Some("0") | Some("off") => false,
|
|
|
|
|
|
_ => true,
|
|
|
|
|
|
};
|
2025-12-07 13:03:43 +09:00
|
|
|
|
|
|
|
|
|
|
if structure_only {
|
2025-12-11 20:54:33 +09:00
|
|
|
|
trace::trace().routing(
|
|
|
|
|
|
"router",
|
|
|
|
|
|
&func_name,
|
|
|
|
|
|
"Structure-only mode enabled, skipping whitelist",
|
|
|
|
|
|
);
|
2025-12-07 13:03:43 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
// Phase 49-4 + Phase 80: Multi-target routing (legacy whitelist)
|
2025-12-10 00:01:53 +09:00
|
|
|
|
// - JoinIR は常時 ON。legacy LoopBuilder は削除済み。
|
|
|
|
|
|
// - 代表2本(print_tokens / ArrayExt.filter)も常に JoinIR で試行する。
|
2025-12-07 13:03:43 +09:00
|
|
|
|
// Note: Arity does NOT include implicit `me` receiver
|
|
|
|
|
|
// Phase 188: Add "main" routing for loop pattern expansion
|
|
|
|
|
|
// Phase 170: Add JsonParserBox methods for selfhost validation
|
|
|
|
|
|
let is_target = match func_name.as_str() {
|
2025-12-11 20:54:33 +09:00
|
|
|
|
"main" => true, // Phase 188-Impl-1: Enable JoinIR for main function (Pattern 1)
|
|
|
|
|
|
"JoinIrMin.main/0" => true, // Phase 188-Impl-2: Enable JoinIR for JoinIrMin.main/0 (Pattern 2)
|
2025-12-10 00:01:53 +09:00
|
|
|
|
"JsonTokenizer.print_tokens/0" => true,
|
|
|
|
|
|
"ArrayExtBox.filter/2" => true,
|
2025-12-07 13:03:43 +09:00
|
|
|
|
// Phase 170-A-1: Enable JsonParserBox methods for JoinIR routing
|
|
|
|
|
|
"JsonParserBox._trim/1" => true,
|
|
|
|
|
|
"JsonParserBox._skip_whitespace/2" => true,
|
2025-12-11 20:54:33 +09:00
|
|
|
|
"JsonParserBox._match_literal/3" => true, // Phase 182: Fixed arity (s, pos, literal)
|
2025-12-07 13:03:43 +09:00
|
|
|
|
"JsonParserBox._parse_string/2" => true,
|
|
|
|
|
|
"JsonParserBox._parse_array/2" => true,
|
|
|
|
|
|
"JsonParserBox._parse_object/2" => true,
|
2025-12-08 21:36:39 +09:00
|
|
|
|
// Phase 182: Add simple loop methods
|
2025-12-11 20:54:33 +09:00
|
|
|
|
"JsonParserBox._parse_number/2" => true, // P2 Break (s, pos)
|
|
|
|
|
|
"JsonParserBox._atoi/1" => true, // P2 Break (s)
|
2025-12-07 13:03:43 +09:00
|
|
|
|
// Phase 170-A-1: Test methods (simplified versions)
|
|
|
|
|
|
"TrimTest.trim/1" => true,
|
2025-12-11 20:54:33 +09:00
|
|
|
|
"Main.trim/1" => true, // Phase 171-fix: Main box variant
|
|
|
|
|
|
"Main.trim_string_simple/1" => true, // Phase 33-13: Simple trim variant
|
|
|
|
|
|
"TrimTest.main/0" => true, // Phase 170: TrimTest.main for loop pattern test
|
2025-12-08 10:13:34 +09:00
|
|
|
|
// Phase 173: JsonParser P5 expansion test
|
|
|
|
|
|
"JsonParserTest._skip_whitespace/3" => true,
|
|
|
|
|
|
"JsonParserTest.main/0" => true,
|
2025-12-08 13:08:44 +09:00
|
|
|
|
// Phase 174: JsonParser complex loop P5B extension test
|
|
|
|
|
|
"JsonParserStringTest.parse_string_min/0" => true,
|
|
|
|
|
|
"JsonParserStringTest.main/0" => true,
|
2025-12-08 13:34:43 +09:00
|
|
|
|
// Phase 175: P5 multi-carrier support (2 carriers: pos + result)
|
|
|
|
|
|
"JsonParserStringTest2.parse_string_min2/0" => true,
|
|
|
|
|
|
"JsonParserStringTest2.main/0" => true,
|
2025-12-07 13:03:43 +09:00
|
|
|
|
_ => false,
|
|
|
|
|
|
};
|
2025-12-05 20:48:31 +09:00
|
|
|
|
|
2025-12-07 13:03:43 +09:00
|
|
|
|
if !is_target {
|
|
|
|
|
|
return Ok(None);
|
|
|
|
|
|
}
|
2025-12-05 20:48:31 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Debug log when routing through JoinIR Frontend
|
feat(joinir): Phase 195 - Unified JoinLoopTrace for all JoinIR debug output
Created centralized tracing module for JoinIR loop lowering operations,
consolidating scattered eprintln! calls into a single SSOT interface.
# Implementation
1. **Created trace.rs module** (~233 lines)
- JoinLoopTrace struct with env var controls
- Unified API: pattern(), varmap(), joinir_stats(), phi(), merge(), etc.
- Global singleton via trace() function
- Supports 5 env vars: NYASH_TRACE_VARMAP, NYASH_JOINIR_DEBUG,
NYASH_OPTION_C_DEBUG, NYASH_JOINIR_MAINLINE_DEBUG, NYASH_LOOPFORM_DEBUG
2. **Updated debug.rs** - Delegates trace_varmap() to JoinLoopTrace
3. **Updated routing.rs** - All eprintln! replaced with trace calls (10 instances)
4. **Updated pattern routers** - All 3 patterns now use unified trace
- pattern1_minimal.rs: 6 replacements
- pattern2_with_break.rs: 6 replacements
- pattern3_with_if_phi.rs: 6 replacements
- router.rs: 2 replacements
5. **Updated merge/block_allocator.rs** - 6 eprintln! → trace calls
# Benefits
- **Single Source of Truth**: All trace control through environment variables
- **Consistent Format**: Unified [trace:*] prefix for easy filtering
- **Zero Overhead**: No output when env vars unset
- **Easy Extension**: Add new trace points via existing API
- **Better Debug Experience**: Structured output with clear categories
# Testing
✅ cargo build --release - Success
✅ NYASH_TRACE_VARMAP=1 - Shows varmap traces only
✅ NYASH_JOINIR_DEBUG=1 - Shows joinir + blocks + routing traces
✅ No env vars - No debug output
✅ apps/tests/loop_min_while.hako - All tests pass
# Related
- Phase 191-194 groundwork (modular merge structure)
- NYASH_TRACE_VARMAP added today for variable_map debugging
- Consolidates ~80 scattered eprintln! calls across JoinIR
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 22:23:51 +09:00
|
|
|
|
// Phase 195: Check trace flags directly from JoinLoopTrace
|
|
|
|
|
|
let debug = trace::trace().is_loopform_enabled() || trace::trace().is_mainline_enabled();
|
2025-12-11 20:54:33 +09:00
|
|
|
|
trace::trace().routing(
|
|
|
|
|
|
"router",
|
|
|
|
|
|
&func_name,
|
|
|
|
|
|
"Routing through JoinIR Frontend mainline",
|
|
|
|
|
|
);
|
2025-12-05 20:48:31 +09:00
|
|
|
|
|
|
|
|
|
|
// Phase 49-3: Implement JoinIR Frontend integration
|
|
|
|
|
|
self.cf_loop_joinir_impl(condition, body, &func_name, debug)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Phase 49-3: JoinIR Frontend integration implementation
|
|
|
|
|
|
///
|
2025-12-08 18:36:13 +09:00
|
|
|
|
/// Routes loop compilation through either:
|
|
|
|
|
|
/// 1. Pattern-based router (Phase 194+) - preferred for new patterns
|
|
|
|
|
|
/// 2. Legacy binding path (Phase 49-3) - for whitelisted functions only
|
2025-12-05 20:48:31 +09:00
|
|
|
|
pub(in crate::mir::builder) fn cf_loop_joinir_impl(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
condition: &ASTNode,
|
|
|
|
|
|
body: &[ASTNode],
|
|
|
|
|
|
func_name: &str,
|
|
|
|
|
|
debug: bool,
|
|
|
|
|
|
) -> Result<Option<ValueId>, String> {
|
2025-12-16 05:17:56 +09:00
|
|
|
|
// Phase 137-2: Dev-only observation via Loop Canonicalizer
|
|
|
|
|
|
if crate::config::env::joinir_dev_enabled() {
|
|
|
|
|
|
use crate::ast::Span;
|
|
|
|
|
|
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
|
|
|
|
|
|
|
|
|
|
|
|
// Reconstruct loop AST for canonicalizer
|
|
|
|
|
|
let loop_ast = ASTNode::Loop {
|
|
|
|
|
|
condition: Box::new(condition.clone()),
|
|
|
|
|
|
body: body.to_vec(),
|
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
match canonicalize_loop_expr(&loop_ast) {
|
|
|
|
|
|
Ok((skeleton, decision)) => {
|
|
|
|
|
|
eprintln!("[loop_canonicalizer] Function: {}", func_name);
|
|
|
|
|
|
eprintln!("[loop_canonicalizer] Skeleton steps: {}", skeleton.steps.len());
|
|
|
|
|
|
eprintln!("[loop_canonicalizer] Carriers: {}", skeleton.carriers.len());
|
|
|
|
|
|
eprintln!("[loop_canonicalizer] Has exits: {}", skeleton.exits.has_any_exit());
|
|
|
|
|
|
eprintln!("[loop_canonicalizer] Decision: {}",
|
|
|
|
|
|
if decision.is_success() { "SUCCESS" } else { "FAIL_FAST" });
|
2025-12-16 05:38:18 +09:00
|
|
|
|
if let Some(pattern) = decision.chosen {
|
|
|
|
|
|
eprintln!("[loop_canonicalizer] Chosen pattern: {:?}", pattern);
|
|
|
|
|
|
}
|
|
|
|
|
|
eprintln!("[loop_canonicalizer] Missing caps: {:?}", decision.missing_caps);
|
2025-12-16 05:17:56 +09:00
|
|
|
|
if decision.is_fail_fast() {
|
|
|
|
|
|
eprintln!("[loop_canonicalizer] Reason: {}", decision.notes.join("; "));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
Err(e) => {
|
|
|
|
|
|
eprintln!("[loop_canonicalizer] Function: {}", func_name);
|
|
|
|
|
|
eprintln!("[loop_canonicalizer] Error: {}", e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-05 22:11:39 +09:00
|
|
|
|
// Phase 194: Use table-driven router instead of if/else chain
|
|
|
|
|
|
use super::patterns::{route_loop_pattern, LoopPatternContext};
|
2025-12-05 20:48:31 +09:00
|
|
|
|
|
2025-12-09 18:32:03 +09:00
|
|
|
|
// Phase 200-C: Pass fn_body_ast to LoopPatternContext if available
|
|
|
|
|
|
// Clone fn_body_ast to avoid borrow checker issues
|
2025-12-16 04:07:17 +09:00
|
|
|
|
let fn_body_clone = self.comp_ctx.fn_body_ast.clone();
|
2025-12-15 03:27:47 +09:00
|
|
|
|
trace::trace().routing(
|
|
|
|
|
|
"router",
|
|
|
|
|
|
func_name,
|
|
|
|
|
|
&format!(
|
|
|
|
|
|
"fn_body_ast is {}",
|
|
|
|
|
|
if fn_body_clone.is_some() { "SOME" } else { "NONE" }
|
|
|
|
|
|
),
|
2025-12-11 20:54:33 +09:00
|
|
|
|
);
|
2025-12-09 18:32:03 +09:00
|
|
|
|
let ctx = if let Some(ref fn_body) = fn_body_clone {
|
2025-12-15 03:27:47 +09:00
|
|
|
|
trace::trace().routing(
|
|
|
|
|
|
"router",
|
|
|
|
|
|
func_name,
|
|
|
|
|
|
&format!("Creating ctx with fn_body ({} nodes)", fn_body.len()),
|
2025-12-11 20:54:33 +09:00
|
|
|
|
);
|
2025-12-09 18:32:03 +09:00
|
|
|
|
LoopPatternContext::with_fn_body(condition, body, &func_name, debug, fn_body)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
LoopPatternContext::new(condition, body, &func_name, debug)
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-12-05 22:11:39 +09:00
|
|
|
|
if let Some(result) = route_loop_pattern(self, &ctx)? {
|
feat(joinir): Phase 195 - Unified JoinLoopTrace for all JoinIR debug output
Created centralized tracing module for JoinIR loop lowering operations,
consolidating scattered eprintln! calls into a single SSOT interface.
# Implementation
1. **Created trace.rs module** (~233 lines)
- JoinLoopTrace struct with env var controls
- Unified API: pattern(), varmap(), joinir_stats(), phi(), merge(), etc.
- Global singleton via trace() function
- Supports 5 env vars: NYASH_TRACE_VARMAP, NYASH_JOINIR_DEBUG,
NYASH_OPTION_C_DEBUG, NYASH_JOINIR_MAINLINE_DEBUG, NYASH_LOOPFORM_DEBUG
2. **Updated debug.rs** - Delegates trace_varmap() to JoinLoopTrace
3. **Updated routing.rs** - All eprintln! replaced with trace calls (10 instances)
4. **Updated pattern routers** - All 3 patterns now use unified trace
- pattern1_minimal.rs: 6 replacements
- pattern2_with_break.rs: 6 replacements
- pattern3_with_if_phi.rs: 6 replacements
- router.rs: 2 replacements
5. **Updated merge/block_allocator.rs** - 6 eprintln! → trace calls
# Benefits
- **Single Source of Truth**: All trace control through environment variables
- **Consistent Format**: Unified [trace:*] prefix for easy filtering
- **Zero Overhead**: No output when env vars unset
- **Easy Extension**: Add new trace points via existing API
- **Better Debug Experience**: Structured output with clear categories
# Testing
✅ cargo build --release - Success
✅ NYASH_TRACE_VARMAP=1 - Shows varmap traces only
✅ NYASH_JOINIR_DEBUG=1 - Shows joinir + blocks + routing traces
✅ No env vars - No debug output
✅ apps/tests/loop_min_while.hako - All tests pass
# Related
- Phase 191-194 groundwork (modular merge structure)
- NYASH_TRACE_VARMAP added today for variable_map debugging
- Consolidates ~80 scattered eprintln! calls across JoinIR
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 22:23:51 +09:00
|
|
|
|
trace::trace().routing("router", func_name, "Pattern router succeeded");
|
2025-12-05 22:11:39 +09:00
|
|
|
|
return Ok(Some(result));
|
2025-12-05 20:48:31 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-08 18:36:13 +09:00
|
|
|
|
// Phase 187-2: Pattern router failed, try legacy whitelist
|
2025-12-11 20:54:33 +09:00
|
|
|
|
trace::trace().routing(
|
|
|
|
|
|
"router",
|
|
|
|
|
|
func_name,
|
|
|
|
|
|
"Pattern router found no match, trying legacy whitelist",
|
|
|
|
|
|
);
|
2025-12-05 20:48:31 +09:00
|
|
|
|
|
2025-12-08 18:36:13 +09:00
|
|
|
|
// Delegate to legacy binding path (routing_legacy_binding.rs)
|
|
|
|
|
|
self.cf_loop_joinir_legacy_binding(condition, body, func_name, debug)
|
2025-12-05 20:48:31 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|