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)
|
// Capture variable snapshot at end of body (before jumping to latch)
|
||||||
let body_end_vars = self.get_current_variable_map();
|
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
|
// Jump to latch if not already terminated
|
||||||
let actual_latch_id = if !is_current_block_terminated(self.parent_builder)? {
|
let actual_latch_id = if !is_current_block_terminated(self.parent_builder)? {
|
||||||
self.emit_jump(latch_id)?;
|
self.emit_jump(latch_id)?;
|
||||||
@ -487,7 +511,8 @@ impl<'a> LoopBuilder<'a> {
|
|||||||
} else {
|
} else {
|
||||||
vec![(continue_merge_id, merged_snapshot.clone())]
|
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)
|
// Step 3: seal body-local PHIs (complete the inputs)
|
||||||
if !body_local_vars.is_empty() {
|
if !body_local_vars.is_empty() {
|
||||||
@ -547,7 +572,6 @@ impl<'a> LoopBuilder<'a> {
|
|||||||
|
|
||||||
// Phase 25.1h: ControlForm統合版に切り替え
|
// Phase 25.1h: ControlForm統合版に切り替え
|
||||||
// continue / break のターゲットブロックをユニーク化して収集
|
// continue / break のターゲットブロックをユニーク化して収集
|
||||||
use std::collections::HashSet;
|
|
||||||
let mut break_set: HashSet<BasicBlockId> = HashSet::new();
|
let mut break_set: HashSet<BasicBlockId> = HashSet::new();
|
||||||
for (bb, _) in &self.exit_snapshots {
|
for (bb, _) in &self.exit_snapshots {
|
||||||
break_set.insert(*bb);
|
break_set.insert(*bb);
|
||||||
|
|||||||
@ -84,6 +84,9 @@ pub struct LoopFormBuilder {
|
|||||||
pub pinned: Vec<PinnedVariable>,
|
pub pinned: Vec<PinnedVariable>,
|
||||||
pub preheader_id: BasicBlockId,
|
pub preheader_id: BasicBlockId,
|
||||||
pub header_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 {
|
impl LoopFormBuilder {
|
||||||
@ -94,6 +97,7 @@ impl LoopFormBuilder {
|
|||||||
pinned: Vec::new(),
|
pinned: Vec::new(),
|
||||||
preheader_id,
|
preheader_id,
|
||||||
header_id,
|
header_id,
|
||||||
|
preheader_vars: HashMap::new(), // Will be set in prepare_structure()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,8 +111,19 @@ impl LoopFormBuilder {
|
|||||||
ops: &mut O,
|
ops: &mut O,
|
||||||
current_vars: &HashMap<String, ValueId>,
|
current_vars: &HashMap<String, ValueId>,
|
||||||
) -> Result<(), String> {
|
) -> 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] === 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
|
// GUARD: Detect invalid ValueId in variable map
|
||||||
@ -116,7 +131,7 @@ impl LoopFormBuilder {
|
|||||||
// Skip this loop construction attempt if detected (likely a premature build)
|
// Skip this loop construction attempt if detected (likely a premature build)
|
||||||
for (name, &value) in current_vars.iter() {
|
for (name, &value) in current_vars.iter() {
|
||||||
if value == ValueId::INVALID {
|
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] ⚠️ 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] This indicates the loop is being built prematurely before variables are defined");
|
||||||
eprintln!("[loopform/prepare] Returning Ok(()) to allow retry with properly initialized variables");
|
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
|
// 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);
|
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] Calling ensure_counter_after(max_existing_id={})", max_existing_id);
|
||||||
eprintln!("[loopform/prepare] current_vars: {:?}", current_vars);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ops.ensure_counter_after(max_existing_id)?;
|
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
|
// Separate variables into carriers and pinned based on parameter status
|
||||||
for (name, &value) in current_vars.iter() {
|
for (name, &value) in current_vars.iter() {
|
||||||
// Phase 26-A-4: ValueIdベース判定に変更(名前ベース → 型安全)
|
// Phase 26-A-4: ValueIdベース判定に変更(名前ベース → 型安全)
|
||||||
if ops.is_parameter(value) {
|
if ops.is_parameter(value) {
|
||||||
|
param_count += 1;
|
||||||
// Pinned variable (parameter, not modified in loop)
|
// Pinned variable (parameter, not modified in loop)
|
||||||
let pinned = PinnedVariable {
|
let pinned = PinnedVariable {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
@ -148,12 +167,13 @@ impl LoopFormBuilder {
|
|||||||
preheader_copy: ops.new_value(), // Allocate NOW
|
preheader_copy: ops.new_value(), // Allocate NOW
|
||||||
header_phi: ops.new_value(), // Allocate NOW
|
header_phi: ops.new_value(), // Allocate NOW
|
||||||
};
|
};
|
||||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
if debug_enabled {
|
||||||
eprintln!("[loopform/prepare] pinned: {} -> init={:?}, copy={:?}, phi={:?}",
|
eprintln!("[loopform/prepare] PINNED: {} -> init={:?}, copy={:?}, phi={:?}",
|
||||||
name, value, pinned.preheader_copy, pinned.header_phi);
|
name, value, pinned.preheader_copy, pinned.header_phi);
|
||||||
}
|
}
|
||||||
self.pinned.push(pinned);
|
self.pinned.push(pinned);
|
||||||
} else {
|
} else {
|
||||||
|
carrier_count += 1;
|
||||||
// Carrier variable (local, modified in loop)
|
// Carrier variable (local, modified in loop)
|
||||||
let carrier = CarrierVariable {
|
let carrier = CarrierVariable {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
@ -162,14 +182,21 @@ impl LoopFormBuilder {
|
|||||||
header_phi: ops.new_value(), // Allocate NOW
|
header_phi: ops.new_value(), // Allocate NOW
|
||||||
latch_value: ValueId(0), // Will be set during seal (placeholder)
|
latch_value: ValueId(0), // Will be set during seal (placeholder)
|
||||||
};
|
};
|
||||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
if debug_enabled {
|
||||||
eprintln!("[loopform/prepare] carrier: {} -> init={:?}, copy={:?}, phi={:?}",
|
eprintln!("[loopform/prepare] CARRIER: {} -> init={:?}, copy={:?}, phi={:?}",
|
||||||
name, value, carrier.preheader_copy, carrier.header_phi);
|
name, value, carrier.preheader_copy, carrier.header_phi);
|
||||||
}
|
}
|
||||||
self.carriers.push(carrier);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,11 +303,15 @@ impl LoopFormBuilder {
|
|||||||
/// - `latch_id`: The block that closes the canonical backedge to `header`.
|
/// - `latch_id`: The block that closes the canonical backedge to `header`.
|
||||||
/// - `continue_snapshots`: Per-`continue` block variable snapshots.
|
/// - `continue_snapshots`: Per-`continue` block variable snapshots.
|
||||||
/// Each entry represents a predecessor of `header` created by `continue`.
|
/// 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>(
|
pub fn seal_phis<O: LoopFormOps>(
|
||||||
&mut self,
|
&mut self,
|
||||||
ops: &mut O,
|
ops: &mut O,
|
||||||
latch_id: BasicBlockId,
|
latch_id: BasicBlockId,
|
||||||
continue_snapshots: &[(BasicBlockId, HashMap<String, ValueId>)],
|
continue_snapshots: &[(BasicBlockId, HashMap<String, ValueId>)],
|
||||||
|
_writes: &std::collections::HashSet<String>, // Step 5-1/5-2: Reserved for future optimization
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let debug = std::env::var("NYASH_LOOPFORM_DEBUG").is_ok();
|
let debug = std::env::var("NYASH_LOOPFORM_DEBUG").is_ok();
|
||||||
|
|
||||||
@ -320,6 +351,20 @@ impl LoopFormBuilder {
|
|||||||
|
|
||||||
sanitize_phi_inputs(&mut inputs);
|
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 {
|
if debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[loopform/seal_phis] pinned '{}' phi={:?} inputs={:?}",
|
"[loopform/seal_phis] pinned '{}' phi={:?} inputs={:?}",
|
||||||
@ -356,6 +401,20 @@ impl LoopFormBuilder {
|
|||||||
|
|
||||||
sanitize_phi_inputs(&mut inputs);
|
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 {
|
if debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[loopform/seal_phis] carrier '{}' phi={:?} inputs={:?}",
|
"[loopform/seal_phis] carrier '{}' phi={:?} inputs={:?}",
|
||||||
@ -954,8 +1013,10 @@ mod tests {
|
|||||||
let continue_snapshots = vec![(cont_bb, cont_snapshot)];
|
let continue_snapshots = vec![(cont_bb, cont_snapshot)];
|
||||||
|
|
||||||
// Act: seal PHIs
|
// Act: seal PHIs
|
||||||
|
use std::collections::HashSet;
|
||||||
|
let writes = HashSet::new(); // Empty writes for test
|
||||||
builder
|
builder
|
||||||
.seal_phis(&mut ops, latch, &continue_snapshots)
|
.seal_phis(&mut ops, latch, &continue_snapshots, &writes)
|
||||||
.expect("seal_phis should succeed");
|
.expect("seal_phis should succeed");
|
||||||
|
|
||||||
// We expect PHI updates for both pinned (p) and carrier (i)
|
// 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))
|
// 1) preheader スナップショット(Env_in(loop))
|
||||||
let base_vars = vars.clone();
|
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();
|
let mut block_var_maps: HashMap<BasicBlockId, HashMap<String, ValueId>> = HashMap::new();
|
||||||
block_var_maps.insert(preheader_bb, base_vars.clone());
|
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 _ = super::pop_increment_hint();
|
||||||
let bend = bend_res?;
|
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) 用)
|
// スナップショット収集(Env_out(loop) 用)
|
||||||
let continue_snaps = super::pop_continue_snapshots();
|
let continue_snaps = super::pop_continue_snapshots();
|
||||||
let exit_snaps = super::pop_exit_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 スナップショット)
|
// 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 スナップショット)
|
// 8) exit PHI(header fallthrough + break スナップショット)
|
||||||
// Option C: Create inspector (build_exit_phis will populate it)
|
// Option C: Create inspector (build_exit_phis will populate it)
|
||||||
|
|||||||
Reference in New Issue
Block a user