feat(joinir): Phase 213-2 Step 2-2 & 2-3 Data structure extensions
Extended PatternPipelineContext and CarrierUpdateInfo for Pattern 3 AST-based generalization. Changes: 1. PatternPipelineContext: - Added loop_condition: Option<ASTNode> - Added loop_body: Option<Vec<ASTNode>> - Added loop_update_summary: Option<LoopUpdateSummary> - Updated build_pattern_context() for Pattern 3 2. CarrierUpdateInfo: - Added then_expr: Option<ASTNode> - Added else_expr: Option<ASTNode> - Updated analyze_loop_updates() with None defaults Status: Phase 213-2 Steps 2-2 & 2-3 complete Next: Create Pattern3IfAnalyzer to extract if statement and populate update summary
This commit is contained in:
@ -97,6 +97,8 @@ NYASH_ENABLE_USING=1 # using 文有効化
|
||||
|
||||
## 現在の JoinIR 統合状況
|
||||
|
||||
> Note (2025-12): 現在は LoopBuilder を物理削除し、JoinIR は常時 ON(NYASH_JOINIR_CORE は deprecated/no-op)。以下のコードスケッチは Phase 121 当時の歴史メモとして残しているよ。
|
||||
|
||||
### Loop PHI 生成(部分統合済み)
|
||||
|
||||
**Phase 49 Mainline Integration**:
|
||||
@ -380,4 +382,3 @@ Detects unreachable basic blocks using MIR CFG information. Complements HC019 by
|
||||
- **CFG Extractor:** `src/mir/cfg_extractor.rs`
|
||||
- **Tests:** `apps/tests/hako_check/test_dead_blocks_*.hako`
|
||||
|
||||
|
||||
|
||||
@ -62,9 +62,13 @@ JoinIR ラインで守るべきルールを先に書いておくよ:
|
||||
- ExitLineReconnector は Condition 役の変数を exit_bindings から除外
|
||||
- **ParamRole の分類**:
|
||||
- `LoopParam`: ループ制御変数(例: `i` in `loop(i < len)`)→ header PHI + exit_bindings
|
||||
- `Condition`: 条件専用変数(例: `digits` in `digits.indexOf(ch)`)→ condition_bindings のみ
|
||||
- `Carrier`: 状態更新変数(例: `sum`, `count`)→ header PHI + exit_bindings
|
||||
- `ExprResult`: ループ戻り値 → exit_phi_builder で処理
|
||||
- `Condition`: 条件専用変数(例: `digits` in `digits.indexOf(ch)`)→ condition_bindings のみ
|
||||
- `Carrier`: 状態更新変数(例: `sum`, `count`)→ header PHI + exit_bindings
|
||||
- `ExprResult`: ループ戻り値 → exit_phi_builder で処理
|
||||
|
||||
10. **JoinIR Core は常時 ON**
|
||||
- LoopBuilder は物理削除済み。JoinIR を OFF にする経路やフォールバックは存在しない。
|
||||
- `NYASH_JOINIR_CORE` は deprecated(0 を指定しても警告して無視)。JoinIR の OFF トグルは提供しない。
|
||||
|
||||
---
|
||||
|
||||
@ -132,7 +136,44 @@ Local Region (1000+):
|
||||
- `JoinValueSpace`: **lowering 内部の分離**(param vs local vs PHI)
|
||||
- 両者は相補的な役割
|
||||
|
||||
詳細は `src/mir/join_ir/lowering/join_value_space.rs` と `phase201-join-value-space-design.md` を参照。
|
||||
### 1.9.5 Phase 205: 領域契約の検証強化
|
||||
|
||||
**追加された Box-First 機能**:
|
||||
|
||||
1. **衝突検出(debug-only)**
|
||||
- 全ての割り当てられた ValueId を追跡(`allocated_ids: HashSet<u32>`)
|
||||
- 重複割り当てを即座に検出し panic(Fail-Fast 原則)
|
||||
- `check_collision()` で実装
|
||||
|
||||
2. **領域検証(debug-only)**
|
||||
- `verify_region(id, expected_region)` で ValueId が期待される領域にいるか検証
|
||||
- 違反時は明確なエラーメッセージと修正ヒントを提供
|
||||
- 例: "ValueId(500) is in Param region, expected Local. Hint: Use alloc_local() for JoinIR values"
|
||||
|
||||
3. **RegionVerifier Box**
|
||||
- 場所: `src/mir/builder/control_flow/joinir/merge/mod.rs::verify_valueid_regions()`
|
||||
- 責務: merge 時に boundary と loop_info の ValueId 領域契約を検証
|
||||
- 検証項目:
|
||||
- 全ての `boundary.join_inputs` が Param 領域(100-999)にいる
|
||||
- 全ての `condition_bindings[].join_value` が Param 領域にいる
|
||||
- 全ての `carrier_phis[].phi_dst` が有効範囲(<= LOCAL_MAX)内
|
||||
|
||||
4. **明示的な領域定数**
|
||||
```rust
|
||||
pub const PHI_RESERVED_MIN: u32 = 0;
|
||||
pub const PHI_RESERVED_MAX: u32 = 99;
|
||||
pub const PARAM_MIN: u32 = 100;
|
||||
pub const PARAM_MAX: u32 = 999;
|
||||
pub const LOCAL_MIN: u32 = 1000;
|
||||
pub const LOCAL_MAX: u32 = 100000;
|
||||
```
|
||||
|
||||
**Fail-Fast 原則の実装**:
|
||||
- 領域違反は即座に panic(デバッグモード)
|
||||
- フォールバックやサイレント修正は一切行わない
|
||||
- エラーメッセージに具体的な修正方法を含める
|
||||
|
||||
詳細は `src/mir/join_ir/lowering/join_value_space.rs` と `phase205-valueid-regions-design.md` を参照。
|
||||
|
||||
---
|
||||
|
||||
@ -534,12 +575,43 @@ Pattern2/4 への統合(実際に Body-local 更新を使うループを JoinI
|
||||
|
||||
方針:
|
||||
|
||||
- **ループの「形」は P1–P4 から増やさない**。
|
||||
- **ループの「形」は P1–P4 から増やさない**。
|
||||
複雑さ(LoopBodyLocal 条件、OR chain、continue 多用など)は BoolExprLowerer /
|
||||
ContinueBranchNormalizer / TrimLoopLowerer (P5) といった補助箱側で吸収する。
|
||||
- JsonParser 側の P5 適用(Trim / `_skip_whitespace` / `_parse_string` 最小版)は実証済み。
|
||||
- JsonParser 側の P5 適用(Trim / `_skip_whitespace` / `_parse_string` 最小版)は実証済み。
|
||||
残りのループは Phase 17x–18x で、P1–P4+P5 の組み合わせとして段階的に実装していく。
|
||||
|
||||
### 4.3 JsonParser 実戦カバレッジ(Phase 210 時点)
|
||||
|
||||
Phase 210 で「軽量ループ 3 本」を実戦投入し、JoinIR インフラが **本番級に動作する** ことを確認したよ:
|
||||
|
||||
- **実戦確認済みループ**(7/13 loops ≒ 54%):
|
||||
- ✅ `_skip_whitespace` (P2 + P5 Trim, Phase 173)
|
||||
- ✅ `_trim` leading/trailing (P2 + P5 Trim, Phase 171/172)
|
||||
- ✅ `_match_literal` 最小版 (P1 Simple, Phase 210)
|
||||
- ✅ `_atoi` 最小版 (P2 Break, NumberAccumulation, Phase 210)
|
||||
- ✅ `_parse_number` 最小版 (P2 Break, Multi-carrier, Phase 210)
|
||||
|
||||
- **Phase 210 の成果**:
|
||||
- 3 本すべて JoinIR → MIR → Runtime 完全成功(RC 正常)
|
||||
- Pattern1 & Pattern2 自動ルーティング正常動作
|
||||
- NumberAccumulation (Mul+Add 2命令), Multi-carrier, PHI Contract, ValueId Regions すべて正常
|
||||
- **制約発見ゼロ** - Phase 190/201/204/205 の統合が完璧に機能
|
||||
|
||||
- **Phase 211/212 の発見** (2025-12-09):
|
||||
- Phase 211: if-sum パターン(ループ内 if 条件付き更新)の設計完了
|
||||
- Phase 212: ⚠️ **AST→MIR 層の制約発見** - ループ内 if/else が MIR に変換されない問題を検出
|
||||
- JoinIR Pattern3 (IfPHI) は動作可能だが、その前段階(AST→MIR)で if が消失
|
||||
- Phase 212.5 で AST→MIR ループ内 if 修正が必要と判明
|
||||
|
||||
- **残りループ** (Phase 211+ で段階的対応予定):
|
||||
- `_parse_array`, `_parse_object` (MethodCall 複数)
|
||||
- `_unescape_string` (複雑なキャリア処理)
|
||||
- その他 6 ループ(Phase 195/200+ 系設計で順次対応)
|
||||
|
||||
**結論**: JoinIR インフラ(P1-P5/JoinValueSpace/PHI契約)は **実戦投入可能な成熟度** に到達 ✨
|
||||
**Phase 212 制約**: AST→MIR 層のループ内 if 変換修正が次の課題
|
||||
|
||||
---
|
||||
|
||||
## 5. selfhost / .hako JoinIR Frontend との関係
|
||||
|
||||
557
docs/development/current/main/phase205-valueid-regions-design.md
Normal file
557
docs/development/current/main/phase205-valueid-regions-design.md
Normal file
@ -0,0 +1,557 @@
|
||||
# Phase 205: ValueId Region Boundaries - Design Document
|
||||
|
||||
**Author**: Claude Sonnet 4.5
|
||||
**Date**: 2025-12-09
|
||||
**Status**: In Progress
|
||||
|
||||
## Overview
|
||||
|
||||
Phase 205 establishes strict ValueId region contracts for JoinIR lowering, completing the Box-First architecture started in Phase 201. This phase ensures that ValueId allocation is:
|
||||
|
||||
1. **Predictable**: Each ValueId belongs to a clearly defined region
|
||||
2. **Verifiable**: Region violations are detected in debug mode
|
||||
3. **Maintainable**: All allocation goes through JoinValueSpace Box
|
||||
|
||||
## ValueId Region Architecture
|
||||
|
||||
### Region Layout
|
||||
|
||||
```text
|
||||
0 100 1000 u32::MAX
|
||||
├──────────┼──────────┼──────────────────────────┤
|
||||
│ PHI │ Param │ Local │
|
||||
│ Reserved│ Region │ Region │
|
||||
└──────────┴──────────┴──────────────────────────┘
|
||||
```
|
||||
|
||||
### Region Definitions
|
||||
|
||||
| Region | Range | Purpose | Examples |
|
||||
|--------|-------|---------|----------|
|
||||
| **PHI Reserved** | 0-99 | LoopHeader PHI destinations | `phi_dst: ValueId(0)` |
|
||||
| **Param Region** | 100-999 | Loop arguments & environment | `Condition.bool_id`, `Carrier.join_id`, `CapturedEnv` |
|
||||
| **Local Region** | 1000+ | JoinIR-internal values | Const, BinOp, Load, etc. |
|
||||
|
||||
### Constants (Phase 205)
|
||||
|
||||
```rust
|
||||
// Explicit region boundaries
|
||||
pub const PHI_RESERVED_MIN: u32 = 0;
|
||||
pub const PHI_RESERVED_MAX: u32 = 99;
|
||||
pub const PARAM_MIN: u32 = 100;
|
||||
pub const PARAM_MAX: u32 = 999;
|
||||
pub const LOCAL_MIN: u32 = 1000;
|
||||
pub const LOCAL_MAX: u32 = 100000;
|
||||
```
|
||||
|
||||
## Box-First Design
|
||||
|
||||
### ValueIdAllocator Box (JoinValueSpace)
|
||||
|
||||
**Responsibility**: Single Source of Truth for ValueId allocation
|
||||
|
||||
**API**:
|
||||
```rust
|
||||
impl JoinValueSpace {
|
||||
// Primary allocation methods
|
||||
pub fn alloc_param(&mut self) -> ValueId; // Returns 100+
|
||||
pub fn alloc_local(&mut self) -> ValueId; // Returns 1000+
|
||||
pub fn reserve_phi(&mut self, id: ValueId); // Marks PHI dst
|
||||
|
||||
// Phase 205: Enhanced verification
|
||||
pub fn verify_region(&self, id: ValueId, expected: Region) -> Result<(), String>;
|
||||
pub fn check_collision(&self, id: ValueId, role: &str); // debug-only
|
||||
}
|
||||
```
|
||||
|
||||
**Invariants**:
|
||||
1. `alloc_param()` never returns id >= 1000
|
||||
2. `alloc_local()` never returns id < 1000
|
||||
3. No ValueId is allocated twice
|
||||
4. PHI dst always in range 0-99
|
||||
|
||||
### RegionVerifier Box
|
||||
|
||||
**Responsibility**: Verify region contracts at merge boundaries
|
||||
|
||||
**Location**: `src/mir/builder/control_flow/joinir/merge/mod.rs`
|
||||
|
||||
**API**:
|
||||
```rust
|
||||
#[cfg(debug_assertions)]
|
||||
fn verify_valueid_regions(
|
||||
boundary: &JoinInlineBoundary,
|
||||
loop_info: &LoopHeaderPhiInfo,
|
||||
join_value_space: &JoinValueSpace,
|
||||
);
|
||||
```
|
||||
|
||||
**Checks**:
|
||||
1. All `boundary.join_inputs` are in Param region
|
||||
2. All `carrier_phis[].phi_dst` are in valid range (<= LOCAL_MAX)
|
||||
3. No overlap between Param and Local regions
|
||||
4. PHI reservations are in PHI Reserved region
|
||||
|
||||
## ValueId Role Mapping
|
||||
|
||||
### Param Region (100-999)
|
||||
|
||||
| Role | Allocated By | Example |
|
||||
|------|-------------|---------|
|
||||
| **Condition.bool_id** | `condition_env_builder.rs` | `ValueId(100)` |
|
||||
| **Carrier.join_id** | Pattern frontend (P1/P2/P3/P4) | `ValueId(101)`, `ValueId(102)` |
|
||||
| **CapturedEnv vars** | Pattern frontend | `ValueId(103+)` |
|
||||
| **Boundary inputs** | `common_init.rs` | `ValueId(104+)` |
|
||||
|
||||
### Local Region (1000+)
|
||||
|
||||
| Role | Allocated By | Example |
|
||||
|------|-------------|---------|
|
||||
| **Const values** | Lowerers (pattern1-4, trim) | `ValueId(1000)` |
|
||||
| **BinOp results** | Lowerers | `ValueId(1001)` |
|
||||
| **Load results** | Lowerers | `ValueId(1002)` |
|
||||
| **Intermediate values** | Lowerers | `ValueId(1003+)` |
|
||||
|
||||
### PHI Reserved (0-99)
|
||||
|
||||
| Role | Allocated By | Example |
|
||||
|------|-------------|---------|
|
||||
| **PHI dst** | MirBuilder (host side) | `ValueId(0)`, `ValueId(1)` |
|
||||
|
||||
**Note**: PHI dst comes from host MirBuilder, NOT JoinValueSpace. `reserve_phi()` is for verification only.
|
||||
|
||||
## Current State Inventory (Task 205-2)
|
||||
|
||||
### Pattern 1 (Minimal)
|
||||
|
||||
**File**: `src/mir/builder/control_flow/joinir/patterns/pattern1_minimal.rs`
|
||||
|
||||
**Status**: ✅ Fully integrated with JoinValueSpace
|
||||
|
||||
**Allocation Sites**:
|
||||
- ConditionEnv: Uses `alloc_param()` via `condition_env_builder.rs`
|
||||
- Carrier (i): Uses `alloc_param()` in frontend
|
||||
- Lowerer: Uses `alloc_local()` for all JoinIR values
|
||||
|
||||
**Raw ValueId Usage**: None detected
|
||||
|
||||
### Pattern 2 (With Break)
|
||||
|
||||
**File**: `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs`
|
||||
|
||||
**Status**: ✅ Fully integrated with JoinValueSpace
|
||||
|
||||
**Allocation Sites**:
|
||||
- ConditionEnv: Uses `alloc_param()` via `condition_env_builder.rs`
|
||||
- Carrier (v): Uses `alloc_param()` in frontend
|
||||
- Lowerer: Uses `alloc_local()` for all JoinIR values
|
||||
|
||||
**Raw ValueId Usage**: None detected
|
||||
|
||||
**Historical Note**: Pattern 2 was the original motivation for Phase 201 - previously had collision between `alloc_join_value()` (param) and `alloc_value()` (local starting from 0).
|
||||
|
||||
### Pattern 3 (With If-PHI)
|
||||
|
||||
**File**: `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs`
|
||||
|
||||
**Status**: ⚠️ Needs verification
|
||||
|
||||
**Allocation Sites**:
|
||||
- ConditionEnv: Uses `alloc_param()` via `condition_env_builder.rs`
|
||||
- Carriers (sum, count): Uses `alloc_param()` in frontend
|
||||
- Lowerer: Uses `alloc_local()` for all JoinIR values
|
||||
|
||||
**Potential Issues**:
|
||||
- If-PHI lowering: Need to verify all temporary values use `alloc_local()`
|
||||
- ExitLine reconnection: Verify no raw `ValueId(..)` usage
|
||||
|
||||
**Action Required**: Task 205-5 will audit
|
||||
|
||||
### Pattern 4 (With Continue)
|
||||
|
||||
**File**: `src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs`
|
||||
|
||||
**Status**: ⚠️ Needs verification
|
||||
|
||||
**Allocation Sites**:
|
||||
- ConditionEnv: Uses `alloc_param()` via `condition_env_builder.rs`
|
||||
- Carriers: Uses `alloc_param()` in frontend
|
||||
- Lowerer: Uses `alloc_local()` for all JoinIR values
|
||||
|
||||
**Potential Issues**:
|
||||
- Continue-pattern has more complex control flow
|
||||
- UpdateSummary handling: Verify all intermediate values use `alloc_local()`
|
||||
|
||||
**Action Required**: Task 205-5 will audit
|
||||
|
||||
### Trim Pattern Lowerer
|
||||
|
||||
**File**: `src/mir/builder/control_flow/joinir/patterns/trim_pattern_lowerer.rs`
|
||||
|
||||
**Status**: ⚠️ Needs verification
|
||||
|
||||
**Allocation Sites**:
|
||||
- Uses `alloc_fn: &mut dyn FnMut() -> ValueId` pattern
|
||||
- Should receive `space.local_allocator()` closure
|
||||
|
||||
**Potential Issues**:
|
||||
- Multiple lowerer sites (JsonParser, other Trim use cases)
|
||||
- Need to ensure all call sites pass `space.local_allocator()`
|
||||
|
||||
**Action Required**: Task 205-5 will audit
|
||||
|
||||
### ConditionEnv Builder
|
||||
|
||||
**File**: `src/mir/builder/control_flow/joinir/patterns/condition_env_builder.rs`
|
||||
|
||||
**Status**: ✅ Already uses `alloc_param()`
|
||||
|
||||
**Implementation**:
|
||||
```rust
|
||||
pub fn build_condition_env(
|
||||
condition_ast: &AstNode,
|
||||
join_value_space: &mut JoinValueSpace,
|
||||
// ...
|
||||
) -> Result<ConditionEnv, String> {
|
||||
let bool_id = join_value_space.alloc_param(); // ✅ Correct
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Exit Binding & Common Init
|
||||
|
||||
**Files**:
|
||||
- `src/mir/builder/control_flow/joinir/patterns/exit_binding.rs`
|
||||
- `src/mir/builder/control_flow/joinir/patterns/common_init.rs`
|
||||
|
||||
**Status**: ⚠️ Needs verification
|
||||
|
||||
**Potential Issues**:
|
||||
- Exit binding may create temporary ValueIds
|
||||
- Common init should use `alloc_param()` for boundary inputs
|
||||
|
||||
**Action Required**: Task 205-5 will audit
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Task 205-3: ValueIdAllocator Box Enhancement
|
||||
|
||||
**Changes to** `src/mir/join_ir/lowering/join_value_space.rs`:
|
||||
|
||||
```rust
|
||||
// Add explicit max constants
|
||||
pub const LOCAL_MAX: u32 = 100000;
|
||||
|
||||
// Add collision detection (debug-only)
|
||||
#[cfg(debug_assertions)]
|
||||
fn check_collision(&self, id: ValueId, role: &str) {
|
||||
if self.allocated_ids.contains(&id) {
|
||||
panic!(
|
||||
"[JoinValueSpace] ValueId collision: {:?} already allocated (role: {})",
|
||||
id, role
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Add region verification
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn verify_region(&self, id: ValueId, expected_region: Region) -> Result<(), String> {
|
||||
let actual = self.region_of(id);
|
||||
if actual != expected_region {
|
||||
return Err(format!(
|
||||
"ValueId {:?} is in {:?} region, expected {:?}",
|
||||
id, actual, expected_region
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Track allocated IDs (debug-only)
|
||||
#[cfg(debug_assertions)]
|
||||
allocated_ids: HashSet<u32>,
|
||||
|
||||
// Update alloc_param/alloc_local to track allocations
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn alloc_param(&mut self) -> ValueId {
|
||||
let id = self.next_param;
|
||||
debug_assert!(id < LOCAL_BASE, "Param region overflow");
|
||||
self.check_collision(ValueId(id), "param");
|
||||
self.allocated_ids.insert(id);
|
||||
self.next_param += 1;
|
||||
ValueId(id)
|
||||
}
|
||||
```
|
||||
|
||||
### Task 205-4: RegionVerifier Box Implementation
|
||||
|
||||
**Location**: `src/mir/builder/control_flow/joinir/merge/mod.rs`
|
||||
|
||||
**Integration Point**: Add to existing `verify_joinir_contracts()` function
|
||||
|
||||
```rust
|
||||
#[cfg(debug_assertions)]
|
||||
fn verify_joinir_contracts(
|
||||
func: &JoinIRFunction,
|
||||
boundary: &JoinInlineBoundary,
|
||||
loop_info: &LoopHeaderPhiInfo,
|
||||
join_value_space: &JoinValueSpace,
|
||||
) {
|
||||
// Existing PHI contract verification
|
||||
verify_phi_contracts(func, loop_info);
|
||||
|
||||
// Phase 205: Add region verification
|
||||
verify_valueid_regions(boundary, loop_info, join_value_space);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn verify_valueid_regions(
|
||||
boundary: &JoinInlineBoundary,
|
||||
loop_info: &LoopHeaderPhiInfo,
|
||||
join_value_space: &JoinValueSpace,
|
||||
) {
|
||||
// 1. Verify boundary inputs are in Param region
|
||||
for join_id in &boundary.join_inputs {
|
||||
let region = join_value_space.region_of(*join_id);
|
||||
if region != Region::Param {
|
||||
panic!(
|
||||
"[RegionVerifier] Boundary input {:?} is in {:?} region, expected Param",
|
||||
join_id, region
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Verify PHI dst are in valid range
|
||||
for (carrier_name, entry) in &loop_info.carrier_phis {
|
||||
let region = join_value_space.region_of(entry.phi_dst);
|
||||
// PHI dst may be in PHI Reserved or early Param range (depending on MirBuilder)
|
||||
if entry.phi_dst.0 > LOCAL_MAX {
|
||||
panic!(
|
||||
"[RegionVerifier] Carrier '{}' PHI dst {:?} exceeds LOCAL_MAX",
|
||||
carrier_name, entry.phi_dst
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Verify JoinValueSpace internal consistency
|
||||
if let Err(e) = join_value_space.verify_no_overlap() {
|
||||
panic!("[RegionVerifier] JoinValueSpace overlap detected: {}", e);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Task 205-5: Pattern Integration Audit
|
||||
|
||||
**Files to Audit**:
|
||||
1. `pattern1_minimal.rs` - ✅ Already correct
|
||||
2. `pattern2_with_break.rs` - ✅ Already correct
|
||||
3. `pattern3_with_if_phi.rs` - ⚠️ Verify If-PHI lowering
|
||||
4. `pattern4_with_continue.rs` - ⚠️ Verify UpdateSummary handling
|
||||
5. `trim_pattern_lowerer.rs` - ⚠️ Verify all call sites
|
||||
6. `exit_binding.rs` - ⚠️ Verify no raw ValueId usage
|
||||
7. `common_init.rs` - ⚠️ Verify boundary input allocation
|
||||
|
||||
**Audit Checklist**:
|
||||
- [ ] No raw `ValueId(..)` construction in lowerers
|
||||
- [ ] All Carrier `join_id` use `alloc_param()`
|
||||
- [ ] All lowerer intermediate values use `alloc_local()`
|
||||
- [ ] All `alloc_fn` closures receive `space.local_allocator()`
|
||||
|
||||
**Fix Strategy**:
|
||||
```rust
|
||||
// ❌ Before (if found):
|
||||
let temp = ValueId(next_id);
|
||||
next_id += 1;
|
||||
|
||||
// ✅ After:
|
||||
let temp = join_value_space.alloc_local();
|
||||
```
|
||||
|
||||
### Task 205-6: Testing & Documentation
|
||||
|
||||
**Test Cases**:
|
||||
1. `loop_min_while.hako` (Pattern 1)
|
||||
2. `loop_with_break.hako` (Pattern 2)
|
||||
3. `loop_if_phi.hako` (Pattern 3)
|
||||
4. `loop_continue_pattern4.hako` (Pattern 4)
|
||||
5. Trim/JsonParser representative case
|
||||
|
||||
**Expected Outcome**:
|
||||
- All 821 tests pass
|
||||
- No regression
|
||||
- Debug assertions detect region violations (if any)
|
||||
|
||||
**Documentation Updates**:
|
||||
1. `joinir-architecture-overview.md`:
|
||||
- Add "ValueId Region Contract" section
|
||||
- Update Box boundary diagram
|
||||
- Link to this design doc
|
||||
2. `CURRENT_TASK.md`:
|
||||
- Mark Phase 205 complete
|
||||
- Add handoff notes for Phase 206
|
||||
|
||||
## Fail-Fast Principles
|
||||
|
||||
### Region Violations
|
||||
|
||||
**Principle**: Detect region violations immediately, fail fast with clear error messages.
|
||||
|
||||
**Implementation**:
|
||||
```rust
|
||||
#[cfg(debug_assertions)]
|
||||
fn verify_region(&self, id: ValueId, expected: Region) -> Result<(), String> {
|
||||
let actual = self.region_of(id);
|
||||
if actual != expected {
|
||||
// ✅ Clear, actionable error message
|
||||
return Err(format!(
|
||||
"ValueId {:?} is in {:?} region, expected {:?}\n\
|
||||
Hint: Use alloc_param() for loop arguments, alloc_local() for JoinIR values",
|
||||
id, actual, expected
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
**No Fallback**: If a region violation occurs, panic immediately. Do not:
|
||||
- Silently remap ValueIds
|
||||
- Use fallback allocation
|
||||
- Continue with corrupted state
|
||||
|
||||
### Collision Detection
|
||||
|
||||
**Principle**: Each ValueId allocated exactly once.
|
||||
|
||||
**Implementation**:
|
||||
```rust
|
||||
#[cfg(debug_assertions)]
|
||||
fn check_collision(&self, id: ValueId, role: &str) {
|
||||
if self.allocated_ids.contains(&id.0) {
|
||||
panic!(
|
||||
"[JoinValueSpace] ValueId collision detected!\n\
|
||||
ID: {:?}\n\
|
||||
Role: {}\n\
|
||||
This indicates a bug in JoinIR lowering - contact maintainer",
|
||||
id, role
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Box Boundaries
|
||||
|
||||
### SSOT (Single Source of Truth)
|
||||
|
||||
**JoinValueSpace is the SSOT for JoinIR ValueId allocation.**
|
||||
|
||||
**Boundary Rules**:
|
||||
1. ✅ **Inside JoinIR lowering**: All ValueIds come from JoinValueSpace
|
||||
2. ❌ **Outside JoinIR lowering**: MirBuilder allocates PHI dst independently
|
||||
3. ⚠️ **Bridge**: `reserve_phi()` synchronizes PHI dst for verification
|
||||
|
||||
**Example**:
|
||||
```rust
|
||||
// ✅ Correct: JoinIR lowering
|
||||
let mut join_value_space = JoinValueSpace::new();
|
||||
let carrier_id = join_value_space.alloc_param(); // Inside SSOT boundary
|
||||
|
||||
// ✅ Correct: MirBuilder allocates PHI dst
|
||||
let phi_dst = mir_builder.alloc_value(); // Outside SSOT boundary
|
||||
|
||||
// ⚠️ Bridge: Sync for verification
|
||||
join_value_space.reserve_phi(phi_dst); // Tell JoinValueSpace about external PHI
|
||||
```
|
||||
|
||||
### Allocator Closures
|
||||
|
||||
**Pattern**: Pass allocation function to lowerers
|
||||
|
||||
```rust
|
||||
// ✅ Correct pattern:
|
||||
fn lower_pattern3(
|
||||
alloc_local: &mut dyn FnMut() -> ValueId, // Receives closure
|
||||
// ...
|
||||
) {
|
||||
let const_id = alloc_local(); // ✅ Uses closure
|
||||
}
|
||||
|
||||
// Call site:
|
||||
lower_pattern3(
|
||||
&mut join_value_space.local_allocator(), // ✅ Passes JoinValueSpace closure
|
||||
// ...
|
||||
);
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Lowerer doesn't need direct JoinValueSpace reference
|
||||
- Maintains Box boundary
|
||||
- Easy to test with mock allocators
|
||||
|
||||
## Success Criteria
|
||||
|
||||
Phase 205 is complete when:
|
||||
|
||||
1. ✅ Design document created (this file)
|
||||
2. ✅ JoinValueSpace has collision detection & region verification (debug-only)
|
||||
3. ✅ RegionVerifier integrated into merge verification
|
||||
4. ✅ All patterns (P1/P2/P3/P4) audited for raw ValueId usage
|
||||
5. ✅ All tests pass (821 tests, 0 regression)
|
||||
6. ✅ Documentation updated (overview + CURRENT_TASK)
|
||||
|
||||
## Future Work (Phase 206+)
|
||||
|
||||
### Potential Enhancements
|
||||
|
||||
1. **Runtime Region Tracking** (if needed):
|
||||
- Track ValueId → Role mapping for better error messages
|
||||
- Example: "ValueId(105) is carrier 'sum', expected local region"
|
||||
|
||||
2. **Region Statistics**:
|
||||
- Report param/local/PHI usage per pattern
|
||||
- Detect potential region exhaustion early
|
||||
|
||||
3. **Contract Testing**:
|
||||
- Generate test cases that deliberately violate regions
|
||||
- Verify debug assertions trigger correctly
|
||||
|
||||
4. **Allocator Modes**:
|
||||
- Dense allocation (minimize gaps)
|
||||
- Sparse allocation (easier debugging)
|
||||
- Deterministic allocation (reproducible builds)
|
||||
|
||||
## References
|
||||
|
||||
- **Phase 201**: JoinValueSpace initial implementation
|
||||
- **Phase 204**: PHI contract verification (dst overwrite, inputs sanity)
|
||||
- **Box-First Principle**: CLAUDE.md Section "箱理論(Box-First)"
|
||||
|
||||
## Appendix: Region Math
|
||||
|
||||
### Current Capacity
|
||||
|
||||
| Region | Range | Capacity | Typical Usage |
|
||||
|--------|-------|----------|---------------|
|
||||
| PHI Reserved | 0-99 | 100 IDs | 1-5 PHIs per loop |
|
||||
| Param | 100-999 | 900 IDs | 3-10 params per loop |
|
||||
| Local | 1000-99999 | 99000 IDs | 10-1000 values per loop |
|
||||
|
||||
### Overflow Scenarios
|
||||
|
||||
**Param Overflow** (highly unlikely):
|
||||
- Would require 900+ loop parameters
|
||||
- Current max observed: ~10 params (Pattern 3)
|
||||
- Debug assertion will catch at param #900
|
||||
|
||||
**Local Overflow** (theoretical):
|
||||
- Would require 99000+ JoinIR instructions
|
||||
- Current max observed: ~100 instructions (JsonParser)
|
||||
- Would indicate pathological code generation
|
||||
|
||||
**PHI Overflow** (impossible):
|
||||
- PHI dst allocated by MirBuilder, not JoinValueSpace
|
||||
- JoinValueSpace only verifies PHI dst <= 99
|
||||
- If violated, indicates bug in MirBuilder
|
||||
|
||||
## Version History
|
||||
|
||||
- **2025-12-09**: Initial design document (Claude Sonnet 4.5)
|
||||
- **Phase 205-1**: Created as part of ValueId region boundary task
|
||||
@ -0,0 +1,753 @@
|
||||
# Phase 210: JsonParser JoinIR ミニ統合(ラウンド1)
|
||||
|
||||
**日付**: 2025-12-09
|
||||
**ゴール**: 既存JoinIRインフラ(P1-P5/JoinValueSpace/PHI契約)で実戦ループ2〜3本を通して観測する
|
||||
**制約**: 新しい箱・大リファクタは禁止。問題発見時は「どの層の制約か」を記録するまで。
|
||||
|
||||
---
|
||||
|
||||
## Section 1: 対象ループの再選定(Task 210-1)
|
||||
|
||||
### 1.1 選定基準
|
||||
|
||||
**Phase 210 の狙い**:
|
||||
- 既存インフラで「理論上いけるはず」のループを実戦投入
|
||||
- 新機能実装ではなく、既存機能の統合観測
|
||||
- Fail-Fast で問題層を特定
|
||||
|
||||
**選定条件**:
|
||||
1. ✅ Phase 190 で理論実装済みパターン (NumberAccumulation)
|
||||
2. ✅ Phase 181 で棚卸し済み(ブロックなし確認済み)
|
||||
3. ✅ 単純構造(LoopBodyLocal なし or Trim パターンのみ)
|
||||
4. ✅ 既存テスト資産が使える(phase190_*, phase183_* など)
|
||||
|
||||
### 1.2 選定結果: 3本のループ
|
||||
|
||||
#### ループ1: _atoi の最小版 (P2 Break) ⭐最優先
|
||||
|
||||
**理由**:
|
||||
- Phase 190-impl-D で E2E 検証済み (`phase190_atoi_impl.hako` → 12 ✅)
|
||||
- NumberAccumulation パターン (`v = v * 10 + digit`) 完全実装済み
|
||||
- CarrierInfo, LoopUpdateAnalyzer, CarrierUpdateLowerer で全対応
|
||||
- 既に JoinIR → MIR パイプライン通過確認済み
|
||||
|
||||
**ループ構造**:
|
||||
```nyash
|
||||
local result = 0
|
||||
local i = 0
|
||||
loop(i < n) {
|
||||
local ch = s.substring(i, i+1)
|
||||
local pos = digits.indexOf(ch)
|
||||
if pos < 0 { break }
|
||||
result = result * 10 + pos // NumberAccumulation
|
||||
i = i + 1
|
||||
}
|
||||
```
|
||||
|
||||
**想定パターン**: Pattern 2 (WithBreak)
|
||||
|
||||
**既知の制約**:
|
||||
- LoopBodyLocal (`ch`, `pos`) への代入は JoinIR 未対応(Phase 186 残タスク)
|
||||
- 回避策: body-local を使わず carrier のみで書ける最小版を使用
|
||||
|
||||
**観測ポイント**:
|
||||
- `[pattern] Pattern2_WithBreak MATCHED`
|
||||
- `[joinir/pattern2] Generated JoinIR`
|
||||
- `[joinir/verify] all contracts satisfied`
|
||||
- Runtime: 正しい整数変換結果
|
||||
|
||||
---
|
||||
|
||||
#### ループ2: _parse_number の最小版 (P2 Break)
|
||||
|
||||
**理由**:
|
||||
- Phase 190-impl-D で E2E 検証済み (`phase190_parse_number_impl.hako` → 123 ✅)
|
||||
- StringAppendChar + NumberAccumulation の組み合わせ
|
||||
- Multi-carrier パターン(`p`, `num_str`)の実証
|
||||
|
||||
**ループ構造**:
|
||||
```nyash
|
||||
local num_str = ""
|
||||
local p = 0
|
||||
loop(p < s.length()) {
|
||||
local ch = s.substring(p, p+1)
|
||||
local digit_pos = digits.indexOf(ch)
|
||||
|
||||
if digit_pos < 0 { break }
|
||||
|
||||
num_str = num_str + ch // StringAppendChar
|
||||
p = p + 1
|
||||
}
|
||||
```
|
||||
|
||||
**想定パターン**: Pattern 2 (WithBreak)
|
||||
|
||||
**既知の制約**:
|
||||
- LoopBodyLocal (`ch`, `digit_pos`) への代入は JoinIR 未対応
|
||||
- 回避策: body-local を読み取り専用として扱う(書き込みなし)
|
||||
|
||||
**観測ポイント**:
|
||||
- Multi-carrier update の正しい PHI 配線
|
||||
- StringAppendChar + CounterLike の組み合わせ動作
|
||||
- Runtime: 正しい数値文字列抽出
|
||||
|
||||
---
|
||||
|
||||
#### ループ3: _match_literal の最小版 (P1 Simple)
|
||||
|
||||
**理由**:
|
||||
- Phase 181 で「P1 Simple」として分類済み
|
||||
- 最も単純なパターン(break なし、continue なし)
|
||||
- Pattern 1 の汎用性確認に最適
|
||||
|
||||
**ループ構造**:
|
||||
```nyash
|
||||
local i = 0
|
||||
loop(i < len) {
|
||||
if s.substring(pos + i, pos + i + 1) != literal.substring(i, i + 1) {
|
||||
return 0
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return 1
|
||||
```
|
||||
|
||||
**想定パターン**: Pattern 1 (SimpleWhile)
|
||||
|
||||
**既知の制約**:
|
||||
- 早期 return がある(LoopForm では break として扱われる可能性)
|
||||
- Pattern 1 vs Pattern 2 のルーティング境界を観測
|
||||
|
||||
**観測ポイント**:
|
||||
- Pattern 1 vs Pattern 2 の自動ルーティング
|
||||
- 早期 return の JoinIR 表現
|
||||
- Runtime: 文字列一致判定の正確性
|
||||
|
||||
---
|
||||
|
||||
### 1.3 除外したループ
|
||||
|
||||
**_skip_whitespace, _trim (leading/trailing)**:
|
||||
- 既に Phase 171/173 で実装・検証済み
|
||||
- Phase 210 では「新規に JoinIR ラインに乗せたいもの」を優先(指示書より)
|
||||
- 比較用として残すが、今回の観測対象からは除外
|
||||
|
||||
**_parse_array, _parse_object, _unescape_string**:
|
||||
- MethodCall 多数、複雑なキャリア処理
|
||||
- Phase 183+ の対象(Phase 210 の範囲外)
|
||||
|
||||
---
|
||||
|
||||
## Section 2: 最小 .hako ハーネスの設計(Task 210-2)
|
||||
|
||||
### 2.1 ハーネス設計方針
|
||||
|
||||
**Phase 210 の制約**:
|
||||
- 既存の Phase190/200 系テストを再利用してもよい(指示書より)
|
||||
- 新規に書く場合は `apps/tests/phase210_*` に配置
|
||||
- RC(Result Code)で結果を返すシンプル構造
|
||||
|
||||
**再利用候補**:
|
||||
1. `apps/tests/phase190_atoi_impl.hako` (既存) ✅
|
||||
2. `apps/tests/phase190_parse_number_impl.hako` (既存) ✅
|
||||
3. `apps/tests/phase210_match_literal_min.hako` (新規作成予定)
|
||||
|
||||
---
|
||||
|
||||
### 2.2 ハーネス1: phase190_atoi_impl.hako (再利用)
|
||||
|
||||
**現状**: Phase 190-impl-D で既に検証済み
|
||||
|
||||
**実行コマンド**:
|
||||
```bash
|
||||
./target/release/hakorune apps/tests/phase190_atoi_impl.hako
|
||||
```
|
||||
|
||||
**期待出力**:
|
||||
```
|
||||
12
|
||||
```
|
||||
|
||||
**観測項目**:
|
||||
- [ ] `[pattern] Pattern2_WithBreak MATCHED`
|
||||
- [ ] `[joinir/pattern2] Generated JoinIR`
|
||||
- [ ] `[joinir/verify] all contracts satisfied`
|
||||
- [ ] Runtime: RC = 12
|
||||
|
||||
---
|
||||
|
||||
### 2.3 ハーネス2: phase190_parse_number_impl.hako (再利用)
|
||||
|
||||
**現状**: Phase 190-impl-D で既に検証済み
|
||||
|
||||
**実行コマンド**:
|
||||
```bash
|
||||
./target/release/hakorune apps/tests/phase190_parse_number_impl.hako
|
||||
```
|
||||
|
||||
**期待出力**:
|
||||
```
|
||||
123
|
||||
```
|
||||
|
||||
**観測項目**:
|
||||
- [ ] `[pattern] Pattern2_WithBreak MATCHED`
|
||||
- [ ] Multi-carrier PHI 配線確認
|
||||
- [ ] StringAppendChar + CounterLike 組み合わせ動作
|
||||
- [ ] Runtime: RC = 123
|
||||
|
||||
---
|
||||
|
||||
### 2.4 ハーネス3: phase210_match_literal_min.hako (新規)
|
||||
|
||||
**設計イメージ**:
|
||||
```nyash
|
||||
static box Main {
|
||||
main() {
|
||||
local s = "hello"
|
||||
local literal = "hello"
|
||||
local pos = 0
|
||||
local len = 5
|
||||
|
||||
local i = 0
|
||||
loop(i < len) {
|
||||
if s.substring(pos + i, pos + i + 1) != literal.substring(i, i + 1) {
|
||||
return 0
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**実行コマンド**:
|
||||
```bash
|
||||
./target/release/hakorune apps/tests/phase210_match_literal_min.hako
|
||||
```
|
||||
|
||||
**期待出力**:
|
||||
```
|
||||
1
|
||||
```
|
||||
|
||||
**観測項目**:
|
||||
- [ ] Pattern 1 vs Pattern 2 ルーティング結果
|
||||
- [ ] 早期 return の JoinIR 表現
|
||||
- [ ] Runtime: RC = 1 (一致成功)
|
||||
|
||||
**実装タイミング**: Task 210-2 の「コード実装」フェーズで作成(今回は設計のみ)
|
||||
|
||||
---
|
||||
|
||||
## Section 3: 実行経路の確認(Task 210-3)
|
||||
|
||||
### 3.1 実行コマンド方針
|
||||
|
||||
**基本実行**:
|
||||
```bash
|
||||
./target/release/hakorune apps/tests/phase210_*.hako
|
||||
```
|
||||
|
||||
**構造確認モード** (必要に応じて):
|
||||
```bash
|
||||
NYASH_JOINIR_STRUCTURE_ONLY=1 ./target/release/hakorune apps/tests/phase210_*.hako
|
||||
```
|
||||
|
||||
**詳細ログ** (問題発生時):
|
||||
```bash
|
||||
NYASH_CLI_VERBOSE=1 ./target/release/hakorune apps/tests/phase210_*.hako
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.2 期待するログのイメージ
|
||||
|
||||
#### Pattern 2 (WithBreak) の場合
|
||||
|
||||
**ルーティング段階**:
|
||||
```
|
||||
[trace:routing] router: function 'Main.main' - try_cf_loop_joinir called
|
||||
[trace:pattern] route: Pattern2_WithBreak MATCHED
|
||||
```
|
||||
|
||||
**JoinIR 生成段階**:
|
||||
```
|
||||
[joinir/pattern2] Generated JoinIR for loop
|
||||
[joinir/pattern2] Carriers: result, i
|
||||
[joinir/pattern2] Update kinds: NumberAccumulation(base=10), CounterLike
|
||||
```
|
||||
|
||||
**検証段階**:
|
||||
```
|
||||
[joinir/verify] Verifying loop header PHIs
|
||||
[joinir/verify] Verifying exit line contract
|
||||
[joinir/verify] Verifying ValueId regions
|
||||
[joinir/verify] all contracts satisfied
|
||||
```
|
||||
|
||||
**MIR マージ段階**:
|
||||
```
|
||||
[joinir/merge] Merging JoinIR into host MIR
|
||||
[joinir/merge] Reconnecting exit line
|
||||
[joinir/merge] Merge complete
|
||||
```
|
||||
|
||||
#### Pattern 1 (SimpleWhile) の場合
|
||||
|
||||
**ルーティング段階**:
|
||||
```
|
||||
[trace:routing] router: function 'Main.main' - try_cf_loop_joinir called
|
||||
[trace:pattern] route: Pattern1_SimpleWhile MATCHED
|
||||
```
|
||||
|
||||
**JoinIR 生成段階**:
|
||||
```
|
||||
[joinir/pattern1] Generated JoinIR for simple loop
|
||||
[joinir/pattern1] Carriers: i
|
||||
[joinir/pattern1] No break/continue, single exit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.3 Fail-Fast 方針
|
||||
|
||||
**Phase 210 の鉄則**: 問題発見時は「記録するまで」に留める。修正は Phase 211+ で。
|
||||
|
||||
#### Fail-Fast ケース1: [joinir/freeze]
|
||||
|
||||
**想定エラー**:
|
||||
```
|
||||
[joinir/freeze] Complex carrier update detected
|
||||
carrier: result
|
||||
reason: MethodCall in addend
|
||||
```
|
||||
|
||||
**対処**:
|
||||
- 記録: 「CarrierUpdate 層でブロック」
|
||||
- 修正: Phase 211+ で MethodCall 対応
|
||||
|
||||
#### Fail-Fast ケース2: Type error
|
||||
|
||||
**想定エラー**:
|
||||
```
|
||||
[ERROR] Type mismatch: expected Integer, got String
|
||||
```
|
||||
|
||||
**対処**:
|
||||
- 記録: 「ConditionEnv 層でブロック(型推論失敗)」
|
||||
- 修正: Phase 211+ で型ヒント強化
|
||||
|
||||
#### Fail-Fast ケース3: ssa-undef-debug
|
||||
|
||||
**想定エラー**:
|
||||
```
|
||||
[ssa-undef-debug] Undefined variable: pos
|
||||
at: LoopBodyLocal assignment
|
||||
```
|
||||
|
||||
**対処**:
|
||||
- 記録: 「LoopBodyLocal 層でブロック(Phase 186 残タスク)」
|
||||
- 回避: body-local を使わない最小版に切り替え
|
||||
|
||||
---
|
||||
|
||||
## Section 4: 観測結果の記録(Task 210-4)
|
||||
|
||||
### 4.1 記録フォーマット
|
||||
|
||||
**このセクションに追記する形で観測結果を記録する**
|
||||
|
||||
#### テストファイル一覧
|
||||
|
||||
| # | ファイル | ループパターン | 実行日 | 結果 |
|
||||
|---|---------|--------------|-------|------|
|
||||
| 1 | phase190_atoi_impl.hako | P2 Break (NumberAccumulation) | 2025-12-09 | ✅ RC=12 |
|
||||
| 2 | phase190_parse_number_impl.hako | P2 Break (Multi-carrier) | 2025-12-09 | ✅ RC=123 |
|
||||
| 3 | phase210_match_literal_min.hako | P1 Simple | 2025-12-09 | ✅ RC=1 |
|
||||
|
||||
#### 観測結果テーブル
|
||||
|
||||
| ループ | Pattern | JoinIR生成 | PHI契約 | MIRマージ | Runtime | エラー層 | 備考 |
|
||||
|-------|---------|-----------|---------|----------|---------|---------|------|
|
||||
| _atoi | P2 | ✅ | ✅ | ✅ | ✅ 12 | なし | NumberAccumulation (Mul+Add) 正常動作 |
|
||||
| _parse_number | P2 | ✅ | ✅ | ✅ | ✅ 123 | なし | Multi-carrier (i, num) 正常動作 |
|
||||
| _match_literal | P1 | ✅ | ✅ | ✅ | ✅ 1 | なし | Pattern1 SimpleWhile 正常動作 |
|
||||
|
||||
**記号**:
|
||||
- ✅: 正常動作
|
||||
- ⚠️: 警告あり(動作はする)
|
||||
- ❌: エラー(Fail-Fast)
|
||||
- `-`: 未実行
|
||||
|
||||
---
|
||||
|
||||
### 4.2 エラー層の分類
|
||||
|
||||
**Phase 210 で観測する層**:
|
||||
|
||||
| 層 | 責任範囲 | 既知の制約 |
|
||||
|----|---------|----------|
|
||||
| **ConditionEnv** | 条件式の変数解決・型推論 | MethodCall in condition (Phase 171-D) |
|
||||
| **LoopBodyLocal** | body-local 変数の代入 | Assignment 未対応 (Phase 186) |
|
||||
| **CarrierUpdate** | Carrier 更新パターンの検出 | Complex addend (Phase 191+) |
|
||||
| **MethodCall** | メソッド呼び出しの lowering | body-local の MethodCall (Phase 183+) |
|
||||
| **PHI Contract** | PHI dst/inputs の検証 | Phase 204/205 で対応済み |
|
||||
| **ValueId Region** | Param/Local region 分離 | Phase 205 で対応済み |
|
||||
|
||||
---
|
||||
|
||||
### 4.3 インフラ達成度マトリクス
|
||||
|
||||
**Phase 210 時点の達成度**:
|
||||
|
||||
| 機能 | Pattern1 | Pattern2 | Pattern3 | Pattern4 | Pattern5 |
|
||||
|-----|----------|----------|----------|----------|----------|
|
||||
| **基本 loop** | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| **Break** | - | ✅ | - | - | - |
|
||||
| **Continue** | - | - | - | ✅ | - |
|
||||
| **If-PHI** | - | - | ✅ | ✅ | - |
|
||||
| **Trim (LoopBodyLocal昇格)** | - | ✅ | - | - | ✅ |
|
||||
| **NumberAccumulation** | - | ✅ | - | - | - |
|
||||
| **StringAppendChar** | - | ✅ | - | ✅ | - |
|
||||
| **Multi-carrier** | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| **PHI Contract** | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| **ValueId Region** | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
|
||||
**未対応機能** (Phase 211+ の課題):
|
||||
- [ ] LoopBodyLocal への代入 (Phase 186)
|
||||
- [ ] MethodCall in condition (Phase 171-D)
|
||||
- [ ] Complex addend in NumberAccumulation (Phase 191+)
|
||||
- [ ] MethodCall in body-local (Phase 183+)
|
||||
|
||||
---
|
||||
|
||||
### 4.4 詳細観測ログ (2025-12-09 実行結果)
|
||||
|
||||
#### ハーネス1: phase190_atoi_impl.hako ✅
|
||||
|
||||
**実行コマンド**:
|
||||
```bash
|
||||
./target/release/hakorune apps/tests/phase190_atoi_impl.hako
|
||||
```
|
||||
|
||||
**主要ログ抽出**:
|
||||
```
|
||||
[pattern2/init] PatternPipelineContext: loop_var='i', loop_var_id=ValueId(4), carriers=1
|
||||
[pattern2/phase201] Using JoinValueSpace: loop_var 'i' → Some(ValueId(100))
|
||||
[pattern2/phase201] Allocated carrier 'result' param ID: ValueId(101)
|
||||
[cf_loop/pattern2] Phase 176-3: Analyzed 1 carrier updates
|
||||
[joinir/pattern2] Phase 176-3: Carrier 'result' update: ValueId(101) -> ValueId(1013)
|
||||
[joinir_block] Compute instruction: Const { dst: ValueId(1011), value: Integer(10) }
|
||||
[joinir_block] Compute instruction: BinOp { dst: ValueId(1012), op: Mul, lhs: ValueId(102), rhs: ValueId(1011) }
|
||||
[joinir_block] Compute instruction: BinOp { dst: ValueId(1013), op: Add, lhs: ValueId(1012), rhs: ValueId(100) }
|
||||
```
|
||||
|
||||
**観測ポイント**:
|
||||
- ✅ Pattern2 ルーティング成功
|
||||
- ✅ NumberAccumulation 検出: `result * 10 + i` → Mul + Add の2命令
|
||||
- ✅ ValueId Regions: Param (100-101), Local (1000+) 正常分離
|
||||
- ✅ PHI 契約: LoopHeader PHI (ValueId(5), ValueId(6)) + Exit PHI 正常配線
|
||||
- ✅ Runtime: 出力 `12` (期待値通り)
|
||||
|
||||
---
|
||||
|
||||
#### ハーネス2: phase190_parse_number_impl.hako ✅
|
||||
|
||||
**実行コマンド**:
|
||||
```bash
|
||||
./target/release/hakorune apps/tests/phase190_parse_number_impl.hako
|
||||
```
|
||||
|
||||
**主要ログ抽出**:
|
||||
```
|
||||
[pattern2/init] PatternPipelineContext: loop_var='i', loop_var_id=ValueId(4), carriers=1
|
||||
[pattern2/phase201] Using JoinValueSpace: loop_var 'i' → Some(ValueId(100))
|
||||
[pattern2/phase201] Allocated carrier 'num' param ID: ValueId(101)
|
||||
[joinir/pattern2] Phase 176-3: Generating JoinIR for 1 carriers: ["num"]
|
||||
[cf_loop/exit_line] ExitMetaCollector: Collected 'num' JoinIR ValueId(1016) → HOST ValueId(2)
|
||||
[DEBUG-177] Phase 33-21: carrier_phis count: 2, names: ["i", "num"]
|
||||
```
|
||||
|
||||
**観測ポイント**:
|
||||
- ✅ Pattern2 ルーティング成功
|
||||
- ✅ Multi-carrier: `i` (loop var), `num` (carrier) の2つ正常動作
|
||||
- ✅ NumberAccumulation: `num * 10 + i` の Mul + Add 生成
|
||||
- ✅ Exit PHI: 2つの carrier が正しく Exit block で統合
|
||||
- ✅ Runtime: 出力 `123` (期待値通り)
|
||||
|
||||
---
|
||||
|
||||
#### ハーネス3: phase210_match_literal_min.hako ✅
|
||||
|
||||
**実行コマンド**:
|
||||
```bash
|
||||
./target/release/hakorune apps/tests/phase210_match_literal_min.hako
|
||||
```
|
||||
|
||||
**主要ログ抽出**:
|
||||
```
|
||||
[joinir/pattern1] Generated JoinIR for Simple While Pattern
|
||||
[joinir/pattern1] Functions: main, loop_step, k_exit
|
||||
[DEBUG-177] Phase 33-21: carrier_phis count: 1, names: ["i"]
|
||||
[cf_loop/joinir] Phase 177-3: Loop header with 1 PHI dsts to protect: {ValueId(11)}
|
||||
```
|
||||
|
||||
**観測ポイント**:
|
||||
- ✅ **Pattern1 ルーティング成功** (Simple While Pattern)
|
||||
- ✅ JoinIR 生成: main, loop_step, k_exit の3関数
|
||||
- ✅ Single carrier: loop var `i` のみ
|
||||
- ✅ PHI 契約: LoopHeader PHI (ValueId(11)) 正常
|
||||
- ✅ Runtime: 出力 `0 1 2` + RC=1 (最終return値正常)
|
||||
- ⚠️ 副作用: `print(i)` が意図せず実行(テストコード設計時の残骸、動作自体は正常)
|
||||
|
||||
---
|
||||
|
||||
### 4.5 Phase 210 総合評価
|
||||
|
||||
**成功基準達成度**:
|
||||
|
||||
| 基準 | 達成 | 詳細 |
|
||||
|-----|------|------|
|
||||
| **最低限の成功** (1本でも通る) | ✅ | 3本すべて JoinIR → MIR → Runtime 到達 |
|
||||
| **理想的な成功** (3本全て通る) | ✅ | Pattern1, Pattern2 両方で観測データ取得成功 |
|
||||
| **Pattern1 動作確認** | ✅ | SimpleWhile パターン正常動作 |
|
||||
| **Pattern2 動作確認** | ✅ | Break パターン正常動作 |
|
||||
| **NumberAccumulation** | ✅ | Mul + Add 2命令生成確認 |
|
||||
| **Multi-carrier** | ✅ | 2 carrier 同時動作確認 |
|
||||
| **PHI Contract** | ✅ | LoopHeader PHI + Exit PHI 正常配線 |
|
||||
| **ValueId Regions** | ✅ | Param/Local region 分離確認 |
|
||||
| **Fail-Fast 発動** | ❌ | エラー0件(すべて正常動作) |
|
||||
|
||||
**重要な発見**:
|
||||
- ✅ **既存インフラは「理論上いけるはず」を超えて「実戦でも完全動作」** することを確認
|
||||
- ✅ Phase 190 (NumberAccumulation), Phase 201 (JoinValueSpace), Phase 204/205 (PHI Contract) の統合が完璧に機能
|
||||
- ✅ Pattern1 と Pattern2 の自動ルーティングが正常動作
|
||||
- ✅ Multi-carrier パターンの PHI 配線も問題なし
|
||||
- ❌ **制約発見なし** - 予想に反して、すべてのループが制約なく動作
|
||||
|
||||
**Phase 210 の結論**:
|
||||
> JoinIR インフラ(P1-P5/JoinValueSpace/PHI契約)は **実戦投入可能** な成熟度に達している✨
|
||||
|
||||
---
|
||||
|
||||
## Section 5: ドキュメント更新(Task 210-5)
|
||||
|
||||
### 5.1 CURRENT_TASK.md への追記
|
||||
|
||||
**追加内容** (Phase 210 完了時):
|
||||
```markdown
|
||||
### Phase 210: JsonParser JoinIR ミニ統合(ラウンド1)✅
|
||||
- **ゴール**: 既存 JoinIR インフラで実戦ループ 2〜3 本を観測
|
||||
- **結果**:
|
||||
- _atoi (P2 Break): ✅ or ⚠️ or ❌ (詳細: phase210-jsonparser-mini-integration.md)
|
||||
- _parse_number (P2 Break): ✅ or ⚠️ or ❌
|
||||
- _match_literal (P1/P2): ✅ or ⚠️ or ❌
|
||||
- **発見した制約**:
|
||||
- [TBD: 実行後に記録]
|
||||
- **次フェーズ**: Phase 211 - 発見した制約の解消
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.2 joinir-architecture-overview.md への追記
|
||||
|
||||
**追加箇所**: Section 1.10 (Coverage Snapshot) など
|
||||
|
||||
**追加内容**:
|
||||
```markdown
|
||||
#### Phase 210: JsonParser Coverage Snapshot
|
||||
|
||||
**実戦投入済みループ**: 3/11 loops (Phase 210 時点)
|
||||
- ✅ _atoi (P2 Break, NumberAccumulation)
|
||||
- ✅ _parse_number (P2 Break, Multi-carrier)
|
||||
- ✅ _match_literal (P1 Simple)
|
||||
|
||||
**残りループ**: 8 loops
|
||||
- Phase 211+: _parse_array, _parse_object (MethodCall 複数)
|
||||
- Phase 212+: _unescape_string (複雑なキャリア処理)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Section 6: 実装タスクの整理
|
||||
|
||||
### Task 210-1: 対象ループの再選定 ✅(このドキュメント完成で完了)
|
||||
|
||||
**成果物**:
|
||||
- このドキュメント (phase210-jsonparser-mini-integration.md)
|
||||
- 選定ループ: _atoi, _parse_number, _match_literal (3本)
|
||||
- 想定パターン: P1 (SimpleWhile), P2 (WithBreak)
|
||||
|
||||
---
|
||||
|
||||
### Task 210-2: 最小 .hako ハーネス準備(次のステップ)
|
||||
|
||||
**実装内容**:
|
||||
1. `phase190_atoi_impl.hako` の再確認(既存)
|
||||
2. `phase190_parse_number_impl.hako` の再確認(既存)
|
||||
3. `phase210_match_literal_min.hako` の新規作成
|
||||
|
||||
**実装タイミング**: Task 210-2 実行時
|
||||
|
||||
---
|
||||
|
||||
### Task 210-3: 実行経路の確認(Task 210-2 の後)
|
||||
|
||||
**実行コマンド**:
|
||||
```bash
|
||||
# ハーネス1
|
||||
./target/release/hakorune apps/tests/phase190_atoi_impl.hako
|
||||
|
||||
# ハーネス2
|
||||
./target/release/hakorune apps/tests/phase190_parse_number_impl.hako
|
||||
|
||||
# ハーネス3
|
||||
./target/release/hakorune apps/tests/phase210_match_literal_min.hako
|
||||
```
|
||||
|
||||
**記録先**: Section 4 の観測結果テーブル
|
||||
|
||||
---
|
||||
|
||||
### Task 210-4: 観測結果の記録(Task 210-3 の後)
|
||||
|
||||
**記録内容**:
|
||||
- 実行日時
|
||||
- ログ出力(Pattern ルーティング、JoinIR 生成、検証、Runtime)
|
||||
- エラー層の分類
|
||||
- インフラ達成度マトリクスの更新
|
||||
|
||||
**記録先**: Section 4 (このドキュメント内)
|
||||
|
||||
---
|
||||
|
||||
### Task 210-5: ドキュメント更新(Task 210-4 の後)
|
||||
|
||||
**更新対象**:
|
||||
1. `CURRENT_TASK.md` - Phase 210 の結果と次フェーズ計画
|
||||
2. `joinir-architecture-overview.md` - JsonParser Coverage Snapshot 更新
|
||||
|
||||
---
|
||||
|
||||
## Section 7: 成功基準
|
||||
|
||||
### 7.1 Phase 210 の成功定義
|
||||
|
||||
**最低限の成功** (1本でも通れば成功):
|
||||
- [ ] いずれか1本のループが JoinIR → MIR → Runtime まで到達
|
||||
- [ ] エラーが出た場合、エラー層が明確に分類できる
|
||||
|
||||
**理想的な成功** (3本全て通る):
|
||||
- [ ] 3本のループすべてが正常実行
|
||||
- [ ] Pattern 1 と Pattern 2 の両方で観測データ取得
|
||||
- [ ] Multi-carrier, NumberAccumulation, StringAppendChar の組み合わせ動作確認
|
||||
|
||||
---
|
||||
|
||||
### 7.2 Fail-Fast の成功定義
|
||||
|
||||
**Phase 210 は Fail-Fast が成功条件**:
|
||||
- ✅ エラーが出たら即座に記録して停止(修正しない)
|
||||
- ✅ エラー層を 6 つの分類(ConditionEnv/LoopBodyLocal/CarrierUpdate/MethodCall/PHI/ValueId)に振り分け
|
||||
- ✅ Phase 211+ の課題として整理
|
||||
|
||||
**失敗条件**:
|
||||
- ❌ エラーを無視して進む
|
||||
- ❌ エラー層が不明なまま終わる
|
||||
- ❌ Phase 210 で新機能実装を始める
|
||||
|
||||
---
|
||||
|
||||
## Section 8: 次フェーズへの接続
|
||||
|
||||
### Phase 211: 制約解消フェーズ
|
||||
|
||||
**Phase 210 で発見した制約を解消する**:
|
||||
1. LoopBodyLocal への代入 (Phase 186 残タスク)
|
||||
2. MethodCall in condition (Phase 171-D)
|
||||
3. Complex addend in NumberAccumulation (Phase 191+)
|
||||
|
||||
**実装戦略**:
|
||||
- Phase 210 の観測結果を基に、最も影響の大きい制約から優先的に解消
|
||||
- 1フェーズ1制約の原則(箱理論: 小さく積む)
|
||||
|
||||
---
|
||||
|
||||
### Phase 212+: JsonParser 完全統合
|
||||
|
||||
**残り8ループの段階的実装**:
|
||||
- Phase 212: _parse_array, _parse_object (MethodCall 複数対応)
|
||||
- Phase 213: _unescape_string (複雑なキャリア処理)
|
||||
- Phase 214: JsonParser 全11ループ完全動作確認
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: 既存テストの確認
|
||||
|
||||
### A.1 phase190_atoi_impl.hako
|
||||
|
||||
**場所**: `apps/tests/phase190_atoi_impl.hako`
|
||||
|
||||
**現状**: Phase 190-impl-D で E2E 検証済み
|
||||
|
||||
**実行結果** (Phase 190 時点):
|
||||
```
|
||||
12
|
||||
```
|
||||
|
||||
**Phase 210 での再確認ポイント**:
|
||||
- [ ] Pattern2 ルーティング確認
|
||||
- [ ] NumberAccumulation 検出確認
|
||||
- [ ] PHI Contract 検証通過確認
|
||||
|
||||
---
|
||||
|
||||
### A.2 phase190_parse_number_impl.hako
|
||||
|
||||
**場所**: `apps/tests/phase190_parse_number_impl.hako`
|
||||
|
||||
**現状**: Phase 190-impl-D で E2E 検証済み
|
||||
|
||||
**実行結果** (Phase 190 時点):
|
||||
```
|
||||
123
|
||||
```
|
||||
|
||||
**Phase 210 での再確認ポイント**:
|
||||
- [ ] Multi-carrier (p, num_str) の PHI 配線確認
|
||||
- [ ] StringAppendChar + CounterLike 組み合わせ確認
|
||||
- [ ] Exit line reconnect 確認
|
||||
|
||||
---
|
||||
|
||||
## Appendix B: 参照ドキュメント
|
||||
|
||||
### B.1 Phase 190 関連
|
||||
|
||||
- **phase190-number-update-design.md** - NumberAccumulation 設計書
|
||||
- **phase190-impl-D 完了報告** - _atoi, _parse_number E2E 検証結果
|
||||
|
||||
### B.2 Phase 181 関連
|
||||
|
||||
- **phase181-jsonparser-loop-roadmap.md** - JsonParser 全11ループの棚卸し
|
||||
|
||||
### B.3 JoinIR アーキテクチャ
|
||||
|
||||
- **joinir-architecture-overview.md** - JoinIR 全体設計
|
||||
- **phase204-phi-contract-verifier.md** - PHI Contract 検証
|
||||
- **phase205-valueid-regions-design.md** - ValueId Region 設計
|
||||
|
||||
---
|
||||
|
||||
## 改訂履歴
|
||||
|
||||
- **2025-12-09**: Task 210-1 完了(対象ループ再選定・設計ドキュメント作成)
|
||||
- 選定ループ: _atoi, _parse_number, _match_literal (3本)
|
||||
- 想定パターン: P1 (SimpleWhile), P2 (WithBreak)
|
||||
- 既存テスト再利用: phase190_atoi_impl.hako, phase190_parse_number_impl.hako
|
||||
- 新規ハーネス設計: phase210_match_literal_min.hako
|
||||
|
||||
---
|
||||
|
||||
**Phase 210 Status**: Task 210-1 完了 ✅ / Task 210-2〜210-5 未実行
|
||||
@ -0,0 +1,277 @@
|
||||
# Phase 211: JsonParser 次の 1 手(中規模ループ候補選定)
|
||||
|
||||
**Phase**: 211
|
||||
**Date**: 2025-12-09
|
||||
**Status**: 🎯 設計フェーズ(コード実装なし)
|
||||
**Prerequisite**: Phase 210 完了(軽量ループ 3 本実戦成功)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Phase 211 の目的
|
||||
|
||||
Phase 210 で「軽量ループ 3 本」が完全成功したため、次は **「中規模の複雑さを持つループ 1 本」** を選び、既存の Pattern/P5 boxes をどう組み合わせるか **設計のみ** 行う。
|
||||
|
||||
### 📋 作業範囲(明確化)
|
||||
|
||||
- ✅ **やること**: ループ 1 本選定 → Pattern/boxes マッピング → 組み合わせ戦略設計
|
||||
- ❌ **やらないこと**: コード実装、ハーネス作成、テスト実行
|
||||
- 🎯 **成果物**: Phase 212+ で実装する際の「設計図」
|
||||
|
||||
---
|
||||
|
||||
## Task 211-1: 中規模ループ候補の選定
|
||||
|
||||
### 候補 A: `_parse_string` 簡略版
|
||||
|
||||
**元の仕様** (Phase 181 より):
|
||||
```hako
|
||||
_parse_string(pos) {
|
||||
local i = pos
|
||||
local escaped = 0 // LoopBodyLocal (フラグ)
|
||||
local buf = new ArrayBox() // Buffer構築
|
||||
|
||||
loop(i < len) {
|
||||
local ch = s.char_at(i)
|
||||
if ch == quote and escaped == 0 { break } // 終了条件
|
||||
if ch == backslash {
|
||||
escaped = 1 // フラグ切り替え
|
||||
} else {
|
||||
if escaped == 1 {
|
||||
buf.append(escape_char(ch)) // エスケープ処理
|
||||
escaped = 0
|
||||
} else {
|
||||
buf.append(ch)
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return buf.to_string()
|
||||
}
|
||||
```
|
||||
|
||||
**簡略版スコープ** (Phase 211 用):
|
||||
- ✅ `escaped` フラグ(LoopBodyLocal の if 分岐)
|
||||
- ✅ `buf` バッファ構築(ArrayBox.append)
|
||||
- ❌ `escape_char()` 詳細処理(Phase 211 では省略 → "X" で代用)
|
||||
- ❌ StringBox.to_string()(単純化のため最終 return は buf のまま)
|
||||
|
||||
**複雑さの軸**:
|
||||
- **A軸 (更新)**: `i = i + 1` (Simple)+ `escaped` フラグ切り替え(IfPHI 必要)
|
||||
- **B軸 (脱出)**: `break` (Pattern 2 Break)
|
||||
- **C軸 (条件)**: `ch == quote and escaped == 0` (Multi-condition)
|
||||
- **D軸 (変数)**: `i`, `escaped`, `buf` (3 carriers)
|
||||
|
||||
### 候補 B: selfhost if-sum パターン
|
||||
|
||||
**元の仕様** (Phase 181 より):
|
||||
```hako
|
||||
// FuncScannerBox._sum_def_count() の簡略版
|
||||
_sum_def_count(defs) {
|
||||
local sum = 0
|
||||
local i = 0
|
||||
loop(i < defs.len()) {
|
||||
local item = defs.get(i)
|
||||
if item != null {
|
||||
sum = sum + 1 // 条件付き加算
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return sum
|
||||
}
|
||||
```
|
||||
|
||||
**複雑さの軸**:
|
||||
- **A軸 (更新)**: `sum = sum + 1` (条件内)+ `i = i + 1` (無条件)
|
||||
- **B軸 (脱出)**: なし(自然終了)
|
||||
- **C軸 (条件)**: `item != null` (Simple)
|
||||
- **D軸 (変数)**: `sum`, `i` (2 carriers)
|
||||
|
||||
---
|
||||
|
||||
## Task 211-2: Pattern/Boxes マッピング(候補ごと)
|
||||
|
||||
### 候補 A マッピング: `_parse_string` 簡略版
|
||||
|
||||
| 軸 | 要求 | 既存 Pattern/Box | Phase 210 時点の対応状況 |
|
||||
|---|-----|----------------|----------------------|
|
||||
| **A軸** | `i = i + 1` + `escaped` フラグ | Pattern 2 + IfPHI | ✅ Phase 210 で multi-carrier 確認済み |
|
||||
| **B軸** | `break` | Pattern 2 Break | ✅ Phase 210 で動作確認済み |
|
||||
| **C軸** | `ch == quote and escaped == 0` | ConditionLowerer + Multi-condition | ✅ Phase 169 で `and` 対応済み |
|
||||
| **D軸** | 3 carriers (`i`, `escaped`, `buf`) | CarrierInfo + Multi-carrier | ✅ Phase 210 で 2-carrier 確認済み(3-carrier は未テスト) |
|
||||
|
||||
**特殊要素**:
|
||||
- **LoopBodyLocal**: `escaped` はループ内 if 分岐で更新される「状態フラグ」
|
||||
- Phase 171 Trim Pattern では「ループ末尾で代入→Carrier 昇格」だったが、今回は **「if 分岐内で更新→PHI 必要」**
|
||||
- 既存 IfPHI ロジック(Phase 61)で対応可能か要検証
|
||||
|
||||
- **Buffer 構築**: `buf.append(ch)` は BoxCall だが、JoinIR では BoxCall は Opaque 扱い
|
||||
- Phase 210 で BoxCall 自体は問題なし(既存パターンで動作)
|
||||
|
||||
**Phase 211 での設計焦点**:
|
||||
1. `escaped` フラグを Carrier として扱うか、LoopBodyLocal+IfPHI で扱うか
|
||||
2. 3-carrier (i, escaped, buf) の PHI 配線が既存ロジックで通るか
|
||||
|
||||
### 候補 B マッピング: selfhost if-sum パターン
|
||||
|
||||
| 軸 | 要求 | 既存 Pattern/Box | Phase 210 時点の対応状況 |
|
||||
|---|-----|----------------|----------------------|
|
||||
| **A軸** | `sum = sum + 1` (条件内) + `i = i + 1` | Pattern 1 + IfPHI | ✅ IfPHI は Phase 61 で実装済み |
|
||||
| **B軸** | なし(自然終了) | Pattern 1 Simple | ✅ Phase 210 で確認済み |
|
||||
| **C軸** | `item != null` | ConditionLowerer | ✅ 比較演算子対応済み |
|
||||
| **D軸** | 2 carriers (`sum`, `i`) | CarrierInfo | ✅ Phase 210 で動作確認済み |
|
||||
|
||||
**特殊要素**:
|
||||
- **条件付き更新**: `sum = sum + 1` が if ブロック内
|
||||
- Phase 61 IfPHI で対応可能(ループ内 if は Merge 経由で Carrier に PHI 接続)
|
||||
|
||||
**Phase 211 での設計焦点**:
|
||||
1. ループ内 if の `sum` 更新が IfPHI → Loop Header PHI に正しく接続されるか確認
|
||||
|
||||
---
|
||||
|
||||
## Task 211-3: 推奨候補の選定と組み合わせ戦略
|
||||
|
||||
### 🎯 推奨: 候補 B (`selfhost if-sum`) を Phase 211 で選定
|
||||
|
||||
**理由**:
|
||||
1. **既存 boxes で完全カバー可能**
|
||||
- Pattern 1 Simple + IfPHI + Multi-carrier(すべて Phase 210 で動作確認済み)
|
||||
- 新規要素: 「ループ内 if の条件付き更新」のみ
|
||||
|
||||
2. **検証価値が高い**
|
||||
- Phase 61 IfPHI が「ループ内 if」でも正しく動作するか実戦確認
|
||||
- selfhost 実用パターン(`_sum_def_count` 等)の代表例
|
||||
|
||||
3. **Phase 212 実装が軽量**
|
||||
- ハーネス作成が簡単(ArrayBox.get + null チェック)
|
||||
- デバッグが容易(条件分岐 1 箇所のみ)
|
||||
|
||||
**候補 A を Phase 212 以降に回す理由**:
|
||||
- 3-carrier は Phase 210 で未テスト(2-carrier までしか確認していない)
|
||||
- `escaped` フラグの LoopBodyLocal+IfPHI 処理が複雑
|
||||
- Phase 211 で「ループ内 if 更新」を先に確認してから、Phase 212+ で 3-carrier に進む方が安全
|
||||
|
||||
---
|
||||
|
||||
## Task 211-4: Boxes 組み合わせ設計(候補 B: if-sum)
|
||||
|
||||
### 使用する既存 Boxes
|
||||
|
||||
| Box 名 | 役割 | Phase 210 確認状況 |
|
||||
|-------|-----|------------------|
|
||||
| **LoopPatternRouter** | Pattern 1 ルーティング | ✅ Phase 210 で動作確認 |
|
||||
| **SimpleWhileMinimal** | Pattern 1 lowering | ✅ Phase 210 で動作確認 |
|
||||
| **ConditionLowerer** | `item != null` → JoinIR | ✅ Phase 169/210 で確認 |
|
||||
| **CarrierInfo** | `sum`, `i` の metadata 管理 | ✅ Phase 210 で確認 |
|
||||
| **IfPhiContext** | ループ内 if の PHI 生成 | ⚠️ Phase 61 実装済みだが、ループ内 if での実戦は未確認 |
|
||||
| **JoinValueSpace** | ValueId 割り当て | ✅ Phase 210 で region 分離確認 |
|
||||
|
||||
### 処理フロー設計(Phase 212 実装時の想定)
|
||||
|
||||
```
|
||||
1. LoopPatternRouter が Pattern 1 を検出
|
||||
↓
|
||||
2. SimpleWhileMinimal が呼び出される
|
||||
↓
|
||||
3. CarrierInfo が `sum`, `i` を carrier として登録
|
||||
↓
|
||||
4. Loop Header PHI 生成:
|
||||
- PHI(sum): entry=0, back_edge=sum_updated
|
||||
- PHI(i): entry=0, back_edge=i_updated
|
||||
↓
|
||||
5. ConditionLowerer が `i < defs.len()` を JoinIR に変換
|
||||
↓
|
||||
6. ループ本体:
|
||||
- `local item = defs.get(i)` → JoinIR BoxCall (Opaque)
|
||||
- `if item != null { ... }` → IfPhiContext 起動
|
||||
↓
|
||||
6a. IfPhiContext が if ブロック内の `sum = sum + 1` を処理
|
||||
- then ブロック: sum_updated = sum_current + 1
|
||||
- else ブロック: sum_updated = sum_current (変更なし)
|
||||
- Merge 点: PHI(sum_updated) ← [then: sum+1, else: sum]
|
||||
↓
|
||||
- `i = i + 1` → 無条件更新
|
||||
↓
|
||||
7. Loop Back Edge:
|
||||
- sum_updated → Header PHI(sum) の back_edge
|
||||
- i_updated → Header PHI(i) の back_edge
|
||||
↓
|
||||
8. Exit PHI:
|
||||
- PHI(sum_final): loop_exit ← Header PHI(sum)
|
||||
- PHI(i_final): loop_exit ← Header PHI(i)
|
||||
```
|
||||
|
||||
### 重要な設計ポイント
|
||||
|
||||
**IfPhiContext の責務**:
|
||||
- ループ内 if の **Merge 点で PHI 生成** → この PHI が Loop Header PHI の back_edge に接続される
|
||||
- Phase 61 実装時は「ループ外 if」を想定していたが、**ループ内 if でも同じロジックが適用できる** はず
|
||||
|
||||
**検証ポイント(Phase 212 で確認)**:
|
||||
1. IfPhiContext がループ内 if を正しく検出するか
|
||||
2. Merge PHI が Header PHI の back_edge に正しく接続されるか
|
||||
3. `sum` の ValueId が Param region (100-999) に割り当てられるか(Phase 201/205 要件)
|
||||
|
||||
---
|
||||
|
||||
## Task 211-5: Phase 212+ 実装スコープ定義
|
||||
|
||||
### Phase 212: if-sum ハーネス実装・実行
|
||||
|
||||
**スコープ**:
|
||||
- ✅ `apps/tests/phase212_if_sum_min.hako` 作成
|
||||
- ✅ 実行 → 観測(Phase 210 と同じ Fail-Fast 戦略)
|
||||
- ✅ IfPhiContext のループ内 if 動作確認
|
||||
- ✅ phase212-if-sum-observation.md にログ記録
|
||||
|
||||
**期待される成果**:
|
||||
- ループ内 if の条件付き更新が正しく動作
|
||||
- IfPhiContext → Header PHI 接続が正常
|
||||
- → **「ループ内 if + multi-carrier」パターンが実戦確認済み** になる
|
||||
|
||||
### Phase 213+: 段階的拡張(候補 A 等)
|
||||
|
||||
**Phase 213**: 3-carrier テスト(`_parse_string` 簡略版の前段階)
|
||||
- 候補: `i`, `sum`, `count` の 3-carrier ループ(ダミー処理)
|
||||
- 目的: 3-carrier の PHI 配線が既存ロジックで通るか確認
|
||||
|
||||
**Phase 214**: `_parse_string` 簡略版(`escaped` フラグ + `buf` バッファ)
|
||||
- 候補 A の実装
|
||||
- 条件: Phase 213 で 3-carrier が成功していること
|
||||
|
||||
**Phase 215+**: 残りの JsonParser ループ(Phase 181 inventory より)
|
||||
- `_read_array`, `_read_object` 等の再帰呼び出しパターン
|
||||
- `_parse_hex` 等の特殊処理
|
||||
|
||||
---
|
||||
|
||||
## 📊 Phase 211 の成果物(このドキュメント)
|
||||
|
||||
### ✅ 達成したこと
|
||||
|
||||
1. **候補選定**: 候補 B (`selfhost if-sum`) を Phase 212 実装対象に選定
|
||||
2. **Pattern/Boxes マッピング**: 既存 boxes で完全カバー可能と確認
|
||||
3. **組み合わせ戦略**: IfPhiContext → Header PHI 接続フローを設計
|
||||
4. **Phase 212+ スコープ**: 段階的拡張計画を定義
|
||||
|
||||
### 🎯 Phase 212 への引き継ぎ事項
|
||||
|
||||
- **実装対象**: `apps/tests/phase212_if_sum_min.hako`(条件付き加算ループ)
|
||||
- **検証ポイント**: IfPhiContext のループ内 if 動作、Header PHI 接続
|
||||
- **期待結果**: Phase 210 同様の完全成功(Fail-Fast トリガーなし)
|
||||
|
||||
---
|
||||
|
||||
## 📝 補足: Phase 210 との差分
|
||||
|
||||
| 項目 | Phase 210 | Phase 211 |
|
||||
|-----|----------|----------|
|
||||
| **複雑さ** | 軽量(Pattern 1/2 基本形) | 中規模(ループ内 if 更新) |
|
||||
| **新規要素** | なし(既存確認のみ) | IfPhiContext のループ内適用 |
|
||||
| **Carrier 数** | 2 まで確認 | 2(Phase 213 で 3 に拡張予定) |
|
||||
| **アプローチ** | 実戦観測 | 設計のみ(Phase 212 で実装) |
|
||||
|
||||
---
|
||||
|
||||
**Phase 211 完了条件**: ✅ このドキュメントの作成完了
|
||||
**次のステップ**: Phase 212(if-sum ハーネス実装・実行)
|
||||
426
docs/development/current/main/phase212-5-loop-if-mir-bug.md
Normal file
426
docs/development/current/main/phase212-5-loop-if-mir-bug.md
Normal file
@ -0,0 +1,426 @@
|
||||
# Phase 212.5: ループ内 if → MIR 変換バグ修正(緊急ミニフェーズ)
|
||||
|
||||
**Phase**: 212.5
|
||||
**Date**: 2025-12-09
|
||||
**Status**: 🔧 In Progress
|
||||
**Prerequisite**: Phase 212 完了(制約発見)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Phase 212.5 の目的
|
||||
|
||||
Phase 212 で発見した「ループ内 if/else が MIR に変換されない」問題を修正する。
|
||||
|
||||
**戦略**:
|
||||
- JoinIR に触らない(AST→MIR Builder だけを修正)
|
||||
- 既存の If lowering 箱を再利用
|
||||
- 最小限の変更で根治
|
||||
|
||||
---
|
||||
|
||||
## Task 212.5-1: 現状の AST / MIR を確認 ✅
|
||||
|
||||
### テストファイル
|
||||
|
||||
**`apps/tests/phase212_if_sum_min.hako`**:
|
||||
|
||||
```hako
|
||||
static box IfSumTest {
|
||||
sum_def_count(defs) {
|
||||
local sum = 0
|
||||
local i = 0
|
||||
local len = 3
|
||||
|
||||
loop(i < len) {
|
||||
// ← この if がループ本体に含まれるはず
|
||||
if i > 0 {
|
||||
sum = sum + 1 // ← 条件付き更新
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
main() {
|
||||
local result = IfSumTest.sum_def_count(0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 期待される AST 構造
|
||||
|
||||
ループ本体の AST ノードには以下が含まれるはず:
|
||||
|
||||
```
|
||||
Loop {
|
||||
condition: BinaryOp(Lt, i, len),
|
||||
body: Block [
|
||||
// ← If ノードがここにあるはず
|
||||
If {
|
||||
condition: BinaryOp(Gt, i, 0),
|
||||
then_block: Block [
|
||||
Assignment(sum, BinOp(Add, sum, 1))
|
||||
],
|
||||
else_block: None
|
||||
},
|
||||
Assignment(i, BinOp(Add, i, 1))
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 実際の MIR 出力(Before)
|
||||
|
||||
```mir
|
||||
define i64 @IfSumTest.sum_def_count/1(? %0) effects(read) {
|
||||
bb1:
|
||||
%2 = const 0 ; sum 初期化
|
||||
%4 = const 0 ; i 初期化
|
||||
br label bb3
|
||||
|
||||
bb2:
|
||||
ret %2 ; return sum
|
||||
|
||||
bb3:
|
||||
%7 = phi [%4, bb1], [%16, bb6] ; ← i の PHI のみ
|
||||
br label bb4
|
||||
|
||||
bb4:
|
||||
%12 = const 3
|
||||
%13 = icmp Lt %7, %12
|
||||
%14 = Not %13
|
||||
br %14, label bb5, label bb6
|
||||
|
||||
bb5:
|
||||
br label bb2
|
||||
|
||||
bb6:
|
||||
; ← ここに if 由来の Compare / Branch が無い!
|
||||
extern_call env.console.log(%7) [effects: pure|io]
|
||||
%15 = const 1
|
||||
%16 = %7 Add %15
|
||||
%7 = copy %16
|
||||
br label bb3
|
||||
}
|
||||
```
|
||||
|
||||
### 問題点の詳細
|
||||
|
||||
**欠落している MIR 命令**:
|
||||
|
||||
bb6 ループ本体ブロックには以下があるべき:
|
||||
|
||||
```mir
|
||||
bb6:
|
||||
; ← if i > 0 の条件チェック
|
||||
%const_0 = const 0
|
||||
%cond = icmp Gt %7, %const_0
|
||||
br %cond, label bb_then, label bb_else
|
||||
|
||||
bb_then:
|
||||
; sum = sum + 1
|
||||
%sum_phi = phi [%2, bb3], [%sum_updated, bb_else] ; ← sum の PHI
|
||||
%const_1 = const 1
|
||||
%sum_updated = %sum_phi Add %const_1
|
||||
br label bb_merge
|
||||
|
||||
bb_else:
|
||||
br label bb_merge
|
||||
|
||||
bb_merge:
|
||||
%sum_final = phi [%sum_updated, bb_then], [%sum_phi, bb_else]
|
||||
; i = i + 1
|
||||
%15 = const 1
|
||||
%16 = %7 Add %15
|
||||
br label bb3
|
||||
```
|
||||
|
||||
**実際には**:
|
||||
- if 由来の `Compare` / `Branch` が一切無い
|
||||
- `sum` 変数に関する処理(PHI・加算)が完全に消失
|
||||
|
||||
### 仮説: どの層が壊しているか
|
||||
|
||||
#### ✅ Parser 層は OK
|
||||
|
||||
理由:
|
||||
- Phase 212 で print を if 内に追加しても同じ結果
|
||||
- Parser が if ノードを落としているなら、syntax error になるはず
|
||||
- → **Parser は正しく AST を生成している可能性が高い**
|
||||
|
||||
#### ❌ LoopForm / control_flow builder が怪しい
|
||||
|
||||
**仮説 1**: ループ本体の AST ノードが **フラット化** されている
|
||||
- `loop { stmt1; stmt2; }` の各 stmt を順次処理する際、
|
||||
- `stmt` が `If` ノードの場合に **match していない** 可能性
|
||||
|
||||
**仮説 2**: ループ本体の `build_block()` が If を **スキップ** している
|
||||
- `build_block()` が Statement を処理する際、
|
||||
- `Statement::Expr(If)` を **式として評価** せずに無視している可能性
|
||||
|
||||
**仮説 3**: If が **Dead Code Elimination (DCE)** で消えている
|
||||
- `sum` の値が return で使われているから DCE で消えないはず
|
||||
- でも念のため確認が必要
|
||||
|
||||
---
|
||||
|
||||
## Task 212.5-2: MIR Builder の責務位置を特定 ✅
|
||||
|
||||
### 確認したファイル
|
||||
|
||||
1. ✅ **`src/mir/builder/stmts.rs`** - Statement 処理
|
||||
2. ✅ **`src/mir/builder/exprs.rs`** - Expression 処理
|
||||
3. ✅ **`src/mir/builder/control_flow/mod.rs`** - cf_if(), cf_loop()
|
||||
|
||||
### 問題の根本原因を特定
|
||||
|
||||
#### 🚨 **発見した問題**
|
||||
|
||||
**`build_statement()` (stmts.rs:215-222)**:
|
||||
|
||||
```rust
|
||||
pub(super) fn build_statement(&mut self, node: ASTNode) -> Result<ValueId, String> {
|
||||
self.current_span = node.span();
|
||||
match node {
|
||||
// 将来ここに While / ForRange / Match / Using など statement 専用分岐を追加する。
|
||||
other => self.build_expression(other), // ← すべて expression として処理
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**問題点**:
|
||||
- `ASTNode::If` のケースが **match に存在しない**
|
||||
- すべての Statement が `other =>` で `build_expression()` に投げられる
|
||||
- **If が式として評価される** → 値が使われない場合に最適化で消える可能性
|
||||
|
||||
#### If の処理フロー(現状)
|
||||
|
||||
```
|
||||
build_statement(ASTNode::If)
|
||||
↓
|
||||
match { other => build_expression(other) } ← If ケースなし
|
||||
↓
|
||||
build_expression(ASTNode::If)
|
||||
↓
|
||||
match { ASTNode::If { ... } => self.cf_if(...) } ← ここで処理
|
||||
↓
|
||||
cf_if(condition, then_branch, else_branch)
|
||||
↓
|
||||
lower_if_form(...) ← JoinIR ベースの PHI 生成
|
||||
```
|
||||
|
||||
#### 新しい仮説
|
||||
|
||||
**仮説 1**: If が式として評価され、**値が使われない**ため DCE で消える
|
||||
- ループ内の `if i > 0 { sum = sum + 1 }` は Statement として書かれている
|
||||
- でも `build_statement()` は `build_expression()` に投げる
|
||||
- `build_expression()` は ValueId を返すが、ループ本体では **その値を使わない**
|
||||
- → 最適化 (DCE) で If ブロック全体が消える?
|
||||
|
||||
**仮説 2**: ループ本体の AST が JoinIR 経路で **フラット化** されている
|
||||
- `cf_loop()` → `try_cf_loop_joinir()` の経路で
|
||||
- ループ本体の AST ノードが別の形式に変換される際に If が消失
|
||||
|
||||
**仮説 3**: `lower_if_form()` がループ内 if を **スキップ** している
|
||||
- `lower_if_form()` が「ループ外の if のみ対応」の可能性
|
||||
- ループ内 if は別の処理が必要だが、その処理が未実装
|
||||
|
||||
### 次の調査対象
|
||||
|
||||
1. **DCE (Dead Code Elimination)** の動作確認
|
||||
- If 式の戻り値が使われない場合に DCE で消えるか?
|
||||
|
||||
2. **`try_cf_loop_joinir()` の実装確認**
|
||||
- ループ本体の AST がどう処理されているか
|
||||
- If ノードが JoinIR 変換時に保持されているか
|
||||
|
||||
3. **`lower_if_form()` の実装確認**
|
||||
- ループ内 if でも正しく動作するか
|
||||
- ループコンテキストでの制約があるか
|
||||
|
||||
---
|
||||
|
||||
## Task 212.5-3: 小さな箱として if-lowering を足す 🔧
|
||||
|
||||
### 根本原因の確定
|
||||
|
||||
**問題**:
|
||||
- `build_statement()` が `ASTNode::If` を **expression 経路にだけ流していた**
|
||||
- Statement としての If(副作用のみが欲しい)が expression として評価される
|
||||
- → 値が使われないと最適化で消える
|
||||
|
||||
**対応方針**:
|
||||
- **Option A** を採用: `build_statement()` に statement 用の If ケースを追加
|
||||
- 既存の If lowering 箱 (`cf_if` / `lower_if_form`) を呼ぶだけ
|
||||
|
||||
### 設計方針
|
||||
|
||||
**原則**:
|
||||
- 新規巨大箱は作らない
|
||||
- 既存の If lowering 箱を再利用
|
||||
- Statement と Expression の If を明確に分離
|
||||
|
||||
### 実装戦略(Option A)
|
||||
|
||||
#### 修正箇所: `src/mir/builder/stmts.rs`
|
||||
|
||||
**Before**:
|
||||
```rust
|
||||
pub(super) fn build_statement(&mut self, node: ASTNode) -> Result<ValueId, String> {
|
||||
self.current_span = node.span();
|
||||
match node {
|
||||
// TODO: While / ForRange / Match / Using …
|
||||
other => self.build_expression(other), // ← If も expression 扱い
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**After**:
|
||||
```rust
|
||||
pub(super) fn build_statement(&mut self, node: ASTNode) -> Result<ValueId, String> {
|
||||
self.current_span = node.span();
|
||||
match node {
|
||||
ASTNode::If { condition, then_body, else_body, .. } => {
|
||||
// Statement としての If - 既存 If lowering を呼ぶ
|
||||
self.build_if_statement(*condition, then_body, else_body)?;
|
||||
// Statement なので値は使わない(Void を返す)
|
||||
Ok(crate::mir::builder::emission::constant::emit_void(self))
|
||||
}
|
||||
// 将来: While / ForRange / Match / Using など
|
||||
other => self.build_expression(other),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 新規関数: `build_if_statement()`
|
||||
|
||||
既存の If lowering を薄くラップする小さい箱:
|
||||
|
||||
```rust
|
||||
/// Statement としての If 処理(副作用のみ)
|
||||
///
|
||||
/// ループ内 if や top-level statement if はここを通る。
|
||||
/// Expression としての if(値を使う場合)は build_expression 経由。
|
||||
pub(super) fn build_if_statement(
|
||||
&mut self,
|
||||
condition: ASTNode,
|
||||
then_body: Vec<ASTNode>,
|
||||
else_body: Option<Vec<ASTNode>>,
|
||||
) -> Result<(), String> {
|
||||
use crate::ast::Span;
|
||||
|
||||
// then_body と else_body を ASTNode::Program に変換
|
||||
let then_node = ASTNode::Program {
|
||||
statements: then_body,
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let else_node = else_body.map(|b| ASTNode::Program {
|
||||
statements: b,
|
||||
span: Span::unknown(),
|
||||
});
|
||||
|
||||
// 既存の If lowering を呼ぶ(cf_if は lower_if_form を呼ぶ)
|
||||
self.cf_if(condition, then_node, else_node)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Expression vs Statement の分離
|
||||
|
||||
**Expression としての If** (既存のまま):
|
||||
```hako
|
||||
local x = if cond { 1 } else { 2 } // ← 値を使う
|
||||
```
|
||||
→ `build_expression()` 経由で処理
|
||||
|
||||
**Statement としての If** (今回追加):
|
||||
```hako
|
||||
if i > 0 { sum = sum + 1 } // ← 副作用のみ
|
||||
```
|
||||
→ `build_statement()` 経由で処理
|
||||
|
||||
### 重要なポイント
|
||||
|
||||
1. **JoinIR 側には触らない**
|
||||
- 今は素の MIR だけ直す
|
||||
- JoinIR Pattern3 (IfPHI) は Phase 212.5 完了後に使う
|
||||
|
||||
2. **既存 If lowering を再利用**
|
||||
- `cf_if()` → `lower_if_form()` の既存パスをそのまま使う
|
||||
- **ループ内 if も top-level if と同じ構造**(特別扱いしない)
|
||||
|
||||
3. **1 箇所だけで修正**
|
||||
- `build_statement()` に If ケースを追加するだけ
|
||||
- 複数箇所で同じことをしない(DRY 原則)
|
||||
|
||||
---
|
||||
|
||||
## Task 212.5-4: phase212_if_sum_min.hako で再検証 🧪
|
||||
|
||||
### 検証手順
|
||||
|
||||
#### Step 1: 素の MIR ダンプ確認
|
||||
|
||||
```bash
|
||||
./target/release/hakorune --dump-mir apps/tests/phase212_if_sum_min.hako 2>&1 | grep -A 50 "sum_def_count"
|
||||
```
|
||||
|
||||
**期待される MIR**:
|
||||
|
||||
- ループ body 内に:
|
||||
- ✅ `Compare` 命令: `%cond = icmp Gt %i, 0`
|
||||
- ✅ `Branch` 命令: `br %cond, label bb_then, label bb_else`
|
||||
- ✅ then ブロック: `sum = sum + 1` 相当の BinOp
|
||||
- ✅ PHI 命令: `sum` の merge PHI
|
||||
|
||||
#### Step 2: JoinIR 経由の E2E テスト
|
||||
|
||||
```bash
|
||||
NYASH_JOINIR_CORE=1 ./target/release/hakorune apps/tests/phase212_if_sum_min.hako
|
||||
```
|
||||
|
||||
**期待される結果**:
|
||||
|
||||
- RC: **2** (i=1, i=2 で sum が increment されるため)
|
||||
- Pattern 3 (IfPHI) または Pattern 1 + IfPHI が選ばれる
|
||||
- Carrier: `i` と `sum` の 2 つ
|
||||
|
||||
---
|
||||
|
||||
## Task 212.5-5: ドキュメント & CURRENT_TASK の更新 📝
|
||||
|
||||
### Before/After まとめ
|
||||
|
||||
**Before** (Phase 212 時点):
|
||||
- ループ内 if が MIR に現れない
|
||||
- `sum` 変数が carrier として認識されない
|
||||
- RC: 0 (期待: 2)
|
||||
|
||||
**After** (Phase 212.5 完了後):
|
||||
- ループ内 if が正常に MIR に変換される
|
||||
- `sum` と `i` の 2-carrier が正常動作
|
||||
- RC: 2 (正常)
|
||||
|
||||
### CURRENT_TASK.md 更新内容
|
||||
|
||||
```markdown
|
||||
- [x] **Phase 212.5: ループ内 if の AST→MIR 修正** ✅ (完了: 2025-12-09)
|
||||
- **目的**: Phase 212 で発見した「ループ内 if が MIR に変換されない」問題を修正
|
||||
- **修正箇所**: [ファイル名・関数名]
|
||||
- **修正内容**: [具体的な変更内容]
|
||||
- **検証結果**: phase212_if_sum_min.hako で RC=2 を確認
|
||||
- **Phase 212 BLOCKED 解消**: ループ内 if の根本問題を解決
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Phase 212.5 の進捗
|
||||
|
||||
- [x] Task 212.5-1: 現状確認・設計メモ作成 ✅
|
||||
- [ ] Task 212.5-2: MIR Builder 責務位置特定
|
||||
- [ ] Task 212.5-3: if-lowering 追加
|
||||
- [ ] Task 212.5-4: 再検証
|
||||
- [ ] Task 212.5-5: ドキュメント更新
|
||||
|
||||
**次のステップ**: Task 212.5-2(ファイル読み込み・責務特定)
|
||||
257
docs/development/current/main/phase212-if-sum-impl.md
Normal file
257
docs/development/current/main/phase212-if-sum-impl.md
Normal file
@ -0,0 +1,257 @@
|
||||
# Phase 212: if-sum ミニ実装 & 実行フェーズ - 観測レポート
|
||||
|
||||
**Phase**: 212
|
||||
**Date**: 2025-12-09
|
||||
**Status**: ⚠️ **BLOCKED** - AST→MIR 変換層の制約発見
|
||||
**Prerequisite**: Phase 211 完了(設計フェーズ)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Phase 212 の目的
|
||||
|
||||
Phase 211 で設計した「if-sum パターン」(ループ内 if での条件付き更新)を、既存 JoinIR インフラ(P1+P3+multi-carrier)だけで実際に動かす。
|
||||
|
||||
**戦略**: Fail-Fast - 問題が出たら「どこで止まったか」を記録するところまでに留める。
|
||||
|
||||
---
|
||||
|
||||
## Task 212-1: .hako テスト関数の追加 ✅
|
||||
|
||||
**ファイル**: `apps/tests/phase212_if_sum_min.hako`
|
||||
|
||||
**初期実装**:
|
||||
```hako
|
||||
static box IfSumTest {
|
||||
sum_def_count(defs) {
|
||||
local sum = 0
|
||||
local i = 0
|
||||
local len = 3
|
||||
|
||||
loop(i < len) {
|
||||
if i > 0 {
|
||||
sum = sum + 1 // ← 条件付き更新
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
main() {
|
||||
local result = IfSumTest.sum_def_count(0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**期待結果**: RC=2 (i=1, i=2 で sum が increment されるため)
|
||||
|
||||
---
|
||||
|
||||
## Task 212-2: ルーティング条件の確認 ✅
|
||||
|
||||
**確認内容**:
|
||||
- `loop_pattern_router.rs` (Phase 194) は構造ベースで Pattern 1-4 を自動分類
|
||||
- `loop_pattern_detection::classify()` が CFG 構造から Pattern 判定
|
||||
- → 名前ベースの whitelist は不要(既存ロジックで対応可能)
|
||||
|
||||
**結論**: 構造ベースルーティングで自動的に Pattern が選ばれるはず
|
||||
|
||||
---
|
||||
|
||||
## Task 212-3: JoinIR 経路で E2E 実行 ⚠️
|
||||
|
||||
### 実行コマンド
|
||||
|
||||
```bash
|
||||
NYASH_JOINIR_CORE=1 ./target/release/hakorune apps/tests/phase212_if_sum_min.hako
|
||||
```
|
||||
|
||||
### 実行結果
|
||||
|
||||
```
|
||||
[joinir/pattern1] Generated JoinIR for Simple While Pattern
|
||||
[joinir/pattern1] Functions: main, loop_step, k_exit
|
||||
...
|
||||
[DEBUG-177] Phase 33-21: carrier_phis count: 1, names: ["i"]
|
||||
...
|
||||
RC: 0
|
||||
```
|
||||
|
||||
**期待**: RC=2
|
||||
**実際**: RC=0
|
||||
|
||||
### Pattern ルーティング観測
|
||||
|
||||
- **選ばれた Pattern**: **Pattern 1 (Simple While)**
|
||||
- **Carrier 数**: **1 つのみ** (`i`)
|
||||
- **欠落している Carrier**: `sum` が carrier として認識されていない
|
||||
|
||||
### MIR ダンプ分析
|
||||
|
||||
```bash
|
||||
./target/release/hakorune --dump-mir apps/tests/phase212_if_sum_min.hako
|
||||
```
|
||||
|
||||
**MIR 出力** (`IfSumTest.sum_def_count/1`):
|
||||
|
||||
```mir
|
||||
define i64 @IfSumTest.sum_def_count/1(? %0) effects(read) {
|
||||
bb1:
|
||||
%2 = const 0 ; ← sum 初期化
|
||||
%4 = const 0 ; ← i 初期化
|
||||
br label bb3
|
||||
|
||||
bb2:
|
||||
ret %2 ; ← return sum
|
||||
|
||||
bb3:
|
||||
%7 = phi [%4, bb1], [%16, bb6] ; ← i の PHI のみ
|
||||
br label bb4
|
||||
|
||||
bb4:
|
||||
%12 = const 3
|
||||
%13 = icmp Lt %7, %12
|
||||
%14 = Not %13
|
||||
br %14, label bb5, label bb6
|
||||
|
||||
bb5:
|
||||
br label bb2
|
||||
|
||||
bb6:
|
||||
extern_call env.console.log(%7) [effects: pure|io] ; ← print(i)
|
||||
%15 = const 1
|
||||
%16 = %7 Add %15 ; ← i = i + 1
|
||||
%7 = copy %16
|
||||
br label bb3
|
||||
}
|
||||
```
|
||||
|
||||
### 🚨 **重大な発見**
|
||||
|
||||
#### 現象
|
||||
|
||||
**ループ内 if/else ブロックが MIR に存在しない!**
|
||||
|
||||
- `.hako` ソースコードには `if i > 0 { sum = sum + 1 }` が書いてあるのに、
|
||||
- MIR には bb6 ブロックに `print(i)` と `i = i + 1` しかない
|
||||
- `sum` 変数に関する処理(条件分岐・加算・PHI)が **完全に消失**
|
||||
|
||||
#### print 追加による検証
|
||||
|
||||
if ブロックが DCE で消えている可能性を考え、if 内に `print(sum)` を追加:
|
||||
|
||||
```hako
|
||||
if i > 0 {
|
||||
sum = sum + 1
|
||||
print(sum) // ← Force if to stay in MIR
|
||||
} else {
|
||||
print(0) // ← Ensure else branch exists
|
||||
}
|
||||
```
|
||||
|
||||
**結果**: MIR は変わらず。if/else ブロック自体が MIR に現れない。
|
||||
|
||||
---
|
||||
|
||||
## Task 212-4: 観測結果の記録 ✅
|
||||
|
||||
### ✅ 成功した部分
|
||||
|
||||
1. **Pattern Routing 動作**: Pattern 1 が構造ベースで正しく選ばれた
|
||||
2. **JoinIR 生成**: Pattern 1 lowerer が動作し、JoinIR 関数 (main/loop_step/k_exit) を生成
|
||||
3. **Carrier 処理**: `i` carrier の PHI 配線は正常
|
||||
|
||||
### ❌ 失敗した部分
|
||||
|
||||
**Root Cause**: **AST → MIR 変換層でループ内 if/else が消失**
|
||||
|
||||
#### 発見した制約
|
||||
|
||||
| 項目 | 内容 |
|
||||
|-----|-----|
|
||||
| **制約層** | AST → MIR 変換(Parser or MIR Builder) |
|
||||
| **現象** | ループ内 if/else の代入文が MIR に変換されない |
|
||||
| **影響範囲** | JoinIR Pattern 3 (IfPHI) が動作する以前の問題 |
|
||||
| **エラーメッセージ** | なし(silent failure) |
|
||||
| **再現性** | 100%(print 追加でも変わらず) |
|
||||
|
||||
#### 詳細分析
|
||||
|
||||
**予想される原因**:
|
||||
|
||||
1. **Parser の制限**:
|
||||
- ループ本体の if/else が正しく AST に変換されていない可能性
|
||||
- AST ノードが生成されても、型やスコープ情報が不完全
|
||||
|
||||
2. **MIR Builder の制限**:
|
||||
- `build_block()` がループ本体の if を処理する際に、条件付き代入を無視
|
||||
- ループ内 if の Merge PHI 生成ロジックが未実装
|
||||
|
||||
3. **変数スコープ問題**:
|
||||
- `sum` がループ外で `local` 宣言されているが、ループ内 if での更新が「新しい定義」として認識されない
|
||||
- ループ内変数更新が SSA 形式に変換されない
|
||||
|
||||
**確認が必要な層**:
|
||||
|
||||
- `src/mir/builder/control_flow/if_form.rs` - if 式の MIR 変換ロジック
|
||||
- `src/mir/builder/control_flow/loop_form.rs` - ループ本体の処理
|
||||
- `src/mir/builder/build_block.rs` - ブロック構築ロジック
|
||||
- `src/parser/` - AST 生成の正確性
|
||||
|
||||
### JoinIR インフラへの影響
|
||||
|
||||
**Phase 212 の結論**:
|
||||
|
||||
- ✅ JoinIR Pattern Routing 自体は正常動作
|
||||
- ✅ Pattern 1 (Simple While) の carrier 処理は完璧
|
||||
- ❌ **ループ内 if の AST→MIR 変換が Phase 212 のブロッカー**
|
||||
|
||||
**Phase 213 への影響**:
|
||||
|
||||
- Phase 213 (3-carrier テスト) も同じ問題に遭遇する可能性が高い
|
||||
- **先に AST→MIR 層の修正が必要**
|
||||
|
||||
---
|
||||
|
||||
## 📊 Phase 212 Overall Evaluation
|
||||
|
||||
### 成果
|
||||
|
||||
1. **Fail-Fast 成功**: Phase 211 の設計段階では見えなかった制約を 1 回の実行で発見
|
||||
2. **制約の層を特定**: JoinIR ではなく **AST→MIR 変換層** の問題と判明
|
||||
3. **再現性確認**: MIR ダンプで問題を可視化・記録
|
||||
|
||||
### 次のステップ
|
||||
|
||||
**Phase 212.5 (緊急対応)**: AST→MIR ループ内 if 変換の調査・修正
|
||||
|
||||
**調査項目**:
|
||||
1. ループ内 if の AST ノードが正しく生成されているか確認
|
||||
2. `build_block()` がループ本体の if をどう処理しているか追跡
|
||||
3. ループ内変数更新の SSA 変換ロジックを確認
|
||||
|
||||
**実装方針**:
|
||||
- Phase 212 は「観測フェーズ」として完了
|
||||
- Phase 212.5 で AST→MIR 修正(別タスク)
|
||||
- Phase 213 以降は Phase 212.5 完了後に再開
|
||||
|
||||
---
|
||||
|
||||
## 📝 参考情報
|
||||
|
||||
### 関連ドキュメント
|
||||
|
||||
- Phase 211 設計: `docs/development/current/main/phase211-loop-candidate-selection.md`
|
||||
- JoinIR アーキテクチャ: `docs/development/current/main/joinir-architecture-overview.md`
|
||||
- Pattern Routing: `src/mir/join_ir/lowering/loop_pattern_router.rs`
|
||||
|
||||
### 関連コード
|
||||
|
||||
- テストファイル: `apps/tests/phase212_if_sum_min.hako`
|
||||
- Pattern 1 Lowerer: `src/mir/join_ir/lowering/loop_patterns/simple_while.rs`
|
||||
- Pattern 3 Lowerer: `src/mir/join_ir/lowering/loop_patterns/with_if_phi.rs`
|
||||
|
||||
---
|
||||
|
||||
**Phase 212 ステータス**: ⚠️ BLOCKED(AST→MIR 層の制約により中断)
|
||||
**次のアクション**: Phase 212.5(AST→MIR ループ内 if 修正)
|
||||
@ -0,0 +1,411 @@
|
||||
# Phase 213: Pattern3 Lowerer 汎用化(if-sum minimal)
|
||||
|
||||
**Phase**: 213
|
||||
**Date**: 2025-12-09
|
||||
**Status**: 🚧 In Progress
|
||||
**Prerequisite**: Phase 212.5 完了(構造ベース if 検出 + Pattern 3 routing)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Phase 213 の目的
|
||||
|
||||
Phase 212.5 で正しく Pattern 3 にルーティングされるようになった `phase212_if_sum_min.hako` を、JoinIR Pattern 3(If-PHI)で正しく実行できるようにする。
|
||||
|
||||
**問題**: 現在の Pattern 3 lowerer は **test-only PoC 実装**
|
||||
- Loop condition: `i <= 5` (hardcoded)
|
||||
- If condition: `i % 2 == 1` (hardcoded)
|
||||
- Update logic: `sum + i` (hardcoded)
|
||||
|
||||
**目標**: AST-based 汎用 Pattern 3 lowerer の実装
|
||||
- LoopUpdateSummary / CarrierInfo / BoolExprLowerer ベースの汎用実装
|
||||
- `phase212_if_sum_min.hako` で RC=2 達成
|
||||
- 既存パターン(`loop_if_phi.hako` 等)の後方互換維持
|
||||
|
||||
---
|
||||
|
||||
## 📋 現状の Pattern 3 実装の問題点
|
||||
|
||||
### 1. ハードコードされた条件・更新式
|
||||
|
||||
**Loop condition** (`loop_with_if_phi_minimal.rs`):
|
||||
```rust
|
||||
// Hardcoded: i <= 5
|
||||
let loop_cond_value = /* ... */;
|
||||
```
|
||||
|
||||
**If condition**:
|
||||
```rust
|
||||
// Hardcoded: i % 2 == 1
|
||||
let if_cond = /* modulo operation */;
|
||||
```
|
||||
|
||||
**Update expressions**:
|
||||
```rust
|
||||
// Hardcoded: sum = sum + i, count = count + 1
|
||||
let sum_update = /* sum + i */;
|
||||
let count_update = /* count + 1 */;
|
||||
```
|
||||
|
||||
### 2. テスト専用の ValueId マッピング
|
||||
|
||||
```rust
|
||||
const PATTERN3_K_EXIT_SUM_FINAL_ID: ValueId = ValueId(24);
|
||||
const PATTERN3_K_EXIT_COUNT_FINAL_ID: ValueId = ValueId(25);
|
||||
```
|
||||
|
||||
これらは特定のテストケース用に固定されており、異なる carrier 構成には対応できない。
|
||||
|
||||
### 3. 汎用性の欠如
|
||||
|
||||
- `phase212_if_sum_min.hako` のような実際の if-sum パターンが動かない
|
||||
- Carrier 構成が変わると動作しない
|
||||
- If 条件が変わると対応できない
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 新しい入力情報アーキテクチャ
|
||||
|
||||
### 入力: PatternPipelineContext
|
||||
|
||||
Phase 213 では、以下の情報を利用して汎用的な lowering を実現:
|
||||
|
||||
**1. LoopFeatures** (from pattern_pipeline.rs)
|
||||
- `has_if`: Loop body に if 文が存在するか
|
||||
- `has_if_else_phi`: PHI merge が必要な if-else か
|
||||
- `carrier_count`: Carrier 変数の数
|
||||
|
||||
**2. CarrierInfo**
|
||||
- Carrier 変数のリスト(名前、host_id、join_id)
|
||||
- 各 carrier の UpdateKind(CounterLike, AccumulationLike, etc.)
|
||||
|
||||
**3. LoopUpdateSummary**
|
||||
```rust
|
||||
pub struct LoopUpdateSummary {
|
||||
pub updates: Vec<CarrierUpdateInfo>, // 各 carrier の更新情報
|
||||
}
|
||||
|
||||
pub struct CarrierUpdateInfo {
|
||||
pub carrier_name: String,
|
||||
pub update_kind: UpdateKind,
|
||||
pub then_expr: Option<ASTNode>, // then branch update
|
||||
pub else_expr: Option<ASTNode>, // else branch update
|
||||
}
|
||||
```
|
||||
|
||||
**4. BoolExprLowerer / condition_to_joinir**
|
||||
- 任意の bool 条件を JoinIR に変換
|
||||
- 既存の `condition_to_joinir()` 関数を活用
|
||||
|
||||
**5. ConditionEnv / JoinValueSpace**
|
||||
- Variable → ValueId マッピング
|
||||
- ValueId allocation 管理
|
||||
|
||||
---
|
||||
|
||||
## 🔄 目標となる変換フロー
|
||||
|
||||
### Phase 213 汎用 Lowering Pipeline
|
||||
|
||||
```
|
||||
Input: PatternPipelineContext
|
||||
├─ loop_condition: ASTNode (e.g., "i < 3")
|
||||
├─ loop_body: Vec<ASTNode> (contains if statement)
|
||||
├─ CarrierInfo (e.g., [i, sum])
|
||||
└─ LoopUpdateSummary (e.g., sum: then=sum+1, else=sum+0)
|
||||
|
||||
Step 1: Loop Condition Lowering
|
||||
loop_condition AST → BoolExprLowerer
|
||||
→ JoinIR loop_cond: ValueId
|
||||
|
||||
Step 2: Extract If Statement from Loop Body
|
||||
Find ASTNode::If in loop_body
|
||||
→ if_condition: ASTNode (e.g., "i > 0")
|
||||
→ then_body: Vec<ASTNode>
|
||||
→ else_body: Option<Vec<ASTNode>>
|
||||
|
||||
Step 3: If Condition Lowering
|
||||
if_condition AST → BoolExprLowerer
|
||||
→ JoinIR if_cond: ValueId
|
||||
|
||||
Step 4: Carrier Update Lowering (from LoopUpdateSummary)
|
||||
For each carrier in CarrierInfo:
|
||||
- Get then_expr from LoopUpdateSummary
|
||||
- Get else_expr from LoopUpdateSummary
|
||||
- Lower then_expr → JoinIR then_value: ValueId
|
||||
- Lower else_expr → JoinIR else_value: ValueId
|
||||
- Generate PHI: carrier_new = phi [then_value, else_value]
|
||||
|
||||
Step 5: JoinIR Function Generation
|
||||
- entry(): Initialize carriers
|
||||
- loop_step(i, carrier1, carrier2, ...):
|
||||
if if_cond:
|
||||
then_branch → update carriers (then values)
|
||||
else:
|
||||
else_branch → update carriers (else values)
|
||||
PHI merge → carrier_new values
|
||||
next iteration or exit
|
||||
- k_exit(carrier1_final, carrier2_final, ...): Return final values
|
||||
|
||||
Step 6: ExitMeta Construction
|
||||
ExitMeta {
|
||||
carriers: [
|
||||
{ name: "sum", join_id: ValueId(X), host_slot: ValueId(Y) },
|
||||
...
|
||||
]
|
||||
}
|
||||
|
||||
Output: (JoinModule, ExitMeta)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Fail-Fast ポリシー
|
||||
|
||||
### 対応外パターンの明示的エラー
|
||||
|
||||
Pattern 3 lowerer は以下の場合に **明示的にエラー**を返す:
|
||||
|
||||
**1. LoopUpdateSummary 不整合**
|
||||
```rust
|
||||
if carrier.then_expr.is_none() || carrier.else_expr.is_none() {
|
||||
return Err(JoinIrError::UnsupportedPattern {
|
||||
reason: format!("Carrier '{}' missing then/else update", carrier.name)
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**2. UpdateKind 未対応**
|
||||
```rust
|
||||
match carrier.update_kind {
|
||||
UpdateKind::Complex | UpdateKind::Unknown => {
|
||||
return Err(JoinIrError::UnsupportedPattern {
|
||||
reason: format!("Carrier '{}' has unsupported UpdateKind: {:?}",
|
||||
carrier.name, carrier.update_kind)
|
||||
});
|
||||
}
|
||||
_ => { /* OK */ }
|
||||
}
|
||||
```
|
||||
|
||||
**3. If 構造不整合**
|
||||
```rust
|
||||
if loop_body.iter().filter(|n| matches!(n, ASTNode::If { .. })).count() != 1 {
|
||||
return Err(JoinIrError::UnsupportedPattern {
|
||||
reason: "Pattern 3 requires exactly one if statement in loop body".to_string()
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**禁止事項**:
|
||||
- ❌ Silent fallback to Pattern 1
|
||||
- ❌ Default values for missing updates
|
||||
- ❌ Ignoring unsupported UpdateKind
|
||||
|
||||
**原則**: **すべての制約は明示的エラーで通知**(Fail-Fast)
|
||||
|
||||
---
|
||||
|
||||
## 📐 設計の核心アイデア
|
||||
|
||||
### 1. 入力を「箱」として分離
|
||||
|
||||
**現状**: ハードコードされた値が scattered
|
||||
**Phase 213**: 入力情報を構造化された箱から取得
|
||||
|
||||
```rust
|
||||
// Before (Phase 195)
|
||||
const LOOP_BOUND: i64 = 5; // Hardcoded
|
||||
const IF_MODULO: i64 = 2; // Hardcoded
|
||||
|
||||
// After (Phase 213)
|
||||
let loop_cond = ctx.loop_condition; // From PatternPipelineContext
|
||||
let if_cond = extract_if_condition(&ctx.loop_body)?; // From AST
|
||||
let updates = ctx.loop_update_summary; // From LoopUpdateSummary
|
||||
```
|
||||
|
||||
### 2. Lowering を既存箱に委譲
|
||||
|
||||
**BoolExprLowerer**: Bool condition → JoinIR
|
||||
```rust
|
||||
let loop_cond_value = condition_to_joinir(
|
||||
loop_cond,
|
||||
&condition_env,
|
||||
&mut join_value_space
|
||||
)?;
|
||||
```
|
||||
|
||||
**CarrierUpdateEmitter**: Update expression → JoinIR
|
||||
```rust
|
||||
let then_value = emit_carrier_update_with_env(
|
||||
carrier.then_expr,
|
||||
&update_env,
|
||||
&mut join_value_space
|
||||
)?;
|
||||
```
|
||||
|
||||
### 3. ExitMeta で複数 Carrier を統一的に扱う
|
||||
|
||||
**現状**: 固定 ValueId の const 定義
|
||||
**Phase 213**: ExitMeta に動的登録
|
||||
|
||||
```rust
|
||||
// Before
|
||||
exit_bindings.push(LoopExitBinding {
|
||||
carrier_name: "sum".to_string(),
|
||||
join_exit_value: PATTERN3_K_EXIT_SUM_FINAL_ID, // Hardcoded!
|
||||
host_slot: sum_var_id,
|
||||
});
|
||||
|
||||
// After
|
||||
for carrier in carrier_info.carriers.iter() {
|
||||
exit_bindings.push(LoopExitBinding {
|
||||
carrier_name: carrier.name.clone(),
|
||||
join_exit_value: carrier.join_final_id, // From JoinIR generation
|
||||
host_slot: carrier.host_id,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 実装の構造
|
||||
|
||||
### Target Files
|
||||
|
||||
**1. JoinIR Lowerer**
|
||||
- `src/mir/join_ir/lowering/loop_with_if_phi_minimal.rs`
|
||||
- **変更内容**:
|
||||
- ハードコード削除
|
||||
- PatternPipelineContext からの入力受け取り
|
||||
- BoolExprLowerer / CarrierUpdateEmitter への委譲
|
||||
|
||||
**2. Pattern 3 Entry Point**
|
||||
- `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs`
|
||||
- **変更内容**:
|
||||
- PatternPipelineContext の構築
|
||||
- ExitMeta の動的構築
|
||||
- Fail-Fast エラーハンドリング
|
||||
|
||||
### Signature Changes
|
||||
|
||||
**Before (Phase 195)**:
|
||||
```rust
|
||||
pub fn lower_loop_with_if_phi_pattern(
|
||||
scope: LoopScopeShape,
|
||||
join_value_space: &mut JoinValueSpace,
|
||||
) -> Option<JoinModule>
|
||||
```
|
||||
|
||||
**After (Phase 213)**:
|
||||
```rust
|
||||
pub fn lower_loop_with_if_phi_pattern(
|
||||
ctx: &PatternPipelineContext,
|
||||
join_value_space: &mut JoinValueSpace,
|
||||
) -> Result<(JoinModule, ExitMeta), JoinIrError>
|
||||
```
|
||||
|
||||
**変更点**:
|
||||
1. 入力: `LoopScopeShape` → `PatternPipelineContext`
|
||||
2. 戻り値: `Option<JoinModule>` → `Result<(JoinModule, ExitMeta), JoinIrError>`
|
||||
3. ExitMeta を返して動的 exit binding を可能に
|
||||
|
||||
---
|
||||
|
||||
## ✅ 検証計画
|
||||
|
||||
### Test Case 1: phase212_if_sum_min.hako(主目標)
|
||||
|
||||
**Input**:
|
||||
```nyash
|
||||
loop(i < 3) {
|
||||
if i > 0 {
|
||||
sum = sum + 1
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
```
|
||||
|
||||
**Expected**:
|
||||
- RC: **2**
|
||||
- Pattern: Pattern 3 (If-Else PHI)
|
||||
- Carriers: `i` (CounterLike), `sum` (AccumulationLike)
|
||||
- Trace: `[joinir/pattern3] Generated JoinIR for Loop with If-Else PHI`
|
||||
|
||||
### Test Case 2: loop_if_phi.hako(後方互換)
|
||||
|
||||
既存の Phase 195 テストケース:
|
||||
```nyash
|
||||
loop(i < 5) {
|
||||
if i % 2 == 1 {
|
||||
sum = sum + i
|
||||
} else {
|
||||
sum = sum + 0
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
```
|
||||
|
||||
**Expected**:
|
||||
- 既存と同じ出力・RC
|
||||
- Regression なし
|
||||
|
||||
### Test Case 3: Multi-carrier Phase 195 tests
|
||||
|
||||
Phase 195 で追加された multi-carrier tests:
|
||||
- sum + count の 2-carrier
|
||||
- sum + count + index の 3-carrier (if exists)
|
||||
|
||||
**Expected**:
|
||||
- 既存と同じ挙動
|
||||
- ExitMeta が複数 carrier を正しく処理
|
||||
|
||||
---
|
||||
|
||||
## 📊 Phase 213 タスクチェックリスト
|
||||
|
||||
- [ ] Task 213-1: 設計ドキュメント作成 ✅ (this file)
|
||||
- [ ] Task 213-2: Pattern3 Lowerer 本体リファクタリング
|
||||
- [ ] Step 2-1: ハードコード削除
|
||||
- [ ] Step 2-2: 入力を Context ベースに変更
|
||||
- [ ] Step 2-3: 条件 lowering を BoolExprLowerer に委譲
|
||||
- [ ] Step 2-4: キャリア更新の一般化
|
||||
- [ ] Step 2-5: PHI 生成
|
||||
- [ ] Step 2-6: 戻り値と boundary 連携
|
||||
- [ ] Task 213-3: Fail-Fast 条件の明確化
|
||||
- [ ] Task 213-4: テスト & 検証
|
||||
- [ ] phase212_if_sum_min.hako → RC=2
|
||||
- [ ] loop_if_phi.hako → Regression check
|
||||
- [ ] Multi-carrier tests → Regression check
|
||||
- [ ] Task 213-5: ドキュメント更新
|
||||
- [ ] phase212-if-sum-impl.md
|
||||
- [ ] joinir-architecture-overview.md
|
||||
- [ ] CURRENT_TASK.md
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Success Criteria
|
||||
|
||||
**Phase 213 is complete when**:
|
||||
1. ✅ `phase212_if_sum_min.hako` produces RC=2
|
||||
2. ✅ All existing Pattern 3 tests pass (no regression)
|
||||
3. ✅ No hardcoded conditions/updates in Pattern 3 lowerer
|
||||
4. ✅ Fail-Fast errors for unsupported patterns
|
||||
5. ✅ Documentation updated (3 files)
|
||||
|
||||
**Commit message format**:
|
||||
```
|
||||
feat(joinir): Phase 213 Pattern3 AST-based generalization
|
||||
|
||||
Phase 213 で Pattern3 lowerer を AST-based 汎用実装に書き換え。
|
||||
phase212_if_sum_min.hako が RC=2 で正常動作。
|
||||
|
||||
- Removed hardcoded conditions/updates
|
||||
- Integrated BoolExprLowerer for dynamic condition lowering
|
||||
- Generalized carrier update via LoopUpdateSummary
|
||||
- Dynamic ExitMeta construction for multi-carrier support
|
||||
- Fail-Fast for unsupported patterns
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Phase 213: READY TO START** 🚀
|
||||
@ -122,6 +122,8 @@
|
||||
|
||||
### 2.1 `NYASH_JOINIR_CORE`
|
||||
|
||||
> 2025-12 現在: JoinIR は常時 ON。`NYASH_JOINIR_CORE` は警告のみで無視される(LoopBuilder 削除済み、config/env で no-op)。
|
||||
|
||||
**使用箇所総数**: 9箇所
|
||||
|
||||
#### カテゴリ別内訳
|
||||
|
||||
Reference in New Issue
Block a user