feat(phi): Step 5-1/5-2/5-4実装 - Writes収集+ValueId比較+PHI縮約
🎯 **実装内容**: - Step 5-1: Writes集合収集(Snapshot比較で再代入検出) - Step 5-2: ValueId比較ロジック(preheader_vars保存) - Step 5-4: φ縮約実装(optimize_same_value()でself-φ撲滅) 📦 **変更ファイル**: - loopform_builder.rs: preheader_vars追加、seal_phis()にPHI縮約ロジック - loop_.rs (JSON): Writes収集実装 - loop_builder.rs (AST): Writes収集実装(JSON経路と統一) ✅ **テスト結果**: 266 PASS / 1 FAIL (既知のmir_funcscanner_skip_ws) 🔧 **Box Theory**: 各箱が単一責任を保持、段階的実装完了 📋 **残タスク**: - Step 5-5: mir_funcscanner_skip_wsのPHI pred mismatch解決 - ValueId(712)の生成箇所特定(body-local PHI疑惑)
This commit is contained in:
@ -335,6 +335,30 @@ impl<'a> LoopBuilder<'a> {
|
||||
// Capture variable snapshot at end of body (before jumping to latch)
|
||||
let body_end_vars = self.get_current_variable_map();
|
||||
|
||||
// Step 5-1: Writes集合収集(選択肢2+3統合: Snapshot比較で再代入検出)
|
||||
// current_vars (preheader) と body_end_vars を比較し、ValueId が変わった変数を特定
|
||||
use std::collections::HashSet;
|
||||
let mut writes = HashSet::new();
|
||||
for (name, &body_value) in &body_end_vars {
|
||||
if let Some(&base_value) = current_vars.get(name) {
|
||||
if body_value != base_value {
|
||||
writes.insert(name.clone());
|
||||
}
|
||||
}
|
||||
// else: body で新規定義された変数(body-local)、header PHI 不要
|
||||
}
|
||||
|
||||
// DEBUG: Log writes collection
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
eprintln!("[loopform/writes] === WRITES COLLECTION (Step 5-1) ===");
|
||||
eprintln!("[loopform/writes] {} variables modified in loop body", writes.len());
|
||||
let mut sorted_writes: Vec<_> = writes.iter().collect();
|
||||
sorted_writes.sort();
|
||||
for name in &sorted_writes {
|
||||
eprintln!("[loopform/writes] WRITE: {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
// Jump to latch if not already terminated
|
||||
let actual_latch_id = if !is_current_block_terminated(self.parent_builder)? {
|
||||
self.emit_jump(latch_id)?;
|
||||
@ -487,7 +511,8 @@ impl<'a> LoopBuilder<'a> {
|
||||
} else {
|
||||
vec![(continue_merge_id, merged_snapshot.clone())]
|
||||
};
|
||||
loopform.seal_phis(self, actual_latch_id, &continue_snaps)?;
|
||||
// Step 5-1/5-2: Pass writes 集合 for PHI縮約
|
||||
loopform.seal_phis(self, actual_latch_id, &continue_snaps, &writes)?;
|
||||
|
||||
// Step 3: seal body-local PHIs (complete the inputs)
|
||||
if !body_local_vars.is_empty() {
|
||||
@ -547,7 +572,6 @@ impl<'a> LoopBuilder<'a> {
|
||||
|
||||
// Phase 25.1h: ControlForm統合版に切り替え
|
||||
// continue / break のターゲットブロックをユニーク化して収集
|
||||
use std::collections::HashSet;
|
||||
let mut break_set: HashSet<BasicBlockId> = HashSet::new();
|
||||
for (bb, _) in &self.exit_snapshots {
|
||||
break_set.insert(*bb);
|
||||
|
||||
@ -84,6 +84,9 @@ pub struct LoopFormBuilder {
|
||||
pub pinned: Vec<PinnedVariable>,
|
||||
pub preheader_id: BasicBlockId,
|
||||
pub header_id: BasicBlockId,
|
||||
/// Step 5-2: Preheader snapshot for ValueId comparison (選択肢3)
|
||||
/// Used in seal_phis() to detect which variables are truly modified vs. invariant
|
||||
pub preheader_vars: HashMap<String, ValueId>,
|
||||
}
|
||||
|
||||
impl LoopFormBuilder {
|
||||
@ -94,6 +97,7 @@ impl LoopFormBuilder {
|
||||
pinned: Vec::new(),
|
||||
preheader_id,
|
||||
header_id,
|
||||
preheader_vars: HashMap::new(), // Will be set in prepare_structure()
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,8 +111,19 @@ impl LoopFormBuilder {
|
||||
ops: &mut O,
|
||||
current_vars: &HashMap<String, ValueId>,
|
||||
) -> Result<(), String> {
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
let debug_enabled = std::env::var("NYASH_LOOPFORM_DEBUG").is_ok();
|
||||
|
||||
// Step 5-2: Save preheader snapshot for ValueId comparison in seal_phis()
|
||||
self.preheader_vars = current_vars.clone();
|
||||
|
||||
if debug_enabled {
|
||||
eprintln!("[loopform/prepare] === START prepare_structure() === {} variables", current_vars.len());
|
||||
eprintln!("[loopform/prepare] Full variable list:");
|
||||
let mut sorted_vars: Vec<_> = current_vars.iter().collect();
|
||||
sorted_vars.sort_by_key(|(name, _)| name.as_str());
|
||||
for (name, value) in &sorted_vars {
|
||||
eprintln!("[loopform/prepare] - {} = {:?}", name, value);
|
||||
}
|
||||
}
|
||||
|
||||
// GUARD: Detect invalid ValueId in variable map
|
||||
@ -116,7 +131,7 @@ impl LoopFormBuilder {
|
||||
// Skip this loop construction attempt if detected (likely a premature build)
|
||||
for (name, &value) in current_vars.iter() {
|
||||
if value == ValueId::INVALID {
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
if debug_enabled {
|
||||
eprintln!("[loopform/prepare] ⚠️ GUARD: Skipping loop preparation due to invalid ValueId for variable '{}'", name);
|
||||
eprintln!("[loopform/prepare] This indicates the loop is being built prematurely before variables are defined");
|
||||
eprintln!("[loopform/prepare] Returning Ok(()) to allow retry with properly initialized variables");
|
||||
@ -130,17 +145,21 @@ impl LoopFormBuilder {
|
||||
// Without this, new_value() can return ValueIds that are already in use
|
||||
let max_existing_id = current_vars.values().map(|v| v.0).max().unwrap_or(0);
|
||||
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
if debug_enabled {
|
||||
eprintln!("[loopform/prepare] Calling ensure_counter_after(max_existing_id={})", max_existing_id);
|
||||
eprintln!("[loopform/prepare] current_vars: {:?}", current_vars);
|
||||
}
|
||||
|
||||
ops.ensure_counter_after(max_existing_id)?;
|
||||
|
||||
// Count variables by classification for summary
|
||||
let mut param_count = 0;
|
||||
let mut carrier_count = 0;
|
||||
|
||||
// Separate variables into carriers and pinned based on parameter status
|
||||
for (name, &value) in current_vars.iter() {
|
||||
// Phase 26-A-4: ValueIdベース判定に変更(名前ベース → 型安全)
|
||||
if ops.is_parameter(value) {
|
||||
param_count += 1;
|
||||
// Pinned variable (parameter, not modified in loop)
|
||||
let pinned = PinnedVariable {
|
||||
name: name.clone(),
|
||||
@ -148,12 +167,13 @@ impl LoopFormBuilder {
|
||||
preheader_copy: ops.new_value(), // Allocate NOW
|
||||
header_phi: ops.new_value(), // Allocate NOW
|
||||
};
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
eprintln!("[loopform/prepare] pinned: {} -> init={:?}, copy={:?}, phi={:?}",
|
||||
if debug_enabled {
|
||||
eprintln!("[loopform/prepare] PINNED: {} -> init={:?}, copy={:?}, phi={:?}",
|
||||
name, value, pinned.preheader_copy, pinned.header_phi);
|
||||
}
|
||||
self.pinned.push(pinned);
|
||||
} else {
|
||||
carrier_count += 1;
|
||||
// Carrier variable (local, modified in loop)
|
||||
let carrier = CarrierVariable {
|
||||
name: name.clone(),
|
||||
@ -162,14 +182,21 @@ impl LoopFormBuilder {
|
||||
header_phi: ops.new_value(), // Allocate NOW
|
||||
latch_value: ValueId(0), // Will be set during seal (placeholder)
|
||||
};
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
eprintln!("[loopform/prepare] carrier: {} -> init={:?}, copy={:?}, phi={:?}",
|
||||
if debug_enabled {
|
||||
eprintln!("[loopform/prepare] CARRIER: {} -> init={:?}, copy={:?}, phi={:?}",
|
||||
name, value, carrier.preheader_copy, carrier.header_phi);
|
||||
}
|
||||
self.carriers.push(carrier);
|
||||
}
|
||||
}
|
||||
|
||||
if debug_enabled {
|
||||
eprintln!("[loopform/prepare] === SUMMARY ===");
|
||||
eprintln!("[loopform/prepare] Total vars: {}", current_vars.len());
|
||||
eprintln!("[loopform/prepare] Pinned (params): {}", param_count);
|
||||
eprintln!("[loopform/prepare] Carriers (locals): {}", carrier_count);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -276,11 +303,15 @@ impl LoopFormBuilder {
|
||||
/// - `latch_id`: The block that closes the canonical backedge to `header`.
|
||||
/// - `continue_snapshots`: Per-`continue` block variable snapshots.
|
||||
/// Each entry represents a predecessor of `header` created by `continue`.
|
||||
/// - `writes`: Variables modified in loop body (Step 5-1: 選択肢2)
|
||||
/// Used to distinguish true carriers from loop-invariant variables
|
||||
/// (Currently unused - PHI optimization uses optimize_same_value() instead)
|
||||
pub fn seal_phis<O: LoopFormOps>(
|
||||
&mut self,
|
||||
ops: &mut O,
|
||||
latch_id: BasicBlockId,
|
||||
continue_snapshots: &[(BasicBlockId, HashMap<String, ValueId>)],
|
||||
_writes: &std::collections::HashSet<String>, // Step 5-1/5-2: Reserved for future optimization
|
||||
) -> Result<(), String> {
|
||||
let debug = std::env::var("NYASH_LOOPFORM_DEBUG").is_ok();
|
||||
|
||||
@ -320,6 +351,20 @@ impl LoopFormBuilder {
|
||||
|
||||
sanitize_phi_inputs(&mut inputs);
|
||||
|
||||
// Step 5-4: φ縮約(self-φ撲滅)
|
||||
// 全入力が同じValueIdなら、PHI不要(loop-invariant)
|
||||
if let Some(same_value) = LoopSnapshotMergeBox::optimize_same_value(&inputs) {
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[loopform/seal_phis] OPTIMIZED pinned '{}': phi={:?} → same_value={:?} (loop-invariant)",
|
||||
pinned.name, pinned.header_phi, same_value
|
||||
);
|
||||
}
|
||||
// Skip PHI update - this variable is truly loop-invariant
|
||||
// The header_phi will become dead code and can be cleaned up later
|
||||
continue;
|
||||
}
|
||||
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[loopform/seal_phis] pinned '{}' phi={:?} inputs={:?}",
|
||||
@ -356,6 +401,20 @@ impl LoopFormBuilder {
|
||||
|
||||
sanitize_phi_inputs(&mut inputs);
|
||||
|
||||
// Step 5-4: φ縮約(self-φ撲滅)
|
||||
// 全入力が同じValueIdなら、PHI不要(誤分類されたloop-invariant)
|
||||
if let Some(same_value) = LoopSnapshotMergeBox::optimize_same_value(&inputs) {
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[loopform/seal_phis] OPTIMIZED carrier '{}': phi={:?} → same_value={:?} (misclassified as carrier, actually loop-invariant)",
|
||||
carrier.name, carrier.header_phi, same_value
|
||||
);
|
||||
}
|
||||
// Skip PHI update - this variable was misclassified as carrier
|
||||
// but is actually loop-invariant (no modifications in loop body)
|
||||
continue;
|
||||
}
|
||||
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[loopform/seal_phis] carrier '{}' phi={:?} inputs={:?}",
|
||||
@ -954,8 +1013,10 @@ mod tests {
|
||||
let continue_snapshots = vec![(cont_bb, cont_snapshot)];
|
||||
|
||||
// Act: seal PHIs
|
||||
use std::collections::HashSet;
|
||||
let writes = HashSet::new(); // Empty writes for test
|
||||
builder
|
||||
.seal_phis(&mut ops, latch, &continue_snapshots)
|
||||
.seal_phis(&mut ops, latch, &continue_snapshots, &writes)
|
||||
.expect("seal_phis should succeed");
|
||||
|
||||
// We expect PHI updates for both pinned (p) and carrier (i)
|
||||
|
||||
@ -227,6 +227,19 @@ pub(super) fn lower_loop_stmt(
|
||||
|
||||
// 1) preheader スナップショット(Env_in(loop))
|
||||
let base_vars = vars.clone();
|
||||
|
||||
// DEBUG: Log preheader snapshot
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
eprintln!("[loop_/lower] === PREHEADER SNAPSHOT (bb={:?}) ===", preheader_bb);
|
||||
eprintln!("[loop_/lower] Function: {}", f.signature.name);
|
||||
eprintln!("[loop_/lower] base_vars.len() = {}", base_vars.len());
|
||||
let mut sorted: Vec<_> = base_vars.iter().collect();
|
||||
sorted.sort_by_key(|(name, _)| name.as_str());
|
||||
for (name, value) in &sorted {
|
||||
eprintln!("[loop_/lower] preheader var: {} = {:?}", name, value);
|
||||
}
|
||||
}
|
||||
|
||||
let mut block_var_maps: HashMap<BasicBlockId, HashMap<String, ValueId>> = HashMap::new();
|
||||
block_var_maps.insert(preheader_bb, base_vars.clone());
|
||||
|
||||
@ -258,6 +271,32 @@ pub(super) fn lower_loop_stmt(
|
||||
let _ = super::pop_increment_hint();
|
||||
let bend = bend_res?;
|
||||
|
||||
// Step 5-1: Writes集合収集(選択肢2+3統合: Snapshot比較で再代入検出)
|
||||
// base_vars (preheader) と body_vars を比較し、ValueId が変わった変数を特定
|
||||
use std::collections::HashSet;
|
||||
let mut writes = HashSet::new();
|
||||
for (name, &body_value) in &body_vars {
|
||||
if let Some(&base_value) = base_vars.get(name) {
|
||||
if body_value != base_value {
|
||||
writes.insert(name.clone());
|
||||
}
|
||||
}
|
||||
// else: body で新規定義された変数(body-local)、header PHI 不要
|
||||
}
|
||||
|
||||
// DEBUG: Log writes collection
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
let func_name = ops.f.signature.name.clone(); // Clone before borrowing
|
||||
eprintln!("[loop_/lower] === WRITES COLLECTION (Step 5-1) ===");
|
||||
eprintln!("[loop_/lower] Function: {}", func_name);
|
||||
eprintln!("[loop_/lower] {} variables modified in loop body", writes.len());
|
||||
let mut sorted_writes: Vec<_> = writes.iter().collect();
|
||||
sorted_writes.sort();
|
||||
for name in &sorted_writes {
|
||||
eprintln!("[loop_/lower] WRITE: {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
// スナップショット収集(Env_out(loop) 用)
|
||||
let continue_snaps = super::pop_continue_snapshots();
|
||||
let exit_snaps = super::pop_exit_snapshots();
|
||||
@ -324,7 +363,8 @@ pub(super) fn lower_loop_stmt(
|
||||
};
|
||||
|
||||
// 7) header PHI seal(latch + canonical continue_merge スナップショット)
|
||||
loopform.seal_phis(&mut ops, latch_bb, &canonical_continue_snaps)?;
|
||||
// Step 5-1/5-2: Pass writes 集合 for PHI縮約
|
||||
loopform.seal_phis(&mut ops, latch_bb, &canonical_continue_snaps, &writes)?;
|
||||
|
||||
// 8) exit PHI(header fallthrough + break スナップショット)
|
||||
// Option C: Create inspector (build_exit_phis will populate it)
|
||||
|
||||
Reference in New Issue
Block a user