1use super::slot::VmSlot;
4
5#[derive(Debug)]
6pub struct Frame {
7 locals: Vec<VmSlot>,
8 base: usize,
9}
10
11impl Frame {
12 pub(crate) fn entry(local_count: usize) -> Frame {
14 Frame {
15 locals: vec![VmSlot::Nil; local_count],
16 base: 0,
17 }
18 }
19
20 pub(crate) fn call(
26 local_count: usize,
27 arity: usize,
28 mut args: Vec<VmSlot>,
29 captures: Vec<VmSlot>,
30 base: usize,
31 ) -> Frame {
32 Self::call_reusing(Vec::new(), local_count, arity, &mut args, captures, base)
33 }
34
35 pub(crate) fn call_reusing(
36 mut locals: Vec<VmSlot>,
37 local_count: usize,
38 arity: usize,
39 args: &mut Vec<VmSlot>,
40 captures: Vec<VmSlot>,
41 base: usize,
42 ) -> Frame {
43 locals.clear();
44 locals.resize(local_count, VmSlot::Nil);
45 for (index, value) in args.drain(..).enumerate() {
46 if let Some(cell) = locals.get_mut(index) {
47 *cell = value;
48 }
49 }
50 for (index, value) in captures.into_iter().enumerate() {
51 if let Some(cell) = locals.get_mut(arity + index) {
52 *cell = value;
53 }
54 }
55 Frame { locals, base }
56 }
57
58 pub(crate) fn call_static_reusing(
63 mut locals: Vec<VmSlot>,
64 local_count: usize,
65 stack: &mut Vec<VmSlot>,
66 argc: usize,
67 ) -> Frame {
68 let base = stack.len() - argc;
69 locals.clear();
70 locals.resize(local_count, VmSlot::Nil);
71 for (index, value) in stack.drain(base..).enumerate() {
72 locals[index] = value;
73 }
74 Frame { locals, base }
75 }
76
77 pub(crate) fn into_locals(self) -> Vec<VmSlot> {
78 self.locals
79 }
80
81 pub(crate) fn local(&self, slot: u16) -> Option<&VmSlot> {
82 self.locals.get(usize::from(slot))
83 }
84
85 pub(crate) fn slot_range(&self, start: usize, count: usize) -> Option<Vec<VmSlot>> {
88 let end = start.checked_add(count)?;
89 if end > self.locals.len() {
90 return None;
91 }
92 Some(self.locals[start..end].to_vec())
93 }
94
95 pub(crate) fn store(&mut self, slot: u16, value: VmSlot) -> bool {
99 match self.locals.get_mut(usize::from(slot)) {
100 Some(cell) => {
101 *cell = value;
102 true
103 }
104 None => false,
105 }
106 }
107
108 pub(crate) fn locals(&self) -> &[VmSlot] {
110 &self.locals
111 }
112
113 pub(crate) fn base(&self) -> usize {
115 self.base
116 }
117
118 #[cfg(feature = "tracing-jit")]
119 pub(crate) fn trace_locals(&self) -> (Vec<crate::jit::TraceValue>, Vec<bool>) {
120 let mut scalar = Vec::with_capacity(self.locals.len());
121 let mut writable = Vec::with_capacity(self.locals.len());
122 for value in &self.locals {
123 match value {
124 VmSlot::Number(value) => {
125 scalar.push(crate::jit::TraceValue::I64(*value));
126 writable.push(true);
127 }
128 VmSlot::Bool(value) => {
129 scalar.push(crate::jit::TraceValue::Bool(*value));
130 writable.push(true);
131 }
132 VmSlot::Nil => {
133 scalar.push(crate::jit::TraceValue::Nil);
134 writable.push(true);
135 }
136 VmSlot::Value(value)
137 if matches!(
138 value.as_ref(),
139 crate::core::Value::Tuple(_) | crate::core::Value::Vector(_)
140 ) =>
141 {
142 scalar.push(crate::jit::TraceValue::Indexed(Box::new(
143 value.as_ref().clone(),
144 )));
145 writable.push(true);
146 }
147 _ => {
148 scalar.push(crate::jit::TraceValue::Unsupported);
149 writable.push(false);
150 }
151 }
152 }
153 (scalar, writable)
154 }
155
156 #[cfg(feature = "tracing-jit")]
157 pub(crate) fn apply_trace_locals(
158 &mut self,
159 values: &[crate::jit::TraceValue],
160 writable: &[bool],
161 ) {
162 for (index, (value, writable)) in values.iter().zip(writable).enumerate() {
163 if !writable {
164 continue;
165 }
166 self.locals[index] = match value {
167 crate::jit::TraceValue::I64(value) => VmSlot::Number(*value),
168 crate::jit::TraceValue::Bool(value) => VmSlot::Bool(*value),
169 crate::jit::TraceValue::Nil => VmSlot::Nil,
170 crate::jit::TraceValue::Indexed(value) => {
171 VmSlot::Value(std::rc::Rc::new(value.as_ref().clone()))
172 }
173 crate::jit::TraceValue::VectorSlice(slice) => {
174 VmSlot::Value(std::rc::Rc::new(crate::core::Value::Vector(
175 slice.values[slice.start..]
176 .iter()
177 .copied()
178 .map(crate::core::Value::Number)
179 .collect(),
180 )))
181 }
182 crate::jit::TraceValue::Unsupported => continue,
183 };
184 }
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::{Frame, VmSlot};
191
192 #[test]
193 fn static_call_moves_arguments_and_preserves_caller_stack() {
194 let mut stack = vec![VmSlot::Number(9), VmSlot::Number(20), VmSlot::Number(22)];
195 let frame = Frame::call_static_reusing(Vec::new(), 3, &mut stack, 2);
196 assert!(matches!(stack.as_slice(), [VmSlot::Number(9)]));
197 assert_eq!(frame.base(), 1);
198 assert!(matches!(frame.local(0), Some(VmSlot::Number(20))));
199 assert!(matches!(frame.local(1), Some(VmSlot::Number(22))));
200 assert!(matches!(frame.local(2), Some(VmSlot::Nil)));
201 }
202}