Merge branch 'main' of https://github.com/moe-charm/nyash
This commit is contained in:
14
apps/tests/mir-compare-multi/main.nyash
Normal file
14
apps/tests/mir-compare-multi/main.nyash
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// mir-compare-multi - exercise Compare ops under JIT-direct
|
||||||
|
|
||||||
|
static box Main {
|
||||||
|
main() {
|
||||||
|
local c = 0
|
||||||
|
if 1 < 2 { c = c + 1 } // true
|
||||||
|
if 2 < 1 { c = c + 10 } // false (unchanged)
|
||||||
|
if 5 == 5 { c = c + 100 } // true
|
||||||
|
if 7 != 7 { c = c + 1000 } // false
|
||||||
|
// Expect 1 + 100 = 101
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
12
apps/tests/vm-plugin-smoke-counter/main.nyash
Normal file
12
apps/tests/vm-plugin-smoke-counter/main.nyash
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// vm-plugin-smoke-counter
|
||||||
|
// Purpose: Verify VM uses plugin boxes deterministically (CounterBox)
|
||||||
|
// Expected: returns 1 after a single inc()
|
||||||
|
|
||||||
|
// Note: VM runner in this branch initializes plugin host and prefers
|
||||||
|
// plugin implementations for core boxes. CounterBox is provided by
|
||||||
|
// libnyash_counter_plugin per nyash.toml.
|
||||||
|
|
||||||
|
local c = new CounterBox()
|
||||||
|
c.inc()
|
||||||
|
return c.get()
|
||||||
|
|
||||||
12
apps/tests/vm-plugin-smoke-filebox/main.nyash
Normal file
12
apps/tests/vm-plugin-smoke-filebox/main.nyash
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// vm-plugin-smoke-filebox
|
||||||
|
// Purpose: Verify VM uses FileBox plugin to open/read a file.
|
||||||
|
// Script expects the test file to be created by the runner script.
|
||||||
|
|
||||||
|
local path = "tmp/vm_filebox_smoke.txt"
|
||||||
|
local f = new FileBox()
|
||||||
|
f.open(path, "r")
|
||||||
|
local s = f.read()
|
||||||
|
f.close()
|
||||||
|
// Return the length of read content to avoid printing large buffers
|
||||||
|
return s.length()
|
||||||
|
|
||||||
@ -87,9 +87,16 @@ impl VM {
|
|||||||
let mut out_len: usize = out.len();
|
let mut out_len: usize = out.len();
|
||||||
unsafe { (p.inner.invoke_fn)(p.inner.type_id, mh.method_id as u32, p.inner.instance_id, tlv.as_ptr(), tlv.len(), out.as_mut_ptr(), &mut out_len) };
|
unsafe { (p.inner.invoke_fn)(p.inner.type_id, mh.method_id as u32, p.inner.instance_id, tlv.as_ptr(), tlv.len(), out.as_mut_ptr(), &mut out_len) };
|
||||||
let vm_out_raw: VMValue = if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
|
let vm_out_raw: VMValue = if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
|
||||||
|
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
|
||||||
|
eprintln!("[VM←Plugin] tag={} size={} bytes={}", tag, _sz, payload.len());
|
||||||
|
}
|
||||||
match tag {
|
match tag {
|
||||||
1 => crate::runtime::plugin_ffi_common::decode::bool(payload).map(VMValue::Bool).unwrap_or(VMValue::Void),
|
1 => crate::runtime::plugin_ffi_common::decode::bool(payload).map(VMValue::Bool).unwrap_or(VMValue::Void),
|
||||||
2 => crate::runtime::plugin_ffi_common::decode::i32(payload).map(|v| VMValue::Integer(v as i64)).unwrap_or(VMValue::Void),
|
2 => {
|
||||||
|
let v = crate::runtime::plugin_ffi_common::decode::i32(payload).unwrap_or_default();
|
||||||
|
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") { eprintln!("[VM←Plugin] decode i32={}", v); }
|
||||||
|
VMValue::Integer(v as i64)
|
||||||
|
},
|
||||||
5 => crate::runtime::plugin_ffi_common::decode::f64(payload).map(VMValue::Float).unwrap_or(VMValue::Void),
|
5 => crate::runtime::plugin_ffi_common::decode::f64(payload).map(VMValue::Float).unwrap_or(VMValue::Void),
|
||||||
6 | 7 => VMValue::String(crate::runtime::plugin_ffi_common::decode::string(payload)),
|
6 | 7 => VMValue::String(crate::runtime::plugin_ffi_common::decode::string(payload)),
|
||||||
8 => {
|
8 => {
|
||||||
|
|||||||
@ -118,6 +118,35 @@ impl NyashInterpreter {
|
|||||||
// Register MethodBox invoker once (idempotent)
|
// Register MethodBox invoker once (idempotent)
|
||||||
self::register_methodbox_invoker();
|
self::register_methodbox_invoker();
|
||||||
|
|
||||||
|
// Best-effort: initialize plugin host/config when running interpreter standalone
|
||||||
|
// This mirrors runner initialization so that `new FileBox()` etc. work without the CLI path.
|
||||||
|
// Policy: enable plugin-builtins for FileBox/TOMLBox by default; Array/Map remain builtin unless explicitly overridden.
|
||||||
|
if std::env::var("NYASH_DISABLE_PLUGINS").ok().as_deref() != Some("1") {
|
||||||
|
if std::path::Path::new("nyash.toml").exists() {
|
||||||
|
let needs_init = {
|
||||||
|
let host = crate::runtime::get_global_plugin_host();
|
||||||
|
host.read().map(|h| h.config_ref().is_none()).unwrap_or(true)
|
||||||
|
};
|
||||||
|
if needs_init {
|
||||||
|
let _ = crate::runtime::init_global_plugin_host("nyash.toml");
|
||||||
|
// Apply config to BoxFactoryRegistry so UnifiedBoxRegistry can resolve plugin boxes
|
||||||
|
crate::runner_plugin_init::init_bid_plugins();
|
||||||
|
}
|
||||||
|
// Prefer plugin implementations for specific types when available
|
||||||
|
if std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().is_none() {
|
||||||
|
std::env::set_var("NYASH_USE_PLUGIN_BUILTINS", "1");
|
||||||
|
}
|
||||||
|
// Merge override list with FileBox/TOMLBox only (safe defaults for interpreter flows)
|
||||||
|
let mut override_types: Vec<String> = if let Ok(list) = std::env::var("NYASH_PLUGIN_OVERRIDE_TYPES") {
|
||||||
|
list.split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect()
|
||||||
|
} else { vec![] };
|
||||||
|
for t in ["FileBox", "TOMLBox"] {
|
||||||
|
if !override_types.iter().any(|x| x == t) { override_types.push(t.to_string()); }
|
||||||
|
}
|
||||||
|
std::env::set_var("NYASH_PLUGIN_OVERRIDE_TYPES", override_types.join(","));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -512,7 +512,7 @@ impl IRBuilder for CraneliftBuilder {
|
|||||||
while args.len() < _argc { args.push(fb.ins().iconst(types::I64, 0)); }
|
while args.len() < _argc { args.push(fb.ins().iconst(types::I64, 0)); }
|
||||||
});
|
});
|
||||||
for _ in 0.._argc { sig.params.push(AbiParam::new(types::I64)); }
|
for _ in 0.._argc { sig.params.push(AbiParam::new(types::I64)); }
|
||||||
if has_ret { sig.returns.push(AbiParam::new(types::I64)); }
|
|
||||||
let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare import failed");
|
let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare import failed");
|
||||||
if let Some(v) = tls_call_import_ret(&mut self.module, func_id, &args, has_ret) { self.value_stack.push(v); }
|
if let Some(v) = tls_call_import_ret(&mut self.module, func_id, &args, has_ret) { self.value_stack.push(v); }
|
||||||
}
|
}
|
||||||
@ -639,11 +639,7 @@ impl IRBuilder for CraneliftBuilder {
|
|||||||
}
|
}
|
||||||
fn emit_plugin_invoke_by_name(&mut self, method: &str, argc: usize, has_ret: bool) {
|
fn emit_plugin_invoke_by_name(&mut self, method: &str, argc: usize, has_ret: bool) {
|
||||||
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||||
Self::with_fb(|fb| {
|
// Collect call args
|
||||||
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
|
|
||||||
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
|
||||||
});
|
|
||||||
// Collect call args
|
|
||||||
let mut arg_vals: Vec<cranelift_codegen::ir::Value> = {
|
let mut arg_vals: Vec<cranelift_codegen::ir::Value> = {
|
||||||
let take_n = argc.min(self.value_stack.len());
|
let take_n = argc.min(self.value_stack.len());
|
||||||
let mut tmp = Vec::new();
|
let mut tmp = Vec::new();
|
||||||
@ -656,7 +652,8 @@ impl IRBuilder for CraneliftBuilder {
|
|||||||
sig.params.push(AbiParam::new(types::I64));
|
sig.params.push(AbiParam::new(types::I64));
|
||||||
sig.params.push(AbiParam::new(types::I64));
|
sig.params.push(AbiParam::new(types::I64));
|
||||||
sig.params.push(AbiParam::new(types::I64));
|
sig.params.push(AbiParam::new(types::I64));
|
||||||
if has_ret { sig.returns.push(AbiParam::new(types::I64)); }
|
sig.returns.push(AbiParam::new(types::I64));
|
||||||
|
|
||||||
let sym = match method { "getattr" => "nyash_plugin_invoke_name_getattr_i64", _ => "nyash_plugin_invoke_name_call_i64" };
|
let sym = match method { "getattr" => "nyash_plugin_invoke_name_getattr_i64", _ => "nyash_plugin_invoke_name_call_i64" };
|
||||||
let func_id = self.module.declare_function(sym, cranelift_module::Linkage::Import, &sig).expect("declare name shim failed");
|
let func_id = self.module.declare_function(sym, cranelift_module::Linkage::Import, &sig).expect("declare name shim failed");
|
||||||
let ret_val = Self::with_fb(|fb| {
|
let ret_val = Self::with_fb(|fb| {
|
||||||
@ -710,13 +707,20 @@ impl IRBuilder for CraneliftBuilder {
|
|||||||
}
|
}
|
||||||
fn switch_to_block(&mut self, index: usize) {
|
fn switch_to_block(&mut self, index: usize) {
|
||||||
if index >= self.blocks.len() { return; }
|
if index >= self.blocks.len() { return; }
|
||||||
|
// Avoid redundant switch_to_block calls that can trip FunctionBuilder state
|
||||||
|
if self.current_block_index == Some(index) { return; }
|
||||||
Self::with_fb(|fb| {
|
Self::with_fb(|fb| {
|
||||||
// If switching away from a non-terminated block, inject jump to keep CFG sane
|
// If switching away from a non-terminated block, inject jump to keep CFG sane
|
||||||
if let Some(cur) = self.current_block_index {
|
if let Some(cur) = self.current_block_index {
|
||||||
if self.cur_needs_term && cur != index { fb.ins().jump(self.blocks[index], &[]); self.cur_needs_term = false; }
|
if self.cur_needs_term && cur != index {
|
||||||
|
fb.ins().jump(self.blocks[index], &[]);
|
||||||
|
self.cur_needs_term = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fb.switch_to_block(self.blocks[index]);
|
fb.switch_to_block(self.blocks[index]);
|
||||||
self.current_block_index = Some(index);
|
self.current_block_index = Some(index);
|
||||||
|
// New current block now requires a terminator before any further switch
|
||||||
|
self.cur_needs_term = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
fn seal_block(&mut self, _index: usize) { /* final sealing handled in end_function */ }
|
fn seal_block(&mut self, _index: usize) { /* final sealing handled in end_function */ }
|
||||||
|
|||||||
@ -65,6 +65,12 @@ impl IRBuilder for ObjectBuilder {
|
|||||||
self.current_name = Some(name.to_string());
|
self.current_name = Some(name.to_string());
|
||||||
self.value_stack.clear();
|
self.value_stack.clear();
|
||||||
self.value_tags.clear();
|
self.value_tags.clear();
|
||||||
|
// Reset contexts to satisfy Cranelift requirements when reusing the builder
|
||||||
|
self.ctx = cranelift_codegen::Context::new();
|
||||||
|
self.fbc = cranelift_frontend::FunctionBuilderContext::new();
|
||||||
|
self.blocks.clear();
|
||||||
|
self.entry_block = None;
|
||||||
|
self.current_block_index = None;
|
||||||
if !self.typed_sig_prepared {
|
if !self.typed_sig_prepared {
|
||||||
let call_conv = self.module.isa().default_call_conv();
|
let call_conv = self.module.isa().default_call_conv();
|
||||||
let mut sig = Signature::new(call_conv);
|
let mut sig = Signature::new(call_conv);
|
||||||
@ -93,6 +99,10 @@ impl IRBuilder for ObjectBuilder {
|
|||||||
let finished = std::mem::replace(&mut self.module, Self::fresh_module());
|
let finished = std::mem::replace(&mut self.module, Self::fresh_module());
|
||||||
let product = finished.finish();
|
let product = finished.finish();
|
||||||
self.object_bytes = Some(product.emit().expect("emit object"));
|
self.object_bytes = Some(product.emit().expect("emit object"));
|
||||||
|
// Clear per-function state to allow reuse
|
||||||
|
self.blocks.clear();
|
||||||
|
self.entry_block = None;
|
||||||
|
self.current_block_index = None;
|
||||||
}
|
}
|
||||||
fn prepare_signature_i64(&mut self, argc: usize, has_ret: bool) { self.desired_argc = argc; self.desired_has_ret = has_ret; }
|
fn prepare_signature_i64(&mut self, argc: usize, has_ret: bool) { self.desired_argc = argc; self.desired_has_ret = has_ret; }
|
||||||
fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) { self.typed_sig_prepared = true; }
|
fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) { self.typed_sig_prepared = true; }
|
||||||
@ -477,7 +487,7 @@ impl IRBuilder for ObjectBuilder {
|
|||||||
// Build signature and declare import
|
// Build signature and declare import
|
||||||
let mut sig = Signature::new(self.module.isa().default_call_conv());
|
let mut sig = Signature::new(self.module.isa().default_call_conv());
|
||||||
for _ in 0..12 { sig.params.push(AbiParam::new(types::I64)); }
|
for _ in 0..12 { sig.params.push(AbiParam::new(types::I64)); }
|
||||||
if has_ret { sig.returns.push(AbiParam::new(types::I64)); }
|
sig.returns.push(AbiParam::new(types::I64));
|
||||||
let func_id = self.module.declare_function("nyash_plugin_invoke3_tagged_i64", cranelift_module::Linkage::Import, &sig).expect("declare plugin invoke tagged");
|
let func_id = self.module.declare_function("nyash_plugin_invoke3_tagged_i64", cranelift_module::Linkage::Import, &sig).expect("declare plugin invoke tagged");
|
||||||
let fref = self.module.declare_func_in_func(func_id, fb.func);
|
let fref = self.module.declare_func_in_func(func_id, fb.func);
|
||||||
|
|
||||||
@ -530,7 +540,7 @@ impl IRBuilder for ObjectBuilder {
|
|||||||
|
|
||||||
let mut sig = Signature::new(self.module.isa().default_call_conv());
|
let mut sig = Signature::new(self.module.isa().default_call_conv());
|
||||||
for _ in 0..5 { sig.params.push(AbiParam::new(types::I64)); }
|
for _ in 0..5 { sig.params.push(AbiParam::new(types::I64)); }
|
||||||
if has_ret { sig.returns.push(AbiParam::new(types::I64)); }
|
sig.returns.push(AbiParam::new(types::I64));
|
||||||
let func_id = self.module.declare_function("nyash.plugin.invoke_by_name_i64", cranelift_module::Linkage::Import, &sig).expect("declare plugin invoke by-name");
|
let func_id = self.module.declare_function("nyash.plugin.invoke_by_name_i64", cranelift_module::Linkage::Import, &sig).expect("declare plugin invoke by-name");
|
||||||
let fref = self.module.declare_func_in_func(func_id, fb.func);
|
let fref = self.module.declare_func_in_func(func_id, fb.func);
|
||||||
|
|
||||||
|
|||||||
@ -494,6 +494,9 @@ impl LowerCore {
|
|||||||
if let Some(v) = self.known_i64.get(src).copied() { self.known_i64.insert(*dst, v); }
|
if let Some(v) = self.known_i64.get(src).copied() { self.known_i64.insert(*dst, v); }
|
||||||
if let Some(v) = self.known_f64.get(src).copied() { self.known_f64.insert(*dst, v); }
|
if let Some(v) = self.known_f64.get(src).copied() { self.known_f64.insert(*dst, v); }
|
||||||
if let Some(v) = self.known_str.get(src).cloned() { self.known_str.insert(*dst, v); }
|
if let Some(v) = self.known_str.get(src).cloned() { self.known_str.insert(*dst, v); }
|
||||||
|
// Propagate handle/type knowledge to keep BoxCall routing stable across copies
|
||||||
|
if self.handle_values.contains(src) { self.handle_values.insert(*dst); }
|
||||||
|
if let Some(bt) = self.box_type_map.get(src).cloned() { self.box_type_map.insert(*dst, bt); }
|
||||||
// Propagate boolean classification through Copy
|
// Propagate boolean classification through Copy
|
||||||
if self.bool_values.contains(src) { self.bool_values.insert(*dst); }
|
if self.bool_values.contains(src) { self.bool_values.insert(*dst); }
|
||||||
// If source is a parameter, materialize it on the stack for downstream ops and persist into dst slot
|
// If source is a parameter, materialize it on the stack for downstream ops and persist into dst slot
|
||||||
@ -713,7 +716,40 @@ impl LowerCore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let handled = self.lower_box_call(func, b, &array, method.as_str(), args, dst.clone())?;
|
let handled = self.lower_box_call(func, b, &array, method.as_str(), args, dst.clone())?;
|
||||||
if trace { eprintln!("[LOWER] BoxCall recv={:?} method={} handled={} box_type={:?} dst?={}", array, method, handled, self.box_type_map.get(&array), dst.is_some()); }
|
if trace {
|
||||||
|
eprintln!(
|
||||||
|
"[LOWER] BoxCall recv={:?} method={} handled={} box_type={:?} dst?={}",
|
||||||
|
array, method, handled, self.box_type_map.get(&array), dst.is_some()
|
||||||
|
);
|
||||||
|
if !handled {
|
||||||
|
let bt = self.box_type_map.get(&array).cloned().unwrap_or_default();
|
||||||
|
let is_param = self.param_index.contains_key(&array);
|
||||||
|
let has_local = self.local_index.contains_key(&array);
|
||||||
|
let is_handle = self.handle_values.contains(&array);
|
||||||
|
// Classify up to first 3 args: i(known_i64) s(known_str) p(param) l(local) h(handle) -(unknown)
|
||||||
|
let mut arg_kinds: Vec<&'static str> = Vec::new();
|
||||||
|
for a in args.iter().take(3) {
|
||||||
|
let k = if self.known_i64.contains_key(a) { "i" }
|
||||||
|
else if self.known_str.contains_key(a) { "s" }
|
||||||
|
else if self.param_index.contains_key(a) { "p" }
|
||||||
|
else if self.local_index.contains_key(a) { "l" }
|
||||||
|
else if self.handle_values.contains(a) { "h" }
|
||||||
|
else { "-" };
|
||||||
|
arg_kinds.push(k);
|
||||||
|
}
|
||||||
|
// Policy hint: whether a mapped HostCall exists for (box_type, method)
|
||||||
|
let policy = crate::jit::policy::invoke::decide_box_method(&bt, method.as_str(), 1 + args.len(), dst.is_some());
|
||||||
|
let policy_str = match policy {
|
||||||
|
crate::jit::policy::invoke::InvokeDecision::HostCall { ref symbol, .. } => format!("hostcall:{}", symbol),
|
||||||
|
crate::jit::policy::invoke::InvokeDecision::PluginInvoke { .. } => "plugin_invoke".to_string(),
|
||||||
|
crate::jit::policy::invoke::InvokeDecision::Fallback { ref reason } => format!("fallback:{}", reason),
|
||||||
|
};
|
||||||
|
eprintln!(
|
||||||
|
"[LOWER] fallback(reason=unhandled) box_type='{}' method='{}' recv[param?{} local?{} handle?{}] args={:?} policy={}",
|
||||||
|
bt, method, is_param, has_local, is_handle, arg_kinds, policy_str
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
if handled {
|
if handled {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,8 +19,9 @@ impl LowerCore {
|
|||||||
let argc = 1 + args.len();
|
let argc = 1 + args.len();
|
||||||
if let Some(pidx) = self.param_index.get(box_val).copied() { b.emit_param_i64(pidx); } else { self.push_value_if_known_or_param(b, box_val); }
|
if let Some(pidx) = self.param_index.get(box_val).copied() { b.emit_param_i64(pidx); } else { self.push_value_if_known_or_param(b, box_val); }
|
||||||
let decision = crate::jit::policy::invoke::decide_box_method(&bt, m, argc, dst.is_some());
|
let decision = crate::jit::policy::invoke::decide_box_method(&bt, m, argc, dst.is_some());
|
||||||
if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, .. } = decision {
|
if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } = decision {
|
||||||
b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some());
|
b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some());
|
||||||
|
crate::jit::observe::lower_plugin_invoke(&box_type, m, type_id, method_id, argc);
|
||||||
if let Some(d) = dst { self.handle_values.insert(*d); }
|
if let Some(d) = dst { self.handle_values.insert(*d); }
|
||||||
} else { if dst.is_some() { b.emit_const_i64(0); } }
|
} else { if dst.is_some() { b.emit_const_i64(0); } }
|
||||||
} else if (bt == "PyRuntimeBox" && (m == "getattr" || m == "call")) {
|
} else if (bt == "PyRuntimeBox" && (m == "getattr" || m == "call")) {
|
||||||
@ -82,8 +83,9 @@ impl LowerCore {
|
|||||||
if let Some(arg0) = args.get(0) { self.push_value_if_known_or_param(b, arg0); }
|
if let Some(arg0) = args.get(0) { self.push_value_if_known_or_param(b, arg0); }
|
||||||
// Resolve plugin invoke for ConsoleBox.method
|
// Resolve plugin invoke for ConsoleBox.method
|
||||||
let decision = crate::jit::policy::invoke::decide_box_method("ConsoleBox", method_name, 2, dst.is_some());
|
let decision = crate::jit::policy::invoke::decide_box_method("ConsoleBox", method_name, 2, dst.is_some());
|
||||||
if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, .. } = decision {
|
if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } = decision {
|
||||||
b.emit_plugin_invoke(type_id, method_id, 2, dst.is_some());
|
b.emit_plugin_invoke(type_id, method_id, 2, dst.is_some());
|
||||||
|
crate::jit::observe::lower_plugin_invoke(&box_type, method_name, type_id, method_id, 2);
|
||||||
} else if dst.is_some() { b.emit_const_i64(0); }
|
} else if dst.is_some() { b.emit_const_i64(0); }
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -172,8 +174,51 @@ impl LowerCore {
|
|||||||
dst.clone(),
|
dst.clone(),
|
||||||
);
|
);
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
|
}
|
||||||
|
// 非コアBox(例: EguiBox など)は共通処理として名前ベースの plugin_invoke にフォールバック
|
||||||
|
// コアBoxの目安: StringBox/ArrayBox/MapBox(この後の分岐で処理)と PyRuntimeBox(専用分岐済)
|
||||||
|
if let Some(bt) = self.box_type_map.get(array).cloned() {
|
||||||
|
let is_core = bt == "StringBox" || bt == "ArrayBox" || bt == "MapBox" || bt == "PyRuntimeBox";
|
||||||
|
if !is_core {
|
||||||
|
// receiver: prefer existing local slot/param; ensure a valid runtime handle
|
||||||
|
if let Some(slot) = self.local_index.get(array).copied() {
|
||||||
|
b.load_local_i64(slot);
|
||||||
|
} else if let Some(pidx) = self.param_index.get(array).copied() {
|
||||||
|
b.emit_param_i64(pidx);
|
||||||
|
b.emit_host_call(crate::jit::r#extern::handles::SYM_HANDLE_OF, 1, true);
|
||||||
|
} else {
|
||||||
|
self.push_value_if_known_or_param(b, array);
|
||||||
|
b.emit_host_call(crate::jit::r#extern::handles::SYM_HANDLE_OF, 1, true);
|
||||||
|
}
|
||||||
|
// push up to 2 args (name-shim supports at most 2 positional args beyond receiver)
|
||||||
|
let take_n = core::cmp::min(args.len(), 2);
|
||||||
|
for i in 0..take_n { if let Some(v) = args.get(i) { self.push_value_if_known_or_param(b, v); } }
|
||||||
|
let argc = 1 + take_n;
|
||||||
|
b.emit_plugin_invoke_by_name(method, argc, dst.is_some());
|
||||||
|
if std::env::var("NYASH_JIT_TRACE_LOWER").ok().as_deref() == Some("1") {
|
||||||
|
crate::jit::events::emit_lower(
|
||||||
|
serde_json::json!({
|
||||||
|
"id": format!("plugin_name:{}:{}", bt, method),
|
||||||
|
"decision": "allow",
|
||||||
|
"reason": "plugin_invoke_by_name",
|
||||||
|
"argc": argc
|
||||||
|
}),
|
||||||
|
"plugin","<jit>"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(d) = dst {
|
||||||
|
self.handle_values.insert(d);
|
||||||
|
let slot = *self.local_index.entry(d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||||
|
b.store_local_i64(slot);
|
||||||
|
}
|
||||||
|
if std::env::var("NYASH_JIT_TRACE_LOWER").ok().as_deref() == Some("1") {
|
||||||
|
eprintln!("[LOWER] {}.{} via name-invoke (argc={})", bt, method, argc);
|
||||||
|
}
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Builtins-to-plugin path (subset for String/Array/Map critical ops)
|
// Builtins-to-plugin path (subset for String/Array/Map critical ops)
|
||||||
|
// Builtins-to-plugin path (subset for String/Array/Map critical ops)
|
||||||
if std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1") {
|
if std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1") {
|
||||||
// StringBox (length/is_empty/charCodeAt)
|
// StringBox (length/is_empty/charCodeAt)
|
||||||
if matches!(method, "length" | "is_empty" | "charCodeAt") {
|
if matches!(method, "length" | "is_empty" | "charCodeAt") {
|
||||||
@ -661,6 +706,32 @@ impl LowerCore {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
// Not handled here
|
// Not handled here
|
||||||
|
if std::env::var("NYASH_JIT_TRACE_LOWER").ok().as_deref() == Some("1") {
|
||||||
|
let bt = self.box_type_map.get(array).cloned().unwrap_or_default();
|
||||||
|
let is_param = self.param_index.contains_key(array);
|
||||||
|
let has_local = self.local_index.contains_key(array);
|
||||||
|
let is_handle = self.handle_values.contains(array);
|
||||||
|
let mut arg_kinds: Vec<&'static str> = Vec::new();
|
||||||
|
for a in args.iter().take(3) {
|
||||||
|
let k = if self.known_i64.contains_key(a) { "i" }
|
||||||
|
else if self.known_str.contains_key(a) { "s" }
|
||||||
|
else if self.param_index.contains_key(a) { "p" }
|
||||||
|
else if self.local_index.contains_key(a) { "l" }
|
||||||
|
else if self.handle_values.contains(a) { "h" }
|
||||||
|
else { "-" };
|
||||||
|
arg_kinds.push(k);
|
||||||
|
}
|
||||||
|
let policy = crate::jit::policy::invoke::decide_box_method(&bt, method, 1 + args.len(), dst.is_some());
|
||||||
|
let policy_str = match policy {
|
||||||
|
crate::jit::policy::invoke::InvokeDecision::HostCall { ref symbol, .. } => format!("hostcall:{}", symbol),
|
||||||
|
crate::jit::policy::invoke::InvokeDecision::PluginInvoke { .. } => "plugin_invoke".to_string(),
|
||||||
|
crate::jit::policy::invoke::InvokeDecision::Fallback { ref reason } => format!("fallback:{}", reason),
|
||||||
|
};
|
||||||
|
eprintln!(
|
||||||
|
"[LOWER] unhandled BoxCall: box_type='{}' method='{}' recv[param?{} local?{} handle?{}] args={:?} policy={}",
|
||||||
|
bt, method, is_param, has_local, is_handle, arg_kinds, policy_str
|
||||||
|
);
|
||||||
|
}
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,6 +56,7 @@ pub mod config;
|
|||||||
|
|
||||||
// Runtime system (plugins, registry, etc.)
|
// Runtime system (plugins, registry, etc.)
|
||||||
pub mod runtime;
|
pub mod runtime;
|
||||||
|
pub mod runner_plugin_init;
|
||||||
pub mod debug;
|
pub mod debug;
|
||||||
pub mod grammar; // Phase 11.9 unified grammar scaffolding
|
pub mod grammar; // Phase 11.9 unified grammar scaffolding
|
||||||
pub mod syntax; // Phase 12.7: syntax sugar config and helpers (mirror lib layout)
|
pub mod syntax; // Phase 12.7: syntax sugar config and helpers (mirror lib layout)
|
||||||
|
|||||||
@ -6,6 +6,54 @@ use std::sync::Arc;
|
|||||||
impl NyashRunner {
|
impl NyashRunner {
|
||||||
/// Execute VM mode (split)
|
/// Execute VM mode (split)
|
||||||
pub(crate) fn execute_vm_mode(&self, filename: &str) {
|
pub(crate) fn execute_vm_mode(&self, filename: &str) {
|
||||||
|
// Enforce plugin-first policy for VM on this branch (deterministic):
|
||||||
|
// - Initialize plugin host if not yet loaded
|
||||||
|
// - Prefer plugin implementations for core boxes
|
||||||
|
// - Optionally fail fast when plugins are missing (NYASH_VM_PLUGIN_STRICT=1)
|
||||||
|
{
|
||||||
|
// Initialize unified registry globals (idempotent)
|
||||||
|
nyash_rust::runtime::init_global_unified_registry();
|
||||||
|
// Init plugin host from nyash.toml if not yet loaded
|
||||||
|
let need_init = {
|
||||||
|
let host = nyash_rust::runtime::get_global_plugin_host();
|
||||||
|
host.read().map(|h| h.config_ref().is_none()).unwrap_or(true)
|
||||||
|
};
|
||||||
|
if need_init {
|
||||||
|
let _ = nyash_rust::runtime::init_global_plugin_host("nyash.toml");
|
||||||
|
crate::runner_plugin_init::init_bid_plugins();
|
||||||
|
}
|
||||||
|
// Prefer plugin-builtins for core types unless explicitly disabled
|
||||||
|
if std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().is_none() {
|
||||||
|
std::env::set_var("NYASH_USE_PLUGIN_BUILTINS", "1");
|
||||||
|
}
|
||||||
|
// Build stable override list
|
||||||
|
let mut override_types: Vec<String> = if let Ok(list) = std::env::var("NYASH_PLUGIN_OVERRIDE_TYPES") {
|
||||||
|
list.split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect()
|
||||||
|
} else { vec![] };
|
||||||
|
for t in [
|
||||||
|
"FileBox", "TOMLBox", // IO/config
|
||||||
|
"ConsoleBox", "StringBox", "IntegerBox", // core value-ish
|
||||||
|
"ArrayBox", "MapBox", // collections
|
||||||
|
"MathBox", "TimeBox" // math/time helpers
|
||||||
|
] {
|
||||||
|
if !override_types.iter().any(|x| x == t) { override_types.push(t.to_string()); }
|
||||||
|
}
|
||||||
|
std::env::set_var("NYASH_PLUGIN_OVERRIDE_TYPES", override_types.join(","));
|
||||||
|
|
||||||
|
// Strict mode: verify providers exist for override types
|
||||||
|
if std::env::var("NYASH_VM_PLUGIN_STRICT").ok().as_deref() == Some("1") {
|
||||||
|
let v2 = nyash_rust::runtime::get_global_registry();
|
||||||
|
let mut missing: Vec<String> = Vec::new();
|
||||||
|
for t in ["FileBox","ConsoleBox","ArrayBox","MapBox","StringBox","IntegerBox"] {
|
||||||
|
if v2.get_provider(t).is_none() { missing.push(t.to_string()); }
|
||||||
|
}
|
||||||
|
if !missing.is_empty() {
|
||||||
|
eprintln!("❌ VM plugin-first strict: missing providers for: {:?}", missing);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Read the file
|
// Read the file
|
||||||
let code = match fs::read_to_string(filename) {
|
let code = match fs::read_to_string(filename) {
|
||||||
Ok(content) => content,
|
Ok(content) => content,
|
||||||
@ -78,43 +126,68 @@ impl NyashRunner {
|
|||||||
match vm.execute_module(&module_vm) {
|
match vm.execute_module(&module_vm) {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
println!("✅ VM execution completed successfully!");
|
println!("✅ VM execution completed successfully!");
|
||||||
// Pretty-print using MIR return type when available to avoid Void-looking floats/bools
|
// Pretty-print with coercions for plugin-backed values
|
||||||
if let Some(func) = compile_result.module.functions.get("main") {
|
// Prefer MIR signature when available, but fall back to runtime coercions to keep VM/JIT consistent.
|
||||||
|
let (ety, sval) = if let Some(func) = compile_result.module.functions.get("main") {
|
||||||
use nyash_rust::mir::MirType;
|
use nyash_rust::mir::MirType;
|
||||||
use nyash_rust::box_trait::{NyashBox, IntegerBox, BoolBox, StringBox};
|
use nyash_rust::box_trait::{NyashBox, IntegerBox, BoolBox, StringBox};
|
||||||
use nyash_rust::boxes::FloatBox;
|
use nyash_rust::boxes::FloatBox;
|
||||||
let (ety, sval) = match &func.signature.return_type {
|
match &func.signature.return_type {
|
||||||
MirType::Float => {
|
MirType::Float => {
|
||||||
if let Some(fb) = result.as_any().downcast_ref::<FloatBox>() {
|
if let Some(fb) = result.as_any().downcast_ref::<FloatBox>() {
|
||||||
("Float", format!("{}", fb.value))
|
("Float", format!("{}", fb.value))
|
||||||
} else if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
|
} else if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
|
||||||
("Float", format!("{}", ib.value as f64))
|
("Float", format!("{}", ib.value as f64))
|
||||||
} else { ("Float", result.to_string_box().value) }
|
} else if let Some(s) = nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) {
|
||||||
|
("String", s)
|
||||||
|
} else {
|
||||||
|
(result.type_name(), result.to_string_box().value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
MirType::Integer => {
|
MirType::Integer => {
|
||||||
if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
|
if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
|
||||||
("Integer", ib.value.to_string())
|
("Integer", ib.value.to_string())
|
||||||
} else { ("Integer", result.to_string_box().value) }
|
} else if let Some(i) = nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref()) {
|
||||||
|
("Integer", i.to_string())
|
||||||
|
} else {
|
||||||
|
(result.type_name(), result.to_string_box().value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
MirType::Bool => {
|
MirType::Bool => {
|
||||||
if let Some(bb) = result.as_any().downcast_ref::<BoolBox>() {
|
if let Some(bb) = result.as_any().downcast_ref::<BoolBox>() {
|
||||||
("Bool", bb.value.to_string())
|
("Bool", bb.value.to_string())
|
||||||
} else if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
|
} else if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
|
||||||
("Bool", (ib.value != 0).to_string())
|
("Bool", (ib.value != 0).to_string())
|
||||||
} else { ("Bool", result.to_string_box().value) }
|
} else {
|
||||||
|
(result.type_name(), result.to_string_box().value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
MirType::String => {
|
MirType::String => {
|
||||||
if let Some(sb) = result.as_any().downcast_ref::<StringBox>() {
|
if let Some(sb) = result.as_any().downcast_ref::<StringBox>() {
|
||||||
("String", sb.value.clone())
|
("String", sb.value.clone())
|
||||||
} else { ("String", result.to_string_box().value) }
|
} else if let Some(s) = nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) {
|
||||||
|
("String", s)
|
||||||
|
} else {
|
||||||
|
(result.type_name(), result.to_string_box().value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => { (result.type_name(), result.to_string_box().value) }
|
_ => {
|
||||||
};
|
if let Some(i) = nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref()) {
|
||||||
println!("ResultType(MIR): {}", ety);
|
("Integer", i.to_string())
|
||||||
println!("Result: {}", sval);
|
} else if let Some(s) = nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) {
|
||||||
|
("String", s)
|
||||||
|
} else { (result.type_name(), result.to_string_box().value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("Result: {:?}", result);
|
if let Some(i) = nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref()) {
|
||||||
}
|
("Integer", i.to_string())
|
||||||
|
} else if let Some(s) = nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) {
|
||||||
|
("String", s)
|
||||||
|
} else { (result.type_name(), result.to_string_box().value) }
|
||||||
|
};
|
||||||
|
println!("ResultType(MIR): {}", ety);
|
||||||
|
println!("Result: {}", sval);
|
||||||
},
|
},
|
||||||
Err(e) => { eprintln!("❌ VM execution error: {}", e); process::exit(1); }
|
Err(e) => { eprintln!("❌ VM execution error: {}", e); process::exit(1); }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -266,26 +266,49 @@ impl PluginLoaderV2 {
|
|||||||
// Encode TLV args via shared helper (numeric→string→toString)
|
// Encode TLV args via shared helper (numeric→string→toString)
|
||||||
let tlv = crate::runtime::plugin_ffi_common::encode_args(args);
|
let tlv = crate::runtime::plugin_ffi_common::encode_args(args);
|
||||||
let (_code, out_len, out) = super::host_bridge::invoke_alloc(plugin.invoke_fn, type_id, method.method_id, instance_id, &tlv);
|
let (_code, out_len, out) = super::host_bridge::invoke_alloc(plugin.invoke_fn, type_id, method.method_id, instance_id, &tlv);
|
||||||
// Minimal decoding by method name
|
// Decode TLV (first entry) generically
|
||||||
match method_name {
|
if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
|
||||||
// Expect UTF-8 string result in bytes → StringBox
|
let bx: Box<dyn NyashBox> = match tag {
|
||||||
"toUtf8" | "toString" => {
|
1 => Box::new(crate::box_trait::BoolBox::new(crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false))),
|
||||||
let s = String::from_utf8_lossy(&out[0..out_len]).to_string();
|
2 => Box::new(crate::box_trait::IntegerBox::new(crate::runtime::plugin_ffi_common::decode::i32(payload).unwrap_or(0) as i64)),
|
||||||
return Ok(Some(Box::new(crate::box_trait::StringBox::new(s))));
|
3 => {
|
||||||
}
|
// i64 payload
|
||||||
// Expect IntegerBox via little-endian i64 in first 8 bytes
|
if payload.len() == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); Box::new(crate::box_trait::IntegerBox::new(i64::from_le_bytes(b))) }
|
||||||
"get" => {
|
else { Box::new(crate::box_trait::IntegerBox::new(0)) }
|
||||||
if out_len >= 8 { let mut buf=[0u8;8]; buf.copy_from_slice(&out[0..8]); let n=i64::from_le_bytes(buf); return Ok(Some(Box::new(crate::box_trait::IntegerBox::new(n)))) }
|
}
|
||||||
return Ok(Some(Box::new(crate::box_trait::IntegerBox::new(0))));
|
5 => {
|
||||||
}
|
let x = crate::runtime::plugin_ffi_common::decode::f64(payload).unwrap_or(0.0);
|
||||||
// Float path (approx): read 8 bytes as f64 and Box as FloatBox
|
Box::new(crate::boxes::FloatBox::new(x))
|
||||||
"toDouble" => {
|
}
|
||||||
if out_len >= 8 { let mut buf=[0u8;8]; buf.copy_from_slice(&out[0..8]); let x=f64::from_le_bytes(buf); return Ok(Some(Box::new(crate::boxes::FloatBox::new(x)))) }
|
6 | 7 => {
|
||||||
return Ok(Some(Box::new(crate::boxes::FloatBox::new(0.0))));
|
let s = crate::runtime::plugin_ffi_common::decode::string(payload);
|
||||||
}
|
Box::new(crate::box_trait::StringBox::new(s))
|
||||||
_ => {}
|
}
|
||||||
|
8 => {
|
||||||
|
// Plugin handle (type_id, instance_id) → wrap into PluginBoxV2
|
||||||
|
if let Some((ret_type, inst)) = crate::runtime::plugin_ffi_common::decode::plugin_handle(payload) {
|
||||||
|
let handle = Arc::new(super::types::PluginHandleInner {
|
||||||
|
type_id: ret_type,
|
||||||
|
invoke_fn: plugin.invoke_fn,
|
||||||
|
instance_id: inst,
|
||||||
|
fini_method_id: None,
|
||||||
|
finalized: std::sync::atomic::AtomicBool::new(false),
|
||||||
|
});
|
||||||
|
Box::new(super::types::PluginBoxV2 { box_type: box_type.to_string(), inner: handle })
|
||||||
|
} else { Box::new(crate::box_trait::VoidBox::new()) }
|
||||||
|
}
|
||||||
|
9 => {
|
||||||
|
// Host handle (u64) → try to map back to BoxRef, else void
|
||||||
|
if let Some(u) = crate::runtime::plugin_ffi_common::decode::u64(payload) {
|
||||||
|
if let Some(arc) = crate::runtime::host_handles::get(u) { return Ok(Some(arc.share_box())); }
|
||||||
|
}
|
||||||
|
Box::new(crate::box_trait::VoidBox::new())
|
||||||
|
}
|
||||||
|
_ => Box::new(crate::box_trait::VoidBox::new()),
|
||||||
|
};
|
||||||
|
return Ok(Some(bx));
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(Some(Box::new(crate::box_trait::VoidBox::new())))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_box(&self, box_type: &str, _args: &[Box<dyn NyashBox>]) -> BidResult<Box<dyn NyashBox>> {
|
pub fn create_box(&self, box_type: &str, _args: &[Box<dyn NyashBox>]) -> BidResult<Box<dyn NyashBox>> {
|
||||||
|
|||||||
15
tools/aot_counter_smoke.sh
Normal file
15
tools/aot_counter_smoke.sh
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd)
|
||||||
|
cd "$ROOT_DIR"
|
||||||
|
|
||||||
|
APP="apps/tests/vm-plugin-smoke-counter/main.nyash"
|
||||||
|
|
||||||
|
echo "[build] core + counter plugin"
|
||||||
|
cargo build --release --features cranelift-jit
|
||||||
|
cargo build -p nyash-counter-plugin --release
|
||||||
|
|
||||||
|
echo "[AOT] emit + link + run"
|
||||||
|
bash tools/build_aot.sh "$APP" app_counter_aot
|
||||||
|
|
||||||
@ -1,88 +1,38 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
if [[ "${NYASH_CLI_VERBOSE:-0}" == "1" ]]; then
|
ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd)
|
||||||
set -x
|
cd "$ROOT_DIR"
|
||||||
fi
|
|
||||||
|
|
||||||
usage() {
|
APP=${1:-apps/tests/mir-branch-ret/main.nyash}
|
||||||
cat << USAGE
|
OUT=${2:-app_aot}
|
||||||
Usage: tools/build_aot.sh <input.nyash> [-o <output>]
|
OBJ_DIR=${OBJ_DIR:-target/aot_objects}
|
||||||
|
OBJ_BASENAME=$(basename "$APP" .nyash)
|
||||||
|
OBJ_PATH="$OBJ_DIR/$OBJ_BASENAME.o"
|
||||||
|
|
||||||
Builds Nyash with Cranelift, emits an AOT object (.o) for the given program,
|
echo "[1/5] build nyash (cranelift-jit)"
|
||||||
links it with libnyrt.a into a native executable, and prints the result path.
|
cargo build --release --features cranelift-jit
|
||||||
|
|
||||||
Options:
|
echo "[2/5] build nyrt (static lib)"
|
||||||
-o <output> Output executable name (default: app)
|
cargo build -p nyrt --release
|
||||||
|
|
||||||
Notes:
|
echo "[3/5] emit object (.o) via jit-direct"
|
||||||
- Requires a Cranelift-enabled build.
|
mkdir -p "$OBJ_DIR"
|
||||||
- Runtime requires nyash.toml and plugin .so files resolvable by nyash.toml paths.
|
env -u NYASH_OPT_DIAG_FORBID_LEGACY NYASH_SKIP_TOML_ENV=1 NYASH_PLUGIN_ONLY=1 NYASH_AOT_OBJECT_OUT="$OBJ_DIR" ./target/release/nyash --jit-direct "$APP"
|
||||||
USAGE
|
|
||||||
}
|
|
||||||
|
|
||||||
if [[ $# -lt 1 ]]; then usage; exit 1; fi
|
if [[ ! -f "$OBJ_PATH" ]]; then
|
||||||
|
echo "❌ object not found: $OBJ_PATH" >&2
|
||||||
INPUT=""
|
echo "Contents of $OBJ_DIR:" >&2
|
||||||
OUT="app"
|
ls -la "$OBJ_DIR" >&2 || true
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case "$1" in
|
|
||||||
-h|--help) usage; exit 0 ;;
|
|
||||||
-o) OUT="$2"; shift 2 ;;
|
|
||||||
*) INPUT="$1"; shift ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ ! -f "$INPUT" ]]; then
|
|
||||||
echo "error: input file not found: $INPUT" >&2
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
ls -l "$OBJ_PATH"
|
||||||
|
|
||||||
echo "[1/4] Building nyash (Cranelift) ..."
|
echo "[4/5] link with nyrt -> $OUT"
|
||||||
if ! cargo build --release --features cranelift-jit >/dev/null; then
|
cc "$OBJ_PATH" \
|
||||||
echo "error: failed to build nyash with Cranelift (feature cranelift-jit)" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[2/4] Emitting object (.o) via JIT (jit-direct) ..."
|
|
||||||
rm -rf target/aot_objects && mkdir -p target/aot_objects
|
|
||||||
# Directly request main.o to be written (engine will treat non-directory path as exact output file)
|
|
||||||
NYASH_AOT_OBJECT_OUT=target/aot_objects/main.o \
|
|
||||||
NYASH_USE_PLUGIN_BUILTINS=1 \
|
|
||||||
NYASH_JIT_ONLY=1 \
|
|
||||||
# Relax strict by default to allow partial lowering to still emit objects.
|
|
||||||
# Users can re-enable strict with: export NYASH_JIT_STRICT=1
|
|
||||||
NYASH_JIT_STRICT=${NYASH_JIT_STRICT:-0} \
|
|
||||||
NYASH_JIT_NATIVE_F64=1 \
|
|
||||||
# Allow f64 shim for PyObjectBox.call (type_id=41, method_id=2)
|
|
||||||
NYASH_JIT_PLUGIN_F64="${NYASH_JIT_PLUGIN_F64:-41:2}" \
|
|
||||||
NYASH_JIT_ARGS_HANDLE_ONLY=1 \
|
|
||||||
NYASH_JIT_THRESHOLD=1 \
|
|
||||||
./target/release/nyash --jit-direct "$INPUT" >/dev/null || true
|
|
||||||
|
|
||||||
OBJ="target/aot_objects/main.o"
|
|
||||||
if [[ ! -f "$OBJ" ]]; then
|
|
||||||
echo "error: object not generated: $OBJ" >&2
|
|
||||||
echo "hint: Ensure main() is lowerable under current JIT coverage." >&2
|
|
||||||
echo "hint: Run jit-direct manually with the same envs to diagnose lowering coverage." >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[3/4] Building libnyrt.a ..."
|
|
||||||
if [[ ! -d crates/nyrt ]]; then
|
|
||||||
echo "error: crates/nyrt not found. Please ensure nyrt runtime crate exists." >&2
|
|
||||||
exit 3
|
|
||||||
fi
|
|
||||||
( cd crates/nyrt && cargo build --release >/dev/null )
|
|
||||||
|
|
||||||
echo "[4/4] Linking $OUT ..."
|
|
||||||
cc "$OBJ" \
|
|
||||||
-L crates/nyrt/target/release \
|
-L crates/nyrt/target/release \
|
||||||
-Wl,--whole-archive -lnyrt -Wl,--no-whole-archive \
|
-Wl,--whole-archive -lnyrt -Wl,--no-whole-archive \
|
||||||
-lpthread -ldl -lm -o "$OUT"
|
-lpthread -ldl -lm -o "$OUT"
|
||||||
|
|
||||||
echo "✅ Done: $OUT"
|
echo "[5/5] run $OUT"
|
||||||
echo " (runtime requires nyash.toml and plugin .so per config)"
|
./"$OUT"
|
||||||
if [[ "${NYASH_CLI_VERBOSE:-0}" == "1" ]]; then
|
|
||||||
echo "info: run with NYASH_CLI_VERBOSE=1 to see detailed steps and commands"
|
|
||||||
fi
|
|
||||||
|
|||||||
20
tools/jit_compare_smoke.sh
Normal file
20
tools/jit_compare_smoke.sh
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd)
|
||||||
|
cd "$ROOT_DIR"
|
||||||
|
|
||||||
|
echo "[build] nyash (cranelift-jit)"
|
||||||
|
cargo build --release --features cranelift-jit
|
||||||
|
|
||||||
|
run_case() {
|
||||||
|
local app="$1"
|
||||||
|
echo "[run] jit-direct: $app"
|
||||||
|
NYASH_JIT_THRESHOLD=1 ./target/release/nyash --jit-direct "$app"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_case apps/tests/mir-branch-ret/main.nyash
|
||||||
|
run_case apps/tests/mir-compare-multi/main.nyash
|
||||||
|
|
||||||
|
echo "[ok] jit compare smokes completed"
|
||||||
|
|
||||||
19
tools/vm_filebox_smoke.sh
Normal file
19
tools/vm_filebox_smoke.sh
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd)
|
||||||
|
cd "$ROOT_DIR"
|
||||||
|
|
||||||
|
echo "[build] nyash (vm)"
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
echo "[build] plugins: filebox"
|
||||||
|
cargo build -p nyash-filebox-plugin --release
|
||||||
|
|
||||||
|
mkdir -p tmp
|
||||||
|
echo -n "OK" > tmp/vm_filebox_smoke.txt
|
||||||
|
|
||||||
|
APP="apps/tests/vm-plugin-smoke-filebox/main.nyash"
|
||||||
|
echo "[run] VM plugin-first strict: $APP"
|
||||||
|
NYASH_VM_PLUGIN_STRICT=1 ./target/release/nyash --backend vm "$APP"
|
||||||
|
|
||||||
16
tools/vm_plugin_smoke.sh
Normal file
16
tools/vm_plugin_smoke.sh
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd)
|
||||||
|
cd "$ROOT_DIR"
|
||||||
|
|
||||||
|
echo "[build] nyash (vm)"
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
echo "[build] core plugins (subset)"
|
||||||
|
cargo build -p nyash-counter-plugin --release
|
||||||
|
|
||||||
|
APP="apps/tests/vm-plugin-smoke-counter/main.nyash"
|
||||||
|
echo "[run] VM plugin-first strict: $APP"
|
||||||
|
NYASH_VM_PLUGIN_STRICT=1 ./target/release/nyash --backend vm "$APP"
|
||||||
|
|
||||||
Reference in New Issue
Block a user