#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FeedbackSlotKind {
Call,
LoadProperty,
StoreProperty,
KeyedLoadProperty,
KeyedStoreProperty,
BinaryOp,
Compare,
ForIn,
TypeOf,
CreateClosure,
LoadGlobal,
StoreGlobal,
InstanceOf,
BinaryOpInc,
UnaryOp,
Literal,
DefineAccessor,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum InlineCacheState {
Uninitialized,
Monomorphic,
Polymorphic,
Megamorphic,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FeedbackMetadata {
slot_kinds: Vec<FeedbackSlotKind>,
}
impl FeedbackMetadata {
pub fn new(slot_kinds: Vec<FeedbackSlotKind>) -> Self {
Self { slot_kinds }
}
pub fn empty() -> Self {
Self {
slot_kinds: Vec::new(),
}
}
pub fn slot_count(&self) -> u32 {
self.slot_kinds.len() as u32
}
pub fn kind_of(&self, slot: u32) -> Option<FeedbackSlotKind> {
self.slot_kinds.get(slot as usize).copied()
}
pub fn slot_kinds(&self) -> &[FeedbackSlotKind] {
&self.slot_kinds
}
}
#[derive(Debug, Clone)]
pub struct FeedbackVector {
slot_kinds: Vec<FeedbackSlotKind>,
states: Vec<InlineCacheState>,
}
impl FeedbackVector {
pub fn new(metadata: &FeedbackMetadata) -> Self {
let count = metadata.slot_kinds.len();
Self {
slot_kinds: metadata.slot_kinds.clone(),
states: vec![InlineCacheState::Uninitialized; count],
}
}
pub fn slot_count(&self) -> u32 {
self.states.len() as u32
}
pub fn kind_of(&self, slot: u32) -> Option<FeedbackSlotKind> {
self.slot_kinds.get(slot as usize).copied()
}
pub fn get_state(&self, slot: u32) -> Option<InlineCacheState> {
self.states.get(slot as usize).copied()
}
pub fn set_state(&mut self, slot: u32, state: InlineCacheState) -> bool {
match self.states.get_mut(slot as usize) {
Some(s) => {
*s = state;
true
}
None => false,
}
}
pub fn transition(&mut self, slot: u32, new_state: InlineCacheState) -> bool {
match self.states.get_mut(slot as usize) {
Some(s) if new_state > *s => {
*s = new_state;
true
}
_ => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_metadata() -> FeedbackMetadata {
FeedbackMetadata::new(vec![
FeedbackSlotKind::Call,
FeedbackSlotKind::LoadProperty,
FeedbackSlotKind::BinaryOp,
])
}
#[test]
fn test_metadata_slot_count() {
let m = make_metadata();
assert_eq!(m.slot_count(), 3);
}
#[test]
fn test_metadata_kind_of() {
let m = make_metadata();
assert_eq!(m.kind_of(0), Some(FeedbackSlotKind::Call));
assert_eq!(m.kind_of(1), Some(FeedbackSlotKind::LoadProperty));
assert_eq!(m.kind_of(2), Some(FeedbackSlotKind::BinaryOp));
assert_eq!(m.kind_of(3), None);
}
#[test]
fn test_metadata_empty() {
let m = FeedbackMetadata::empty();
assert_eq!(m.slot_count(), 0);
assert_eq!(m.kind_of(0), None);
assert!(m.slot_kinds().is_empty());
}
#[test]
fn test_vector_initial_state_uninitialized() {
let m = make_metadata();
let v = FeedbackVector::new(&m);
assert_eq!(v.slot_count(), 3);
for i in 0..3 {
assert_eq!(v.get_state(i), Some(InlineCacheState::Uninitialized));
}
}
#[test]
fn test_vector_out_of_range() {
let m = make_metadata();
let v = FeedbackVector::new(&m);
assert_eq!(v.get_state(3), None);
assert_eq!(v.kind_of(3), None);
}
#[test]
fn test_vector_set_state() {
let m = make_metadata();
let mut v = FeedbackVector::new(&m);
assert!(v.set_state(0, InlineCacheState::Monomorphic));
assert_eq!(v.get_state(0), Some(InlineCacheState::Monomorphic));
assert!(!v.set_state(99, InlineCacheState::Megamorphic));
}
#[test]
fn test_vector_transition_advances_forward() {
let m = FeedbackMetadata::new(vec![FeedbackSlotKind::BinaryOp]);
let mut v = FeedbackVector::new(&m);
assert!(v.transition(0, InlineCacheState::Monomorphic));
assert_eq!(v.get_state(0), Some(InlineCacheState::Monomorphic));
assert!(v.transition(0, InlineCacheState::Polymorphic));
assert_eq!(v.get_state(0), Some(InlineCacheState::Polymorphic));
assert!(v.transition(0, InlineCacheState::Megamorphic));
assert_eq!(v.get_state(0), Some(InlineCacheState::Megamorphic));
}
#[test]
fn test_vector_transition_does_not_go_backward() {
let m = FeedbackMetadata::new(vec![FeedbackSlotKind::Compare]);
let mut v = FeedbackVector::new(&m);
v.set_state(0, InlineCacheState::Megamorphic);
assert!(!v.transition(0, InlineCacheState::Monomorphic));
assert_eq!(v.get_state(0), Some(InlineCacheState::Megamorphic));
}
#[test]
fn test_vector_kind_of() {
let m = make_metadata();
let v = FeedbackVector::new(&m);
assert_eq!(v.kind_of(0), Some(FeedbackSlotKind::Call));
assert_eq!(v.kind_of(1), Some(FeedbackSlotKind::LoadProperty));
assert_eq!(v.kind_of(2), Some(FeedbackSlotKind::BinaryOp));
}
#[test]
fn test_vector_empty_metadata() {
let m = FeedbackMetadata::empty();
let v = FeedbackVector::new(&m);
assert_eq!(v.slot_count(), 0);
assert_eq!(v.get_state(0), None);
}
#[test]
fn test_inline_cache_state_ordering() {
use InlineCacheState::*;
assert!(Uninitialized < Monomorphic);
assert!(Monomorphic < Polymorphic);
assert!(Polymorphic < Megamorphic);
}
#[test]
fn test_all_slot_kind_variants_round_trip() {
let all_kinds = vec![
FeedbackSlotKind::Call,
FeedbackSlotKind::LoadProperty,
FeedbackSlotKind::StoreProperty,
FeedbackSlotKind::KeyedLoadProperty,
FeedbackSlotKind::KeyedStoreProperty,
FeedbackSlotKind::BinaryOp,
FeedbackSlotKind::Compare,
FeedbackSlotKind::ForIn,
FeedbackSlotKind::TypeOf,
FeedbackSlotKind::CreateClosure,
FeedbackSlotKind::LoadGlobal,
FeedbackSlotKind::StoreGlobal,
FeedbackSlotKind::InstanceOf,
FeedbackSlotKind::BinaryOpInc,
FeedbackSlotKind::UnaryOp,
FeedbackSlotKind::Literal,
FeedbackSlotKind::DefineAccessor,
];
let metadata = FeedbackMetadata::new(all_kinds.clone());
assert_eq!(metadata.slot_count(), 17);
for (i, &expected) in all_kinds.iter().enumerate() {
assert_eq!(metadata.kind_of(i as u32), Some(expected));
}
assert_eq!(metadata.kind_of(17), None);
}
#[test]
fn test_metadata_slot_kinds_nonempty() {
let m = make_metadata();
let kinds = m.slot_kinds();
assert_eq!(kinds.len(), 3);
assert_eq!(kinds[0], FeedbackSlotKind::Call);
assert_eq!(kinds[1], FeedbackSlotKind::LoadProperty);
assert_eq!(kinds[2], FeedbackSlotKind::BinaryOp);
}
#[test]
fn test_metadata_equality() {
let a = FeedbackMetadata::new(vec![FeedbackSlotKind::Call, FeedbackSlotKind::Compare]);
let b = FeedbackMetadata::new(vec![FeedbackSlotKind::Call, FeedbackSlotKind::Compare]);
let c = FeedbackMetadata::new(vec![FeedbackSlotKind::Call]);
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn test_transition_same_state_returns_false() {
let m = FeedbackMetadata::new(vec![FeedbackSlotKind::BinaryOp]);
let mut v = FeedbackVector::new(&m);
assert!(!v.transition(0, InlineCacheState::Uninitialized));
assert_eq!(v.get_state(0), Some(InlineCacheState::Uninitialized));
v.set_state(0, InlineCacheState::Polymorphic);
assert!(!v.transition(0, InlineCacheState::Polymorphic));
assert_eq!(v.get_state(0), Some(InlineCacheState::Polymorphic));
}
#[test]
fn test_transition_out_of_range_returns_false() {
let m = FeedbackMetadata::new(vec![FeedbackSlotKind::Call]);
let mut v = FeedbackVector::new(&m);
assert!(!v.transition(99, InlineCacheState::Megamorphic));
}
#[test]
fn test_set_state_allows_downgrade() {
let m = FeedbackMetadata::new(vec![FeedbackSlotKind::Compare]);
let mut v = FeedbackVector::new(&m);
v.set_state(0, InlineCacheState::Megamorphic);
assert!(v.set_state(0, InlineCacheState::Uninitialized));
assert_eq!(v.get_state(0), Some(InlineCacheState::Uninitialized));
}
#[test]
fn test_full_transition_chain() {
let m = FeedbackMetadata::new(vec![FeedbackSlotKind::LoadProperty]);
let mut v = FeedbackVector::new(&m);
assert_eq!(v.get_state(0), Some(InlineCacheState::Uninitialized));
assert!(v.transition(0, InlineCacheState::Monomorphic));
assert_eq!(v.get_state(0), Some(InlineCacheState::Monomorphic));
assert!(v.transition(0, InlineCacheState::Polymorphic));
assert_eq!(v.get_state(0), Some(InlineCacheState::Polymorphic));
assert!(v.transition(0, InlineCacheState::Megamorphic));
assert_eq!(v.get_state(0), Some(InlineCacheState::Megamorphic));
assert!(!v.transition(0, InlineCacheState::Megamorphic));
assert!(!v.transition(0, InlineCacheState::Polymorphic));
}
#[test]
fn test_vector_slot_count_matches_metadata() {
for n in [0usize, 1, 5, 100] {
let kinds = vec![FeedbackSlotKind::BinaryOp; n];
let m = FeedbackMetadata::new(kinds);
let v = FeedbackVector::new(&m);
assert_eq!(v.slot_count(), n as u32);
assert_eq!(v.slot_count(), m.slot_count());
}
}
#[test]
fn test_vector_set_state_all_slots() {
let m = FeedbackMetadata::new(vec![
FeedbackSlotKind::Call,
FeedbackSlotKind::LoadProperty,
FeedbackSlotKind::Compare,
]);
let mut v = FeedbackVector::new(&m);
v.set_state(0, InlineCacheState::Monomorphic);
v.set_state(1, InlineCacheState::Polymorphic);
v.set_state(2, InlineCacheState::Megamorphic);
assert_eq!(v.get_state(0), Some(InlineCacheState::Monomorphic));
assert_eq!(v.get_state(1), Some(InlineCacheState::Polymorphic));
assert_eq!(v.get_state(2), Some(InlineCacheState::Megamorphic));
}
}