selfhost/runtime: Stage 0-1 runner + MIR JSON loader (summary) with trace; compiler: scopebox/loopform prepass wiring (flags, child args); libs: add P1 standard boxes (console/string/array/map) as thin wrappers; runner: pass --box-pref via env; ops_calls dispatcher skeleton; docs: selfhost executor roadmap + scopebox/loopform notes; smokes: selfhost runner + identity prepasses; CURRENT_TASK: update plan and box lib schedule
This commit is contained in:
@ -5,6 +5,9 @@
|
||||
*/
|
||||
|
||||
use super::{Effect, EffectMask, ValueId};
|
||||
use crate::mir::types::{
|
||||
BarrierOp, BinaryOp, CompareOp, ConstValue, MirType, TypeOpKind, UnaryOp, WeakRefOp,
|
||||
};
|
||||
// use crate::value::NyashValue; // Commented out to avoid circular dependency
|
||||
use std::fmt;
|
||||
|
||||
@ -291,76 +294,7 @@ pub enum MirInstruction {
|
||||
},
|
||||
}
|
||||
|
||||
/// Constant values in MIR
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ConstValue {
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
String(String),
|
||||
Null,
|
||||
Void,
|
||||
}
|
||||
|
||||
/// Binary operations
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BinaryOp {
|
||||
// Arithmetic
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Mod,
|
||||
|
||||
// Bitwise
|
||||
BitAnd,
|
||||
BitOr,
|
||||
BitXor,
|
||||
Shl,
|
||||
Shr,
|
||||
|
||||
// Logical
|
||||
And,
|
||||
Or,
|
||||
}
|
||||
|
||||
/// Unary operations
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum UnaryOp {
|
||||
// Arithmetic
|
||||
Neg,
|
||||
|
||||
// Logical
|
||||
Not,
|
||||
|
||||
// Bitwise
|
||||
BitNot,
|
||||
}
|
||||
|
||||
/// Comparison operations
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CompareOp {
|
||||
Eq,
|
||||
Ne,
|
||||
Lt,
|
||||
Le,
|
||||
Gt,
|
||||
Ge,
|
||||
}
|
||||
|
||||
/// MIR type system
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum MirType {
|
||||
Integer,
|
||||
Float,
|
||||
Bool,
|
||||
String,
|
||||
Box(String), // Box type with name
|
||||
Array(Box<MirType>),
|
||||
Future(Box<MirType>), // Future containing a type
|
||||
Void,
|
||||
Unknown,
|
||||
}
|
||||
// types moved to crate::mir::types
|
||||
|
||||
impl MirInstruction {
|
||||
/// Get the effect mask for this instruction
|
||||
@ -587,26 +521,7 @@ impl MirInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/// Kind of unified type operation (PoC)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TypeOpKind {
|
||||
Check,
|
||||
Cast,
|
||||
}
|
||||
|
||||
/// Kind of unified weak reference operation (PoC)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum WeakRefOp {
|
||||
New,
|
||||
Load,
|
||||
}
|
||||
|
||||
/// Kind of unified barrier operation (PoC)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BarrierOp {
|
||||
Read,
|
||||
Write,
|
||||
}
|
||||
// enums TypeOpKind/WeakRefOp/BarrierOp moved to crate::mir::types
|
||||
|
||||
impl ConstValue {
|
||||
/*
|
||||
@ -767,18 +682,7 @@ impl fmt::Display for MirInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ConstValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ConstValue::Integer(n) => write!(f, "{}", n),
|
||||
ConstValue::Float(fl) => write!(f, "{}", fl),
|
||||
ConstValue::Bool(b) => write!(f, "{}", b),
|
||||
ConstValue::String(s) => write!(f, "\"{}\"", s),
|
||||
ConstValue::Null => write!(f, "null"),
|
||||
ConstValue::Void => write!(f, "void"),
|
||||
}
|
||||
}
|
||||
}
|
||||
// Display for ConstValue moved to crate::mir::types
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -6,9 +6,10 @@
|
||||
//! in `MirInstruction`.
|
||||
|
||||
use super::{BasicBlockId, ConstValue, Effect, EffectMask, ValueId};
|
||||
use crate::mir::instruction::{
|
||||
BarrierOp as MirBarrierOp, BinaryOp as MirBinOp, MirInstruction, MirType,
|
||||
TypeOpKind as MirTypeOpKind, WeakRefOp as MirWeakRefOp,
|
||||
use crate::mir::instruction::MirInstruction;
|
||||
use crate::mir::types::{
|
||||
BarrierOp as MirBarrierOp, BinaryOp as MirBinOp, MirType, TypeOpKind as MirTypeOpKind,
|
||||
WeakRefOp as MirWeakRefOp,
|
||||
};
|
||||
|
||||
// Local macro utilities for generating InstructionMeta boilerplate.
|
||||
@ -114,6 +115,13 @@ pub fn effects_via_meta(i: &MirInstruction) -> Option<EffectMask> {
|
||||
if let Some(k) = JumpInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = PrintInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = DebugInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = TypeCheckInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = CopyInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = NopInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = ThrowInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = CatchInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = SafepointInst::from_mir(i) { return Some(k.effects()); }
|
||||
if let Some(k) = FunctionNewInst::from_mir(i) { return Some(k.effects()); }
|
||||
None
|
||||
}
|
||||
|
||||
@ -136,6 +144,13 @@ pub fn dst_via_meta(i: &MirInstruction) -> Option<ValueId> {
|
||||
if let Some(_k) = PrintInst::from_mir(i) { return None; }
|
||||
if let Some(_k) = DebugInst::from_mir(i) { return None; }
|
||||
if let Some(k) = CallLikeInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(k) = TypeCheckInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(k) = CopyInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(_k) = NopInst::from_mir(i) { return None; }
|
||||
if let Some(_k) = ThrowInst::from_mir(i) { return None; }
|
||||
if let Some(k) = CatchInst::from_mir(i) { return k.dst(); }
|
||||
if let Some(_k) = SafepointInst::from_mir(i) { return None; }
|
||||
if let Some(k) = FunctionNewInst::from_mir(i) { return k.dst(); }
|
||||
None
|
||||
}
|
||||
|
||||
@ -158,6 +173,13 @@ pub fn used_via_meta(i: &MirInstruction) -> Option<Vec<ValueId>> {
|
||||
if let Some(k) = PrintInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = DebugInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = CallLikeInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = TypeCheckInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = CopyInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = NopInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = ThrowInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = CatchInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = SafepointInst::from_mir(i) { return Some(k.used()); }
|
||||
if let Some(k) = FunctionNewInst::from_mir(i) { return Some(k.used()); }
|
||||
None
|
||||
}
|
||||
|
||||
@ -369,6 +391,17 @@ inst_meta! {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- FunctionNew ---- (macro-generated)
|
||||
inst_meta! {
|
||||
pub struct FunctionNewInst { dst: ValueId, captures: Vec<(String, ValueId)>, me: Option<ValueId> }
|
||||
=> {
|
||||
from_mir = |i| match i { MirInstruction::FunctionNew { dst, captures, me, .. } => Some(FunctionNewInst { dst: *dst, captures: captures.clone(), me: *me }), _ => None };
|
||||
effects = |_: &Self| EffectMask::PURE.add(Effect::Alloc);
|
||||
dst = |s: &Self| Some(s.dst);
|
||||
used = |s: &Self| { let mut v: Vec<ValueId> = s.captures.iter().map(|(_, id)| *id).collect(); if let Some(m) = s.me { v.push(m); } v };
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Store ---- (macro-generated)
|
||||
inst_meta! {
|
||||
pub struct StoreInst { value: ValueId, ptr: ValueId }
|
||||
@ -446,6 +479,72 @@ inst_meta! {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- TypeCheck ---- (macro-generated)
|
||||
inst_meta! {
|
||||
pub struct TypeCheckInst { dst: ValueId, value: ValueId }
|
||||
=> {
|
||||
from_mir = |i| match i { MirInstruction::TypeCheck { dst, value, .. } => Some(TypeCheckInst { dst: *dst, value: *value }), _ => None };
|
||||
effects = |_: &Self| EffectMask::PURE;
|
||||
dst = |s: &Self| Some(s.dst);
|
||||
used = |s: &Self| vec![s.value];
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Copy ---- (macro-generated)
|
||||
inst_meta! {
|
||||
pub struct CopyInst { dst: ValueId, src: ValueId }
|
||||
=> {
|
||||
from_mir = |i| match i { MirInstruction::Copy { dst, src } => Some(CopyInst { dst: *dst, src: *src }), _ => None };
|
||||
effects = |_: &Self| EffectMask::PURE;
|
||||
dst = |s: &Self| Some(s.dst);
|
||||
used = |s: &Self| vec![s.src];
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Nop ---- (macro-generated)
|
||||
inst_meta! {
|
||||
pub struct NopInst { }
|
||||
=> {
|
||||
from_mir = |i| match i { MirInstruction::Nop => Some(NopInst {}), _ => None };
|
||||
effects = |_: &Self| EffectMask::PURE;
|
||||
dst = |_: &Self| None;
|
||||
used = |_: &Self| Vec::new();
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Throw ---- (macro-generated)
|
||||
inst_meta! {
|
||||
pub struct ThrowInst { exception: ValueId, effects_mask: EffectMask }
|
||||
=> {
|
||||
from_mir = |i| match i { MirInstruction::Throw { exception, effects } => Some(ThrowInst { exception: *exception, effects_mask: *effects }), _ => None };
|
||||
effects = |s: &Self| s.effects_mask;
|
||||
dst = |_: &Self| None;
|
||||
used = |s: &Self| vec![s.exception];
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Catch ---- (macro-generated)
|
||||
inst_meta! {
|
||||
pub struct CatchInst { exception_value: ValueId }
|
||||
=> {
|
||||
from_mir = |i| match i { MirInstruction::Catch { exception_value, .. } => Some(CatchInst { exception_value: *exception_value }), _ => None };
|
||||
effects = |_: &Self| EffectMask::CONTROL;
|
||||
dst = |s: &Self| Some(s.exception_value);
|
||||
used = |_: &Self| Vec::new();
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Safepoint ---- (macro-generated)
|
||||
inst_meta! {
|
||||
pub struct SafepointInst { }
|
||||
=> {
|
||||
from_mir = |i| match i { MirInstruction::Safepoint => Some(SafepointInst {}), _ => None };
|
||||
effects = |_: &Self| EffectMask::PURE;
|
||||
dst = |_: &Self| None;
|
||||
used = |_: &Self| Vec::new();
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Call-like (dst/used only; effects fallback in MirInstruction) ----
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CallLikeInst {
|
||||
|
||||
@ -75,7 +75,7 @@ pub fn build_simple_loop<L: LoopBuilderApi>(
|
||||
let void_id = lb.new_value();
|
||||
lb.emit(MirInstruction::Const {
|
||||
dst: void_id,
|
||||
value: super::instruction::ConstValue::Void,
|
||||
value: crate::mir::ConstValue::Void,
|
||||
})?;
|
||||
Ok(void_id)
|
||||
}
|
||||
|
||||
@ -42,47 +42,14 @@ pub struct LoopBuilder<'a> {
|
||||
no_phi_mode: bool,
|
||||
}
|
||||
|
||||
// Local copy: detect a variable name assigned within an AST fragment
|
||||
fn extract_assigned_var_local(ast: &ASTNode) -> Option<String> {
|
||||
match ast {
|
||||
ASTNode::Assignment { target, .. } => {
|
||||
if let ASTNode::Variable { name, .. } = target.as_ref() {
|
||||
Some(name.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
ASTNode::Program { statements, .. } => statements
|
||||
.last()
|
||||
.and_then(|st| extract_assigned_var_local(st)),
|
||||
ASTNode::If {
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} => {
|
||||
let then_prog = ASTNode::Program {
|
||||
statements: then_body.clone(),
|
||||
span: crate::ast::Span::unknown(),
|
||||
};
|
||||
let tvar = extract_assigned_var_local(&then_prog);
|
||||
let evar = else_body.as_ref().and_then(|eb| {
|
||||
let ep = ASTNode::Program {
|
||||
statements: eb.clone(),
|
||||
span: crate::ast::Span::unknown(),
|
||||
};
|
||||
extract_assigned_var_local(&ep)
|
||||
});
|
||||
match (tvar, evar) {
|
||||
(Some(tv), Some(ev)) if tv == ev => Some(tv),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
// (removed) extract_assigned_var_local was a local helper used during
|
||||
// diagnostics and is no longer referenced. Keep the file lean and avoid
|
||||
// dead_code warnings.
|
||||
|
||||
impl<'a> LoopBuilder<'a> {
|
||||
// --- Small helpers for continue/break commonization ---
|
||||
// =============================================================
|
||||
// Control Helpers — break/continue/jumps/unreachable handling
|
||||
// =============================================================
|
||||
|
||||
/// Emit a jump to `target` from the current block and record predecessor metadata.
|
||||
fn jump_with_pred(&mut self, target: BasicBlockId) -> Result<(), String> {
|
||||
@ -124,6 +91,9 @@ impl<'a> LoopBuilder<'a> {
|
||||
self.switch_to_unreachable_block_with_void()
|
||||
}
|
||||
|
||||
// =============================================================
|
||||
// Lifecycle — create builder, main loop construction
|
||||
// =============================================================
|
||||
/// 新しいループビルダーを作成
|
||||
pub fn new(parent: &'a mut super::builder::MirBuilder) -> Self {
|
||||
let no_phi_mode = parent.is_no_phi_mode();
|
||||
@ -272,6 +242,9 @@ impl<'a> LoopBuilder<'a> {
|
||||
Ok(void_dst)
|
||||
}
|
||||
|
||||
// =============================================================
|
||||
// PHI Helpers — prepare/finalize PHIs and block sealing
|
||||
// =============================================================
|
||||
/// ループ変数の準備(事前検出または遅延生成)
|
||||
fn prepare_loop_variables(
|
||||
&mut self,
|
||||
@ -400,6 +373,7 @@ impl<'a> LoopBuilder<'a> {
|
||||
.emit_instruction(MirInstruction::Const { dst, value })
|
||||
}
|
||||
|
||||
/// ブロック先頭に PHI 命令を挿入(不変条件: PHI は常にブロック先頭)
|
||||
fn emit_phi_at_block_start(
|
||||
&mut self,
|
||||
block_id: BasicBlockId,
|
||||
@ -454,6 +428,9 @@ impl<'a> LoopBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================
|
||||
// Variable Map Utilities — snapshots and rebinding
|
||||
// =============================================================
|
||||
fn get_current_variable_map(&self) -> HashMap<String, ValueId> {
|
||||
self.parent_builder.variable_map.clone()
|
||||
}
|
||||
@ -509,192 +486,157 @@ impl<'a> LoopBuilder<'a> {
|
||||
void_id
|
||||
}))
|
||||
}
|
||||
ASTNode::If {
|
||||
condition,
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} => {
|
||||
// Lower a simple if inside loop, ensuring continue/break inside branches are handled
|
||||
let cond_val = self.parent_builder.build_expression(*condition.clone())?;
|
||||
let then_bb = self.new_block();
|
||||
let else_bb = self.new_block();
|
||||
let merge_bb = self.new_block();
|
||||
self.emit_branch(cond_val, then_bb, else_bb)?;
|
||||
|
||||
// Capture pre-if variable map (used for phi normalization)
|
||||
let pre_if_var_map = self.get_current_variable_map();
|
||||
let pre_then_var_value = pre_if_var_map.clone();
|
||||
|
||||
// then
|
||||
self.set_current_block(then_bb)?;
|
||||
for s in then_body.iter().cloned() {
|
||||
let _ = self.build_statement(s)?;
|
||||
// Stop if block terminated
|
||||
let cur_id = self.current_block()?;
|
||||
let terminated = {
|
||||
if let Some(ref fun_ro) = self.parent_builder.current_function {
|
||||
if let Some(bb) = fun_ro.get_block(cur_id) {
|
||||
bb.is_terminated()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
if terminated {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let then_var_map_end = self.get_current_variable_map();
|
||||
// Only jump to merge if not already terminated (e.g., continue/break)
|
||||
let then_reaches_merge = {
|
||||
let cur_id = self.current_block()?;
|
||||
let need_jump = {
|
||||
if let Some(ref fun_ro) = self.parent_builder.current_function {
|
||||
if let Some(bb) = fun_ro.get_block(cur_id) {
|
||||
!bb.is_terminated()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
if need_jump {
|
||||
self.emit_jump(merge_bb)?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
// else
|
||||
self.set_current_block(else_bb)?;
|
||||
let mut else_var_map_end_opt: Option<
|
||||
std::collections::HashMap<String, super::ValueId>,
|
||||
> = None;
|
||||
if let Some(es) = else_body.clone() {
|
||||
for s in es.into_iter() {
|
||||
let _ = self.build_statement(s)?;
|
||||
let cur_id = self.current_block()?;
|
||||
let terminated = {
|
||||
if let Some(ref fun_ro) = self.parent_builder.current_function {
|
||||
if let Some(bb) = fun_ro.get_block(cur_id) {
|
||||
bb.is_terminated()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
if terminated {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else_var_map_end_opt = Some(self.get_current_variable_map());
|
||||
}
|
||||
let else_reaches_merge = {
|
||||
let cur_id = self.current_block()?;
|
||||
let need_jump = {
|
||||
if let Some(ref fun_ro) = self.parent_builder.current_function {
|
||||
if let Some(bb) = fun_ro.get_block(cur_id) {
|
||||
!bb.is_terminated()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
if need_jump {
|
||||
self.emit_jump(merge_bb)?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
// Continue at merge
|
||||
self.set_current_block(merge_bb)?;
|
||||
// If branches assign variables, emit PHIs per variable and bind them.
|
||||
// Previous logic handled only a single variable; here we generalize to all assigned vars.
|
||||
fn collect_assigned_vars(ast: &ASTNode, out: &mut std::collections::HashSet<String>) {
|
||||
match ast {
|
||||
ASTNode::Assignment { target, .. } => {
|
||||
if let ASTNode::Variable { name, .. } = target.as_ref() {
|
||||
out.insert(name.clone());
|
||||
}
|
||||
}
|
||||
ASTNode::Program { statements, .. } => {
|
||||
for s in statements { collect_assigned_vars(s, out); }
|
||||
}
|
||||
ASTNode::If { then_body, else_body, .. } => {
|
||||
let tp = ASTNode::Program { statements: then_body.clone(), span: crate::ast::Span::unknown() };
|
||||
collect_assigned_vars(&tp, out);
|
||||
if let Some(eb) = else_body {
|
||||
let ep = ASTNode::Program { statements: eb.clone(), span: crate::ast::Span::unknown() };
|
||||
collect_assigned_vars(&ep, out);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut vars: std::collections::HashSet<String> = std::collections::HashSet::new();
|
||||
let then_prog = ASTNode::Program { statements: then_body.clone(), span: crate::ast::Span::unknown() };
|
||||
collect_assigned_vars(&then_prog, &mut vars);
|
||||
if let Some(es) = &else_body {
|
||||
let else_prog = ASTNode::Program { statements: es.clone(), span: crate::ast::Span::unknown() };
|
||||
collect_assigned_vars(&else_prog, &mut vars);
|
||||
}
|
||||
|
||||
// Reset to pre-if map before rebinding to ensure a clean environment
|
||||
self.parent_builder.variable_map = pre_if_var_map.clone();
|
||||
for var_name in vars.into_iter() {
|
||||
// then-side value: from then end map if assigned there; otherwise pre-if value
|
||||
let then_val = then_var_map_end.get(&var_name).copied().or_else(|| pre_then_var_value.get(&var_name).copied());
|
||||
// else-side value: prefer else end map when else assigns; otherwise pre-if value
|
||||
let else_val = else_var_map_end_opt
|
||||
.as_ref()
|
||||
.and_then(|m| m.get(&var_name).copied())
|
||||
.or_else(|| pre_then_var_value.get(&var_name).copied());
|
||||
|
||||
if let (Some(tv), Some(ev)) = (then_val, else_val) {
|
||||
// Build incoming list only for predecessors that actually reach merge
|
||||
let mut incomings: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||
if then_reaches_merge { incomings.push((then_bb, tv)); }
|
||||
if else_reaches_merge { incomings.push((else_bb, ev)); }
|
||||
match incomings.len() {
|
||||
0 => { /* neither branch reaches merge: nothing to bind */ }
|
||||
1 => {
|
||||
// Single predecessor reaching merge: no PHI needed
|
||||
let (_pred, v) = incomings[0];
|
||||
self.parent_builder.variable_map.insert(var_name, v);
|
||||
}
|
||||
_ => {
|
||||
let phi_id = self.new_value();
|
||||
if self.no_phi_mode {
|
||||
for (pred, v) in incomings.iter().copied() {
|
||||
self.parent_builder.insert_edge_copy(pred, phi_id, v)?;
|
||||
}
|
||||
} else {
|
||||
self.emit_phi_at_block_start(merge_bb, phi_id, incomings)?;
|
||||
}
|
||||
self.parent_builder.variable_map.insert(var_name, phi_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let void_id = self.new_value();
|
||||
self.emit_const(void_id, ConstValue::Void)?;
|
||||
Ok(void_id)
|
||||
}
|
||||
ASTNode::If { condition, then_body, else_body, .. } =>
|
||||
self.lower_if_in_loop(*condition, then_body, else_body),
|
||||
ASTNode::Break { .. } => self.do_break(),
|
||||
ASTNode::Continue { .. } => self.do_continue(),
|
||||
other => self.parent_builder.build_expression(other),
|
||||
}
|
||||
}
|
||||
|
||||
/// Lower an if-statement inside a loop, preserving continue/break semantics and emitting PHIs per assigned variable.
|
||||
fn lower_if_in_loop(
|
||||
&mut self,
|
||||
condition: ASTNode,
|
||||
then_body: Vec<ASTNode>,
|
||||
else_body: Option<Vec<ASTNode>>,
|
||||
) -> Result<ValueId, String> {
|
||||
// Evaluate condition and create blocks
|
||||
let cond_val = self.parent_builder.build_expression(condition)?;
|
||||
let then_bb = self.new_block();
|
||||
let else_bb = self.new_block();
|
||||
let merge_bb = self.new_block();
|
||||
self.emit_branch(cond_val, then_bb, else_bb)?;
|
||||
|
||||
// Capture pre-if variable map (used for phi normalization)
|
||||
let pre_if_var_map = self.get_current_variable_map();
|
||||
let pre_then_var_value = pre_if_var_map.clone();
|
||||
|
||||
// then branch
|
||||
self.set_current_block(then_bb)?;
|
||||
for s in then_body.iter().cloned() {
|
||||
let _ = self.build_statement(s)?;
|
||||
// Stop if block terminated
|
||||
let cur_id = self.current_block()?;
|
||||
let terminated = {
|
||||
if let Some(ref fun_ro) = self.parent_builder.current_function {
|
||||
if let Some(bb) = fun_ro.get_block(cur_id) { bb.is_terminated() } else { false }
|
||||
} else { false }
|
||||
};
|
||||
if terminated { break; }
|
||||
}
|
||||
let then_var_map_end = self.get_current_variable_map();
|
||||
// Only jump to merge if not already terminated (e.g., continue/break)
|
||||
// Capture the actual predecessor block that reaches merge (entry block may not be the exit).
|
||||
let then_pred_to_merge: Option<BasicBlockId> = {
|
||||
let cur_id = self.current_block()?;
|
||||
let need_jump = {
|
||||
if let Some(ref fun_ro) = self.parent_builder.current_function {
|
||||
if let Some(bb) = fun_ro.get_block(cur_id) { !bb.is_terminated() } else { false }
|
||||
} else { false }
|
||||
};
|
||||
if need_jump {
|
||||
// Emit the edge now; record the real predecessor (cur_id), not the entry then_bb.
|
||||
self.emit_jump(merge_bb)?;
|
||||
Some(cur_id)
|
||||
} else {
|
||||
// Terminated path (e.g., continue/break) — no incoming to merge.
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// else branch
|
||||
self.set_current_block(else_bb)?;
|
||||
let mut else_var_map_end_opt: Option<HashMap<String, ValueId>> = None;
|
||||
if let Some(es) = else_body.clone() {
|
||||
for s in es.into_iter() {
|
||||
let _ = self.build_statement(s)?;
|
||||
let cur_id = self.current_block()?;
|
||||
let terminated = {
|
||||
if let Some(ref fun_ro) = self.parent_builder.current_function {
|
||||
if let Some(bb) = fun_ro.get_block(cur_id) { bb.is_terminated() } else { false }
|
||||
} else { false }
|
||||
};
|
||||
if terminated { break; }
|
||||
}
|
||||
else_var_map_end_opt = Some(self.get_current_variable_map());
|
||||
}
|
||||
let else_pred_to_merge: Option<BasicBlockId> = {
|
||||
let cur_id = self.current_block()?;
|
||||
let need_jump = {
|
||||
if let Some(ref fun_ro) = self.parent_builder.current_function {
|
||||
if let Some(bb) = fun_ro.get_block(cur_id) { !bb.is_terminated() } else { false }
|
||||
} else { false }
|
||||
};
|
||||
if need_jump {
|
||||
self.emit_jump(merge_bb)?;
|
||||
Some(cur_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// Continue at merge
|
||||
self.set_current_block(merge_bb)?;
|
||||
// collect assigned variables in both branches
|
||||
fn collect_assigned_vars(ast: &ASTNode, out: &mut std::collections::HashSet<String>) {
|
||||
match ast {
|
||||
ASTNode::Assignment { target, .. } => {
|
||||
if let ASTNode::Variable { name, .. } = target.as_ref() { out.insert(name.clone()); }
|
||||
}
|
||||
ASTNode::Program { statements, .. } => { for s in statements { collect_assigned_vars(s, out); } }
|
||||
ASTNode::If { then_body, else_body, .. } => {
|
||||
let tp = ASTNode::Program { statements: then_body.clone(), span: crate::ast::Span::unknown() };
|
||||
collect_assigned_vars(&tp, out);
|
||||
if let Some(eb) = else_body {
|
||||
let ep = ASTNode::Program { statements: eb.clone(), span: crate::ast::Span::unknown() };
|
||||
collect_assigned_vars(&ep, out);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut vars: std::collections::HashSet<String> = std::collections::HashSet::new();
|
||||
let then_prog = ASTNode::Program { statements: then_body.clone(), span: crate::ast::Span::unknown() };
|
||||
collect_assigned_vars(&then_prog, &mut vars);
|
||||
if let Some(es) = &else_body {
|
||||
let else_prog = ASTNode::Program { statements: es.clone(), span: crate::ast::Span::unknown() };
|
||||
collect_assigned_vars(&else_prog, &mut vars);
|
||||
}
|
||||
|
||||
// Reset to pre-if map before rebinding to ensure a clean environment
|
||||
self.parent_builder.variable_map = pre_if_var_map.clone();
|
||||
for var_name in vars.into_iter() {
|
||||
let then_val = then_var_map_end.get(&var_name).copied().or_else(|| pre_then_var_value.get(&var_name).copied());
|
||||
let else_val = else_var_map_end_opt.as_ref().and_then(|m| m.get(&var_name).copied()).or_else(|| pre_then_var_value.get(&var_name).copied());
|
||||
|
||||
if let (Some(tv), Some(ev)) = (then_val, else_val) {
|
||||
let mut incomings: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||
if let Some(pred) = then_pred_to_merge { incomings.push((pred, tv)); }
|
||||
if let Some(pred) = else_pred_to_merge { incomings.push((pred, ev)); }
|
||||
match incomings.len() {
|
||||
0 => {}
|
||||
1 => {
|
||||
let (_pred, v) = incomings[0];
|
||||
self.parent_builder.variable_map.insert(var_name, v);
|
||||
}
|
||||
_ => {
|
||||
let phi_id = self.new_value();
|
||||
if self.no_phi_mode {
|
||||
for (pred, v) in incomings.iter().copied() {
|
||||
self.parent_builder.insert_edge_copy(pred, phi_id, v)?;
|
||||
}
|
||||
} else {
|
||||
self.emit_phi_at_block_start(merge_bb, phi_id, incomings)?;
|
||||
}
|
||||
self.parent_builder.variable_map.insert(var_name, phi_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let void_id = self.new_value();
|
||||
self.emit_const(void_id, ConstValue::Void)?;
|
||||
Ok(void_id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ pub mod function;
|
||||
pub mod instruction;
|
||||
pub mod instruction_kinds; // small kind-specific metadata (Const/BinOp)
|
||||
pub mod instruction_introspection; // Introspection helpers for tests (instruction names)
|
||||
pub mod types; // core MIR enums (ConstValue, Ops, MirType)
|
||||
pub mod loop_api; // Minimal LoopBuilder facade (adapter-ready)
|
||||
pub mod loop_builder; // SSA loop construction with phi nodes
|
||||
pub mod optimizer;
|
||||
@ -33,9 +34,9 @@ pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
|
||||
pub use builder::MirBuilder;
|
||||
pub use effect::{Effect, EffectMask};
|
||||
pub use function::{FunctionSignature, MirFunction, MirModule};
|
||||
pub use instruction::{
|
||||
BarrierOp, BinaryOp, CompareOp, ConstValue, MirInstruction, MirType, TypeOpKind, UnaryOp,
|
||||
WeakRefOp,
|
||||
pub use instruction::MirInstruction;
|
||||
pub use types::{
|
||||
BarrierOp, BinaryOp, CompareOp, ConstValue, MirType, TypeOpKind, UnaryOp, WeakRefOp,
|
||||
};
|
||||
pub use optimizer::MirOptimizer;
|
||||
pub use printer::MirPrinter;
|
||||
|
||||
@ -225,13 +225,12 @@ fn resolve_type_from_value(
|
||||
def_map: &std::collections::HashMap<ValueId, (super::basic_block::BasicBlockId, usize)>,
|
||||
id: ValueId,
|
||||
) -> Option<MirType> {
|
||||
use super::instruction::ConstValue;
|
||||
if let Some((bb, idx)) = def_map.get(&id).copied() {
|
||||
if let Some(block) = function.blocks.get(&bb) {
|
||||
if idx < block.instructions.len() {
|
||||
match &block.instructions[idx] {
|
||||
MirInstruction::Const {
|
||||
value: ConstValue::String(s),
|
||||
value: crate::mir::ConstValue::String(s),
|
||||
..
|
||||
} => {
|
||||
return Some(map_type_name(s));
|
||||
@ -244,7 +243,7 @@ fn resolve_type_from_value(
|
||||
if let Some(sblock) = function.blocks.get(&sbb) {
|
||||
if sidx < sblock.instructions.len() {
|
||||
if let MirInstruction::Const {
|
||||
value: ConstValue::String(s),
|
||||
value: crate::mir::ConstValue::String(s),
|
||||
..
|
||||
} = &sblock.instructions[sidx]
|
||||
{
|
||||
@ -313,7 +312,7 @@ fn diagnose_unlowered_type_ops(
|
||||
if let Some(b) = function.blocks.get(&bb) {
|
||||
if idx < b.instructions.len() {
|
||||
if let MirInstruction::Const {
|
||||
value: super::instruction::ConstValue::String(s),
|
||||
value: crate::mir::ConstValue::String(s),
|
||||
..
|
||||
} = &b.instructions[idx]
|
||||
{
|
||||
|
||||
@ -41,7 +41,7 @@ pub fn diagnose_unlowered_type_ops(
|
||||
if let Some(b) = function.blocks.get(&bb) {
|
||||
if idx < b.instructions.len() {
|
||||
if let MirInstruction::Const {
|
||||
value: crate::mir::instruction::ConstValue::String(s),
|
||||
value: crate::mir::ConstValue::String(s),
|
||||
..
|
||||
} = &b.instructions[idx]
|
||||
{
|
||||
|
||||
@ -239,7 +239,7 @@ pub fn normalize_legacy_instructions(
|
||||
value,
|
||||
expected_type,
|
||||
} => {
|
||||
let ty = crate::mir::instruction::MirType::Box(expected_type.clone());
|
||||
let ty = crate::mir::MirType::Box(expected_type.clone());
|
||||
*term = I::TypeOp {
|
||||
dst: *dst,
|
||||
op: TypeOpKind::Check,
|
||||
@ -370,7 +370,7 @@ pub fn normalize_ref_field_access(
|
||||
function.next_value_id += 1;
|
||||
out.push(I::Const {
|
||||
dst: new_id,
|
||||
value: crate::mir::instruction::ConstValue::String(field),
|
||||
value: crate::mir::ConstValue::String(field),
|
||||
});
|
||||
out.push(I::BoxCall {
|
||||
dst: Some(dst),
|
||||
@ -391,7 +391,7 @@ pub fn normalize_ref_field_access(
|
||||
function.next_value_id += 1;
|
||||
out.push(I::Const {
|
||||
dst: new_id,
|
||||
value: crate::mir::instruction::ConstValue::String(field),
|
||||
value: crate::mir::ConstValue::String(field),
|
||||
});
|
||||
out.push(I::Barrier {
|
||||
op: BarrierOp::Write,
|
||||
@ -423,7 +423,7 @@ pub fn normalize_ref_field_access(
|
||||
function.next_value_id += 1;
|
||||
block.instructions.push(I::Const {
|
||||
dst: new_id,
|
||||
value: crate::mir::instruction::ConstValue::String(field),
|
||||
value: crate::mir::ConstValue::String(field),
|
||||
});
|
||||
I::BoxCall {
|
||||
dst: Some(dst),
|
||||
@ -443,7 +443,7 @@ pub fn normalize_ref_field_access(
|
||||
function.next_value_id += 1;
|
||||
block.instructions.push(I::Const {
|
||||
dst: new_id,
|
||||
value: crate::mir::instruction::ConstValue::String(field),
|
||||
value: crate::mir::ConstValue::String(field),
|
||||
});
|
||||
block.instructions.push(I::Barrier {
|
||||
op: BarrierOp::Write,
|
||||
|
||||
@ -11,7 +11,7 @@ use crate::mir::{BinaryOp, CompareOp, EffectMask, MirInstruction as I, MirModule
|
||||
/// Not x => Compare(Eq, x, Const false)
|
||||
/// BitNot x => BinOp(BitXor, x, Const(-1))
|
||||
pub fn normalize_pure_core13(_opt: &mut MirOptimizer, module: &mut MirModule) -> OptimizationStats {
|
||||
use crate::mir::instruction::ConstValue;
|
||||
use crate::mir::types::ConstValue;
|
||||
let mut stats = OptimizationStats::new();
|
||||
for (_fname, function) in &mut module.functions {
|
||||
for (_bb, block) in &mut function.blocks {
|
||||
@ -43,7 +43,7 @@ pub fn normalize_pure_core13(_opt: &mut MirOptimizer, module: &mut MirModule) ->
|
||||
// prepend type name as Const String
|
||||
let ty_id = ValueId::new(function.next_value_id);
|
||||
function.next_value_id += 1;
|
||||
out.push(I::Const { dst: ty_id, value: ConstValue::String(box_type) });
|
||||
out.push(I::Const { dst: ty_id, value: crate::mir::ConstValue::String(box_type) });
|
||||
let mut call_args = Vec::with_capacity(1 + args.len());
|
||||
call_args.push(ty_id);
|
||||
call_args.append(&mut args);
|
||||
@ -61,19 +61,19 @@ pub fn normalize_pure_core13(_opt: &mut MirOptimizer, module: &mut MirModule) ->
|
||||
crate::mir::UnaryOp::Neg => {
|
||||
let zero = ValueId::new(function.next_value_id);
|
||||
function.next_value_id += 1;
|
||||
out.push(I::Const { dst: zero, value: ConstValue::Integer(0) });
|
||||
out.push(I::Const { dst: zero, value: crate::mir::ConstValue::Integer(0) });
|
||||
out.push(I::BinOp { dst, op: BinaryOp::Sub, lhs: zero, rhs: operand });
|
||||
}
|
||||
crate::mir::UnaryOp::Not => {
|
||||
let f = ValueId::new(function.next_value_id);
|
||||
function.next_value_id += 1;
|
||||
out.push(I::Const { dst: f, value: ConstValue::Bool(false) });
|
||||
out.push(I::Const { dst: f, value: crate::mir::ConstValue::Bool(false) });
|
||||
out.push(I::Compare { dst, op: CompareOp::Eq, lhs: operand, rhs: f });
|
||||
}
|
||||
crate::mir::UnaryOp::BitNot => {
|
||||
let all1 = ValueId::new(function.next_value_id);
|
||||
function.next_value_id += 1;
|
||||
out.push(I::Const { dst: all1, value: ConstValue::Integer(-1) });
|
||||
out.push(I::Const { dst: all1, value: crate::mir::ConstValue::Integer(-1) });
|
||||
out.push(I::BinOp { dst, op: BinaryOp::BitXor, lhs: operand, rhs: all1 });
|
||||
}
|
||||
}
|
||||
|
||||
@ -338,10 +338,6 @@ impl MirPrinter {
|
||||
output
|
||||
}
|
||||
|
||||
fn format_dst(&self, dst: &ValueId, types: &HashMap<ValueId, MirType>) -> String {
|
||||
printer_helpers::format_dst(dst, types)
|
||||
}
|
||||
|
||||
/// Format a single instruction
|
||||
fn format_instruction(
|
||||
&self,
|
||||
|
||||
111
src/mir/types.rs
Normal file
111
src/mir/types.rs
Normal file
@ -0,0 +1,111 @@
|
||||
/*!
|
||||
* MIR Core Types — split from instruction.rs for clarity (behavior-preserving)
|
||||
*/
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// Constant values in MIR
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ConstValue {
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
String(String),
|
||||
Null,
|
||||
Void,
|
||||
}
|
||||
|
||||
impl fmt::Display for ConstValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ConstValue::Integer(n) => write!(f, "{}", n),
|
||||
ConstValue::Float(fl) => write!(f, "{}", fl),
|
||||
ConstValue::Bool(b) => write!(f, "{}", b),
|
||||
ConstValue::String(s) => write!(f, "\"{}\"", s),
|
||||
ConstValue::Null => write!(f, "null"),
|
||||
ConstValue::Void => write!(f, "void"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Binary operations
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BinaryOp {
|
||||
// Arithmetic
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Mod,
|
||||
|
||||
// Bitwise
|
||||
BitAnd,
|
||||
BitOr,
|
||||
BitXor,
|
||||
Shl,
|
||||
Shr,
|
||||
|
||||
// Logical
|
||||
And,
|
||||
Or,
|
||||
}
|
||||
|
||||
/// Unary operations
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum UnaryOp {
|
||||
// Arithmetic
|
||||
Neg,
|
||||
|
||||
// Logical
|
||||
Not,
|
||||
|
||||
// Bitwise
|
||||
BitNot,
|
||||
}
|
||||
|
||||
/// Comparison operations
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CompareOp {
|
||||
Eq,
|
||||
Ne,
|
||||
Lt,
|
||||
Le,
|
||||
Gt,
|
||||
Ge,
|
||||
}
|
||||
|
||||
/// MIR type system
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum MirType {
|
||||
Integer,
|
||||
Float,
|
||||
Bool,
|
||||
String,
|
||||
Box(String), // Box type with name
|
||||
Array(Box<MirType>),
|
||||
Future(Box<MirType>), // Future containing a type
|
||||
Void,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/// Kind of unified type operation (PoC)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TypeOpKind {
|
||||
Check,
|
||||
Cast,
|
||||
}
|
||||
|
||||
/// Kind of unified weak reference operation (PoC)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum WeakRefOp {
|
||||
New,
|
||||
Load,
|
||||
}
|
||||
|
||||
/// Kind of unified barrier operation (PoC)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BarrierOp {
|
||||
Read,
|
||||
Write,
|
||||
}
|
||||
|
||||
@ -4,10 +4,9 @@
|
||||
* Implements dominance checking, SSA verification, and semantic analysis
|
||||
*/
|
||||
|
||||
use super::{BasicBlockId, MirFunction, MirModule, ValueId};
|
||||
use super::{MirFunction, MirModule};
|
||||
use crate::debug::log as dlog;
|
||||
use crate::mir::verification_types::VerificationError;
|
||||
use std::collections::HashMap;
|
||||
mod cfg;
|
||||
mod dom;
|
||||
mod awaits;
|
||||
|
||||
@ -69,7 +69,7 @@ pub fn check_weakref_and_barrier(function: &MirFunction) -> Result<(), Vec<Verif
|
||||
| MirInstruction::BarrierWrite { ptr } => {
|
||||
if let Some((_db, _di, def_inst)) = def_map.get(ptr) {
|
||||
if let MirInstruction::Const {
|
||||
value: crate::mir::instruction::ConstValue::Void,
|
||||
value: crate::mir::ConstValue::Void,
|
||||
..
|
||||
} = def_inst
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user