use crate::executor::VirtualMachine;
use crate::executor::objects::method_registry::{MethodFnV2, MethodHandler};
use crate::feedback::{FeedbackSlot, ICState};
use shape_value::heap_value::HeapKind;
pub(crate) struct MethodIcHit {
pub handler: MethodHandler,
}
#[inline]
pub(crate) fn method_ic_check(
vm: &VirtualMachine,
ip: usize,
receiver_kind: HeapKind,
method_name_id: u32,
) -> Option<MethodIcHit> {
let func_id = vm.call_stack.last()?.function_id? as usize;
let fv = vm.feedback_vectors.get(func_id)?.as_ref()?;
let slot = fv.get_slot(ip)?;
match slot {
FeedbackSlot::Method(fb) if fb.state == ICState::Monomorphic => {
let entry = fb.entries.first()?;
if entry.receiver_kind == receiver_kind as u8
&& entry.method_name_id == method_name_id
&& entry.handler_ptr != 0
{
let handler: MethodFnV2 = unsafe { std::mem::transmute(entry.handler_ptr) };
Some(MethodIcHit { handler })
} else {
None
}
}
_ => None,
}
}
#[inline]
pub(crate) fn method_ic_record(
vm: &mut VirtualMachine,
ip: usize,
receiver_kind: u8,
method_name_id: u32,
handler: &MethodHandler,
) {
if let Some(fv) = vm.current_feedback_vector() {
fv.record_method(ip, receiver_kind, method_name_id, *handler as usize);
}
}
pub(crate) struct PropertyIcHit {
pub field_idx: u16,
pub field_type_tag: u16,
}
#[inline]
pub(crate) fn property_ic_check(
vm: &VirtualMachine,
ip: usize,
runtime_schema_id: u64,
) -> Option<PropertyIcHit> {
let func_id = vm.call_stack.last()?.function_id? as usize;
let fv = vm.feedback_vectors.get(func_id)?.as_ref()?;
let slot = fv.get_slot(ip)?;
match slot {
FeedbackSlot::Property(fb) if fb.state == ICState::Monomorphic => {
let entry = fb.entries.first()?;
if entry.schema_id == runtime_schema_id {
Some(PropertyIcHit {
field_idx: entry.field_idx,
field_type_tag: entry.field_type_tag,
})
} else {
None
}
}
_ => None,
}
}
#[inline]
pub(crate) fn megamorphic_property_check(
vm: &VirtualMachine,
ip: usize,
runtime_schema_id: u64,
field_name: &str,
) -> Option<PropertyIcHit> {
let func_id = vm.call_stack.last()?.function_id? as usize;
let fv = vm.feedback_vectors.get(func_id)?.as_ref()?;
let slot = fv.get_slot(ip)?;
match slot {
FeedbackSlot::Property(fb) if fb.state == ICState::Megamorphic => {
let key =
crate::megamorphic_cache::MegamorphicCache::hash_key(runtime_schema_id, field_name);
let (field_idx, field_type_tag) = vm.megamorphic_cache.probe(key)?;
Some(PropertyIcHit {
field_idx,
field_type_tag,
})
}
_ => None,
}
}
#[inline]
pub(crate) fn megamorphic_property_insert(
vm: &mut VirtualMachine,
runtime_schema_id: u64,
field_name: &str,
field_idx: u16,
field_type_tag: u16,
) {
let key = crate::megamorphic_cache::MegamorphicCache::hash_key(runtime_schema_id, field_name);
vm.megamorphic_cache.insert(key, field_idx, field_type_tag);
}
const IC_OPERAND_I48: u8 = 1; const IC_OPERAND_F64: u8 = 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ArithmeticIcHint {
BothI48,
BothF64,
None,
}
#[inline]
pub(crate) fn arithmetic_ic_check(vm: &VirtualMachine, ip: usize) -> ArithmeticIcHint {
let hint = (|| -> Option<ArithmeticIcHint> {
let func_id = vm.call_stack.last()?.function_id? as usize;
let fv = vm.feedback_vectors.get(func_id)?.as_ref()?;
let slot = fv.get_slot(ip)?;
match slot {
FeedbackSlot::Arithmetic(fb) if fb.state == ICState::Monomorphic => {
let pair = fb.type_pairs.first()?;
if pair.left_tag == IC_OPERAND_I48 && pair.right_tag == IC_OPERAND_I48 {
Some(ArithmeticIcHint::BothI48)
} else if pair.left_tag == IC_OPERAND_F64 && pair.right_tag == IC_OPERAND_F64 {
Some(ArithmeticIcHint::BothF64)
} else {
Some(ArithmeticIcHint::None)
}
}
_ => None,
}
})();
hint.unwrap_or(ArithmeticIcHint::None)
}
pub(crate) struct DynMethodIcHit {
pub function_id: u16,
}
#[inline]
pub(crate) fn dyn_method_ic_check(
vm: &VirtualMachine,
ip: usize,
concrete_kind: u8,
method_hash: u32,
) -> Option<DynMethodIcHit> {
let func_id = vm.call_stack.last()?.function_id? as usize;
let fv = vm.feedback_vectors.get(func_id)?.as_ref()?;
let slot = fv.get_slot(ip)?;
match slot {
FeedbackSlot::Method(fb) if fb.state == ICState::Monomorphic => {
let entry = fb.entries.first()?;
if entry.receiver_kind == concrete_kind && entry.method_name_id == method_hash {
if entry.handler_ptr != 0 {
Some(DynMethodIcHit {
function_id: entry.handler_ptr as u16,
})
} else {
None
}
} else {
None
}
}
_ => None,
}
}
#[inline]
pub(crate) fn dyn_method_ic_record(
vm: &mut VirtualMachine,
ip: usize,
concrete_kind: u8,
method_hash: u32,
resolved_function_id: u16,
) {
if let Some(fv) = vm.current_feedback_vector() {
fv.record_method(
ip,
concrete_kind,
method_hash,
resolved_function_id as usize,
);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ClosureCallIcHit {
pub function_id: u16,
pub total_calls: u64,
}
pub fn closure_call_ic_check(
vm: &VirtualMachine,
ip: usize,
) -> Option<ClosureCallIcHit> {
let func_id = vm.call_stack.last()?.function_id? as usize;
let fv = vm.feedback_vectors.get(func_id)?.as_ref()?;
let slot = fv.get_slot(ip)?;
match slot {
FeedbackSlot::Call(fb) if fb.state == ICState::Monomorphic => {
let target = fb.targets.first()?;
Some(ClosureCallIcHit {
function_id: target.function_id,
total_calls: fb.total_calls,
})
}
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::feedback::FeedbackVector;
#[test]
fn test_arithmetic_ic_hint_i48() {
let mut fv = FeedbackVector::new(0);
fv.record_arithmetic(10, IC_OPERAND_I48, IC_OPERAND_I48);
assert_eq!(fv.slots.len(), 1);
match fv.get_slot(10).unwrap() {
FeedbackSlot::Arithmetic(fb) => {
assert_eq!(fb.state, ICState::Monomorphic);
assert_eq!(fb.type_pairs[0].left_tag, IC_OPERAND_I48);
assert_eq!(fb.type_pairs[0].right_tag, IC_OPERAND_I48);
}
_ => panic!("expected Arithmetic slot"),
}
}
#[test]
fn test_arithmetic_ic_hint_f64() {
let mut fv = FeedbackVector::new(0);
fv.record_arithmetic(10, IC_OPERAND_F64, IC_OPERAND_F64);
match fv.get_slot(10).unwrap() {
FeedbackSlot::Arithmetic(fb) => {
assert_eq!(fb.state, ICState::Monomorphic);
assert_eq!(fb.type_pairs[0].left_tag, IC_OPERAND_F64);
assert_eq!(fb.type_pairs[0].right_tag, IC_OPERAND_F64);
}
_ => panic!("expected Arithmetic slot"),
}
}
#[test]
fn test_method_ic_handler_roundtrip() {
fn dummy_handler(
_vm: &mut VirtualMachine,
_args: &[shape_value::KindedSlot],
_ctx: Option<&mut shape_runtime::context::ExecutionContext>,
) -> Result<shape_value::KindedSlot, shape_value::VMError> {
Ok(shape_value::KindedSlot::none())
}
let ptr = dummy_handler as MethodFnV2 as usize;
assert_ne!(ptr, 0);
let recovered: MethodFnV2 = unsafe { std::mem::transmute(ptr) };
assert_eq!(recovered as usize, ptr);
let handler: MethodHandler = dummy_handler;
assert_eq!(handler as usize, ptr);
}
}