AI協調開発研究ドキュメントの完成と Phase 10.9-β 進捗
【AI協調開発研究】 - AI二重化モデルの学術論文draft完成(workshop_paper_draft.md) - 「隠れた危機」分析とbirthの原則哲学化 - TyEnv「唯一の真実」協調会話を保存・研究資料に統合 - papers管理構造の整備(wip/under-review/published分離) 【Phase 10.9-β HostCall進捗】 - JitConfigBox: relax_numeric フラグ追加(i64→f64コアーション制御) - HostcallRegistryBox: 署名検証・白黒リスト・コアーション対応 - JitHostcallRegistryBox: Nyash側レジストリ操作API - Lower統合: env直読 → jit::config::current() 参照に統一 - 数値緩和設定: NYASH_JIT_HOSTCALL_RELAX_NUMERIC/Config.set_flag 【検証サンプル拡充】 - math.sin/cos/abs/min/max 関数スタイル(examples/jit_math_function_style_*.nyash) - 境界ケース: 署名不一致・コアーション許可・mutating拒否サンプル - E2E実証: String.length→allow, Array.push→fallback, math関数の署名一致観測 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -30,6 +30,8 @@ pub trait IRBuilder {
|
||||
fn emit_return(&mut self);
|
||||
/// Phase 10_d scaffolding: host-call emission (symbolic)
|
||||
fn emit_host_call(&mut self, _symbol: &str, _argc: usize, _has_ret: bool) { }
|
||||
/// Typed host-call emission: params kinds and return type hint (f64 when true)
|
||||
fn emit_host_call_typed(&mut self, _symbol: &str, _params: &[ParamKind], _has_ret: bool, _ret_is_f64: bool) { }
|
||||
// ==== Phase 10.7 (control-flow wiring, default no-op) ====
|
||||
/// Optional: prepare N basic blocks and return their handles (0..N-1)
|
||||
fn prepare_blocks(&mut self, _count: usize) { }
|
||||
@ -95,6 +97,7 @@ 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 emit_host_call_typed(&mut self, _symbol: &str, _params: &[ParamKind], has_ret: bool, _ret_is_f64: bool) { if has_ret { self.consts += 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; }
|
||||
@ -135,6 +138,16 @@ use cranelift_codegen::ir::InstBuilder;
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_host_stub0() -> i64 { 0 }
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_math_sin_f64(x: f64) -> f64 { x.sin() }
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_math_cos_f64(x: f64) -> f64 { x.cos() }
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_math_abs_f64(x: f64) -> f64 { x.abs() }
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_math_min_f64(a: f64, b: f64) -> f64 { a.min(b) }
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_math_max_f64(a: f64, b: f64) -> f64 { a.max(b) }
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
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; }
|
||||
@ -222,6 +235,10 @@ 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 {
|
||||
crate::jit::events::emit(
|
||||
"hostcall", "<jit>", None, None,
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ARRAY_LEN_H, "decision":"allow", "argc":1, "arg_types":["Handle"]})
|
||||
);
|
||||
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; }
|
||||
@ -236,7 +253,10 @@ extern "C" fn nyash_array_push_h(handle: u64, val: i64) -> i64 {
|
||||
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"}));
|
||||
crate::jit::events::emit(
|
||||
"hostcall", "<jit>", None, None,
|
||||
serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating"})
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
_ => {}
|
||||
@ -245,7 +265,10 @@ extern "C" fn nyash_array_push_h(handle: u64, val: i64) -> i64 {
|
||||
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"}));
|
||||
crate::jit::events::emit(
|
||||
"hostcall", "<jit>", None, None,
|
||||
serde_json::json!({"id": sym, "decision":"allow", "argc":2, "arg_types":["Handle","I64"]})
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -253,6 +276,10 @@ extern "C" fn nyash_array_push_h(handle: u64, val: i64) -> i64 {
|
||||
}
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_array_get_h(handle: u64, idx: i64) -> i64 {
|
||||
crate::jit::events::emit(
|
||||
"hostcall", "<jit>", None, None,
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ARRAY_GET_H, "decision":"allow", "argc":2, "arg_types":["Handle","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)));
|
||||
@ -282,7 +309,10 @@ 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"}));
|
||||
crate::jit::events::emit(
|
||||
"hostcall", "<jit>", None, None,
|
||||
serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating"})
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
if let Some(obj) = crate::jit::rt::handles::get(handle) {
|
||||
@ -291,7 +321,10 @@ extern "C" fn nyash_array_set_h(handle: u64, idx: i64, val: i64) -> i64 {
|
||||
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"}));
|
||||
crate::jit::events::emit(
|
||||
"hostcall", "<jit>", None, None,
|
||||
serde_json::json!({"id": sym, "decision":"allow", "argc":3, "arg_types":["Handle","I64","I64"]})
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -299,7 +332,10 @@ extern "C" fn nyash_array_set_h(handle: u64, idx: i64, val: i64) -> i64 {
|
||||
}
|
||||
#[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"}));
|
||||
crate::jit::events::emit(
|
||||
"hostcall", "<jit>", None, None,
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_MAP_SIZE_H, "decision":"allow", "argc":1, "arg_types":["Handle"]})
|
||||
);
|
||||
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; }
|
||||
@ -309,7 +345,10 @@ extern "C" fn nyash_map_size_h(handle: u64) -> i64 {
|
||||
}
|
||||
#[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"}));
|
||||
crate::jit::events::emit(
|
||||
"hostcall", "<jit>", None, None,
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_MAP_GET_H, "decision":"allow", "argc":2, "arg_types":["Handle","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));
|
||||
@ -324,7 +363,10 @@ 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"}));
|
||||
crate::jit::events::emit(
|
||||
"hostcall", "<jit>", None, None,
|
||||
serde_json::json!({"id": sym, "decision":"fallback", "reason":"policy_denied_mutating"})
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
if let Some(obj) = crate::jit::rt::handles::get(handle) {
|
||||
@ -332,7 +374,10 @@ extern "C" fn nyash_map_set_h(handle: u64, key: i64, val: i64) -> i64 {
|
||||
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"}));
|
||||
crate::jit::events::emit(
|
||||
"hostcall", "<jit>", None, None,
|
||||
serde_json::json!({"id": sym, "decision":"allow", "argc":3, "arg_types":["Handle","I64","I64"]})
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -353,7 +398,10 @@ extern "C" fn nyash_map_has_h(handle: u64, key: i64) -> i64 {
|
||||
}
|
||||
#[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"}));
|
||||
crate::jit::events::emit(
|
||||
"hostcall", "<jit>", None, None,
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_LEN_H, "decision":"allow", "argc":1, "arg_types":["Handle"]})
|
||||
);
|
||||
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>() {
|
||||
@ -368,7 +416,10 @@ extern "C" fn nyash_any_length_h(handle: u64) -> i64 {
|
||||
}
|
||||
#[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"}));
|
||||
crate::jit::events::emit(
|
||||
"hostcall", "<jit>", None, None,
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, "decision":"allow", "argc":1, "arg_types":["Handle"]})
|
||||
);
|
||||
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>() {
|
||||
@ -387,7 +438,10 @@ extern "C" fn nyash_any_is_empty_h(handle: u64) -> i64 {
|
||||
}
|
||||
#[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"}));
|
||||
crate::jit::events::emit(
|
||||
"hostcall", "<jit>", None, None,
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, "decision":"allow", "argc":2, "arg_types":["Handle","I64"]})
|
||||
);
|
||||
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>() {
|
||||
@ -790,6 +844,52 @@ impl IRBuilder for CraneliftBuilder {
|
||||
fb.finalize();
|
||||
}
|
||||
|
||||
fn emit_host_call_typed(&mut self, symbol: &str, params: &[ParamKind], has_ret: bool, ret_is_f64: bool) {
|
||||
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use cranelift_module::{Linkage, Module};
|
||||
|
||||
// Pop values according to params length (right-to-left), then reverse
|
||||
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||
let take_n = params.len().min(self.value_stack.len());
|
||||
for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { args.push(v); } }
|
||||
args.reverse();
|
||||
|
||||
// Build typed signature
|
||||
let call_conv = self.module.isa().default_call_conv();
|
||||
let mut sig = Signature::new(call_conv);
|
||||
let abi_param_for_kind = |k: &ParamKind| {
|
||||
match k {
|
||||
ParamKind::I64 => AbiParam::new(types::I64),
|
||||
ParamKind::F64 => AbiParam::new(types::F64),
|
||||
ParamKind::B1 => {
|
||||
// Map b1 to I64 unless native-b1 ABI is enabled; keep simple here
|
||||
AbiParam::new(types::I64)
|
||||
}
|
||||
}
|
||||
};
|
||||
for k in params { sig.params.push(abi_param_for_kind(k)); }
|
||||
if has_ret {
|
||||
if ret_is_f64 { sig.returns.push(AbiParam::new(types::F64)); }
|
||||
else { sig.returns.push(AbiParam::new(types::I64)); }
|
||||
}
|
||||
|
||||
let func_id = self.module
|
||||
.declare_function(symbol, Linkage::Import, &sig)
|
||||
.expect("declare typed import failed");
|
||||
|
||||
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 fref = self.module.declare_func_in_func(func_id, fb.func);
|
||||
let call_inst = fb.ins().call(fref, &args);
|
||||
if has_ret {
|
||||
let results = fb.inst_results(call_inst).to_vec();
|
||||
if let Some(v) = results.get(0).copied() { self.value_stack.push(v); }
|
||||
}
|
||||
fb.finalize();
|
||||
}
|
||||
|
||||
// ==== Phase 10.7 block APIs ====
|
||||
fn prepare_blocks(&mut self, count: usize) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
@ -1039,6 +1139,12 @@ 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);
|
||||
// Math f64 externs
|
||||
builder.symbol("nyash.math.sin_f64", nyash_math_sin_f64 as *const u8);
|
||||
builder.symbol("nyash.math.cos_f64", nyash_math_cos_f64 as *const u8);
|
||||
builder.symbol("nyash.math.abs_f64", nyash_math_abs_f64 as *const u8);
|
||||
builder.symbol("nyash.math.min_f64", nyash_math_min_f64 as *const u8);
|
||||
builder.symbol("nyash.math.max_f64", nyash_math_max_f64 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);
|
||||
|
||||
@ -8,6 +8,8 @@ pub struct LowerCore {
|
||||
pub covered: usize,
|
||||
/// Minimal constant propagation for i64 to feed host-call args
|
||||
known_i64: std::collections::HashMap<ValueId, i64>,
|
||||
/// Minimal constant propagation for f64 (math.* signature checks)
|
||||
known_f64: std::collections::HashMap<ValueId, f64>,
|
||||
/// Parameter index mapping for ValueId
|
||||
param_index: std::collections::HashMap<ValueId, usize>,
|
||||
/// Track values produced by Phi (for minimal PHI path)
|
||||
@ -18,6 +20,8 @@ pub struct LowerCore {
|
||||
bool_values: std::collections::HashSet<ValueId>,
|
||||
/// Track PHI destinations that are boolean (all inputs derived from bool_values)
|
||||
bool_phi_values: std::collections::HashSet<ValueId>,
|
||||
/// Track values that are FloatBox instances (for arg type classification)
|
||||
float_box_values: std::collections::HashSet<ValueId>,
|
||||
// Per-function statistics (last lowered)
|
||||
last_phi_total: u64,
|
||||
last_phi_b1: u64,
|
||||
@ -28,7 +32,7 @@ pub struct LowerCore {
|
||||
}
|
||||
|
||||
impl LowerCore {
|
||||
pub fn new() -> Self { Self { unsupported: 0, covered: 0, known_i64: std::collections::HashMap::new(), param_index: std::collections::HashMap::new(), phi_values: std::collections::HashSet::new(), phi_param_index: std::collections::HashMap::new(), bool_values: std::collections::HashSet::new(), bool_phi_values: std::collections::HashSet::new(), last_phi_total: 0, last_phi_b1: 0, last_ret_bool_hint_used: false, local_index: std::collections::HashMap::new(), next_local: 0 } }
|
||||
pub fn new() -> Self { Self { unsupported: 0, covered: 0, known_i64: std::collections::HashMap::new(), known_f64: std::collections::HashMap::new(), param_index: std::collections::HashMap::new(), phi_values: std::collections::HashSet::new(), phi_param_index: std::collections::HashMap::new(), bool_values: std::collections::HashSet::new(), bool_phi_values: std::collections::HashSet::new(), float_box_values: std::collections::HashSet::new(), last_phi_total: 0, last_phi_b1: 0, last_ret_bool_hint_used: false, local_index: std::collections::HashMap::new(), next_local: 0 } }
|
||||
|
||||
/// Get statistics for the last lowered function
|
||||
pub fn last_stats(&self) -> (u64, u64, bool) { (self.last_phi_total, self.last_phi_b1, self.last_ret_bool_hint_used) }
|
||||
@ -232,10 +236,22 @@ impl LowerCore {
|
||||
} else {
|
||||
builder.prepare_signature_i64(func.params.len(), true);
|
||||
}
|
||||
// Pre-scan FloatBox creations across all blocks for arg classification
|
||||
self.float_box_values.clear();
|
||||
for bb in bb_ids.iter() {
|
||||
if let Some(block) = func.blocks.get(bb) {
|
||||
for ins in block.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::NewBox { dst, box_type, .. } = ins { if box_type == "FloatBox" { self.float_box_values.insert(*dst); } }
|
||||
if let crate::mir::MirInstruction::Copy { dst, src } = ins { if self.float_box_values.contains(src) { self.float_box_values.insert(*dst); } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder.begin_function(&func.signature.name);
|
||||
// Iterate blocks in the sorted order to keep indices stable
|
||||
self.phi_values.clear();
|
||||
self.phi_param_index.clear();
|
||||
self.float_box_values.clear();
|
||||
for (idx, bb_id) in bb_ids.iter().enumerate() {
|
||||
let bb = func.blocks.get(bb_id).unwrap();
|
||||
builder.switch_to_block(idx);
|
||||
@ -262,7 +278,10 @@ impl LowerCore {
|
||||
}
|
||||
for instr in bb.instructions.iter() {
|
||||
self.cover_if_supported(instr);
|
||||
self.try_emit(builder, instr, *bb_id);
|
||||
self.try_emit(builder, instr, *bb_id, func);
|
||||
// Track FloatBox creations for later arg classification
|
||||
if let crate::mir::MirInstruction::NewBox { dst, box_type, .. } = instr { if box_type == "FloatBox" { self.float_box_values.insert(*dst); } }
|
||||
if let crate::mir::MirInstruction::Copy { dst, src } = instr { if self.float_box_values.contains(src) { self.float_box_values.insert(*dst); } }
|
||||
}
|
||||
if let Some(term) = &bb.terminator {
|
||||
self.cover_if_supported(term);
|
||||
@ -353,10 +372,10 @@ impl LowerCore {
|
||||
}
|
||||
builder.seal_block(target_index);
|
||||
}
|
||||
_ => {
|
||||
self.try_emit(builder, term, *bb_id);
|
||||
}
|
||||
_ => { /* other terminators handled via generic emission below */ }
|
||||
}
|
||||
// Also allow other terminators to be emitted if needed
|
||||
self.try_emit(builder, term, *bb_id, func);
|
||||
}
|
||||
}
|
||||
builder.end_function();
|
||||
@ -443,28 +462,53 @@ impl LowerCore {
|
||||
| I::Jump { .. }
|
||||
| I::Branch { .. }
|
||||
| I::Return { .. }
|
||||
| I::BoxCall { .. }
|
||||
| I::ArrayGet { .. }
|
||||
| I::ArraySet { .. }
|
||||
);
|
||||
if supported { self.covered += 1; } else { self.unsupported += 1; }
|
||||
}
|
||||
|
||||
fn try_emit(&mut self, b: &mut dyn IRBuilder, instr: &MirInstruction, cur_bb: crate::mir::BasicBlockId) {
|
||||
fn try_emit(&mut self, b: &mut dyn IRBuilder, instr: &MirInstruction, cur_bb: crate::mir::BasicBlockId, func: &crate::mir::MirFunction) {
|
||||
use crate::mir::MirInstruction as I;
|
||||
match instr {
|
||||
I::Cast { dst, value, target_type: _ } => {
|
||||
I::NewBox { dst, box_type, args } => {
|
||||
// Track boxed numeric literals to aid signature checks (FloatBox/IntegerBox)
|
||||
if box_type == "FloatBox" {
|
||||
if let Some(src) = args.get(0) {
|
||||
if let Some(fv) = self.known_f64.get(src).copied() {
|
||||
self.known_f64.insert(*dst, fv);
|
||||
} else if let Some(iv) = self.known_i64.get(src).copied() {
|
||||
self.known_f64.insert(*dst, iv as f64);
|
||||
}
|
||||
}
|
||||
} else if box_type == "IntegerBox" {
|
||||
if let Some(src) = args.get(0) {
|
||||
if let Some(iv) = self.known_i64.get(src).copied() {
|
||||
self.known_i64.insert(*dst, iv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
I::Cast { dst, value, target_type } => {
|
||||
// Minimal cast footing: materialize source when param/known
|
||||
// Bool→Int: rely on producers (compare) and branch/b1 loaders; here we just reuse integer path
|
||||
self.push_value_if_known_or_param(b, value);
|
||||
// Track known i64 if source known
|
||||
if let Some(v) = self.known_i64.get(value).copied() { self.known_i64.insert(*dst, v); }
|
||||
// Track known f64 for float casts
|
||||
if matches!(target_type, crate::mir::MirType::Float) {
|
||||
if let Some(iv) = self.known_i64.get(value).copied() {
|
||||
self.known_f64.insert(*dst, iv as f64);
|
||||
}
|
||||
}
|
||||
}
|
||||
I::Const { dst, value } => match value {
|
||||
ConstValue::Integer(i) => {
|
||||
b.emit_const_i64(*i);
|
||||
self.known_i64.insert(*dst, *i);
|
||||
}
|
||||
ConstValue::Float(f) => b.emit_const_f64(*f),
|
||||
ConstValue::Float(f) => { b.emit_const_f64(*f); self.known_f64.insert(*dst, *f); }
|
||||
ConstValue::Bool(bv) => {
|
||||
let iv = if *bv { 1 } else { 0 };
|
||||
b.emit_const_i64(iv);
|
||||
@ -478,6 +522,7 @@ impl LowerCore {
|
||||
},
|
||||
I::Copy { dst, src } => {
|
||||
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 source is a parameter, materialize it on the stack for downstream ops
|
||||
if let Some(pidx) = self.param_index.get(src).copied() {
|
||||
b.emit_param_i64(pidx);
|
||||
@ -558,7 +603,7 @@ impl LowerCore {
|
||||
}
|
||||
}
|
||||
I::ArrayGet { array, index, .. } => {
|
||||
if std::env::var("NYASH_JIT_HOSTCALL").ok().as_deref() == Some("1") {
|
||||
if crate::jit::config::current().hostcall {
|
||||
let idx = self.known_i64.get(index).copied().unwrap_or(0);
|
||||
if let Some(pidx) = self.param_index.get(array).copied() {
|
||||
// Handle-based: push handle value from param, then index
|
||||
@ -575,7 +620,7 @@ impl LowerCore {
|
||||
}
|
||||
}
|
||||
I::ArraySet { array, index, value } => {
|
||||
if std::env::var("NYASH_JIT_HOSTCALL").ok().as_deref() == Some("1") {
|
||||
if crate::jit::config::current().hostcall {
|
||||
let idx = self.known_i64.get(index).copied().unwrap_or(0);
|
||||
let val = self.known_i64.get(value).copied().unwrap_or(0);
|
||||
if let Some(pidx) = self.param_index.get(array).copied() {
|
||||
@ -593,7 +638,7 @@ impl LowerCore {
|
||||
}
|
||||
}
|
||||
I::BoxCall { box_val: array, method, args, dst, .. } => {
|
||||
if std::env::var("NYASH_JIT_HOSTCALL").ok().as_deref() == Some("1") {
|
||||
if crate::jit::config::current().hostcall {
|
||||
match method.as_str() {
|
||||
"len" | "length" => {
|
||||
if let Some(pidx) = self.param_index.get(array).copied() {
|
||||
@ -606,6 +651,106 @@ impl LowerCore {
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, dst.is_some());
|
||||
}
|
||||
}
|
||||
// math.* minimal boundary: use registry signature to decide allow/fallback (no actual hostcall yet)
|
||||
"sin" | "cos" | "abs" | "min" | "max" => {
|
||||
use crate::jit::hostcall_registry::{check_signature, ArgKind};
|
||||
// Build symbol and observed arg kinds (f64 if known float, else i64)
|
||||
let sym = format!("nyash.math.{}", method);
|
||||
let mut observed: Vec<ArgKind> = Vec::new();
|
||||
for v in args.iter() {
|
||||
if self.known_f64.contains_key(v) { observed.push(ArgKind::F64); }
|
||||
else { observed.push(ArgKind::I64); }
|
||||
}
|
||||
// Prepare arg_types for event payload
|
||||
// Classify argument kinds using known maps and FloatBox tracking; as a last resort, scan for NewBox(FloatBox)
|
||||
let mut observed_kinds: Vec<crate::jit::hostcall_registry::ArgKind> = Vec::new();
|
||||
for v in args.iter() {
|
||||
let mut kind = if self.known_f64.contains_key(v) || self.float_box_values.contains(v) {
|
||||
crate::jit::hostcall_registry::ArgKind::F64
|
||||
} else { crate::jit::hostcall_registry::ArgKind::I64 };
|
||||
if let crate::jit::hostcall_registry::ArgKind::I64 = kind {
|
||||
'scanv: for (_bb_id, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::NewBox { dst, box_type, .. } = ins {
|
||||
if *dst == *v && box_type == "FloatBox" { kind = crate::jit::hostcall_registry::ArgKind::F64; break 'scanv; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
observed_kinds.push(kind);
|
||||
}
|
||||
let arg_types: Vec<&'static str> = observed_kinds.iter().map(|k| match k { crate::jit::hostcall_registry::ArgKind::I64 => "I64", crate::jit::hostcall_registry::ArgKind::F64 => "F64", crate::jit::hostcall_registry::ArgKind::Handle => "Handle" }).collect();
|
||||
match check_signature(&sym, &observed_kinds) {
|
||||
Ok(()) => {
|
||||
// allow: record decision; execution remains on VM for now (thin bridge)
|
||||
crate::jit::events::emit(
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
None,
|
||||
None,
|
||||
serde_json::json!({
|
||||
"id": sym,
|
||||
"decision": "allow",
|
||||
"reason": "sig_ok",
|
||||
"argc": observed.len(),
|
||||
"arg_types": arg_types
|
||||
})
|
||||
);
|
||||
// If native f64 is enabled, emit a typed hostcall to math extern
|
||||
if crate::jit::config::current().native_f64 {
|
||||
let (symbol, arity) = match method.as_str() {
|
||||
"sin" => ("nyash.math.sin_f64", 1),
|
||||
"cos" => ("nyash.math.cos_f64", 1),
|
||||
"abs" => ("nyash.math.abs_f64", 1),
|
||||
"min" => ("nyash.math.min_f64", 2),
|
||||
"max" => ("nyash.math.max_f64", 2),
|
||||
_ => ("nyash.math.sin_f64", 1),
|
||||
};
|
||||
// Push f64 args from known_f64 or coerce known_i64
|
||||
for i in 0..arity {
|
||||
if let Some(v) = args.get(i) {
|
||||
// Try direct known values
|
||||
if let Some(fv) = self.known_f64.get(v).copied() { b.emit_const_f64(fv); continue; }
|
||||
if let Some(iv) = self.known_i64.get(v).copied() { b.emit_const_f64(iv as f64); continue; }
|
||||
// Try unwrap FloatBox: scan blocks to find NewBox FloatBox { args: [src] } and reuse src const
|
||||
let mut emitted = false;
|
||||
'scan: for (_bb_id, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::NewBox { dst, box_type, args: nb_args } = ins {
|
||||
if *dst == *v && box_type == "FloatBox" {
|
||||
if let Some(srcv) = nb_args.get(0) {
|
||||
if let Some(fv) = self.known_f64.get(srcv).copied() { b.emit_const_f64(fv); emitted = true; break 'scan; }
|
||||
if let Some(iv) = self.known_i64.get(srcv).copied() { b.emit_const_f64(iv as f64); emitted = true; break 'scan; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !emitted { b.emit_const_f64(0.0); }
|
||||
} else { b.emit_const_f64(0.0); }
|
||||
}
|
||||
let kinds: Vec<super::builder::ParamKind> = (0..arity).map(|_| super::builder::ParamKind::F64).collect();
|
||||
b.emit_host_call_typed(symbol, &kinds, dst.is_some(), true);
|
||||
}
|
||||
}
|
||||
Err(reason) => {
|
||||
crate::jit::events::emit(
|
||||
"hostcall",
|
||||
"<jit>",
|
||||
None,
|
||||
None,
|
||||
serde_json::json!({
|
||||
"id": sym,
|
||||
"decision": "fallback",
|
||||
"reason": reason,
|
||||
"argc": observed.len(),
|
||||
"arg_types": arg_types
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
// no-op: VM側で実行される
|
||||
}
|
||||
"isEmpty" | "empty" => {
|
||||
if let Some(pidx) = self.param_index.get(array).copied() {
|
||||
b.emit_param_i64(pidx);
|
||||
|
||||
Reference in New Issue
Block a user