chore: Phase 25.1 完了 - LoopForm v2/Stage1 CLI/環境変数削減 + Phase 26-D からの変更

Phase 25.1 完了成果:
-  LoopForm v2 テスト・ドキュメント・コメント完備
  - 4ケース(A/B/C/D)完全テストカバレッジ
  - 最小再現ケース作成(SSAバグ調査用)
  - SSOT文書作成(loopform_ssot.md)
  - 全ソースに [LoopForm] コメントタグ追加

-  Stage-1 CLI デバッグ環境構築
  - stage1_cli.hako 実装
  - stage1_bridge.rs ブリッジ実装
  - デバッグツール作成(stage1_debug.sh/stage1_minimal.sh)
  - アーキテクチャ改善提案文書

-  環境変数削減計画策定
  - 25変数の完全調査・分類
  - 6段階削減ロードマップ(25→5、80%削減)
  - 即時削除可能変数特定(NYASH_CONFIG/NYASH_DEBUG)

Phase 26-D からの累積変更:
- PHI実装改善(ExitPhiBuilder/HeaderPhiBuilder等)
- MIRビルダーリファクタリング
- 型伝播・最適化パス改善
- その他約300ファイルの累積変更

🎯 技術的成果:
- SSAバグ根本原因特定(条件分岐内loop変数変更)
- Region+next_iパターン適用完了(UsingCollectorBox等)
- LoopFormパターン文書化・テスト化完了
- セルフホスティング基盤強化

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: ChatGPT <noreply@openai.com>
Co-Authored-By: Task Assistant <task@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-21 06:25:17 +09:00
parent baf028a94f
commit f9d100ce01
366 changed files with 14322 additions and 5236 deletions

View File

@ -17,7 +17,9 @@ pub(super) fn builder_debug_log(msg: &str) {
if let Ok(cap_s) = std::env::var("NYASH_BUILDER_DEBUG_LIMIT") {
if let Ok(cap) = cap_s.parse::<usize>() {
let n = BUILDER_DEBUG_COUNT.fetch_add(1, Ordering::Relaxed);
if n >= cap { return; }
if n >= cap {
return;
}
}
}
eprintln!("[BUILDER] {}", msg);
@ -32,28 +34,38 @@ impl super::MirBuilder {
#[inline]
pub(crate) fn next_value_id(&mut self) -> super::ValueId {
if let Some(ref mut f) = self.current_function {
f.next_value_id() // Function context
f.next_value_id() // Function context
} else {
self.value_gen.next() // Module context
self.value_gen.next() // Module context
}
}
// ---- LocalSSA convenience (readability helpers) ----
#[allow(dead_code)]
#[inline]
pub(crate) fn local_recv(&mut self, v: super::ValueId) -> super::ValueId { super::ssa::local::recv(self, v) }
pub(crate) fn local_recv(&mut self, v: super::ValueId) -> super::ValueId {
super::ssa::local::recv(self, v)
}
#[allow(dead_code)]
#[inline]
pub(crate) fn local_arg(&mut self, v: super::ValueId) -> super::ValueId { super::ssa::local::arg(self, v) }
pub(crate) fn local_arg(&mut self, v: super::ValueId) -> super::ValueId {
super::ssa::local::arg(self, v)
}
#[allow(dead_code)]
#[inline]
pub(crate) fn local_cmp_operand(&mut self, v: super::ValueId) -> super::ValueId { super::ssa::local::cmp_operand(self, v) }
pub(crate) fn local_cmp_operand(&mut self, v: super::ValueId) -> super::ValueId {
super::ssa::local::cmp_operand(self, v)
}
#[allow(dead_code)]
#[inline]
pub(crate) fn local_field_base(&mut self, v: super::ValueId) -> super::ValueId { super::ssa::local::field_base(self, v) }
pub(crate) fn local_field_base(&mut self, v: super::ValueId) -> super::ValueId {
super::ssa::local::field_base(self, v)
}
#[allow(dead_code)]
#[inline]
pub(crate) fn local_cond(&mut self, v: super::ValueId) -> super::ValueId { super::ssa::local::cond(self, v) }
pub(crate) fn local_cond(&mut self, v: super::ValueId) -> super::ValueId {
super::ssa::local::cond(self, v)
}
/// Ensure a basic block exists in the current function
pub(crate) fn ensure_block_exists(&mut self, block_id: BasicBlockId) -> Result<(), String> {
if let Some(ref mut function) = self.current_function {
@ -93,12 +105,15 @@ impl super::MirBuilder {
// and LoopBuilder/IfBuilder already manage PHIs for ALL variables in variable_map,
// including pinned slots.
}
if false && !self.suppress_pin_entry_copy_next { // Keep old code for reference
if false && !self.suppress_pin_entry_copy_next {
// Keep old code for reference
// First pass: copy all pin slots and remember old->new mapping
let names: Vec<String> = self.variable_map.keys().cloned().collect();
let mut pin_renames: Vec<(super::ValueId, super::ValueId)> = Vec::new();
for name in names.iter() {
if !name.starts_with("__pin$") { continue; }
if !name.starts_with("__pin$") {
continue;
}
if let Some(&src) = self.variable_map.get(name) {
let dst = self.next_value_id();
self.emit_instruction(super::MirInstruction::Copy { dst, src })?;
@ -169,8 +184,13 @@ impl super::MirBuilder {
crate::mir::definitions::call_unified::TypeCertainty::Union,
args.len(),
);
if super::utils::builder_debug_enabled() || std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
if matches!(method.as_str(), "parse" | "substring" | "has_errors" | "length") {
if super::utils::builder_debug_enabled()
|| std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1")
{
if matches!(
method.as_str(),
"parse" | "substring" | "has_errors" | "length"
) {
eprintln!(
"[boxcall-decision] method={} bb={:?} recv=%{} class_hint={:?} prefer_legacy={}",
method,
@ -314,18 +334,27 @@ impl super::MirBuilder {
/// Pin a block-crossing ephemeral value into a pseudo local slot and register it in variable_map
/// so it participates in PHI merges across branches/blocks. Safe default for correctness-first.
pub(crate) fn pin_to_slot(&mut self, v: super::ValueId, hint: &str) -> Result<super::ValueId, String> {
pub(crate) fn pin_to_slot(
&mut self,
v: super::ValueId,
hint: &str,
) -> Result<super::ValueId, String> {
self.temp_slot_counter = self.temp_slot_counter.wrapping_add(1);
let slot_name = format!("__pin${}${}", self.temp_slot_counter, hint);
// Phase 25.1b: Use function-local ID allocator to avoid SSA verification failures
let dst = if let Some(ref mut f) = self.current_function {
f.next_value_id() // Function context: use local ID
f.next_value_id() // Function context: use local ID
} else {
self.value_gen.next() // Module context: use global ID
self.value_gen.next() // Module context: use global ID
};
self.emit_instruction(super::MirInstruction::Copy { dst, src: v })?;
if super::utils::builder_debug_enabled() || std::env::var("NYASH_PIN_TRACE").ok().as_deref() == Some("1") {
super::utils::builder_debug_log(&format!("pin slot={} src={} dst={}", slot_name, v.0, dst.0));
if super::utils::builder_debug_enabled()
|| std::env::var("NYASH_PIN_TRACE").ok().as_deref() == Some("1")
{
super::utils::builder_debug_log(&format!(
"pin slot={} src={} dst={}",
slot_name, v.0, dst.0
));
}
// Propagate lightweight metadata so downstream resolution/type inference remains stable
crate::mir::builder::metadata::propagate::propagate(self, v, dst);
@ -339,12 +368,15 @@ impl super::MirBuilder {
/// Ensure a value has a local definition in the current block by inserting a Copy.
#[allow(dead_code)]
pub(crate) fn materialize_local(&mut self, v: super::ValueId) -> Result<super::ValueId, String> {
pub(crate) fn materialize_local(
&mut self,
v: super::ValueId,
) -> Result<super::ValueId, String> {
// Phase 25.1b: Use function-local ID allocator to avoid SSA verification failures
let dst = if let Some(ref mut f) = self.current_function {
f.next_value_id() // Function context: use local ID
f.next_value_id() // Function context: use local ID
} else {
self.value_gen.next() // Module context: use global ID
self.value_gen.next() // Module context: use global ID
};
self.emit_instruction(super::MirInstruction::Copy { dst, src: v })?;
// Propagate metadata (type/origin) from source to the new local copy
@ -354,11 +386,18 @@ impl super::MirBuilder {
/// Insert a Copy immediately after PHI nodes in the current block (position-stable).
#[allow(dead_code)]
pub(crate) fn insert_copy_after_phis(&mut self, dst: super::ValueId, src: super::ValueId) -> Result<(), String> {
if let (Some(ref mut function), Some(bb)) = (&mut self.current_function, self.current_block) {
pub(crate) fn insert_copy_after_phis(
&mut self,
dst: super::ValueId,
src: super::ValueId,
) -> Result<(), String> {
if let (Some(ref mut function), Some(bb)) = (&mut self.current_function, self.current_block)
{
if std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") {
eprintln!("[utils/insert-copy-after-phis] bb={:?} dst=%{} src=%{} attempting...",
bb, dst.0, src.0);
eprintln!(
"[utils/insert-copy-after-phis] bb={:?} dst=%{} src=%{} attempting...",
bb, dst.0, src.0
);
}
if let Some(block) = function.get_block_mut(bb) {
if std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") {
@ -382,7 +421,11 @@ impl super::MirBuilder {
/// Ensure a value is safe to use in the current block by slotifying (pinning) it.
/// Currently correctness-first: always pin to get a block-local def and PHI participation.
pub(crate) fn ensure_slotified_for_use(&mut self, v: super::ValueId, hint: &str) -> Result<super::ValueId, String> {
pub(crate) fn ensure_slotified_for_use(
&mut self,
v: super::ValueId,
hint: &str,
) -> Result<super::ValueId, String> {
self.pin_to_slot(v, hint)
}