Phase 10.7 - JIT統計とイベント機能の完成
主要な実装: - PHI(b1)統計追跡: phi_total_slots/phi_b1_slotsをJSON出力 - 関数単位統計API: JitStatsBox.perFunction()で詳細統計取得 - JITイベントシステム: compile/execute/fallback/trapをJSONL形式で記録 - Store/Load命令対応: ローカル変数を含む関数のJIT実行が可能に 新しいBox: - JitStatsBox: JIT統計の取得 - JitConfigBox: JIT設定の管理(将来用) - JitEventsBox: イベントのJSONL出力(将来用) - JitPolicyBox: 実行ポリシー管理(将来用) CLI拡張: - --jit-exec, --jit-stats, --jit-dump等のフラグ追加 - --jit-directモードでの独立JIT実行 - NYASH_JIT_*環境変数によるきめ細かい制御 ドキュメント: - Phase 10.7実装計画の詳細化 - Phase 10.9 (ビルトインBox JIT) の計画追加 - JIT統計JSONスキーマ v1の仕様化 ChatGPT5との共同開発により、JIT基盤が大幅に強化されました。 次はPhase 10.9でビルトインBoxのJIT対応を進め、 Python統合(Phase 10.1)への道を開きます。 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -9,11 +9,16 @@ pub enum BinOpKind { Add, Sub, Mul, Div, Mod }
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum CmpKind { Eq, Ne, Lt, Le, Gt, Ge }
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ParamKind { I64, F64, B1 }
|
||||
|
||||
pub trait IRBuilder {
|
||||
fn begin_function(&mut self, name: &str);
|
||||
fn end_function(&mut self);
|
||||
/// Optional: prepare a simple `i64` ABI signature with `argc` params
|
||||
fn prepare_signature_i64(&mut self, _argc: usize, _has_ret: bool) { }
|
||||
/// Optional: prepare typed ABI signature for params and f64 return flag
|
||||
fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) { }
|
||||
/// Load i64 parameter at index and push to value stack (Core-1 path)
|
||||
fn emit_param_i64(&mut self, _index: usize) { }
|
||||
fn emit_const_i64(&mut self, _val: i64);
|
||||
@ -36,10 +41,18 @@ pub trait IRBuilder {
|
||||
fn br_if_top_is_true(&mut self, _then_index: usize, _else_index: usize) { }
|
||||
/// Optional: unconditional jump to target block index
|
||||
fn jump_to(&mut self, _target_index: usize) { }
|
||||
/// Optional: ensure target block has one i64 block param (for minimal PHI)
|
||||
fn ensure_block_param_i64(&mut self, _index: usize) { }
|
||||
/// Optional: push current block's first param (i64) onto the value stack
|
||||
fn push_block_param_i64(&mut self) { }
|
||||
/// Optional: ensure target block has N i64 block params (for PHI)
|
||||
fn ensure_block_params_i64(&mut self, _index: usize, _count: usize) { }
|
||||
/// Optional: ensure target block has N b1 block params (for PHI of bool)
|
||||
fn ensure_block_params_b1(&mut self, index: usize, count: usize) { self.ensure_block_params_i64(index, count); }
|
||||
/// Optional: ensure target block has one i64 block param (backward compat)
|
||||
fn ensure_block_param_i64(&mut self, index: usize) { self.ensure_block_params_i64(index, 1); }
|
||||
/// Optional: push current block's param at position onto the value stack (default=0)
|
||||
fn push_block_param_i64_at(&mut self, _pos: usize) { }
|
||||
/// Optional: push current block's boolean param (b1) at position; default converts i64 0/1 → b1
|
||||
fn push_block_param_b1_at(&mut self, _pos: usize) { self.push_block_param_i64_at(_pos); }
|
||||
/// Optional: push current block's first param (i64) onto the value stack (backward compat)
|
||||
fn push_block_param_i64(&mut self) { self.push_block_param_i64_at(0); }
|
||||
/// Optional: conditional branch with explicit arg counts for then/else; pops args from stack
|
||||
fn br_if_with_args(&mut self, _then_index: usize, _else_index: usize, _then_n: usize, _else_n: usize) {
|
||||
// fallback to no-arg br_if
|
||||
@ -47,6 +60,16 @@ pub trait IRBuilder {
|
||||
}
|
||||
/// Optional: jump with explicit arg count; pops args from stack
|
||||
fn jump_with_args(&mut self, _target_index: usize, _n: usize) { self.jump_to(_target_index); }
|
||||
/// Optional: hint that function returns a boolean (b1) value (footing only)
|
||||
fn hint_ret_bool(&mut self, _is_b1: bool) { }
|
||||
|
||||
// ==== Minimal local slots for Load/Store (i64 only) ====
|
||||
/// Ensure an i64 local slot exists for the given index
|
||||
fn ensure_local_i64(&mut self, _index: usize) { }
|
||||
/// Store top-of-stack (normalized to i64) into local slot
|
||||
fn store_local_i64(&mut self, _index: usize) { }
|
||||
/// Load i64 from local slot and push to stack
|
||||
fn load_local_i64(&mut self, _index: usize) { }
|
||||
}
|
||||
|
||||
pub struct NoopBuilder {
|
||||
@ -72,6 +95,9 @@ impl IRBuilder for NoopBuilder {
|
||||
fn emit_jump(&mut self) { self.branches += 1; }
|
||||
fn emit_branch(&mut self) { self.branches += 1; }
|
||||
fn emit_return(&mut self) { self.rets += 1; }
|
||||
fn ensure_local_i64(&mut self, _index: usize) { /* no-op */ }
|
||||
fn store_local_i64(&mut self, _index: usize) { self.consts += 1; }
|
||||
fn load_local_i64(&mut self, _index: usize) { self.consts += 1; }
|
||||
}
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
@ -88,11 +114,17 @@ pub struct CraneliftBuilder {
|
||||
blocks: Vec<cranelift_codegen::ir::Block>,
|
||||
current_block_index: Option<usize>,
|
||||
block_param_counts: std::collections::HashMap<usize, usize>,
|
||||
// Local stack slots for minimal Load/Store lowering (i64 only)
|
||||
local_slots: std::collections::HashMap<usize, cranelift_codegen::ir::StackSlot>,
|
||||
// Finalized function pointer (if any)
|
||||
compiled_closure: Option<std::sync::Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>>,
|
||||
compiled_closure: Option<std::sync::Arc<dyn Fn(&[crate::jit::abi::JitValue]) -> crate::jit::abi::JitValue + Send + Sync>>,
|
||||
// Desired simple ABI (Phase 10_c minimal): i64 params count and i64 return
|
||||
desired_argc: usize,
|
||||
desired_has_ret: bool,
|
||||
desired_ret_is_f64: bool,
|
||||
typed_sig_prepared: bool,
|
||||
// Return-type hint: function returns boolean (footing only; ABI remains i64 for now)
|
||||
ret_hint_is_b1: bool,
|
||||
}
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
@ -106,7 +138,7 @@ extern "C" fn nyash_host_stub0() -> i64 { 0 }
|
||||
extern "C" fn nyash_array_len(arr_param_index: i64) -> i64 {
|
||||
// Interpret first arg as function param index and fetch from thread-local args
|
||||
if arr_param_index < 0 { return 0; }
|
||||
crate::jit::rt::with_args(|args| {
|
||||
crate::jit::rt::with_legacy_vm_args(|args| {
|
||||
let idx = arr_param_index as usize;
|
||||
if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) {
|
||||
if let Some(ab) = b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
@ -121,7 +153,7 @@ extern "C" fn nyash_array_len(arr_param_index: i64) -> i64 {
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_array_push(arr_param_index: i64, val: i64) -> i64 {
|
||||
if arr_param_index < 0 { return 0; }
|
||||
crate::jit::rt::with_args(|args| {
|
||||
crate::jit::rt::with_legacy_vm_args(|args| {
|
||||
let idx = arr_param_index as usize;
|
||||
if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) {
|
||||
if let Some(ab) = b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
@ -137,7 +169,7 @@ extern "C" fn nyash_array_push(arr_param_index: i64, val: i64) -> i64 {
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_array_get(arr_param_index: i64, idx: i64) -> i64 {
|
||||
if arr_param_index < 0 { return 0; }
|
||||
crate::jit::rt::with_args(|args| {
|
||||
crate::jit::rt::with_legacy_vm_args(|args| {
|
||||
let pidx = arr_param_index as usize;
|
||||
if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(pidx) {
|
||||
if let Some(ab) = b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
@ -153,7 +185,7 @@ extern "C" fn nyash_array_get(arr_param_index: i64, idx: i64) -> i64 {
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_array_set(arr_param_index: i64, idx: i64, val: i64) -> i64 {
|
||||
if arr_param_index < 0 { return 0; }
|
||||
crate::jit::rt::with_args(|args| {
|
||||
crate::jit::rt::with_legacy_vm_args(|args| {
|
||||
let pidx = arr_param_index as usize;
|
||||
if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(pidx) {
|
||||
if let Some(ab) = b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
@ -174,7 +206,7 @@ extern "C" fn nyash_map_set(_map: u64, _key: i64, _val: i64) -> i64 { 0 }
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_map_size(map_param_index: i64) -> i64 {
|
||||
if map_param_index < 0 { return 0; }
|
||||
crate::jit::rt::with_args(|args| {
|
||||
crate::jit::rt::with_legacy_vm_args(|args| {
|
||||
let idx = map_param_index as usize;
|
||||
if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) {
|
||||
if let Some(mb) = b.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
|
||||
@ -187,8 +219,234 @@ extern "C" fn nyash_map_size(map_param_index: i64) -> i64 {
|
||||
})
|
||||
}
|
||||
|
||||
// === Handle-based externs (10.7c) ===
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_array_len_h(handle: u64) -> i64 {
|
||||
if let Some(obj) = crate::jit::rt::handles::get(handle) {
|
||||
if let Some(arr) = obj.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
if let Some(ib) = arr.length().as_any().downcast_ref::<crate::box_trait::IntegerBox>() { return ib.value; }
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_array_push_h(handle: u64, val: i64) -> i64 {
|
||||
// Policy/Events: classify and decide
|
||||
use crate::jit::hostcall_registry::{classify, HostcallKind};
|
||||
let sym = crate::jit::r#extern::collections::SYM_ARRAY_PUSH_H;
|
||||
match (classify(sym), crate::jit::policy::current().read_only) {
|
||||
(HostcallKind::Mutating, true) => {
|
||||
crate::jit::events::emit("hostcall", "<jit>", None, None, serde_json::json!({"id": sym, "decision":"fallback"}));
|
||||
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>() {
|
||||
let ib = crate::box_trait::IntegerBox::new(val);
|
||||
let _ = arr.push(Box::new(ib));
|
||||
crate::jit::events::emit("hostcall", "<jit>", None, None, serde_json::json!({"id": sym, "decision":"allow"}));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_array_get_h(handle: u64, idx: i64) -> i64 {
|
||||
if let Some(obj) = crate::jit::rt::handles::get(handle) {
|
||||
if let Some(arr) = obj.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
let val = arr.get(Box::new(crate::box_trait::IntegerBox::new(idx)));
|
||||
if let Some(ib) = val.as_any().downcast_ref::<crate::box_trait::IntegerBox>() { return ib.value; }
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_array_last_h(handle: u64) -> i64 {
|
||||
if let Some(obj) = crate::jit::rt::handles::get(handle) {
|
||||
if let Some(arr) = obj.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
// Return last element as i64 if IntegerBox, else 0
|
||||
if let Ok(items) = arr.items.read() {
|
||||
if let Some(last) = items.last() {
|
||||
if let Some(ib) = last.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
|
||||
return ib.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_array_set_h(handle: u64, idx: i64, val: i64) -> i64 {
|
||||
use crate::jit::hostcall_registry::{classify, HostcallKind};
|
||||
let sym = crate::jit::r#extern::collections::SYM_ARRAY_SET_H;
|
||||
if classify(sym) == HostcallKind::Mutating && crate::jit::policy::current().read_only {
|
||||
crate::jit::events::emit("hostcall", "<jit>", None, None, serde_json::json!({"id": sym, "decision":"fallback"}));
|
||||
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>() {
|
||||
let _ = arr.set(
|
||||
Box::new(crate::box_trait::IntegerBox::new(idx)),
|
||||
Box::new(crate::box_trait::IntegerBox::new(val)),
|
||||
);
|
||||
crate::jit::events::emit("hostcall", "<jit>", None, None, serde_json::json!({"id": sym, "decision":"allow"}));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_map_size_h(handle: u64) -> i64 {
|
||||
crate::jit::events::emit("hostcall", "<jit>", None, None, serde_json::json!({"id": crate::jit::r#extern::collections::SYM_MAP_SIZE_H, "decision":"allow"}));
|
||||
if let Some(obj) = crate::jit::rt::handles::get(handle) {
|
||||
if let Some(map) = obj.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
|
||||
if let Some(ib) = map.size().as_any().downcast_ref::<crate::box_trait::IntegerBox>() { return ib.value; }
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_map_get_h(handle: u64, key: i64) -> i64 {
|
||||
crate::jit::events::emit("hostcall", "<jit>", None, None, serde_json::json!({"id": crate::jit::r#extern::collections::SYM_MAP_GET_H, "decision":"allow"}));
|
||||
if let Some(obj) = crate::jit::rt::handles::get(handle) {
|
||||
if let Some(map) = obj.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
|
||||
let key_box = Box::new(crate::box_trait::IntegerBox::new(key));
|
||||
let val = map.get(key_box);
|
||||
if let Some(ib) = val.as_any().downcast_ref::<crate::box_trait::IntegerBox>() { return ib.value; }
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_map_set_h(handle: u64, key: i64, val: i64) -> i64 {
|
||||
use crate::jit::hostcall_registry::{classify, HostcallKind};
|
||||
let sym = crate::jit::r#extern::collections::SYM_MAP_SET_H;
|
||||
if classify(sym) == HostcallKind::Mutating && crate::jit::policy::current().read_only {
|
||||
crate::jit::events::emit("hostcall", "<jit>", None, None, serde_json::json!({"id": sym, "decision":"fallback"}));
|
||||
return 0;
|
||||
}
|
||||
if let Some(obj) = crate::jit::rt::handles::get(handle) {
|
||||
if let Some(map) = obj.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
|
||||
let key_box = Box::new(crate::box_trait::IntegerBox::new(key));
|
||||
let val_box = Box::new(crate::box_trait::IntegerBox::new(val));
|
||||
let _ = map.set(key_box, val_box);
|
||||
crate::jit::events::emit("hostcall", "<jit>", None, None, serde_json::json!({"id": sym, "decision":"allow"}));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_map_has_h(handle: u64, key: i64) -> i64 {
|
||||
if let Some(obj) = crate::jit::rt::handles::get(handle) {
|
||||
if let Some(map) = obj.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
|
||||
let key_box = Box::new(crate::box_trait::IntegerBox::new(key));
|
||||
let val = map.get(key_box);
|
||||
// Treat presence if result is not Void
|
||||
let is_present = !val.as_any().is::<crate::box_trait::VoidBox>();
|
||||
return if is_present { 1 } else { 0 };
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_any_length_h(handle: u64) -> i64 {
|
||||
crate::jit::events::emit("hostcall", "<jit>", None, None, serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_LEN_H, "decision":"allow"}));
|
||||
if let Some(obj) = crate::jit::rt::handles::get(handle) {
|
||||
// Array length
|
||||
if let Some(arr) = obj.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
if let Some(ib) = arr.length().as_any().downcast_ref::<crate::box_trait::IntegerBox>() { return ib.value; }
|
||||
}
|
||||
// String length
|
||||
if let Some(sb) = obj.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||
return sb.value.len() as i64;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_any_is_empty_h(handle: u64) -> i64 {
|
||||
crate::jit::events::emit("hostcall", "<jit>", None, None, serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, "decision":"allow"}));
|
||||
if let Some(obj) = crate::jit::rt::handles::get(handle) {
|
||||
// Array empty?
|
||||
if let Some(arr) = obj.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
if let Ok(items) = arr.items.read() { return if items.is_empty() { 1 } else { 0 }; }
|
||||
}
|
||||
// String empty?
|
||||
if let Some(sb) = obj.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||
return if sb.value.is_empty() { 1 } else { 0 };
|
||||
}
|
||||
// Map empty?
|
||||
if let Some(map) = obj.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
|
||||
if let Some(ib) = map.size().as_any().downcast_ref::<crate::box_trait::IntegerBox>() { return if ib.value == 0 { 1 } else { 0 }; }
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_string_charcode_at_h(handle: u64, idx: i64) -> i64 {
|
||||
crate::jit::events::emit("hostcall", "<jit>", None, None, serde_json::json!({"id": crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, "decision":"allow"}));
|
||||
if idx < 0 { return -1; }
|
||||
if let Some(obj) = crate::jit::rt::handles::get(handle) {
|
||||
if let Some(sb) = obj.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||
let s = &sb.value;
|
||||
let i = idx as usize;
|
||||
if i < s.len() {
|
||||
// Return UTF-8 byte at index (ASCII-friendly PoC)
|
||||
return s.as_bytes()[i] as i64;
|
||||
} else { return -1; }
|
||||
}
|
||||
}
|
||||
-1
|
||||
}
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
impl IRBuilder for CraneliftBuilder {
|
||||
fn prepare_signature_typed(&mut self, params: &[ParamKind], ret_is_f64: bool) {
|
||||
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||
fn abi_param_for_kind(k: ParamKind, cfg: &crate::jit::config::JitConfig) -> cranelift_codegen::ir::AbiParam {
|
||||
use cranelift_codegen::ir::types;
|
||||
match k {
|
||||
ParamKind::I64 => cranelift_codegen::ir::AbiParam::new(types::I64),
|
||||
ParamKind::F64 => cranelift_codegen::ir::AbiParam::new(types::F64),
|
||||
ParamKind::B1 => {
|
||||
let _ = cfg.native_bool_abi;
|
||||
#[cfg(feature = "jit-b1-abi")]
|
||||
{
|
||||
if crate::jit::config::probe_capabilities().supports_b1_sig && cfg.native_bool_abi { return cranelift_codegen::ir::AbiParam::new(types::B1); }
|
||||
}
|
||||
cranelift_codegen::ir::AbiParam::new(types::I64)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.desired_argc = params.len();
|
||||
self.desired_has_ret = true;
|
||||
self.desired_ret_is_f64 = ret_is_f64;
|
||||
let call_conv = self.module.isa().default_call_conv();
|
||||
let mut sig = Signature::new(call_conv);
|
||||
let cfg_now = crate::jit::config::current();
|
||||
for &k in params { sig.params.push(abi_param_for_kind(k, &cfg_now)); }
|
||||
if self.desired_has_ret {
|
||||
// Decide return ABI: prefer F64 if requested; otherwise Bool may use B1 when supported
|
||||
if self.desired_ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); }
|
||||
else {
|
||||
let mut used_b1 = false;
|
||||
#[cfg(feature = "jit-b1-abi")]
|
||||
{
|
||||
let cfg_now = crate::jit::config::current();
|
||||
if crate::jit::config::probe_capabilities().supports_b1_sig && cfg_now.native_bool_abi && self.ret_hint_is_b1 {
|
||||
sig.returns.push(AbiParam::new(types::B1));
|
||||
used_b1 = true;
|
||||
}
|
||||
}
|
||||
if !used_b1 { sig.returns.push(AbiParam::new(types::I64)); }
|
||||
}
|
||||
}
|
||||
self.ctx.func.signature = sig;
|
||||
self.typed_sig_prepared = true;
|
||||
}
|
||||
fn emit_param_i64(&mut self, index: usize) {
|
||||
if let Some(v) = self.entry_param(index) {
|
||||
self.value_stack.push(v);
|
||||
@ -197,6 +455,7 @@ impl IRBuilder for CraneliftBuilder {
|
||||
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;
|
||||
}
|
||||
fn begin_function(&mut self, name: &str) {
|
||||
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||
@ -204,14 +463,31 @@ impl IRBuilder for CraneliftBuilder {
|
||||
|
||||
self.current_name = Some(name.to_string());
|
||||
self.value_stack.clear();
|
||||
// Keep any pre-created blocks (from prepare_blocks)
|
||||
// Keep any pre-created blocks (from prepare_blocks or typed signature)
|
||||
|
||||
// Minimal signature: (i64 x argc) -> i64? (Core-1 integer path)
|
||||
let call_conv = self.module.isa().default_call_conv();
|
||||
let mut sig = Signature::new(call_conv);
|
||||
for _ in 0..self.desired_argc { sig.params.push(AbiParam::new(types::I64)); }
|
||||
if self.desired_has_ret { sig.returns.push(AbiParam::new(types::I64)); }
|
||||
self.ctx.func.signature = sig;
|
||||
// Build default signature only if a typed one wasn't prepared
|
||||
if !self.typed_sig_prepared {
|
||||
// Minimal signature: (i64 x argc) -> i64? (Core-1 integer path)
|
||||
let call_conv = self.module.isa().default_call_conv();
|
||||
let mut sig = Signature::new(call_conv);
|
||||
for _ in 0..self.desired_argc { sig.params.push(AbiParam::new(types::I64)); }
|
||||
if self.desired_has_ret {
|
||||
if self.desired_ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); }
|
||||
else {
|
||||
let mut used_b1 = false;
|
||||
#[cfg(feature = "jit-b1-abi")]
|
||||
{
|
||||
let cfg_now = crate::jit::config::current();
|
||||
if crate::jit::config::probe_capabilities().supports_b1_sig && cfg_now.native_bool_abi && self.ret_hint_is_b1 {
|
||||
sig.returns.push(AbiParam::new(types::B1));
|
||||
used_b1 = true;
|
||||
}
|
||||
}
|
||||
if !used_b1 { sig.returns.push(AbiParam::new(types::I64)); }
|
||||
}
|
||||
}
|
||||
self.ctx.func.signature = sig;
|
||||
}
|
||||
self.ctx.func.name = cranelift_codegen::ir::UserFuncName::user(0, 0);
|
||||
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
@ -253,15 +529,69 @@ impl IRBuilder for CraneliftBuilder {
|
||||
// Get finalized code pointer and wrap into a safe closure
|
||||
let code = self.module.get_finalized_function(func_id);
|
||||
|
||||
// SAFETY: We compiled a function with simple i64 ABI; we still call without args for now
|
||||
// SAFETY: We compiled a function with simple (i64 x N) -> i64/f64 というABIだよ。
|
||||
// ランタイムでは JitValue から i64 へ正規化して、引数個数に応じた関数型にtransmuteして呼び出すにゃ。
|
||||
let argc = self.desired_argc;
|
||||
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;
|
||||
unsafe {
|
||||
let f: extern "C" fn() -> i64 = std::mem::transmute(code);
|
||||
let closure = std::sync::Arc::new(move |_args: &[crate::backend::vm::VMValue]| -> crate::backend::vm::VMValue {
|
||||
let v = f();
|
||||
crate::backend::vm::VMValue::Integer(v)
|
||||
let closure = std::sync::Arc::new(move |args: &[crate::jit::abi::JitValue]| -> crate::jit::abi::JitValue {
|
||||
// 正規化: 足りなければ0で埋め、余分は切り捨て
|
||||
let mut a: [i64; 6] = [0; 6];
|
||||
let take = core::cmp::min(core::cmp::min(argc, 6), args.len());
|
||||
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()
|
||||
}
|
||||
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])
|
||||
}
|
||||
};
|
||||
if ret_is_f64 {
|
||||
let ret_f64 = match argc {
|
||||
0 => { let f: extern "C" fn() -> f64 = std::mem::transmute(code_usize); f() }
|
||||
1 => { let f: extern "C" fn(i64) -> f64 = std::mem::transmute(code_usize); f(a[0]) }
|
||||
2 => { let f: extern "C" fn(i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1]) }
|
||||
3 => { let f: extern "C" fn(i64,i64,i64) -> f64 = std::mem::transmute(code_usize); f(a[0],a[1],a[2]) }
|
||||
4 => { let f: extern "C" fn(i64,i64,i64,i64) -> f64 = 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) -> 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]) }
|
||||
};
|
||||
return crate::jit::abi::JitValue::F64(ret_f64);
|
||||
}
|
||||
crate::jit::abi::JitValue::I64(ret_i64)
|
||||
});
|
||||
self.compiled_closure = Some(closure);
|
||||
}
|
||||
// Reset typed signature flag for next function
|
||||
self.typed_sig_prepared = false;
|
||||
}
|
||||
|
||||
fn emit_const_i64(&mut self, val: i64) {
|
||||
@ -277,22 +607,56 @@ impl IRBuilder for CraneliftBuilder {
|
||||
fb.finalize();
|
||||
}
|
||||
|
||||
fn emit_const_f64(&mut self, _val: f64) { self.stats.0 += 1; /* not yet supported in Core-1 */ }
|
||||
|
||||
fn emit_binop(&mut self, op: BinOpKind) {
|
||||
fn emit_const_f64(&mut self, val: f64) {
|
||||
self.stats.0 += 1;
|
||||
if !crate::jit::config::current().native_f64 { return; }
|
||||
use cranelift_codegen::ir::types;
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
if self.value_stack.len() < 2 { return; }
|
||||
let rhs = self.value_stack.pop().unwrap();
|
||||
let lhs = self.value_stack.pop().unwrap();
|
||||
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); }
|
||||
let res = match op {
|
||||
BinOpKind::Add => fb.ins().iadd(lhs, rhs),
|
||||
BinOpKind::Sub => fb.ins().isub(lhs, rhs),
|
||||
BinOpKind::Mul => fb.ins().imul(lhs, rhs),
|
||||
BinOpKind::Div => fb.ins().sdiv(lhs, rhs),
|
||||
BinOpKind::Mod => fb.ins().srem(lhs, rhs),
|
||||
let v = fb.ins().f64const(val);
|
||||
self.value_stack.push(v);
|
||||
fb.finalize();
|
||||
}
|
||||
|
||||
fn emit_binop(&mut self, op: BinOpKind) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use cranelift_codegen::ir::types;
|
||||
if self.value_stack.len() < 2 { return; }
|
||||
let mut rhs = self.value_stack.pop().unwrap();
|
||||
let mut lhs = self.value_stack.pop().unwrap();
|
||||
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); }
|
||||
// Choose op by operand type (I64 vs F64). If mixed and native_f64, promote to F64.
|
||||
let lty = fb.func.dfg.value_type(lhs);
|
||||
let rty = fb.func.dfg.value_type(rhs);
|
||||
let native_f64 = crate::jit::config::current().native_f64;
|
||||
let mut use_f64 = native_f64 && (lty == types::F64 || rty == types::F64);
|
||||
if use_f64 {
|
||||
if lty != types::F64 { lhs = fb.ins().fcvt_from_sint(types::F64, lhs); }
|
||||
if rty != types::F64 { rhs = fb.ins().fcvt_from_sint(types::F64, rhs); }
|
||||
}
|
||||
let res = if use_f64 {
|
||||
match op {
|
||||
BinOpKind::Add => fb.ins().fadd(lhs, rhs),
|
||||
BinOpKind::Sub => fb.ins().fsub(lhs, rhs),
|
||||
BinOpKind::Mul => fb.ins().fmul(lhs, rhs),
|
||||
BinOpKind::Div => fb.ins().fdiv(lhs, rhs),
|
||||
BinOpKind::Mod => {
|
||||
// Minimal path: produce 0.0 (fmod未実装)。将来はホストコール/Libcallに切替
|
||||
fb.ins().f64const(0.0)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match op {
|
||||
BinOpKind::Add => fb.ins().iadd(lhs, rhs),
|
||||
BinOpKind::Sub => fb.ins().isub(lhs, rhs),
|
||||
BinOpKind::Mul => fb.ins().imul(lhs, rhs),
|
||||
BinOpKind::Div => fb.ins().sdiv(lhs, rhs),
|
||||
BinOpKind::Mod => fb.ins().srem(lhs, rhs),
|
||||
}
|
||||
};
|
||||
self.value_stack.push(res);
|
||||
self.stats.1 += 1;
|
||||
@ -300,24 +664,42 @@ impl IRBuilder for CraneliftBuilder {
|
||||
}
|
||||
|
||||
fn emit_compare(&mut self, op: CmpKind) {
|
||||
use cranelift_codegen::ir::{condcodes::IntCC};
|
||||
use cranelift_codegen::ir::{condcodes::{IntCC, FloatCC}, types};
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
if self.value_stack.len() < 2 { return; }
|
||||
let rhs = self.value_stack.pop().unwrap();
|
||||
let lhs = self.value_stack.pop().unwrap();
|
||||
let mut rhs = self.value_stack.pop().unwrap();
|
||||
let mut lhs = self.value_stack.pop().unwrap();
|
||||
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); }
|
||||
let cc = match op {
|
||||
CmpKind::Eq => IntCC::Equal,
|
||||
CmpKind::Ne => IntCC::NotEqual,
|
||||
CmpKind::Lt => IntCC::SignedLessThan,
|
||||
CmpKind::Le => IntCC::SignedLessThanOrEqual,
|
||||
CmpKind::Gt => IntCC::SignedGreaterThan,
|
||||
CmpKind::Ge => IntCC::SignedGreaterThanOrEqual,
|
||||
let lty = fb.func.dfg.value_type(lhs);
|
||||
let rty = fb.func.dfg.value_type(rhs);
|
||||
let native_f64 = crate::jit::config::current().native_f64;
|
||||
let use_f64 = native_f64 && (lty == types::F64 || rty == types::F64);
|
||||
let b1 = if use_f64 {
|
||||
if lty != types::F64 { lhs = fb.ins().fcvt_from_sint(types::F64, lhs); }
|
||||
if rty != types::F64 { rhs = fb.ins().fcvt_from_sint(types::F64, rhs); }
|
||||
let cc = match op {
|
||||
CmpKind::Eq => FloatCC::Equal,
|
||||
CmpKind::Ne => FloatCC::NotEqual,
|
||||
CmpKind::Lt => FloatCC::LessThan,
|
||||
CmpKind::Le => FloatCC::LessThanOrEqual,
|
||||
CmpKind::Gt => FloatCC::GreaterThan,
|
||||
CmpKind::Ge => FloatCC::GreaterThanOrEqual,
|
||||
};
|
||||
fb.ins().fcmp(cc, lhs, rhs)
|
||||
} else {
|
||||
let cc = match op {
|
||||
CmpKind::Eq => IntCC::Equal,
|
||||
CmpKind::Ne => IntCC::NotEqual,
|
||||
CmpKind::Lt => IntCC::SignedLessThan,
|
||||
CmpKind::Le => IntCC::SignedLessThanOrEqual,
|
||||
CmpKind::Gt => IntCC::SignedGreaterThan,
|
||||
CmpKind::Ge => IntCC::SignedGreaterThanOrEqual,
|
||||
};
|
||||
fb.ins().icmp(cc, lhs, rhs)
|
||||
};
|
||||
let b1 = fb.ins().icmp(cc, lhs, rhs);
|
||||
// Keep b1 on the stack; users (branch) can consume directly, arithmetic should not assume compare value as i64
|
||||
// Keep b1 on the stack; users (branch) can consume directly
|
||||
self.value_stack.push(b1);
|
||||
self.stats.2 += 1;
|
||||
fb.finalize();
|
||||
@ -331,13 +713,44 @@ 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 let Some(v) = self.value_stack.pop() {
|
||||
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);
|
||||
let v_ty = fb.func.dfg.value_type(v);
|
||||
if v_ty != ret_ty {
|
||||
use cranelift_codegen::ir::types;
|
||||
if ret_ty == types::F64 && v_ty == types::I64 {
|
||||
v = fb.ins().fcvt_from_sint(types::F64, v);
|
||||
} else if ret_ty == types::I64 && v_ty == types::F64 {
|
||||
v = fb.ins().fcvt_to_sint(types::I64, v);
|
||||
} else if ret_ty == types::I64 {
|
||||
// If returning i64 but we currently have a boolean, normalize via select(b1,1,0)
|
||||
use cranelift_codegen::ir::types;
|
||||
let one = fb.ins().iconst(types::I64, 1);
|
||||
let zero = fb.ins().iconst(types::I64, 0);
|
||||
v = fb.ins().select(v, one, zero);
|
||||
}
|
||||
#[cfg(feature = "jit-b1-abi")]
|
||||
{
|
||||
use cranelift_codegen::ir::types;
|
||||
if ret_ty == types::B1 && v_ty == types::I64 {
|
||||
use cranelift_codegen::ir::condcodes::IntCC;
|
||||
v = fb.ins().icmp_imm(IntCC::NotEqual, v, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
fb.ins().return_(&[v]);
|
||||
} else {
|
||||
// Return 0 if empty stack (defensive)
|
||||
use cranelift_codegen::ir::types;
|
||||
let zero = fb.ins().iconst(types::I64, 0);
|
||||
fb.ins().return_(&[zero]);
|
||||
let ret_ty = fb.func.signature.returns.get(0).map(|p| p.value_type).unwrap_or(types::I64);
|
||||
if ret_ty == types::F64 {
|
||||
let z = fb.ins().f64const(0.0);
|
||||
fb.ins().return_(&[z]);
|
||||
} else {
|
||||
let zero = fb.ins().iconst(types::I64, 0);
|
||||
fb.ins().return_(&[zero]);
|
||||
}
|
||||
}
|
||||
fb.finalize();
|
||||
}
|
||||
@ -416,14 +829,18 @@ impl IRBuilder for CraneliftBuilder {
|
||||
let cond_b1 = if let Some(v) = self.value_stack.pop() {
|
||||
let ty = fb.func.dfg.value_type(v);
|
||||
if ty == types::I64 {
|
||||
fb.ins().icmp_imm(IntCC::NotEqual, v, 0)
|
||||
let out = fb.ins().icmp_imm(IntCC::NotEqual, v, 0);
|
||||
crate::jit::rt::b1_norm_inc(1);
|
||||
out
|
||||
} else {
|
||||
// assume already b1
|
||||
v
|
||||
}
|
||||
} else {
|
||||
let zero = fb.ins().iconst(types::I64, 0);
|
||||
fb.ins().icmp_imm(IntCC::NotEqual, zero, 0)
|
||||
let out = fb.ins().icmp_imm(IntCC::NotEqual, zero, 0);
|
||||
crate::jit::rt::b1_norm_inc(1);
|
||||
out
|
||||
};
|
||||
fb.ins().brif(cond_b1, self.blocks[then_index], &[], self.blocks[else_index], &[]);
|
||||
self.stats.3 += 1;
|
||||
@ -440,34 +857,60 @@ impl IRBuilder for CraneliftBuilder {
|
||||
fb.finalize();
|
||||
}
|
||||
fn ensure_block_param_i64(&mut self, index: usize) {
|
||||
self.ensure_block_params_i64(index, 1);
|
||||
}
|
||||
fn ensure_block_params_i64(&mut self, index: usize, needed: usize) {
|
||||
use cranelift_codegen::ir::types;
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
if index >= self.blocks.len() { return; }
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
let count = self.block_param_counts.get(&index).copied().unwrap_or(0);
|
||||
if count == 0 {
|
||||
let have = self.block_param_counts.get(&index).copied().unwrap_or(0);
|
||||
if needed > have {
|
||||
let b = self.blocks[index];
|
||||
let _v = fb.append_block_param(b, types::I64);
|
||||
self.block_param_counts.insert(index, 1);
|
||||
for _ in have..needed {
|
||||
let _v = fb.append_block_param(b, types::I64);
|
||||
}
|
||||
self.block_param_counts.insert(index, needed);
|
||||
}
|
||||
fb.finalize();
|
||||
}
|
||||
fn push_block_param_i64(&mut self) {
|
||||
fn ensure_block_params_b1(&mut self, index: usize, needed: usize) {
|
||||
// Store as i64 block params for ABI stability; consumers can convert to b1
|
||||
self.ensure_block_params_i64(index, needed);
|
||||
}
|
||||
fn push_block_param_i64_at(&mut self, pos: usize) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use cranelift_codegen::ir::types;
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
// Determine current block
|
||||
let b = if let Some(idx) = self.current_block_index { self.blocks[idx] } else if let Some(b) = self.entry_block { b } else { fb.create_block() };
|
||||
// Fetch first param if exists
|
||||
// Ensure we have an active insertion point before emitting any instructions
|
||||
fb.switch_to_block(b);
|
||||
let params = fb.func.dfg.block_params(b).to_vec();
|
||||
if let Some(v) = params.get(0).copied() { self.value_stack.push(v); }
|
||||
if let Some(v) = params.get(pos).copied() { self.value_stack.push(v); }
|
||||
else {
|
||||
// defensive: push 0
|
||||
// defensive fallback
|
||||
let zero = fb.ins().iconst(types::I64, 0);
|
||||
self.value_stack.push(zero);
|
||||
}
|
||||
fb.finalize();
|
||||
}
|
||||
fn push_block_param_b1_at(&mut self, pos: usize) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use cranelift_codegen::ir::{types, condcodes::IntCC};
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
let b = if let Some(idx) = self.current_block_index { self.blocks[idx] } else if let Some(b) = self.entry_block { b } else { fb.create_block() };
|
||||
let params = fb.func.dfg.block_params(b).to_vec();
|
||||
if let Some(v) = params.get(pos).copied() {
|
||||
let ty = fb.func.dfg.value_type(v);
|
||||
let b1 = if ty == types::I64 { fb.ins().icmp_imm(IntCC::NotEqual, v, 0) } else { v };
|
||||
self.value_stack.push(b1);
|
||||
} else {
|
||||
let zero = fb.ins().iconst(types::I64, 0);
|
||||
let b1 = fb.ins().icmp_imm(IntCC::NotEqual, zero, 0);
|
||||
self.value_stack.push(b1);
|
||||
}
|
||||
fb.finalize();
|
||||
}
|
||||
fn br_if_with_args(&mut self, then_index: usize, else_index: usize, then_n: usize, else_n: usize) {
|
||||
use cranelift_codegen::ir::{types, condcodes::IntCC};
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
@ -478,10 +921,12 @@ impl IRBuilder for CraneliftBuilder {
|
||||
// Condition
|
||||
let cond_b1 = if let Some(v) = self.value_stack.pop() {
|
||||
let ty = fb.func.dfg.value_type(v);
|
||||
if ty == types::I64 { fb.ins().icmp_imm(IntCC::NotEqual, v, 0) } else { v }
|
||||
if ty == types::I64 { let out = fb.ins().icmp_imm(IntCC::NotEqual, v, 0); crate::jit::rt::b1_norm_inc(1); out } else { v }
|
||||
} else {
|
||||
let zero = fb.ins().iconst(types::I64, 0);
|
||||
fb.ins().icmp_imm(IntCC::NotEqual, zero, 0)
|
||||
let out = fb.ins().icmp_imm(IntCC::NotEqual, zero, 0);
|
||||
crate::jit::rt::b1_norm_inc(1);
|
||||
out
|
||||
};
|
||||
// Pop else args then then args (so stack order can be value-friendly)
|
||||
let mut else_args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||
@ -507,6 +952,58 @@ impl IRBuilder for CraneliftBuilder {
|
||||
self.stats.3 += 1;
|
||||
fb.finalize();
|
||||
}
|
||||
|
||||
fn hint_ret_bool(&mut self, is_b1: bool) { self.ret_hint_is_b1 = is_b1; }
|
||||
|
||||
fn ensure_local_i64(&mut self, index: usize) {
|
||||
use cranelift_codegen::ir::{StackSlotData, StackSlotKind};
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
if self.local_slots.contains_key(&index) { return; }
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
let slot = fb.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8));
|
||||
self.local_slots.insert(index, slot);
|
||||
fb.finalize();
|
||||
}
|
||||
fn store_local_i64(&mut self, index: usize) {
|
||||
use cranelift_codegen::ir::{types, condcodes::IntCC};
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
if let Some(mut v) = self.value_stack.pop() {
|
||||
// Ensure slot without overlapping FunctionBuilder borrows
|
||||
if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); }
|
||||
let slot = self.local_slots.get(&index).copied();
|
||||
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); }
|
||||
let ty = fb.func.dfg.value_type(v);
|
||||
if ty != types::I64 {
|
||||
if ty == types::F64 {
|
||||
v = fb.ins().fcvt_to_sint(types::I64, v);
|
||||
} else {
|
||||
// Convert unknown ints/bools to i64 via (v!=0)?1:0
|
||||
let one = fb.ins().iconst(types::I64, 1);
|
||||
let zero = fb.ins().iconst(types::I64, 0);
|
||||
let b1 = fb.ins().icmp_imm(IntCC::NotEqual, v, 0);
|
||||
v = fb.ins().select(b1, one, zero);
|
||||
}
|
||||
}
|
||||
if let Some(slot) = slot { fb.ins().stack_store(v, slot, 0); }
|
||||
fb.finalize();
|
||||
}
|
||||
}
|
||||
fn load_local_i64(&mut self, index: usize) {
|
||||
use cranelift_codegen::ir::types;
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); }
|
||||
if let Some(&slot) = self.local_slots.get(&index) {
|
||||
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); }
|
||||
let v = fb.ins().stack_load(types::I64, slot, 0);
|
||||
self.value_stack.push(v);
|
||||
self.stats.0 += 1;
|
||||
fb.finalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
@ -542,6 +1039,19 @@ impl CraneliftBuilder {
|
||||
builder.symbol(c::SYM_MAP_GET, nyash_map_get as *const u8);
|
||||
builder.symbol(c::SYM_MAP_SET, nyash_map_set as *const u8);
|
||||
builder.symbol(c::SYM_MAP_SIZE, nyash_map_size as *const u8);
|
||||
// Handle-based symbols
|
||||
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_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);
|
||||
builder.symbol(c::SYM_MAP_GET_H, nyash_map_get_h as *const u8);
|
||||
builder.symbol(c::SYM_MAP_SET_H, nyash_map_set_h as *const u8);
|
||||
builder.symbol(c::SYM_MAP_HAS_H, nyash_map_has_h as *const u8);
|
||||
builder.symbol(c::SYM_ANY_LEN_H, nyash_any_length_h as *const u8);
|
||||
builder.symbol(c::SYM_ANY_IS_EMPTY_H, nyash_any_is_empty_h as *const u8);
|
||||
builder.symbol(c::SYM_STRING_CHARCODE_AT_H, nyash_string_charcode_at_h as *const u8);
|
||||
}
|
||||
let module = cranelift_jit::JITModule::new(builder);
|
||||
let ctx = cranelift_codegen::Context::new();
|
||||
@ -555,14 +1065,18 @@ impl CraneliftBuilder {
|
||||
blocks: Vec::new(),
|
||||
current_block_index: None,
|
||||
block_param_counts: std::collections::HashMap::new(),
|
||||
local_slots: std::collections::HashMap::new(),
|
||||
compiled_closure: None,
|
||||
desired_argc: 0,
|
||||
desired_has_ret: true,
|
||||
desired_ret_is_f64: false,
|
||||
typed_sig_prepared: false,
|
||||
ret_hint_is_b1: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Take ownership of compiled closure if available
|
||||
pub fn take_compiled_closure(&mut self) -> Option<std::sync::Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>> {
|
||||
pub fn take_compiled_closure(&mut self) -> Option<std::sync::Arc<dyn Fn(&[crate::jit::abi::JitValue]) -> crate::jit::abi::JitValue + Send + Sync>> {
|
||||
self.compiled_closure.take()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user