Files
hakorune/src/mir/loop_builder/loop_form.rs
nyash-codex 0c527dcd22 feat(runtime): Phase 101-A dev-debug ログの Ring0.log 統一 - 34箇所完了
## Phase 101-A 完了項目
-  llvm.rs: 13箇所([joinir/llvm], [parse/context]) → Ring0.log
-  loop_form.rs: [loopform] 系ログ → Ring0.log
-  loopform_builder.rs: 16箇所([loopform/prepare], [loopform/seal_phis]) → Ring0.log
-  loop_snapshot_merge.rs: 5箇所([Option C]) → Ring0.log
-  全テストPASS(ビルド成功)

## 置き換え箇所(34箇所)

**llvm.rs**(13箇所):
- [joinir/llvm] JoinIR 実験パスログ(12箇所)
- [parse/context] プリロードファイルリスト(1箇所)

**loop_form.rs**(複数箇所):
- [loopform] 基本ログ
- [loopform/condition] 条件式処理
- [loopform/writes] 変数書き込み収集

**loopform_builder.rs**(16箇所):
- [loopform/prepare] 構造準備
- [loopform/seal_phis] PHI シーリング処理

**loop_snapshot_merge.rs**(5箇所):
- [Option C] Exit PHI 分類
- [Option C] 変数解析

## 技術的成果
- Ring0.log で dev-debug ログを一元管理
- stderr の cleanness 向上(ユーザー向けメッセージのみ)
- 環境に応じた出力制御が可能(NYASH_LOOPFORM_DEBUG等)
- Phase 99-100 で確立した 3層設計を実装レベルで完成

## 実装パターン
```rust
// Before
eprintln!("[loopform] variable_map: {:?}", map);

// After
crate::runtime::get_global_ring0().log.debug(&format!(
    "[loopform] variable_map: {:?}", map
));
```

## 統計
- Phase 98: 7箇所(ConsoleService)
- Phase 100: 29箇所(ConsoleService)
- Phase 101-A: 34箇所(Ring0.log)
- **合計**: 70箇所で統一(ConsoleService/Ring0.log)
- 残り: ~905箇所(test含む)

## ドキュメント更新
- logging_policy.md: Section 7-A 追加(Phase 101-A 実装記録)
- ring0-inventory.md: Category 2 更新(dev-debug 進捗反映)
- CURRENT_TASK.md: Phase 85 セクション追記

## Phase 85-101-A 総括
- Phase 95.5-97: CoreServices 6個完全実装(String/Integer/Bool/Array/Map/Console)
- Phase 98-98.5: ConsoleService 代表パス拡張(7箇所)
- Phase 99: ログ/出力ポリシー確定(3層設計文書化)
- Phase 100: user-facing 出力の ConsoleService 化(29箇所)
- Phase 101-A: dev-debug ログの Ring0.log 統一(34箇所) 

次: Phase 101-B(internal/test ログの整理、別検討)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 12:25:32 +09:00

579 lines
27 KiB
Rust
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.

use super::{ConstValue, LoopBuilder, ValueId};
use crate::ast::ASTNode;
use crate::mir::control_form::{is_control_form_trace_on, ControlForm, LoopShape};
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
use crate::mir::utils::is_current_block_terminated;
use crate::mir::{BasicBlockId, MirInstruction};
use std::collections::{BTreeMap, BTreeSet};
impl<'a> LoopBuilder<'a> {
/// SSA形式でループを構築 (LoopForm v2 only)
pub fn build_loop(
&mut self,
condition: ASTNode,
body: Vec<ASTNode>,
) -> Result<ValueId, String> {
// Phase 7-F: Legacy loop builder removed - LoopForm v2 is now the only implementation
self.build_loop_with_loopform(condition, body)
}
/// SSA形式でループを構築 (LoopFormBuilder implementation)
fn build_loop_with_loopform(
&mut self,
condition: ASTNode,
body: Vec<ASTNode>,
) -> Result<ValueId, String> {
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!("[build_loop_with_loopform] === ENTRY ===");
if let Some(ref func) = self.parent_builder.current_function {
eprintln!(
"[build_loop_with_loopform] fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _
);
}
eprintln!("[build_loop_with_loopform] condition={:?}", condition);
eprintln!("[build_loop_with_loopform] body.len()={}", body.len());
}
// Create loop structure blocks following LLVM canonical form
// We need a dedicated preheader block to materialize loop entry copies
let before_loop_id = self.current_block()?;
// Capture variable snapshot BEFORE creating new blocks (at loop entry point)
let current_vars = self.get_current_variable_map();
// DEBUG: Show variable map before guard check
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
crate::runtime::get_global_ring0().log.debug(&format!(
"[loopform] before_loop_id={:?}, variable_map size={}",
before_loop_id,
current_vars.len()
));
for (name, value) in &current_vars {
crate::runtime::get_global_ring0().log.debug(&format!(" {} -> {:?}", name, value));
}
}
// Phase 25.3: GUARD check removed - ValueId(0) is valid for first parameters
// Previous code incorrectly assumed ValueId(0) always meant uninitialized variables,
// but it's actually the correct ID for the first parameter in functions like:
// skip_whitespace(s, idx) -> s=ValueId(0), idx=ValueId(1)
// This caused loops in such functions to be entirely skipped.
let preheader_id = self.new_block();
let header_id = self.new_block();
let body_id = self.new_block();
let latch_id = self.new_block();
let exit_id = self.new_block();
// Phase 25.1q: canonical continue merge block
// All continue 文は一度このブロックに集約してから header へ戻る。
let continue_merge_id = self.new_block();
// Jump from current block to preheader
let entry_block = self.current_block()?;
self.emit_jump(preheader_id)?;
// 📦 Hotfix 6: Add CFG predecessor for preheader (same as legacy version)
crate::mir::builder::loops::add_predecessor(
self.parent_builder,
preheader_id,
entry_block,
)?;
// Initialize LoopFormBuilder with preheader and header blocks
let mut loopform = LoopFormBuilder::new(preheader_id, header_id);
// Pass 1: Prepare structure (allocate all ValueIds upfront)
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
crate::runtime::get_global_ring0().log.debug(&format!(
"[loopform] Block IDs: preheader={:?}, header={:?}, body={:?}, latch={:?}, exit={:?}",
preheader_id, header_id, body_id, latch_id, exit_id
));
crate::runtime::get_global_ring0().log.debug(&format!(
"[loopform] variable_map at loop entry (size={}):",
current_vars.len()
));
let mut loop_count = 0;
for (name, value) in &current_vars {
loop_count += 1;
crate::runtime::get_global_ring0().log.debug(&format!(" [{}] {} -> {:?}", loop_count, name, value));
// Phase 26-A-4: ValueIdベース判定に変更名前ベース → 型安全)
let is_param = self.is_parameter(*value);
crate::runtime::get_global_ring0().log.debug(&format!(" param={}", is_param));
}
crate::runtime::get_global_ring0().log.debug(&format!("[loopform] iterated {} times", loop_count));
if let Some(ref func) = self.parent_builder.current_function {
crate::runtime::get_global_ring0().log.debug(&format!(
"[loopform] BEFORE prepare_structure: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _
));
} else {
crate::runtime::get_global_ring0().log.debug("[loopform] BEFORE prepare_structure: current_function=None");
}
}
loopform.prepare_structure(self, &current_vars)?;
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
if let Some(ref func) = self.parent_builder.current_function {
crate::runtime::get_global_ring0().log.debug(&format!(
"[loopform] AFTER prepare_structure: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _
));
} else {
crate::runtime::get_global_ring0().log.debug("[loopform] AFTER prepare_structure: current_function=None");
}
}
// Pass 2: Emit preheader (copies and jump to header)
loopform.emit_preheader(self)?;
// 📦 Hotfix 6: Add CFG predecessor for header from preheader (same as legacy version)
crate::mir::builder::loops::add_predecessor(self.parent_builder, header_id, preheader_id)?;
// Pass 3: Emit header PHIs (incomplete, only preheader edge)
self.set_current_block(header_id)?;
// Ensure header block exists before emitting PHIs
self.parent_builder.ensure_block_exists(header_id)?;
// Phase 27.4-C Refactor: JoinIR Loop φ バイパスフラグ統一取得
let fn_name = self
.parent_builder
.current_function
.as_ref()
.map(|f| f.signature.name.clone())
.unwrap_or_default();
let bypass_flags = crate::mir::phi_core::loopform_builder::get_loop_bypass_flags(&fn_name);
if bypass_flags.header {
// Phase 27.4-C: JoinIR 実験経路では Header φ を生成しない。
// Pinned/Carrier の値は preheader の copy をそのまま使う。
//
// ⚠️ 重要: このモードでは MIR は不完全(φ 抜けであり、VM で実行できない。
// JoinIR runner 専用モードであることに注意。
if crate::mir::phi_core::loopform_builder::is_loopform_debug_enabled() {
crate::runtime::get_global_ring0().log.debug(&format!("[loopform/27.4-C] Header φ bypass active for: {}", fn_name));
crate::runtime::get_global_ring0().log.debug("[loopform/27.4-C] Skipping emit_header_phis() - using preheader values directly");
}
} else {
// 従来どおり HeaderPhiBuilder を使って φ を準備
loopform.emit_header_phis(self)?;
}
if crate::mir::phi_core::loopform_builder::is_loopform_debug_enabled() {
crate::runtime::get_global_ring0().log.debug("[loopform] variable_map after emit_header_phis:");
for (name, value) in self.get_current_variable_map().iter() {
crate::runtime::get_global_ring0().log.debug(&format!(" {} -> {:?}", name, value));
}
}
// Set up loop context for break/continue
crate::mir::builder::loops::push_loop_context(self.parent_builder, header_id, exit_id);
self.loop_header = Some(header_id);
// 既定の continue 先を canonical continue_merge ブロックにする。
// ここを差し替えることで do_loop_exit(Continue) のターゲットを一元化する。
self.continue_target = Some(continue_merge_id);
self.continue_snapshots.clear();
self.exit_snapshots.clear();
// [LoopForm] header-cond: cond true → body, false → exit (Case A/B)
// - Case A: loop(i < n) → header can branch to exit directly
// - Case B: loop(1 == 1) → header always enters body, exit only via break
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
crate::runtime::get_global_ring0().log.debug(&format!(
"[loopform/condition] BEFORE build_expression: current_block={:?}",
self.current_block()?
));
if let Some(ref func) = self.parent_builder.current_function {
crate::runtime::get_global_ring0().log.debug(&format!(
"[loopform/condition] BEFORE: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _
));
}
}
let cond_value = self.parent_builder.build_expression(condition)?;
// Capture the ACTUAL block that emits the branch (might differ from header_id
// if build_expression created new blocks)
let branch_source_block = self.current_block()?;
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
crate::runtime::get_global_ring0().log.debug(&format!(
"[loopform/condition] AFTER build_expression: branch_source_block={:?}",
branch_source_block
));
if let Some(ref func) = self.parent_builder.current_function {
crate::runtime::get_global_ring0().log.debug(&format!(
"[loopform/condition] AFTER: fn='{}', counter={}, func_ptr={:p}",
func.signature.name, func.next_value_id, func as *const _
));
}
}
self.emit_branch(cond_value, body_id, exit_id)?;
// 📦 Hotfix 6: Add CFG predecessors for branch targets (Cytron et al. 1991 requirement)
// This ensures exit_block.predecessors is populated before Exit PHI generation
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
crate::runtime::get_global_ring0().log.debug(&format!(
"[loopform/condition] BEFORE add_predecessor: exit_id={:?}, branch_source={:?}",
exit_id, branch_source_block
));
}
crate::mir::builder::loops::add_predecessor(
self.parent_builder,
body_id,
branch_source_block,
)?;
crate::mir::builder::loops::add_predecessor(
self.parent_builder,
exit_id,
branch_source_block,
)?;
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
crate::runtime::get_global_ring0().log.debug(&format!(
"[loopform/condition] AFTER emit_branch: current_block={:?}",
self.current_block()?
));
crate::runtime::get_global_ring0().log.debug(&format!(
"[loopform/condition] Added predecessors: body={:?} exit={:?} from={:?}",
body_id, exit_id, branch_source_block
));
// Verify predecessors were added
if let Some(ref func) = self.parent_builder.current_function {
if let Some(exit_block) = func.blocks.get(&exit_id) {
crate::runtime::get_global_ring0().log.debug(&format!(
"[loopform/condition] exit_block.predecessors = {:?}",
exit_block.predecessors
));
}
}
}
// Lower loop body
self.set_current_block(body_id)?;
for stmt in body {
self.build_statement(stmt)?;
if is_current_block_terminated(self.parent_builder)? {
break;
}
}
// Capture variable snapshot at end of body (before jumping to latch)
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 {
// Skip __pin$ temporary variables - they are always BodyLocalInternal
// (Task先生の発見: これらをcarrier扱いすると未定義ValueIdエラーの原因になる)
if name.starts_with("__pin$") && name.contains("$@") {
continue;
}
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() {
crate::runtime::get_global_ring0().log.debug("[loopform/writes] === WRITES COLLECTION (Step 5-1) ===");
crate::runtime::get_global_ring0().log.debug(&format!(
"[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 {
crate::runtime::get_global_ring0().log.debug(&format!("[loopform/writes] WRITE: {}", name));
}
}
// Jump to latch if not already terminated
let actual_latch_id = if !is_current_block_terminated(self.parent_builder)? {
self.emit_jump(latch_id)?;
latch_id
} else {
// Body is terminated (break/continue), use current block as latch
self.current_block()?
};
// Latch: jump back to header
self.set_current_block(latch_id)?;
// Update variable map with body end values for sealing
for (name, value) in &body_end_vars {
self.update_variable(name.clone(), *value);
}
self.emit_jump(header_id)?;
// 📦 Hotfix 6: Add CFG predecessor for header from latch (same as legacy version)
crate::mir::builder::loops::add_predecessor(self.parent_builder, header_id, latch_id)?;
// Phase 25.1c/k: body-local 変数の PHI 生成
// BreakFinderBox / FuncScannerBox 等で、loop body 内で新規宣言された local 変数が
// loop header に戻った時に undefined になる問題を修正
//
// Step 5-5-B: EXPERIMENTAL - Body-local Header PHI generation DISABLED
// Reason: Option C design states body-local variables should NOT have header PHIs
// - BodyLocalExit: needs EXIT PHI only, NOT header PHI
// - BodyLocalInternal: needs NO PHI at all
//
// TODO Step 5-3: Integrate Option C classification (LoopScopeShape) here
//
// TEMPORARY DISABLE to test hypothesis that header PHIs are the root cause
let trace_loop_phi = std::env::var("HAKO_LOOP_PHI_TRACE").ok().as_deref() == Some("1");
// DISABLED: Body-local header PHI generation
// This code was causing undefined value errors because it created header PHIs
// for variables that should only have exit PHIs (or no PHIs at all)
if false { // Disabled for Step 5-5-B experiment
// [Original code removed - see git history if needed]
}
// Pass 4: Generate continue_merge PHIs first, then seal header PHIs
// Phase 25.1c/k: canonical continue_merge ブロックで PHI を生成してから seal_phis を呼ぶ
let raw_continue_snaps = self.continue_snapshots.clone();
// Step 1: continue_merge ブロックで PHI 生成merged_snapshot を作る)
// Phase 25.2: LoopSnapshotMergeBox を使って整理
self.set_current_block(continue_merge_id)?;
let merged_snapshot: BTreeMap<String, ValueId> = if !raw_continue_snaps.is_empty() {
if trace_loop_phi {
eprintln!(
"[loop-phi/continue-merge] Generating PHI nodes for {} continue paths",
raw_continue_snaps.len()
);
}
// すべての continue snapshot に現れる変数を収集
let mut all_vars: BTreeMap<String, Vec<(BasicBlockId, ValueId)>> = BTreeMap::new();
for (continue_bb, snapshot) in &raw_continue_snaps {
for (var_name, &value) in snapshot {
all_vars
.entry(var_name.clone())
.or_default()
.push((*continue_bb, value));
}
}
// 各変数について PHI ノードを生成
// ========================================
// Phase 59b: PhiInputCollector インライン化
// ========================================
let mut merged = BTreeMap::new();
for (var_name, inputs) in all_vars {
// Step 1: sanitize (BTreeMap で重複削除&ソート)
let mut sanitized: BTreeMap<BasicBlockId, ValueId> = BTreeMap::new();
for (bb, val) in &inputs {
sanitized.insert(*bb, *val);
}
let final_inputs: Vec<(BasicBlockId, ValueId)> = sanitized.into_iter().collect();
// Step 2: optimize_same_value
let same_value = if final_inputs.is_empty() {
None
} else if final_inputs.len() == 1 {
Some(final_inputs[0].1)
} else {
let first_val = final_inputs[0].1;
if final_inputs.iter().all(|(_, val)| *val == first_val) {
Some(first_val)
} else {
None
}
};
// Step 3: PHI 生成 or 同一値を使用
let result_value = if let Some(same_val) = same_value {
// 全て同じ値 or 単一入力 → PHI 不要
same_val
} else {
// 異なる値を持つ場合は PHI ノードを生成
let phi_id = self.new_value();
if let Some(ref mut func) = self.parent_builder.current_function {
if let Some(merge_block) = func.blocks.get_mut(&continue_merge_id) {
merge_block.add_instruction(MirInstruction::Phi {
dst: phi_id,
inputs: final_inputs,
type_hint: None, // Phase 63-6
});
}
}
if trace_loop_phi {
eprintln!(
"[loop-phi/continue-merge] Generated PHI for '{}': {:?}",
var_name, phi_id
);
}
phi_id
};
merged.insert(var_name, result_value);
}
// Note: 変数マップへの反映は seal_phis に委譲(干渉を避ける)
if trace_loop_phi {
eprintln!(
"[loop-phi/continue-merge] Merged {} variables from {} paths",
merged.len(),
raw_continue_snaps.len()
);
}
merged
} else {
BTreeMap::new()
};
self.emit_jump(header_id)?;
crate::mir::builder::loops::add_predecessor(
self.parent_builder,
header_id,
continue_merge_id,
)?;
// Step 2: merged_snapshot を使って seal_phis を呼ぶ
// Phase 25.3: Continue merge PHI実装Task先生の発見
// - continueが無いループでも、Latchブロックの値をHeader PHIに伝播する必要がある
// - これにより、Exit PHIがHeader PHI経由で正しい値を受け取れる
let continue_snaps: Vec<(BasicBlockId, BTreeMap<String, ValueId>)> = {
// まず、merged_snapshotcontinue merge PHI結果を追加
let mut snaps = if !merged_snapshot.is_empty() {
vec![(continue_merge_id, merged_snapshot.clone())]
} else {
vec![]
};
// continueが無い場合でも、Latchブロックのスナップショットを追加
// これにより、seal_phis()がLatchからの値をHeader PHIに正しく接続できる
if raw_continue_snaps.is_empty() {
// continue文が無い場合、Latchブロックの現在の変数マップをキャプチャ
// Note: このタイミングでは current_block == exit_id だが、
// variable_map はLatch実行後の状態を保持している
let latch_snapshot = self.get_current_variable_map();
snaps.push((actual_latch_id, latch_snapshot));
}
snaps
};
// Phase 27.4C Refactor: Header φ バイパスフラグを統一取得seal_phis に渡す)
// Note: fn_name は既に line 299-304 で取得済み、String として保持されている
let bypass_flags_for_seal =
crate::mir::phi_core::loopform_builder::get_loop_bypass_flags(&fn_name);
// Step 5-1/5-2: Pass writes 集合 for PHI縮約
// Phase 27.4C: header_bypass フラグも渡す
loopform.seal_phis(
self,
actual_latch_id,
&continue_snaps,
&writes,
bypass_flags_for_seal.header,
)?;
// Step 3: seal body-local PHIs (complete the inputs)
// Step 5-5-A: REMOVED - PHIs now created complete with both inputs upfront
// Old sealing code was overwriting our preheader+latch inputs with latch-only,
// causing "phi pred mismatch" errors.
//
// Body-local PHIs are now created at line 408-456 with BOTH inputs:
// - preheader: poison value (variable doesn't exist yet)
// - latch: actual value from loop body
//
// No further sealing is needed!
// Exit block
self.set_current_block(exit_id)?;
// Phase 25.1h: ControlForm統合版に切り替え
// continue / break のターゲットブロックをユニーク化して収集
// Phase 25.1: HashSet → BTreeSet決定性確保
let mut break_set: BTreeSet<BasicBlockId> = BTreeSet::new();
for (bb, _) in &self.exit_snapshots {
break_set.insert(*bb);
}
// LoopShape の continue_targets は「header への canonical backedge」を表す。
// continue が一つ以上存在する場合は continue_merge_id を 1 つだけ登録する。
let continue_targets: Vec<BasicBlockId> = if self.continue_snapshots.is_empty() {
Vec::new()
} else {
vec![continue_merge_id]
};
let break_targets: Vec<BasicBlockId> = break_set.into_iter().collect();
let loop_shape = LoopShape {
preheader: preheader_id,
header: header_id,
body: body_id,
latch: latch_id,
exit: exit_id,
continue_targets,
break_targets,
};
let form = ControlForm::from_loop(loop_shape.clone());
// Region/GC 観測レイヤPhase 25.1l:
// NYASH_REGION_TRACE=1 のときだけ、StageB 周辺ループの
// Region 情報entry/exit/slotsをログに出すよ。
crate::mir::region::observer::observe_control_form(self.parent_builder, &form);
// Phase 27.6-2: JoinIR Exit φ バイパスチェック
let fn_name = self
.parent_builder
.current_function
.as_ref()
.map(|f| f.signature.name.as_str())
.unwrap_or("");
let exit_bypass = crate::mir::phi_core::loopform_builder::joinir_exit_bypass_enabled()
&& crate::mir::phi_core::loopform_builder::is_joinir_exit_bypass_target(fn_name);
if exit_bypass {
// Phase 27.6-2: JoinIR 実験経路では Exit φ を生成しない。
// ループ内で定義された値だけで exit 後を構成するJoinIR の k_exit 引数として表現)。
//
// ⚠️ 重要: このモードでは MIR は不完全Exit φ 抜けであり、VM で実行できない。
// JoinIR runner 専用モードであることに注意。
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
eprintln!(
"[loopform/exit-bypass] func={} exit={:?} header={:?} (JoinIR experiment only)",
fn_name, exit_id, header_id
);
}
} else {
// [LoopForm] exit PHI for Case A/B (uses exit_snapshots + exit_preds)
// - Case A: header+break → exit PHI includes both paths
// - Case B: break-only → exit PHI excludes header (not a predecessor)
let exit_snaps = self.exit_snapshots.clone();
crate::mir::phi_core::loopform_builder::build_exit_phis_for_control(
&loopform,
self,
&form,
&exit_snaps,
branch_source_block,
)?;
}
// Pop loop context
crate::mir::builder::loops::pop_loop_context(self.parent_builder);
// ControlForm 観測: 環境フラグ未設定時は既定ONのとき LoopShape をダンプ
if is_control_form_trace_on() {
form.debug_dump();
#[cfg(debug_assertions)]
if let Some(ref func) = self.parent_builder.current_function {
loop_shape.debug_validate(func);
}
}
// Return void value
let void_dst = self.new_value();
self.emit_const(void_dst, ConstValue::Void)?;
Ok(void_dst)
}
}