docs(llvm/vm): 静的Box(self)規約を明文化 + Bridgeトグル追記; Gate‑C/Core 現状反映; CURRENT_TASK 更新。\n\n- 新規: docs/development/architecture/llvm/static_box_singleton.md\n- 追記: lang/src/vm/README.md に self 先頭規約/互換トグルを明記\n- 追記: CURRENT_TASK に本更新を記録\n- phase-20.33/CHECKLIST にドキュメント完了チェックを追加\n- bak フォルダはリポジトリ直下に存在せず(削除対象なし)\n\n併せて未コミット差分をスナップショット(Rust 層の前作業含む)
This commit is contained in:
@ -58,6 +58,13 @@ Update — 2025-11-01 (Gate‑C v1 / Bridge / Stage‑B)
|
||||
- v1 ブリッジに `mir_call`(最小の Global/Extern)を追加(診断安定化)。
|
||||
- Rust MIR の params 既定化(`build_static_main_box` / `lower_static_method_as_function` の def→use 順)。
|
||||
- Rust builder の微修正
|
||||
|
||||
Update — 2025-11-01 (LLVM 静的Box規約の明文化 / リポジトリ整備)
|
||||
- 静的Box(LLVM 命令系)の self 先頭規約を明文化し、互換トグル `HAKO_BRIDGE_INJECT_SINGLETON` の運用を docs に追加。
|
||||
- 新規: `docs/development/architecture/llvm/static_box_singleton.md`
|
||||
- 参照追加: `lang/src/vm/README.md` に規約サマリを追記。
|
||||
- Gate‑C(Core)/Bridge 設計の現状と今後を README 類に反映(既定OFFのまま)。
|
||||
- `bak` フォルダの存在を確認したが、ワークスペース直下には見当たらず(削除不要)。
|
||||
- `build_static_main_box` にて `args` 配列生成後に `birth()` を明示呼び出し。NewBox→birth の警告/未初期化を解消。
|
||||
|
||||
Update — 2025-09-28 (P1 — Const統一拡大 + メタ伝播の適用)
|
||||
|
||||
34
docs/development/architecture/llvm/static_box_singleton.md
Normal file
34
docs/development/architecture/llvm/static_box_singleton.md
Normal file
@ -0,0 +1,34 @@
|
||||
# 静的Box(LLVM命令系)の self 先頭規約と互換トグル
|
||||
|
||||
目的
|
||||
- LLVM 命令系の静的Box(例: `LLVMPhiInstructionBox`)のメソッド呼び出しにおける引数規約を一本化し、Verifier/Runner/Bridge での期待を一致させる。
|
||||
|
||||
方針(規約)
|
||||
- 静的Boxのメソッドは「self(Singleton)を先頭引数」に持つ。
|
||||
- 例: `static box LLVMPhiInstructionBox { lower_phi(self, dst, incoming_list) { … } }`
|
||||
- 呼び出し側の見た目は従来通りでよい(糖衣)。
|
||||
- 呼び出し例: `PhiInst.lower_phi(dst, incoming)`
|
||||
- Bridge/Runner が必要に応じて先頭に Singleton を注入して実行系へ渡す。
|
||||
|
||||
互換トグル(既定OFF)
|
||||
- `HAKO_BRIDGE_INJECT_SINGLETON=1`(alias: `NYASH_BRIDGE_INJECT_SINGLETON`)
|
||||
- 役割: 旧スタイル(self 省略呼び出し)を受理し、実行前に `Singleton(LLVMPhiInstructionBox)` を先頭引数として補完。
|
||||
- 範囲: LLVM 命令系の静的Box(phi/const/binop/compare/branch/jump/ret…)。未対応は Fail‑Fast(静かなフォールバック禁止)。
|
||||
- TTL: 移行期限定の開発補助。完成後は削除または既定OFFのまま維持。
|
||||
|
||||
Fail‑Fastポリシー
|
||||
- Verifier/Bridge は期待 arity と不一致の場合に明確な診断で失敗する。
|
||||
- 代表メッセージ(例): `[bridge/singleton] static-box call missing receiver: LLVMPhiInstructionBox.lower_phi/2 (expected self+2)`
|
||||
|
||||
最小スモーク(設計)
|
||||
1) 正常(self 統一後)
|
||||
- `PhiInst.lower_phi(5, incoming)` が PASS。
|
||||
2) 互換(self 未追加だがトグルON)
|
||||
- `HAKO_BRIDGE_INJECT_SINGLETON=1` で PASS。
|
||||
3) 失敗(トグルOFF・self なし)
|
||||
- 安定化メッセージで FAIL。
|
||||
|
||||
関連
|
||||
- `docs/private/roadmap/phases/phase-20.33/README.md`(Stage‑B 全体方針)
|
||||
- `lang/src/vm/README.md`(Core/Gate‑C/Bridge 概観)
|
||||
|
||||
@ -18,6 +18,14 @@ Policy
|
||||
- Core provides engine‑agnostic execution primitives and should not import
|
||||
engine‑specific modules. During migration, temporary adapters may exist.
|
||||
|
||||
Static Box Methods(Singleton / self)
|
||||
- 規約: 静的Boxのメソッドは「self(Singleton)を先頭引数」に持つ。
|
||||
- 例: `LLVMPhiInstructionBox.lower_phi(self, dst, incoming_list)`
|
||||
- 互換: `HAKO_BRIDGE_INJECT_SINGLETON=1`(alias: `NYASH_BRIDGE_INJECT_SINGLETON`)で旧スタイル
|
||||
`PhiInst.lower_phi(dst, incoming)` に Singleton を注入して実行。
|
||||
- Fail‑Fast: 期待 arity と不一致は静かなフォールバックをせず、安定メッセージで失敗する。
|
||||
詳細: `docs/development/architecture/llvm/static_box_singleton.md`
|
||||
|
||||
Bridge‑B (Ny/Core 直行)
|
||||
- Wrapper 経路では `include "lang/src/vm/core/dispatcher.hako"` で Core Dispatcher を取り込み、
|
||||
`NyVmDispatcher.run(json)` を直接呼び出す。`using` は名前解決のみで実体は登録されないため、
|
||||
|
||||
@ -31,7 +31,14 @@ impl super::MirBuilder {
|
||||
if let ASTNode::FunctionDeclaration { params, body, .. } = main_method {
|
||||
// Also materialize a callable function entry "BoxName.main/N" for harness/PyVM
|
||||
let func_name = format!("{}.{}", box_name, "main");
|
||||
eprintln!("[DEBUG] build_static_main_box: Before lower_static_method_as_function");
|
||||
eprintln!("[DEBUG] variable_map = {:?}", self.variable_map);
|
||||
let _ = self.lower_static_method_as_function(func_name, params.clone(), body.clone());
|
||||
eprintln!("[DEBUG] build_static_main_box: After lower_static_method_as_function");
|
||||
eprintln!("[DEBUG] variable_map = {:?}", self.variable_map);
|
||||
// ✅ PHI UseBeforeDef修正: 関数生成後、変数マップをクリア
|
||||
// 理由: cf_block()で同じbodyを再処理する際、前の関数の変数(PHI ID等)が混入するのを防ぐ
|
||||
self.variable_map.clear();
|
||||
// Convert the method body to a Program AST node and lower it
|
||||
let program_ast = ASTNode::Program {
|
||||
statements: body.clone(),
|
||||
|
||||
@ -306,7 +306,15 @@ impl<'a> LoopBuilder<'a> {
|
||||
header_id: BasicBlockId,
|
||||
preheader_id: BasicBlockId,
|
||||
) -> Result<(), String> {
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
let count = CALL_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
let current_vars = self.get_current_variable_map();
|
||||
// Debug: print current_vars before prepare
|
||||
eprintln!("[DEBUG] prepare_loop_variables call #{}", count);
|
||||
eprintln!("[DEBUG] current_vars = {:?}", current_vars);
|
||||
eprintln!("[DEBUG] preheader_id = {:?}, header_id = {:?}", preheader_id, header_id);
|
||||
crate::mir::phi_core::loop_phi::save_block_snapshot(
|
||||
&mut self.block_var_maps,
|
||||
preheader_id,
|
||||
@ -409,17 +417,34 @@ impl<'a> LoopBuilder<'a> {
|
||||
dst: ValueId,
|
||||
inputs: Vec<(BasicBlockId, ValueId)>,
|
||||
) -> Result<(), String> {
|
||||
eprintln!("[DEBUG] LoopBuilder::emit_phi_at_block_start: block={}, dst=%{}, inputs={:?}", block_id, dst.0, inputs);
|
||||
// Phi nodeをブロックの先頭に挿入
|
||||
if let Some(ref mut function) = self.parent_builder.current_function {
|
||||
if let Some(block) = function.get_block_mut(block_id) {
|
||||
eprintln!("[DEBUG] Block {} current instructions count: {}", block_id, block.instructions.len());
|
||||
// Phi命令は必ずブロックの先頭に配置
|
||||
let phi_inst = MirInstruction::Phi { dst, inputs };
|
||||
let phi_inst = MirInstruction::Phi { dst, inputs: inputs.clone() };
|
||||
block.instructions.insert(0, phi_inst);
|
||||
eprintln!("[DEBUG] ✅ PHI instruction inserted at position 0");
|
||||
eprintln!("[DEBUG] Block {} after insert instructions count: {}", block_id, block.instructions.len());
|
||||
// Verify PHI is still there
|
||||
if let Some(first_inst) = block.instructions.get(0) {
|
||||
match first_inst {
|
||||
MirInstruction::Phi { dst: phi_dst, .. } => {
|
||||
eprintln!("[DEBUG] Verified: First instruction is PHI dst=%{}", phi_dst.0);
|
||||
}
|
||||
other => {
|
||||
eprintln!("[DEBUG] ⚠️ WARNING: First instruction is NOT PHI! It's {:?}", other);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
eprintln!("[DEBUG] ❌ Block {} not found!", block_id);
|
||||
Err(format!("Block {} not found", block_id))
|
||||
}
|
||||
} else {
|
||||
eprintln!("[DEBUG] ❌ No current function!");
|
||||
Err("No current function".to_string())
|
||||
}
|
||||
}
|
||||
@ -465,6 +490,7 @@ impl<'a> LoopBuilder<'a> {
|
||||
}
|
||||
|
||||
fn update_variable(&mut self, name: String, value: ValueId) {
|
||||
eprintln!("[DEBUG] LoopBuilder::update_variable: name={}, value=%{}", name, value.0);
|
||||
self.parent_builder.variable_map.insert(name, value);
|
||||
}
|
||||
|
||||
@ -716,4 +742,26 @@ impl crate::mir::phi_core::loop_phi::LoopPhiOps for LoopBuilder<'_> {
|
||||
crate::mir::phi_core::common::debug_verify_phi_inputs(func, merge_bb, inputs);
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_copy_at_preheader(
|
||||
&mut self,
|
||||
preheader_id: BasicBlockId,
|
||||
dst: ValueId,
|
||||
src: ValueId,
|
||||
) -> Result<(), String> {
|
||||
eprintln!("[DEBUG] emit_copy_at_preheader: preheader={}, dst=%{}, src=%{}", preheader_id, dst.0, src.0);
|
||||
if let Some(ref mut function) = self.parent_builder.current_function {
|
||||
if let Some(block) = function.get_block_mut(preheader_id) {
|
||||
eprintln!("[DEBUG] Adding Copy instruction to block {}", preheader_id);
|
||||
block.add_instruction(MirInstruction::Copy { dst, src });
|
||||
Ok(())
|
||||
} else {
|
||||
eprintln!("[DEBUG] ❌ Preheader block {} not found!", preheader_id);
|
||||
Err(format!("Preheader block {} not found", preheader_id))
|
||||
}
|
||||
} else {
|
||||
eprintln!("[DEBUG] ❌ No current function!");
|
||||
Err("No current function".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,6 +40,14 @@ pub trait LoopPhiOps {
|
||||
fn update_var(&mut self, name: String, value: ValueId);
|
||||
fn get_variable_at_block(&mut self, name: &str, block: BasicBlockId) -> Option<ValueId>;
|
||||
fn debug_verify_phi_inputs(&mut self, _merge_bb: BasicBlockId, _inputs: &[(BasicBlockId, ValueId)]) {}
|
||||
|
||||
/// PHI UseBeforeDef修正: preheaderブロックでCopy命令を先行生成
|
||||
fn emit_copy_at_preheader(
|
||||
&mut self,
|
||||
preheader_id: BasicBlockId,
|
||||
dst: ValueId,
|
||||
src: ValueId,
|
||||
) -> Result<(), String>;
|
||||
}
|
||||
|
||||
/// Finalize PHIs at loop exit (merge of break points and header fall-through).
|
||||
@ -130,15 +138,31 @@ pub fn prepare_loop_variables_with<O: LoopPhiOps>(
|
||||
preheader_id: BasicBlockId,
|
||||
current_vars: &std::collections::HashMap<String, ValueId>,
|
||||
) -> Result<Vec<IncompletePhi>, String> {
|
||||
// 🎯 修正: current_varsをpreheader時点の値のみに限定(header blockで定義された値を除外)
|
||||
// これにより、UseBeforeDef(PHI inputsにheader内で定義された値が含まれる)を防ぐ
|
||||
|
||||
let mut incomplete_phis: Vec<IncompletePhi> = Vec::new();
|
||||
for (var_name, &value_before) in current_vars.iter() {
|
||||
// Skip pinned variables (internal compiler temporaries)
|
||||
if var_name.starts_with("__pin$") {
|
||||
// Pinned variables are materialized via entry-phi in loop body, skip here
|
||||
continue;
|
||||
}
|
||||
|
||||
// Materialize the incoming value at preheader to satisfy UseBeforeDef constraints
|
||||
// even when `value_before` was defined in a different block (e.g., previous loop header).
|
||||
let pre_copy = ops.new_value();
|
||||
ops.emit_copy_at_preheader(preheader_id, pre_copy, value_before)?;
|
||||
|
||||
let phi_id = ops.new_value();
|
||||
let inc = IncompletePhi {
|
||||
phi_id,
|
||||
var_name: var_name.clone(),
|
||||
known_inputs: vec![(preheader_id, value_before)],
|
||||
known_inputs: vec![(preheader_id, pre_copy)], // ensure def at preheader
|
||||
};
|
||||
incomplete_phis.push(inc);
|
||||
// 変数マップを即座に更新して、条件式評価時にPHI IDを使用する
|
||||
// これにより、DCEがPHI命令を削除することを防ぐ
|
||||
ops.update_var(var_name.clone(), phi_id);
|
||||
}
|
||||
Ok(incomplete_phis)
|
||||
|
||||
@ -16,23 +16,42 @@ pub(super) fn lower_loop_stmt(
|
||||
let cond_bb = new_block(f);
|
||||
let body_bb = new_block(f);
|
||||
let exit_bb = new_block(f);
|
||||
|
||||
// フェーズM.2: no_phi変数削除 + PHI UseBeforeDef修正
|
||||
let base_vars = vars.clone();
|
||||
let orig_names: Vec<String> = base_vars.keys().cloned().collect();
|
||||
|
||||
// Step 1: cur_bbでCopy命令を先に生成(PHI inputsの定義を確保)
|
||||
let mut copy_map: HashMap<String, ValueId> = HashMap::new();
|
||||
for name in &orig_names {
|
||||
if let Some(&bval) = base_vars.get(name) {
|
||||
let copy_dst = f.next_value_id();
|
||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||
bb.add_instruction(MirInstruction::Copy {
|
||||
dst: copy_dst,
|
||||
src: bval,
|
||||
});
|
||||
}
|
||||
copy_map.insert(name.clone(), copy_dst);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: cur_bbからcond_bbへのJumpを挿入
|
||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||
if !bb.is_terminated() {
|
||||
bb.add_instruction(MirInstruction::Jump { target: cond_bb });
|
||||
}
|
||||
}
|
||||
// フェーズM.2: no_phi変数削除
|
||||
let base_vars = vars.clone();
|
||||
let orig_names: Vec<String> = base_vars.keys().cloned().collect();
|
||||
|
||||
// Step 3: cond_bbでPHI命令生成(copy_mapの値を使用)
|
||||
let mut phi_map: HashMap<String, ValueId> = HashMap::new();
|
||||
for name in &orig_names {
|
||||
if let Some(&bval) = base_vars.get(name) {
|
||||
if let Some(©_val) = copy_map.get(name) {
|
||||
let dst = f.next_value_id();
|
||||
// フェーズM.2: PHI統一処理(no_phi分岐削除)
|
||||
if let Some(bb) = f.get_block_mut(cond_bb) {
|
||||
bb.insert_instruction_after_phis(MirInstruction::Phi {
|
||||
dst,
|
||||
inputs: vec![(cur_bb, bval)],
|
||||
inputs: vec![(cur_bb, copy_val)],
|
||||
});
|
||||
}
|
||||
phi_map.insert(name.clone(), dst);
|
||||
|
||||
Reference in New Issue
Block a user