Phase 2: TypeOp変換の一本化(Optimizer安全ネット削除)\nPhase 3: 可視化/スナップショット基盤(--mir-verbose-effects, snapshot/compare/ci_check)\nDocs: Phase 1/2 完了マーク・利用方法追記

This commit is contained in:
Moe Charm
2025-08-24 01:58:41 +09:00
parent 3c3dc86be0
commit 0aef8d49a7
19 changed files with 835 additions and 132 deletions

View File

@ -12,9 +12,12 @@ use clap::{Arg, Command, ArgMatches};
pub struct CliConfig {
pub file: Option<String>,
pub debug_fuel: Option<usize>,
pub dump_ast: bool,
pub dump_mir: bool,
pub verify_mir: bool,
pub mir_verbose: bool,
pub mir_verbose_effects: bool,
pub no_optimize: bool,
pub backend: String,
pub compile_wasm: bool,
pub compile_native: bool,
@ -51,6 +54,12 @@ impl CliConfig {
.help("Set parser debug fuel limit (default: 100000, 'unlimited' for no limit)")
.default_value("100000")
)
.arg(
Arg::new("dump-ast")
.long("dump-ast")
.help("Dump parsed AST and exit")
.action(clap::ArgAction::SetTrue)
)
.arg(
Arg::new("dump-mir")
.long("dump-mir")
@ -69,6 +78,18 @@ impl CliConfig {
.help("Show verbose MIR output with statistics")
.action(clap::ArgAction::SetTrue)
)
.arg(
Arg::new("mir-verbose-effects")
.long("mir-verbose-effects")
.help("Show per-instruction effect category (pure/readonly/side)")
.action(clap::ArgAction::SetTrue)
)
.arg(
Arg::new("no-optimize")
.long("no-optimize")
.help("Disable MIR optimizer passes (dump raw Builder MIR)")
.action(clap::ArgAction::SetTrue)
)
.arg(
Arg::new("backend")
.long("backend")
@ -133,9 +154,12 @@ impl CliConfig {
Self {
file: matches.get_one::<String>("file").cloned(),
debug_fuel: parse_debug_fuel(matches.get_one::<String>("debug-fuel").unwrap()),
dump_ast: matches.get_flag("dump-ast"),
dump_mir: matches.get_flag("dump-mir"),
verify_mir: matches.get_flag("verify"),
mir_verbose: matches.get_flag("mir-verbose"),
mir_verbose_effects: matches.get_flag("mir-verbose-effects"),
no_optimize: matches.get_flag("no-optimize"),
backend: matches.get_one::<String>("backend").unwrap().clone(),
compile_wasm: matches.get_flag("compile-wasm"),
compile_native: matches.get_flag("compile-native") || matches.get_flag("aot"),

View File

@ -13,6 +13,16 @@ use crate::ast::{ASTNode, LiteralValue, BinaryOperator};
use std::collections::HashMap;
use std::collections::HashSet;
fn builder_debug_enabled() -> bool {
std::env::var("NYASH_BUILDER_DEBUG").is_ok()
}
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
@ -606,25 +616,46 @@ impl MirBuilder {
/// Build print statement - converts to console output
fn build_print_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
// Early lowering for print(isType(...)) / print(asType(...)) to avoid undefined SSA when optimizations run
if let ASTNode::FunctionCall { name, arguments, .. } = &expression {
if (name == "isType" || name == "asType") && arguments.len() == 2 {
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" { super::TypeOpKind::Check } else { super::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::Print {
value: dst,
effects: EffectMask::PURE.add(Effect::Io),
})?;
self.emit_instruction(MirInstruction::Print { value: 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" { super::TypeOpKind::Check } else { super::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::Print { value: 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));
// For now, use a special Print instruction (minimal scope)
self.emit_instruction(MirInstruction::Print {
@ -746,6 +777,19 @@ impl MirBuilder {
if let Some(ref mut function) = self.current_function {
if let Some(block) = function.get_block_mut(block_id) {
if builder_debug_enabled() {
eprintln!("[BUILDER] emit @bb{} -> {}", block_id, match &instruction {
MirInstruction::TypeOp { dst, op, value, ty } => format!("typeop {:?} {} {:?} -> {}", op, value, ty, dst),
MirInstruction::Print { value, .. } => format!("print {}", value),
MirInstruction::BoxCall { box_val, method, args, dst, .. } => format!("boxcall {}.{}({:?}) -> {:?}", box_val, method, args, dst),
MirInstruction::Call { func, args, dst, .. } => format!("call {}({:?}) -> {:?}", func, args, dst),
MirInstruction::NewBox { dst, box_type, args } => format!("new {}({:?}) -> {}", box_type, args, dst),
MirInstruction::Const { dst, value } => format!("const {:?} -> {}", value, dst),
MirInstruction::Branch { condition, then_bb, else_bb } => format!("br {}, {}, {}", condition, then_bb, else_bb),
MirInstruction::Jump { target } => format!("br {}", target),
_ => format!("{:?}", instruction),
});
}
block.add_instruction(instruction);
Ok(())
} else {
@ -1178,11 +1222,11 @@ impl MirBuilder {
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 ASTNode::Literal { value: crate::ast::LiteralValue::String(type_name), .. } = &arguments[0] {
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 mir_ty = Self::parse_type_name_to_mir(&type_name);
let dst = self.value_gen.next();
let op = if method == "is" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?;
@ -1230,8 +1274,8 @@ impl MirBuilder {
// Secondary interception for is/as in case early path did not trigger
if (method == "is" || method == "as") && arguments.len() == 1 {
if let ASTNode::Literal { value: crate::ast::LiteralValue::String(type_name), .. } = &arguments[0] {
let mir_ty = Self::parse_type_name_to_mir(type_name);
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" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
self.emit_instruction(MirInstruction::TypeOp { dst, op, value: object_value, ty: mir_ty })?;
@ -1318,17 +1362,16 @@ impl MirBuilder {
/// Extract string literal from AST node if possible
/// Supports: Literal("Type") and new StringBox("Type")
fn extract_string_literal(node: &ASTNode) -> Option<String> {
match node {
ASTNode::Literal { value: crate::ast::LiteralValue::String(s), .. } => Some(s.clone()),
ASTNode::New { class, arguments, .. } if class == "StringBox" => {
if arguments.len() == 1 {
if let ASTNode::Literal { value: crate::ast::LiteralValue::String(s), .. } = &arguments[0] {
return Some(s.clone());
}
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;
}
None
_ => return None,
}
_ => None,
}
}

View File

@ -129,7 +129,8 @@ impl EffectMask {
/// Check if the computation is pure (no side effects)
pub fn is_pure(self) -> bool {
self.contains(Effect::Pure) || self.0 == 0
// 純粋性は一次カテゴリで判定Pureビットが立っていてもIO/WRITE/CONTROL等があれば純粋ではない
self.primary_category() == Effect::Pure
}
/// Check if the computation is mutable (modifies state)
@ -346,5 +347,9 @@ mod tests {
let display = format!("{}", read_io);
assert!(display.contains("read"));
assert!(display.contains("io"));
// is_pure should be false when IO is present, even if PURE bit was set initially
let mixed = EffectMask::PURE | EffectMask::IO;
assert!(!mixed.is_pure());
}
}

View File

@ -42,6 +42,7 @@ pub struct MirCompileResult {
pub struct MirCompiler {
builder: MirBuilder,
verifier: MirVerifier,
optimize: bool,
}
impl MirCompiler {
@ -50,6 +51,15 @@ impl MirCompiler {
Self {
builder: MirBuilder::new(),
verifier: MirVerifier::new(),
optimize: true,
}
}
/// Create with options
pub fn with_options(optimize: bool) -> Self {
Self {
builder: MirBuilder::new(),
verifier: MirVerifier::new(),
optimize,
}
}
@ -58,9 +68,13 @@ impl MirCompiler {
// Convert AST to MIR using builder
let mut module = self.builder.build_module(ast)?;
// Optimize (safety net lowering for is/as → TypeOp 等)
let mut optimizer = MirOptimizer::new();
let _ = optimizer.optimize_module(&mut module);
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() && stats.diagnostics_reported > 0 {
return Err(format!("Diagnostic failure: {} unlowered type-op calls detected", stats.diagnostics_reported));
}
}
// Verify the generated MIR
let verification_result = self.verifier.verify_module(&module);

View File

@ -31,6 +31,7 @@ impl MirOptimizer {
self
}
/// Run all optimization passes on a MIR module
pub fn optimize_module(&mut self, module: &mut MirModule) -> OptimizationStats {
let mut stats = OptimizationStats::new();
@ -51,8 +52,7 @@ impl MirOptimizer {
// Pass 4: Intrinsic function optimization
stats.merge(self.optimize_intrinsic_calls(module));
// Pass 4.5: Lower well-known intrinsics (is/as → TypeOp) as a safety net
stats.merge(self.lower_type_ops(module));
// Safety-net passesは削除Phase 2: 変換の一本化)。診断のみ後段で実施。
// Pass 5: BoxField dependency optimization
stats.merge(self.optimize_boxfield_operations(module));
@ -60,6 +60,9 @@ impl MirOptimizer {
if self.debug {
println!("✅ Optimization complete: {}", stats);
}
// Diagnostics (informational): report unlowered patterns
let diag = self.diagnose_unlowered_type_ops(module);
stats.merge(diag);
stats
}
@ -128,11 +131,12 @@ impl MirOptimizer {
// Remove unused pure instructions
let mut eliminated = 0;
for (_, block) in &mut function.blocks {
for (bbid, block) in &mut function.blocks {
block.instructions.retain(|instruction| {
if instruction.effects().is_pure() {
if let Some(dst) = instruction.dst_value() {
if !used_values.contains(&dst) {
opt_debug(&format!("DCE drop @{}: {:?}", bbid.as_u32(), instruction));
eliminated += 1;
return false;
}
@ -258,101 +262,6 @@ impl MirOptimizer {
0
}
/// Safety-net lowering: convert known is/as patterns into TypeOp when possible
fn lower_type_ops(&mut self, module: &mut MirModule) -> OptimizationStats {
let mut stats = OptimizationStats::new();
for (_name, function) in &mut module.functions {
stats.intrinsic_optimizations += self.lower_type_ops_in_function(function);
}
stats
}
fn lower_type_ops_in_function(&mut self, function: &mut MirFunction) -> usize {
// Build a simple definition map: ValueId -> (block_id, index)
let mut def_map: std::collections::HashMap<ValueId, (super::basic_block::BasicBlockId, usize)> = std::collections::HashMap::new();
for (bb_id, block) in &function.blocks {
for (i, inst) in block.instructions.iter().enumerate() {
if let Some(dst) = inst.dst_value() {
def_map.insert(dst, (*bb_id, i));
}
}
if let Some(term) = &block.terminator {
if let Some(dst) = term.dst_value() {
def_map.insert(dst, (*bb_id, usize::MAX));
}
}
}
let mut lowered = 0;
// Collect replacements first to avoid borrow checker issues
let mut replacements: Vec<(super::basic_block::BasicBlockId, usize, MirInstruction)> = Vec::new();
// First pass: identify replacements
for (bb_id, block) in &function.blocks {
for i in 0..block.instructions.len() {
let replace = match &block.instructions[i] {
MirInstruction::BoxCall { dst, box_val, method, args, .. } => {
let is_is = method == "is" || method == "isType";
let is_as = method == "as" || method == "asType";
if (is_is || is_as) && args.len() == 1 {
// Try to resolve type name from arg
let arg_id = args[0];
if let Some((def_bb, def_idx)) = def_map.get(&arg_id).copied() {
// Look for pattern: NewBox StringBox(Const String)
if let Some(def_block) = function.blocks.get(&def_bb) {
if def_idx < def_block.instructions.len() {
if let MirInstruction::NewBox { box_type, args: sb_args, .. } = &def_block.instructions[def_idx] {
if box_type == "StringBox" && sb_args.len() == 1 {
let str_id = sb_args[0];
if let Some((sbb, sidx)) = def_map.get(&str_id).copied() {
if let Some(sblock) = function.blocks.get(&sbb) {
if sidx < sblock.instructions.len() {
if let MirInstruction::Const { value, .. } = &sblock.instructions[sidx] {
if let super::instruction::ConstValue::String(s) = value {
// Build TypeOp to replace
let ty = map_type_name(s);
let op = if is_is { TypeOpKind::Check } else { TypeOpKind::Cast };
let new_inst = MirInstruction::TypeOp {
dst: dst.unwrap_or(*box_val),
op,
value: *box_val,
ty,
};
Some(new_inst)
} else { None }
} else { None }
} else { None }
} else { None }
} else { None }
} else { None }
} else { None }
} else { None }
} else { None }
} else { None }
} else { None }
}
_ => None,
};
if let Some(new_inst) = replace {
replacements.push((*bb_id, i, new_inst));
}
}
}
// Second pass: apply replacements
for (bb_id, idx, new_inst) in replacements {
if let Some(block) = function.blocks.get_mut(&bb_id) {
if idx < block.instructions.len() {
block.instructions[idx] = new_inst;
lowered += 1;
}
}
}
lowered
}
/// Optimize BoxField operations
fn optimize_boxfield_operations(&mut self, module: &mut MirModule) -> OptimizationStats {
@ -392,6 +301,44 @@ fn map_type_name(name: &str) -> MirType {
}
}
fn opt_debug_enabled() -> bool { std::env::var("NYASH_OPT_DEBUG").is_ok() }
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
/// Supports: Const String("T") and NewBox(StringBox, Const String("T"))
fn resolve_type_from_value(
function: &MirFunction,
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), .. } => {
return Some(map_type_name(s));
}
MirInstruction::NewBox { box_type, args, .. } if box_type == "StringBox" && args.len() == 1 => {
let inner = args[0];
if let Some((sbb, sidx)) = def_map.get(&inner).copied() {
if let Some(sblock) = function.blocks.get(&sbb) {
if sidx < sblock.instructions.len() {
if let MirInstruction::Const { value: ConstValue::String(s), .. } = &sblock.instructions[sidx] {
return Some(map_type_name(s));
}
}
}
}
}
_ => {}
}
}
}
}
None
}
impl Default for MirOptimizer {
fn default() -> Self {
Self::new()
@ -406,6 +353,7 @@ pub struct OptimizationStats {
pub reorderings: usize,
pub intrinsic_optimizations: usize,
pub boxfield_optimizations: usize,
pub diagnostics_reported: usize,
}
impl OptimizationStats {
@ -419,6 +367,7 @@ impl OptimizationStats {
self.reorderings += other.reorderings;
self.intrinsic_optimizations += other.intrinsic_optimizations;
self.boxfield_optimizations += other.boxfield_optimizations;
self.diagnostics_reported += other.diagnostics_reported;
}
pub fn total_optimizations(&self) -> usize {
@ -441,6 +390,51 @@ impl std::fmt::Display for OptimizationStats {
}
}
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();
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();
for (bb_id, block) in &function.blocks {
for (i, inst) in block.instructions.iter().enumerate() {
if let Some(dst) = inst.dst_value() { def_map.insert(dst, (*bb_id, i)); }
}
if let Some(term) = &block.terminator { if let Some(dst) = term.dst_value() { def_map.insert(dst, (*bb_id, usize::MAX)); } }
}
let mut count = 0usize;
for (_bb, block) in &function.blocks {
for inst in &block.instructions {
match inst {
MirInstruction::BoxCall { method, .. } if method == "is" || method == "as" || method == "isType" || method == "asType" => { count += 1; }
MirInstruction::Call { func, .. } => {
if let Some((bb, idx)) = def_map.get(func).copied() {
if let Some(b) = function.blocks.get(&bb) {
if idx < b.instructions.len() {
if let MirInstruction::Const { value: super::instruction::ConstValue::String(s), .. } = &b.instructions[idx] {
if s == "isType" || s == "asType" { count += 1; }
}
}
}
}
}
_ => {}
}
}
}
if count > 0 {
stats.diagnostics_reported += count;
if diag_on {
eprintln!("[OPT][DIAG] Function '{}' has {} unlowered type-op calls", fname, count);
}
}
}
stats
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -489,4 +483,36 @@ mod tests {
assert!(key.contains("const"));
assert!(key.contains("42"));
}
#[test]
fn test_dce_does_not_drop_typeop_used_by_print() {
// Build a simple function: %v=TypeOp(check); print %v; ensure TypeOp remains after optimize
let signature = FunctionSignature {
name: "main".to_string(),
params: vec![],
return_type: MirType::Void,
effects: super::super::effect::EffectMask::PURE,
};
let mut func = MirFunction::new(signature, BasicBlockId::new(0));
let bb0 = BasicBlockId::new(0);
let mut b0 = BasicBlock::new(bb0);
let v0 = ValueId::new(0);
let v1 = ValueId::new(1);
b0.add_instruction(MirInstruction::NewBox { dst: v0, box_type: "IntegerBox".to_string(), args: vec![] });
b0.add_instruction(MirInstruction::TypeOp { dst: v1, op: TypeOpKind::Check, value: v0, ty: MirType::Integer });
b0.add_instruction(MirInstruction::Print { value: v1, effects: super::super::effect::EffectMask::IO });
b0.add_instruction(MirInstruction::Return { value: None });
func.add_block(b0);
let mut module = MirModule::new("test".to_string());
module.add_function(func);
let mut opt = MirOptimizer::new();
let _stats = opt.optimize_module(&mut module);
// Ensure TypeOp remains in bb0
let f = module.get_function("main").unwrap();
let block = f.get_block(&bb0).unwrap();
let has_typeop = block.all_instructions().any(|i| matches!(i, MirInstruction::TypeOp { .. }));
assert!(has_typeop, "TypeOp should not be dropped by DCE when used by print");
}
}

View File

@ -18,6 +18,9 @@ pub struct MirPrinter {
/// Whether to show line numbers
show_line_numbers: bool,
/// Whether to show per-instruction effect category
show_effects_inline: bool,
}
impl MirPrinter {
@ -27,6 +30,7 @@ impl MirPrinter {
indent_level: 0,
verbose: false,
show_line_numbers: true,
show_effects_inline: false,
}
}
@ -36,6 +40,7 @@ impl MirPrinter {
indent_level: 0,
verbose: true,
show_line_numbers: true,
show_effects_inline: false,
}
}
@ -50,6 +55,12 @@ impl MirPrinter {
self.show_line_numbers = show;
self
}
/// Show per-instruction effect category (pure/readonly/side)
pub fn set_show_effects_inline(&mut self, show: bool) -> &mut Self {
self.show_effects_inline = show;
self
}
/// Print a complete MIR module
pub fn print_module(&self, module: &MirModule) -> String {
@ -127,6 +138,70 @@ impl MirPrinter {
if stats.is_pure {
writeln!(output, " ; Pure: yes").unwrap();
}
// Verbose: highlight MIR26-unified ops presence for snapshotting (TypeOp/WeakRef/Barrier)
let mut type_check = 0usize;
let mut type_cast = 0usize;
let mut weak_new = 0usize;
let mut weak_load = 0usize;
let mut barrier_read = 0usize;
let mut barrier_write = 0usize;
for block in function.blocks.values() {
for inst in &block.instructions {
match inst {
MirInstruction::TypeCheck { .. } => type_check += 1,
MirInstruction::Cast { .. } => type_cast += 1,
MirInstruction::TypeOp { op, .. } => match op {
super::TypeOpKind::Check => type_check += 1,
super::TypeOpKind::Cast => type_cast += 1,
},
MirInstruction::WeakNew { .. } => weak_new += 1,
MirInstruction::WeakLoad { .. } => weak_load += 1,
MirInstruction::WeakRef { op, .. } => match op {
super::WeakRefOp::New => weak_new += 1,
super::WeakRefOp::Load => weak_load += 1,
},
MirInstruction::BarrierRead { .. } => barrier_read += 1,
MirInstruction::BarrierWrite { .. } => barrier_write += 1,
MirInstruction::Barrier { op, .. } => match op {
super::BarrierOp::Read => barrier_read += 1,
super::BarrierOp::Write => barrier_write += 1,
},
_ => {}
}
}
if let Some(term) = &block.terminator {
match term {
MirInstruction::TypeCheck { .. } => type_check += 1,
MirInstruction::Cast { .. } => type_cast += 1,
MirInstruction::TypeOp { op, .. } => match op {
super::TypeOpKind::Check => type_check += 1,
super::TypeOpKind::Cast => type_cast += 1,
},
MirInstruction::WeakNew { .. } => weak_new += 1,
MirInstruction::WeakLoad { .. } => weak_load += 1,
MirInstruction::WeakRef { op, .. } => match op {
super::WeakRefOp::New => weak_new += 1,
super::WeakRefOp::Load => weak_load += 1,
},
MirInstruction::BarrierRead { .. } => barrier_read += 1,
MirInstruction::BarrierWrite { .. } => barrier_write += 1,
MirInstruction::Barrier { op, .. } => match op {
super::BarrierOp::Read => barrier_read += 1,
super::BarrierOp::Write => barrier_write += 1,
},
_ => {}
}
}
}
if type_check + type_cast > 0 {
writeln!(output, " ; TypeOp: {} (check: {}, cast: {})", type_check + type_cast, type_check, type_cast).unwrap();
}
if weak_new + weak_load > 0 {
writeln!(output, " ; WeakRef: {} (new: {}, load: {})", weak_new + weak_load, weak_new, weak_load).unwrap();
}
if barrier_read + barrier_write > 0 {
writeln!(output, " ; Barrier: {} (read: {}, write: {})", barrier_read + barrier_write, barrier_read, barrier_write).unwrap();
}
writeln!(output).unwrap();
}
@ -174,7 +249,13 @@ impl MirPrinter {
write!(output, " ").unwrap();
}
writeln!(output, "{}", self.format_instruction(instruction)).unwrap();
let mut line = self.format_instruction(instruction);
if self.show_effects_inline {
let eff = instruction.effects();
let cat = if eff.is_pure() { "pure" } else if eff.is_read_only() { "readonly" } else { "side" };
line.push_str(&format!(" ; eff: {}", cat));
}
writeln!(output, "{}", line).unwrap();
line_num += 1;
}

View File

@ -113,6 +113,25 @@ impl NyashRunner {
/// Execute file-based mode with backend selection
fn execute_file_mode(&self, filename: &str) {
if self.config.dump_ast {
println!("🧠 Nyash AST Dump - Processing file: {}", filename);
let code = match fs::read_to_string(filename) {
Ok(content) => content,
Err(e) => {
eprintln!("❌ Error reading file {}: {}", filename, e);
process::exit(1);
}
};
let ast = match NyashParser::parse_from_string(&code) {
Ok(ast) => ast,
Err(e) => {
eprintln!("❌ Parse error: {}", e);
process::exit(1);
}
};
println!("{:#?}", ast);
return;
}
if self.config.dump_mir || self.config.verify_mir {
println!("🚀 Nyash MIR Compiler - Processing file: {} 🚀", filename);
self.execute_mir_mode(filename);
@ -271,8 +290,8 @@ impl NyashRunner {
}
};
// Compile to MIR
let mut mir_compiler = MirCompiler::new();
// Compile to MIR (opt passes configurable)
let mut mir_compiler = MirCompiler::with_options(!self.config.no_optimize);
let compile_result = match mir_compiler.compile(ast) {
Ok(result) => result,
Err(e) => {
@ -298,11 +317,8 @@ impl NyashRunner {
// Dump MIR if requested
if self.config.dump_mir {
let printer = if self.config.mir_verbose {
MirPrinter::verbose()
} else {
MirPrinter::new()
};
let mut printer = if self.config.mir_verbose { MirPrinter::verbose() } else { MirPrinter::new() };
if self.config.mir_verbose_effects { printer.set_show_effects_inline(true); }
println!("🚀 MIR Output for {}:", filename);
println!("{}", printer.print_module(&compile_result.module));
@ -345,8 +361,8 @@ impl NyashRunner {
rt
};
// Compile to MIR
let mut mir_compiler = MirCompiler::new();
// Compile to MIR (opt passes configurable)
let mut mir_compiler = MirCompiler::with_options(!self.config.no_optimize);
let compile_result = match mir_compiler.compile(ast) {
Ok(result) => result,
Err(e) => {

View File

@ -843,6 +843,20 @@ mod stub {
use once_cell::sync::Lazy;
use std::sync::{Arc, RwLock};
// Stub implementation of PluginBoxV2 for WASM/non-plugin builds
#[derive(Debug, Clone)]
pub struct PluginBoxV2 {
pub box_type: String,
pub inner: std::sync::Arc<PluginHandleInner>,
}
#[derive(Debug)]
pub struct PluginHandleInner {
pub type_id: u32,
pub instance_id: u32,
pub fini_method_id: Option<u32>,
}
pub struct PluginLoaderV2 {
pub config: Option<()>, // Dummy config for compatibility
}