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:
19
AGENTS.md
19
AGENTS.md
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user