Files
hakorune/docs/private/papers/paper-d-ssa-construction/technical-details.md
Selfhosting Dev 4c0e6726e3 🔧 refactor(llvm-py): Fix resolver PHI handling and add trace improvements
Changes to resolver.py:
- Improved PHI value tracking in _value_at_end_i64() (lines 268-285)
- Added trace logging for snap hits with PHI detection
- Fixed PHI placeholder reuse logic to preserve dominance
- PHI values now returned directly from snapshots when valid

Changes to llvm_builder.py:
- Fixed externcall instruction parsing (line 522: 'func' instead of 'name')
- Improved block snapshot tracing (line 439)
- Added PHI incoming metadata tracking (lines 316-376)
- Enhanced definition tracking for lifetime hints

This should help debug the string carry=0 issue in esc_dirname_smoke where
PHI values were being incorrectly coerced instead of preserved.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-14 16:25:21 +09:00

278 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SSA構築の技術詳細
## 1. Nyash特有のSSA課題
### 1.1 Box型システムとSSA
```nyash
// Nyashコード
local str = "hello"
local num = 42
local result = str + num // 動的な型
```
```llvm
; LLVM IRでの課題
%str = call i64 @nyash_string_new(i8* @.str.hello) ; handle
%num = i64 42
%result = ? ; concat_si? concat_ii? 実行時まで不明
```
### 1.2 PHI型の決定問題
```llvm
; 複雑な合流での型推論
bb1:
%val1 = i64 123 ; integer handle
br label %merge
bb2:
%val2 = i8* @string ; string pointer
%handle = ptrtoint i8* %val2 to i64
br label %merge
merge:
%phi = phi ??? [ %val1, %bb1 ], [ %handle, %bb2 ]
; i64? i8*? 文脈依存で決定が必要
```
## 2. BuilderCursor設計の詳細
### 2.1 問題:位置管理の複雑さ
```rust
// 悪い例グローバルなbuilder状態
builder.position_at_end(bb1);
emit_instructions();
builder.position_at_end(bb2); // 位置が変わる!
// bb1の続きを書きたいが...
```
### 2.2 解決BuilderCursor
```rust
pub struct BuilderCursor<'ctx, 'b> {
builder: &'b Builder<'ctx>,
closed_by_bid: HashMap<BasicBlockId, bool>,
cur_bid: Option<BasicBlockId>,
cur_llbb: Option<BasicBlock<'ctx>>,
}
impl BuilderCursor {
pub fn with_block<R>(&mut self, bid, bb, f: impl FnOnce(&mut Self) -> R) -> R {
// 状態を保存
let prev = (self.cur_bid, self.cur_llbb);
self.at_end(bid, bb);
let result = f(self);
// 状態を復元
(self.cur_bid, self.cur_llbb) = prev;
result
}
}
```
### 2.3 終端管理
```rust
pub fn emit_term(&mut self, bid: BasicBlockId, f: impl FnOnce(&Builder)) {
self.assert_open(bid); // 閉じたブロックへの挿入を防止
f(self.builder);
self.closed_by_bid.insert(bid, true); // 明示的に閉じる
}
```
## 3. Sealed SSAの実装
### 3.1 従来のアプローチ(問題あり)
```rust
// emit_jump/branchで即座にPHI配線
if let Some(phis) = phis_by_block.get(target) {
for (dst, phi, inputs) in phis {
// predからの値をその場で配線
let val = vmap.get(vid)?; // でも値がまだない場合も...
phi.add_incoming(&[(val, pred_bb)]);
}
}
```
### 3.2 Sealed SSAアプローチ
```rust
// ブロック終了時にスナップショット
let mut block_end_values: HashMap<BlockId, HashMap<ValueId, Value>> = HashMap::new();
// 各ブロック降下後
let snapshot = vmap.iter()
.filter(|(vid, _)| defined_in_block.contains(vid))
.map(|(k, v)| (*k, *v))
.collect();
block_end_values.insert(bid, snapshot);
// seal時にスナップショットから配線
fn seal_block(...) {
let val = block_end_values[&pred_bid].get(&vid)
.or_else(|| /* フォールバック */);
}
```
### 3.3 PHI正規化の課題
```rust
// 理想pred数 = incoming数
assert_eq!(phi.count_incoming(), preds.get(&bb).len());
// 現実MIR PHIとCFG predsの不一致
// - MIRは静的に決定
// - CFGは動的に変化最適化、終端追加など
```
## 4. 型変換の統一戦略
### 4.1 基本方針
```llvm
; すべてのBox値はi64 handleとして統一
; 必要な箇所でのみptr変換
; 原則
%handle = i64 ...
%ptr = inttoptr i64 %handle to i8* ; 必要時のみ
; PHIも原則i64
%phi = phi i64 [...], [...]
```
### 4.2 文字列処理の特殊性
```llvm
; 文字列リテラル
%str_ptr = getelementptr [6 x i8], [6 x i8]* @.str.hello, i32 0, i32 0
%handle = call i64 @nyash_string_new(i8* %str_ptr)
; 文字列操作handleベース
%len = call i64 @nyash.string.len_h(i64 %handle)
%sub = call i64 @nyash.string.substring_hii(i64 %handle, i64 %start, i64 %end)
```
## 5. デバッグとトレース
### 5.1 環境変数による制御
```bash
NYASH_CLI_VERBOSE=1 # 基本ログ
NYASH_LLVM_TRACE_PHI=1 # PHI配線の詳細
NYASH_LLVM_PHI_SEALED=1 # Sealed SSAモード
NYASH_ENABLE_LOOPFORM=1 # LoopForm実験
```
### 5.2 診断出力の例
```
[PHI:new] fn=Main_esc_json_1 bb=30 dst=30 ty=i64 inputs=(23->7),(27->30)
[PHI] sealed add pred_bb=27 val=30 ty=i64 (snapshot)
[PHI] sealed add (synth) pred_bb=23 zero-ty=i64
[LLVM] terminator present for bb=27
[LoopForm] detect while-pattern: header=15 body=16 other=17
```
## 6. 未解決の技術課題
### 6.1 完全なDominance保証
- 現状hoistingとentry block配置で部分対応
- 課題:ループ内での循環参照
- 将来LoopFormでの構造化解決
### 6.2 最適PHI配置
- 現状MIR指定の場所に素直に配置
- 課題冗長なPHIの削減
- 将来PHI最小化アルゴリズム
### 6.3 例外安全性
- 現状:ゼロ値合成でクラッシュ回避
- 課題:意味的正確性の保証
- 将来Box型システムでのnull安全性
## 7. 箱理論による革命的簡略化
### 7.1 実装アーキテクチャ
```python
class BoxBasedSSA:
def __init__(self):
self.boxes = {} # block_id -> {var: value}
self.current_box = {}
self.deferred_phis = [] # 後処理用
```
### 7.2 PHI処理の簡略化
```python
# 従来複雑なdominance計算とキャッシュ
def resolve_phi_complex(self, phi_info):
# 300行のResolver処理...
# dominance確認、型変換、キャッシュ管理
# 箱理論:単純な値選択
def resolve_phi_simple(self, var, predecessors):
for pred_id, _ in predecessors:
if self.came_from(pred_id):
return self.boxes[pred_id].get(var, 0)
return 0
```
### 7.3 alloca/load/store方式への転換
```python
# SSA形式を諦めて、メモリベースの実装
def emit_variable_access(self, var):
if var not in self.allocas:
# 変数用のメモリ確保
self.allocas[var] = self.builder.alloca(self.i64, name=var)
# 読み込み
def load_var():
return self.builder.load(self.allocas[var])
# 書き込み
def store_var(value):
self.builder.store(value, self.allocas[var])
```
### 7.4 型システムの単純化
```python
# すべてをi64として扱う
def to_i64(self, value):
if is_pointer(value):
# ポインタ→ハンドル変換
return self.call_from_i8_string(value)
elif is_integer(value):
return value
else:
return 0 # デフォルト
# 必要時のみポインタ変換
def to_ptr_if_needed(self, value, context):
if context == "console_log":
return self.call_to_i8p_h(value)
return value
```
### 7.5 パフォーマンス特性
```
従来のSSA実装:
- コンパイル時間: 遅いPHI配線で50分
- 実行時性能: 最適
- メモリ使用: 少ない
箱理論実装:
- コンパイル時間: 高速5分以内
- 実行時性能: やや遅いalloca/load/storeのオーバーヘッド
- メモリ使用: やや多い変数ごとにalloca
トレードオフ: "動かないより100倍マシ"
```
### 7.6 実装の段階的移行
```python
# Phase 1: 最小動作確認(現在)
- allocaベースで全変数管理
- PHI完全スキップ
- 動作優先
# Phase 2: 部分的最適化(将来)
- 読み取り専用変数はSSA
- ループ変数のみalloca
- 段階的性能改善
# Phase 3: 完全最適化(長期)
- 箱理論の知見を活かしたSSA再実装
- 100行のシンプルさを維持
```
---
*これらの技術詳細は、論文の Technical Section の基礎となる。箱理論により、理論的な美しさより実装の実用性を優先した新しいアプローチを示している。*