docs(phi): Phase 25.1 - LoopForm v2 コメント整備 + ケース表完成
- ✅ [LoopForm] タグで統一コメント追加 - src/mir/loop_builder.rs - header-cond: Case A/B分岐説明 - exit-break path / continue-backedge path - exit PHI for Case A/B - src/mir/phi_core/loop_snapshot_merge.rs - Case A/B分岐: header ∈ exit_preds判定ロジック - src/mir/phi_core/exit_phi_builder.rs - LoopForm Process ステップバイステップ説明 - ✅ UsingCollectorBox Region+next_i化 - lang/src/compiler/parser/using/using_collector_box.hako - 全ループをLoopForm v2形式に統一 - next_i, next_j, next_k, next_t パターン導入 - SSA安全化(未定義変数撲滅) - ✅ LoopForm v2 ケース表完成 - docs/development/architecture/loops/loopform_ssot.md - Case A/B/C/D の完全な表 - テスト対応マッピング - 実装ファイル対応表 🎯 成果: LoopForm v2の「形」をソース・テスト・ドキュメントで完全固定
This commit is contained in:
@ -66,31 +66,31 @@ impl ExitPhiBuilder {
|
||||
/// let exit_builder = ExitPhiBuilder::new(body_builder);
|
||||
/// ```
|
||||
pub fn new(body_local_builder: BodyLocalPhiBuilder) -> Self {
|
||||
Self {
|
||||
body_local_builder,
|
||||
}
|
||||
Self { body_local_builder }
|
||||
}
|
||||
|
||||
/// Build Exit PHIs
|
||||
/// [LoopForm] Build Exit PHIs
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `ops` - LoopFormOps trait implementation
|
||||
/// * `exit_id` - Exit block ID
|
||||
/// * `header_id` - Loop header block ID
|
||||
/// * `branch_source_block` - Branch source block ID (early exit path)
|
||||
/// * `branch_source_block` - Branch source block ID (early exit path, Case A)
|
||||
/// * `header_vals` - Header variable values (parameter values)
|
||||
/// * `exit_snapshots` - Exit predecessor snapshots
|
||||
/// * `exit_snapshots` - Exit predecessor snapshots (from break statements)
|
||||
/// * `pinned_vars` - Pinned variable names (loop-invariant parameters)
|
||||
/// * `carrier_vars` - Carrier variable names (loop-modified variables)
|
||||
///
|
||||
/// # Returns
|
||||
/// Result: Ok(()) on success, Err(msg) on failure
|
||||
///
|
||||
/// # Process
|
||||
/// 1. Get exit predecessors (CFG validation)
|
||||
/// 2. Filter phantom blocks
|
||||
/// # [LoopForm] Process
|
||||
/// 1. Get exit predecessors (CFG validation) - determines Case A/B
|
||||
/// 2. Filter phantom blocks (Step 5-5-H)
|
||||
/// 3. Record definitions in inspector
|
||||
/// 4. Generate PHI inputs using LoopSnapshotMergeBox::merge_exit_with_classification
|
||||
/// 4. [LoopForm] Generate PHI inputs using LoopSnapshotMergeBox::merge_exit_with_classification
|
||||
/// - Case A: header+break paths included
|
||||
/// - Case B: break paths only (header not a predecessor)
|
||||
/// 5. For each variable, use PhiInputCollector to optimize and generate PHI nodes
|
||||
///
|
||||
/// # Example
|
||||
@ -119,18 +119,14 @@ impl ExitPhiBuilder {
|
||||
) -> Result<(), String> {
|
||||
ops.set_current_block(exit_id)?;
|
||||
|
||||
// 1. Exit predecessorsを取得(CFG検証)
|
||||
// [LoopForm] 1. Exit predecessorsを取得(CFG検証)- Case A/B判定のキー
|
||||
let exit_preds_set = ops.get_block_predecessors(exit_id);
|
||||
let exit_preds: Vec<BasicBlockId> = exit_preds_set.iter().copied().collect();
|
||||
|
||||
// 2. Phantom blockをフィルタリング
|
||||
let filtered_snapshots = self.filter_phantom_blocks(
|
||||
exit_snapshots,
|
||||
&exit_preds_set,
|
||||
ops,
|
||||
);
|
||||
// [LoopForm] 2. Phantom blockをフィルタリング(Step 5-5-H)
|
||||
let filtered_snapshots = self.filter_phantom_blocks(exit_snapshots, &exit_preds_set, ops);
|
||||
|
||||
// 3. Inspectorに定義を記録
|
||||
// [LoopForm] 3. Inspectorに定義を記録(変数分類の基盤)
|
||||
let inspector = self.body_local_builder.inspector_mut();
|
||||
for pinned_name in pinned_vars {
|
||||
inspector.record_definition(pinned_name, header_id);
|
||||
@ -145,7 +141,9 @@ impl ExitPhiBuilder {
|
||||
inspector.record_snapshot(branch_source_block, header_vals);
|
||||
}
|
||||
|
||||
// 4. LoopSnapshotMergeBoxでPHI入力を生成(static function call)
|
||||
// [LoopForm] 4. LoopSnapshotMergeBoxでPHI入力を生成(static function call)
|
||||
// - branch_source が exit pred のときだけ header snapshot を追加(Case A)
|
||||
// - break-only の場合は header を除外(Case B)
|
||||
let all_vars = LoopSnapshotMergeBox::merge_exit_with_classification(
|
||||
header_id,
|
||||
header_vals,
|
||||
@ -248,7 +246,11 @@ pub trait LoopFormOps {
|
||||
fn new_value(&mut self) -> ValueId;
|
||||
|
||||
/// Emit PHI instruction
|
||||
fn emit_phi(&mut self, phi_id: ValueId, inputs: Vec<(BasicBlockId, ValueId)>) -> Result<(), String>;
|
||||
fn emit_phi(
|
||||
&mut self,
|
||||
phi_id: ValueId,
|
||||
inputs: Vec<(BasicBlockId, ValueId)>,
|
||||
) -> Result<(), String>;
|
||||
|
||||
/// Update variable binding
|
||||
fn update_var(&mut self, var_name: String, value_id: ValueId);
|
||||
@ -305,7 +307,10 @@ mod tests {
|
||||
}
|
||||
|
||||
fn get_block_predecessors(&self, block_id: BasicBlockId) -> HashSet<BasicBlockId> {
|
||||
self.predecessors.get(&block_id).cloned().unwrap_or_default()
|
||||
self.predecessors
|
||||
.get(&block_id)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn block_exists(&self, block_id: BasicBlockId) -> bool {
|
||||
@ -318,7 +323,11 @@ mod tests {
|
||||
id
|
||||
}
|
||||
|
||||
fn emit_phi(&mut self, phi_id: ValueId, inputs: Vec<(BasicBlockId, ValueId)>) -> Result<(), String> {
|
||||
fn emit_phi(
|
||||
&mut self,
|
||||
phi_id: ValueId,
|
||||
inputs: Vec<(BasicBlockId, ValueId)>,
|
||||
) -> Result<(), String> {
|
||||
self.emitted_phis.push((phi_id, inputs));
|
||||
Ok(())
|
||||
}
|
||||
@ -571,7 +580,7 @@ mod tests {
|
||||
let header_id = BasicBlockId(5);
|
||||
let exit_id = BasicBlockId(10);
|
||||
let branch_source = BasicBlockId(7); // Early break
|
||||
let exit_pred1 = BasicBlockId(8); // After loop body
|
||||
let exit_pred1 = BasicBlockId(8); // After loop body
|
||||
|
||||
ops.add_block(header_id);
|
||||
ops.add_block(exit_id);
|
||||
@ -590,7 +599,7 @@ mod tests {
|
||||
let mut snapshot1 = HashMap::new();
|
||||
snapshot1.insert("s".to_string(), ValueId(1));
|
||||
snapshot1.insert("idx".to_string(), ValueId(10)); // Modified
|
||||
snapshot1.insert("ch".to_string(), ValueId(15)); // Body-local
|
||||
snapshot1.insert("ch".to_string(), ValueId(15)); // Body-local
|
||||
|
||||
let exit_snapshots = vec![(exit_pred1, snapshot1)];
|
||||
let pinned_vars = vec!["s".to_string()];
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
use crate::mir::{BasicBlockId, ValueId};
|
||||
use std::collections::{HashMap, BTreeSet, BTreeMap};
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
|
||||
// Option C PHI bug fix: Use box-based classification
|
||||
use super::local_scope_inspector::LocalScopeInspectorBox;
|
||||
@ -208,6 +208,15 @@ impl LoopSnapshotMergeBox {
|
||||
eprintln!("[Option C] carrier_vars: {:?}", carrier_vars);
|
||||
}
|
||||
|
||||
// [LoopForm] Case A/B 分岐:
|
||||
// - header_id ∈ exit_preds → header fallthrough を exit PHI 入力に含める(Case A)
|
||||
// - header_id ∉ exit_preds → break-only, header 由来の値を PHI に入れない(Case B)
|
||||
//
|
||||
// Header is a valid predecessor only when CFG actually branches to exit from the header.
|
||||
// 例: loop(1 == 1) { ... break } のように header→exit のエッジが無い場合は、
|
||||
// header 値を exit PHI 入力に含めると「非支配ブロックからの値参照」で壊れる。
|
||||
let header_in_exit_preds = exit_preds.contains(&header_id);
|
||||
|
||||
// LoopVarClassBox でフィルタリング
|
||||
let classifier = LoopVarClassBox::new();
|
||||
|
||||
@ -226,12 +235,16 @@ impl LoopSnapshotMergeBox {
|
||||
pinned_vars,
|
||||
carrier_vars,
|
||||
inspector,
|
||||
exit_preds, // ← 実際のCFG predecessorsを使用!
|
||||
exit_preds, // ← 実際のCFG predecessorsを使用!
|
||||
);
|
||||
|
||||
if debug {
|
||||
eprintln!("[Option C] var '{}': {:?} needs_exit_phi={}",
|
||||
var_name, class, class.needs_exit_phi());
|
||||
eprintln!(
|
||||
"[Option C] var '{}': {:?} needs_exit_phi={}",
|
||||
var_name,
|
||||
class,
|
||||
class.needs_exit_phi()
|
||||
);
|
||||
let defining_blocks = inspector.get_defining_blocks(&var_name);
|
||||
eprintln!("[Option C] defining_blocks: {:?}", defining_blocks);
|
||||
}
|
||||
@ -244,9 +257,15 @@ impl LoopSnapshotMergeBox {
|
||||
if !class.needs_exit_phi() || !is_in_all_preds {
|
||||
if debug {
|
||||
if !class.needs_exit_phi() {
|
||||
eprintln!("[Option C] → SKIP exit PHI for '{}' (class={:?})", var_name, class);
|
||||
eprintln!(
|
||||
"[Option C] → SKIP exit PHI for '{}' (class={:?})",
|
||||
var_name, class
|
||||
);
|
||||
} else {
|
||||
eprintln!("[Option C] → SKIP exit PHI for '{}' (NOT in all preds, class={:?})", var_name, class);
|
||||
eprintln!(
|
||||
"[Option C] → SKIP exit PHI for '{}' (NOT in all preds, class={:?})",
|
||||
var_name, class
|
||||
);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
@ -256,18 +275,26 @@ impl LoopSnapshotMergeBox {
|
||||
let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||
|
||||
// Header fallthrough(header に存在する変数のみ)
|
||||
if let Some(&val) = header_vals.get(&var_name) {
|
||||
inputs.push((header_id, val));
|
||||
if header_in_exit_preds {
|
||||
if let Some(&val) = header_vals.get(&var_name) {
|
||||
inputs.push((header_id, val));
|
||||
}
|
||||
} else if debug {
|
||||
eprintln!(
|
||||
"[Option C] header not a predecessor → skip header input for '{}'",
|
||||
var_name
|
||||
);
|
||||
}
|
||||
|
||||
// Break snapshots
|
||||
for (bb, snap) in exit_snapshots {
|
||||
// Step 5-5-H: CRITICAL - Skip phantom blocks from stale snapshots
|
||||
// After loopform renumbering, snapshots may contain BlockIds that no longer exist.
|
||||
// ONLY use snapshots from blocks that are actual CFG predecessors.
|
||||
// Step 5-5-H: CRITICAL - Skip phantom exit preds
|
||||
if !exit_preds.contains(bb) {
|
||||
if debug {
|
||||
eprintln!("[Option C] ⚠️ SKIP phantom exit pred (not in CFG): {:?} for var '{}'", bb, var_name);
|
||||
eprintln!(
|
||||
"[Option C] ⚠️ SKIP phantom exit pred (not in CFG): {:?} for var '{}'",
|
||||
bb, var_name
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user