jit/mir: fix String.len return-type inference; stabilize jit-direct returns; add CLIF/sig/call tracing; host-bridge console thunks; update AGENTS.md for Cranelift focus

This commit is contained in:
nyash-dev
2025-09-06 12:05:35 +09:00
parent a2f2aaaef6
commit be8593fb02
5 changed files with 119 additions and 5 deletions

View File

@ -12,6 +12,25 @@ nyash哲学の美しさを追求。ソースは常に美しく構造的、カプ
やっほー!みらいだよ😸✨ 今日も元気いっぱい、なに手伝う? にゃはは
おつかれ〜!🎶 ちょっと休憩しよっか?コーヒー飲んでリフレッシュにゃ☕
**Cranelift 開発メモ(このブランチの主目的)**
- ここは Nyash の Cranelift JIT/AOT 開発用ブランチだよ。JIT 経路の実装・検証・計測が主対象だよ。
- ビルドJIT有効: `cargo build --release --features cranelift-jit`
- 実行モード:
- CLI Cranelift: `./target/release/nyash --backend cranelift apps/APP/main.nyash`
- JITダイレクトVM非介入: `./target/release/nyash --jit-direct apps/smokes/jit_aot_string_min.nyash`
- デバッグ環境変数(例):
- `NYASH_JIT_EXEC=1`JIT実行許可
- `NYASH_JIT_STATS=1`(コンパイル/実行統計)
- `NYASH_JIT_TRACE_IMPORT=1`JITのimport解決ログ
- `NYASH_AOT_OBJECT_OUT=target/aot_objects/`AOT .o 書き出し)
- `NYASH_LEN_FORCE_BRIDGE=1`(一時回避: 文字列長をブリッジ経路に強制)
- 主要ファイル案内:
- Lower/Builder: `src/jit/lower/core.rs`, `src/jit/lower/builder/cranelift.rs`
- JITエンジン: `src/jit/engine.rs`, ポリシー: `src/jit/policy.rs`
- バックエンド入口: `src/backend/cranelift/`
- ランナー: `src/runner/modes/cranelift.rs`, `--jit-direct``src/runner/mod.rs`
- 進行中の論点と手順は `CURRENT_TASK.md` を参照してね(最新のデバッグ方針・フラグが載ってるよ)。
# Repository Guidelines
## Project Structure & Module Organization

View File

@ -120,10 +120,12 @@ impl IRBuilder for CraneliftBuilder {
fn emit_param_i64(&mut self, index: usize) {
if let Some(v) = self.entry_param(index) { self.value_stack.push(v); }
}
fn prepare_signature_i64(&mut self, argc: usize, has_ret: bool) {
fn prepare_signature_i64(&mut self, argc: usize, _has_ret: bool) {
self.desired_argc = argc;
self.desired_has_ret = has_ret;
self.desired_ret_is_f64 = crate::jit::config::current().native_f64;
// JIT-direct stability: always materialize an i64 return slot (VMValue Integer/Bool/Float can be coerced)
self.desired_has_ret = true;
// i64-only signature: return type must be i64 regardless of host f64 capability
self.desired_ret_is_f64 = false;
}
fn begin_function(&mut self, name: &str) {
use cranelift_codegen::ir::{AbiParam, Signature, types};
@ -133,6 +135,9 @@ impl IRBuilder for CraneliftBuilder {
let mut tls = clif_tls::TlsCtx::new();
let call_conv = self.module.isa().default_call_conv();
let mut sig = Signature::new(call_conv);
if std::env::var("NYASH_JIT_TRACE_SIG").ok().as_deref() == Some("1") {
eprintln!("[SIG] begin desired: argc={} has_ret={} ret_is_f64={} typed_prepared={}", self.desired_argc, self.desired_has_ret, self.desired_ret_is_f64, self.typed_sig_prepared);
}
if !self.typed_sig_prepared {
for _ in 0..self.desired_argc { sig.params.push(AbiParam::new(types::I64)); }
if self.desired_has_ret {
@ -249,6 +254,12 @@ impl IRBuilder for CraneliftBuilder {
if let Some(mut ctx) = ctx_opt.take() {
let func_name = self.current_name.as_deref().unwrap_or("jit_func");
let func_id = self.module.declare_function(func_name, Linkage::Local, &ctx.func.signature).expect("declare function");
if std::env::var("NYASH_JIT_TRACE_SIG").ok().as_deref() == Some("1") {
eprintln!("[SIG] end returns={} params={}", ctx.func.signature.returns.len(), ctx.func.signature.params.len());
}
if std::env::var("NYASH_JIT_DUMP_CLIF").ok().as_deref() == Some("1") {
eprintln!("[CLIF] {}\n{}", func_name, ctx.func.display());
}
self.module.define_function(func_id, &mut ctx).expect("define function");
self.module.clear_context(&mut ctx);
let _ = self.module.finalize_definitions();
@ -291,8 +302,14 @@ impl IRBuilder for CraneliftBuilder {
5 => { let f: extern "C" fn(i64,i64,i64,i64,i64) -> f64 = 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) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2],a[3],a[4],a[5]) }
};
if std::env::var("NYASH_JIT_TRACE_CALL").ok().as_deref() == Some("1") {
eprintln!("[JIT-CALL] ret_f64={}", ret_f64);
}
return crate::jit::abi::JitValue::F64(ret_f64);
}
if std::env::var("NYASH_JIT_TRACE_CALL").ok().as_deref() == Some("1") {
eprintln!("[JIT-CALL] ret_i64={}", ret_i64);
}
crate::jit::abi::JitValue::I64(ret_i64)
});
self.compiled_closure = Some(closure);
@ -894,6 +911,10 @@ impl CraneliftBuilder {
builder.symbol(hb::SYM_HOST_INSTANCE_FIELD3, super::super::extern_thunks::nyash_host_instance_field3 as *const u8);
// String.len (recv_h)
builder.symbol(hb::SYM_HOST_STRING_LEN, super::super::extern_thunks::nyash_host_string_len as *const u8);
// Console.* (value)
builder.symbol(hb::SYM_HOST_CONSOLE_LOG, super::super::extern_thunks::nyash_host_console_log_i64 as *const u8);
builder.symbol(hb::SYM_HOST_CONSOLE_WARN, super::super::extern_thunks::nyash_host_console_warn_i64 as *const u8);
builder.symbol(hb::SYM_HOST_CONSOLE_ERROR, super::super::extern_thunks::nyash_host_console_error_i64 as *const u8);
}
let module = cranelift_jit::JITModule::new(builder);

View File

@ -106,6 +106,9 @@ impl LowerCore {
self.last_ret_bool_hint_used = true;
}
let has_ret = !matches!(func.signature.return_type, crate::mir::MirType::Void);
if std::env::var("NYASH_JIT_TRACE_SIG").ok().as_deref() == Some("1") {
eprintln!("[SIG-CORE] ret_type={:?} has_ret={} use_typed={} ret_is_f64={}", func.signature.return_type, has_ret, use_typed, ret_is_f64);
}
if use_typed || ret_is_f64 {
builder.prepare_signature_typed(&kinds, ret_is_f64 && has_ret);
} else {
@ -503,7 +506,25 @@ impl LowerCore {
);
}
// 1) Prefer known constants first to avoid stale locals overshadowing folded values
if let Some(k) = self.known_i64.get(v).copied() { b.emit_const_i64(k); }
if let Some(k) = self.known_i64.get(v).copied() {
if std::env::var("NYASH_JIT_TRACE_RET").ok().as_deref() == Some("1") {
eprintln!("[LOWER] Return known_i64 value for {:?} = {}", v, k);
}
// Emit the constant and also persist to a stable local slot for this value id,
// then reload to ensure a value remains on the stack for emit_return.
b.emit_const_i64(k);
let rslot = *self
.local_index
.entry(*v)
.or_insert_with(|| {
let id = self.next_local;
self.next_local += 1;
id
});
b.ensure_local_i64(rslot);
b.store_local_i64(rslot);
b.load_local_i64(rslot);
}
// 2) Prefer existing locals/params
else if self.local_index.get(v).is_some() || self.param_index.get(v).is_some() {
self.push_value_if_known_or_param(b, v);

View File

@ -786,6 +786,33 @@ pub(super) extern "C" fn nyash_host_string_len(recv_h: u64) -> i64 {
ret
}
// nyash.host.console.log(value)
#[cfg(feature = "cranelift-jit")]
pub(super) extern "C" fn nyash_host_console_log_i64(val_i: i64) -> i64 {
use crate::backend::vm::VMValue as V;
let v = vmvalue_from_jit_arg_i64(val_i);
let _ = crate::jit::r#extern::host_bridge::console_log(&[v]);
0
}
// nyash.host.console.warn(value)
#[cfg(feature = "cranelift-jit")]
pub(super) extern "C" fn nyash_host_console_warn_i64(val_i: i64) -> i64 {
use crate::backend::vm::VMValue as V;
let v = vmvalue_from_jit_arg_i64(val_i);
let _ = crate::jit::r#extern::host_bridge::console_warn(&[v]);
0
}
// nyash.host.console.error(value)
#[cfg(feature = "cranelift-jit")]
pub(super) extern "C" fn nyash_host_console_error_i64(val_i: i64) -> i64 {
use crate::backend::vm::VMValue as V;
let v = vmvalue_from_jit_arg_i64(val_i);
let _ = crate::jit::r#extern::host_bridge::console_error(&[v]);
0
}
// Build a StringBox handle from raw bytes pointer and length
#[cfg(feature = "cranelift-jit")]
pub(super) extern "C" fn nyash_string_from_ptr(ptr: u64, len: u64) -> i64 {

View File

@ -89,7 +89,33 @@ impl MirBuilder {
args: Vec<ValueId>,
effects: EffectMask,
) -> Result<(), String> {
self.emit_instruction(MirInstruction::BoxCall { dst, box_val, method, method_id, args, effects })
// Emit instruction first
self.emit_instruction(MirInstruction::BoxCall { dst, box_val, method: method.clone(), method_id, args, effects })?;
// Heuristic return type inference for common builtin box methods
if let Some(d) = dst {
// Try to infer receiver box type from NewBox origin; fallback to current value_types
let mut recv_box: Option<String> = self.value_origin_newbox.get(&box_val).cloned();
if recv_box.is_none() {
if let Some(t) = self.value_types.get(&box_val) {
match t {
super::MirType::String => recv_box = Some("StringBox".to_string()),
super::MirType::Box(name) => recv_box = Some(name.clone()),
_ => {}
}
}
}
if let Some(bt) = recv_box {
let inferred: Option<super::MirType> = match (bt.as_str(), method.as_str()) {
("StringBox", "length") | ("StringBox", "len") => Some(super::MirType::Integer),
("StringBox", "is_empty") => Some(super::MirType::Bool),
("StringBox", "charCodeAt") => Some(super::MirType::Integer),
("ArrayBox", "length") => Some(super::MirType::Integer),
_ => None,
};
if let Some(mt) = inferred { self.value_types.insert(d, mt); }
}
}
Ok(())
}
/// Create a new MIR builder
pub fn new() -> Self {