use std::rc::Rc;
use crate::chunk::Op;
use crate::{Chunk, CompiledFunction, Vm, VmClosure, VmEnv, VmValue};
pub const VMENV_CAPTURE_COUNTS: [usize; 4] = [0, 5, 25, 100];
pub const INLINE_CACHE_LOOKUP_COUNTS: [usize; 4] = [8, 32, 128, 512];
pub struct InlineCacheSlotLookupFixture {
chunk: Chunk,
offsets: Vec<usize>,
}
impl InlineCacheSlotLookupFixture {
pub fn new(op_count: usize) -> Self {
let mut chunk = Chunk::new();
let mut offsets = Vec::with_capacity(op_count);
for _ in 0..op_count {
offsets.push(chunk.code.len());
chunk.emit(Op::Add, 1);
}
Self { chunk, offsets }
}
pub fn op_count(&self) -> usize {
self.offsets.len()
}
pub fn invoke(&self) -> usize {
let mut acc = 0usize;
for &offset in &self.offsets {
if let Some(slot) = self.chunk.inline_cache_slot(offset) {
acc = acc.wrapping_add(slot);
}
}
acc
}
pub fn invoke_btreemap_control(&self) -> usize {
let mut acc = 0usize;
for &offset in &self.offsets {
if let Some(slot) = self.chunk.inline_cache_slot_via_btreemap_for_bench(offset) {
acc = acc.wrapping_add(slot);
}
}
acc
}
}
pub const ADAPTIVE_BINARY_CACHE_READ_COUNTS: [usize; 4] = [8, 32, 128, 512];
pub struct AdaptiveBinaryCacheReadFixture {
chunk: Chunk,
offsets: Vec<usize>,
slots: Vec<usize>,
}
impl AdaptiveBinaryCacheReadFixture {
pub fn new(op_count: usize) -> Self {
use crate::chunk::{AdaptiveBinaryOp, AdaptiveBinaryState, BinaryShape, InlineCacheEntry};
let mut chunk = Chunk::new();
let mut offsets = Vec::with_capacity(op_count);
for _ in 0..op_count {
offsets.push(chunk.code.len());
chunk.emit(Op::Add, 1);
}
let mut slots = Vec::with_capacity(op_count);
for &offset in &offsets {
let slot = chunk
.inline_cache_slot(offset)
.expect("Op::Add registers an inline-cache slot at emit time");
chunk.set_inline_cache_entry(
slot,
InlineCacheEntry::AdaptiveBinary {
op: AdaptiveBinaryOp::Add,
state: AdaptiveBinaryState::Specialized {
shape: BinaryShape::Int,
hits: 1_000,
misses: 0,
},
},
);
slots.push(slot);
}
Self {
chunk,
offsets,
slots,
}
}
pub fn op_count(&self) -> usize {
self.offsets.len()
}
pub fn invoke_peek(&self) -> u64 {
use crate::chunk::AdaptiveBinaryState;
let mut acc = 0u64;
for &slot in &self.slots {
if let Some((_op, state)) = self.chunk.peek_adaptive_binary_cache(slot) {
let hits = match state {
AdaptiveBinaryState::Specialized { hits, .. } => hits,
AdaptiveBinaryState::Warmup { hits, .. } => hits as u64,
};
acc = acc.wrapping_add(hits);
}
}
acc
}
pub fn invoke_clone_control(&self) -> u64 {
use crate::chunk::{AdaptiveBinaryState, InlineCacheEntry};
let mut acc = 0u64;
for &slot in &self.slots {
let entry = self.chunk.inline_cache_entry(slot);
if let InlineCacheEntry::AdaptiveBinary { state, .. } = entry {
let hits = match state {
AdaptiveBinaryState::Specialized { hits, .. } => hits,
AdaptiveBinaryState::Warmup { hits, .. } => hits as u64,
};
acc = acc.wrapping_add(hits);
}
}
acc
}
}
pub const METHOD_CACHE_READ_COUNTS: [usize; 4] = [8, 32, 128, 512];
pub struct MethodCacheReadFixture {
chunk: Chunk,
offsets: Vec<usize>,
slots: Vec<usize>,
}
impl MethodCacheReadFixture {
pub fn new(op_count: usize) -> Self {
use crate::chunk::{InlineCacheEntry, MethodCacheTarget};
let mut chunk = Chunk::new();
let mut offsets = Vec::with_capacity(op_count);
for _ in 0..op_count {
offsets.push(chunk.code.len());
chunk.emit_method_call(0, 1, 1);
}
let mut slots = Vec::with_capacity(op_count);
for &offset in &offsets {
let slot = chunk
.inline_cache_slot(offset)
.expect("Op::MethodCall registers an inline-cache slot at emit time");
chunk.set_inline_cache_entry(
slot,
InlineCacheEntry::Method {
name_idx: 0,
argc: 1,
target: MethodCacheTarget::ListContains,
},
);
slots.push(slot);
}
Self {
chunk,
offsets,
slots,
}
}
pub fn op_count(&self) -> usize {
self.offsets.len()
}
pub fn invoke_peek(&self) -> usize {
let mut acc = 0usize;
for &slot in &self.slots {
if let Some((_name_idx, argc, _target)) = self.chunk.peek_method_cache(slot) {
acc = acc.wrapping_add(argc);
}
}
acc
}
pub fn invoke_clone_control(&self) -> usize {
use crate::chunk::InlineCacheEntry;
let mut acc = 0usize;
for &slot in &self.slots {
let entry = self.chunk.inline_cache_entry(slot);
if let InlineCacheEntry::Method { argc, .. } = entry {
acc = acc.wrapping_add(argc);
}
}
acc
}
}
pub const PROPERTY_CACHE_READ_COUNTS: [usize; 4] = [8, 32, 128, 512];
pub struct PropertyCacheReadFixture {
chunk: Chunk,
offsets: Vec<usize>,
slots: Vec<usize>,
}
impl PropertyCacheReadFixture {
pub fn new(op_count: usize) -> Self {
use crate::chunk::{InlineCacheEntry, PropertyCacheTarget};
let mut chunk = Chunk::new();
let mut offsets = Vec::with_capacity(op_count);
for _ in 0..op_count {
offsets.push(chunk.code.len());
chunk.emit_u16(Op::GetProperty, 0, 1);
}
let mut slots = Vec::with_capacity(op_count);
for &offset in &offsets {
let slot = chunk
.inline_cache_slot(offset)
.expect("Op::GetProperty registers an inline-cache slot at emit time");
chunk.set_inline_cache_entry(
slot,
InlineCacheEntry::Property {
name_idx: 7,
target: PropertyCacheTarget::ListCount,
},
);
slots.push(slot);
}
Self {
chunk,
offsets,
slots,
}
}
pub fn op_count(&self) -> usize {
self.offsets.len()
}
pub fn invoke_peek(&self) -> usize {
let mut acc = 0usize;
for &slot in &self.slots {
if let Some((name_idx, _target)) = self.chunk.peek_property_cache(slot) {
acc = acc.wrapping_add(name_idx as usize);
}
}
acc
}
pub fn invoke_clone_control(&self) -> usize {
use crate::chunk::InlineCacheEntry;
let mut acc = 0usize;
for &slot in &self.slots {
let entry = self.chunk.inline_cache_entry(slot);
if let InlineCacheEntry::Property { name_idx, .. } = entry {
acc = acc.wrapping_add(name_idx as usize);
}
}
acc
}
}
pub const DIRECT_CALL_STATE_READ_COUNTS: [usize; 4] = [8, 32, 128, 512];
pub struct DirectCallStateReadFixture {
chunk: Chunk,
offsets: Vec<usize>,
slots: Vec<usize>,
}
impl DirectCallStateReadFixture {
pub fn new(op_count: usize) -> Self {
use crate::chunk::{DirectCallState, DirectCallTarget, InlineCacheEntry};
let target_closure = synthetic_direct_call_closure();
let mut chunk = Chunk::new();
let mut offsets = Vec::with_capacity(op_count);
for _ in 0..op_count {
offsets.push(chunk.code.len());
chunk.emit_u8(Op::Call, 1, 1);
}
let mut slots = Vec::with_capacity(op_count);
for &offset in &offsets {
let slot = chunk
.inline_cache_slot(offset)
.expect("Op::Call registers an inline-cache slot at emit time");
chunk.set_inline_cache_entry(
slot,
InlineCacheEntry::DirectCall {
state: DirectCallState::Specialized {
argc: 1,
target: DirectCallTarget::Closure(Rc::clone(&target_closure)),
hits: 1_000,
misses: 0,
},
},
);
slots.push(slot);
}
Self {
chunk,
offsets,
slots,
}
}
pub fn op_count(&self) -> usize {
self.offsets.len()
}
pub fn invoke_peek(&self) -> usize {
use crate::chunk::DirectCallState;
let mut acc = 0usize;
for &slot in &self.slots {
if let Some(DirectCallState::Specialized { argc, .. }) =
self.chunk.peek_direct_call_state(slot)
{
acc = acc.wrapping_add(argc);
}
}
acc
}
pub fn invoke_clone_control(&self) -> usize {
use crate::chunk::{DirectCallState, InlineCacheEntry};
let mut acc = 0usize;
for &slot in &self.slots {
let entry = self.chunk.inline_cache_entry(slot);
if let InlineCacheEntry::DirectCall {
state: DirectCallState::Specialized { argc, .. },
} = entry
{
acc = acc.wrapping_add(argc);
}
}
acc
}
}
fn synthetic_direct_call_closure() -> Rc<VmClosure> {
let func = CompiledFunction {
name: "synthetic_direct_call_target".to_string(),
type_params: Vec::new(),
nominal_type_names: Vec::new(),
params: Vec::new(),
default_start: None,
chunk: Rc::new(Chunk::new()),
is_generator: false,
is_stream: false,
has_rest_param: false,
has_runtime_type_checks: false,
};
Rc::new(VmClosure {
func: Rc::new(func),
env: VmEnv::new(),
source_dir: None,
module_functions: None,
module_state: None,
})
}
pub struct NonModuleClosureCallFixture {
capture_count: usize,
last_capture_name: Option<String>,
caller_env: VmEnv,
closure: VmClosure,
}
impl NonModuleClosureCallFixture {
pub fn new(capture_count: usize) -> Self {
let nested_inner = synthetic_closure("nested_inner", VmEnv::new());
let mut caller_env = VmEnv::new();
caller_env
.define(
"nested_inner",
VmValue::Closure(Rc::new(nested_inner)),
false,
)
.expect("synthetic caller closure binding should be valid");
let mut closure_env = VmEnv::new();
for index in 0..capture_count {
closure_env
.define(
&format!("captured_{index:03}"),
VmValue::Int(index as i64),
false,
)
.expect("synthetic captured binding should be valid");
}
let closure = synthetic_closure(&format!("capture_{capture_count:03}"), closure_env);
Self {
capture_count,
last_capture_name: capture_count
.checked_sub(1)
.map(|index| format!("captured_{index:03}")),
caller_env,
closure,
}
}
pub fn capture_count(&self) -> usize {
self.capture_count
}
pub fn invoke(&self) -> usize {
let env = Vm::closure_call_env(&self.caller_env, &self.closure);
let mut score = env.scope_depth();
if let Some(name) = self.last_capture_name.as_deref() {
if let Some(VmValue::Int(value)) = env.get(name) {
score += value as usize;
}
}
if matches!(env.get("nested_inner"), Some(VmValue::Closure(_))) {
score += 1;
}
score
}
}
fn synthetic_closure(name: &str, env: VmEnv) -> VmClosure {
let func = CompiledFunction {
name: name.to_string(),
type_params: Vec::new(),
nominal_type_names: Vec::new(),
params: Vec::new(),
default_start: None,
chunk: Rc::new(Chunk::new()),
is_generator: false,
is_stream: false,
has_rest_param: false,
has_runtime_type_checks: false,
};
VmClosure {
func: Rc::new(func),
env,
source_dir: None,
module_functions: None,
module_state: None,
}
}