merge: resolve conflicts (prefer cranelift-dev for Core-13 defaults; drop modules/using, add env.local/env.box shims)

This commit is contained in:
Tomoaki
2025-09-08 01:28:39 +09:00
373 changed files with 14800 additions and 614 deletions

View File

@ -95,9 +95,39 @@ pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result<B
MirInstruction::Copy { dst, src } => {
if let Some(v) = regs.get(src).cloned() { regs.insert(*dst, v); }
}
MirInstruction::Debug { .. } | MirInstruction::Print { .. } | MirInstruction::Barrier { .. } | MirInstruction::BarrierRead { .. } | MirInstruction::BarrierWrite { .. } | MirInstruction::Safepoint | MirInstruction::Load { .. } | MirInstruction::Store { .. } | MirInstruction::TypeOp { .. } | MirInstruction::Compare { .. } | MirInstruction::NewBox { .. } | MirInstruction::PluginInvoke { .. } | MirInstruction::BoxCall { .. } | MirInstruction::ExternCall { .. } | MirInstruction::RefGet { .. } | MirInstruction::RefSet { .. } | MirInstruction::WeakRef { .. } | MirInstruction::FutureNew { .. } | MirInstruction::FutureSet { .. } | MirInstruction::Await { .. } | MirInstruction::Throw { .. } | MirInstruction::Catch { .. } => {
MirInstruction::Debug { .. } | MirInstruction::Print { .. } | MirInstruction::Barrier { .. } | MirInstruction::BarrierRead { .. } | MirInstruction::BarrierWrite { .. } | MirInstruction::Safepoint | MirInstruction::Load { .. } | MirInstruction::Store { .. } | MirInstruction::TypeOp { .. } | MirInstruction::Compare { .. } | MirInstruction::NewBox { .. } | MirInstruction::PluginInvoke { .. } | MirInstruction::BoxCall { .. } | MirInstruction::RefGet { .. } | MirInstruction::RefSet { .. } | MirInstruction::WeakRef { .. } | MirInstruction::FutureNew { .. } | MirInstruction::FutureSet { .. } | MirInstruction::Await { .. } | MirInstruction::Throw { .. } | MirInstruction::Catch { .. } => {
// ignore for minimal path
}
MirInstruction::ExternCall { dst, iface_name, method_name, args, .. } => {
use crate::backend::vm::VMValue as V;
match (iface_name.as_str(), method_name.as_str()) {
("env.local", "get") => {
if let Some(d) = dst { if let Some(a0) = args.get(0) { if let Some(v) = regs.get(a0).cloned() { regs.insert(*d, v); } } }
}
("env.local", "set") => {
if args.len() >= 2 { if let Some(v) = regs.get(&args[1]).cloned() { regs.insert(args[0], v); } }
// dst ignored
}
("env.box", "new") => {
if let Some(d) = dst {
if let Some(a0) = args.get(0) {
if let Some(V::String(ty)) = regs.get(a0).cloned() {
let reg = crate::runtime::box_registry::get_global_registry();
// Collect args as NyashBox
let mut ny_args: Vec<Box<dyn crate::box_trait::NyashBox>> = Vec::new();
for vid in args.iter().skip(1) {
if let Some(v) = regs.get(vid).cloned() { ny_args.push(v.to_nyash_box()); }
}
if let Ok(b) = reg.create_box(&ty, &ny_args) {
regs.insert(*d, V::from_nyash_box(b));
}
}
}
}
}
_ => { /* ignore other externs in skeleton */ }
}
}
MirInstruction::Phi { .. } => { /* handled above */ }
_ => {}
}

View File

@ -6,12 +6,70 @@ use crate::backend::{VM, VMError, VMValue};
impl VM {
/// Execute ExternCall instruction
pub(crate) fn execute_extern_call(&mut self, dst: Option<ValueId>, iface_name: &str, method_name: &str, args: &[ValueId]) -> Result<ControlFlow, VMError> {
// Core-13 pure shims: env.local.{get,set}, env.box.new
match (iface_name, method_name) {
("env.local", "get") => {
if args.len() != 1 { return Err(VMError::InvalidInstruction("env.local.get arity".into())); }
let ptr = args[0];
let v = self.get_value(ptr).unwrap_or(crate::backend::vm::VMValue::Void);
if let Some(d) = dst { self.set_value(d, v); }
return Ok(ControlFlow::Continue);
}
("env.local", "set") => {
if args.len() != 2 { return Err(VMError::InvalidInstruction("env.local.set arity".into())); }
let ptr = args[0];
let val = self.get_value(args[1])?;
self.set_value(ptr, val);
if let Some(d) = dst { self.set_value(d, crate::backend::vm::VMValue::Void); }
return Ok(ControlFlow::Continue);
}
("env.box", "new") => {
if args.is_empty() { return Err(VMError::InvalidInstruction("env.box.new requires type name".into())); }
// first arg must be Const String type name
let ty = self.get_value(args[0])?;
let ty_name = match ty { crate::backend::vm::VMValue::String(s) => s, _ => return Err(VMError::InvalidInstruction("env.box.new first arg must be string".into())) };
// remaining args as NyashBox
let mut ny_args: Vec<Box<dyn NyashBox>> = Vec::new();
for id in args.iter().skip(1) {
let v = self.get_value(*id)?;
ny_args.push(v.to_nyash_box());
}
let reg = crate::runtime::box_registry::get_global_registry();
match reg.create_box(&ty_name, &ny_args) {
Ok(b) => { if let Some(d) = dst { self.set_value(d, crate::backend::vm::VMValue::from_nyash_box(b)); } }
Err(e) => { return Err(VMError::InvalidInstruction(format!("env.box.new failed for {}: {}", ty_name, e))); }
}
return Ok(ControlFlow::Continue);
}
_ => {}
}
// Optional routing to name→slot handlers for stability and diagnostics
if crate::config::env::extern_route_slots() {
if let Some(slot) = crate::runtime::extern_registry::resolve_slot(iface_name, method_name) {
// Decode args to VMValue as needed by handlers below
let vm_args: Vec<VMValue> = args.iter().filter_map(|a| self.get_value(*a).ok()).collect();
match (iface_name, method_name, slot) {
("env.local", "get", 40) => {
if let Some(d) = dst { if let Some(a0) = args.get(0) { let v = self.get_value(*a0).unwrap_or(VMValue::Void); self.set_value(d, v); } }
return Ok(ControlFlow::Continue);
}
("env.local", "set", 41) => {
if args.len() >= 2 { let ptr = args[0]; let val = vm_args.get(1).cloned().unwrap_or(VMValue::Void); self.set_value(ptr, val); }
if let Some(d) = dst { self.set_value(d, VMValue::Void); }
return Ok(ControlFlow::Continue);
}
("env.box", "new", 50) => {
if vm_args.is_empty() { return Err(VMError::InvalidInstruction("env.box.new requires type".into())); }
let ty = &vm_args[0]; let ty_name = match ty { VMValue::String(s) => s.clone(), _ => return Err(VMError::InvalidInstruction("env.box.new first arg must be string".into())) };
let mut ny_args: Vec<Box<dyn NyashBox>> = Vec::new();
for v in vm_args.iter().skip(1) { ny_args.push(v.to_nyash_box()); }
let reg = crate::runtime::box_registry::get_global_registry();
match reg.create_box(&ty_name, &ny_args) {
Ok(b) => { if let Some(d) = dst { self.set_value(d, VMValue::from_nyash_box(b)); } }
Err(e) => { return Err(VMError::InvalidInstruction(format!("env.box.new failed for {}: {}", ty_name, e))); }
}
return Ok(ControlFlow::Continue);
}
("env.console", m @ ("log" | "warn" | "error" | "println"), 10) => {
if let Some(a0) = vm_args.get(0) {
match m { "warn" => eprintln!("[warn] {}", a0.to_string()), "error" => eprintln!("[error] {}", a0.to_string()), _ => println!("{}", a0.to_string()), }

View File

@ -57,10 +57,6 @@ pub struct CliConfig {
// Phase-15: JSON IR v0 bridge
pub ny_parser_pipe: bool,
pub json_file: Option<String>,
// Using/module resolution helpers (MVP)
pub using: Option<String>,
pub using_path: Option<String>,
pub modules: Option<String>,
// Build system (MVP)
pub build_path: Option<String>,
pub build_app: Option<String>,
@ -107,24 +103,7 @@ impl CliConfig {
.value_name("FILE")
.help("Read Ny JSON IR v0 from a file and execute via MIR Interpreter")
)
.arg(
Arg::new("using")
.long("using")
.value_name("LIST")
.help("Declare namespaces or aliases (comma-separated). Ex: 'acme.util, acme.math as M' or '\"apps/x.nyash\" as X'")
)
.arg(
Arg::new("using-path")
.long("using-path")
.value_name("PATHS")
.help("Search paths for using (':' separated). Ex: 'apps:lib:.'")
)
.arg(
Arg::new("module")
.long("module")
.value_name("MAP")
.help("Namespace to path mapping (comma-separated). Ex: 'acme.util=apps/acme/util.nyash'")
)
.arg(
Arg::new("debug-fuel")
.long("debug-fuel")
@ -427,9 +406,6 @@ impl CliConfig {
parser_ny: matches.get_one::<String>("parser").map(|s| s == "ny").unwrap_or(false),
ny_parser_pipe: matches.get_flag("ny-parser-pipe"),
json_file: matches.get_one::<String>("json-file").cloned(),
using: matches.get_one::<String>("using").cloned(),
using_path: matches.get_one::<String>("using-path").cloned(),
modules: matches.get_one::<String>("module").cloned(),
// Build system (MVP)
build_path: matches.get_one::<String>("build").cloned(),
build_app: matches.get_one::<String>("build-app").cloned(),
@ -441,31 +417,9 @@ impl CliConfig {
}
}
/// Parse debug fuel value ("unlimited" or numeric)
fn parse_debug_fuel(value: &str) -> Option<usize> {
if value == "unlimited" {
None // No limit
} else {
value.parse::<usize>().ok()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_debug_fuel() {
assert_eq!(parse_debug_fuel("unlimited"), None);
assert_eq!(parse_debug_fuel("1000"), Some(1000));
assert_eq!(parse_debug_fuel("invalid"), None);
}
#[test]
fn test_default_config() {
// This test would require mocking clap's behavior
// For now, we just ensure the structure is valid
let config = CliConfig {
impl Default for CliConfig {
fn default() -> Self {
Self {
file: None,
debug_fuel: Some(100000),
dump_ast: false,
@ -505,17 +459,39 @@ mod tests {
parser_ny: false,
ny_parser_pipe: false,
json_file: None,
using: None,
using_path: None,
modules: None,
build_path: None,
build_app: None,
build_out: None,
build_aot: None,
build_profile: None,
build_target: None,
};
}
}
}
/// Parse debug fuel value ("unlimited" or numeric)
fn parse_debug_fuel(value: &str) -> Option<usize> {
if value == "unlimited" {
None // No limit
} else {
value.parse::<usize>().ok()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_debug_fuel() {
assert_eq!(parse_debug_fuel("unlimited"), None);
assert_eq!(parse_debug_fuel("1000"), Some(1000));
assert_eq!(parse_debug_fuel("invalid"), None);
}
#[test]
fn test_default_config() {
let config = CliConfig::default();
assert_eq!(config.backend, "interpreter");
assert_eq!(config.iterations, 10);
}

View File

@ -86,12 +86,27 @@ pub fn await_max_ms() -> u64 {
}
// ---- Phase 11.8 MIR cleanup toggles ----
pub fn mir_core13() -> bool { std::env::var("NYASH_MIR_CORE13").ok().as_deref() == Some("1") }
/// Core-13 minimal MIR mode toggle
/// Default: ON (unless explicitly disabled with NYASH_MIR_CORE13=0)
pub fn mir_core13() -> bool {
match std::env::var("NYASH_MIR_CORE13").ok() {
Some(v) => {
let lv = v.to_ascii_lowercase();
!(lv == "0" || lv == "false" || lv == "off")
}
None => true,
}
}
pub fn mir_ref_boxcall() -> bool { std::env::var("NYASH_MIR_REF_BOXCALL").ok().as_deref() == Some("1") || mir_core13() }
pub fn mir_array_boxcall() -> bool { std::env::var("NYASH_MIR_ARRAY_BOXCALL").ok().as_deref() == Some("1") || mir_core13() }
pub fn mir_plugin_invoke() -> bool { std::env::var("NYASH_MIR_PLUGIN_INVOKE").ok().as_deref() == Some("1") }
pub fn plugin_only() -> bool { std::env::var("NYASH_PLUGIN_ONLY").ok().as_deref() == Some("1") }
/// Core-13 "pure" mode: after normalization, only the 13 canonical ops are allowed.
/// If enabled, the optimizer will try lightweight rewrites for Load/Store/NewBox/Unary,
/// and the final verifier will reject any remaining non-Core-13 ops.
pub fn mir_core13_pure() -> bool { std::env::var("NYASH_MIR_CORE13_PURE").ok().as_deref() == Some("1") }
// ---- Optimizer diagnostics ----
pub fn opt_debug() -> bool { std::env::var("NYASH_OPT_DEBUG").is_ok() }
pub fn opt_diag() -> bool { std::env::var("NYASH_OPT_DIAG").is_ok() }

View File

@ -157,6 +157,10 @@ impl MirBuilder {
/// Emit a weak reference creation (Unified: WeakRef(New))
#[allow(dead_code)]
pub(super) fn emit_weak_new(&mut self, box_val: ValueId) -> Result<ValueId, String> {
if crate::config::env::mir_core13_pure() {
// Pure mode: avoid WeakRef emission; pass-through
return Ok(box_val);
}
let dst = self.value_gen.next();
self.emit_instruction(MirInstruction::WeakRef { dst, op: super::WeakRefOp::New, value: box_val })?;
Ok(dst)
@ -165,6 +169,10 @@ impl MirBuilder {
/// Emit a weak reference load (Unified: WeakRef(Load))
#[allow(dead_code)]
pub(super) fn emit_weak_load(&mut self, weak_ref: ValueId) -> Result<ValueId, String> {
if crate::config::env::mir_core13_pure() {
// Pure mode: avoid WeakRef emission; pass-through
return Ok(weak_ref);
}
let dst = self.value_gen.next();
self.emit_instruction(MirInstruction::WeakRef { dst, op: super::WeakRefOp::Load, value: weak_ref })?;
Ok(dst)
@ -771,6 +779,27 @@ impl MirBuilder {
/// 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
// Core-13 pure mode: emit ExternCall(env.box.new) with type name const only
if crate::config::env::mir_core13_pure() {
// Emit Const String for type name
let ty_id = self.value_gen.next();
self.emit_instruction(MirInstruction::Const { dst: ty_id, value: ConstValue::String(class.clone()) })?;
// Evaluate arguments (pass through to env.box.new shim)
let mut arg_vals: Vec<ValueId> = Vec::with_capacity(arguments.len());
for a in arguments { arg_vals.push(self.build_expression(a)?); }
// Build arg list: [type, a1, a2, ...]
let mut args: Vec<ValueId> = Vec::with_capacity(1 + arg_vals.len());
args.push(ty_id);
args.extend(arg_vals);
// Call env.box.new
let dst = self.value_gen.next();
self.emit_instruction(MirInstruction::ExternCall {
dst: Some(dst), iface_name: "env.box".to_string(), method_name: "new".to_string(), args, effects: EffectMask::PURE,
})?;
// 型注釈(最小)
self.value_types.insert(dst, super::MirType::Box(class.clone()));
return Ok(dst);
}
// Optimization: Primitive wrappers → emit Const directly when possible
if class == "IntegerBox" && arguments.len() == 1 {

View File

@ -73,10 +73,35 @@ impl super::MirBuilder {
// 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)?;
// Core-13 純化: UnaryOp を直接 展開Neg/Not/BitNot
if crate::config::env::mir_core13_pure() {
match operator.as_str() {
"-" => {
let zero = self.value_gen.next();
self.emit_instruction(MirInstruction::Const { dst: zero, value: crate::mir::ConstValue::Integer(0) })?;
let dst = self.value_gen.next();
self.emit_instruction(MirInstruction::BinOp { dst, op: crate::mir::BinaryOp::Sub, lhs: zero, rhs: operand_val })?;
return Ok(dst);
}
"!" | "not" => {
let f = self.value_gen.next();
self.emit_instruction(MirInstruction::Const { dst: f, value: crate::mir::ConstValue::Bool(false) })?;
let dst = self.value_gen.next();
self.emit_instruction(MirInstruction::Compare { dst, op: crate::mir::CompareOp::Eq, lhs: operand_val, rhs: f })?;
return Ok(dst);
}
"~" => {
let all1 = self.value_gen.next();
self.emit_instruction(MirInstruction::Const { dst: all1, value: crate::mir::ConstValue::Integer(-1) })?;
let dst = self.value_gen.next();
self.emit_instruction(MirInstruction::BinOp { dst, op: crate::mir::BinaryOp::BitXor, lhs: operand_val, rhs: all1 })?;
return Ok(dst);
}
_ => {}
}
}
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)
}

View File

@ -126,6 +126,37 @@ impl MirCompiler {
return Err(format!("Core-13 strict: final MIR contains {} legacy ops", legacy_count));
}
}
// Core-13 pure: allow only the 13 canonical ops; reject all others
if crate::config::env::mir_core13_pure() {
let mut bad = 0usize;
let is_allowed = |i: &MirInstruction| -> bool {
matches!(i,
MirInstruction::Const { .. }
| MirInstruction::BinOp { .. }
| MirInstruction::Compare { .. }
| MirInstruction::Jump { .. }
| MirInstruction::Branch { .. }
| MirInstruction::Return { .. }
| MirInstruction::Phi { .. }
| MirInstruction::Call { .. }
| MirInstruction::BoxCall { .. }
| MirInstruction::ExternCall { .. }
| MirInstruction::TypeOp { .. }
| MirInstruction::Safepoint
| MirInstruction::Barrier { .. }
)
};
for (_fname, function) in &module.functions {
for (_bb, block) in &function.blocks {
for inst in &block.instructions { if !is_allowed(inst) { bad += 1; } }
if let Some(term) = &block.terminator { if !is_allowed(term) { bad += 1; } }
}
}
if bad > 0 {
return Err(format!("Core-13 pure strict: final MIR contains {} non-Core-13 ops", bad));
}
}
// Verify the generated MIR
let verification_result = self.verifier.verify_module(&module);
@ -292,6 +323,7 @@ mod tests {
#[test]
fn test_lowering_await_expression() {
if crate::config::env::mir_core13_pure() { eprintln!("[TEST] skip await under Core-13 pure mode"); return; }
// Build AST: await 1 (semantic is nonsensical but should emit Await)
let ast = ASTNode::AwaitExpression { expression: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: crate::ast::Span::unknown() }), span: crate::ast::Span::unknown() };
let mut compiler = MirCompiler::new();
@ -302,6 +334,7 @@ mod tests {
#[test]
fn test_await_has_checkpoints() {
if crate::config::env::mir_core13_pure() { eprintln!("[TEST] skip await under Core-13 pure mode"); return; }
use crate::ast::{LiteralValue, Span};
// Build: await 1
let ast = ASTNode::AwaitExpression { expression: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }), span: Span::unknown() };
@ -317,6 +350,7 @@ mod tests {
#[test]
fn test_rewritten_await_still_checkpoints() {
if crate::config::env::mir_core13_pure() { eprintln!("[TEST] skip await under Core-13 pure mode"); return; }
use crate::ast::{LiteralValue, Span};
// Enable rewrite so Await → ExternCall(env.future.await)
std::env::set_var("NYASH_REWRITE_FUTURE", "1");
@ -388,6 +422,11 @@ mod tests {
#[test]
fn test_try_catch_compilation() {
// Core-13 pure モードでは Try/Catch 命令は許容集合外のためスキップ
if crate::config::env::mir_core13_pure() {
eprintln!("[TEST] skip try/catch under Core-13 pure mode");
return;
}
let mut compiler = MirCompiler::new();
let try_catch_ast = ASTNode::TryCatch {

View File

@ -85,7 +85,12 @@ impl MirOptimizer {
// Lowererがより正確にBox種別を選べるようにする。
let updates = crate::mir::passes::type_hints::propagate_param_type_hints(module);
if updates > 0 { stats.intrinsic_optimizations += updates as usize; }
// Pass 7 (optional): Core-13 pure normalization
if crate::config::env::mir_core13_pure() {
stats.merge(self.normalize_pure_core13(module));
}
if self.debug {
println!("✅ Optimization complete: {}", stats);
}
@ -115,6 +120,120 @@ impl MirOptimizer {
stats
}
/// Core-13 "pure" normalization: rewrite a few non-13 ops to allowed forms.
/// - Load(dst, ptr) => ExternCall(Some dst, env.local.get, [ptr])
/// - Store(val, ptr) => ExternCall(None, env.local.set, [ptr, val])
/// - NewBox(dst, T, args...) => ExternCall(Some dst, env.box.new, [Const String(T), args...])
/// - UnaryOp:
/// Neg x => BinOp(Sub, Const 0, x)
/// Not x => Compare(Eq, x, Const false)
/// BitNot x => BinOp(BitXor, x, Const(-1))
fn normalize_pure_core13(&mut self, module: &mut MirModule) -> OptimizationStats {
use super::instruction::ConstValue;
use super::{MirInstruction as I, BinaryOp, CompareOp};
let mut stats = OptimizationStats::new();
for (_fname, function) in &mut module.functions {
for (_bb, block) in &mut function.blocks {
let mut out: Vec<I> = Vec::with_capacity(block.instructions.len() + 8);
let old = std::mem::take(&mut block.instructions);
for inst in old.into_iter() {
match inst {
I::Load { dst, ptr } => {
out.push(I::ExternCall {
dst: Some(dst), iface_name: "env.local".to_string(), method_name: "get".to_string(),
args: vec![ptr], effects: super::EffectMask::READ,
});
stats.intrinsic_optimizations += 1;
}
I::Store { value, ptr } => {
out.push(I::ExternCall {
dst: None, iface_name: "env.local".to_string(), method_name: "set".to_string(),
args: vec![ptr, value], effects: super::EffectMask::WRITE,
});
stats.intrinsic_optimizations += 1;
}
I::NewBox { dst, box_type, mut args } => {
// prepend type name as Const String
let ty_id = super::ValueId::new(function.next_value_id);
function.next_value_id += 1;
out.push(I::Const { dst: ty_id, value: ConstValue::String(box_type) });
let mut call_args = Vec::with_capacity(1 + args.len());
call_args.push(ty_id);
call_args.append(&mut args);
out.push(I::ExternCall {
dst: Some(dst), iface_name: "env.box".to_string(), method_name: "new".to_string(),
args: call_args, effects: super::EffectMask::PURE, // constructor is logically alloc; conservatively PURE here
});
stats.intrinsic_optimizations += 1;
}
I::UnaryOp { dst, op, operand } => {
match op {
super::UnaryOp::Neg => {
let zero = super::ValueId::new(function.next_value_id); function.next_value_id += 1;
out.push(I::Const { dst: zero, value: ConstValue::Integer(0) });
out.push(I::BinOp { dst, op: BinaryOp::Sub, lhs: zero, rhs: operand });
}
super::UnaryOp::Not => {
let f = super::ValueId::new(function.next_value_id); function.next_value_id += 1;
out.push(I::Const { dst: f, value: ConstValue::Bool(false) });
out.push(I::Compare { dst, op: CompareOp::Eq, lhs: operand, rhs: f });
}
super::UnaryOp::BitNot => {
let all1 = super::ValueId::new(function.next_value_id); function.next_value_id += 1;
out.push(I::Const { dst: all1, value: ConstValue::Integer(-1) });
out.push(I::BinOp { dst, op: BinaryOp::BitXor, lhs: operand, rhs: all1 });
}
}
stats.intrinsic_optimizations += 1;
}
other => out.push(other),
}
}
block.instructions = out;
if let Some(term) = block.terminator.take() {
block.terminator = Some(match term {
I::Load { dst, ptr } => {
I::ExternCall { dst: Some(dst), iface_name: "env.local".to_string(), method_name: "get".to_string(), args: vec![ptr], effects: super::EffectMask::READ }
}
I::Store { value, ptr } => {
I::ExternCall { dst: None, iface_name: "env.local".to_string(), method_name: "set".to_string(), args: vec![ptr, value], effects: super::EffectMask::WRITE }
}
I::NewBox { dst, box_type, mut args } => {
let ty_id = super::ValueId::new(function.next_value_id);
function.next_value_id += 1;
block.instructions.push(I::Const { dst: ty_id, value: ConstValue::String(box_type) });
let mut call_args = Vec::with_capacity(1 + args.len());
call_args.push(ty_id);
call_args.append(&mut args);
I::ExternCall { dst: Some(dst), iface_name: "env.box".to_string(), method_name: "new".to_string(), args: call_args, effects: super::EffectMask::PURE }
}
I::UnaryOp { dst, op, operand } => {
match op {
super::UnaryOp::Neg => {
let zero = super::ValueId::new(function.next_value_id); function.next_value_id += 1;
block.instructions.push(I::Const { dst: zero, value: ConstValue::Integer(0) });
I::BinOp { dst, op: BinaryOp::Sub, lhs: zero, rhs: operand }
}
super::UnaryOp::Not => {
let f = super::ValueId::new(function.next_value_id); function.next_value_id += 1;
block.instructions.push(I::Const { dst: f, value: ConstValue::Bool(false) });
I::Compare { dst, op: CompareOp::Eq, lhs: operand, rhs: f }
}
super::UnaryOp::BitNot => {
let all1 = super::ValueId::new(function.next_value_id); function.next_value_id += 1;
block.instructions.push(I::Const { dst: all1, value: ConstValue::Integer(-1) });
I::BinOp { dst, op: BinaryOp::BitXor, lhs: operand, rhs: all1 }
}
}
}
other => other,
});
}
}
}
stats
}
/// Eliminate dead code in a single function
fn eliminate_dead_code_in_function(&mut self, function: &mut MirFunction) -> usize {

View File

@ -34,9 +34,17 @@ static EXTERNS: Lazy<Vec<ExternSpec>> = Lazy::new(|| vec![
ExternSpec { iface: "env.future", method: "birth", min_arity: 1, max_arity: 1, slot: Some(20) },
ExternSpec { iface: "env.future", method: "set", min_arity: 2, max_arity: 2, slot: Some(21) },
ExternSpec { iface: "env.future", method: "await", min_arity: 1, max_arity: 1, slot: Some(22) },
// modules (minimal registry)
ExternSpec { iface: "env.modules", method: "set", min_arity: 2, max_arity: 2, slot: None },
ExternSpec { iface: "env.modules", method: "get", min_arity: 1, max_arity: 1, slot: None },
<<<<<<< HEAD
// core-13 pure support shims
ExternSpec { iface: "env.local", method: "get", min_arity: 1, max_arity: 1, slot: Some(40) },
ExternSpec { iface: "env.local", method: "set", min_arity: 2, max_arity: 2, slot: Some(41) },
ExternSpec { iface: "env.box", method: "new", min_arity: 1, max_arity: 255, slot: Some(50) },
=======
// core-13 pure support shims
ExternSpec { iface: "env.local", method: "get", min_arity: 1, max_arity: 1, slot: Some(40) },
ExternSpec { iface: "env.local", method: "set", min_arity: 2, max_arity: 2, slot: Some(41) },
ExternSpec { iface: "env.box", method: "new", min_arity: 1, max_arity: 255, slot: Some(50) },
>>>>>>> cranelift-dev
]);
pub fn resolve(iface: &str, method: &str) -> Option<ExternSpec> {

View File

@ -68,6 +68,8 @@ mod tests {
#[test]
fn default_none_on_missing_or_invalid() {
// Ensure env override is not present for this test
env::remove_var("NYASH_SYNTAX_SUGAR_LEVEL");
let dir = tempdir().unwrap();
let file = dir.path().join("nyash.toml");
fs::write(&file, "[syntax]\nsugar_level='unknown'\n").unwrap();
@ -77,4 +79,3 @@ mod tests {
assert_eq!(cfg2.level, SugarLevel::None);
}
}

View File

@ -0,0 +1,83 @@
mod tests {
use crate::mir::{
MirModule, MirFunction, FunctionSignature, MirType, BasicBlock, BasicBlockId, ValueId,
MirInstruction as I,
};
use crate::mir::optimizer::MirOptimizer;
fn mk_func(name: &str) -> (MirFunction, BasicBlockId) {
let sig = FunctionSignature { name: name.to_string(), params: vec![], return_type: MirType::Void, effects: crate::mir::effect::EffectMask::PURE };
let entry = BasicBlockId::new(0);
(MirFunction::new(sig, entry), entry)
}
#[test]
fn core13_normalizes_array_get_set_to_boxcall() {
// Build function with ArrayGet/ArraySet, then optimize under Core-13
let (mut f, bb0) = mk_func("core13_array_norm");
let mut b0 = BasicBlock::new(bb0);
let arr = ValueId::new(0);
let idx = ValueId::new(1);
let val = ValueId::new(2);
let dst = ValueId::new(3);
b0.add_instruction(I::ArrayGet { dst, array: arr, index: idx });
b0.add_instruction(I::ArraySet { array: arr, index: idx, value: val });
b0.add_instruction(I::Return { value: None });
f.add_block(b0);
let mut m = MirModule::new("test_core13_array".into());
m.add_function(f);
let mut opt = MirOptimizer::new();
let _ = opt.optimize_module(&mut m);
let func = m.get_function("core13_array_norm").unwrap();
let block = func.get_block(bb0).unwrap();
// Expect only BoxCall in place of ArrayGet/ArraySet
let mut saw_get = false;
let mut saw_set = false;
for inst in block.all_instructions() {
match inst {
I::BoxCall { method, .. } if method == "get" => { saw_get = true; },
I::BoxCall { method, .. } if method == "set" => { saw_set = true; },
I::ArrayGet { .. } | I::ArraySet { .. } => panic!("legacy Array* must be normalized under Core-13"),
_ => {}
}
}
assert!(saw_get && saw_set, "expected BoxCall(get) and BoxCall(set) present");
}
#[test]
fn core13_normalizes_ref_get_set_to_boxcall() {
// Build function with RefGet/RefSet, then optimize under Core-13
let (mut f, bb0) = mk_func("core13_ref_norm");
let mut b0 = BasicBlock::new(bb0);
let r = ValueId::new(10);
let v = ValueId::new(11);
let dst = ValueId::new(12);
b0.add_instruction(I::RefGet { dst, reference: r, field: "foo".to_string() });
b0.add_instruction(I::RefSet { reference: r, field: "bar".to_string(), value: v });
b0.add_instruction(I::Return { value: None });
f.add_block(b0);
let mut m = MirModule::new("test_core13_ref".into());
m.add_function(f);
let mut opt = MirOptimizer::new();
let _ = opt.optimize_module(&mut m);
let func = m.get_function("core13_ref_norm").unwrap();
let block = func.get_block(bb0).unwrap();
// Expect BoxCall(getField/setField), a Barrier(Write) before setField, and no RefGet/RefSet remain
let mut saw_get_field = false;
let mut saw_set_field = false;
for inst in block.all_instructions() {
match inst {
I::BoxCall { method, .. } if method == "getField" => { saw_get_field = true; },
I::BoxCall { method, .. } if method == "setField" => { saw_set_field = true; },
I::RefGet { .. } | I::RefSet { .. } => panic!("legacy Ref* must be normalized under Core-13"),
_ => {}
}
}
assert!(saw_get_field && saw_set_field, "expected BoxCall(getField) and BoxCall(setField) present");
}
}