📚 Phase 11 documentation: Everything is Box × MIR15 revolution
Key updates: - Document MIR 26→15 instruction reduction plan (transitioning status) - Add Core-15 target instruction set in INSTRUCTION_SET.md - Save AI conference analyses validating Box Theory and 15-instruction design - Create MIR annotation system proposal for optimization hints - Update SKIP_PHASE_10_DECISION.md with LLVM direct migration rationale Technical insights: - RefNew/RefGet/RefSet can be eliminated through Box unification - GC/sync/async all achievable with 15 core instructions - BoxCall lowering can automatically insert GC barriers - 2-3x performance improvement expected with LLVM - Build time reduction 50%, binary size reduction 40% Status: Design complete, implementation pending
This commit is contained in:
@ -145,6 +145,30 @@ impl JitEngine {
|
||||
return Some(h);
|
||||
}
|
||||
// If Cranelift path did not produce a closure, treat as not compiled
|
||||
// Even if a closure was not produced, attempt AOT object emission when requested
|
||||
if let Ok(path) = std::env::var("NYASH_AOT_OBJECT_OUT") {
|
||||
if !path.is_empty() {
|
||||
let mut lower2 = crate::jit::lower::core::LowerCore::new();
|
||||
let mut objb = crate::jit::lower::builder::ObjectBuilder::new();
|
||||
match lower2.lower_function(mir, &mut objb) {
|
||||
Err(e) => eprintln!("[AOT] lower failed for {}: {}", func_name, e),
|
||||
Ok(()) => {
|
||||
if let Some(bytes) = objb.take_object_bytes() {
|
||||
use std::path::Path;
|
||||
let p = Path::new(&path);
|
||||
let out_path = if p.is_dir() || path.ends_with('/') { p.join(format!("{}.o", func_name)) } else { p.to_path_buf() };
|
||||
if let Some(parent) = out_path.parent() { let _ = std::fs::create_dir_all(parent); }
|
||||
match std::fs::write(&out_path, bytes) {
|
||||
Ok(_) => eprintln!("[AOT] wrote object: {}", out_path.display()),
|
||||
Err(e) => eprintln!("[AOT] failed to write object {}: {}", out_path.display(), e),
|
||||
}
|
||||
} else {
|
||||
eprintln!("[AOT] no object bytes available for {}", func_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
#[cfg(not(feature = "cranelift-jit"))]
|
||||
|
||||
4
src/jit/extern/collections.rs
vendored
4
src/jit/extern/collections.rs
vendored
@ -30,6 +30,10 @@ pub const SYM_ANY_IS_EMPTY_H: &str = "nyash.any.is_empty_h";
|
||||
pub const SYM_STRING_CHARCODE_AT_H: &str = "nyash.string.charCodeAt_h";
|
||||
pub const SYM_STRING_BIRTH_H: &str = "nyash.string.birth_h";
|
||||
pub const SYM_INTEGER_BIRTH_H: &str = "nyash.integer.birth_h";
|
||||
// String-like operations (handle, handle)
|
||||
pub const SYM_STRING_CONCAT_HH: &str = "nyash.string.concat_hh";
|
||||
pub const SYM_STRING_EQ_HH: &str = "nyash.string.eq_hh";
|
||||
pub const SYM_STRING_LT_HH: &str = "nyash.string.lt_hh";
|
||||
|
||||
fn as_array(args: &[VMValue]) -> Option<&crate::boxes::array::ArrayBox> {
|
||||
match args.get(0) {
|
||||
|
||||
1
src/jit/extern/mod.rs
vendored
1
src/jit/extern/mod.rs
vendored
@ -7,3 +7,4 @@
|
||||
pub mod collections;
|
||||
pub mod handles;
|
||||
pub mod birth;
|
||||
pub mod runtime;
|
||||
|
||||
8
src/jit/extern/runtime.rs
vendored
Normal file
8
src/jit/extern/runtime.rs
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
//! Runtime/GC related hostcall symbol names reserved for JIT/AOT.
|
||||
|
||||
/// Runtime safepoint checkpoint (no-op stub for now)
|
||||
pub const SYM_RT_CHECKPOINT: &str = "nyash.rt.checkpoint";
|
||||
|
||||
/// Write barrier hint for GC (no-op stub for now)
|
||||
pub const SYM_GC_BARRIER_WRITE: &str = "nyash.gc.barrier_write";
|
||||
|
||||
@ -30,6 +30,9 @@ fn ensure_default() {
|
||||
"nyash.map.get_h",
|
||||
"nyash.map.has_h",
|
||||
"nyash.string.charCodeAt_h",
|
||||
"nyash.string.concat_hh",
|
||||
"nyash.string.eq_hh",
|
||||
"nyash.string.lt_hh",
|
||||
"nyash.array.get_h",
|
||||
] { r.ro.insert(s.to_string()); }
|
||||
// Mutating defaults
|
||||
@ -52,6 +55,9 @@ fn ensure_default() {
|
||||
r.sig.entry("nyash.array.len_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle], ret: ArgKind::I64 });
|
||||
// String helpers
|
||||
r.sig.entry("nyash.string.charCodeAt_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::I64], ret: ArgKind::I64 });
|
||||
r.sig.entry("nyash.string.concat_hh".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::Handle], ret: ArgKind::Handle });
|
||||
r.sig.entry("nyash.string.eq_hh".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::Handle], ret: ArgKind::I64 });
|
||||
r.sig.entry("nyash.string.lt_hh".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::Handle], ret: ArgKind::I64 });
|
||||
// Any helpers (length/is_empty)
|
||||
r.sig.entry("nyash.any.length_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle], ret: ArgKind::I64 });
|
||||
r.sig.entry("nyash.any.is_empty_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle], ret: ArgKind::I64 });
|
||||
|
||||
@ -536,7 +536,8 @@ use super::extern_thunks::{
|
||||
nyash_array_last_h, nyash_map_size_h, nyash_map_get_h, nyash_map_get_hh,
|
||||
nyash_map_set_h, nyash_map_has_h,
|
||||
nyash_string_charcode_at_h, nyash_string_birth_h, nyash_integer_birth_h,
|
||||
nyash_any_length_h, nyash_any_is_empty_h,
|
||||
nyash_string_concat_hh, nyash_string_eq_hh, nyash_string_lt_hh,
|
||||
nyash_any_length_h, nyash_any_is_empty_h, nyash_console_birth_h,
|
||||
};
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
@ -742,8 +743,7 @@ impl IRBuilder for CraneliftBuilder {
|
||||
let entry = self.blocks[0];
|
||||
fb.append_block_params_for_function_params(entry);
|
||||
fb.switch_to_block(entry);
|
||||
// Entry block can be sealed immediately
|
||||
fb.seal_block(entry);
|
||||
// Defer sealing to allow entry PHI params when needed
|
||||
self.entry_block = Some(entry);
|
||||
self.current_block_index = Some(0);
|
||||
fb.finalize();
|
||||
@ -775,6 +775,7 @@ impl IRBuilder for CraneliftBuilder {
|
||||
// SAFETY: We compiled a function with simple (i64 x N) -> i64/f64 というABIだよ。
|
||||
// ランタイムでは JitValue から i64 へ正規化して、引数個数に応じた関数型にtransmuteして呼び出すにゃ。
|
||||
let argc = self.desired_argc;
|
||||
let has_ret = self.desired_has_ret;
|
||||
let ret_is_f64 = self.desired_has_ret && self.desired_ret_is_f64;
|
||||
// capture code as usize to avoid raw pointer Send/Sync issues in closure
|
||||
let code_usize = code as usize;
|
||||
@ -786,35 +787,26 @@ impl IRBuilder for CraneliftBuilder {
|
||||
for i in 0..take {
|
||||
a[i] = match args[i] { crate::jit::abi::JitValue::I64(v) => v, crate::jit::abi::JitValue::Bool(b) => if b {1} else {0}, crate::jit::abi::JitValue::F64(f) => f as i64, crate::jit::abi::JitValue::Handle(h) => h as i64 };
|
||||
}
|
||||
let ret_i64 = match argc {
|
||||
0 => {
|
||||
let f: extern "C" fn() -> i64 = std::mem::transmute(code_usize);
|
||||
f()
|
||||
// Call according to return type expectation
|
||||
let ret_i64 = if has_ret {
|
||||
match argc {
|
||||
0 => { let f: extern "C" fn() -> i64 = std::mem::transmute(code_usize); f() }
|
||||
1 => { let f: extern "C" fn(i64) -> i64 = std::mem::transmute(code_usize); f(a[0]) }
|
||||
2 => { let f: extern "C" fn(i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1]) }
|
||||
3 => { let f: extern "C" fn(i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2]) }
|
||||
4 => { let f: extern "C" fn(i64,i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3]) }
|
||||
5 => { let f: extern "C" fn(i64,i64,i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4]) }
|
||||
_ => { let f: extern "C" fn(i64,i64,i64,i64,i64,i64) -> i64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4],a[5]) }
|
||||
}
|
||||
1 => {
|
||||
let f: extern "C" fn(i64) -> i64 = std::mem::transmute(code_usize);
|
||||
f(a[0])
|
||||
}
|
||||
2 => {
|
||||
let f: extern "C" fn(i64, i64) -> i64 = std::mem::transmute(code_usize);
|
||||
f(a[0], a[1])
|
||||
}
|
||||
3 => {
|
||||
let f: extern "C" fn(i64, i64, i64) -> i64 = std::mem::transmute(code_usize);
|
||||
f(a[0], a[1], a[2])
|
||||
}
|
||||
4 => {
|
||||
let f: extern "C" fn(i64, i64, i64, i64) -> i64 = std::mem::transmute(code_usize);
|
||||
f(a[0], a[1], a[2], a[3])
|
||||
}
|
||||
5 => {
|
||||
let f: extern "C" fn(i64, i64, i64, i64, i64) -> i64 = std::mem::transmute(code_usize);
|
||||
f(a[0], a[1], a[2], a[3], a[4])
|
||||
}
|
||||
_ => {
|
||||
// 上限6(十分なPoC)
|
||||
let f: extern "C" fn(i64, i64, i64, i64, i64, i64) -> i64 = std::mem::transmute(code_usize);
|
||||
f(a[0], a[1], a[2], a[3], a[4], a[5])
|
||||
} else {
|
||||
match argc {
|
||||
0 => { let f: extern "C" fn() = std::mem::transmute(code_usize); f(); 0 }
|
||||
1 => { let f: extern "C" fn(i64) = std::mem::transmute(code_usize); f(a[0]); 0 }
|
||||
2 => { let f: extern "C" fn(i64,i64) = std::mem::transmute(code_usize); f(a[0],a[1]); 0 }
|
||||
3 => { let f: extern "C" fn(i64,i64,i64) = std::mem::transmute(code_usize); f(a[0],a[1],a[2]); 0 }
|
||||
4 => { let f: extern "C" fn(i64,i64,i64,i64) = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3]); 0 }
|
||||
5 => { let f: extern "C" fn(i64,i64,i64,i64,i64) = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4]); 0 }
|
||||
_ => { let f: extern "C" fn(i64,i64,i64,i64,i64,i64) = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4],a[5]); 0 }
|
||||
}
|
||||
};
|
||||
if ret_is_f64 {
|
||||
@ -833,6 +825,73 @@ impl IRBuilder for CraneliftBuilder {
|
||||
});
|
||||
self.compiled_closure = Some(closure);
|
||||
}
|
||||
// Important: keep finalized code alive by preserving the JITModule.
|
||||
// Swap current module with a fresh one and leak the old module to avoid freeing code memory.
|
||||
{
|
||||
// Build a fresh JITModule with the same symbol registrations for the next compilation
|
||||
let mut jb = cranelift_jit::JITBuilder::new(cranelift_module::default_libcall_names())
|
||||
.expect("failed to create JITBuilder");
|
||||
// Register host-call symbols (keep in sync with new())
|
||||
jb.symbol("nyash.host.stub0", nyash_host_stub0 as *const u8);
|
||||
{
|
||||
use crate::jit::r#extern::collections as c;
|
||||
use crate::jit::r#extern::{handles as h, birth as b, runtime as r};
|
||||
use super::extern_thunks::{
|
||||
nyash_plugin_invoke_name_getattr_i64, nyash_plugin_invoke_name_call_i64,
|
||||
nyash_handle_of, nyash_box_birth_h, nyash_box_birth_i64,
|
||||
nyash_rt_checkpoint, nyash_gc_barrier_write,
|
||||
};
|
||||
jb.symbol(c::SYM_ARRAY_LEN, nyash_array_len as *const u8);
|
||||
jb.symbol(c::SYM_ARRAY_GET, nyash_array_get as *const u8);
|
||||
jb.symbol(c::SYM_ARRAY_SET, nyash_array_set as *const u8);
|
||||
jb.symbol(c::SYM_ARRAY_PUSH, nyash_array_push as *const u8);
|
||||
jb.symbol(c::SYM_MAP_GET, nyash_map_get as *const u8);
|
||||
jb.symbol(c::SYM_MAP_SET, nyash_map_set as *const u8);
|
||||
jb.symbol(c::SYM_MAP_SIZE, nyash_map_size as *const u8);
|
||||
jb.symbol("nyash.math.sin_f64", nyash_math_sin_f64 as *const u8);
|
||||
jb.symbol("nyash.math.cos_f64", nyash_math_cos_f64 as *const u8);
|
||||
jb.symbol("nyash.math.abs_f64", nyash_math_abs_f64 as *const u8);
|
||||
jb.symbol("nyash.math.min_f64", nyash_math_min_f64 as *const u8);
|
||||
jb.symbol("nyash.math.max_f64", nyash_math_max_f64 as *const u8);
|
||||
// Handle-based symbols
|
||||
jb.symbol(c::SYM_ARRAY_LEN_H, nyash_array_len_h as *const u8);
|
||||
jb.symbol(c::SYM_ARRAY_GET_H, nyash_array_get_h as *const u8);
|
||||
jb.symbol(c::SYM_ARRAY_SET_H, nyash_array_set_h as *const u8);
|
||||
jb.symbol(c::SYM_ARRAY_PUSH_H, nyash_array_push_h as *const u8);
|
||||
jb.symbol(c::SYM_ARRAY_LAST_H, nyash_array_last_h as *const u8);
|
||||
jb.symbol(c::SYM_MAP_SIZE_H, nyash_map_size_h as *const u8);
|
||||
jb.symbol(c::SYM_MAP_GET_H, nyash_map_get_h as *const u8);
|
||||
jb.symbol(c::SYM_MAP_GET_HH, nyash_map_get_hh as *const u8);
|
||||
jb.symbol(c::SYM_MAP_SET_H, nyash_map_set_h as *const u8);
|
||||
jb.symbol(c::SYM_MAP_HAS_H, nyash_map_has_h as *const u8);
|
||||
jb.symbol(c::SYM_ANY_LEN_H, nyash_any_length_h as *const u8);
|
||||
jb.symbol(c::SYM_ANY_IS_EMPTY_H, nyash_any_is_empty_h as *const u8);
|
||||
jb.symbol(c::SYM_STRING_CHARCODE_AT_H, nyash_string_charcode_at_h as *const u8);
|
||||
jb.symbol(c::SYM_STRING_BIRTH_H, nyash_string_birth_h as *const u8);
|
||||
jb.symbol(c::SYM_INTEGER_BIRTH_H, nyash_integer_birth_h as *const u8);
|
||||
// String-like binary ops (handle, handle)
|
||||
jb.symbol(c::SYM_STRING_CONCAT_HH, nyash_string_concat_hh as *const u8);
|
||||
jb.symbol(c::SYM_STRING_EQ_HH, nyash_string_eq_hh as *const u8);
|
||||
jb.symbol(c::SYM_STRING_LT_HH, nyash_string_lt_hh as *const u8);
|
||||
jb.symbol(b::SYM_BOX_BIRTH_H, nyash_box_birth_h as *const u8);
|
||||
jb.symbol("nyash.box.birth_i64", nyash_box_birth_i64 as *const u8);
|
||||
// Handle helpers
|
||||
jb.symbol(h::SYM_HANDLE_OF, nyash_handle_of as *const u8);
|
||||
// Plugin invoke shims (i64/f64)
|
||||
jb.symbol("nyash_plugin_invoke3_i64", nyash_plugin_invoke3_i64 as *const u8);
|
||||
jb.symbol("nyash_plugin_invoke3_f64", nyash_plugin_invoke3_f64 as *const u8);
|
||||
// By-name plugin invoke shims (method-name specific)
|
||||
jb.symbol("nyash_plugin_invoke_name_getattr_i64", nyash_plugin_invoke_name_getattr_i64 as *const u8);
|
||||
jb.symbol("nyash_plugin_invoke_name_call_i64", nyash_plugin_invoke_name_call_i64 as *const u8);
|
||||
// Reserved runtime/GC symbols
|
||||
jb.symbol(r::SYM_RT_CHECKPOINT, nyash_rt_checkpoint as *const u8);
|
||||
jb.symbol(r::SYM_GC_BARRIER_WRITE, nyash_gc_barrier_write as *const u8);
|
||||
}
|
||||
let new_module = cranelift_jit::JITModule::new(jb);
|
||||
// Leak the old module so finalized code stays valid
|
||||
let old = std::mem::replace(&mut self.module, new_module);
|
||||
let _leaked: &'static mut cranelift_jit::JITModule = Box::leak(Box::new(old));
|
||||
}
|
||||
// Reset typed signature flag for next function
|
||||
self.typed_sig_prepared = false;
|
||||
}
|
||||
@ -956,6 +1015,12 @@ impl IRBuilder for CraneliftBuilder {
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
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); }
|
||||
// If function has no return values, emit a plain return
|
||||
if fb.func.signature.returns.is_empty() {
|
||||
fb.ins().return_(&[]);
|
||||
fb.finalize();
|
||||
return;
|
||||
}
|
||||
if let Some(mut v) = self.value_stack.pop() {
|
||||
// Normalize return type if needed
|
||||
let ret_ty = fb.func.signature.returns.get(0).map(|p| p.value_type).unwrap_or(cranelift_codegen::ir::types::I64);
|
||||
@ -1528,7 +1593,7 @@ impl IRBuilder for ObjectBuilder {
|
||||
let entry = self.blocks[0];
|
||||
fb.append_block_params_for_function_params(entry);
|
||||
fb.switch_to_block(entry);
|
||||
fb.seal_block(entry);
|
||||
// Defer sealing to allow entry PHI params when needed
|
||||
self.entry_block = Some(entry);
|
||||
self.current_block_index = Some(0);
|
||||
fb.finalize();
|
||||
@ -1621,6 +1686,11 @@ impl IRBuilder for ObjectBuilder {
|
||||
use cranelift_frontend::FunctionBuilder; use cranelift_codegen::ir::types;
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
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); }
|
||||
if fb.func.signature.returns.is_empty() {
|
||||
fb.ins().return_(&[]);
|
||||
fb.finalize();
|
||||
return;
|
||||
}
|
||||
if let Some(mut v) = self.value_stack.pop() {
|
||||
let ret_ty = fb.func.signature.returns.get(0).map(|p| p.value_type).unwrap_or(types::I64);
|
||||
let v_ty = fb.func.dfg.value_type(v);
|
||||
@ -1748,12 +1818,16 @@ impl CraneliftBuilder {
|
||||
// Initialize a minimal JITModule to validate linking; not used yet
|
||||
let mut builder = cranelift_jit::JITBuilder::new(cranelift_module::default_libcall_names())
|
||||
.expect("failed to create JITBuilder");
|
||||
// Register host-call symbols (PoC: map to simple C-ABI stubs)
|
||||
builder.symbol("nyash.host.stub0", nyash_host_stub0 as *const u8);
|
||||
// Register host-call symbols (PoC: map to simple C-ABI stubs)
|
||||
builder.symbol("nyash.host.stub0", nyash_host_stub0 as *const u8);
|
||||
{
|
||||
use crate::jit::r#extern::collections as c;
|
||||
use crate::jit::r#extern::{handles as h, birth as b};
|
||||
use super::extern_thunks::{nyash_plugin_invoke_name_getattr_i64, nyash_plugin_invoke_name_call_i64, nyash_handle_of, nyash_box_birth_h, nyash_box_birth_i64};
|
||||
use crate::jit::r#extern::{handles as h, birth as b, runtime as r};
|
||||
use super::extern_thunks::{
|
||||
nyash_plugin_invoke_name_getattr_i64, nyash_plugin_invoke_name_call_i64,
|
||||
nyash_handle_of, nyash_box_birth_h, nyash_box_birth_i64,
|
||||
nyash_rt_checkpoint, nyash_gc_barrier_write,
|
||||
};
|
||||
builder.symbol(c::SYM_ARRAY_LEN, nyash_array_len as *const u8);
|
||||
builder.symbol(c::SYM_ARRAY_GET, nyash_array_get as *const u8);
|
||||
builder.symbol(c::SYM_ARRAY_SET, nyash_array_set as *const u8);
|
||||
@ -1783,6 +1857,11 @@ impl CraneliftBuilder {
|
||||
builder.symbol(c::SYM_STRING_CHARCODE_AT_H, nyash_string_charcode_at_h as *const u8);
|
||||
builder.symbol(c::SYM_STRING_BIRTH_H, nyash_string_birth_h as *const u8);
|
||||
builder.symbol(c::SYM_INTEGER_BIRTH_H, nyash_integer_birth_h as *const u8);
|
||||
builder.symbol("nyash.console.birth_h", nyash_console_birth_h as *const u8);
|
||||
// String-like binary ops (handle, handle)
|
||||
builder.symbol(c::SYM_STRING_CONCAT_HH, nyash_string_concat_hh as *const u8);
|
||||
builder.symbol(c::SYM_STRING_EQ_HH, nyash_string_eq_hh as *const u8);
|
||||
builder.symbol(c::SYM_STRING_LT_HH, nyash_string_lt_hh as *const u8);
|
||||
builder.symbol(b::SYM_BOX_BIRTH_H, nyash_box_birth_h as *const u8);
|
||||
builder.symbol("nyash.box.birth_i64", nyash_box_birth_i64 as *const u8);
|
||||
// Handle helpers
|
||||
@ -1793,6 +1872,9 @@ impl CraneliftBuilder {
|
||||
// By-name plugin invoke shims (method-name specific)
|
||||
builder.symbol("nyash_plugin_invoke_name_getattr_i64", nyash_plugin_invoke_name_getattr_i64 as *const u8);
|
||||
builder.symbol("nyash_plugin_invoke_name_call_i64", nyash_plugin_invoke_name_call_i64 as *const u8);
|
||||
// Reserved runtime/GC symbols
|
||||
builder.symbol(r::SYM_RT_CHECKPOINT, nyash_rt_checkpoint as *const u8);
|
||||
builder.symbol(r::SYM_GC_BARRIER_WRITE, nyash_gc_barrier_write as *const u8);
|
||||
}
|
||||
let module = cranelift_jit::JITModule::new(builder);
|
||||
let ctx = cranelift_codegen::Context::new();
|
||||
|
||||
@ -11,7 +11,7 @@ pub struct LowerCore {
|
||||
/// Minimal constant propagation for f64 (math.* signature checks)
|
||||
known_f64: std::collections::HashMap<ValueId, f64>,
|
||||
/// Parameter index mapping for ValueId
|
||||
param_index: std::collections::HashMap<ValueId, usize>,
|
||||
pub(super) param_index: std::collections::HashMap<ValueId, usize>,
|
||||
/// Track values produced by Phi (for minimal PHI path)
|
||||
phi_values: std::collections::HashSet<ValueId>,
|
||||
/// Map (block, phi dst) -> param index in that block (for multi-PHI)
|
||||
@ -23,16 +23,16 @@ pub struct LowerCore {
|
||||
/// Track values that are FloatBox instances (for arg type classification)
|
||||
float_box_values: std::collections::HashSet<ValueId>,
|
||||
/// Track values that are plugin handles (generic box/handle, type unknown at compile time)
|
||||
handle_values: std::collections::HashSet<ValueId>,
|
||||
pub(super) handle_values: std::collections::HashSet<ValueId>,
|
||||
// Per-function statistics (last lowered)
|
||||
last_phi_total: u64,
|
||||
last_phi_b1: u64,
|
||||
last_ret_bool_hint_used: bool,
|
||||
// Minimal local slot mapping for Load/Store (ptr ValueId -> slot index)
|
||||
local_index: std::collections::HashMap<ValueId, usize>,
|
||||
next_local: usize,
|
||||
pub(super) local_index: std::collections::HashMap<ValueId, usize>,
|
||||
pub(super) next_local: usize,
|
||||
/// Track NewBox origins: ValueId -> box type name (e.g., "PyRuntimeBox")
|
||||
box_type_map: std::collections::HashMap<ValueId, String>,
|
||||
pub(super) box_type_map: std::collections::HashMap<ValueId, String>,
|
||||
}
|
||||
|
||||
impl LowerCore {
|
||||
@ -44,7 +44,7 @@ impl LowerCore {
|
||||
/// Walk the MIR function and count supported/unsupported instructions.
|
||||
/// In the future, this will build CLIF via Cranelift builders.
|
||||
pub fn lower_function(&mut self, func: &MirFunction, builder: &mut dyn IRBuilder) -> Result<(), String> {
|
||||
// Prepare a simple i64 ABI based on param count; always assume i64 return for now
|
||||
// Prepare ABI based on MIR signature
|
||||
// Reset per-function stats
|
||||
self.last_phi_total = 0; self.last_phi_b1 = 0; self.last_ret_bool_hint_used = false;
|
||||
// Build param index map
|
||||
@ -235,10 +235,11 @@ impl LowerCore {
|
||||
crate::jit::rt::ret_bool_hint_inc(1);
|
||||
self.last_ret_bool_hint_used = true;
|
||||
}
|
||||
let has_ret = !matches!(func.signature.return_type, crate::mir::MirType::Void);
|
||||
if use_typed || ret_is_f64 {
|
||||
builder.prepare_signature_typed(&kinds, ret_is_f64);
|
||||
builder.prepare_signature_typed(&kinds, ret_is_f64 && has_ret);
|
||||
} else {
|
||||
builder.prepare_signature_i64(func.params.len(), true);
|
||||
builder.prepare_signature_i64(func.params.len(), has_ret);
|
||||
}
|
||||
// Pre-scan FloatBox creations across all blocks for arg classification
|
||||
self.float_box_values.clear();
|
||||
@ -725,8 +726,8 @@ impl LowerCore {
|
||||
if self.bool_values.contains(src) { self.bool_values.insert(*dst); }
|
||||
// Otherwise no-op for codegen (stack-machine handles sources directly later)
|
||||
}
|
||||
I::BinOp { dst, op, lhs, rhs } => { self.lower_binop(b, op, lhs, rhs, dst); }
|
||||
I::Compare { op, lhs, rhs, dst } => { self.lower_compare(b, op, lhs, rhs, dst); }
|
||||
I::BinOp { dst, op, lhs, rhs } => { self.lower_binop(b, op, lhs, rhs, dst, func); }
|
||||
I::Compare { op, lhs, rhs, dst } => { self.lower_compare(b, op, lhs, rhs, dst, func); }
|
||||
I::Jump { .. } => self.lower_jump(b),
|
||||
I::Branch { .. } => self.lower_branch(b),
|
||||
I::Return { value } => {
|
||||
|
||||
@ -1,11 +1,44 @@
|
||||
//! Core ops lowering (non-hostcall): BinOp, Compare, Branch, Jump
|
||||
use super::builder::{IRBuilder, BinOpKind, CmpKind};
|
||||
use crate::mir::{BinaryOp, CompareOp, ValueId};
|
||||
use crate::mir::{BinaryOp, CompareOp, ValueId, MirFunction, MirType};
|
||||
|
||||
use super::core::LowerCore;
|
||||
|
||||
impl LowerCore {
|
||||
pub fn lower_binop(&mut self, b: &mut dyn IRBuilder, op: &BinaryOp, lhs: &ValueId, rhs: &ValueId, dst: &ValueId) {
|
||||
fn is_string_like(&self, func: &MirFunction, v: &ValueId) -> bool {
|
||||
// Check per-value type metadata
|
||||
if let Some(mt) = func.metadata.value_types.get(v) {
|
||||
if matches!(mt, MirType::String) { return true; }
|
||||
if let MirType::Box(ref name) = mt { if name == "StringBox" { return true; } }
|
||||
}
|
||||
// Check if this value is a parameter with String or StringBox type
|
||||
if let Some(pidx) = self.param_index.get(v).copied() {
|
||||
if let Some(pt) = func.signature.params.get(pidx) {
|
||||
if matches!(pt, MirType::String) { return true; }
|
||||
if let MirType::Box(ref name) = pt { if name == "StringBox" { return true; } }
|
||||
}
|
||||
}
|
||||
// Check if it originates from a StringBox NewBox
|
||||
if let Some(name) = self.box_type_map.get(v) { if name == "StringBox" { return true; } }
|
||||
false
|
||||
}
|
||||
|
||||
pub fn lower_binop(&mut self, b: &mut dyn IRBuilder, op: &BinaryOp, lhs: &ValueId, rhs: &ValueId, dst: &ValueId, func: &MirFunction) {
|
||||
// Route string-like addition to hostcall (handle,handle)
|
||||
if crate::jit::config::current().hostcall {
|
||||
if matches!(op, BinaryOp::Add) {
|
||||
if self.is_string_like(func, lhs) || self.is_string_like(func, rhs) {
|
||||
self.push_value_if_known_or_param(b, lhs);
|
||||
self.push_value_if_known_or_param(b, rhs);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_STRING_CONCAT_HH, 2, true);
|
||||
// Track handle result for downstream usages
|
||||
self.handle_values.insert(*dst);
|
||||
let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||
b.store_local_i64(slot);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.push_value_if_known_or_param(b, lhs);
|
||||
self.push_value_if_known_or_param(b, rhs);
|
||||
let kind = match op {
|
||||
@ -30,7 +63,20 @@ impl LowerCore {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_compare(&mut self, b: &mut dyn IRBuilder, op: &CompareOp, lhs: &ValueId, rhs: &ValueId, dst: &ValueId) {
|
||||
pub fn lower_compare(&mut self, b: &mut dyn IRBuilder, op: &CompareOp, lhs: &ValueId, rhs: &ValueId, dst: &ValueId, func: &MirFunction) {
|
||||
// Route string-like comparisons (Eq/Lt) to hostcalls (i64 0/1)
|
||||
if crate::jit::config::current().hostcall {
|
||||
if matches!(op, CompareOp::Eq | CompareOp::Lt) {
|
||||
if self.is_string_like(func, lhs) || self.is_string_like(func, rhs) {
|
||||
self.push_value_if_known_or_param(b, lhs);
|
||||
self.push_value_if_known_or_param(b, rhs);
|
||||
let sym = match op { CompareOp::Eq => crate::jit::r#extern::collections::SYM_STRING_EQ_HH, CompareOp::Lt => crate::jit::r#extern::collections::SYM_STRING_LT_HH, _ => unreachable!() };
|
||||
b.emit_host_call(sym, 2, true);
|
||||
self.bool_values.insert(*dst);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.push_value_if_known_or_param(b, lhs);
|
||||
self.push_value_if_known_or_param(b, rhs);
|
||||
let kind = match op {
|
||||
|
||||
@ -141,6 +141,38 @@ pub(super) extern "C" fn nyash_math_min_f64(a: f64, b: f64) -> f64 { a.min(b) }
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_math_max_f64(a: f64, b: f64) -> f64 { a.max(b) }
|
||||
|
||||
// ---- Console (handle) ----
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_console_birth_h() -> i64 {
|
||||
if let Ok(host_g) = crate::runtime::get_global_plugin_host().read() {
|
||||
if let Ok(b) = host_g.create_box("ConsoleBox", &[]) {
|
||||
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::from(b);
|
||||
let h = crate::jit::rt::handles::to_handle(arc);
|
||||
return h as i64;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// ---- Runtime/GC stubs ----
|
||||
// Minimal no-op checkpoints and barriers for reservation. They optionally trace when envs are set.
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_rt_checkpoint() -> i64 {
|
||||
if std::env::var("NYASH_RUNTIME_CHECKPOINT_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[nyash.rt.checkpoint] reached");
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_gc_barrier_write(handle_or_ptr: u64) -> i64 {
|
||||
let _ = handle_or_ptr; // reserved; currently unused
|
||||
if std::env::var("NYASH_GC_BARRIER_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[nyash.gc.barrier_write] h=0x{:x}", handle_or_ptr);
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// ---- Array (handle) ----
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_array_len_h(handle: u64) -> i64 {
|
||||
@ -522,3 +554,69 @@ pub(super) extern "C" fn nyash_integer_birth_h() -> i64 {
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// ---- String-like helpers and ops (handle, handle) ----
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
fn handle_to_string_like(handle: u64) -> Option<String> {
|
||||
// Prefer runtime handle registry
|
||||
if let Some(obj) = crate::jit::rt::handles::get(handle) {
|
||||
if let Some(sb) = obj.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||
return Some(sb.value.clone());
|
||||
}
|
||||
if let Some(pb) = obj.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
if pb.box_type == "StringBox" {
|
||||
if let Ok(host) = crate::runtime::get_global_plugin_host().read() {
|
||||
if let Ok(val_opt) = host.invoke_instance_method("StringBox", "toUtf8", pb.instance_id(), &[]) {
|
||||
if let Some(vb) = val_opt { if let Some(sbb) = vb.as_any().downcast_ref::<crate::box_trait::StringBox>() { return Some(sbb.value.clone()); } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback for any NyashBox
|
||||
return Some(obj.to_string_box().value);
|
||||
}
|
||||
// Legacy fallback: treat small values as VM arg index
|
||||
if handle <= 16 {
|
||||
let idx = handle as usize;
|
||||
let val = crate::jit::rt::with_legacy_vm_args(|args| args.get(idx).cloned());
|
||||
if let Some(v) = val {
|
||||
use crate::backend::vm::VMValue as V;
|
||||
return match v {
|
||||
V::String(s) => Some(s),
|
||||
V::BoxRef(b) => Some(b.to_string_box().value),
|
||||
V::Integer(i) => Some(i.to_string()),
|
||||
V::Float(f) => Some(f.to_string()),
|
||||
V::Bool(b) => Some(b.to_string()),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_string_concat_hh(a_h: u64, b_h: u64) -> i64 {
|
||||
events::emit_runtime(serde_json::json!({"id": c::SYM_STRING_CONCAT_HH, "decision":"allow", "argc":2, "arg_types":["Handle","Handle"]}), "hostcall", "<jit>");
|
||||
let a = handle_to_string_like(a_h).unwrap_or_default();
|
||||
let b = handle_to_string_like(b_h).unwrap_or_default();
|
||||
let s = format!("{}{}", a, b);
|
||||
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::new(crate::box_trait::StringBox::new(s));
|
||||
let h = crate::jit::rt::handles::to_handle(arc);
|
||||
h as i64
|
||||
}
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_string_eq_hh(a_h: u64, b_h: u64) -> i64 {
|
||||
events::emit_runtime(serde_json::json!({"id": c::SYM_STRING_EQ_HH, "decision":"allow", "argc":2, "arg_types":["Handle","Handle"]}), "hostcall", "<jit>");
|
||||
let a = handle_to_string_like(a_h).unwrap_or_default();
|
||||
let b = handle_to_string_like(b_h).unwrap_or_default();
|
||||
if a == b { 1 } else { 0 }
|
||||
}
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_string_lt_hh(a_h: u64, b_h: u64) -> i64 {
|
||||
events::emit_runtime(serde_json::json!({"id": c::SYM_STRING_LT_HH, "decision":"allow", "argc":2, "arg_types":["Handle","Handle"]}), "hostcall", "<jit>");
|
||||
let a = handle_to_string_like(a_h).unwrap_or_default();
|
||||
let b = handle_to_string_like(b_h).unwrap_or_default();
|
||||
if a < b { 1 } else { 0 }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user