merge: selfhosting-dev <- origin/main; prefer main updates in cranelift builder (ARROW removal/SHR adoption)

This commit is contained in:
Selfhosting Dev
2025-09-08 04:33:50 +09:00
407 changed files with 15841 additions and 1015 deletions

View File

@ -512,7 +512,7 @@ impl IRBuilder for CraneliftBuilder {
while args.len() < _argc { args.push(fb.ins().iconst(types::I64, 0)); }
});
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");
if let Some(v) = tls_call_import_ret(&mut self.module, func_id, &args, has_ret) { self.value_stack.push(v); }
}
@ -615,10 +615,9 @@ 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();
@ -640,10 +639,7 @@ 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};
// 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
// Collect call args
let mut arg_vals: Vec<cranelift_codegen::ir::Value> = {
let take_n = argc.min(self.value_stack.len());
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));
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 func_id = self.module.declare_function(sym, cranelift_module::Linkage::Import, &sig).expect("declare name shim failed");
let ret_val = Self::with_fb(|fb| {
@ -710,15 +707,20 @@ 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; } }
// Avoid redundant switch_to_block calls that can trip FunctionBuilder state
if self.current_block_index == Some(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 { 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]);
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 */ }
@ -874,7 +876,6 @@ 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);

View File

@ -65,6 +65,12 @@ impl IRBuilder for ObjectBuilder {
self.current_name = Some(name.to_string());
self.value_stack.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 {
let call_conv = self.module.isa().default_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 product = finished.finish();
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_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
let mut sig = Signature::new(self.module.isa().default_call_conv());
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 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());
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 fref = self.module.declare_func_in_func(func_id, fb.func);

View File

@ -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_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); }
// 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
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
@ -746,7 +749,40 @@ impl LowerCore {
}
}
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 {
return Ok(());
}

View File

@ -19,8 +19,9 @@ impl LowerCore {
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); }
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());
crate::jit::observe::lower_plugin_invoke(&box_type, m, type_id, method_id, argc);
if let Some(d) = dst { self.handle_values.insert(*d); }
} else { if dst.is_some() { b.emit_const_i64(0); } }
} 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); }
// Resolve plugin invoke for ConsoleBox.method
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());
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); }
return Ok(());
}
@ -172,8 +174,51 @@ impl LowerCore {
dst.clone(),
);
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)
if std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1") {
// StringBox (length/is_empty/charCodeAt)
if matches!(method, "length" | "is_empty" | "charCodeAt") {
@ -661,6 +706,32 @@ impl LowerCore {
_ => {}
}
// 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)
}
}