Phase 12: 統一TypeBox ABI実装開始 - ChatGPT5による極小コアABI基盤構築
- TypeBox ABI雛形: メソッドスロット管理システム追加 - Type Registry: Array/Map/StringBoxの基本メソッド定義 - Host API: C ABI逆呼び出しシステム実装 - Phase 12ドキュメント整理: 設計文書統合・アーカイブ化 - MIR Builder: クリーンアップと分離実装完了 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1107
src/mir/builder.rs
1107
src/mir/builder.rs
File diff suppressed because it is too large
Load Diff
112
src/mir/builder/ops.rs
Normal file
112
src/mir/builder/ops.rs
Normal file
@ -0,0 +1,112 @@
|
||||
use super::{MirInstruction, ValueId, MirType};
|
||||
use crate::ast::{ASTNode, BinaryOperator};
|
||||
use crate::mir::{BinaryOp, UnaryOp, CompareOp, TypeOpKind};
|
||||
|
||||
// Internal classification for binary operations
|
||||
#[derive(Debug)]
|
||||
enum BinaryOpType {
|
||||
Arithmetic(BinaryOp),
|
||||
Comparison(CompareOp),
|
||||
}
|
||||
|
||||
impl super::MirBuilder {
|
||||
// Build a binary operation
|
||||
pub(super) fn build_binary_op(
|
||||
&mut self,
|
||||
left: ASTNode,
|
||||
operator: BinaryOperator,
|
||||
right: ASTNode,
|
||||
) -> Result<ValueId, String> {
|
||||
let lhs = self.build_expression(left)?;
|
||||
let rhs = self.build_expression(right)?;
|
||||
let dst = self.value_gen.next();
|
||||
|
||||
let mir_op = self.convert_binary_operator(operator)?;
|
||||
|
||||
match mir_op {
|
||||
// Arithmetic operations
|
||||
BinaryOpType::Arithmetic(op) => {
|
||||
self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
|
||||
// Arithmetic results are integers for now (Core-1)
|
||||
self.value_types.insert(dst, MirType::Integer);
|
||||
}
|
||||
// Comparison operations
|
||||
BinaryOpType::Comparison(op) => {
|
||||
// 80/20: If both operands originate from IntegerBox, cast to integer first
|
||||
let (lhs2, rhs2) = if self
|
||||
.value_origin_newbox
|
||||
.get(&lhs)
|
||||
.map(|s| s == "IntegerBox")
|
||||
.unwrap_or(false)
|
||||
&& self
|
||||
.value_origin_newbox
|
||||
.get(&rhs)
|
||||
.map(|s| s == "IntegerBox")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let li = self.value_gen.next();
|
||||
let ri = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::TypeOp {
|
||||
dst: li,
|
||||
op: TypeOpKind::Cast,
|
||||
value: lhs,
|
||||
ty: MirType::Integer,
|
||||
})?;
|
||||
self.emit_instruction(MirInstruction::TypeOp {
|
||||
dst: ri,
|
||||
op: TypeOpKind::Cast,
|
||||
value: rhs,
|
||||
ty: MirType::Integer,
|
||||
})?;
|
||||
(li, ri)
|
||||
} else {
|
||||
(lhs, rhs)
|
||||
};
|
||||
self.emit_instruction(MirInstruction::Compare { dst, op, lhs: lhs2, rhs: rhs2 })?;
|
||||
self.value_types.insert(dst, MirType::Bool);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
// Build a unary operation
|
||||
pub(super) fn build_unary_op(&mut self, operator: String, operand: ASTNode) -> Result<ValueId, String> {
|
||||
let operand_val = self.build_expression(operand)?;
|
||||
let dst = self.value_gen.next();
|
||||
|
||||
let mir_op = self.convert_unary_operator(operator)?;
|
||||
|
||||
self.emit_instruction(MirInstruction::UnaryOp { dst, op: mir_op, operand: operand_val })?;
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
// Convert AST binary operator to MIR enum or compare
|
||||
fn convert_binary_operator(&self, op: BinaryOperator) -> Result<BinaryOpType, String> {
|
||||
match op {
|
||||
BinaryOperator::Add => Ok(BinaryOpType::Arithmetic(BinaryOp::Add)),
|
||||
BinaryOperator::Subtract => Ok(BinaryOpType::Arithmetic(BinaryOp::Sub)),
|
||||
BinaryOperator::Multiply => Ok(BinaryOpType::Arithmetic(BinaryOp::Mul)),
|
||||
BinaryOperator::Divide => Ok(BinaryOpType::Arithmetic(BinaryOp::Div)),
|
||||
BinaryOperator::Modulo => Ok(BinaryOpType::Arithmetic(BinaryOp::Mod)),
|
||||
BinaryOperator::Equal => Ok(BinaryOpType::Comparison(CompareOp::Eq)),
|
||||
BinaryOperator::NotEqual => Ok(BinaryOpType::Comparison(CompareOp::Ne)),
|
||||
BinaryOperator::Less => Ok(BinaryOpType::Comparison(CompareOp::Lt)),
|
||||
BinaryOperator::LessEqual => Ok(BinaryOpType::Comparison(CompareOp::Le)),
|
||||
BinaryOperator::Greater => Ok(BinaryOpType::Comparison(CompareOp::Gt)),
|
||||
BinaryOperator::GreaterEqual => Ok(BinaryOpType::Comparison(CompareOp::Ge)),
|
||||
BinaryOperator::And => Ok(BinaryOpType::Arithmetic(BinaryOp::And)),
|
||||
BinaryOperator::Or => Ok(BinaryOpType::Arithmetic(BinaryOp::Or)),
|
||||
}
|
||||
}
|
||||
|
||||
// Convert AST unary operator to MIR operator
|
||||
fn convert_unary_operator(&self, op: String) -> Result<UnaryOp, String> {
|
||||
match op.as_str() {
|
||||
"-" => Ok(UnaryOp::Neg),
|
||||
"!" | "not" => Ok(UnaryOp::Not),
|
||||
"~" => Ok(UnaryOp::BitNot),
|
||||
_ => Err(format!("Unsupported unary operator: {}", op)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,39 +1,301 @@
|
||||
use super::{MirInstruction, EffectMask, Effect, ConstValue, ValueId};
|
||||
use crate::mir::TypeOpKind;
|
||||
use crate::mir::loop_builder::LoopBuilder;
|
||||
use crate::ast::ASTNode;
|
||||
|
||||
impl super::MirBuilder {
|
||||
// Print statement: env.console.log(value) with early TypeOp handling
|
||||
pub(super) fn build_print_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
||||
self.build_print_statement_legacy(expression)
|
||||
super::utils::builder_debug_log("enter build_print_statement");
|
||||
match &expression {
|
||||
// print(isType(val, "Type")) / print(asType(...))
|
||||
ASTNode::FunctionCall { name, arguments, .. }
|
||||
if (name == "isType" || name == "asType") && arguments.len() == 2 =>
|
||||
{
|
||||
super::utils::builder_debug_log("pattern: print(FunctionCall isType|asType)");
|
||||
if let Some(type_name) = super::MirBuilder::extract_string_literal(&arguments[1]) {
|
||||
super::utils::builder_debug_log(&format!("extract_string_literal OK: {}", type_name));
|
||||
let val = self.build_expression(arguments[0].clone())?;
|
||||
let ty = super::MirBuilder::parse_type_name_to_mir(&type_name);
|
||||
let dst = self.value_gen.next();
|
||||
let op = if name == "isType" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||
super::utils::builder_debug_log(&format!("emit TypeOp {:?} value={} dst= {}", op, val, dst));
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?;
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: None,
|
||||
iface_name: "env.console".to_string(),
|
||||
method_name: "log".to_string(),
|
||||
args: vec![dst],
|
||||
effects: EffectMask::PURE.add(Effect::Io),
|
||||
})?;
|
||||
return Ok(dst);
|
||||
} else {
|
||||
super::utils::builder_debug_log("extract_string_literal FAIL");
|
||||
}
|
||||
}
|
||||
// print(obj.is("Type")) / print(obj.as("Type"))
|
||||
ASTNode::MethodCall { object, method, arguments, .. }
|
||||
if (method == "is" || method == "as") && arguments.len() == 1 =>
|
||||
{
|
||||
super::utils::builder_debug_log("pattern: print(MethodCall is|as)");
|
||||
if let Some(type_name) = super::MirBuilder::extract_string_literal(&arguments[0]) {
|
||||
super::utils::builder_debug_log(&format!("extract_string_literal OK: {}", type_name));
|
||||
let obj_val = self.build_expression(*object.clone())?;
|
||||
let ty = super::MirBuilder::parse_type_name_to_mir(&type_name);
|
||||
let dst = self.value_gen.next();
|
||||
let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||
super::utils::builder_debug_log(&format!("emit TypeOp {:?} obj={} dst= {}", op, obj_val, dst));
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?;
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: None,
|
||||
iface_name: "env.console".to_string(),
|
||||
method_name: "log".to_string(),
|
||||
args: vec![dst],
|
||||
effects: EffectMask::PURE.add(Effect::Io),
|
||||
})?;
|
||||
return Ok(dst);
|
||||
} else {
|
||||
super::utils::builder_debug_log("extract_string_literal FAIL");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let value = self.build_expression(expression)?;
|
||||
super::utils::builder_debug_log(&format!("fallback print value={}", value));
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: None,
|
||||
iface_name: "env.console".to_string(),
|
||||
method_name: "log".to_string(),
|
||||
args: vec![value],
|
||||
effects: EffectMask::PURE.add(Effect::Io),
|
||||
})?;
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
// Block: sequentially build statements and return last value or Void
|
||||
pub(super) fn build_block(&mut self, statements: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||
self.build_block_legacy(statements)
|
||||
let mut last_value = None;
|
||||
for statement in statements { last_value = Some(self.build_expression(statement)?); }
|
||||
Ok(last_value.unwrap_or_else(|| {
|
||||
let void_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: void_val, value: ConstValue::Void }).unwrap();
|
||||
void_val
|
||||
}))
|
||||
}
|
||||
pub(super) fn build_if_statement(&mut self, condition: ASTNode, then_branch: ASTNode, else_branch: Option<ASTNode>) -> Result<ValueId, String> {
|
||||
self.build_if_statement_legacy(condition, then_branch, else_branch)
|
||||
|
||||
// If: lower to Branch + Phi, bind reassigned var name if identical
|
||||
pub(super) fn build_if_statement(
|
||||
&mut self,
|
||||
condition: ASTNode,
|
||||
then_branch: ASTNode,
|
||||
else_branch: Option<ASTNode>,
|
||||
) -> Result<ValueId, String> {
|
||||
let condition_val = self.build_expression(condition)?;
|
||||
let then_block = self.block_gen.next();
|
||||
let else_block = self.block_gen.next();
|
||||
let merge_block = self.block_gen.next();
|
||||
self.emit_instruction(MirInstruction::Branch { condition: condition_val, then_bb: then_block, else_bb: else_block })?;
|
||||
|
||||
// then
|
||||
self.current_block = Some(then_block);
|
||||
self.ensure_block_exists(then_block)?;
|
||||
let then_ast_for_analysis = then_branch.clone();
|
||||
let then_value = self.build_expression(then_branch)?;
|
||||
if !self.is_current_block_terminated() { self.emit_instruction(MirInstruction::Jump { target: merge_block })?; }
|
||||
|
||||
// else
|
||||
self.current_block = Some(else_block);
|
||||
self.ensure_block_exists(else_block)?;
|
||||
let (else_value, else_ast_for_analysis) = if let Some(else_ast) = else_branch {
|
||||
let val = self.build_expression(else_ast.clone())?;
|
||||
(val, Some(else_ast))
|
||||
} else {
|
||||
let void_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: void_val, value: ConstValue::Void })?;
|
||||
(void_val, None)
|
||||
};
|
||||
if !self.is_current_block_terminated() { self.emit_instruction(MirInstruction::Jump { target: merge_block })?; }
|
||||
|
||||
// merge + phi
|
||||
self.current_block = Some(merge_block);
|
||||
self.ensure_block_exists(merge_block)?;
|
||||
let result_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Phi { dst: result_val, inputs: vec![(then_block, then_value), (else_block, else_value)] })?;
|
||||
|
||||
// heuristic: bind same var name to phi result
|
||||
let assigned_var_then = extract_assigned_var(&then_ast_for_analysis);
|
||||
let assigned_var_else = else_ast_for_analysis.as_ref().and_then(|a| extract_assigned_var(a));
|
||||
if let (Some(a), Some(b)) = (assigned_var_then, assigned_var_else) { if a == b { self.variable_map.insert(a, result_val); } }
|
||||
Ok(result_val)
|
||||
}
|
||||
|
||||
// Loop: delegate to LoopBuilder
|
||||
pub(super) fn build_loop_statement(&mut self, condition: ASTNode, body: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||
self.build_loop_statement_legacy(condition, body)
|
||||
let mut loop_builder = LoopBuilder::new(self);
|
||||
loop_builder.build_loop(condition, body)
|
||||
}
|
||||
pub(super) fn build_try_catch_statement(&mut self, try_body: Vec<ASTNode>, catch_clauses: Vec<crate::ast::CatchClause>, finally_body: Option<Vec<ASTNode>>) -> Result<ValueId, String> {
|
||||
self.build_try_catch_statement_legacy(try_body, catch_clauses, finally_body)
|
||||
|
||||
// Try/Catch/Finally lowering with basic Extern semantics when disabled
|
||||
pub(super) fn build_try_catch_statement(
|
||||
&mut self,
|
||||
try_body: Vec<ASTNode>,
|
||||
catch_clauses: Vec<crate::ast::CatchClause>,
|
||||
finally_body: Option<Vec<ASTNode>>,
|
||||
) -> Result<ValueId, String> {
|
||||
if std::env::var("NYASH_BUILDER_DISABLE_TRYCATCH").ok().as_deref() == Some("1") {
|
||||
let try_ast = ASTNode::Program { statements: try_body, span: crate::ast::Span::unknown() };
|
||||
let result = self.build_expression(try_ast)?;
|
||||
return Ok(result);
|
||||
}
|
||||
let try_block = self.block_gen.next();
|
||||
let catch_block = self.block_gen.next();
|
||||
let finally_block = if finally_body.is_some() { Some(self.block_gen.next()) } else { None };
|
||||
let exit_block = self.block_gen.next();
|
||||
|
||||
if let Some(catch_clause) = catch_clauses.first() {
|
||||
if std::env::var("NYASH_DEBUG_TRYCATCH").ok().as_deref() == Some("1") {
|
||||
eprintln!("[BUILDER] Emitting catch handler for {:?}", catch_clause.exception_type);
|
||||
}
|
||||
let exception_value = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Catch { exception_type: catch_clause.exception_type.clone(), exception_value, handler_bb: catch_block })?;
|
||||
}
|
||||
|
||||
self.emit_instruction(MirInstruction::Jump { target: try_block })?;
|
||||
self.start_new_block(try_block)?;
|
||||
let try_ast = ASTNode::Program { statements: try_body, span: crate::ast::Span::unknown() };
|
||||
let _try_result = self.build_expression(try_ast)?;
|
||||
if !self.is_current_block_terminated() { let next_target = finally_block.unwrap_or(exit_block); self.emit_instruction(MirInstruction::Jump { target: next_target })?; }
|
||||
|
||||
self.start_new_block(catch_block)?;
|
||||
if std::env::var("NYASH_DEBUG_TRYCATCH").ok().as_deref() == Some("1") { eprintln!("[BUILDER] Enter catch block {:?}", catch_block); }
|
||||
if let Some(catch_clause) = catch_clauses.first() {
|
||||
if std::env::var("NYASH_DEBUG_TRYCATCH").ok().as_deref() == Some("1") { eprintln!("[BUILDER] Emitting catch handler for {:?}", catch_clause.exception_type); }
|
||||
let catch_ast = ASTNode::Program { statements: catch_clause.body.clone(), span: crate::ast::Span::unknown() };
|
||||
self.build_expression(catch_ast)?;
|
||||
}
|
||||
if !self.is_current_block_terminated() { let next_target = finally_block.unwrap_or(exit_block); self.emit_instruction(MirInstruction::Jump { target: next_target })?; }
|
||||
|
||||
if let (Some(finally_block_id), Some(finally_statements)) = (finally_block, finally_body) {
|
||||
self.start_new_block(finally_block_id)?;
|
||||
let finally_ast = ASTNode::Program { statements: finally_statements, span: crate::ast::Span::unknown() };
|
||||
self.build_expression(finally_ast)?;
|
||||
self.emit_instruction(MirInstruction::Jump { target: exit_block })?;
|
||||
}
|
||||
|
||||
self.start_new_block(exit_block)?;
|
||||
let result = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: result, value: ConstValue::Void })?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// Throw: emit Throw or fallback to env.debug.trace when disabled
|
||||
pub(super) fn build_throw_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
||||
self.build_throw_statement_legacy(expression)
|
||||
if std::env::var("NYASH_BUILDER_DISABLE_THROW").ok().as_deref() == Some("1") {
|
||||
let v = self.build_expression(expression)?;
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: None,
|
||||
iface_name: "env.debug".to_string(),
|
||||
method_name: "trace".to_string(),
|
||||
args: vec![v],
|
||||
effects: EffectMask::PURE.add(Effect::Debug),
|
||||
})?;
|
||||
return Ok(v);
|
||||
}
|
||||
let exception_value = self.build_expression(expression)?;
|
||||
self.emit_instruction(MirInstruction::Throw { exception: exception_value, effects: EffectMask::PANIC })?;
|
||||
Ok(exception_value)
|
||||
}
|
||||
pub(super) fn build_local_statement(&mut self, variables: Vec<String>, initial_values: Vec<Option<Box<ASTNode>>>) -> Result<ValueId, String> {
|
||||
self.build_local_statement_legacy(variables, initial_values)
|
||||
|
||||
// Local declarations with optional initializers
|
||||
pub(super) fn build_local_statement(
|
||||
&mut self,
|
||||
variables: Vec<String>,
|
||||
initial_values: Vec<Option<Box<ASTNode>>>,
|
||||
) -> Result<ValueId, String> {
|
||||
let mut last_value = None;
|
||||
for (i, var_name) in variables.iter().enumerate() {
|
||||
let value_id = if i < initial_values.len() && initial_values[i].is_some() {
|
||||
let init_expr = initial_values[i].as_ref().unwrap();
|
||||
self.build_expression(*init_expr.clone())?
|
||||
} else {
|
||||
self.value_gen.next()
|
||||
};
|
||||
self.variable_map.insert(var_name.clone(), value_id);
|
||||
last_value = Some(value_id);
|
||||
}
|
||||
Ok(last_value.unwrap_or_else(|| self.value_gen.next()))
|
||||
}
|
||||
|
||||
// Return statement
|
||||
pub(super) fn build_return_statement(&mut self, value: Option<Box<ASTNode>>) -> Result<ValueId, String> {
|
||||
self.build_return_statement_legacy(value)
|
||||
let return_value = if let Some(expr) = value {
|
||||
self.build_expression(*expr)?
|
||||
} else {
|
||||
let void_dst = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: void_dst, value: ConstValue::Void })?;
|
||||
void_dst
|
||||
};
|
||||
self.emit_instruction(MirInstruction::Return { value: Some(return_value) })?;
|
||||
Ok(return_value)
|
||||
}
|
||||
|
||||
// Nowait: prefer env.future.spawn_instance if method call; else FutureNew
|
||||
pub(super) fn build_nowait_statement(&mut self, variable: String, expression: ASTNode) -> Result<ValueId, String> {
|
||||
self.build_nowait_statement_legacy(variable, expression)
|
||||
if let ASTNode::MethodCall { object, method, arguments, .. } = expression.clone() {
|
||||
let recv_val = self.build_expression(*object)?;
|
||||
let mname_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: mname_id, value: super::ConstValue::String(method.clone()) })?;
|
||||
let mut arg_vals: Vec<ValueId> = Vec::with_capacity(2 + arguments.len());
|
||||
arg_vals.push(recv_val);
|
||||
arg_vals.push(mname_id);
|
||||
for a in arguments.into_iter() { arg_vals.push(self.build_expression(a)?); }
|
||||
let future_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: Some(future_id),
|
||||
iface_name: "env.future".to_string(),
|
||||
method_name: "spawn_instance".to_string(),
|
||||
args: arg_vals,
|
||||
effects: crate::mir::effect::EffectMask::PURE.add(crate::mir::effect::Effect::Io),
|
||||
})?;
|
||||
self.variable_map.insert(variable.clone(), future_id);
|
||||
return Ok(future_id);
|
||||
}
|
||||
let expression_value = self.build_expression(expression)?;
|
||||
let future_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::FutureNew { dst: future_id, value: expression_value })?;
|
||||
self.variable_map.insert(variable.clone(), future_id);
|
||||
Ok(future_id)
|
||||
}
|
||||
|
||||
// Await: insert Safepoint before/after and emit Await
|
||||
pub(super) fn build_await_expression(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
||||
self.build_await_expression_legacy(expression)
|
||||
let future_value = self.build_expression(expression)?;
|
||||
self.emit_instruction(MirInstruction::Safepoint)?;
|
||||
let result_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Await { dst: result_id, future: future_value })?;
|
||||
self.emit_instruction(MirInstruction::Safepoint)?;
|
||||
Ok(result_id)
|
||||
}
|
||||
|
||||
// me: resolve to param if present; else symbolic const (stable mapping)
|
||||
pub(super) fn build_me_expression(&mut self) -> Result<ValueId, String> {
|
||||
self.build_me_expression_legacy()
|
||||
if let Some(id) = self.variable_map.get("me").cloned() { return Ok(id); }
|
||||
let me_value = self.value_gen.next();
|
||||
let me_tag = if let Some(ref cls) = self.current_static_box { cls.clone() } else { "__me__".to_string() };
|
||||
self.emit_instruction(MirInstruction::Const { dst: me_value, value: ConstValue::String(me_tag) })?;
|
||||
self.variable_map.insert("me".to_string(), me_value);
|
||||
Ok(me_value)
|
||||
}
|
||||
}
|
||||
|
||||
// Local helper for if-statement analysis
|
||||
fn extract_assigned_var(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(st)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
65
src/mir/builder/utils.rs
Normal file
65
src/mir/builder/utils.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use std::fs;
|
||||
|
||||
// Resolve include path using nyash.toml include.roots if present
|
||||
pub(super) fn resolve_include_path_builder(filename: &str) -> String {
|
||||
if filename.starts_with("./") || filename.starts_with("../") {
|
||||
return filename.to_string();
|
||||
}
|
||||
let parts: Vec<&str> = filename.splitn(2, '/').collect();
|
||||
if parts.len() == 2 {
|
||||
let root = parts[0];
|
||||
let rest = parts[1];
|
||||
let cfg_path = "nyash.toml";
|
||||
if let Ok(toml_str) = fs::read_to_string(cfg_path) {
|
||||
if let Ok(toml_val) = toml::from_str::<toml::Value>(&toml_str) {
|
||||
if let Some(include) = toml_val.get("include") {
|
||||
if let Some(roots) = include.get("roots").and_then(|v| v.as_table()) {
|
||||
if let Some(root_path) = roots.get(root).and_then(|v| v.as_str()) {
|
||||
let mut base = root_path.to_string();
|
||||
if !base.ends_with('/') && !base.ends_with('\\') {
|
||||
base.push('/');
|
||||
}
|
||||
return format!("{}{}", base, rest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
format!("./{}", filename)
|
||||
}
|
||||
|
||||
// Optional builder debug logging
|
||||
pub(super) fn builder_debug_enabled() -> bool {
|
||||
std::env::var("NYASH_BUILDER_DEBUG").is_ok()
|
||||
}
|
||||
|
||||
pub(super) fn builder_debug_log(msg: &str) {
|
||||
if builder_debug_enabled() {
|
||||
eprintln!("[BUILDER] {}", msg);
|
||||
}
|
||||
}
|
||||
|
||||
// PHI-based return type inference helper
|
||||
pub(super) fn infer_type_from_phi(
|
||||
function: &super::MirFunction,
|
||||
ret_val: super::ValueId,
|
||||
types: &std::collections::HashMap<super::ValueId, super::MirType>,
|
||||
) -> Option<super::MirType> {
|
||||
for (_bid, bb) in function.blocks.iter() {
|
||||
for inst in bb.instructions.iter() {
|
||||
if let super::MirInstruction::Phi { dst, inputs } = inst {
|
||||
if *dst == ret_val {
|
||||
let mut it = inputs.iter().filter_map(|(_, v)| types.get(v));
|
||||
if let Some(first) = it.next() {
|
||||
if it.all(|mt| mt == first) {
|
||||
return Some(first.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@ -1,118 +0,0 @@
|
||||
/*!
|
||||
* MIR Builder - AST to MIR conversion
|
||||
*
|
||||
* This is the main entry point for the MIR builder.
|
||||
* Most implementation has been moved to submodules:
|
||||
* - core.rs: Core builder functionality
|
||||
* - expressions.rs: Expression conversion
|
||||
* - statements.rs: Statement conversion
|
||||
* - control_flow.rs: Control flow conversion
|
||||
* - box_handlers.rs: Box-related conversions
|
||||
*/
|
||||
|
||||
pub mod core;
|
||||
pub mod expressions;
|
||||
pub mod statements;
|
||||
pub mod control_flow;
|
||||
pub mod box_handlers;
|
||||
pub mod loop_builder;
|
||||
|
||||
pub use self::core::MirBuilder;
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::{MirModule, MirFunction, MirInstruction, FunctionSignature, MirType, EffectMask, ValueId};
|
||||
|
||||
impl MirBuilder {
|
||||
/// Build a MIR module from an AST
|
||||
pub fn build_module(&mut self, ast: ASTNode) -> Result<MirModule, String> {
|
||||
// Create a new module
|
||||
let module = MirModule::new("main".to_string());
|
||||
|
||||
// Create a main function to contain the AST
|
||||
let main_signature = FunctionSignature {
|
||||
name: "main".to_string(),
|
||||
params: vec![],
|
||||
return_type: MirType::Void,
|
||||
effects: EffectMask::PURE,
|
||||
};
|
||||
|
||||
let entry_block = self.block_gen.next();
|
||||
let mut main_function = MirFunction::new(main_signature, entry_block);
|
||||
main_function.metadata.is_entry_point = true;
|
||||
|
||||
// Set up building context
|
||||
self.current_module = Some(module);
|
||||
self.current_function = Some(main_function);
|
||||
self.current_block = Some(entry_block);
|
||||
|
||||
// Optional: Add safepoint at function entry
|
||||
if std::env::var("NYASH_BUILDER_SAFEPOINT_ENTRY").ok().as_deref() == Some("1") {
|
||||
self.emit_instruction(MirInstruction::Safepoint)?;
|
||||
}
|
||||
|
||||
// Convert AST to MIR
|
||||
let result_value = self.build_expression(ast)?;
|
||||
|
||||
// Add return instruction if needed
|
||||
if let Some(block_id) = self.current_block {
|
||||
if let Some(ref mut function) = self.current_function {
|
||||
if let Some(block) = function.get_block_mut(block_id) {
|
||||
if !block.is_terminated() {
|
||||
block.add_instruction(MirInstruction::Return {
|
||||
value: Some(result_value),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize and return module
|
||||
let mut module = self.current_module.take().unwrap();
|
||||
let function = self.current_function.take().unwrap();
|
||||
module.add_function(function);
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ast::{ASTNode, LiteralValue, Span};
|
||||
|
||||
#[test]
|
||||
fn test_build_literal() {
|
||||
let mut builder = MirBuilder::new();
|
||||
let ast = ASTNode::Literal {
|
||||
value: LiteralValue::Integer(42),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
|
||||
let result = builder.build_module(ast);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let module = result.unwrap();
|
||||
assert!(module.functions.contains_key("main"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_simple_program() {
|
||||
let mut builder = MirBuilder::new();
|
||||
let ast = ASTNode::Program {
|
||||
statements: vec![
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(2),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
],
|
||||
span: Span::unknown(),
|
||||
};
|
||||
|
||||
let result = builder.build_module(ast);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
/*!
|
||||
* MIR Builder Box Handlers - Box-related AST node conversion
|
||||
*
|
||||
* Handles conversion of Box-related AST nodes (new expressions, box declarations) to MIR instructions
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::ast::ASTNode;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
impl MirBuilder {
|
||||
/// Build static box Main - extracts main() method body and converts to Program
|
||||
pub(super) fn build_static_main_box(&mut self, methods: HashMap<String, ASTNode>) -> Result<ValueId, String> {
|
||||
// Look for the main() method
|
||||
if let Some(main_method) = methods.get("main") {
|
||||
if let ASTNode::FunctionDeclaration { body, .. } = main_method {
|
||||
// Convert the method body to a Program AST node and lower it
|
||||
let program_ast = ASTNode::Program {
|
||||
statements: body.clone(),
|
||||
span: crate::ast::Span::unknown(),
|
||||
};
|
||||
|
||||
// Use existing Program lowering logic
|
||||
self.build_expression(program_ast)
|
||||
} else {
|
||||
Err("main method in static box Main is not a FunctionDeclaration".to_string())
|
||||
}
|
||||
} else {
|
||||
Err("static box Main must contain a main() method".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// Build box declaration - register type metadata
|
||||
pub(super) fn build_box_declaration(&mut self, name: String, methods: HashMap<String, ASTNode>, fields: Vec<String>, weak_fields: Vec<String>) -> Result<(), String> {
|
||||
// For Phase 8.4, we'll emit metadata instructions to register the box type
|
||||
// In a full implementation, this would register type information for later use
|
||||
|
||||
// Create a type registration constant
|
||||
let type_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: type_id,
|
||||
value: ConstValue::String(format!("__box_type_{}", name)),
|
||||
})?;
|
||||
|
||||
// For each field, emit metadata about the field
|
||||
for field in fields {
|
||||
let field_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: field_id,
|
||||
value: ConstValue::String(format!("__field_{}_{}", name, field)),
|
||||
})?;
|
||||
}
|
||||
|
||||
// Record weak fields for this box
|
||||
if !weak_fields.is_empty() {
|
||||
let set: HashSet<String> = weak_fields.into_iter().collect();
|
||||
self.weak_fields_by_box.insert(name.clone(), set);
|
||||
}
|
||||
|
||||
// Process methods - now methods is a HashMap
|
||||
for (method_name, method_ast) in methods {
|
||||
if let ASTNode::FunctionDeclaration { .. } = method_ast {
|
||||
let method_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: method_id,
|
||||
value: ConstValue::String(format!("__method_{}_{}", name, method_name)),
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -1,293 +0,0 @@
|
||||
/*!
|
||||
* MIR Builder Core - Core builder functionality
|
||||
*
|
||||
* Contains the MirBuilder struct and core instruction emission functionality
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::mir::TypeOpKind;
|
||||
use crate::ast::ASTNode;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub fn builder_debug_enabled() -> bool {
|
||||
std::env::var("NYASH_BUILDER_DEBUG").is_ok()
|
||||
}
|
||||
|
||||
pub fn builder_debug_log(msg: &str) {
|
||||
if builder_debug_enabled() {
|
||||
eprintln!("[BUILDER] {}", msg);
|
||||
}
|
||||
}
|
||||
|
||||
/// MIR builder for converting AST to SSA form
|
||||
pub struct MirBuilder {
|
||||
/// Current module being built
|
||||
pub(super) current_module: Option<MirModule>,
|
||||
|
||||
/// Current function being built
|
||||
pub(super) current_function: Option<MirFunction>,
|
||||
|
||||
/// Current basic block being built
|
||||
pub(super) current_block: Option<BasicBlockId>,
|
||||
|
||||
/// Value ID generator
|
||||
pub(super) value_gen: ValueIdGenerator,
|
||||
|
||||
/// Basic block ID generator
|
||||
pub(super) block_gen: BasicBlockIdGenerator,
|
||||
|
||||
/// Variable name to ValueId mapping (for SSA conversion)
|
||||
pub(super) variable_map: HashMap<String, ValueId>,
|
||||
|
||||
/// Pending phi functions to be inserted
|
||||
#[allow(dead_code)]
|
||||
pub(super) pending_phis: Vec<(BasicBlockId, ValueId, String)>,
|
||||
|
||||
/// Origin tracking for simple optimizations (e.g., object.method after new)
|
||||
/// Maps a ValueId to the class name if it was produced by NewBox of that class
|
||||
pub(super) value_origin_newbox: HashMap<ValueId, String>,
|
||||
|
||||
/// Names of user-defined boxes declared in the current module
|
||||
pub(super) user_defined_boxes: HashSet<String>,
|
||||
|
||||
/// Weak field registry: BoxName -> {weak field names}
|
||||
pub(super) weak_fields_by_box: HashMap<String, HashSet<String>>,
|
||||
|
||||
/// Remember class of object fields after assignments: (base_id, field) -> class_name
|
||||
pub(super) field_origin_class: HashMap<(ValueId, String), String>,
|
||||
}
|
||||
|
||||
impl MirBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current_module: None,
|
||||
current_function: None,
|
||||
current_block: None,
|
||||
value_gen: ValueIdGenerator::new(),
|
||||
block_gen: BasicBlockIdGenerator::new(),
|
||||
variable_map: HashMap::new(),
|
||||
pending_phis: Vec::new(),
|
||||
value_origin_newbox: HashMap::new(),
|
||||
user_defined_boxes: HashSet::new(),
|
||||
weak_fields_by_box: HashMap::new(),
|
||||
field_origin_class: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn emit_type_check(&mut self, value: ValueId, expected_type: String) -> Result<ValueId, String> {
|
||||
let dst = self.value_gen.next();
|
||||
let instruction = MirInstruction::TypeOp {
|
||||
dst,
|
||||
op: TypeOpKind::Check,
|
||||
value,
|
||||
ty: MirType::Box(expected_type),
|
||||
};
|
||||
self.emit_instruction(instruction)?;
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
pub(super) fn emit_cast(&mut self, value: ValueId, target_type: super::MirType) -> Result<ValueId, String> {
|
||||
let dst = self.value_gen.next();
|
||||
let instruction = MirInstruction::TypeOp { dst, op: TypeOpKind::Cast, value, ty: target_type };
|
||||
self.emit_instruction(instruction)?;
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
pub(super) fn emit_weak_new(&mut self, box_val: ValueId) -> Result<ValueId, String> {
|
||||
// Legacy emission toggle (default OFF): NYASH_BUILDER_LEGACY_WEAK=1
|
||||
let legacy = std::env::var("NYASH_BUILDER_LEGACY_WEAK").ok().as_deref() == Some("1");
|
||||
let dst = self.value_gen.next();
|
||||
let instruction = if legacy {
|
||||
if builder_debug_enabled() { eprintln!("[BUILDER] emit WeakNew (legacy on)"); }
|
||||
MirInstruction::WeakNew { dst, box_val }
|
||||
} else {
|
||||
if builder_debug_enabled() { eprintln!("[BUILDER] emit WeakRef(New)"); }
|
||||
MirInstruction::WeakRef { dst, op: super::super::WeakRefOp::New, value: box_val }
|
||||
};
|
||||
self.emit_instruction(instruction)?;
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
pub(super) fn emit_weak_load(&mut self, weak_ref: ValueId) -> Result<ValueId, String> {
|
||||
// Legacy emission toggle (default OFF): NYASH_BUILDER_LEGACY_WEAK=1
|
||||
let legacy = std::env::var("NYASH_BUILDER_LEGACY_WEAK").ok().as_deref() == Some("1");
|
||||
let dst = self.value_gen.next();
|
||||
let instruction = if legacy {
|
||||
if builder_debug_enabled() { eprintln!("[BUILDER] emit WeakLoad (legacy on)"); }
|
||||
MirInstruction::WeakLoad { dst, weak_ref }
|
||||
} else {
|
||||
if builder_debug_enabled() { eprintln!("[BUILDER] emit WeakRef(Load)"); }
|
||||
MirInstruction::WeakRef { dst, op: super::super::WeakRefOp::Load, value: weak_ref }
|
||||
};
|
||||
self.emit_instruction(instruction)?;
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
pub(super) fn emit_barrier_read(&mut self, ptr: ValueId) -> Result<(), String> {
|
||||
// Legacy emission toggle (default OFF): NYASH_BUILDER_LEGACY_BARRIER=1
|
||||
let legacy = std::env::var("NYASH_BUILDER_LEGACY_BARRIER").ok().as_deref() == Some("1");
|
||||
let instruction = if legacy {
|
||||
if builder_debug_enabled() { eprintln!("[BUILDER] emit BarrierRead (legacy on)"); }
|
||||
MirInstruction::BarrierRead { ptr }
|
||||
} else {
|
||||
if builder_debug_enabled() { eprintln!("[BUILDER] emit Barrier(Read)"); }
|
||||
MirInstruction::Barrier { op: super::super::BarrierOp::Read, ptr }
|
||||
};
|
||||
self.emit_instruction(instruction)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn emit_barrier_write(&mut self, ptr: ValueId) -> Result<(), String> {
|
||||
// Legacy emission toggle (default OFF): NYASH_BUILDER_LEGACY_BARRIER=1
|
||||
let legacy = std::env::var("NYASH_BUILDER_LEGACY_BARRIER").ok().as_deref() == Some("1");
|
||||
let instruction = if legacy {
|
||||
if builder_debug_enabled() { eprintln!("[BUILDER] emit BarrierWrite (legacy on)"); }
|
||||
MirInstruction::BarrierWrite { ptr }
|
||||
} else {
|
||||
if builder_debug_enabled() { eprintln!("[BUILDER] emit Barrier(Write)"); }
|
||||
MirInstruction::Barrier { op: super::super::BarrierOp::Write, ptr }
|
||||
};
|
||||
self.emit_instruction(instruction)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn emit_instruction(&mut self, instruction: MirInstruction) -> Result<(), String> {
|
||||
// Ensure we have a current function to emit into
|
||||
if self.current_function.is_none() {
|
||||
return Err("Cannot emit instruction without current function".to_string());
|
||||
}
|
||||
|
||||
// Ensure we have a current block to emit into
|
||||
if self.current_block.is_none() {
|
||||
return Err("Cannot emit instruction without current block".to_string());
|
||||
}
|
||||
|
||||
let current_block_id = self.current_block.unwrap();
|
||||
// Ensure the block exists first (uses &mut self)
|
||||
self.ensure_block_exists(current_block_id)?;
|
||||
// Then borrow current_function mutably to add instruction
|
||||
{
|
||||
let f = self.current_function.as_mut().unwrap();
|
||||
if let Some(bb) = f.get_block_mut(current_block_id) {
|
||||
bb.add_instruction(instruction);
|
||||
} else {
|
||||
return Err(format!("Block {:?} not found in current function", current_block_id));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn ensure_block_exists(&mut self, block_id: BasicBlockId) -> Result<(), String> {
|
||||
let current_function = self.current_function.as_mut()
|
||||
.ok_or("No current function")?;
|
||||
|
||||
if current_function.get_block(block_id).is_none() {
|
||||
current_function.add_block(BasicBlock::new(block_id));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn start_new_block(&mut self, block_id: BasicBlockId) -> Result<(), String> {
|
||||
// Ensure the block exists in the current function
|
||||
self.ensure_block_exists(block_id)?;
|
||||
|
||||
// Set as current block
|
||||
self.current_block = Some(block_id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MirBuilder {
|
||||
/// Build a MIR module from AST (thin outer shell aligning with legacy builder)
|
||||
pub fn build_module(&mut self, ast: ASTNode) -> Result<MirModule, String> {
|
||||
// Create empty module and a main function
|
||||
let module = MirModule::new("main".to_string());
|
||||
let main_signature = FunctionSignature {
|
||||
name: "main".to_string(),
|
||||
params: vec![],
|
||||
return_type: MirType::Void,
|
||||
effects: EffectMask::PURE,
|
||||
};
|
||||
let entry_block = self.block_gen.next();
|
||||
let mut main_function = MirFunction::new(main_signature, entry_block);
|
||||
main_function.metadata.is_entry_point = true;
|
||||
|
||||
// Set context
|
||||
self.current_module = Some(module);
|
||||
self.current_function = Some(main_function);
|
||||
self.current_block = Some(entry_block);
|
||||
|
||||
// Optional entry safepoint
|
||||
if std::env::var("NYASH_BUILDER_SAFEPOINT_ENTRY").ok().as_deref() == Some("1") {
|
||||
self.emit_instruction(MirInstruction::Safepoint)?;
|
||||
}
|
||||
|
||||
// Lower AST to MIR
|
||||
let result_value = self.build_expression(ast)?;
|
||||
|
||||
// Ensure a return at the end
|
||||
if let Some(block_id) = self.current_block {
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
if let Some(bb) = f.get_block_mut(block_id) {
|
||||
if !bb.is_terminated() {
|
||||
bb.add_instruction(MirInstruction::Return { value: Some(result_value) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize
|
||||
let mut module = self.current_module.take().unwrap();
|
||||
let function = self.current_function.take().unwrap();
|
||||
module.add_function(function);
|
||||
Ok(module)
|
||||
}
|
||||
}
|
||||
|
||||
// Adapter: Implement LoopBuilderApi for modularized MirBuilder to enable shared helpers
|
||||
impl crate::mir::loop_api::LoopBuilderApi for MirBuilder {
|
||||
fn new_block(&mut self) -> super::BasicBlockId { self.block_gen.next() }
|
||||
fn current_block(&self) -> Result<super::BasicBlockId, String> {
|
||||
self.current_block.ok_or_else(|| "No current block".to_string())
|
||||
}
|
||||
fn start_new_block(&mut self, block: super::BasicBlockId) -> Result<(), String> {
|
||||
self.start_new_block(block)
|
||||
}
|
||||
fn emit(&mut self, inst: super::MirInstruction) -> Result<(), String> {
|
||||
self.emit_instruction(inst)
|
||||
}
|
||||
fn new_value(&mut self) -> super::ValueId { self.value_gen.next() }
|
||||
|
||||
fn add_predecessor(&mut self, block: super::BasicBlockId, pred: super::BasicBlockId) -> Result<(), String> {
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
if let Some(bb) = f.get_block_mut(block) {
|
||||
bb.add_predecessor(pred);
|
||||
Ok(())
|
||||
} else { Err(format!("Block {} not found", block.as_u32())) }
|
||||
} else { Err("No current function".into()) }
|
||||
}
|
||||
|
||||
fn seal_block(&mut self, block: super::BasicBlockId) -> Result<(), String> {
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
if let Some(bb) = f.get_block_mut(block) {
|
||||
bb.seal();
|
||||
Ok(())
|
||||
} else { Err(format!("Block {} not found", block.as_u32())) }
|
||||
} else { Err("No current function".into()) }
|
||||
}
|
||||
|
||||
fn insert_phi_at_block_start(&mut self, block: super::BasicBlockId, dst: super::ValueId, inputs: Vec<(super::BasicBlockId, super::ValueId)>) -> Result<(), String> {
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
if let Some(bb) = f.get_block_mut(block) {
|
||||
let inst = super::MirInstruction::Phi { dst, inputs };
|
||||
bb.effects = bb.effects | inst.effects();
|
||||
bb.instructions.insert(0, inst);
|
||||
Ok(())
|
||||
} else { Err(format!("Block {} not found", block.as_u32())) }
|
||||
} else { Err("No current function".into()) }
|
||||
}
|
||||
}
|
||||
@ -1,596 +0,0 @@
|
||||
/*!
|
||||
* MIR Builder Expressions - Expression AST node conversion
|
||||
*
|
||||
* Handles conversion of expression AST nodes to MIR instructions
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::mir::TypeOpKind;
|
||||
use crate::ast::{ASTNode, LiteralValue, BinaryOperator};
|
||||
|
||||
/// Binary operation type classification
|
||||
enum BinaryOpType {
|
||||
Arithmetic(BinaryOp),
|
||||
Comparison(CompareOp),
|
||||
}
|
||||
|
||||
impl MirBuilder {
|
||||
/// Build a literal value into MIR
|
||||
pub(super) fn build_literal(&mut self, literal: LiteralValue) -> Result<ValueId, String> {
|
||||
let const_value = match literal {
|
||||
LiteralValue::Integer(n) => ConstValue::Integer(n),
|
||||
LiteralValue::Float(f) => ConstValue::Float(f),
|
||||
LiteralValue::String(s) => ConstValue::String(s),
|
||||
LiteralValue::Bool(b) => ConstValue::Bool(b),
|
||||
LiteralValue::Null => ConstValue::Null,
|
||||
LiteralValue::Void => ConstValue::Void,
|
||||
};
|
||||
|
||||
let dst = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst,
|
||||
value: const_value,
|
||||
})?;
|
||||
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
/// Build a binary operation
|
||||
pub(super) fn build_binary_op(&mut self, left: ASTNode, operator: BinaryOperator, right: ASTNode) -> Result<ValueId, String> {
|
||||
let lhs = self.build_expression(left)?;
|
||||
let rhs = self.build_expression(right)?;
|
||||
let dst = self.value_gen.next();
|
||||
|
||||
let mir_op = self.convert_binary_operator(operator)?;
|
||||
|
||||
match mir_op {
|
||||
// Arithmetic operations
|
||||
BinaryOpType::Arithmetic(op) => {
|
||||
self.emit_instruction(MirInstruction::BinOp {
|
||||
dst, op, lhs, rhs
|
||||
})?;
|
||||
},
|
||||
|
||||
// Comparison operations
|
||||
BinaryOpType::Comparison(op) => {
|
||||
// 80/20: If both operands originate from IntegerBox, cast to integer first
|
||||
let (lhs2, rhs2) = if self.value_origin_newbox.get(&lhs).map(|s| s == "IntegerBox").unwrap_or(false)
|
||||
&& self.value_origin_newbox.get(&rhs).map(|s| s == "IntegerBox").unwrap_or(false) {
|
||||
let li = self.value_gen.next();
|
||||
let ri = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst: li, op: TypeOpKind::Cast, value: lhs, ty: MirType::Integer })?;
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst: ri, op: TypeOpKind::Cast, value: rhs, ty: MirType::Integer })?;
|
||||
(li, ri)
|
||||
} else { (lhs, rhs) };
|
||||
self.emit_instruction(MirInstruction::Compare { dst, op, lhs: lhs2, rhs: rhs2 })?;
|
||||
},
|
||||
}
|
||||
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
/// Build a unary operation
|
||||
pub(super) fn build_unary_op(&mut self, operator: String, operand: ASTNode) -> Result<ValueId, String> {
|
||||
let operand_val = self.build_expression(operand)?;
|
||||
let dst = self.value_gen.next();
|
||||
|
||||
let mir_op = self.convert_unary_operator(operator)?;
|
||||
|
||||
self.emit_instruction(MirInstruction::UnaryOp {
|
||||
dst,
|
||||
op: mir_op,
|
||||
operand: operand_val,
|
||||
})?;
|
||||
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
/// Convert AST binary operator to MIR operator
|
||||
fn convert_binary_operator(&self, op: BinaryOperator) -> Result<BinaryOpType, String> {
|
||||
match op {
|
||||
BinaryOperator::Add => Ok(BinaryOpType::Arithmetic(BinaryOp::Add)),
|
||||
BinaryOperator::Subtract => Ok(BinaryOpType::Arithmetic(BinaryOp::Sub)),
|
||||
BinaryOperator::Multiply => Ok(BinaryOpType::Arithmetic(BinaryOp::Mul)),
|
||||
BinaryOperator::Divide => Ok(BinaryOpType::Arithmetic(BinaryOp::Div)),
|
||||
BinaryOperator::Modulo => Ok(BinaryOpType::Arithmetic(BinaryOp::Mod)),
|
||||
BinaryOperator::Equal => Ok(BinaryOpType::Comparison(CompareOp::Eq)),
|
||||
BinaryOperator::NotEqual => Ok(BinaryOpType::Comparison(CompareOp::Ne)),
|
||||
BinaryOperator::Less => Ok(BinaryOpType::Comparison(CompareOp::Lt)),
|
||||
BinaryOperator::LessEqual => Ok(BinaryOpType::Comparison(CompareOp::Le)),
|
||||
BinaryOperator::Greater => Ok(BinaryOpType::Comparison(CompareOp::Gt)),
|
||||
BinaryOperator::GreaterEqual => Ok(BinaryOpType::Comparison(CompareOp::Ge)),
|
||||
BinaryOperator::And => Ok(BinaryOpType::Arithmetic(BinaryOp::And)),
|
||||
BinaryOperator::Or => Ok(BinaryOpType::Arithmetic(BinaryOp::Or)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert AST unary operator to MIR operator
|
||||
fn convert_unary_operator(&self, op: String) -> Result<UnaryOp, String> {
|
||||
match op.as_str() {
|
||||
"-" => Ok(UnaryOp::Neg),
|
||||
"!" | "not" => Ok(UnaryOp::Not),
|
||||
"~" => Ok(UnaryOp::BitNot),
|
||||
_ => Err(format!("Unsupported unary operator: {}", op)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build variable access
|
||||
pub(super) fn build_variable_access(&mut self, name: String) -> Result<ValueId, String> {
|
||||
if let Some(&value_id) = self.variable_map.get(&name) {
|
||||
Ok(value_id)
|
||||
} else {
|
||||
Err(format!("Undefined variable: {}", name))
|
||||
}
|
||||
}
|
||||
|
||||
/// Build field access
|
||||
pub(super) fn build_field_access(&mut self, object: ASTNode, field: String) -> Result<ValueId, String> {
|
||||
// Clone the object before building expression if we need to check it later
|
||||
let object_clone = object.clone();
|
||||
|
||||
// First, build the object expression to get its ValueId
|
||||
let object_value = self.build_expression(object)?;
|
||||
|
||||
// Get the field from the object using RefGet
|
||||
let field_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::RefGet {
|
||||
dst: field_val,
|
||||
reference: object_value,
|
||||
field: field.clone(),
|
||||
})?;
|
||||
|
||||
// If we recorded origin class for this field on this base object, propagate it to this value id
|
||||
if let Some(class_name) = self.field_origin_class.get(&(object_value, field.clone())).cloned() {
|
||||
self.value_origin_newbox.insert(field_val, class_name);
|
||||
}
|
||||
|
||||
// If we can infer the box type and the field is weak, emit WeakLoad (+ optional barrier)
|
||||
let mut inferred_class: Option<String> = self.value_origin_newbox.get(&object_value).cloned();
|
||||
// Fallback: if the object is a nested field access like (X.Y).Z, consult recorded field origins for X.Y
|
||||
if inferred_class.is_none() {
|
||||
if let ASTNode::FieldAccess { object: inner_obj, field: ref parent_field, .. } = object_clone {
|
||||
// Build inner base to get a stable id and consult mapping
|
||||
if let Ok(base_id) = self.build_expression(*inner_obj.clone()) {
|
||||
if let Some(cls) = self.field_origin_class.get(&(base_id, parent_field.clone())) {
|
||||
inferred_class = Some(cls.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(class_name) = inferred_class {
|
||||
if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) {
|
||||
if weak_set.contains(&field) {
|
||||
// Barrier (read) PoC
|
||||
let _ = self.emit_barrier_read(field_val);
|
||||
// WeakLoad
|
||||
let loaded = self.emit_weak_load(field_val)?;
|
||||
return Ok(loaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(field_val)
|
||||
}
|
||||
|
||||
/// Build function call
|
||||
pub(super) fn build_function_call(&mut self, name: String, args: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||
// Minimal TypeOp wiring via function-style: isType(value, "Type"), asType(value, "Type")
|
||||
if (name == "isType" || name == "asType") && args.len() == 2 {
|
||||
if let Some(type_name) = Self::extract_string_literal(&args[1]) {
|
||||
let val = self.build_expression(args[0].clone())?;
|
||||
let ty = Self::parse_type_name_to_mir(&type_name);
|
||||
let dst = self.value_gen.next();
|
||||
let op = if name == "isType" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?;
|
||||
return Ok(dst);
|
||||
}
|
||||
}
|
||||
// Build argument values
|
||||
let mut arg_values = Vec::new();
|
||||
for arg in args {
|
||||
arg_values.push(self.build_expression(arg)?);
|
||||
}
|
||||
// Prepare function identifier as a const String value id
|
||||
let func_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: func_val,
|
||||
value: ConstValue::String(name),
|
||||
})?;
|
||||
|
||||
// Destination for the call result
|
||||
let dst = self.value_gen.next();
|
||||
|
||||
// Emit Call with conservative read effects
|
||||
self.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(dst),
|
||||
func: func_val,
|
||||
args: arg_values,
|
||||
effects: EffectMask::READ.add(Effect::ReadHeap),
|
||||
})?;
|
||||
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
/// Parse type name string to MIR type
|
||||
pub(super) fn parse_type_name_to_mir(name: &str) -> super::MirType {
|
||||
match name {
|
||||
"Integer" | "Int" | "I64" => super::MirType::Integer,
|
||||
"Float" | "F64" => super::MirType::Float,
|
||||
"Bool" | "Boolean" => super::MirType::Bool,
|
||||
"String" => super::MirType::String,
|
||||
"Void" | "Unit" => super::MirType::Void,
|
||||
other => super::MirType::Box(other.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract string literal from AST node if possible
|
||||
/// Supports: Literal("Type") and new StringBox("Type")
|
||||
pub(super) fn extract_string_literal(node: &ASTNode) -> Option<String> {
|
||||
let mut cur = node;
|
||||
loop {
|
||||
match cur {
|
||||
ASTNode::Literal { value: crate::ast::LiteralValue::String(s), .. } => return Some(s.clone()),
|
||||
ASTNode::New { class, arguments, .. } if class == "StringBox" && arguments.len() == 1 => {
|
||||
cur = &arguments[0];
|
||||
continue;
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Build me expression
|
||||
pub(super) fn build_me_expression(&mut self) -> Result<ValueId, String> {
|
||||
// If lowering a method/birth function, "me" should be a parameter
|
||||
if let Some(id) = self.variable_map.get("me").cloned() {
|
||||
return Ok(id);
|
||||
}
|
||||
|
||||
// Fallback: use a symbolic constant (legacy behavior)
|
||||
let me_value = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: me_value,
|
||||
value: ConstValue::String("__me__".to_string()),
|
||||
})?;
|
||||
// Register a stable mapping so subsequent 'me' resolves to the same ValueId
|
||||
self.variable_map.insert("me".to_string(), me_value);
|
||||
Ok(me_value)
|
||||
}
|
||||
|
||||
/// Build method call: object.method(arguments)
|
||||
pub(super) fn build_method_call(&mut self, object: ASTNode, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||
// Minimal TypeOp wiring via method-style syntax: value.is("Type") / value.as("Type")
|
||||
if (method == "is" || method == "as") && arguments.len() == 1 {
|
||||
if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
|
||||
// Build the object expression
|
||||
let object_value = self.build_expression(object.clone())?;
|
||||
// Map string to MIR type
|
||||
let mir_ty = Self::parse_type_name_to_mir(&type_name);
|
||||
let dst = self.value_gen.next();
|
||||
let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?;
|
||||
return Ok(dst);
|
||||
}
|
||||
}
|
||||
// ExternCall判定はobjectの変数解決より先に行う(未定義変数で落とさない)
|
||||
if let ASTNode::Variable { name: object_name, .. } = object.clone() {
|
||||
// Build argument expressions first (externはobject自体を使わない)
|
||||
let mut arg_values = Vec::new();
|
||||
for arg in &arguments {
|
||||
arg_values.push(self.build_expression(arg.clone())?);
|
||||
}
|
||||
match (object_name.as_str(), method.as_str()) {
|
||||
("console", "log") => {
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: None,
|
||||
iface_name: "env.console".to_string(),
|
||||
method_name: "log".to_string(),
|
||||
args: arg_values,
|
||||
effects: EffectMask::IO,
|
||||
})?;
|
||||
let void_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: void_id, value: ConstValue::Void })?;
|
||||
return Ok(void_id);
|
||||
},
|
||||
("canvas", "fillRect") | ("canvas", "fillText") => {
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: None,
|
||||
iface_name: "env.canvas".to_string(),
|
||||
method_name: method,
|
||||
args: arg_values,
|
||||
effects: EffectMask::IO,
|
||||
})?;
|
||||
let void_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: void_id, value: ConstValue::Void })?;
|
||||
return Ok(void_id);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Build the object expression
|
||||
let object_value = self.build_expression(object.clone())?;
|
||||
|
||||
// Secondary interception for is/as in case early path did not trigger
|
||||
if (method == "is" || method == "as") && arguments.len() == 1 {
|
||||
if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
|
||||
let mir_ty = Self::parse_type_name_to_mir(&type_name);
|
||||
let dst = self.value_gen.next();
|
||||
let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?;
|
||||
return Ok(dst);
|
||||
}
|
||||
}
|
||||
|
||||
// Build argument expressions
|
||||
let mut arg_values = Vec::new();
|
||||
for arg in &arguments {
|
||||
arg_values.push(self.build_expression(arg.clone())?);
|
||||
}
|
||||
|
||||
// Create result value
|
||||
let result_id = self.value_gen.next();
|
||||
|
||||
// Optimization: If the object is a direct `new ClassName(...)`, lower to a direct Call
|
||||
if let ASTNode::New { class, .. } = object {
|
||||
// Build function name and only lower to Call if the function exists (user-defined)
|
||||
let func_name = format!("{}.{}{}", class, method, format!("/{}", arg_values.len()));
|
||||
let can_lower = self.user_defined_boxes.contains(&class)
|
||||
&& if let Some(ref module) = self.current_module { module.functions.contains_key(&func_name) } else { false };
|
||||
if can_lower {
|
||||
let func_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: func_val, value: ConstValue::String(func_name) })?;
|
||||
let mut call_args = Vec::with_capacity(arg_values.len() + 1);
|
||||
call_args.push(object_value);
|
||||
call_args.extend(arg_values);
|
||||
self.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(result_id),
|
||||
func: func_val,
|
||||
args: call_args,
|
||||
effects: EffectMask::READ.add(Effect::ReadHeap),
|
||||
})?;
|
||||
return Ok(result_id);
|
||||
}
|
||||
// else fall through to BoxCall below
|
||||
} else {
|
||||
// If the object originates from a NewBox in this function, we can lower to Call as well
|
||||
if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() {
|
||||
let func_name = format!("{}.{}{}", class_name, method, format!("/{}", arg_values.len()));
|
||||
let can_lower = self.user_defined_boxes.contains(&class_name)
|
||||
&& if let Some(ref module) = self.current_module { module.functions.contains_key(&func_name) } else { false };
|
||||
if can_lower {
|
||||
let func_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: func_val, value: ConstValue::String(func_name) })?;
|
||||
let mut call_args = Vec::with_capacity(arg_values.len() + 1);
|
||||
call_args.push(object_value);
|
||||
call_args.extend(arg_values);
|
||||
self.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(result_id),
|
||||
func: func_val,
|
||||
args: call_args,
|
||||
effects: EffectMask::READ.add(Effect::ReadHeap),
|
||||
})?;
|
||||
return Ok(result_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Emit a PluginInvoke instruction for regular method calls
|
||||
self.emit_instruction(MirInstruction::PluginInvoke {
|
||||
dst: Some(result_id),
|
||||
box_val: object_value,
|
||||
method,
|
||||
args: arg_values,
|
||||
effects: EffectMask::READ.add(Effect::ReadHeap),
|
||||
})?;
|
||||
Ok(result_id)
|
||||
}
|
||||
|
||||
/// Build from expression: from Parent.method(arguments)
|
||||
pub(super) fn build_from_expression(&mut self, parent: String, method: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||
// Build argument expressions
|
||||
let mut arg_values = Vec::new();
|
||||
for arg in arguments {
|
||||
arg_values.push(self.build_expression(arg)?);
|
||||
}
|
||||
|
||||
// Get parent box's me value if available
|
||||
if let Some(&parent_me) = self.variable_map.get(&format!("{}.__me__", parent)) {
|
||||
// Emit from call with parent's me
|
||||
let result = self.value_gen.next();
|
||||
let func_id = self.value_gen.next(); // Placeholder for from resolution
|
||||
self.emit_instruction(MirInstruction::Call {
|
||||
dst: Some(result),
|
||||
func: func_id,
|
||||
args: vec![parent_me], // Include parent's me plus other args
|
||||
effects: EffectMask::READ.add(Effect::ReadHeap),
|
||||
})?;
|
||||
Ok(result)
|
||||
} else {
|
||||
// Fallback behavior without proper parent context
|
||||
let result = self.value_gen.next();
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// Build expression from AST
|
||||
pub(super) fn build_expression(&mut self, ast: ASTNode) -> Result<ValueId, String> {
|
||||
match ast {
|
||||
ASTNode::Literal { value, .. } => {
|
||||
self.build_literal(value)
|
||||
},
|
||||
|
||||
ASTNode::BinaryOp { left, operator, right, .. } => {
|
||||
self.build_binary_op(*left, operator, *right)
|
||||
},
|
||||
|
||||
ASTNode::UnaryOp { operator, operand, .. } => {
|
||||
let op_string = match operator {
|
||||
crate::ast::UnaryOperator::Minus => "-".to_string(),
|
||||
crate::ast::UnaryOperator::Not => "not".to_string(),
|
||||
};
|
||||
self.build_unary_op(op_string, *operand)
|
||||
},
|
||||
|
||||
ASTNode::Variable { name, .. } => {
|
||||
self.build_variable_access(name.clone())
|
||||
},
|
||||
|
||||
ASTNode::Me { .. } => {
|
||||
self.build_me_expression()
|
||||
},
|
||||
|
||||
ASTNode::MethodCall { object, method, arguments, .. } => {
|
||||
// Early TypeOp lowering for method-style is()/as()
|
||||
if (method == "is" || method == "as") && arguments.len() == 1 {
|
||||
if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
|
||||
let obj_val = self.build_expression(*object.clone())?;
|
||||
let ty = Self::parse_type_name_to_mir(&type_name);
|
||||
let dst = self.value_gen.next();
|
||||
let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?;
|
||||
return Ok(dst);
|
||||
}
|
||||
}
|
||||
self.build_method_call(*object.clone(), method.clone(), arguments.clone())
|
||||
},
|
||||
|
||||
ASTNode::FromCall { parent, method, arguments, .. } => {
|
||||
self.build_from_expression(parent.clone(), method.clone(), arguments.clone())
|
||||
},
|
||||
|
||||
ASTNode::Assignment { target, value, .. } => {
|
||||
// Check if target is a field access for RefSet
|
||||
if let ASTNode::FieldAccess { object, field, .. } = target.as_ref() {
|
||||
self.build_field_assignment(*object.clone(), field.clone(), *value.clone())
|
||||
} else if let ASTNode::Variable { name, .. } = target.as_ref() {
|
||||
self.build_assignment(name.clone(), *value.clone())
|
||||
} else {
|
||||
Err("Invalid assignment target".to_string())
|
||||
}
|
||||
},
|
||||
|
||||
ASTNode::FunctionCall { name, arguments, .. } => {
|
||||
self.build_function_call(name.clone(), arguments.clone())
|
||||
},
|
||||
|
||||
ASTNode::FieldAccess { object, field, .. } => {
|
||||
self.build_field_access(*object.clone(), field.clone())
|
||||
},
|
||||
|
||||
ASTNode::New { class, arguments, .. } => {
|
||||
self.build_new_expression(class.clone(), arguments.clone())
|
||||
},
|
||||
|
||||
ASTNode::AwaitExpression { expression, .. } => {
|
||||
self.build_await_expression(*expression)
|
||||
},
|
||||
|
||||
_ => {
|
||||
Err(format!("Unsupported expression type: {:?}", ast))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Build assignment: variable = value
|
||||
pub(super) fn build_assignment(&mut self, var_name: String, value: ASTNode) -> Result<ValueId, String> {
|
||||
let value_id = self.build_expression(value)?;
|
||||
|
||||
// In SSA form, each assignment creates a new value
|
||||
self.variable_map.insert(var_name.clone(), value_id);
|
||||
|
||||
Ok(value_id)
|
||||
}
|
||||
|
||||
/// Build field assignment: object.field = value
|
||||
pub(super) fn build_field_assignment(&mut self, object: ASTNode, field: String, value: ASTNode) -> Result<ValueId, String> {
|
||||
// Build the object and value expressions
|
||||
let object_value = self.build_expression(object)?;
|
||||
let mut value_result = self.build_expression(value)?;
|
||||
|
||||
// If we can infer the box type and the field is weak, create WeakRef before store
|
||||
if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() {
|
||||
if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) {
|
||||
if weak_set.contains(&field) {
|
||||
value_result = self.emit_weak_new(value_result)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the field using RefSet
|
||||
self.emit_instruction(MirInstruction::RefSet {
|
||||
reference: object_value,
|
||||
field: field.clone(),
|
||||
value: value_result,
|
||||
})?;
|
||||
|
||||
// Emit a write barrier for weak fields (PoC)
|
||||
if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() {
|
||||
if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) {
|
||||
if weak_set.contains(&field) {
|
||||
let _ = self.emit_barrier_write(value_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Record origin class for this field (if the value originates from NewBox of a known class)
|
||||
if let Some(class_name) = self.value_origin_newbox.get(&value_result).cloned() {
|
||||
self.field_origin_class.insert((object_value, field.clone()), class_name);
|
||||
}
|
||||
|
||||
// Return the assigned value
|
||||
Ok(value_result)
|
||||
}
|
||||
|
||||
/// Build new expression: new ClassName(arguments)
|
||||
pub(super) fn build_new_expression(&mut self, class: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||
// Phase 9.78a: Unified Box creation using NewBox instruction
|
||||
|
||||
// First, evaluate all arguments to get their ValueIds
|
||||
let mut arg_values = Vec::new();
|
||||
for arg in arguments {
|
||||
let arg_value = self.build_expression(arg)?;
|
||||
arg_values.push(arg_value);
|
||||
}
|
||||
|
||||
// Generate the destination ValueId
|
||||
let dst = self.value_gen.next();
|
||||
|
||||
// Emit NewBox instruction for all Box types
|
||||
// VM will handle optimization for basic types internally
|
||||
self.emit_instruction(MirInstruction::NewBox {
|
||||
dst,
|
||||
box_type: class.clone(),
|
||||
args: arg_values.clone(),
|
||||
})?;
|
||||
|
||||
// Record origin for optimization: dst was created by NewBox of class
|
||||
self.value_origin_newbox.insert(dst, class);
|
||||
|
||||
// Immediately call birth(...) (plugin invoke) on the created instance
|
||||
self.emit_instruction(MirInstruction::PluginInvoke {
|
||||
dst: None,
|
||||
box_val: dst,
|
||||
method: "birth".to_string(),
|
||||
args: arg_values,
|
||||
effects: EffectMask::READ.add(Effect::ReadHeap),
|
||||
})?;
|
||||
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
/// Build await expression: await expression
|
||||
pub(super) fn build_await_expression(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
||||
// Evaluate the expression (should be a Future)
|
||||
let future_value = self.build_expression(expression)?;
|
||||
// Insert checkpoint before await (safepoint)
|
||||
self.emit_instruction(MirInstruction::Safepoint)?;
|
||||
// Create result value for the await
|
||||
let result_id = self.value_gen.next();
|
||||
// Emit await instruction
|
||||
self.emit_instruction(MirInstruction::Await { dst: result_id, future: future_value })?;
|
||||
// Insert checkpoint after await (safepoint)
|
||||
self.emit_instruction(MirInstruction::Safepoint)?;
|
||||
Ok(result_id)
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
/*!
|
||||
* MIR Builder Module - Modular AST to MIR conversion
|
||||
*
|
||||
* This module contains the refactored MIR builder split into focused sub-modules:
|
||||
*
|
||||
* - `core`: Core builder functionality and instruction emission
|
||||
* - `expressions`: Expression AST node conversion
|
||||
* - `statements`: Statement AST node conversion
|
||||
* - `control_flow`: Control flow constructs (if, loop, try-catch)
|
||||
* - `box_handlers`: Box-related operations (new, declarations)
|
||||
*/
|
||||
|
||||
pub mod core;
|
||||
pub mod expressions;
|
||||
pub mod statements;
|
||||
pub mod control_flow;
|
||||
pub mod box_handlers;
|
||||
|
||||
// Re-export the main builder struct and key functionality
|
||||
pub use self::core::MirBuilder;
|
||||
|
||||
// Re-export commonly used types from the parent module
|
||||
pub use super::{
|
||||
MirInstruction, BasicBlock, BasicBlockId, MirFunction, MirModule,
|
||||
FunctionSignature, ValueId, ConstValue, BinaryOp, UnaryOp, CompareOp,
|
||||
MirType, EffectMask, Effect, BasicBlockIdGenerator, ValueIdGenerator, TypeOpKind
|
||||
};
|
||||
@ -1,177 +0,0 @@
|
||||
/*!
|
||||
* MIR Builder Statements - Statement AST node conversion
|
||||
*
|
||||
* Handles conversion of statement AST nodes to MIR instructions
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::mir::builder_modularized::core::builder_debug_log;
|
||||
use crate::mir::TypeOpKind;
|
||||
use crate::ast::ASTNode;
|
||||
|
||||
impl MirBuilder {
|
||||
/// Build print statement - converts to console output
|
||||
pub(super) fn build_print_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
||||
builder_debug_log("enter build_print_statement");
|
||||
// 根治: print(isType(...)) / print(asType(...)) / print(obj.is(...)) / print(obj.as(...)) は必ずTypeOpを先に生成してからprintする
|
||||
match &expression {
|
||||
ASTNode::FunctionCall { name, arguments, .. } if (name == "isType" || name == "asType") && arguments.len() == 2 => {
|
||||
builder_debug_log("pattern: print(FunctionCall isType|asType)");
|
||||
if let Some(type_name) = Self::extract_string_literal(&arguments[1]) {
|
||||
builder_debug_log(&format!("extract_string_literal OK: {}", type_name));
|
||||
let val = self.build_expression(arguments[0].clone())?;
|
||||
let ty = Self::parse_type_name_to_mir(&type_name);
|
||||
let dst = self.value_gen.next();
|
||||
let op = if name == "isType" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||
builder_debug_log(&format!("emit TypeOp {:?} value={} dst= {}", op, val, dst));
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?;
|
||||
self.emit_instruction(MirInstruction::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![dst], effects: EffectMask::PURE.add(Effect::Io) })?;
|
||||
return Ok(dst);
|
||||
} else {
|
||||
builder_debug_log("extract_string_literal FAIL");
|
||||
}
|
||||
}
|
||||
ASTNode::MethodCall { object, method, arguments, .. } if (method == "is" || method == "as") && arguments.len() == 1 => {
|
||||
builder_debug_log("pattern: print(MethodCall is|as)");
|
||||
if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
|
||||
builder_debug_log(&format!("extract_string_literal OK: {}", type_name));
|
||||
let obj_val = self.build_expression(*object.clone())?;
|
||||
let ty = Self::parse_type_name_to_mir(&type_name);
|
||||
let dst = self.value_gen.next();
|
||||
let op = if method == "is" { TypeOpKind::Check } else { TypeOpKind::Cast };
|
||||
builder_debug_log(&format!("emit TypeOp {:?} obj={} dst= {}", op, obj_val, dst));
|
||||
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?;
|
||||
self.emit_instruction(MirInstruction::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![dst], effects: EffectMask::PURE.add(Effect::Io) })?;
|
||||
return Ok(dst);
|
||||
} else {
|
||||
builder_debug_log("extract_string_literal FAIL");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let value = self.build_expression(expression)?;
|
||||
builder_debug_log(&format!("fallback print value={}", value));
|
||||
|
||||
self.emit_instruction(MirInstruction::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![value], effects: EffectMask::PURE.add(Effect::Io) })?;
|
||||
|
||||
// Return the value that was printed
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// Build a block of statements
|
||||
pub(super) fn build_block(&mut self, statements: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||
let mut last_value = None;
|
||||
|
||||
for statement in statements {
|
||||
last_value = Some(self.build_expression(statement)?);
|
||||
}
|
||||
|
||||
// Return last value or void
|
||||
Ok(last_value.unwrap_or_else(|| {
|
||||
let void_val = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: void_val,
|
||||
value: ConstValue::Void,
|
||||
}).unwrap();
|
||||
void_val
|
||||
}))
|
||||
}
|
||||
|
||||
/// Build local variable declarations with optional initial values
|
||||
pub(super) fn build_local_statement(&mut self, variables: Vec<String>, initial_values: Vec<Option<Box<ASTNode>>>) -> Result<ValueId, String> {
|
||||
let mut last_value = None;
|
||||
|
||||
// Process each variable declaration
|
||||
for (i, var_name) in variables.iter().enumerate() {
|
||||
let value_id = if i < initial_values.len() && initial_values[i].is_some() {
|
||||
// Variable has initial value - evaluate it
|
||||
let init_expr = initial_values[i].as_ref().unwrap();
|
||||
self.build_expression(*init_expr.clone())?
|
||||
} else {
|
||||
// No initial value - do not emit a const; leave uninitialized until assigned
|
||||
// Use a fresh SSA id only for name binding; consumers should not use it before assignment
|
||||
self.value_gen.next()
|
||||
};
|
||||
|
||||
// Register variable in SSA form
|
||||
self.variable_map.insert(var_name.clone(), value_id);
|
||||
last_value = Some(value_id);
|
||||
}
|
||||
|
||||
// Return the last bound value id (no emission); callers shouldn't rely on this value
|
||||
Ok(last_value.unwrap_or_else(|| {
|
||||
// create a dummy id without emission
|
||||
self.value_gen.next()
|
||||
}))
|
||||
}
|
||||
|
||||
/// Build return statement
|
||||
pub(super) fn build_return_statement(&mut self, value: Option<Box<ASTNode>>) -> Result<ValueId, String> {
|
||||
let return_value = if let Some(expr) = value {
|
||||
self.build_expression(*expr)?
|
||||
} else {
|
||||
// Return void if no value specified
|
||||
let void_dst = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const {
|
||||
dst: void_dst,
|
||||
value: ConstValue::Void,
|
||||
})?;
|
||||
void_dst
|
||||
};
|
||||
|
||||
// Emit return instruction
|
||||
self.emit_instruction(MirInstruction::Return {
|
||||
value: Some(return_value),
|
||||
})?;
|
||||
|
||||
Ok(return_value)
|
||||
}
|
||||
|
||||
/// Build a throw statement
|
||||
pub(super) fn build_throw_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
||||
if std::env::var("NYASH_BUILDER_DISABLE_THROW").ok().as_deref() == Some("1") {
|
||||
let v = self.build_expression(expression)?;
|
||||
self.emit_instruction(MirInstruction::ExternCall { dst: None, iface_name: "env.debug".to_string(), method_name: "trace".to_string(), args: vec![v], effects: EffectMask::PURE.add(Effect::Debug) })?;
|
||||
return Ok(v);
|
||||
}
|
||||
let exception_value = self.build_expression(expression)?;
|
||||
self.emit_instruction(MirInstruction::Throw { exception: exception_value, effects: EffectMask::PANIC })?;
|
||||
Ok(exception_value)
|
||||
}
|
||||
|
||||
/// Build nowait statement: nowait variable = expression
|
||||
pub(super) fn build_nowait_statement(&mut self, variable: String, expression: ASTNode) -> Result<ValueId, String> {
|
||||
// If the expression is a method call on a receiver, spawn it asynchronously via env.future.spawn_instance
|
||||
if let ASTNode::MethodCall { object, method, arguments, .. } = expression.clone() {
|
||||
// Build receiver value
|
||||
let recv_val = self.build_expression(*object)?;
|
||||
// Build method name as Const String
|
||||
let mname_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: mname_id, value: crate::mir::ConstValue::String(method.clone()) })?;
|
||||
// Build argument values
|
||||
let mut arg_vals: Vec<ValueId> = Vec::with_capacity(2 + arguments.len());
|
||||
arg_vals.push(recv_val);
|
||||
arg_vals.push(mname_id);
|
||||
for a in arguments.into_iter() { arg_vals.push(self.build_expression(a)?); }
|
||||
// Emit extern call to env.future.spawn_instance, capturing Future result
|
||||
let future_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: Some(future_id),
|
||||
iface_name: "env.future".to_string(),
|
||||
method_name: "spawn_instance".to_string(),
|
||||
args: arg_vals,
|
||||
effects: crate::mir::effect::EffectMask::PURE.add(crate::mir::effect::Effect::Io),
|
||||
})?;
|
||||
// Store the future in the variable
|
||||
self.variable_map.insert(variable.clone(), future_id);
|
||||
return Ok(future_id);
|
||||
}
|
||||
// Fallback: evaluate synchronously and wrap into a resolved Future
|
||||
let expression_value = self.build_expression(expression)?;
|
||||
let future_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::FutureNew { dst: future_id, value: expression_value })?;
|
||||
self.variable_map.insert(variable.clone(), future_id);
|
||||
Ok(future_id)
|
||||
}
|
||||
}
|
||||
@ -11,8 +11,6 @@ pub mod instruction_introspection; // Introspection helpers for tests (core inst
|
||||
pub mod basic_block;
|
||||
pub mod function;
|
||||
pub mod builder;
|
||||
#[cfg(feature = "mir_modular_builder")]
|
||||
pub mod builder_modularized; // Modular split of MIR builder (optional)
|
||||
pub mod loop_builder; // SSA loop construction with phi nodes
|
||||
pub mod loop_api; // Minimal LoopBuilder facade (adapter-ready)
|
||||
pub mod verification;
|
||||
@ -29,10 +27,7 @@ pub use instruction::{MirInstruction, BinaryOp, CompareOp, UnaryOp, ConstValue,
|
||||
pub use instruction_v2::{MirInstructionV2, AtomicOrdering}; // New 25-instruction set
|
||||
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
|
||||
pub use function::{MirFunction, MirModule, FunctionSignature};
|
||||
#[cfg(not(feature = "mir_modular_builder"))]
|
||||
pub use builder::MirBuilder;
|
||||
#[cfg(feature = "mir_modular_builder")]
|
||||
pub use builder_modularized::MirBuilder;
|
||||
pub use verification::{MirVerifier, VerificationError};
|
||||
pub use ownership_verifier_simple::{OwnershipVerifier, OwnershipError, OwnershipStats}; // Simple ownership forest verification
|
||||
pub use printer::MirPrinter;
|
||||
@ -81,8 +76,8 @@ impl MirCompiler {
|
||||
if self.optimize {
|
||||
let mut optimizer = MirOptimizer::new();
|
||||
let stats = optimizer.optimize_module(&mut module);
|
||||
if (std::env::var("NYASH_OPT_DIAG_FAIL").is_ok()
|
||||
|| std::env::var("NYASH_OPT_DIAG_FORBID_LEGACY").is_ok())
|
||||
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));
|
||||
}
|
||||
|
||||
@ -40,14 +40,22 @@ impl MirOptimizer {
|
||||
println!("🚀 Starting MIR optimization passes");
|
||||
}
|
||||
|
||||
// Pass 0: Normalize legacy instructions to unified forms (TypeOp/WeakRef/Barrier/Array→BoxCall/Plugin→BoxCall)
|
||||
// Env toggles for phased MIR cleanup
|
||||
let core13 = crate::config::env::mir_core13();
|
||||
let mut ref_to_boxcall = crate::config::env::mir_ref_boxcall();
|
||||
if core13 { ref_to_boxcall = true; }
|
||||
|
||||
// Pass 0: Normalize legacy instructions to unified forms
|
||||
// - Includes optional Array→BoxCall guarded by env (inside the pass)
|
||||
stats.merge(self.normalize_legacy_instructions(module));
|
||||
// Pass 0.1: RefGet/RefSet → BoxCall(getField/setField)
|
||||
stats.merge(self.normalize_ref_field_access(module));
|
||||
// Pass 0.1: RefGet/RefSet → BoxCall(getField/setField) (guarded)
|
||||
if ref_to_boxcall {
|
||||
stats.merge(self.normalize_ref_field_access(module));
|
||||
}
|
||||
|
||||
// Option: Force BoxCall → PluginInvoke (env)
|
||||
if std::env::var("NYASH_MIR_PLUGIN_INVOKE").ok().as_deref() == Some("1")
|
||||
|| std::env::var("NYASH_PLUGIN_ONLY").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::mir_plugin_invoke()
|
||||
|| crate::config::env::plugin_only() {
|
||||
stats.merge(self.force_plugin_invoke(module));
|
||||
}
|
||||
|
||||
@ -373,9 +381,13 @@ impl MirOptimizer {
|
||||
fn normalize_legacy_instructions(&mut self, module: &mut MirModule) -> OptimizationStats {
|
||||
use super::{TypeOpKind, WeakRefOp, BarrierOp, MirInstruction as I, MirType};
|
||||
let mut stats = OptimizationStats::new();
|
||||
let rw_dbg = std::env::var("NYASH_REWRITE_DEBUG").ok().as_deref() == Some("1");
|
||||
let rw_sp = std::env::var("NYASH_REWRITE_SAFEPOINT").ok().as_deref() == Some("1");
|
||||
let rw_future = std::env::var("NYASH_REWRITE_FUTURE").ok().as_deref() == Some("1");
|
||||
let rw_dbg = crate::config::env::rewrite_debug();
|
||||
let rw_sp = crate::config::env::rewrite_safepoint();
|
||||
let rw_future = crate::config::env::rewrite_future();
|
||||
// Phase 11.8 toggles
|
||||
let core13 = crate::config::env::mir_core13();
|
||||
let mut array_to_boxcall = crate::config::env::mir_array_boxcall();
|
||||
if core13 { array_to_boxcall = true; }
|
||||
for (_fname, function) in &mut module.functions {
|
||||
for (_bb, block) in &mut function.blocks {
|
||||
// Rewrite in-place for normal instructions
|
||||
@ -410,14 +422,16 @@ impl MirOptimizer {
|
||||
let v = *value;
|
||||
*inst = I::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![v], effects: EffectMask::PURE.add(Effect::Io) };
|
||||
}
|
||||
I::RefGet { .. } | I::RefSet { .. } => { /* handled in normalize_ref_field_access pass */ }
|
||||
I::ArrayGet { dst, array, index } => {
|
||||
I::RefGet { .. } | I::RefSet { .. } => { /* handled in normalize_ref_field_access pass (guarded) */ }
|
||||
I::ArrayGet { dst, array, index } if array_to_boxcall => {
|
||||
let d = *dst; let a = *array; let i = *index;
|
||||
*inst = I::BoxCall { dst: Some(d), box_val: a, method: "get".to_string(), method_id: None, args: vec![i], effects: EffectMask::READ };
|
||||
let mid = crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "get");
|
||||
*inst = I::BoxCall { dst: Some(d), box_val: a, method: "get".to_string(), method_id: mid, args: vec![i], effects: EffectMask::READ };
|
||||
}
|
||||
I::ArraySet { array, index, value } => {
|
||||
I::ArraySet { array, index, value } if array_to_boxcall => {
|
||||
let a = *array; let i = *index; let v = *value;
|
||||
*inst = I::BoxCall { dst: None, box_val: a, method: "set".to_string(), method_id: None, args: vec![i, v], effects: EffectMask::WRITE };
|
||||
let mid = crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "set");
|
||||
*inst = I::BoxCall { dst: None, box_val: a, method: "set".to_string(), method_id: mid, args: vec![i, v], effects: EffectMask::WRITE };
|
||||
}
|
||||
I::PluginInvoke { dst, box_val, method, args, effects } => {
|
||||
let d = *dst; let recv = *box_val; let m = method.clone(); let as_ = args.clone(); let eff = *effects;
|
||||
@ -477,12 +491,14 @@ impl MirOptimizer {
|
||||
let v = *value;
|
||||
*term = I::ExternCall { dst: None, iface_name: "env.console".to_string(), method_name: "log".to_string(), args: vec![v], effects: EffectMask::PURE.add(Effect::Io) };
|
||||
}
|
||||
I::RefGet { .. } | I::RefSet { .. } => { /* handled in normalize_ref_field_access pass */ }
|
||||
I::ArrayGet { dst, array, index } => {
|
||||
*term = I::BoxCall { dst: Some(*dst), box_val: *array, method: "get".to_string(), method_id: None, args: vec![*index], effects: EffectMask::READ };
|
||||
I::RefGet { .. } | I::RefSet { .. } => { /* handled in normalize_ref_field_access pass (guarded) */ }
|
||||
I::ArrayGet { dst, array, index } if array_to_boxcall => {
|
||||
let mid = crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "get");
|
||||
*term = I::BoxCall { dst: Some(*dst), box_val: *array, method: "get".to_string(), method_id: mid, args: vec![*index], effects: EffectMask::READ };
|
||||
}
|
||||
I::ArraySet { array, index, value } => {
|
||||
*term = I::BoxCall { dst: None, box_val: *array, method: "set".to_string(), method_id: None, args: vec![*index, *value], effects: EffectMask::WRITE };
|
||||
I::ArraySet { array, index, value } if array_to_boxcall => {
|
||||
let mid = crate::mir::slot_registry::resolve_slot_by_type_name("ArrayBox", "set");
|
||||
*term = I::BoxCall { dst: None, box_val: *array, method: "set".to_string(), method_id: mid, args: vec![*index, *value], effects: EffectMask::WRITE };
|
||||
}
|
||||
I::PluginInvoke { dst, box_val, method, args, effects } => {
|
||||
*term = I::BoxCall { dst: *dst, box_val: *box_val, method: method.clone(), method_id: None, args: args.clone(), effects: *effects };
|
||||
@ -517,7 +533,7 @@ impl MirOptimizer {
|
||||
|
||||
/// Normalize RefGet/RefSet to BoxCall("getField"/"setField") with Const String field argument.
|
||||
fn normalize_ref_field_access(&mut self, module: &mut MirModule) -> OptimizationStats {
|
||||
use super::MirInstruction as I;
|
||||
use super::{MirInstruction as I, BarrierOp};
|
||||
let mut stats = OptimizationStats::new();
|
||||
for (_fname, function) in &mut module.functions {
|
||||
for (_bb, block) in &mut function.blocks {
|
||||
@ -536,6 +552,8 @@ impl MirOptimizer {
|
||||
let new_id = super::ValueId::new(function.next_value_id);
|
||||
function.next_value_id += 1;
|
||||
out.push(I::Const { dst: new_id, value: super::instruction::ConstValue::String(field) });
|
||||
// Prepend an explicit write barrier before setField to make side-effects visible
|
||||
out.push(I::Barrier { op: BarrierOp::Write, ptr: reference });
|
||||
out.push(I::BoxCall { dst: None, box_val: reference, method: "setField".to_string(), method_id: None, args: vec![new_id, value], effects: super::EffectMask::WRITE });
|
||||
stats.intrinsic_optimizations += 1;
|
||||
}
|
||||
@ -556,6 +574,7 @@ impl MirOptimizer {
|
||||
let new_id = super::ValueId::new(function.next_value_id);
|
||||
function.next_value_id += 1;
|
||||
block.instructions.push(I::Const { dst: new_id, value: super::instruction::ConstValue::String(field) });
|
||||
block.instructions.push(I::Barrier { op: BarrierOp::Write, ptr: reference });
|
||||
I::BoxCall { dst: None, box_val: reference, method: "setField".to_string(), method_id: None, args: vec![new_id, value], effects: super::EffectMask::WRITE }
|
||||
}
|
||||
other => other,
|
||||
@ -579,7 +598,7 @@ fn map_type_name(name: &str) -> MirType {
|
||||
}
|
||||
}
|
||||
|
||||
fn opt_debug_enabled() -> bool { std::env::var("NYASH_OPT_DEBUG").is_ok() }
|
||||
fn opt_debug_enabled() -> bool { crate::config::env::opt_debug() }
|
||||
fn opt_debug(msg: &str) { if opt_debug_enabled() { eprintln!("[OPT] {}", msg); } }
|
||||
|
||||
/// Resolve a MIR type from a value id that should represent a type name
|
||||
@ -672,7 +691,7 @@ impl MirOptimizer {
|
||||
/// Diagnostic: detect unlowered is/as/isType/asType after Builder
|
||||
fn diagnose_unlowered_type_ops(&mut self, module: &MirModule) -> OptimizationStats {
|
||||
let mut stats = OptimizationStats::new();
|
||||
let diag_on = self.debug || std::env::var("NYASH_OPT_DIAG").is_ok();
|
||||
let diag_on = self.debug || crate::config::env::opt_diag();
|
||||
for (fname, function) in &module.functions {
|
||||
// def map for resolving constants
|
||||
let mut def_map: std::collections::HashMap<ValueId, (super::basic_block::BasicBlockId, usize)> = std::collections::HashMap::new();
|
||||
@ -718,8 +737,8 @@ impl MirOptimizer {
|
||||
fn diagnose_legacy_instructions(&mut self, module: &MirModule) -> OptimizationStats {
|
||||
let mut stats = OptimizationStats::new();
|
||||
let diag_on = self.debug
|
||||
|| std::env::var("NYASH_OPT_DIAG").is_ok()
|
||||
|| std::env::var("NYASH_OPT_DIAG_FORBID_LEGACY").is_ok();
|
||||
|| crate::config::env::opt_diag()
|
||||
|| crate::config::env::opt_diag_forbid_legacy();
|
||||
for (fname, function) in &module.functions {
|
||||
let mut count = 0usize;
|
||||
for (_bb, block) in &function.blocks {
|
||||
|
||||
Reference in New Issue
Block a user