Files
hakorune/src/mir/join_ir/lowering/inline_boundary.rs

204 lines
7.2 KiB
Rust
Raw Normal View History

//! Phase 188-Impl-3: JoinInlineBoundary - Boundary information for JoinIR inlining
//!
//! This module defines the boundary between JoinIR fragments and the host MIR function.
//! It enables clean separation of concerns:
//!
//! - **Box A**: JoinIR Frontend (doesn't know about host ValueIds)
//! - **Box B**: Join→MIR Bridge (converts to MIR using local ValueIds)
//! - **Box C**: JoinInlineBoundary (stores boundary info - THIS FILE)
//! - **Box D**: JoinMirInlineMerger (injects Copy instructions at boundary)
//!
//! ## Design Philosophy
//!
//! The JoinIR lowerer should work with **local ValueIds** (0, 1, 2, ...) without
//! knowing anything about the host function's ValueId space. This ensures:
//!
//! 1. **Modularity**: JoinIR lowerers are pure transformers
//! 2. **Reusability**: Same lowerer can be used in different contexts
//! 3. **Testability**: JoinIR can be tested independently
//! 4. **Correctness**: SSA properties are maintained via explicit Copy instructions
//!
//! ## Example
//!
//! For `loop(i < 3) { print(i); i = i + 1 }`:
//!
//! ```text
//! Host Function:
//! ValueId(4) = Const 0 // i = 0 in host
//!
//! JoinIR Fragment (uses local IDs 0, 1, 2, ...):
//! ValueId(0) = param // i_param (local to JoinIR)
//! ValueId(1) = Const 3
//! ValueId(2) = Compare ...
//!
//! Boundary:
//! join_inputs: [ValueId(0)] // JoinIR's param slot
//! host_inputs: [ValueId(4)] // Host's `i` variable
//!
//! Merged MIR (with Copy injection):
//! entry:
//! ValueId(100) = Copy ValueId(4) // Connect host→JoinIR
//! ValueId(101) = Const 3
//! ...
//! ```
use crate::mir::ValueId;
/// Boundary information for inlining a JoinIR fragment into a host function
///
/// This structure captures the "interface" between a JoinIR fragment and the
/// host function, allowing the merger to inject necessary Copy instructions
/// to connect the two SSA value spaces.
///
/// # Design Note
///
/// This is a **pure data structure** with no logic. All transformation logic
/// lives in the merger (merge_joinir_mir_blocks).
#[derive(Debug, Clone)]
pub struct JoinInlineBoundary {
/// JoinIR-local ValueIds that act as "input slots"
///
/// These are the ValueIds used **inside** the JoinIR fragment to refer
/// to values that come from the host. They should be small sequential
/// IDs (0, 1, 2, ...) since JoinIR lowerers allocate locally.
///
/// Example: For a loop variable `i`, JoinIR uses ValueId(0) as the parameter.
pub join_inputs: Vec<ValueId>,
/// Host-function ValueIds that provide the input values
///
/// These are the ValueIds from the **host function** that correspond to
/// the join_inputs. The merger will inject Copy instructions to connect
/// host_inputs[i] → join_inputs[i].
///
/// Example: If host has `i` as ValueId(4), then host_inputs = [ValueId(4)].
pub host_inputs: Vec<ValueId>,
/// JoinIR-local ValueIds that represent outputs (if any)
///
/// For loops that produce values (e.g., loop result), these are the
/// JoinIR-local ValueIds that should be visible to the host after inlining.
///
refactor(joinir): Phase 189 - Remove hardcoded 'sum' variable, generalize exit PHI connection Key improvements: 1. **Eliminate hardcoded variable name**: Replace hardcoded "sum" with generic ValueId-based variable_map updates. Add new JoinInlineBoundary constructor `new_with_input_and_host_outputs()` for Pattern 3. 2. **Generalize output slot mapping**: Exit PHI result now updates all host_outputs entries in variable_map, not just hardcoded "sum". Prepares for future multi-carrier patterns. 3. **PHI preservation in blocks**: Fix block finalization to preserve existing PHI instructions (from handle_select) instead of overwriting them. 4. **Stable function name→ValueId mapping**: Ensure consistent ValueId assignment for tail call detection across multi-function merges. 5. **Enhanced debugging**: Add detailed logging in block converter and meta analysis for PHI verification. Files modified: - src/mir/builder/control_flow.rs: Remove hardcoded "sum", use boundary outputs - src/mir/join_ir/lowering/inline_boundary.rs: Add new constructor - src/mir/join_ir_vm_bridge/joinir_block_converter.rs: Stable mappings, PHI preservation - src/mir/join_ir_vm_bridge/meta.rs: Debug output for PHI tracking - src/mir/builder/joinir_id_remapper.rs: PHI value remapping - src/mir/builder/joinir_inline_boundary_injector.rs: Span preservation Test status: Pattern 3 (loop_if_phi.hako) still produces correct result (sum=9, RC=9) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: ChatGPT <noreply@openai.com> Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 19:39:54 +09:00
/// Phase 188/189 ではまだ利用していないが、将来的な Multi-carrier パターン
/// (複数の変数を一度に返すループ) のために予約している。
pub join_outputs: Vec<ValueId>,
/// Host-function ValueIds that receive the outputs
///
/// These are the destination ValueIds in the host function that should
refactor(joinir): Phase 189 - Remove hardcoded 'sum' variable, generalize exit PHI connection Key improvements: 1. **Eliminate hardcoded variable name**: Replace hardcoded "sum" with generic ValueId-based variable_map updates. Add new JoinInlineBoundary constructor `new_with_input_and_host_outputs()` for Pattern 3. 2. **Generalize output slot mapping**: Exit PHI result now updates all host_outputs entries in variable_map, not just hardcoded "sum". Prepares for future multi-carrier patterns. 3. **PHI preservation in blocks**: Fix block finalization to preserve existing PHI instructions (from handle_select) instead of overwriting them. 4. **Stable function name→ValueId mapping**: Ensure consistent ValueId assignment for tail call detection across multi-function merges. 5. **Enhanced debugging**: Add detailed logging in block converter and meta analysis for PHI verification. Files modified: - src/mir/builder/control_flow.rs: Remove hardcoded "sum", use boundary outputs - src/mir/join_ir/lowering/inline_boundary.rs: Add new constructor - src/mir/join_ir_vm_bridge/joinir_block_converter.rs: Stable mappings, PHI preservation - src/mir/join_ir_vm_bridge/meta.rs: Debug output for PHI tracking - src/mir/builder/joinir_id_remapper.rs: PHI value remapping - src/mir/builder/joinir_inline_boundary_injector.rs: Span preservation Test status: Pattern 3 (loop_if_phi.hako) still produces correct result (sum=9, RC=9) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: ChatGPT <noreply@openai.com> Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 19:39:54 +09:00
/// receive the values from join_outputs, or (Pattern 3 のような単一
/// キャリアのケースでは) ループ exit PHI の結果を受け取るホスト側の
/// SSA スロットを表す。
///
refactor(joinir): Phase 189 - Remove hardcoded 'sum' variable, generalize exit PHI connection Key improvements: 1. **Eliminate hardcoded variable name**: Replace hardcoded "sum" with generic ValueId-based variable_map updates. Add new JoinInlineBoundary constructor `new_with_input_and_host_outputs()` for Pattern 3. 2. **Generalize output slot mapping**: Exit PHI result now updates all host_outputs entries in variable_map, not just hardcoded "sum". Prepares for future multi-carrier patterns. 3. **PHI preservation in blocks**: Fix block finalization to preserve existing PHI instructions (from handle_select) instead of overwriting them. 4. **Stable function name→ValueId mapping**: Ensure consistent ValueId assignment for tail call detection across multi-function merges. 5. **Enhanced debugging**: Add detailed logging in block converter and meta analysis for PHI verification. Files modified: - src/mir/builder/control_flow.rs: Remove hardcoded "sum", use boundary outputs - src/mir/join_ir/lowering/inline_boundary.rs: Add new constructor - src/mir/join_ir_vm_bridge/joinir_block_converter.rs: Stable mappings, PHI preservation - src/mir/join_ir_vm_bridge/meta.rs: Debug output for PHI tracking - src/mir/builder/joinir_id_remapper.rs: PHI value remapping - src/mir/builder/joinir_inline_boundary_injector.rs: Span preservation Test status: Pattern 3 (loop_if_phi.hako) still produces correct result (sum=9, RC=9) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: ChatGPT <noreply@openai.com> Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 19:39:54 +09:00
/// Phase 188-Impl-3 までは未使用だったが、Phase 189 で
/// loop_if_phi.hako の sum のような「ループの出口で更新されるキャリア」の
/// 再接続に利用する。
pub host_outputs: Vec<ValueId>,
}
impl JoinInlineBoundary {
/// Create a new boundary with input mappings only
///
/// This is the common case for loops like Pattern 1 where:
/// - Inputs: loop variables (e.g., `i` in `loop(i < 3)`)
/// - Outputs: none (loop returns void/0)
pub fn new_inputs_only(join_inputs: Vec<ValueId>, host_inputs: Vec<ValueId>) -> Self {
assert_eq!(
join_inputs.len(),
host_inputs.len(),
"join_inputs and host_inputs must have same length"
);
Self {
join_inputs,
host_inputs,
join_outputs: vec![],
host_outputs: vec![],
}
}
/// Create a new boundary with both inputs and outputs
///
/// Reserved for future loop patterns that produce values.
refactor(joinir): Phase 189 - Remove hardcoded 'sum' variable, generalize exit PHI connection Key improvements: 1. **Eliminate hardcoded variable name**: Replace hardcoded "sum" with generic ValueId-based variable_map updates. Add new JoinInlineBoundary constructor `new_with_input_and_host_outputs()` for Pattern 3. 2. **Generalize output slot mapping**: Exit PHI result now updates all host_outputs entries in variable_map, not just hardcoded "sum". Prepares for future multi-carrier patterns. 3. **PHI preservation in blocks**: Fix block finalization to preserve existing PHI instructions (from handle_select) instead of overwriting them. 4. **Stable function name→ValueId mapping**: Ensure consistent ValueId assignment for tail call detection across multi-function merges. 5. **Enhanced debugging**: Add detailed logging in block converter and meta analysis for PHI verification. Files modified: - src/mir/builder/control_flow.rs: Remove hardcoded "sum", use boundary outputs - src/mir/join_ir/lowering/inline_boundary.rs: Add new constructor - src/mir/join_ir_vm_bridge/joinir_block_converter.rs: Stable mappings, PHI preservation - src/mir/join_ir_vm_bridge/meta.rs: Debug output for PHI tracking - src/mir/builder/joinir_id_remapper.rs: PHI value remapping - src/mir/builder/joinir_inline_boundary_injector.rs: Span preservation Test status: Pattern 3 (loop_if_phi.hako) still produces correct result (sum=9, RC=9) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: ChatGPT <noreply@openai.com> Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 19:39:54 +09:00
///
/// 現在の実装では Multi-carrier 出力には未対応だが、型としては複数出力を
/// 表現できるようにしておく。
#[allow(dead_code)]
pub fn new_with_outputs(
join_inputs: Vec<ValueId>,
host_inputs: Vec<ValueId>,
join_outputs: Vec<ValueId>,
host_outputs: Vec<ValueId>,
) -> Self {
assert_eq!(
join_inputs.len(),
host_inputs.len(),
"join_inputs and host_inputs must have same length"
);
assert_eq!(
join_outputs.len(),
host_outputs.len(),
"join_outputs and host_outputs must have same length"
);
Self {
join_inputs,
host_inputs,
join_outputs,
host_outputs,
}
}
refactor(joinir): Phase 189 - Remove hardcoded 'sum' variable, generalize exit PHI connection Key improvements: 1. **Eliminate hardcoded variable name**: Replace hardcoded "sum" with generic ValueId-based variable_map updates. Add new JoinInlineBoundary constructor `new_with_input_and_host_outputs()` for Pattern 3. 2. **Generalize output slot mapping**: Exit PHI result now updates all host_outputs entries in variable_map, not just hardcoded "sum". Prepares for future multi-carrier patterns. 3. **PHI preservation in blocks**: Fix block finalization to preserve existing PHI instructions (from handle_select) instead of overwriting them. 4. **Stable function name→ValueId mapping**: Ensure consistent ValueId assignment for tail call detection across multi-function merges. 5. **Enhanced debugging**: Add detailed logging in block converter and meta analysis for PHI verification. Files modified: - src/mir/builder/control_flow.rs: Remove hardcoded "sum", use boundary outputs - src/mir/join_ir/lowering/inline_boundary.rs: Add new constructor - src/mir/join_ir_vm_bridge/joinir_block_converter.rs: Stable mappings, PHI preservation - src/mir/join_ir_vm_bridge/meta.rs: Debug output for PHI tracking - src/mir/builder/joinir_id_remapper.rs: PHI value remapping - src/mir/builder/joinir_inline_boundary_injector.rs: Span preservation Test status: Pattern 3 (loop_if_phi.hako) still produces correct result (sum=9, RC=9) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: ChatGPT <noreply@openai.com> Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 19:39:54 +09:00
/// Create a new boundary with inputs and **host outputs only**
///
/// JoinIR 側の exit 値 (k_exit の引数など) を 1 つの PHI にまとめ、
/// その PHI 結果をホスト側の変数スロットへ再接続したい場合に使う。
///
/// 典型例: Pattern 3 (loop_if_phi.hako)
/// - join_inputs : [i_init, sum_init]
/// - host_inputs : [host_i, host_sum]
/// - host_outputs : [host_sum] // ループ exit 時に上書きしたい変数
pub fn new_with_input_and_host_outputs(
join_inputs: Vec<ValueId>,
host_inputs: Vec<ValueId>,
host_outputs: Vec<ValueId>,
) -> Self {
assert_eq!(
join_inputs.len(),
host_inputs.len(),
"join_inputs and host_inputs must have same length"
);
Self {
join_inputs,
host_inputs,
join_outputs: vec![],
host_outputs,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_boundary_inputs_only() {
let boundary = JoinInlineBoundary::new_inputs_only(
vec![ValueId(0)], // JoinIR uses ValueId(0) for loop var
vec![ValueId(4)], // Host has loop var at ValueId(4)
);
assert_eq!(boundary.join_inputs.len(), 1);
assert_eq!(boundary.host_inputs.len(), 1);
assert_eq!(boundary.join_outputs.len(), 0);
assert_eq!(boundary.host_outputs.len(), 0);
}
#[test]
#[should_panic(expected = "join_inputs and host_inputs must have same length")]
fn test_boundary_mismatched_inputs() {
JoinInlineBoundary::new_inputs_only(
vec![ValueId(0), ValueId(1)],
vec![ValueId(4)],
);
}
}