Files
hakorune/src/mir/mod.rs
nyash-codex 5bc0fa861f feat(joinir): Phase 188 Pattern 1 Core Implementation + Phase 189 Planning
Phase 188 Status: Planning & Foundation Complete (100%)

Completed Tasks:
 Task 188-1: Error Inventory (5 patterns identified)
 Task 188-2: Pattern Classification (3 patterns selected)
 Task 188-3: Design (51KB comprehensive blueprint)
 Task 188-4: Implementation Foundation (1,802 lines scaffolding)
 Task 188-5: Verification & Documentation
 Pattern 1 Core Implementation: Detection + Lowering + Routing

Pattern 1 Implementation (322 lines):
- Pattern Detection: is_simple_while_pattern() in loop_pattern_detection.rs
- JoinIR Lowering: lower_simple_while_to_joinir() in simple_while_minimal.rs (219 lines)
  - Generates 3 functions: entry, loop_step (tail-recursive), k_exit
  - Implements condition negation: exit_cond = !(i < 3)
  - Tail-recursive Call pattern with state propagation
- Routing: Added "main" to function routing list in control_flow.rs
- Build:  SUCCESS (0 errors, 34 warnings)

Infrastructure Blocker Identified:
- merge_joinir_mir_blocks() only handles single-function JoinIR modules
- Pattern 1 generates 3 functions (entry + loop_step + k_exit)
- Current implementation only merges first function → loop body never executes
- Root cause: control_flow.rs line ~850 takes only .next() function

Phase 189 Planning Complete:
- Goal: Refactor merge_joinir_mir_blocks() for multi-function support
- Strategy: Sequential Merge (Option A) - merge all functions in call order
- Effort estimate: 5.5-7.5 hours
- Deliverables: README.md (16KB), current-analysis.md (15KB), QUICKSTART.md (5.8KB)

Files Modified/Created:
- src/mir/loop_pattern_detection.rs (+50 lines) - Pattern detection
- src/mir/join_ir/lowering/simple_while_minimal.rs (+219 lines) - Lowering
- src/mir/join_ir/lowering/loop_patterns.rs (+803 lines) - Foundation skeleton
- src/mir/join_ir/lowering/mod.rs (+2 lines) - Module registration
- src/mir/builder/control_flow.rs (+1 line) - Routing fix
- src/mir/builder/loop_frontend_binding.rs (+20 lines) - Binding updates
- tools/test_phase188_foundation.sh (executable) - Foundation verification
- CURRENT_TASK.md (updated) - Phase 188/189 status

Next: Phase 189 implementation (merge_joinir_mir_blocks refactor)

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 07:47:22 +09:00

575 lines
21 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.

/*!
* Nyash MIR (Mid-level Intermediate Representation) - Stage 1 Implementation
*
* ChatGPT5-designed MIR infrastructure for native compilation support
* Based on SSA form with effect tracking and Box-aware optimizations
*/
#[cfg(feature = "aot-plan-import")]
pub mod aot_plan_import;
pub mod basic_block;
pub mod builder;
pub mod definitions; // Unified MIR definitions (MirCall, Callee, etc.)
pub mod effect;
pub mod function;
pub mod instruction;
pub mod instruction_introspection; // Introspection helpers for tests (instruction names)
pub mod instruction_kinds; // small kind-specific metadata (Const/BinOp)
pub mod loop_api; // Minimal LoopBuilder facade (adapter-ready)
pub mod if_in_loop_phi; // Phase 187-2: Minimal if-in-loop PHI emitter (extracted from loop_builder)
pub mod naming; // Static box / entry naming rulesNamingBox
pub mod optimizer;
pub mod ssot; // Shared helpers (SSOT) for instruction lowering
pub mod types; // core MIR enums (ConstValue, Ops, MirType)
pub mod utils; // Phase 15 control flow utilities for root treatment
// pub mod lowerers; // reserved: Stage-3 loop lowering (while/for-range)
pub mod control_form;
pub mod function_emission; // FunctionEmissionBoxMirFunction直編集の発行ヘルパ
pub mod hints; // scaffold: zero-cost guidance (no-op)
pub mod join_ir; // Phase 26-H: 関数正規化IRJoinIR
pub mod join_ir_ops; // Phase 27.8: JoinIR 命令意味箱ops box
pub mod join_ir_runner; // Phase 27.2: JoinIR 実行器(実験用)
pub mod join_ir_vm_bridge; // Phase 27-shortterm S-4: JoinIR → Rust VM ブリッジ
pub mod join_ir_vm_bridge_dispatch; // Phase 30 F-4.4: JoinIR VM ブリッジ dispatch helper
pub mod cfg_extractor; // Phase 154: CFG extraction for hako_check
pub mod loop_form; // ControlForm::LoopShape の薄いエイリアス
pub mod loop_pattern_detection; // Phase 188: Loop pattern detection for JoinIR lowering
pub mod optimizer_passes; // optimizer passes (normalize/diagnostics)
pub mod optimizer_stats; // extracted stats struct
pub mod passes;
pub mod phi_core; // Phase 1 scaffold: unified PHI entry (re-exports only)
pub mod printer;
mod printer_helpers; // internal helpers extracted from printer.rs
pub mod query; // Phase 26-G: MIR read/write/CFGビュー (MirQuery)
pub mod region; // Phase 25.1l: Region/GC観測レイヤLoopForm v2 × RefKind
pub mod slot_registry; // Phase 9.79b.1: method slot resolution (IDs)
mod spanned_instruction;
pub mod value_id;
pub mod value_kind; // Phase 26-A: ValueId型安全化
pub mod verification;
pub mod verification_types; // extracted error types // Optimization subpasses (e.g., type_hints) // Phase 25.1f: Loop/If 共通ビューControlForm
// Re-export main types for easy access
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
pub use builder::MirBuilder;
pub use cfg_extractor::extract_cfg_info; // Phase 154: CFG extraction
pub use definitions::{CallFlags, Callee, MirCall}; // Unified call definitions
pub use effect::{Effect, EffectMask};
pub use function::{FunctionSignature, MirFunction, MirModule};
pub use instruction::MirInstruction;
pub use join_ir_runner::{run_joinir_function, JoinRuntimeError, JoinValue};
pub use optimizer::MirOptimizer;
pub use printer::MirPrinter;
pub use query::{MirQuery, MirQueryBox};
pub use slot_registry::{BoxTypeId, MethodSlot};
pub use spanned_instruction::{SpannedInstRef, SpannedInstruction};
pub use types::{
BarrierOp, BinaryOp, CompareOp, ConstValue, MirType, TypeOpKind, UnaryOp, WeakRefOp,
};
pub use value_id::{LocalId, ValueId, ValueIdGenerator};
pub use value_kind::{MirValueKind, TypedValueId}; // Phase 26-A: ValueId型安全化
pub use verification::MirVerifier;
pub use verification_types::VerificationError;
// Phase 15 control flow utilities (段階的根治戦略)
pub use utils::{
capture_actual_predecessor_and_jump, collect_phi_incoming_if_reachable,
execute_statement_with_termination_check, is_current_block_terminated,
};
/// MIR compilation result
#[derive(Debug, Clone)]
pub struct MirCompileResult {
pub module: MirModule,
pub verification_result: Result<(), Vec<VerificationError>>,
}
/// MIR compiler - converts AST to MIR/SSA form
pub struct MirCompiler {
builder: MirBuilder,
verifier: MirVerifier,
optimize: bool,
}
impl MirCompiler {
/// Create a new MIR compiler
pub fn new() -> Self {
Self {
builder: MirBuilder::new(),
verifier: MirVerifier::new(),
optimize: true,
}
}
/// Create with options
pub fn with_options(optimize: bool) -> Self {
Self {
builder: MirBuilder::new(),
verifier: MirVerifier::new(),
optimize,
}
}
/// Compile AST to MIR module with verification
pub fn compile_with_source(
&mut self,
ast: crate::ast::ASTNode,
source_file: Option<&str>,
) -> Result<MirCompileResult, String> {
if let Some(src) = source_file {
self.builder.set_source_file_hint(src.to_string());
} else {
self.builder.clear_source_file_hint();
}
// Convert AST to MIR using builder
let mut module = self.builder.build_module(ast)?;
if self.optimize {
let mut optimizer = MirOptimizer::new();
let stats = optimizer.optimize_module(&mut module);
if (crate::config::env::opt_diag_fail() || crate::config::env::opt_diag_forbid_legacy())
&& stats.diagnostics_reported > 0
{
return Err(format!(
"Diagnostic failure: {} issues detected (unlowered/legacy)",
stats.diagnostics_reported
));
}
}
// Verify the generated MIR
let verification_result = self.verifier.verify_module(&module);
Ok(MirCompileResult {
module,
verification_result,
})
}
/// Compile AST to MIR module with verification (no source hint).
pub fn compile(&mut self, ast: crate::ast::ASTNode) -> Result<MirCompileResult, String> {
self.compile_with_source(ast, None)
}
/// Dump MIR to string for debugging
pub fn dump_mir(&self, module: &MirModule) -> String {
MirPrinter::new().print_module(module)
}
}
impl Default for MirCompiler {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{ASTNode, LiteralValue};
#[test]
fn test_basic_mir_compilation() {
let mut compiler = MirCompiler::new();
// Create a simple literal AST node
let ast = ASTNode::Literal {
value: LiteralValue::Integer(42),
span: crate::ast::Span::unknown(),
};
// Compile to MIR
let result = compiler.compile(ast);
assert!(result.is_ok(), "Basic MIR compilation should succeed");
let compile_result = result.unwrap();
assert!(
!compile_result.module.functions.is_empty(),
"Module should contain at least one function"
);
}
#[test]
fn test_mir_dump() {
let mut compiler = MirCompiler::new();
let ast = ASTNode::Literal {
value: LiteralValue::Integer(42),
span: crate::ast::Span::unknown(),
};
let result = compiler.compile(ast).unwrap();
let mir_dump = compiler.dump_mir(&result.module);
assert!(!mir_dump.is_empty(), "MIR dump should not be empty");
assert!(
mir_dump.contains("define"),
"MIR dump should contain function definition"
);
}
#[test]
fn test_lowering_is_type_function_call_in_print() {
// Build AST: print(isType(42, "Integer"))
let ast = ASTNode::Print {
expression: Box::new(ASTNode::FunctionCall {
name: "isType".to_string(),
arguments: vec![
ASTNode::Literal {
value: LiteralValue::Integer(42),
span: crate::ast::Span::unknown(),
},
ASTNode::Literal {
value: LiteralValue::String("Integer".to_string()),
span: crate::ast::Span::unknown(),
},
],
span: crate::ast::Span::unknown(),
}),
span: crate::ast::Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile should succeed");
// Ensure TypeOp exists in the resulting MIR
let has_typeop = result.module.functions.values().any(|f| {
f.blocks.values().any(|b| {
b.all_spanned_instructions()
.any(|sp| matches!(sp.inst, MirInstruction::TypeOp { .. }))
})
});
assert!(
has_typeop,
"Expected TypeOp lowering for print(isType(...))"
);
}
#[test]
fn test_lowering_is_method_call_in_print() {
// Build AST: print( (42).is("Integer") )
let ast = ASTNode::Print {
expression: Box::new(ASTNode::MethodCall {
object: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(42),
span: crate::ast::Span::unknown(),
}),
method: "is".to_string(),
arguments: vec![ASTNode::Literal {
value: LiteralValue::String("Integer".to_string()),
span: crate::ast::Span::unknown(),
}],
span: crate::ast::Span::unknown(),
}),
span: crate::ast::Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile should succeed");
// Ensure TypeOp exists in the resulting MIR
let has_typeop = result.module.functions.values().any(|f| {
f.blocks.values().any(|b| {
b.all_spanned_instructions()
.any(|sp| matches!(sp.inst, MirInstruction::TypeOp { .. }))
})
});
assert!(
has_typeop,
"Expected TypeOp lowering for print(obj.is(...))"
);
}
#[test]
#[ignore = "MIR13 migration: extern console.log expectation pending"]
fn test_lowering_extern_console_log() {
// Build AST: console.log("hi") → ExternCall env.console.log
let ast = ASTNode::MethodCall {
object: Box::new(ASTNode::Variable {
name: "console".to_string(),
span: crate::ast::Span::unknown(),
}),
method: "log".to_string(),
arguments: vec![ASTNode::Literal {
value: LiteralValue::String("hi".to_string()),
span: crate::ast::Span::unknown(),
}],
span: crate::ast::Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile should succeed");
let dump = MirPrinter::verbose().print_module(&result.module);
assert!(
dump.contains("extern_call env.console.log"),
"Expected extern_call env.console.log in MIR dump. Got:\n{}",
dump
);
}
#[test]
fn test_lowering_boxcall_array_push() {
// Build AST: (new ArrayBox()).push(1)
let ast = ASTNode::MethodCall {
object: Box::new(ASTNode::New {
class: "ArrayBox".to_string(),
arguments: vec![],
type_arguments: vec![],
span: crate::ast::Span::unknown(),
}),
method: "push".to_string(),
arguments: vec![ASTNode::Literal {
value: LiteralValue::Integer(1),
span: crate::ast::Span::unknown(),
}],
span: crate::ast::Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile should succeed");
let dump = MirPrinter::new().print_module(&result.module);
// Expect a BoxCall to push (printer formats as `call <box>.<method>(...)`)
assert!(
dump.contains(".push("),
"Expected BoxCall to .push(...). Got:\n{}",
dump
);
}
#[test]
#[ignore = "MIR13 migration: method id naming in printer pending"]
fn test_boxcall_method_id_on_universal_slot() {
// Build AST: (new ArrayBox()).toString()
let ast = ASTNode::MethodCall {
object: Box::new(ASTNode::New {
class: "ArrayBox".to_string(),
arguments: vec![],
type_arguments: vec![],
span: crate::ast::Span::unknown(),
}),
method: "toString".to_string(),
arguments: vec![],
span: crate::ast::Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile should succeed");
let dump = MirPrinter::new().print_module(&result.module);
// Expect a BoxCall with numeric method id [#0] for toString universal slot
assert!(
dump.contains("toString[#0]"),
"Expected method_id #0 for toString. Dump:\n{}",
dump
);
}
#[test]
fn test_lowering_await_expression() {
if crate::config::env::mir_core13_pure() {
eprintln!("[TEST] skip await under Core-13 pure mode");
return;
}
// Build AST: await 1 (semantic is nonsensical but should emit Await)
let ast = ASTNode::AwaitExpression {
expression: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: crate::ast::Span::unknown(),
}),
span: crate::ast::Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile should succeed");
let dump = MirPrinter::new().print_module(&result.module);
assert!(
dump.contains("await"),
"Expected await in MIR dump. Got:\n{}",
dump
);
}
// Legacy await / safepoint モデルのテストCore-13/Pure 以降とは挙動差あり).
#[test]
#[ignore]
fn test_await_has_checkpoints() {
if crate::config::env::mir_core13_pure() {
eprintln!("[TEST] skip await under Core-13 pure mode");
return;
}
use crate::ast::{LiteralValue, Span};
// Build: await 1
let ast = ASTNode::AwaitExpression {
expression: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
}),
span: Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile");
// Verifier should pass (await flanked by safepoints)
assert!(
result.verification_result.is_ok(),
"Verifier failed for await checkpoints: {:?}",
result.verification_result
);
let dump = compiler.dump_mir(&result.module);
// Expect at least two safepoints in the function (before/after await)
let sp_count = dump.matches("safepoint").count();
assert!(
sp_count >= 2,
"Expected >=2 safepoints around await, got {}. Dump:\n{}",
sp_count,
dump
);
}
// Legacy await rewrite テスト(現行の Future 統合とは独立にアーカイブ扱い).
#[test]
#[ignore]
fn test_rewritten_await_still_checkpoints() {
if crate::config::env::mir_core13_pure() {
eprintln!("[TEST] skip await under Core-13 pure mode");
return;
}
use crate::ast::{LiteralValue, Span};
// Enable rewrite so Await → ExternCall(env.future.await)
std::env::set_var("NYASH_REWRITE_FUTURE", "1");
let ast = ASTNode::AwaitExpression {
expression: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
}),
span: Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile");
// Verifier should still pass (checkpoint verification includes ExternCall await)
assert!(
result.verification_result.is_ok(),
"Verifier failed for rewritten await checkpoints: {:?}",
result.verification_result
);
let dump = compiler.dump_mir(&result.module);
assert!(
dump.contains("env.future.await"),
"Expected rewritten await extern call. Dump:\n{}",
dump
);
let sp_count = dump.matches("safepoint").count();
assert!(
sp_count >= 2,
"Expected >=2 safepoints around rewritten await, got {}. Dump:\n{}",
sp_count,
dump
);
// Cleanup env
std::env::remove_var("NYASH_REWRITE_FUTURE");
}
#[test]
#[ignore = "MIR13 migration: throw/safepoint expectations pending"]
fn test_throw_compilation() {
let mut compiler = MirCompiler::new();
let throw_ast = ASTNode::Throw {
expression: Box::new(ASTNode::Literal {
value: LiteralValue::String("Test exception".to_string()),
span: crate::ast::Span::unknown(),
}),
span: crate::ast::Span::unknown(),
};
let result = compiler.compile(throw_ast);
assert!(result.is_ok(), "Throw compilation should succeed");
let compile_result = result.unwrap();
let mir_dump = compiler.dump_mir(&compile_result.module);
assert!(
mir_dump.contains("throw"),
"MIR should contain throw instruction"
);
assert!(
mir_dump.contains("safepoint"),
"MIR should contain safepoint instruction"
);
}
#[test]
#[ignore = "MIR13 migration: loop safepoint expectation pending"]
fn test_loop_compilation() {
let mut compiler = MirCompiler::new();
let loop_ast = ASTNode::Loop {
condition: Box::new(ASTNode::Literal {
value: LiteralValue::Bool(true),
span: crate::ast::Span::unknown(),
}),
body: vec![ASTNode::Print {
expression: Box::new(ASTNode::Literal {
value: LiteralValue::String("Loop body".to_string()),
span: crate::ast::Span::unknown(),
}),
span: crate::ast::Span::unknown(),
}],
span: crate::ast::Span::unknown(),
};
let result = compiler.compile(loop_ast);
assert!(result.is_ok(), "Loop compilation should succeed");
let compile_result = result.unwrap();
let mir_dump = compiler.dump_mir(&compile_result.module);
assert!(
mir_dump.contains("br"),
"MIR should contain branch instructions"
);
assert!(
mir_dump.contains("safepoint"),
"MIR should contain safepoint instructions"
);
}
#[test]
fn test_try_catch_compilation() {
// Core-13 pure モードでは Try/Catch 命令は許容集合外のためスキップ
if crate::config::env::mir_core13_pure() {
eprintln!("[TEST] skip try/catch under Core-13 pure mode");
return;
}
let mut compiler = MirCompiler::new();
let try_catch_ast = ASTNode::TryCatch {
try_body: vec![ASTNode::Print {
expression: Box::new(ASTNode::Literal {
value: LiteralValue::String("Try block".to_string()),
span: crate::ast::Span::unknown(),
}),
span: crate::ast::Span::unknown(),
}],
catch_clauses: vec![crate::ast::CatchClause {
exception_type: Some("Exception".to_string()),
variable_name: Some("e".to_string()),
body: vec![ASTNode::Print {
expression: Box::new(ASTNode::Literal {
value: LiteralValue::String("Catch block".to_string()),
span: crate::ast::Span::unknown(),
}),
span: crate::ast::Span::unknown(),
}],
span: crate::ast::Span::unknown(),
}],
finally_body: None,
span: crate::ast::Span::unknown(),
};
let result = compiler.compile(try_catch_ast);
assert!(result.is_ok(), "TryCatch compilation should succeed");
let compile_result = result.unwrap();
let mir_dump = compiler.dump_mir(&compile_result.module);
assert!(
mir_dump.contains("catch"),
"MIR should contain catch instruction"
);
}
}