- RoutingDecision の missing_caps を Vec<CapabilityTag> に変更(型安全化) - error_tags は to_tag() メソッドで自動生成 - 全 callsite を enum variant に修正 - capability_tags モジュール(文字列定数群)を完全削除 - 全テスト PASS(型安全性向上を確認) - フォーマット適用
246 lines
11 KiB
Rust
246 lines
11 KiB
Rust
//! JoinIR routing logic for loop lowering
|
||
|
||
use super::trace;
|
||
use crate::ast::ASTNode;
|
||
use crate::mir::builder::MirBuilder;
|
||
use crate::mir::ValueId;
|
||
|
||
impl MirBuilder {
|
||
/// Phase 49: Try JoinIR Frontend for mainline integration
|
||
///
|
||
/// 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.
|
||
///
|
||
/// # 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
|
||
.scope_ctx
|
||
.current_function
|
||
.as_ref()
|
||
.map(|f| f.signature.name.clone())
|
||
.unwrap_or_default();
|
||
|
||
// Phase 195: Use unified trace
|
||
trace::trace().routing("router", &func_name, "try_cf_loop_joinir called");
|
||
|
||
// 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
|
||
// Phase 196: Default to structure-first routing now that LoopBuilder is removed.
|
||
// - Default: ON (structure_only = true) to allow JoinIR patterns to run for all loops.
|
||
// - To revert to the previous whitelist-only behavior, set NYASH_JOINIR_STRUCTURE_ONLY=0.
|
||
let structure_only = match std::env::var("NYASH_JOINIR_STRUCTURE_ONLY").ok().as_deref() {
|
||
Some("0") | Some("off") => false,
|
||
_ => true,
|
||
};
|
||
|
||
if structure_only {
|
||
trace::trace().routing(
|
||
"router",
|
||
&func_name,
|
||
"Structure-only mode enabled, skipping whitelist",
|
||
);
|
||
} else {
|
||
// Phase 49-4 + Phase 80: Multi-target routing (legacy whitelist)
|
||
// - JoinIR は常時 ON。legacy LoopBuilder は削除済み。
|
||
// - 代表2本(print_tokens / ArrayExt.filter)も常に JoinIR で試行する。
|
||
// 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() {
|
||
"main" => true, // Phase 188-Impl-1: Enable JoinIR for main function (Pattern 1)
|
||
"JoinIrMin.main/0" => true, // Phase 188-Impl-2: Enable JoinIR for JoinIrMin.main/0 (Pattern 2)
|
||
"JsonTokenizer.print_tokens/0" => true,
|
||
"ArrayExtBox.filter/2" => true,
|
||
// Phase 170-A-1: Enable JsonParserBox methods for JoinIR routing
|
||
"JsonParserBox._trim/1" => true,
|
||
"JsonParserBox._skip_whitespace/2" => true,
|
||
"JsonParserBox._match_literal/3" => true, // Phase 182: Fixed arity (s, pos, literal)
|
||
"JsonParserBox._parse_string/2" => true,
|
||
"JsonParserBox._parse_array/2" => true,
|
||
"JsonParserBox._parse_object/2" => true,
|
||
// Phase 182: Add simple loop methods
|
||
"JsonParserBox._parse_number/2" => true, // P2 Break (s, pos)
|
||
"JsonParserBox._atoi/1" => true, // P2 Break (s)
|
||
// Phase 170-A-1: Test methods (simplified versions)
|
||
"TrimTest.trim/1" => true,
|
||
"Main.trim/1" => true, // Phase 171-fix: Main box variant
|
||
"Main.trim_string_simple/1" => true, // Phase 33-13: Simple trim variant
|
||
"TrimTest.main/0" => true, // Phase 170: TrimTest.main for loop pattern test
|
||
// Phase 173: JsonParser P5 expansion test
|
||
"JsonParserTest._skip_whitespace/3" => true,
|
||
"JsonParserTest.main/0" => true,
|
||
// Phase 174: JsonParser complex loop P5B extension test
|
||
"JsonParserStringTest.parse_string_min/0" => true,
|
||
"JsonParserStringTest.main/0" => true,
|
||
// Phase 175: P5 multi-carrier support (2 carriers: pos + result)
|
||
"JsonParserStringTest2.parse_string_min2/0" => true,
|
||
"JsonParserStringTest2.main/0" => true,
|
||
_ => false,
|
||
};
|
||
|
||
if !is_target {
|
||
return Ok(None);
|
||
}
|
||
}
|
||
|
||
// Debug log when routing through JoinIR Frontend
|
||
// Phase 195: Check trace flags directly from JoinLoopTrace
|
||
let debug = trace::trace().is_loopform_enabled() || trace::trace().is_mainline_enabled();
|
||
trace::trace().routing(
|
||
"router",
|
||
&func_name,
|
||
"Routing through JoinIR Frontend mainline",
|
||
);
|
||
|
||
// Phase 49-3: Implement JoinIR Frontend integration
|
||
self.cf_loop_joinir_impl(condition, body, &func_name, debug)
|
||
}
|
||
|
||
/// Phase 49-3: JoinIR Frontend integration implementation
|
||
///
|
||
/// 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
|
||
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> {
|
||
// Phase 137-2/137-4: 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"
|
||
}
|
||
);
|
||
if let Some(pattern) = decision.chosen {
|
||
eprintln!("[loop_canonicalizer] Chosen pattern: {:?}", pattern);
|
||
}
|
||
eprintln!(
|
||
"[loop_canonicalizer] Missing caps: {:?}",
|
||
decision.missing_caps
|
||
);
|
||
if decision.is_fail_fast() {
|
||
eprintln!(
|
||
"[loop_canonicalizer] Reason: {}",
|
||
decision.notes.join("; ")
|
||
);
|
||
}
|
||
|
||
// Phase 137-4: Router parity verification
|
||
if let Some(canonical_pattern) = decision.chosen {
|
||
// Get actual pattern from router (will be determined by LoopPatternContext)
|
||
// We need to defer this check until after ctx is created
|
||
// Store decision for later parity check
|
||
trace::trace().debug(
|
||
"canonicalizer",
|
||
&format!(
|
||
"Phase 137-4: Canonical pattern chosen: {:?} (parity check pending)",
|
||
canonical_pattern
|
||
),
|
||
);
|
||
}
|
||
}
|
||
Err(e) => {
|
||
eprintln!("[loop_canonicalizer] Function: {}", func_name);
|
||
eprintln!("[loop_canonicalizer] Error: {}", e);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Phase 194: Use table-driven router instead of if/else chain
|
||
use super::patterns::{route_loop_pattern, LoopPatternContext};
|
||
|
||
// Phase 200-C: Pass fn_body_ast to LoopPatternContext if available
|
||
// Clone fn_body_ast to avoid borrow checker issues
|
||
let fn_body_clone = self.comp_ctx.fn_body_ast.clone();
|
||
trace::trace().routing(
|
||
"router",
|
||
func_name,
|
||
&format!(
|
||
"fn_body_ast is {}",
|
||
if fn_body_clone.is_some() {
|
||
"SOME"
|
||
} else {
|
||
"NONE"
|
||
}
|
||
),
|
||
);
|
||
let ctx = if let Some(ref fn_body) = fn_body_clone {
|
||
trace::trace().routing(
|
||
"router",
|
||
func_name,
|
||
&format!("Creating ctx with fn_body ({} nodes)", fn_body.len()),
|
||
);
|
||
LoopPatternContext::with_fn_body(condition, body, &func_name, debug, fn_body)
|
||
} else {
|
||
LoopPatternContext::new(condition, body, &func_name, debug)
|
||
};
|
||
|
||
// Phase 137-4: Router parity verification (after ctx is created)
|
||
if crate::config::env::joinir_dev_enabled() {
|
||
self.verify_router_parity(condition, body, func_name, &ctx)?;
|
||
}
|
||
|
||
if let Some(result) = route_loop_pattern(self, &ctx)? {
|
||
trace::trace().routing("router", func_name, "Pattern router succeeded");
|
||
return Ok(Some(result));
|
||
}
|
||
|
||
// Phase 187-2: Pattern router failed, try legacy whitelist
|
||
trace::trace().routing(
|
||
"router",
|
||
func_name,
|
||
"Pattern router found no match, trying legacy whitelist",
|
||
);
|
||
|
||
// Delegate to legacy binding path (routing_legacy_binding.rs)
|
||
self.cf_loop_joinir_legacy_binding(condition, body, func_name, debug)
|
||
}
|
||
}
|