Phase 33 NORM canon test: enforce normalized dev route for P1/P2/JP mini
This commit is contained in:
@ -31,6 +31,20 @@
|
|||||||
- DigitPos promotion を二重値化し、`digit_pos` から boolean carrier `is_digit_pos`(ConditionOnly)と integer carrier `digit_value`(LoopState)を生成。
|
- DigitPos promotion を二重値化し、`digit_pos` から boolean carrier `is_digit_pos`(ConditionOnly)と integer carrier `digit_value`(LoopState)を生成。
|
||||||
- UpdateEnv で `digit_pos` 解決時に `digit_value` を優先し、NumberAccumulation(`result = result * 10 + digit_pos`)と break 条件の両方で DigitPos パターンが安全に利用可能に。
|
- UpdateEnv で `digit_pos` 解決時に `digit_value` を優先し、NumberAccumulation(`result = result * 10 + digit_pos`)と break 条件の両方で DigitPos パターンが安全に利用可能に。
|
||||||
- 現在: `cargo test --release --lib` で 931/931 テスト PASS(既知 FAIL なし)。
|
- 現在: `cargo test --release --lib` で 931/931 テスト PASS(既知 FAIL なし)。
|
||||||
|
- Phase 28-NORM-P2(dev-only):
|
||||||
|
- Normalized JoinIR のミニ実装を Pattern1 に続き Pattern2 最小ケースまで拡張(Structured→Normalized→Structured を比較)。
|
||||||
|
- 対応外の Structured JoinModule では normalize_pattern2_minimal が Fail-Fast するようガードを追加し、normalized_dev テストで固定。
|
||||||
|
- Phase 29-NORM-P2-APPLY(dev-only):
|
||||||
|
- Phase 34 の break fixture(i/acc/n の単純 break ループ)を Structured→Normalized→Structured の往復に通し、VM 実行結果が Structured 直経路と一致することを dev テストで固定。
|
||||||
|
- ガードは 3 パラメータまで緩和しつつ、DigitPos/Trim などの重いキャリアはまだ非対応のまま。
|
||||||
|
- Phase 30-NORM-P2-DEV-RUN(dev-only):
|
||||||
|
- JoinIR runner に `NYASH_JOINIR_NORMALIZED_DEV_RUN=1` を追加し、Pattern1/2 ミニケースだけ Structured→Normalized→Structured を挟んで dev 実行できるようにした(`normalized_dev` + debug 限定)。通常経路(Structured→MIR)は不変。
|
||||||
|
- Phase 31-NORM-JP-MINI(dev-only):
|
||||||
|
- JsonParser 系のシンプルな P2 ループ(skip_whitespace ミニ fixture)を Structured→Normalized→Structured 経由でも実行し、runner dev スイッチの比較テストで Structured 直経路と一致することを固定。
|
||||||
|
- Phase 32-NORM-CANON-PREP(dev-only):
|
||||||
|
- JoinIR→MIR ブリッジの入口を `bridge_joinir_to_mir` に一本化し、normalized_dev スイッチ(feature + env)で Structured→Normalized→Structured の dev roundtrip を切り替える準備を整えた。P1/P2/JP mini の比較テストも VM ブリッジ経路で追加。
|
||||||
|
- Phase 33-NORM-CANON-TEST(dev-only):
|
||||||
|
- P1/P2(Phase 34 break fixture)/JsonParser skip_ws mini について、normalized_dev ON 時は shape_guard 経由で必ず Normalized roundtrip を通すようブリッジと runner を固めた。normalized_joinir_min.rs の runner/VM 比較テストを拡張し、Normalized が壊れたら dev スイートが必ず赤になるようにした(本番 CLI は従来どおり Structured→MIR)。
|
||||||
|
|
||||||
### 1. いまコード側で意識しておきたいフォーカス
|
### 1. いまコード側で意識しておきたいフォーカス
|
||||||
|
|
||||||
|
|||||||
@ -30,6 +30,8 @@ gui-examples = ["gui"]
|
|||||||
all-examples = ["gui-examples"]
|
all-examples = ["gui-examples"]
|
||||||
dynamic-file = []
|
dynamic-file = []
|
||||||
wasm-backend = ["dep:wasmtime", "dep:wabt"]
|
wasm-backend = ["dep:wasmtime", "dep:wabt"]
|
||||||
|
# Dev-only normalized JoinIR experiment (Pattern1)
|
||||||
|
normalized_dev = []
|
||||||
# TLV C shim wiring (default OFF): enables optional dependency `nyash-tlv`
|
# TLV C shim wiring (default OFF): enables optional dependency `nyash-tlv`
|
||||||
tlv-shim = ["dep:nyash-tlv"]
|
tlv-shim = ["dep:nyash-tlv"]
|
||||||
# Core C shims (design-stage; default OFF)
|
# Core C shims (design-stage; default OFF)
|
||||||
|
|||||||
@ -1106,7 +1106,65 @@ Normalized JoinIR では、制御構造を次の 3 要素だけで表現する
|
|||||||
これにより、現在 JoinIR 層で苦労している「PHI 配線」「exit_bindings/jump_args 整合性」「評価順のねじれ」は、
|
これにより、現在 JoinIR 層で苦労している「PHI 配線」「exit_bindings/jump_args 整合性」「評価順のねじれ」は、
|
||||||
Normalized JoinIR 側では「Env フィールドの更新順」と「どの継続を呼ぶか」に還元される想定だよ。
|
Normalized JoinIR 側では「Env フィールドの更新順」と「どの継続を呼ぶか」に還元される想定だよ。
|
||||||
|
|
||||||
### 3.3 Phase 26-H のフェーズ分割(案)
|
### 3.3 Normalized JoinIR 導入のメリットと初期コスト
|
||||||
|
|
||||||
|
Normalized JoinIR を 1 段挟むと、開発の手触りがどう変わるかをここでまとめておくよ。
|
||||||
|
|
||||||
|
#### 3.3.1 楽になるポイント
|
||||||
|
|
||||||
|
- **デバッグ時の「問いの分離」**
|
||||||
|
- Structured JoinIR だけで `_parse_number` / `_atoi` を追うときは、常に同時に 4 つくらいの問いを抱えることになる:
|
||||||
|
1. Pattern 判定は正しいか(P1/P2/P3/P4/P5)
|
||||||
|
2. CarrierInfo / LoopState / LoopLocalZero / FromHost の設計は妥当か
|
||||||
|
3. 評価順(header → body-init → break → updates → tail)は正しいか
|
||||||
|
4. PHI / exit_bindings / jump_args / variable_map の整合は取れているか
|
||||||
|
- Normalized JoinIR を挟むと、これが層ごとに分割される:
|
||||||
|
- 上(Structured): Pattern 判定 + CarrierInfo/Condition/Update の設計
|
||||||
|
- 中(Normalized): EnvLayout + step/kont 合成(制御骨格)
|
||||||
|
- 下(Bridge): TailCall+Env から MIR/VM の PHI/branch を作る
|
||||||
|
- これにより、「これは Pattern/Carrier 問題」「これは Env+継続の組み立て問題」「これは Bridge 問題」と、デバッグ時にレイヤ単位で切り分けやすくなる。
|
||||||
|
|
||||||
|
- **PHI まわりの思考負荷軽減**
|
||||||
|
- Structured JoinIR では、LoopHeader PHI / Exit PHI / Select 展開 PHI と、exit_bindings/jump_args/variable_map 再接続が絡み合う。
|
||||||
|
- Normalized フェーズでは、キャリアや DigitPos/NumberAccumulation などはすべて Env フィールドになり、PHI は JoinIR’ には現れない:
|
||||||
|
- 「どこから値が来たか?」は「どの Env フィールドがどの順番で更新されたか」を見るだけでよくなる。
|
||||||
|
- PHI/exit_bindings は Bridge 側の責務として明示的に分離される。
|
||||||
|
|
||||||
|
- **while/if/break/continue/return の骨格が共通化される**
|
||||||
|
- Pattern1–5 ごとに似たロジックが散らばりがちな現在と違い、Normalized では:
|
||||||
|
- ループ: `loop_step(env, k_exit)` + `loop_body(env, k_exit)`
|
||||||
|
- if: 条件判定 → then/else 継続 → join 継続
|
||||||
|
- break: `TailCallKont(k_exit, env)`
|
||||||
|
- continue: `TailCallFn(loop_step, env', k_exit)`
|
||||||
|
- こうした「世界共通のテンプレ」を 1 箱にまとめておけるので、新しい Pattern や JsonParser/selfhost のループを追加するときも、構造箱(Structured)側だけで頑張ればよくなる。
|
||||||
|
|
||||||
|
- **「1箱 = 1質問」を守りやすくなる**
|
||||||
|
- Structured JoinIR では、PatternLowerer が一時的に「構造+評価順+PHI 配線+exit_bindings」を抱える瞬間がある。
|
||||||
|
- Normalized を挟むと:
|
||||||
|
- Structured: 「どの値を Env に載せるか」を決める箱
|
||||||
|
- Normalized: 「Env と継続にどう分解するか」を決める箱
|
||||||
|
- Bridge: 「TailCall+Env を MIR/VM にどう落とすか」を決める箱
|
||||||
|
- という分担にできるので、「この箱は何の質問に答えるか」が清潔に保ちやすくなる。
|
||||||
|
|
||||||
|
#### 3.3.2 初期コストと注意点
|
||||||
|
|
||||||
|
- **新しい IR モデルを 1 セット覚える必要がある**
|
||||||
|
- Env+Fn+Kont+TailCall の世界観を JoinIR 層の前提として追加することになる。
|
||||||
|
- 一度定着すれば Structured より思考負荷は下がるが、導入初期は「型・不変条件・ダンプの読み方」に慣れるまで少しコストが乗る。
|
||||||
|
|
||||||
|
- **移行期間中は 2 経路の比較が発生する**
|
||||||
|
- AST → JoinIR(Structured) → MIR
|
||||||
|
- AST → JoinIR(Structured) → JoinIR(Normalized) → MIR
|
||||||
|
- の 2 経路をしばらく併走させ、dev で結果比較をするフェーズが必要になる。
|
||||||
|
- Phase 26-H / 28-NORM-P2 のように「小さいサブセットから比較用にだけ回す」方針で進めることで、コストは制御可能にする。
|
||||||
|
|
||||||
|
- **導入フェーズごとに Fail-Fast とテストが必須**
|
||||||
|
- Structured→Normalized は対応範囲外の Structured JoinModule については必ず Fail-Fast(panic/Err)し、サイレントに正規化を試みない。
|
||||||
|
- `normalized_dev` feature 配下で、Structured↔Normalized↔Structured の roundtrip と VM 実行結果比較を常に追加していく運用にする。
|
||||||
|
|
||||||
|
全体として、短期的には「新しいレイヤと比較テストを抱えるコスト」が増えるけれど、中長期では Pattern2〜4 や JsonParser 系の「評価順+PHI+DigitPos/num_str」が絡むループで、デバッグと設計の負荷をかなり下げることを狙った設計だよ。
|
||||||
|
|
||||||
|
### 3.4 Phase 26-H のフェーズ分割(案)
|
||||||
|
|
||||||
この正規化レイヤを一度に入れるのは重いので、Phase 26-H ラインとして小さく段階分けして進める方針だよ。
|
この正規化レイヤを一度に入れるのは重いので、Phase 26-H ラインとして小さく段階分けして進める方針だよ。
|
||||||
(番号は仮で、実際の Phase 番号は current の進行に合わせて調整する想定)
|
(番号は仮で、実際の Phase 番号は current の進行に合わせて調整する想定)
|
||||||
@ -1136,6 +1194,41 @@ Normalized JoinIR 側では「Env フィールドの更新順」と「どの継
|
|||||||
各サブフェーズでは「既存意味論を変えない」「Fail-Fast 原則を維持する」「新旧経路の比較テストを先に用意する」をガードとして運用するよ。
|
各サブフェーズでは「既存意味論を変えない」「Fail-Fast 原則を維持する」「新旧経路の比較テストを先に用意する」をガードとして運用するよ。
|
||||||
詳細な API/型設計は、Phase 26-H.A/B の中で `EnvLayoutBox` / `LoopStepSynthesizer` / `JpVerifier` 等の箱として段階的に固めていく想定。
|
詳細な API/型設計は、Phase 26-H.A/B の中で `EnvLayoutBox` / `LoopStepSynthesizer` / `JpVerifier` 等の箱として段階的に固めていく想定。
|
||||||
|
|
||||||
### 3.4 Phase 27-CLEAN – Pattern2–4 の軽量整理
|
### 3.5 Phase 27-CLEAN – Pattern2–4 の軽量整理
|
||||||
|
|
||||||
- Pattern2〜4/loop_with_break_minimal まわりで可視性・ログ・補助関数を整理し、joinir_dev フラグ配下のデバッグログに寄せる。意味論は変えずに「読みやすさ」「追いやすさ」を優先するクリーンアップフェーズだよ。
|
- Pattern2〜4/loop_with_break_minimal まわりで可視性・ログ・補助関数を整理し、joinir_dev フラグ配下のデバッグログに寄せる。意味論は変えずに「読みやすさ」「追いやすさ」を優先するクリーンアップフェーズだよ。
|
||||||
|
|
||||||
|
### 3.6 Phase 28-NORM-P2 – Normalized JoinIR プロトタイプ拡張(dev-only)
|
||||||
|
|
||||||
|
- Phase 26-H で用意した Normalized JoinIR の極小サブセットを、Pattern1 に続いて Pattern2(最小 break ループ)まで拡張。
|
||||||
|
- Structured → Normalized → Structured の往復と VM 実行比較を dev フィーチャ (`normalized_dev` + debug) でテスト済み。
|
||||||
|
- 対象は joinir_min_loop 相当の「ループ変数1つ+breakのみ」のミニケースに限定し、本番経路(Structured→MIR)は不変。
|
||||||
|
- normalize_pattern2_minimal は対応外の Structured JoinModule では Fail-Fast するようにガードを追加し、対応範囲をテストで固定。
|
||||||
|
|
||||||
|
### 3.7 Phase 29-NORM-P2-APPLY – Pattern2 実ループへの dev 適用
|
||||||
|
|
||||||
|
- Phase 34 の fixture `loop_frontend_break.program.json`(`i/acc/n` のシンプル break ループ)を Structured→Normalized→Structured の往復経路に載せ、VM 実行結果が Structured 直経路と一致することを dev テストで確認。
|
||||||
|
- `normalize_pattern2_minimal` のガードを 3 パラメータ(loop var + acc + host)まで許容する形に緩めつつ、DigitPos/Trim などの heavy carrier は依然として非対応に固定。
|
||||||
|
- すべて `normalized_dev` feature + debug_assertions 配下の実験経路に閉じ、本番 Structured→MIR パスの挙動は不変。
|
||||||
|
|
||||||
|
### 3.8 Phase 30-NORM-P2-DEV-RUN – runner で Normalized を試走(dev)
|
||||||
|
|
||||||
|
- JoinIR runner に dev 専用の `NYASH_JOINIR_NORMALIZED_DEV_RUN=1` スイッチを追加し、Pattern1/2 のミニケースだけ Structured→Normalized→Structured を噛ませてから実行できるようにした(`normalized_dev` feature + debug ビルド限定)。
|
||||||
|
- runner 経路でも Structured 直実行との stdout/結果が一致することをテスト(loop_min_while と Phase 34 break fixture)で確認。フラグ OFF 時の挙動は従来と同じ。
|
||||||
|
|
||||||
|
### 3.9 Phase 31-NORM-JP-MINI – JsonParser ミニ P2 を Normalized dev 経由で試走
|
||||||
|
|
||||||
|
- JsonParser 系のシンプルな P2 ループ(skip_whitespace 簡易版)を Structured→Normalized→Structured の dev ランナー経路に載せ、通常経路との実行結果一致を比較。
|
||||||
|
- `jsonparser_skip_ws_mini.program.json`(docs/private/roadmap2/phases/normalized_dev/fixtures 配下)由来の JoinModule を使い、`NYASH_JOINIR_NORMALIZED_DEV_RUN=1` + `normalized_dev` + debug 限定で切替可能にした。
|
||||||
|
- 本番経路(Structured→MIR)は引き続き不変で、Normalized は dev 比較専用のまま。
|
||||||
|
|
||||||
|
### 3.10 Phase 32-NORM-CANON-PREP – Normalized 本番導入の下地づくり
|
||||||
|
|
||||||
|
- JoinIR→MIR ブリッジに Structured/Normalized の入口を分けた `bridge_joinir_to_mir_*` を用意し、conversion pipeline / VM ランナーがこの 1 箇所で dev roundtrip を切替できるように整理。
|
||||||
|
- Normalized dev スイッチを `normalized_dev_enabled()` に集約(feature `normalized_dev` + `NYASH_JOINIR_NORMALIZED_DEV_RUN=1`)。P1/P2 ミニ + JsonParser mini で env ON/OFF の比較テストを追加し、いつでも canonical route に昇格できる状態を固めた。
|
||||||
|
|
||||||
|
### 3.11 Phase 33-NORM-CANON-TEST – P1/P2/JP mini をテスト必須ラインへ
|
||||||
|
|
||||||
|
- `bridge_joinir_to_mir` / JoinIR runner は `shape_guard` で P1/P2 ミニ + JsonParser skip_ws mini を検知した場合、`normalized_dev_enabled()` が ON なら必ず Structured→Normalized→Structured の dev roundtrip を経由(正規化失敗は dev panic)。未対応形状は静かに Structured 直通。
|
||||||
|
- tests/normalized_joinir_min.rs を Phase 33 前提に拡張し、P1/P2/JP mini の runner/VM 比較テストを env ON で実行。Normalized が壊れればこのスイートが必ず赤になる構造にした(feature OFF の CI は従来どおり無関係)。
|
||||||
|
- 本番 CLI 挙動は Structured→MIR のまま維持しつつ、Normalized を canonical に昇格させる前段階として dev テストで SSOT 相当の役割を担わせている。
|
||||||
|
|||||||
@ -0,0 +1,65 @@
|
|||||||
|
Status: Active
|
||||||
|
Scope: Phase 26‑H.C ― Normalized JoinIR (Pattern1) を MIR に落とす最小ブリッジの設計と比較テスト計画(dev専用)。
|
||||||
|
|
||||||
|
# Phase 26‑H.C 指示書 — Normalized→MIR ブリッジ(Pattern1 最小)+比較テスト
|
||||||
|
|
||||||
|
## ゴール
|
||||||
|
- Normalized JoinIR(NormalizedModule)から MIR への「本物のブリッジ」を、Pattern1 最小ケースだけで実装する。
|
||||||
|
- Structured→MIR と Structured→Normalized→MIR の結果が一致することをテストで確認する(dev 専用、CLI はまだ触らない)。
|
||||||
|
|
||||||
|
## A. Normalized→Structured or Normalized→MIR の経路方針
|
||||||
|
- Option 1: Normalized→Structured→既存 MIR ブリッジ
|
||||||
|
- 長所: 既存の JoinIR→MIR パイプラインを再利用できる。
|
||||||
|
- 短所: フェーズが一段増える(Normalized→Structured が joinir→joinir の一歩になる)。
|
||||||
|
- Option 2: Normalized→MIR を直接作るミニブリッジ(Pattern1 限定) **←今回これを優先**
|
||||||
|
- 長所: TailCall/Env をそのまま MIR に落とす感触を掴める。
|
||||||
|
- 短所: 一部ロジックが JoinIR→MIR と重複する。
|
||||||
|
- どちらを選んでも「Pattern1 最小ケース限定」「テスト専用 helper」とする(本番パスへは配線しない)。今回は Option 2 で進め、必要があれば Option 1 のスケルトンも残す。
|
||||||
|
|
||||||
|
## B. Normalized→Structured ミニ変換(Option 1 採用時のメモ)
|
||||||
|
- 追加場所: `src/mir/join_ir/normalized.rs`
|
||||||
|
- API 例: `pub fn normalize_pattern1_to_structured(norm: &NormalizedModule) -> JoinModule`
|
||||||
|
- 制約: `norm.phase == JoinIrPhase::Normalized`、関数は 1 つ(loop_step + 仮の k_exit 程度)を想定。
|
||||||
|
- 変換の要点:
|
||||||
|
- EnvLayout の fields から JoinFunction の params を再構成(最小なら1引数でも可)。
|
||||||
|
- `JpInst::Let` → `JoinInst::Compute(MirLikeInst::Const/BinOp/UnaryOp/Compare)` に戻す。
|
||||||
|
- TailCallFn/TailCallKont/If を Pattern1 が生成していた `JoinInst::Call/Jump/Ret` 相当に戻す。
|
||||||
|
- `NormalizedModule.entry` を `JoinModule.entry` に写す。
|
||||||
|
- `NormalizedModule.structured_backup` は比較用に残すが、ブリッジでは本体から再構成する。
|
||||||
|
|
||||||
|
## C. Normalized→MIR ミニブリッジ(今回の主経路: Option 2)
|
||||||
|
- 追加ファイル案: `src/mir/join_ir/lowering/normalized_pattern1_to_mir.rs`
|
||||||
|
- API 例:
|
||||||
|
```rust
|
||||||
|
pub fn lower_normalized_pattern1_to_mir(norm: &NormalizedModule) -> crate::mir::Module { ... }
|
||||||
|
```
|
||||||
|
- 最低限やること:
|
||||||
|
- EnvLayout の 1 フィールドを MIR のループ変数に対応させる。
|
||||||
|
- `JpFunction` 本体を 1 ブロック or 小ブロック列に変換(Let→MIR Assign/Compute、TailCallFn→ループ末尾 Jump 等)。
|
||||||
|
- Pattern1 の `loop_min_while` 相当が生成していた MIR と構造的に一致するかをテストで確認。
|
||||||
|
- ガード: dev/テスト専用 helper から明示的に呼ぶ。ランナー/CLI には配線しない。
|
||||||
|
|
||||||
|
## D. 比較用テストの追加
|
||||||
|
- テストファイル案: `tests/normalized_pattern1_bridge.rs`
|
||||||
|
- シナリオ:
|
||||||
|
1. Structured JoinIR (Pattern1: loop_min_while 相当) を既存 lowerer で生成。
|
||||||
|
2. そのコピーを `normalize_pattern1_minimal` に通し NormalizedModule を得る。
|
||||||
|
3. 既存経路: Structured → 既存 JoinIR→MIR ブリッジ → 実行 or MIR dump。
|
||||||
|
4. 新経路: Structured → Normalized → (C のブリッジ)→ MIR → 実行 or MIR dump。
|
||||||
|
5. 比較:
|
||||||
|
- 実行結果一致(RC + stdout)。
|
||||||
|
- 余裕があれば MIR の基本ブロック数や命令種も比較し、構造乖離がないことを確認。
|
||||||
|
- テスト名例:
|
||||||
|
- `test_normalized_pattern1_minimal_roundtrip`
|
||||||
|
- `test_normalized_pattern1_exec_result_matches_structured`
|
||||||
|
|
||||||
|
## E. ガードと完了条件
|
||||||
|
- ガード:
|
||||||
|
- 新ブリッジはテスト専用(ランナー/CLI からは呼ばない)。
|
||||||
|
- JoinModule.phase が Structured のままでも既存経路は従来どおり動作。
|
||||||
|
- Normalized 経路は dev/テスト専用 helper からのみ呼ぶ。
|
||||||
|
- 完了条件:
|
||||||
|
- NormalizedModule から MIR を生成する経路(Option 2)が 1 本通る。
|
||||||
|
- Pattern1 最小ケースで Structured→MIR と Structured→Normalized→MIR が同じ結果になるテストが緑。
|
||||||
|
- 既存 `cargo test --release` が 0 FAIL のまま。
|
||||||
|
|
||||||
@ -527,8 +527,8 @@ pub enum ASTNode {
|
|||||||
/// 値・型は右辺と同じ、副作用として左辺に代入
|
/// 値・型は右辺と同じ、副作用として左辺に代入
|
||||||
/// 使用例: local y = (x = x + 1), if (x = next()) != null { }
|
/// 使用例: local y = (x = x + 1), if (x = next()) != null { }
|
||||||
GroupedAssignmentExpr {
|
GroupedAssignmentExpr {
|
||||||
lhs: String, // 変数名
|
lhs: String, // 変数名
|
||||||
rhs: Box<ASTNode>, // 右辺式
|
rhs: Box<ASTNode>, // 右辺式
|
||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -68,16 +68,14 @@ impl MirInterpreter {
|
|||||||
})
|
})
|
||||||
.unwrap_or(1_000_000);
|
.unwrap_or(1_000_000);
|
||||||
let mut steps: u64 = 0;
|
let mut steps: u64 = 0;
|
||||||
let trace_log_path: Option<String> = std::env::var("NYASH_VM_TRACE_LOG")
|
let trace_log_path: Option<String> = std::env::var("NYASH_VM_TRACE_LOG").ok().map(|v| {
|
||||||
.ok()
|
let trimmed = v.trim();
|
||||||
.map(|v| {
|
if trimmed.is_empty() || trimmed == "1" {
|
||||||
let trimmed = v.trim();
|
"__mir__.log".to_string()
|
||||||
if trimmed.is_empty() || trimmed == "1" {
|
} else {
|
||||||
"__mir__.log".to_string()
|
v
|
||||||
} else {
|
}
|
||||||
v
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
steps += 1;
|
steps += 1;
|
||||||
|
|||||||
@ -313,7 +313,10 @@ impl MirInterpreter {
|
|||||||
("ConsoleBox", 400) => {
|
("ConsoleBox", 400) => {
|
||||||
// log/println
|
// log/println
|
||||||
if let VMValue::BoxRef(bx) = receiver {
|
if let VMValue::BoxRef(bx) = receiver {
|
||||||
if let Some(console) = bx.as_any().downcast_ref::<crate::boxes::console_box::ConsoleBox>() {
|
if let Some(console) = bx
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<crate::boxes::console_box::ConsoleBox>()
|
||||||
|
{
|
||||||
let message = if args.len() > 1 {
|
let message = if args.len() > 1 {
|
||||||
self.reg_load(args[1])?.to_string()
|
self.reg_load(args[1])?.to_string()
|
||||||
} else if args.len() > 0 {
|
} else if args.len() > 0 {
|
||||||
@ -330,7 +333,10 @@ impl MirInterpreter {
|
|||||||
("ConsoleBox", 401) => {
|
("ConsoleBox", 401) => {
|
||||||
// warn
|
// warn
|
||||||
if let VMValue::BoxRef(bx) = receiver {
|
if let VMValue::BoxRef(bx) = receiver {
|
||||||
if let Some(console) = bx.as_any().downcast_ref::<crate::boxes::console_box::ConsoleBox>() {
|
if let Some(console) = bx
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<crate::boxes::console_box::ConsoleBox>()
|
||||||
|
{
|
||||||
let message = if args.len() > 1 {
|
let message = if args.len() > 1 {
|
||||||
self.reg_load(args[1])?.to_string()
|
self.reg_load(args[1])?.to_string()
|
||||||
} else if args.len() > 0 {
|
} else if args.len() > 0 {
|
||||||
@ -347,7 +353,10 @@ impl MirInterpreter {
|
|||||||
("ConsoleBox", 402) => {
|
("ConsoleBox", 402) => {
|
||||||
// error
|
// error
|
||||||
if let VMValue::BoxRef(bx) = receiver {
|
if let VMValue::BoxRef(bx) = receiver {
|
||||||
if let Some(console) = bx.as_any().downcast_ref::<crate::boxes::console_box::ConsoleBox>() {
|
if let Some(console) = bx
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<crate::boxes::console_box::ConsoleBox>()
|
||||||
|
{
|
||||||
let message = if args.len() > 1 {
|
let message = if args.len() > 1 {
|
||||||
self.reg_load(args[1])?.to_string()
|
self.reg_load(args[1])?.to_string()
|
||||||
} else if args.len() > 0 {
|
} else if args.len() > 0 {
|
||||||
@ -364,7 +373,10 @@ impl MirInterpreter {
|
|||||||
("ConsoleBox", 403) => {
|
("ConsoleBox", 403) => {
|
||||||
// clear
|
// clear
|
||||||
if let VMValue::BoxRef(bx) = receiver {
|
if let VMValue::BoxRef(bx) = receiver {
|
||||||
if let Some(console) = bx.as_any().downcast_ref::<crate::boxes::console_box::ConsoleBox>() {
|
if let Some(console) = bx
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<crate::boxes::console_box::ConsoleBox>()
|
||||||
|
{
|
||||||
console.clear();
|
console.clear();
|
||||||
return Ok(VMValue::Void);
|
return Ok(VMValue::Void);
|
||||||
}
|
}
|
||||||
@ -405,7 +417,10 @@ impl MirInterpreter {
|
|||||||
// Plugin Box methods (slot >= 1000)
|
// Plugin Box methods (slot >= 1000)
|
||||||
(_, slot) if slot >= 1000 => {
|
(_, slot) if slot >= 1000 => {
|
||||||
if let VMValue::BoxRef(bx) = receiver {
|
if let VMValue::BoxRef(bx) = receiver {
|
||||||
if let Some(_p) = bx.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
if let Some(_p) = bx
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||||
|
{
|
||||||
let host = crate::runtime::plugin_loader_unified::get_global_plugin_host();
|
let host = crate::runtime::plugin_loader_unified::get_global_plugin_host();
|
||||||
let _host = host.read().unwrap();
|
let _host = host.read().unwrap();
|
||||||
let _argv = self.load_args_as_boxes(args)?;
|
let _argv = self.load_args_as_boxes(args)?;
|
||||||
@ -447,22 +462,20 @@ impl MirInterpreter {
|
|||||||
|
|
||||||
// 2. Lookup type in TypeRegistry and get slot
|
// 2. Lookup type in TypeRegistry and get slot
|
||||||
// Note: Try exact arity first, then try with args.len()-1 (in case receiver is duplicated in args)
|
// Note: Try exact arity first, then try with args.len()-1 (in case receiver is duplicated in args)
|
||||||
let slot = crate::runtime::type_registry::resolve_slot_by_name(
|
let slot =
|
||||||
type_name,
|
crate::runtime::type_registry::resolve_slot_by_name(type_name, method, args.len())
|
||||||
method,
|
.or_else(|| {
|
||||||
args.len(),
|
// Fallback: try with one less argument (receiver might be in args)
|
||||||
).or_else(|| {
|
if args.len() > 0 {
|
||||||
// Fallback: try with one less argument (receiver might be in args)
|
crate::runtime::type_registry::resolve_slot_by_name(
|
||||||
if args.len() > 0 {
|
type_name,
|
||||||
crate::runtime::type_registry::resolve_slot_by_name(
|
method,
|
||||||
type_name,
|
args.len() - 1,
|
||||||
method,
|
)
|
||||||
args.len() - 1,
|
} else {
|
||||||
)
|
None
|
||||||
} else {
|
}
|
||||||
None
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(slot) = slot {
|
if let Some(slot) = slot {
|
||||||
// 3. Use unified dispatch
|
// 3. Use unified dispatch
|
||||||
@ -489,9 +502,8 @@ impl MirInterpreter {
|
|||||||
if let Some(arg_id) = args.get(0) {
|
if let Some(arg_id) = args.get(0) {
|
||||||
let ch = self.reg_load(*arg_id)?.to_string();
|
let ch = self.reg_load(*arg_id)?.to_string();
|
||||||
let c = ch.chars().next().unwrap_or('\0');
|
let c = ch.chars().next().unwrap_or('\0');
|
||||||
let is_alpha = ('A'..='Z').contains(&c)
|
let is_alpha =
|
||||||
|| ('a'..='z').contains(&c)
|
('A'..='Z').contains(&c) || ('a'..='z').contains(&c) || c == '_';
|
||||||
|| c == '_';
|
|
||||||
return Ok(VMValue::Bool(is_alpha));
|
return Ok(VMValue::Bool(is_alpha));
|
||||||
} else {
|
} else {
|
||||||
return Err(self.err_invalid("is_alpha requires 1 argument"));
|
return Err(self.err_invalid("is_alpha requires 1 argument"));
|
||||||
@ -520,12 +532,7 @@ impl MirInterpreter {
|
|||||||
let host = crate::runtime::plugin_loader_unified::get_global_plugin_host();
|
let host = crate::runtime::plugin_loader_unified::get_global_plugin_host();
|
||||||
let host = host.read().unwrap();
|
let host = host.read().unwrap();
|
||||||
let argv = self.load_args_as_boxes(args)?;
|
let argv = self.load_args_as_boxes(args)?;
|
||||||
match host.invoke_instance_method(
|
match host.invoke_instance_method(&p.box_type, method, p.inner.instance_id, &argv) {
|
||||||
&p.box_type,
|
|
||||||
method,
|
|
||||||
p.inner.instance_id,
|
|
||||||
&argv,
|
|
||||||
) {
|
|
||||||
Ok(Some(ret)) => return Ok(VMValue::from_nyash_box(ret)),
|
Ok(Some(ret)) => return Ok(VMValue::from_nyash_box(ret)),
|
||||||
Ok(None) => return Ok(VMValue::Void),
|
Ok(None) => return Ok(VMValue::Void),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
@ -253,11 +253,7 @@ impl MirInterpreter {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if std::env::var("NYASH_EMIT_MIR_TRACE")
|
if std::env::var("NYASH_EMIT_MIR_TRACE").ok().as_deref() == Some("1") {
|
||||||
.ok()
|
|
||||||
.as_deref()
|
|
||||||
== Some("1")
|
|
||||||
{
|
|
||||||
if let Some(name) = chosen_name {
|
if let Some(name) = chosen_name {
|
||||||
eprintln!("[vm/entry] main={}", name);
|
eprintln!("[vm/entry] main={}", name);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -194,9 +194,7 @@ impl StaticBoxRegistry {
|
|||||||
|
|
||||||
/// 登録済み/検出済みの静的Box名一覧
|
/// 登録済み/検出済みの静的Box名一覧
|
||||||
pub fn all_box_names(&self) -> impl Iterator<Item = &String> {
|
pub fn all_box_names(&self) -> impl Iterator<Item = &String> {
|
||||||
self.declarations
|
self.declarations.keys().chain(self.detected_boxes.iter())
|
||||||
.keys()
|
|
||||||
.chain(self.detected_boxes.iter())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// declarations への直接アクセス (既存コードとの互換性)
|
/// declarations への直接アクセス (既存コードとの互換性)
|
||||||
@ -250,7 +248,7 @@ mod tests {
|
|||||||
"JsonParserBox.parse/1".to_string(),
|
"JsonParserBox.parse/1".to_string(),
|
||||||
"JsonParserBox.toString/0".to_string(),
|
"JsonParserBox.toString/0".to_string(),
|
||||||
"ProgramJSONBox.get/1".to_string(),
|
"ProgramJSONBox.get/1".to_string(),
|
||||||
"Main.main/0".to_string(), // 除外される
|
"Main.main/0".to_string(), // 除外される
|
||||||
"StringBox.length/0".to_string(), // 除外される
|
"StringBox.length/0".to_string(), // 除外される
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -190,7 +190,8 @@ impl UnifiedBoxRegistry {
|
|||||||
// core_required (6個) + 特殊型の一部を予約型として保護
|
// core_required (6個) + 特殊型の一部を予約型として保護
|
||||||
CoreBoxId::from_name(name)
|
CoreBoxId::from_name(name)
|
||||||
.map(|id| {
|
.map(|id| {
|
||||||
id.is_core_required() || matches!(id, CoreBoxId::Result | CoreBoxId::Method)
|
id.is_core_required()
|
||||||
|
|| matches!(id, CoreBoxId::Result | CoreBoxId::Method)
|
||||||
})
|
})
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,7 +29,11 @@ macro_rules! ny_wrap_string {
|
|||||||
pub fn $name(&self) -> StringBox {
|
pub fn $name(&self) -> StringBox {
|
||||||
match self.$inner() {
|
match self.$inner() {
|
||||||
Ok(result) => StringBox::new(result),
|
Ok(result) => StringBox::new(result),
|
||||||
Err(e) => panic!("FileHandleBox.{}() failed: {}", stringify!($name).trim_start_matches("ny_"), e),
|
Err(e) => panic!(
|
||||||
|
"FileHandleBox.{}() failed: {}",
|
||||||
|
stringify!($name).trim_start_matches("ny_"),
|
||||||
|
e
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -40,7 +44,11 @@ macro_rules! ny_wrap_bool {
|
|||||||
pub fn $name(&self) -> BoolBox {
|
pub fn $name(&self) -> BoolBox {
|
||||||
match self.$inner() {
|
match self.$inner() {
|
||||||
Ok(result) => BoolBox::new(result),
|
Ok(result) => BoolBox::new(result),
|
||||||
Err(e) => panic!("FileHandleBox.{}() failed: {}", stringify!($name).trim_start_matches("ny_"), e),
|
Err(e) => panic!(
|
||||||
|
"FileHandleBox.{}() failed: {}",
|
||||||
|
stringify!($name).trim_start_matches("ny_"),
|
||||||
|
e
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -51,7 +59,11 @@ macro_rules! ny_wrap_integer {
|
|||||||
pub fn $name(&self) -> crate::box_trait::IntegerBox {
|
pub fn $name(&self) -> crate::box_trait::IntegerBox {
|
||||||
match self.$inner() {
|
match self.$inner() {
|
||||||
Ok(result) => crate::box_trait::IntegerBox::new(result as i64),
|
Ok(result) => crate::box_trait::IntegerBox::new(result as i64),
|
||||||
Err(e) => panic!("FileHandleBox.{}() failed: {}", stringify!($name).trim_start_matches("ny_"), e),
|
Err(e) => panic!(
|
||||||
|
"FileHandleBox.{}() failed: {}",
|
||||||
|
stringify!($name).trim_start_matches("ny_"),
|
||||||
|
e
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -156,8 +168,8 @@ impl FileHandleBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get FileIo provider to check capabilities
|
// Get FileIo provider to check capabilities
|
||||||
let provider = provider_lock::get_filebox_provider()
|
let provider =
|
||||||
.ok_or_else(provider_not_initialized)?;
|
provider_lock::get_filebox_provider().ok_or_else(provider_not_initialized)?;
|
||||||
|
|
||||||
// NoFs profile check (Fail-Fast)
|
// NoFs profile check (Fail-Fast)
|
||||||
let caps = provider.caps();
|
let caps = provider.caps();
|
||||||
@ -171,8 +183,8 @@ impl FileHandleBox {
|
|||||||
// Create NEW independent Ring0FsFileIo instance for this handle
|
// Create NEW independent Ring0FsFileIo instance for this handle
|
||||||
// IMPORTANT: We must create a new instance, not clone the Arc
|
// IMPORTANT: We must create a new instance, not clone the Arc
|
||||||
// because Ring0FsFileIo has internal state (path field)
|
// because Ring0FsFileIo has internal state (path field)
|
||||||
use crate::runtime::get_global_ring0;
|
|
||||||
use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo;
|
use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo;
|
||||||
|
use crate::runtime::get_global_ring0;
|
||||||
|
|
||||||
let ring0 = get_global_ring0();
|
let ring0 = get_global_ring0();
|
||||||
|
|
||||||
@ -281,11 +293,12 @@ impl FileHandleBox {
|
|||||||
///
|
///
|
||||||
/// Unified metadata access through FileIo trait.
|
/// Unified metadata access through FileIo trait.
|
||||||
fn metadata_internal(&self) -> Result<crate::boxes::file::provider::FileStat, String> {
|
fn metadata_internal(&self) -> Result<crate::boxes::file::provider::FileStat, String> {
|
||||||
let io = self.io.as_ref()
|
let io = self
|
||||||
|
.io
|
||||||
|
.as_ref()
|
||||||
.ok_or_else(|| "FileHandleBox is not open".to_string())?;
|
.ok_or_else(|| "FileHandleBox is not open".to_string())?;
|
||||||
|
|
||||||
io.stat()
|
io.stat().map_err(|e| format!("Metadata failed: {}", e))
|
||||||
.map_err(|e| format!("Metadata failed: {}", e))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get file size in bytes
|
/// Get file size in bytes
|
||||||
@ -308,7 +321,9 @@ impl FileHandleBox {
|
|||||||
///
|
///
|
||||||
/// Uses FileIo::exists() for direct check.
|
/// Uses FileIo::exists() for direct check.
|
||||||
pub fn exists(&self) -> Result<bool, String> {
|
pub fn exists(&self) -> Result<bool, String> {
|
||||||
let io = self.io.as_ref()
|
let io = self
|
||||||
|
.io
|
||||||
|
.as_ref()
|
||||||
.ok_or_else(|| "FileHandleBox is not open".to_string())?;
|
.ok_or_else(|| "FileHandleBox is not open".to_string())?;
|
||||||
|
|
||||||
Ok(io.exists())
|
Ok(io.exists())
|
||||||
@ -395,7 +410,9 @@ impl NyashBox for FileHandleBox {
|
|||||||
fn to_string_box(&self) -> StringBox {
|
fn to_string_box(&self) -> StringBox {
|
||||||
StringBox::new(format!(
|
StringBox::new(format!(
|
||||||
"FileHandleBox(path={}, mode={}, open={})",
|
"FileHandleBox(path={}, mode={}, open={})",
|
||||||
self.path, self.mode, self.is_open()
|
self.path,
|
||||||
|
self.mode,
|
||||||
|
self.is_open()
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,8 +459,8 @@ mod tests {
|
|||||||
|
|
||||||
/// Helper: Initialize FileBox provider for tests
|
/// Helper: Initialize FileBox provider for tests
|
||||||
fn init_test_provider() {
|
fn init_test_provider() {
|
||||||
use crate::runtime::ring0::{default_ring0, init_global_ring0};
|
|
||||||
use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo;
|
use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo;
|
||||||
|
use crate::runtime::ring0::{default_ring0, init_global_ring0};
|
||||||
use std::panic;
|
use std::panic;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -720,7 +737,7 @@ mod tests {
|
|||||||
init_test_provider();
|
init_test_provider();
|
||||||
|
|
||||||
let path = "/tmp/phase111_append_test.txt";
|
let path = "/tmp/phase111_append_test.txt";
|
||||||
let _ = fs::remove_file(path); // cleanup
|
let _ = fs::remove_file(path); // cleanup
|
||||||
|
|
||||||
// First write (truncate)
|
// First write (truncate)
|
||||||
let mut handle = FileHandleBox::new();
|
let mut handle = FileHandleBox::new();
|
||||||
@ -751,7 +768,7 @@ mod tests {
|
|||||||
// Write test file
|
// Write test file
|
||||||
let mut handle = FileHandleBox::new();
|
let mut handle = FileHandleBox::new();
|
||||||
handle.open(path, "w").unwrap();
|
handle.open(path, "w").unwrap();
|
||||||
handle.write_all("hello").unwrap(); // 5 bytes
|
handle.write_all("hello").unwrap(); // 5 bytes
|
||||||
handle.close().unwrap();
|
handle.close().unwrap();
|
||||||
|
|
||||||
// Check size
|
// Check size
|
||||||
@ -967,7 +984,9 @@ mod tests {
|
|||||||
|
|
||||||
// Test metadata_internal() via stat()
|
// Test metadata_internal() via stat()
|
||||||
handle.open(path, "r").unwrap();
|
handle.open(path, "r").unwrap();
|
||||||
let stat = handle.metadata_internal().expect("metadata_internal should succeed");
|
let stat = handle
|
||||||
|
.metadata_internal()
|
||||||
|
.expect("metadata_internal should succeed");
|
||||||
assert!(stat.is_file);
|
assert!(stat.is_file);
|
||||||
assert!(!stat.is_dir);
|
assert!(!stat.is_dir);
|
||||||
assert_eq!(stat.size, 5);
|
assert_eq!(stat.size, 5);
|
||||||
|
|||||||
@ -89,8 +89,8 @@ impl FileBox {
|
|||||||
// Create NEW independent provider instance for this FileBox
|
// Create NEW independent provider instance for this FileBox
|
||||||
// IMPORTANT: We must create a new instance, not clone the Arc,
|
// IMPORTANT: We must create a new instance, not clone the Arc,
|
||||||
// because Ring0FsFileIo has internal state (path field)
|
// because Ring0FsFileIo has internal state (path field)
|
||||||
use crate::runtime::get_global_ring0;
|
|
||||||
use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo;
|
use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo;
|
||||||
|
use crate::runtime::get_global_ring0;
|
||||||
|
|
||||||
let ring0 = get_global_ring0();
|
let ring0 = get_global_ring0();
|
||||||
|
|
||||||
@ -126,7 +126,8 @@ impl FileBox {
|
|||||||
}
|
}
|
||||||
// Phase 108: UTF-8 conversion (text-oriented design)
|
// Phase 108: UTF-8 conversion (text-oriented design)
|
||||||
let text = String::from_utf8_lossy(buf).to_string();
|
let text = String::from_utf8_lossy(buf).to_string();
|
||||||
provider.write(&text)
|
provider
|
||||||
|
.write(&text)
|
||||||
.map_err(|e| format!("Write failed: {:?}", e))
|
.map_err(|e| format!("Write failed: {:?}", e))
|
||||||
} else {
|
} else {
|
||||||
Err(no_provider_available())
|
Err(no_provider_available())
|
||||||
@ -160,7 +161,10 @@ impl FileBox {
|
|||||||
Err(e) => Box::new(StringBox::new(format!("Error: {:?}", e))),
|
Err(e) => Box::new(StringBox::new(format!("Error: {:?}", e))),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Box::new(StringBox::new(format!("Error: {}", no_provider_available())))
|
Box::new(StringBox::new(format!(
|
||||||
|
"Error: {}",
|
||||||
|
no_provider_available()
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,8 +234,8 @@ impl std::fmt::Display for FileBox {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::runtime::ring0::{default_ring0, GLOBAL_RING0};
|
|
||||||
use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo;
|
use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo;
|
||||||
|
use crate::runtime::ring0::{default_ring0, GLOBAL_RING0};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
@ -345,11 +349,7 @@ mod tests {
|
|||||||
|
|
||||||
let fb = FileBox::open(tmp_path).expect("open failed");
|
let fb = FileBox::open(tmp_path).expect("open failed");
|
||||||
let exists_box = fb.exists();
|
let exists_box = fb.exists();
|
||||||
let exists = exists_box
|
let exists = exists_box.as_any().downcast_ref::<BoolBox>().unwrap().value;
|
||||||
.as_any()
|
|
||||||
.downcast_ref::<BoolBox>()
|
|
||||||
.unwrap()
|
|
||||||
.value;
|
|
||||||
|
|
||||||
assert!(exists);
|
assert!(exists);
|
||||||
|
|
||||||
|
|||||||
@ -60,7 +60,8 @@ impl FileCaps {
|
|||||||
return Err("Read not supported by FileBox provider".to_string());
|
return Err("Read not supported by FileBox provider".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"w" | "a" => { // Phase 111: "a" added
|
"w" | "a" => {
|
||||||
|
// Phase 111: "a" added
|
||||||
if !self.write {
|
if !self.write {
|
||||||
return Err("Write not supported by FileBox provider".to_string());
|
return Err("Write not supported by FileBox provider".to_string());
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/config/env/joinir_dev.rs
vendored
20
src/config/env/joinir_dev.rs
vendored
@ -99,6 +99,24 @@ pub fn read_quoted_ifmerge_enabled() -> bool {
|
|||||||
env_bool("HAKO_JOINIR_READ_QUOTED_IFMERGE")
|
env_bool("HAKO_JOINIR_READ_QUOTED_IFMERGE")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// NYASH_JOINIR_NORMALIZED_DEV_RUN=1 - Run JoinIR runner through Normalized roundtrip (dev only)
|
||||||
|
pub fn joinir_normalized_dev_run_enabled() -> bool {
|
||||||
|
// Feature gate at call sites still applies; this helper just centralizes the env read.
|
||||||
|
env_bool("NYASH_JOINIR_NORMALIZED_DEV_RUN")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unified switch for Normalized dev experiments (feature + env).
|
||||||
|
///
|
||||||
|
/// - Requires `--features normalized_dev`
|
||||||
|
/// - Requires `NYASH_JOINIR_NORMALIZED_DEV_RUN=1`
|
||||||
|
pub fn normalized_dev_enabled() -> bool {
|
||||||
|
if cfg!(feature = "normalized_dev") {
|
||||||
|
joinir_normalized_dev_run_enabled()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Phase 82: NYASH_PHI_FALLBACK_DISABLED=1 - Disable if_phi fallback (dev mode)
|
/// Phase 82: NYASH_PHI_FALLBACK_DISABLED=1 - Disable if_phi fallback (dev mode)
|
||||||
///
|
///
|
||||||
/// lifecycle.rs の infer_type_from_phi* callsite を封じて、
|
/// lifecycle.rs の infer_type_from_phi* callsite を封じて、
|
||||||
@ -124,7 +142,7 @@ pub fn read_quoted_ifmerge_enabled() -> bool {
|
|||||||
/// Phase 84-5: if_phi.rs 削除後は常に true
|
/// Phase 84-5: if_phi.rs 削除後は常に true
|
||||||
/// 環境変数は統計目的でのみ残す
|
/// 環境変数は統計目的でのみ残す
|
||||||
pub fn phi_fallback_disabled() -> bool {
|
pub fn phi_fallback_disabled() -> bool {
|
||||||
true // Phase 84-5: Always disabled after if_phi.rs deletion
|
true // Phase 84-5: Always disabled after if_phi.rs deletion
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Phase 84-5: 統計・メトリクス用(オプション)
|
/// Phase 84-5: 統計・メトリクス用(オプション)
|
||||||
|
|||||||
@ -71,7 +71,7 @@ fn main() {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use nyash_rust::box_trait::{NyashBox, StringBox};
|
use nyash_rust::box_trait::{NyashBox, StringBox};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -30,10 +30,10 @@ mod exprs_peek; // peek expression
|
|||||||
mod exprs_qmark; // ?-propagate
|
mod exprs_qmark; // ?-propagate
|
||||||
mod fields; // field access/assignment lowering split
|
mod fields; // field access/assignment lowering split
|
||||||
mod if_form;
|
mod if_form;
|
||||||
mod lifecycle;
|
|
||||||
mod loop_frontend_binding; // Phase 50: Loop Frontend Binding (JoinIR variable mapping)
|
|
||||||
mod joinir_id_remapper; // Phase 189: JoinIR ID remapping (ValueId/BlockId translation)
|
mod joinir_id_remapper; // Phase 189: JoinIR ID remapping (ValueId/BlockId translation)
|
||||||
mod joinir_inline_boundary_injector; // Phase 189: JoinInlineBoundary Copy instruction injector
|
mod joinir_inline_boundary_injector; // Phase 189: JoinInlineBoundary Copy instruction injector
|
||||||
|
mod lifecycle;
|
||||||
|
mod loop_frontend_binding; // Phase 50: Loop Frontend Binding (JoinIR variable mapping)
|
||||||
pub(crate) mod loops;
|
pub(crate) mod loops;
|
||||||
mod ops;
|
mod ops;
|
||||||
mod phi;
|
mod phi;
|
||||||
@ -285,7 +285,7 @@ impl MirBuilder {
|
|||||||
method_tail_index_source_len: 0,
|
method_tail_index_source_len: 0,
|
||||||
|
|
||||||
current_region_stack: Vec::new(),
|
current_region_stack: Vec::new(),
|
||||||
fn_body_ast: None, // Phase 200-C: Initialize to None
|
fn_body_ast: None, // Phase 200-C: Initialize to None
|
||||||
reserved_value_ids: HashSet::new(), // Phase 201-A: Initialize to empty
|
reserved_value_ids: HashSet::new(), // Phase 201-A: Initialize to empty
|
||||||
|
|
||||||
loop_header_stack: Vec::new(),
|
loop_header_stack: Vec::new(),
|
||||||
|
|||||||
@ -144,9 +144,7 @@ impl<'a> CalleeGuardBox<'a> {
|
|||||||
// Previously this converted to Global("JsonParserBox.parse") which failed
|
// Previously this converted to Global("JsonParserBox.parse") which failed
|
||||||
// because the function table expected "JsonParserBox.parse/1" with arity.
|
// because the function table expected "JsonParserBox.parse/1" with arity.
|
||||||
if trace_enabled {
|
if trace_enabled {
|
||||||
eprintln!(
|
eprintln!("[static-runtime-guard] StaticCompiler trusting methodize:");
|
||||||
"[static-runtime-guard] StaticCompiler trusting methodize:"
|
|
||||||
);
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
" {}.{} receiver %{} - passing through as static box call",
|
" {}.{} receiver %{} - passing through as static box call",
|
||||||
box_name, method, recv.0
|
box_name, method, recv.0
|
||||||
|
|||||||
@ -344,7 +344,11 @@ impl MirBuilder {
|
|||||||
body: Vec<ASTNode>,
|
body: Vec<ASTNode>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// Phase 200-C: Store fn_body for capture analysis
|
// Phase 200-C: Store fn_body for capture analysis
|
||||||
eprintln!("[lower_static_method_as_function] Storing fn_body with {} nodes for '{}'", body.len(), func_name);
|
eprintln!(
|
||||||
|
"[lower_static_method_as_function] Storing fn_body with {} nodes for '{}'",
|
||||||
|
body.len(),
|
||||||
|
func_name
|
||||||
|
);
|
||||||
self.fn_body_ast = Some(body.clone());
|
self.fn_body_ast = Some(body.clone());
|
||||||
|
|
||||||
// Step 1: Context準備
|
// Step 1: Context準備
|
||||||
|
|||||||
@ -5,9 +5,9 @@
|
|||||||
//!
|
//!
|
||||||
//! Phase 4 Extraction: Separated from merge_joinir_mir_blocks (lines 159-194)
|
//! Phase 4 Extraction: Separated from merge_joinir_mir_blocks (lines 159-194)
|
||||||
|
|
||||||
use crate::mir::MirModule;
|
|
||||||
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
|
|
||||||
use super::super::trace;
|
use super::super::trace;
|
||||||
|
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
|
||||||
|
use crate::mir::MirModule;
|
||||||
|
|
||||||
/// Phase 1: Allocate new block IDs for ALL functions (Phase 189)
|
/// Phase 1: Allocate new block IDs for ALL functions (Phase 189)
|
||||||
///
|
///
|
||||||
@ -25,10 +25,16 @@ pub(super) fn allocate_blocks(
|
|||||||
// This exit_block_id will be returned and used by instruction_rewriter and exit_phi_builder
|
// This exit_block_id will be returned and used by instruction_rewriter and exit_phi_builder
|
||||||
let exit_block_id = builder.block_gen.next();
|
let exit_block_id = builder.block_gen.next();
|
||||||
|
|
||||||
eprintln!("[cf_loop/joinir/block_allocator] Phase 177-3: Allocated exit_block_id = {:?}", exit_block_id);
|
eprintln!(
|
||||||
|
"[cf_loop/joinir/block_allocator] Phase 177-3: Allocated exit_block_id = {:?}",
|
||||||
|
exit_block_id
|
||||||
|
);
|
||||||
|
|
||||||
// Phase 195: Use unified trace
|
// Phase 195: Use unified trace
|
||||||
trace::trace().blocks("allocator", "Phase 189: Allocating block IDs for all functions");
|
trace::trace().blocks(
|
||||||
|
"allocator",
|
||||||
|
"Phase 189: Allocating block IDs for all functions",
|
||||||
|
);
|
||||||
|
|
||||||
// DETERMINISM FIX: Sort functions by name to ensure consistent iteration order
|
// DETERMINISM FIX: Sort functions by name to ensure consistent iteration order
|
||||||
let mut functions: Vec<_> = mir_module.functions.iter().collect();
|
let mut functions: Vec<_> = mir_module.functions.iter().collect();
|
||||||
@ -50,7 +56,10 @@ pub(super) fn allocate_blocks(
|
|||||||
// Phase 195: Use unified trace
|
// Phase 195: Use unified trace
|
||||||
trace::trace().blocks(
|
trace::trace().blocks(
|
||||||
"allocator",
|
"allocator",
|
||||||
&format!("Block remap: {}:{:?} → {:?}", func_name, old_block_id, new_block_id),
|
&format!(
|
||||||
|
"Block remap: {}:{:?} → {:?}",
|
||||||
|
func_name, old_block_id, new_block_id
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
|
use super::LoopHeaderPhiInfo;
|
||||||
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
||||||
use crate::mir::join_ir::lowering::join_value_space::{LOCAL_MAX, PARAM_MAX, PARAM_MIN};
|
use crate::mir::join_ir::lowering::join_value_space::{LOCAL_MAX, PARAM_MAX, PARAM_MIN};
|
||||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||||
use super::LoopHeaderPhiInfo;
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub(super) fn verify_loop_header_phis(
|
pub(super) fn verify_loop_header_phis(
|
||||||
@ -99,10 +99,7 @@ pub(super) fn verify_exit_line(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub(super) fn verify_valueid_regions(
|
pub(super) fn verify_valueid_regions(loop_info: &LoopHeaderPhiInfo, boundary: &JoinInlineBoundary) {
|
||||||
loop_info: &LoopHeaderPhiInfo,
|
|
||||||
boundary: &JoinInlineBoundary,
|
|
||||||
) {
|
|
||||||
fn region_name(id: ValueId) -> &'static str {
|
fn region_name(id: ValueId) -> &'static str {
|
||||||
if id.0 < PARAM_MIN {
|
if id.0 < PARAM_MIN {
|
||||||
"PHI Reserved"
|
"PHI Reserved"
|
||||||
|
|||||||
@ -81,21 +81,25 @@ impl ExitMetaCollector {
|
|||||||
debug: bool,
|
debug: bool,
|
||||||
) -> Vec<LoopExitBinding> {
|
) -> Vec<LoopExitBinding> {
|
||||||
let mut bindings = Vec::new();
|
let mut bindings = Vec::new();
|
||||||
let verbose = debug || crate::config::env::joinir_dev_enabled();
|
let dev_on = crate::config::env::joinir_dev_enabled();
|
||||||
|
let verbose = debug || dev_on;
|
||||||
|
let strict = crate::config::env::joinir_strict_enabled() || dev_on;
|
||||||
|
|
||||||
if debug {
|
if verbose {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/exit_line] ExitMetaCollector: Collecting {} exit values",
|
"[joinir/exit-line] ExitMetaCollector: Collecting {} exit values",
|
||||||
exit_meta.exit_values.len()
|
exit_meta.exit_values.len()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over ExitMeta entries and build bindings
|
// Iterate over ExitMeta entries and build bindings
|
||||||
for (carrier_name, join_exit_value) in &exit_meta.exit_values {
|
for (carrier_name, join_exit_value) in &exit_meta.exit_values {
|
||||||
eprintln!(
|
if verbose {
|
||||||
"[cf_loop/exit_line] ExitMetaCollector DEBUG: Checking carrier '{}' in variable_map",
|
eprintln!(
|
||||||
carrier_name
|
"[joinir/exit-line] checking carrier '{}' in variable_map",
|
||||||
);
|
carrier_name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Look up host slot from variable_map
|
// Look up host slot from variable_map
|
||||||
if let Some(&host_slot) = builder.variable_map.get(carrier_name) {
|
if let Some(&host_slot) = builder.variable_map.get(carrier_name) {
|
||||||
@ -103,7 +107,8 @@ impl ExitMetaCollector {
|
|||||||
|
|
||||||
// Phase 228-8: Look up role from carrier_info if available
|
// Phase 228-8: Look up role from carrier_info if available
|
||||||
let role = if let Some(ci) = carrier_info {
|
let role = if let Some(ci) = carrier_info {
|
||||||
ci.carriers.iter()
|
ci.carriers
|
||||||
|
.iter()
|
||||||
.find(|c| c.name == *carrier_name)
|
.find(|c| c.name == *carrier_name)
|
||||||
.map(|c| c.role)
|
.map(|c| c.role)
|
||||||
.unwrap_or(CarrierRole::LoopState)
|
.unwrap_or(CarrierRole::LoopState)
|
||||||
@ -118,18 +123,21 @@ impl ExitMetaCollector {
|
|||||||
role,
|
role,
|
||||||
};
|
};
|
||||||
|
|
||||||
eprintln!(
|
if verbose {
|
||||||
"[cf_loop/exit_line] ExitMetaCollector: Collected '{}' JoinIR {:?} → HOST {:?}, role={:?}",
|
eprintln!(
|
||||||
carrier_name, join_exit_value, host_slot, role
|
"[joinir/exit-line] collected '{}' JoinIR {:?} → HOST {:?}, role={:?}",
|
||||||
);
|
carrier_name, join_exit_value, host_slot, role
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
bindings.push(binding);
|
bindings.push(binding);
|
||||||
} else {
|
} else {
|
||||||
// Phase 228-8: Check if this is a ConditionOnly carrier
|
// Phase 228-8: Check if this is a ConditionOnly carrier
|
||||||
// Phase 247-EX: Also check if this is a FromHost carrier (e.g., digit_value)
|
// Phase 247-EX: Also check if this is a FromHost carrier (e.g., digit_value)
|
||||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierRole, CarrierInit};
|
use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole};
|
||||||
let carrier_meta = if let Some(ci) = carrier_info {
|
let carrier_meta = if let Some(ci) = carrier_info {
|
||||||
ci.carriers.iter()
|
ci.carriers
|
||||||
|
.iter()
|
||||||
.find(|c| c.name == *carrier_name)
|
.find(|c| c.name == *carrier_name)
|
||||||
.map(|c| (c.role, c.init))
|
.map(|c| (c.role, c.init))
|
||||||
} else {
|
} else {
|
||||||
@ -147,10 +155,12 @@ impl ExitMetaCollector {
|
|||||||
role: CarrierRole::ConditionOnly,
|
role: CarrierRole::ConditionOnly,
|
||||||
};
|
};
|
||||||
|
|
||||||
eprintln!(
|
if verbose {
|
||||||
"[cf_loop/exit_line] Phase 228-8: Collected ConditionOnly carrier '{}' JoinIR {:?} (not in variable_map)",
|
eprintln!(
|
||||||
carrier_name, join_exit_value
|
"[joinir/exit-line] collected ConditionOnly carrier '{}' JoinIR {:?} (not in variable_map)",
|
||||||
);
|
carrier_name, join_exit_value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
bindings.push(binding);
|
bindings.push(binding);
|
||||||
}
|
}
|
||||||
@ -164,10 +174,12 @@ impl ExitMetaCollector {
|
|||||||
role: CarrierRole::LoopState,
|
role: CarrierRole::LoopState,
|
||||||
};
|
};
|
||||||
|
|
||||||
eprintln!(
|
if verbose {
|
||||||
"[cf_loop/exit_line] Phase 247-EX: Collected FromHost carrier '{}' JoinIR {:?} (not in variable_map)",
|
eprintln!(
|
||||||
carrier_name, join_exit_value
|
"[joinir/exit-line] collected FromHost carrier '{}' JoinIR {:?} (not in variable_map)",
|
||||||
);
|
carrier_name, join_exit_value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
bindings.push(binding);
|
bindings.push(binding);
|
||||||
}
|
}
|
||||||
@ -180,18 +192,25 @@ impl ExitMetaCollector {
|
|||||||
role: CarrierRole::LoopState,
|
role: CarrierRole::LoopState,
|
||||||
};
|
};
|
||||||
|
|
||||||
eprintln!(
|
if verbose {
|
||||||
"[cf_loop/exit_line] Phase 247-EX: Collected loop-local carrier '{}' JoinIR {:?} (no host slot)",
|
eprintln!(
|
||||||
carrier_name, join_exit_value
|
"[joinir/exit-line] collected loop-local carrier '{}' JoinIR {:?} (no host slot)",
|
||||||
);
|
carrier_name, join_exit_value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
bindings.push(binding);
|
bindings.push(binding);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!(
|
let msg = format!(
|
||||||
"[cf_loop/exit_line] ExitMetaCollector DEBUG: Carrier '{}' not in variable_map and not ConditionOnly/FromHost (skip)",
|
"[joinir/exit-line] carrier '{}' not in variable_map and not ConditionOnly/FromHost (skip)",
|
||||||
carrier_name
|
carrier_name
|
||||||
);
|
);
|
||||||
|
if strict {
|
||||||
|
panic!("{}", msg);
|
||||||
|
} else if verbose {
|
||||||
|
eprintln!("{}", msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,7 +218,7 @@ impl ExitMetaCollector {
|
|||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/exit_line] ExitMetaCollector: Collected {} bindings: {:?}",
|
"[joinir/exit-line] ExitMetaCollector: collected {} bindings: {:?}",
|
||||||
bindings.len(),
|
bindings.len(),
|
||||||
bindings
|
bindings
|
||||||
);
|
);
|
||||||
@ -211,7 +230,6 @@ impl ExitMetaCollector {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_empty_exit_meta() {
|
fn test_empty_exit_meta() {
|
||||||
|
|||||||
@ -48,14 +48,14 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//! No changes to exit_line module needed!
|
//! No changes to exit_line module needed!
|
||||||
|
|
||||||
pub mod reconnector;
|
|
||||||
pub mod meta_collector;
|
pub mod meta_collector;
|
||||||
|
pub mod reconnector;
|
||||||
|
|
||||||
pub use reconnector::ExitLineReconnector;
|
|
||||||
pub use meta_collector::ExitMetaCollector;
|
pub use meta_collector::ExitMetaCollector;
|
||||||
|
pub use reconnector::ExitLineReconnector;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
/// Phase 33-10-Refactor-P2: ExitLineOrchestrator facade
|
/// Phase 33-10-Refactor-P2: ExitLineOrchestrator facade
|
||||||
///
|
///
|
||||||
@ -84,9 +84,10 @@ impl ExitLineOrchestrator {
|
|||||||
carrier_phis: &BTreeMap<String, ValueId>,
|
carrier_phis: &BTreeMap<String, ValueId>,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
if debug {
|
let verbose = debug || crate::config::env::joinir_dev_enabled();
|
||||||
|
if verbose {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir/exit_line] ExitLineOrchestrator: Starting Phase 6 reconnection with {} carrier PHIs",
|
"[joinir/exit-line] orchestrator start: {} carrier PHIs",
|
||||||
carrier_phis.len()
|
carrier_phis.len()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -94,8 +95,8 @@ impl ExitLineOrchestrator {
|
|||||||
// Phase 33-13: Delegate to ExitLineReconnector with carrier_phis
|
// Phase 33-13: Delegate to ExitLineReconnector with carrier_phis
|
||||||
ExitLineReconnector::reconnect(builder, boundary, carrier_phis, debug)?;
|
ExitLineReconnector::reconnect(builder, boundary, carrier_phis, debug)?;
|
||||||
|
|
||||||
if debug {
|
if verbose {
|
||||||
eprintln!("[cf_loop/joinir/exit_line] ExitLineOrchestrator: Phase 6 complete");
|
eprintln!("[joinir/exit-line] orchestrator complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -79,26 +79,39 @@ impl ExitLineReconnector {
|
|||||||
carrier_phis: &BTreeMap<String, ValueId>,
|
carrier_phis: &BTreeMap<String, ValueId>,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let strict = crate::config::env::joinir_strict_enabled();
|
let dev_on = crate::config::env::joinir_dev_enabled();
|
||||||
|
let strict = crate::config::env::joinir_strict_enabled() || dev_on;
|
||||||
|
let verbose = debug || dev_on;
|
||||||
|
|
||||||
// Phase 177-STRUCT: Always log for debugging
|
if verbose {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[DEBUG-177/reconnect] ExitLineReconnector: {} exit bindings, {} carrier PHIs",
|
"[joinir/exit-line] reconnect: {} exit bindings, {} carrier PHIs",
|
||||||
boundary.exit_bindings.len(),
|
boundary.exit_bindings.len(),
|
||||||
carrier_phis.len()
|
carrier_phis.len()
|
||||||
);
|
);
|
||||||
|
if !boundary.exit_bindings.is_empty() {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/exit-line] bindings {:?}",
|
||||||
|
boundary
|
||||||
|
.exit_bindings
|
||||||
|
.iter()
|
||||||
|
.map(|b| (&b.carrier_name, b.role, b.join_exit_value))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Early return for empty exit_bindings
|
// Early return for empty exit_bindings
|
||||||
if boundary.exit_bindings.is_empty() {
|
if boundary.exit_bindings.is_empty() {
|
||||||
if debug {
|
if verbose {
|
||||||
eprintln!("[cf_loop/joinir/exit_line] ExitLineReconnector: No exit bindings, skipping reconnect");
|
eprintln!("[joinir/exit-line] reconnect: no exit bindings, skip");
|
||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if debug {
|
if verbose {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir/exit_line] ExitLineReconnector: Reconnecting {} exit bindings with {} carrier PHIs",
|
"[joinir/exit-line] reconnecting {} exit bindings with {} carrier PHIs",
|
||||||
boundary.exit_bindings.len(),
|
boundary.exit_bindings.len(),
|
||||||
carrier_phis.len()
|
carrier_phis.len()
|
||||||
);
|
);
|
||||||
@ -109,9 +122,9 @@ impl ExitLineReconnector {
|
|||||||
// Phase 228-8: Skip ConditionOnly carriers (no variable_map update needed)
|
// Phase 228-8: Skip ConditionOnly carriers (no variable_map update needed)
|
||||||
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
||||||
if binding.role == CarrierRole::ConditionOnly {
|
if binding.role == CarrierRole::ConditionOnly {
|
||||||
if debug {
|
if verbose {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir/exit_line] Phase 228-8: Skipping ConditionOnly carrier '{}' (no variable_map update)",
|
"[joinir/exit-line] skip ConditionOnly carrier '{}' (no variable_map update)",
|
||||||
binding.carrier_name
|
binding.carrier_name
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -121,9 +134,9 @@ impl ExitLineReconnector {
|
|||||||
// Phase 33-13: Look up the PHI dst for this carrier
|
// Phase 33-13: Look up the PHI dst for this carrier
|
||||||
let phi_dst = carrier_phis.get(&binding.carrier_name);
|
let phi_dst = carrier_phis.get(&binding.carrier_name);
|
||||||
|
|
||||||
if debug {
|
if verbose {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir/exit_line] ExitLineReconnector: Carrier '{}' → phi_dst={:?}",
|
"[joinir/exit-line] carrier '{}' → phi_dst={:?}",
|
||||||
binding.carrier_name, phi_dst
|
binding.carrier_name, phi_dst
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -132,33 +145,34 @@ impl ExitLineReconnector {
|
|||||||
if let Some(&phi_value) = phi_dst {
|
if let Some(&phi_value) = phi_dst {
|
||||||
if let Some(var_vid) = builder.variable_map.get_mut(&binding.carrier_name) {
|
if let Some(var_vid) = builder.variable_map.get_mut(&binding.carrier_name) {
|
||||||
// Phase 177-STRUCT: Always log for debugging
|
// Phase 177-STRUCT: Always log for debugging
|
||||||
eprintln!(
|
if verbose {
|
||||||
"[DEBUG-177/reconnect] Updated variable_map['{}'] {:?} → {:?}",
|
eprintln!(
|
||||||
binding.carrier_name, *var_vid, phi_value
|
"[joinir/exit-line] variable_map['{}'] {:?} → {:?}",
|
||||||
);
|
binding.carrier_name, *var_vid, phi_value
|
||||||
|
);
|
||||||
|
}
|
||||||
*var_vid = phi_value;
|
*var_vid = phi_value;
|
||||||
} else if debug {
|
} else if verbose {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir/exit_line] ExitLineReconnector WARNING: Carrier '{}' not found in variable_map",
|
"[joinir/exit-line] warning: carrier '{}' not found in variable_map",
|
||||||
binding.carrier_name
|
binding.carrier_name
|
||||||
);
|
);
|
||||||
} else if strict {
|
} else if strict {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"[pattern2/exit_line] Missing variable_map entry for carrier '{}' (exit reconnection)",
|
"[joinir/exit-line] missing variable_map entry for carrier '{}' (exit reconnection)",
|
||||||
binding.carrier_name
|
binding.carrier_name
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if strict && binding.role != CarrierRole::ConditionOnly {
|
if strict && binding.role != CarrierRole::ConditionOnly {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"[pattern2/exit_line] Missing PHI dst for carrier '{}' ({} PHIs available)",
|
"[joinir/exit-line] missing PHI dst for carrier '{}' ({} PHIs available)",
|
||||||
binding.carrier_name,
|
binding.carrier_name,
|
||||||
carrier_phis.len()
|
carrier_phis.len()
|
||||||
));
|
));
|
||||||
}
|
} else if verbose {
|
||||||
if debug {
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir/exit_line] ExitLineReconnector WARNING: No PHI dst for carrier '{}' (may be condition-only variable)",
|
"[joinir/exit-line] warning: No PHI dst for carrier '{}' (may be condition-only variable)",
|
||||||
binding.carrier_name
|
binding.carrier_name
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -169,7 +183,7 @@ impl ExitLineReconnector {
|
|||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
if !boundary.host_outputs.is_empty() && debug {
|
if !boundary.host_outputs.is_empty() && debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir/exit_line] WARNING: Using deprecated host_outputs. Migrate to exit_bindings."
|
"[joinir/exit-line] WARNING: Using deprecated host_outputs. Migrate to exit_bindings."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -85,7 +85,8 @@ pub(super) fn build_exit_phi(
|
|||||||
if debug {
|
if debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir] Created exit block: {:?} with {} carrier PHIs",
|
"[cf_loop/joinir] Created exit block: {:?} with {} carrier PHIs",
|
||||||
exit_block_id, carrier_phis.len()
|
exit_block_id,
|
||||||
|
carrier_phis.len()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
phi_result
|
phi_result
|
||||||
|
|||||||
@ -12,10 +12,10 @@
|
|||||||
//! 4. 非対応の場合 → remapper.get_value(expr_result) を返す
|
//! 4. 非対応の場合 → remapper.get_value(expr_result) を返す
|
||||||
//! 5. expr_result が None → None を返す
|
//! 5. expr_result が None → None を返す
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use crate::mir::ValueId;
|
|
||||||
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
|
|
||||||
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
|
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
|
||||||
|
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
|
||||||
|
use crate::mir::ValueId;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
pub struct ExprResultResolver;
|
pub struct ExprResultResolver;
|
||||||
|
|
||||||
@ -60,7 +60,10 @@ impl ExprResultResolver {
|
|||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir] Phase 221: Resolving expr_result {:?}, exit_bindings={:?}",
|
"[cf_loop/joinir] Phase 221: Resolving expr_result {:?}, exit_bindings={:?}",
|
||||||
expr_result_id,
|
expr_result_id,
|
||||||
exit_bindings.iter().map(|b| (b.carrier_name.as_str(), b.join_exit_value)).collect::<Vec<_>>()
|
exit_bindings
|
||||||
|
.iter()
|
||||||
|
.map(|b| (b.carrier_name.as_str(), b.join_exit_value))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,13 +117,7 @@ mod tests {
|
|||||||
let carrier_phis = BTreeMap::new();
|
let carrier_phis = BTreeMap::new();
|
||||||
let remapper = JoinIrIdRemapper::new();
|
let remapper = JoinIrIdRemapper::new();
|
||||||
|
|
||||||
let result = ExprResultResolver::resolve(
|
let result = ExprResultResolver::resolve(None, &[], &carrier_phis, &remapper, false);
|
||||||
None,
|
|
||||||
&[],
|
|
||||||
&carrier_phis,
|
|
||||||
&remapper,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(result.unwrap(), None);
|
assert_eq!(result.unwrap(), None);
|
||||||
}
|
}
|
||||||
@ -156,13 +153,8 @@ mod tests {
|
|||||||
let mut remapper = JoinIrIdRemapper::new();
|
let mut remapper = JoinIrIdRemapper::new();
|
||||||
remapper.set_value(ValueId(42), ValueId(200));
|
remapper.set_value(ValueId(42), ValueId(200));
|
||||||
|
|
||||||
let result = ExprResultResolver::resolve(
|
let result =
|
||||||
Some(ValueId(42)),
|
ExprResultResolver::resolve(Some(ValueId(42)), &[], &carrier_phis, &remapper, false);
|
||||||
&[],
|
|
||||||
&carrier_phis,
|
|
||||||
&remapper,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(result.unwrap(), Some(ValueId(200)));
|
assert_eq!(result.unwrap(), Some(ValueId(200)));
|
||||||
}
|
}
|
||||||
@ -172,13 +164,8 @@ mod tests {
|
|||||||
let carrier_phis = BTreeMap::new();
|
let carrier_phis = BTreeMap::new();
|
||||||
let remapper = JoinIrIdRemapper::new();
|
let remapper = JoinIrIdRemapper::new();
|
||||||
|
|
||||||
let result = ExprResultResolver::resolve(
|
let result =
|
||||||
Some(ValueId(999)),
|
ExprResultResolver::resolve(Some(ValueId(999)), &[], &carrier_phis, &remapper, false);
|
||||||
&[],
|
|
||||||
&carrier_phis,
|
|
||||||
&remapper,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(result.unwrap_err().contains("not found in remapper"));
|
assert!(result.unwrap_err().contains("not found in remapper"));
|
||||||
|
|||||||
@ -61,7 +61,8 @@ pub(super) fn merge_and_rewrite(
|
|||||||
let boundary_input_set: std::collections::HashSet<ValueId> = boundary
|
let boundary_input_set: std::collections::HashSet<ValueId> = boundary
|
||||||
.map(|b| b.join_inputs.iter().copied().collect())
|
.map(|b| b.join_inputs.iter().copied().collect())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let strict_exit = crate::config::env::joinir_strict_enabled();
|
let strict_exit =
|
||||||
|
crate::config::env::joinir_strict_enabled() || crate::config::env::joinir_dev_enabled();
|
||||||
|
|
||||||
// Phase 189-Fix: Collect return values from JoinIR functions for exit PHI
|
// Phase 189-Fix: Collect return values from JoinIR functions for exit PHI
|
||||||
let mut exit_phi_inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
let mut exit_phi_inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||||
@ -614,7 +615,7 @@ pub(super) fn merge_and_rewrite(
|
|||||||
let expected_args = carrier_info.carriers.len() + 1; // loop_var + carriers
|
let expected_args = carrier_info.carriers.len() + 1; // loop_var + carriers
|
||||||
if remapped_args.len() < expected_args {
|
if remapped_args.len() < expected_args {
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
"[pattern2/exit_line] jump_args length mismatch: expected at least {} (loop_var + carriers) but got {} in block {:?}",
|
"[joinir/exit-line] jump_args length mismatch: expected at least {} (loop_var + carriers) but got {} in block {:?}",
|
||||||
expected_args,
|
expected_args,
|
||||||
remapped_args.len(),
|
remapped_args.len(),
|
||||||
old_block.id
|
old_block.id
|
||||||
@ -673,7 +674,7 @@ pub(super) fn merge_and_rewrite(
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
"[pattern2/exit_line] Missing jump_args entry for carrier '{}' at index {} in block {:?}",
|
"[joinir/exit-line] Missing jump_args entry for carrier '{}' at index {} in block {:?}",
|
||||||
carrier.name, jump_args_idx, old_block.id
|
carrier.name, jump_args_idx, old_block.id
|
||||||
);
|
);
|
||||||
if strict_exit {
|
if strict_exit {
|
||||||
|
|||||||
@ -27,8 +27,8 @@
|
|||||||
//! Called from merge pipeline between Phase 3 (remap_values) and Phase 4
|
//! Called from merge pipeline between Phase 3 (remap_values) and Phase 4
|
||||||
//! (instruction_rewriter).
|
//! (instruction_rewriter).
|
||||||
|
|
||||||
use crate::mir::{MirInstruction, ValueId, BasicBlockId};
|
use super::loop_header_phi_info::{CarrierPhiEntry, LoopHeaderPhiInfo};
|
||||||
use super::loop_header_phi_info::{LoopHeaderPhiInfo, CarrierPhiEntry};
|
use crate::mir::{BasicBlockId, MirInstruction, ValueId};
|
||||||
|
|
||||||
/// Builder for loop header PHIs
|
/// Builder for loop header PHIs
|
||||||
///
|
///
|
||||||
@ -64,7 +64,12 @@ impl LoopHeaderPhiBuilder {
|
|||||||
entry_block: BasicBlockId,
|
entry_block: BasicBlockId,
|
||||||
loop_var_name: &str,
|
loop_var_name: &str,
|
||||||
loop_var_init: ValueId,
|
loop_var_init: ValueId,
|
||||||
carriers: &[(String, ValueId, crate::mir::join_ir::lowering::carrier_info::CarrierInit, crate::mir::join_ir::lowering::carrier_info::CarrierRole)], // Phase 228: Added CarrierInit and CarrierRole
|
carriers: &[(
|
||||||
|
String,
|
||||||
|
ValueId,
|
||||||
|
crate::mir::join_ir::lowering::carrier_info::CarrierInit,
|
||||||
|
crate::mir::join_ir::lowering::carrier_info::CarrierRole,
|
||||||
|
)], // Phase 228: Added CarrierInit and CarrierRole
|
||||||
expr_result_is_loop_var: bool,
|
expr_result_is_loop_var: bool,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
) -> Result<LoopHeaderPhiInfo, String> {
|
) -> Result<LoopHeaderPhiInfo, String> {
|
||||||
@ -188,6 +193,7 @@ impl LoopHeaderPhiBuilder {
|
|||||||
info: &LoopHeaderPhiInfo,
|
info: &LoopHeaderPhiInfo,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
|
let dev_debug = debug || crate::config::env::joinir_dev_enabled();
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir] Phase 33-16: Finalizing header PHIs at {:?}",
|
"[cf_loop/joinir] Phase 33-16: Finalizing header PHIs at {:?}",
|
||||||
@ -206,16 +212,20 @@ impl LoopHeaderPhiBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the header block from current function
|
// Get the header block from current function
|
||||||
let current_func = builder.current_function.as_mut().ok_or(
|
let current_func = builder
|
||||||
"Phase 33-16: No current function when finalizing header PHIs"
|
.current_function
|
||||||
)?;
|
.as_mut()
|
||||||
|
.ok_or("Phase 33-16: No current function when finalizing header PHIs")?;
|
||||||
|
|
||||||
let header_block = current_func.blocks.get_mut(&info.header_block).ok_or_else(|| {
|
let header_block = current_func
|
||||||
format!(
|
.blocks
|
||||||
"Phase 33-16: Header block {:?} not found in current function",
|
.get_mut(&info.header_block)
|
||||||
info.header_block
|
.ok_or_else(|| {
|
||||||
)
|
format!(
|
||||||
})?;
|
"Phase 33-16: Header block {:?} not found in current function",
|
||||||
|
info.header_block
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
// Insert PHIs at the beginning of the header block (before other instructions)
|
// Insert PHIs at the beginning of the header block (before other instructions)
|
||||||
// Sorted by carrier name for determinism
|
// Sorted by carrier name for determinism
|
||||||
@ -233,11 +243,11 @@ impl LoopHeaderPhiBuilder {
|
|||||||
|
|
||||||
phi_instructions.push(phi);
|
phi_instructions.push(phi);
|
||||||
|
|
||||||
if debug {
|
if dev_debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir] Finalized carrier '{}' PHI: {:?} = phi [({:?}, {:?}), ({:?}, {:?})]",
|
"[joinir/header-phi] Finalized carrier '{}' PHI: {:?} = phi [({:?}, {:?}), ({:?}, {:?})]",
|
||||||
name, entry.phi_dst, entry_block, entry_val, latch_block, latch_val
|
name, entry.phi_dst, entry_block, entry_val, latch_block, latch_val
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,9 +263,9 @@ impl LoopHeaderPhiBuilder {
|
|||||||
new_spans.append(&mut header_block.instruction_spans);
|
new_spans.append(&mut header_block.instruction_spans);
|
||||||
header_block.instruction_spans = new_spans;
|
header_block.instruction_spans = new_spans;
|
||||||
|
|
||||||
if debug {
|
if dev_debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir] Header block now has {} instructions",
|
"[joinir/header-phi] Header block now has {} instructions",
|
||||||
header_block.instructions.len()
|
header_block.instructions.len()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,8 +10,8 @@
|
|||||||
//! - exit_phi_builder: to reference current loop values
|
//! - exit_phi_builder: to reference current loop values
|
||||||
//! - ExitLineReconnector: to update variable_map with final values
|
//! - ExitLineReconnector: to update variable_map with final values
|
||||||
|
|
||||||
use crate::mir::{BasicBlockId, ValueId};
|
|
||||||
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
||||||
|
use crate::mir::{BasicBlockId, ValueId};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
/// Information about loop header PHIs
|
/// Information about loop header PHIs
|
||||||
@ -104,7 +104,9 @@ impl LoopHeaderPhiInfo {
|
|||||||
/// Check if all carriers have latch incoming set
|
/// Check if all carriers have latch incoming set
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn all_latch_set(&self) -> bool {
|
pub fn all_latch_set(&self) -> bool {
|
||||||
self.carrier_phis.values().all(|e| e.latch_incoming.is_some())
|
self.carrier_phis
|
||||||
|
.values()
|
||||||
|
.all(|e| e.latch_incoming.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Phase 201-A: Get reserved ValueIds (PHI dsts that must not be overwritten)
|
/// Phase 201-A: Get reserved ValueIds (PHI dsts that must not be overwritten)
|
||||||
|
|||||||
@ -38,7 +38,12 @@ impl MergeResult {
|
|||||||
|
|
||||||
/// Add a carrier input
|
/// Add a carrier input
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn add_carrier_input(&mut self, carrier_name: String, from_block: BasicBlockId, value: ValueId) {
|
pub fn add_carrier_input(
|
||||||
|
&mut self,
|
||||||
|
carrier_name: String,
|
||||||
|
from_block: BasicBlockId,
|
||||||
|
value: ValueId,
|
||||||
|
) {
|
||||||
self.carrier_inputs
|
self.carrier_inputs
|
||||||
.entry(carrier_name)
|
.entry(carrier_name)
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
|
|||||||
@ -13,24 +13,24 @@
|
|||||||
//! Phase 4 Refactoring: Breaking down 714-line merge_joinir_mir_blocks() into focused modules
|
//! Phase 4 Refactoring: Breaking down 714-line merge_joinir_mir_blocks() into focused modules
|
||||||
|
|
||||||
mod block_allocator;
|
mod block_allocator;
|
||||||
mod value_collector;
|
|
||||||
mod instruction_rewriter;
|
|
||||||
mod exit_phi_builder;
|
|
||||||
pub mod exit_line;
|
|
||||||
mod loop_header_phi_info;
|
|
||||||
mod loop_header_phi_builder;
|
|
||||||
mod tail_call_classifier;
|
|
||||||
mod merge_result;
|
|
||||||
mod expr_result_resolver;
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
mod contract_checks;
|
mod contract_checks;
|
||||||
|
pub mod exit_line;
|
||||||
|
mod exit_phi_builder;
|
||||||
|
mod expr_result_resolver;
|
||||||
|
mod instruction_rewriter;
|
||||||
|
mod loop_header_phi_builder;
|
||||||
|
mod loop_header_phi_info;
|
||||||
|
mod merge_result;
|
||||||
|
mod tail_call_classifier;
|
||||||
|
mod value_collector;
|
||||||
|
|
||||||
// Phase 33-17: Re-export for use by other modules
|
// Phase 33-17: Re-export for use by other modules
|
||||||
pub use loop_header_phi_info::LoopHeaderPhiInfo;
|
|
||||||
pub use loop_header_phi_builder::LoopHeaderPhiBuilder;
|
pub use loop_header_phi_builder::LoopHeaderPhiBuilder;
|
||||||
|
pub use loop_header_phi_info::LoopHeaderPhiInfo;
|
||||||
|
|
||||||
use crate::mir::{MirModule, ValueId};
|
|
||||||
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
||||||
|
use crate::mir::{MirModule, ValueId};
|
||||||
|
|
||||||
/// Phase 49-3.2: Merge JoinIR-generated MIR blocks into current_function
|
/// Phase 49-3.2: Merge JoinIR-generated MIR blocks into current_function
|
||||||
///
|
///
|
||||||
@ -100,7 +100,12 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
let cond_summary: Vec<String> = boundary
|
let cond_summary: Vec<String> = boundary
|
||||||
.condition_bindings
|
.condition_bindings
|
||||||
.iter()
|
.iter()
|
||||||
.map(|b| format!("{}: host {:?} → join {:?}", b.name, b.host_value, b.join_value))
|
.map(|b| {
|
||||||
|
format!(
|
||||||
|
"{}: host {:?} → join {:?}",
|
||||||
|
b.name, b.host_value, b.join_value
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@ -123,8 +128,7 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
let carriers: Vec<String> = ci.carriers.iter().map(|c| c.name.clone()).collect();
|
let carriers: Vec<String> = ci.carriers.iter().map(|c| c.name.clone()).collect();
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir] Boundary carrier_info: loop_var='{}', carriers={:?}",
|
"[cf_loop/joinir] Boundary carrier_info: loop_var='{}', carriers={:?}",
|
||||||
ci.loop_var_name,
|
ci.loop_var_name, carriers
|
||||||
carriers
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -134,7 +138,8 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
|
|
||||||
// Phase 1: Allocate block IDs for all functions
|
// Phase 1: Allocate block IDs for all functions
|
||||||
// Phase 177-3: block_allocator now returns exit_block_id to avoid conflicts
|
// Phase 177-3: block_allocator now returns exit_block_id to avoid conflicts
|
||||||
let (mut remapper, exit_block_id) = block_allocator::allocate_blocks(builder, mir_module, debug)?;
|
let (mut remapper, exit_block_id) =
|
||||||
|
block_allocator::allocate_blocks(builder, mir_module, debug)?;
|
||||||
|
|
||||||
// Phase 2: Collect values from all functions
|
// Phase 2: Collect values from all functions
|
||||||
let (mut used_values, value_to_func_name, function_params) =
|
let (mut used_values, value_to_func_name, function_params) =
|
||||||
@ -181,35 +186,55 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
.ok_or("JoinIR module has no functions (Phase 201-A)")?;
|
.ok_or("JoinIR module has no functions (Phase 201-A)")?;
|
||||||
let entry_block_remapped = remapper
|
let entry_block_remapped = remapper
|
||||||
.get_block(entry_func_name, entry_func.entry_block)
|
.get_block(entry_func_name, entry_func.entry_block)
|
||||||
.ok_or_else(|| format!("Entry block not found for {} (Phase 201-A)", entry_func_name))?;
|
.ok_or_else(|| {
|
||||||
|
format!(
|
||||||
|
"Entry block not found for {} (Phase 201-A)",
|
||||||
|
entry_func_name
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
// Get host's current block as the entry edge
|
// Get host's current block as the entry edge
|
||||||
let host_entry_block = builder.current_block.ok_or(
|
let host_entry_block = builder
|
||||||
"Phase 201-A: No current block when building header PHIs"
|
.current_block
|
||||||
)?;
|
.ok_or("Phase 201-A: No current block when building header PHIs")?;
|
||||||
|
|
||||||
// Get loop variable's initial value from HOST
|
// Get loop variable's initial value from HOST
|
||||||
let loop_var_init = boundary.host_inputs.first().copied().ok_or(
|
let loop_var_init = boundary
|
||||||
"Phase 201-A: No host_inputs in boundary for loop_var_init"
|
.host_inputs
|
||||||
)?;
|
.first()
|
||||||
|
.copied()
|
||||||
|
.ok_or("Phase 201-A: No host_inputs in boundary for loop_var_init")?;
|
||||||
|
|
||||||
// Phase 228-4: Extract carriers with their initialization strategy
|
// Phase 228-4: Extract carriers with their initialization strategy
|
||||||
let other_carriers: Vec<(String, ValueId, crate::mir::join_ir::lowering::carrier_info::CarrierInit, crate::mir::join_ir::lowering::carrier_info::CarrierRole)> =
|
let other_carriers: Vec<(
|
||||||
if let Some(ref carrier_info) = boundary.carrier_info {
|
String,
|
||||||
// Use carrier_info if available (Phase 228)
|
ValueId,
|
||||||
carrier_info.carriers
|
crate::mir::join_ir::lowering::carrier_info::CarrierInit,
|
||||||
.iter()
|
crate::mir::join_ir::lowering::carrier_info::CarrierRole,
|
||||||
.filter(|c| c.name != *loop_var_name)
|
)> = if let Some(ref carrier_info) = boundary.carrier_info {
|
||||||
.map(|c| (c.name.clone(), c.host_id, c.init, c.role))
|
// Use carrier_info if available (Phase 228)
|
||||||
.collect()
|
carrier_info
|
||||||
} else {
|
.carriers
|
||||||
// Fallback: exit_bindings から取得(既存動作)
|
.iter()
|
||||||
boundary.exit_bindings
|
.filter(|c| c.name != *loop_var_name)
|
||||||
.iter()
|
.map(|c| (c.name.clone(), c.host_id, c.init, c.role))
|
||||||
.filter(|b| b.carrier_name != *loop_var_name)
|
.collect()
|
||||||
.map(|b| (b.carrier_name.clone(), b.host_slot, crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState))
|
} else {
|
||||||
.collect()
|
// Fallback: exit_bindings から取得(既存動作)
|
||||||
};
|
boundary
|
||||||
|
.exit_bindings
|
||||||
|
.iter()
|
||||||
|
.filter(|b| b.carrier_name != *loop_var_name)
|
||||||
|
.map(|b| {
|
||||||
|
(
|
||||||
|
b.carrier_name.clone(),
|
||||||
|
b.host_slot,
|
||||||
|
crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost,
|
||||||
|
crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@ -219,7 +244,10 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir] loop_var_init={:?}, carriers={:?}",
|
"[cf_loop/joinir] loop_var_init={:?}, carriers={:?}",
|
||||||
loop_var_init,
|
loop_var_init,
|
||||||
other_carriers.iter().map(|(n, _, _, _)| n.as_str()).collect::<Vec<_>>()
|
other_carriers
|
||||||
|
.iter()
|
||||||
|
.map(|(n, _, _, _)| n.as_str())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,33 +263,53 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
debug,
|
debug,
|
||||||
)?
|
)?
|
||||||
} else {
|
} else {
|
||||||
LoopHeaderPhiInfo::empty(remapper.get_block(
|
LoopHeaderPhiInfo::empty(
|
||||||
mir_module.functions.iter().next().unwrap().0,
|
remapper
|
||||||
mir_module.functions.iter().next().unwrap().1.entry_block
|
.get_block(
|
||||||
).unwrap())
|
mir_module.functions.iter().next().unwrap().0,
|
||||||
|
mir_module.functions.iter().next().unwrap().1.entry_block,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LoopHeaderPhiInfo::empty(remapper.get_block(
|
LoopHeaderPhiInfo::empty(
|
||||||
mir_module.functions.iter().next().unwrap().0,
|
remapper
|
||||||
mir_module.functions.iter().next().unwrap().1.entry_block
|
.get_block(
|
||||||
).unwrap())
|
mir_module.functions.iter().next().unwrap().0,
|
||||||
|
mir_module.functions.iter().next().unwrap().1.entry_block,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Phase 201-A: Get reserved PHI dst ValueIds and set in MirBuilder
|
// Phase 201-A: Get reserved PHI dst ValueIds and set in MirBuilder
|
||||||
let reserved_phi_dsts = loop_header_phi_info.reserved_value_ids();
|
let reserved_phi_dsts = loop_header_phi_info.reserved_value_ids();
|
||||||
if debug && !reserved_phi_dsts.is_empty() {
|
if debug && !reserved_phi_dsts.is_empty() {
|
||||||
eprintln!("[cf_loop/joinir] Phase 201-A: Reserved PHI dsts: {:?}", reserved_phi_dsts);
|
eprintln!(
|
||||||
|
"[cf_loop/joinir] Phase 201-A: Reserved PHI dsts: {:?}",
|
||||||
|
reserved_phi_dsts
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 201-A: Set reserved IDs in MirBuilder so next_value_id() skips them
|
// Phase 201-A: Set reserved IDs in MirBuilder so next_value_id() skips them
|
||||||
// This protects against carrier corruption when break conditions emit Const instructions
|
// This protects against carrier corruption when break conditions emit Const instructions
|
||||||
builder.reserved_value_ids = reserved_phi_dsts.clone();
|
builder.reserved_value_ids = reserved_phi_dsts.clone();
|
||||||
if debug && !builder.reserved_value_ids.is_empty() {
|
if debug && !builder.reserved_value_ids.is_empty() {
|
||||||
eprintln!("[cf_loop/joinir] Phase 201-A: Set builder.reserved_value_ids = {:?}", builder.reserved_value_ids);
|
eprintln!(
|
||||||
|
"[cf_loop/joinir] Phase 201-A: Set builder.reserved_value_ids = {:?}",
|
||||||
|
builder.reserved_value_ids
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 3: Remap ValueIds (with reserved PHI dsts protection)
|
// Phase 3: Remap ValueIds (with reserved PHI dsts protection)
|
||||||
remap_values(builder, &used_values, &mut remapper, &reserved_phi_dsts, debug)?;
|
remap_values(
|
||||||
|
builder,
|
||||||
|
&used_values,
|
||||||
|
&mut remapper,
|
||||||
|
&reserved_phi_dsts,
|
||||||
|
debug,
|
||||||
|
)?;
|
||||||
|
|
||||||
// Phase 177-3 DEBUG: Verify remapper state after Phase 3
|
// Phase 177-3 DEBUG: Verify remapper state after Phase 3
|
||||||
eprintln!("[DEBUG-177] === Remapper state after Phase 3 ===");
|
eprintln!("[DEBUG-177] === Remapper state after Phase 3 ===");
|
||||||
@ -372,12 +420,18 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
eprintln!(
|
eprintln!(
|
||||||
"[DEBUG-177] Phase 33-21: carrier_phis count: {}, names: {:?}",
|
"[DEBUG-177] Phase 33-21: carrier_phis count: {}, names: {:?}",
|
||||||
loop_header_phi_info.carrier_phis.len(),
|
loop_header_phi_info.carrier_phis.len(),
|
||||||
loop_header_phi_info.carrier_phis.iter().map(|(n, _)| n.as_str()).collect::<Vec<_>>()
|
loop_header_phi_info
|
||||||
|
.carrier_phis
|
||||||
|
.iter()
|
||||||
|
.map(|(n, _)| n.as_str())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
);
|
);
|
||||||
// Map main's parameters to header PHI dsts
|
// Map main's parameters to header PHI dsts
|
||||||
// main params: [i_init, carrier1_init, ...]
|
// main params: [i_init, carrier1_init, ...]
|
||||||
// carrier_phis: [("i", entry), ("sum", entry), ...]
|
// carrier_phis: [("i", entry), ("sum", entry), ...]
|
||||||
for (idx, (carrier_name, entry)) in loop_header_phi_info.carrier_phis.iter().enumerate() {
|
for (idx, (carrier_name, entry)) in
|
||||||
|
loop_header_phi_info.carrier_phis.iter().enumerate()
|
||||||
|
{
|
||||||
if let Some(&main_param) = main_params.get(idx) {
|
if let Some(&main_param) = main_params.get(idx) {
|
||||||
// Phase 177-3: Don't override condition_bindings
|
// Phase 177-3: Don't override condition_bindings
|
||||||
if condition_binding_ids.contains(&main_param) {
|
if condition_binding_ids.contains(&main_param) {
|
||||||
@ -401,7 +455,11 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
// They appear in condition_bindings (added by Phase 176-5) but need PHI remapping.
|
// They appear in condition_bindings (added by Phase 176-5) but need PHI remapping.
|
||||||
for (carrier_name, entry) in &loop_header_phi_info.carrier_phis {
|
for (carrier_name, entry) in &loop_header_phi_info.carrier_phis {
|
||||||
// Check if this carrier has a condition_binding
|
// Check if this carrier has a condition_binding
|
||||||
if let Some(binding) = boundary.condition_bindings.iter().find(|cb| cb.name == *carrier_name) {
|
if let Some(binding) = boundary
|
||||||
|
.condition_bindings
|
||||||
|
.iter()
|
||||||
|
.find(|cb| cb.name == *carrier_name)
|
||||||
|
{
|
||||||
// Skip if it's a true condition-only variable (already protected above)
|
// Skip if it's a true condition-only variable (already protected above)
|
||||||
if condition_binding_ids.contains(&binding.join_value) {
|
if condition_binding_ids.contains(&binding.join_value) {
|
||||||
continue;
|
continue;
|
||||||
@ -451,8 +509,11 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
// Find which carrier this param belongs to by matching join_value
|
// Find which carrier this param belongs to by matching join_value
|
||||||
// Check if this param was already handled by Phase 177-3-B
|
// Check if this param was already handled by Phase 177-3-B
|
||||||
let already_mapped = boundary.condition_bindings.iter().any(|cb| {
|
let already_mapped = boundary.condition_bindings.iter().any(|cb| {
|
||||||
cb.join_value == *loop_step_param &&
|
cb.join_value == *loop_step_param
|
||||||
loop_header_phi_info.carrier_phis.iter().any(|(name, _)| name == &cb.name)
|
&& loop_header_phi_info
|
||||||
|
.carrier_phis
|
||||||
|
.iter()
|
||||||
|
.any(|(name, _)| name == &cb.name)
|
||||||
});
|
});
|
||||||
if already_mapped {
|
if already_mapped {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@ -467,7 +528,9 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
// generates params in exit_bindings order.
|
// generates params in exit_bindings order.
|
||||||
//
|
//
|
||||||
// Solution: Use carrier_order (Vec<String>) which preserves insertion order.
|
// Solution: Use carrier_order (Vec<String>) which preserves insertion order.
|
||||||
if let Some(param_idx) = loop_step_params.iter().position(|p| p == loop_step_param) {
|
if let Some(param_idx) =
|
||||||
|
loop_step_params.iter().position(|p| p == loop_step_param)
|
||||||
|
{
|
||||||
// Map params[i] to carrier_order[i]
|
// Map params[i] to carrier_order[i]
|
||||||
if let (Some(carrier_name), Some(entry)) = (
|
if let (Some(carrier_name), Some(entry)) = (
|
||||||
loop_header_phi_info.get_carrier_at_index(param_idx),
|
loop_header_phi_info.get_carrier_at_index(param_idx),
|
||||||
@ -483,7 +546,9 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if function_params.get(main_func_name).is_none() && function_params.get(loop_step_func_name).is_none() {
|
if function_params.get(main_func_name).is_none()
|
||||||
|
&& function_params.get(loop_step_func_name).is_none()
|
||||||
|
{
|
||||||
// Fallback: Use old behavior (ValueId(0), ValueId(1), ...)
|
// Fallback: Use old behavior (ValueId(0), ValueId(1), ...)
|
||||||
// This handles patterns that don't have loop_step function
|
// This handles patterns that don't have loop_step function
|
||||||
if let Some(phi_dst) = loop_header_phi_info.get_carrier_phi(loop_var_name) {
|
if let Some(phi_dst) = loop_header_phi_info.get_carrier_phi(loop_var_name) {
|
||||||
@ -570,11 +635,7 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
loop_header_phi_info.carrier_phis.len()
|
loop_header_phi_info.carrier_phis.len()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
LoopHeaderPhiBuilder::finalize(
|
LoopHeaderPhiBuilder::finalize(builder, &loop_header_phi_info, debug)?;
|
||||||
builder,
|
|
||||||
&loop_header_phi_info,
|
|
||||||
debug,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 5: Build exit PHI (expr result only, not carrier PHIs)
|
// Phase 5: Build exit PHI (expr result only, not carrier PHIs)
|
||||||
@ -633,12 +694,18 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
let entry_block = loop_header_phi_info.header_block;
|
let entry_block = loop_header_phi_info.header_block;
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!("[cf_loop/joinir] Entry block (from loop_header_phi_info): {:?}", entry_block);
|
eprintln!(
|
||||||
|
"[cf_loop/joinir] Entry block (from loop_header_phi_info): {:?}",
|
||||||
|
entry_block
|
||||||
|
);
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[cf_loop/joinir] Current block before emit_jump: {:?}",
|
"[cf_loop/joinir] Current block before emit_jump: {:?}",
|
||||||
builder.current_block
|
builder.current_block
|
||||||
);
|
);
|
||||||
eprintln!("[cf_loop/joinir] Jumping to entry block: {:?}", entry_block);
|
eprintln!(
|
||||||
|
"[cf_loop/joinir] Jumping to entry block: {:?}",
|
||||||
|
entry_block
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::mir::builder::emission::branch::emit_jump(builder, entry_block)?;
|
crate::mir::builder::emission::branch::emit_jump(builder, entry_block)?;
|
||||||
@ -684,7 +751,10 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
// Future loops will set their own reserved IDs
|
// Future loops will set their own reserved IDs
|
||||||
if !builder.reserved_value_ids.is_empty() {
|
if !builder.reserved_value_ids.is_empty() {
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!("[cf_loop/joinir] Phase 201-A: Clearing reserved_value_ids (was {:?})", builder.reserved_value_ids);
|
eprintln!(
|
||||||
|
"[cf_loop/joinir] Phase 201-A: Clearing reserved_value_ids (was {:?})",
|
||||||
|
builder.reserved_value_ids
|
||||||
|
);
|
||||||
}
|
}
|
||||||
builder.reserved_value_ids.clear();
|
builder.reserved_value_ids.clear();
|
||||||
}
|
}
|
||||||
@ -698,7 +768,9 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
// Check if expr_result is the loop variable
|
// Check if expr_result is the loop variable
|
||||||
if let Some(loop_var_name) = &b.loop_var_name {
|
if let Some(loop_var_name) = &b.loop_var_name {
|
||||||
// Find the exit binding for the loop variable
|
// Find the exit binding for the loop variable
|
||||||
let loop_var_binding = b.exit_bindings.iter()
|
let loop_var_binding = b
|
||||||
|
.exit_bindings
|
||||||
|
.iter()
|
||||||
.find(|binding| binding.carrier_name == *loop_var_name);
|
.find(|binding| binding.carrier_name == *loop_var_name);
|
||||||
|
|
||||||
if let Some(binding) = loop_var_binding {
|
if let Some(binding) = loop_var_binding {
|
||||||
@ -751,7 +823,10 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
|||||||
// Return expr_result if present, otherwise fall back to exit_phi_result_id
|
// Return expr_result if present, otherwise fall back to exit_phi_result_id
|
||||||
if let Some(resolved) = expr_result_value {
|
if let Some(resolved) = expr_result_value {
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!("[cf_loop/joinir] Phase 246-EX-FIX: Returning expr_result_value {:?}", resolved);
|
eprintln!(
|
||||||
|
"[cf_loop/joinir] Phase 246-EX-FIX: Returning expr_result_value {:?}",
|
||||||
|
resolved
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(Some(resolved))
|
Ok(Some(resolved))
|
||||||
} else {
|
} else {
|
||||||
@ -779,8 +854,11 @@ fn remap_values(
|
|||||||
debug: bool,
|
debug: bool,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!("[cf_loop/joinir] Phase 3: Remapping {} ValueIds (reserved: {})",
|
eprintln!(
|
||||||
used_values.len(), reserved_ids.len());
|
"[cf_loop/joinir] Phase 3: Remapping {} ValueIds (reserved: {})",
|
||||||
|
used_values.len(),
|
||||||
|
reserved_ids.len()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for old_value in used_values {
|
for old_value in used_values {
|
||||||
@ -792,7 +870,10 @@ fn remap_values(
|
|||||||
}
|
}
|
||||||
// Skip reserved ID - will try next one
|
// Skip reserved ID - will try next one
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!("[cf_loop/joinir] Phase 201-A: Skipping reserved PHI dst {:?}", candidate);
|
eprintln!(
|
||||||
|
"[cf_loop/joinir] Phase 201-A: Skipping reserved PHI dst {:?}",
|
||||||
|
candidate
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -943,7 +1024,12 @@ fn verify_phi_inputs_defined(
|
|||||||
});
|
});
|
||||||
|
|
||||||
for instr in &header_block_data.instructions {
|
for instr in &header_block_data.instructions {
|
||||||
if let crate::mir::MirInstruction::Phi { dst, inputs, type_hint: _ } = instr {
|
if let crate::mir::MirInstruction::Phi {
|
||||||
|
dst,
|
||||||
|
inputs,
|
||||||
|
type_hint: _,
|
||||||
|
} = instr
|
||||||
|
{
|
||||||
for (value_id, pred_block) in inputs {
|
for (value_id, pred_block) in inputs {
|
||||||
// Conservative sanity check: ValueId should not be suspiciously large
|
// Conservative sanity check: ValueId should not be suspiciously large
|
||||||
// Phase 201 JoinValueSpace uses regions:
|
// Phase 201 JoinValueSpace uses regions:
|
||||||
|
|||||||
@ -68,9 +68,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_classify_loop_entry() {
|
fn test_classify_loop_entry() {
|
||||||
let result = classify_tail_call(
|
let result = classify_tail_call(
|
||||||
true, // is_entry_func_entry_block
|
true, // is_entry_func_entry_block
|
||||||
true, // has_loop_header_phis
|
true, // has_loop_header_phis
|
||||||
true, // has_boundary
|
true, // has_boundary
|
||||||
);
|
);
|
||||||
assert_eq!(result, TailCallKind::LoopEntry);
|
assert_eq!(result, TailCallKind::LoopEntry);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,10 +5,10 @@
|
|||||||
//!
|
//!
|
||||||
//! Phase 4 Extraction: Separated from merge_joinir_mir_blocks (lines 202-246)
|
//! Phase 4 Extraction: Separated from merge_joinir_mir_blocks (lines 202-246)
|
||||||
|
|
||||||
use crate::mir::{MirInstruction, MirModule, ValueId};
|
|
||||||
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
|
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
|
||||||
use std::collections::BTreeSet; // Phase 222.5-E: HashMap → BTreeMap for determinism
|
use crate::mir::{MirInstruction, MirModule, ValueId};
|
||||||
use std::collections::BTreeMap; // Phase 222.5-E: HashMap → BTreeMap for determinism
|
use std::collections::BTreeMap;
|
||||||
|
use std::collections::BTreeSet; // Phase 222.5-E: HashMap → BTreeMap for determinism // Phase 222.5-E: HashMap → BTreeMap for determinism
|
||||||
|
|
||||||
/// Phase 2: Collect all ValueIds used across ALL functions (Phase 189)
|
/// Phase 2: Collect all ValueIds used across ALL functions (Phase 189)
|
||||||
///
|
///
|
||||||
@ -22,15 +22,13 @@ pub(super) fn collect_values(
|
|||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
BTreeSet<ValueId>,
|
BTreeSet<ValueId>,
|
||||||
BTreeMap<ValueId, String>, // Phase 222.5-E: HashMap → BTreeMap for determinism
|
BTreeMap<ValueId, String>, // Phase 222.5-E: HashMap → BTreeMap for determinism
|
||||||
BTreeMap<String, Vec<ValueId>>, // Phase 222.5-E: HashMap → BTreeMap for determinism
|
BTreeMap<String, Vec<ValueId>>, // Phase 222.5-E: HashMap → BTreeMap for determinism
|
||||||
),
|
),
|
||||||
String,
|
String,
|
||||||
> {
|
> {
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!(
|
eprintln!("[cf_loop/joinir] Phase 189: Collecting value IDs from all functions");
|
||||||
"[cf_loop/joinir] Phase 189: Collecting value IDs from all functions"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut used_values: BTreeSet<ValueId> = BTreeSet::new();
|
let mut used_values: BTreeSet<ValueId> = BTreeSet::new();
|
||||||
|
|||||||
@ -6,8 +6,8 @@
|
|||||||
//! - MIR block merging (merge/) ✅ Phase 4
|
//! - MIR block merging (merge/) ✅ Phase 4
|
||||||
//! - Unified tracing (trace.rs) ✅ Phase 195
|
//! - Unified tracing (trace.rs) ✅ Phase 195
|
||||||
|
|
||||||
|
pub(in crate::mir::builder) mod merge;
|
||||||
pub(in crate::mir::builder) mod patterns;
|
pub(in crate::mir::builder) mod patterns;
|
||||||
pub(in crate::mir::builder) mod routing;
|
pub(in crate::mir::builder) mod routing;
|
||||||
pub(in crate::mir::builder) mod routing_legacy_binding;
|
pub(in crate::mir::builder) mod routing_legacy_binding;
|
||||||
pub(in crate::mir::builder) mod merge;
|
|
||||||
pub(in crate::mir::builder) mod trace;
|
pub(in crate::mir::builder) mod trace;
|
||||||
|
|||||||
@ -27,13 +27,13 @@
|
|||||||
//! - **Testability**: Can test conversion independently
|
//! - **Testability**: Can test conversion independently
|
||||||
//! - **Reduces duplication**: Eliminates 120 lines across Pattern 1-4
|
//! - **Reduces duplication**: Eliminates 120 lines across Pattern 1-4
|
||||||
|
|
||||||
use crate::mir::ValueId;
|
|
||||||
use crate::mir::builder::MirBuilder;
|
use crate::mir::builder::MirBuilder;
|
||||||
use crate::mir::join_ir::JoinModule;
|
|
||||||
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
||||||
|
use crate::mir::join_ir::JoinModule;
|
||||||
|
use crate::mir::ValueId;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
pub struct JoinIRConversionPipeline;
|
pub(crate) struct JoinIRConversionPipeline;
|
||||||
|
|
||||||
impl JoinIRConversionPipeline {
|
impl JoinIRConversionPipeline {
|
||||||
/// Execute unified conversion pipeline
|
/// Execute unified conversion pipeline
|
||||||
@ -82,41 +82,28 @@ impl JoinIRConversionPipeline {
|
|||||||
pattern_name: &str,
|
pattern_name: &str,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
) -> Result<Option<ValueId>, String> {
|
) -> Result<Option<ValueId>, String> {
|
||||||
use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta;
|
|
||||||
use crate::mir::join_ir::frontend::JoinFuncMetaMap;
|
|
||||||
use super::super::trace;
|
use super::super::trace;
|
||||||
|
use crate::mir::join_ir::frontend::JoinFuncMetaMap;
|
||||||
|
use crate::mir::join_ir_vm_bridge::bridge_joinir_to_mir_with_meta;
|
||||||
|
|
||||||
// Step 1: Log JoinIR stats (functions and blocks)
|
// Step 1: Log JoinIR stats (functions and blocks)
|
||||||
trace::trace().joinir_stats(
|
trace::trace().joinir_stats(
|
||||||
pattern_name,
|
pattern_name,
|
||||||
join_module.functions.len(),
|
join_module.functions.len(),
|
||||||
join_module
|
join_module.functions.values().map(|f| f.body.len()).sum(),
|
||||||
.functions
|
|
||||||
.values()
|
|
||||||
.map(|f| f.body.len())
|
|
||||||
.sum(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 2: JoinModule → MirModule conversion
|
// Step 2: JoinModule → MirModule conversion
|
||||||
// Pass empty meta map since minimal lowerers don't use metadata
|
// Pass empty meta map since minimal lowerers don't use metadata
|
||||||
let empty_meta: JoinFuncMetaMap = BTreeMap::new();
|
let empty_meta: JoinFuncMetaMap = BTreeMap::new();
|
||||||
let mir_module = convert_join_module_to_mir_with_meta(&join_module, &empty_meta)
|
let mir_module = bridge_joinir_to_mir_with_meta(&join_module, &empty_meta)
|
||||||
.map_err(|e| {
|
.map_err(|e| format!("[{}/pipeline] MIR conversion failed: {:?}", pattern_name, e))?;
|
||||||
format!(
|
|
||||||
"[{}/pipeline] MIR conversion failed: {:?}",
|
|
||||||
pattern_name, e
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Step 3: Log MIR stats (functions and blocks)
|
// Step 3: Log MIR stats (functions and blocks)
|
||||||
trace::trace().joinir_stats(
|
trace::trace().joinir_stats(
|
||||||
pattern_name,
|
pattern_name,
|
||||||
mir_module.functions.len(),
|
mir_module.functions.len(),
|
||||||
mir_module
|
mir_module.functions.values().map(|f| f.blocks.len()).sum(),
|
||||||
.functions
|
|
||||||
.values()
|
|
||||||
.map(|f| f.blocks.len())
|
|
||||||
.sum(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 4: Merge into current function
|
// Step 4: Merge into current function
|
||||||
|
|||||||
@ -10,17 +10,17 @@
|
|||||||
//!
|
//!
|
||||||
//! This orchestrator coordinates the three modules for a complete workflow.
|
//! This orchestrator coordinates the three modules for a complete workflow.
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
// Phase 222.5-C: Import modular components
|
// Phase 222.5-C: Import modular components
|
||||||
use super::exit_binding_validator::validate_exit_binding;
|
use super::exit_binding_applicator::{
|
||||||
|
apply_exit_bindings_to_boundary, create_loop_var_exit_binding,
|
||||||
|
};
|
||||||
use super::exit_binding_constructor::build_loop_exit_bindings;
|
use super::exit_binding_constructor::build_loop_exit_bindings;
|
||||||
use super::exit_binding_applicator::{apply_exit_bindings_to_boundary, create_loop_var_exit_binding};
|
use super::exit_binding_validator::validate_exit_binding;
|
||||||
|
|
||||||
/// Builder for generating loop exit bindings
|
/// Builder for generating loop exit bindings
|
||||||
///
|
///
|
||||||
@ -29,7 +29,7 @@ use super::exit_binding_applicator::{apply_exit_bindings_to_boundary, create_loo
|
|||||||
///
|
///
|
||||||
/// Eliminates hardcoded variable names and ValueId plumbing scattered across lowerers.
|
/// Eliminates hardcoded variable names and ValueId plumbing scattered across lowerers.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct ExitBindingBuilder<'a> {
|
pub(crate) struct ExitBindingBuilder<'a> {
|
||||||
carrier_info: &'a CarrierInfo,
|
carrier_info: &'a CarrierInfo,
|
||||||
exit_meta: &'a ExitMeta,
|
exit_meta: &'a ExitMeta,
|
||||||
variable_map: &'a mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
variable_map: &'a mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||||
@ -60,7 +60,7 @@ impl<'a> ExitBindingBuilder<'a> {
|
|||||||
///
|
///
|
||||||
/// ExitBindingBuilder instance, or error if metadata is inconsistent
|
/// ExitBindingBuilder instance, or error if metadata is inconsistent
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn new(
|
pub(crate) fn new(
|
||||||
carrier_info: &'a CarrierInfo,
|
carrier_info: &'a CarrierInfo,
|
||||||
exit_meta: &'a ExitMeta,
|
exit_meta: &'a ExitMeta,
|
||||||
variable_map: &'a mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
variable_map: &'a mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||||
@ -86,7 +86,7 @@ impl<'a> ExitBindingBuilder<'a> {
|
|||||||
///
|
///
|
||||||
/// Vec of LoopExitBinding, one per carrier, sorted by carrier name
|
/// Vec of LoopExitBinding, one per carrier, sorted by carrier name
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn build_loop_exit_bindings(&mut self) -> Result<Vec<LoopExitBinding>, String> {
|
pub(crate) fn build_loop_exit_bindings(&mut self) -> Result<Vec<LoopExitBinding>, String> {
|
||||||
// Phase 222.5-C: Delegate to constructor module
|
// Phase 222.5-C: Delegate to constructor module
|
||||||
build_loop_exit_bindings(self.carrier_info, self.exit_meta, self.variable_map)
|
build_loop_exit_bindings(self.carrier_info, self.exit_meta, self.variable_map)
|
||||||
}
|
}
|
||||||
@ -106,7 +106,7 @@ impl<'a> ExitBindingBuilder<'a> {
|
|||||||
///
|
///
|
||||||
/// Success or error if boundary cannot be updated
|
/// Success or error if boundary cannot be updated
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn apply_to_boundary(&self, boundary: &mut JoinInlineBoundary) -> Result<(), String> {
|
pub(crate) fn apply_to_boundary(&self, boundary: &mut JoinInlineBoundary) -> Result<(), String> {
|
||||||
// Phase 222.5-C: Delegate to applicator module
|
// Phase 222.5-C: Delegate to applicator module
|
||||||
apply_exit_bindings_to_boundary(
|
apply_exit_bindings_to_boundary(
|
||||||
self.carrier_info,
|
self.carrier_info,
|
||||||
@ -160,7 +160,9 @@ mod tests {
|
|||||||
let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map)
|
let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map)
|
||||||
.expect("Failed to create builder");
|
.expect("Failed to create builder");
|
||||||
|
|
||||||
let bindings = builder.build_loop_exit_bindings().expect("Failed to build bindings");
|
let bindings = builder
|
||||||
|
.build_loop_exit_bindings()
|
||||||
|
.expect("Failed to build bindings");
|
||||||
|
|
||||||
assert_eq!(bindings.len(), 1);
|
assert_eq!(bindings.len(), 1);
|
||||||
assert_eq!(bindings[0].carrier_name, "sum");
|
assert_eq!(bindings[0].carrier_name, "sum");
|
||||||
@ -213,7 +215,9 @@ mod tests {
|
|||||||
let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map)
|
let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map)
|
||||||
.expect("Failed to create builder");
|
.expect("Failed to create builder");
|
||||||
|
|
||||||
let bindings = builder.build_loop_exit_bindings().expect("Failed to build bindings");
|
let bindings = builder
|
||||||
|
.build_loop_exit_bindings()
|
||||||
|
.expect("Failed to build bindings");
|
||||||
|
|
||||||
assert_eq!(bindings.len(), 2);
|
assert_eq!(bindings.len(), 2);
|
||||||
// Bindings should be sorted by carrier name
|
// Bindings should be sorted by carrier name
|
||||||
@ -345,24 +349,27 @@ mod tests {
|
|||||||
let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map)
|
let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map)
|
||||||
.expect("Failed to create builder");
|
.expect("Failed to create builder");
|
||||||
|
|
||||||
let _ = builder.build_loop_exit_bindings().expect("Failed to build bindings");
|
let _ = builder
|
||||||
|
.build_loop_exit_bindings()
|
||||||
|
.expect("Failed to build bindings");
|
||||||
|
|
||||||
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
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.apply_to_boundary(&mut boundary)
|
builder
|
||||||
|
.apply_to_boundary(&mut boundary)
|
||||||
.expect("Failed to apply to boundary");
|
.expect("Failed to apply to boundary");
|
||||||
|
|
||||||
// Should have loop_var + sum carrier in exit_bindings
|
// Should have loop_var + sum carrier in exit_bindings
|
||||||
|
|||||||
@ -38,7 +38,7 @@ use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
|||||||
use crate::mir::BasicBlockId;
|
use crate::mir::BasicBlockId;
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
pub struct LoopScopeShapeBuilder;
|
pub(crate) struct LoopScopeShapeBuilder;
|
||||||
|
|
||||||
impl LoopScopeShapeBuilder {
|
impl LoopScopeShapeBuilder {
|
||||||
/// Create LoopScopeShape with empty body_locals
|
/// Create LoopScopeShape with empty body_locals
|
||||||
|
|||||||
@ -46,20 +46,20 @@ pub(in crate::mir::builder) mod common_init;
|
|||||||
pub(in crate::mir::builder) mod condition_env_builder;
|
pub(in crate::mir::builder) mod condition_env_builder;
|
||||||
pub(in crate::mir::builder) mod conversion_pipeline;
|
pub(in crate::mir::builder) mod conversion_pipeline;
|
||||||
pub(in crate::mir::builder) mod exit_binding;
|
pub(in crate::mir::builder) mod exit_binding;
|
||||||
pub(in crate::mir::builder) mod exit_binding_validator; // Phase 222.5-C
|
pub(in crate::mir::builder) mod exit_binding_applicator; // Phase 222.5-C
|
||||||
pub(in crate::mir::builder) mod exit_binding_constructor; // Phase 222.5-C
|
pub(in crate::mir::builder) mod exit_binding_constructor; // Phase 222.5-C
|
||||||
pub(in crate::mir::builder) mod exit_binding_applicator; // Phase 222.5-C
|
pub(in crate::mir::builder) mod exit_binding_validator; // Phase 222.5-C
|
||||||
pub(in crate::mir::builder) mod loop_scope_shape_builder;
|
pub(in crate::mir::builder) mod loop_scope_shape_builder;
|
||||||
pub(in crate::mir::builder) mod pattern_pipeline;
|
|
||||||
pub(in crate::mir::builder) mod pattern1_minimal;
|
pub(in crate::mir::builder) mod pattern1_minimal;
|
||||||
pub(in crate::mir::builder) mod pattern2_with_break;
|
pub(in crate::mir::builder) mod pattern2_with_break;
|
||||||
pub(in crate::mir::builder) mod pattern3_with_if_phi;
|
pub(in crate::mir::builder) mod pattern3_with_if_phi;
|
||||||
pub(in crate::mir::builder) mod pattern4_carrier_analyzer;
|
pub(in crate::mir::builder) mod pattern4_carrier_analyzer;
|
||||||
pub(in crate::mir::builder) mod pattern4_with_continue;
|
pub(in crate::mir::builder) mod pattern4_with_continue;
|
||||||
|
pub(in crate::mir::builder) mod pattern_pipeline;
|
||||||
pub(in crate::mir::builder) mod router;
|
pub(in crate::mir::builder) mod router;
|
||||||
pub(in crate::mir::builder) mod trim_loop_lowering; // Phase 180: Dedicated Trim/P5 lowering module
|
pub(in crate::mir::builder) mod trim_loop_lowering; // Phase 180: Dedicated Trim/P5 lowering module
|
||||||
pub(in crate::mir::builder) mod trim_pattern_validator;
|
|
||||||
pub(in crate::mir::builder) mod trim_pattern_lowerer;
|
pub(in crate::mir::builder) mod trim_pattern_lowerer;
|
||||||
|
pub(in crate::mir::builder) mod trim_pattern_validator;
|
||||||
|
|
||||||
// Re-export router for convenience
|
// Re-export router for convenience
|
||||||
pub(in crate::mir::builder) use router::{route_loop_pattern, LoopPatternContext};
|
pub(in crate::mir::builder) use router::{route_loop_pattern, LoopPatternContext};
|
||||||
|
|||||||
@ -5,10 +5,10 @@
|
|||||||
//! - Trim break condition generation
|
//! - Trim break condition generation
|
||||||
//! - Carrier binding setup in ConditionEnv
|
//! - Carrier binding setup in ConditionEnv
|
||||||
|
|
||||||
use crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper;
|
use crate::ast::{ASTNode, Span, UnaryOperator};
|
||||||
use crate::mir::join_ir::lowering::condition_env::ConditionBinding;
|
use crate::mir::join_ir::lowering::condition_env::ConditionBinding;
|
||||||
|
use crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper;
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
use crate::ast::{ASTNode, UnaryOperator, Span};
|
|
||||||
|
|
||||||
pub(in crate::mir::builder::control_flow::joinir::patterns) struct TrimPatternLowerer;
|
pub(in crate::mir::builder::control_flow::joinir::patterns) struct TrimPatternLowerer;
|
||||||
|
|
||||||
@ -61,8 +61,12 @@ impl TrimPatternLowerer {
|
|||||||
let carrier_name = &trim_helper.carrier_name;
|
let carrier_name = &trim_helper.carrier_name;
|
||||||
|
|
||||||
// Get host ValueId for carrier
|
// Get host ValueId for carrier
|
||||||
let host_value_id = get_host_value(carrier_name)
|
let host_value_id = get_host_value(carrier_name).ok_or_else(|| {
|
||||||
.ok_or_else(|| format!("[TrimPatternLowerer] Carrier '{}' not in variable_map", carrier_name))?;
|
format!(
|
||||||
|
"[TrimPatternLowerer] Carrier '{}' not in variable_map",
|
||||||
|
carrier_name
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
// Allocate JoinIR ValueId
|
// Allocate JoinIR ValueId
|
||||||
let joinir_value_id = alloc_join_value();
|
let joinir_value_id = alloc_join_value();
|
||||||
@ -94,7 +98,8 @@ impl TrimPatternLowerer {
|
|||||||
insert_to_env: impl FnOnce(String, ValueId),
|
insert_to_env: impl FnOnce(String, ValueId),
|
||||||
alloc_join_value: &mut dyn FnMut() -> ValueId,
|
alloc_join_value: &mut dyn FnMut() -> ValueId,
|
||||||
) -> Result<ConditionBinding, String> {
|
) -> Result<ConditionBinding, String> {
|
||||||
let binding = Self::setup_trim_carrier_binding(trim_helper, get_host_value, alloc_join_value)?;
|
let binding =
|
||||||
|
Self::setup_trim_carrier_binding(trim_helper, get_host_value, alloc_join_value)?;
|
||||||
|
|
||||||
// Insert into env
|
// Insert into env
|
||||||
insert_to_env(binding.name.clone(), binding.join_value);
|
insert_to_env(binding.name.clone(), binding.join_value);
|
||||||
@ -122,7 +127,9 @@ mod tests {
|
|||||||
|
|
||||||
// Should be UnaryOp::Not
|
// Should be UnaryOp::Not
|
||||||
match result {
|
match result {
|
||||||
ASTNode::UnaryOp { operator, operand, .. } => {
|
ASTNode::UnaryOp {
|
||||||
|
operator, operand, ..
|
||||||
|
} => {
|
||||||
assert_eq!(operator, UnaryOperator::Not);
|
assert_eq!(operator, UnaryOperator::Not);
|
||||||
// Operand should be Variable with carrier name
|
// Operand should be Variable with carrier name
|
||||||
match *operand {
|
match *operand {
|
||||||
@ -151,11 +158,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let get_value = |name: &str| variable_map.get(name).copied();
|
let get_value = |name: &str| variable_map.get(name).copied();
|
||||||
let result = TrimPatternLowerer::setup_trim_carrier_binding(
|
let result = TrimPatternLowerer::setup_trim_carrier_binding(&helper, get_value, &mut alloc);
|
||||||
&helper,
|
|
||||||
get_value,
|
|
||||||
&mut alloc,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
let binding = result.unwrap();
|
let binding = result.unwrap();
|
||||||
@ -178,11 +181,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let get_value = |name: &str| variable_map.get(name).copied();
|
let get_value = |name: &str| variable_map.get(name).copied();
|
||||||
let result = TrimPatternLowerer::setup_trim_carrier_binding(
|
let result = TrimPatternLowerer::setup_trim_carrier_binding(&helper, get_value, &mut alloc);
|
||||||
&helper,
|
|
||||||
get_value,
|
|
||||||
&mut alloc,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(result.unwrap_err().contains("not in variable_map"));
|
assert!(result.unwrap_err().contains("not in variable_map"));
|
||||||
@ -209,12 +208,8 @@ mod tests {
|
|||||||
env.insert(name, value);
|
env.insert(name, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = TrimPatternLowerer::add_to_condition_env(
|
let result =
|
||||||
&helper,
|
TrimPatternLowerer::add_to_condition_env(&helper, get_value, insert, &mut alloc);
|
||||||
get_value,
|
|
||||||
insert,
|
|
||||||
&mut alloc,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
let binding = result.unwrap();
|
let binding = result.unwrap();
|
||||||
|
|||||||
@ -31,10 +31,10 @@ impl TrimPatternValidator {
|
|||||||
ch_value: ValueId,
|
ch_value: ValueId,
|
||||||
whitespace_chars: &[String],
|
whitespace_chars: &[String],
|
||||||
) -> Result<ValueId, String> {
|
) -> Result<ValueId, String> {
|
||||||
use crate::mir::builder::emission::constant::emit_string;
|
|
||||||
use crate::mir::builder::emission::compare::emit_eq_to;
|
use crate::mir::builder::emission::compare::emit_eq_to;
|
||||||
use crate::mir::types::BinaryOp;
|
use crate::mir::builder::emission::constant::emit_string;
|
||||||
use crate::mir::instruction::MirInstruction;
|
use crate::mir::instruction::MirInstruction;
|
||||||
|
use crate::mir::types::BinaryOp;
|
||||||
|
|
||||||
if whitespace_chars.is_empty() {
|
if whitespace_chars.is_empty() {
|
||||||
return Err("[emit_whitespace_check] Empty whitespace_chars".to_string());
|
return Err("[emit_whitespace_check] Empty whitespace_chars".to_string());
|
||||||
@ -89,18 +89,32 @@ impl TrimPatternValidator {
|
|||||||
) -> Option<(String, Box<ASTNode>)> {
|
) -> Option<(String, Box<ASTNode>)> {
|
||||||
for stmt in loop_body {
|
for stmt in loop_body {
|
||||||
// Look for: local ch = ...
|
// Look for: local ch = ...
|
||||||
if let ASTNode::Local { variables, initial_values, .. } = stmt {
|
if let ASTNode::Local {
|
||||||
|
variables,
|
||||||
|
initial_values,
|
||||||
|
..
|
||||||
|
} = stmt
|
||||||
|
{
|
||||||
for (i, var) in variables.iter().enumerate() {
|
for (i, var) in variables.iter().enumerate() {
|
||||||
if var == var_name {
|
if var == var_name {
|
||||||
if let Some(Some(init_expr_box)) = initial_values.get(i) {
|
if let Some(Some(init_expr_box)) = initial_values.get(i) {
|
||||||
// Check if it's a substring method call
|
// Check if it's a substring method call
|
||||||
if let ASTNode::MethodCall { object, method, arguments, .. } = init_expr_box.as_ref() {
|
if let ASTNode::MethodCall {
|
||||||
|
object,
|
||||||
|
method,
|
||||||
|
arguments,
|
||||||
|
..
|
||||||
|
} = init_expr_box.as_ref()
|
||||||
|
{
|
||||||
if method == "substring" && arguments.len() == 2 {
|
if method == "substring" && arguments.len() == 2 {
|
||||||
// Extract object name
|
// Extract object name
|
||||||
if let ASTNode::Variable { name, .. } = object.as_ref() {
|
if let ASTNode::Variable { name, .. } = object.as_ref() {
|
||||||
// Return object name and start expression
|
// Return object name and start expression
|
||||||
// (We assume second arg is start+1, first arg is start)
|
// (We assume second arg is start+1, first arg is start)
|
||||||
return Some((name.clone(), Box::new(arguments[0].clone())));
|
return Some((
|
||||||
|
name.clone(),
|
||||||
|
Box::new(arguments[0].clone()),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,45 +130,41 @@ impl TrimPatternValidator {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ast::{LiteralValue, Span, BinaryOperator};
|
use crate::ast::{BinaryOperator, LiteralValue, Span};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_extract_substring_args_valid() {
|
fn test_extract_substring_args_valid() {
|
||||||
// Create: local ch = s.substring(start, start+1)
|
// Create: local ch = s.substring(start, start+1)
|
||||||
let body = vec![
|
let body = vec![ASTNode::Local {
|
||||||
ASTNode::Local {
|
variables: vec!["ch".to_string()],
|
||||||
variables: vec!["ch".to_string()],
|
initial_values: vec![Some(Box::new(ASTNode::MethodCall {
|
||||||
initial_values: vec![
|
object: Box::new(ASTNode::Variable {
|
||||||
Some(Box::new(ASTNode::MethodCall {
|
name: "s".to_string(),
|
||||||
object: Box::new(ASTNode::Variable {
|
span: Span::unknown(),
|
||||||
name: "s".to_string(),
|
}),
|
||||||
|
method: "substring".to_string(),
|
||||||
|
arguments: vec![
|
||||||
|
ASTNode::Variable {
|
||||||
|
name: "start".to_string(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
},
|
||||||
|
ASTNode::BinaryOp {
|
||||||
|
operator: BinaryOperator::Add,
|
||||||
|
left: Box::new(ASTNode::Variable {
|
||||||
|
name: "start".to_string(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
}),
|
||||||
|
right: Box::new(ASTNode::Literal {
|
||||||
|
value: LiteralValue::Integer(1),
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
}),
|
}),
|
||||||
method: "substring".to_string(),
|
|
||||||
arguments: vec![
|
|
||||||
ASTNode::Variable {
|
|
||||||
name: "start".to_string(),
|
|
||||||
span: Span::unknown(),
|
|
||||||
},
|
|
||||||
ASTNode::BinaryOp {
|
|
||||||
operator: BinaryOperator::Add,
|
|
||||||
left: Box::new(ASTNode::Variable {
|
|
||||||
name: "start".to_string(),
|
|
||||||
span: Span::unknown(),
|
|
||||||
}),
|
|
||||||
right: Box::new(ASTNode::Literal {
|
|
||||||
value: LiteralValue::Integer(1),
|
|
||||||
span: Span::unknown(),
|
|
||||||
}),
|
|
||||||
span: Span::unknown(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
})),
|
},
|
||||||
],
|
],
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
},
|
}))],
|
||||||
];
|
span: Span::unknown(),
|
||||||
|
}];
|
||||||
|
|
||||||
let result = TrimPatternValidator::extract_substring_args(&body, "ch");
|
let result = TrimPatternValidator::extract_substring_args(&body, "ch");
|
||||||
assert!(result.is_some());
|
assert!(result.is_some());
|
||||||
@ -173,32 +183,28 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_extract_substring_args_wrong_var() {
|
fn test_extract_substring_args_wrong_var() {
|
||||||
// local other_var = s.substring(0, 1)
|
// local other_var = s.substring(0, 1)
|
||||||
let body = vec![
|
let body = vec![ASTNode::Local {
|
||||||
ASTNode::Local {
|
variables: vec!["other_var".to_string()],
|
||||||
variables: vec!["other_var".to_string()],
|
initial_values: vec![Some(Box::new(ASTNode::MethodCall {
|
||||||
initial_values: vec![
|
object: Box::new(ASTNode::Variable {
|
||||||
Some(Box::new(ASTNode::MethodCall {
|
name: "s".to_string(),
|
||||||
object: Box::new(ASTNode::Variable {
|
span: Span::unknown(),
|
||||||
name: "s".to_string(),
|
}),
|
||||||
span: Span::unknown(),
|
method: "substring".to_string(),
|
||||||
}),
|
arguments: vec![
|
||||||
method: "substring".to_string(),
|
ASTNode::Literal {
|
||||||
arguments: vec![
|
value: LiteralValue::Integer(0),
|
||||||
ASTNode::Literal {
|
|
||||||
value: LiteralValue::Integer(0),
|
|
||||||
span: Span::unknown(),
|
|
||||||
},
|
|
||||||
ASTNode::Literal {
|
|
||||||
value: LiteralValue::Integer(1),
|
|
||||||
span: Span::unknown(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
})),
|
},
|
||||||
|
ASTNode::Literal {
|
||||||
|
value: LiteralValue::Integer(1),
|
||||||
|
span: Span::unknown(),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
},
|
}))],
|
||||||
];
|
span: Span::unknown(),
|
||||||
|
}];
|
||||||
|
|
||||||
let result = TrimPatternValidator::extract_substring_args(&body, "ch");
|
let result = TrimPatternValidator::extract_substring_args(&body, "ch");
|
||||||
assert!(result.is_none());
|
assert!(result.is_none());
|
||||||
@ -207,28 +213,22 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_extract_substring_args_wrong_method() {
|
fn test_extract_substring_args_wrong_method() {
|
||||||
// local ch = s.charAt(0)
|
// local ch = s.charAt(0)
|
||||||
let body = vec![
|
let body = vec![ASTNode::Local {
|
||||||
ASTNode::Local {
|
variables: vec!["ch".to_string()],
|
||||||
variables: vec!["ch".to_string()],
|
initial_values: vec![Some(Box::new(ASTNode::MethodCall {
|
||||||
initial_values: vec![
|
object: Box::new(ASTNode::Variable {
|
||||||
Some(Box::new(ASTNode::MethodCall {
|
name: "s".to_string(),
|
||||||
object: Box::new(ASTNode::Variable {
|
span: Span::unknown(),
|
||||||
name: "s".to_string(),
|
}),
|
||||||
span: Span::unknown(),
|
method: "charAt".to_string(),
|
||||||
}),
|
arguments: vec![ASTNode::Literal {
|
||||||
method: "charAt".to_string(),
|
value: LiteralValue::Integer(0),
|
||||||
arguments: vec![
|
span: Span::unknown(),
|
||||||
ASTNode::Literal {
|
}],
|
||||||
value: LiteralValue::Integer(0),
|
|
||||||
span: Span::unknown(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
span: Span::unknown(),
|
|
||||||
})),
|
|
||||||
],
|
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
},
|
}))],
|
||||||
];
|
span: Span::unknown(),
|
||||||
|
}];
|
||||||
|
|
||||||
let result = TrimPatternValidator::extract_substring_args(&body, "ch");
|
let result = TrimPatternValidator::extract_substring_args(&body, "ch");
|
||||||
assert!(result.is_none());
|
assert!(result.is_none());
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
//! JoinIR routing logic for loop lowering
|
//! JoinIR routing logic for loop lowering
|
||||||
|
|
||||||
|
use 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::trace;
|
|
||||||
|
|
||||||
impl MirBuilder {
|
impl MirBuilder {
|
||||||
/// Phase 49: Try JoinIR Frontend for mainline integration
|
/// Phase 49: Try JoinIR Frontend for mainline integration
|
||||||
@ -42,16 +42,17 @@ impl MirBuilder {
|
|||||||
// Phase 196: Default to structure-first routing now that LoopBuilder is removed.
|
// Phase 196: Default to structure-first routing now that LoopBuilder is removed.
|
||||||
// - Default: ON (structure_only = true) to allow JoinIR patterns to run for all loops.
|
// - Default: ON (structure_only = true) to allow JoinIR patterns to run for all loops.
|
||||||
// - To revert to the previous whitelist-only behavior, set NYASH_JOINIR_STRUCTURE_ONLY=0.
|
// - To revert to the previous whitelist-only behavior, set NYASH_JOINIR_STRUCTURE_ONLY=0.
|
||||||
let structure_only = match std::env::var("NYASH_JOINIR_STRUCTURE_ONLY")
|
let structure_only = match std::env::var("NYASH_JOINIR_STRUCTURE_ONLY").ok().as_deref() {
|
||||||
.ok()
|
|
||||||
.as_deref()
|
|
||||||
{
|
|
||||||
Some("0") | Some("off") => false,
|
Some("0") | Some("off") => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
if structure_only {
|
if structure_only {
|
||||||
trace::trace().routing("router", &func_name, "Structure-only mode enabled, skipping whitelist");
|
trace::trace().routing(
|
||||||
|
"router",
|
||||||
|
&func_name,
|
||||||
|
"Structure-only mode enabled, skipping whitelist",
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// Phase 49-4 + Phase 80: Multi-target routing (legacy whitelist)
|
// Phase 49-4 + Phase 80: Multi-target routing (legacy whitelist)
|
||||||
// - JoinIR は常時 ON。legacy LoopBuilder は削除済み。
|
// - JoinIR は常時 ON。legacy LoopBuilder は削除済み。
|
||||||
@ -60,25 +61,25 @@ impl MirBuilder {
|
|||||||
// Phase 188: Add "main" routing for loop pattern expansion
|
// Phase 188: Add "main" routing for loop pattern expansion
|
||||||
// Phase 170: Add JsonParserBox methods for selfhost validation
|
// Phase 170: Add JsonParserBox methods for selfhost validation
|
||||||
let is_target = match func_name.as_str() {
|
let is_target = match func_name.as_str() {
|
||||||
"main" => true, // Phase 188-Impl-1: Enable JoinIR for main function (Pattern 1)
|
"main" => true, // Phase 188-Impl-1: Enable JoinIR for main function (Pattern 1)
|
||||||
"JoinIrMin.main/0" => true, // Phase 188-Impl-2: Enable JoinIR for JoinIrMin.main/0 (Pattern 2)
|
"JoinIrMin.main/0" => true, // Phase 188-Impl-2: Enable JoinIR for JoinIrMin.main/0 (Pattern 2)
|
||||||
"JsonTokenizer.print_tokens/0" => true,
|
"JsonTokenizer.print_tokens/0" => true,
|
||||||
"ArrayExtBox.filter/2" => true,
|
"ArrayExtBox.filter/2" => true,
|
||||||
// Phase 170-A-1: Enable JsonParserBox methods for JoinIR routing
|
// Phase 170-A-1: Enable JsonParserBox methods for JoinIR routing
|
||||||
"JsonParserBox._trim/1" => true,
|
"JsonParserBox._trim/1" => true,
|
||||||
"JsonParserBox._skip_whitespace/2" => true,
|
"JsonParserBox._skip_whitespace/2" => true,
|
||||||
"JsonParserBox._match_literal/3" => true, // Phase 182: Fixed arity (s, pos, literal)
|
"JsonParserBox._match_literal/3" => true, // Phase 182: Fixed arity (s, pos, literal)
|
||||||
"JsonParserBox._parse_string/2" => true,
|
"JsonParserBox._parse_string/2" => true,
|
||||||
"JsonParserBox._parse_array/2" => true,
|
"JsonParserBox._parse_array/2" => true,
|
||||||
"JsonParserBox._parse_object/2" => true,
|
"JsonParserBox._parse_object/2" => true,
|
||||||
// Phase 182: Add simple loop methods
|
// Phase 182: Add simple loop methods
|
||||||
"JsonParserBox._parse_number/2" => true, // P2 Break (s, pos)
|
"JsonParserBox._parse_number/2" => true, // P2 Break (s, pos)
|
||||||
"JsonParserBox._atoi/1" => true, // P2 Break (s)
|
"JsonParserBox._atoi/1" => true, // P2 Break (s)
|
||||||
// Phase 170-A-1: Test methods (simplified versions)
|
// Phase 170-A-1: Test methods (simplified versions)
|
||||||
"TrimTest.trim/1" => true,
|
"TrimTest.trim/1" => true,
|
||||||
"Main.trim/1" => true, // Phase 171-fix: Main box variant
|
"Main.trim/1" => true, // Phase 171-fix: Main box variant
|
||||||
"Main.trim_string_simple/1" => true, // Phase 33-13: Simple trim variant
|
"Main.trim_string_simple/1" => true, // Phase 33-13: Simple trim variant
|
||||||
"TrimTest.main/0" => true, // Phase 170: TrimTest.main for loop pattern test
|
"TrimTest.main/0" => true, // Phase 170: TrimTest.main for loop pattern test
|
||||||
// Phase 173: JsonParser P5 expansion test
|
// Phase 173: JsonParser P5 expansion test
|
||||||
"JsonParserTest._skip_whitespace/3" => true,
|
"JsonParserTest._skip_whitespace/3" => true,
|
||||||
"JsonParserTest.main/0" => true,
|
"JsonParserTest.main/0" => true,
|
||||||
@ -99,7 +100,11 @@ impl MirBuilder {
|
|||||||
// Debug log when routing through JoinIR Frontend
|
// Debug log when routing through JoinIR Frontend
|
||||||
// Phase 195: Check trace flags directly from JoinLoopTrace
|
// Phase 195: Check trace flags directly from JoinLoopTrace
|
||||||
let debug = trace::trace().is_loopform_enabled() || trace::trace().is_mainline_enabled();
|
let debug = trace::trace().is_loopform_enabled() || trace::trace().is_mainline_enabled();
|
||||||
trace::trace().routing("router", &func_name, "Routing through JoinIR Frontend mainline");
|
trace::trace().routing(
|
||||||
|
"router",
|
||||||
|
&func_name,
|
||||||
|
"Routing through JoinIR Frontend mainline",
|
||||||
|
);
|
||||||
|
|
||||||
// Phase 49-3: Implement JoinIR Frontend integration
|
// Phase 49-3: Implement JoinIR Frontend integration
|
||||||
self.cf_loop_joinir_impl(condition, body, &func_name, debug)
|
self.cf_loop_joinir_impl(condition, body, &func_name, debug)
|
||||||
@ -123,9 +128,20 @@ impl MirBuilder {
|
|||||||
// Phase 200-C: Pass fn_body_ast to LoopPatternContext if available
|
// Phase 200-C: Pass fn_body_ast to LoopPatternContext if available
|
||||||
// Clone fn_body_ast to avoid borrow checker issues
|
// Clone fn_body_ast to avoid borrow checker issues
|
||||||
let fn_body_clone = self.fn_body_ast.clone();
|
let fn_body_clone = self.fn_body_ast.clone();
|
||||||
eprintln!("[routing] fn_body_ast is {} for '{}'", if fn_body_clone.is_some() { "SOME" } else { "NONE" }, func_name);
|
eprintln!(
|
||||||
|
"[routing] fn_body_ast is {} for '{}'",
|
||||||
|
if fn_body_clone.is_some() {
|
||||||
|
"SOME"
|
||||||
|
} else {
|
||||||
|
"NONE"
|
||||||
|
},
|
||||||
|
func_name
|
||||||
|
);
|
||||||
let ctx = if let Some(ref fn_body) = fn_body_clone {
|
let ctx = if let Some(ref fn_body) = fn_body_clone {
|
||||||
eprintln!("[routing] Creating ctx with fn_body ({} nodes)", fn_body.len());
|
eprintln!(
|
||||||
|
"[routing] Creating ctx with fn_body ({} nodes)",
|
||||||
|
fn_body.len()
|
||||||
|
);
|
||||||
LoopPatternContext::with_fn_body(condition, body, &func_name, debug, fn_body)
|
LoopPatternContext::with_fn_body(condition, body, &func_name, debug, fn_body)
|
||||||
} else {
|
} else {
|
||||||
LoopPatternContext::new(condition, body, &func_name, debug)
|
LoopPatternContext::new(condition, body, &func_name, debug)
|
||||||
@ -137,7 +153,11 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Phase 187-2: Pattern router failed, try legacy whitelist
|
// Phase 187-2: Pattern router failed, try legacy whitelist
|
||||||
trace::trace().routing("router", func_name, "Pattern router found no match, trying legacy whitelist");
|
trace::trace().routing(
|
||||||
|
"router",
|
||||||
|
func_name,
|
||||||
|
"Pattern router found no match, trying legacy whitelist",
|
||||||
|
);
|
||||||
|
|
||||||
// Delegate to legacy binding path (routing_legacy_binding.rs)
|
// Delegate to legacy binding path (routing_legacy_binding.rs)
|
||||||
self.cf_loop_joinir_legacy_binding(condition, body, func_name, debug)
|
self.cf_loop_joinir_legacy_binding(condition, body, func_name, debug)
|
||||||
|
|||||||
@ -7,10 +7,10 @@
|
|||||||
//! Phase 194+ uses the pattern-based router instead. This legacy path is
|
//! Phase 194+ uses the pattern-based router instead. This legacy path is
|
||||||
//! kept for backward compatibility with existing whitelist entries.
|
//! kept for backward compatibility with existing whitelist entries.
|
||||||
|
|
||||||
|
use 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::trace;
|
|
||||||
|
|
||||||
impl MirBuilder {
|
impl MirBuilder {
|
||||||
/// Phase 49-3: Legacy JoinIR Frontend integration via LoopFrontendBinding
|
/// Phase 49-3: Legacy JoinIR Frontend integration via LoopFrontendBinding
|
||||||
@ -21,7 +21,7 @@ impl MirBuilder {
|
|||||||
/// # Pipeline
|
/// # Pipeline
|
||||||
/// 1. Build Loop AST → JSON v0 format (with "defs" array)
|
/// 1. Build Loop AST → JSON v0 format (with "defs" array)
|
||||||
/// 2. AstToJoinIrLowerer::lower_program_json() → JoinModule
|
/// 2. AstToJoinIrLowerer::lower_program_json() → JoinModule
|
||||||
/// 3. convert_join_module_to_mir_with_meta() → MirModule
|
/// 3. bridge_joinir_to_mir_with_meta() → MirModule
|
||||||
/// 4. Merge MIR blocks into current_function
|
/// 4. Merge MIR blocks into current_function
|
||||||
pub(in crate::mir::builder) fn cf_loop_joinir_legacy_binding(
|
pub(in crate::mir::builder) fn cf_loop_joinir_legacy_binding(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -32,7 +32,7 @@ impl MirBuilder {
|
|||||||
) -> Result<Option<ValueId>, String> {
|
) -> Result<Option<ValueId>, String> {
|
||||||
use super::super::super::loop_frontend_binding::LoopFrontendBinding;
|
use super::super::super::loop_frontend_binding::LoopFrontendBinding;
|
||||||
use crate::mir::join_ir::frontend::{AstToJoinIrLowerer, JoinFuncMetaMap};
|
use crate::mir::join_ir::frontend::{AstToJoinIrLowerer, JoinFuncMetaMap};
|
||||||
use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta;
|
use crate::mir::join_ir_vm_bridge::bridge_joinir_to_mir_with_meta;
|
||||||
use crate::mir::types::ConstValue;
|
use crate::mir::types::ConstValue;
|
||||||
use crate::mir::MirInstruction;
|
use crate::mir::MirInstruction;
|
||||||
use crate::r#macro::ast_json::ast_to_json;
|
use crate::r#macro::ast_json::ast_to_json;
|
||||||
@ -42,7 +42,11 @@ impl MirBuilder {
|
|||||||
"JsonTokenizer.print_tokens/0" => LoopFrontendBinding::for_print_tokens(),
|
"JsonTokenizer.print_tokens/0" => LoopFrontendBinding::for_print_tokens(),
|
||||||
"ArrayExtBox.filter/2" => LoopFrontendBinding::for_array_filter(),
|
"ArrayExtBox.filter/2" => LoopFrontendBinding::for_array_filter(),
|
||||||
_ => {
|
_ => {
|
||||||
trace::trace().routing("router", func_name, "No legacy binding defined, falling back");
|
trace::trace().routing(
|
||||||
|
"router",
|
||||||
|
func_name,
|
||||||
|
"No legacy binding defined, falling back",
|
||||||
|
);
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -79,7 +83,10 @@ impl MirBuilder {
|
|||||||
if ext_ref == "me" || ext_ref.starts_with("me.") {
|
if ext_ref == "me" || ext_ref.starts_with("me.") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
trace::trace().debug("router", &format!("Adding '{}' to params (external_ref)", ext_ref));
|
trace::trace().debug(
|
||||||
|
"router",
|
||||||
|
&format!("Adding '{}' to params (external_ref)", ext_ref),
|
||||||
|
);
|
||||||
params.push(serde_json::json!(ext_ref));
|
params.push(serde_json::json!(ext_ref));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,15 +164,11 @@ impl MirBuilder {
|
|||||||
trace::trace().joinir_stats(
|
trace::trace().joinir_stats(
|
||||||
"router",
|
"router",
|
||||||
join_module.functions.len(),
|
join_module.functions.len(),
|
||||||
join_module
|
join_module.functions.values().map(|f| f.body.len()).sum(),
|
||||||
.functions
|
|
||||||
.values()
|
|
||||||
.map(|f| f.body.len())
|
|
||||||
.sum(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 4: Convert JoinModule to MIR
|
// Step 4: Convert JoinModule to MIR
|
||||||
let mir_module = convert_join_module_to_mir_with_meta(&join_module, &join_meta)
|
let mir_module = bridge_joinir_to_mir_with_meta(&join_module, &join_meta)
|
||||||
.map_err(|e| format!("JoinIR→MIR conversion failed: {}", e.message))?;
|
.map_err(|e| format!("JoinIR→MIR conversion failed: {}", e.message))?;
|
||||||
|
|
||||||
// Debug MIR module if trace enabled
|
// Debug MIR module if trace enabled
|
||||||
@ -187,7 +190,11 @@ impl MirBuilder {
|
|||||||
for (block_id, block) in &func.blocks {
|
for (block_id, block) in &func.blocks {
|
||||||
trace::trace().blocks(
|
trace::trace().blocks(
|
||||||
"router",
|
"router",
|
||||||
&format!("Block {:?}: {} instructions", block_id, block.instructions.len()),
|
&format!(
|
||||||
|
"Block {:?}: {} instructions",
|
||||||
|
block_id,
|
||||||
|
block.instructions.len()
|
||||||
|
),
|
||||||
);
|
);
|
||||||
for (i, inst) in block.instructions.iter().enumerate() {
|
for (i, inst) in block.instructions.iter().enumerate() {
|
||||||
trace::trace().instructions("router", &format!("[{}] {:?}", i, inst));
|
trace::trace().instructions("router", &format!("[{}] {:?}", i, inst));
|
||||||
|
|||||||
@ -41,8 +41,8 @@
|
|||||||
//! NYASH_TRACE_VARMAP=1 NYASH_JOINIR_DEBUG=1 ./target/release/hakorune test.hako
|
//! NYASH_TRACE_VARMAP=1 NYASH_JOINIR_DEBUG=1 ./target/release/hakorune test.hako
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
/// Unified tracer for JoinIR loop operations.
|
/// Unified tracer for JoinIR loop operations.
|
||||||
///
|
///
|
||||||
@ -206,7 +206,10 @@ impl JoinLoopTrace {
|
|||||||
/// - `msg`: Human-readable message about the routing decision
|
/// - `msg`: Human-readable message about the routing decision
|
||||||
pub fn routing(&self, tag: &str, func_name: &str, msg: &str) {
|
pub fn routing(&self, tag: &str, func_name: &str, msg: &str) {
|
||||||
if self.joinir_enabled || self.mainline_enabled {
|
if self.joinir_enabled || self.mainline_enabled {
|
||||||
eprintln!("[trace:routing] {}: function '{}' - {}", tag, func_name, msg);
|
eprintln!(
|
||||||
|
"[trace:routing] {}: function '{}' - {}",
|
||||||
|
tag, func_name, msg
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,15 +26,14 @@ pub(in crate::mir::builder) fn extract_loop_variable_from_condition(
|
|||||||
condition: &ASTNode,
|
condition: &ASTNode,
|
||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
match condition {
|
match condition {
|
||||||
ASTNode::BinaryOp {
|
ASTNode::BinaryOp { operator, left, .. }
|
||||||
operator, left, ..
|
if matches!(
|
||||||
} if matches!(
|
operator,
|
||||||
operator,
|
BinaryOperator::Less
|
||||||
BinaryOperator::Less
|
| BinaryOperator::Greater
|
||||||
| BinaryOperator::Greater
|
| BinaryOperator::LessEqual
|
||||||
| BinaryOperator::LessEqual
|
| BinaryOperator::GreaterEqual
|
||||||
| BinaryOperator::GreaterEqual
|
) =>
|
||||||
) =>
|
|
||||||
{
|
{
|
||||||
// Binary comparison: extract variable from left side
|
// Binary comparison: extract variable from left side
|
||||||
match &**left {
|
match &**left {
|
||||||
|
|||||||
@ -117,7 +117,10 @@ impl super::MirBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Phase 200-C: Store fn_body_ast for inline main() lowering
|
// Phase 200-C: Store fn_body_ast for inline main() lowering
|
||||||
eprintln!("[build_static_main_box] Storing fn_body_ast with {} nodes for inline main()", body.len());
|
eprintln!(
|
||||||
|
"[build_static_main_box] Storing fn_body_ast with {} nodes for inline main()",
|
||||||
|
body.len()
|
||||||
|
);
|
||||||
self.fn_body_ast = Some(body.clone());
|
self.fn_body_ast = Some(body.clone());
|
||||||
|
|
||||||
// Lower statements in order to preserve def→use
|
// Lower statements in order to preserve def→use
|
||||||
|
|||||||
@ -235,7 +235,8 @@ impl MirBuilder {
|
|||||||
crate::mir::join_ir::lowering::should_panic_on_joinir_failure(func_name, false);
|
crate::mir::join_ir::lowering::should_panic_on_joinir_failure(func_name, false);
|
||||||
|
|
||||||
// Core ON + 本線対象の場合は環境変数に関わらず試行
|
// Core ON + 本線対象の場合は環境変数に関わらず試行
|
||||||
let should_try_joinir = core_mainline || (joinir_enabled && is_target && (joinir_toplevel || joinir_dryrun));
|
let should_try_joinir =
|
||||||
|
core_mainline || (joinir_enabled && is_target && (joinir_toplevel || joinir_dryrun));
|
||||||
|
|
||||||
if should_try_joinir {
|
if should_try_joinir {
|
||||||
if let Some(ref func) = self.current_function {
|
if let Some(ref func) = self.current_function {
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
//! - JoinIR fragment → host MIR への ID変換
|
//! - JoinIR fragment → host MIR への ID変換
|
||||||
//! - 決定性を重視した実装
|
//! - 決定性を重視した実装
|
||||||
|
|
||||||
use std::collections::BTreeMap; // Phase 222.5-E: HashMap → BTreeMap for determinism
|
|
||||||
use crate::mir::{BasicBlock, BasicBlockId, MirInstruction, ValueId};
|
use crate::mir::{BasicBlock, BasicBlockId, MirInstruction, ValueId};
|
||||||
|
use std::collections::BTreeMap; // Phase 222.5-E: HashMap → BTreeMap for determinism
|
||||||
|
|
||||||
/// JoinIR ID space を host MIR ID space に変換する
|
/// JoinIR ID space を host MIR ID space に変換する
|
||||||
pub struct JoinIrIdRemapper {
|
pub struct JoinIrIdRemapper {
|
||||||
@ -29,7 +29,9 @@ impl JoinIrIdRemapper {
|
|||||||
|
|
||||||
/// Block ID mapping を取得
|
/// Block ID mapping を取得
|
||||||
pub fn get_block(&self, func_name: &str, old_id: BasicBlockId) -> Option<BasicBlockId> {
|
pub fn get_block(&self, func_name: &str, old_id: BasicBlockId) -> Option<BasicBlockId> {
|
||||||
self.block_map.get(&(func_name.to_string(), old_id)).copied()
|
self.block_map
|
||||||
|
.get(&(func_name.to_string(), old_id))
|
||||||
|
.copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Value ID mapping を取得
|
/// Value ID mapping を取得
|
||||||
@ -70,7 +72,9 @@ impl JoinIrIdRemapper {
|
|||||||
Compare { dst, lhs, rhs, .. } => vec![*dst, *lhs, *rhs],
|
Compare { dst, lhs, rhs, .. } => vec![*dst, *lhs, *rhs],
|
||||||
Load { dst, ptr } => vec![*dst, *ptr],
|
Load { dst, ptr } => vec![*dst, *ptr],
|
||||||
Store { value, ptr } => vec![*value, *ptr],
|
Store { value, ptr } => vec![*value, *ptr],
|
||||||
Call { dst, func, args, .. } => {
|
Call {
|
||||||
|
dst, func, args, ..
|
||||||
|
} => {
|
||||||
let mut vals = vec![*func];
|
let mut vals = vec![*func];
|
||||||
if let Some(d) = dst {
|
if let Some(d) = dst {
|
||||||
vals.push(*d);
|
vals.push(*d);
|
||||||
@ -78,7 +82,9 @@ impl JoinIrIdRemapper {
|
|||||||
vals.extend(args.iter().copied());
|
vals.extend(args.iter().copied());
|
||||||
vals
|
vals
|
||||||
}
|
}
|
||||||
BoxCall { dst, box_val, args, .. } => {
|
BoxCall {
|
||||||
|
dst, box_val, args, ..
|
||||||
|
} => {
|
||||||
let mut vals = vec![*box_val];
|
let mut vals = vec![*box_val];
|
||||||
if let Some(d) = dst {
|
if let Some(d) = dst {
|
||||||
vals.push(*d);
|
vals.push(*d);
|
||||||
@ -86,7 +92,9 @@ impl JoinIrIdRemapper {
|
|||||||
vals.extend(args.iter().copied());
|
vals.extend(args.iter().copied());
|
||||||
vals
|
vals
|
||||||
}
|
}
|
||||||
PluginInvoke { dst, box_val, args, .. } => {
|
PluginInvoke {
|
||||||
|
dst, box_val, args, ..
|
||||||
|
} => {
|
||||||
let mut vals = vec![*box_val];
|
let mut vals = vec![*box_val];
|
||||||
if let Some(d) = dst {
|
if let Some(d) = dst {
|
||||||
vals.push(*d);
|
vals.push(*d);
|
||||||
@ -107,7 +115,9 @@ impl JoinIrIdRemapper {
|
|||||||
vals.extend(args.iter().copied());
|
vals.extend(args.iter().copied());
|
||||||
vals
|
vals
|
||||||
}
|
}
|
||||||
NewClosure { dst, captures, me, .. } => {
|
NewClosure {
|
||||||
|
dst, captures, me, ..
|
||||||
|
} => {
|
||||||
let mut vals = vec![*dst];
|
let mut vals = vec![*dst];
|
||||||
vals.extend(captures.iter().map(|(_, v)| *v));
|
vals.extend(captures.iter().map(|(_, v)| *v));
|
||||||
if let Some(m) = me {
|
if let Some(m) = me {
|
||||||
@ -119,10 +129,14 @@ impl JoinIrIdRemapper {
|
|||||||
Debug { value, .. } => vec![*value],
|
Debug { value, .. } => vec![*value],
|
||||||
DebugLog { values, .. } => values.clone(),
|
DebugLog { values, .. } => values.clone(),
|
||||||
Throw { exception, .. } => vec![*exception],
|
Throw { exception, .. } => vec![*exception],
|
||||||
Catch { exception_value, .. } => vec![*exception_value],
|
Catch {
|
||||||
|
exception_value, ..
|
||||||
|
} => vec![*exception_value],
|
||||||
RefNew { dst, box_val } => vec![*dst, *box_val],
|
RefNew { dst, box_val } => vec![*dst, *box_val],
|
||||||
RefGet { dst, reference, .. } => vec![*dst, *reference],
|
RefGet { dst, reference, .. } => vec![*dst, *reference],
|
||||||
RefSet { reference, value, .. } => vec![*reference, *value],
|
RefSet {
|
||||||
|
reference, value, ..
|
||||||
|
} => vec![*reference, *value],
|
||||||
WeakNew { dst, box_val } => vec![*dst, *box_val],
|
WeakNew { dst, box_val } => vec![*dst, *box_val],
|
||||||
WeakLoad { dst, weak_ref } => vec![*dst, *weak_ref],
|
WeakLoad { dst, weak_ref } => vec![*dst, *weak_ref],
|
||||||
WeakRef { dst, value, .. } => vec![*dst, *value],
|
WeakRef { dst, value, .. } => vec![*dst, *value],
|
||||||
@ -136,7 +150,11 @@ impl JoinIrIdRemapper {
|
|||||||
Cast { dst, value, .. } => vec![*dst, *value],
|
Cast { dst, value, .. } => vec![*dst, *value],
|
||||||
TypeOp { dst, value, .. } => vec![*dst, *value],
|
TypeOp { dst, value, .. } => vec![*dst, *value],
|
||||||
ArrayGet { dst, array, index } => vec![*dst, *array, *index],
|
ArrayGet { dst, array, index } => vec![*dst, *array, *index],
|
||||||
ArraySet { array, index, value } => vec![*array, *index, *value],
|
ArraySet {
|
||||||
|
array,
|
||||||
|
index,
|
||||||
|
value,
|
||||||
|
} => vec![*array, *index, *value],
|
||||||
Jump { .. } | Nop | Safepoint => vec![],
|
Jump { .. } | Nop | Safepoint => vec![],
|
||||||
ExternCall { dst, args, .. } => {
|
ExternCall { dst, args, .. } => {
|
||||||
let mut vals = Vec::new();
|
let mut vals = Vec::new();
|
||||||
@ -185,14 +203,27 @@ impl JoinIrIdRemapper {
|
|||||||
value: remap(*value),
|
value: remap(*value),
|
||||||
ptr: remap(*ptr),
|
ptr: remap(*ptr),
|
||||||
},
|
},
|
||||||
Call { dst, func, callee, args, effects } => Call {
|
Call {
|
||||||
|
dst,
|
||||||
|
func,
|
||||||
|
callee,
|
||||||
|
args,
|
||||||
|
effects,
|
||||||
|
} => Call {
|
||||||
dst: dst.map(remap),
|
dst: dst.map(remap),
|
||||||
func: remap(*func),
|
func: remap(*func),
|
||||||
callee: callee.clone(),
|
callee: callee.clone(),
|
||||||
args: args.iter().map(|&a| remap(a)).collect(),
|
args: args.iter().map(|&a| remap(a)).collect(),
|
||||||
effects: *effects,
|
effects: *effects,
|
||||||
},
|
},
|
||||||
BoxCall { dst, box_val, method, method_id, args, effects } => BoxCall {
|
BoxCall {
|
||||||
|
dst,
|
||||||
|
box_val,
|
||||||
|
method,
|
||||||
|
method_id,
|
||||||
|
args,
|
||||||
|
effects,
|
||||||
|
} => BoxCall {
|
||||||
dst: dst.map(remap),
|
dst: dst.map(remap),
|
||||||
box_val: remap(*box_val),
|
box_val: remap(*box_val),
|
||||||
method: method.clone(),
|
method: method.clone(),
|
||||||
@ -200,7 +231,13 @@ impl JoinIrIdRemapper {
|
|||||||
args: args.iter().map(|&a| remap(a)).collect(),
|
args: args.iter().map(|&a| remap(a)).collect(),
|
||||||
effects: *effects,
|
effects: *effects,
|
||||||
},
|
},
|
||||||
PluginInvoke { dst, box_val, method, args, effects } => PluginInvoke {
|
PluginInvoke {
|
||||||
|
dst,
|
||||||
|
box_val,
|
||||||
|
method,
|
||||||
|
args,
|
||||||
|
effects,
|
||||||
|
} => PluginInvoke {
|
||||||
dst: dst.map(remap),
|
dst: dst.map(remap),
|
||||||
box_val: remap(*box_val),
|
box_val: remap(*box_val),
|
||||||
method: method.clone(),
|
method: method.clone(),
|
||||||
@ -211,16 +248,29 @@ impl JoinIrIdRemapper {
|
|||||||
dst: remap(*dst),
|
dst: remap(*dst),
|
||||||
src: remap(*src),
|
src: remap(*src),
|
||||||
},
|
},
|
||||||
NewBox { dst, box_type, args } => NewBox {
|
NewBox {
|
||||||
|
dst,
|
||||||
|
box_type,
|
||||||
|
args,
|
||||||
|
} => NewBox {
|
||||||
dst: remap(*dst),
|
dst: remap(*dst),
|
||||||
box_type: box_type.clone(),
|
box_type: box_type.clone(),
|
||||||
args: args.iter().map(|&a| remap(a)).collect(),
|
args: args.iter().map(|&a| remap(a)).collect(),
|
||||||
},
|
},
|
||||||
NewClosure { dst, params, body, captures, me } => NewClosure {
|
NewClosure {
|
||||||
|
dst,
|
||||||
|
params,
|
||||||
|
body,
|
||||||
|
captures,
|
||||||
|
me,
|
||||||
|
} => NewClosure {
|
||||||
dst: remap(*dst),
|
dst: remap(*dst),
|
||||||
params: params.clone(),
|
params: params.clone(),
|
||||||
body: body.clone(),
|
body: body.clone(),
|
||||||
captures: captures.iter().map(|(n, v)| (n.clone(), remap(*v))).collect(),
|
captures: captures
|
||||||
|
.iter()
|
||||||
|
.map(|(n, v)| (n.clone(), remap(*v)))
|
||||||
|
.collect(),
|
||||||
me: me.map(remap),
|
me: me.map(remap),
|
||||||
},
|
},
|
||||||
Print { value, effects } => Print {
|
Print { value, effects } => Print {
|
||||||
@ -239,7 +289,11 @@ impl JoinIrIdRemapper {
|
|||||||
exception: remap(*exception),
|
exception: remap(*exception),
|
||||||
effects: *effects,
|
effects: *effects,
|
||||||
},
|
},
|
||||||
Catch { exception_type, exception_value, handler_bb } => Catch {
|
Catch {
|
||||||
|
exception_type,
|
||||||
|
exception_value,
|
||||||
|
handler_bb,
|
||||||
|
} => Catch {
|
||||||
exception_type: exception_type.clone(),
|
exception_type: exception_type.clone(),
|
||||||
exception_value: remap(*exception_value),
|
exception_value: remap(*exception_value),
|
||||||
handler_bb: *handler_bb,
|
handler_bb: *handler_bb,
|
||||||
@ -248,12 +302,20 @@ impl JoinIrIdRemapper {
|
|||||||
dst: remap(*dst),
|
dst: remap(*dst),
|
||||||
box_val: remap(*box_val),
|
box_val: remap(*box_val),
|
||||||
},
|
},
|
||||||
RefGet { dst, reference, field } => RefGet {
|
RefGet {
|
||||||
|
dst,
|
||||||
|
reference,
|
||||||
|
field,
|
||||||
|
} => RefGet {
|
||||||
dst: remap(*dst),
|
dst: remap(*dst),
|
||||||
reference: remap(*reference),
|
reference: remap(*reference),
|
||||||
field: field.clone(),
|
field: field.clone(),
|
||||||
},
|
},
|
||||||
RefSet { reference, field, value } => RefSet {
|
RefSet {
|
||||||
|
reference,
|
||||||
|
field,
|
||||||
|
value,
|
||||||
|
} => RefSet {
|
||||||
reference: remap(*reference),
|
reference: remap(*reference),
|
||||||
field: field.clone(),
|
field: field.clone(),
|
||||||
value: remap(*value),
|
value: remap(*value),
|
||||||
@ -271,12 +333,8 @@ impl JoinIrIdRemapper {
|
|||||||
op: *op,
|
op: *op,
|
||||||
value: remap(*value),
|
value: remap(*value),
|
||||||
},
|
},
|
||||||
BarrierRead { ptr } => BarrierRead {
|
BarrierRead { ptr } => BarrierRead { ptr: remap(*ptr) },
|
||||||
ptr: remap(*ptr),
|
BarrierWrite { ptr } => BarrierWrite { ptr: remap(*ptr) },
|
||||||
},
|
|
||||||
BarrierWrite { ptr } => BarrierWrite {
|
|
||||||
ptr: remap(*ptr),
|
|
||||||
},
|
|
||||||
Barrier { op, ptr } => Barrier {
|
Barrier { op, ptr } => Barrier {
|
||||||
op: *op,
|
op: *op,
|
||||||
ptr: remap(*ptr),
|
ptr: remap(*ptr),
|
||||||
@ -293,12 +351,20 @@ impl JoinIrIdRemapper {
|
|||||||
dst: remap(*dst),
|
dst: remap(*dst),
|
||||||
future: remap(*future),
|
future: remap(*future),
|
||||||
},
|
},
|
||||||
TypeCheck { dst, value, expected_type } => TypeCheck {
|
TypeCheck {
|
||||||
|
dst,
|
||||||
|
value,
|
||||||
|
expected_type,
|
||||||
|
} => TypeCheck {
|
||||||
dst: remap(*dst),
|
dst: remap(*dst),
|
||||||
value: remap(*value),
|
value: remap(*value),
|
||||||
expected_type: expected_type.clone(),
|
expected_type: expected_type.clone(),
|
||||||
},
|
},
|
||||||
Cast { dst, value, target_type } => Cast {
|
Cast {
|
||||||
|
dst,
|
||||||
|
value,
|
||||||
|
target_type,
|
||||||
|
} => Cast {
|
||||||
dst: remap(*dst),
|
dst: remap(*dst),
|
||||||
value: remap(*value),
|
value: remap(*value),
|
||||||
target_type: target_type.clone(),
|
target_type: target_type.clone(),
|
||||||
@ -314,12 +380,22 @@ impl JoinIrIdRemapper {
|
|||||||
array: remap(*array),
|
array: remap(*array),
|
||||||
index: remap(*index),
|
index: remap(*index),
|
||||||
},
|
},
|
||||||
ArraySet { array, index, value } => ArraySet {
|
ArraySet {
|
||||||
|
array,
|
||||||
|
index,
|
||||||
|
value,
|
||||||
|
} => ArraySet {
|
||||||
array: remap(*array),
|
array: remap(*array),
|
||||||
index: remap(*index),
|
index: remap(*index),
|
||||||
value: remap(*value),
|
value: remap(*value),
|
||||||
},
|
},
|
||||||
ExternCall { dst, iface_name, method_name, args, effects } => ExternCall {
|
ExternCall {
|
||||||
|
dst,
|
||||||
|
iface_name,
|
||||||
|
method_name,
|
||||||
|
args,
|
||||||
|
effects,
|
||||||
|
} => ExternCall {
|
||||||
dst: dst.map(remap),
|
dst: dst.map(remap),
|
||||||
iface_name: iface_name.clone(),
|
iface_name: iface_name.clone(),
|
||||||
method_name: method_name.clone(),
|
method_name: method_name.clone(),
|
||||||
@ -327,12 +403,13 @@ impl JoinIrIdRemapper {
|
|||||||
effects: *effects,
|
effects: *effects,
|
||||||
},
|
},
|
||||||
// Phase 189 FIX: Remap PHI dst and input values (BlockId remapping is done in control_flow.rs)
|
// Phase 189 FIX: Remap PHI dst and input values (BlockId remapping is done in control_flow.rs)
|
||||||
Phi { dst, inputs, type_hint } => Phi {
|
Phi {
|
||||||
|
dst,
|
||||||
|
inputs,
|
||||||
|
type_hint,
|
||||||
|
} => Phi {
|
||||||
dst: remap(*dst),
|
dst: remap(*dst),
|
||||||
inputs: inputs
|
inputs: inputs.iter().map(|(bb, val)| (*bb, remap(*val))).collect(),
|
||||||
.iter()
|
|
||||||
.map(|(bb, val)| (*bb, remap(*val)))
|
|
||||||
.collect(),
|
|
||||||
type_hint: type_hint.clone(),
|
type_hint: type_hint.clone(),
|
||||||
},
|
},
|
||||||
// Pass through unchanged (Branch/Jump/Return handled separately)
|
// Pass through unchanged (Branch/Jump/Return handled separately)
|
||||||
|
|||||||
@ -5,9 +5,9 @@
|
|||||||
//! - Entry block への Copy instruction 挿入
|
//! - Entry block への Copy instruction 挿入
|
||||||
//! - SSA 値空間の接続
|
//! - SSA 値空間の接続
|
||||||
|
|
||||||
use std::collections::BTreeMap; // Phase 222.5-E: HashMap → BTreeMap for determinism
|
|
||||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
|
||||||
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
||||||
|
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||||
|
use std::collections::BTreeMap; // Phase 222.5-E: HashMap → BTreeMap for determinism
|
||||||
|
|
||||||
pub struct BoundaryInjector;
|
pub struct BoundaryInjector;
|
||||||
|
|
||||||
@ -56,10 +56,11 @@ impl BoundaryInjector {
|
|||||||
func: &mut MirFunction,
|
func: &mut MirFunction,
|
||||||
entry_block_id: BasicBlockId,
|
entry_block_id: BasicBlockId,
|
||||||
boundary: &JoinInlineBoundary,
|
boundary: &JoinInlineBoundary,
|
||||||
value_map: &BTreeMap<ValueId, ValueId>, // Phase 222.5-E: HashMap → BTreeMap for determinism
|
value_map: &BTreeMap<ValueId, ValueId>, // Phase 222.5-E: HashMap → BTreeMap for determinism
|
||||||
phi_dst_ids: &std::collections::HashSet<ValueId>,
|
phi_dst_ids: &std::collections::HashSet<ValueId>,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
) -> Result<BTreeMap<ValueId, ValueId>, String> { // Phase 222.5-E: HashMap → BTreeMap for determinism
|
) -> Result<BTreeMap<ValueId, ValueId>, String> {
|
||||||
|
// Phase 222.5-E: HashMap → BTreeMap for determinism
|
||||||
// Phase 33-20: When loop_var_name is set, ALL join_inputs are handled by header PHIs
|
// Phase 33-20: When loop_var_name is set, ALL join_inputs are handled by header PHIs
|
||||||
// This includes the loop variable AND all other carriers from exit_bindings.
|
// This includes the loop variable AND all other carriers from exit_bindings.
|
||||||
// We skip ALL join_inputs Copy instructions, only condition_bindings remain.
|
// We skip ALL join_inputs Copy instructions, only condition_bindings remain.
|
||||||
@ -67,7 +68,7 @@ impl BoundaryInjector {
|
|||||||
|
|
||||||
// Phase 171-fix: Check both join_inputs and condition_bindings
|
// Phase 171-fix: Check both join_inputs and condition_bindings
|
||||||
let effective_join_inputs = if skip_all_join_inputs {
|
let effective_join_inputs = if skip_all_join_inputs {
|
||||||
0 // Phase 33-20: All join_inputs are handled by header PHIs
|
0 // Phase 33-20: All join_inputs are handled by header PHIs
|
||||||
} else {
|
} else {
|
||||||
boundary.join_inputs.len()
|
boundary.join_inputs.len()
|
||||||
};
|
};
|
||||||
@ -104,7 +105,10 @@ impl BoundaryInjector {
|
|||||||
let mut reallocations = BTreeMap::new();
|
let mut reallocations = BTreeMap::new();
|
||||||
|
|
||||||
for binding in &boundary.condition_bindings {
|
for binding in &boundary.condition_bindings {
|
||||||
let remapped_join = value_map.get(&binding.join_value).copied().unwrap_or(binding.join_value);
|
let remapped_join = value_map
|
||||||
|
.get(&binding.join_value)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(binding.join_value);
|
||||||
|
|
||||||
if phi_dst_ids.contains(&remapped_join) {
|
if phi_dst_ids.contains(&remapped_join) {
|
||||||
// Collision detected! Allocate a fresh ValueId
|
// Collision detected! Allocate a fresh ValueId
|
||||||
@ -132,29 +136,27 @@ impl BoundaryInjector {
|
|||||||
// Phase 171: Inject Copy instructions for join_inputs (loop parameters)
|
// Phase 171: Inject Copy instructions for join_inputs (loop parameters)
|
||||||
// Phase 33-20: Skip ALL join_inputs when loop_var_name is set (header PHIs handle them)
|
// Phase 33-20: Skip ALL join_inputs when loop_var_name is set (header PHIs handle them)
|
||||||
if !skip_all_join_inputs {
|
if !skip_all_join_inputs {
|
||||||
for (join_input, host_input) in boundary
|
for (join_input, host_input) in
|
||||||
.join_inputs
|
boundary.join_inputs.iter().zip(boundary.host_inputs.iter())
|
||||||
.iter()
|
|
||||||
.zip(boundary.host_inputs.iter())
|
|
||||||
{
|
{
|
||||||
// リマップ後の ValueId を取得
|
// リマップ後の ValueId を取得
|
||||||
let remapped_join = value_map.get(join_input).copied().unwrap_or(*join_input);
|
let remapped_join = value_map.get(join_input).copied().unwrap_or(*join_input);
|
||||||
let remapped_host = *host_input; // host_input is already in host space
|
let remapped_host = *host_input; // host_input is already in host space
|
||||||
|
|
||||||
// Copy instruction: remapped_join = Copy remapped_host
|
// Copy instruction: remapped_join = Copy remapped_host
|
||||||
let copy_inst = MirInstruction::Copy {
|
let copy_inst = MirInstruction::Copy {
|
||||||
dst: remapped_join,
|
dst: remapped_join,
|
||||||
src: remapped_host,
|
src: remapped_host,
|
||||||
};
|
};
|
||||||
|
|
||||||
copy_instructions.push(copy_inst);
|
copy_instructions.push(copy_inst);
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[BoundaryInjector] Join input: Copy {:?} = Copy {:?}",
|
"[BoundaryInjector] Join input: Copy {:?} = Copy {:?}",
|
||||||
remapped_join, remapped_host
|
remapped_join, remapped_host
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,10 +180,16 @@ impl BoundaryInjector {
|
|||||||
// Phase 177-3 Option B: Use pre-allocated reallocations for PHI collision cases
|
// Phase 177-3 Option B: Use pre-allocated reallocations for PHI collision cases
|
||||||
for binding in &boundary.condition_bindings {
|
for binding in &boundary.condition_bindings {
|
||||||
// Look up the remapped JoinIR ValueId from value_map
|
// Look up the remapped JoinIR ValueId from value_map
|
||||||
let remapped_join = value_map.get(&binding.join_value).copied().unwrap_or(binding.join_value);
|
let remapped_join = value_map
|
||||||
|
.get(&binding.join_value)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(binding.join_value);
|
||||||
|
|
||||||
// Phase 177-3 Option B: Check if this binding was reallocated (PHI collision case)
|
// Phase 177-3 Option B: Check if this binding was reallocated (PHI collision case)
|
||||||
let final_dst = reallocations.get(&binding.join_value).copied().unwrap_or(remapped_join);
|
let final_dst = reallocations
|
||||||
|
.get(&binding.join_value)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(remapped_join);
|
||||||
|
|
||||||
// Copy instruction: final_dst = Copy host_value
|
// Copy instruction: final_dst = Copy host_value
|
||||||
let copy_inst = MirInstruction::Copy {
|
let copy_inst = MirInstruction::Copy {
|
||||||
@ -203,7 +211,9 @@ impl BoundaryInjector {
|
|||||||
// Entry block の先頭に Copy instructions を挿入
|
// Entry block の先頭に Copy instructions を挿入
|
||||||
// Reverse order to preserve original order when inserting at position 0
|
// Reverse order to preserve original order when inserting at position 0
|
||||||
// Phase 189 FIX: Also insert corresponding spans
|
// Phase 189 FIX: Also insert corresponding spans
|
||||||
let default_span = entry_block.instruction_spans.first()
|
let default_span = entry_block
|
||||||
|
.instruction_spans
|
||||||
|
.first()
|
||||||
.copied()
|
.copied()
|
||||||
.unwrap_or_else(crate::ast::Span::unknown);
|
.unwrap_or_else(crate::ast::Span::unknown);
|
||||||
for inst in copy_instructions.into_iter().rev() {
|
for inst in copy_instructions.into_iter().rev() {
|
||||||
|
|||||||
@ -341,9 +341,11 @@ impl super::MirBuilder {
|
|||||||
// P3-D は「既知メソッドの戻り値型」を直接推論する。
|
// P3-D は「既知メソッドの戻り値型」を直接推論する。
|
||||||
// BoxCall の method 名から TypeAnnotationBox と同じマッピングで型を取得。
|
// BoxCall の method 名から TypeAnnotationBox と同じマッピングで型を取得。
|
||||||
if hint.is_none() {
|
if hint.is_none() {
|
||||||
if let Some(mt) =
|
if let Some(mt) = MethodReturnHintBox::resolve_for_return(
|
||||||
MethodReturnHintBox::resolve_for_return(&function, *v, &self.value_types)
|
&function,
|
||||||
{
|
*v,
|
||||||
|
&self.value_types,
|
||||||
|
) {
|
||||||
if std::env::var("NYASH_P3D_DEBUG").is_ok() {
|
if std::env::var("NYASH_P3D_DEBUG").is_ok() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[lifecycle/p3d] {} type inferred via MethodReturnHintBox: {:?}",
|
"[lifecycle/p3d] {} type inferred via MethodReturnHintBox: {:?}",
|
||||||
|
|||||||
@ -158,9 +158,9 @@ impl LoopFrontendBinding {
|
|||||||
Self {
|
Self {
|
||||||
counter_var: "i".to_string(),
|
counter_var: "i".to_string(),
|
||||||
counter_init: 0,
|
counter_init: 0,
|
||||||
accumulator_var: None, // No accumulator
|
accumulator_var: None, // No accumulator
|
||||||
bound_expr: BoundExpr::Constant(3), // Constant bound
|
bound_expr: BoundExpr::Constant(3), // Constant bound
|
||||||
external_refs: vec![], // No external refs
|
external_refs: vec![], // No external refs
|
||||||
pattern: LoopPattern::Simple {
|
pattern: LoopPattern::Simple {
|
||||||
has_accumulator: false,
|
has_accumulator: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -16,18 +16,20 @@ fn enabled() -> bool {
|
|||||||
/// Trace when a newbox/class origin is registered.
|
/// Trace when a newbox/class origin is registered.
|
||||||
pub fn origin(event: &str, vid: ValueId, class: &str) {
|
pub fn origin(event: &str, vid: ValueId, class: &str) {
|
||||||
if enabled() {
|
if enabled() {
|
||||||
get_global_ring0()
|
get_global_ring0().log.debug(&format!(
|
||||||
.log
|
"[type-trace] origin:{} %{} ← {}",
|
||||||
.debug(&format!("[type-trace] origin:{} %{} ← {}", event, vid.0, class));
|
event, vid.0, class
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trace when a concrete MirType is recorded.
|
/// Trace when a concrete MirType is recorded.
|
||||||
pub fn ty(event: &str, vid: ValueId, ty: &MirType) {
|
pub fn ty(event: &str, vid: ValueId, ty: &MirType) {
|
||||||
if enabled() {
|
if enabled() {
|
||||||
get_global_ring0()
|
get_global_ring0().log.debug(&format!(
|
||||||
.log
|
"[type-trace] type:{} %{} ← {:?}",
|
||||||
.debug(&format!("[type-trace] type:{} %{} ← {:?}", event, vid.0, ty));
|
event, vid.0, ty
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -81,7 +81,7 @@ impl TypeRegistry {
|
|||||||
source: format!("param:{}:{:?}", param_name, ty),
|
source: format!("param:{}:{:?}", param_name, ty),
|
||||||
timestamp: self.trace_log.len(),
|
timestamp: self.trace_log.len(),
|
||||||
};
|
};
|
||||||
self.trace_log.push(entry.clone());
|
self.trace_log.push(entry.clone());
|
||||||
get_global_ring0()
|
get_global_ring0()
|
||||||
.log
|
.log
|
||||||
.debug(&format!("[type-registry] {} {:?}", entry.source, vid));
|
.debug(&format!("[type-registry] {} {:?}", entry.source, vid));
|
||||||
@ -217,9 +217,10 @@ impl TypeRegistry {
|
|||||||
|
|
||||||
// 最終フォールバック: UnknownBox
|
// 最終フォールバック: UnknownBox
|
||||||
if self.trace_enabled {
|
if self.trace_enabled {
|
||||||
get_global_ring0()
|
get_global_ring0().log.warn(&format!(
|
||||||
.log
|
"[type-registry] WARNING: UnknownBox for %{}",
|
||||||
.warn(&format!("[type-registry] WARNING: UnknownBox for %{}", vid.0));
|
vid.0
|
||||||
|
));
|
||||||
}
|
}
|
||||||
"UnknownBox".to_string()
|
"UnknownBox".to_string()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,33 +72,23 @@ impl HintSink {
|
|||||||
match cfg.sink {
|
match cfg.sink {
|
||||||
HintSinkTarget::None => {}
|
HintSinkTarget::None => {}
|
||||||
HintSinkTarget::Stderr => match hint {
|
HintSinkTarget::Stderr => match hint {
|
||||||
HintKind::ScopeEnter(id) => {
|
HintKind::ScopeEnter(id) => get_global_ring0()
|
||||||
get_global_ring0()
|
.log
|
||||||
.log
|
.debug(&format!("[mir][hint] ScopeEnter({})", id)),
|
||||||
.debug(&format!("[mir][hint] ScopeEnter({})", id))
|
HintKind::ScopeLeave(id) => get_global_ring0()
|
||||||
}
|
.log
|
||||||
HintKind::ScopeLeave(id) => {
|
.debug(&format!("[mir][hint] ScopeLeave({})", id)),
|
||||||
get_global_ring0()
|
|
||||||
.log
|
|
||||||
.debug(&format!("[mir][hint] ScopeLeave({})", id))
|
|
||||||
}
|
|
||||||
HintKind::Defer(calls) => get_global_ring0()
|
HintKind::Defer(calls) => get_global_ring0()
|
||||||
.log
|
.log
|
||||||
.debug(&format!("[mir][hint] Defer({})", calls.join(";"))),
|
.debug(&format!("[mir][hint] Defer({})", calls.join(";"))),
|
||||||
HintKind::JoinResult(var) => get_global_ring0()
|
HintKind::JoinResult(var) => get_global_ring0()
|
||||||
.log
|
.log
|
||||||
.debug(&format!("[mir][hint] JoinResult({})", var)),
|
.debug(&format!("[mir][hint] JoinResult({})", var)),
|
||||||
HintKind::LoopCarrier(vars) => {
|
HintKind::LoopCarrier(vars) => get_global_ring0()
|
||||||
get_global_ring0()
|
.log
|
||||||
.log
|
.debug(&format!("[mir][hint] LoopCarrier({})", vars.join(","))),
|
||||||
.debug(&format!("[mir][hint] LoopCarrier({})", vars.join(",")))
|
HintKind::LoopHeader => get_global_ring0().log.debug("[mir][hint] LoopHeader"),
|
||||||
}
|
HintKind::LoopLatch => get_global_ring0().log.debug("[mir][hint] LoopLatch"),
|
||||||
HintKind::LoopHeader => {
|
|
||||||
get_global_ring0().log.debug("[mir][hint] LoopHeader")
|
|
||||||
}
|
|
||||||
HintKind::LoopLatch => {
|
|
||||||
get_global_ring0().log.debug("[mir][hint] LoopLatch")
|
|
||||||
}
|
|
||||||
HintKind::NoEmptyPhi => get_global_ring0().log.debug("[mir][hint] NoEmptyPhi"),
|
HintKind::NoEmptyPhi => get_global_ring0().log.debug("[mir][hint] NoEmptyPhi"),
|
||||||
},
|
},
|
||||||
HintSinkTarget::Jsonl(ref path) => {
|
HintSinkTarget::Jsonl(ref path) => {
|
||||||
|
|||||||
@ -150,12 +150,17 @@ impl MirInstruction {
|
|||||||
// Handle Call instructions here (not in inst_meta) because CallLikeInst
|
// Handle Call instructions here (not in inst_meta) because CallLikeInst
|
||||||
// doesn't have the callee field needed for Callee::Method receiver handling.
|
// doesn't have the callee field needed for Callee::Method receiver handling.
|
||||||
// This is the single source of truth for Call's used values.
|
// This is the single source of truth for Call's used values.
|
||||||
if let MirInstruction::Call { callee, func, args, .. } = self {
|
if let MirInstruction::Call {
|
||||||
|
callee, func, args, ..
|
||||||
|
} = self
|
||||||
|
{
|
||||||
use crate::mir::definitions::call_unified::Callee;
|
use crate::mir::definitions::call_unified::Callee;
|
||||||
let mut used: Vec<ValueId> = Vec::new();
|
let mut used: Vec<ValueId> = Vec::new();
|
||||||
match callee {
|
match callee {
|
||||||
// Unified path: Callee::Method with receiver
|
// Unified path: Callee::Method with receiver
|
||||||
Some(Callee::Method { receiver: Some(r), .. }) => {
|
Some(Callee::Method {
|
||||||
|
receiver: Some(r), ..
|
||||||
|
}) => {
|
||||||
used.push(*r);
|
used.push(*r);
|
||||||
}
|
}
|
||||||
// Legacy path: func ValueId is the callable
|
// Legacy path: func ValueId is the callable
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
//! - 単一関数: cond 評価 → Select(cond, then_val, else_val) → Ret
|
//! - 単一関数: cond 評価 → Select(cond, then_val, else_val) → Ret
|
||||||
|
|
||||||
use super::{AstToJoinIrLowerer, BTreeMap, ExtractCtx, JoinFunction, JoinInst, JoinModule};
|
use super::{AstToJoinIrLowerer, BTreeMap, ExtractCtx, JoinFunction, JoinInst, JoinModule};
|
||||||
|
use crate::mir::join_ir::JoinIrPhase;
|
||||||
|
|
||||||
impl AstToJoinIrLowerer {
|
impl AstToJoinIrLowerer {
|
||||||
/// If Return pattern の共通 lowering
|
/// If Return pattern の共通 lowering
|
||||||
@ -152,6 +153,7 @@ impl AstToJoinIrLowerer {
|
|||||||
JoinModule {
|
JoinModule {
|
||||||
functions,
|
functions,
|
||||||
entry: Some(func_id),
|
entry: Some(func_id),
|
||||||
|
phase: JoinIrPhase::Structured,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
//! - `create_k_exit_function()`: k_exit 関数生成
|
//! - `create_k_exit_function()`: k_exit 関数生成
|
||||||
|
|
||||||
use super::{AstToJoinIrLowerer, JoinModule};
|
use super::{AstToJoinIrLowerer, JoinModule};
|
||||||
|
use crate::mir::join_ir::JoinIrPhase;
|
||||||
use crate::mir::join_ir::{JoinFuncId, JoinFunction, JoinInst};
|
use crate::mir::join_ir::{JoinFuncId, JoinFunction, JoinInst};
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@ -403,5 +404,6 @@ pub fn build_join_module(
|
|||||||
JoinModule {
|
JoinModule {
|
||||||
functions,
|
functions,
|
||||||
entry: Some(entry_id),
|
entry: Some(entry_id),
|
||||||
|
phase: JoinIrPhase::Structured,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
use super::BTreeMap;
|
use super::BTreeMap;
|
||||||
use super::{AstToJoinIrLowerer, ConstValue, ExtractCtx, JoinFunction, JoinInst, JoinModule};
|
use super::{AstToJoinIrLowerer, ConstValue, ExtractCtx, JoinFunction, JoinInst, JoinModule};
|
||||||
|
use crate::mir::join_ir::JoinIrPhase;
|
||||||
|
|
||||||
impl AstToJoinIrLowerer {
|
impl AstToJoinIrLowerer {
|
||||||
/// Phase 34-8: Break/Continue 付きループの lowering(パターン検出)
|
/// Phase 34-8: Break/Continue 付きループの lowering(パターン検出)
|
||||||
@ -435,6 +436,7 @@ impl AstToJoinIrLowerer {
|
|||||||
JoinModule {
|
JoinModule {
|
||||||
functions,
|
functions,
|
||||||
entry: Some(entry_id),
|
entry: Some(entry_id),
|
||||||
|
phase: JoinIrPhase::Structured,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,6 +644,7 @@ impl AstToJoinIrLowerer {
|
|||||||
JoinModule {
|
JoinModule {
|
||||||
functions,
|
functions,
|
||||||
entry: Some(entry_id),
|
entry: Some(entry_id),
|
||||||
|
phase: JoinIrPhase::Structured,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -889,6 +892,7 @@ impl AstToJoinIrLowerer {
|
|||||||
JoinModule {
|
JoinModule {
|
||||||
functions,
|
functions,
|
||||||
entry: Some(entry_id),
|
entry: Some(entry_id),
|
||||||
|
phase: JoinIrPhase::Structured,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,6 +43,56 @@ mod tests;
|
|||||||
pub(crate) use context::ExtractCtx;
|
pub(crate) use context::ExtractCtx;
|
||||||
pub(crate) use stmt_handlers::StatementEffect;
|
pub(crate) use stmt_handlers::StatementEffect;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum FunctionRoute {
|
||||||
|
IfReturn,
|
||||||
|
LoopFrontend,
|
||||||
|
NestedIf,
|
||||||
|
ReadQuoted,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_function_route(func_name: &str) -> Result<FunctionRoute, String> {
|
||||||
|
const IF_RETURN_NAMES: &[&str] = &["test", "local", "_read_value_from_pair"];
|
||||||
|
const LOOP_NAMES: &[&str] = &["simple", "filter", "print_tokens", "map", "reduce", "fold", "jsonparser_skip_ws_mini"];
|
||||||
|
|
||||||
|
if IF_RETURN_NAMES.contains(&func_name) {
|
||||||
|
return Ok(FunctionRoute::IfReturn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if LOOP_NAMES.contains(&func_name) {
|
||||||
|
return Ok(FunctionRoute::LoopFrontend);
|
||||||
|
}
|
||||||
|
|
||||||
|
if func_name == "parse_loop" {
|
||||||
|
if crate::config::env::joinir_dev_enabled()
|
||||||
|
&& std::env::var("HAKO_JOINIR_NESTED_IF").ok().as_deref() == Some("1")
|
||||||
|
{
|
||||||
|
return Ok(FunctionRoute::NestedIf);
|
||||||
|
}
|
||||||
|
return Err(
|
||||||
|
"[joinir/frontend] 'parse_loop' requires HAKO_JOINIR_NESTED_IF=1 (dev only)"
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if func_name == "read_quoted_from" {
|
||||||
|
if crate::config::env::joinir_dev_enabled()
|
||||||
|
&& std::env::var("HAKO_JOINIR_READ_QUOTED").ok().as_deref() == Some("1")
|
||||||
|
{
|
||||||
|
return Ok(FunctionRoute::ReadQuoted);
|
||||||
|
}
|
||||||
|
return Err(
|
||||||
|
"[joinir/frontend] 'read_quoted_from' requires HAKO_JOINIR_READ_QUOTED=1 (dev only)"
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(format!(
|
||||||
|
"[joinir/frontend] unsupported function '{}' (dev fixture not registered)",
|
||||||
|
func_name
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// AST/CFG → JoinIR 変換器
|
/// AST/CFG → JoinIR 変換器
|
||||||
///
|
///
|
||||||
/// Phase 34-2: Program(JSON v0) から tiny IfSelect ケースを JoinIR に変換
|
/// Phase 34-2: Program(JSON v0) から tiny IfSelect ケースを JoinIR に変換
|
||||||
@ -85,51 +135,17 @@ impl AstToJoinIrLowerer {
|
|||||||
.as_str()
|
.as_str()
|
||||||
.expect("Function must have 'name'");
|
.expect("Function must have 'name'");
|
||||||
|
|
||||||
// 3. 関数名で分岐(Phase P3: LoopFrontendBinding 層導入)
|
let route = resolve_function_route(func_name)
|
||||||
//
|
.unwrap_or_else(|msg| panic!("{msg}"));
|
||||||
// パターン分類:
|
|
||||||
// - If Return pattern: test/local/_read_value_from_pair
|
match route {
|
||||||
// - Loop pattern: simple 等 → LoopFrontendBinding 経由
|
FunctionRoute::IfReturn => self.lower_if_return_pattern(program_json),
|
||||||
// - NestedIfMerge pattern: parse_loop (dev flag gated)
|
FunctionRoute::LoopFrontend => loop_frontend_binding::lower_loop_by_function_name(
|
||||||
// - ReadQuoted pattern: read_quoted_from (dev flag gated)
|
self,
|
||||||
match func_name {
|
program_json,
|
||||||
"test" | "local" | "_read_value_from_pair" => {
|
),
|
||||||
self.lower_if_return_pattern(program_json)
|
FunctionRoute::NestedIf => self.lower_nested_if_pattern(program_json),
|
||||||
}
|
FunctionRoute::ReadQuoted => self.lower_read_quoted_pattern(program_json),
|
||||||
"simple" | "filter" | "print_tokens" | "map" | "reduce" | "fold" => {
|
|
||||||
// Phase P3: LoopFrontendBinding 層経由でディスパッチ
|
|
||||||
loop_frontend_binding::lower_loop_by_function_name(self, program_json)
|
|
||||||
}
|
|
||||||
"parse_loop" => {
|
|
||||||
// Phase 41-4: NestedIfMerge pattern (dev flag gated)
|
|
||||||
// Guard: JoinIR dev + HAKO_JOINIR_NESTED_IF=1 を要求
|
|
||||||
if crate::config::env::joinir_dev_enabled()
|
|
||||||
&& std::env::var("HAKO_JOINIR_NESTED_IF").ok().as_deref() == Some("1")
|
|
||||||
{
|
|
||||||
self.lower_nested_if_pattern(program_json)
|
|
||||||
} else {
|
|
||||||
// Dev flag が OFF の場合は panic(旧ルートにフォールバックするため)
|
|
||||||
panic!(
|
|
||||||
"parse_loop NestedIfMerge requires HAKO_JOINIR_NESTED_IF=1. \
|
|
||||||
Set env var to enable Phase 41-4 route."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"read_quoted_from" => {
|
|
||||||
// Phase 45: read_quoted_from pattern (dev flag gated)
|
|
||||||
// Guard if + Loop with break + accumulator
|
|
||||||
if crate::config::env::joinir_dev_enabled()
|
|
||||||
&& std::env::var("HAKO_JOINIR_READ_QUOTED").ok().as_deref() == Some("1")
|
|
||||||
{
|
|
||||||
self.lower_read_quoted_pattern(program_json)
|
|
||||||
} else {
|
|
||||||
panic!(
|
|
||||||
"read_quoted_from JoinIR requires HAKO_JOINIR_READ_QUOTED=1. \
|
|
||||||
Set env var to enable Phase 45 route."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => panic!("Unsupported function: {}", func_name),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
use super::BTreeMap;
|
use super::BTreeMap;
|
||||||
use super::{AstToJoinIrLowerer, ExtractCtx, JoinFunction, JoinInst, JoinModule};
|
use super::{AstToJoinIrLowerer, ExtractCtx, JoinFunction, JoinInst, JoinModule};
|
||||||
|
use crate::mir::join_ir::JoinIrPhase;
|
||||||
|
|
||||||
impl AstToJoinIrLowerer {
|
impl AstToJoinIrLowerer {
|
||||||
/// Phase 41-4.2: ネスト if パターンの lowering
|
/// Phase 41-4.2: ネスト if パターンの lowering
|
||||||
@ -193,6 +194,7 @@ impl AstToJoinIrLowerer {
|
|||||||
JoinModule {
|
JoinModule {
|
||||||
functions,
|
functions,
|
||||||
entry: Some(func_id),
|
entry: Some(func_id),
|
||||||
|
phase: JoinIrPhase::Structured,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,7 @@ use super::BTreeMap;
|
|||||||
use super::{
|
use super::{
|
||||||
AstToJoinIrLowerer, ConstValue, ExtractCtx, JoinFunction, JoinInst, JoinModule, MergePair,
|
AstToJoinIrLowerer, ConstValue, ExtractCtx, JoinFunction, JoinInst, JoinModule, MergePair,
|
||||||
};
|
};
|
||||||
|
use crate::mir::join_ir::JoinIrPhase;
|
||||||
|
|
||||||
impl AstToJoinIrLowerer {
|
impl AstToJoinIrLowerer {
|
||||||
/// Phase 45: read_quoted_from パターンの lowering
|
/// Phase 45: read_quoted_from パターンの lowering
|
||||||
@ -494,6 +495,7 @@ impl AstToJoinIrLowerer {
|
|||||||
JoinModule {
|
JoinModule {
|
||||||
functions,
|
functions,
|
||||||
entry: Some(entry_id),
|
entry: Some(entry_id),
|
||||||
|
phase: JoinIrPhase::Structured,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -110,13 +110,12 @@ impl<'a> BoolExprLowerer<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Emit Compare instruction
|
// Emit Compare instruction
|
||||||
self.builder
|
self.builder.emit_instruction(MirInstruction::Compare {
|
||||||
.emit_instruction(MirInstruction::Compare {
|
dst,
|
||||||
dst,
|
op: cmp_op,
|
||||||
op: cmp_op,
|
lhs,
|
||||||
lhs,
|
rhs,
|
||||||
rhs,
|
})?;
|
||||||
})?;
|
|
||||||
|
|
||||||
// Mark result type as Bool
|
// Mark result type as Bool
|
||||||
self.builder.value_types.insert(dst, MirType::Bool);
|
self.builder.value_types.insert(dst, MirType::Bool);
|
||||||
@ -130,13 +129,12 @@ impl<'a> BoolExprLowerer<'a> {
|
|||||||
let dst = self.builder.next_value_id();
|
let dst = self.builder.next_value_id();
|
||||||
|
|
||||||
// Emit BinOp And instruction
|
// Emit BinOp And instruction
|
||||||
self.builder
|
self.builder.emit_instruction(MirInstruction::BinOp {
|
||||||
.emit_instruction(MirInstruction::BinOp {
|
dst,
|
||||||
dst,
|
op: BinaryOp::BitAnd, // Use BitAnd for logical AND
|
||||||
op: BinaryOp::BitAnd, // Use BitAnd for logical AND
|
lhs,
|
||||||
lhs,
|
rhs,
|
||||||
rhs,
|
})?;
|
||||||
})?;
|
|
||||||
|
|
||||||
// Mark result type as Bool
|
// Mark result type as Bool
|
||||||
self.builder.value_types.insert(dst, MirType::Bool);
|
self.builder.value_types.insert(dst, MirType::Bool);
|
||||||
@ -150,13 +148,12 @@ impl<'a> BoolExprLowerer<'a> {
|
|||||||
let dst = self.builder.next_value_id();
|
let dst = self.builder.next_value_id();
|
||||||
|
|
||||||
// Emit BinOp Or instruction
|
// Emit BinOp Or instruction
|
||||||
self.builder
|
self.builder.emit_instruction(MirInstruction::BinOp {
|
||||||
.emit_instruction(MirInstruction::BinOp {
|
dst,
|
||||||
dst,
|
op: BinaryOp::BitOr, // Use BitOr for logical OR
|
||||||
op: BinaryOp::BitOr, // Use BitOr for logical OR
|
lhs,
|
||||||
lhs,
|
rhs,
|
||||||
rhs,
|
})?;
|
||||||
})?;
|
|
||||||
|
|
||||||
// Mark result type as Bool
|
// Mark result type as Bool
|
||||||
self.builder.value_types.insert(dst, MirType::Bool);
|
self.builder.value_types.insert(dst, MirType::Bool);
|
||||||
@ -179,12 +176,11 @@ impl<'a> BoolExprLowerer<'a> {
|
|||||||
let dst = self.builder.next_value_id();
|
let dst = self.builder.next_value_id();
|
||||||
|
|
||||||
// Emit UnaryOp Not instruction
|
// Emit UnaryOp Not instruction
|
||||||
self.builder
|
self.builder.emit_instruction(MirInstruction::UnaryOp {
|
||||||
.emit_instruction(MirInstruction::UnaryOp {
|
dst,
|
||||||
dst,
|
op: crate::mir::UnaryOp::Not,
|
||||||
op: crate::mir::UnaryOp::Not,
|
operand: operand_val,
|
||||||
operand: operand_val,
|
})?;
|
||||||
})?;
|
|
||||||
|
|
||||||
// Mark result type as Bool
|
// Mark result type as Bool
|
||||||
self.builder.value_types.insert(dst, MirType::Bool);
|
self.builder.value_types.insert(dst, MirType::Bool);
|
||||||
@ -217,7 +213,7 @@ impl<'a> BoolExprLowerer<'a> {
|
|||||||
// use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span};
|
// use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span};
|
||||||
// use crate::mir::builder::MirBuilder;
|
// use crate::mir::builder::MirBuilder;
|
||||||
// use crate::mir::FunctionSignature;
|
// use crate::mir::FunctionSignature;
|
||||||
//
|
//
|
||||||
// /// Helper to create a test MirBuilder
|
// /// Helper to create a test MirBuilder
|
||||||
// fn create_test_builder() -> MirBuilder {
|
// fn create_test_builder() -> MirBuilder {
|
||||||
// let mut builder = MirBuilder::new();
|
// let mut builder = MirBuilder::new();
|
||||||
@ -232,13 +228,13 @@ impl<'a> BoolExprLowerer<'a> {
|
|||||||
// builder.start_new_block();
|
// builder.start_new_block();
|
||||||
// builder
|
// builder
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// /// Test: Simple comparison (i < 10)
|
// /// Test: Simple comparison (i < 10)
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn test_simple_comparison() {
|
// fn test_simple_comparison() {
|
||||||
// let mut builder = create_test_builder();
|
// let mut builder = create_test_builder();
|
||||||
// let mut lowerer = BoolExprLowerer::new(&mut builder);
|
// let mut lowerer = BoolExprLowerer::new(&mut builder);
|
||||||
//
|
//
|
||||||
// // AST: i < 10
|
// // AST: i < 10
|
||||||
// let ast = ASTNode::BinaryOp {
|
// let ast = ASTNode::BinaryOp {
|
||||||
// operator: BinaryOperator::Less,
|
// operator: BinaryOperator::Less,
|
||||||
@ -252,17 +248,17 @@ impl<'a> BoolExprLowerer<'a> {
|
|||||||
// }),
|
// }),
|
||||||
// span: Span::unknown(),
|
// span: Span::unknown(),
|
||||||
// };
|
// };
|
||||||
//
|
//
|
||||||
// let result = lowerer.lower_condition(&ast);
|
// let result = lowerer.lower_condition(&ast);
|
||||||
// assert!(result.is_ok(), "Simple comparison should succeed");
|
// assert!(result.is_ok(), "Simple comparison should succeed");
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// /// Test: OR chain (ch == " " || ch == "\t")
|
// /// Test: OR chain (ch == " " || ch == "\t")
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn test_or_chain() {
|
// fn test_or_chain() {
|
||||||
// let mut builder = create_test_builder();
|
// let mut builder = create_test_builder();
|
||||||
// let mut lowerer = BoolExprLowerer::new(&mut builder);
|
// let mut lowerer = BoolExprLowerer::new(&mut builder);
|
||||||
//
|
//
|
||||||
// // AST: ch == " " || ch == "\t"
|
// // AST: ch == " " || ch == "\t"
|
||||||
// let ast = ASTNode::BinaryOp {
|
// let ast = ASTNode::BinaryOp {
|
||||||
// operator: BinaryOperator::Or,
|
// operator: BinaryOperator::Or,
|
||||||
@ -292,17 +288,17 @@ impl<'a> BoolExprLowerer<'a> {
|
|||||||
// }),
|
// }),
|
||||||
// span: Span::unknown(),
|
// span: Span::unknown(),
|
||||||
// };
|
// };
|
||||||
//
|
//
|
||||||
// let result = lowerer.lower_condition(&ast);
|
// let result = lowerer.lower_condition(&ast);
|
||||||
// assert!(result.is_ok(), "OR chain should succeed");
|
// assert!(result.is_ok(), "OR chain should succeed");
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// /// Test: Complex mixed condition (i < len && (c == " " || c == "\t"))
|
// /// Test: Complex mixed condition (i < len && (c == " " || c == "\t"))
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn test_complex_mixed_condition() {
|
// fn test_complex_mixed_condition() {
|
||||||
// let mut builder = create_test_builder();
|
// let mut builder = create_test_builder();
|
||||||
// let mut lowerer = BoolExprLowerer::new(&mut builder);
|
// let mut lowerer = BoolExprLowerer::new(&mut builder);
|
||||||
//
|
//
|
||||||
// // AST: i < len && (c == " " || c == "\t")
|
// // AST: i < len && (c == " " || c == "\t")
|
||||||
// let ast = ASTNode::BinaryOp {
|
// let ast = ASTNode::BinaryOp {
|
||||||
// operator: BinaryOperator::And,
|
// operator: BinaryOperator::And,
|
||||||
@ -348,17 +344,17 @@ impl<'a> BoolExprLowerer<'a> {
|
|||||||
// }),
|
// }),
|
||||||
// span: Span::unknown(),
|
// span: Span::unknown(),
|
||||||
// };
|
// };
|
||||||
//
|
//
|
||||||
// let result = lowerer.lower_condition(&ast);
|
// let result = lowerer.lower_condition(&ast);
|
||||||
// assert!(result.is_ok(), "Complex mixed condition should succeed");
|
// assert!(result.is_ok(), "Complex mixed condition should succeed");
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// /// Test: NOT operator (!condition)
|
// /// Test: NOT operator (!condition)
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn test_not_operator() {
|
// fn test_not_operator() {
|
||||||
// let mut builder = create_test_builder();
|
// let mut builder = create_test_builder();
|
||||||
// let mut lowerer = BoolExprLowerer::new(&mut builder);
|
// let mut lowerer = BoolExprLowerer::new(&mut builder);
|
||||||
//
|
//
|
||||||
// // AST: !(i < 10)
|
// // AST: !(i < 10)
|
||||||
// let ast = ASTNode::UnaryOp {
|
// let ast = ASTNode::UnaryOp {
|
||||||
// operator: crate::ast::UnaryOperator::Not,
|
// operator: crate::ast::UnaryOperator::Not,
|
||||||
|
|||||||
@ -210,15 +210,12 @@ impl CarrierInfo {
|
|||||||
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
|
||||||
) -> Result<Self, String> {
|
) -> Result<Self, String> {
|
||||||
// Find loop variable
|
// Find loop variable
|
||||||
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()
|
"Loop variable '{}' not found in variable_map",
|
||||||
.ok_or_else(|| {
|
loop_var_name
|
||||||
format!(
|
)
|
||||||
"Loop variable '{}' not found in variable_map",
|
})?;
|
||||||
loop_var_name
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Collect all non-loop-var variables as carriers
|
// Collect all non-loop-var variables as carriers
|
||||||
let mut carriers: Vec<CarrierVar> = variable_map
|
let mut carriers: Vec<CarrierVar> = variable_map
|
||||||
@ -280,9 +277,10 @@ impl CarrierInfo {
|
|||||||
let mut carriers = Vec::new();
|
let mut carriers = Vec::new();
|
||||||
|
|
||||||
for name in carrier_names {
|
for name in carrier_names {
|
||||||
let host_id = variable_map.get(&name).copied().ok_or_else(|| {
|
let host_id = variable_map
|
||||||
format!("Carrier variable '{}' not found in variable_map", name)
|
.get(&name)
|
||||||
})?;
|
.copied()
|
||||||
|
.ok_or_else(|| format!("Carrier variable '{}' not found in variable_map", name))?;
|
||||||
|
|
||||||
carriers.push(CarrierVar {
|
carriers.push(CarrierVar {
|
||||||
name,
|
name,
|
||||||
@ -408,7 +406,9 @@ impl CarrierInfo {
|
|||||||
/// eprintln!("Whitespace chars: {:?}", helper.whitespace_chars);
|
/// eprintln!("Whitespace chars: {:?}", helper.whitespace_chars);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn trim_helper(&self) -> Option<&crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper> {
|
pub fn trim_helper(
|
||||||
|
&self,
|
||||||
|
) -> Option<&crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper> {
|
||||||
self.trim_helper.as_ref()
|
self.trim_helper.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,7 +430,10 @@ impl CarrierInfo {
|
|||||||
/// * `Some(ValueId)` - 対応する carrier の join_id が見つかった場合
|
/// * `Some(ValueId)` - 対応する carrier の join_id が見つかった場合
|
||||||
/// * `None` - promoted_loopbodylocals に含まれない、または join_id 未設定の場合
|
/// * `None` - promoted_loopbodylocals に含まれない、または join_id 未設定の場合
|
||||||
pub fn resolve_promoted_join_id(&self, original_name: &str) -> Option<ValueId> {
|
pub fn resolve_promoted_join_id(&self, original_name: &str) -> Option<ValueId> {
|
||||||
if !self.promoted_loopbodylocals.contains(&original_name.to_string()) {
|
if !self
|
||||||
|
.promoted_loopbodylocals
|
||||||
|
.contains(&original_name.to_string())
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -646,29 +649,21 @@ mod tests {
|
|||||||
CarrierVar {
|
CarrierVar {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
host_id: ValueId(id),
|
host_id: ValueId(id),
|
||||||
join_id: None, // Phase 177-STRUCT-1
|
join_id: None, // Phase 177-STRUCT-1
|
||||||
role: CarrierRole::LoopState, // Phase 227: Default to LoopState
|
role: CarrierRole::LoopState, // Phase 227: Default to LoopState
|
||||||
init: CarrierInit::FromHost, // Phase 228: Default to FromHost
|
init: CarrierInit::FromHost, // Phase 228: Default to FromHost
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper: Create a CarrierInfo for testing
|
// Helper: Create a CarrierInfo for testing
|
||||||
fn test_carrier_info(loop_var: &str, loop_id: u32, carriers: Vec<CarrierVar>) -> CarrierInfo {
|
fn test_carrier_info(loop_var: &str, loop_id: u32, carriers: Vec<CarrierVar>) -> CarrierInfo {
|
||||||
CarrierInfo::with_carriers(
|
CarrierInfo::with_carriers(loop_var.to_string(), ValueId(loop_id), carriers)
|
||||||
loop_var.to_string(),
|
|
||||||
ValueId(loop_id),
|
|
||||||
carriers,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_merge_from_empty() {
|
fn test_merge_from_empty() {
|
||||||
// Merge empty CarrierInfo should not change anything
|
// Merge empty CarrierInfo should not change anything
|
||||||
let mut carrier_info = test_carrier_info(
|
let mut carrier_info = test_carrier_info("i", 5, vec![test_carrier("sum", 10)]);
|
||||||
"i",
|
|
||||||
5,
|
|
||||||
vec![test_carrier("sum", 10)],
|
|
||||||
);
|
|
||||||
|
|
||||||
let other = test_carrier_info("j", 20, vec![]);
|
let other = test_carrier_info("j", 20, vec![]);
|
||||||
|
|
||||||
@ -681,17 +676,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_merge_from_new_carrier() {
|
fn test_merge_from_new_carrier() {
|
||||||
// Merge a new carrier that doesn't exist yet
|
// Merge a new carrier that doesn't exist yet
|
||||||
let mut carrier_info = test_carrier_info(
|
let mut carrier_info = test_carrier_info("i", 5, vec![test_carrier("sum", 10)]);
|
||||||
"i",
|
|
||||||
5,
|
|
||||||
vec![test_carrier("sum", 10)],
|
|
||||||
);
|
|
||||||
|
|
||||||
let other = test_carrier_info(
|
let other = test_carrier_info("j", 20, vec![test_carrier("count", 15)]);
|
||||||
"j",
|
|
||||||
20,
|
|
||||||
vec![test_carrier("count", 15)],
|
|
||||||
);
|
|
||||||
|
|
||||||
carrier_info.merge_from(&other);
|
carrier_info.merge_from(&other);
|
||||||
|
|
||||||
@ -704,11 +691,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_merge_from_duplicate_carrier() {
|
fn test_merge_from_duplicate_carrier() {
|
||||||
// Merge a carrier with the same name should NOT duplicate
|
// Merge a carrier with the same name should NOT duplicate
|
||||||
let mut carrier_info = test_carrier_info(
|
let mut carrier_info = test_carrier_info("i", 5, vec![test_carrier("sum", 10)]);
|
||||||
"i",
|
|
||||||
5,
|
|
||||||
vec![test_carrier("sum", 10)],
|
|
||||||
);
|
|
||||||
|
|
||||||
let other = test_carrier_info(
|
let other = test_carrier_info(
|
||||||
"j",
|
"j",
|
||||||
@ -728,19 +711,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_merge_from_multiple_carriers() {
|
fn test_merge_from_multiple_carriers() {
|
||||||
// Merge multiple carriers
|
// Merge multiple carriers
|
||||||
let mut carrier_info = test_carrier_info(
|
let mut carrier_info = test_carrier_info("i", 5, vec![test_carrier("sum", 10)]);
|
||||||
"i",
|
|
||||||
5,
|
|
||||||
vec![test_carrier("sum", 10)],
|
|
||||||
);
|
|
||||||
|
|
||||||
let other = test_carrier_info(
|
let other = test_carrier_info(
|
||||||
"j",
|
"j",
|
||||||
20,
|
20,
|
||||||
vec![
|
vec![test_carrier("count", 15), test_carrier("product", 18)],
|
||||||
test_carrier("count", 15),
|
|
||||||
test_carrier("product", 18),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
carrier_info.merge_from(&other);
|
carrier_info.merge_from(&other);
|
||||||
@ -758,19 +734,13 @@ mod tests {
|
|||||||
let mut carrier_info = test_carrier_info(
|
let mut carrier_info = test_carrier_info(
|
||||||
"i",
|
"i",
|
||||||
5,
|
5,
|
||||||
vec![
|
vec![test_carrier("zebra", 30), test_carrier("alpha", 10)],
|
||||||
test_carrier("zebra", 30),
|
|
||||||
test_carrier("alpha", 10),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let other = test_carrier_info(
|
let other = test_carrier_info(
|
||||||
"j",
|
"j",
|
||||||
20,
|
20,
|
||||||
vec![
|
vec![test_carrier("beta", 15), test_carrier("gamma", 18)],
|
||||||
test_carrier("beta", 15),
|
|
||||||
test_carrier("gamma", 18),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
carrier_info.merge_from(&other);
|
carrier_info.merge_from(&other);
|
||||||
|
|||||||
@ -67,12 +67,7 @@ pub fn emit_carrier_update_with_env(
|
|||||||
// Get carrier parameter ValueId from env
|
// Get carrier parameter ValueId from env
|
||||||
let carrier_param = env
|
let carrier_param = env
|
||||||
.resolve(&carrier.name)
|
.resolve(&carrier.name)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| format!("Carrier '{}' not found in UpdateEnv", carrier.name))?;
|
||||||
format!(
|
|
||||||
"Carrier '{}' not found in UpdateEnv",
|
|
||||||
carrier.name
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Allocate result ValueId
|
// Allocate result ValueId
|
||||||
let result = alloc_value();
|
let result = alloc_value();
|
||||||
@ -99,12 +94,7 @@ pub fn emit_carrier_update_with_env(
|
|||||||
// Get carrier parameter ValueId from env
|
// Get carrier parameter ValueId from env
|
||||||
let carrier_param = env
|
let carrier_param = env
|
||||||
.resolve(&carrier.name)
|
.resolve(&carrier.name)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| format!("Carrier '{}' not found in UpdateEnv", carrier.name))?;
|
||||||
format!(
|
|
||||||
"Carrier '{}' not found in UpdateEnv",
|
|
||||||
carrier.name
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Resolve RHS (Phase 184: Now supports body-local variables!)
|
// Resolve RHS (Phase 184: Now supports body-local variables!)
|
||||||
let rhs_id = match rhs {
|
let rhs_id = match rhs {
|
||||||
@ -255,12 +245,7 @@ pub fn emit_carrier_update(
|
|||||||
// Get carrier parameter ValueId from env
|
// Get carrier parameter ValueId from env
|
||||||
let carrier_param = env
|
let carrier_param = env
|
||||||
.get(&carrier.name)
|
.get(&carrier.name)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| format!("Carrier '{}' not found in ConditionEnv", carrier.name))?;
|
||||||
format!(
|
|
||||||
"Carrier '{}' not found in ConditionEnv",
|
|
||||||
carrier.name
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Allocate result ValueId
|
// Allocate result ValueId
|
||||||
let result = alloc_value();
|
let result = alloc_value();
|
||||||
@ -287,12 +272,7 @@ pub fn emit_carrier_update(
|
|||||||
// Get carrier parameter ValueId from env
|
// Get carrier parameter ValueId from env
|
||||||
let carrier_param = env
|
let carrier_param = env
|
||||||
.get(&carrier.name)
|
.get(&carrier.name)
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| format!("Carrier '{}' not found in ConditionEnv", carrier.name))?;
|
||||||
format!(
|
|
||||||
"Carrier '{}' not found in ConditionEnv",
|
|
||||||
carrier.name
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Resolve RHS
|
// Resolve RHS
|
||||||
let rhs_id = match rhs {
|
let rhs_id = match rhs {
|
||||||
@ -304,14 +284,12 @@ pub fn emit_carrier_update(
|
|||||||
}));
|
}));
|
||||||
const_id
|
const_id
|
||||||
}
|
}
|
||||||
UpdateRhs::Variable(var_name) => {
|
UpdateRhs::Variable(var_name) => env.get(var_name).ok_or_else(|| {
|
||||||
env.get(var_name).ok_or_else(|| {
|
format!(
|
||||||
format!(
|
"Update RHS variable '{}' not found in ConditionEnv",
|
||||||
"Update RHS variable '{}' not found in ConditionEnv",
|
var_name
|
||||||
var_name
|
)
|
||||||
)
|
})?,
|
||||||
})?
|
|
||||||
}
|
|
||||||
// Phase 188: String updates now emit JoinIR BinOp
|
// Phase 188: String updates now emit JoinIR BinOp
|
||||||
// StringAppendLiteral: s = s + "literal"
|
// StringAppendLiteral: s = s + "literal"
|
||||||
UpdateRhs::StringLiteral(s) => {
|
UpdateRhs::StringLiteral(s) => {
|
||||||
@ -441,13 +419,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut instructions = Vec::new();
|
let mut instructions = Vec::new();
|
||||||
let result = emit_carrier_update(
|
let result =
|
||||||
&carrier,
|
emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);
|
||||||
&update,
|
|
||||||
&mut alloc_value,
|
|
||||||
&env,
|
|
||||||
&mut instructions,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
let result_id = result.unwrap();
|
let result_id = result.unwrap();
|
||||||
@ -497,13 +470,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut instructions = Vec::new();
|
let mut instructions = Vec::new();
|
||||||
let result = emit_carrier_update(
|
let result =
|
||||||
&carrier,
|
emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);
|
||||||
&update,
|
|
||||||
&mut alloc_value,
|
|
||||||
&env,
|
|
||||||
&mut instructions,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
let result_id = result.unwrap();
|
let result_id = result.unwrap();
|
||||||
@ -553,13 +521,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut instructions = Vec::new();
|
let mut instructions = Vec::new();
|
||||||
let result = emit_carrier_update(
|
let result =
|
||||||
&carrier,
|
emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);
|
||||||
&update,
|
|
||||||
&mut alloc_value,
|
|
||||||
&env,
|
|
||||||
&mut instructions,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
let result_id = result.unwrap();
|
let result_id = result.unwrap();
|
||||||
@ -596,13 +559,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut instructions = Vec::new();
|
let mut instructions = Vec::new();
|
||||||
let result = emit_carrier_update(
|
let result =
|
||||||
&carrier,
|
emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);
|
||||||
&update,
|
|
||||||
&mut alloc_value,
|
|
||||||
&env,
|
|
||||||
&mut instructions,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(result.unwrap_err().contains("Carrier 'unknown' not found"));
|
assert!(result.unwrap_err().contains("Carrier 'unknown' not found"));
|
||||||
@ -627,13 +585,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut instructions = Vec::new();
|
let mut instructions = Vec::new();
|
||||||
let result = emit_carrier_update(
|
let result =
|
||||||
&carrier,
|
emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);
|
||||||
&update,
|
|
||||||
&mut alloc_value,
|
|
||||||
&env,
|
|
||||||
&mut instructions,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(result.unwrap_err().contains("doesn't match carrier"));
|
assert!(result.unwrap_err().contains("doesn't match carrier"));
|
||||||
@ -658,16 +611,13 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut instructions = Vec::new();
|
let mut instructions = Vec::new();
|
||||||
let result = emit_carrier_update(
|
let result =
|
||||||
&carrier,
|
emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);
|
||||||
&update,
|
|
||||||
&mut alloc_value,
|
|
||||||
&env,
|
|
||||||
&mut instructions,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(result.unwrap_err().contains("Update RHS variable 'unknown_var' not found"));
|
assert!(result
|
||||||
|
.unwrap_err()
|
||||||
|
.contains("Update RHS variable 'unknown_var' not found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@ -765,7 +715,12 @@ mod tests {
|
|||||||
|
|
||||||
// Should use x=100 (condition env), not x=200 (body-local env)
|
// Should use x=100 (condition env), not x=200 (body-local env)
|
||||||
match &instructions[0] {
|
match &instructions[0] {
|
||||||
JoinInst::Compute(MirLikeInst::BinOp { dst: _, op: _, lhs: _, rhs }) => {
|
JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: _,
|
||||||
|
op: _,
|
||||||
|
lhs: _,
|
||||||
|
rhs,
|
||||||
|
}) => {
|
||||||
assert_eq!(*rhs, ValueId(100)); // Condition env wins
|
assert_eq!(*rhs, ValueId(100)); // Condition env wins
|
||||||
}
|
}
|
||||||
_ => panic!("Expected BinOp instruction"),
|
_ => panic!("Expected BinOp instruction"),
|
||||||
|
|||||||
@ -107,7 +107,8 @@ impl ComplexAddendNormalizer {
|
|||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
..
|
..
|
||||||
} | ASTNode::BinaryOp {
|
}
|
||||||
|
| ASTNode::BinaryOp {
|
||||||
operator: BinaryOperator::Subtract,
|
operator: BinaryOperator::Subtract,
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
|
|||||||
@ -61,7 +61,7 @@ impl ConditionEnv {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
name_to_join: BTreeMap::new(), // Phase 222.5-D: HashMap → BTreeMap for determinism
|
name_to_join: BTreeMap::new(), // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||||
captured: BTreeMap::new(), // Phase 222.5-D: HashMap → BTreeMap for determinism
|
captured: BTreeMap::new(), // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +82,9 @@ impl ConditionEnv {
|
|||||||
/// Returns `Some(ValueId)` if the variable exists in the environment,
|
/// Returns `Some(ValueId)` if the variable exists in the environment,
|
||||||
/// `None` otherwise.
|
/// `None` otherwise.
|
||||||
pub fn get(&self, name: &str) -> Option<ValueId> {
|
pub fn get(&self, name: &str) -> Option<ValueId> {
|
||||||
self.name_to_join.get(name).copied()
|
self.name_to_join
|
||||||
|
.get(name)
|
||||||
|
.copied()
|
||||||
.or_else(|| self.captured.get(name).copied())
|
.or_else(|| self.captured.get(name).copied())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +128,9 @@ impl ConditionEnv {
|
|||||||
///
|
///
|
||||||
/// Phase 200-B: Includes both name_to_join and captured variables.
|
/// Phase 200-B: Includes both name_to_join and captured variables.
|
||||||
pub fn names(&self) -> Vec<String> {
|
pub fn names(&self) -> Vec<String> {
|
||||||
let mut names: Vec<_> = self.name_to_join.keys()
|
let mut names: Vec<_> = self
|
||||||
|
.name_to_join
|
||||||
|
.keys()
|
||||||
.chain(self.captured.keys())
|
.chain(self.captured.keys())
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
@ -235,8 +239,8 @@ mod tests {
|
|||||||
fn test_condition_binding() {
|
fn test_condition_binding() {
|
||||||
let binding = ConditionBinding::new(
|
let binding = ConditionBinding::new(
|
||||||
"start".to_string(),
|
"start".to_string(),
|
||||||
ValueId(33), // HOST
|
ValueId(33), // HOST
|
||||||
ValueId(1), // JoinIR
|
ValueId(1), // JoinIR
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(binding.name, "start");
|
assert_eq!(binding.name, "start");
|
||||||
|
|||||||
@ -100,12 +100,8 @@ where
|
|||||||
| BinaryOperator::Greater => {
|
| BinaryOperator::Greater => {
|
||||||
lower_comparison(operator, left, right, alloc_value, env, instructions)
|
lower_comparison(operator, left, right, alloc_value, env, instructions)
|
||||||
}
|
}
|
||||||
BinaryOperator::And => {
|
BinaryOperator::And => lower_logical_and(left, right, alloc_value, env, instructions),
|
||||||
lower_logical_and(left, right, alloc_value, env, instructions)
|
BinaryOperator::Or => lower_logical_or(left, right, alloc_value, env, instructions),
|
||||||
}
|
|
||||||
BinaryOperator::Or => {
|
|
||||||
lower_logical_or(left, right, alloc_value, env, instructions)
|
|
||||||
}
|
|
||||||
_ => Err(format!(
|
_ => Err(format!(
|
||||||
"Unsupported binary operator in condition: {:?}",
|
"Unsupported binary operator in condition: {:?}",
|
||||||
operator
|
operator
|
||||||
@ -264,7 +260,10 @@ where
|
|||||||
return Err("Float literals not supported in JoinIR conditions yet".to_string());
|
return Err("Float literals not supported in JoinIR conditions yet".to_string());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(format!("Unsupported literal type in condition: {:?}", value));
|
return Err(format!(
|
||||||
|
"Unsupported literal type in condition: {:?}",
|
||||||
|
value
|
||||||
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -317,7 +316,14 @@ where
|
|||||||
let recv_val = lower_value_expression(object, alloc_value, env, instructions)?;
|
let recv_val = lower_value_expression(object, alloc_value, env, instructions)?;
|
||||||
|
|
||||||
// 2. Lower method call using MethodCallLowerer (will lower arguments internally)
|
// 2. Lower method call using MethodCallLowerer (will lower arguments internally)
|
||||||
MethodCallLowerer::lower_for_condition(recv_val, method, arguments, alloc_value, env, instructions)
|
MethodCallLowerer::lower_for_condition(
|
||||||
|
recv_val,
|
||||||
|
method,
|
||||||
|
arguments,
|
||||||
|
alloc_value,
|
||||||
|
env,
|
||||||
|
instructions,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => Err(format!(
|
_ => Err(format!(
|
||||||
|
|||||||
@ -15,9 +15,9 @@
|
|||||||
//! **Fail-Safe**: Implementations return explicit errors for unsupported patterns,
|
//! **Fail-Safe**: Implementations return explicit errors for unsupported patterns,
|
||||||
//! allowing callers to fall back to alternative paths.
|
//! allowing callers to fall back to alternative paths.
|
||||||
|
|
||||||
|
use super::scope_manager::ScopeManager;
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
use super::scope_manager::ScopeManager;
|
|
||||||
|
|
||||||
/// Phase 244: Context for condition lowering
|
/// Phase 244: Context for condition lowering
|
||||||
///
|
///
|
||||||
@ -158,12 +158,12 @@ pub trait ConditionLoweringBox<S: ScopeManager> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ast::{Span, LiteralValue, BinaryOperator};
|
use crate::ast::{BinaryOperator, LiteralValue, Span};
|
||||||
use crate::mir::builder::MirBuilder;
|
use crate::mir::builder::MirBuilder;
|
||||||
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
|
|
||||||
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
|
|
||||||
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
||||||
use crate::mir::join_ir::lowering::expr_lowerer::{ExprLowerer, ExprContext};
|
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
|
||||||
|
use crate::mir::join_ir::lowering::expr_lowerer::{ExprContext, ExprLowerer};
|
||||||
|
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
|
||||||
|
|
||||||
fn span() -> Span {
|
fn span() -> Span {
|
||||||
Span::unknown()
|
Span::unknown()
|
||||||
|
|||||||
@ -79,7 +79,12 @@ pub enum ConditionPattern {
|
|||||||
pub fn analyze_condition_pattern(cond: &ASTNode) -> ConditionPattern {
|
pub fn analyze_condition_pattern(cond: &ASTNode) -> ConditionPattern {
|
||||||
match cond {
|
match cond {
|
||||||
// Comparison operators: ==, !=, <, >, <=, >=
|
// Comparison operators: ==, !=, <, >, <=, >=
|
||||||
ASTNode::BinaryOp { operator, left, right, .. } => {
|
ASTNode::BinaryOp {
|
||||||
|
operator,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
// Check if operator is a comparison
|
// Check if operator is a comparison
|
||||||
let is_comparison = matches!(
|
let is_comparison = matches!(
|
||||||
operator,
|
operator,
|
||||||
@ -225,12 +230,12 @@ pub struct NormalizedCondition {
|
|||||||
/// ```
|
/// ```
|
||||||
fn flip_compare_op(op: CompareOp) -> CompareOp {
|
fn flip_compare_op(op: CompareOp) -> CompareOp {
|
||||||
match op {
|
match op {
|
||||||
CompareOp::Lt => CompareOp::Gt, // < → >
|
CompareOp::Lt => CompareOp::Gt, // < → >
|
||||||
CompareOp::Gt => CompareOp::Lt, // > → <
|
CompareOp::Gt => CompareOp::Lt, // > → <
|
||||||
CompareOp::Le => CompareOp::Ge, // <= → >=
|
CompareOp::Le => CompareOp::Ge, // <= → >=
|
||||||
CompareOp::Ge => CompareOp::Le, // >= → <=
|
CompareOp::Ge => CompareOp::Le, // >= → <=
|
||||||
CompareOp::Eq => CompareOp::Eq, // == → == (不変)
|
CompareOp::Eq => CompareOp::Eq, // == → == (不変)
|
||||||
CompareOp::Ne => CompareOp::Ne, // != → != (不変)
|
CompareOp::Ne => CompareOp::Ne, // != → != (不変)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,12 +283,24 @@ fn binary_op_to_compare_op(op: &BinaryOperator) -> Option<CompareOp> {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn normalize_comparison(cond: &ASTNode) -> Option<NormalizedCondition> {
|
pub fn normalize_comparison(cond: &ASTNode) -> Option<NormalizedCondition> {
|
||||||
match cond {
|
match cond {
|
||||||
ASTNode::BinaryOp { operator, left, right, .. } => {
|
ASTNode::BinaryOp {
|
||||||
|
operator,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
// Comparison operator のみ受理
|
// Comparison operator のみ受理
|
||||||
let compare_op = binary_op_to_compare_op(operator)?;
|
let compare_op = binary_op_to_compare_op(operator)?;
|
||||||
|
|
||||||
// Case 1: var CmpOp literal (e.g., i > 0)
|
// Case 1: var CmpOp literal (e.g., i > 0)
|
||||||
if let (ASTNode::Variable { name: left_var, .. }, ASTNode::Literal { value: LiteralValue::Integer(right_val), .. }) = (left.as_ref(), right.as_ref()) {
|
if let (
|
||||||
|
ASTNode::Variable { name: left_var, .. },
|
||||||
|
ASTNode::Literal {
|
||||||
|
value: LiteralValue::Integer(right_val),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
) = (left.as_ref(), right.as_ref())
|
||||||
|
{
|
||||||
return Some(NormalizedCondition {
|
return Some(NormalizedCondition {
|
||||||
left_var: left_var.clone(),
|
left_var: left_var.clone(),
|
||||||
op: compare_op,
|
op: compare_op,
|
||||||
@ -292,7 +309,16 @@ pub fn normalize_comparison(cond: &ASTNode) -> Option<NormalizedCondition> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Case 2: literal CmpOp var (e.g., 0 < i) → 左右反転
|
// Case 2: literal CmpOp var (e.g., 0 < i) → 左右反転
|
||||||
if let (ASTNode::Literal { value: LiteralValue::Integer(left_val), .. }, ASTNode::Variable { name: right_var, .. }) = (left.as_ref(), right.as_ref()) {
|
if let (
|
||||||
|
ASTNode::Literal {
|
||||||
|
value: LiteralValue::Integer(left_val),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
ASTNode::Variable {
|
||||||
|
name: right_var, ..
|
||||||
|
},
|
||||||
|
) = (left.as_ref(), right.as_ref())
|
||||||
|
{
|
||||||
return Some(NormalizedCondition {
|
return Some(NormalizedCondition {
|
||||||
left_var: right_var.clone(),
|
left_var: right_var.clone(),
|
||||||
op: flip_compare_op(compare_op), // 演算子を反転
|
op: flip_compare_op(compare_op), // 演算子を反転
|
||||||
@ -301,7 +327,13 @@ pub fn normalize_comparison(cond: &ASTNode) -> Option<NormalizedCondition> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Case 3: var CmpOp var (e.g., i > j)
|
// Case 3: var CmpOp var (e.g., i > j)
|
||||||
if let (ASTNode::Variable { name: left_var, .. }, ASTNode::Variable { name: right_var, .. }) = (left.as_ref(), right.as_ref()) {
|
if let (
|
||||||
|
ASTNode::Variable { name: left_var, .. },
|
||||||
|
ASTNode::Variable {
|
||||||
|
name: right_var, ..
|
||||||
|
},
|
||||||
|
) = (left.as_ref(), right.as_ref())
|
||||||
|
{
|
||||||
return Some(NormalizedCondition {
|
return Some(NormalizedCondition {
|
||||||
left_var: left_var.clone(),
|
left_var: left_var.clone(),
|
||||||
op: compare_op,
|
op: compare_op,
|
||||||
@ -351,7 +383,10 @@ mod tests {
|
|||||||
fn test_simple_comparison_greater() {
|
fn test_simple_comparison_greater() {
|
||||||
// i > 0
|
// i > 0
|
||||||
let cond = binop(BinaryOperator::Greater, var("i"), int_lit(0));
|
let cond = binop(BinaryOperator::Greater, var("i"), int_lit(0));
|
||||||
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison);
|
assert_eq!(
|
||||||
|
analyze_condition_pattern(&cond),
|
||||||
|
ConditionPattern::SimpleComparison
|
||||||
|
);
|
||||||
assert!(is_simple_comparison(&cond));
|
assert!(is_simple_comparison(&cond));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,7 +394,10 @@ mod tests {
|
|||||||
fn test_simple_comparison_less() {
|
fn test_simple_comparison_less() {
|
||||||
// i < 10
|
// i < 10
|
||||||
let cond = binop(BinaryOperator::Less, var("i"), int_lit(10));
|
let cond = binop(BinaryOperator::Less, var("i"), int_lit(10));
|
||||||
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison);
|
assert_eq!(
|
||||||
|
analyze_condition_pattern(&cond),
|
||||||
|
ConditionPattern::SimpleComparison
|
||||||
|
);
|
||||||
assert!(is_simple_comparison(&cond));
|
assert!(is_simple_comparison(&cond));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,7 +405,10 @@ mod tests {
|
|||||||
fn test_simple_comparison_equal() {
|
fn test_simple_comparison_equal() {
|
||||||
// i == 5
|
// i == 5
|
||||||
let cond = binop(BinaryOperator::Equal, var("i"), int_lit(5));
|
let cond = binop(BinaryOperator::Equal, var("i"), int_lit(5));
|
||||||
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison);
|
assert_eq!(
|
||||||
|
analyze_condition_pattern(&cond),
|
||||||
|
ConditionPattern::SimpleComparison
|
||||||
|
);
|
||||||
assert!(is_simple_comparison(&cond));
|
assert!(is_simple_comparison(&cond));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,7 +416,10 @@ mod tests {
|
|||||||
fn test_simple_comparison_not_equal() {
|
fn test_simple_comparison_not_equal() {
|
||||||
// i != 0
|
// i != 0
|
||||||
let cond = binop(BinaryOperator::NotEqual, var("i"), int_lit(0));
|
let cond = binop(BinaryOperator::NotEqual, var("i"), int_lit(0));
|
||||||
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison);
|
assert_eq!(
|
||||||
|
analyze_condition_pattern(&cond),
|
||||||
|
ConditionPattern::SimpleComparison
|
||||||
|
);
|
||||||
assert!(is_simple_comparison(&cond));
|
assert!(is_simple_comparison(&cond));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,7 +428,10 @@ mod tests {
|
|||||||
// Phase 242-EX-A: i % 2 == 1 (BinaryOp in LHS) is now SimpleComparison
|
// Phase 242-EX-A: i % 2 == 1 (BinaryOp in LHS) is now SimpleComparison
|
||||||
let lhs = binop(BinaryOperator::Modulo, var("i"), int_lit(2));
|
let lhs = binop(BinaryOperator::Modulo, var("i"), int_lit(2));
|
||||||
let cond = binop(BinaryOperator::Equal, lhs, int_lit(1));
|
let cond = binop(BinaryOperator::Equal, lhs, int_lit(1));
|
||||||
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison);
|
assert_eq!(
|
||||||
|
analyze_condition_pattern(&cond),
|
||||||
|
ConditionPattern::SimpleComparison
|
||||||
|
);
|
||||||
assert!(is_simple_comparison(&cond));
|
assert!(is_simple_comparison(&cond));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,7 +440,10 @@ mod tests {
|
|||||||
// Phase 242-EX-A: i == a + b (BinaryOp in RHS) is now SimpleComparison
|
// Phase 242-EX-A: i == a + b (BinaryOp in RHS) is now SimpleComparison
|
||||||
let rhs = binop(BinaryOperator::Add, var("a"), var("b"));
|
let rhs = binop(BinaryOperator::Add, var("a"), var("b"));
|
||||||
let cond = binop(BinaryOperator::Equal, var("i"), rhs);
|
let cond = binop(BinaryOperator::Equal, var("i"), rhs);
|
||||||
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison);
|
assert_eq!(
|
||||||
|
analyze_condition_pattern(&cond),
|
||||||
|
ConditionPattern::SimpleComparison
|
||||||
|
);
|
||||||
assert!(is_simple_comparison(&cond));
|
assert!(is_simple_comparison(&cond));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,7 +452,10 @@ mod tests {
|
|||||||
// Pattern3/4 で使う典型的なフィルタ条件: i % 2 == 1
|
// Pattern3/4 で使う典型的なフィルタ条件: i % 2 == 1
|
||||||
let lhs = binop(BinaryOperator::Modulo, var("i"), int_lit(2));
|
let lhs = binop(BinaryOperator::Modulo, var("i"), int_lit(2));
|
||||||
let cond = binop(BinaryOperator::Equal, lhs, int_lit(1));
|
let cond = binop(BinaryOperator::Equal, lhs, int_lit(1));
|
||||||
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison);
|
assert_eq!(
|
||||||
|
analyze_condition_pattern(&cond),
|
||||||
|
ConditionPattern::SimpleComparison
|
||||||
|
);
|
||||||
assert!(is_simple_comparison(&cond));
|
assert!(is_simple_comparison(&cond));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -535,7 +588,10 @@ mod tests {
|
|||||||
fn test_analyze_pattern_literal_cmp_var() {
|
fn test_analyze_pattern_literal_cmp_var() {
|
||||||
// Phase 222: 0 < i → SimpleComparison
|
// Phase 222: 0 < i → SimpleComparison
|
||||||
let cond = binop(BinaryOperator::Less, int_lit(0), var("i"));
|
let cond = binop(BinaryOperator::Less, int_lit(0), var("i"));
|
||||||
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison);
|
assert_eq!(
|
||||||
|
analyze_condition_pattern(&cond),
|
||||||
|
ConditionPattern::SimpleComparison
|
||||||
|
);
|
||||||
assert!(is_simple_comparison(&cond));
|
assert!(is_simple_comparison(&cond));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -543,7 +599,10 @@ mod tests {
|
|||||||
fn test_analyze_pattern_var_cmp_var() {
|
fn test_analyze_pattern_var_cmp_var() {
|
||||||
// Phase 222: i > j → SimpleComparison
|
// Phase 222: i > j → SimpleComparison
|
||||||
let cond = binop(BinaryOperator::Greater, var("i"), var("j"));
|
let cond = binop(BinaryOperator::Greater, var("i"), var("j"));
|
||||||
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison);
|
assert_eq!(
|
||||||
|
analyze_condition_pattern(&cond),
|
||||||
|
ConditionPattern::SimpleComparison
|
||||||
|
);
|
||||||
assert!(is_simple_comparison(&cond));
|
assert!(is_simple_comparison(&cond));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,10 +40,7 @@ use std::collections::BTreeSet;
|
|||||||
/// );
|
/// );
|
||||||
/// // Result: ["end", "len", "start"] (sorted, 'i' excluded)
|
/// // Result: ["end", "len", "start"] (sorted, 'i' excluded)
|
||||||
/// ```
|
/// ```
|
||||||
pub fn extract_condition_variables(
|
pub fn extract_condition_variables(cond_ast: &ASTNode, exclude_vars: &[String]) -> Vec<String> {
|
||||||
cond_ast: &ASTNode,
|
|
||||||
exclude_vars: &[String],
|
|
||||||
) -> Vec<String> {
|
|
||||||
let mut all_vars = BTreeSet::new();
|
let mut all_vars = BTreeSet::new();
|
||||||
collect_variables_recursive(cond_ast, &mut all_vars);
|
collect_variables_recursive(cond_ast, &mut all_vars);
|
||||||
|
|
||||||
|
|||||||
@ -152,11 +152,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_normalize_digit_pos_lt_zero() {
|
fn test_normalize_digit_pos_lt_zero() {
|
||||||
// Pattern: digit_pos < 0
|
// Pattern: digit_pos < 0
|
||||||
let cond = binary_op(
|
let cond = binary_op(var_node("digit_pos"), BinaryOperator::Less, int_literal(0));
|
||||||
var_node("digit_pos"),
|
|
||||||
BinaryOperator::Less,
|
|
||||||
int_literal(0),
|
|
||||||
);
|
|
||||||
|
|
||||||
let normalized = DigitPosConditionNormalizer::normalize(&cond, "digit_pos", "is_digit_pos");
|
let normalized = DigitPosConditionNormalizer::normalize(&cond, "digit_pos", "is_digit_pos");
|
||||||
|
|
||||||
@ -166,14 +162,12 @@ mod tests {
|
|||||||
operator: UnaryOperator::Not,
|
operator: UnaryOperator::Not,
|
||||||
operand,
|
operand,
|
||||||
..
|
..
|
||||||
} => {
|
} => match operand.as_ref() {
|
||||||
match operand.as_ref() {
|
ASTNode::Variable { name, .. } => {
|
||||||
ASTNode::Variable { name, .. } => {
|
assert_eq!(name, "is_digit_pos");
|
||||||
assert_eq!(name, "is_digit_pos");
|
|
||||||
}
|
|
||||||
_ => panic!("Expected Variable node"),
|
|
||||||
}
|
}
|
||||||
}
|
_ => panic!("Expected Variable node"),
|
||||||
|
},
|
||||||
_ => panic!("Expected UnaryOp Not node"),
|
_ => panic!("Expected UnaryOp Not node"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -201,21 +195,13 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_no_normalize_wrong_variable() {
|
fn test_no_normalize_wrong_variable() {
|
||||||
// Pattern: other_var < 0 (different variable name)
|
// Pattern: other_var < 0 (different variable name)
|
||||||
let cond = binary_op(
|
let cond = binary_op(var_node("other_var"), BinaryOperator::Less, int_literal(0));
|
||||||
var_node("other_var"),
|
|
||||||
BinaryOperator::Less,
|
|
||||||
int_literal(0),
|
|
||||||
);
|
|
||||||
|
|
||||||
let normalized = DigitPosConditionNormalizer::normalize(&cond, "digit_pos", "is_digit_pos");
|
let normalized = DigitPosConditionNormalizer::normalize(&cond, "digit_pos", "is_digit_pos");
|
||||||
|
|
||||||
// Should NOT transform (return original)
|
// Should NOT transform (return original)
|
||||||
match normalized {
|
match normalized {
|
||||||
ASTNode::BinaryOp {
|
ASTNode::BinaryOp { operator, left, .. } => {
|
||||||
operator,
|
|
||||||
left,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
assert_eq!(operator, BinaryOperator::Less);
|
assert_eq!(operator, BinaryOperator::Less);
|
||||||
match left.as_ref() {
|
match left.as_ref() {
|
||||||
ASTNode::Variable { name, .. } => {
|
ASTNode::Variable { name, .. } => {
|
||||||
@ -231,20 +217,14 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_no_normalize_wrong_constant() {
|
fn test_no_normalize_wrong_constant() {
|
||||||
// Pattern: digit_pos < 10 (different constant, not 0)
|
// Pattern: digit_pos < 10 (different constant, not 0)
|
||||||
let cond = binary_op(
|
let cond = binary_op(var_node("digit_pos"), BinaryOperator::Less, int_literal(10));
|
||||||
var_node("digit_pos"),
|
|
||||||
BinaryOperator::Less,
|
|
||||||
int_literal(10),
|
|
||||||
);
|
|
||||||
|
|
||||||
let normalized = DigitPosConditionNormalizer::normalize(&cond, "digit_pos", "is_digit_pos");
|
let normalized = DigitPosConditionNormalizer::normalize(&cond, "digit_pos", "is_digit_pos");
|
||||||
|
|
||||||
// Should NOT transform (return original)
|
// Should NOT transform (return original)
|
||||||
match normalized {
|
match normalized {
|
||||||
ASTNode::BinaryOp {
|
ASTNode::BinaryOp {
|
||||||
operator,
|
operator, right, ..
|
||||||
right,
|
|
||||||
..
|
|
||||||
} => {
|
} => {
|
||||||
assert_eq!(operator, BinaryOperator::Less);
|
assert_eq!(operator, BinaryOperator::Less);
|
||||||
match right.as_ref() {
|
match right.as_ref() {
|
||||||
|
|||||||
@ -15,12 +15,12 @@
|
|||||||
//! **Fail-Safe**: Unsupported AST nodes return explicit errors, allowing callers
|
//! **Fail-Safe**: Unsupported AST nodes return explicit errors, allowing callers
|
||||||
//! to fall back to legacy paths.
|
//! to fall back to legacy paths.
|
||||||
|
|
||||||
use crate::ast::ASTNode;
|
|
||||||
use crate::mir::ValueId;
|
|
||||||
use crate::mir::join_ir::JoinInst;
|
|
||||||
use crate::mir::builder::MirBuilder;
|
|
||||||
use super::scope_manager::ScopeManager;
|
|
||||||
use super::condition_lowerer::lower_condition_to_joinir;
|
use super::condition_lowerer::lower_condition_to_joinir;
|
||||||
|
use super::scope_manager::ScopeManager;
|
||||||
|
use crate::ast::ASTNode;
|
||||||
|
use crate::mir::builder::MirBuilder;
|
||||||
|
use crate::mir::join_ir::JoinInst;
|
||||||
|
use crate::mir::ValueId;
|
||||||
|
|
||||||
mod ast_support;
|
mod ast_support;
|
||||||
mod scope_resolution;
|
mod scope_resolution;
|
||||||
@ -165,11 +165,9 @@ impl<'env, 'builder, S: ScopeManager> ExprLowerer<'env, 'builder, S> {
|
|||||||
pub fn lower(&mut self, ast: &ASTNode) -> Result<ValueId, ExprLoweringError> {
|
pub fn lower(&mut self, ast: &ASTNode) -> Result<ValueId, ExprLoweringError> {
|
||||||
match self.context {
|
match self.context {
|
||||||
ExprContext::Condition => self.lower_condition(ast),
|
ExprContext::Condition => self.lower_condition(ast),
|
||||||
ExprContext::General => {
|
ExprContext::General => Err(ExprLoweringError::UnsupportedNode(
|
||||||
Err(ExprLoweringError::UnsupportedNode(
|
"General expression context not yet implemented (Phase 231)".to_string(),
|
||||||
"General expression context not yet implemented (Phase 231)".to_string()
|
)),
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,9 +183,10 @@ impl<'env, 'builder, S: ScopeManager> ExprLowerer<'env, 'builder, S> {
|
|||||||
fn lower_condition(&mut self, ast: &ASTNode) -> Result<ValueId, ExprLoweringError> {
|
fn lower_condition(&mut self, ast: &ASTNode) -> Result<ValueId, ExprLoweringError> {
|
||||||
// 1. Check if AST is supported in condition context
|
// 1. Check if AST is supported in condition context
|
||||||
if !ast_support::is_supported_condition(ast) {
|
if !ast_support::is_supported_condition(ast) {
|
||||||
return Err(ExprLoweringError::UnsupportedNode(
|
return Err(ExprLoweringError::UnsupportedNode(format!(
|
||||||
format!("Unsupported condition node: {:?}", ast)
|
"Unsupported condition node: {:?}",
|
||||||
));
|
ast
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Build ConditionEnv from ScopeManager
|
// 2. Build ConditionEnv from ScopeManager
|
||||||
@ -204,17 +203,18 @@ impl<'env, 'builder, S: ScopeManager> ExprLowerer<'env, 'builder, S> {
|
|||||||
id
|
id
|
||||||
};
|
};
|
||||||
|
|
||||||
let (result_value, instructions) = lower_condition_to_joinir(
|
let (result_value, instructions) =
|
||||||
ast,
|
lower_condition_to_joinir(ast, &mut alloc_value, &condition_env)
|
||||||
&mut alloc_value,
|
.map_err(|e| ExprLoweringError::LoweringError(e))?;
|
||||||
&condition_env,
|
|
||||||
).map_err(|e| ExprLoweringError::LoweringError(e))?;
|
|
||||||
|
|
||||||
// Phase 235: 保存しておき、テストから観察できるようにする
|
// Phase 235: 保存しておき、テストから観察できるようにする
|
||||||
self.last_instructions = instructions;
|
self.last_instructions = instructions;
|
||||||
|
|
||||||
if self.debug {
|
if self.debug {
|
||||||
eprintln!("[expr_lowerer/phase231] Lowered condition → ValueId({:?})", result_value);
|
eprintln!(
|
||||||
|
"[expr_lowerer/phase231] Lowered condition → ValueId({:?})",
|
||||||
|
result_value
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result_value)
|
Ok(result_value)
|
||||||
@ -230,7 +230,7 @@ impl<'env, 'builder, S: ScopeManager> ExprLowerer<'env, 'builder, S> {
|
|||||||
// Phase 244: ConditionLoweringBox trait implementation
|
// Phase 244: ConditionLoweringBox trait implementation
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
use super::condition_lowering_box::{ConditionLoweringBox, ConditionContext};
|
use super::condition_lowering_box::{ConditionContext, ConditionLoweringBox};
|
||||||
|
|
||||||
impl<'env, 'builder, S: ScopeManager> ConditionLoweringBox<S> for ExprLowerer<'env, 'builder, S> {
|
impl<'env, 'builder, S: ScopeManager> ConditionLoweringBox<S> for ExprLowerer<'env, 'builder, S> {
|
||||||
/// Phase 244: Implement ConditionLoweringBox trait for ExprLowerer
|
/// Phase 244: Implement ConditionLoweringBox trait for ExprLowerer
|
||||||
@ -280,13 +280,13 @@ impl<'env, 'builder, S: ScopeManager> ConditionLoweringBox<S> for ExprLowerer<'e
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::test_helpers::{bin, lit_i, span, var};
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ast::{BinaryOperator, LiteralValue, Span, UnaryOperator};
|
use crate::ast::{BinaryOperator, LiteralValue, Span, UnaryOperator};
|
||||||
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::ConditionEnv;
|
||||||
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
|
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
|
||||||
use crate::mir::join_ir::{BinOpKind, MirLikeInst, UnaryOp as JoinUnaryOp};
|
use crate::mir::join_ir::{BinOpKind, MirLikeInst, UnaryOp as JoinUnaryOp};
|
||||||
use super::test_helpers::{bin, lit_i, span, var};
|
|
||||||
|
|
||||||
// Helper to create a test MirBuilder (Phase 231: minimal stub)
|
// Helper to create a test MirBuilder (Phase 231: minimal stub)
|
||||||
fn create_test_builder() -> MirBuilder {
|
fn create_test_builder() -> MirBuilder {
|
||||||
@ -340,7 +340,10 @@ mod tests {
|
|||||||
let mut expr_lowerer = ExprLowerer::new(&scope, ExprContext::Condition, &mut builder);
|
let mut expr_lowerer = ExprLowerer::new(&scope, ExprContext::Condition, &mut builder);
|
||||||
let result = expr_lowerer.lower(&ast);
|
let result = expr_lowerer.lower(&ast);
|
||||||
|
|
||||||
assert!(result.is_ok(), "Should lower simple comparison successfully");
|
assert!(
|
||||||
|
result.is_ok(),
|
||||||
|
"Should lower simple comparison successfully"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -381,7 +384,10 @@ mod tests {
|
|||||||
let mut expr_lowerer = ExprLowerer::new(&scope, ExprContext::Condition, &mut builder);
|
let mut expr_lowerer = ExprLowerer::new(&scope, ExprContext::Condition, &mut builder);
|
||||||
let result = expr_lowerer.lower(&ast);
|
let result = expr_lowerer.lower(&ast);
|
||||||
|
|
||||||
assert!(matches!(result, Err(ExprLoweringError::VariableNotFound(_))));
|
assert!(matches!(
|
||||||
|
result,
|
||||||
|
Err(ExprLoweringError::VariableNotFound(_))
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -406,7 +412,9 @@ mod tests {
|
|||||||
let mut builder = create_test_builder();
|
let mut builder = create_test_builder();
|
||||||
|
|
||||||
// AST: Break (unsupported in condition context)
|
// AST: Break (unsupported in condition context)
|
||||||
let ast = ASTNode::Break { span: Span::unknown() };
|
let ast = ASTNode::Break {
|
||||||
|
span: Span::unknown(),
|
||||||
|
};
|
||||||
|
|
||||||
let mut expr_lowerer = ExprLowerer::new(&scope, ExprContext::Condition, &mut builder);
|
let mut expr_lowerer = ExprLowerer::new(&scope, ExprContext::Condition, &mut builder);
|
||||||
let result = expr_lowerer.lower(&ast);
|
let result = expr_lowerer.lower(&ast);
|
||||||
@ -429,7 +437,9 @@ mod tests {
|
|||||||
}),
|
}),
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
};
|
};
|
||||||
assert!(ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(&ast));
|
assert!(ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(
|
||||||
|
&ast
|
||||||
|
));
|
||||||
|
|
||||||
// Supported: MethodCall
|
// Supported: MethodCall
|
||||||
let ast = ASTNode::MethodCall {
|
let ast = ASTNode::MethodCall {
|
||||||
@ -441,10 +451,14 @@ mod tests {
|
|||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
};
|
};
|
||||||
assert!(ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(&ast));
|
assert!(ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(
|
||||||
|
&ast
|
||||||
|
));
|
||||||
|
|
||||||
// Unsupported: Break node
|
// Unsupported: Break node
|
||||||
let ast = ASTNode::Break { span: Span::unknown() };
|
let ast = ASTNode::Break {
|
||||||
|
span: Span::unknown(),
|
||||||
|
};
|
||||||
assert!(!ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(&ast));
|
assert!(!ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(&ast));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,10 +521,9 @@ mod tests {
|
|||||||
|
|
||||||
fn assert_has_compare(instructions: &[JoinInst]) {
|
fn assert_has_compare(instructions: &[JoinInst]) {
|
||||||
assert!(
|
assert!(
|
||||||
instructions.iter().any(|inst| matches!(
|
instructions
|
||||||
inst,
|
.iter()
|
||||||
JoinInst::Compute(MirLikeInst::Compare { .. }
|
.any(|inst| matches!(inst, JoinInst::Compute(MirLikeInst::Compare { .. }))),
|
||||||
))),
|
|
||||||
"Expected at least one Compare instruction, got {:?}",
|
"Expected at least one Compare instruction, got {:?}",
|
||||||
instructions
|
instructions
|
||||||
);
|
);
|
||||||
@ -532,8 +545,11 @@ mod tests {
|
|||||||
assert!(
|
assert!(
|
||||||
instructions.iter().any(|inst| matches!(
|
instructions.iter().any(|inst| matches!(
|
||||||
inst,
|
inst,
|
||||||
JoinInst::Compute(MirLikeInst::UnaryOp { op: JoinUnaryOp::Not, .. }
|
JoinInst::Compute(MirLikeInst::UnaryOp {
|
||||||
))),
|
op: JoinUnaryOp::Not,
|
||||||
|
..
|
||||||
|
})
|
||||||
|
)),
|
||||||
"Expected at least one UnaryOp::Not, got {:?}",
|
"Expected at least one UnaryOp::Not, got {:?}",
|
||||||
instructions
|
instructions
|
||||||
);
|
);
|
||||||
|
|||||||
@ -5,14 +5,19 @@ pub(crate) fn is_supported_condition(ast: &ASTNode) -> bool {
|
|||||||
match ast {
|
match ast {
|
||||||
Literal { .. } => true,
|
Literal { .. } => true,
|
||||||
Variable { .. } => true,
|
Variable { .. } => true,
|
||||||
BinaryOp { operator, left, right, .. } => {
|
BinaryOp {
|
||||||
|
operator,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
is_supported_binary_op(operator)
|
is_supported_binary_op(operator)
|
||||||
&& is_supported_condition(left)
|
&& is_supported_condition(left)
|
||||||
&& is_supported_condition(right)
|
&& is_supported_condition(right)
|
||||||
}
|
}
|
||||||
UnaryOp { operator, operand, .. } => {
|
UnaryOp {
|
||||||
matches!(operator, UnaryOperator::Not) && is_supported_condition(operand)
|
operator, operand, ..
|
||||||
}
|
} => matches!(operator, UnaryOperator::Not) && is_supported_condition(operand),
|
||||||
MethodCall { .. } => is_supported_method_call(ast),
|
MethodCall { .. } => is_supported_method_call(ast),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
@ -20,7 +25,10 @@ pub(crate) fn is_supported_condition(ast: &ASTNode) -> bool {
|
|||||||
|
|
||||||
fn is_supported_binary_op(op: &BinaryOperator) -> bool {
|
fn is_supported_binary_op(op: &BinaryOperator) -> bool {
|
||||||
use BinaryOperator::*;
|
use BinaryOperator::*;
|
||||||
matches!(op, Less | LessEqual | Greater | GreaterEqual | Equal | NotEqual | And | Or)
|
matches!(
|
||||||
|
op,
|
||||||
|
Less | LessEqual | Greater | GreaterEqual | Equal | NotEqual | And | Or
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_supported_method_call(ast: &ASTNode) -> bool {
|
fn is_supported_method_call(ast: &ASTNode) -> bool {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
use super::{ExprLoweringError, ScopeManager};
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
|
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
|
||||||
use super::{ExprLoweringError, ScopeManager};
|
|
||||||
|
|
||||||
pub(crate) fn build_condition_env_from_scope<S: ScopeManager>(
|
pub(crate) fn build_condition_env_from_scope<S: ScopeManager>(
|
||||||
scope: &S,
|
scope: &S,
|
||||||
@ -30,7 +30,9 @@ fn collect_vars(ast: &ASTNode, vars: &mut Vec<String>) {
|
|||||||
collect_vars(right, vars);
|
collect_vars(right, vars);
|
||||||
}
|
}
|
||||||
ASTNode::UnaryOp { operand, .. } => collect_vars(operand, vars),
|
ASTNode::UnaryOp { operand, .. } => collect_vars(operand, vars),
|
||||||
ASTNode::MethodCall { object, arguments, .. } => {
|
ASTNode::MethodCall {
|
||||||
|
object, arguments, ..
|
||||||
|
} => {
|
||||||
collect_vars(object, vars);
|
collect_vars(object, vars);
|
||||||
for arg in arguments {
|
for arg in arguments {
|
||||||
collect_vars(arg, vars);
|
collect_vars(arg, vars);
|
||||||
|
|||||||
@ -30,7 +30,6 @@
|
|||||||
//! - `value_id_ranges::funcscanner_append_defs` - ValueId allocation strategy
|
//! - `value_id_ranges::funcscanner_append_defs` - ValueId allocation strategy
|
||||||
//! - `loop_scope_shape::CaseAContext` - Context extraction
|
//! - `loop_scope_shape::CaseAContext` - Context extraction
|
||||||
|
|
||||||
|
|
||||||
use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext;
|
use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext;
|
||||||
use crate::mir::join_ir::lowering::value_id_ranges;
|
use crate::mir::join_ir::lowering::value_id_ranges;
|
||||||
use crate::mir::join_ir::{
|
use crate::mir::join_ir::{
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
//! - ValueId/BTreeMap の初期化ボイラープレート統一化
|
//! - ValueId/BTreeMap の初期化ボイラープレート統一化
|
||||||
//! - Pinned/Carrier 変数の一元管理
|
//! - Pinned/Carrier 変数の一元管理
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
/// Entry関数構築用の統一ビルダー
|
/// Entry関数構築用の統一ビルダー
|
||||||
///
|
///
|
||||||
|
|||||||
@ -84,19 +84,19 @@
|
|||||||
//! - `loop_to_join` - Main loop lowering coordinator
|
//! - `loop_to_join` - Main loop lowering coordinator
|
||||||
|
|
||||||
// Pattern-specific lowering modules
|
// Pattern-specific lowering modules
|
||||||
pub mod skip_ws;
|
|
||||||
pub mod trim;
|
|
||||||
pub mod append_defs;
|
pub mod append_defs;
|
||||||
|
pub mod skip_ws;
|
||||||
pub mod stage1_using_resolver;
|
pub mod stage1_using_resolver;
|
||||||
|
pub mod trim;
|
||||||
|
|
||||||
// Helper modules
|
// Helper modules
|
||||||
pub mod entry_builder;
|
pub mod entry_builder;
|
||||||
pub mod whitespace_check;
|
pub mod whitespace_check;
|
||||||
|
|
||||||
// Re-export public lowering functions
|
// Re-export public lowering functions
|
||||||
pub(crate) use skip_ws::lower_case_a_skip_ws_with_scope;
|
|
||||||
pub(crate) use trim::lower_case_a_trim_with_scope;
|
|
||||||
pub(crate) use append_defs::lower_case_a_append_defs_with_scope;
|
pub(crate) use append_defs::lower_case_a_append_defs_with_scope;
|
||||||
|
pub(crate) use skip_ws::lower_case_a_skip_ws_with_scope;
|
||||||
pub(crate) use stage1_using_resolver::lower_case_a_stage1_usingresolver_with_scope;
|
pub(crate) use stage1_using_resolver::lower_case_a_stage1_usingresolver_with_scope;
|
||||||
|
pub(crate) use trim::lower_case_a_trim_with_scope;
|
||||||
|
|
||||||
// Re-export helper utilities
|
// Re-export helper utilities
|
||||||
|
|||||||
@ -31,7 +31,6 @@
|
|||||||
//! - `value_id_ranges::skip_ws` - ValueId allocation strategy
|
//! - `value_id_ranges::skip_ws` - ValueId allocation strategy
|
||||||
//! - `loop_scope_shape::CaseAContext` - Context extraction
|
//! - `loop_scope_shape::CaseAContext` - Context extraction
|
||||||
|
|
||||||
|
|
||||||
use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext;
|
use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext;
|
||||||
use crate::mir::join_ir::lowering::value_id_ranges;
|
use crate::mir::join_ir::lowering::value_id_ranges;
|
||||||
use crate::mir::join_ir::lowering::value_id_ranges::skip_ws as vid;
|
use crate::mir::join_ir::lowering::value_id_ranges::skip_ws as vid;
|
||||||
|
|||||||
@ -30,7 +30,6 @@
|
|||||||
//! - `value_id_ranges::stage1_using_resolver` - ValueId allocation strategy
|
//! - `value_id_ranges::stage1_using_resolver` - ValueId allocation strategy
|
||||||
//! - `loop_scope_shape::CaseAContext` - Context extraction
|
//! - `loop_scope_shape::CaseAContext` - Context extraction
|
||||||
|
|
||||||
|
|
||||||
use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext;
|
use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext;
|
||||||
use crate::mir::join_ir::lowering::value_id_ranges;
|
use crate::mir::join_ir::lowering::value_id_ranges;
|
||||||
use crate::mir::join_ir::lowering::value_id_ranges::stage1_using_resolver as stage1_vid;
|
use crate::mir::join_ir::lowering::value_id_ranges::stage1_using_resolver as stage1_vid;
|
||||||
|
|||||||
@ -44,7 +44,6 @@
|
|||||||
//! - `value_id_ranges::funcscanner_trim` - ValueId allocation strategy
|
//! - `value_id_ranges::funcscanner_trim` - ValueId allocation strategy
|
||||||
//! - `whitespace_check` - Whitespace detection helper (shared with skip_ws)
|
//! - `whitespace_check` - Whitespace detection helper (shared with skip_ws)
|
||||||
|
|
||||||
|
|
||||||
use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext;
|
use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext;
|
||||||
use crate::mir::join_ir::lowering::value_id_ranges;
|
use crate::mir::join_ir::lowering::value_id_ranges;
|
||||||
use crate::mir::join_ir::{
|
use crate::mir::join_ir::{
|
||||||
|
|||||||
@ -59,10 +59,7 @@ impl WhitespaceDetector {
|
|||||||
/// 具体的な JoinInst 生成は呼び出し側で行う。
|
/// 具体的な JoinInst 生成は呼び出し側で行う。
|
||||||
/// ここは判定ロジック(どの文字を空白と判定するか)を記録する。
|
/// ここは判定ロジック(どの文字を空白と判定するか)を記録する。
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn build_whitespace_check_expr(
|
pub fn build_whitespace_check_expr(ch_value: ValueId, _debug: bool) -> Option<ValueId> {
|
||||||
ch_value: ValueId,
|
|
||||||
_debug: bool,
|
|
||||||
) -> Option<ValueId> {
|
|
||||||
// NOTE: JoinInst を生成する実装は呼び出し側で行う
|
// NOTE: JoinInst を生成する実装は呼び出し側で行う
|
||||||
// ここは判定ロジック(どの文字を空白と判定するか)を記録
|
// ここは判定ロジック(どの文字を空白と判定するか)を記録
|
||||||
|
|
||||||
|
|||||||
@ -133,7 +133,10 @@ pub fn try_lower_if_to_joinir(
|
|||||||
// IfMerge が成功すればそれを返す、失敗したら Select を試行
|
// IfMerge が成功すればそれを返す、失敗したら Select を試行
|
||||||
// Phase 61-1: context がある場合は with_context() を使用
|
// Phase 61-1: context がある場合は with_context() を使用
|
||||||
let if_merge_lowerer = if let Some(ctx) = context {
|
let if_merge_lowerer = if let Some(ctx) = context {
|
||||||
crate::mir::join_ir::lowering::if_merge::IfMergeLowerer::with_context(debug_level, ctx.clone())
|
crate::mir::join_ir::lowering::if_merge::IfMergeLowerer::with_context(
|
||||||
|
debug_level,
|
||||||
|
ctx.clone(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
crate::mir::join_ir::lowering::if_merge::IfMergeLowerer::new(debug_level)
|
crate::mir::join_ir::lowering::if_merge::IfMergeLowerer::new(debug_level)
|
||||||
};
|
};
|
||||||
@ -153,7 +156,10 @@ pub fn try_lower_if_to_joinir(
|
|||||||
// 4. IfMerge が失敗したら Select を試行(単一変数パターン)
|
// 4. IfMerge が失敗したら Select を試行(単一変数パターン)
|
||||||
// Phase 61-1: context がある場合は with_context() を使用
|
// Phase 61-1: context がある場合は with_context() を使用
|
||||||
let if_select_lowerer = if let Some(ctx) = context {
|
let if_select_lowerer = if let Some(ctx) = context {
|
||||||
crate::mir::join_ir::lowering::if_select::IfSelectLowerer::with_context(debug_level, ctx.clone())
|
crate::mir::join_ir::lowering::if_select::IfSelectLowerer::with_context(
|
||||||
|
debug_level,
|
||||||
|
ctx.clone(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
crate::mir::join_ir::lowering::if_select::IfSelectLowerer::new(debug_level)
|
crate::mir::join_ir::lowering::if_select::IfSelectLowerer::new(debug_level)
|
||||||
};
|
};
|
||||||
|
|||||||
@ -42,8 +42,8 @@
|
|||||||
//! ...
|
//! ...
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::mir::ValueId;
|
|
||||||
use super::carrier_info::CarrierRole;
|
use super::carrier_info::CarrierRole;
|
||||||
|
use crate::mir::ValueId;
|
||||||
|
|
||||||
/// Explicit binding between JoinIR exit value and host variable
|
/// Explicit binding between JoinIR exit value and host variable
|
||||||
///
|
///
|
||||||
@ -278,11 +278,11 @@ impl JoinInlineBoundary {
|
|||||||
host_outputs: vec![],
|
host_outputs: vec![],
|
||||||
exit_bindings: vec![],
|
exit_bindings: vec![],
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
condition_inputs: vec![], // Phase 171: Default to empty (deprecated)
|
condition_inputs: vec![], // Phase 171: Default to empty (deprecated)
|
||||||
condition_bindings: vec![], // Phase 171-fix: Default to empty
|
condition_bindings: vec![], // Phase 171-fix: Default to empty
|
||||||
expr_result: None, // Phase 33-14: Default to carrier-only pattern
|
expr_result: None, // Phase 33-14: Default to carrier-only pattern
|
||||||
loop_var_name: None, // Phase 33-16
|
loop_var_name: None, // Phase 33-16
|
||||||
carrier_info: None, // Phase 228: Default to None
|
carrier_info: None, // Phase 228: Default to None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,11 +321,11 @@ impl JoinInlineBoundary {
|
|||||||
host_outputs,
|
host_outputs,
|
||||||
exit_bindings: vec![],
|
exit_bindings: vec![],
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
condition_inputs: vec![], // Phase 171: Default to empty (deprecated)
|
condition_inputs: vec![], // Phase 171: Default to empty (deprecated)
|
||||||
condition_bindings: vec![], // Phase 171-fix: Default to empty
|
condition_bindings: vec![], // Phase 171-fix: Default to empty
|
||||||
expr_result: None, // Phase 33-14
|
expr_result: None, // Phase 33-14
|
||||||
loop_var_name: None, // Phase 33-16
|
loop_var_name: None, // Phase 33-16
|
||||||
carrier_info: None, // Phase 228
|
carrier_info: None, // Phase 228
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,11 +381,11 @@ impl JoinInlineBoundary {
|
|||||||
host_outputs: vec![],
|
host_outputs: vec![],
|
||||||
exit_bindings,
|
exit_bindings,
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
condition_inputs: vec![], // Phase 171: Default to empty (deprecated)
|
condition_inputs: vec![], // Phase 171: Default to empty (deprecated)
|
||||||
condition_bindings: vec![], // Phase 171-fix: Default to empty
|
condition_bindings: vec![], // Phase 171-fix: Default to empty
|
||||||
expr_result: None, // Phase 33-14
|
expr_result: None, // Phase 33-14
|
||||||
loop_var_name: None, // Phase 33-16
|
loop_var_name: None, // Phase 33-16
|
||||||
carrier_info: None, // Phase 228
|
carrier_info: None, // Phase 228
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,9 +429,9 @@ impl JoinInlineBoundary {
|
|||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
condition_inputs,
|
condition_inputs,
|
||||||
condition_bindings: vec![], // Phase 171-fix: Will be populated by new constructor
|
condition_bindings: vec![], // Phase 171-fix: Will be populated by new constructor
|
||||||
expr_result: None, // Phase 33-14
|
expr_result: None, // Phase 33-14
|
||||||
loop_var_name: None, // Phase 33-16
|
loop_var_name: None, // Phase 33-16
|
||||||
carrier_info: None, // Phase 228
|
carrier_info: None, // Phase 228
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -479,9 +479,9 @@ impl JoinInlineBoundary {
|
|||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
condition_inputs,
|
condition_inputs,
|
||||||
condition_bindings: vec![], // Phase 171-fix: Will be populated by new constructor
|
condition_bindings: vec![], // Phase 171-fix: Will be populated by new constructor
|
||||||
expr_result: None, // Phase 33-14
|
expr_result: None, // Phase 33-14
|
||||||
loop_var_name: None, // Phase 33-16
|
loop_var_name: None, // Phase 33-16
|
||||||
carrier_info: None, // Phase 228
|
carrier_info: None, // Phase 228
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,11 +534,11 @@ impl JoinInlineBoundary {
|
|||||||
host_outputs: vec![],
|
host_outputs: vec![],
|
||||||
exit_bindings: vec![],
|
exit_bindings: vec![],
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
condition_inputs: vec![], // Deprecated, use condition_bindings instead
|
condition_inputs: vec![], // Deprecated, use condition_bindings instead
|
||||||
condition_bindings,
|
condition_bindings,
|
||||||
expr_result: None, // Phase 33-14
|
expr_result: None, // Phase 33-14
|
||||||
loop_var_name: None, // Phase 33-16
|
loop_var_name: None, // Phase 33-16
|
||||||
carrier_info: None, // Phase 228
|
carrier_info: None, // Phase 228
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -550,8 +550,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_boundary_inputs_only() {
|
fn test_boundary_inputs_only() {
|
||||||
let boundary = JoinInlineBoundary::new_inputs_only(
|
let boundary = JoinInlineBoundary::new_inputs_only(
|
||||||
vec![ValueId(0)], // JoinIR uses ValueId(0) for loop var
|
vec![ValueId(0)], // JoinIR uses ValueId(0) for loop var
|
||||||
vec![ValueId(4)], // Host has loop var at ValueId(4)
|
vec![ValueId(4)], // Host has loop var at ValueId(4)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(boundary.join_inputs.len(), 1);
|
assert_eq!(boundary.join_inputs.len(), 1);
|
||||||
@ -560,17 +560,14 @@ mod tests {
|
|||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
{
|
{
|
||||||
assert_eq!(boundary.host_outputs.len(), 0);
|
assert_eq!(boundary.host_outputs.len(), 0);
|
||||||
assert_eq!(boundary.condition_inputs.len(), 0); // Phase 171: Deprecated field
|
assert_eq!(boundary.condition_inputs.len(), 0); // Phase 171: Deprecated field
|
||||||
}
|
}
|
||||||
assert_eq!(boundary.condition_bindings.len(), 0); // Phase 171-fix: New field
|
assert_eq!(boundary.condition_bindings.len(), 0); // Phase 171-fix: New field
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "join_inputs and host_inputs must have same length")]
|
#[should_panic(expected = "join_inputs and host_inputs must have same length")]
|
||||||
fn test_boundary_mismatched_inputs() {
|
fn test_boundary_mismatched_inputs() {
|
||||||
JoinInlineBoundary::new_inputs_only(
|
JoinInlineBoundary::new_inputs_only(vec![ValueId(0), ValueId(1)], vec![ValueId(4)]);
|
||||||
vec![ValueId(0), ValueId(1)],
|
|
||||||
vec![ValueId(4)],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,9 +24,9 @@
|
|||||||
//! .build();
|
//! .build();
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::mir::ValueId;
|
|
||||||
use super::condition_to_joinir::ConditionBinding;
|
use super::condition_to_joinir::ConditionBinding;
|
||||||
use super::inline_boundary::{JoinInlineBoundary, LoopExitBinding};
|
use super::inline_boundary::{JoinInlineBoundary, LoopExitBinding};
|
||||||
|
use crate::mir::ValueId;
|
||||||
|
|
||||||
/// Role of a parameter in JoinIR lowering (Phase 200-A)
|
/// Role of a parameter in JoinIR lowering (Phase 200-A)
|
||||||
///
|
///
|
||||||
@ -96,7 +96,7 @@ impl JoinInlineBoundaryBuilder {
|
|||||||
expr_result: None,
|
expr_result: None,
|
||||||
loop_var_name: None,
|
loop_var_name: None,
|
||||||
carrier_info: None, // Phase 228: Initialize as None
|
carrier_info: None, // Phase 228: Initialize as None
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +224,8 @@ impl JoinInlineBoundaryBuilder {
|
|||||||
// Phase 200-B: Add to condition_bindings without PHI
|
// Phase 200-B: Add to condition_bindings without PHI
|
||||||
// 1. Allocate JoinIR-local ValueId
|
// 1. Allocate JoinIR-local ValueId
|
||||||
let join_id = ValueId(
|
let join_id = ValueId(
|
||||||
(self.boundary.join_inputs.len() + self.boundary.condition_bindings.len()) as u32
|
(self.boundary.join_inputs.len() + self.boundary.condition_bindings.len())
|
||||||
|
as u32,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 2. Create ConditionBinding
|
// 2. Create ConditionBinding
|
||||||
@ -257,7 +258,9 @@ impl JoinInlineBoundaryBuilder {
|
|||||||
///
|
///
|
||||||
/// `Some(ValueId)` if the variable exists in condition_bindings, `None` otherwise.
|
/// `Some(ValueId)` if the variable exists in condition_bindings, `None` otherwise.
|
||||||
pub fn get_condition_binding(&self, name: &str) -> Option<ValueId> {
|
pub fn get_condition_binding(&self, name: &str) -> Option<ValueId> {
|
||||||
self.boundary.condition_bindings.iter()
|
self.boundary
|
||||||
|
.condition_bindings
|
||||||
|
.iter()
|
||||||
.find(|b| b.name == name)
|
.find(|b| b.name == name)
|
||||||
.map(|b| b.join_value)
|
.map(|b| b.join_value)
|
||||||
}
|
}
|
||||||
@ -363,18 +366,18 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_builder_pattern3_style() {
|
fn test_builder_pattern3_style() {
|
||||||
// Pattern3 style: Two carriers (i + sum), exit_bindings, loop_var_name
|
// Pattern3 style: Two carriers (i + sum), exit_bindings, loop_var_name
|
||||||
|
|
||||||
|
|
||||||
let boundary = JoinInlineBoundaryBuilder::new()
|
let boundary = JoinInlineBoundaryBuilder::new()
|
||||||
.with_inputs(vec![ValueId(0), ValueId(1)], vec![ValueId(100), ValueId(101)])
|
.with_inputs(
|
||||||
.with_exit_bindings(vec![
|
vec![ValueId(0), ValueId(1)],
|
||||||
LoopExitBinding {
|
vec![ValueId(100), ValueId(101)],
|
||||||
carrier_name: "sum".to_string(),
|
)
|
||||||
join_exit_value: ValueId(18),
|
.with_exit_bindings(vec![LoopExitBinding {
|
||||||
host_slot: ValueId(101),
|
carrier_name: "sum".to_string(),
|
||||||
role: CarrierRole::LoopState,
|
join_exit_value: ValueId(18),
|
||||||
}
|
host_slot: ValueId(101),
|
||||||
])
|
role: CarrierRole::LoopState,
|
||||||
|
}])
|
||||||
.with_loop_var_name(Some("i".to_string()))
|
.with_loop_var_name(Some("i".to_string()))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -391,8 +394,8 @@ mod tests {
|
|||||||
// Pattern4 style: Dynamic carrier count, continue support
|
// Pattern4 style: Dynamic carrier count, continue support
|
||||||
let boundary = JoinInlineBoundaryBuilder::new()
|
let boundary = JoinInlineBoundaryBuilder::new()
|
||||||
.with_inputs(
|
.with_inputs(
|
||||||
vec![ValueId(0), ValueId(1), ValueId(2)], // i + 2 carriers
|
vec![ValueId(0), ValueId(1), ValueId(2)], // i + 2 carriers
|
||||||
vec![ValueId(100), ValueId(101), ValueId(102)]
|
vec![ValueId(100), ValueId(101), ValueId(102)],
|
||||||
)
|
)
|
||||||
.with_exit_bindings(vec![
|
.with_exit_bindings(vec![
|
||||||
LoopExitBinding {
|
LoopExitBinding {
|
||||||
@ -406,7 +409,7 @@ mod tests {
|
|||||||
join_exit_value: ValueId(20),
|
join_exit_value: ValueId(20),
|
||||||
host_slot: ValueId(101),
|
host_slot: ValueId(101),
|
||||||
role: CarrierRole::LoopState,
|
role: CarrierRole::LoopState,
|
||||||
}
|
},
|
||||||
])
|
])
|
||||||
.with_loop_var_name(Some("i".to_string()))
|
.with_loop_var_name(Some("i".to_string()))
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@ -224,7 +224,11 @@ impl<'a> LoopBodyLocalInitLowerer<'a> {
|
|||||||
///
|
///
|
||||||
/// * `Ok(ValueId)` - JoinIR ValueId of computed result
|
/// * `Ok(ValueId)` - JoinIR ValueId of computed result
|
||||||
/// * `Err(msg)` - Unsupported expression (Fail-Fast)
|
/// * `Err(msg)` - Unsupported expression (Fail-Fast)
|
||||||
fn lower_init_expr(&mut self, expr: &ASTNode, env: &LoopBodyLocalEnv) -> Result<ValueId, String> {
|
fn lower_init_expr(
|
||||||
|
&mut self,
|
||||||
|
expr: &ASTNode,
|
||||||
|
env: &LoopBodyLocalEnv,
|
||||||
|
) -> Result<ValueId, String> {
|
||||||
match expr {
|
match expr {
|
||||||
// Constant literal: 42, 0, 1, "string" (use Literal with value)
|
// Constant literal: 42, 0, 1, "string" (use Literal with value)
|
||||||
ASTNode::Literal { value, .. } => {
|
ASTNode::Literal { value, .. } => {
|
||||||
@ -488,7 +492,6 @@ impl<'a> LoopBodyLocalInitLowerer<'a> {
|
|||||||
|
|
||||||
Ok(result_id)
|
Ok(result_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@ -133,25 +133,33 @@ pub fn try_lower_loop_pattern_to_joinir(
|
|||||||
// Step 3: Route to appropriate lowerer based on pattern
|
// Step 3: Route to appropriate lowerer based on pattern
|
||||||
match pattern {
|
match pattern {
|
||||||
LoopPatternKind::Pattern4Continue => {
|
LoopPatternKind::Pattern4Continue => {
|
||||||
if let Some(inst) = super::loop_patterns::lower_loop_with_continue_to_joinir(loop_form, lowerer) {
|
if let Some(inst) =
|
||||||
|
super::loop_patterns::lower_loop_with_continue_to_joinir(loop_form, lowerer)
|
||||||
|
{
|
||||||
eprintln!("[try_lower_loop_pattern] ✅ Pattern 4 (Continue) matched");
|
eprintln!("[try_lower_loop_pattern] ✅ Pattern 4 (Continue) matched");
|
||||||
return Some(inst);
|
return Some(inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LoopPatternKind::Pattern3IfPhi => {
|
LoopPatternKind::Pattern3IfPhi => {
|
||||||
if let Some(inst) = super::loop_patterns::lower_loop_with_conditional_phi_to_joinir(loop_form, lowerer) {
|
if let Some(inst) =
|
||||||
|
super::loop_patterns::lower_loop_with_conditional_phi_to_joinir(loop_form, lowerer)
|
||||||
|
{
|
||||||
eprintln!("[try_lower_loop_pattern] ✅ Pattern 3 (If-Else PHI) matched");
|
eprintln!("[try_lower_loop_pattern] ✅ Pattern 3 (If-Else PHI) matched");
|
||||||
return Some(inst);
|
return Some(inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LoopPatternKind::Pattern2Break => {
|
LoopPatternKind::Pattern2Break => {
|
||||||
if let Some(inst) = super::loop_patterns::lower_loop_with_break_to_joinir(loop_form, lowerer) {
|
if let Some(inst) =
|
||||||
|
super::loop_patterns::lower_loop_with_break_to_joinir(loop_form, lowerer)
|
||||||
|
{
|
||||||
eprintln!("[try_lower_loop_pattern] ✅ Pattern 2 (Break) matched");
|
eprintln!("[try_lower_loop_pattern] ✅ Pattern 2 (Break) matched");
|
||||||
return Some(inst);
|
return Some(inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LoopPatternKind::Pattern1SimpleWhile => {
|
LoopPatternKind::Pattern1SimpleWhile => {
|
||||||
if let Some(inst) = super::loop_patterns::lower_simple_while_to_joinir(loop_form, lowerer) {
|
if let Some(inst) =
|
||||||
|
super::loop_patterns::lower_simple_while_to_joinir(loop_form, lowerer)
|
||||||
|
{
|
||||||
eprintln!("[try_lower_loop_pattern] ✅ Pattern 1 (Simple While) matched");
|
eprintln!("[try_lower_loop_pattern] ✅ Pattern 1 (Simple While) matched");
|
||||||
return Some(inst);
|
return Some(inst);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,13 +48,13 @@
|
|||||||
|
|
||||||
pub mod simple_while;
|
pub mod simple_while;
|
||||||
pub mod with_break;
|
pub mod with_break;
|
||||||
pub mod with_if_phi;
|
|
||||||
pub mod with_continue;
|
pub mod with_continue;
|
||||||
|
pub mod with_if_phi;
|
||||||
|
|
||||||
pub use simple_while::lower_simple_while_to_joinir;
|
pub use simple_while::lower_simple_while_to_joinir;
|
||||||
pub use with_break::lower_loop_with_break_to_joinir;
|
pub use with_break::lower_loop_with_break_to_joinir;
|
||||||
pub use with_if_phi::lower_loop_with_conditional_phi_to_joinir;
|
|
||||||
pub use with_continue::lower_loop_with_continue_to_joinir;
|
pub use with_continue::lower_loop_with_continue_to_joinir;
|
||||||
|
pub use with_if_phi::lower_loop_with_conditional_phi_to_joinir;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Helper Functions (Shared Utilities)
|
// Helper Functions (Shared Utilities)
|
||||||
@ -76,7 +76,6 @@ pub use with_continue::lower_loop_with_continue_to_joinir;
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// Pattern 1: Simple While Loop Tests
|
// Pattern 1: Simple While Loop Tests
|
||||||
|
|||||||
@ -236,15 +236,17 @@ impl CaseALoweringShape {
|
|||||||
// Phase 170-C-2b: Use UpdateSummary if available
|
// Phase 170-C-2b: Use UpdateSummary if available
|
||||||
if let Some(ref summary) = features.update_summary {
|
if let Some(ref summary) = features.update_summary {
|
||||||
// Single carrier with CounterLike update → StringExamination
|
// Single carrier with CounterLike update → StringExamination
|
||||||
if summary.carriers.len() == 1
|
if summary.carriers.len() == 1 && summary.carriers[0].kind == UpdateKind::CounterLike {
|
||||||
&& summary.carriers[0].kind == UpdateKind::CounterLike
|
|
||||||
{
|
|
||||||
return CaseALoweringShape::StringExamination;
|
return CaseALoweringShape::StringExamination;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any AccumulationLike carrier → ArrayAccumulation (for single carrier)
|
// Any AccumulationLike carrier → ArrayAccumulation (for single carrier)
|
||||||
// or IterationWithAccumulation (for multiple carriers)
|
// or IterationWithAccumulation (for multiple carriers)
|
||||||
if summary.carriers.iter().any(|c| c.kind == UpdateKind::AccumulationLike) {
|
if summary
|
||||||
|
.carriers
|
||||||
|
.iter()
|
||||||
|
.any(|c| c.kind == UpdateKind::AccumulationLike)
|
||||||
|
{
|
||||||
if carrier_count == 1 {
|
if carrier_count == 1 {
|
||||||
return CaseALoweringShape::ArrayAccumulation;
|
return CaseALoweringShape::ArrayAccumulation;
|
||||||
} else {
|
} else {
|
||||||
@ -299,12 +301,14 @@ impl CaseALoweringShape {
|
|||||||
// Note: carriers is BTreeSet<String>, so each item is already a String
|
// Note: carriers is BTreeSet<String>, so each item is already a String
|
||||||
let carrier_names: Vec<String> = scope.carriers.iter().cloned().collect();
|
let carrier_names: Vec<String> = scope.carriers.iter().cloned().collect();
|
||||||
let update_summary =
|
let update_summary =
|
||||||
crate::mir::join_ir::lowering::loop_update_summary::analyze_loop_updates_by_name(&carrier_names);
|
crate::mir::join_ir::lowering::loop_update_summary::analyze_loop_updates_by_name(
|
||||||
|
&carrier_names,
|
||||||
|
);
|
||||||
|
|
||||||
// Create stub features (Phase 170-B will use real LoopFeatures)
|
// Create stub features (Phase 170-B will use real LoopFeatures)
|
||||||
let stub_features = crate::mir::loop_pattern_detection::LoopFeatures {
|
let stub_features = crate::mir::loop_pattern_detection::LoopFeatures {
|
||||||
has_break: false, // Unknown from LoopScopeShape alone
|
has_break: false, // Unknown from LoopScopeShape alone
|
||||||
has_continue: false, // Unknown from LoopScopeShape alone
|
has_continue: false, // Unknown from LoopScopeShape alone
|
||||||
has_if: false,
|
has_if: false,
|
||||||
has_if_else_phi: false,
|
has_if_else_phi: false,
|
||||||
carrier_count,
|
carrier_count,
|
||||||
@ -319,7 +323,10 @@ impl CaseALoweringShape {
|
|||||||
/// Is this a recognized lowering shape?
|
/// Is this a recognized lowering shape?
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn is_recognized(&self) -> bool {
|
pub fn is_recognized(&self) -> bool {
|
||||||
!matches!(self, CaseALoweringShape::NotCaseA | CaseALoweringShape::Generic)
|
!matches!(
|
||||||
|
self,
|
||||||
|
CaseALoweringShape::NotCaseA | CaseALoweringShape::Generic
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get human-readable name for tracing/debugging
|
/// Get human-readable name for tracing/debugging
|
||||||
@ -340,9 +347,18 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_shape_display() {
|
fn test_shape_display() {
|
||||||
assert_eq!(CaseALoweringShape::StringExamination.name(), "StringExamination");
|
assert_eq!(
|
||||||
assert_eq!(CaseALoweringShape::ArrayAccumulation.name(), "ArrayAccumulation");
|
CaseALoweringShape::StringExamination.name(),
|
||||||
assert_eq!(CaseALoweringShape::IterationWithAccumulation.name(), "IterationWithAccumulation");
|
"StringExamination"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
CaseALoweringShape::ArrayAccumulation.name(),
|
||||||
|
"ArrayAccumulation"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
CaseALoweringShape::IterationWithAccumulation.name(),
|
||||||
|
"IterationWithAccumulation"
|
||||||
|
);
|
||||||
assert_eq!(CaseALoweringShape::Generic.name(), "Generic");
|
assert_eq!(CaseALoweringShape::Generic.name(), "Generic");
|
||||||
assert_eq!(CaseALoweringShape::NotCaseA.name(), "NotCaseA");
|
assert_eq!(CaseALoweringShape::NotCaseA.name(), "NotCaseA");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -147,7 +147,10 @@ impl LoopToJoinLowerer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Phase 33-23: Validator箱に検証を委譲
|
// Phase 33-23: Validator箱に検証を委譲
|
||||||
if !self.validator.is_supported_case_a(func, ®ion, &exit_edges, &scope) {
|
if !self
|
||||||
|
.validator
|
||||||
|
.is_supported_case_a(func, ®ion, &exit_edges, &scope)
|
||||||
|
{
|
||||||
if self.debug {
|
if self.debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[LoopToJoinLowerer] rejected by validator: {:?}",
|
"[LoopToJoinLowerer] rejected by validator: {:?}",
|
||||||
|
|||||||
@ -81,7 +81,8 @@ impl LoopUpdateAnalyzer {
|
|||||||
pub fn analyze_carrier_updates(
|
pub fn analyze_carrier_updates(
|
||||||
body: &[ASTNode],
|
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
|
||||||
let mut updates = BTreeMap::new();
|
let mut updates = BTreeMap::new();
|
||||||
|
|
||||||
// Extract carrier names for quick lookup
|
// Extract carrier names for quick lookup
|
||||||
@ -109,7 +110,9 @@ impl LoopUpdateAnalyzer {
|
|||||||
if let Some(target_name) = Self::extract_variable_name(target) {
|
if let Some(target_name) = Self::extract_variable_name(target) {
|
||||||
if carrier_names.contains(&target_name.as_str()) {
|
if carrier_names.contains(&target_name.as_str()) {
|
||||||
// This is a carrier update, analyze the RHS
|
// This is a carrier update, analyze the RHS
|
||||||
if let Some(update_expr) = Self::analyze_update_value(&target_name, value) {
|
if let Some(update_expr) =
|
||||||
|
Self::analyze_update_value(&target_name, value)
|
||||||
|
{
|
||||||
updates.insert(target_name, update_expr);
|
updates.insert(target_name, update_expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,9 +236,7 @@ impl LoopUpdateAnalyzer {
|
|||||||
} => Some(UpdateRhs::StringLiteral(s.clone())),
|
} => Some(UpdateRhs::StringLiteral(s.clone())),
|
||||||
|
|
||||||
// Variable: sum + i (also handles: result + ch)
|
// Variable: sum + i (also handles: result + ch)
|
||||||
ASTNode::Variable { name, .. } => {
|
ASTNode::Variable { name, .. } => Some(UpdateRhs::Variable(name.clone())),
|
||||||
Some(UpdateRhs::Variable(name.clone()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Phase 190: Number accumulation pattern detection
|
// Phase 190: Number accumulation pattern detection
|
||||||
// This is called from analyze_update_value, so we're analyzing the full RHS of an assignment
|
// This is called from analyze_update_value, so we're analyzing the full RHS of an assignment
|
||||||
@ -245,9 +246,9 @@ impl LoopUpdateAnalyzer {
|
|||||||
|
|
||||||
// Phase 178: Method call or other complex expression
|
// Phase 178: Method call or other complex expression
|
||||||
// e.g., result + s.substring(pos, end)
|
// e.g., result + s.substring(pos, end)
|
||||||
ASTNode::Call { .. }
|
ASTNode::Call { .. } | ASTNode::MethodCall { .. } | ASTNode::UnaryOp { .. } => {
|
||||||
| ASTNode::MethodCall { .. }
|
Some(UpdateRhs::Other)
|
||||||
| ASTNode::UnaryOp { .. } => Some(UpdateRhs::Other),
|
}
|
||||||
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@ -617,7 +618,11 @@ mod tests {
|
|||||||
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers);
|
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers);
|
||||||
|
|
||||||
// Verify both carriers have updates
|
// Verify both carriers have updates
|
||||||
assert_eq!(updates.len(), 2, "Should detect updates for both i and result");
|
assert_eq!(
|
||||||
|
updates.len(),
|
||||||
|
2,
|
||||||
|
"Should detect updates for both i and result"
|
||||||
|
);
|
||||||
|
|
||||||
// Verify i = i + 1 (Const increment)
|
// Verify i = i + 1 (Const increment)
|
||||||
if let Some(UpdateExpr::BinOp { lhs, op, rhs }) = updates.get("i") {
|
if let Some(UpdateExpr::BinOp { lhs, op, rhs }) = updates.get("i") {
|
||||||
@ -640,10 +645,16 @@ mod tests {
|
|||||||
assert_eq!(*base, 10, "NumberAccumulation should use base 10");
|
assert_eq!(*base, 10, "NumberAccumulation should use base 10");
|
||||||
assert_eq!(digit_var, "digit_pos", "Should use digit_pos variable");
|
assert_eq!(digit_var, "digit_pos", "Should use digit_pos variable");
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected NumberAccumulation for result update, got {:?}", rhs);
|
panic!(
|
||||||
|
"Expected NumberAccumulation for result update, got {:?}",
|
||||||
|
rhs
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected BinOp for result update, got {:?}", updates.get("result"));
|
panic!(
|
||||||
|
"Expected BinOp for result update, got {:?}",
|
||||||
|
updates.get("result")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -155,7 +155,9 @@ impl LoopUpdateSummary {
|
|||||||
///
|
///
|
||||||
/// Returns a set of variable names that are assigned (LHS) in the loop body.
|
/// Returns a set of variable names that are assigned (LHS) in the loop body.
|
||||||
/// This prevents phantom carriers from non-assigned variables.
|
/// This prevents phantom carriers from non-assigned variables.
|
||||||
fn extract_assigned_variables(loop_body: &[crate::ast::ASTNode]) -> std::collections::HashSet<String> {
|
fn extract_assigned_variables(
|
||||||
|
loop_body: &[crate::ast::ASTNode],
|
||||||
|
) -> std::collections::HashSet<String> {
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
let mut assigned = std::collections::HashSet::new();
|
let mut assigned = std::collections::HashSet::new();
|
||||||
|
|
||||||
@ -170,7 +172,11 @@ fn extract_assigned_variables(loop_body: &[crate::ast::ASTNode]) -> std::collect
|
|||||||
visit_node(value, assigned);
|
visit_node(value, assigned);
|
||||||
}
|
}
|
||||||
// If statement: recurse into then/else branches
|
// If statement: recurse into then/else branches
|
||||||
ASTNode::If { then_body, else_body, .. } => {
|
ASTNode::If {
|
||||||
|
then_body,
|
||||||
|
else_body,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
for stmt in then_body {
|
for stmt in then_body {
|
||||||
visit_node(stmt, assigned);
|
visit_node(stmt, assigned);
|
||||||
}
|
}
|
||||||
@ -207,7 +213,12 @@ fn classify_update_kind_from_rhs(rhs: &crate::ast::ASTNode) -> UpdateKind {
|
|||||||
match rhs {
|
match rhs {
|
||||||
// x = x + 1 → CounterLike
|
// x = x + 1 → CounterLike
|
||||||
// x = x + n → AccumulationLike (where n is not 1)
|
// x = x + n → AccumulationLike (where n is not 1)
|
||||||
ASTNode::BinaryOp { operator, left, right, .. } => {
|
ASTNode::BinaryOp {
|
||||||
|
operator,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
if matches!(operator, BinaryOperator::Add) {
|
if matches!(operator, BinaryOperator::Add) {
|
||||||
// Check if left is self-reference (will be validated by caller)
|
// Check if left is self-reference (will be validated by caller)
|
||||||
if matches!(left.as_ref(), ASTNode::Variable { .. }) {
|
if matches!(left.as_ref(), ASTNode::Variable { .. }) {
|
||||||
@ -215,9 +226,9 @@ fn classify_update_kind_from_rhs(rhs: &crate::ast::ASTNode) -> UpdateKind {
|
|||||||
if let ASTNode::Literal { value, .. } = right.as_ref() {
|
if let ASTNode::Literal { value, .. } = right.as_ref() {
|
||||||
if let LiteralValue::Integer(n) = value {
|
if let LiteralValue::Integer(n) = value {
|
||||||
if *n == 1 {
|
if *n == 1 {
|
||||||
return UpdateKind::CounterLike; // x = x + 1
|
return UpdateKind::CounterLike; // x = x + 1
|
||||||
} else {
|
} else {
|
||||||
return UpdateKind::AccumulationLike; // x = x + n
|
return UpdateKind::AccumulationLike; // x = x + n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -251,7 +262,10 @@ fn classify_update_kind_from_rhs(rhs: &crate::ast::ASTNode) -> UpdateKind {
|
|||||||
/// Phase 219: Extract assignment RHS for a given variable
|
/// Phase 219: Extract assignment RHS for a given variable
|
||||||
///
|
///
|
||||||
/// Returns the RHS expression of the first assignment to `var_name` in loop body.
|
/// Returns the RHS expression of the first assignment to `var_name` in loop body.
|
||||||
fn find_assignment_rhs<'a>(var_name: &str, loop_body: &'a [crate::ast::ASTNode]) -> Option<&'a crate::ast::ASTNode> {
|
fn find_assignment_rhs<'a>(
|
||||||
|
var_name: &str,
|
||||||
|
loop_body: &'a [crate::ast::ASTNode],
|
||||||
|
) -> Option<&'a crate::ast::ASTNode> {
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
|
|
||||||
fn visit_node<'a>(var_name: &str, node: &'a ASTNode) -> Option<&'a ASTNode> {
|
fn visit_node<'a>(var_name: &str, node: &'a ASTNode) -> Option<&'a ASTNode> {
|
||||||
@ -265,7 +279,11 @@ fn find_assignment_rhs<'a>(var_name: &str, loop_body: &'a [crate::ast::ASTNode])
|
|||||||
// Recurse into value
|
// Recurse into value
|
||||||
visit_node(var_name, value)
|
visit_node(var_name, value)
|
||||||
}
|
}
|
||||||
ASTNode::If { then_body, else_body, .. } => {
|
ASTNode::If {
|
||||||
|
then_body,
|
||||||
|
else_body,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
for stmt in then_body {
|
for stmt in then_body {
|
||||||
if let Some(rhs) = visit_node(var_name, stmt) {
|
if let Some(rhs) = visit_node(var_name, stmt) {
|
||||||
return Some(rhs);
|
return Some(rhs);
|
||||||
@ -354,7 +372,7 @@ pub fn analyze_loop_updates_by_name(carrier_names: &[String]) -> LoopUpdateSumma
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|name| CarrierUpdateInfo {
|
.map(|name| CarrierUpdateInfo {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
kind: UpdateKind::AccumulationLike, // Default to accumulation
|
kind: UpdateKind::AccumulationLike, // Default to accumulation
|
||||||
then_expr: None,
|
then_expr: None,
|
||||||
else_expr: None,
|
else_expr: None,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -61,11 +61,7 @@ impl LoopViewBuilder {
|
|||||||
///
|
///
|
||||||
/// - `Some(JoinModule)`: Lowering成功
|
/// - `Some(JoinModule)`: Lowering成功
|
||||||
/// - `None`: 未サポートパターン(フォールバック経路へ)
|
/// - `None`: 未サポートパターン(フォールバック経路へ)
|
||||||
pub fn build(
|
pub fn build(&self, scope: LoopScopeShape, func_name: Option<&str>) -> Option<JoinModule> {
|
||||||
&self,
|
|
||||||
scope: LoopScopeShape,
|
|
||||||
func_name: Option<&str>,
|
|
||||||
) -> Option<JoinModule> {
|
|
||||||
let name = func_name.unwrap_or("");
|
let name = func_name.unwrap_or("");
|
||||||
|
|
||||||
// Phase 188-Impl-1: Pattern 1 (Simple While Loop) detection
|
// Phase 188-Impl-1: Pattern 1 (Simple While Loop) detection
|
||||||
@ -139,7 +135,10 @@ impl LoopViewBuilder {
|
|||||||
&mut join_value_space,
|
&mut join_value_space,
|
||||||
) {
|
) {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
eprintln!("[LoopViewBuilder] Pattern 1 lowering succeeded for {:?}", name);
|
eprintln!(
|
||||||
|
"[LoopViewBuilder] Pattern 1 lowering succeeded for {:?}",
|
||||||
|
name
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return Some(result);
|
return Some(result);
|
||||||
}
|
}
|
||||||
@ -181,7 +180,9 @@ impl LoopViewBuilder {
|
|||||||
}
|
}
|
||||||
CaseALoweringShape::IterationWithAccumulation => {
|
CaseALoweringShape::IterationWithAccumulation => {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
eprintln!("[LoopViewBuilder] Shape: IterationWithAccumulation → stage1 lowerer");
|
eprintln!(
|
||||||
|
"[LoopViewBuilder] Shape: IterationWithAccumulation → stage1 lowerer"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
generic_case_a::lower_case_a_stage1_usingresolver_with_scope(scope)
|
generic_case_a::lower_case_a_stage1_usingresolver_with_scope(scope)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,24 @@ pub(crate) fn build_fragment_meta(
|
|||||||
i_exit: ValueId,
|
i_exit: ValueId,
|
||||||
carrier_exit_ids: &[ValueId],
|
carrier_exit_ids: &[ValueId],
|
||||||
) -> JoinFragmentMeta {
|
) -> JoinFragmentMeta {
|
||||||
|
let dev_on = env::joinir_dev_enabled();
|
||||||
|
|
||||||
|
if carrier_exit_ids.len() != carrier_info.carriers.len() {
|
||||||
|
let msg = format!(
|
||||||
|
"[joinir/boundary] exit value length mismatch: carriers={} exit_ids={}",
|
||||||
|
carrier_info.carriers.len(),
|
||||||
|
carrier_exit_ids.len()
|
||||||
|
);
|
||||||
|
debug_assert!(
|
||||||
|
carrier_exit_ids.len() == carrier_info.carriers.len(),
|
||||||
|
"{}",
|
||||||
|
msg
|
||||||
|
);
|
||||||
|
if dev_on {
|
||||||
|
panic!("{}", msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut exit_values = Vec::new();
|
let mut exit_values = Vec::new();
|
||||||
exit_values.push((loop_var_name.to_string(), i_exit));
|
exit_values.push((loop_var_name.to_string(), i_exit));
|
||||||
|
|
||||||
@ -16,9 +34,9 @@ pub(crate) fn build_fragment_meta(
|
|||||||
exit_values.push((carrier.name.clone(), carrier_exit_ids[idx]));
|
exit_values.push((carrier.name.clone(), carrier_exit_ids[idx]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if env::joinir_debug_level() > 0 {
|
if env::joinir_debug_level() > 0 || dev_on {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[joinir/boundary_builder] Exit values (loop_var='{}', carriers={}): {:?}",
|
"[joinir/boundary] Exit values (loop_var='{}', carriers={}): {:?}",
|
||||||
loop_var_name,
|
loop_var_name,
|
||||||
carrier_info.carriers.len(),
|
carrier_info.carriers.len(),
|
||||||
exit_values
|
exit_values
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
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::condition_env::ConditionEnv;
|
||||||
use crate::mir::join_ir::lowering::condition_lowering_box::ConditionContext;
|
use crate::mir::join_ir::lowering::condition_lowering_box::ConditionContext;
|
||||||
use crate::mir::join_ir::lowering::condition_to_joinir::lower_condition_to_joinir;
|
use crate::mir::join_ir::lowering::condition_to_joinir::lower_condition_to_joinir;
|
||||||
use crate::mir::join_ir::lowering::expr_lowerer::{ExprContext, ExprLowerer};
|
use crate::mir::join_ir::lowering::expr_lowerer::{ExprContext, ExprLowerer};
|
||||||
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
|
|
||||||
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
|
|
||||||
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
|
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
|
||||||
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
|
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
|
||||||
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
|
||||||
use crate::mir::join_ir::JoinInst;
|
use crate::mir::join_ir::JoinInst;
|
||||||
|
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
use crate::mir::builder::MirBuilder;
|
|
||||||
|
|
||||||
/// Build a Pattern2ScopeManager for ExprLowerer paths.
|
/// Build a Pattern2ScopeManager for ExprLowerer paths.
|
||||||
fn make_scope_manager<'a>(
|
fn make_scope_manager<'a>(
|
||||||
|
|||||||
@ -62,18 +62,18 @@ use crate::mir::builder::MirBuilder;
|
|||||||
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::condition_to_joinir::{lower_condition_to_joinir, ConditionEnv};
|
use crate::mir::join_ir::lowering::condition_to_joinir::{lower_condition_to_joinir, ConditionEnv};
|
||||||
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_body_local_env::LoopBodyLocalEnv; // Phase 244
|
||||||
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, UpdateRhs};
|
use crate::mir::join_ir::lowering::loop_update_analyzer::{UpdateExpr, UpdateRhs};
|
||||||
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv; // Phase 244
|
|
||||||
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv; // Phase 244
|
|
||||||
use crate::mir::join_ir::{
|
use crate::mir::join_ir::{
|
||||||
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
|
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, MirLikeInst,
|
||||||
MirLikeInst, UnaryOp,
|
UnaryOp,
|
||||||
};
|
};
|
||||||
use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScopeBox;
|
|
||||||
use crate::mir::loop_pattern_detection::error_messages::{
|
use crate::mir::loop_pattern_detection::error_messages::{
|
||||||
format_unsupported_condition_error, extract_body_local_names,
|
extract_body_local_names, format_unsupported_condition_error,
|
||||||
};
|
};
|
||||||
|
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv; // Phase 244
|
||||||
|
use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScopeBox;
|
||||||
use crate::mir::ValueId;
|
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
|
||||||
|
|
||||||
@ -130,15 +130,15 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
// Phase 170-D-impl-3: Validate that loop condition only uses supported variable scopes
|
// Phase 170-D-impl-3: Validate that loop condition only uses supported variable scopes
|
||||||
// LoopConditionScopeBox checks that loop conditions don't reference loop-body-local variables
|
// LoopConditionScopeBox checks that loop conditions don't reference loop-body-local variables
|
||||||
let loop_var_name = carrier_info.loop_var_name.clone();
|
let loop_var_name = carrier_info.loop_var_name.clone();
|
||||||
let loop_cond_scope = LoopConditionScopeBox::analyze(
|
let loop_cond_scope =
|
||||||
&loop_var_name,
|
LoopConditionScopeBox::analyze(&loop_var_name, &[condition], Some(&_scope));
|
||||||
&[condition],
|
|
||||||
Some(&_scope),
|
|
||||||
);
|
|
||||||
|
|
||||||
if loop_cond_scope.has_loop_body_local() {
|
if loop_cond_scope.has_loop_body_local() {
|
||||||
let body_local_names = extract_body_local_names(&loop_cond_scope.vars);
|
let body_local_names = extract_body_local_names(&loop_cond_scope.vars);
|
||||||
return Err(format_unsupported_condition_error("pattern4", &body_local_names));
|
return Err(format_unsupported_condition_error(
|
||||||
|
"pattern4",
|
||||||
|
&body_local_names,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@ -152,7 +152,11 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
eprintln!(
|
eprintln!(
|
||||||
"[joinir/pattern4] Phase 202-C: Generating JoinIR for {} carriers: {:?}",
|
"[joinir/pattern4] Phase 202-C: Generating JoinIR for {} carriers: {:?}",
|
||||||
carrier_count,
|
carrier_count,
|
||||||
carrier_info.carriers.iter().map(|c| &c.name).collect::<Vec<_>>()
|
carrier_info
|
||||||
|
.carriers
|
||||||
|
.iter()
|
||||||
|
.map(|c| &c.name)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
);
|
);
|
||||||
|
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
@ -171,7 +175,7 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
for _ in 0..carrier_count {
|
for _ in 0..carrier_count {
|
||||||
carrier_init_ids.push(join_value_space.alloc_local());
|
carrier_init_ids.push(join_value_space.alloc_local());
|
||||||
}
|
}
|
||||||
let loop_result = join_value_space.alloc_local(); // result from loop_step
|
let loop_result = join_value_space.alloc_local(); // result from loop_step
|
||||||
|
|
||||||
// loop_step() parameters: [i_param, carrier1_param, carrier2_param, ...]
|
// loop_step() parameters: [i_param, carrier1_param, carrier2_param, ...]
|
||||||
let i_param = join_value_space.alloc_local();
|
let i_param = join_value_space.alloc_local();
|
||||||
@ -202,10 +206,12 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
|
|
||||||
// Phase 169 / Phase 171-fix / Phase 244: Lower condition using ConditionLoweringBox trait
|
// Phase 169 / Phase 171-fix / Phase 244: Lower condition using ConditionLoweringBox trait
|
||||||
let (cond_value, mut cond_instructions) = {
|
let (cond_value, mut cond_instructions) = {
|
||||||
use crate::mir::join_ir::lowering::expr_lowerer::{ExprContext, ExprLowerer};
|
|
||||||
use crate::mir::join_ir::lowering::condition_lowering_box::{ConditionLoweringBox, ConditionContext};
|
|
||||||
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
|
|
||||||
use crate::mir::builder::MirBuilder;
|
use crate::mir::builder::MirBuilder;
|
||||||
|
use crate::mir::join_ir::lowering::condition_lowering_box::{
|
||||||
|
ConditionContext, ConditionLoweringBox,
|
||||||
|
};
|
||||||
|
use crate::mir::join_ir::lowering::expr_lowerer::{ExprContext, ExprLowerer};
|
||||||
|
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
|
||||||
|
|
||||||
// Build minimal ScopeManager for header condition
|
// Build minimal ScopeManager for header condition
|
||||||
let empty_body_env = LoopBodyLocalEnv::new();
|
let empty_body_env = LoopBodyLocalEnv::new();
|
||||||
@ -221,7 +227,8 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
if ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(condition) {
|
if ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(condition) {
|
||||||
// Phase 244: ExprLowerer via ConditionLoweringBox trait
|
// Phase 244: ExprLowerer via ConditionLoweringBox trait
|
||||||
let mut dummy_builder = MirBuilder::new();
|
let mut dummy_builder = MirBuilder::new();
|
||||||
let mut expr_lowerer = ExprLowerer::new(&scope_manager, ExprContext::Condition, &mut dummy_builder);
|
let mut expr_lowerer =
|
||||||
|
ExprLowerer::new(&scope_manager, ExprContext::Condition, &mut dummy_builder);
|
||||||
|
|
||||||
let context = ConditionContext {
|
let context = ConditionContext {
|
||||||
loop_var_name: loop_var_name.clone(),
|
loop_var_name: loop_var_name.clone(),
|
||||||
@ -263,7 +270,7 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
let mut carrier_next_ids: Vec<ValueId> = Vec::new();
|
let mut carrier_next_ids: Vec<ValueId> = Vec::new();
|
||||||
let mut carrier_merged_ids: Vec<ValueId> = Vec::new();
|
let mut carrier_merged_ids: Vec<ValueId> = Vec::new();
|
||||||
for _ in 0..carrier_count {
|
for _ in 0..carrier_count {
|
||||||
carrier_next_ids.push(alloc_value()); // carrier_next = carrier + ...
|
carrier_next_ids.push(alloc_value()); // carrier_next = carrier + ...
|
||||||
carrier_merged_ids.push(alloc_value()); // carrier_merged = Select(...)
|
carrier_merged_ids.push(alloc_value()); // carrier_merged = Select(...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,11 +310,8 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
let mut loop_step_params = vec![i_param];
|
let mut loop_step_params = vec![i_param];
|
||||||
loop_step_params.extend(carrier_param_ids.iter().copied());
|
loop_step_params.extend(carrier_param_ids.iter().copied());
|
||||||
|
|
||||||
let mut loop_step_func = JoinFunction::new(
|
let mut loop_step_func =
|
||||||
loop_step_id,
|
JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_step_params);
|
||||||
"loop_step".to_string(),
|
|
||||||
loop_step_params,
|
|
||||||
);
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
// Natural Exit Condition Check (Phase 169: from AST)
|
// Natural Exit Condition Check (Phase 169: from AST)
|
||||||
@ -408,9 +412,15 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
let carrier_name = &carrier_info.carriers[idx].name;
|
let carrier_name = &carrier_info.carriers[idx].name;
|
||||||
|
|
||||||
// Phase 197: Extract RHS from update expression metadata
|
// Phase 197: Extract RHS from update expression metadata
|
||||||
eprintln!("[loop_with_continue_minimal] Processing carrier '{}' (idx={})", carrier_name, idx);
|
eprintln!(
|
||||||
|
"[loop_with_continue_minimal] Processing carrier '{}' (idx={})",
|
||||||
|
carrier_name, idx
|
||||||
|
);
|
||||||
let rhs = if let Some(update_expr) = carrier_updates.get(carrier_name) {
|
let rhs = if let Some(update_expr) = carrier_updates.get(carrier_name) {
|
||||||
eprintln!("[loop_with_continue_minimal] Found update expr: {:?}", update_expr);
|
eprintln!(
|
||||||
|
"[loop_with_continue_minimal] Found update expr: {:?}",
|
||||||
|
update_expr
|
||||||
|
);
|
||||||
match update_expr {
|
match update_expr {
|
||||||
UpdateExpr::BinOp { op, rhs, .. } => {
|
UpdateExpr::BinOp { op, rhs, .. } => {
|
||||||
// Verify operator is Add (only supported for now)
|
// Verify operator is Add (only supported for now)
|
||||||
@ -451,12 +461,12 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
// This is effectively a passthrough (no JoinIR update)
|
// This is effectively a passthrough (no JoinIR update)
|
||||||
loop_step_func.body.push(JoinInst::Select {
|
loop_step_func.body.push(JoinInst::Select {
|
||||||
dst: carrier_merged,
|
dst: carrier_merged,
|
||||||
cond: continue_cond, // Condition doesn't matter when both values are same
|
cond: continue_cond, // Condition doesn't matter when both values are same
|
||||||
then_val: carrier_param,
|
then_val: carrier_param,
|
||||||
else_val: carrier_param,
|
else_val: carrier_param,
|
||||||
type_hint: None,
|
type_hint: None,
|
||||||
});
|
});
|
||||||
continue; // Skip the BinOp and normal Select below
|
continue; // Skip the BinOp and normal Select below
|
||||||
}
|
}
|
||||||
// Phase 178: String updates detected but not lowered to JoinIR yet
|
// Phase 178: String updates detected but not lowered to JoinIR yet
|
||||||
// Skip JoinIR update - use Select passthrough to keep carrier_merged defined
|
// Skip JoinIR update - use Select passthrough to keep carrier_merged defined
|
||||||
@ -469,12 +479,12 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
// This is effectively a passthrough (no JoinIR update)
|
// This is effectively a passthrough (no JoinIR update)
|
||||||
loop_step_func.body.push(JoinInst::Select {
|
loop_step_func.body.push(JoinInst::Select {
|
||||||
dst: carrier_merged,
|
dst: carrier_merged,
|
||||||
cond: continue_cond, // Condition doesn't matter when both values are same
|
cond: continue_cond, // Condition doesn't matter when both values are same
|
||||||
then_val: carrier_param,
|
then_val: carrier_param,
|
||||||
else_val: carrier_param,
|
else_val: carrier_param,
|
||||||
type_hint: None,
|
type_hint: None,
|
||||||
});
|
});
|
||||||
continue; // Skip the BinOp and normal Select below
|
continue; // Skip the BinOp and normal Select below
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -495,8 +505,10 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// carrier_next = carrier_param + rhs
|
// carrier_next = carrier_param + rhs
|
||||||
eprintln!("[loop_with_continue_minimal] Generating: ValueId({}) = ValueId({}) + ValueId({})",
|
eprintln!(
|
||||||
carrier_next.0, carrier_param.0, rhs.0);
|
"[loop_with_continue_minimal] Generating: ValueId({}) = ValueId({}) + ValueId({})",
|
||||||
|
carrier_next.0, carrier_param.0, rhs.0
|
||||||
|
);
|
||||||
loop_step_func
|
loop_step_func
|
||||||
.body
|
.body
|
||||||
.push(JoinInst::Compute(MirLikeInst::BinOp {
|
.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
@ -510,8 +522,8 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
loop_step_func.body.push(JoinInst::Select {
|
loop_step_func.body.push(JoinInst::Select {
|
||||||
dst: carrier_merged,
|
dst: carrier_merged,
|
||||||
cond: continue_cond,
|
cond: continue_cond,
|
||||||
then_val: carrier_param, // Continue: no update
|
then_val: carrier_param, // Continue: no update
|
||||||
else_val: carrier_next, // Normal: update
|
else_val: carrier_next, // Normal: update
|
||||||
type_hint: None,
|
type_hint: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -525,7 +537,7 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
loop_step_func.body.push(JoinInst::Call {
|
loop_step_func.body.push(JoinInst::Call {
|
||||||
func: loop_step_id,
|
func: loop_step_id,
|
||||||
args: tail_call_args,
|
args: tail_call_args,
|
||||||
k_next: None, // CRITICAL: None for tail call
|
k_next: None, // CRITICAL: None for tail call
|
||||||
dst: None,
|
dst: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -534,11 +546,8 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
// ==================================================================
|
// ==================================================================
|
||||||
// k_exit(carrier1_exit, carrier2_exit, ...) function
|
// k_exit(carrier1_exit, carrier2_exit, ...) function
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
let mut k_exit_func = JoinFunction::new(
|
let mut k_exit_func =
|
||||||
k_exit_id,
|
JoinFunction::new(k_exit_id, "k_exit".to_string(), carrier_exit_ids.clone());
|
||||||
"k_exit".to_string(),
|
|
||||||
carrier_exit_ids.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// For now, return the first carrier's exit value (or void if no carriers)
|
// For now, return the first carrier's exit value (or void if no carriers)
|
||||||
// TODO: Consider returning a tuple or using a different mechanism
|
// TODO: Consider returning a tuple or using a different mechanism
|
||||||
@ -562,7 +571,11 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
eprintln!(
|
eprintln!(
|
||||||
"[joinir/pattern4] Carriers: {} ({:?})",
|
"[joinir/pattern4] Carriers: {} ({:?})",
|
||||||
carrier_count,
|
carrier_count,
|
||||||
carrier_info.carriers.iter().map(|c| c.name.as_str()).collect::<Vec<_>>()
|
carrier_info
|
||||||
|
.carriers
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.name.as_str())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
);
|
);
|
||||||
|
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
|
|||||||
@ -32,14 +32,16 @@ use crate::ast::ASTNode;
|
|||||||
use crate::mir::join_ir::lowering::carrier_info::{ExitMeta, JoinFragmentMeta};
|
use crate::mir::join_ir::lowering::carrier_info::{ExitMeta, JoinFragmentMeta};
|
||||||
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
|
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
|
||||||
use crate::mir::join_ir::lowering::condition_lowerer::lower_value_expression;
|
use crate::mir::join_ir::lowering::condition_lowerer::lower_value_expression;
|
||||||
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
use crate::mir::join_ir::lowering::condition_pattern::{analyze_condition_pattern, ConditionPattern};
|
use crate::mir::join_ir::lowering::condition_pattern::{
|
||||||
use crate::mir::ValueId;
|
analyze_condition_pattern, ConditionPattern,
|
||||||
use crate::mir::join_ir::{
|
|
||||||
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
|
|
||||||
MirLikeInst, UnaryOp,
|
|
||||||
};
|
};
|
||||||
|
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
||||||
|
use crate::mir::join_ir::{
|
||||||
|
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, MirLikeInst,
|
||||||
|
UnaryOp,
|
||||||
|
};
|
||||||
|
use crate::mir::ValueId;
|
||||||
|
|
||||||
/// Phase 213: Lower if-sum pattern to JoinIR using AST
|
/// Phase 213: Lower if-sum pattern to JoinIR using AST
|
||||||
///
|
///
|
||||||
@ -83,22 +85,34 @@ pub fn lower_if_sum_pattern(
|
|||||||
// Uses cond_env for variable resolution (e.g., `len` in `i < len`)
|
// Uses cond_env for variable resolution (e.g., `len` in `i < len`)
|
||||||
let (loop_var, loop_op, loop_lhs_val, loop_limit_val, loop_limit_insts) =
|
let (loop_var, loop_op, loop_lhs_val, loop_limit_val, loop_limit_insts) =
|
||||||
extract_loop_condition(loop_condition, &mut alloc_value, cond_env)?;
|
extract_loop_condition(loop_condition, &mut alloc_value, cond_env)?;
|
||||||
eprintln!("[joinir/pattern3/if-sum] Loop condition: {} {:?} ValueId({})", loop_var, loop_op, loop_limit_val.0);
|
eprintln!(
|
||||||
|
"[joinir/pattern3/if-sum] Loop condition: {} {:?} ValueId({})",
|
||||||
|
loop_var, loop_op, loop_limit_val.0
|
||||||
|
);
|
||||||
|
|
||||||
// Step 2: Extract if condition info (e.g., i > 0 → var="i", op=Gt, value=ValueId)
|
// Step 2: Extract if condition info (e.g., i > 0 → var="i", op=Gt, value=ValueId)
|
||||||
// Phase 220-D: Now returns ValueId and instructions
|
// Phase 220-D: Now returns ValueId and instructions
|
||||||
// Phase 242-EX-A: Now supports complex LHS
|
// Phase 242-EX-A: Now supports complex LHS
|
||||||
let (if_var, if_op, if_lhs_val, if_value_val, if_value_insts) =
|
let (if_var, if_op, if_lhs_val, if_value_val, if_value_insts) =
|
||||||
extract_if_condition(if_stmt, &mut alloc_value, cond_env)?;
|
extract_if_condition(if_stmt, &mut alloc_value, cond_env)?;
|
||||||
eprintln!("[joinir/pattern3/if-sum] If condition: {} {:?} ValueId({})", if_var, if_op, if_value_val.0);
|
eprintln!(
|
||||||
|
"[joinir/pattern3/if-sum] If condition: {} {:?} ValueId({})",
|
||||||
|
if_var, if_op, if_value_val.0
|
||||||
|
);
|
||||||
|
|
||||||
// Step 3: Extract then-branch update (e.g., sum = sum + 1 → var="sum", addend=1)
|
// Step 3: Extract then-branch update (e.g., sum = sum + 1 → var="sum", addend=1)
|
||||||
let (update_var, update_addend) = extract_then_update(if_stmt)?;
|
let (update_var, update_addend) = extract_then_update(if_stmt)?;
|
||||||
eprintln!("[joinir/pattern3/if-sum] Then update: {} += {}", update_var, update_addend);
|
eprintln!(
|
||||||
|
"[joinir/pattern3/if-sum] Then update: {} += {}",
|
||||||
|
update_var, update_addend
|
||||||
|
);
|
||||||
|
|
||||||
// Step 4: Extract counter update (e.g., i = i + 1 → var="i", step=1)
|
// Step 4: Extract counter update (e.g., i = i + 1 → var="i", step=1)
|
||||||
let (counter_var, counter_step) = extract_counter_update(body, &loop_var)?;
|
let (counter_var, counter_step) = extract_counter_update(body, &loop_var)?;
|
||||||
eprintln!("[joinir/pattern3/if-sum] Counter update: {} += {}", counter_var, counter_step);
|
eprintln!(
|
||||||
|
"[joinir/pattern3/if-sum] Counter update: {} += {}",
|
||||||
|
counter_var, counter_step
|
||||||
|
);
|
||||||
|
|
||||||
// Step 5: Generate JoinIR
|
// Step 5: Generate JoinIR
|
||||||
let mut alloc_value = || join_value_space.alloc_local();
|
let mut alloc_value = || join_value_space.alloc_local();
|
||||||
@ -111,10 +125,10 @@ pub fn lower_if_sum_pattern(
|
|||||||
|
|
||||||
// === ValueId allocation ===
|
// === ValueId allocation ===
|
||||||
// main() locals
|
// main() locals
|
||||||
let i_init_val = alloc_value(); // i = 0
|
let i_init_val = alloc_value(); // i = 0
|
||||||
let sum_init_val = alloc_value(); // sum = 0
|
let sum_init_val = alloc_value(); // sum = 0
|
||||||
let count_init_val = alloc_value(); // count = 0 (optional)
|
let count_init_val = alloc_value(); // count = 0 (optional)
|
||||||
let loop_result = alloc_value(); // result from loop_step
|
let loop_result = alloc_value(); // result from loop_step
|
||||||
|
|
||||||
// loop_step params
|
// loop_step params
|
||||||
let i_param = alloc_value();
|
let i_param = alloc_value();
|
||||||
@ -124,19 +138,19 @@ pub fn lower_if_sum_pattern(
|
|||||||
// loop_step locals
|
// loop_step locals
|
||||||
// Phase 220-D: loop_limit_val and if_value_val are already allocated by extract_*_condition()
|
// Phase 220-D: loop_limit_val and if_value_val are already allocated by extract_*_condition()
|
||||||
// and will be used directly from their return values
|
// and will be used directly from their return values
|
||||||
let cmp_loop = alloc_value(); // loop condition comparison
|
let cmp_loop = alloc_value(); // loop condition comparison
|
||||||
let exit_cond = alloc_value(); // negated loop condition
|
let exit_cond = alloc_value(); // negated loop condition
|
||||||
let if_cmp = alloc_value(); // if condition comparison
|
let if_cmp = alloc_value(); // if condition comparison
|
||||||
let sum_then = alloc_value(); // sum + update_addend
|
let sum_then = alloc_value(); // sum + update_addend
|
||||||
let count_const = alloc_value(); // count increment (1)
|
let count_const = alloc_value(); // count increment (1)
|
||||||
let count_then = alloc_value(); // count + 1
|
let count_then = alloc_value(); // count + 1
|
||||||
let const_0 = alloc_value(); // 0 for else branch
|
let const_0 = alloc_value(); // 0 for else branch
|
||||||
let sum_else = alloc_value(); // sum + 0 (identity)
|
let sum_else = alloc_value(); // sum + 0 (identity)
|
||||||
let count_else = alloc_value(); // count + 0 (identity)
|
let count_else = alloc_value(); // count + 0 (identity)
|
||||||
let sum_new = alloc_value(); // Select result for sum
|
let sum_new = alloc_value(); // Select result for sum
|
||||||
let count_new = alloc_value(); // Select result for count
|
let count_new = alloc_value(); // Select result for count
|
||||||
let step_const = alloc_value(); // counter step
|
let step_const = alloc_value(); // counter step
|
||||||
let i_next = alloc_value(); // i + step
|
let i_next = alloc_value(); // i + step
|
||||||
|
|
||||||
// k_exit params
|
// k_exit params
|
||||||
let sum_final = alloc_value();
|
let sum_final = alloc_value();
|
||||||
@ -148,7 +162,7 @@ pub fn lower_if_sum_pattern(
|
|||||||
// i_init = 0 (initial value from ctx)
|
// i_init = 0 (initial value from ctx)
|
||||||
main_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
main_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
dst: i_init_val,
|
dst: i_init_val,
|
||||||
value: ConstValue::Integer(0), // TODO: Get from AST
|
value: ConstValue::Integer(0), // TODO: Get from AST
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// sum_init = 0
|
// sum_init = 0
|
||||||
@ -195,19 +209,23 @@ pub fn lower_if_sum_pattern(
|
|||||||
// Compare: i < limit (or other op from AST)
|
// Compare: i < limit (or other op from AST)
|
||||||
// Phase 242-EX-A: Use computed LHS if available, otherwise use loop parameter
|
// Phase 242-EX-A: Use computed LHS if available, otherwise use loop parameter
|
||||||
let loop_lhs = loop_lhs_val.unwrap_or(i_param);
|
let loop_lhs = loop_lhs_val.unwrap_or(i_param);
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
loop_step_func
|
||||||
dst: cmp_loop,
|
.body
|
||||||
op: loop_op,
|
.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
lhs: loop_lhs,
|
dst: cmp_loop,
|
||||||
rhs: loop_limit_val,
|
op: loop_op,
|
||||||
}));
|
lhs: loop_lhs,
|
||||||
|
rhs: loop_limit_val,
|
||||||
|
}));
|
||||||
|
|
||||||
// exit_cond = !cmp_loop
|
// exit_cond = !cmp_loop
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::UnaryOp {
|
loop_step_func
|
||||||
dst: exit_cond,
|
.body
|
||||||
op: UnaryOp::Not,
|
.push(JoinInst::Compute(MirLikeInst::UnaryOp {
|
||||||
operand: cmp_loop,
|
dst: exit_cond,
|
||||||
}));
|
op: UnaryOp::Not,
|
||||||
|
operand: cmp_loop,
|
||||||
|
}));
|
||||||
|
|
||||||
// Jump to exit if condition is false
|
// Jump to exit if condition is false
|
||||||
loop_step_func.body.push(JoinInst::Jump {
|
loop_step_func.body.push(JoinInst::Jump {
|
||||||
@ -226,86 +244,110 @@ pub fn lower_if_sum_pattern(
|
|||||||
// Compare: if_var <op> if_value
|
// Compare: if_var <op> if_value
|
||||||
// Phase 242-EX-A: Use computed LHS if available, otherwise use loop parameter
|
// Phase 242-EX-A: Use computed LHS if available, otherwise use loop parameter
|
||||||
let if_lhs = if_lhs_val.unwrap_or(i_param);
|
let if_lhs = if_lhs_val.unwrap_or(i_param);
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
loop_step_func
|
||||||
dst: if_cmp,
|
.body
|
||||||
op: if_op,
|
.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
lhs: if_lhs,
|
dst: if_cmp,
|
||||||
rhs: if_value_val,
|
op: if_op,
|
||||||
}));
|
lhs: if_lhs,
|
||||||
|
rhs: if_value_val,
|
||||||
|
}));
|
||||||
|
|
||||||
// --- Then Branch ---
|
// --- Then Branch ---
|
||||||
// sum_then = sum + update_addend
|
// sum_then = sum + update_addend
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
loop_step_func
|
||||||
dst: const_0,
|
.body
|
||||||
value: ConstValue::Integer(update_addend),
|
.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
}));
|
dst: const_0,
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
value: ConstValue::Integer(update_addend),
|
||||||
dst: sum_then,
|
}));
|
||||||
op: BinOpKind::Add,
|
loop_step_func
|
||||||
lhs: sum_param,
|
.body
|
||||||
rhs: const_0,
|
.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
}));
|
dst: sum_then,
|
||||||
|
op: BinOpKind::Add,
|
||||||
|
lhs: sum_param,
|
||||||
|
rhs: const_0,
|
||||||
|
}));
|
||||||
|
|
||||||
// count_then = count + 1
|
// count_then = count + 1
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
loop_step_func
|
||||||
dst: count_const,
|
.body
|
||||||
value: ConstValue::Integer(1),
|
.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
}));
|
dst: count_const,
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
value: ConstValue::Integer(1),
|
||||||
dst: count_then,
|
}));
|
||||||
op: BinOpKind::Add,
|
loop_step_func
|
||||||
lhs: count_param,
|
.body
|
||||||
rhs: count_const,
|
.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
}));
|
dst: count_then,
|
||||||
|
op: BinOpKind::Add,
|
||||||
|
lhs: count_param,
|
||||||
|
rhs: count_const,
|
||||||
|
}));
|
||||||
|
|
||||||
// --- Else Branch ---
|
// --- Else Branch ---
|
||||||
// sum_else = sum + 0 (identity)
|
// sum_else = sum + 0 (identity)
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
loop_step_func
|
||||||
dst: step_const, // reuse for 0
|
.body
|
||||||
value: ConstValue::Integer(0),
|
.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
}));
|
dst: step_const, // reuse for 0
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
value: ConstValue::Integer(0),
|
||||||
dst: sum_else,
|
}));
|
||||||
op: BinOpKind::Add,
|
loop_step_func
|
||||||
lhs: sum_param,
|
.body
|
||||||
rhs: step_const,
|
.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
}));
|
dst: sum_else,
|
||||||
|
op: BinOpKind::Add,
|
||||||
|
lhs: sum_param,
|
||||||
|
rhs: step_const,
|
||||||
|
}));
|
||||||
|
|
||||||
// count_else = count + 0 (identity)
|
// count_else = count + 0 (identity)
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
loop_step_func
|
||||||
dst: count_else,
|
.body
|
||||||
op: BinOpKind::Add,
|
.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
lhs: count_param,
|
dst: count_else,
|
||||||
rhs: step_const, // 0
|
op: BinOpKind::Add,
|
||||||
}));
|
lhs: count_param,
|
||||||
|
rhs: step_const, // 0
|
||||||
|
}));
|
||||||
|
|
||||||
// --- Select ---
|
// --- Select ---
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Select {
|
loop_step_func
|
||||||
dst: sum_new,
|
.body
|
||||||
cond: if_cmp,
|
.push(JoinInst::Compute(MirLikeInst::Select {
|
||||||
then_val: sum_then,
|
dst: sum_new,
|
||||||
else_val: sum_else,
|
cond: if_cmp,
|
||||||
}));
|
then_val: sum_then,
|
||||||
|
else_val: sum_else,
|
||||||
|
}));
|
||||||
|
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Select {
|
loop_step_func
|
||||||
dst: count_new,
|
.body
|
||||||
cond: if_cmp,
|
.push(JoinInst::Compute(MirLikeInst::Select {
|
||||||
then_val: count_then,
|
dst: count_new,
|
||||||
else_val: count_else,
|
cond: if_cmp,
|
||||||
}));
|
then_val: count_then,
|
||||||
|
else_val: count_else,
|
||||||
|
}));
|
||||||
|
|
||||||
// --- Counter Update ---
|
// --- Counter Update ---
|
||||||
let step_const2 = alloc_value();
|
let step_const2 = alloc_value();
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
loop_step_func
|
||||||
dst: step_const2,
|
.body
|
||||||
value: ConstValue::Integer(counter_step),
|
.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
}));
|
dst: step_const2,
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
value: ConstValue::Integer(counter_step),
|
||||||
dst: i_next,
|
}));
|
||||||
op: BinOpKind::Add,
|
loop_step_func
|
||||||
lhs: i_param,
|
.body
|
||||||
rhs: step_const2,
|
.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
}));
|
dst: i_next,
|
||||||
|
op: BinOpKind::Add,
|
||||||
|
lhs: i_param,
|
||||||
|
rhs: step_const2,
|
||||||
|
}));
|
||||||
|
|
||||||
// --- Tail Recursion ---
|
// --- Tail Recursion ---
|
||||||
loop_step_func.body.push(JoinInst::Call {
|
loop_step_func.body.push(JoinInst::Call {
|
||||||
@ -343,9 +385,18 @@ pub fn lower_if_sum_pattern(
|
|||||||
let fragment_meta = JoinFragmentMeta::with_expr_result(sum_final, exit_meta);
|
let fragment_meta = JoinFragmentMeta::with_expr_result(sum_final, exit_meta);
|
||||||
|
|
||||||
eprintln!("[joinir/pattern3/if-sum] Generated AST-based JoinIR");
|
eprintln!("[joinir/pattern3/if-sum] Generated AST-based JoinIR");
|
||||||
eprintln!("[joinir/pattern3/if-sum] Loop: {} {:?} ValueId({})", loop_var, loop_op, loop_limit_val.0);
|
eprintln!(
|
||||||
eprintln!("[joinir/pattern3/if-sum] If: {} {:?} ValueId({})", if_var, if_op, if_value_val.0);
|
"[joinir/pattern3/if-sum] Loop: {} {:?} ValueId({})",
|
||||||
eprintln!("[joinir/pattern3/if-sum] Phase 215-2: expr_result={:?}", sum_final);
|
loop_var, loop_op, loop_limit_val.0
|
||||||
|
);
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/pattern3/if-sum] If: {} {:?} ValueId({})",
|
||||||
|
if_var, if_op, if_value_val.0
|
||||||
|
);
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/pattern3/if-sum] Phase 215-2: expr_result={:?}",
|
||||||
|
sum_final
|
||||||
|
);
|
||||||
|
|
||||||
Ok((join_module, fragment_meta))
|
Ok((join_module, fragment_meta))
|
||||||
}
|
}
|
||||||
@ -406,7 +457,12 @@ where
|
|||||||
ConditionValue::Variable(var_name) => {
|
ConditionValue::Variable(var_name) => {
|
||||||
let var_node = ASTNode::Variable {
|
let var_node = ASTNode::Variable {
|
||||||
name: var_name,
|
name: var_name,
|
||||||
span: crate::ast::Span { start: 0, end: 0, line: 1, column: 1 },
|
span: crate::ast::Span {
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
line: 1,
|
||||||
|
column: 1,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
lower_value_expression(&var_node, alloc_value, cond_env, &mut limit_instructions)?
|
lower_value_expression(&var_node, alloc_value, cond_env, &mut limit_instructions)?
|
||||||
}
|
}
|
||||||
@ -418,7 +474,12 @@ where
|
|||||||
// Phase 242-EX-A: Normalization failed → handle complex conditions dynamically
|
// Phase 242-EX-A: Normalization failed → handle complex conditions dynamically
|
||||||
// Support: `expr CmpOp expr` (e.g., `i % 2 == 1`, `a + b > c`)
|
// Support: `expr CmpOp expr` (e.g., `i % 2 == 1`, `a + b > c`)
|
||||||
match cond {
|
match cond {
|
||||||
ASTNode::BinaryOp { operator, left, right, .. } => {
|
ASTNode::BinaryOp {
|
||||||
|
operator,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
use crate::ast::BinaryOperator;
|
use crate::ast::BinaryOperator;
|
||||||
|
|
||||||
// Convert operator to CompareOp
|
// Convert operator to CompareOp
|
||||||
@ -429,7 +490,12 @@ where
|
|||||||
BinaryOperator::GreaterEqual => CompareOp::Ge,
|
BinaryOperator::GreaterEqual => CompareOp::Ge,
|
||||||
BinaryOperator::Equal => CompareOp::Eq,
|
BinaryOperator::Equal => CompareOp::Eq,
|
||||||
BinaryOperator::NotEqual => CompareOp::Ne,
|
BinaryOperator::NotEqual => CompareOp::Ne,
|
||||||
_ => return Err(format!("[if-sum] Unsupported operator in condition: {:?}", operator)),
|
_ => {
|
||||||
|
return Err(format!(
|
||||||
|
"[if-sum] Unsupported operator in condition: {:?}",
|
||||||
|
operator
|
||||||
|
))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Lower left-hand side (complex expression)
|
// Lower left-hand side (complex expression)
|
||||||
@ -473,7 +539,7 @@ where
|
|||||||
{
|
{
|
||||||
match if_stmt {
|
match if_stmt {
|
||||||
ASTNode::If { condition, .. } => {
|
ASTNode::If { condition, .. } => {
|
||||||
extract_loop_condition(condition, alloc_value, cond_env) // Same format
|
extract_loop_condition(condition, alloc_value, cond_env) // Same format
|
||||||
}
|
}
|
||||||
_ => Err("[if-sum] Expected If statement".to_string()),
|
_ => Err("[if-sum] Expected If statement".to_string()),
|
||||||
}
|
}
|
||||||
@ -490,7 +556,13 @@ fn extract_then_update(if_stmt: &ASTNode) -> Result<(String, i64), String> {
|
|||||||
if let ASTNode::Assignment { target, value, .. } = stmt {
|
if let ASTNode::Assignment { target, value, .. } = stmt {
|
||||||
let target_name = extract_variable_name(&**target)?;
|
let target_name = extract_variable_name(&**target)?;
|
||||||
// Check if value is var + lit
|
// Check if value is var + lit
|
||||||
if let ASTNode::BinaryOp { operator: crate::ast::BinaryOperator::Add, left, right, .. } = value.as_ref() {
|
if let ASTNode::BinaryOp {
|
||||||
|
operator: crate::ast::BinaryOperator::Add,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
..
|
||||||
|
} = value.as_ref()
|
||||||
|
{
|
||||||
let lhs_name = extract_variable_name(left)?;
|
let lhs_name = extract_variable_name(left)?;
|
||||||
if lhs_name == target_name {
|
if lhs_name == target_name {
|
||||||
let addend = extract_integer_literal(right)?;
|
let addend = extract_integer_literal(right)?;
|
||||||
@ -513,7 +585,13 @@ fn extract_counter_update(body: &[ASTNode], loop_var: &str) -> Result<(String, i
|
|||||||
if let ASTNode::Assignment { target, value, .. } = stmt {
|
if let ASTNode::Assignment { target, value, .. } = stmt {
|
||||||
if let Ok(target_name) = extract_variable_name(&**target) {
|
if let Ok(target_name) = extract_variable_name(&**target) {
|
||||||
if target_name == loop_var {
|
if target_name == loop_var {
|
||||||
if let ASTNode::BinaryOp { operator: crate::ast::BinaryOperator::Add, left, right, .. } = value.as_ref() {
|
if let ASTNode::BinaryOp {
|
||||||
|
operator: crate::ast::BinaryOperator::Add,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
..
|
||||||
|
} = value.as_ref()
|
||||||
|
{
|
||||||
let lhs_name = extract_variable_name(left)?;
|
let lhs_name = extract_variable_name(left)?;
|
||||||
if lhs_name == target_name {
|
if lhs_name == target_name {
|
||||||
let step = extract_integer_literal(right)?;
|
let step = extract_integer_literal(right)?;
|
||||||
@ -524,7 +602,10 @@ fn extract_counter_update(body: &[ASTNode], loop_var: &str) -> Result<(String, i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(format!("[if-sum] No counter update found for '{}'", loop_var))
|
Err(format!(
|
||||||
|
"[if-sum] No counter update found for '{}'",
|
||||||
|
loop_var
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract variable name from AST node
|
/// Extract variable name from AST node
|
||||||
@ -541,7 +622,10 @@ fn extract_variable_name(node: &ASTNode) -> Result<String, String> {
|
|||||||
/// For condition values, use `lower_value_expression()` which supports variables.
|
/// For condition values, use `lower_value_expression()` which supports variables.
|
||||||
fn extract_integer_literal(node: &ASTNode) -> Result<i64, String> {
|
fn extract_integer_literal(node: &ASTNode) -> Result<i64, String> {
|
||||||
match node {
|
match node {
|
||||||
ASTNode::Literal { value: crate::ast::LiteralValue::Integer(n), .. } => Ok(*n),
|
ASTNode::Literal {
|
||||||
|
value: crate::ast::LiteralValue::Integer(n),
|
||||||
|
..
|
||||||
|
} => Ok(*n),
|
||||||
_ => Err(format!("[if-sum] Expected integer literal, got {:?}", node)),
|
_ => Err(format!("[if-sum] Expected integer literal, got {:?}", node)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -602,14 +686,8 @@ mod tests {
|
|||||||
int_lit(1),
|
int_lit(1),
|
||||||
);
|
);
|
||||||
|
|
||||||
let sum_update = assignment(
|
let sum_update = assignment(var("sum"), bin(BinaryOperator::Add, var("sum"), int_lit(1)));
|
||||||
var("sum"),
|
let counter_update = assignment(var("i"), bin(BinaryOperator::Add, var("i"), int_lit(1)));
|
||||||
bin(BinaryOperator::Add, var("sum"), int_lit(1)),
|
|
||||||
);
|
|
||||||
let counter_update = assignment(
|
|
||||||
var("i"),
|
|
||||||
bin(BinaryOperator::Add, var("i"), int_lit(1)),
|
|
||||||
);
|
|
||||||
|
|
||||||
let if_stmt = ASTNode::If {
|
let if_stmt = ASTNode::If {
|
||||||
condition: Box::new(if_condition),
|
condition: Box::new(if_condition),
|
||||||
@ -634,7 +712,9 @@ mod tests {
|
|||||||
for func in module.functions.values() {
|
for func in module.functions.values() {
|
||||||
for inst in &func.body {
|
for inst in &func.body {
|
||||||
match inst {
|
match inst {
|
||||||
JoinInst::Compute(MirLikeInst::BinOp { op: BinOpKind::Mod, .. }) => {
|
JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
op: BinOpKind::Mod, ..
|
||||||
|
}) => {
|
||||||
has_mod = true;
|
has_mod = true;
|
||||||
}
|
}
|
||||||
JoinInst::Compute(MirLikeInst::Compare { .. }) => {
|
JoinInst::Compute(MirLikeInst::Compare { .. }) => {
|
||||||
|
|||||||
@ -116,7 +116,10 @@ impl MethodCallLowerer {
|
|||||||
if args.len() != expected_arity {
|
if args.len() != expected_arity {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Arity mismatch: {}.{}() expects {} args, got {}",
|
"Arity mismatch: {}.{}() expects {} args, got {}",
|
||||||
recv_val.0, method_name, expected_arity, args.len()
|
recv_val.0,
|
||||||
|
method_name,
|
||||||
|
expected_arity,
|
||||||
|
args.len()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +130,7 @@ impl MethodCallLowerer {
|
|||||||
arg_ast,
|
arg_ast,
|
||||||
alloc_value,
|
alloc_value,
|
||||||
env,
|
env,
|
||||||
instructions
|
instructions,
|
||||||
)?;
|
)?;
|
||||||
lowered_args.push(arg_val);
|
lowered_args.push(arg_val);
|
||||||
}
|
}
|
||||||
@ -201,7 +204,10 @@ impl MethodCallLowerer {
|
|||||||
if args.len() != expected_arity {
|
if args.len() != expected_arity {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Arity mismatch: {}.{}() expects {} args, got {}",
|
"Arity mismatch: {}.{}() expects {} args, got {}",
|
||||||
recv_val.0, method_name, expected_arity, args.len()
|
recv_val.0,
|
||||||
|
method_name,
|
||||||
|
expected_arity,
|
||||||
|
args.len()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,7 +220,7 @@ impl MethodCallLowerer {
|
|||||||
alloc_value,
|
alloc_value,
|
||||||
cond_env,
|
cond_env,
|
||||||
body_local_env,
|
body_local_env,
|
||||||
instructions
|
instructions,
|
||||||
)?;
|
)?;
|
||||||
lowered_args.push(arg_val);
|
lowered_args.push(arg_val);
|
||||||
}
|
}
|
||||||
@ -311,8 +317,8 @@ impl MethodCallLowerer {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::mir::join_ir::JoinInst;
|
|
||||||
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
|
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
|
||||||
|
use crate::mir::join_ir::JoinInst;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_resolve_string_length() {
|
fn test_resolve_string_length() {
|
||||||
@ -391,7 +397,9 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(result.unwrap_err().contains("not allowed in loop condition"));
|
assert!(result
|
||||||
|
.unwrap_err()
|
||||||
|
.contains("not allowed in loop condition"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -461,7 +469,9 @@ mod tests {
|
|||||||
&mut instructions,
|
&mut instructions,
|
||||||
);
|
);
|
||||||
assert!(cond_result.is_err());
|
assert!(cond_result.is_err());
|
||||||
assert!(cond_result.unwrap_err().contains("not allowed in loop condition"));
|
assert!(cond_result
|
||||||
|
.unwrap_err()
|
||||||
|
.contains("not allowed in loop condition"));
|
||||||
|
|
||||||
// But IS allowed in init context
|
// But IS allowed in init context
|
||||||
// Phase 226: Create empty LoopBodyLocalEnv
|
// Phase 226: Create empty LoopBodyLocalEnv
|
||||||
|
|||||||
@ -24,26 +24,20 @@ pub mod carrier_info; // Phase 196: Carrier metadata for loop lowering
|
|||||||
pub(crate) mod carrier_update_emitter; // Phase 179: Carrier update instruction emission
|
pub(crate) mod carrier_update_emitter; // Phase 179: Carrier update instruction emission
|
||||||
pub(crate) mod common; // Internal lowering utilities
|
pub(crate) mod common; // Internal lowering utilities
|
||||||
pub mod complex_addend_normalizer; // Phase 192: Complex addend normalization (AST preprocessing)
|
pub mod complex_addend_normalizer; // Phase 192: Complex addend normalization (AST preprocessing)
|
||||||
pub mod digitpos_condition_normalizer; // Phase 224-E: DigitPos condition normalizer (digit_pos < 0 → !is_digit_pos)
|
|
||||||
pub mod condition_env; // Phase 171-fix: Condition expression environment
|
pub mod condition_env; // Phase 171-fix: Condition expression environment
|
||||||
|
pub(crate) mod condition_lowerer; // Phase 171-fix: Core condition lowering logic
|
||||||
pub mod condition_lowering_box; // Phase 244: Unified condition lowering interface (trait-based)
|
pub mod condition_lowering_box; // Phase 244: Unified condition lowering interface (trait-based)
|
||||||
pub mod condition_pattern; // Phase 219-fix: If condition pattern detection (simple vs complex)
|
pub mod condition_pattern; // Phase 219-fix: If condition pattern detection (simple vs complex)
|
||||||
pub mod loop_body_local_env; // Phase 184: Body-local variable environment
|
|
||||||
pub mod loop_body_local_init; // Phase 186: Body-local init expression lowering
|
|
||||||
pub(crate) mod condition_lowerer; // Phase 171-fix: Core condition lowering logic
|
|
||||||
pub mod condition_to_joinir; // Phase 169: JoinIR condition lowering orchestrator (refactored)
|
pub mod condition_to_joinir; // Phase 169: JoinIR condition lowering orchestrator (refactored)
|
||||||
pub mod method_call_lowerer; // Phase 224-B: MethodCall lowering (metadata-driven)
|
|
||||||
pub(crate) mod condition_var_extractor; // Phase 171-fix: Variable extraction from condition AST
|
pub(crate) mod condition_var_extractor; // Phase 171-fix: Variable extraction from condition AST
|
||||||
pub mod continue_branch_normalizer; // Phase 33-19: Continue branch normalization for Pattern B
|
pub mod continue_branch_normalizer; // Phase 33-19: Continue branch normalization for Pattern B
|
||||||
pub mod expr_lowerer; // Phase 231: Unified expression lowering with scope management
|
pub mod digitpos_condition_normalizer; // Phase 224-E: DigitPos condition normalizer (digit_pos < 0 → !is_digit_pos)
|
||||||
pub mod loop_update_analyzer; // Phase 197: Update expression analyzer for carrier semantics
|
|
||||||
pub mod loop_update_summary; // Phase 170-C-2: Update pattern summary for shape detection
|
|
||||||
pub(crate) mod exit_args_resolver; // Internal exit argument resolution
|
pub(crate) mod exit_args_resolver; // Internal exit argument resolution
|
||||||
|
pub mod expr_lowerer; // Phase 231: Unified expression lowering with scope management
|
||||||
pub mod funcscanner_append_defs;
|
pub mod funcscanner_append_defs;
|
||||||
pub mod funcscanner_trim;
|
pub mod funcscanner_trim;
|
||||||
pub(crate) mod generic_case_a; // Phase 192: Modularized Case A lowering
|
pub(crate) mod generic_case_a; // Phase 192: Modularized Case A lowering
|
||||||
pub mod generic_type_resolver; // Phase 66: P3-C ジェネリック型推論箱
|
pub mod generic_type_resolver; // Phase 66: P3-C ジェネリック型推論箱
|
||||||
pub mod method_return_hint; // Phase 83: P3-D 既知メソッド戻り値型推論箱
|
|
||||||
pub mod if_dry_runner; // Phase 33-10.0
|
pub mod if_dry_runner; // Phase 33-10.0
|
||||||
pub(crate) mod if_lowering_router; // Phase 33-12: If-expression routing (re-exported)
|
pub(crate) mod if_lowering_router; // Phase 33-12: If-expression routing (re-exported)
|
||||||
pub mod if_merge; // Phase 33-7
|
pub mod if_merge; // Phase 33-7
|
||||||
@ -53,20 +47,26 @@ pub(crate) mod if_select; // Phase 33: Internal If/Select lowering
|
|||||||
pub mod inline_boundary; // Phase 188-Impl-3: JoinIR→Host boundary
|
pub mod inline_boundary; // Phase 188-Impl-3: JoinIR→Host boundary
|
||||||
pub mod inline_boundary_builder; // Phase 200-2: Builder pattern for JoinInlineBoundary
|
pub mod inline_boundary_builder; // Phase 200-2: Builder pattern for JoinInlineBoundary
|
||||||
pub mod join_value_space; // Phase 201: Unified JoinIR ValueId allocation
|
pub mod join_value_space; // Phase 201: Unified JoinIR ValueId allocation
|
||||||
|
pub mod loop_body_local_env; // Phase 184: Body-local variable environment
|
||||||
|
pub mod loop_body_local_init; // Phase 186: Body-local init expression lowering
|
||||||
pub(crate) mod loop_form_intake; // Internal loop form intake
|
pub(crate) mod loop_form_intake; // Internal loop form intake
|
||||||
pub mod scope_manager; // Phase 231: Unified variable scope management
|
|
||||||
pub(crate) mod loop_pattern_router; // Phase 33-12: Loop pattern routing (re-exported)
|
pub(crate) mod loop_pattern_router; // Phase 33-12: Loop pattern routing (re-exported)
|
||||||
pub(crate) mod loop_pattern_validator; // Phase 33-23: Loop structure validation
|
pub(crate) mod loop_pattern_validator; // Phase 33-23: Loop structure validation
|
||||||
pub(crate) mod loop_patterns; // Phase 188: Pattern-based loop lowering (3 patterns)
|
pub(crate) mod loop_patterns; // Phase 188: Pattern-based loop lowering (3 patterns)
|
||||||
pub mod loop_scope_shape;
|
pub mod loop_scope_shape;
|
||||||
pub mod loop_to_join;
|
pub mod loop_to_join;
|
||||||
|
pub mod loop_update_analyzer; // Phase 197: Update expression analyzer for carrier semantics
|
||||||
|
pub mod loop_update_summary; // Phase 170-C-2: Update pattern summary for shape detection
|
||||||
pub(crate) mod loop_view_builder; // Phase 33-23: Loop lowering dispatch
|
pub(crate) mod loop_view_builder; // Phase 33-23: Loop lowering dispatch
|
||||||
pub mod loop_with_break_minimal; // Phase 188-Impl-2: Pattern 2 minimal lowerer
|
pub mod loop_with_break_minimal; // Phase 188-Impl-2: Pattern 2 minimal lowerer
|
||||||
pub mod loop_with_continue_minimal; // Phase 195: Pattern 4 minimal lowerer
|
pub mod loop_with_continue_minimal;
|
||||||
// Phase 242-EX-A: loop_with_if_phi_minimal removed - replaced by loop_with_if_phi_if_sum
|
pub mod method_call_lowerer; // Phase 224-B: MethodCall lowering (metadata-driven)
|
||||||
|
pub mod method_return_hint; // Phase 83: P3-D 既知メソッド戻り値型推論箱
|
||||||
|
pub mod scope_manager; // Phase 231: Unified variable scope management // Phase 195: Pattern 4 minimal lowerer
|
||||||
|
// Phase 242-EX-A: loop_with_if_phi_minimal removed - replaced by loop_with_if_phi_if_sum
|
||||||
pub mod loop_with_if_phi_if_sum; // Phase 213: Pattern 3 AST-based if-sum lowerer (Phase 242-EX-A: supports complex conditions)
|
pub mod loop_with_if_phi_if_sum; // Phase 213: Pattern 3 AST-based if-sum lowerer (Phase 242-EX-A: supports complex conditions)
|
||||||
pub mod simple_while_minimal; // Phase 188-Impl-1: Pattern 1 minimal lowerer
|
|
||||||
pub mod min_loop;
|
pub mod min_loop;
|
||||||
|
pub mod simple_while_minimal; // Phase 188-Impl-1: Pattern 1 minimal lowerer
|
||||||
pub mod skip_ws;
|
pub mod skip_ws;
|
||||||
pub mod stage1_using_resolver;
|
pub mod stage1_using_resolver;
|
||||||
pub mod stageb_body;
|
pub mod stageb_body;
|
||||||
|
|||||||
@ -19,11 +19,11 @@
|
|||||||
//! Phase 231 starts with Pattern2-specific implementation to validate the design.
|
//! Phase 231 starts with Pattern2-specific implementation to validate the design.
|
||||||
//! Future phases will generalize to Pattern1, Pattern3, etc.
|
//! Future phases will generalize to Pattern1, Pattern3, etc.
|
||||||
|
|
||||||
use crate::mir::ValueId;
|
use super::carrier_info::CarrierInfo;
|
||||||
use super::condition_env::ConditionEnv;
|
use super::condition_env::ConditionEnv;
|
||||||
use super::loop_body_local_env::LoopBodyLocalEnv;
|
use super::loop_body_local_env::LoopBodyLocalEnv;
|
||||||
use super::carrier_info::CarrierInfo;
|
|
||||||
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
|
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
|
||||||
|
use crate::mir::ValueId;
|
||||||
|
|
||||||
/// Phase 231: Scope kind for variables
|
/// Phase 231: Scope kind for variables
|
||||||
///
|
///
|
||||||
@ -146,8 +146,7 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. Promoted LoopBodyLocal → Carrier lookup(命名規約は CarrierInfo 側に集約)
|
// 4. Promoted LoopBodyLocal → Carrier lookup(命名規約は CarrierInfo 側に集約)
|
||||||
self.carrier_info
|
self.carrier_info.resolve_promoted_join_id(name)
|
||||||
.resolve_promoted_join_id(name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scope_of(&self, name: &str) -> Option<VarScopeKind> {
|
fn scope_of(&self, name: &str) -> Option<VarScopeKind> {
|
||||||
@ -182,7 +181,7 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierVar, CarrierRole, CarrierInit};
|
use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole, CarrierVar};
|
||||||
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedVar;
|
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedVar;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -218,15 +217,13 @@ mod tests {
|
|||||||
let carrier_info = CarrierInfo {
|
let carrier_info = CarrierInfo {
|
||||||
loop_var_name: "i".to_string(),
|
loop_var_name: "i".to_string(),
|
||||||
loop_var_id: ValueId(1),
|
loop_var_id: ValueId(1),
|
||||||
carriers: vec![
|
carriers: vec![CarrierVar {
|
||||||
CarrierVar {
|
name: "sum".to_string(),
|
||||||
name: "sum".to_string(),
|
host_id: ValueId(2),
|
||||||
host_id: ValueId(2),
|
join_id: Some(ValueId(101)),
|
||||||
join_id: Some(ValueId(101)),
|
role: CarrierRole::LoopState,
|
||||||
role: CarrierRole::LoopState,
|
init: CarrierInit::FromHost,
|
||||||
init: CarrierInit::FromHost,
|
}],
|
||||||
},
|
|
||||||
],
|
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec![],
|
promoted_loopbodylocals: vec![],
|
||||||
};
|
};
|
||||||
@ -250,15 +247,13 @@ mod tests {
|
|||||||
let carrier_info = CarrierInfo {
|
let carrier_info = CarrierInfo {
|
||||||
loop_var_name: "i".to_string(),
|
loop_var_name: "i".to_string(),
|
||||||
loop_var_id: ValueId(1),
|
loop_var_id: ValueId(1),
|
||||||
carriers: vec![
|
carriers: vec![CarrierVar {
|
||||||
CarrierVar {
|
name: "is_digit_pos".to_string(),
|
||||||
name: "is_digit_pos".to_string(),
|
host_id: ValueId(2),
|
||||||
host_id: ValueId(2),
|
join_id: Some(ValueId(102)),
|
||||||
join_id: Some(ValueId(102)),
|
role: CarrierRole::ConditionOnly,
|
||||||
role: CarrierRole::ConditionOnly,
|
init: CarrierInit::BoolConst(false),
|
||||||
init: CarrierInit::BoolConst(false),
|
}],
|
||||||
},
|
|
||||||
],
|
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec!["digit_pos".to_string()],
|
promoted_loopbodylocals: vec!["digit_pos".to_string()],
|
||||||
};
|
};
|
||||||
@ -307,7 +302,8 @@ mod tests {
|
|||||||
condition_env.insert("i".to_string(), ValueId(100));
|
condition_env.insert("i".to_string(), ValueId(100));
|
||||||
condition_env.insert("len".to_string(), ValueId(201));
|
condition_env.insert("len".to_string(), ValueId(201));
|
||||||
|
|
||||||
let mut captured_env = crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv::new();
|
let mut captured_env =
|
||||||
|
crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv::new();
|
||||||
captured_env.add_var(CapturedVar {
|
captured_env.add_var(CapturedVar {
|
||||||
name: "len".to_string(),
|
name: "len".to_string(),
|
||||||
host_id: ValueId(42),
|
host_id: ValueId(42),
|
||||||
|
|||||||
@ -45,11 +45,10 @@
|
|||||||
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_scope_shape::LoopScopeShape;
|
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||||
use crate::mir::join_ir::{
|
use crate::mir::join_ir::{
|
||||||
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
|
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, MirLikeInst,
|
||||||
MirLikeInst, UnaryOp,
|
UnaryOp,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Lower Pattern 1 (Simple While Loop) to JoinIR
|
/// Lower Pattern 1 (Simple While Loop) to JoinIR
|
||||||
///
|
///
|
||||||
/// # Phase 188-Impl-3: Pure JoinIR Fragment Generation
|
/// # Phase 188-Impl-3: Pure JoinIR Fragment Generation
|
||||||
@ -106,17 +105,17 @@ pub(crate) fn lower_simple_while_minimal(
|
|||||||
// ValueId allocation (Phase 188-Impl-3: Sequential local IDs)
|
// ValueId allocation (Phase 188-Impl-3: Sequential local IDs)
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
// main() locals
|
// main() locals
|
||||||
let i_init = alloc_value(); // ValueId(0) - loop init value
|
let i_init = alloc_value(); // ValueId(0) - loop init value
|
||||||
let loop_result = alloc_value(); // ValueId(1) - result from loop_step
|
let loop_result = alloc_value(); // ValueId(1) - result from loop_step
|
||||||
let const_0_main = alloc_value(); // ValueId(2) - return value
|
let const_0_main = alloc_value(); // ValueId(2) - return value
|
||||||
|
|
||||||
// loop_step locals
|
// loop_step locals
|
||||||
let i_param = alloc_value(); // ValueId(3) - parameter
|
let i_param = alloc_value(); // ValueId(3) - parameter
|
||||||
let const_3 = alloc_value(); // ValueId(4) - comparison constant
|
let const_3 = alloc_value(); // ValueId(4) - comparison constant
|
||||||
let cmp_lt = alloc_value(); // ValueId(5) - i < 3
|
let cmp_lt = alloc_value(); // ValueId(5) - i < 3
|
||||||
let exit_cond = alloc_value(); // ValueId(6) - !(i < 3)
|
let exit_cond = alloc_value(); // ValueId(6) - !(i < 3)
|
||||||
let const_1 = alloc_value(); // ValueId(7) - increment constant
|
let const_1 = alloc_value(); // ValueId(7) - increment constant
|
||||||
let i_next = alloc_value(); // ValueId(8) - i + 1
|
let i_next = alloc_value(); // ValueId(8) - i + 1
|
||||||
|
|
||||||
// k_exit locals
|
// k_exit locals
|
||||||
let const_0_exit = alloc_value(); // ValueId(9) - exit return value
|
let const_0_exit = alloc_value(); // ValueId(9) - exit return value
|
||||||
@ -151,11 +150,8 @@ pub(crate) fn lower_simple_while_minimal(
|
|||||||
// ==================================================================
|
// ==================================================================
|
||||||
// loop_step(i) function
|
// loop_step(i) function
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
let mut loop_step_func = JoinFunction::new(
|
let mut loop_step_func =
|
||||||
loop_step_id,
|
JoinFunction::new(loop_step_id, "loop_step".to_string(), vec![i_param]);
|
||||||
"loop_step".to_string(),
|
|
||||||
vec![i_param],
|
|
||||||
);
|
|
||||||
|
|
||||||
// exit_cond = !(i < 3)
|
// exit_cond = !(i < 3)
|
||||||
// Step 1: const 3
|
// Step 1: const 3
|
||||||
@ -196,9 +192,7 @@ pub(crate) fn lower_simple_while_minimal(
|
|||||||
// Phase 188-Impl-1-E: Use Print instruction
|
// Phase 188-Impl-1-E: Use Print instruction
|
||||||
loop_step_func
|
loop_step_func
|
||||||
.body
|
.body
|
||||||
.push(JoinInst::Compute(MirLikeInst::Print {
|
.push(JoinInst::Compute(MirLikeInst::Print { value: i_param }));
|
||||||
value: i_param,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// i_next = i + 1
|
// i_next = i + 1
|
||||||
// Step 1: const 1
|
// Step 1: const 1
|
||||||
@ -223,7 +217,7 @@ pub(crate) fn lower_simple_while_minimal(
|
|||||||
loop_step_func.body.push(JoinInst::Call {
|
loop_step_func.body.push(JoinInst::Call {
|
||||||
func: loop_step_id,
|
func: loop_step_id,
|
||||||
args: vec![i_next],
|
args: vec![i_next],
|
||||||
k_next: None, // CRITICAL: None for tail call
|
k_next: None, // CRITICAL: None for tail call
|
||||||
dst: None,
|
dst: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -167,7 +167,7 @@ impl<'a> UpdateEnv<'a> {
|
|||||||
// Phase 247-EX: Naming convention - "digit_pos" → "digit_value" (not "digit_pos_value")
|
// Phase 247-EX: Naming convention - "digit_pos" → "digit_value" (not "digit_pos_value")
|
||||||
// Extract base name: "digit_pos" → "digit", "pos" → "pos"
|
// Extract base name: "digit_pos" → "digit", "pos" → "pos"
|
||||||
let base_name = if promoted_name.ends_with("_pos") {
|
let base_name = if promoted_name.ends_with("_pos") {
|
||||||
&promoted_name[..promoted_name.len() - 4] // Remove "_pos" suffix
|
&promoted_name[..promoted_name.len() - 4] // Remove "_pos" suffix
|
||||||
} else {
|
} else {
|
||||||
promoted_name.as_str()
|
promoted_name.as_str()
|
||||||
};
|
};
|
||||||
@ -359,8 +359,8 @@ mod tests {
|
|||||||
let mut cond_env = ConditionEnv::new();
|
let mut cond_env = ConditionEnv::new();
|
||||||
|
|
||||||
// Register both carriers in ConditionEnv
|
// Register both carriers in ConditionEnv
|
||||||
cond_env.insert("is_digit_pos".to_string(), ValueId(100)); // Boolean carrier
|
cond_env.insert("is_digit_pos".to_string(), ValueId(100)); // Boolean carrier
|
||||||
cond_env.insert("digit_value".to_string(), ValueId(200)); // Integer carrier (digit_pos → digit)
|
cond_env.insert("digit_value".to_string(), ValueId(200)); // Integer carrier (digit_pos → digit)
|
||||||
|
|
||||||
let body_env = LoopBodyLocalEnv::new();
|
let body_env = LoopBodyLocalEnv::new();
|
||||||
let promoted: Vec<String> = vec!["digit_pos".to_string()];
|
let promoted: Vec<String> = vec!["digit_pos".to_string()];
|
||||||
|
|||||||
@ -35,6 +35,9 @@ pub mod verify;
|
|||||||
// Phase 30.x: JSON serialization (jsonir v0)
|
// Phase 30.x: JSON serialization (jsonir v0)
|
||||||
pub mod json;
|
pub mod json;
|
||||||
|
|
||||||
|
// Phase 26-H.B: Normalized JoinIR (テスト専用ミニ)
|
||||||
|
pub mod normalized;
|
||||||
|
|
||||||
// Phase 34-1: Frontend (AST→JoinIR) — skeleton only
|
// Phase 34-1: Frontend (AST→JoinIR) — skeleton only
|
||||||
pub mod frontend;
|
pub mod frontend;
|
||||||
|
|
||||||
@ -44,6 +47,12 @@ pub use lowering::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Re-export verification functions
|
// Re-export verification functions
|
||||||
|
pub use normalized::{
|
||||||
|
normalize_pattern1_minimal, normalize_pattern2_minimal, normalized_pattern1_to_structured,
|
||||||
|
normalized_pattern2_to_structured, NormalizedModule,
|
||||||
|
};
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
pub use normalized::fixtures;
|
||||||
pub use verify::verify_progress_for_skip_ws;
|
pub use verify::verify_progress_for_skip_ws;
|
||||||
|
|
||||||
// Phase 200-3: Contract verification functions are in merge/mod.rs (private module access)
|
// Phase 200-3: Contract verification functions are in merge/mod.rs (private module access)
|
||||||
@ -194,6 +203,15 @@ impl LoopExitShape {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// JoinIR フェーズメタデータ。
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum JoinIrPhase {
|
||||||
|
/// Lowering 直後の構造化 JoinIR(Pattern1–5 / CarrierInfo / Boundary/ExitLine)
|
||||||
|
Structured,
|
||||||
|
/// 将来導入予定の正規化済み JoinIR(関数+継続+Env、TailCall-only)
|
||||||
|
Normalized,
|
||||||
|
}
|
||||||
|
|
||||||
/// JoinIR 関数
|
/// JoinIR 関数
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct JoinFunction {
|
pub struct JoinFunction {
|
||||||
@ -471,9 +489,7 @@ pub enum MirLikeInst {
|
|||||||
/// Phase 188: Print 文(コンソール出力)
|
/// Phase 188: Print 文(コンソール出力)
|
||||||
/// print(value) の構造を JoinIR で表現
|
/// print(value) の構造を JoinIR で表現
|
||||||
/// MIR 変換時に Print 命令に変換
|
/// MIR 変換時に Print 命令に変換
|
||||||
Print {
|
Print { value: VarId },
|
||||||
value: VarId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Phase 188-Impl-3: 条件付き値選択(三項演算子)
|
/// Phase 188-Impl-3: 条件付き値選択(三項演算子)
|
||||||
/// cond が true なら then_val を、false なら else_val を dst に代入
|
/// cond が true なら then_val を、false なら else_val を dst に代入
|
||||||
@ -535,6 +551,9 @@ pub struct JoinModule {
|
|||||||
|
|
||||||
/// エントリーポイント関数ID
|
/// エントリーポイント関数ID
|
||||||
pub entry: Option<JoinFuncId>,
|
pub entry: Option<JoinFuncId>,
|
||||||
|
|
||||||
|
/// JoinIR のフェーズ(構造化 / 正規化)
|
||||||
|
pub phase: JoinIrPhase,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JoinModule {
|
impl JoinModule {
|
||||||
@ -542,12 +561,25 @@ impl JoinModule {
|
|||||||
Self {
|
Self {
|
||||||
functions: BTreeMap::new(),
|
functions: BTreeMap::new(),
|
||||||
entry: None,
|
entry: None,
|
||||||
|
phase: JoinIrPhase::Structured,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_function(&mut self, func: JoinFunction) {
|
pub fn add_function(&mut self, func: JoinFunction) {
|
||||||
self.functions.insert(func.id, func);
|
self.functions.insert(func.id, func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_structured(&self) -> bool {
|
||||||
|
self.phase == JoinIrPhase::Structured
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_normalized(&self) -> bool {
|
||||||
|
self.phase == JoinIrPhase::Normalized
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_normalized(&mut self) {
|
||||||
|
self.phase = JoinIrPhase::Normalized;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for JoinModule {
|
impl Default for JoinModule {
|
||||||
@ -584,6 +616,16 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(module.functions.len(), 1);
|
assert_eq!(module.functions.len(), 1);
|
||||||
assert!(module.functions.contains_key(&JoinFuncId::new(0)));
|
assert!(module.functions.contains_key(&JoinFuncId::new(0)));
|
||||||
|
assert_eq!(module.phase, JoinIrPhase::Structured);
|
||||||
|
assert!(module.is_structured());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mark_normalized() {
|
||||||
|
let mut module = JoinModule::new();
|
||||||
|
assert!(module.is_structured());
|
||||||
|
module.mark_normalized();
|
||||||
|
assert!(module.is_normalized());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user