cranelift: fix FB block-switch panic by safe switch wrapper; add Array.set(H,H) thunk + lowering; route all builder switching via safe path; pass identical_exec add/console_log/string_len/array_map_string
This commit is contained in:
1
src/jit/extern/collections.rs
vendored
1
src/jit/extern/collections.rs
vendored
@ -17,6 +17,7 @@ pub const SYM_MAP_SIZE: &str = "nyash.map.size";
|
||||
pub const SYM_ARRAY_LEN_H: &str = "nyash.array.len_h";
|
||||
pub const SYM_ARRAY_GET_H: &str = "nyash.array.get_h";
|
||||
pub const SYM_ARRAY_SET_H: &str = "nyash.array.set_h";
|
||||
pub const SYM_ARRAY_SET_HH: &str = "nyash.array.set_hh";
|
||||
pub const SYM_ARRAY_PUSH_H: &str = "nyash.array.push_h";
|
||||
pub const SYM_ARRAY_LAST_H: &str = "nyash.array.last_h";
|
||||
pub const SYM_MAP_SIZE_H: &str = "nyash.map.size_h";
|
||||
|
||||
@ -615,9 +615,10 @@ impl IRBuilder for CraneliftBuilder {
|
||||
if has_ret { sig.returns.push(AbiParam::new(if use_f64 { types::F64 } else { types::I64 })); }
|
||||
let symbol = if use_f64 { "nyash_plugin_invoke3_f64" } else { "nyash_plugin_invoke3_i64" };
|
||||
let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare plugin shim failed");
|
||||
// Ensure we are in a valid block context using the builder's safe switch
|
||||
if let Some(idx) = self.current_block_index { self.switch_to_block(idx); }
|
||||
else if self.entry_block.is_some() { self.switch_to_block(0); }
|
||||
let ret_val = Self::with_fb(|fb| {
|
||||
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); }
|
||||
while arg_vals.len() < 3 { let z = fb.ins().iconst(types::I64, 0); arg_vals.push(z); }
|
||||
// handle.of on receiver (redundant-safe)
|
||||
let call_conv_h = self.module.isa().default_call_conv();
|
||||
@ -639,10 +640,9 @@ impl IRBuilder for CraneliftBuilder {
|
||||
}
|
||||
fn emit_plugin_invoke_by_name(&mut self, method: &str, argc: usize, has_ret: bool) {
|
||||
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||
Self::with_fb(|fb| {
|
||||
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); }
|
||||
});
|
||||
// Ensure we are in a valid block context using the builder's safe switch
|
||||
if let Some(idx) = self.current_block_index { self.switch_to_block(idx); }
|
||||
else if self.entry_block.is_some() { self.switch_to_block(0); }
|
||||
// Collect call args
|
||||
let mut arg_vals: Vec<cranelift_codegen::ir::Value> = {
|
||||
let take_n = argc.min(self.value_stack.len());
|
||||
@ -710,10 +710,12 @@ impl IRBuilder for CraneliftBuilder {
|
||||
}
|
||||
fn switch_to_block(&mut self, index: usize) {
|
||||
if index >= self.blocks.len() { return; }
|
||||
// If already on the target block, avoid re-switching (CLIF panics when switching from an unfilled block)
|
||||
if let Some(cur) = self.current_block_index { if cur == index { return; } }
|
||||
Self::with_fb(|fb| {
|
||||
// If switching away from a non-terminated block, inject jump to keep CFG sane
|
||||
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 { fb.ins().jump(self.blocks[index], &[]); self.cur_needs_term = false; }
|
||||
}
|
||||
fb.switch_to_block(self.blocks[index]);
|
||||
self.current_block_index = Some(index);
|
||||
@ -872,6 +874,7 @@ impl CraneliftBuilder {
|
||||
builder.symbol(c::SYM_ARRAY_LEN_H, nyash_array_len_h as *const u8);
|
||||
builder.symbol(c::SYM_ARRAY_GET_H, nyash_array_get_h as *const u8);
|
||||
builder.symbol(c::SYM_ARRAY_SET_H, nyash_array_set_h as *const u8);
|
||||
builder.symbol(c::SYM_ARRAY_SET_HH, super::super::extern_thunks::nyash_array_set_hh as *const u8);
|
||||
builder.symbol(c::SYM_ARRAY_PUSH_H, nyash_array_push_h as *const u8);
|
||||
builder.symbol(c::SYM_ARRAY_LAST_H, nyash_array_last_h as *const u8);
|
||||
builder.symbol(c::SYM_MAP_SIZE_H, nyash_map_size_h as *const u8);
|
||||
|
||||
@ -696,6 +696,39 @@ impl LowerCore {
|
||||
I::BoxCall { box_val: array, method, args, dst, .. } => {
|
||||
// Prefer ops_ext; if not handled, fall back to legacy path below
|
||||
let trace = std::env::var("NYASH_JIT_TRACE_LOWER").ok().as_deref() == Some("1");
|
||||
// Handle ArrayBox.set with handle-valued value for literal strings
|
||||
if method == "set" && self.box_type_map.get(&array).map(|s| s=="ArrayBox").unwrap_or(false) {
|
||||
// Expect args: [index, value]
|
||||
let argc = 3usize;
|
||||
// Receiver handle: prefer param or local slot; else -1 sentinel
|
||||
if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); }
|
||||
else if let Some(slot) = self.local_index.get(&array).copied() { b.load_local_i64(slot); }
|
||||
else { b.emit_const_i64(-1); }
|
||||
// Index as i64
|
||||
if let Some(idx_v) = args.get(0) {
|
||||
if let Some(iv) = self.known_i64.get(idx_v).copied() { b.emit_const_i64(iv); }
|
||||
else { self.push_value_if_known_or_param(b, idx_v); }
|
||||
} else { b.emit_const_i64(0); }
|
||||
// Value as handle: for String literal, synthesize a handle; else prefer param/local handle
|
||||
if let Some(val_v) = args.get(1) {
|
||||
let mut emitted_val_handle = false;
|
||||
if let Some(s) = self.known_str.get(val_v).cloned() {
|
||||
b.emit_string_handle_from_literal(&s);
|
||||
emitted_val_handle = true;
|
||||
} else if let Some(slot) = self.local_index.get(val_v).copied() {
|
||||
b.load_local_i64(slot);
|
||||
emitted_val_handle = true;
|
||||
} else if let Some(pidx) = self.param_index.get(val_v).copied() {
|
||||
b.emit_param_i64(pidx);
|
||||
emitted_val_handle = true;
|
||||
}
|
||||
if !emitted_val_handle { b.emit_const_i64(0); }
|
||||
} else { b.emit_const_i64(0); }
|
||||
// Emit handle-handle variant hostcall
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_SET_HH, argc, false);
|
||||
if trace { eprintln!("[LOWER] BoxCall(ArrayBox.set) → ARRAY_SET_HH"); }
|
||||
return Ok(());
|
||||
}
|
||||
// Early constant fold: StringBox literal length/len (allow disabling via NYASH_JIT_DISABLE_LEN_CONST=1)
|
||||
if std::env::var("NYASH_JIT_DISABLE_LEN_CONST").ok().as_deref() != Some("1")
|
||||
&& (method == "len" || method == "length")
|
||||
|
||||
@ -241,6 +241,32 @@ pub(super) extern "C" fn nyash_array_set_h(handle: u64, idx: i64, val: i64) -> i
|
||||
0
|
||||
}
|
||||
|
||||
// Array.set where value is a handle (StringBox, IntegerBox, etc.)
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_array_set_hh(handle: u64, idx: i64, val_h: u64) -> i64 {
|
||||
use crate::jit::hostcall_registry::{classify, HostcallKind};
|
||||
let sym = c::SYM_ARRAY_SET_HH;
|
||||
let pol = crate::jit::policy::current();
|
||||
let wh = pol.hostcall_whitelist;
|
||||
if classify(sym) == HostcallKind::Mutating && pol.read_only && !wh.iter().any(|s| s == sym) {
|
||||
events::emit_runtime(serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating"}), "hostcall", "<jit>");
|
||||
return 0;
|
||||
}
|
||||
if let Some(obj) = crate::jit::rt::handles::get(handle) {
|
||||
if let Some(arr) = obj.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
// Convert value handle to Box<dyn NyashBox>
|
||||
if let Some(v_arc) = crate::jit::rt::handles::get(val_h) {
|
||||
// Prefer share semantics for identity boxes
|
||||
let val_box: Box<dyn crate::box_trait::NyashBox> = v_arc.as_ref().clone_or_share();
|
||||
let _ = arr.set(Box::new(crate::box_trait::IntegerBox::new(idx)), val_box);
|
||||
events::emit_runtime(serde_json::json!({"id": sym, "decision":"allow", "argc":3, "arg_types":["Handle","I64","Handle"]}), "hostcall", "<jit>");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_array_push_h(handle: u64, val: i64) -> i64 {
|
||||
use crate::jit::hostcall_registry::{classify, HostcallKind};
|
||||
|
||||
Reference in New Issue
Block a user