1111 lines
34 KiB
Markdown
1111 lines
34 KiB
Markdown
|
|
# Phase 2: ループ変数破損バグ完全根治計画
|
|||
|
|
|
|||
|
|
**作成日**: 2025-10-17
|
|||
|
|
**優先度**: P0(最重要)
|
|||
|
|
**所要時間**: 3-4日(調査完了済み、実装開始)
|
|||
|
|
**背景**: 4回以上のPHIバグ再発歴 + 今回発見の3つのバグ → 構造的保証による完全根治が必要
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📋 Executive Summary
|
|||
|
|
|
|||
|
|
**今回発見したバグ(Task先生4人並列調査)**:
|
|||
|
|
1. **P0 - パラメータレジスタ上書きバグ**: MIR Builder が v%0-v%N を再利用
|
|||
|
|
2. **P1 - メソッド降下の不安定性**: origin 推論失敗時に BoxCall/Extern で揺れる
|
|||
|
|
3. **P2 - variable_map の ValueId 衝突**: メソッド結果が既存変数と衝突
|
|||
|
|
|
|||
|
|
**根治方針**: 3つのバグ修正 + LoopFormBox統合による**構造的保証**
|
|||
|
|
|
|||
|
|
**目標**: ループ構造を強制的に正規化し、PHIバグを**構造的に**防止する
|
|||
|
|
|
|||
|
|
**核心アイデア**:
|
|||
|
|
```
|
|||
|
|
Header = PHI群 + Branch のみ(構造強制)
|
|||
|
|
条件式 = 別ブロックで構築(副作用隔離)
|
|||
|
|
一時値 = pin スロット(__pin$...)のみ
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**成果物**:
|
|||
|
|
- P0修正: ParameterGuardBox(パラメータレジスタ保護)
|
|||
|
|
- P1修正: OriginPropagationBox(origin推論強化)
|
|||
|
|
- P2修正: ValueIdAllocatorBox(ValueId衝突回避)
|
|||
|
|
- LoopFormBox(構造保証Box)
|
|||
|
|
- LoopFormVerifierBox(構造強制Verifier)
|
|||
|
|
- 回帰テスト30個
|
|||
|
|
- 環境変数フラグ(戻せる実装)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🐛 今回発見したバグの詳細と修正方針
|
|||
|
|
|
|||
|
|
### P0: パラメータレジスタ上書きバグ
|
|||
|
|
|
|||
|
|
**症状**:
|
|||
|
|
- `skip_ws(s, i, end)` でループ変数 j(v%4) がメソッド receiver copy で String に上書き
|
|||
|
|
- MIR証拠: `%4 = copy %23` (v%4=Integer j → v%23=String s)
|
|||
|
|
- エラー: "Type error: compare Lt on String("0") and Integer(1)"
|
|||
|
|
|
|||
|
|
**根本原因**:
|
|||
|
|
- MIR Builder が関数パラメータレジスタ v%0-v%N をローカル変数で再利用
|
|||
|
|
|
|||
|
|
**修正内容**:
|
|||
|
|
```rust
|
|||
|
|
// src/mir/builder/guards/parameter_guard.rs(既存、Phase 2.4で実装済み)
|
|||
|
|
pub struct ParameterGuardBox {
|
|||
|
|
param_count: usize,
|
|||
|
|
reserved_registers: HashSet<ValueId>,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 修正: v%(N+1) からローカル変数開始
|
|||
|
|
impl ParameterGuardBox {
|
|||
|
|
pub fn validate_no_overwrite(&self, vid: ValueId) -> Result<(), String> {
|
|||
|
|
if self.reserved_registers.contains(&vid) {
|
|||
|
|
return Err(format!("Attempted to overwrite parameter register {:?}", vid));
|
|||
|
|
}
|
|||
|
|
Ok(())
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**テスト**:
|
|||
|
|
- `json_query_vm` - パラメータ参照が正しいか
|
|||
|
|
- `param_register_protection_vm` - パラメータレジスタ保護を確認
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### P1: メソッド降下の不安定性
|
|||
|
|
|
|||
|
|
**症状**:
|
|||
|
|
- `s.substring(j, j+1)` が origin 推論失敗時に BoxCall/Extern で揺れる
|
|||
|
|
|
|||
|
|
**根本原因**:
|
|||
|
|
- receiver の origin 情報が不明 → `infer_receiver()` が "UnknownBox" を返す
|
|||
|
|
- **重要**: Extern 正規化は既に完全実装済み(Task 4確認)、問題は origin 推論の失敗
|
|||
|
|
|
|||
|
|
**修正内容**:
|
|||
|
|
```rust
|
|||
|
|
// src/mir/builder/origin/propagation.rs(新規)
|
|||
|
|
pub struct OriginPropagationBox;
|
|||
|
|
|
|||
|
|
impl OriginPropagationBox {
|
|||
|
|
/// メソッド呼び出し結果に origin を伝播
|
|||
|
|
pub fn propagate_method_result(
|
|||
|
|
builder: &mut MirBuilder,
|
|||
|
|
receiver_origin: Option<&str>,
|
|||
|
|
method: &str,
|
|||
|
|
result: ValueId,
|
|||
|
|
) -> Result<(), String> {
|
|||
|
|
match (receiver_origin, method) {
|
|||
|
|
(Some("StringBox"), "substring" | "indexOf" | "lastIndexOf") => {
|
|||
|
|
// String → String
|
|||
|
|
builder.origin_tracker.set_origin(result, "StringBox");
|
|||
|
|
}
|
|||
|
|
(Some("ArrayBox"), "slice") => {
|
|||
|
|
// Array → Array
|
|||
|
|
builder.origin_tracker.set_origin(result, "ArrayBox");
|
|||
|
|
}
|
|||
|
|
(Some("MapBox"), "keys" | "values") => {
|
|||
|
|
// Map → Array
|
|||
|
|
builder.origin_tracker.set_origin(result, "ArrayBox");
|
|||
|
|
}
|
|||
|
|
_ => {}
|
|||
|
|
}
|
|||
|
|
Ok(())
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**テスト**:
|
|||
|
|
- `method_origin_propagation_vm` - substring の origin が正しく伝播
|
|||
|
|
- `method_lowering_stability_vm` - Extern正規化が安定
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### P2: variable_map の ValueId 衝突
|
|||
|
|
|
|||
|
|
**症状**:
|
|||
|
|
- メソッド呼び出し結果 dst が既存ループ変数と同じ ValueId を割り当て
|
|||
|
|
|
|||
|
|
**根本原因**:
|
|||
|
|
- `value_gen.next()` が既存の variable_map を考慮しない
|
|||
|
|
- ensure() 修正でカバー済み: receiver/arg/cond ✅
|
|||
|
|
- 未カバー: メソッド結果、BinOp結果、Assignment RHS ❌
|
|||
|
|
|
|||
|
|
**修正内容**:
|
|||
|
|
```rust
|
|||
|
|
// src/mir/builder/value_allocator_box.rs(新規)
|
|||
|
|
pub struct ValueIdAllocatorBox {
|
|||
|
|
next_id: usize,
|
|||
|
|
param_guard: ParameterGuardBox,
|
|||
|
|
in_use: HashSet<ValueId>,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl ValueIdAllocatorBox {
|
|||
|
|
pub fn allocate_safe(&mut self) -> ValueId {
|
|||
|
|
loop {
|
|||
|
|
let candidate = ValueId::new(self.next_id);
|
|||
|
|
self.next_id += 1;
|
|||
|
|
|
|||
|
|
// 3重チェック
|
|||
|
|
if !self.param_guard.is_parameter(candidate)
|
|||
|
|
&& !self.in_use.contains(&candidate) {
|
|||
|
|
return candidate;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
pub fn sync_in_use(&mut self, variable_map: &HashMap<String, ValueId>) {
|
|||
|
|
self.in_use = variable_map.values().copied().collect();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**テスト**:
|
|||
|
|
- `valueid_collision_avoidance_vm` - ValueId衝突が発生しない
|
|||
|
|
- `method_result_allocation_vm` - メソッド結果が安全に割り当て
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🏗️ アーキテクチャ設計(統合版)
|
|||
|
|
|
|||
|
|
### 全体構成
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────────────┐
|
|||
|
|
│ MIR Builder │
|
|||
|
|
├─────────────────────────────────────────────────────┤
|
|||
|
|
│ │
|
|||
|
|
│ ┌────────────────────┐ ┌──────────────────────┐ │
|
|||
|
|
│ │ ParameterGuardBox │ │ ValueIdAllocatorBox │ │
|
|||
|
|
│ │ (P0修正) │ │ (P2修正) │ │
|
|||
|
|
│ │ - v%0-v%N保護 │ │ - 衝突回避 │ │
|
|||
|
|
│ └────────────────────┘ └──────────────────────┘ │
|
|||
|
|
│ ↑ ↑ │
|
|||
|
|
│ │ │ │
|
|||
|
|
│ ┌────────────────────┐ ┌──────────────────────┐ │
|
|||
|
|
│ │OriginPropagation │ │ LoopFormBox │ │
|
|||
|
|
│ │ Box (P1修正) │ │ (構造保証) │ │
|
|||
|
|
│ │ - origin伝播 │ │ - Header正規化 │ │
|
|||
|
|
│ └────────────────────┘ │ - ⭐ safe_next_value()│ │
|
|||
|
|
│ └──────────────────────┘ │
|
|||
|
|
│ │
|
|||
|
|
│ ┌──────────────────────────────────────────────┐ │
|
|||
|
|
│ │ LoopFormVerifierBox │ │
|
|||
|
|
│ │ (構造強制) │ │
|
|||
|
|
│ │ - PHI位置検証 │ │
|
|||
|
|
│ │ - Header構造検証 │ │
|
|||
|
|
│ └──────────────────────────────────────────────┘ │
|
|||
|
|
└─────────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### ⭐ ValueIdAllocatorBox + LoopFormBox 統合戦略
|
|||
|
|
|
|||
|
|
**核心アイデア**: 2つのBoxの**相補的関係**を活用
|
|||
|
|
|
|||
|
|
**ValueIdAllocatorBox**: **経路の正規化** (Path Normalization)
|
|||
|
|
- すべてのValueId割り当てを1点に集約
|
|||
|
|
- 衝突回避(params, variable_map, value_types, local_ssa_map)
|
|||
|
|
- 117箇所の `value_gen.next()` 呼び出しを統一
|
|||
|
|
|
|||
|
|
**LoopFormBox**: **構造の正規化** (Structure Normalization)
|
|||
|
|
- PHI配置ルールを構造的に強制
|
|||
|
|
- Header = PHI + Branch のみ
|
|||
|
|
- 条件式を別ブロックで構築(副作用隔離)
|
|||
|
|
|
|||
|
|
**統合のメリット**:
|
|||
|
|
1. **二重の保証**: 経路(ValueIdAllocatorBox)+ 構造(LoopFormBox)
|
|||
|
|
2. **SSA違反の理論的防止**: PHI生成時に `safe_next_value()` 使用 → 衝突不可能
|
|||
|
|
3. **段階的実装**: Phase 2でValueIdAllocatorBox → LoopFormBoxがそれを利用
|
|||
|
|
|
|||
|
|
**実装例**:
|
|||
|
|
```rust
|
|||
|
|
// LoopFormBox::create_header() 内
|
|||
|
|
let phi_value = builder.safe_next_value(); // ← ValueIdAllocatorBox経由
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**関連ドキュメント**:
|
|||
|
|
- [ValueId割り当て経路分析](../../../analysis/valueid-allocation-paths-analysis.md)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 1. LoopFormBox(構造保証)
|
|||
|
|
|
|||
|
|
**責務**: ループ構造を正規化し、PHIバグを構造的に防止
|
|||
|
|
|
|||
|
|
**設計原則**:
|
|||
|
|
1. **スコープ境界強制**: Header で変数束縛を禁止
|
|||
|
|
2. **副作用隔離**: 条件式は別ブロックで構築
|
|||
|
|
3. **構造検証**: Verifier で構造違反を即座検出
|
|||
|
|
|
|||
|
|
#### 1.1 データ構造
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
// src/mir/loop_builder/loopform_box.rs
|
|||
|
|
|
|||
|
|
/// LoopForm構造の保証Box
|
|||
|
|
pub struct LoopFormBox {
|
|||
|
|
/// Builder参照(emit用)
|
|||
|
|
builder: *mut MirBuilder, // 循環参照回避のため raw pointer
|
|||
|
|
|
|||
|
|
/// Header block ID
|
|||
|
|
header_bb: BlockId,
|
|||
|
|
|
|||
|
|
/// Condition block ID(副作用隔離)
|
|||
|
|
condition_bb: Option<BlockId>,
|
|||
|
|
|
|||
|
|
/// PHI nodes(搬送変数のみ)
|
|||
|
|
phi_nodes: Vec<PhiNode>,
|
|||
|
|
|
|||
|
|
/// 搬送変数(キャリア)
|
|||
|
|
carrier_vars: HashSet<String>,
|
|||
|
|
|
|||
|
|
/// Pin slots(一時値)
|
|||
|
|
pin_slots: Vec<ValueId>,
|
|||
|
|
|
|||
|
|
/// Preheader block ID
|
|||
|
|
preheader_bb: BlockId,
|
|||
|
|
|
|||
|
|
/// Latch block ID
|
|||
|
|
latch_bb: Option<BlockId>,
|
|||
|
|
|
|||
|
|
/// Exit block ID
|
|||
|
|
exit_bb: Option<BlockId>,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// PHI node descriptor
|
|||
|
|
#[derive(Debug, Clone)]
|
|||
|
|
pub struct PhiNode {
|
|||
|
|
pub var_name: String,
|
|||
|
|
pub phi_value: ValueId,
|
|||
|
|
pub preheader_input: ValueId,
|
|||
|
|
pub latch_input: Option<ValueId>, // Latch未確定時はNone
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// LoopForm構造(出力)
|
|||
|
|
pub struct LoopStructure {
|
|||
|
|
pub header_bb: BlockId,
|
|||
|
|
pub condition_bb: Option<BlockId>,
|
|||
|
|
pub body_bb: BlockId,
|
|||
|
|
pub latch_bb: BlockId,
|
|||
|
|
pub exit_bb: BlockId,
|
|||
|
|
pub phi_nodes: Vec<PhiNode>,
|
|||
|
|
pub carrier_vars: HashSet<String>,
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 1.2 主要メソッド
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
impl LoopFormBox {
|
|||
|
|
/// LoopFormBox作成
|
|||
|
|
pub fn new(builder: &mut MirBuilder) -> Self {
|
|||
|
|
Self {
|
|||
|
|
builder: builder as *mut _,
|
|||
|
|
header_bb: BlockId::invalid(),
|
|||
|
|
condition_bb: None,
|
|||
|
|
phi_nodes: Vec::new(),
|
|||
|
|
carrier_vars: HashSet::new(),
|
|||
|
|
pin_slots: Vec::new(),
|
|||
|
|
preheader_bb: BlockId::invalid(),
|
|||
|
|
latch_bb: None,
|
|||
|
|
exit_bb: None,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// ループ構造構築(メインエントリーポイント)
|
|||
|
|
pub fn build_loop(
|
|||
|
|
&mut self,
|
|||
|
|
condition: &ast::Expr,
|
|||
|
|
preheader_vars: &HashMap<String, ValueId>,
|
|||
|
|
body: &[ast::Statement],
|
|||
|
|
) -> Result<LoopStructure, String> {
|
|||
|
|
// Step 1: 搬送変数特定
|
|||
|
|
let carriers = self.identify_carriers(preheader_vars, body)?;
|
|||
|
|
|
|||
|
|
// Step 2: Header block作成(PHIのみ)
|
|||
|
|
let header_bb = self.create_header(carriers)?;
|
|||
|
|
|
|||
|
|
// Step 3: Condition block作成(副作用隔離)
|
|||
|
|
let cond_bb = self.create_condition_block(condition)?;
|
|||
|
|
|
|||
|
|
// Step 4: Body/Latch/Exit作成
|
|||
|
|
let (body_bb, latch_bb, exit_bb) = self.create_body_latch_exit(body)?;
|
|||
|
|
|
|||
|
|
// Step 5: PHI入力更新(Latch確定後)
|
|||
|
|
self.update_phi_inputs(latch_bb)?;
|
|||
|
|
|
|||
|
|
// Step 6: 構造検証
|
|||
|
|
self.verify_structure()?;
|
|||
|
|
|
|||
|
|
Ok(LoopStructure {
|
|||
|
|
header_bb,
|
|||
|
|
condition_bb: Some(cond_bb),
|
|||
|
|
body_bb,
|
|||
|
|
latch_bb,
|
|||
|
|
exit_bb,
|
|||
|
|
phi_nodes: self.phi_nodes.clone(),
|
|||
|
|
carrier_vars: self.carrier_vars.clone(),
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 搬送変数特定(preheader定義 ∩ body代入)
|
|||
|
|
fn identify_carriers(
|
|||
|
|
&mut self,
|
|||
|
|
preheader_vars: &HashMap<String, ValueId>,
|
|||
|
|
body: &[ast::Statement],
|
|||
|
|
) -> Result<HashSet<String>, String> {
|
|||
|
|
// 既存のLoopCarrierAnalyzerBoxを使用
|
|||
|
|
let analyzer = LoopCarrierAnalyzerBox::new();
|
|||
|
|
let assigned = analyzer.collect_assigned_vars(body)?;
|
|||
|
|
|
|||
|
|
let carriers: HashSet<String> = preheader_vars
|
|||
|
|
.keys()
|
|||
|
|
.filter(|k| assigned.contains(*k))
|
|||
|
|
.cloned()
|
|||
|
|
.collect();
|
|||
|
|
|
|||
|
|
self.carrier_vars = carriers.clone();
|
|||
|
|
Ok(carriers)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Header block作成(PHI + Branch のみ)
|
|||
|
|
fn create_header(&mut self, carriers: HashSet<String>) -> Result<BlockId, String> {
|
|||
|
|
let builder = unsafe { &mut *self.builder };
|
|||
|
|
|
|||
|
|
// Header block作成
|
|||
|
|
let header_bb = builder.create_block()?;
|
|||
|
|
self.header_bb = header_bb;
|
|||
|
|
builder.set_current_block(header_bb);
|
|||
|
|
|
|||
|
|
// 搬送変数ごとにPHI作成
|
|||
|
|
for var_name in &carriers {
|
|||
|
|
let preheader_value = builder.variable_map.get(var_name)
|
|||
|
|
.ok_or_else(|| format!("Carrier variable '{}' not found in preheader", var_name))?;
|
|||
|
|
|
|||
|
|
// ⭐ ValueIdAllocatorBox統合: 衝突回避のため safe_next_value() を使用
|
|||
|
|
// Phase 2.P2修正: builder.value_gen.next() → builder.safe_next_value()
|
|||
|
|
let phi_value = builder.safe_next_value();
|
|||
|
|
|
|||
|
|
// PHI emit(Latch入力はNoneで仮作成)
|
|||
|
|
builder.emit_instruction(MirInstruction::Phi {
|
|||
|
|
dst: phi_value,
|
|||
|
|
inputs: vec![(*preheader_value, self.preheader_bb)],
|
|||
|
|
})?;
|
|||
|
|
|
|||
|
|
// PHI node記録
|
|||
|
|
self.phi_nodes.push(PhiNode {
|
|||
|
|
var_name: var_name.clone(),
|
|||
|
|
phi_value,
|
|||
|
|
preheader_input: *preheader_value,
|
|||
|
|
latch_input: None,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// variable_map更新(Header内ではPHI値を使用)
|
|||
|
|
builder.variable_map.insert(var_name.clone(), phi_value);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Ok(header_bb)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Condition block作成(副作用隔離)
|
|||
|
|
fn create_condition_block(&mut self, condition: &ast::Expr) -> Result<BlockId, String> {
|
|||
|
|
let builder = unsafe { &mut *self.builder };
|
|||
|
|
|
|||
|
|
// Condition block作成
|
|||
|
|
let cond_bb = builder.create_block()?;
|
|||
|
|
self.condition_bb = Some(cond_bb);
|
|||
|
|
builder.set_current_block(cond_bb);
|
|||
|
|
|
|||
|
|
// 🔥 重要: variable_map スナップショット(副作用隔離)
|
|||
|
|
let saved_map = builder.variable_map.clone();
|
|||
|
|
|
|||
|
|
// 条件式構築(pin スロットに格納)
|
|||
|
|
let cond_value = builder.build_expression(condition)?;
|
|||
|
|
|
|||
|
|
// Pin slot記録
|
|||
|
|
self.pin_slots.push(cond_value);
|
|||
|
|
|
|||
|
|
// 🔥 重要: variable_map リストア(副作用破棄)
|
|||
|
|
builder.variable_map = saved_map;
|
|||
|
|
|
|||
|
|
// Branch emit(Header → Condition → Body/Exit)
|
|||
|
|
builder.set_current_block(self.header_bb);
|
|||
|
|
builder.emit_instruction(MirInstruction::Branch {
|
|||
|
|
condition: cond_value,
|
|||
|
|
true_target: BlockId::invalid(), // Body(後で設定)
|
|||
|
|
false_target: BlockId::invalid(), // Exit(後で設定)
|
|||
|
|
})?;
|
|||
|
|
|
|||
|
|
Ok(cond_bb)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// PHI入力更新(Latch確定後)
|
|||
|
|
fn update_phi_inputs(&mut self, latch_bb: BlockId) -> Result<(), String> {
|
|||
|
|
let builder = unsafe { &mut *self.builder };
|
|||
|
|
|
|||
|
|
for phi_node in &mut self.phi_nodes {
|
|||
|
|
// Latch blockでの変数値を取得
|
|||
|
|
let latch_value = builder.variable_map.get(&phi_node.var_name)
|
|||
|
|
.ok_or_else(|| format!("Carrier '{}' not found in latch", phi_node.var_name))?;
|
|||
|
|
|
|||
|
|
phi_node.latch_input = Some(*latch_value);
|
|||
|
|
|
|||
|
|
// PHI命令更新
|
|||
|
|
builder.set_current_block(self.header_bb);
|
|||
|
|
// 既存PHIを検索して更新(実装省略)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Ok(())
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 構造検証(LoopFormVerifierBox呼び出し)
|
|||
|
|
fn verify_structure(&self) -> Result<(), String> {
|
|||
|
|
let builder = unsafe { &*self.builder };
|
|||
|
|
let header_block = builder.get_block(self.header_bb)?;
|
|||
|
|
|
|||
|
|
LoopFormVerifierBox::verify_loop_header(header_block)?;
|
|||
|
|
Ok(())
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2. LoopFormVerifierBox(構造強制)
|
|||
|
|
|
|||
|
|
**責務**: LoopForm構造ルールを強制
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
// src/mir/verification/loopform.rs
|
|||
|
|
|
|||
|
|
pub struct LoopFormVerifierBox;
|
|||
|
|
|
|||
|
|
impl LoopFormVerifierBox {
|
|||
|
|
/// Loop Header構造検証
|
|||
|
|
pub fn verify_loop_header(block: &MirBlock) -> Result<(), String> {
|
|||
|
|
// Rule 1: PHI はブロック先頭のみ
|
|||
|
|
Self::verify_phi_at_start(block)?;
|
|||
|
|
|
|||
|
|
// Rule 2: Header = PHI + Branch のみ
|
|||
|
|
Self::verify_header_minimal(block)?;
|
|||
|
|
|
|||
|
|
// Rule 3: PHI以外の命令でvariable_map更新禁止
|
|||
|
|
Self::verify_no_rebinding(block)?;
|
|||
|
|
|
|||
|
|
Ok(())
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Rule 1: PHI はブロック先頭のみ
|
|||
|
|
fn verify_phi_at_start(block: &MirBlock) -> Result<(), String> {
|
|||
|
|
let mut phi_section = true;
|
|||
|
|
|
|||
|
|
for (i, inst) in block.instructions.iter().enumerate() {
|
|||
|
|
match inst {
|
|||
|
|
MirInstruction::Phi{..} => {
|
|||
|
|
if !phi_section {
|
|||
|
|
return Err(format!(
|
|||
|
|
"PHI instruction at position {} not at block start (block: bb{})",
|
|||
|
|
i, block.id
|
|||
|
|
));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_ => {
|
|||
|
|
phi_section = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Ok(())
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Rule 2: Header = PHI + Branch のみ
|
|||
|
|
fn verify_header_minimal(block: &MirBlock) -> Result<(), String> {
|
|||
|
|
let non_phi_instructions: Vec<_> = block.instructions.iter()
|
|||
|
|
.filter(|i| !matches!(i, MirInstruction::Phi{..}))
|
|||
|
|
.collect();
|
|||
|
|
|
|||
|
|
if non_phi_instructions.len() > 1 {
|
|||
|
|
return Err(format!(
|
|||
|
|
"Loop header bb{} has {} non-PHI instructions (expected 1 Branch only)",
|
|||
|
|
block.id, non_phi_instructions.len()
|
|||
|
|
));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 最後の命令がBranchであること確認
|
|||
|
|
if let Some(last) = non_phi_instructions.last() {
|
|||
|
|
if !matches!(last, MirInstruction::Branch{..}) {
|
|||
|
|
return Err(format!(
|
|||
|
|
"Loop header bb{} must end with Branch (found: {:?})",
|
|||
|
|
block.id, last
|
|||
|
|
));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Ok(())
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Rule 3: PHI以外の命令でvariable_map更新禁止
|
|||
|
|
fn verify_no_rebinding(block: &MirBlock) -> Result<(), String> {
|
|||
|
|
// この検証は実行時に行う(静的には困難)
|
|||
|
|
// 環境変数 HAKO_LOOPFORM_TRACE=1 でトレース可能
|
|||
|
|
Ok(())
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📅 実装ステップ(週次計画、統合版)
|
|||
|
|
|
|||
|
|
### Day 1(今日、4時間): P0修正(緊急パッチ)+ P2基盤
|
|||
|
|
|
|||
|
|
**午前(2時間): P0修正**
|
|||
|
|
1. ParameterGuardBox強化
|
|||
|
|
- 場所: `src/mir/builder/guards/parameter_guard.rs` (既存)
|
|||
|
|
- 修正: VarTracker に統合、v%(N+1) からローカル変数開始
|
|||
|
|
- ファイル: `src/mir/builder/var_tracker.rs` 新規メソッド追加
|
|||
|
|
|
|||
|
|
2. ビルド&テスト
|
|||
|
|
- `json_query_vm` 復活確認 ✅
|
|||
|
|
|
|||
|
|
**午後(2時間): P2基盤実装**
|
|||
|
|
3. ValueIdAllocatorBox骨格作成
|
|||
|
|
- 場所: `src/mir/builder/value_allocator_box.rs` (新規)
|
|||
|
|
- データ構造定義
|
|||
|
|
- `new()`, `allocate_safe()`, `sync_in_use()` 実装
|
|||
|
|
|
|||
|
|
4. 初回テスト
|
|||
|
|
- 最小ケースでValueId衝突回避を確認
|
|||
|
|
|
|||
|
|
**成果物**:
|
|||
|
|
- ✅ json_query_vm PASS(P0修正)
|
|||
|
|
- ✅ ValueIdAllocatorBox基盤完成(P2)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Day 2(明日、8時間): P1修正 + P2完成 + LoopFormBox骨格
|
|||
|
|
|
|||
|
|
**午前(4時間): P1修正**
|
|||
|
|
1. OriginPropagationBox実装
|
|||
|
|
- 場所: `src/mir/builder/origin/propagation.rs` (新規)
|
|||
|
|
- `propagate_method_result()` 実装
|
|||
|
|
- substring/slice/keys/values の origin 伝播
|
|||
|
|
|
|||
|
|
2. MirBuilder統合
|
|||
|
|
- `build_method_call()` 内で `OriginPropagationBox` 呼び出し
|
|||
|
|
- テスト: `method_origin_propagation_vm` 作成・実行
|
|||
|
|
|
|||
|
|
**午後(4時間): P2完成 + LoopFormBox骨格**
|
|||
|
|
3. ValueIdAllocatorBox完成
|
|||
|
|
- MirBuilder統合(`safe_next_value()` メソッド追加)
|
|||
|
|
- フラグ制御(`HAKO_USE_VALUE_ALLOCATOR_BOX=1`)
|
|||
|
|
- テスト: `valueid_collision_avoidance_vm` 作成・実行
|
|||
|
|
|
|||
|
|
4. LoopFormBox骨格作成
|
|||
|
|
- 場所: `src/mir/loop_builder/loopform_box.rs` (新規)
|
|||
|
|
- データ構造定義(LoopFormBox, PhiNode, LoopStructure)
|
|||
|
|
- `new()`, `identify_carriers()` 実装
|
|||
|
|
|
|||
|
|
**成果物**:
|
|||
|
|
- ✅ P1修正完了(origin伝播)
|
|||
|
|
- ✅ P2修正完了(ValueId衝突回避)
|
|||
|
|
- ✅ LoopFormBox骨格
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Day 3(2日後、8時間): LoopFormBox実装
|
|||
|
|
|
|||
|
|
**午前(4時間)**:
|
|||
|
|
1. Header block作成
|
|||
|
|
- `create_header()` 実装
|
|||
|
|
- PHI作成ロジック
|
|||
|
|
- variable_map更新
|
|||
|
|
|
|||
|
|
2. Condition block作成
|
|||
|
|
- `create_condition_block()` 実装
|
|||
|
|
- variable_map スナップショット/リストア
|
|||
|
|
- pin スロット管理
|
|||
|
|
|
|||
|
|
**午後(4時間)**:
|
|||
|
|
3. Body/Latch/Exit作成
|
|||
|
|
- `create_body_latch_exit()` 実装
|
|||
|
|
- PHI入力更新(`update_phi_inputs()`)
|
|||
|
|
|
|||
|
|
4. 初回テスト
|
|||
|
|
- 最小ループ(単一搬送変数)で動作確認
|
|||
|
|
- MIRダンプで構造確認
|
|||
|
|
|
|||
|
|
**成果物**:
|
|||
|
|
- ✅ LoopFormBox基本実装完了
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Day 4(3日後、8時間): LoopFormVerifierBox + 統合
|
|||
|
|
|
|||
|
|
**午前(4時間)**:
|
|||
|
|
1. LoopFormVerifierBox実装
|
|||
|
|
- 場所: `src/mir/verification/loopform.rs` (新規)
|
|||
|
|
- Rule 1: PHI位置検証
|
|||
|
|
- Rule 2: Header構造検証
|
|||
|
|
- Rule 3: 変数束縛禁止検証
|
|||
|
|
|
|||
|
|
2. Verifierテスト
|
|||
|
|
- 構造違反を検出できるか確認
|
|||
|
|
- エラーメッセージの分かりやすさ確認
|
|||
|
|
|
|||
|
|
**午後(4時間)**:
|
|||
|
|
3. MirBuilder統合
|
|||
|
|
- 場所: `src/mir/builder.rs` 修正
|
|||
|
|
- 環境変数フラグ(`HAKO_USE_LOOPFORM_BOX=1`)
|
|||
|
|
- Fallback実装(フラグ無効時は既存動作)
|
|||
|
|
|
|||
|
|
4. 統合テスト
|
|||
|
|
- quick profile で動作確認
|
|||
|
|
- P0/P1/P2 すべての修正が有効か確認
|
|||
|
|
|
|||
|
|
**成果物**:
|
|||
|
|
- ✅ LoopFormVerifierBox完成
|
|||
|
|
- ✅ MirBuilder統合完了
|
|||
|
|
- ✅ 環境変数フラグで切り替え可能
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Day 5(4日後、8時間): テスト&ドキュメント
|
|||
|
|
|
|||
|
|
**午前(4時間)**:
|
|||
|
|
1. 回帰テスト作成(30個)
|
|||
|
|
- **P0テスト(10個)**: パラメータレジスタ保護
|
|||
|
|
- `param_register_protection_vm` - 基本保護
|
|||
|
|
- `json_query_vm` - 実戦ケース
|
|||
|
|
- `param_overwrite_detection_vm` - 上書き検出
|
|||
|
|
- **P1テスト(5個)**: origin伝播
|
|||
|
|
- `method_origin_propagation_vm` - substring/slice/keys/values
|
|||
|
|
- `method_lowering_stability_vm` - Extern正規化安定性
|
|||
|
|
- **P2テスト(5個)**: ValueId衝突回避
|
|||
|
|
- `valueid_collision_avoidance_vm` - 基本衝突回避
|
|||
|
|
- `method_result_allocation_vm` - メソッド結果安全割り当て
|
|||
|
|
- **LoopFormテスト(10個)**: 構造保証
|
|||
|
|
- 単一搬送変数(i, acc)
|
|||
|
|
- 複数搬送変数(i, j, sum)
|
|||
|
|
- 条件式複雑化(&&, ||, 短絡評価)
|
|||
|
|
- メソッド呼び出し(s.substring)
|
|||
|
|
- continue/break、入れ子ループ
|
|||
|
|
|
|||
|
|
2. エラーケーステスト(10個)
|
|||
|
|
- 構造違反検出(PHI位置違反)
|
|||
|
|
- variable_map汚染検出
|
|||
|
|
- 一時変数の過大PHI化
|
|||
|
|
|
|||
|
|
**午後(4時間)**:
|
|||
|
|
3. スモークテスト統合
|
|||
|
|
- quick profile でLoopFormBox有効化
|
|||
|
|
- 全スモーク再実行(170テスト)
|
|||
|
|
- 差分確認
|
|||
|
|
|
|||
|
|
4. ドキュメント作成
|
|||
|
|
- 使用ガイド作成
|
|||
|
|
- トラブルシューティングガイド作成
|
|||
|
|
- CURRENT_TASK.md更新
|
|||
|
|
|
|||
|
|
**成果物**:
|
|||
|
|
- ✅ 30個のユニットテスト PASS
|
|||
|
|
- ✅ 170個のスモークテスト PASS
|
|||
|
|
- ✅ ドキュメント完成
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🧪 テスト戦略(統合版)
|
|||
|
|
|
|||
|
|
### 1. ユニットテスト(40個)
|
|||
|
|
|
|||
|
|
#### カテゴリA: P0 - パラメータレジスタ保護(10個)
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
// tests/parameter_guard_unit.rs
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_param_register_protection_basic() {
|
|||
|
|
// function(p1, p2) で v%0=me, v%1=p1, v%2=p2
|
|||
|
|
// local i = 0 は v%3 から開始
|
|||
|
|
// v%0-v%2 への代入は禁止
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_param_overwrite_detection() {
|
|||
|
|
// ParameterGuardBox が v%0-v%N への代入を検出
|
|||
|
|
// Fail-Fast エラー発生
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_json_query_skip_ws() {
|
|||
|
|
// 実戦ケース: skip_ws(s, i, end)
|
|||
|
|
// ループ内メソッド呼び出しでパラメータ保護
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### カテゴリB: P1 - origin伝播(5個)
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
// tests/origin_propagation_unit.rs
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_substring_origin_propagation() {
|
|||
|
|
// local s = "hello"
|
|||
|
|
// local sub = s.substring(0, 3)
|
|||
|
|
// origin(sub) == "StringBox"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_map_keys_values_origin() {
|
|||
|
|
// local map = new MapBox()
|
|||
|
|
// local keys = map.keys()
|
|||
|
|
// origin(keys) == "ArrayBox"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### カテゴリC: P2 - ValueId衝突回避(5個)
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
// tests/valueid_allocator_unit.rs
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_valueid_collision_avoidance() {
|
|||
|
|
// local i = 0
|
|||
|
|
// loop(i < 10) {
|
|||
|
|
// local temp = arr.get(i) // temp は v%i と異なる
|
|||
|
|
// i = i + 1
|
|||
|
|
// }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_method_result_safe_allocation() {
|
|||
|
|
// メソッド呼び出し結果が既存変数と衝突しない
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### カテゴリD: LoopForm - 基本ループ(10個)
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
// tests/loopform_unit.rs
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_single_carrier_loop() {
|
|||
|
|
// local i = 0
|
|||
|
|
// loop(i < 10) { i = i + 1 }
|
|||
|
|
// 搬送変数: i のみ
|
|||
|
|
// Header: PHI(i) + Branch のみ
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_double_carrier_loop() {
|
|||
|
|
// local i = 0, local acc = 0
|
|||
|
|
// loop(i < 10) { acc = acc + i; i = i + 1 }
|
|||
|
|
// 搬送変数: i, acc
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_loop_with_non_carrier() {
|
|||
|
|
// local i = 0
|
|||
|
|
// loop(i < 10) { local temp = i * 2; i = i + 1 }
|
|||
|
|
// 搬送変数: i のみ(temp は一時変数)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### カテゴリB: 条件式複雑化(5個)
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
#[test]
|
|||
|
|
fn test_loop_condition_short_circuit() {
|
|||
|
|
// loop(i < end && is_valid(arr.get(i))) { ... }
|
|||
|
|
// Condition block で短絡評価処理
|
|||
|
|
// variable_map汚染なし確認
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_loop_condition_method_call() {
|
|||
|
|
// loop(i < s.size()) { ... }
|
|||
|
|
// Condition block で s.size() 呼び出し
|
|||
|
|
// variable_map汚染なし確認
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### カテゴリC: 制御フロー(5個)
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
#[test]
|
|||
|
|
fn test_loop_with_break() {
|
|||
|
|
// loop(i < 10) { if condition { break }; i = i + 1 }
|
|||
|
|
// Exit block への分岐確認
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_loop_with_continue() {
|
|||
|
|
// loop(i < 10) { if condition { continue }; i = i + 1 }
|
|||
|
|
// Latch block への分岐確認
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_nested_loop() {
|
|||
|
|
// loop(i < 10) { loop(j < 5) { ... } }
|
|||
|
|
// 各ループのHeader独立性確認
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### カテゴリD: エラー検出(5個)
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
#[test]
|
|||
|
|
fn test_verify_phi_at_start_violation() {
|
|||
|
|
// 手動でPHI位置違反を作成
|
|||
|
|
// LoopFormVerifierBox がエラー検出
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#[test]
|
|||
|
|
fn test_verify_header_minimal_violation() {
|
|||
|
|
// Header に PHI + Branch 以外の命令を挿入
|
|||
|
|
// LoopFormVerifierBox がエラー検出
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2. 統合テスト(スモークテスト)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# quick profile で LoopFormBox 有効化
|
|||
|
|
HAKO_USE_LOOPFORM_BOX=1 tools/smokes/v2/run.sh --profile quick
|
|||
|
|
|
|||
|
|
# 期待: 170 PASS / 0 FAIL
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 3. 回帰テスト(既存バグの再発防止)
|
|||
|
|
|
|||
|
|
| テストケース | 検証内容 | 関連バグ |
|
|||
|
|
|------------|---------|---------|
|
|||
|
|
| `json_query_vm` | パラメータレジスタ上書きなし | 2025-10-17 |
|
|||
|
|
| `loop_carrier_overphiification_vm` | 一時変数の過大PHI化なし | 2025-10-18 |
|
|||
|
|
| `loop_condition_var_map_pollution_vm` | 条件式による変数マップ汚染なし | 2025-10-16 |
|
|||
|
|
| `loop_localssa_collision_vm` | LocalSSA衝突なし | 2025-10-16 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🚨 リスク分析
|
|||
|
|
|
|||
|
|
### リスク1: 既存コードとの互換性 ⚠️⚠️
|
|||
|
|
|
|||
|
|
**リスク**: LoopFormBox 有効化で既存テストが失敗
|
|||
|
|
|
|||
|
|
**軽減策**:
|
|||
|
|
- 環境変数フラグで段階的導入(`HAKO_USE_LOOPFORM_BOX=1`)
|
|||
|
|
- Fallback実装(フラグ無効時は既存動作)
|
|||
|
|
- quick → plugins → full の段階的スモークテスト
|
|||
|
|
|
|||
|
|
**Contingency Plan**:
|
|||
|
|
- フラグ無効でロールバック
|
|||
|
|
- 問題箇所を特定して個別修正
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### リスク2: パフォーマンス劣化 ⚠️
|
|||
|
|
|
|||
|
|
**リスク**: Condition block 分離でブロック数増加 → 最適化パフォーマンス低下
|
|||
|
|
|
|||
|
|
**軽減策**:
|
|||
|
|
- ベンチマーク測定(`bench_unified.sh`)
|
|||
|
|
- 許容範囲: 5%以内の劣化
|
|||
|
|
|
|||
|
|
**Contingency Plan**:
|
|||
|
|
- Optimizer Pass で Condition block をインライン化
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### リスク3: 実装バグ ⚠️⚠️⚠️
|
|||
|
|
|
|||
|
|
**リスク**: LoopFormBox 自体にバグ → 新しい問題発生
|
|||
|
|
|
|||
|
|
**軽減策**:
|
|||
|
|
- 20個のユニットテスト
|
|||
|
|
- LoopFormVerifierBox で構造検証
|
|||
|
|
- 段階的導入(quick → plugins → full)
|
|||
|
|
|
|||
|
|
**Contingency Plan**:
|
|||
|
|
- フラグ無効でロールバック
|
|||
|
|
- Task先生調査 → バグ修正
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔄 Fallback戦略
|
|||
|
|
|
|||
|
|
### フラグ制御
|
|||
|
|
|
|||
|
|
```rust
|
|||
|
|
// src/mir/builder.rs
|
|||
|
|
|
|||
|
|
impl MirBuilder {
|
|||
|
|
fn build_loop(&mut self, ...) -> Result<(), String> {
|
|||
|
|
// 環境変数チェック
|
|||
|
|
if crate::runtime::env_gate_box::bool_any(&[
|
|||
|
|
"HAKO_USE_LOOPFORM_BOX",
|
|||
|
|
"NYASH_USE_LOOPFORM_BOX",
|
|||
|
|
]) {
|
|||
|
|
// LoopFormBox経由(新実装)
|
|||
|
|
if let Some(ref mut loopform) = self.loopform {
|
|||
|
|
return loopform.build_loop(...);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Fallback: 既存動作
|
|||
|
|
self.build_loop_legacy(...)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### ロールバック手順
|
|||
|
|
|
|||
|
|
1. **即座の無効化**:
|
|||
|
|
```bash
|
|||
|
|
export HAKO_USE_LOOPFORM_BOX=0
|
|||
|
|
cargo build --release
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. **問題箇所の特定**:
|
|||
|
|
```bash
|
|||
|
|
HAKO_TRACE_LOOPFORM=1 ./target/release/hakorune --backend vm <test_case>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
3. **バグ修正 or フラグ維持**:
|
|||
|
|
- バグが小さい → 修正してリトライ
|
|||
|
|
- バグが大きい → フラグ無効のまま Phase 3 へ延期
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ✅ 成功基準(統合版)
|
|||
|
|
|
|||
|
|
### Phase 2 完了の定義
|
|||
|
|
|
|||
|
|
| 項目 | 基準 | 確認方法 |
|
|||
|
|
|-----|------|---------|
|
|||
|
|
| **P0修正** | パラメータレジスタ保護完了 | `json_query_vm` PASS |
|
|||
|
|
| **P1修正** | origin伝播完了 | `method_origin_propagation_vm` PASS |
|
|||
|
|
| **P2修正** | ValueId衝突回避完了 | `valueid_collision_avoidance_vm` PASS |
|
|||
|
|
| **LoopFormBox実装** | 完了 | コードレビュー |
|
|||
|
|
| **LoopFormVerifierBox** | 完了 | 構造違反検出テスト PASS |
|
|||
|
|
| **ユニットテスト** | 40個すべて PASS | `cargo test parameter_guard loopform origin valueid` |
|
|||
|
|
| **統合テスト** | quick profile 170 PASS | `HAKO_USE_LOOPFORM_BOX=1 tools/smokes/v2/run.sh --profile quick` |
|
|||
|
|
| **回帰テスト** | 4つの既存バグすべて再発なし | 個別スモーク実行 |
|
|||
|
|
| **パフォーマンス** | 5%以内の劣化 | `bench_unified.sh` |
|
|||
|
|
| **ドキュメント** | 設計書・使用ガイド・トラブルシューティング完成 | レビュー |
|
|||
|
|
|
|||
|
|
### 各修正の個別成功基準
|
|||
|
|
|
|||
|
|
#### P0: パラメータレジスタ保護
|
|||
|
|
|
|||
|
|
| テスト | 期待結果 |
|
|||
|
|
|-------|---------|
|
|||
|
|
| `json_query_vm` | PASS(パラメータ参照が正しい) |
|
|||
|
|
| `param_register_protection_vm` | PASS(v%0-v%N 保護) |
|
|||
|
|
| `param_overwrite_detection_vm` | FAIL(Fail-Fastエラー検出) |
|
|||
|
|
|
|||
|
|
#### P1: origin伝播
|
|||
|
|
|
|||
|
|
| テスト | 期待結果 |
|
|||
|
|
|-------|---------|
|
|||
|
|
| `method_origin_propagation_vm` | PASS(substring/slice/keys/valuesの origin 伝播) |
|
|||
|
|
| `method_lowering_stability_vm` | PASS(Extern正規化が安定) |
|
|||
|
|
|
|||
|
|
#### P2: ValueId衝突回避
|
|||
|
|
|
|||
|
|
| テスト | 期待結果 |
|
|||
|
|
|-------|---------|
|
|||
|
|
| `valueid_collision_avoidance_vm` | PASS(ValueId衝突なし) |
|
|||
|
|
| `method_result_allocation_vm` | PASS(メソッド結果が安全割り当て) |
|
|||
|
|
|
|||
|
|
#### LoopForm: 構造保証
|
|||
|
|
|
|||
|
|
| テスト | 期待結果 |
|
|||
|
|
|-------|---------|
|
|||
|
|
| LoopFormテスト10個 | すべて PASS |
|
|||
|
|
| LoopFormVerifierテスト10個 | 構造違反を正しく検出 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📚 関連ドキュメント
|
|||
|
|
|
|||
|
|
### 既存ドキュメント
|
|||
|
|
- [ループ変数破損バグ調査](../../issues/loop-variable-corruption-investigation.md) - 背景
|
|||
|
|
- [LoopCarrierAnalyzerBox](../../../mir/loop_builder/carrier_analyzer.rs) - 既存実装
|
|||
|
|
- [Phase 31 INDEX](./phase-31-box-Normalization/INDEX_JA.md) - PHI配置
|
|||
|
|
|
|||
|
|
### 新規作成予定
|
|||
|
|
- LoopFormBox設計書(このドキュメント)
|
|||
|
|
- LoopFormBox使用ガイド(Day 5作成)
|
|||
|
|
- LoopFormBoxトラブルシューティング(Day 5作成)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎯 次のステップ(統合版)
|
|||
|
|
|
|||
|
|
### 今日(Day 1、4時間)
|
|||
|
|
**午前(2時間)**:
|
|||
|
|
1. ✅ ドキュメント作成完了
|
|||
|
|
2. ✅ Commit完了
|
|||
|
|
|
|||
|
|
**午後(2-4時間)**:
|
|||
|
|
3. P0実装開始(パラメータレジスタ保護)
|
|||
|
|
4. P2基盤実装(ValueIdAllocatorBox骨格)
|
|||
|
|
|
|||
|
|
### 明日以降(Day 2-5、32時間)
|
|||
|
|
**Day 2(8時間)**: P1修正 + P2完成 + LoopFormBox骨格
|
|||
|
|
**Day 3(8時間)**: LoopFormBox実装
|
|||
|
|
**Day 4(8時間)**: LoopFormVerifierBox + 統合
|
|||
|
|
**Day 5(8時間)**: テスト&ドキュメント
|
|||
|
|
|
|||
|
|
### 5日後(完了時)
|
|||
|
|
- ✅ P0/P1/P2 すべての修正完了
|
|||
|
|
- ✅ LoopFormBox + Verifier完成
|
|||
|
|
- ✅ 40個のユニットテスト PASS
|
|||
|
|
- ✅ 170個のスモークテスト PASS
|
|||
|
|
- ✅ **PHIバグ完全根治達成**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📊 実装サマリー
|
|||
|
|
|
|||
|
|
| 項目 | 内容 | 所要時間 |
|
|||
|
|
|-----|------|---------|
|
|||
|
|
| **P0修正** | ParameterGuardBox強化 | 2時間 |
|
|||
|
|
| **P1修正** | OriginPropagationBox実装 | 4時間 |
|
|||
|
|
| **P2修正** | ValueIdAllocatorBox実装 | 6時間 |
|
|||
|
|
| **LoopFormBox** | 構造保証Box実装 | 12時間 |
|
|||
|
|
| **Verifier** | LoopFormVerifierBox実装 | 4時間 |
|
|||
|
|
| **テスト** | ユニット40個+スモーク統合 | 8時間 |
|
|||
|
|
| **ドキュメント** | 使用ガイド+トラブルシューティング | 4時間 |
|
|||
|
|
| **合計** | - | **40時間(5日)** |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🏆 期待される効果
|
|||
|
|
|
|||
|
|
### 短期効果(Day 1完了時)
|
|||
|
|
- ✅ `json_query_vm` 復活
|
|||
|
|
- ✅ パラメータレジスタ破壊バグ修正
|
|||
|
|
|
|||
|
|
### 中期効果(Day 3完了時)
|
|||
|
|
- ✅ P0/P1/P2 すべてのバグ修正完了
|
|||
|
|
- ✅ ValueId衝突なし
|
|||
|
|
- ✅ origin推論安定
|
|||
|
|
|
|||
|
|
### 長期効果(Day 5完了時)
|
|||
|
|
- ✅ **PHIバグの完全根治**(構造的保証)
|
|||
|
|
- ✅ 将来のPHIバグを**理論的に防止**
|
|||
|
|
- ✅ Hakoruneスクリプト化準備(Phase 4で移植可能)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**作成日**: 2025-10-17
|
|||
|
|
**最終更新**: 2025-10-17(統合計画作成完了)
|
|||
|
|
**作成者**: Claude Code + ChatGPT協調
|
|||
|
|
**次回更新**: Phase 2 Day 1完了時
|