2025-08-27 17:06:46 +09:00
|
|
|
|
use crate::mir::{MirFunction, MirInstruction, ConstValue, BinaryOp, CompareOp, ValueId};
|
2025-08-27 03:16:57 +09:00
|
|
|
|
use super::builder::{IRBuilder, BinOpKind, CmpKind};
|
|
|
|
|
|
|
|
|
|
|
|
/// Lower(Core-1): Minimal lowering skeleton for Const/Move/BinOp/Cmp/Branch/Ret
|
|
|
|
|
|
/// This does not emit real CLIF yet; it only walks MIR and validates coverage.
|
|
|
|
|
|
pub struct LowerCore {
|
|
|
|
|
|
pub unsupported: usize,
|
|
|
|
|
|
pub covered: usize,
|
2025-08-27 17:06:46 +09:00
|
|
|
|
/// Minimal constant propagation for i64 to feed host-call args
|
2025-08-29 03:28:42 +09:00
|
|
|
|
pub(super) known_i64: std::collections::HashMap<ValueId, i64>,
|
2025-08-28 12:09:09 +09:00
|
|
|
|
/// Minimal constant propagation for f64 (math.* signature checks)
|
|
|
|
|
|
known_f64: std::collections::HashMap<ValueId, f64>,
|
2025-08-27 17:06:46 +09:00
|
|
|
|
/// Parameter index mapping for ValueId
|
|
|
|
|
|
param_index: std::collections::HashMap<ValueId, usize>,
|
2025-08-27 17:58:50 +09:00
|
|
|
|
/// Track values produced by Phi (for minimal PHI path)
|
|
|
|
|
|
phi_values: std::collections::HashSet<ValueId>,
|
2025-08-28 09:26:58 +09:00
|
|
|
|
/// Map (block, phi dst) -> param index in that block (for multi-PHI)
|
|
|
|
|
|
phi_param_index: std::collections::HashMap<(crate::mir::BasicBlockId, ValueId), usize>,
|
|
|
|
|
|
/// Track values that are boolean (b1) results, e.g., Compare destinations
|
2025-08-29 03:28:42 +09:00
|
|
|
|
pub(super) bool_values: std::collections::HashSet<ValueId>,
|
2025-08-28 09:26:58 +09:00
|
|
|
|
/// Track PHI destinations that are boolean (all inputs derived from bool_values)
|
|
|
|
|
|
bool_phi_values: std::collections::HashSet<ValueId>,
|
2025-08-28 12:09:09 +09:00
|
|
|
|
/// Track values that are FloatBox instances (for arg type classification)
|
|
|
|
|
|
float_box_values: std::collections::HashSet<ValueId>,
|
2025-08-28 09:26:58 +09:00
|
|
|
|
// Per-function statistics (last lowered)
|
|
|
|
|
|
last_phi_total: u64,
|
|
|
|
|
|
last_phi_b1: u64,
|
|
|
|
|
|
last_ret_bool_hint_used: bool,
|
|
|
|
|
|
// Minimal local slot mapping for Load/Store (ptr ValueId -> slot index)
|
|
|
|
|
|
local_index: std::collections::HashMap<ValueId, usize>,
|
|
|
|
|
|
next_local: usize,
|
2025-08-27 03:16:57 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl LowerCore {
|
2025-08-28 12:09:09 +09:00
|
|
|
|
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 } }
|
2025-08-28 09:26:58 +09:00
|
|
|
|
|
|
|
|
|
|
/// 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) }
|
2025-08-27 03:16:57 +09:00
|
|
|
|
|
|
|
|
|
|
/// Walk the MIR function and count supported/unsupported instructions.
|
|
|
|
|
|
/// In the future, this will build CLIF via Cranelift builders.
|
|
|
|
|
|
pub fn lower_function(&mut self, func: &MirFunction, builder: &mut dyn IRBuilder) -> Result<(), String> {
|
2025-08-27 17:06:46 +09:00
|
|
|
|
// Prepare a simple i64 ABI based on param count; always assume i64 return for now
|
2025-08-28 09:26:58 +09:00
|
|
|
|
// Reset per-function stats
|
|
|
|
|
|
self.last_phi_total = 0; self.last_phi_b1 = 0; self.last_ret_bool_hint_used = false;
|
2025-08-27 17:06:46 +09:00
|
|
|
|
// Build param index map
|
|
|
|
|
|
self.param_index.clear();
|
|
|
|
|
|
for (i, v) in func.params.iter().copied().enumerate() {
|
|
|
|
|
|
self.param_index.insert(v, i);
|
|
|
|
|
|
}
|
2025-08-27 17:52:37 +09:00
|
|
|
|
// Prepare block mapping (Phase 10.7): deterministic ordering by sorted keys
|
2025-08-27 17:08:49 +09:00
|
|
|
|
let mut bb_ids: Vec<_> = func.blocks.keys().copied().collect();
|
|
|
|
|
|
bb_ids.sort_by_key(|b| b.0);
|
|
|
|
|
|
builder.prepare_blocks(bb_ids.len());
|
2025-08-28 09:26:58 +09:00
|
|
|
|
// Seed boolean lattice with boolean parameters from MIR signature
|
|
|
|
|
|
if !func.signature.params.is_empty() {
|
|
|
|
|
|
for (idx, vid) in func.params.iter().copied().enumerate() {
|
|
|
|
|
|
if let Some(mt) = func.signature.params.get(idx) {
|
|
|
|
|
|
if matches!(mt, crate::mir::MirType::Bool) {
|
|
|
|
|
|
self.bool_values.insert(vid);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Pre-scan to classify boolean-producing values and propagate via Copy/Phi/Load-Store heuristics.
|
|
|
|
|
|
self.bool_values.clear();
|
|
|
|
|
|
let mut copy_edges: Vec<(crate::mir::ValueId, crate::mir::ValueId)> = Vec::new();
|
|
|
|
|
|
let mut phi_defs: Vec<(crate::mir::ValueId, Vec<crate::mir::ValueId>)> = Vec::new();
|
|
|
|
|
|
let mut stores: Vec<(crate::mir::ValueId, crate::mir::ValueId)> = Vec::new(); // (ptr, value)
|
|
|
|
|
|
let mut loads: Vec<(crate::mir::ValueId, crate::mir::ValueId)> = Vec::new(); // (dst, ptr)
|
|
|
|
|
|
for bb in bb_ids.iter() {
|
|
|
|
|
|
if let Some(block) = func.blocks.get(bb) {
|
|
|
|
|
|
for ins in block.instructions.iter() {
|
|
|
|
|
|
match ins {
|
|
|
|
|
|
crate::mir::MirInstruction::Compare { dst, .. } => { self.bool_values.insert(*dst); }
|
|
|
|
|
|
crate::mir::MirInstruction::Const { dst, value } => {
|
|
|
|
|
|
if let ConstValue::Bool(_) = value { self.bool_values.insert(*dst); }
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::mir::MirInstruction::Cast { dst, target_type, .. } => {
|
|
|
|
|
|
if matches!(target_type, crate::mir::MirType::Bool) { self.bool_values.insert(*dst); }
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::mir::MirInstruction::TypeOp { dst, op, ty, .. } => {
|
|
|
|
|
|
// Check and cast-to-bool produce boolean
|
|
|
|
|
|
if matches!(op, crate::mir::TypeOpKind::Check) || matches!(ty, crate::mir::MirType::Bool) { self.bool_values.insert(*dst); }
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::mir::MirInstruction::Copy { dst, src } => { copy_edges.push((*dst, *src)); }
|
|
|
|
|
|
crate::mir::MirInstruction::Phi { dst, inputs } => {
|
|
|
|
|
|
let vs: Vec<_> = inputs.iter().map(|(_, v)| *v).collect();
|
|
|
|
|
|
phi_defs.push((*dst, vs));
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::mir::MirInstruction::Store { value, ptr } => { stores.push((*ptr, *value)); }
|
|
|
|
|
|
crate::mir::MirInstruction::Load { dst, ptr } => { loads.push((*dst, *ptr)); }
|
|
|
|
|
|
_ => {}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if let Some(term) = &block.terminator {
|
|
|
|
|
|
match term {
|
|
|
|
|
|
crate::mir::MirInstruction::Compare { dst, .. } => { self.bool_values.insert(*dst); }
|
|
|
|
|
|
crate::mir::MirInstruction::Const { dst, value } => {
|
|
|
|
|
|
if let ConstValue::Bool(_) = value { self.bool_values.insert(*dst); }
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::mir::MirInstruction::Cast { dst, target_type, .. } => {
|
|
|
|
|
|
if matches!(target_type, crate::mir::MirType::Bool) { self.bool_values.insert(*dst); }
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::mir::MirInstruction::TypeOp { dst, op, ty, .. } => {
|
|
|
|
|
|
if matches!(op, crate::mir::TypeOpKind::Check) || matches!(ty, crate::mir::MirType::Bool) { self.bool_values.insert(*dst); }
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::mir::MirInstruction::Copy { dst, src } => { copy_edges.push((*dst, *src)); }
|
|
|
|
|
|
crate::mir::MirInstruction::Phi { dst, inputs } => {
|
|
|
|
|
|
let vs: Vec<_> = inputs.iter().map(|(_, v)| *v).collect();
|
|
|
|
|
|
phi_defs.push((*dst, vs));
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::mir::MirInstruction::Branch { condition, .. } => { self.bool_values.insert(*condition); }
|
|
|
|
|
|
crate::mir::MirInstruction::Store { value, ptr } => { stores.push((*ptr, *value)); }
|
|
|
|
|
|
crate::mir::MirInstruction::Load { dst, ptr } => { loads.push((*dst, *ptr)); }
|
|
|
|
|
|
_ => {}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Fixed-point boolean lattice propagation
|
|
|
|
|
|
let mut changed = true;
|
|
|
|
|
|
let mut store_bool_ptrs: std::collections::HashSet<crate::mir::ValueId> = std::collections::HashSet::new();
|
|
|
|
|
|
while changed {
|
|
|
|
|
|
changed = false;
|
|
|
|
|
|
// Copy propagation
|
|
|
|
|
|
for (dst, src) in copy_edges.iter().copied() {
|
|
|
|
|
|
if self.bool_values.contains(&src) && !self.bool_values.contains(&dst) {
|
|
|
|
|
|
self.bool_values.insert(dst);
|
|
|
|
|
|
changed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
// Pointer alias propagation for Store/Load lattice
|
|
|
|
|
|
if store_bool_ptrs.contains(&src) && !store_bool_ptrs.contains(&dst) {
|
|
|
|
|
|
store_bool_ptrs.insert(dst);
|
|
|
|
|
|
changed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Store marking
|
|
|
|
|
|
for (ptr, val) in stores.iter().copied() {
|
|
|
|
|
|
if self.bool_values.contains(&val) && !store_bool_ptrs.contains(&ptr) {
|
|
|
|
|
|
store_bool_ptrs.insert(ptr);
|
|
|
|
|
|
changed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Load propagation
|
|
|
|
|
|
for (dst, ptr) in loads.iter().copied() {
|
|
|
|
|
|
if store_bool_ptrs.contains(&ptr) && !self.bool_values.contains(&dst) {
|
|
|
|
|
|
self.bool_values.insert(dst);
|
|
|
|
|
|
changed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// PHI closure for value booleans
|
|
|
|
|
|
for (dst, inputs) in phi_defs.iter() {
|
|
|
|
|
|
if inputs.iter().all(|v| self.bool_values.contains(v)) && !self.bool_values.contains(dst) {
|
|
|
|
|
|
self.bool_values.insert(*dst);
|
|
|
|
|
|
self.bool_phi_values.insert(*dst);
|
|
|
|
|
|
changed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// PHI closure for pointer aliases: if all inputs are bool-storing pointers, mark dst pointer as such
|
|
|
|
|
|
for (dst, inputs) in phi_defs.iter() {
|
|
|
|
|
|
if inputs.iter().all(|v| store_bool_ptrs.contains(v)) && !store_bool_ptrs.contains(dst) {
|
|
|
|
|
|
store_bool_ptrs.insert(*dst);
|
|
|
|
|
|
changed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Always-on PHI statistics: count total/b1 phi slots using current heuristics
|
|
|
|
|
|
{
|
|
|
|
|
|
use crate::mir::MirInstruction;
|
|
|
|
|
|
let mut total_phi_slots: usize = 0;
|
|
|
|
|
|
let mut total_phi_b1_slots: usize = 0;
|
|
|
|
|
|
for (dst, inputs) in phi_defs.iter() {
|
|
|
|
|
|
total_phi_slots += 1;
|
|
|
|
|
|
// Heuristics consistent with dump path
|
|
|
|
|
|
let used_as_branch = func.blocks.values().any(|bbx| {
|
|
|
|
|
|
if let Some(MirInstruction::Branch { condition, .. }) = &bbx.terminator { condition == dst } else { false }
|
|
|
|
|
|
});
|
|
|
|
|
|
let is_b1 = self.bool_phi_values.contains(dst)
|
|
|
|
|
|
|| inputs.iter().all(|v| {
|
|
|
|
|
|
self.bool_values.contains(v) || self.known_i64.get(v).map(|&iv| iv == 0 || iv == 1).unwrap_or(false)
|
|
|
|
|
|
})
|
|
|
|
|
|
|| used_as_branch;
|
|
|
|
|
|
if is_b1 { total_phi_b1_slots += 1; }
|
|
|
|
|
|
}
|
|
|
|
|
|
if total_phi_slots > 0 {
|
|
|
|
|
|
crate::jit::rt::phi_total_inc(total_phi_slots as u64);
|
|
|
|
|
|
crate::jit::rt::phi_b1_inc(total_phi_b1_slots as u64);
|
|
|
|
|
|
self.last_phi_total = total_phi_slots as u64;
|
|
|
|
|
|
self.last_phi_b1 = total_phi_b1_slots as u64;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Optional: collect PHI targets and ordering per successor for minimal/multi PHI path
|
|
|
|
|
|
let cfg_now = crate::jit::config::current();
|
|
|
|
|
|
let enable_phi_min = cfg_now.phi_min;
|
|
|
|
|
|
// For each successor block, store ordered list of phi dst and a map pred->input for each phi
|
|
|
|
|
|
let mut succ_phi_order: std::collections::HashMap<crate::mir::BasicBlockId, Vec<crate::mir::ValueId>> = std::collections::HashMap::new();
|
|
|
|
|
|
let mut succ_phi_inputs: std::collections::HashMap<crate::mir::BasicBlockId, Vec<(crate::mir::BasicBlockId, crate::mir::ValueId)>> = std::collections::HashMap::new();
|
2025-08-27 17:52:37 +09:00
|
|
|
|
if enable_phi_min {
|
|
|
|
|
|
for (bb_id, bb) in func.blocks.iter() {
|
2025-08-28 09:26:58 +09:00
|
|
|
|
let mut order: Vec<crate::mir::ValueId> = Vec::new();
|
|
|
|
|
|
for ins in bb.instructions.iter() {
|
|
|
|
|
|
if let crate::mir::MirInstruction::Phi { dst, inputs } = ins {
|
|
|
|
|
|
order.push(*dst);
|
|
|
|
|
|
// store all (pred,val) pairs in flat vec grouped by succ
|
|
|
|
|
|
for (pred, val) in inputs.iter() { succ_phi_inputs.entry(*bb_id).or_default().push((*pred, *val)); }
|
2025-08-27 17:52:37 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-28 09:26:58 +09:00
|
|
|
|
if !order.is_empty() { succ_phi_order.insert(*bb_id, order); }
|
2025-08-27 17:52:37 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-28 09:26:58 +09:00
|
|
|
|
// Decide ABI: typed or i64-only
|
|
|
|
|
|
let native_f64 = cfg_now.native_f64;
|
|
|
|
|
|
let native_bool = cfg_now.native_bool;
|
|
|
|
|
|
let mut use_typed = false;
|
|
|
|
|
|
let mut kinds: Vec<super::builder::ParamKind> = Vec::new();
|
|
|
|
|
|
for mt in func.signature.params.iter() {
|
|
|
|
|
|
let k = match mt {
|
|
|
|
|
|
crate::mir::MirType::Float if native_f64 => { use_typed = true; super::builder::ParamKind::F64 }
|
|
|
|
|
|
crate::mir::MirType::Bool if native_bool => { use_typed = true; super::builder::ParamKind::B1 }
|
|
|
|
|
|
_ => super::builder::ParamKind::I64,
|
|
|
|
|
|
};
|
|
|
|
|
|
kinds.push(k);
|
|
|
|
|
|
}
|
|
|
|
|
|
let ret_is_f64 = native_f64 && matches!(func.signature.return_type, crate::mir::MirType::Float);
|
|
|
|
|
|
// Hint return bool footing (no-op in current backend; keeps switch point centralized)
|
|
|
|
|
|
let ret_is_bool = matches!(func.signature.return_type, crate::mir::MirType::Bool);
|
|
|
|
|
|
if ret_is_bool {
|
|
|
|
|
|
builder.hint_ret_bool(true);
|
|
|
|
|
|
// Track how many functions are lowered with boolean return hint (for stats)
|
|
|
|
|
|
crate::jit::rt::ret_bool_hint_inc(1);
|
|
|
|
|
|
self.last_ret_bool_hint_used = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if use_typed || ret_is_f64 {
|
|
|
|
|
|
builder.prepare_signature_typed(&kinds, ret_is_f64);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
builder.prepare_signature_i64(func.params.len(), true);
|
|
|
|
|
|
}
|
2025-08-28 12:09:09 +09:00
|
|
|
|
// 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); } }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-27 03:16:57 +09:00
|
|
|
|
builder.begin_function(&func.signature.name);
|
2025-08-27 17:08:49 +09:00
|
|
|
|
// Iterate blocks in the sorted order to keep indices stable
|
2025-08-27 17:58:50 +09:00
|
|
|
|
self.phi_values.clear();
|
2025-08-28 09:26:58 +09:00
|
|
|
|
self.phi_param_index.clear();
|
2025-08-28 12:09:09 +09:00
|
|
|
|
self.float_box_values.clear();
|
2025-08-27 17:08:49 +09:00
|
|
|
|
for (idx, bb_id) in bb_ids.iter().enumerate() {
|
|
|
|
|
|
let bb = func.blocks.get(bb_id).unwrap();
|
|
|
|
|
|
builder.switch_to_block(idx);
|
2025-08-28 09:26:58 +09:00
|
|
|
|
// Pre-scan PHIs in this block and ensure block parameters count (multi-PHI)
|
|
|
|
|
|
if enable_phi_min {
|
|
|
|
|
|
let mut local_phi_order: Vec<ValueId> = Vec::new();
|
|
|
|
|
|
// Also detect boolean PHIs: inputs all from boolean-producing values
|
|
|
|
|
|
for ins in bb.instructions.iter() {
|
|
|
|
|
|
if let crate::mir::MirInstruction::Phi { dst, inputs } = ins {
|
|
|
|
|
|
local_phi_order.push(*dst);
|
|
|
|
|
|
// decide if this phi is boolean
|
|
|
|
|
|
if inputs.iter().all(|(_, v)| self.bool_values.contains(v)) && !inputs.is_empty() {
|
|
|
|
|
|
self.bool_phi_values.insert(*dst);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if !local_phi_order.is_empty() {
|
|
|
|
|
|
builder.ensure_block_params_i64(idx, local_phi_order.len());
|
|
|
|
|
|
for (i, v) in local_phi_order.into_iter().enumerate() {
|
|
|
|
|
|
self.phi_values.insert(v);
|
|
|
|
|
|
self.phi_param_index.insert((*bb_id, v), i);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-27 03:16:57 +09:00
|
|
|
|
for instr in bb.instructions.iter() {
|
|
|
|
|
|
self.cover_if_supported(instr);
|
2025-08-28 22:31:51 +09:00
|
|
|
|
if let Err(e) = self.try_emit(builder, instr, *bb_id, func) { return Err(e); }
|
2025-08-28 12:09:09 +09:00
|
|
|
|
// 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); } }
|
2025-08-27 03:16:57 +09:00
|
|
|
|
}
|
|
|
|
|
|
if let Some(term) = &bb.terminator {
|
|
|
|
|
|
self.cover_if_supported(term);
|
2025-08-27 17:08:49 +09:00
|
|
|
|
// Branch/Jump need block mapping: pass indices
|
|
|
|
|
|
match term {
|
|
|
|
|
|
crate::mir::MirInstruction::Branch { condition, then_bb, else_bb } => {
|
|
|
|
|
|
// Try to place condition on stack (param/const path); builder will adapt
|
|
|
|
|
|
self.push_value_if_known_or_param(builder, condition);
|
|
|
|
|
|
// Map BasicBlockId -> index
|
|
|
|
|
|
let then_index = bb_ids.iter().position(|x| x == then_bb).unwrap_or(0);
|
|
|
|
|
|
let else_index = bb_ids.iter().position(|x| x == else_bb).unwrap_or(0);
|
2025-08-27 17:52:37 +09:00
|
|
|
|
if enable_phi_min {
|
2025-08-28 09:26:58 +09:00
|
|
|
|
// For multi-PHI, push args in successor's phi order
|
|
|
|
|
|
let mut then_n = 0usize; let mut else_n = 0usize;
|
|
|
|
|
|
if let Some(order) = succ_phi_order.get(then_bb) {
|
|
|
|
|
|
let mut cnt = 0usize;
|
|
|
|
|
|
for dst in order.iter() {
|
|
|
|
|
|
// find input from current block
|
|
|
|
|
|
if let Some(bb_succ) = func.blocks.get(then_bb) {
|
|
|
|
|
|
// locate the specific phi to read its inputs
|
|
|
|
|
|
for ins in bb_succ.instructions.iter() {
|
|
|
|
|
|
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
|
|
|
|
|
if d2 == dst {
|
|
|
|
|
|
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) {
|
|
|
|
|
|
self.push_value_if_known_or_param(builder, val);
|
|
|
|
|
|
cnt += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if cnt > 0 { builder.ensure_block_params_i64(then_index, cnt); }
|
|
|
|
|
|
then_n = cnt;
|
2025-08-27 17:52:37 +09:00
|
|
|
|
}
|
2025-08-28 09:26:58 +09:00
|
|
|
|
if let Some(order) = succ_phi_order.get(else_bb) {
|
|
|
|
|
|
let mut cnt = 0usize;
|
|
|
|
|
|
for dst in order.iter() {
|
|
|
|
|
|
if let Some(bb_succ) = func.blocks.get(else_bb) {
|
|
|
|
|
|
for ins in bb_succ.instructions.iter() {
|
|
|
|
|
|
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
|
|
|
|
|
if d2 == dst {
|
|
|
|
|
|
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) {
|
|
|
|
|
|
self.push_value_if_known_or_param(builder, val);
|
|
|
|
|
|
cnt += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if cnt > 0 { builder.ensure_block_params_i64(else_index, cnt); }
|
|
|
|
|
|
else_n = cnt;
|
2025-08-27 17:52:37 +09:00
|
|
|
|
}
|
|
|
|
|
|
builder.br_if_with_args(then_index, else_index, then_n, else_n);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
builder.br_if_top_is_true(then_index, else_index);
|
|
|
|
|
|
}
|
2025-08-27 17:08:49 +09:00
|
|
|
|
builder.seal_block(then_index);
|
|
|
|
|
|
builder.seal_block(else_index);
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::mir::MirInstruction::Jump { target } => {
|
|
|
|
|
|
let target_index = bb_ids.iter().position(|x| x == target).unwrap_or(0);
|
2025-08-27 17:52:37 +09:00
|
|
|
|
if enable_phi_min {
|
|
|
|
|
|
let mut n = 0usize;
|
2025-08-28 09:26:58 +09:00
|
|
|
|
if let Some(order) = succ_phi_order.get(target) {
|
|
|
|
|
|
let mut cnt = 0usize;
|
|
|
|
|
|
if let Some(bb_succ) = func.blocks.get(target) {
|
|
|
|
|
|
for dst in order.iter() {
|
|
|
|
|
|
for ins in bb_succ.instructions.iter() {
|
|
|
|
|
|
if let crate::mir::MirInstruction::Phi { dst: d2, inputs } = ins {
|
|
|
|
|
|
if d2 == dst {
|
|
|
|
|
|
if let Some((_, val)) = inputs.iter().find(|(pred, _)| pred == bb_id) {
|
|
|
|
|
|
self.push_value_if_known_or_param(builder, val);
|
|
|
|
|
|
cnt += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if cnt > 0 { builder.ensure_block_params_i64(target_index, cnt); }
|
|
|
|
|
|
n = cnt;
|
2025-08-27 17:52:37 +09:00
|
|
|
|
}
|
|
|
|
|
|
builder.jump_with_args(target_index, n);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
builder.jump_to(target_index);
|
|
|
|
|
|
}
|
2025-08-27 17:08:49 +09:00
|
|
|
|
builder.seal_block(target_index);
|
|
|
|
|
|
}
|
2025-08-28 12:09:09 +09:00
|
|
|
|
_ => { /* other terminators handled via generic emission below */ }
|
2025-08-27 17:08:49 +09:00
|
|
|
|
}
|
2025-08-28 12:09:09 +09:00
|
|
|
|
// Also allow other terminators to be emitted if needed
|
2025-08-28 22:31:51 +09:00
|
|
|
|
if let Err(e) = self.try_emit(builder, term, *bb_id, func) { return Err(e); }
|
2025-08-27 03:16:57 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
builder.end_function();
|
2025-08-28 09:26:58 +09:00
|
|
|
|
if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") {
|
|
|
|
|
|
let succs = succ_phi_order.len();
|
|
|
|
|
|
eprintln!("[JIT] cfg: blocks={} phi_succ={} (phi_min={})", bb_ids.len(), succs, enable_phi_min);
|
|
|
|
|
|
if enable_phi_min {
|
|
|
|
|
|
let mut total_phi_slots: usize = 0;
|
|
|
|
|
|
let mut total_phi_b1_slots: usize = 0;
|
|
|
|
|
|
for (succ, order) in succ_phi_order.iter() {
|
|
|
|
|
|
let mut preds_set: std::collections::BTreeSet<i64> = std::collections::BTreeSet::new();
|
|
|
|
|
|
let mut phi_lines: Vec<String> = Vec::new();
|
|
|
|
|
|
if let Some(bb_succ) = func.blocks.get(succ) {
|
|
|
|
|
|
for ins in bb_succ.instructions.iter() {
|
|
|
|
|
|
if let crate::mir::MirInstruction::Phi { dst, inputs } = ins {
|
|
|
|
|
|
// collect preds for block-level summary
|
|
|
|
|
|
for (pred, _) in inputs.iter() { preds_set.insert(pred.0 as i64); }
|
|
|
|
|
|
// build detailed mapping text: dst<-pred:val,...
|
|
|
|
|
|
let mut pairs: Vec<String> = Vec::new();
|
|
|
|
|
|
for (pred, val) in inputs.iter() {
|
|
|
|
|
|
pairs.push(format!("{}:{}", pred.0, val.0));
|
|
|
|
|
|
}
|
|
|
|
|
|
// Heuristics: boolean PHI if (1) pre-analysis marked it, or
|
|
|
|
|
|
// (2) all inputs look boolean-like (from bool producers or 0/1 const), or
|
|
|
|
|
|
// (3) used as a branch condition somewhere.
|
|
|
|
|
|
let used_as_branch = func.blocks.values().any(|bbx| {
|
|
|
|
|
|
if let Some(MirInstruction::Branch { condition, .. }) = &bbx.terminator { condition == dst } else { false }
|
|
|
|
|
|
});
|
|
|
|
|
|
let is_b1 = self.bool_phi_values.contains(dst)
|
|
|
|
|
|
|| inputs.iter().all(|(_, v)| {
|
|
|
|
|
|
self.bool_values.contains(v) || self.known_i64.get(v).map(|&iv| iv == 0 || iv == 1).unwrap_or(false)
|
|
|
|
|
|
})
|
|
|
|
|
|
|| used_as_branch;
|
|
|
|
|
|
let tag = if is_b1 { " (b1)" } else { "" };
|
|
|
|
|
|
phi_lines.push(format!(" dst v{}{} <- {}", dst.0, tag, pairs.join(", ")));
|
|
|
|
|
|
total_phi_slots += 1;
|
|
|
|
|
|
if is_b1 { total_phi_b1_slots += 1; }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
let preds_list: Vec<String> = preds_set.into_iter().map(|p| p.to_string()).collect();
|
|
|
|
|
|
eprintln!("[JIT] phi: bb={} slots={} preds={}", succ.0, order.len(), preds_list.join("|"));
|
|
|
|
|
|
for ln in phi_lines { eprintln!("[JIT]{}", ln); }
|
|
|
|
|
|
}
|
|
|
|
|
|
eprintln!("[JIT] phi_summary: total_slots={} b1_slots={}", total_phi_slots, total_phi_b1_slots);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-27 03:16:57 +09:00
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-27 17:06:46 +09:00
|
|
|
|
/// Push a value onto the builder stack if it is a known i64 const or a parameter.
|
2025-08-29 03:28:42 +09:00
|
|
|
|
pub(super) fn push_value_if_known_or_param(&self, b: &mut dyn IRBuilder, id: &ValueId) {
|
2025-08-27 17:58:50 +09:00
|
|
|
|
if self.phi_values.contains(id) {
|
2025-08-28 09:26:58 +09:00
|
|
|
|
// Multi-PHI: find the param index for this phi in the current block
|
|
|
|
|
|
// We don't have the current block id here; rely on builder's current block context and our stored index being positional.
|
|
|
|
|
|
// As an approximation, prefer position 0 if unknown.
|
|
|
|
|
|
let pos = self.phi_param_index.iter().find_map(|((_, vid), idx)| if vid == id { Some(*idx) } else { None }).unwrap_or(0);
|
|
|
|
|
|
// Use b1 loader for boolean PHIs when enabled
|
|
|
|
|
|
if crate::jit::config::current().native_bool && self.bool_phi_values.contains(id) {
|
|
|
|
|
|
b.push_block_param_b1_at(pos);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
b.push_block_param_i64_at(pos);
|
|
|
|
|
|
}
|
2025-08-27 17:58:50 +09:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-08-27 17:06:46 +09:00
|
|
|
|
if let Some(pidx) = self.param_index.get(id).copied() {
|
|
|
|
|
|
b.emit_param_i64(pidx);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if let Some(v) = self.known_i64.get(id).copied() {
|
|
|
|
|
|
b.emit_const_i64(v);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-27 03:16:57 +09:00
|
|
|
|
fn cover_if_supported(&mut self, instr: &MirInstruction) {
|
|
|
|
|
|
use crate::mir::MirInstruction as I;
|
|
|
|
|
|
let supported = matches!(
|
|
|
|
|
|
instr,
|
|
|
|
|
|
I::Const { .. }
|
|
|
|
|
|
| I::Copy { .. }
|
2025-08-28 09:26:58 +09:00
|
|
|
|
| I::Cast { .. }
|
2025-08-29 08:36:07 +09:00
|
|
|
|
| I::TypeCheck { .. }
|
|
|
|
|
|
| I::TypeOp { .. }
|
2025-08-27 03:16:57 +09:00
|
|
|
|
| I::BinOp { .. }
|
|
|
|
|
|
| I::Compare { .. }
|
|
|
|
|
|
| I::Jump { .. }
|
|
|
|
|
|
| I::Branch { .. }
|
|
|
|
|
|
| I::Return { .. }
|
2025-08-29 08:36:07 +09:00
|
|
|
|
| I::Call { .. }
|
2025-08-28 12:09:09 +09:00
|
|
|
|
| I::BoxCall { .. }
|
2025-08-27 03:16:57 +09:00
|
|
|
|
| I::ArrayGet { .. }
|
|
|
|
|
|
| I::ArraySet { .. }
|
2025-08-29 08:36:07 +09:00
|
|
|
|
| I::NewBox { .. }
|
|
|
|
|
|
| I::Store { .. }
|
|
|
|
|
|
| I::Load { .. }
|
|
|
|
|
|
| I::Phi { .. }
|
2025-08-29 13:46:18 +09:00
|
|
|
|
// PrintはJIT経路では未対応(VMにフォールバックしてコンソール出力を保持)
|
|
|
|
|
|
// | I::Print { .. }
|
2025-08-29 08:36:07 +09:00
|
|
|
|
| I::Debug { .. }
|
|
|
|
|
|
| I::ExternCall { .. }
|
|
|
|
|
|
| I::Safepoint
|
|
|
|
|
|
| I::Nop
|
2025-08-27 03:16:57 +09:00
|
|
|
|
);
|
|
|
|
|
|
if supported { self.covered += 1; } else { self.unsupported += 1; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-28 22:31:51 +09:00
|
|
|
|
fn try_emit(&mut self, b: &mut dyn IRBuilder, instr: &MirInstruction, cur_bb: crate::mir::BasicBlockId, func: &crate::mir::MirFunction) -> Result<(), String> {
|
2025-08-27 03:16:57 +09:00
|
|
|
|
use crate::mir::MirInstruction as I;
|
|
|
|
|
|
match instr {
|
2025-08-28 12:09:09 +09:00
|
|
|
|
I::NewBox { dst, box_type, args } => {
|
2025-08-29 08:36:07 +09:00
|
|
|
|
// Minimal JIT lowering for builtin pluginized boxes: birth() via handle-based shim
|
|
|
|
|
|
if std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1") && args.is_empty() {
|
|
|
|
|
|
let bt = box_type.as_str();
|
|
|
|
|
|
match bt {
|
|
|
|
|
|
"StringBox" => {
|
|
|
|
|
|
// Emit host-call to create a new StringBox handle; push as i64
|
|
|
|
|
|
b.emit_host_call(crate::jit::r#extern::collections::SYM_STRING_BIRTH_H, 0, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
"IntegerBox" => {
|
|
|
|
|
|
b.emit_host_call(crate::jit::r#extern::collections::SYM_INTEGER_BIRTH_H, 0, true);
|
|
|
|
|
|
}
|
2025-08-29 13:46:18 +09:00
|
|
|
|
_ => {
|
|
|
|
|
|
// Any other NewBox (e.g., ArrayBox/MapBox/etc.) is UNSUPPORTED in JIT for now
|
|
|
|
|
|
self.unsupported += 1;
|
|
|
|
|
|
}
|
2025-08-29 08:36:07 +09:00
|
|
|
|
}
|
2025-08-29 13:46:18 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
// NewBox with args or NYASH_USE_PLUGIN_BUILTINS!=1 → unsupported in JIT
|
|
|
|
|
|
self.unsupported += 1;
|
2025-08-29 08:36:07 +09:00
|
|
|
|
}
|
2025-08-28 12:09:09 +09:00
|
|
|
|
// 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 } => {
|
2025-08-28 09:26:58 +09:00
|
|
|
|
// 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); }
|
2025-08-28 12:09:09 +09:00
|
|
|
|
// 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-28 09:26:58 +09:00
|
|
|
|
}
|
2025-08-27 17:06:46 +09:00
|
|
|
|
I::Const { dst, value } => match value {
|
|
|
|
|
|
ConstValue::Integer(i) => {
|
|
|
|
|
|
b.emit_const_i64(*i);
|
|
|
|
|
|
self.known_i64.insert(*dst, *i);
|
|
|
|
|
|
}
|
2025-08-28 12:09:09 +09:00
|
|
|
|
ConstValue::Float(f) => { b.emit_const_f64(*f); self.known_f64.insert(*dst, *f); }
|
2025-08-27 17:06:46 +09:00
|
|
|
|
ConstValue::Bool(bv) => {
|
|
|
|
|
|
let iv = if *bv { 1 } else { 0 };
|
|
|
|
|
|
b.emit_const_i64(iv);
|
|
|
|
|
|
self.known_i64.insert(*dst, iv);
|
2025-08-28 09:26:58 +09:00
|
|
|
|
// Mark this value as boolean producer
|
|
|
|
|
|
self.bool_values.insert(*dst);
|
2025-08-27 17:06:46 +09:00
|
|
|
|
}
|
|
|
|
|
|
ConstValue::String(_) | ConstValue::Null | ConstValue::Void => {
|
2025-08-27 03:16:57 +09:00
|
|
|
|
// leave unsupported for now
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2025-08-27 17:06:46 +09:00
|
|
|
|
I::Copy { dst, src } => {
|
|
|
|
|
|
if let Some(v) = self.known_i64.get(src).copied() { self.known_i64.insert(*dst, v); }
|
2025-08-28 12:09:09 +09:00
|
|
|
|
if let Some(v) = self.known_f64.get(src).copied() { self.known_f64.insert(*dst, v); }
|
2025-08-27 17:06:46 +09:00
|
|
|
|
// 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);
|
|
|
|
|
|
}
|
2025-08-28 09:26:58 +09:00
|
|
|
|
// Propagate boolean classification through Copy
|
|
|
|
|
|
if self.bool_values.contains(src) { self.bool_values.insert(*dst); }
|
2025-08-27 17:06:46 +09:00
|
|
|
|
// Otherwise no-op for codegen (stack-machine handles sources directly later)
|
|
|
|
|
|
}
|
2025-08-29 03:28:42 +09:00
|
|
|
|
I::BinOp { dst, op, lhs, rhs } => { self.lower_binop(b, op, lhs, rhs, dst); }
|
|
|
|
|
|
I::Compare { op, lhs, rhs, dst } => { self.lower_compare(b, op, lhs, rhs, dst); }
|
|
|
|
|
|
I::Jump { .. } => self.lower_jump(b),
|
|
|
|
|
|
I::Branch { .. } => self.lower_branch(b),
|
2025-08-27 17:06:46 +09:00
|
|
|
|
I::Return { value } => {
|
|
|
|
|
|
if let Some(v) = value { self.push_value_if_known_or_param(b, v); }
|
|
|
|
|
|
b.emit_return()
|
|
|
|
|
|
}
|
2025-08-28 09:26:58 +09:00
|
|
|
|
I::Store { value, ptr } => {
|
|
|
|
|
|
// Minimal lowering: materialize value if known/param and store to a local slot keyed by ptr
|
|
|
|
|
|
self.push_value_if_known_or_param(b, value);
|
|
|
|
|
|
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.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::Phi { dst, .. } => {
|
|
|
|
|
|
// Minimal PHI: load current block param; b1 when classified boolean
|
|
|
|
|
|
let pos = self.phi_param_index.get(&(cur_bb, *dst)).copied().unwrap_or(0);
|
|
|
|
|
|
if self.bool_phi_values.contains(dst) {
|
|
|
|
|
|
b.push_block_param_b1_at(pos);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
b.push_block_param_i64_at(pos);
|
|
|
|
|
|
}
|
2025-08-27 17:52:37 +09:00
|
|
|
|
}
|
2025-08-29 04:37:30 +09:00
|
|
|
|
I::ArrayGet { array, index, .. } => {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
// Prepare receiver + index on stack
|
|
|
|
|
|
let argc = 2usize;
|
|
|
|
|
|
if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); }
|
|
|
|
|
|
if let Some(iv) = self.known_i64.get(index).copied() { b.emit_const_i64(iv); } else { self.push_value_if_known_or_param(b, index); }
|
|
|
|
|
|
// Decide policy
|
|
|
|
|
|
let decision = crate::jit::policy::invoke::decide_box_method("ArrayBox", "get", argc, true);
|
|
|
|
|
|
match decision {
|
|
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => {
|
|
|
|
|
|
b.emit_plugin_invoke(type_id, method_id, argc, true);
|
|
|
|
|
|
crate::jit::observe::lower_plugin_invoke(&box_type, "get", type_id, method_id, argc);
|
2025-08-29 04:37:30 +09:00
|
|
|
|
}
|
2025-08-29 21:39:47 +09:00
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => {
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64"], "allow", "mapped_symbol");
|
|
|
|
|
|
b.emit_host_call(&symbol, argc, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => super::core_hostcall::lower_array_get(b, &self.param_index, &self.known_i64, array, index),
|
2025-08-29 04:37:30 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
I::ArraySet { array, index, value } => {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
let argc = 3usize;
|
|
|
|
|
|
if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); }
|
|
|
|
|
|
if let Some(iv) = self.known_i64.get(index).copied() { b.emit_const_i64(iv); } else { self.push_value_if_known_or_param(b, index); }
|
|
|
|
|
|
if let Some(vv) = self.known_i64.get(value).copied() { b.emit_const_i64(vv); } else { self.push_value_if_known_or_param(b, value); }
|
|
|
|
|
|
let decision = crate::jit::policy::invoke::decide_box_method("ArrayBox", "set", argc, false);
|
|
|
|
|
|
match decision {
|
|
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => {
|
|
|
|
|
|
b.emit_plugin_invoke(type_id, method_id, argc, false);
|
|
|
|
|
|
crate::jit::observe::lower_plugin_invoke(&box_type, "set", type_id, method_id, argc);
|
2025-08-29 04:37:30 +09:00
|
|
|
|
}
|
2025-08-29 21:39:47 +09:00
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => {
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64","I64"], "allow", "mapped_symbol");
|
|
|
|
|
|
b.emit_host_call(&symbol, argc, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => super::core_hostcall::lower_array_set(b, &self.param_index, &self.known_i64, array, index, value),
|
2025-08-29 04:37:30 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-27 17:06:46 +09:00
|
|
|
|
I::BoxCall { box_val: array, method, args, dst, .. } => {
|
2025-08-29 03:28:42 +09:00
|
|
|
|
if super::core_hostcall::lower_boxcall_simple_reads(b, &self.param_index, &self.known_i64, array, method.as_str(), args, dst.clone()) {
|
|
|
|
|
|
// handled in helper (read-only simple methods)
|
|
|
|
|
|
} else if matches!(method.as_str(), "sin" | "cos" | "abs" | "min" | "max") {
|
|
|
|
|
|
super::core_hostcall::lower_math_call(
|
|
|
|
|
|
func,
|
|
|
|
|
|
b,
|
|
|
|
|
|
&self.known_i64,
|
|
|
|
|
|
&self.known_f64,
|
|
|
|
|
|
&self.float_box_values,
|
|
|
|
|
|
method.as_str(),
|
|
|
|
|
|
args,
|
|
|
|
|
|
dst.clone(),
|
|
|
|
|
|
);
|
2025-08-29 04:37:30 +09:00
|
|
|
|
} else if std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1") {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
// StringBox(length/is_empty/charCodeAt): policy+observe経由に統一
|
|
|
|
|
|
if matches!(method.as_str(), "length" | "is_empty" | "charCodeAt") {
|
|
|
|
|
|
// receiver
|
|
|
|
|
|
if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); }
|
|
|
|
|
|
let mut argc = 1usize;
|
|
|
|
|
|
if method.as_str() == "charCodeAt" {
|
|
|
|
|
|
if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); }
|
|
|
|
|
|
argc = 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
if method.as_str() == "is_empty" { b.hint_ret_bool(true); }
|
|
|
|
|
|
let decision = crate::jit::policy::invoke::decide_box_method("StringBox", method.as_str(), argc, dst.is_some());
|
|
|
|
|
|
match decision {
|
|
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => {
|
|
|
|
|
|
b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some());
|
|
|
|
|
|
crate::jit::observe::lower_plugin_invoke(&box_type, method.as_str(), type_id, method_id, argc);
|
2025-08-29 08:36:07 +09:00
|
|
|
|
return Ok(());
|
|
|
|
|
|
}
|
2025-08-29 21:39:47 +09:00
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => {
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(&symbol, argc, &if argc==1 { ["Handle"][..].to_vec() } else { ["Handle","I64"][..].to_vec() }, "allow", "mapped_symbol");
|
|
|
|
|
|
b.emit_host_call(&symbol, argc, dst.is_some());
|
|
|
|
|
|
return Ok(());
|
2025-08-29 08:36:07 +09:00
|
|
|
|
}
|
2025-08-29 21:39:47 +09:00
|
|
|
|
_ => {}
|
2025-08-29 08:36:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Integer.get/set specialized when receiver is Integer (avoid Map collision)
|
|
|
|
|
|
if matches!(method.as_str(), "get" | "set") {
|
|
|
|
|
|
let recv_is_int = func.metadata.value_types.get(array).map(|mt| matches!(mt, crate::mir::MirType::Integer)).unwrap_or(false);
|
|
|
|
|
|
if recv_is_int {
|
|
|
|
|
|
if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() {
|
|
|
|
|
|
if let Ok(h) = ph.resolve_method("IntegerBox", method.as_str()) {
|
|
|
|
|
|
if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); }
|
|
|
|
|
|
let mut argc = 1usize;
|
|
|
|
|
|
if method.as_str() == "set" {
|
|
|
|
|
|
if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); }
|
|
|
|
|
|
argc = 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
b.emit_plugin_invoke(h.type_id, h.method_id, argc, dst.is_some());
|
|
|
|
|
|
crate::jit::events::emit_lower(
|
|
|
|
|
|
serde_json::json!({
|
|
|
|
|
|
"id": format!("plugin:{}:{}", h.box_type, method.as_str()),
|
|
|
|
|
|
"decision":"allow","reason":"plugin_invoke","argc": argc,
|
|
|
|
|
|
"type_id": h.type_id, "method_id": h.method_id
|
|
|
|
|
|
}),
|
|
|
|
|
|
"plugin","<jit>"
|
|
|
|
|
|
);
|
|
|
|
|
|
return Ok(());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-29 04:37:30 +09:00
|
|
|
|
match method.as_str() {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
"len" | "length" => {
|
2025-08-29 04:37:30 +09:00
|
|
|
|
// Resolve ArrayBox plugin method and emit plugin_invoke (symbolic)
|
|
|
|
|
|
if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
let mname = "length";
|
2025-08-29 04:37:30 +09:00
|
|
|
|
if let Ok(h) = ph.resolve_method("ArrayBox", mname) {
|
|
|
|
|
|
// Receiver
|
|
|
|
|
|
if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); }
|
|
|
|
|
|
let mut argc = 1usize;
|
2025-08-29 21:39:47 +09:00
|
|
|
|
// length only
|
2025-08-29 04:37:30 +09:00
|
|
|
|
b.emit_plugin_invoke(h.type_id, h.method_id, argc, dst.is_some());
|
|
|
|
|
|
crate::jit::events::emit_lower(
|
|
|
|
|
|
serde_json::json!({
|
|
|
|
|
|
"id": format!("plugin:{}:{}", h.box_type, mname),
|
|
|
|
|
|
"decision":"allow","reason":"plugin_invoke","argc": argc,
|
|
|
|
|
|
"type_id": h.type_id, "method_id": h.method_id
|
|
|
|
|
|
}),
|
|
|
|
|
|
"plugin","<jit>"
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-29 05:07:47 +09:00
|
|
|
|
// Map: size/get/has (RO) and set (mutating; allowed only when policy.read_only=false)
|
|
|
|
|
|
"size" | "get" | "has" | "set" => {
|
2025-08-29 04:37:30 +09:00
|
|
|
|
if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() {
|
|
|
|
|
|
if let Ok(h) = ph.resolve_method("MapBox", method.as_str()) {
|
2025-08-29 05:07:47 +09:00
|
|
|
|
if method.as_str() == "set" && crate::jit::policy::current().read_only {
|
|
|
|
|
|
// Deny mutating under read-only policy
|
|
|
|
|
|
crate::jit::events::emit_lower(
|
|
|
|
|
|
serde_json::json!({
|
|
|
|
|
|
"id": format!("plugin:{}:{}", "MapBox", "set"),
|
|
|
|
|
|
"decision":"fallback","reason":"policy_denied_mutating"
|
|
|
|
|
|
}),
|
|
|
|
|
|
"plugin","<jit>"
|
|
|
|
|
|
);
|
|
|
|
|
|
// Do not emit plugin call; VM path will handle
|
|
|
|
|
|
return Ok(());
|
|
|
|
|
|
}
|
2025-08-29 04:37:30 +09:00
|
|
|
|
if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); }
|
|
|
|
|
|
let mut argc = 1usize;
|
|
|
|
|
|
if matches!(method.as_str(), "get" | "has") {
|
|
|
|
|
|
if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); }
|
|
|
|
|
|
argc += 1;
|
2025-08-29 05:07:47 +09:00
|
|
|
|
} else if method.as_str() == "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); }
|
|
|
|
|
|
argc += 2;
|
2025-08-29 04:37:30 +09:00
|
|
|
|
}
|
|
|
|
|
|
b.emit_plugin_invoke(h.type_id, h.method_id, argc, dst.is_some());
|
|
|
|
|
|
crate::jit::events::emit_lower(
|
|
|
|
|
|
serde_json::json!({
|
|
|
|
|
|
"id": format!("plugin:{}:{}", h.box_type, method.as_str()),
|
|
|
|
|
|
"decision":"allow","reason":"plugin_invoke","argc": argc,
|
|
|
|
|
|
"type_id": h.type_id, "method_id": h.method_id
|
|
|
|
|
|
}),
|
|
|
|
|
|
"plugin","<jit>"
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => { /* other BoxCalls handled below */ }
|
|
|
|
|
|
}
|
2025-08-29 03:28:42 +09:00
|
|
|
|
} else if crate::jit::config::current().hostcall {
|
2025-08-27 17:06:46 +09:00
|
|
|
|
match method.as_str() {
|
|
|
|
|
|
"len" | "length" => {
|
2025-08-28 09:26:58 +09:00
|
|
|
|
if let Some(pidx) = self.param_index.get(array).copied() {
|
2025-08-29 02:05:39 +09:00
|
|
|
|
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>"
|
2025-08-28 22:31:51 +09:00
|
|
|
|
);
|
2025-08-28 09:26:58 +09:00
|
|
|
|
b.emit_param_i64(pidx);
|
|
|
|
|
|
b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, dst.is_some());
|
|
|
|
|
|
} else {
|
2025-08-29 02:05:39 +09:00
|
|
|
|
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>"
|
2025-08-28 22:31:51 +09:00
|
|
|
|
);
|
2025-08-28 09:26:58 +09:00
|
|
|
|
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());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-28 12:09:09 +09:00
|
|
|
|
// 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
|
2025-08-28 22:31:51 +09:00
|
|
|
|
// Classify argument kinds using TyEnv when available; fallback to known maps/FloatBox tracking
|
2025-08-28 12:09:09 +09:00
|
|
|
|
let mut observed_kinds: Vec<crate::jit::hostcall_registry::ArgKind> = Vec::new();
|
|
|
|
|
|
for v in args.iter() {
|
2025-08-28 22:31:51 +09:00
|
|
|
|
let kind = if let Some(mt) = func.metadata.value_types.get(v) {
|
|
|
|
|
|
match mt {
|
|
|
|
|
|
crate::mir::MirType::Float => crate::jit::hostcall_registry::ArgKind::F64,
|
|
|
|
|
|
crate::mir::MirType::Integer => crate::jit::hostcall_registry::ArgKind::I64,
|
|
|
|
|
|
crate::mir::MirType::Bool => crate::jit::hostcall_registry::ArgKind::I64, // b1はI64 0/1に正規化
|
|
|
|
|
|
crate::mir::MirType::String | crate::mir::MirType::Box(_) => crate::jit::hostcall_registry::ArgKind::Handle,
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
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 }
|
2025-08-28 12:09:09 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-28 22:31:51 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
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 }
|
|
|
|
|
|
};
|
2025-08-28 12:09:09 +09:00
|
|
|
|
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)
|
2025-08-29 02:05:39 +09:00
|
|
|
|
crate::jit::events::emit_lower(
|
|
|
|
|
|
serde_json::json!({
|
|
|
|
|
|
"id": sym,
|
|
|
|
|
|
"decision": "allow",
|
|
|
|
|
|
"reason": "sig_ok",
|
|
|
|
|
|
"argc": observed.len(),
|
|
|
|
|
|
"arg_types": arg_types
|
|
|
|
|
|
}),
|
|
|
|
|
|
"hostcall","<jit>"
|
|
|
|
|
|
);
|
2025-08-28 12:09:09 +09:00
|
|
|
|
// 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側で実行される
|
|
|
|
|
|
}
|
2025-08-28 09:26:58 +09:00
|
|
|
|
"isEmpty" | "empty" => {
|
|
|
|
|
|
if let Some(pidx) = self.param_index.get(array).copied() {
|
2025-08-28 22:31:51 +09:00
|
|
|
|
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"]})
|
|
|
|
|
|
);
|
2025-08-28 09:26:58 +09:00
|
|
|
|
b.emit_param_i64(pidx);
|
|
|
|
|
|
// returns i64 0/1
|
|
|
|
|
|
b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_IS_EMPTY_H, 1, dst.is_some());
|
2025-08-28 22:31:51 +09:00
|
|
|
|
} else {
|
2025-08-29 02:05:39 +09:00
|
|
|
|
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>"
|
|
|
|
|
|
);
|
2025-08-28 09:26:58 +09:00
|
|
|
|
}
|
2025-08-27 17:06:46 +09:00
|
|
|
|
}
|
|
|
|
|
|
"push" => {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
// argc=2: (array, value)
|
|
|
|
|
|
let argc = 2usize;
|
2025-08-27 17:06:46 +09:00
|
|
|
|
let val = args.get(0).and_then(|v| self.known_i64.get(v)).copied().unwrap_or(0);
|
2025-08-28 09:26:58 +09:00
|
|
|
|
if let Some(pidx) = self.param_index.get(array).copied() {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
// Prepare args
|
2025-08-28 09:26:58 +09:00
|
|
|
|
b.emit_param_i64(pidx);
|
|
|
|
|
|
b.emit_const_i64(val);
|
2025-08-29 21:39:47 +09:00
|
|
|
|
// Decide policy
|
|
|
|
|
|
let decision = crate::jit::policy::invoke::decide_box_method("ArrayBox", "push", argc, false);
|
|
|
|
|
|
match decision {
|
|
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => {
|
|
|
|
|
|
b.emit_plugin_invoke(type_id, method_id, argc, false);
|
|
|
|
|
|
crate::jit::observe::lower_plugin_invoke(&box_type, "push", type_id, method_id, argc);
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => {
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64"], "allow", "mapped_symbol");
|
|
|
|
|
|
b.emit_host_call(&symbol, argc, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
// Fallback to existing hostcall path
|
|
|
|
|
|
let sym = crate::jit::r#extern::collections::SYM_ARRAY_PUSH_H;
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(sym, argc, &["Handle","I64"], "fallback", "policy_or_unknown");
|
|
|
|
|
|
b.emit_host_call(sym, argc, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-28 09:26:58 +09:00
|
|
|
|
} else {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
// No receiver param index
|
2025-08-28 09:26:58 +09:00
|
|
|
|
let arr_idx = -1;
|
|
|
|
|
|
b.emit_const_i64(arr_idx);
|
|
|
|
|
|
b.emit_const_i64(val);
|
2025-08-29 21:39:47 +09:00
|
|
|
|
let sym = crate::jit::r#extern::collections::SYM_ARRAY_PUSH;
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(sym, argc, &["I64","I64"], "fallback", "receiver_not_param");
|
|
|
|
|
|
b.emit_host_call(sym, argc, false);
|
2025-08-28 09:26:58 +09:00
|
|
|
|
}
|
2025-08-27 17:06:46 +09:00
|
|
|
|
}
|
|
|
|
|
|
"size" => {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
let argc = 1usize;
|
2025-08-28 09:26:58 +09:00
|
|
|
|
if let Some(pidx) = self.param_index.get(array).copied() {
|
|
|
|
|
|
b.emit_param_i64(pidx);
|
2025-08-29 21:39:47 +09:00
|
|
|
|
let decision = crate::jit::policy::invoke::decide_box_method("MapBox", "size", argc, dst.is_some());
|
|
|
|
|
|
match decision {
|
|
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => {
|
|
|
|
|
|
b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some());
|
|
|
|
|
|
crate::jit::observe::lower_plugin_invoke(&box_type, "size", type_id, method_id, argc);
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => {
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle"], "allow", "mapped_symbol");
|
|
|
|
|
|
b.emit_host_call(&symbol, argc, dst.is_some());
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
let sym = crate::jit::r#extern::collections::SYM_MAP_SIZE_H;
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(sym, argc, &["Handle"], "fallback", "policy_or_unknown");
|
|
|
|
|
|
b.emit_host_call(sym, argc, dst.is_some());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-28 09:26:58 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
let map_idx = -1;
|
|
|
|
|
|
b.emit_const_i64(map_idx);
|
2025-08-29 21:39:47 +09:00
|
|
|
|
let sym = crate::jit::r#extern::collections::SYM_MAP_SIZE;
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(sym, argc, &["I64"], "fallback", "receiver_not_param");
|
|
|
|
|
|
b.emit_host_call(sym, argc, dst.is_some());
|
2025-08-28 09:26:58 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
"get" => {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
let argc = 2usize;
|
2025-08-28 09:26:58 +09:00
|
|
|
|
if let Some(pidx) = self.param_index.get(array).copied() {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
b.emit_param_i64(pidx);
|
|
|
|
|
|
if let Some(k) = args.get(0).and_then(|v| self.known_i64.get(v)).copied() { b.emit_const_i64(k); } else if let Some(kvid) = args.get(0) { self.push_value_if_known_or_param(b, kvid); } else { b.emit_const_i64(0); }
|
|
|
|
|
|
let decision = crate::jit::policy::invoke::decide_box_method("MapBox", "get", argc, dst.is_some());
|
|
|
|
|
|
match decision {
|
|
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => {
|
|
|
|
|
|
b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some());
|
|
|
|
|
|
crate::jit::observe::lower_plugin_invoke(&box_type, "get", type_id, method_id, argc);
|
2025-08-28 22:31:51 +09:00
|
|
|
|
}
|
2025-08-29 21:39:47 +09:00
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => {
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64"], "allow", "mapped_symbol");
|
|
|
|
|
|
b.emit_host_call(&symbol, argc, dst.is_some());
|
2025-08-28 22:31:51 +09:00
|
|
|
|
}
|
2025-08-29 21:39:47 +09:00
|
|
|
|
_ => {
|
|
|
|
|
|
let sym = crate::jit::r#extern::collections::SYM_MAP_GET_H;
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(sym, argc, &["Handle","I64"], "fallback", "policy_or_unknown");
|
|
|
|
|
|
b.emit_host_call(sym, argc, dst.is_some());
|
2025-08-28 22:31:51 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
let sym = crate::jit::r#extern::collections::SYM_MAP_GET_H;
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(sym, argc, &["I64","I64"], "fallback", "receiver_not_param");
|
|
|
|
|
|
b.emit_host_call(sym, argc, dst.is_some());
|
2025-08-28 09:26:58 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
"set" => {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
let argc = 3usize;
|
2025-08-28 09:26:58 +09:00
|
|
|
|
if let Some(pidx) = self.param_index.get(array).copied() {
|
|
|
|
|
|
let key = args.get(0).and_then(|v| self.known_i64.get(v)).copied().unwrap_or(0);
|
|
|
|
|
|
let val = args.get(1).and_then(|v| self.known_i64.get(v)).copied().unwrap_or(0);
|
|
|
|
|
|
b.emit_param_i64(pidx);
|
|
|
|
|
|
b.emit_const_i64(key);
|
|
|
|
|
|
b.emit_const_i64(val);
|
2025-08-29 21:39:47 +09:00
|
|
|
|
let decision = crate::jit::policy::invoke::decide_box_method("MapBox", "set", argc, false);
|
|
|
|
|
|
match decision {
|
|
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => {
|
|
|
|
|
|
b.emit_plugin_invoke(type_id, method_id, argc, false);
|
|
|
|
|
|
crate::jit::observe::lower_plugin_invoke(&box_type, "set", type_id, method_id, argc);
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => {
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64","I64"], "allow", "mapped_symbol");
|
|
|
|
|
|
b.emit_host_call(&symbol, argc, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
let sym = crate::jit::r#extern::collections::SYM_MAP_SET_H;
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(sym, argc, &["Handle","I64","I64"], "fallback", "policy_or_unknown");
|
|
|
|
|
|
b.emit_host_call(sym, argc, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-28 22:31:51 +09:00
|
|
|
|
} else {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
let sym = crate::jit::r#extern::collections::SYM_MAP_SET;
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(sym, argc, &["I64","I64","I64"], "fallback", "receiver_not_param");
|
|
|
|
|
|
b.emit_host_call(sym, argc, false);
|
2025-08-28 09:26:58 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
"charCodeAt" => {
|
|
|
|
|
|
// String.charCodeAt(index)
|
|
|
|
|
|
if let Some(pidx) = self.param_index.get(array).copied() {
|
|
|
|
|
|
let idx = args.get(0).and_then(|v| self.known_i64.get(v)).copied().unwrap_or(0);
|
2025-08-29 02:05:39 +09:00
|
|
|
|
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>"
|
2025-08-28 22:31:51 +09:00
|
|
|
|
);
|
2025-08-28 09:26:58 +09:00
|
|
|
|
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());
|
2025-08-28 22:31:51 +09:00
|
|
|
|
} else {
|
2025-08-29 02:05:39 +09:00
|
|
|
|
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>"
|
2025-08-28 22:31:51 +09:00
|
|
|
|
);
|
2025-08-28 09:26:58 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
"has" => {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
let argc = 2usize;
|
2025-08-28 09:26:58 +09:00
|
|
|
|
if let Some(pidx) = self.param_index.get(array).copied() {
|
|
|
|
|
|
let key = args.get(0).and_then(|v| self.known_i64.get(v)).copied().unwrap_or(0);
|
|
|
|
|
|
b.emit_param_i64(pidx);
|
|
|
|
|
|
b.emit_const_i64(key);
|
2025-08-29 21:39:47 +09:00
|
|
|
|
let decision = crate::jit::policy::invoke::decide_box_method("MapBox", "has", argc, dst.is_some());
|
|
|
|
|
|
match decision {
|
|
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => {
|
|
|
|
|
|
b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some());
|
|
|
|
|
|
crate::jit::observe::lower_plugin_invoke(&box_type, "has", type_id, method_id, argc);
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => {
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(&symbol, argc, &["Handle","I64"], "allow", "mapped_symbol");
|
|
|
|
|
|
b.emit_host_call(&symbol, argc, dst.is_some());
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
let sym = crate::jit::r#extern::collections::SYM_MAP_HAS_H;
|
|
|
|
|
|
crate::jit::observe::lower_hostcall(sym, argc, &["Handle","I64"], "fallback", "policy_or_unknown");
|
|
|
|
|
|
b.emit_host_call(sym, argc, dst.is_some());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-28 09:26:58 +09:00
|
|
|
|
}
|
2025-08-27 17:06:46 +09:00
|
|
|
|
}
|
|
|
|
|
|
_ => {}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-27 03:16:57 +09:00
|
|
|
|
}
|
|
|
|
|
|
_ => {}
|
|
|
|
|
|
}
|
2025-08-28 22:31:51 +09:00
|
|
|
|
Ok(())
|
2025-08-27 03:16:57 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-28 09:26:58 +09:00
|
|
|
|
|
2025-08-29 02:12:48 +09:00
|
|
|
|
pub use super::cfg_dot::dump_cfg_dot;
|