use super::slot::VmSlot;
#[derive(Debug)]
pub struct Frame {
locals: Vec<VmSlot>,
base: usize,
}
impl Frame {
pub(crate) fn entry(local_count: usize) -> Frame {
Frame {
locals: vec![VmSlot::Nil; local_count],
base: 0,
}
}
pub(crate) fn call(
local_count: usize,
arity: usize,
mut args: Vec<VmSlot>,
captures: Vec<VmSlot>,
base: usize,
) -> Frame {
Self::call_reusing(Vec::new(), local_count, arity, &mut args, captures, base)
}
pub(crate) fn call_reusing(
mut locals: Vec<VmSlot>,
local_count: usize,
arity: usize,
args: &mut Vec<VmSlot>,
captures: Vec<VmSlot>,
base: usize,
) -> Frame {
locals.clear();
locals.resize(local_count, VmSlot::Nil);
for (index, value) in args.drain(..).enumerate() {
if let Some(cell) = locals.get_mut(index) {
*cell = value;
}
}
for (index, value) in captures.into_iter().enumerate() {
if let Some(cell) = locals.get_mut(arity + index) {
*cell = value;
}
}
Frame { locals, base }
}
pub(crate) fn call_static_reusing(
mut locals: Vec<VmSlot>,
local_count: usize,
stack: &mut Vec<VmSlot>,
argc: usize,
) -> Frame {
let base = stack.len() - argc;
locals.clear();
locals.resize(local_count, VmSlot::Nil);
for (index, value) in stack.drain(base..).enumerate() {
locals[index] = value;
}
Frame { locals, base }
}
pub(crate) fn into_locals(self) -> Vec<VmSlot> {
self.locals
}
pub(crate) fn local(&self, slot: u16) -> Option<&VmSlot> {
self.locals.get(usize::from(slot))
}
pub(crate) fn slot_range(&self, start: usize, count: usize) -> Option<Vec<VmSlot>> {
let end = start.checked_add(count)?;
if end > self.locals.len() {
return None;
}
Some(self.locals[start..end].to_vec())
}
pub(crate) fn store(&mut self, slot: u16, value: VmSlot) -> bool {
match self.locals.get_mut(usize::from(slot)) {
Some(cell) => {
*cell = value;
true
}
None => false,
}
}
pub(crate) fn locals(&self) -> &[VmSlot] {
&self.locals
}
pub(crate) fn base(&self) -> usize {
self.base
}
#[cfg(feature = "tracing-jit")]
pub(crate) fn trace_locals(&self) -> (Vec<crate::jit::TraceValue>, Vec<bool>) {
let mut scalar = Vec::with_capacity(self.locals.len());
let mut writable = Vec::with_capacity(self.locals.len());
for value in &self.locals {
match value {
VmSlot::Number(value) => {
scalar.push(crate::jit::TraceValue::I64(*value));
writable.push(true);
}
VmSlot::Bool(value) => {
scalar.push(crate::jit::TraceValue::Bool(*value));
writable.push(true);
}
VmSlot::Nil => {
scalar.push(crate::jit::TraceValue::Nil);
writable.push(true);
}
VmSlot::Value(value)
if matches!(
value.as_ref(),
crate::core::Value::Tuple(_) | crate::core::Value::Vector(_)
) =>
{
scalar.push(crate::jit::TraceValue::Indexed(Box::new(
value.as_ref().clone(),
)));
writable.push(true);
}
_ => {
scalar.push(crate::jit::TraceValue::Unsupported);
writable.push(false);
}
}
}
(scalar, writable)
}
#[cfg(feature = "tracing-jit")]
pub(crate) fn apply_trace_locals(
&mut self,
values: &[crate::jit::TraceValue],
writable: &[bool],
) {
for (index, (value, writable)) in values.iter().zip(writable).enumerate() {
if !writable {
continue;
}
self.locals[index] = match value {
crate::jit::TraceValue::I64(value) => VmSlot::Number(*value),
crate::jit::TraceValue::Bool(value) => VmSlot::Bool(*value),
crate::jit::TraceValue::Nil => VmSlot::Nil,
crate::jit::TraceValue::Indexed(value) => {
VmSlot::Value(std::rc::Rc::new(value.as_ref().clone()))
}
crate::jit::TraceValue::VectorSlice(slice) => {
VmSlot::Value(std::rc::Rc::new(crate::core::Value::Vector(
slice.values[slice.start..]
.iter()
.copied()
.map(crate::core::Value::Number)
.collect(),
)))
}
crate::jit::TraceValue::Unsupported => continue,
};
}
}
}
#[cfg(test)]
mod tests {
use super::{Frame, VmSlot};
#[test]
fn static_call_moves_arguments_and_preserves_caller_stack() {
let mut stack = vec![VmSlot::Number(9), VmSlot::Number(20), VmSlot::Number(22)];
let frame = Frame::call_static_reusing(Vec::new(), 3, &mut stack, 2);
assert!(matches!(stack.as_slice(), [VmSlot::Number(9)]));
assert_eq!(frame.base(), 1);
assert!(matches!(frame.local(0), Some(VmSlot::Number(20))));
assert!(matches!(frame.local(1), Some(VmSlot::Number(22))));
assert!(matches!(frame.local(2), Some(VmSlot::Nil)));
}
}