joinir: clean pattern visibility and refactor pattern2 pipeline
This commit is contained in:
@ -1032,3 +1032,110 @@ JoinIR は Rust 側だけでなく、将来的に .hako selfhost コンパイラ
|
|||||||
- 根本原因: carrier 検出ロジックの name heuristic が脆弱
|
- 根本原因: carrier 検出ロジックの name heuristic が脆弱
|
||||||
- **次フェーズ**: Carrier 検出修正(Phase 219)
|
- **次フェーズ**: Carrier 検出修正(Phase 219)
|
||||||
- 詳細: phase218-jsonparser-if-sum-min.md
|
- 詳細: phase218-jsonparser-if-sum-min.md
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. JoinIR → JoinIR 正規化レイヤ(構想 / Phase 26-H ライン)
|
||||||
|
|
||||||
|
### 3.1 Structured JoinIR と Normalized JoinIR(概念レベル)
|
||||||
|
|
||||||
|
JoinIR ラインは、今後は「同じ JoinIR をフェーズごとに正規化していく」二段構成で扱う想定だよ:
|
||||||
|
|
||||||
|
- **Structured JoinIR(現行層)**
|
||||||
|
- Pattern1–5 / CarrierInfo / ConditionEnv / UpdateEnv / Boundary / ExitLine までを終えた状態。
|
||||||
|
- while/if/break/continue/return がまだ「構造」として残っていてよい層。
|
||||||
|
- いまの JoinIR lowering(Pattern lowerer 群)が出力しているものはここ。
|
||||||
|
|
||||||
|
- **Normalized JoinIR(JoinIR’ / CPS 風層)**
|
||||||
|
- 制御構造をすべて「関数+継続+ Env」の形に正規化した状態。
|
||||||
|
- ループは `loop_step(env, k_exit)` と exit 継続のペア、if は `if_branch(env, k_then, k_else)` + join 継続で表現。
|
||||||
|
- while/break/continue/return は **TailCallFn/TailCallKont + If** だけに還元され、goto/生の branch は現れない。
|
||||||
|
- Env は「ループキャリア + DigitPos/num_str などの Derived + captured 変数」を 1 つの struct としてまとめる。
|
||||||
|
|
||||||
|
パイプラインのイメージ:
|
||||||
|
|
||||||
|
```text
|
||||||
|
AST
|
||||||
|
↓
|
||||||
|
JoinIR(Structured) // Pattern1–5, CarrierInfo, ConditionEnv/UpdateEnv, Boundary/ExitLine
|
||||||
|
↓ JoinIR パス A: 正規化(JoinIR → JoinIR)
|
||||||
|
JoinIR(Normalized) // 関数 + 継続 + Env のみ(TailCall-only)
|
||||||
|
↓ JoinIR→MIR bridge
|
||||||
|
MIR
|
||||||
|
```
|
||||||
|
|
||||||
|
型レベルでは、次の二案のどちらかで扱う想定だよ(詳細は今後の Phase で選択):
|
||||||
|
|
||||||
|
- **案① 型を分ける**
|
||||||
|
- `JoinModuleRaw`(Structured)、`JoinModuleCps`(Normalized)を別型にする。
|
||||||
|
- Bridge(JoinIR→MIR)は `JoinModuleCps` だけを受け取る。
|
||||||
|
- **案② 型は 1 つ+フェーズフラグ**
|
||||||
|
- `JoinModule { phase: JoinIrPhase, ... }` として、`phase = Structured / Normalized` をメタデータで持つ。
|
||||||
|
- JoinIR パスはすべて `fn run(&mut JoinModule)`(= joinir→joinir)で構成し、
|
||||||
|
Verifier が「phase=Normalized なら while/if/break/continue/return 禁止」などの不変条件をチェックする。
|
||||||
|
|
||||||
|
どちらの案でも本質は同じで、「JoinIR の下に JoinIR’(= 正規形フェーズ)を 1 段挟む」という設計、というのがポイントだよ。
|
||||||
|
|
||||||
|
### 3.2 Normalized JoinIR の基本モデル(ラフスケッチ)
|
||||||
|
|
||||||
|
Normalized JoinIR では、制御構造を次の 3 要素だけで表現する想定だよ:
|
||||||
|
|
||||||
|
- **Env(環境)**
|
||||||
|
- そのループ/if に必要な情報を 1 つにまとめた struct。
|
||||||
|
- フィールド種別の例:
|
||||||
|
- `Carrier`(LoopState キャリア: i, sum, result, num_state 等)
|
||||||
|
- `Derived`(DigitPos 二重値, NumberAccumulation 等)
|
||||||
|
- `Captured`(外側のローカルや関数パラメータ: s, len 等)
|
||||||
|
|
||||||
|
- **Fn(通常の関数)**
|
||||||
|
- 典型例: `loop_step(env, k_exit)` / `loop_body(env, k_exit)`。
|
||||||
|
- 末尾は常に `TailCallFn` または `TailCallKont`。
|
||||||
|
|
||||||
|
- **Kont(継続)**
|
||||||
|
- 典型例: ループ exit 後の処理、if 後の join、関数 return など。
|
||||||
|
- 末尾は別の Fn/Kont への tail-call。
|
||||||
|
|
||||||
|
制御の不変条件(Normalized フェーズ):
|
||||||
|
|
||||||
|
1. 制御フローは `TailCallFn` / `TailCallKont` / `If(cond, then_k, else_k, env)` のみで表現する。
|
||||||
|
2. 各ループは「`loop_step(env, k_exit)` 関数 + `k_exit(env)` 継続」のペアとして現れる(break は必ず k_exit へ TailCall)。
|
||||||
|
3. continue は `loop_step` への TailCall として現れる(ヘッダ PHI 相当は Env の書き込み順で表現)。
|
||||||
|
4. return は専用の `return_kont(env)` への TailCall で表現される。
|
||||||
|
5. EnvLayoutVerifier(検証箱)が、常に正しい EnvLayout が使われていることをチェックする。
|
||||||
|
|
||||||
|
これにより、現在 JoinIR 層で苦労している「PHI 配線」「exit_bindings/jump_args 整合性」「評価順のねじれ」は、
|
||||||
|
Normalized JoinIR 側では「Env フィールドの更新順」と「どの継続を呼ぶか」に還元される想定だよ。
|
||||||
|
|
||||||
|
### 3.3 Phase 26-H のフェーズ分割(案)
|
||||||
|
|
||||||
|
この正規化レイヤを一度に入れるのは重いので、Phase 26-H ラインとして小さく段階分けして進める方針だよ。
|
||||||
|
(番号は仮で、実際の Phase 番号は current の進行に合わせて調整する想定)
|
||||||
|
|
||||||
|
1. **Phase 26-H.A – モデル定義と Doc 固定(コード変更最小)**
|
||||||
|
- JoinIR Architecture Overview(このファイル)に Structured / Normalized の二層構造を明文化(本節)。
|
||||||
|
- `JoinIrPhase` などのメタ情報だけを追加し、既存 lowering / Bridge の挙動は変えない。
|
||||||
|
- Normalized JoinIR の不変条件(TailCall-only / EnvLayout 整合性)を Verifier の設計として書き出す。
|
||||||
|
|
||||||
|
2. **Phase 26-H.B – 最小サブセットでの JoinIR→JoinIR パス導入**
|
||||||
|
- Pattern1(単純 while, break/continue なし)だけを対象にした Normalized パスを実装。
|
||||||
|
- パイプライン:
|
||||||
|
- 既存: `JoinIR(Structured) → MIR`
|
||||||
|
- 新規: `JoinIR(Structured) → JoinIR(Normalized, small subset) → MIR`
|
||||||
|
- 新規パスは dev フラグの裏側で比較用にのみ使い、E2E で結果が一致することを確認(既定経路は従来のまま)。
|
||||||
|
|
||||||
|
3. **Phase 26-H.C – Pattern2–4 / DigitPos / JsonParser への拡張**
|
||||||
|
- Pattern2/3/4 と DigitPos / NumberAccumulation / Trim を Normalized パスの対象に広げる。
|
||||||
|
- JsonParser `_parse_number` / `_atoi` を優先対象にし、JoinIR(Normalized) 経由での実行を dev フラグ付きで有効化。
|
||||||
|
- JoinIR(Structured) → JoinIR(Normalized) の各ステップに構造テストを追加し、「Env の更新」「継続の呼び出し順」が期待どおりかを固定。
|
||||||
|
|
||||||
|
4. **Phase 26-H.D – Normalized JoinIR を canonical route に昇格**
|
||||||
|
- JoinIR→MIR Bridge が「Normalized フェーズの JoinIR だけ」を受け取るように変更。
|
||||||
|
- Structured JoinIR → MIR の直接パスは「比較テスト専用」として残すか、段階的に削除。
|
||||||
|
- selfhost / JsonParser / hako_check の代表ループはすべて Normalized JoinIR 経由で通ることを確認。
|
||||||
|
|
||||||
|
各サブフェーズでは「既存意味論を変えない」「Fail-Fast 原則を維持する」「新旧経路の比較テストを先に用意する」をガードとして運用するよ。
|
||||||
|
詳細な API/型設計は、Phase 26-H.A/B の中で `EnvLayoutBox` / `LoopStepSynthesizer` / `JpVerifier` 等の箱として段階的に固めていく想定。
|
||||||
|
|
||||||
|
### 3.4 Phase 27-CLEAN – Pattern2–4 の軽量整理
|
||||||
|
|
||||||
|
- Pattern2〜4/loop_with_break_minimal まわりで可視性・ログ・補助関数を整理し、joinir_dev フラグ配下のデバッグログに寄せる。意味論は変えずに「読みやすさ」「追いやすさ」を優先するクリーンアップフェーズだよ。
|
||||||
|
|||||||
@ -20,8 +20,8 @@
|
|||||||
//! - Delegates to specialized analyzers for break/continue logic
|
//! - Delegates to specialized analyzers for break/continue logic
|
||||||
|
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use crate::mir::loop_pattern_detection::LoopFeatures;
|
|
||||||
use crate::mir::loop_pattern_detection::break_condition_analyzer::BreakConditionAnalyzer;
|
use crate::mir::loop_pattern_detection::break_condition_analyzer::BreakConditionAnalyzer;
|
||||||
|
use crate::mir::loop_pattern_detection::LoopFeatures;
|
||||||
|
|
||||||
/// Detect if a loop body contains continue statements
|
/// Detect if a loop body contains continue statements
|
||||||
///
|
///
|
||||||
@ -37,7 +37,7 @@ use crate::mir::loop_pattern_detection::break_condition_analyzer::BreakCondition
|
|||||||
///
|
///
|
||||||
/// This is a simple recursive scan that doesn't handle nested loops perfectly,
|
/// This is a simple recursive scan that doesn't handle nested loops perfectly,
|
||||||
/// but is sufficient for initial pattern detection.
|
/// but is sufficient for initial pattern detection.
|
||||||
pub fn detect_continue_in_body(body: &[ASTNode]) -> bool {
|
pub(crate) fn detect_continue_in_body(body: &[ASTNode]) -> bool {
|
||||||
for stmt in body {
|
for stmt in body {
|
||||||
if has_continue_node(stmt) {
|
if has_continue_node(stmt) {
|
||||||
return true;
|
return true;
|
||||||
@ -55,7 +55,7 @@ pub fn detect_continue_in_body(body: &[ASTNode]) -> bool {
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// `true` if at least one break statement is found in the body or nested structures
|
/// `true` if at least one break statement is found in the body or nested structures
|
||||||
pub fn detect_break_in_body(body: &[ASTNode]) -> bool {
|
pub(crate) fn detect_break_in_body(body: &[ASTNode]) -> bool {
|
||||||
for stmt in body {
|
for stmt in body {
|
||||||
if has_break_node(stmt) {
|
if has_break_node(stmt) {
|
||||||
return true;
|
return true;
|
||||||
@ -78,11 +78,7 @@ pub fn detect_break_in_body(body: &[ASTNode]) -> bool {
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// A LoopFeatures struct containing all detected structural characteristics
|
/// A LoopFeatures struct containing all detected structural characteristics
|
||||||
pub fn extract_features(
|
pub(crate) fn extract_features(body: &[ASTNode], has_continue: bool, has_break: bool) -> LoopFeatures {
|
||||||
body: &[ASTNode],
|
|
||||||
has_continue: bool,
|
|
||||||
has_break: bool,
|
|
||||||
) -> LoopFeatures {
|
|
||||||
// Phase 212.5: Detect ANY if statement in loop body (structural detection)
|
// Phase 212.5: Detect ANY if statement in loop body (structural detection)
|
||||||
let has_if = detect_if_in_body(body);
|
let has_if = detect_if_in_body(body);
|
||||||
|
|
||||||
@ -147,8 +143,12 @@ fn detect_if_else_phi_in_body(body: &[ASTNode]) -> bool {
|
|||||||
} = node
|
} = node
|
||||||
{
|
{
|
||||||
// Check if both branches have assignments
|
// Check if both branches have assignments
|
||||||
let then_has_assign = then_body.iter().any(|n| matches!(n, ASTNode::Assignment { .. }));
|
let then_has_assign = then_body
|
||||||
let else_has_assign = else_body.iter().any(|n| matches!(n, ASTNode::Assignment { .. }));
|
.iter()
|
||||||
|
.any(|n| matches!(n, ASTNode::Assignment { .. }));
|
||||||
|
let else_has_assign = else_body
|
||||||
|
.iter()
|
||||||
|
.any(|n| matches!(n, ASTNode::Assignment { .. }));
|
||||||
if then_has_assign && else_has_assign {
|
if then_has_assign && else_has_assign {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -193,7 +193,11 @@ fn count_carriers_in_body(body: &[ASTNode]) -> usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Return at least 1 if we have assignments, otherwise 0
|
// Return at least 1 if we have assignments, otherwise 0
|
||||||
if count > 0 { 1 } else { 0 }
|
if count > 0 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursive helper to check if AST node contains continue
|
/// Recursive helper to check if AST node contains continue
|
||||||
@ -248,7 +252,7 @@ fn has_break_node(node: &ASTNode) -> bool {
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// `true` if an `if ... else { break }` pattern is found
|
/// `true` if an `if ... else { break }` pattern is found
|
||||||
pub fn has_break_in_else_clause(body: &[ASTNode]) -> bool {
|
pub(crate) fn has_break_in_else_clause(body: &[ASTNode]) -> bool {
|
||||||
BreakConditionAnalyzer::has_break_in_else_clause(body)
|
BreakConditionAnalyzer::has_break_in_else_clause(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,7 +290,7 @@ pub fn has_break_in_else_clause(body: &[ASTNode]) -> bool {
|
|||||||
/// // <- Returns the "!(ch == " ")" condition (negated)
|
/// // <- Returns the "!(ch == " ")" condition (negated)
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn extract_break_condition(body: &[ASTNode]) -> Option<&ASTNode> {
|
pub(crate) fn extract_break_condition(body: &[ASTNode]) -> Option<&ASTNode> {
|
||||||
BreakConditionAnalyzer::extract_break_condition(body).ok()
|
BreakConditionAnalyzer::extract_break_condition(body).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,13 +300,17 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_detect_continue_simple() {
|
fn test_detect_continue_simple() {
|
||||||
let continue_node = ASTNode::Continue { span: crate::ast::Span::unknown() };
|
let continue_node = ASTNode::Continue {
|
||||||
|
span: crate::ast::Span::unknown(),
|
||||||
|
};
|
||||||
assert!(has_continue_node(&continue_node));
|
assert!(has_continue_node(&continue_node));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_detect_break_simple() {
|
fn test_detect_break_simple() {
|
||||||
let break_node = ASTNode::Break { span: crate::ast::Span::unknown() };
|
let break_node = ASTNode::Break {
|
||||||
|
span: crate::ast::Span::unknown(),
|
||||||
|
};
|
||||||
assert!(has_break_node(&break_node));
|
assert!(has_break_node(&break_node));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -32,13 +32,13 @@
|
|||||||
//! This module is now a thin wrapper around `CarrierInfo::from_variable_map()`.
|
//! This module is now a thin wrapper around `CarrierInfo::from_variable_map()`.
|
||||||
//! The primary logic lives in `carrier_info.rs` for consistency across MIR and JoinIR contexts.
|
//! The primary logic lives in `carrier_info.rs` for consistency across MIR and JoinIR contexts.
|
||||||
|
|
||||||
use crate::mir::builder::MirBuilder;
|
|
||||||
use crate::mir::ValueId;
|
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
|
use crate::mir::builder::MirBuilder;
|
||||||
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
||||||
|
use crate::mir::ValueId;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
pub struct CommonPatternInitializer;
|
pub(crate) struct CommonPatternInitializer;
|
||||||
|
|
||||||
impl CommonPatternInitializer {
|
impl CommonPatternInitializer {
|
||||||
/// Initialize pattern context: extract loop var, build CarrierInfo
|
/// Initialize pattern context: extract loop var, build CarrierInfo
|
||||||
@ -84,28 +84,24 @@ impl CommonPatternInitializer {
|
|||||||
) -> Result<(String, ValueId, CarrierInfo), String> {
|
) -> Result<(String, ValueId, CarrierInfo), String> {
|
||||||
// Step 1: Extract loop variable from condition
|
// Step 1: Extract loop variable from condition
|
||||||
let loop_var_name = builder.extract_loop_variable_from_condition(condition)?;
|
let loop_var_name = builder.extract_loop_variable_from_condition(condition)?;
|
||||||
let loop_var_id = variable_map
|
let loop_var_id = variable_map.get(&loop_var_name).copied().ok_or_else(|| {
|
||||||
.get(&loop_var_name)
|
format!(
|
||||||
.copied()
|
"[common_init] Loop variable '{}' not found in variable_map",
|
||||||
.ok_or_else(|| {
|
loop_var_name
|
||||||
format!(
|
)
|
||||||
"[common_init] Loop variable '{}' not found in variable_map",
|
})?;
|
||||||
loop_var_name
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Phase 183-2: Delegate to CarrierInfo::from_variable_map for consistency
|
// Phase 183-2: Delegate to CarrierInfo::from_variable_map for consistency
|
||||||
// Phase 222.5-D: Direct BTreeMap usage (no conversion needed)
|
// Phase 222.5-D: Direct BTreeMap usage (no conversion needed)
|
||||||
|
|
||||||
// Step 2: Use CarrierInfo::from_variable_map as primary initialization method
|
// Step 2: Use CarrierInfo::from_variable_map as primary initialization method
|
||||||
let mut carrier_info = CarrierInfo::from_variable_map(
|
let mut carrier_info = CarrierInfo::from_variable_map(loop_var_name.clone(), variable_map)?;
|
||||||
loop_var_name.clone(),
|
|
||||||
variable_map,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Step 3: Apply exclusions if provided (Pattern 2 specific)
|
// Step 3: Apply exclusions if provided (Pattern 2 specific)
|
||||||
if let Some(excluded) = exclude_carriers {
|
if let Some(excluded) = exclude_carriers {
|
||||||
carrier_info.carriers.retain(|c| !excluded.contains(&c.name.as_str()));
|
carrier_info
|
||||||
|
.carriers
|
||||||
|
.retain(|c| !excluded.contains(&c.name.as_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((loop_var_name, loop_var_id, carrier_info))
|
Ok((loop_var_name, loop_var_id, carrier_info))
|
||||||
@ -150,28 +146,33 @@ impl CommonPatternInitializer {
|
|||||||
_loop_var_name: &str,
|
_loop_var_name: &str,
|
||||||
_variable_map: &BTreeMap<String, ValueId>,
|
_variable_map: &BTreeMap<String, ValueId>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
use crate::mir::join_ir::lowering::loop_update_analyzer::{LoopUpdateAnalyzer, UpdateExpr, UpdateRhs};
|
|
||||||
use crate::mir::join_ir::lowering::carrier_info::CarrierVar;
|
use crate::mir::join_ir::lowering::carrier_info::CarrierVar;
|
||||||
|
use crate::mir::join_ir::lowering::loop_update_analyzer::{
|
||||||
|
LoopUpdateAnalyzer, UpdateExpr, UpdateRhs,
|
||||||
|
};
|
||||||
|
|
||||||
// Create dummy carriers from body assignment targets for analysis
|
// Create dummy carriers from body assignment targets for analysis
|
||||||
let dummy_carriers: Vec<CarrierVar> = body.iter().filter_map(|node| {
|
let dummy_carriers: Vec<CarrierVar> = body
|
||||||
match node {
|
.iter()
|
||||||
ASTNode::Assignment { target, .. } => {
|
.filter_map(|node| {
|
||||||
if let ASTNode::Variable { name, .. } = target.as_ref() {
|
match node {
|
||||||
Some(CarrierVar {
|
ASTNode::Assignment { target, .. } => {
|
||||||
|
if let ASTNode::Variable { name, .. } = target.as_ref() {
|
||||||
|
Some(CarrierVar {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
host_id: ValueId(0), // Dummy
|
host_id: ValueId(0), // Dummy
|
||||||
join_id: None,
|
join_id: None,
|
||||||
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, // Phase 227: Default
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, // Phase 227: Default
|
||||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228: Default
|
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228: Default
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
_ => None,
|
})
|
||||||
}
|
.collect();
|
||||||
}).collect();
|
|
||||||
|
|
||||||
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(body, &dummy_carriers);
|
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(body, &dummy_carriers);
|
||||||
|
|
||||||
|
|||||||
@ -35,7 +35,7 @@ use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
|
|||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
pub struct ConditionEnvBuilder;
|
pub(crate) struct ConditionEnvBuilder;
|
||||||
|
|
||||||
impl ConditionEnvBuilder {
|
impl ConditionEnvBuilder {
|
||||||
/// Phase 201: Build ConditionEnv using JoinValueSpace (disjoint ValueId regions)
|
/// Phase 201: Build ConditionEnv using JoinValueSpace (disjoint ValueId regions)
|
||||||
@ -65,10 +65,8 @@ impl ConditionEnvBuilder {
|
|||||||
space: &mut JoinValueSpace,
|
space: &mut JoinValueSpace,
|
||||||
) -> Result<(ConditionEnv, Vec<ConditionBinding>, ValueId), String> {
|
) -> Result<(ConditionEnv, Vec<ConditionBinding>, ValueId), String> {
|
||||||
// Extract all variables used in the condition (excluding loop parameter)
|
// Extract all variables used in the condition (excluding loop parameter)
|
||||||
let condition_var_names = extract_condition_variables(
|
let condition_var_names =
|
||||||
break_condition,
|
extract_condition_variables(break_condition, &[loop_var_name.to_string()]);
|
||||||
&[loop_var_name.to_string()],
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut env = ConditionEnv::new();
|
let mut env = ConditionEnv::new();
|
||||||
let mut bindings = Vec::new();
|
let mut bindings = Vec::new();
|
||||||
@ -79,16 +77,13 @@ impl ConditionEnvBuilder {
|
|||||||
|
|
||||||
// For each condition variable, allocate JoinIR-local ValueId and build binding
|
// For each condition variable, allocate JoinIR-local ValueId and build binding
|
||||||
for var_name in &condition_var_names {
|
for var_name in &condition_var_names {
|
||||||
let host_id = variable_map
|
let host_id = variable_map.get(var_name).copied().ok_or_else(|| {
|
||||||
.get(var_name)
|
format!(
|
||||||
.copied()
|
"Condition variable '{}' not found in variable_map. \
|
||||||
.ok_or_else(|| {
|
|
||||||
format!(
|
|
||||||
"Condition variable '{}' not found in variable_map. \
|
|
||||||
Loop condition references undefined variable.",
|
Loop condition references undefined variable.",
|
||||||
var_name
|
var_name
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Phase 201: Allocate from Param region to avoid collision with locals
|
// Phase 201: Allocate from Param region to avoid collision with locals
|
||||||
let join_id = space.alloc_param();
|
let join_id = space.alloc_param();
|
||||||
@ -108,7 +103,10 @@ impl ConditionEnvBuilder {
|
|||||||
///
|
///
|
||||||
/// Uses JoinValueSpace to allocate the loop parameter ValueId.
|
/// Uses JoinValueSpace to allocate the loop parameter ValueId.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn build_loop_param_only_v2(loop_var_name: &str, space: &mut JoinValueSpace) -> (ConditionEnv, ValueId) {
|
pub fn build_loop_param_only_v2(
|
||||||
|
loop_var_name: &str,
|
||||||
|
space: &mut JoinValueSpace,
|
||||||
|
) -> (ConditionEnv, ValueId) {
|
||||||
let mut env = ConditionEnv::new();
|
let mut env = ConditionEnv::new();
|
||||||
let loop_var_join_id = space.alloc_param();
|
let loop_var_join_id = space.alloc_param();
|
||||||
env.insert(loop_var_name.to_string(), loop_var_join_id);
|
env.insert(loop_var_name.to_string(), loop_var_join_id);
|
||||||
@ -177,7 +175,10 @@ impl ConditionEnvBuilder {
|
|||||||
let debug = env::var("NYASH_CAPTURE_DEBUG").is_ok();
|
let debug = env::var("NYASH_CAPTURE_DEBUG").is_ok();
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!("[capture/env_builder] Building ConditionEnv with {} captured vars", captured.vars.len());
|
eprintln!(
|
||||||
|
"[capture/env_builder] Building ConditionEnv with {} captured vars",
|
||||||
|
captured.vars.len()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 1: Build base ConditionEnv with loop params using v2 API (Phase 222.5-B)
|
// Step 1: Build base ConditionEnv with loop params using v2 API (Phase 222.5-B)
|
||||||
@ -197,17 +198,25 @@ impl ConditionEnvBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 2b: Add to boundary with Condition role
|
// 2b: Add to boundary with Condition role
|
||||||
boundary.add_param_with_role(&var.name, host_id, crate::mir::join_ir::lowering::inline_boundary_builder::ParamRole::Condition);
|
boundary.add_param_with_role(
|
||||||
|
&var.name,
|
||||||
|
host_id,
|
||||||
|
crate::mir::join_ir::lowering::inline_boundary_builder::ParamRole::Condition,
|
||||||
|
);
|
||||||
|
|
||||||
// 2c: Get JoinIR ValueId from boundary
|
// 2c: Get JoinIR ValueId from boundary
|
||||||
let join_id = boundary.get_condition_binding(&var.name)
|
let join_id = boundary
|
||||||
|
.get_condition_binding(&var.name)
|
||||||
.expect("captured var should be in boundary after add_param_with_role");
|
.expect("captured var should be in boundary after add_param_with_role");
|
||||||
|
|
||||||
// 2d: Add to ConditionEnv.captured map
|
// 2d: Add to ConditionEnv.captured map
|
||||||
env.captured.insert(var.name.clone(), join_id);
|
env.captured.insert(var.name.clone(), join_id);
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!("[capture/env_builder] Added captured var '{}': host={:?}, join={:?}", var.name, host_id, join_id);
|
eprintln!(
|
||||||
|
"[capture/env_builder] Added captured var '{}': host={:?}, join={:?}",
|
||||||
|
var.name, host_id, join_id
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +233,11 @@ impl ConditionEnvBuilder {
|
|||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
let param_count = env.iter().count();
|
let param_count = env.iter().count();
|
||||||
eprintln!("[capture/env_builder] Final ConditionEnv: {} params, {} captured", param_count, env.captured.len());
|
eprintln!(
|
||||||
|
"[capture/env_builder] Final ConditionEnv: {} params, {} captured",
|
||||||
|
param_count,
|
||||||
|
env.captured.len()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
(env, loop_var_join_id)
|
(env, loop_var_join_id)
|
||||||
@ -351,9 +364,7 @@ mod tests {
|
|||||||
|
|
||||||
// Should return error
|
// Should return error
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(result
|
assert!(result.unwrap_err().contains("undefined_var"));
|
||||||
.unwrap_err()
|
|
||||||
.contains("undefined_var"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Phase 201: Test that v2 API uses JoinValueSpace correctly
|
/// Phase 201: Test that v2 API uses JoinValueSpace correctly
|
||||||
@ -410,7 +421,8 @@ mod tests {
|
|||||||
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
||||||
|
|
||||||
let mut space = JoinValueSpace::new();
|
let mut space = JoinValueSpace::new();
|
||||||
let (env, loop_var_join_id) = ConditionEnvBuilder::build_loop_param_only_v2("i", &mut space);
|
let (env, loop_var_join_id) =
|
||||||
|
ConditionEnvBuilder::build_loop_param_only_v2("i", &mut space);
|
||||||
|
|
||||||
// Phase 201: Should use Param region
|
// Phase 201: Should use Param region
|
||||||
assert_eq!(loop_var_join_id, ValueId(100));
|
assert_eq!(loop_var_join_id, ValueId(100));
|
||||||
|
|||||||
@ -3,11 +3,9 @@
|
|||||||
//! Applies exit bindings to JoinInlineBoundary.
|
//! Applies exit bindings to JoinInlineBoundary.
|
||||||
//! Single-responsibility box for boundary application logic.
|
//! Single-responsibility box for boundary application logic.
|
||||||
|
|
||||||
use crate::mir::ValueId;
|
|
||||||
use crate::mir::join_ir::lowering::inline_boundary::{
|
|
||||||
JoinInlineBoundary, LoopExitBinding,
|
|
||||||
};
|
|
||||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta};
|
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta};
|
||||||
|
use crate::mir::join_ir::lowering::inline_boundary::{JoinInlineBoundary, LoopExitBinding};
|
||||||
|
use crate::mir::ValueId;
|
||||||
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||||
|
|
||||||
/// Apply bindings to JoinInlineBoundary
|
/// Apply bindings to JoinInlineBoundary
|
||||||
@ -27,7 +25,7 @@ use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for deter
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// Success or error if boundary cannot be updated
|
/// Success or error if boundary cannot be updated
|
||||||
pub fn apply_exit_bindings_to_boundary(
|
pub(crate) fn apply_exit_bindings_to_boundary(
|
||||||
carrier_info: &CarrierInfo,
|
carrier_info: &CarrierInfo,
|
||||||
exit_meta: &ExitMeta,
|
exit_meta: &ExitMeta,
|
||||||
variable_map: &BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
variable_map: &BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||||
@ -40,13 +38,14 @@ pub fn apply_exit_bindings_to_boundary(
|
|||||||
let mut join_outputs = vec![carrier_info.loop_var_id]; // legacy field for compatibility
|
let mut join_outputs = vec![carrier_info.loop_var_id]; // legacy field for compatibility
|
||||||
|
|
||||||
for carrier in &carrier_info.carriers {
|
for carrier in &carrier_info.carriers {
|
||||||
let post_loop_id = variable_map.get(&carrier.name).copied().ok_or_else(|| {
|
let post_loop_id = variable_map
|
||||||
format!("Post-loop ValueId not found for carrier '{}'", carrier.name)
|
.get(&carrier.name)
|
||||||
})?;
|
.copied()
|
||||||
|
.ok_or_else(|| format!("Post-loop ValueId not found for carrier '{}'", carrier.name))?;
|
||||||
|
|
||||||
let join_exit_id = exit_meta.find_binding(&carrier.name).ok_or_else(|| {
|
let join_exit_id = exit_meta
|
||||||
format!("Exit value not found for carrier '{}'", carrier.name)
|
.find_binding(&carrier.name)
|
||||||
})?;
|
.ok_or_else(|| format!("Exit value not found for carrier '{}'", carrier.name))?;
|
||||||
|
|
||||||
bindings.push(LoopExitBinding {
|
bindings.push(LoopExitBinding {
|
||||||
carrier_name: carrier.name.clone(),
|
carrier_name: carrier.name.clone(),
|
||||||
@ -83,7 +82,7 @@ pub fn apply_exit_bindings_to_boundary(
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// LoopExitBinding for the loop variable
|
/// LoopExitBinding for the loop variable
|
||||||
pub fn create_loop_var_exit_binding(carrier_info: &CarrierInfo) -> LoopExitBinding {
|
pub(crate) fn create_loop_var_exit_binding(carrier_info: &CarrierInfo) -> LoopExitBinding {
|
||||||
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
||||||
LoopExitBinding {
|
LoopExitBinding {
|
||||||
carrier_name: carrier_info.loop_var_name.clone(),
|
carrier_name: carrier_info.loop_var_name.clone(),
|
||||||
@ -126,16 +125,16 @@ mod tests {
|
|||||||
let mut boundary = JoinInlineBoundary {
|
let mut boundary = JoinInlineBoundary {
|
||||||
host_inputs: vec![],
|
host_inputs: vec![],
|
||||||
join_inputs: vec![],
|
join_inputs: vec![],
|
||||||
exit_bindings: vec![], // Phase 171: Add missing field
|
exit_bindings: vec![], // Phase 171: Add missing field
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
host_outputs: vec![], // legacy, unused in new assertions
|
host_outputs: vec![], // legacy, unused in new assertions
|
||||||
join_outputs: vec![],
|
join_outputs: vec![],
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
condition_inputs: vec![], // Phase 171: Add missing field
|
condition_inputs: vec![], // Phase 171: Add missing field
|
||||||
condition_bindings: vec![], // Phase 171-fix: Add missing field
|
condition_bindings: vec![], // Phase 171-fix: Add missing field
|
||||||
expr_result: None, // Phase 33-14: Add missing field
|
expr_result: None, // Phase 33-14: Add missing field
|
||||||
loop_var_name: None, // Phase 33-16: Add missing field
|
loop_var_name: None, // Phase 33-16: Add missing field
|
||||||
carrier_info: None, // Phase 228: Add missing field
|
carrier_info: None, // Phase 228: Add missing field
|
||||||
};
|
};
|
||||||
|
|
||||||
apply_exit_bindings_to_boundary(&carrier_info, &exit_meta, &variable_map, &mut boundary)
|
apply_exit_bindings_to_boundary(&carrier_info, &exit_meta, &variable_map, &mut boundary)
|
||||||
@ -155,11 +154,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_loop_var_exit_binding() {
|
fn test_loop_var_exit_binding() {
|
||||||
let carrier_info = CarrierInfo::with_carriers(
|
let carrier_info = CarrierInfo::with_carriers("i".to_string(), ValueId(5), vec![]);
|
||||||
"i".to_string(),
|
|
||||||
ValueId(5),
|
|
||||||
vec![],
|
|
||||||
);
|
|
||||||
|
|
||||||
let binding = create_loop_var_exit_binding(&carrier_info);
|
let binding = create_loop_var_exit_binding(&carrier_info);
|
||||||
assert_eq!(binding.carrier_name, "i");
|
assert_eq!(binding.carrier_name, "i");
|
||||||
|
|||||||
@ -3,9 +3,9 @@
|
|||||||
//! Constructs loop exit bindings and allocates post-loop ValueIds.
|
//! Constructs loop exit bindings and allocates post-loop ValueIds.
|
||||||
//! Single-responsibility box for binding construction logic.
|
//! Single-responsibility box for binding construction logic.
|
||||||
|
|
||||||
use crate::mir::ValueId;
|
|
||||||
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
|
|
||||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta};
|
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta};
|
||||||
|
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
|
||||||
|
use crate::mir::ValueId;
|
||||||
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||||
|
|
||||||
/// Generate loop exit bindings
|
/// Generate loop exit bindings
|
||||||
@ -24,7 +24,7 @@ use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for deter
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// Vec of LoopExitBinding, one per carrier, sorted by carrier name
|
/// Vec of LoopExitBinding, one per carrier, sorted by carrier name
|
||||||
pub fn build_loop_exit_bindings(
|
pub(crate) fn build_loop_exit_bindings(
|
||||||
carrier_info: &CarrierInfo,
|
carrier_info: &CarrierInfo,
|
||||||
exit_meta: &ExitMeta,
|
exit_meta: &ExitMeta,
|
||||||
variable_map: &mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
variable_map: &mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||||
@ -33,7 +33,8 @@ pub fn build_loop_exit_bindings(
|
|||||||
|
|
||||||
// Process each carrier in sorted order
|
// Process each carrier in sorted order
|
||||||
for carrier in &carrier_info.carriers {
|
for carrier in &carrier_info.carriers {
|
||||||
let join_exit_id = exit_meta.find_binding(&carrier.name)
|
let join_exit_id = exit_meta
|
||||||
|
.find_binding(&carrier.name)
|
||||||
.ok_or_else(|| format!("Carrier '{}' missing in ExitMeta", carrier.name))?;
|
.ok_or_else(|| format!("Carrier '{}' missing in ExitMeta", carrier.name))?;
|
||||||
|
|
||||||
bindings.push(LoopExitBinding {
|
bindings.push(LoopExitBinding {
|
||||||
@ -64,12 +65,10 @@ pub fn build_loop_exit_bindings(
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// Newly allocated ValueId
|
/// Newly allocated ValueId
|
||||||
pub fn allocate_new_value_id(variable_map: &BTreeMap<String, ValueId>) -> ValueId { // Phase 222.5-D: HashMap → BTreeMap for determinism
|
pub(crate) fn allocate_new_value_id(variable_map: &BTreeMap<String, ValueId>) -> ValueId {
|
||||||
|
// Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||||
// Find the maximum ValueId in current variable_map
|
// Find the maximum ValueId in current variable_map
|
||||||
let max_id = variable_map.values()
|
let max_id = variable_map.values().map(|v| v.0).max().unwrap_or(0);
|
||||||
.map(|v| v.0)
|
|
||||||
.max()
|
|
||||||
.unwrap_or(0);
|
|
||||||
|
|
||||||
// Allocate next sequential ID
|
// Allocate next sequential ID
|
||||||
// Note: This is a temporary strategy and should be replaced with
|
// Note: This is a temporary strategy and should be replaced with
|
||||||
@ -131,14 +130,14 @@ mod tests {
|
|||||||
host_id: ValueId(11),
|
host_id: ValueId(11),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
||||||
},
|
},
|
||||||
CarrierVar {
|
CarrierVar {
|
||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -20,7 +20,7 @@ use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta};
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// Ok(()) if validation passes, Err with descriptive message if validation fails
|
/// Ok(()) if validation passes, Err with descriptive message if validation fails
|
||||||
pub fn validate_exit_binding(
|
pub(crate) fn validate_exit_binding(
|
||||||
carrier_info: &CarrierInfo,
|
carrier_info: &CarrierInfo,
|
||||||
exit_meta: &ExitMeta,
|
exit_meta: &ExitMeta,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
@ -44,10 +44,7 @@ pub fn validate_exit_binding(
|
|||||||
// Validate that all carriers in CarrierInfo have exit values
|
// Validate that all carriers in CarrierInfo have exit values
|
||||||
for carrier in &carrier_info.carriers {
|
for carrier in &carrier_info.carriers {
|
||||||
if exit_meta.find_binding(&carrier.name).is_none() {
|
if exit_meta.find_binding(&carrier.name).is_none() {
|
||||||
return Err(format!(
|
return Err(format!("Carrier '{}' missing in ExitMeta", carrier.name));
|
||||||
"Carrier '{}' missing in ExitMeta",
|
|
||||||
carrier.name
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,14 +88,14 @@ mod tests {
|
|||||||
host_id: ValueId(11),
|
host_id: ValueId(11),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
||||||
},
|
},
|
||||||
CarrierVar {
|
CarrierVar {
|
||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
//! Pattern 1: Simple While Loop minimal lowerer
|
//! Pattern 1: Simple While Loop minimal lowerer
|
||||||
|
|
||||||
|
use super::super::trace;
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use crate::mir::builder::MirBuilder;
|
use crate::mir::builder::MirBuilder;
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
use super::super::trace;
|
|
||||||
|
|
||||||
/// Phase 194: Detection function for Pattern 1
|
/// Phase 194: Detection function for Pattern 1
|
||||||
///
|
///
|
||||||
@ -11,7 +11,7 @@ use super::super::trace;
|
|||||||
///
|
///
|
||||||
/// Pattern 1 matches:
|
/// Pattern 1 matches:
|
||||||
/// - Pattern kind is Pattern1SimpleWhile (no break, no continue, no if-else PHI)
|
/// - Pattern kind is Pattern1SimpleWhile (no break, no continue, no if-else PHI)
|
||||||
pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
||||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||||
ctx.pattern_kind == LoopPatternKind::Pattern1SimpleWhile
|
ctx.pattern_kind == LoopPatternKind::Pattern1SimpleWhile
|
||||||
}
|
}
|
||||||
@ -19,7 +19,7 @@ pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext)
|
|||||||
/// Phase 194: Lowering function for Pattern 1
|
/// Phase 194: Lowering function for Pattern 1
|
||||||
///
|
///
|
||||||
/// Wrapper around cf_loop_pattern1_minimal to match router signature
|
/// Wrapper around cf_loop_pattern1_minimal to match router signature
|
||||||
pub fn lower(
|
pub(crate) fn lower(
|
||||||
builder: &mut MirBuilder,
|
builder: &mut MirBuilder,
|
||||||
ctx: &super::router::LoopPatternContext,
|
ctx: &super::router::LoopPatternContext,
|
||||||
) -> Result<Option<ValueId>, String> {
|
) -> Result<Option<ValueId>, String> {
|
||||||
@ -51,12 +51,7 @@ impl MirBuilder {
|
|||||||
|
|
||||||
// Phase 179-B: Use PatternPipelineContext for unified preprocessing
|
// Phase 179-B: Use PatternPipelineContext for unified preprocessing
|
||||||
use super::pattern_pipeline::{build_pattern_context, PatternVariant};
|
use super::pattern_pipeline::{build_pattern_context, PatternVariant};
|
||||||
let ctx = build_pattern_context(
|
let ctx = build_pattern_context(self, condition, body, PatternVariant::Pattern1)?;
|
||||||
self,
|
|
||||||
condition,
|
|
||||||
body,
|
|
||||||
PatternVariant::Pattern1,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Phase 195: Use unified trace
|
// Phase 195: Use unified trace
|
||||||
trace::trace().varmap("pattern1_start", &self.variable_map);
|
trace::trace().varmap("pattern1_start", &self.variable_map);
|
||||||
@ -85,7 +80,7 @@ impl MirBuilder {
|
|||||||
vec![ValueId(0)], // JoinIR's main() parameter (loop variable)
|
vec![ValueId(0)], // JoinIR's main() parameter (loop variable)
|
||||||
vec![ctx.loop_var_id], // Host's loop variable
|
vec![ctx.loop_var_id], // Host's loop variable
|
||||||
)
|
)
|
||||||
.with_loop_var_name(Some(ctx.loop_var_name.clone())) // Phase 33-16: Enable header PHI generation for SSA correctness
|
.with_loop_var_name(Some(ctx.loop_var_name.clone())) // Phase 33-16: Enable header PHI generation for SSA correctness
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Phase 33-22: Use JoinIRConversionPipeline for unified conversion flow
|
// Phase 33-22: Use JoinIRConversionPipeline for unified conversion flow
|
||||||
@ -116,7 +111,10 @@ impl MirBuilder {
|
|||||||
let void_val = crate::mir::builder::emission::constant::emit_void(self);
|
let void_val = crate::mir::builder::emission::constant::emit_void(self);
|
||||||
|
|
||||||
// Phase 195: Use unified trace
|
// Phase 195: Use unified trace
|
||||||
trace::trace().debug("pattern1", &format!("Loop complete, returning Void {:?}", void_val));
|
trace::trace().debug(
|
||||||
|
"pattern1",
|
||||||
|
&format!("Loop complete, returning Void {:?}", void_val),
|
||||||
|
);
|
||||||
|
|
||||||
Ok(Some(void_val))
|
Ok(Some(void_val))
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -15,11 +15,11 @@
|
|||||||
//! - Hardcoded loop condition (i <= 5), if condition (i % 2 == 1)
|
//! - Hardcoded loop condition (i <= 5), if condition (i % 2 == 1)
|
||||||
//! - Kept for backward compatibility with existing tests
|
//! - Kept for backward compatibility with existing tests
|
||||||
|
|
||||||
|
use super::super::merge::exit_line::meta_collector::ExitMetaCollector;
|
||||||
|
use super::super::trace;
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use crate::mir::builder::MirBuilder;
|
use crate::mir::builder::MirBuilder;
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
use super::super::merge::exit_line::meta_collector::ExitMetaCollector;
|
|
||||||
use super::super::trace;
|
|
||||||
|
|
||||||
// Phase 213: Hardcoded ValueIds removed - now using ExitMeta-based exit binding generation
|
// Phase 213: Hardcoded ValueIds removed - now using ExitMeta-based exit binding generation
|
||||||
// See: ExitMetaCollector usage below (lines 115-135)
|
// See: ExitMetaCollector usage below (lines 115-135)
|
||||||
@ -32,7 +32,7 @@ use super::super::trace;
|
|||||||
/// - Pattern kind is Pattern3IfPhi (has if-else with PHI, no break/continue)
|
/// - Pattern kind is Pattern3IfPhi (has if-else with PHI, no break/continue)
|
||||||
///
|
///
|
||||||
/// NOTE: Priority is now handled by pattern classification, not router order
|
/// NOTE: Priority is now handled by pattern classification, not router order
|
||||||
pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
||||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||||
ctx.pattern_kind == LoopPatternKind::Pattern3IfPhi
|
ctx.pattern_kind == LoopPatternKind::Pattern3IfPhi
|
||||||
}
|
}
|
||||||
@ -40,7 +40,7 @@ pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext)
|
|||||||
/// Phase 194: Lowering function for Pattern 3
|
/// Phase 194: Lowering function for Pattern 3
|
||||||
///
|
///
|
||||||
/// Wrapper around cf_loop_pattern3_with_if_phi to match router signature
|
/// Wrapper around cf_loop_pattern3_with_if_phi to match router signature
|
||||||
pub fn lower(
|
pub(crate) fn lower(
|
||||||
builder: &mut MirBuilder,
|
builder: &mut MirBuilder,
|
||||||
ctx: &super::router::LoopPatternContext,
|
ctx: &super::router::LoopPatternContext,
|
||||||
) -> Result<Option<ValueId>, String> {
|
) -> Result<Option<ValueId>, String> {
|
||||||
@ -72,22 +72,23 @@ impl MirBuilder {
|
|||||||
) -> Result<Option<ValueId>, String> {
|
) -> Result<Option<ValueId>, String> {
|
||||||
// Phase 179-B: Use PatternPipelineContext for unified preprocessing
|
// Phase 179-B: Use PatternPipelineContext for unified preprocessing
|
||||||
use super::pattern_pipeline::{build_pattern_context, PatternVariant};
|
use super::pattern_pipeline::{build_pattern_context, PatternVariant};
|
||||||
let ctx = build_pattern_context(
|
let ctx = build_pattern_context(self, condition, body, PatternVariant::Pattern3)?;
|
||||||
self,
|
|
||||||
condition,
|
|
||||||
body,
|
|
||||||
PatternVariant::Pattern3,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Phase 213: AST-based if-sum pattern detection
|
// Phase 213: AST-based if-sum pattern detection
|
||||||
// Phase 242-EX-A: Legacy mode removed - all if-sum patterns now handled dynamically
|
// Phase 242-EX-A: Legacy mode removed - all if-sum patterns now handled dynamically
|
||||||
if !ctx.is_if_sum_pattern() {
|
if !ctx.is_if_sum_pattern() {
|
||||||
// Not an if-sum pattern → let router try other patterns or fall back
|
// Not an if-sum pattern → let router try other patterns or fall back
|
||||||
trace::trace().debug("pattern3", "Not an if-sum pattern, returning None to try other patterns");
|
trace::trace().debug(
|
||||||
|
"pattern3",
|
||||||
|
"Not an if-sum pattern, returning None to try other patterns",
|
||||||
|
);
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
trace::trace().debug("pattern3", "Detected if-sum pattern, using AST-based lowerer");
|
trace::trace().debug(
|
||||||
|
"pattern3",
|
||||||
|
"Detected if-sum pattern, using AST-based lowerer",
|
||||||
|
);
|
||||||
self.lower_pattern3_if_sum(&ctx, condition, body, debug)
|
self.lower_pattern3_if_sum(&ctx, condition, body, debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,35 +135,34 @@ impl MirBuilder {
|
|||||||
for binding in &condition_bindings {
|
for binding in &condition_bindings {
|
||||||
trace::trace().debug(
|
trace::trace().debug(
|
||||||
"pattern3/if-sum",
|
"pattern3/if-sum",
|
||||||
&format!(" '{}': HOST {:?} → JoinIR {:?}", binding.name, binding.host_value, binding.join_value),
|
&format!(
|
||||||
|
" '{}': HOST {:?} → JoinIR {:?}",
|
||||||
|
binding.name, binding.host_value, binding.join_value
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call AST-based if-sum lowerer with ConditionEnv
|
// Call AST-based if-sum lowerer with ConditionEnv
|
||||||
let (join_module, fragment_meta) = lower_if_sum_pattern(
|
let (join_module, fragment_meta) =
|
||||||
condition,
|
lower_if_sum_pattern(condition, if_stmt, body, &cond_env, &mut join_value_space)?;
|
||||||
if_stmt,
|
|
||||||
body,
|
|
||||||
&cond_env,
|
|
||||||
&mut join_value_space,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let exit_meta = &fragment_meta.exit_meta;
|
let exit_meta = &fragment_meta.exit_meta;
|
||||||
|
|
||||||
trace::trace().debug(
|
trace::trace().debug(
|
||||||
"pattern3/if-sum",
|
"pattern3/if-sum",
|
||||||
&format!("ExitMeta: {} exit values", exit_meta.exit_values.len())
|
&format!("ExitMeta: {} exit values", exit_meta.exit_values.len()),
|
||||||
);
|
);
|
||||||
for (carrier_name, join_value) in &exit_meta.exit_values {
|
for (carrier_name, join_value) in &exit_meta.exit_values {
|
||||||
trace::trace().debug(
|
trace::trace().debug(
|
||||||
"pattern3/if-sum",
|
"pattern3/if-sum",
|
||||||
&format!(" {} → ValueId({})", carrier_name, join_value.0)
|
&format!(" {} → ValueId({})", carrier_name, join_value.0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build exit bindings using ExitMetaCollector
|
// Build exit bindings using ExitMetaCollector
|
||||||
// Phase 228-8: Pass carrier_info to include ConditionOnly carriers
|
// Phase 228-8: Pass carrier_info to include ConditionOnly carriers
|
||||||
let exit_bindings = ExitMetaCollector::collect(self, exit_meta, Some(&ctx.carrier_info), debug);
|
let exit_bindings =
|
||||||
|
ExitMetaCollector::collect(self, exit_meta, Some(&ctx.carrier_info), debug);
|
||||||
|
|
||||||
// Build boundary with carrier inputs
|
// Build boundary with carrier inputs
|
||||||
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
||||||
@ -174,9 +174,7 @@ impl MirBuilder {
|
|||||||
|
|
||||||
// Allocate join_inputs dynamically from JoinValueSpace
|
// Allocate join_inputs dynamically from JoinValueSpace
|
||||||
// These ValueIds (0, 1, 2, ...) represent JoinIR function parameters
|
// These ValueIds (0, 1, 2, ...) represent JoinIR function parameters
|
||||||
let join_inputs: Vec<ValueId> = (0..total_inputs)
|
let join_inputs: Vec<ValueId> = (0..total_inputs).map(|i| ValueId(i as u32)).collect();
|
||||||
.map(|i| ValueId(i as u32))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Build host_inputs: loop_var + exit_bindings (in order)
|
// Build host_inputs: loop_var + exit_bindings (in order)
|
||||||
let mut host_inputs = vec![ctx.loop_var_id];
|
let mut host_inputs = vec![ctx.loop_var_id];
|
||||||
@ -197,15 +195,16 @@ impl MirBuilder {
|
|||||||
"pattern3/if-sum",
|
"pattern3/if-sum",
|
||||||
&format!(
|
&format!(
|
||||||
"Boundary inputs: {} total (loop_var + {} exit bindings)",
|
"Boundary inputs: {} total (loop_var + {} exit bindings)",
|
||||||
total_inputs, exit_bindings.len()
|
total_inputs,
|
||||||
)
|
exit_bindings.len()
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Phase 215-2: Pass expr_result to boundary
|
// Phase 215-2: Pass expr_result to boundary
|
||||||
// Phase 220-D: Pass condition_bindings for variable remapping
|
// Phase 220-D: Pass condition_bindings for variable remapping
|
||||||
let mut boundary_builder = JoinInlineBoundaryBuilder::new()
|
let mut boundary_builder = JoinInlineBoundaryBuilder::new()
|
||||||
.with_inputs(join_inputs, host_inputs)
|
.with_inputs(join_inputs, host_inputs)
|
||||||
.with_condition_bindings(condition_bindings) // Phase 220-D: Map condition-only vars
|
.with_condition_bindings(condition_bindings) // Phase 220-D: Map condition-only vars
|
||||||
.with_exit_bindings(exit_bindings)
|
.with_exit_bindings(exit_bindings)
|
||||||
.with_loop_var_name(Some(ctx.loop_var_name.clone()));
|
.with_loop_var_name(Some(ctx.loop_var_name.clone()));
|
||||||
|
|
||||||
@ -213,7 +212,7 @@ impl MirBuilder {
|
|||||||
if let Some(expr_id) = fragment_meta.expr_result {
|
if let Some(expr_id) = fragment_meta.expr_result {
|
||||||
trace::trace().debug(
|
trace::trace().debug(
|
||||||
"pattern3/if-sum",
|
"pattern3/if-sum",
|
||||||
&format!("Passing expr_result={:?} to boundary", expr_id)
|
&format!("Passing expr_result={:?} to boundary", expr_id),
|
||||||
);
|
);
|
||||||
boundary_builder = boundary_builder.with_expr_result(Some(expr_id));
|
boundary_builder = boundary_builder.with_expr_result(Some(expr_id));
|
||||||
}
|
}
|
||||||
@ -234,7 +233,7 @@ impl MirBuilder {
|
|||||||
if let Some(expr_val) = merge_result {
|
if let Some(expr_val) = merge_result {
|
||||||
trace::trace().debug(
|
trace::trace().debug(
|
||||||
"pattern3/if-sum",
|
"pattern3/if-sum",
|
||||||
&format!("Loop complete, returning expr_result {:?}", expr_val)
|
&format!("Loop complete, returning expr_result {:?}", expr_val),
|
||||||
);
|
);
|
||||||
Ok(Some(expr_val))
|
Ok(Some(expr_val))
|
||||||
} else {
|
} else {
|
||||||
@ -243,7 +242,7 @@ impl MirBuilder {
|
|||||||
let void_val = constant::emit_void(self);
|
let void_val = constant::emit_void(self);
|
||||||
trace::trace().debug(
|
trace::trace().debug(
|
||||||
"pattern3/if-sum",
|
"pattern3/if-sum",
|
||||||
&format!("Loop complete, returning Void {:?}", void_val)
|
&format!("Loop complete, returning Void {:?}", void_val),
|
||||||
);
|
);
|
||||||
Ok(Some(void_val))
|
Ok(Some(void_val))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ use crate::mir::join_ir::lowering::continue_branch_normalizer::ContinueBranchNor
|
|||||||
use crate::mir::join_ir::lowering::loop_update_analyzer::{LoopUpdateAnalyzer, UpdateExpr};
|
use crate::mir::join_ir::lowering::loop_update_analyzer::{LoopUpdateAnalyzer, UpdateExpr};
|
||||||
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||||
|
|
||||||
pub struct Pattern4CarrierAnalyzer;
|
pub(crate) struct Pattern4CarrierAnalyzer;
|
||||||
|
|
||||||
impl Pattern4CarrierAnalyzer {
|
impl Pattern4CarrierAnalyzer {
|
||||||
/// Analyze and filter carriers for continue pattern
|
/// Analyze and filter carriers for continue pattern
|
||||||
@ -54,10 +54,8 @@ impl Pattern4CarrierAnalyzer {
|
|||||||
all_carriers: &CarrierInfo,
|
all_carriers: &CarrierInfo,
|
||||||
) -> Result<CarrierInfo, String> {
|
) -> Result<CarrierInfo, String> {
|
||||||
// Identify which carriers are updated in loop body
|
// Identify which carriers are updated in loop body
|
||||||
let carrier_updates = LoopUpdateAnalyzer::analyze_carrier_updates(
|
let carrier_updates =
|
||||||
loop_body,
|
LoopUpdateAnalyzer::analyze_carrier_updates(loop_body, &all_carriers.carriers);
|
||||||
&all_carriers.carriers,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Filter carriers: only keep those that have update expressions
|
// Filter carriers: only keep those that have update expressions
|
||||||
let updated_carriers: Vec<CarrierVar> = all_carriers
|
let updated_carriers: Vec<CarrierVar> = all_carriers
|
||||||
@ -87,7 +85,8 @@ impl Pattern4CarrierAnalyzer {
|
|||||||
pub fn analyze_carrier_updates(
|
pub fn analyze_carrier_updates(
|
||||||
loop_body: &[ASTNode],
|
loop_body: &[ASTNode],
|
||||||
carriers: &[CarrierVar],
|
carriers: &[CarrierVar],
|
||||||
) -> BTreeMap<String, UpdateExpr> { // Phase 222.5-D: HashMap → BTreeMap for determinism
|
) -> BTreeMap<String, UpdateExpr> {
|
||||||
|
// Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||||
LoopUpdateAnalyzer::analyze_carrier_updates(loop_body, carriers)
|
LoopUpdateAnalyzer::analyze_carrier_updates(loop_body, carriers)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,9 +154,9 @@ impl Pattern4CarrierAnalyzer {
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
then_body.iter().any(|n| Self::has_continue(n))
|
then_body.iter().any(|n| Self::has_continue(n))
|
||||||
|| else_body.as_ref().map_or(false, |body| {
|
|| else_body
|
||||||
body.iter().any(|n| Self::has_continue(n))
|
.as_ref()
|
||||||
})
|
.map_or(false, |body| body.iter().any(|n| Self::has_continue(n)))
|
||||||
}
|
}
|
||||||
ASTNode::Loop { body, .. } => body.iter().any(|n| Self::has_continue(n)),
|
ASTNode::Loop { body, .. } => body.iter().any(|n| Self::has_continue(n)),
|
||||||
_ => false,
|
_ => false,
|
||||||
@ -331,9 +330,7 @@ mod tests {
|
|||||||
}),
|
}),
|
||||||
span: span.clone(),
|
span: span.clone(),
|
||||||
}],
|
}],
|
||||||
else_body: Some(vec![ASTNode::Continue {
|
else_body: Some(vec![ASTNode::Continue { span: span.clone() }]),
|
||||||
span: span.clone(),
|
|
||||||
}]),
|
|
||||||
span: span.clone(),
|
span: span.clone(),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
|||||||
@ -31,12 +31,12 @@
|
|||||||
//! - **ExitMeta**: Maps final carrier values to host variable slots
|
//! - **ExitMeta**: Maps final carrier values to host variable slots
|
||||||
//! - **Phase 33-21 fix**: Correct remapping of function parameters to header PHI dsts
|
//! - **Phase 33-21 fix**: Correct remapping of function parameters to header PHI dsts
|
||||||
|
|
||||||
|
use super::super::trace;
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use crate::mir::builder::MirBuilder;
|
use crate::mir::builder::MirBuilder;
|
||||||
use crate::mir::ValueId;
|
|
||||||
use super::super::trace;
|
|
||||||
use crate::mir::loop_pattern_detection::error_messages;
|
|
||||||
use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
|
use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
|
||||||
|
use crate::mir::loop_pattern_detection::error_messages;
|
||||||
|
use crate::mir::ValueId;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
/// Phase 194+: Detection function for Pattern 4
|
/// Phase 194+: Detection function for Pattern 4
|
||||||
@ -62,9 +62,9 @@ use std::collections::BTreeMap;
|
|||||||
/// 3. **Phase 178**: No string/complex carrier updates (JoinIR doesn't support string concat)
|
/// 3. **Phase 178**: No string/complex carrier updates (JoinIR doesn't support string concat)
|
||||||
///
|
///
|
||||||
/// If all conditions are met, Pattern 4 is detected.
|
/// If all conditions are met, Pattern 4 is detected.
|
||||||
pub fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
pub(crate) fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
||||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
|
||||||
use super::common_init::CommonPatternInitializer;
|
use super::common_init::CommonPatternInitializer;
|
||||||
|
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||||
|
|
||||||
// Basic pattern check
|
// Basic pattern check
|
||||||
if ctx.pattern_kind != LoopPatternKind::Pattern4Continue {
|
if ctx.pattern_kind != LoopPatternKind::Pattern4Continue {
|
||||||
@ -97,17 +97,12 @@ pub fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext)
|
|||||||
/// 4. Create JoinInlineBoundary for input/output mapping
|
/// 4. Create JoinInlineBoundary for input/output mapping
|
||||||
/// 5. Merge MIR blocks into current_function
|
/// 5. Merge MIR blocks into current_function
|
||||||
/// 6. Return loop result (first carrier value)
|
/// 6. Return loop result (first carrier value)
|
||||||
pub fn lower(
|
pub(crate) fn lower(
|
||||||
builder: &mut MirBuilder,
|
builder: &mut MirBuilder,
|
||||||
ctx: &super::router::LoopPatternContext,
|
ctx: &super::router::LoopPatternContext,
|
||||||
) -> Result<Option<ValueId>, String> {
|
) -> Result<Option<ValueId>, String> {
|
||||||
// Phase 33-19: Connect stub to actual implementation
|
// Phase 33-19: Connect stub to actual implementation
|
||||||
builder.cf_loop_pattern4_with_continue(
|
builder.cf_loop_pattern4_with_continue(ctx.condition, ctx.body, ctx.func_name, ctx.debug)
|
||||||
ctx.condition,
|
|
||||||
ctx.body,
|
|
||||||
ctx.func_name,
|
|
||||||
ctx.debug,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MirBuilder {
|
impl MirBuilder {
|
||||||
@ -170,10 +165,10 @@ fn prepare_pattern4_context(
|
|||||||
) -> Result<Pattern4Prepared, String> {
|
) -> Result<Pattern4Prepared, String> {
|
||||||
use super::pattern4_carrier_analyzer::Pattern4CarrierAnalyzer;
|
use super::pattern4_carrier_analyzer::Pattern4CarrierAnalyzer;
|
||||||
use super::pattern_pipeline::{build_pattern_context, PatternVariant};
|
use super::pattern_pipeline::{build_pattern_context, PatternVariant};
|
||||||
use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScopeBox;
|
|
||||||
use crate::mir::loop_pattern_detection::loop_body_cond_promoter::{
|
use crate::mir::loop_pattern_detection::loop_body_cond_promoter::{
|
||||||
LoopBodyCondPromoter, ConditionPromotionRequest, ConditionPromotionResult,
|
ConditionPromotionRequest, ConditionPromotionResult, LoopBodyCondPromoter,
|
||||||
};
|
};
|
||||||
|
use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScopeBox;
|
||||||
|
|
||||||
// Normalize continue branches for analysis/lowering
|
// Normalize continue branches for analysis/lowering
|
||||||
let normalized_body = Pattern4CarrierAnalyzer::normalize_continue_branches(body);
|
let normalized_body = Pattern4CarrierAnalyzer::normalize_continue_branches(body);
|
||||||
@ -195,28 +190,33 @@ fn prepare_pattern4_context(
|
|||||||
&normalized_body,
|
&normalized_body,
|
||||||
&carrier_info_prelim.carriers,
|
&carrier_info_prelim.carriers,
|
||||||
);
|
);
|
||||||
let mut carrier_info = Pattern4CarrierAnalyzer::analyze_carriers(
|
let mut carrier_info =
|
||||||
&normalized_body,
|
Pattern4CarrierAnalyzer::analyze_carriers(&normalized_body, &carrier_info_prelim)?;
|
||||||
&carrier_info_prelim,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
trace::trace().debug(
|
trace::trace().debug(
|
||||||
"pattern4",
|
"pattern4",
|
||||||
&format!(
|
&format!(
|
||||||
"CarrierInfo: loop_var={}, carriers={:?}",
|
"CarrierInfo: loop_var={}, carriers={:?}",
|
||||||
carrier_info.loop_var_name,
|
carrier_info.loop_var_name,
|
||||||
carrier_info.carriers.iter().map(|c| &c.name).collect::<Vec<_>>()
|
carrier_info
|
||||||
)
|
.carriers
|
||||||
|
.iter()
|
||||||
|
.map(|c| &c.name)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
trace::trace().debug(
|
trace::trace().debug(
|
||||||
"pattern4",
|
"pattern4",
|
||||||
&format!("Analyzed {} carrier update expressions", carrier_updates.len())
|
&format!(
|
||||||
|
"Analyzed {} carrier update expressions",
|
||||||
|
carrier_updates.len()
|
||||||
|
),
|
||||||
);
|
);
|
||||||
for (carrier_name, update_expr) in &carrier_updates {
|
for (carrier_name, update_expr) in &carrier_updates {
|
||||||
trace::trace().debug(
|
trace::trace().debug(
|
||||||
"pattern4",
|
"pattern4",
|
||||||
&format!(" {} → {:?}", carrier_name, update_expr)
|
&format!(" {} → {:?}", carrier_name, update_expr),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,18 +228,15 @@ fn prepare_pattern4_context(
|
|||||||
vec![condition]
|
vec![condition]
|
||||||
};
|
};
|
||||||
|
|
||||||
let cond_scope = LoopConditionScopeBox::analyze(
|
let cond_scope =
|
||||||
&loop_var_name,
|
LoopConditionScopeBox::analyze(&loop_var_name, &conditions_to_analyze, Some(&loop_scope));
|
||||||
&conditions_to_analyze,
|
|
||||||
Some(&loop_scope),
|
|
||||||
);
|
|
||||||
|
|
||||||
if cond_scope.has_loop_body_local() {
|
if cond_scope.has_loop_body_local() {
|
||||||
let promotion_req = ConditionPromotionRequest {
|
let promotion_req = ConditionPromotionRequest {
|
||||||
loop_param_name: &loop_var_name,
|
loop_param_name: &loop_var_name,
|
||||||
cond_scope: &cond_scope,
|
cond_scope: &cond_scope,
|
||||||
scope_shape: Some(&loop_scope),
|
scope_shape: Some(&loop_scope),
|
||||||
break_cond: None, // Pattern 4 has no break
|
break_cond: None, // Pattern 4 has no break
|
||||||
continue_cond,
|
continue_cond,
|
||||||
loop_body: &normalized_body,
|
loop_body: &normalized_body,
|
||||||
};
|
};
|
||||||
@ -285,13 +282,15 @@ fn prepare_pattern4_context(
|
|||||||
} else {
|
} else {
|
||||||
return Err(error_messages::format_error_pattern4_trim_not_safe(
|
return Err(error_messages::format_error_pattern4_trim_not_safe(
|
||||||
&helper.carrier_name,
|
&helper.carrier_name,
|
||||||
helper.whitespace_count()
|
helper.whitespace_count(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConditionPromotionResult::CannotPromote { reason, vars } => {
|
ConditionPromotionResult::CannotPromote { reason, vars } => {
|
||||||
return Err(error_messages::format_error_pattern4_promotion_failed(&vars, &reason));
|
return Err(error_messages::format_error_pattern4_promotion_failed(
|
||||||
|
&vars, &reason,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -311,11 +310,11 @@ fn lower_pattern4_joinir(
|
|||||||
prepared: &Pattern4Prepared,
|
prepared: &Pattern4Prepared,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
) -> Result<Option<ValueId>, String> {
|
) -> Result<Option<ValueId>, String> {
|
||||||
|
use super::super::merge::exit_line::meta_collector::ExitMetaCollector;
|
||||||
|
use super::conversion_pipeline::JoinIRConversionPipeline;
|
||||||
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
||||||
use crate::mir::join_ir::lowering::loop_with_continue_minimal::lower_loop_with_continue_minimal;
|
use crate::mir::join_ir::lowering::loop_with_continue_minimal::lower_loop_with_continue_minimal;
|
||||||
use super::super::merge::exit_line::meta_collector::ExitMetaCollector;
|
|
||||||
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
||||||
use super::conversion_pipeline::JoinIRConversionPipeline;
|
|
||||||
|
|
||||||
trace::trace().varmap("pattern4_start", &builder.variable_map);
|
trace::trace().varmap("pattern4_start", &builder.variable_map);
|
||||||
|
|
||||||
@ -338,12 +337,12 @@ fn lower_pattern4_joinir(
|
|||||||
|
|
||||||
trace::trace().debug(
|
trace::trace().debug(
|
||||||
"pattern4",
|
"pattern4",
|
||||||
&format!("ExitMeta: {} exit bindings", exit_meta.exit_values.len())
|
&format!("ExitMeta: {} exit bindings", exit_meta.exit_values.len()),
|
||||||
);
|
);
|
||||||
for (carrier_name, join_value) in &exit_meta.exit_values {
|
for (carrier_name, join_value) in &exit_meta.exit_values {
|
||||||
trace::trace().debug(
|
trace::trace().debug(
|
||||||
"pattern4",
|
"pattern4",
|
||||||
&format!(" {} → ValueId({})", carrier_name, join_value.0)
|
&format!(" {} → ValueId({})", carrier_name, join_value.0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,7 +355,9 @@ fn lower_pattern4_joinir(
|
|||||||
|
|
||||||
for carrier in &prepared.carrier_info.carriers {
|
for carrier in &prepared.carrier_info.carriers {
|
||||||
if !exit_bindings.iter().any(|b| b.carrier_name == carrier.name) {
|
if !exit_bindings.iter().any(|b| b.carrier_name == carrier.name) {
|
||||||
return Err(error_messages::format_error_pattern4_carrier_not_found(&carrier.name));
|
return Err(error_messages::format_error_pattern4_carrier_not_found(
|
||||||
|
&carrier.name,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,21 +369,27 @@ fn lower_pattern4_joinir(
|
|||||||
|
|
||||||
trace::trace().debug(
|
trace::trace().debug(
|
||||||
"pattern4",
|
"pattern4",
|
||||||
&format!("host_inputs: {:?}", host_inputs.iter().map(|v| v.0).collect::<Vec<_>>())
|
&format!(
|
||||||
|
"host_inputs: {:?}",
|
||||||
|
host_inputs.iter().map(|v| v.0).collect::<Vec<_>>()
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut join_inputs = vec![ValueId(0)]; // ValueId(0) = i_init in JoinIR
|
let mut join_inputs = vec![ValueId(0)]; // ValueId(0) = i_init in JoinIR
|
||||||
for idx in 0..prepared.carrier_info.carriers.len() {
|
for idx in 0..prepared.carrier_info.carriers.len() {
|
||||||
join_inputs.push(ValueId((idx + 1) as u32)); // ValueId(1..N) = carrier inits
|
join_inputs.push(ValueId((idx + 1) as u32)); // ValueId(1..N) = carrier inits
|
||||||
}
|
}
|
||||||
|
|
||||||
trace::trace().debug(
|
trace::trace().debug(
|
||||||
"pattern4",
|
"pattern4",
|
||||||
&format!("join_inputs: {:?}", join_inputs.iter().map(|v| v.0).collect::<Vec<_>>())
|
&format!(
|
||||||
|
"join_inputs: {:?}",
|
||||||
|
join_inputs.iter().map(|v| v.0).collect::<Vec<_>>()
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let boundary = JoinInlineBoundaryBuilder::new()
|
let boundary = JoinInlineBoundaryBuilder::new()
|
||||||
.with_inputs(join_inputs, host_inputs) // Dynamic carrier count
|
.with_inputs(join_inputs, host_inputs) // Dynamic carrier count
|
||||||
.with_exit_bindings(exit_bindings)
|
.with_exit_bindings(exit_bindings)
|
||||||
.with_loop_var_name(Some(prepared.loop_var_name.clone()))
|
.with_loop_var_name(Some(prepared.loop_var_name.clone()))
|
||||||
.with_carrier_info(prepared.carrier_info.clone())
|
.with_carrier_info(prepared.carrier_info.clone())
|
||||||
@ -397,7 +404,10 @@ fn lower_pattern4_joinir(
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
let void_val = crate::mir::builder::emission::constant::emit_void(builder);
|
let void_val = crate::mir::builder::emission::constant::emit_void(builder);
|
||||||
trace::trace().debug("pattern4", &format!("Loop complete, returning Void {:?}", void_val));
|
trace::trace().debug(
|
||||||
|
"pattern4",
|
||||||
|
&format!("Loop complete, returning Void {:?}", void_val),
|
||||||
|
);
|
||||||
|
|
||||||
Ok(Some(void_val))
|
Ok(Some(void_val))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,14 +34,14 @@
|
|||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use crate::mir::builder::MirBuilder;
|
use crate::mir::builder::MirBuilder;
|
||||||
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
||||||
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
|
|
||||||
use crate::mir::join_ir::lowering::condition_env::ConditionBinding;
|
use crate::mir::join_ir::lowering::condition_env::ConditionBinding;
|
||||||
|
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
|
||||||
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||||
use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
|
use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
|
||||||
use crate::mir::join_ir::lowering::loop_update_summary::LoopUpdateSummary; // Phase 213
|
use crate::mir::join_ir::lowering::loop_update_summary::LoopUpdateSummary; // Phase 213
|
||||||
use crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper;
|
use crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper;
|
||||||
use crate::mir::ValueId;
|
|
||||||
use crate::mir::BasicBlockId;
|
use crate::mir::BasicBlockId;
|
||||||
|
use crate::mir::ValueId;
|
||||||
use std::collections::{BTreeMap, BTreeSet}; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
use std::collections::{BTreeMap, BTreeSet}; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||||
|
|
||||||
use super::common_init::CommonPatternInitializer;
|
use super::common_init::CommonPatternInitializer;
|
||||||
@ -72,9 +72,8 @@ use super::loop_scope_shape_builder::LoopScopeShapeBuilder;
|
|||||||
/// let join_module = lower_simple_while_minimal(ctx.loop_scope)?;
|
/// let join_module = lower_simple_while_minimal(ctx.loop_scope)?;
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PatternPipelineContext {
|
pub(crate) struct PatternPipelineContext {
|
||||||
// === Common Data (All Patterns) ===
|
// === Common Data (All Patterns) ===
|
||||||
|
|
||||||
/// Loop variable name (e.g., "i")
|
/// Loop variable name (e.g., "i")
|
||||||
pub loop_var_name: String,
|
pub loop_var_name: String,
|
||||||
|
|
||||||
@ -88,7 +87,6 @@ pub struct PatternPipelineContext {
|
|||||||
pub loop_scope: LoopScopeShape,
|
pub loop_scope: LoopScopeShape,
|
||||||
|
|
||||||
// === Pattern 2/4: Break/Continue Condition ===
|
// === Pattern 2/4: Break/Continue Condition ===
|
||||||
|
|
||||||
/// Condition environment (variable → JoinIR ValueId mapping)
|
/// Condition environment (variable → JoinIR ValueId mapping)
|
||||||
/// Used by Pattern 2 (break condition) and Pattern 4 (continue condition)
|
/// Used by Pattern 2 (break condition) and Pattern 4 (continue condition)
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -105,21 +103,18 @@ pub struct PatternPipelineContext {
|
|||||||
pub carrier_updates: Option<BTreeMap<String, UpdateExpr>>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
pub carrier_updates: Option<BTreeMap<String, UpdateExpr>>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||||
|
|
||||||
// === Pattern 2/4: Trim Pattern Support ===
|
// === Pattern 2/4: Trim Pattern Support ===
|
||||||
|
|
||||||
/// Trim loop helper (if Trim pattern detected during promotion)
|
/// Trim loop helper (if Trim pattern detected during promotion)
|
||||||
/// Used by Pattern 2 (string trim) - Pattern 4 support TBD
|
/// Used by Pattern 2 (string trim) - Pattern 4 support TBD
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub trim_helper: Option<TrimLoopHelper>,
|
pub trim_helper: Option<TrimLoopHelper>,
|
||||||
|
|
||||||
// === Pattern 2: Break Condition ===
|
// === Pattern 2: Break Condition ===
|
||||||
|
|
||||||
/// Effective break condition (may be modified for Trim pattern)
|
/// Effective break condition (may be modified for Trim pattern)
|
||||||
/// Used only by Pattern 2
|
/// Used only by Pattern 2
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub break_condition: Option<ASTNode>,
|
pub break_condition: Option<ASTNode>,
|
||||||
|
|
||||||
// === Pattern 3: If-Sum Generalization (Phase 213) ===
|
// === Pattern 3: If-Sum Generalization (Phase 213) ===
|
||||||
|
|
||||||
/// Loop condition AST node
|
/// Loop condition AST node
|
||||||
/// Used by Pattern 3 for dynamic loop condition lowering
|
/// Used by Pattern 3 for dynamic loop condition lowering
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -138,7 +133,7 @@ pub struct PatternPipelineContext {
|
|||||||
|
|
||||||
/// Pattern variant selector
|
/// Pattern variant selector
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum PatternVariant {
|
pub(crate) enum PatternVariant {
|
||||||
/// Pattern 1: Simple while loop (no break, no continue, no if-else PHI)
|
/// Pattern 1: Simple while loop (no break, no continue, no if-else PHI)
|
||||||
Pattern1,
|
Pattern1,
|
||||||
/// Pattern 2: Loop with break statement
|
/// Pattern 2: Loop with break statement
|
||||||
@ -193,7 +188,7 @@ impl PatternPipelineContext {
|
|||||||
// Complex conditions (e.g., i % 2 == 1) → fallback to legacy mode
|
// Complex conditions (e.g., i % 2 == 1) → fallback to legacy mode
|
||||||
if let Some(ASTNode::If { condition, .. }) = if_stmt {
|
if let Some(ASTNode::If { condition, .. }) = if_stmt {
|
||||||
use crate::mir::join_ir::lowering::condition_pattern::{
|
use crate::mir::join_ir::lowering::condition_pattern::{
|
||||||
analyze_condition_pattern, normalize_comparison, ConditionPattern
|
analyze_condition_pattern, normalize_comparison, ConditionPattern,
|
||||||
};
|
};
|
||||||
|
|
||||||
// (a) Pattern check: must be SimpleComparison
|
// (a) Pattern check: must be SimpleComparison
|
||||||
@ -213,7 +208,10 @@ impl PatternPipelineContext {
|
|||||||
// Phase 219: Use assignment-based carrier detection
|
// Phase 219: Use assignment-based carrier detection
|
||||||
// (1 counter like "i" + 1-2 accumulators like "sum", "count")
|
// (1 counter like "i" + 1-2 accumulators like "sum", "count")
|
||||||
use crate::mir::join_ir::lowering::loop_update_summary::analyze_loop_updates_from_ast;
|
use crate::mir::join_ir::lowering::loop_update_summary::analyze_loop_updates_from_ast;
|
||||||
let carrier_names: Vec<String> = self.carrier_info.carriers.iter()
|
let carrier_names: Vec<String> = self
|
||||||
|
.carrier_info
|
||||||
|
.carriers
|
||||||
|
.iter()
|
||||||
.map(|c| c.name.clone())
|
.map(|c| c.name.clone())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@ -232,9 +230,9 @@ impl PatternPipelineContext {
|
|||||||
///
|
///
|
||||||
/// Returns the first if statement found in loop_body, if any.
|
/// Returns the first if statement found in loop_body, if any.
|
||||||
pub fn extract_if_statement(&self) -> Option<&ASTNode> {
|
pub fn extract_if_statement(&self) -> Option<&ASTNode> {
|
||||||
self.loop_body.as_ref().and_then(|body| {
|
self.loop_body
|
||||||
body.iter().find(|stmt| matches!(stmt, ASTNode::If { .. }))
|
.as_ref()
|
||||||
})
|
.and_then(|body| body.iter().find(|stmt| matches!(stmt, ASTNode::If { .. })))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,20 +261,19 @@ impl PatternPipelineContext {
|
|||||||
/// - Loop variable not found in variable_map
|
/// - Loop variable not found in variable_map
|
||||||
/// - Condition variable not found (Pattern 2/4)
|
/// - Condition variable not found (Pattern 2/4)
|
||||||
/// - Trim pattern promotion fails (Pattern 2/4)
|
/// - Trim pattern promotion fails (Pattern 2/4)
|
||||||
pub fn build_pattern_context(
|
pub(crate) fn build_pattern_context(
|
||||||
builder: &mut MirBuilder,
|
builder: &mut MirBuilder,
|
||||||
condition: &ASTNode,
|
condition: &ASTNode,
|
||||||
body: &[ASTNode],
|
body: &[ASTNode],
|
||||||
variant: PatternVariant,
|
variant: PatternVariant,
|
||||||
) -> Result<PatternPipelineContext, String> {
|
) -> Result<PatternPipelineContext, String> {
|
||||||
// Step 1: Common initialization (all patterns)
|
// Step 1: Common initialization (all patterns)
|
||||||
let (loop_var_name, loop_var_id, carrier_info) =
|
let (loop_var_name, loop_var_id, carrier_info) = CommonPatternInitializer::initialize_pattern(
|
||||||
CommonPatternInitializer::initialize_pattern(
|
builder,
|
||||||
builder,
|
condition,
|
||||||
condition,
|
&builder.variable_map,
|
||||||
&builder.variable_map,
|
None, // No exclusions for now (Pattern 2/4 will filter carriers later)
|
||||||
None, // No exclusions for now (Pattern 2/4 will filter carriers later)
|
)?;
|
||||||
)?;
|
|
||||||
|
|
||||||
// Step 2: Build LoopScopeShape
|
// Step 2: Build LoopScopeShape
|
||||||
let loop_scope = match variant {
|
let loop_scope = match variant {
|
||||||
@ -304,39 +301,46 @@ pub fn build_pattern_context(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Step 3: Pattern-specific preprocessing
|
// Step 3: Pattern-specific preprocessing
|
||||||
let (condition_env, condition_bindings, carrier_updates, trim_helper, break_condition,
|
let (
|
||||||
loop_condition, loop_body, loop_update_summary) =
|
condition_env,
|
||||||
match variant {
|
condition_bindings,
|
||||||
PatternVariant::Pattern1 => {
|
carrier_updates,
|
||||||
// Pattern 1: No additional preprocessing needed
|
trim_helper,
|
||||||
(None, None, None, None, None, None, None, None)
|
break_condition,
|
||||||
}
|
loop_condition,
|
||||||
PatternVariant::Pattern3 => {
|
loop_body,
|
||||||
// Pattern 3: Phase 213 - Store loop condition and body for AST-based lowering
|
loop_update_summary,
|
||||||
(
|
) = match variant {
|
||||||
None, // No condition_env
|
PatternVariant::Pattern1 => {
|
||||||
None, // No condition_bindings
|
// Pattern 1: No additional preprocessing needed
|
||||||
None, // No carrier_updates (old style)
|
(None, None, None, None, None, None, None, None)
|
||||||
None, // No trim_helper
|
}
|
||||||
None, // No break_condition
|
PatternVariant::Pattern3 => {
|
||||||
Some(condition.clone()), // loop_condition (Phase 213)
|
// Pattern 3: Phase 213 - Store loop condition and body for AST-based lowering
|
||||||
Some(body.to_vec()), // loop_body (Phase 213)
|
(
|
||||||
None, // loop_update_summary (TODO: Phase 213-2-3)
|
None, // No condition_env
|
||||||
)
|
None, // No condition_bindings
|
||||||
}
|
None, // No carrier_updates (old style)
|
||||||
PatternVariant::Pattern2 | PatternVariant::Pattern4 => {
|
None, // No trim_helper
|
||||||
// Pattern 2/4: Full preprocessing will be handled by existing code
|
None, // No break_condition
|
||||||
// For now, return empty values (will be populated by pattern-specific logic)
|
Some(condition.clone()), // loop_condition (Phase 213)
|
||||||
//
|
Some(body.to_vec()), // loop_body (Phase 213)
|
||||||
// Note: Pattern 2/4 have complex preprocessing that includes:
|
None, // loop_update_summary (TODO: Phase 213-2-3)
|
||||||
// - Break/continue condition analysis
|
)
|
||||||
// - Carrier update analysis
|
}
|
||||||
// - Trim pattern promotion
|
PatternVariant::Pattern2 | PatternVariant::Pattern4 => {
|
||||||
// These will remain in pattern2/pattern4.rs for now and will be
|
// Pattern 2/4: Full preprocessing will be handled by existing code
|
||||||
// gradually migrated into this pipeline in future phases.
|
// For now, return empty values (will be populated by pattern-specific logic)
|
||||||
(None, None, None, None, None, None, None, None)
|
//
|
||||||
}
|
// Note: Pattern 2/4 have complex preprocessing that includes:
|
||||||
};
|
// - Break/continue condition analysis
|
||||||
|
// - Carrier update analysis
|
||||||
|
// - Trim pattern promotion
|
||||||
|
// These will remain in pattern2/pattern4.rs for now and will be
|
||||||
|
// gradually migrated into this pipeline in future phases.
|
||||||
|
(None, None, None, None, None, None, None, None)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Ok(PatternPipelineContext {
|
Ok(PatternPipelineContext {
|
||||||
loop_var_name,
|
loop_var_name,
|
||||||
@ -348,9 +352,9 @@ pub fn build_pattern_context(
|
|||||||
carrier_updates,
|
carrier_updates,
|
||||||
trim_helper,
|
trim_helper,
|
||||||
break_condition,
|
break_condition,
|
||||||
loop_condition, // Phase 213
|
loop_condition, // Phase 213
|
||||||
loop_body, // Phase 213
|
loop_body, // Phase 213
|
||||||
loop_update_summary, // Phase 213
|
loop_update_summary, // Phase 213
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,14 +402,14 @@ mod tests {
|
|||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
||||||
},
|
},
|
||||||
CarrierVar {
|
CarrierVar {
|
||||||
name: "count".to_string(),
|
name: "count".to_string(),
|
||||||
host_id: ValueId(11),
|
host_id: ValueId(11),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
@ -423,9 +427,9 @@ mod tests {
|
|||||||
carrier_updates: None,
|
carrier_updates: None,
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
break_condition: None,
|
break_condition: None,
|
||||||
loop_condition: None, // Phase 213
|
loop_condition: None, // Phase 213
|
||||||
loop_body: None, // Phase 213
|
loop_body: None, // Phase 213
|
||||||
loop_update_summary: None, // Phase 213
|
loop_update_summary: None, // Phase 213
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(ctx.carrier_count(), 2);
|
assert_eq!(ctx.carrier_count(), 2);
|
||||||
@ -466,9 +470,9 @@ mod tests {
|
|||||||
whitespace_chars: vec![" ".to_string(), "\t".to_string()],
|
whitespace_chars: vec![" ".to_string(), "\t".to_string()],
|
||||||
}),
|
}),
|
||||||
break_condition: None,
|
break_condition: None,
|
||||||
loop_condition: None, // Phase 213
|
loop_condition: None, // Phase 213
|
||||||
loop_body: None, // Phase 213
|
loop_body: None, // Phase 213
|
||||||
loop_update_summary: None, // Phase 213
|
loop_update_summary: None, // Phase 213
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(ctx.is_trim_pattern());
|
assert!(ctx.is_trim_pattern());
|
||||||
|
|||||||
@ -30,7 +30,7 @@ use crate::mir::loop_pattern_detection::{LoopFeatures, LoopPatternKind};
|
|||||||
use super::ast_feature_extractor as ast_features;
|
use super::ast_feature_extractor as ast_features;
|
||||||
|
|
||||||
/// Context passed to pattern detect/lower functions
|
/// Context passed to pattern detect/lower functions
|
||||||
pub struct LoopPatternContext<'a> {
|
pub(crate) struct LoopPatternContext<'a> {
|
||||||
/// Loop condition AST node
|
/// Loop condition AST node
|
||||||
pub condition: &'a ASTNode,
|
pub condition: &'a ASTNode,
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ impl<'a> LoopPatternContext<'a> {
|
|||||||
/// Phase 194+: Automatically detects continue/break statements in body
|
/// Phase 194+: Automatically detects continue/break statements in body
|
||||||
/// Phase 192: Extract features and classify pattern from AST
|
/// Phase 192: Extract features and classify pattern from AST
|
||||||
/// Phase 193: Feature extraction delegated to ast_feature_extractor module
|
/// Phase 193: Feature extraction delegated to ast_feature_extractor module
|
||||||
pub fn new(
|
pub(crate) fn new(
|
||||||
condition: &'a ASTNode,
|
condition: &'a ASTNode,
|
||||||
body: &'a [ASTNode],
|
body: &'a [ASTNode],
|
||||||
func_name: &'a str,
|
func_name: &'a str,
|
||||||
@ -99,7 +99,7 @@ impl<'a> LoopPatternContext<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Phase 200-C: Create context with fn_body for capture analysis
|
/// Phase 200-C: Create context with fn_body for capture analysis
|
||||||
pub fn with_fn_body(
|
pub(crate) fn with_fn_body(
|
||||||
condition: &'a ASTNode,
|
condition: &'a ASTNode,
|
||||||
body: &'a [ASTNode],
|
body: &'a [ASTNode],
|
||||||
func_name: &'a str,
|
func_name: &'a str,
|
||||||
@ -117,19 +117,19 @@ impl<'a> LoopPatternContext<'a> {
|
|||||||
|
|
||||||
/// Entry in the loop pattern router table.
|
/// Entry in the loop pattern router table.
|
||||||
/// Each pattern registers a detect function and a lower function.
|
/// Each pattern registers a detect function and a lower function.
|
||||||
pub struct LoopPatternEntry {
|
pub(crate) struct LoopPatternEntry {
|
||||||
/// Human-readable pattern name for debugging
|
/// Human-readable pattern name for debugging
|
||||||
pub name: &'static str,
|
pub(crate) name: &'static str,
|
||||||
|
|
||||||
/// Priority (lower = tried first). Pattern1=10, Pattern2=20, Pattern3=30
|
/// Priority (lower = tried first). Pattern1=10, Pattern2=20, Pattern3=30
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub priority: u8,
|
pub(crate) priority: u8,
|
||||||
|
|
||||||
/// Detection function: returns true if this pattern matches
|
/// Detection function: returns true if this pattern matches
|
||||||
pub detect: fn(&MirBuilder, &LoopPatternContext) -> bool,
|
pub(crate) detect: fn(&MirBuilder, &LoopPatternContext) -> bool,
|
||||||
|
|
||||||
/// Lowering function: performs the actual JoinIR generation
|
/// Lowering function: performs the actual JoinIR generation
|
||||||
pub lower: fn(&mut MirBuilder, &LoopPatternContext) -> Result<Option<ValueId>, String>,
|
pub(crate) lower: fn(&mut MirBuilder, &LoopPatternContext) -> Result<Option<ValueId>, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Static table of all registered loop patterns.
|
/// Static table of all registered loop patterns.
|
||||||
@ -156,16 +156,16 @@ pub struct LoopPatternEntry {
|
|||||||
/// - Structure: has_break && !has_continue
|
/// - Structure: has_break && !has_continue
|
||||||
///
|
///
|
||||||
/// Note: func_name is now only used for debug logging, not pattern detection
|
/// Note: func_name is now only used for debug logging, not pattern detection
|
||||||
pub static LOOP_PATTERNS: &[LoopPatternEntry] = &[
|
pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[
|
||||||
LoopPatternEntry {
|
LoopPatternEntry {
|
||||||
name: "Pattern4_WithContinue",
|
name: "Pattern4_WithContinue",
|
||||||
priority: 5, // Highest priority - continue is most specific
|
priority: 5, // Highest priority - continue is most specific
|
||||||
detect: super::pattern4_with_continue::can_lower,
|
detect: super::pattern4_with_continue::can_lower,
|
||||||
lower: super::pattern4_with_continue::lower,
|
lower: super::pattern4_with_continue::lower,
|
||||||
},
|
},
|
||||||
LoopPatternEntry {
|
LoopPatternEntry {
|
||||||
name: "Pattern3_WithIfPhi",
|
name: "Pattern3_WithIfPhi",
|
||||||
priority: 30, // NOTE: Pattern 3 must be checked BEFORE Pattern 1 (both use "main")
|
priority: 30, // NOTE: Pattern 3 must be checked BEFORE Pattern 1 (both use "main")
|
||||||
detect: super::pattern3_with_if_phi::can_lower,
|
detect: super::pattern3_with_if_phi::can_lower,
|
||||||
lower: super::pattern3_with_if_phi::lower,
|
lower: super::pattern3_with_if_phi::lower,
|
||||||
},
|
},
|
||||||
@ -195,7 +195,7 @@ pub static LOOP_PATTERNS: &[LoopPatternEntry] = &[
|
|||||||
/// - Pattern detection: `ctx.pattern_kind` (from `loop_pattern_detection::classify`)
|
/// - Pattern detection: `ctx.pattern_kind` (from `loop_pattern_detection::classify`)
|
||||||
/// - No redundant pattern detection in detect functions
|
/// - No redundant pattern detection in detect functions
|
||||||
/// - All patterns use structure-based classification
|
/// - All patterns use structure-based classification
|
||||||
pub fn route_loop_pattern(
|
pub(crate) fn route_loop_pattern(
|
||||||
builder: &mut MirBuilder,
|
builder: &mut MirBuilder,
|
||||||
ctx: &LoopPatternContext,
|
ctx: &LoopPatternContext,
|
||||||
) -> Result<Option<ValueId>, String> {
|
) -> Result<Option<ValueId>, String> {
|
||||||
@ -217,7 +217,13 @@ pub fn route_loop_pattern(
|
|||||||
// No pattern matched - return None (caller will handle error)
|
// No pattern matched - return None (caller will handle error)
|
||||||
// Phase 187-2: Legacy LoopBuilder removed, all loops must use JoinIR
|
// Phase 187-2: Legacy LoopBuilder removed, all loops must use JoinIR
|
||||||
if ctx.debug {
|
if ctx.debug {
|
||||||
trace::trace().debug("route", &format!("No pattern matched for function '{}' (pattern_kind={:?})", ctx.func_name, ctx.pattern_kind));
|
trace::trace().debug(
|
||||||
|
"route",
|
||||||
|
&format!(
|
||||||
|
"No pattern matched for function '{}' (pattern_kind={:?})",
|
||||||
|
ctx.func_name, ctx.pattern_kind
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,7 +50,7 @@ use crate::mir::ValueId;
|
|||||||
/// Trim pattern lowering orchestrator
|
/// Trim pattern lowering orchestrator
|
||||||
///
|
///
|
||||||
/// Phase 180: Single entry point for all Trim/P5 lowering operations.
|
/// Phase 180: Single entry point for all Trim/P5 lowering operations.
|
||||||
pub struct TrimLoopLowerer;
|
pub(crate) struct TrimLoopLowerer;
|
||||||
|
|
||||||
/// Result of successful Trim lowering preprocessing
|
/// Result of successful Trim lowering preprocessing
|
||||||
///
|
///
|
||||||
@ -59,7 +59,7 @@ pub struct TrimLoopLowerer;
|
|||||||
/// - Updated carrier info with promoted carrier
|
/// - Updated carrier info with promoted carrier
|
||||||
/// - Condition environment bindings
|
/// - Condition environment bindings
|
||||||
/// - Trim helper for pattern-specific operations
|
/// - Trim helper for pattern-specific operations
|
||||||
pub struct TrimLoweringResult {
|
pub(crate) struct TrimLoweringResult {
|
||||||
/// Replaced break condition (e.g., `!is_carrier`)
|
/// Replaced break condition (e.g., `!is_carrier`)
|
||||||
///
|
///
|
||||||
/// Pattern2/4 will use this instead of the original break condition
|
/// Pattern2/4 will use this instead of the original break condition
|
||||||
@ -98,12 +98,14 @@ impl TrimLoopLowerer {
|
|||||||
Self::is_var_used_in_condition(var_name, left)
|
Self::is_var_used_in_condition(var_name, left)
|
||||||
|| Self::is_var_used_in_condition(var_name, right)
|
|| Self::is_var_used_in_condition(var_name, right)
|
||||||
}
|
}
|
||||||
ASTNode::UnaryOp { operand, .. } => {
|
ASTNode::UnaryOp { operand, .. } => Self::is_var_used_in_condition(var_name, operand),
|
||||||
Self::is_var_used_in_condition(var_name, operand)
|
ASTNode::MethodCall {
|
||||||
}
|
object, arguments, ..
|
||||||
ASTNode::MethodCall { object, arguments, .. } => {
|
} => {
|
||||||
Self::is_var_used_in_condition(var_name, object)
|
Self::is_var_used_in_condition(var_name, object)
|
||||||
|| arguments.iter().any(|arg| Self::is_var_used_in_condition(var_name, arg))
|
|| arguments
|
||||||
|
.iter()
|
||||||
|
.any(|arg| Self::is_var_used_in_condition(var_name, arg))
|
||||||
}
|
}
|
||||||
// Add other node types as needed
|
// Add other node types as needed
|
||||||
_ => false,
|
_ => false,
|
||||||
@ -173,11 +175,8 @@ impl TrimLoopLowerer {
|
|||||||
// TODO: Phase 180-3 will implement full logic from Pattern2
|
// TODO: Phase 180-3 will implement full logic from Pattern2
|
||||||
|
|
||||||
// Step 1: Check if condition references LoopBodyLocal variables
|
// Step 1: Check if condition references LoopBodyLocal variables
|
||||||
let cond_scope = LoopConditionScopeBox::analyze(
|
let cond_scope =
|
||||||
loop_var_name,
|
LoopConditionScopeBox::analyze(loop_var_name, &[loop_cond, break_cond], Some(scope));
|
||||||
&[loop_cond, break_cond],
|
|
||||||
Some(scope),
|
|
||||||
);
|
|
||||||
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[TrimLoopLowerer] Analyzing condition scope: {} variables",
|
"[TrimLoopLowerer] Analyzing condition scope: {} variables",
|
||||||
@ -194,7 +193,9 @@ impl TrimLoopLowerer {
|
|||||||
|
|
||||||
// Phase 183-2: Filter to only condition LoopBodyLocal (skip body-only)
|
// Phase 183-2: Filter to only condition LoopBodyLocal (skip body-only)
|
||||||
use crate::mir::loop_pattern_detection::loop_condition_scope::CondVarScope;
|
use crate::mir::loop_pattern_detection::loop_condition_scope::CondVarScope;
|
||||||
let condition_body_locals: Vec<_> = cond_scope.vars.iter()
|
let condition_body_locals: Vec<_> = cond_scope
|
||||||
|
.vars
|
||||||
|
.iter()
|
||||||
.filter(|v| v.scope == CondVarScope::LoopBodyLocal)
|
.filter(|v| v.scope == CondVarScope::LoopBodyLocal)
|
||||||
.filter(|v| {
|
.filter(|v| {
|
||||||
// Check if variable is actually used in break condition
|
// Check if variable is actually used in break condition
|
||||||
@ -213,7 +214,10 @@ impl TrimLoopLowerer {
|
|||||||
eprintln!(
|
eprintln!(
|
||||||
"[TrimLoopLowerer] Phase 183: Found {} condition LoopBodyLocal variables: {:?}",
|
"[TrimLoopLowerer] Phase 183: Found {} condition LoopBodyLocal variables: {:?}",
|
||||||
condition_body_locals.len(),
|
condition_body_locals.len(),
|
||||||
condition_body_locals.iter().map(|v| &v.name).collect::<Vec<_>>()
|
condition_body_locals
|
||||||
|
.iter()
|
||||||
|
.map(|v| &v.name)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 2: Try promotion via LoopBodyCarrierPromoter
|
// Step 2: Try promotion via LoopBodyCarrierPromoter
|
||||||
@ -242,14 +246,12 @@ impl TrimLoopLowerer {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Step 4: Safety check via TrimLoopHelper
|
// Step 4: Safety check via TrimLoopHelper
|
||||||
let trim_helper = carrier_info
|
let trim_helper = carrier_info.trim_helper().ok_or_else(|| {
|
||||||
.trim_helper()
|
format!(
|
||||||
.ok_or_else(|| {
|
"[TrimLoopLowerer] Promoted but no TrimLoopHelper attached (carrier: '{}')",
|
||||||
format!(
|
trim_info.carrier_name
|
||||||
"[TrimLoopLowerer] Promoted but no TrimLoopHelper attached (carrier: '{}')",
|
)
|
||||||
trim_info.carrier_name
|
})?;
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if !trim_helper.is_safe_trim() {
|
if !trim_helper.is_safe_trim() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
@ -262,15 +264,13 @@ impl TrimLoopLowerer {
|
|||||||
eprintln!("[TrimLoopLowerer] Safe Trim pattern detected, implementing lowering");
|
eprintln!("[TrimLoopLowerer] Safe Trim pattern detected, implementing lowering");
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[TrimLoopLowerer] Carrier: '{}', original var: '{}', whitespace chars: {:?}",
|
"[TrimLoopLowerer] Carrier: '{}', original var: '{}', whitespace chars: {:?}",
|
||||||
trim_helper.carrier_name, trim_helper.original_var, trim_helper.whitespace_chars
|
trim_helper.carrier_name,
|
||||||
|
trim_helper.original_var,
|
||||||
|
trim_helper.whitespace_chars
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 5: Generate carrier initialization code
|
// Step 5: Generate carrier initialization code
|
||||||
Self::generate_carrier_initialization(
|
Self::generate_carrier_initialization(builder, body, trim_helper)?;
|
||||||
builder,
|
|
||||||
body,
|
|
||||||
trim_helper,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[TrimLoopLowerer] Registered carrier '{}' in variable_map",
|
"[TrimLoopLowerer] Registered carrier '{}' in variable_map",
|
||||||
@ -286,11 +286,8 @@ impl TrimLoopLowerer {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Step 7: Setup ConditionEnv bindings
|
// Step 7: Setup ConditionEnv bindings
|
||||||
let condition_bindings = Self::setup_condition_env_bindings(
|
let condition_bindings =
|
||||||
builder,
|
Self::setup_condition_env_bindings(builder, trim_helper, alloc_join_value)?;
|
||||||
trim_helper,
|
|
||||||
alloc_join_value,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[TrimLoopLowerer] Added {} condition bindings",
|
"[TrimLoopLowerer] Added {} condition bindings",
|
||||||
@ -333,13 +330,14 @@ impl TrimLoopLowerer {
|
|||||||
use crate::mir::builder::control_flow::joinir::patterns::trim_pattern_validator::TrimPatternValidator;
|
use crate::mir::builder::control_flow::joinir::patterns::trim_pattern_validator::TrimPatternValidator;
|
||||||
|
|
||||||
// Extract substring pattern from body
|
// Extract substring pattern from body
|
||||||
let (s_name, start_expr) = TrimPatternValidator::extract_substring_args(body, &trim_helper.original_var)
|
let (s_name, start_expr) =
|
||||||
.ok_or_else(|| {
|
TrimPatternValidator::extract_substring_args(body, &trim_helper.original_var)
|
||||||
format!(
|
.ok_or_else(|| {
|
||||||
|
format!(
|
||||||
"[TrimLoopLowerer] Failed to extract substring pattern for Trim carrier '{}'",
|
"[TrimLoopLowerer] Failed to extract substring pattern for Trim carrier '{}'",
|
||||||
trim_helper.carrier_name
|
trim_helper.carrier_name
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[TrimLoopLowerer] Extracted substring pattern: s='{}', start={:?}",
|
"[TrimLoopLowerer] Extracted substring pattern: s='{}', start={:?}",
|
||||||
@ -347,11 +345,10 @@ impl TrimLoopLowerer {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Get ValueIds for string and start
|
// Get ValueIds for string and start
|
||||||
let s_id = builder
|
let s_id =
|
||||||
.variable_map
|
builder.variable_map.get(&s_name).copied().ok_or_else(|| {
|
||||||
.get(&s_name)
|
format!("[TrimLoopLowerer] String variable '{}' not found", s_name)
|
||||||
.copied()
|
})?;
|
||||||
.ok_or_else(|| format!("[TrimLoopLowerer] String variable '{}' not found", s_name))?;
|
|
||||||
|
|
||||||
// Compile start expression to get ValueId
|
// Compile start expression to get ValueId
|
||||||
let start_id = builder.build_expression_impl(*start_expr)?;
|
let start_id = builder.build_expression_impl(*start_expr)?;
|
||||||
@ -378,11 +375,17 @@ impl TrimLoopLowerer {
|
|||||||
vec![start_id, start_plus_1],
|
vec![start_id, start_plus_1],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
eprintln!("[TrimLoopLowerer] Generated initial substring call: ch0 = {:?}", ch0);
|
eprintln!(
|
||||||
|
"[TrimLoopLowerer] Generated initial substring call: ch0 = {:?}",
|
||||||
|
ch0
|
||||||
|
);
|
||||||
|
|
||||||
// Generate: is_ch_match0 = (ch0 == " " || ch0 == "\t" || ...)
|
// Generate: is_ch_match0 = (ch0 == " " || ch0 == "\t" || ...)
|
||||||
let is_ch_match0 =
|
let is_ch_match0 = TrimPatternValidator::emit_whitespace_check(
|
||||||
TrimPatternValidator::emit_whitespace_check(builder, ch0, &trim_helper.whitespace_chars)?;
|
builder,
|
||||||
|
ch0,
|
||||||
|
&trim_helper.whitespace_chars,
|
||||||
|
)?;
|
||||||
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[TrimLoopLowerer] Generated initial whitespace check: is_ch_match0 = {:?}",
|
"[TrimLoopLowerer] Generated initial whitespace check: is_ch_match0 = {:?}",
|
||||||
@ -486,7 +489,9 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
assert!(TrimLoopLowerer::is_var_used_in_condition("ch", &var_node));
|
assert!(TrimLoopLowerer::is_var_used_in_condition("ch", &var_node));
|
||||||
assert!(!TrimLoopLowerer::is_var_used_in_condition("other", &var_node));
|
assert!(!TrimLoopLowerer::is_var_used_in_condition(
|
||||||
|
"other", &var_node
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -506,7 +511,9 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
assert!(TrimLoopLowerer::is_var_used_in_condition("ch", &cond_node));
|
assert!(TrimLoopLowerer::is_var_used_in_condition("ch", &cond_node));
|
||||||
assert!(!TrimLoopLowerer::is_var_used_in_condition("other", &cond_node));
|
assert!(!TrimLoopLowerer::is_var_used_in_condition(
|
||||||
|
"other", &cond_node
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -525,8 +532,13 @@ mod tests {
|
|||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(TrimLoopLowerer::is_var_used_in_condition("digit_pos", &cond_node));
|
assert!(TrimLoopLowerer::is_var_used_in_condition(
|
||||||
assert!(!TrimLoopLowerer::is_var_used_in_condition("other", &cond_node));
|
"digit_pos",
|
||||||
|
&cond_node
|
||||||
|
));
|
||||||
|
assert!(!TrimLoopLowerer::is_var_used_in_condition(
|
||||||
|
"other", &cond_node
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -566,6 +578,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
assert!(TrimLoopLowerer::is_var_used_in_condition("ch", &or_node));
|
assert!(TrimLoopLowerer::is_var_used_in_condition("ch", &or_node));
|
||||||
assert!(!TrimLoopLowerer::is_var_used_in_condition("other", &or_node));
|
assert!(!TrimLoopLowerer::is_var_used_in_condition(
|
||||||
|
"other", &or_node
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,6 +53,8 @@ fn execute_function(
|
|||||||
mut current_func: JoinFuncId,
|
mut current_func: JoinFuncId,
|
||||||
mut current_args: Vec<JoinValue>,
|
mut current_args: Vec<JoinValue>,
|
||||||
) -> Result<JoinValue, JoinRuntimeError> {
|
) -> Result<JoinValue, JoinRuntimeError> {
|
||||||
|
let verbose = crate::config::env::joinir_dev_enabled();
|
||||||
|
|
||||||
'exec: loop {
|
'exec: loop {
|
||||||
let func = module.functions.get(¤t_func).ok_or_else(|| {
|
let func = module.functions.get(¤t_func).ok_or_else(|| {
|
||||||
JoinRuntimeError::new(format!("Function {:?} not found", current_func))
|
JoinRuntimeError::new(format!("Function {:?} not found", current_func))
|
||||||
@ -137,10 +139,12 @@ fn execute_function(
|
|||||||
} => {
|
} => {
|
||||||
// 1. Evaluate cond (Bool or Int)
|
// 1. Evaluate cond (Bool or Int)
|
||||||
let cond_value = read_var(&locals, *cond)?;
|
let cond_value = read_var(&locals, *cond)?;
|
||||||
eprintln!(
|
if verbose {
|
||||||
"[SELECT DEBUG] cond={:?}, cond_value={:?}",
|
eprintln!(
|
||||||
cond, cond_value
|
"[joinir/runner/select] cond={:?}, cond_value={:?}",
|
||||||
);
|
cond, cond_value
|
||||||
|
);
|
||||||
|
}
|
||||||
let cond_bool = match cond_value {
|
let cond_bool = match cond_value {
|
||||||
JoinValue::Bool(b) => b,
|
JoinValue::Bool(b) => b,
|
||||||
JoinValue::Int(i) => i != 0, // Int も許す(0=false, それ以外=true)
|
JoinValue::Int(i) => i != 0, // Int も許す(0=false, それ以外=true)
|
||||||
@ -155,17 +159,21 @@ fn execute_function(
|
|||||||
// 2. Select then_val or else_val
|
// 2. Select then_val or else_val
|
||||||
let then_value = read_var(&locals, *then_val)?;
|
let then_value = read_var(&locals, *then_val)?;
|
||||||
let else_value = read_var(&locals, *else_val)?;
|
let else_value = read_var(&locals, *else_val)?;
|
||||||
eprintln!(
|
if verbose {
|
||||||
"[SELECT DEBUG] cond_bool={}, then_val={:?}={:?}, else_val={:?}={:?}",
|
eprintln!(
|
||||||
cond_bool, then_val, then_value, else_val, else_value
|
"[joinir/runner/select] cond_bool={}, then_val={:?}={:?}, else_val={:?}={:?}",
|
||||||
);
|
cond_bool, then_val, then_value, else_val, else_value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let selected_id = if cond_bool { *then_val } else { *else_val };
|
let selected_id = if cond_bool { *then_val } else { *else_val };
|
||||||
let selected_value = read_var(&locals, selected_id)?;
|
let selected_value = read_var(&locals, selected_id)?;
|
||||||
eprintln!(
|
if verbose {
|
||||||
"[SELECT DEBUG] selected_id={:?}, selected_value={:?}",
|
eprintln!(
|
||||||
selected_id, selected_value
|
"[joinir/runner/select] selected_id={:?}, selected_value={:?}",
|
||||||
);
|
selected_id, selected_value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 3. Write to dst
|
// 3. Write to dst
|
||||||
locals.insert(*dst, selected_value);
|
locals.insert(*dst, selected_value);
|
||||||
|
|||||||
Reference in New Issue
Block a user