feat(llvm): Phase 132-P0 - block_end_values tuple-key fix for cross-function isolation
## Problem `block_end_values` used block ID only as key, causing collisions when multiple functions share the same block IDs (e.g., bb0 in both condition_fn and main). ## Root Cause - condition_fn's bb0 → block_end_values[0] - main's bb0 → block_end_values[0] (OVERWRITES!) - PHI resolution gets wrong snapshot → dominance error ## Solution (Box-First principle) Change key from `int` to `Tuple[str, int]` (func_name, block_id): ```python # Before block_end_values: Dict[int, Dict[int, ir.Value]] # After block_end_values: Dict[Tuple[str, int], Dict[int, ir.Value]] ``` ## Files Modified (Python - 6 files) 1. `llvm_builder.py` - Type annotation update 2. `function_lower.py` - Pass func_name to lower_blocks 3. `block_lower.py` - Use tuple keys for snapshot save/load 4. `resolver.py` - Add func_name parameter to resolve_incoming 5. `wiring.py` - Thread func_name through PHI wiring 6. `phi_manager.py` - Debug traces ## Files Modified (Rust - cleanup) - Removed deprecated `loop_to_join.rs` (297 lines deleted) - Updated pattern lowerers for cleaner exit handling - Added lifecycle management improvements ## Verification - ✅ Pattern 1: VM RC: 3, LLVM Result: 3 (no regression) - ⚠️ Case C: Still has dominance error (separate root cause) - Needs additional scope fixes (phi_manager, resolver caches) ## Design Principles - **Box-First**: Each function is an isolated Box with scoped state - **SSOT**: (func_name, block_id) uniquely identifies block snapshots - **Fail-Fast**: No cross-function state contamination ## Known Issues (Phase 132-P1) Other function-local state needs same treatment: - phi_manager.predeclared - resolver caches (i64_cache, ptr_cache, etc.) - builder._jump_only_blocks ## Documentation - docs/development/current/main/investigations/phase132-p0-case-c-root-cause.md - docs/development/current/main/investigations/phase132-p0-tuple-key-implementation.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -14,7 +14,8 @@
|
||||
//! ## 解決策
|
||||
//!
|
||||
//! ConditionPatternBox を導入し、if条件が「単純比較」かどうかを判定する。
|
||||
//! AST-based lowerer は単純比較のみ処理可能とし、複雑条件はlegacy modeへフォールバック。
|
||||
//! ただし「単純/複雑/legacy」の語彙は混線しやすいので、routing 用には
|
||||
//! `ConditionCapability` を使って「どの経路で扱うか」を明示する。
|
||||
//!
|
||||
//! Phase 222: 左右反転(literal on left → var on left)と変数同士の比較をサポート。
|
||||
//!
|
||||
@ -35,6 +36,64 @@
|
||||
use crate::ast::{ASTNode, BinaryOperator, LiteralValue};
|
||||
use crate::mir::CompareOp;
|
||||
|
||||
/// ConditionCapability: 条件式をどの戦略で扱えるか(routing 用)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ConditionCapability {
|
||||
/// Pattern3 if-sum AST-based lowerer で比較として扱える
|
||||
IfSumComparable,
|
||||
/// 上記以外(caller が別経路を選ぶ)
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
fn is_if_sum_value_expr(expr: &ASTNode) -> bool {
|
||||
match expr {
|
||||
ASTNode::Variable { .. } | ASTNode::Literal { .. } => true,
|
||||
ASTNode::BinaryOp {
|
||||
operator, left, right, ..
|
||||
} => matches!(
|
||||
operator,
|
||||
BinaryOperator::Add
|
||||
| BinaryOperator::Subtract
|
||||
| BinaryOperator::Multiply
|
||||
| BinaryOperator::Divide
|
||||
| BinaryOperator::Modulo
|
||||
) && is_if_sum_value_expr(left.as_ref())
|
||||
&& is_if_sum_value_expr(right.as_ref()),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 条件式の“能力”を判定(routing のための入口)
|
||||
pub fn analyze_condition_capability(cond: &ASTNode) -> ConditionCapability {
|
||||
match cond {
|
||||
ASTNode::BinaryOp {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
..
|
||||
} => {
|
||||
let is_comparison = matches!(
|
||||
operator,
|
||||
BinaryOperator::Equal
|
||||
| BinaryOperator::NotEqual
|
||||
| BinaryOperator::Less
|
||||
| BinaryOperator::Greater
|
||||
| BinaryOperator::LessEqual
|
||||
| BinaryOperator::GreaterEqual
|
||||
);
|
||||
if !is_comparison {
|
||||
return ConditionCapability::Unsupported;
|
||||
}
|
||||
if is_if_sum_value_expr(left.as_ref()) && is_if_sum_value_expr(right.as_ref()) {
|
||||
ConditionCapability::IfSumComparable
|
||||
} else {
|
||||
ConditionCapability::Unsupported
|
||||
}
|
||||
}
|
||||
_ => ConditionCapability::Unsupported,
|
||||
}
|
||||
}
|
||||
|
||||
/// if条件のパターン種別
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ConditionPattern {
|
||||
@ -176,8 +235,8 @@ pub fn analyze_condition_pattern(cond: &ASTNode) -> ConditionPattern {
|
||||
/// // i > 0 → true
|
||||
/// assert!(is_simple_comparison(&simple_condition));
|
||||
///
|
||||
/// // i % 2 == 1 → false
|
||||
/// assert!(!is_simple_comparison(&complex_condition));
|
||||
/// // i % 2 == 1 → true(Phase 242-EX-A で比較のオペランドに算術式を許可)
|
||||
/// assert!(is_simple_comparison(&complex_condition));
|
||||
/// ```
|
||||
pub fn is_simple_comparison(cond: &ASTNode) -> bool {
|
||||
analyze_condition_pattern(cond) == ConditionPattern::SimpleComparison
|
||||
@ -605,4 +664,51 @@ mod tests {
|
||||
);
|
||||
assert!(is_simple_comparison(&cond));
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// ConditionCapability (routing) Tests
|
||||
// ========================================================================
|
||||
|
||||
#[test]
|
||||
fn test_capability_if_sum_comparable_simple() {
|
||||
let cond = binop(BinaryOperator::Greater, var("i"), int_lit(0));
|
||||
assert_eq!(
|
||||
analyze_condition_capability(&cond),
|
||||
ConditionCapability::IfSumComparable
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_capability_if_sum_comparable_binop_operand() {
|
||||
let lhs = binop(BinaryOperator::Modulo, var("i"), int_lit(2));
|
||||
let cond = binop(BinaryOperator::Equal, lhs, int_lit(1));
|
||||
assert_eq!(
|
||||
analyze_condition_capability(&cond),
|
||||
ConditionCapability::IfSumComparable
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_capability_rejects_logical_and() {
|
||||
let cond = binop(BinaryOperator::And, var("a"), var("b"));
|
||||
assert_eq!(
|
||||
analyze_condition_capability(&cond),
|
||||
ConditionCapability::Unsupported
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_capability_rejects_method_call_operand() {
|
||||
let method_call = ASTNode::MethodCall {
|
||||
object: Box::new(var("obj")),
|
||||
method: "get".to_string(),
|
||||
arguments: vec![],
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let cond = binop(BinaryOperator::Greater, method_call, int_lit(0));
|
||||
assert_eq!(
|
||||
analyze_condition_capability(&cond),
|
||||
ConditionCapability::Unsupported
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,8 +10,9 @@
|
||||
//!
|
||||
//! ## Design Philosophy
|
||||
//!
|
||||
//! The JoinIR lowerer should work with **local ValueIds** (0, 1, 2, ...) without
|
||||
//! knowing anything about the host function's ValueId space. This ensures:
|
||||
//! The JoinIR lowerer should work with **JoinIR-side ValueIds** allocated via
|
||||
//! `JoinValueSpace` (Param: 100-999, Local: 1000+) without knowing anything about
|
||||
//! the host function's ValueId space. This ensures:
|
||||
//!
|
||||
//! 1. **Modularity**: JoinIR lowerers are pure transformers
|
||||
//! 2. **Reusability**: Same lowerer can be used in different contexts
|
||||
@ -26,13 +27,13 @@
|
||||
//! Host Function:
|
||||
//! ValueId(4) = Const 0 // i = 0 in host
|
||||
//!
|
||||
//! JoinIR Fragment (uses local IDs 0, 1, 2, ...):
|
||||
//! ValueId(0) = param // i_param (local to JoinIR)
|
||||
//! ValueId(1) = Const 3
|
||||
//! ValueId(2) = Compare ...
|
||||
//! JoinIR Fragment:
|
||||
//! ValueId(100) = param // i_param (JoinIR Param region)
|
||||
//! ValueId(1000) = Const 3
|
||||
//! ValueId(1001) = Compare ...
|
||||
//!
|
||||
//! Boundary:
|
||||
//! join_inputs: [ValueId(0)] // JoinIR's param slot
|
||||
//! join_inputs: [ValueId(100)] // JoinIR's param slot (Param region)
|
||||
//! host_inputs: [ValueId(4)] // Host's `i` variable
|
||||
//!
|
||||
//! Merged MIR (with Copy injection):
|
||||
@ -115,11 +116,11 @@ pub struct LoopExitBinding {
|
||||
pub struct JoinInlineBoundary {
|
||||
/// JoinIR-local ValueIds that act as "input slots"
|
||||
///
|
||||
/// These are the ValueIds used **inside** the JoinIR fragment to refer
|
||||
/// to values that come from the host. They should be small sequential
|
||||
/// IDs (0, 1, 2, ...) since JoinIR lowerers allocate locally.
|
||||
/// These are the ValueIds used **inside** the JoinIR fragment to refer
|
||||
/// to values that come from the host. They should be in the JoinValueSpace
|
||||
/// Param region (100-999). (They are typically allocated sequentially.)
|
||||
///
|
||||
/// Example: For a loop variable `i`, JoinIR uses ValueId(0) as the parameter.
|
||||
/// Example: For a loop variable `i`, JoinIR uses ValueId(100) as the parameter.
|
||||
pub join_inputs: Vec<ValueId>,
|
||||
|
||||
/// Host-function ValueIds that provide the input values
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
//! # Integration Points
|
||||
//!
|
||||
//! Called from:
|
||||
//! - `loop_to_join.rs::LoopToJoinLowerer::lower_loop()`
|
||||
//! - `loop_to_join::LoopToJoinLowerer::lower_loop()`
|
||||
//! - `loop_form_intake.rs::handle_loop_form()`
|
||||
|
||||
use crate::mir::join_ir::JoinInst;
|
||||
@ -94,7 +94,7 @@ use crate::mir::loop_form::LoopForm;
|
||||
/// # Integration Point
|
||||
///
|
||||
/// This function should be called from loop lowering entry points:
|
||||
/// - `loop_to_join.rs::LoopToJoinLowerer::lower_loop()`
|
||||
/// - `loop_to_join::LoopToJoinLowerer::lower_loop()`
|
||||
/// - `loop_form_intake.rs::handle_loop_form()`
|
||||
///
|
||||
/// # Example Usage
|
||||
|
||||
@ -1,297 +0,0 @@
|
||||
//! Phase 31: LoopToJoinLowerer - 統一 Loop→JoinIR 変換箱
|
||||
//!
|
||||
//! このモジュールは MIR の LoopForm を JoinIR に変換する統一インターフェースを提供する。
|
||||
//!
|
||||
//! ## 設計思想(Phase 33-23 責務分離完了)
|
||||
//!
|
||||
//! - **単一エントリポイント**: `LoopToJoinLowerer::lower()` ですべてのループを処理
|
||||
//! - **責務分離**: 検証・選択・調整を専用Boxに委譲
|
||||
//! - `LoopPatternValidator`: 構造検証(180行)
|
||||
//! - `LoopViewBuilder`: Lowering選択(343行)
|
||||
//! - `LoopToJoinLowerer`: コーディネーター(本ファイル)
|
||||
//! - **既存コード再利用**: generic_case_a の `_with_scope` 関数を内部で呼び出し
|
||||
//!
|
||||
//! ## 責務分離(Phase 33-9.1)
|
||||
//!
|
||||
//! **LoopToJoinLowerer の責務**:
|
||||
//! - MirQuery/LoopFormIntake/LoopScopeShape構築
|
||||
//! - Validator/Builder呼び出し調整
|
||||
//! - Strict modeエラーハンドリング
|
||||
//!
|
||||
//! **非責務**:
|
||||
//! - 構造検証(→ LoopPatternValidator)
|
||||
//! - Lowering選択(→ LoopViewBuilder)
|
||||
//! - if/else の PHI には触らない(If lowering の責務)
|
||||
//!
|
||||
//! ## 使用例
|
||||
//!
|
||||
//! ```ignore
|
||||
//! let lowerer = LoopToJoinLowerer::new();
|
||||
//! let join_module = lowerer.lower(func, &loop_form, &query)?;
|
||||
//! ```
|
||||
|
||||
use crate::mir::control_form::LoopId;
|
||||
use crate::mir::join_ir::lowering::loop_form_intake::intake_loop_form;
|
||||
use crate::mir::join_ir::lowering::loop_pattern_validator::LoopPatternValidator;
|
||||
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||
use crate::mir::join_ir::lowering::loop_view_builder::LoopViewBuilder;
|
||||
use crate::mir::join_ir::JoinModule;
|
||||
use crate::mir::loop_form::LoopForm;
|
||||
use crate::mir::query::MirQueryBox;
|
||||
use crate::mir::MirFunction;
|
||||
|
||||
/// Phase 32 L-1.2: 汎用 Case-A lowering が有効かどうか
|
||||
///
|
||||
/// `NYASH_JOINIR_LOWER_GENERIC=1` の場合、関数名フィルタを外して
|
||||
/// 構造ベースで Case-A ループを拾う。
|
||||
fn generic_case_a_enabled() -> bool {
|
||||
crate::mir::join_ir::env_flag_is_1("NYASH_JOINIR_LOWER_GENERIC")
|
||||
}
|
||||
|
||||
/// Loop→JoinIR 変換の統一箱(Phase 33-23 コーディネーター)
|
||||
///
|
||||
/// Phase 31 で導入された統一インターフェース。
|
||||
/// Phase 33-23 で責務分離完了(Validator/Builder委譲)。
|
||||
pub struct LoopToJoinLowerer {
|
||||
/// デバッグモード(詳細ログ出力)
|
||||
debug: bool,
|
||||
/// 構造検証箱(Phase 33-23)
|
||||
validator: LoopPatternValidator,
|
||||
/// Lowering選択箱(Phase 33-23)
|
||||
builder: LoopViewBuilder,
|
||||
}
|
||||
|
||||
impl Default for LoopToJoinLowerer {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl LoopToJoinLowerer {
|
||||
/// 新しい LoopToJoinLowerer を作成(Phase 33-23 Boxインスタンス化)
|
||||
pub fn new() -> Self {
|
||||
let debug = std::env::var("NYASH_LOOPTOJOIN_DEBUG")
|
||||
.map(|v| v == "1")
|
||||
.unwrap_or(false);
|
||||
Self {
|
||||
debug,
|
||||
validator: LoopPatternValidator::new(),
|
||||
builder: LoopViewBuilder::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// MIR LoopForm を JoinIR に変換
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `func`: MIR 関数
|
||||
/// - `loop_form`: 変換対象の LoopForm
|
||||
/// - `func_name`: 関数名(オプション、ルーティング用)
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `Some(JoinModule)`: 変換成功
|
||||
/// - `None`: 変換失敗(フォールバック経路へ)
|
||||
pub fn lower(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
loop_form: &LoopForm,
|
||||
func_name: Option<&str>,
|
||||
) -> Option<JoinModule> {
|
||||
let strict_on = crate::config::env::joinir_strict_enabled();
|
||||
let is_minimal_target = func_name
|
||||
.map(super::loop_scope_shape::is_case_a_minimal_target)
|
||||
.unwrap_or(false);
|
||||
if self.debug {
|
||||
eprintln!(
|
||||
"[LoopToJoinLowerer] lower() called for {:?}",
|
||||
func_name.unwrap_or("<unknown>")
|
||||
);
|
||||
}
|
||||
|
||||
// Phase 32 L-1.2: 早期フィルタは削除。構造チェック後に条件付きで適用する。
|
||||
|
||||
// Step 1: MirQuery を構築
|
||||
let query = MirQueryBox::new(func);
|
||||
|
||||
// Step 2: LoopFormIntake を構築
|
||||
// Phase 70-2: var_classes 引数削除完了(Trio 依存ゼロ)
|
||||
let intake = intake_loop_form(loop_form, &query, func)?;
|
||||
|
||||
// Step 3: LoopScopeShape を構築
|
||||
// Phase 48-2: from_loop_form() で Trio を内部化(LoopExitLivenessBox 依存削除)
|
||||
let scope = LoopScopeShape::from_loop_form(loop_form, &intake, &query, func_name)?;
|
||||
|
||||
if self.debug {
|
||||
eprintln!(
|
||||
"[LoopToJoinLowerer] LoopScopeShape built: pinned={:?}, carriers={:?}, exit_live={:?}",
|
||||
scope.pinned, scope.carriers, scope.exit_live
|
||||
);
|
||||
}
|
||||
|
||||
// Phase 32 Step 3-C: View メソッドで構造情報を取得(常に実行)
|
||||
let loop_id = LoopId(0); // 単一ループの場合は 0
|
||||
let region = loop_form.to_region_view(loop_id);
|
||||
let _control = loop_form.to_control_view(loop_id);
|
||||
let exit_edges = loop_form.to_exit_edges(loop_id);
|
||||
|
||||
// Debug: view ベースの情報をログ
|
||||
if self.debug {
|
||||
eprintln!(
|
||||
"[LoopToJoinLowerer] Phase 32 views: func={:?} loop_id={:?} header={:?} exits={:?}",
|
||||
func_name.unwrap_or("<unknown>"),
|
||||
loop_id,
|
||||
region.header,
|
||||
exit_edges.iter().map(|e| e.to).collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
// Phase 33-23: Validator箱に検証を委譲
|
||||
if !self
|
||||
.validator
|
||||
.is_supported_case_a(func, ®ion, &exit_edges, &scope)
|
||||
{
|
||||
if self.debug {
|
||||
eprintln!(
|
||||
"[LoopToJoinLowerer] rejected by validator: {:?}",
|
||||
func_name.unwrap_or("<unknown>")
|
||||
);
|
||||
}
|
||||
if strict_on && is_minimal_target {
|
||||
panic!(
|
||||
"[joinir/loop] strict mode: validator rejected {}",
|
||||
func_name.unwrap_or("<unknown>")
|
||||
);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
// Phase 32 L-1.2: 環境変数で分岐
|
||||
// OFF(既定): 従来の minimal 4 本だけ
|
||||
// ON: 構造だけで通す(関数名フィルタを外す)
|
||||
if !generic_case_a_enabled() {
|
||||
if !func_name.map_or(false, super::loop_scope_shape::is_case_a_minimal_target) {
|
||||
if self.debug {
|
||||
eprintln!(
|
||||
"[LoopToJoinLowerer] rejected by name filter (generic disabled): {:?}",
|
||||
func_name.unwrap_or("<unknown>")
|
||||
);
|
||||
}
|
||||
if strict_on && is_minimal_target {
|
||||
panic!(
|
||||
"[joinir/loop] strict mode: name filter rejected {}",
|
||||
func_name.unwrap_or("<unknown>")
|
||||
);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
} else if self.debug {
|
||||
eprintln!(
|
||||
"[LoopToJoinLowerer] generic Case-A enabled, allowing {:?}",
|
||||
func_name.unwrap_or("<unknown>")
|
||||
);
|
||||
}
|
||||
|
||||
// Phase 33-23: Builder箱にlowering選択を委譲
|
||||
let out = self.builder.build(scope, func_name);
|
||||
if out.is_none() && strict_on && is_minimal_target {
|
||||
panic!(
|
||||
"[joinir/loop] strict mode: lowering failed for {}",
|
||||
func_name.unwrap_or("<unknown>")
|
||||
);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
// Phase 33-23: Validation/Lowering選択ロジックはValidator/Builderに移動
|
||||
// - is_supported_case_a_loop_view() → LoopPatternValidator::is_supported_case_a()
|
||||
// - lower_with_scope() → LoopViewBuilder::build()
|
||||
// - has_safe_progress() → LoopPatternValidator::validate_progress_carrier()
|
||||
|
||||
// ========================================
|
||||
// Case-A helpers for specific function patterns
|
||||
// ========================================
|
||||
|
||||
/// Case-A 汎用 lowerer の「Main.skip/1 用」薄いラッパー。
|
||||
/// 実際のロジックは `lower` に集約されている。
|
||||
pub fn lower_case_a_for_skip_ws(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
loop_form: &LoopForm,
|
||||
) -> Option<JoinModule> {
|
||||
self.lower(func, loop_form, Some("Main.skip/1"))
|
||||
}
|
||||
|
||||
/// Case-A 汎用 lowerer の「FuncScannerBox.trim/1 用」薄いラッパー。
|
||||
/// 実際のロジックは `lower` に集約されている。
|
||||
pub fn lower_case_a_for_trim(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
loop_form: &LoopForm,
|
||||
) -> Option<JoinModule> {
|
||||
self.lower(func, loop_form, Some("FuncScannerBox.trim/1"))
|
||||
}
|
||||
|
||||
/// Case-A 汎用 lowerer の「FuncScannerBox.append_defs/2 用」薄いラッパー。
|
||||
/// 実際のロジックは `lower` に集約されている。
|
||||
pub fn lower_case_a_for_append_defs(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
loop_form: &LoopForm,
|
||||
) -> Option<JoinModule> {
|
||||
self.lower(func, loop_form, Some("FuncScannerBox.append_defs/2"))
|
||||
}
|
||||
|
||||
/// Case-A 汎用 lowerer の「Stage1UsingResolverBox.resolve_for_source/5 用」薄いラッパー。
|
||||
/// 実際のロジックは `lower` に集約されている。
|
||||
pub fn lower_case_a_for_stage1_resolver(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
loop_form: &LoopForm,
|
||||
) -> Option<JoinModule> {
|
||||
self.lower(
|
||||
func,
|
||||
loop_form,
|
||||
Some("Stage1UsingResolverBox.resolve_for_source/5"),
|
||||
)
|
||||
}
|
||||
|
||||
/// Case-A 汎用 lowerer の「StageBBodyExtractorBox.build_body_src/2 用」薄いラッパー。
|
||||
/// 実際のロジックは `lower` に集約されている。
|
||||
pub fn lower_case_a_for_stageb_body(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
loop_form: &LoopForm,
|
||||
) -> Option<JoinModule> {
|
||||
self.lower(
|
||||
func,
|
||||
loop_form,
|
||||
Some("StageBBodyExtractorBox.build_body_src/2"),
|
||||
)
|
||||
}
|
||||
|
||||
/// Case-A 汎用 lowerer の「StageBFuncScannerBox.scan_all_boxes/1 用」薄いラッパー。
|
||||
/// 実際のロジックは `lower` に集約されている。
|
||||
pub fn lower_case_a_for_stageb_funcscanner(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
loop_form: &LoopForm,
|
||||
) -> Option<JoinModule> {
|
||||
self.lower(
|
||||
func,
|
||||
loop_form,
|
||||
Some("StageBFuncScannerBox.scan_all_boxes/1"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_lowerer_creation() {
|
||||
let lowerer = LoopToJoinLowerer::new();
|
||||
assert!(!lowerer.debug || lowerer.debug); // Just check it compiles
|
||||
}
|
||||
}
|
||||
73
src/mir/join_ir/lowering/loop_to_join/case_a_entrypoints.rs
Normal file
73
src/mir/join_ir/lowering/loop_to_join/case_a_entrypoints.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use super::LoopToJoinLowerer;
|
||||
use crate::mir::join_ir::JoinModule;
|
||||
use crate::mir::loop_form::LoopForm;
|
||||
use crate::mir::MirFunction;
|
||||
|
||||
impl LoopToJoinLowerer {
|
||||
/// Case-A 汎用 lowerer の「Main.skip/1 用」薄いラッパー。
|
||||
pub fn lower_case_a_for_skip_ws(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
loop_form: &LoopForm,
|
||||
) -> Option<JoinModule> {
|
||||
self.lower(func, loop_form, Some("Main.skip/1"))
|
||||
}
|
||||
|
||||
/// Case-A 汎用 lowerer の「FuncScannerBox.trim/1 用」薄いラッパー。
|
||||
pub fn lower_case_a_for_trim(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
loop_form: &LoopForm,
|
||||
) -> Option<JoinModule> {
|
||||
self.lower(func, loop_form, Some("FuncScannerBox.trim/1"))
|
||||
}
|
||||
|
||||
/// Case-A 汎用 lowerer の「FuncScannerBox.append_defs/2 用」薄いラッパー。
|
||||
pub fn lower_case_a_for_append_defs(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
loop_form: &LoopForm,
|
||||
) -> Option<JoinModule> {
|
||||
self.lower(func, loop_form, Some("FuncScannerBox.append_defs/2"))
|
||||
}
|
||||
|
||||
/// Case-A 汎用 lowerer の「Stage1UsingResolverBox.resolve_for_source/5 用」薄いラッパー。
|
||||
pub fn lower_case_a_for_stage1_resolver(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
loop_form: &LoopForm,
|
||||
) -> Option<JoinModule> {
|
||||
self.lower(
|
||||
func,
|
||||
loop_form,
|
||||
Some("Stage1UsingResolverBox.resolve_for_source/5"),
|
||||
)
|
||||
}
|
||||
|
||||
/// Case-A 汎用 lowerer の「StageBBodyExtractorBox.build_body_src/2 用」薄いラッパー。
|
||||
pub fn lower_case_a_for_stageb_body(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
loop_form: &LoopForm,
|
||||
) -> Option<JoinModule> {
|
||||
self.lower(
|
||||
func,
|
||||
loop_form,
|
||||
Some("StageBBodyExtractorBox.build_body_src/2"),
|
||||
)
|
||||
}
|
||||
|
||||
/// Case-A 汎用 lowerer の「StageBFuncScannerBox.scan_all_boxes/1 用」薄いラッパー。
|
||||
pub fn lower_case_a_for_stageb_funcscanner(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
loop_form: &LoopForm,
|
||||
) -> Option<JoinModule> {
|
||||
self.lower(
|
||||
func,
|
||||
loop_form,
|
||||
Some("StageBFuncScannerBox.scan_all_boxes/1"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
166
src/mir/join_ir/lowering/loop_to_join/core.rs
Normal file
166
src/mir/join_ir/lowering/loop_to_join/core.rs
Normal file
@ -0,0 +1,166 @@
|
||||
use crate::mir::control_form::LoopId;
|
||||
use crate::mir::join_ir::lowering::loop_form_intake::intake_loop_form;
|
||||
use crate::mir::join_ir::lowering::loop_pattern_validator::LoopPatternValidator;
|
||||
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||
use crate::mir::join_ir::lowering::loop_view_builder::LoopViewBuilder;
|
||||
use crate::mir::join_ir::JoinModule;
|
||||
use crate::mir::loop_form::LoopForm;
|
||||
use crate::mir::query::MirQueryBox;
|
||||
use crate::mir::MirFunction;
|
||||
|
||||
fn generic_case_a_enabled() -> bool {
|
||||
crate::mir::join_ir::env_flag_is_1("NYASH_JOINIR_LOWER_GENERIC")
|
||||
}
|
||||
|
||||
/// Loop→JoinIR 変換の統一箱(coordinator)
|
||||
///
|
||||
/// - MirQuery/Intake/LoopScopeShape の構築
|
||||
/// - Validator/Builder 呼び出しの調整
|
||||
/// - strict mode の fail-fast(対象関数のみ)
|
||||
pub struct LoopToJoinLowerer {
|
||||
debug: bool,
|
||||
validator: LoopPatternValidator,
|
||||
builder: LoopViewBuilder,
|
||||
}
|
||||
|
||||
impl Default for LoopToJoinLowerer {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl LoopToJoinLowerer {
|
||||
pub fn new() -> Self {
|
||||
let debug = std::env::var("NYASH_LOOPTOJOIN_DEBUG")
|
||||
.map(|v| v == "1")
|
||||
.unwrap_or(false);
|
||||
Self {
|
||||
debug,
|
||||
validator: LoopPatternValidator::new(),
|
||||
builder: LoopViewBuilder::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// MIR LoopForm を JoinIR (JoinModule) に変換
|
||||
///
|
||||
/// - `Some(JoinModule)`: 変換成功
|
||||
/// - `None`: 未サポート(上位のフォールバックへ)
|
||||
pub fn lower(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
loop_form: &LoopForm,
|
||||
func_name: Option<&str>,
|
||||
) -> Option<JoinModule> {
|
||||
let strict_on = crate::config::env::joinir_strict_enabled();
|
||||
let is_minimal_target = func_name
|
||||
.map(super::super::loop_scope_shape::is_case_a_minimal_target)
|
||||
.unwrap_or(false);
|
||||
|
||||
if self.debug {
|
||||
eprintln!(
|
||||
"[LoopToJoinLowerer] lower() called for {:?}",
|
||||
func_name.unwrap_or("<unknown>")
|
||||
);
|
||||
}
|
||||
|
||||
let query = MirQueryBox::new(func);
|
||||
let intake = intake_loop_form(loop_form, &query, func)?;
|
||||
let scope = LoopScopeShape::from_loop_form(loop_form, &intake, &query, func_name)?;
|
||||
|
||||
if self.debug {
|
||||
eprintln!(
|
||||
"[LoopToJoinLowerer] LoopScopeShape built: pinned={:?}, carriers={:?}, exit_live={:?}",
|
||||
scope.pinned, scope.carriers, scope.exit_live
|
||||
);
|
||||
}
|
||||
|
||||
let loop_id = LoopId(0);
|
||||
let region = loop_form.to_region_view(loop_id);
|
||||
let exit_edges = loop_form.to_exit_edges(loop_id);
|
||||
|
||||
if self.debug {
|
||||
eprintln!(
|
||||
"[LoopToJoinLowerer] views: func={:?} loop_id={:?} header={:?} exits={:?}",
|
||||
func_name.unwrap_or("<unknown>"),
|
||||
loop_id,
|
||||
region.header,
|
||||
exit_edges.iter().map(|e| e.to).collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
if !self
|
||||
.validator
|
||||
.is_supported_case_a(func, ®ion, &exit_edges, &scope)
|
||||
{
|
||||
if self.debug {
|
||||
eprintln!(
|
||||
"[LoopToJoinLowerer] rejected by validator: {:?}",
|
||||
func_name.unwrap_or("<unknown>")
|
||||
);
|
||||
}
|
||||
if strict_on && is_minimal_target {
|
||||
panic!(
|
||||
"[joinir/loop] strict mode: validator rejected {}",
|
||||
func_name.unwrap_or("<unknown>")
|
||||
);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
if !generic_case_a_enabled() {
|
||||
if !func_name
|
||||
.map_or(false, super::super::loop_scope_shape::is_case_a_minimal_target)
|
||||
{
|
||||
if self.debug {
|
||||
eprintln!(
|
||||
"[LoopToJoinLowerer] rejected by name filter (generic disabled): {:?}",
|
||||
func_name.unwrap_or("<unknown>")
|
||||
);
|
||||
}
|
||||
if strict_on && is_minimal_target {
|
||||
panic!(
|
||||
"[joinir/loop] strict mode: name filter rejected {}",
|
||||
func_name.unwrap_or("<unknown>")
|
||||
);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
} else if self.debug {
|
||||
eprintln!(
|
||||
"[LoopToJoinLowerer] generic Case-A enabled, allowing {:?}",
|
||||
func_name.unwrap_or("<unknown>")
|
||||
);
|
||||
}
|
||||
|
||||
let out = self.builder.build(scope, func_name);
|
||||
if out.is_none() && strict_on && is_minimal_target {
|
||||
panic!(
|
||||
"[joinir/loop] strict mode: lowering failed for {}",
|
||||
func_name.unwrap_or("<unknown>")
|
||||
);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// 旧コメント/ドキュメントとの整合のための別名(導線の明確化)
|
||||
pub fn lower_loop(
|
||||
&self,
|
||||
func: &MirFunction,
|
||||
loop_form: &LoopForm,
|
||||
func_name: Option<&str>,
|
||||
) -> Option<JoinModule> {
|
||||
self.lower(func, loop_form, func_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_lowerer_creation() {
|
||||
let lowerer = LoopToJoinLowerer::new();
|
||||
assert!(!lowerer.debug || lowerer.debug);
|
||||
}
|
||||
}
|
||||
|
||||
19
src/mir/join_ir/lowering/loop_to_join/mod.rs
Normal file
19
src/mir/join_ir/lowering/loop_to_join/mod.rs
Normal file
@ -0,0 +1,19 @@
|
||||
//! Phase 31/33-23: Loop→JoinIR lowering entry (LoopToJoinLowerer)
|
||||
//!
|
||||
//! このモジュールは「LoopForm を JoinIR (JoinModule) に変換する統一エントリ」を提供する。
|
||||
//! 本体ロジックは coordinator に集約し、責務を分割して保守性を上げる。
|
||||
//!
|
||||
//! ## 責務境界(Box化)
|
||||
//! - `LoopPatternValidator`(`../loop_pattern_validator.rs`): 構造検証(shape guard)
|
||||
//! - `LoopViewBuilder`(`../loop_view_builder.rs`): lowerer 選択(routing)
|
||||
//! - `LoopToJoinLowerer`(このモジュール): intake/scope 構築と strict ハンドリング(coordinator)
|
||||
//!
|
||||
//! ## 注意
|
||||
//! - Phase 進捗ログはここに混ぜない(現役の導線のみ)。
|
||||
//! - “とりあえず通す”フォールバックは増やさない。失敗は `None` で上位に返す。
|
||||
|
||||
mod core;
|
||||
mod case_a_entrypoints;
|
||||
|
||||
pub use core::LoopToJoinLowerer;
|
||||
|
||||
@ -34,7 +34,7 @@ use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
|
||||
use crate::mir::join_ir::lowering::condition_lowerer::lower_value_expression;
|
||||
#[cfg(debug_assertions)]
|
||||
use crate::mir::join_ir::lowering::condition_pattern::{
|
||||
analyze_condition_pattern, ConditionPattern,
|
||||
analyze_condition_capability, ConditionCapability,
|
||||
};
|
||||
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
||||
use crate::mir::join_ir::{
|
||||
@ -71,11 +71,11 @@ pub fn lower_if_sum_pattern(
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if let ASTNode::If { condition, .. } = if_stmt {
|
||||
let pattern = analyze_condition_pattern(condition);
|
||||
let capability = analyze_condition_capability(condition);
|
||||
debug_assert!(
|
||||
matches!(pattern, ConditionPattern::SimpleComparison),
|
||||
"[if-sum] Unexpected complex condition pattern passed to AST-based lowerer: {:?}",
|
||||
pattern
|
||||
matches!(capability, ConditionCapability::IfSumComparable),
|
||||
"[if-sum] Unsupported condition passed to AST-based lowerer: {:?}",
|
||||
capability
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -82,15 +82,16 @@ use crate::mir::join_ir::{
|
||||
/// # Boundary Contract
|
||||
///
|
||||
/// This function returns a JoinModule with:
|
||||
/// - **Input slot**: ValueId(0) in loop_step function represents the loop variable
|
||||
/// - **Caller responsibility**: Create JoinInlineBoundary to map ValueId(0) to host's loop var
|
||||
/// - **Input slot**: main() の param(JoinValueSpace の Param region)でループ変数を受け取る
|
||||
/// - **Caller responsibility**: Create JoinInlineBoundary to map that param ValueId to host's loop var
|
||||
pub(crate) fn lower_simple_while_minimal(
|
||||
_scope: LoopScopeShape,
|
||||
join_value_space: &mut JoinValueSpace,
|
||||
) -> Option<JoinModule> {
|
||||
// Phase 202-A: Use JoinValueSpace for Local region allocation (1000+)
|
||||
// This ensures no collision with Param region (100-999) used by ConditionEnv/CarrierInfo
|
||||
let mut alloc_value = || join_value_space.alloc_local();
|
||||
// Phase 202-A/Phase 205: Use JoinValueSpace for Param/Local region allocation.
|
||||
// - Params: boundary join_inputs (host loop var wiring)
|
||||
// - Locals: constants, intermediate values
|
||||
// NOTE: Avoid holding multiple closures borrowing join_value_space mutably at once.
|
||||
|
||||
let mut join_module = JoinModule::new();
|
||||
|
||||
@ -102,36 +103,34 @@ pub(crate) fn lower_simple_while_minimal(
|
||||
let k_exit_id = JoinFuncId::new(2);
|
||||
|
||||
// ==================================================================
|
||||
// ValueId allocation (Phase 188-Impl-3: Sequential local IDs)
|
||||
// ValueId allocation
|
||||
// ==================================================================
|
||||
// main() locals
|
||||
let i_init = alloc_value(); // ValueId(0) - loop init value
|
||||
let loop_result = alloc_value(); // ValueId(1) - result from loop_step
|
||||
let const_0_main = alloc_value(); // ValueId(2) - return value
|
||||
// main() params/locals
|
||||
let i_main_param = join_value_space.alloc_param(); // boundary input (host loop var → this param)
|
||||
let loop_result = join_value_space.alloc_local(); // result from loop_step
|
||||
let const_0_main = join_value_space.alloc_local(); // return value
|
||||
|
||||
// loop_step locals
|
||||
let i_param = alloc_value(); // ValueId(3) - parameter
|
||||
let const_3 = alloc_value(); // ValueId(4) - comparison constant
|
||||
let cmp_lt = alloc_value(); // ValueId(5) - i < 3
|
||||
let exit_cond = alloc_value(); // ValueId(6) - !(i < 3)
|
||||
let const_1 = alloc_value(); // ValueId(7) - increment constant
|
||||
let i_next = alloc_value(); // ValueId(8) - i + 1
|
||||
// loop_step params/locals
|
||||
let i_step_param = join_value_space.alloc_param(); // loop_step parameter
|
||||
let const_3 = join_value_space.alloc_local(); // comparison constant
|
||||
let cmp_lt = join_value_space.alloc_local(); // i < 3
|
||||
let exit_cond = join_value_space.alloc_local(); // !(i < 3)
|
||||
let const_1 = join_value_space.alloc_local(); // increment constant
|
||||
let i_next = join_value_space.alloc_local(); // i + 1
|
||||
|
||||
// k_exit locals
|
||||
// Phase 132: i_exit receives loop variable from Jump
|
||||
let i_exit = alloc_value(); // ValueId(9) - exit parameter (loop variable)
|
||||
// k_exit params
|
||||
let i_exit_param = join_value_space.alloc_param(); // exit parameter (loop variable)
|
||||
|
||||
// ==================================================================
|
||||
// main() function
|
||||
// ==================================================================
|
||||
// Phase 188-Impl-3: main() takes i as a parameter (boundary input)
|
||||
// The host will inject a Copy instruction: i_init_local = Copy host_i
|
||||
let mut main_func = JoinFunction::new(main_id, "main".to_string(), vec![i_init]);
|
||||
// main() takes loop var as a param (boundary input)
|
||||
let mut main_func = JoinFunction::new(main_id, "main".to_string(), vec![i_main_param]);
|
||||
|
||||
// result = loop_step(i_init)
|
||||
// result = loop_step(i_main_param)
|
||||
main_func.body.push(JoinInst::Call {
|
||||
func: loop_step_id,
|
||||
args: vec![i_init],
|
||||
args: vec![i_main_param],
|
||||
k_next: None,
|
||||
dst: Some(loop_result),
|
||||
});
|
||||
@ -152,7 +151,7 @@ pub(crate) fn lower_simple_while_minimal(
|
||||
// loop_step(i) function
|
||||
// ==================================================================
|
||||
let mut loop_step_func =
|
||||
JoinFunction::new(loop_step_id, "loop_step".to_string(), vec![i_param]);
|
||||
JoinFunction::new(loop_step_id, "loop_step".to_string(), vec![i_step_param]);
|
||||
|
||||
// exit_cond = !(i < 3)
|
||||
// Step 1: const 3
|
||||
@ -169,7 +168,7 @@ pub(crate) fn lower_simple_while_minimal(
|
||||
.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: cmp_lt,
|
||||
op: CompareOp::Lt,
|
||||
lhs: i_param,
|
||||
lhs: i_step_param,
|
||||
rhs: const_3,
|
||||
}));
|
||||
|
||||
@ -186,7 +185,7 @@ pub(crate) fn lower_simple_while_minimal(
|
||||
// Pass loop variable to exit continuation for return value parity
|
||||
loop_step_func.body.push(JoinInst::Jump {
|
||||
cont: k_exit_id.as_cont(),
|
||||
args: vec![i_param],
|
||||
args: vec![i_step_param],
|
||||
cond: Some(exit_cond),
|
||||
});
|
||||
|
||||
@ -194,7 +193,7 @@ pub(crate) fn lower_simple_while_minimal(
|
||||
// Phase 188-Impl-1-E: Use Print instruction
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::Print { value: i_param }));
|
||||
.push(JoinInst::Compute(MirLikeInst::Print { value: i_step_param }));
|
||||
|
||||
// i_next = i + 1
|
||||
// Step 1: const 1
|
||||
@ -211,7 +210,7 @@ pub(crate) fn lower_simple_while_minimal(
|
||||
.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: i_next,
|
||||
op: BinOpKind::Add,
|
||||
lhs: i_param,
|
||||
lhs: i_step_param,
|
||||
rhs: const_1,
|
||||
}));
|
||||
|
||||
@ -228,12 +227,12 @@ pub(crate) fn lower_simple_while_minimal(
|
||||
// ==================================================================
|
||||
// k_exit(i_exit) function - Phase 132: receives loop variable
|
||||
// ==================================================================
|
||||
let mut k_exit_func = JoinFunction::new(k_exit_id, "k_exit".to_string(), vec![i_exit]);
|
||||
let mut k_exit_func = JoinFunction::new(k_exit_id, "k_exit".to_string(), vec![i_exit_param]);
|
||||
|
||||
// Phase 132: return i_exit (loop variable at exit)
|
||||
// This ensures VM/LLVM parity for `return i` after loop
|
||||
k_exit_func.body.push(JoinInst::Ret {
|
||||
value: Some(i_exit),
|
||||
value: Some(i_exit_param),
|
||||
});
|
||||
|
||||
join_module.add_function(k_exit_func);
|
||||
|
||||
Reference in New Issue
Block a user