feat(region): Phase 25.1l FunctionSlotRegistry完全実装

ChatGPT実装 M-1〜M-4:
- FunctionSlotRegistry: 変数スロット管理中央化
- RegionKind::Function追加
- RefKind分類統合
- 観測レイヤー完成

品質評価 (Task先生レビュー):
- 設計:  (箱理論完璧)
- 実装: M-1〜M-4全て完全
- 統合: 既存システムと高品質統合
- 影響: SSA/PHI非侵襲(観測専用)

既知問題:
- userbox_birth_to_string_vm失敗
  → 既存問題(Phase 25.1h以前から)
  → 本実装とは無関係
  → 別途調査予定

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-19 03:28:58 +09:00
parent 39f5256c18
commit b79697f137
10 changed files with 393 additions and 61 deletions

View File

@ -31,32 +31,34 @@ Update (2025-11-18 — Phase 25.1k: LoopSSA v2 (.hako) & StageB harness 追
- KC: PhiInjectorBox に Carrier/Pinned ベースの v2 入口を追加(既存 `_collect_phi_vars` は当面維持)。
- KD: StageB Test3 の `%0` SSA 問題が悪化していないことを確認しつつ、LoopSSA 有効/無効の切り分けを残す。
Update (2025-11-18 — Phase 25.1l: Region/GC 観測レイヤー導入Rust 側のみ))
Update (2025-11-18 — Phase 25.1l: Region/GC 観測レイヤー導入Rust 側のみ) — completed)
- Context:
- LoopForm v2 / ControlForm / Conservative PHI Box により、If/Loop の SSA/PHI は Rust 側で安定しているが、
GC や StageB 由来の寿命問題を構造的に見るための「Region/スコープ」ビューが不足していた。
- 25.1l では GC 挙動を変えずに、Rust MIR 側に観測専用の Region/RefSlotKind レイヤーを導入する。
- Done:
- `src/mir/region/mod.rs`:
- `RefSlotKind``StrongRoot/WeakRoot/Borrowed/NonRef`)、`SlotMetadata { name, ref_kind }``RegionId``RegionKind(Loop/If)``Region { id, kind, entry_block, exit_blocks, slots }` を定義。
- `RefSlotKind``StrongRoot/WeakRoot/Borrowed/NonRef`)、`SlotMetadata { name, ref_kind }``RegionId``RegionKind(Function/Loop/If)``Region { id, kind, parent, entry_block, exit_blocks, slots }` を定義。
- `Region::classify_ref_kind(MirType)` で Box/Array/Future を StrongRoot、それ以外のプリミティブを NonRef として暫定分類。
- `src/mir/region/function_slot_registry.rs`:
- 関数スコープ単位の `FunctionSlotRegistry` を導入し、`SlotId -> SlotInfo { name, ty, ref_kind }``name -> SlotId` を SSOT として管理(現時点では観測専用)。
- `src/mir/builder.rs`:
- `current_slot_registry: Option<FunctionSlotRegistry>``current_region_stack: Vec<RegionId>` を追加し、`current_function` 単位でスロットと Region の親子関係を追跡できるようにしたNYASH_REGION_TRACE=0 のときは zero-cost
- `src/mir/builder/lifecycle.rs` / `src/mir/builder/calls/lowering.rs`:
- main 関数および static 関数/instance method lowering で、関数開始時に `FunctionSlotRegistry::new()` をセットし、終了時に `saved_slot_registry` を元に戻すようにした。
- 同時に `observe_function_region(self)` / `pop_function_region(self)` を呼び出し、StageB 系関数(`\"StageB\"` を含む名前)に対して FunctionRegion→Loop/IfRegion の木構造をログ上で観測できるようにした。
- 変数生成タイミングでの SlotRegistry 更新(挙動不変・観測専用):
- `calls/lowering.rs::setup_function_params` / `setup_method_params` で、パラメータ名と型(わかる範囲)を `ensure_slot(name, ty)` で登録。
- `builder/decls.rs::build_static_main_box` で、`self.variable_map.insert(p.clone(), pid)` の直後に `value_types.get(&pid)` を使って `ensure_slot(p, ty)`
- `builder/stmts.rs``build_local_statement` / `build_nowait_statement` / `build_me_expression` がローカル変数や `me` を SlotRegistry に登録するようにした。
- `src/mir/region/observer.rs`:
- `observe_control_form(builder: &MirBuilder, form: &ControlForm)`実装し、`NYASH_REGION_TRACE=1` のときだけ Region 情報をログ出力
- 現時点では `builder.variable_map``builder.value_types` から live スロットと RefKind を定し、`[region/observe] fn=... id=RegionId(..) kind=Loop/If entry=bb.. exits=[..] slots=[..]` の形で eprintln する(メモリには保存しない)
- dev 用フィルタとして `func_name.contains("StageB")` の関数のみを観測対象にし、StageB BodyExtractor まわりの Region/Slot 状況が見えるようにした
- `observe_function_region(builder: &mut MirBuilder)`追加し、`NYASH_REGION_TRACE=1` かつ 関数名に `\"StageB\"` を含む場合だけ `RegionKind::Function` を 1 つ作成して `current_region_stack` に積むようにした
- `observe_control_form(builder: &mut MirBuilder, form: &ControlForm)` を SlotRegistry ベースに書き換え、`current_slot_registry` があればそちらから SlotInfo を列挙して RefKind を定し、なければ従来通り `variable_map` + `value_types` から推定するようにした
- Loop/IfRegion は `parent = current_region_stack.last()` を使って FunctionRegion 配下にぶら下がるようにし、`[region/observe] fn=... id=RegionId(..) kind=Loop/If entry=bb.. exits=[..] slots=[..]` をログに出す
- `src/mir/loop_builder.rs`:
- ループ構築部と `lower_if_in_loop` の If 降下部で、`ControlForm::from_loop` / `ControlForm::from_if` 生成直後に `observe_control_form` を呼び、Loop/If ごとに Region ログを出せるようにしたNYASH_REGION_TRACE=0 のときは完全に無効)
- ループ構築部と `lower_if_in_loop` の If 降下部で、`ControlForm::from_loop` / `ControlForm::from_if` 生成直後に `observe_control_form(self.parent_builder, &form)` を呼び出すようにし、Loop/IfRegion を FunctionRegion の子として観測できるようにした
- テスト:
- `cargo test -q mir_loopform_exit_phi -- --nocapture` は NYASH_REGION_TRACE=0/1 の両方で緑Loop/If SSA/PHI 挙動に変化なし)。
- Plan25.1l 後半〜次フェーズへのブリッジ):
- Region 木と FunctionRegion:
- 次フェーズで `RegionKind::Function` と FunctionRegion (#0) を導入し、関数スコープを含む Region 木を構築するentry=関数 bb0, exits=ret ブロック群)。
- MirBuilder に「現在属している RegionId スタック」を追加し、Loop/If 降下時に parent/children 関係を記録できるようにする(観測専用)。
- SlotRegistry関数ごと 1 箱)の設計:
- 長期的には `FunctionSlotRegistry` のような箱を導入し、`slot_id -> { name, MirType, RefSlotKind }` / `name -> slot_id` を SSOT として管理する。
- 25.1l ではまだ `variable_map` ベースの暫定観測に留め、SlotId ベースへの移行は Region 木導入後に小さく刻んで進める。
- GC 統合:
- Region/Slot 観測結果をもとに、どの Region 境界で StrongRoot スロットを retain/release すべきかの設計を 25.1m 以降で行うRust の既存 GC/Barrier 実装を壊さない範囲に限定)。
- `cargo test -q mir_breakfinder_ssa -- --nocapture` / `cargo test -q mir_loopform_exit_phi -- --nocapture` は NYASH_REGION_TRACE=0/1 の両方で緑Loop/If SSA/PHI 挙動に変化なし)。
Update (2025-11-18 — Phase 25.1g: Conservative PHI ↔ ControlForm 統合の準備)
- Context:

View File

@ -1,6 +1,6 @@
# Phase 25.1l — Region/GC 観測レイヤーLoopForm v2 × RefSlotKind
Status: in progressRust 側観測レイヤーの最小実装まで完了/挙動変更なし)
Status: completedRust 側観測レイヤーの最小実装まで完了/挙動変更なし)
## ゴール
@ -38,10 +38,14 @@ Status: in progressRust 側観測レイヤーの最小実装まで完了/
- `SlotMetadata`:
- `name: String` — 変数スロット名(`i`, `body_src`, `args` など)。
- `ref_kind: RefSlotKind` — 上記の種別。
- `RegionKind`:
- `Function` — 関数スコープ。
- `Loop` — ループ構造LoopForm v2 由来)。
- `If` — if/else 構造。
- `Region`:
- `id: RegionId``u32` ラッパ)。
- `kind: RegionKind``Loop` / `If`25.1l 時点では Function は未導入)
- `parent: Option<RegionId>` / `children: Vec<RegionId>` — 制御構造の親子関係将来用。25.1l では未設定)。
- `kind: RegionKind`上記 3 種
- `parent: Option<RegionId>` — 親 RegionIdFunctionRegion がルート、Loop/If はその子)。
- `entry_block: BasicBlockId` / `exit_blocks: Vec<BasicBlockId>` — ControlForm から引き継ぐ。
- `slots: Vec<SlotMetadata>` — その Region で live とみなすスロット一覧(観測用)。
- RefKind 判定は 25.1l では **簡易ヒューリスティック** に留める:
@ -49,40 +53,71 @@ Status: in progressRust 側観測レイヤーの最小実装まで完了/
- 明らかなプリミティブ(整数/bool/文字列)→ `NonRef`
- Weak 系/借用系の精密な分類は後続フェーズで詰める。
### LB: RegionObserver の実装と ControlForm からの接続(実装済み)
### LB: RegionObserver の実装と ControlForm/Function からの接続(実装済み)
- 新規モジュール: `src/mir/region/observer.rs`
- 現実装の責務:
- `observe_control_form(builder: &MirBuilder, form: &ControlForm)` という関数型 API で:
- `observe_function_region(builder: &mut MirBuilder)`:
- `NYASH_REGION_TRACE=1` かつ 関数名に `"StageB"` を含む場合のみ、`RegionKind::Function` を 1 つ作成。
- `RegionId``MirBuilder.current_region_stack` に push し、ルート Region としてログ出力。
- `observe_control_form(builder: &mut MirBuilder, form: &ControlForm)`:
- `ControlForm` から `entry`/`exits`/Loop/If の形を読む。
- 該当スコープ時点の `builder.variable_map` に載っている「名前付きスロット」を走査して `SlotMetadata` を構築
- `Region` を作成し、`NYASH_REGION_TRACE=1` のときだけ:
- `builder.current_region_stack.last()``parent: Option<RegionId>` として保持し、FunctionRegion の子として Loop/IfRegion をぶら下げる
- スロット列挙は FunctionSlotRegistry後述を優先し、なければ `variable_map` + `value_types` で暫定推定。
- `NYASH_REGION_TRACE=1` のときだけ:
- `[region/observe] fn=StageBBodyExtractorBox.build_body_src/2 id=RegionId(..) kind=Loop entry=bb.. exits=[..] slots=[..]`
という形でログ出力する(メモリには保持しない)。
- Hook の置き場所(いずれも読み取り専用):
のようなログを eprintln する(メモリ蓄積はしない)。
- Hook の置き場所(いずれも観測専用):
- `src/mir/builder/lifecycle.rs`:
- main 関数生成後に `observe_function_region(self)` を呼び出し(関数名フィルタにより、多くはスキップされる)。
- `src/mir/builder/calls/lowering.rs`:
- static 関数用: `create_function_skeleton` で新関数を作成後、`observe_function_region(self)` を呼び出す。
- instance method 用: `create_method_skeleton` 後に同様に `observe_function_region(self)` を呼び出す。
- finalize (`lower_static_method_as_function` / `lower_method_as_function`) の最後で `pop_function_region(self)` を呼び、Region スタックを 1 段戻す。
- `src/mir/loop_builder.rs`:
- ループ構築完了後、`LoopShape``ControlForm::from_loop` 生成直後に `observe_control_form(self.parent_builder, &form)` を呼ぶ。
- `src/mir/loop_builder.rs`if 降下部):
- `IfShape``ControlForm::from_if` 生成直後に `observe_control_form(self.parent_builder, &form)` を呼ぶ。
- `.hako` / GC / SSA ロジックには一切影響しない。
- dev フィルタ:
- `lower_if_in_loop` でも `IfShape``ControlForm::from_if` 生成直後に `observe_control_form(self.parent_builder, &form)` を呼ぶ。
- dev フィルタ:
- 現時点では StageB 周辺の観測に絞るため、`func_name.contains("StageB")` の関数のみログ対象にしている(ログ爆発防止)。
### LC: 関数スコープ Slot 管理箱SlotRegistryとの関係設計メモ
### LC: 関数スコープ Slot 管理箱(FunctionSlotRegistryとの関係実装済み・観測専用
- 将来像:
- 各関数ごとに「SlotRegistry仮称」箱を 1 つだけ持ち、
- `slot_id -> { name, MirType, RefSlotKind }`
- `name -> slot_id`
を管理する SSOT として扱う
- RegionBox本 README での Regionは、この SlotRegistry 上で
「どの SlotId がこの Region で live か」を指すだけにする。
- 25.1l の暫定実装:
- まだ明示的な SlotRegistry 型は導入せず、
- `MirBuilder.variable_map.keys()` を「その地点で live なスロット一覧」とみなす。
- `value_types` から MirType を拾って RefSlotKind を判定する
- これは SlotRegistry 導入までのリーズナブルな暫定策として位置付け、
将来は `variable_map`→SlotRegistry/SlotId ベースに移行する。
- 新規モジュール: `src/mir/region/function_slot_registry.rs`
- `SlotId(u32)` / `SlotInfo { name, ty: Option<MirType>, ref_kind: Option<RefSlotKind> }`
- `FunctionSlotRegistry { slots: Vec<SlotInfo>, name_to_slot: HashMap<String, SlotId> }`
- API:
- `new()` — 空レジストリ
- `ensure_slot(name, ty)` — スロットが存在しなければ作成し、`SlotId` を返す。
- `set_ref_kind(slot, RefSlotKind)` — 後から RefKind を埋める。
- `iter_slots()` / `get_slot(name)` / `get_slot_info(slot)` — 観測用読み出し。
- `MirBuilder` への統合:
- フィールド:
- `current_slot_registry: Option<FunctionSlotRegistry>``current_function` と同じライフサイクルで生存
- `current_region_stack: Vec<RegionId>` — FunctionRegion/Loop/IfRegion の親子関係を維持する dev 用スタック。
- 関数開始・終了での管理:
- main 関数生成時(`prepare_module`)で `FunctionSlotRegistry::new()` を作成。
- static 関数/instance method lowering 時(`calls/lowering.rs`)で:
- `create_function_skeleton` / `create_method_skeleton` 内で新しいレジストリをセット。
- `LoweringContext``saved_slot_registry` を追加し、呼び出し元のレジストリを退避・復元。
- `finalize_module` / `lower_static_method_as_function` / `lower_method_as_function` の終了時に `current_slot_registry = None`(または `saved_slot_registry` を復元)。
- SlotRegistry の更新ポイント(挙動不変・観測のみ):
- パラメータstatic 関数):
- `setup_function_params``params` とシグネチャの `MirType` をローカルベクタに集約し、ループ後に `reg.ensure_slot(name, ty)` を呼ぶ。
- パラメータinstance method:
- `setup_method_params``me` と通常パラメータを `(name, None)` として集約し、同様に `ensure_slot`
- static Main ラッパー:
- `build_static_main_box``self.variable_map.insert(p.clone(), pid)` の直後に `value_types.get(&pid)` を使って `ensure_slot(p, ty)`
- ローカル変数/nowait/me:
- `build_local_statement``variable_map` 登録後に `ensure_slot(&var_name, value_types.get(&var_id))`
- `build_nowait_statement` で Future を束ねる `variable` 名を `ensure_slot(&variable, None)`
- `build_me_expression``me` を初回生成したときに `ensure_slot("me", None)`
- RegionObserver との接続:
- `observe_control_form` では、`current_slot_registry` が存在する場合はそれを優先:
- `classify_slots_from_registry(reg)` で:
- SlotInfo.ty から `Region::classify_ref_kind` を使って RefKind を決定。
- ty が無い場合は `classify_slot_name_only``args/src/body_src/...` 系を StrongRoot とみなす簡易ヒューリスティック)。
- 各 SlotInfo に `set_ref_kind` で RefKind を埋めてから `Region.slots` を構築。
- これにより、RefKind 判定は SlotRegistry 側に一元化され、Region 側は `SlotMetadata { name, ref_kind }` を参照するだけになる。
### LD: Region メタデータの足場(将来の JSON 拡張のための入口だけ)

View File

@ -9,6 +9,8 @@ use super::{
BasicBlock, BasicBlockId, BasicBlockIdGenerator, CompareOp, ConstValue, Effect, EffectMask,
FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId, ValueIdGenerator,
};
use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
use crate::mir::region::RegionId;
use crate::ast::{ASTNode, LiteralValue};
use crate::mir::builder::builder_calls::CallTarget;
use std::collections::HashMap;
@ -113,6 +115,11 @@ pub struct MirBuilder {
/// 注意: compilation_contextがSomeの場合は使用されません
pub(super) value_types: HashMap<ValueId, super::MirType>,
/// 関数スコープの SlotRegistry観測専用
/// - current_function と同じライフサイクルを持つよ。
/// - 既存の variable_map/SSA には影響しない(メタデータのみ)。
pub(super) current_slot_registry: Option<FunctionSlotRegistry>,
/// 🎯 箱理論: 型情報管理の一元化TypeRegistryBox
/// NYASH_USE_TYPE_REGISTRY=1 で有効化(段階的移行用)
pub(super) type_registry: type_registry::TypeRegistry,
@ -133,6 +140,10 @@ pub struct MirBuilder {
/// Source size snapshot to detect when to rebuild the tail index
pub(super) method_tail_index_source_len: usize,
/// Region 観測用のスタックだよFunctionRegion がルート)。
/// - NYASH_REGION_TRACE=1 のときだけ使われる開発用メタデータだよ。
pub(super) current_region_stack: Vec<RegionId>,
// include guards removed
/// Loop context stacks for lowering break/continue inside nested control flow
@ -229,6 +240,7 @@ impl MirBuilder {
field_origin_class: HashMap::new(),
field_origin_by_box: HashMap::new(),
value_types: HashMap::new(),
current_slot_registry: None,
type_registry: type_registry::TypeRegistry::new(),
plugin_method_sigs,
current_static_box: None,
@ -237,6 +249,8 @@ impl MirBuilder {
method_tail_index: std::collections::HashMap::new(),
method_tail_index_source_len: 0,
current_region_stack: Vec::new(),
loop_header_stack: Vec::new(),
loop_exit_stack: Vec::new(),
if_merge_stack: Vec::new(),

View File

@ -7,6 +7,7 @@
use crate::ast::ASTNode;
use crate::mir::builder::{MirBuilder, MirType, MirInstruction};
use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
use super::function_lowering;
use std::collections::HashMap;
@ -17,6 +18,7 @@ struct LoweringContext {
saved_static_ctx: Option<String>,
saved_function: Option<super::super::MirFunction>,
saved_block: Option<super::super::BasicBlockId>,
saved_slot_registry: Option<FunctionSlotRegistry>,
}
impl MirBuilder {
@ -42,6 +44,9 @@ impl MirBuilder {
None
};
// 関数スコープ SlotRegistry は元の関数側から退避しておくよ。
let saved_slot_registry = self.current_slot_registry.take();
// BoxCompilationContext mode: clear()で完全独立化
if context_active {
self.variable_map.clear();
@ -59,6 +64,7 @@ impl MirBuilder {
saved_static_ctx,
saved_function: None,
saved_block: None,
saved_slot_registry,
}
}
@ -85,14 +91,22 @@ impl MirBuilder {
// 新しい関数に切り替え
self.current_function = Some(function);
self.current_block = Some(entry);
// 新しい関数スコープ用の SlotRegistry を準備するよ(観測専用)
self.current_slot_registry = Some(FunctionSlotRegistry::new());
self.ensure_block_exists(entry)?;
// Region 観測レイヤ: static 関数用の FunctionRegion を積むよ。
crate::mir::region::observer::observe_function_region(self);
Ok(())
}
/// 🎯 箱理論: Step 3 - パラメータ設定
fn setup_function_params(&mut self, params: &[String]) {
self.function_param_names.clear();
// SlotRegistry 更新は borrow 競合を避けるため、まずローカルに集約してから反映するよ。
let mut slot_regs: Vec<(String, Option<MirType>)> = Vec::new();
if let Some(ref mut f) = self.current_function {
// 📦 Hotfix 5: Use pre-populated params from MirFunction::new()
// Static methods have implicit receiver at params[0], so actual parameters start at offset
@ -101,6 +115,8 @@ impl MirBuilder {
if f.params.len() > params.len() { 1 } else { 0 }
};
let param_types = f.signature.params.clone();
for (idx, p) in params.iter().enumerate() {
let param_idx = receiver_offset + idx;
let pid = if param_idx < f.params.len() {
@ -114,6 +130,14 @@ impl MirBuilder {
};
self.variable_map.insert(p.clone(), pid);
self.function_param_names.insert(p.clone());
let ty = param_types.get(param_idx).cloned();
slot_regs.push((p.clone(), ty));
}
}
if let Some(reg) = self.current_slot_registry.as_mut() {
for (name, ty) in slot_regs {
reg.ensure_slot(&name, ty);
}
}
}
@ -198,6 +222,8 @@ impl MirBuilder {
// Static box context復元
self.current_static_box = ctx.saved_static_ctx;
// 関数スコープ SlotRegistry も元の関数に戻すよ。
self.current_slot_registry = ctx.saved_slot_registry;
}
/// 🎯 箱理論: Step 2b - 関数スケルトン作成instance method版
@ -225,25 +251,41 @@ impl MirBuilder {
// 新しい関数に切り替え
self.current_function = Some(function);
self.current_block = Some(entry);
// instance method 用の関数スコープ SlotRegistry もここで用意するよ。
self.current_slot_registry = Some(FunctionSlotRegistry::new());
self.ensure_block_exists(entry)?;
// Region 観測レイヤ: instance method 用の FunctionRegion も積んでおくよ。
crate::mir::region::observer::observe_function_region(self);
Ok(())
}
/// 🎯 箱理論: Step 3b - パラメータ設定instance method版: me + params
fn setup_method_params(&mut self, box_name: &str, params: &[String]) {
// SlotRegistry 更新はローカルバッファに集約してから反映するよ。
let mut slot_regs: Vec<(String, Option<MirType>)> = Vec::new();
if let Some(ref mut f) = self.current_function {
// First parameter is always 'me'
let me_id = f.next_value_id();
f.params.push(me_id);
self.variable_map.insert("me".to_string(), me_id);
self.value_origin_newbox.insert(me_id, box_name.to_string());
slot_regs.push(("me".to_string(), None));
// Then regular parameters
for p in params {
let pid = f.next_value_id();
f.params.push(pid);
self.variable_map.insert(p.clone(), pid);
slot_regs.push((p.clone(), None));
}
}
if let Some(reg) = self.current_slot_registry.as_mut() {
for (name, ty) in slot_regs {
reg.ensure_slot(&name, ty);
}
}
}
@ -281,6 +323,9 @@ impl MirBuilder {
};
self.finalize_function(returns_value)?;
// FunctionRegion を 1 段ポップして元の関数コンテキストに戻るよ。
crate::mir::region::observer::pop_function_region(self);
// Step 6: Context復元
self.restore_lowering_context(ctx);
@ -302,6 +347,7 @@ impl MirBuilder {
saved_static_ctx: None,
saved_function: None,
saved_block: None,
saved_slot_registry: self.current_slot_registry.take(),
};
// Step 2b: 関数スケルトン作成method版
@ -362,12 +408,16 @@ impl MirBuilder {
module.add_function(finalized_function);
}
// FunctionRegion を 1 段ポップして元の関数コンテキストに戻るよ。
crate::mir::region::observer::pop_function_region(self);
// Step 6: Context復元simple version
self.current_function = ctx.saved_function;
self.current_block = ctx.saved_block;
if let Some(saved) = ctx.saved_var_map {
self.variable_map = saved;
}
self.current_slot_registry = ctx.saved_slot_registry;
Ok(())
}

View File

@ -87,6 +87,11 @@ impl super::MirBuilder {
crate::mir::builder::metadata::propagate::propagate(self, v, pid);
}
self.variable_map.insert(p.clone(), pid);
// 関数スコープ SlotRegistry にも登録しておくよ(観測専用)
if let Some(reg) = self.current_slot_registry.as_mut() {
let ty = self.value_types.get(&pid).cloned();
reg.ensure_slot(p, ty);
}
}
// Lower statements in order to preserve def→use
let lowered = self.cf_block(body.clone());

View File

@ -65,6 +65,14 @@ impl super::MirBuilder {
self.current_function = Some(main_function);
self.current_block = Some(entry_block);
// 関数スコープの SlotRegistry を初期化するよ(観測専用)。
// main 関数用のスロット登録箱として使う想定だよ。
self.current_slot_registry =
Some(crate::mir::region::function_slot_registry::FunctionSlotRegistry::new());
// Region 観測レイヤ: main 関数の FunctionRegion を 1 つ作っておくよ。
crate::mir::region::observer::observe_function_region(self);
// Hint: scope enter at function entry (id=0 for main)
self.hint_scope_enter(0);
@ -335,6 +343,12 @@ impl super::MirBuilder {
module.add_function(f);
}
// main 関数スコープの Region スタックをポップするよ。
crate::mir::region::observer::pop_function_region(self);
// main 関数スコープの SlotRegistry を解放するよ。
self.current_slot_registry = None;
Ok(module)
}
}

View File

@ -230,6 +230,11 @@ impl super::MirBuilder {
eprintln!("[build_local_statement] Inserting '{}' -> {:?} into variable_map", var_name, var_id);
}
self.variable_map.insert(var_name.clone(), var_id);
// SlotRegistry にもローカル変数スロットを登録しておくよ(観測専用)
if let Some(reg) = self.current_slot_registry.as_mut() {
let ty = self.value_types.get(&var_id).cloned();
reg.ensure_slot(&var_name, ty);
}
last_value = Some(var_id);
}
Ok(last_value.unwrap_or_else(|| self.value_gen.next()))
@ -303,6 +308,9 @@ impl super::MirBuilder {
effects: crate::mir::effect::EffectMask::PURE.add(crate::mir::effect::Effect::Io),
})?;
self.variable_map.insert(variable.clone(), future_id);
if let Some(reg) = self.current_slot_registry.as_mut() {
reg.ensure_slot(&variable, None);
}
return Ok(future_id);
}
let expression_value = self.build_expression(expression)?;
@ -312,6 +320,9 @@ impl super::MirBuilder {
value: expression_value,
})?;
self.variable_map.insert(variable.clone(), future_id);
if let Some(reg) = self.current_slot_registry.as_mut() {
reg.ensure_slot(&variable, None);
}
Ok(future_id)
}
@ -343,6 +354,9 @@ impl super::MirBuilder {
};
let me_value = crate::mir::builder::emission::constant::emit_string(self, me_tag);
self.variable_map.insert("me".to_string(), me_value);
if let Some(reg) = self.current_slot_registry.as_mut() {
reg.ensure_slot("me", None);
}
// P0: Known 化 — 分かる範囲で me の起源クラスを付与(挙動不変)。
super::origin::infer::annotate_me_origin(self, me_value);
Ok(me_value)

View File

@ -0,0 +1,98 @@
/*!
* FunctionSlotRegistry 関数スコープのスロット情報を管理する箱だよ。
*
* 目的:
* - 変数名ごとの「スロット」を 1 箇所に集約して管理すること。
* - 各スロットに型情報や RefSlotKind をひも付けられる足場を用意すること。
*
* このフェーズでは観測専用:
* - MIR/SSA の挙動は一切変えないよ。
* - MirBuilder.variable_map や PHI 生成ロジックには影響を与えないよ。
*/
use crate::mir::MirType;
use super::RefSlotKind;
use std::collections::HashMap;
/// 1 関数内でのスロット ID だよ(添字ベースの薄いラッパー)。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SlotId(pub u32);
/// 1 スロットに対応するメタデータだよ。
#[derive(Debug, Clone)]
pub struct SlotInfo {
/// スロット名(変数名)だよ。
pub name: String,
/// MIR 型情報(分かっていれば Someだよ。
pub ty: Option<MirType>,
/// GC/寿命管理の観点から見た種別(まだ観測専用)だよ。
pub ref_kind: Option<RefSlotKind>,
}
/// 関数スコープごとのスロットレジストリだよ。
///
/// - `slots`: SlotId → SlotInfo の順序付き配列
/// - `name_to_slot`: 変数名 → SlotId の逆引き
#[derive(Debug, Default, Clone)]
pub struct FunctionSlotRegistry {
slots: Vec<SlotInfo>,
name_to_slot: HashMap<String, SlotId>,
}
impl FunctionSlotRegistry {
/// 空のレジストリを作るよ。
pub fn new() -> Self {
Self::default()
}
/// スロットを「なければ作る・あれば返す」で確保するよ。
///
/// - name: スロット名(変数名)
/// - ty: 初期の型情報(後から埋めても OK
pub fn ensure_slot(&mut self, name: &str, ty: Option<MirType>) -> SlotId {
if let Some(slot) = self.name_to_slot.get(name).copied() {
// 既存スロットに対しては、型がまだ None で新しい情報があれば埋める程度に留める
if let (Some(new_ty), Some(info)) = (
ty,
self.slots.get_mut(slot.0 as usize),
) {
if info.ty.is_none() {
info.ty = Some(new_ty);
}
}
return slot;
}
let id = SlotId(self.slots.len() as u32);
self.slots.push(SlotInfo {
name: name.to_string(),
ty,
ref_kind: None,
});
self.name_to_slot.insert(name.to_string(), id);
id
}
/// RefSlotKind を後から埋めるためのヘルパーだよ。
pub fn set_ref_kind(&mut self, slot: SlotId, kind: RefSlotKind) {
if let Some(info) = self.slots.get_mut(slot.0 as usize) {
info.ref_kind = Some(kind);
}
}
/// 全スロットを列挙するイテレータだよ(観測専用)。
pub fn iter_slots(&self) -> impl Iterator<Item = &SlotInfo> {
self.slots.iter()
}
/// 名前から SlotId を引くよ。
pub fn get_slot(&self, name: &str) -> Option<SlotId> {
self.name_to_slot.get(name).copied()
}
/// SlotId から SlotInfo を引くよ。
pub fn get_slot_info(&self, slot: SlotId) -> Option<&SlotInfo> {
self.slots.get(slot.0 as usize)
}
}

View File

@ -38,9 +38,10 @@ pub struct SlotMetadata {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RegionId(pub u32);
/// Region の種別Loop / If など)だよ。
/// Region の種別(Function / Loop / If など)だよ。
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RegionKind {
Function,
Loop,
If,
}
@ -70,4 +71,4 @@ impl Region {
}
pub mod observer;
pub mod function_slot_registry;

View File

@ -7,7 +7,10 @@
use crate::mir::builder::MirBuilder;
use crate::mir::control_form::{ControlForm, ControlKind};
use crate::mir::region::{RefSlotKind, Region, RegionId, RegionKind, SlotMetadata};
use crate::mir::region::{
function_slot_registry::FunctionSlotRegistry, RefSlotKind, Region, RegionId, RegionKind,
SlotMetadata,
};
use crate::mir::ValueId;
use std::sync::atomic::{AtomicU32, Ordering};
@ -24,7 +27,7 @@ fn is_region_trace_on() -> bool {
///
/// - 25.1l では StageB 周辺のデバッグが主目的なので、
/// まずは `StageBBodyExtractorBox.*` などに絞って使う想定だよ。
pub fn observe_control_form(builder: &MirBuilder, form: &ControlForm) {
pub fn observe_control_form(builder: &mut MirBuilder, form: &ControlForm) {
if !is_region_trace_on() {
return;
}
@ -57,21 +60,20 @@ pub fn observe_control_form(builder: &MirBuilder, form: &ControlForm) {
let entry_block = form.entry;
let exit_blocks = form.exits.clone();
// 変数スロットは variable_map と value_types から best-effort で推定するよ。
let mut slots: Vec<SlotMetadata> = Vec::new();
// 変数スロットは SlotRegistry があればそれを優先し、なければ
// variable_map と value_types から best-effort で推定するよ。
let slots: Vec<SlotMetadata> = if let Some(reg) = builder.current_slot_registry.as_mut() {
classify_slots_from_registry(reg)
} else {
classify_slots_from_variable_map(builder)
};
for (name, &vid) in builder.variable_map.iter() {
let ref_kind = classify_slot(builder, vid, name.as_str());
slots.push(SlotMetadata {
name: name.clone(),
ref_kind,
});
}
let parent = builder.current_region_stack.last().copied();
let region = Region {
id,
kind,
parent: None,
parent,
entry_block,
exit_blocks,
slots,
@ -83,12 +85,110 @@ pub fn observe_control_form(builder: &MirBuilder, form: &ControlForm) {
);
}
/// 関数エントリ時の Region 観測だよFunctionRegion を 1 つ作ってスタックに積む)。
pub fn observe_function_region(builder: &mut MirBuilder) {
if !is_region_trace_on() {
return;
}
if builder.compilation_context.is_some() {
return;
}
let func_name = builder
.current_function
.as_ref()
.map(|f| f.signature.name.as_str())
.unwrap_or("<unknown>");
// まずは StageB 系だけを対象にしてログ量を抑えるよ。
if !func_name.contains("StageB") {
return;
}
let id = RegionId(NEXT_REGION_ID.fetch_add(1, Ordering::Relaxed));
let entry_block = builder
.current_function
.as_ref()
.map(|f| f.entry_block)
.unwrap_or_else(|| crate::mir::BasicBlockId::new(0));
let region = Region {
id,
kind: RegionKind::Function,
parent: None,
entry_block,
exit_blocks: Vec::new(),
slots: Vec::new(),
};
builder.current_region_stack.push(id);
eprintln!(
"[region/observe] fn={} id={:?} kind={:?} entry={:?} exits={:?} slots={:?}",
func_name, region.id, region.kind, region.entry_block, region.exit_blocks, region.slots
);
}
/// 関数終了時に Region スタックを 1 段ポップするよ。
pub fn pop_function_region(builder: &mut MirBuilder) {
if !is_region_trace_on() {
return;
}
let _ = builder.current_region_stack.pop();
}
fn classify_slots_from_registry(reg: &mut FunctionSlotRegistry) -> Vec<SlotMetadata> {
// まず SlotRegistry 側に RefKind を埋めてもらうよ(型情報+名前ヒューリスティック)。
for info in reg.iter_slots().cloned().collect::<Vec<_>>() {
if info.ref_kind.is_none() {
let kind = info
.ty
.as_ref()
.map(Region::classify_ref_kind)
.unwrap_or_else(|| classify_slot_name_only(info.name.as_str()));
if let Some(id) = reg.get_slot(info.name.as_str()) {
reg.set_ref_kind(id, kind);
}
}
}
let mut out = Vec::new();
for info in reg.iter_slots() {
let ref_kind = info
.ref_kind
.unwrap_or_else(|| classify_slot_name_only(info.name.as_str()));
out.push(SlotMetadata {
name: info.name.clone(),
ref_kind,
});
}
out
}
fn classify_slots_from_variable_map(builder: &MirBuilder) -> Vec<SlotMetadata> {
let mut slots = Vec::new();
for (name, &vid) in builder.variable_map.iter() {
let ref_kind = classify_slot(builder, vid, name.as_str());
slots.push(SlotMetadata {
name: name.clone(),
ref_kind,
});
}
slots
}
fn classify_slot(builder: &MirBuilder, v: ValueId, name: &str) -> RefSlotKind {
if let Some(ty) = builder.value_types.get(&v) {
return Region::classify_ref_kind(ty);
}
// 型情報が無い場合は名前ヒューリスティックで軽く分類する(観測専用)。
classify_slot_name_only(name)
}
fn classify_slot_name_only(name: &str) -> RefSlotKind {
if matches!(
name,
"args"
@ -104,4 +204,3 @@ fn classify_slot(builder: &MirBuilder, v: ValueId, name: &str) -> RefSlotKind {
RefSlotKind::NonRef
}
}