wip(phase15): AOT修正作業中 - Nyプラグインと標準ライブラリ実装
Phase 15のAOT/ネイティブビルド修正作業を継続中。 ChatGPTによるstd実装とプラグインシステムの改修を含む。 主な変更点: - apps/std/: string.nyashとarray.nyashの標準ライブラリ追加 - apps/smokes/: stdライブラリのスモークテスト追加 - プラグインローダーv2の実装改修 - BoxCallのハンドル管理改善 - JIT hostcall registryの更新 - ビルドスクリプト(build_aot.sh, build_llvm.sh)の調整 まだ修正作業中のため、一部の機能は不完全な状態。 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -24,6 +24,7 @@ fn ensure_default() {
|
||||
// Read-only defaults
|
||||
for s in [
|
||||
"nyash.array.len_h",
|
||||
"nyash.string.len_h",
|
||||
"nyash.any.length_h",
|
||||
"nyash.any.is_empty_h",
|
||||
"nyash.map.size_h",
|
||||
@ -54,6 +55,7 @@ fn ensure_default() {
|
||||
r.sig.entry("nyash.array.get_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::I64], ret: ArgKind::Handle });
|
||||
r.sig.entry("nyash.array.len_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle], ret: ArgKind::I64 });
|
||||
// String helpers
|
||||
r.sig.entry("nyash.string.len_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle], ret: ArgKind::I64 });
|
||||
r.sig.entry("nyash.string.charCodeAt_h".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::I64], ret: ArgKind::I64 });
|
||||
r.sig.entry("nyash.string.concat_hh".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::Handle], ret: ArgKind::Handle });
|
||||
r.sig.entry("nyash.semantics.add_hh".to_string()).or_default().push(Signature { args: vec![ArgKind::Handle, ArgKind::Handle], ret: ArgKind::Handle });
|
||||
|
||||
@ -80,26 +80,329 @@ impl IRBuilder for ObjectBuilder {
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
||||
fb.finalize();
|
||||
let obj_id = self.module.declare_function(self.current_name.as_deref().unwrap_or("jit_aot"), cranelift_module::Linkage::Local, &self.ctx.func.signature).expect("declare func");
|
||||
self.module.define_function(obj_id, &mut self.ctx).expect("define");
|
||||
self.module.clear_context(&mut self.ctx);
|
||||
let finished = std::mem::replace(&mut self.module, Self::fresh_module());
|
||||
let product = finished.finish();
|
||||
self.object_bytes = Some(product.emit().expect("emit object"));
|
||||
// Export as ny_main so that nyrt can locate the entrypoint when linking AOT objects
|
||||
let obj_id = self.module.declare_function("ny_main", cranelift_module::Linkage::Export, &self.ctx.func.signature).expect("declare func");
|
||||
self.module.define_function(obj_id, &mut self.ctx).expect("define");
|
||||
self.module.clear_context(&mut self.ctx);
|
||||
let finished = std::mem::replace(&mut self.module, Self::fresh_module());
|
||||
let product = finished.finish();
|
||||
self.object_bytes = Some(product.emit().expect("emit object"));
|
||||
}
|
||||
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; }
|
||||
fn emit_param_i64(&mut self, index: usize) { if let Some(v) = self.entry_param(index) { self.value_stack.push(v); } }
|
||||
fn emit_const_i64(&mut self, val: i64) { use cranelift_codegen::ir::types; use cranelift_frontend::FunctionBuilder; let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); let v = fb.ins().iconst(types::I64, val); self.value_stack.push(v); self.stats.0 += 1; }
|
||||
fn emit_const_f64(&mut self, val: f64) { use cranelift_frontend::FunctionBuilder; use cranelift_codegen::ir::types; let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); let v = fb.ins().f64const(val); self.value_stack.push(v); }
|
||||
fn emit_binop(&mut self, _op: super::BinOpKind) { self.stats.1 += 1; }
|
||||
fn emit_compare(&mut self, _op: super::CmpKind) { self.stats.2 += 1; }
|
||||
fn emit_const_i64(&mut self, val: i64) {
|
||||
use cranelift_codegen::ir::types;
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
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().iconst(types::I64, val);
|
||||
self.value_stack.push(v);
|
||||
self.stats.0 += 1;
|
||||
}
|
||||
fn emit_const_f64(&mut self, val: f64) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use cranelift_codegen::ir::types;
|
||||
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().f64const(val);
|
||||
self.value_stack.push(v);
|
||||
}
|
||||
fn emit_binop(&mut self, op: super::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); }
|
||||
// Ensure i64 operands
|
||||
if fb.func.dfg.value_type(lhs) != types::I64 { lhs = fb.ins().fcvt_to_sint(types::I64, lhs); }
|
||||
if fb.func.dfg.value_type(rhs) != types::I64 { rhs = fb.ins().fcvt_to_sint(types::I64, rhs); }
|
||||
let res = match op {
|
||||
super::BinOpKind::Add => fb.ins().iadd(lhs, rhs),
|
||||
super::BinOpKind::Sub => fb.ins().isub(lhs, rhs),
|
||||
super::BinOpKind::Mul => fb.ins().imul(lhs, rhs),
|
||||
super::BinOpKind::Div => fb.ins().sdiv(lhs, rhs),
|
||||
super::BinOpKind::Mod => fb.ins().srem(lhs, rhs),
|
||||
};
|
||||
self.value_stack.push(res);
|
||||
self.stats.1 += 1;
|
||||
}
|
||||
fn emit_compare(&mut self, op: super::CmpKind) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use cranelift_codegen::ir::{types, condcodes::IntCC};
|
||||
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); }
|
||||
// Ensure i64 operands
|
||||
if fb.func.dfg.value_type(lhs) != types::I64 { lhs = fb.ins().fcvt_to_sint(types::I64, lhs); }
|
||||
if fb.func.dfg.value_type(rhs) != types::I64 { rhs = fb.ins().fcvt_to_sint(types::I64, rhs); }
|
||||
let cc = match op {
|
||||
super::CmpKind::Eq => IntCC::Equal,
|
||||
super::CmpKind::Ne => IntCC::NotEqual,
|
||||
super::CmpKind::Lt => IntCC::SignedLessThan,
|
||||
super::CmpKind::Le => IntCC::SignedLessThanOrEqual,
|
||||
super::CmpKind::Gt => IntCC::SignedGreaterThan,
|
||||
super::CmpKind::Ge => IntCC::SignedGreaterThanOrEqual,
|
||||
};
|
||||
let b1 = fb.ins().icmp(cc, lhs, rhs);
|
||||
let one = fb.ins().iconst(types::I64, 1);
|
||||
let zero = fb.ins().iconst(types::I64, 0);
|
||||
let sel = fb.ins().select(b1, one, zero);
|
||||
self.value_stack.push(sel);
|
||||
self.stats.2 += 1;
|
||||
}
|
||||
fn emit_jump(&mut self) { self.stats.3 += 1; }
|
||||
fn emit_branch(&mut self) { self.stats.3 += 1; }
|
||||
fn emit_return(&mut self) { self.stats.4 += 1; }
|
||||
fn ensure_local_i64(&mut self, _index: usize) {}
|
||||
fn store_local_i64(&mut self, _index: usize) {}
|
||||
fn load_local_i64(&mut self, _index: usize) {}
|
||||
fn emit_return(&mut self) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
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 self.desired_has_ret {
|
||||
if self.desired_ret_is_f64 {
|
||||
use cranelift_codegen::ir::types;
|
||||
let v = if let Some(v) = self.value_stack.pop() { v } else { fb.ins().f64const(0.0) };
|
||||
// Coerce i64 to f64 if needed
|
||||
let v2 = if fb.func.dfg.value_type(v) != types::F64 { fb.ins().fcvt_from_sint(types::F64, v) } else { v };
|
||||
fb.ins().return_(&[v2]);
|
||||
} else {
|
||||
use cranelift_codegen::ir::types;
|
||||
let v = if let Some(v) = self.value_stack.pop() { v } else { fb.ins().iconst(types::I64, 0) };
|
||||
let v2 = if fb.func.dfg.value_type(v) != types::I64 { fb.ins().fcvt_to_sint(types::I64, v) } else { v };
|
||||
fb.ins().return_(&[v2]);
|
||||
}
|
||||
} else {
|
||||
fb.ins().return_(&[]);
|
||||
}
|
||||
self.stats.4 += 1;
|
||||
}
|
||||
fn ensure_local_i64(&mut self, index: usize) {
|
||||
use cranelift_codegen::ir::StackSlotData;
|
||||
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(cranelift_codegen::ir::StackSlotKind::ExplicitSlot, 8));
|
||||
self.local_slots.insert(index, slot);
|
||||
}
|
||||
fn store_local_i64(&mut self, index: usize) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use cranelift_codegen::ir::{types, condcodes::IntCC};
|
||||
if let Some(mut v) = self.value_stack.pop() {
|
||||
if !self.local_slots.contains_key(&index) { self.ensure_local_i64(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); }
|
||||
// Coerce to i64 if needed
|
||||
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 { 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) = self.local_slots.get(&index) { fb.ins().stack_store(v, slot, 0); }
|
||||
}
|
||||
}
|
||||
fn load_local_i64(&mut self, index: usize) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use cranelift_codegen::ir::types;
|
||||
if !self.local_slots.contains_key(&index) { self.ensure_local_i64(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); }
|
||||
if let Some(&slot) = self.local_slots.get(&index) {
|
||||
let v = fb.ins().stack_load(types::I64, slot, 0);
|
||||
self.value_stack.push(v);
|
||||
}
|
||||
}
|
||||
fn prepare_blocks(&mut self, count: usize) { use cranelift_frontend::FunctionBuilder; if count == 0 { return; } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); if self.blocks.len() < count { for _ in 0..(count - self.blocks.len()) { self.blocks.push(fb.create_block()); } } }
|
||||
fn switch_to_block(&mut self, index: usize) { use cranelift_frontend::FunctionBuilder; if index >= self.blocks.len() { return; } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); fb.switch_to_block(self.blocks[index]); self.current_block_index = Some(index); }
|
||||
fn ensure_block_params_i64(&mut self, index: usize, count: usize) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use cranelift_codegen::ir::types;
|
||||
if index >= self.blocks.len() { return; }
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
let b = self.blocks[index];
|
||||
let has_inst = fb.func.layout.first_inst(b).is_some();
|
||||
if !has_inst {
|
||||
let current = fb.func.dfg.block_params(b).len();
|
||||
if count > current { for _ in current..count { let _ = fb.append_block_param(b, types::I64); } }
|
||||
}
|
||||
self.block_param_counts.insert(index, count);
|
||||
}
|
||||
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);
|
||||
let b = if let Some(i) = self.current_block_index { self.blocks[i] } else if let Some(e) = self.entry_block { e } else { return; };
|
||||
let params = fb.func.dfg.block_params(b).to_vec();
|
||||
let v = params.get(pos).copied().unwrap_or_else(|| fb.ins().iconst(types::I64, 0));
|
||||
self.value_stack.push(v);
|
||||
}
|
||||
fn br_if_top_is_true(&mut self, then_index: usize, else_index: usize) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use cranelift_codegen::ir::{types, condcodes::IntCC};
|
||||
if then_index >= self.blocks.len() || else_index >= self.blocks.len() { return; }
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
let cond_val = if let Some(v) = self.value_stack.pop() { v } else { fb.ins().iconst(types::I64, 0) };
|
||||
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 b1 = if fb.func.dfg.value_type(cond_val) == types::I64 { fb.ins().icmp_imm(IntCC::NotEqual, cond_val, 0) } else { fb.ins().icmp_imm(IntCC::NotEqual, cond_val, 0) };
|
||||
fb.ins().brif(b1, self.blocks[then_index], &[], self.blocks[else_index], &[]);
|
||||
self.stats.3 += 1;
|
||||
}
|
||||
fn jump_to(&mut self, target_index: usize) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
if target_index >= self.blocks.len() { return; }
|
||||
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); }
|
||||
fb.ins().jump(self.blocks[target_index], &[]);
|
||||
self.stats.3 += 1;
|
||||
}
|
||||
fn emit_select_i64(&mut self) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use cranelift_codegen::ir::{types, condcodes::IntCC};
|
||||
if self.value_stack.len() < 3 { return; }
|
||||
let mut else_v = self.value_stack.pop().unwrap();
|
||||
let mut then_v = self.value_stack.pop().unwrap();
|
||||
let cond_v = 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 cond_b1 = if fb.func.dfg.value_type(cond_v) == types::I64 { fb.ins().icmp_imm(IntCC::NotEqual, cond_v, 0) } else { fb.ins().icmp_imm(IntCC::NotEqual, cond_v, 0) };
|
||||
if fb.func.dfg.value_type(then_v) != types::I64 { then_v = fb.ins().fcvt_to_sint(types::I64, then_v); }
|
||||
if fb.func.dfg.value_type(else_v) != types::I64 { else_v = fb.ins().fcvt_to_sint(types::I64, else_v); }
|
||||
let sel = fb.ins().select(cond_b1, then_v, else_v);
|
||||
self.value_stack.push(sel);
|
||||
}
|
||||
fn emit_host_call(&mut self, symbol: &str, argc: usize, has_ret: bool) {
|
||||
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
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 mut sig = Signature::new(self.module.isa().default_call_conv());
|
||||
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 hostcall");
|
||||
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::with_capacity(argc);
|
||||
for _ in 0..argc { if let Some(v) = self.value_stack.pop() { args.push(v); } else { args.push(fb.ins().iconst(types::I64, 0)); } }
|
||||
args.reverse();
|
||||
// Ensure i64 for all
|
||||
for a in args.iter_mut() { if fb.func.dfg.value_type(*a) != types::I64 { *a = fb.ins().fcvt_to_sint(types::I64, *a); } }
|
||||
let fref = self.module.declare_func_in_func(func_id, fb.func);
|
||||
let call_inst = fb.ins().call(fref, &args);
|
||||
if has_ret { if let Some(v) = fb.inst_results(call_inst).get(0).copied() { self.value_stack.push(v); } }
|
||||
}
|
||||
fn emit_host_call_typed(&mut self, symbol: &str, params: &[super::ParamKind], has_ret: bool, ret_is_f64: bool) {
|
||||
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
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 mut sig = Signature::new(self.module.isa().default_call_conv());
|
||||
for &k in params {
|
||||
match k { super::ParamKind::I64 => sig.params.push(AbiParam::new(types::I64)), super::ParamKind::F64 => sig.params.push(AbiParam::new(types::F64)), super::ParamKind::B1 => sig.params.push(AbiParam::new(types::I64)) }
|
||||
}
|
||||
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, cranelift_module::Linkage::Import, &sig).expect("declare hostcall typed");
|
||||
// Gather args from stack (reverse)
|
||||
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::with_capacity(params.len());
|
||||
for &k in params.iter().rev() {
|
||||
let mut v = if let Some(v) = self.value_stack.pop() {
|
||||
v
|
||||
} else {
|
||||
match k { super::ParamKind::I64 | super::ParamKind::B1 => fb.ins().iconst(types::I64, 0), super::ParamKind::F64 => fb.ins().f64const(0.0) }
|
||||
};
|
||||
// Coerce
|
||||
v = match k {
|
||||
super::ParamKind::I64 | super::ParamKind::B1 => { if fb.func.dfg.value_type(v) != types::I64 { fb.ins().fcvt_to_sint(types::I64, v) } else { v } },
|
||||
super::ParamKind::F64 => { if fb.func.dfg.value_type(v) != types::F64 { fb.ins().fcvt_from_sint(types::F64, v) } else { v } },
|
||||
};
|
||||
args.push(v);
|
||||
}
|
||||
args.reverse();
|
||||
let fref = self.module.declare_func_in_func(func_id, fb.func);
|
||||
let call_inst = fb.ins().call(fref, &args);
|
||||
if has_ret {
|
||||
if let Some(mut v) = fb.inst_results(call_inst).get(0).copied() {
|
||||
if ret_is_f64 && fb.func.dfg.value_type(v) != types::F64 { v = fb.ins().fcvt_from_sint(types::F64, v); }
|
||||
if !ret_is_f64 && fb.func.dfg.value_type(v) != types::I64 { v = fb.ins().fcvt_to_sint(types::I64, v); }
|
||||
self.value_stack.push(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn emit_host_call_fixed3(&mut self, symbol: &str, has_ret: bool) {
|
||||
self.emit_host_call(symbol, 3, has_ret);
|
||||
}
|
||||
fn emit_string_handle_from_literal(&mut self, s: &str) {
|
||||
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
// Pack up to 16 bytes of the literal into two u64 words
|
||||
let bytes = s.as_bytes();
|
||||
let mut lo: u64 = 0; let mut hi: u64 = 0;
|
||||
let take = core::cmp::min(16, bytes.len());
|
||||
for i in 0..take.min(8) { lo |= (bytes[i] as u64) << (8 * i as u32); }
|
||||
for i in 8..take { hi |= (bytes[i] as u64) << (8 * (i - 8) as u32); }
|
||||
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); }
|
||||
// Declare import: nyash.string.from_u64x2(lo, hi, len) -> i64
|
||||
let mut sig = Signature::new(self.module.isa().default_call_conv());
|
||||
sig.params.push(AbiParam::new(types::I64));
|
||||
sig.params.push(AbiParam::new(types::I64));
|
||||
sig.params.push(AbiParam::new(types::I64));
|
||||
sig.returns.push(AbiParam::new(types::I64));
|
||||
let func_id = self.module.declare_function("nyash.string.from_u64x2", cranelift_module::Linkage::Import, &sig).expect("declare string.from_u64x2");
|
||||
let lo_v = fb.ins().iconst(types::I64, lo as i64);
|
||||
let hi_v = fb.ins().iconst(types::I64, hi as i64);
|
||||
let len_v = fb.ins().iconst(types::I64, bytes.len() as i64);
|
||||
let fref = self.module.declare_func_in_func(func_id, fb.func);
|
||||
let call_inst = fb.ins().call(fref, &[lo_v, hi_v, len_v]);
|
||||
if let Some(v) = fb.inst_results(call_inst).get(0).copied() { self.value_stack.push(v); }
|
||||
}
|
||||
fn br_if_with_args(&mut self, then_index: usize, else_index: usize, then_n: usize, else_n: usize) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use cranelift_codegen::ir::{types, condcodes::IntCC};
|
||||
if then_index >= self.blocks.len() || else_index >= self.blocks.len() { return; }
|
||||
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); }
|
||||
// Pop else args, then then args (stack topに近い方から)
|
||||
let mut else_args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||
for _ in 0..else_n { if let Some(v) = self.value_stack.pop() { else_args.push(v); } else { else_args.push(fb.ins().iconst(types::I64, 0)); } }
|
||||
else_args.reverse();
|
||||
let mut then_args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||
for _ in 0..then_n { if let Some(v) = self.value_stack.pop() { then_args.push(v); } else { then_args.push(fb.ins().iconst(types::I64, 0)); } }
|
||||
then_args.reverse();
|
||||
// Cond
|
||||
let cond_val = if let Some(v) = self.value_stack.pop() { v } else { fb.ins().iconst(types::I64, 0) };
|
||||
let b1 = if fb.func.dfg.value_type(cond_val) == types::I64 { fb.ins().icmp_imm(IntCC::NotEqual, cond_val, 0) } else { fb.ins().icmp_imm(IntCC::NotEqual, cond_val, 0) };
|
||||
// Coerce args to i64
|
||||
for a in then_args.iter_mut() { if fb.func.dfg.value_type(*a) != types::I64 { *a = fb.ins().fcvt_to_sint(types::I64, *a); } }
|
||||
for a in else_args.iter_mut() { if fb.func.dfg.value_type(*a) != types::I64 { *a = fb.ins().fcvt_to_sint(types::I64, *a); } }
|
||||
fb.ins().brif(b1, self.blocks[then_index], &then_args, self.blocks[else_index], &else_args);
|
||||
self.stats.3 += 1;
|
||||
}
|
||||
fn jump_with_args(&mut self, target_index: usize, n: usize) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use cranelift_codegen::ir::types;
|
||||
if target_index >= self.blocks.len() { return; }
|
||||
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 mut args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||
for _ in 0..n { if let Some(v) = self.value_stack.pop() { args.push(v); } else { args.push(fb.ins().iconst(types::I64, 0)); } }
|
||||
args.reverse();
|
||||
for a in args.iter_mut() { if fb.func.dfg.value_type(*a) != types::I64 { *a = fb.ins().fcvt_to_sint(types::I64, *a); } }
|
||||
fb.ins().jump(self.blocks[target_index], &args);
|
||||
self.stats.3 += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,7 +182,87 @@ impl LowerCore {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Emit robust length retrieval with fallback for String/Any:
|
||||
/// 1) Prefer `nyash.string.len_h(recv)`
|
||||
/// 2) If that yields 0 at runtime, select `nyash.any.length_h(recv)`
|
||||
/// Returns: pushes selected length (i64) onto builder stack.
|
||||
fn emit_len_with_fallback_param(&mut self, b: &mut dyn IRBuilder, pidx: usize) {
|
||||
use super::builder::CmpKind;
|
||||
// Temp locals
|
||||
let t_string = self.next_local; self.next_local += 1;
|
||||
let t_any = self.next_local; self.next_local += 1;
|
||||
let t_cond = self.next_local; self.next_local += 1;
|
||||
// String.len_h
|
||||
b.emit_param_i64(pidx);
|
||||
b.emit_host_call("nyash.string.len_h", 1, true);
|
||||
b.store_local_i64(t_string);
|
||||
// Any.length_h
|
||||
b.emit_param_i64(pidx);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true);
|
||||
b.store_local_i64(t_any);
|
||||
// cond = (string_len == 0)
|
||||
b.load_local_i64(t_string);
|
||||
b.emit_const_i64(0);
|
||||
b.emit_compare(CmpKind::Eq);
|
||||
b.store_local_i64(t_cond);
|
||||
// select(cond ? any_len : string_len)
|
||||
b.load_local_i64(t_cond); // cond (bottom)
|
||||
b.load_local_i64(t_any); // then
|
||||
b.load_local_i64(t_string); // else
|
||||
b.emit_select_i64();
|
||||
}
|
||||
|
||||
fn emit_len_with_fallback_local_handle(&mut self, b: &mut dyn IRBuilder, slot: usize) {
|
||||
use super::builder::CmpKind;
|
||||
let t_string = self.next_local; self.next_local += 1;
|
||||
let t_any = self.next_local; self.next_local += 1;
|
||||
let t_cond = self.next_local; self.next_local += 1;
|
||||
// String.len_h
|
||||
b.load_local_i64(slot);
|
||||
b.emit_host_call("nyash.string.len_h", 1, true);
|
||||
b.store_local_i64(t_string);
|
||||
// Any.length_h
|
||||
b.load_local_i64(slot);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true);
|
||||
b.store_local_i64(t_any);
|
||||
// cond = (string_len == 0)
|
||||
b.load_local_i64(t_string);
|
||||
b.emit_const_i64(0);
|
||||
b.emit_compare(CmpKind::Eq);
|
||||
b.store_local_i64(t_cond);
|
||||
// select(cond ? any_len : string_len)
|
||||
b.load_local_i64(t_cond);
|
||||
b.load_local_i64(t_any);
|
||||
b.load_local_i64(t_string);
|
||||
b.emit_select_i64();
|
||||
}
|
||||
|
||||
fn emit_len_with_fallback_literal(&mut self, b: &mut dyn IRBuilder, s: &str) {
|
||||
use super::builder::CmpKind;
|
||||
let t_string = self.next_local; self.next_local += 1;
|
||||
let t_any = self.next_local; self.next_local += 1;
|
||||
let t_cond = self.next_local; self.next_local += 1;
|
||||
// String.len_h on literal handle
|
||||
b.emit_string_handle_from_literal(s);
|
||||
b.emit_host_call("nyash.string.len_h", 1, true);
|
||||
b.store_local_i64(t_string);
|
||||
// Any.length_h on literal handle (recreate handle; safe in v0)
|
||||
b.emit_string_handle_from_literal(s);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true);
|
||||
b.store_local_i64(t_any);
|
||||
// cond = (string_len == 0)
|
||||
b.load_local_i64(t_string);
|
||||
b.emit_const_i64(0);
|
||||
b.emit_compare(CmpKind::Eq);
|
||||
b.store_local_i64(t_cond);
|
||||
// select(cond ? any_len : string_len)
|
||||
b.load_local_i64(t_cond);
|
||||
b.load_local_i64(t_any);
|
||||
b.load_local_i64(t_string);
|
||||
b.emit_select_i64();
|
||||
}
|
||||
|
||||
|
||||
fn try_emit(&mut self, b: &mut dyn IRBuilder, instr: &MirInstruction, cur_bb: crate::mir::BasicBlockId, func: &crate::mir::MirFunction) -> Result<(), String> {
|
||||
use crate::mir::MirInstruction as I;
|
||||
match instr {
|
||||
@ -425,13 +505,21 @@ 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); }
|
||||
// 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);
|
||||
}
|
||||
// Propagate boolean classification through Copy
|
||||
if self.bool_values.contains(src) { self.bool_values.insert(*dst); }
|
||||
// Otherwise no-op for codegen (stack-machine handles sources directly later)
|
||||
// If source is a parameter, materialize it on the stack for downstream ops and persist into dst slot
|
||||
if let Some(pidx) = self.param_index.get(src).copied() {
|
||||
b.emit_param_i64(pidx);
|
||||
let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||
b.ensure_local_i64(slot);
|
||||
b.store_local_i64(slot);
|
||||
} else if let Some(src_slot) = self.local_index.get(src).copied() {
|
||||
// If source already has a local slot (e.g., a handle), copy into dst's slot
|
||||
b.load_local_i64(src_slot);
|
||||
let dst_slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||
b.ensure_local_i64(dst_slot);
|
||||
b.store_local_i64(dst_slot);
|
||||
}
|
||||
}
|
||||
I::BinOp { dst, op, lhs, rhs } => { self.lower_binop(b, op, lhs, rhs, dst, func); }
|
||||
I::Compare { op, lhs, rhs, dst } => { self.lower_compare(b, op, lhs, rhs, dst, func); }
|
||||
@ -470,11 +558,15 @@ impl LowerCore {
|
||||
b.ensure_local_i64(slot);
|
||||
b.store_local_i64(slot);
|
||||
}
|
||||
I::Load { dst: _, ptr } => {
|
||||
// Minimal lowering: load from local slot keyed by ptr, default 0 if unset
|
||||
let slot = *self.local_index.entry(*ptr).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||
b.ensure_local_i64(slot);
|
||||
b.load_local_i64(slot);
|
||||
I::Load { dst, ptr } => {
|
||||
// Minimal lowering: load from local slot keyed by ptr, then materialize into dst's own slot
|
||||
let src_slot = *self.local_index.entry(*ptr).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||
b.ensure_local_i64(src_slot);
|
||||
b.load_local_i64(src_slot);
|
||||
// Persist into dst's slot to make subsequent uses find it via local_index
|
||||
let dst_slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||
b.ensure_local_i64(dst_slot);
|
||||
b.store_local_i64(dst_slot);
|
||||
}
|
||||
I::Phi { dst, .. } => {
|
||||
// PHI をローカルに materialize して後続の Return で安定参照
|
||||
@ -525,9 +617,10 @@ impl LowerCore {
|
||||
}
|
||||
}
|
||||
I::BoxCall { box_val: array, method, args, dst, .. } => {
|
||||
// Clean path: delegate to ops_ext and return
|
||||
let _ = self.lower_box_call(func, b, &array, method.as_str(), args, dst.clone())?;
|
||||
return Ok(());
|
||||
// Prefer ops_ext; if not handled, fall back to legacy path below
|
||||
if self.lower_box_call(func, b, &array, method.as_str(), args, dst.clone())? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
/* legacy BoxCall branch removed (now handled in ops_ext)
|
||||
// handled in helper (read-only simple methods)
|
||||
@ -663,21 +756,80 @@ impl LowerCore {
|
||||
} else if crate::jit::config::current().hostcall {
|
||||
match method.as_str() {
|
||||
"len" | "length" => {
|
||||
// Constant fold: if receiver is NewBox(StringBox, Const String), return its length directly
|
||||
if let Some(did) = dst.as_ref() {
|
||||
let mut lit_len: Option<i64> = None;
|
||||
for (_bid, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::NewBox { dst: ndst, box_type, args } = ins {
|
||||
if ndst == array && box_type == "StringBox" && args.len() == 1 {
|
||||
let src = args[0];
|
||||
if let Some(s) = self.known_str.get(&src) { lit_len = Some(s.len() as i64); break; }
|
||||
// scan Const directly
|
||||
for (_b2, bb2) in func.blocks.iter() {
|
||||
for ins2 in bb2.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::Const { dst: cdst, value } = ins2 { if *cdst == src { if let crate::mir::ConstValue::String(sv) = value { lit_len = Some(sv.len() as i64); break; } } }
|
||||
}
|
||||
if lit_len.is_some() { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if lit_len.is_some() { break; }
|
||||
}
|
||||
if let Some(n) = lit_len {
|
||||
b.emit_const_i64(n);
|
||||
self.known_i64.insert(*did, n);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
if let Some(pidx) = self.param_index.get(array).copied() {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_LEN_H, "decision":"allow", "reason":"sig_ok", "argc":1, "arg_types":["Handle"]}),
|
||||
"hostcall","<jit>"
|
||||
);
|
||||
b.emit_param_i64(pidx);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, dst.is_some());
|
||||
// Param 経路: string.len_h → 0 の場合 any.length_h へフォールバック
|
||||
self.emit_len_with_fallback_param(b, pidx);
|
||||
} else {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_LEN_H, "decision":"fallback", "reason":"receiver_not_param", "argc":1, "arg_types":["Handle"]}),
|
||||
"hostcall","<jit>"
|
||||
);
|
||||
let arr_idx = -1;
|
||||
b.emit_const_i64(arr_idx);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, dst.is_some());
|
||||
// Try local handle (AOT/JIT-AOT) before legacy index fallback
|
||||
if let Some(slot) = self.local_index.get(array).copied() {
|
||||
// ローカルハンドル: string.len_h → any.length_h フォールバック
|
||||
self.emit_len_with_fallback_local_handle(b, slot);
|
||||
} else if self.box_type_map.get(array).map(|s| s == "StringBox").unwrap_or(false) {
|
||||
// Attempt reconstruction for StringBox literal: scan NewBox(StringBox, Const String)
|
||||
let mut lit: Option<String> = None;
|
||||
for (_bid, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::NewBox { dst, box_type, args } = ins {
|
||||
if dst == array && box_type == "StringBox" && args.len() == 1 {
|
||||
if let Some(src) = args.get(0) {
|
||||
if let Some(s) = self.known_str.get(src).cloned() { lit = Some(s); break; }
|
||||
// Also scan Const directly
|
||||
for (_bid2, bb2) in func.blocks.iter() {
|
||||
for ins2 in bb2.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::Const { dst: cdst, value } = ins2 { if cdst == src { if let crate::mir::ConstValue::String(sv) = value { lit = Some(sv.clone()); break; } } }
|
||||
}
|
||||
if lit.is_some() { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if lit.is_some() { break; }
|
||||
}
|
||||
if let Some(s) = lit {
|
||||
// リテラル復元: string.len_h → any.length_h フォールバック
|
||||
self.emit_len_with_fallback_literal(b, &s);
|
||||
} else {
|
||||
let arr_idx = -1;
|
||||
b.emit_const_i64(arr_idx);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, dst.is_some());
|
||||
}
|
||||
} else {
|
||||
let arr_idx = -1;
|
||||
b.emit_const_i64(arr_idx);
|
||||
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)
|
||||
|
||||
@ -17,7 +17,7 @@ impl LowerCore {
|
||||
let m = method;
|
||||
if (bt == "PyRuntimeBox" && (m == "import")) {
|
||||
let argc = 1 + args.len();
|
||||
if let Some(pidx) = self.param_index.get(box_val).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); }
|
||||
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 {
|
||||
b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some());
|
||||
@ -35,7 +35,7 @@ impl LowerCore {
|
||||
}
|
||||
} else if self.handle_values.contains(box_val) && (m == "getattr" || m == "call") {
|
||||
let argc = 1 + args.len();
|
||||
b.emit_const_i64(-1);
|
||||
if let Some(slot) = self.local_index.get(box_val).copied() { b.load_local_i64(slot); } else { b.emit_const_i64(-1); }
|
||||
for a in args.iter() { self.push_value_if_known_or_param(b, a); }
|
||||
b.emit_plugin_invoke_by_name(m, argc, dst.is_some());
|
||||
if let Some(d) = dst {
|
||||
@ -228,7 +228,7 @@ impl LowerCore {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
// String.len: (1) const string → 定数埋め込み、(2) StringBox → host-bridge
|
||||
// String.len/length: robust handling
|
||||
"len" => {
|
||||
// (1) const string literal case
|
||||
let mut lit_len: Option<i64> = None;
|
||||
@ -247,20 +247,96 @@ impl LowerCore {
|
||||
b.emit_const_i64(n);
|
||||
return Ok(true);
|
||||
}
|
||||
// (2) StringBox via host-bridge
|
||||
// (2) prefer host-bridge when enabled
|
||||
if std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1") {
|
||||
if let Some(bt) = self.box_type_map.get(array) {
|
||||
if bt == "StringBox" {
|
||||
if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref() == Some("1") { eprintln!("[LOWER]string.len via host-bridge"); }
|
||||
self.push_value_if_known_or_param(b, array);
|
||||
b.emit_host_call(crate::jit::r#extern::host_bridge::SYM_HOST_STRING_LEN, 1, dst.is_some());
|
||||
return Ok(true);
|
||||
}
|
||||
if self.box_type_map.get(array).map(|s| s == "StringBox").unwrap_or(false) {
|
||||
if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref() == Some("1") { eprintln!("[LOWER]string.len via host-bridge"); }
|
||||
self.push_value_if_known_or_param(b, array);
|
||||
b.emit_host_call(crate::jit::r#extern::host_bridge::SYM_HOST_STRING_LEN, 1, dst.is_some());
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
// (3) Fallback: emit string.len_h with Any.length_h guard
|
||||
if self.box_type_map.get(array).map(|s| s == "StringBox").unwrap_or(false) {
|
||||
if let Some(pidx) = self.param_index.get(array).copied() {
|
||||
self.emit_len_with_fallback_param(b, pidx);
|
||||
return Ok(true);
|
||||
}
|
||||
if let Some(slot) = self.local_index.get(array).copied() {
|
||||
self.emit_len_with_fallback_local_handle(b, slot);
|
||||
return Ok(true);
|
||||
}
|
||||
// Try to reconstruct literal handle
|
||||
let mut lit: Option<String> = None;
|
||||
for (_bid, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::NewBox { dst, box_type, args } = ins {
|
||||
if dst == array && box_type == "StringBox" && args.len() == 1 {
|
||||
if let Some(src) = args.get(0) {
|
||||
if let Some(s) = self.known_str.get(src).cloned() { lit = Some(s); break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if lit.is_some() { break; }
|
||||
}
|
||||
if let Some(s) = lit { self.emit_len_with_fallback_literal(b, &s); return Ok(true); }
|
||||
// As a last resort, convert receiver to handle via nyash.handle.of and apply fallback on temp slot
|
||||
self.push_value_if_known_or_param(b, array);
|
||||
b.emit_host_call("nyash.handle.of", 1, true);
|
||||
let t_recv = { let id = self.next_local; self.next_local += 1; id };
|
||||
b.store_local_i64(t_recv);
|
||||
self.emit_len_with_fallback_local_handle(b, t_recv);
|
||||
return Ok(true);
|
||||
}
|
||||
// Not a StringBox: let other branches handle
|
||||
return Ok(false);
|
||||
}
|
||||
// Array length variants (length/len)
|
||||
// Alias: String.length → same as len
|
||||
"length" => {
|
||||
if self.box_type_map.get(array).map(|s| s == "StringBox").unwrap_or(false) {
|
||||
// Reuse len handler
|
||||
return self.lower_box_call(func, b, array, "len", args, dst);
|
||||
}
|
||||
// Array length is handled below; otherwise not handled here
|
||||
return Ok(false);
|
||||
}
|
||||
// Array/String length variants (length/len)
|
||||
"len" | "length" => {
|
||||
match self.box_type_map.get(array).map(|s| s.as_str()) {
|
||||
Some("StringBox") => {
|
||||
if let Some(pidx) = self.param_index.get(array).copied() {
|
||||
self.emit_len_with_fallback_param(b, pidx);
|
||||
return Ok(true);
|
||||
}
|
||||
if let Some(slot) = self.local_index.get(array).copied() {
|
||||
self.emit_len_with_fallback_local_handle(b, slot);
|
||||
return Ok(true);
|
||||
}
|
||||
// Try literal reconstruction
|
||||
let mut lit: Option<String> = None;
|
||||
for (_bid, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::NewBox { dst, box_type, args } = ins {
|
||||
if dst == array && box_type == "StringBox" && args.len() == 1 {
|
||||
if let Some(src) = args.get(0) { if let Some(s) = self.known_str.get(src).cloned() { lit = Some(s); break; } }
|
||||
}
|
||||
}
|
||||
}
|
||||
if lit.is_some() { break; }
|
||||
}
|
||||
if let Some(s) = lit { self.emit_len_with_fallback_literal(b, &s); return Ok(true); }
|
||||
// Last resort: handle.of
|
||||
self.push_value_if_known_or_param(b, array);
|
||||
b.emit_host_call("nyash.handle.of", 1, true);
|
||||
let slot = { let id = self.next_local; self.next_local += 1; id };
|
||||
b.store_local_i64(slot);
|
||||
self.emit_len_with_fallback_local_handle(b, slot);
|
||||
return Ok(true);
|
||||
}
|
||||
Some("ArrayBox") => {},
|
||||
_ => { return Ok(false); }
|
||||
}
|
||||
if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() {
|
||||
if let Ok(h) = ph.resolve_method("ArrayBox", "length") {
|
||||
if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); }
|
||||
@ -317,6 +393,18 @@ impl LowerCore {
|
||||
return Ok(true);
|
||||
}
|
||||
let argc = match method { "size" => 1, "get" | "has" => 2, "set" => 3, _ => 1 };
|
||||
// If receiver is a local handle (AOT/JIT-AOT), prefer handle-based hostcalls directly
|
||||
if self.handle_values.contains(array) {
|
||||
self.push_value_if_known_or_param(b, array);
|
||||
match method {
|
||||
"size" => b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_SIZE_H, argc, dst.is_some()),
|
||||
"get" => { if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_GET_H, argc, dst.is_some()) }
|
||||
"has" => { if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_HAS_H, argc, dst.is_some()) }
|
||||
"set" => { if let Some(k) = args.get(0) { self.push_value_if_known_or_param(b, k); } else { b.emit_const_i64(0); } if let Some(v) = args.get(1) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_SET_H, argc, dst.is_some()) }
|
||||
_ => {}
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() {
|
||||
if let Ok(h) = ph.resolve_method("MapBox", method) {
|
||||
// receiver
|
||||
@ -366,10 +454,10 @@ impl LowerCore {
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
// receiver unknown
|
||||
b.emit_const_i64(-1);
|
||||
// receiver unknown: try local handle (AOT/JIT-AOT)
|
||||
self.push_value_if_known_or_param(b, array);
|
||||
match method {
|
||||
"size" => b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_SIZE, argc, dst.is_some()),
|
||||
"size" => b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_SIZE_H, argc, dst.is_some()),
|
||||
"get" => {
|
||||
if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); }
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_GET_H, argc, dst.is_some())
|
||||
@ -381,7 +469,7 @@ impl LowerCore {
|
||||
"set" => {
|
||||
if let Some(k) = args.get(0) { self.push_value_if_known_or_param(b, k); } else { b.emit_const_i64(0); }
|
||||
if let Some(v) = args.get(1) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); }
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_SET, argc, dst.is_some())
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_SET_H, argc, dst.is_some())
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@ -326,100 +326,7 @@ pub fn lower_box_call(
|
||||
}
|
||||
}
|
||||
|
||||
// Handle simple read-only BoxCall methods. Returns true if handled.
|
||||
pub fn lower_boxcall_simple_reads(
|
||||
b: &mut dyn IRBuilder,
|
||||
param_index: &HashMap<ValueId, usize>,
|
||||
known_i64: &HashMap<ValueId, i64>,
|
||||
recv: &ValueId,
|
||||
method: &str,
|
||||
args: &Vec<ValueId>,
|
||||
dst: Option<ValueId>,
|
||||
) -> bool {
|
||||
if !crate::jit::config::current().hostcall { return false; }
|
||||
// When plugin builtins are enabled, prefer plugin_invoke for length to exercise shim path
|
||||
let use_plugin = std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1");
|
||||
match method {
|
||||
// Any.length / Array.length
|
||||
"len" | "length" => {
|
||||
if use_plugin { return false; }
|
||||
if let Some(pidx) = param_index.get(recv).copied() {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_LEN_H, "decision":"allow", "reason":"sig_ok", "argc":1, "arg_types":["Handle"]}),
|
||||
"hostcall","<jit>"
|
||||
);
|
||||
b.emit_param_i64(pidx);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, dst.is_some());
|
||||
} else {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_LEN_H, "decision":"fallback", "reason":"receiver_not_param", "argc":1, "arg_types":["Handle"]}),
|
||||
"hostcall","<jit>"
|
||||
);
|
||||
let arr_idx = -1;
|
||||
b.emit_const_i64(arr_idx);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ARRAY_LEN, 1, dst.is_some());
|
||||
}
|
||||
true
|
||||
}
|
||||
// Any.isEmpty
|
||||
"isEmpty" | "empty" | "is_empty" => {
|
||||
if let Some(pidx) = param_index.get(recv).copied() {
|
||||
crate::jit::events::emit(
|
||||
"hostcall","<jit>",None,None,
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, "decision":"allow", "reason":"sig_ok", "argc":1, "arg_types":["Handle"]})
|
||||
);
|
||||
b.emit_param_i64(pidx);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, 1, dst.is_some());
|
||||
} else {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, "decision":"fallback", "reason":"receiver_not_param", "argc":1, "arg_types":["Handle"]}),
|
||||
"hostcall","<jit>"
|
||||
);
|
||||
}
|
||||
true
|
||||
}
|
||||
// Map.size
|
||||
"size" => {
|
||||
if let Some(pidx) = param_index.get(recv).copied() {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_MAP_SIZE_H, "decision":"allow", "reason":"sig_ok", "argc":1, "arg_types":["Handle"]}),
|
||||
"hostcall","<jit>"
|
||||
);
|
||||
b.emit_param_i64(pidx);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_SIZE_H, 1, dst.is_some());
|
||||
} else {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_MAP_SIZE_H, "decision":"fallback", "reason":"receiver_not_param", "argc":1, "arg_types":["Handle"]}),
|
||||
"hostcall","<jit>"
|
||||
);
|
||||
let map_idx = -1;
|
||||
b.emit_const_i64(map_idx);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_MAP_SIZE, 1, dst.is_some());
|
||||
}
|
||||
true
|
||||
}
|
||||
// String.charCodeAt(index)
|
||||
"charCodeAt" => {
|
||||
if let Some(pidx) = param_index.get(recv).copied() {
|
||||
let idx = args.get(0).and_then(|v| known_i64.get(v).copied()).unwrap_or(0);
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, "decision":"allow", "reason":"sig_ok", "argc":2, "arg_types":["Handle","I64"]}),
|
||||
"hostcall","<jit>"
|
||||
);
|
||||
b.emit_param_i64(pidx);
|
||||
b.emit_const_i64(idx);
|
||||
b.emit_host_call(crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, 2, dst.is_some());
|
||||
} else {
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({"id": crate::jit::r#extern::collections::SYM_STRING_CHARCODE_AT_H, "decision":"fallback", "reason":"receiver_not_param", "argc":2, "arg_types":["Handle","I64"]}),
|
||||
"hostcall","<jit>"
|
||||
);
|
||||
}
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
// (was: lower_boxcall_simple_reads) Removed; logic consolidated in core.rs length/charCodeAt handlers.
|
||||
|
||||
// Map.get(key): handle I64 and HH variants with registry check and events
|
||||
pub fn lower_map_get(
|
||||
|
||||
@ -18,7 +18,8 @@ fn use_plugin_builtins() -> bool {
|
||||
pub fn decide_box_method(box_type: &str, method: &str, argc: usize, has_ret: bool) -> InvokeDecision {
|
||||
// HostCall mapping for common collections/strings/instance ops
|
||||
let symbol = match (box_type, method) {
|
||||
("ArrayBox", "length") | ("StringBox", "length") | ("StringBox", "len") => crate::jit::r#extern::collections::SYM_ANY_LEN_H,
|
||||
("ArrayBox", "length") => crate::jit::r#extern::collections::SYM_ANY_LEN_H,
|
||||
("StringBox", "length") | ("StringBox", "len") => "nyash.string.len_h",
|
||||
("ArrayBox", "get") => crate::jit::r#extern::collections::SYM_ARRAY_GET_H,
|
||||
("ArrayBox", "set") => crate::jit::r#extern::collections::SYM_ARRAY_SET_H,
|
||||
("ArrayBox", "push") => crate::jit::r#extern::collections::SYM_ARRAY_PUSH_H,
|
||||
|
||||
Reference in New Issue
Block a user