stak_vm/
vm.rs

1#[cfg(feature = "profile")]
2use crate::profiler::Profiler;
3use crate::{
4    Error, StackSlot,
5    code::{INTEGER_BASE, NUMBER_BASE, SHARE_BASE, TAG_BASE},
6    cons::{Cons, NEVER},
7    instruction::Instruction,
8    memory::Memory,
9    number::Number,
10    primitive_set::PrimitiveSet,
11    r#type::Type,
12    value::{TypedValue, Value},
13};
14#[cfg(feature = "profile")]
15use core::cell::RefCell;
16use core::fmt::{self, Display, Formatter};
17
18macro_rules! trace {
19    ($prefix:literal, $data:expr) => {
20        #[cfg(feature = "trace_instruction")]
21        std::eprintln!("{}: {}", $prefix, $data);
22    };
23}
24
25macro_rules! trace_memory {
26    ($self:expr) => {
27        #[cfg(feature = "trace_memory")]
28        std::eprintln!("{}", $self);
29    };
30}
31
32macro_rules! profile_event {
33    ($self:expr, $name:literal) => {
34        #[cfg(feature = "profile")]
35        (&$self).profile_event($name);
36    };
37}
38
39struct Arity {
40    // A count does not include a variadic argument.
41    count: usize,
42    variadic: bool,
43}
44
45/// A virtual machine.
46pub struct Vm<'a, T: PrimitiveSet> {
47    primitive_set: T,
48    memory: Memory<'a>,
49    #[cfg(feature = "profile")]
50    profiler: Option<RefCell<&'a mut dyn Profiler>>,
51}
52
53// Note that some routines look unnecessarily complicated as we need to mark all
54// volatile variables live across garbage collections.
55impl<'a, T: PrimitiveSet> Vm<'a, T> {
56    /// Creates a virtual machine.
57    pub fn new(heap: &'a mut [Value], primitive_set: T) -> Result<Self, Error> {
58        Ok(Self {
59            primitive_set,
60            memory: Memory::new(heap)?,
61            #[cfg(feature = "profile")]
62            profiler: None,
63        })
64    }
65
66    /// Sets a profiler.
67    #[cfg(feature = "profile")]
68    pub fn with_profiler(self, profiler: &'a mut dyn Profiler) -> Self {
69        Self {
70            profiler: Some(profiler.into()),
71            ..self
72        }
73    }
74
75    /// Returns a reference to a primitive set.
76    pub const fn primitive_set(&self) -> &T {
77        &self.primitive_set
78    }
79
80    /// Returns a mutable reference to a primitive set.
81    pub fn primitive_set_mut(&mut self) -> &mut T {
82        &mut self.primitive_set
83    }
84
85    /// Runs bytecodes on a virtual machine.
86    pub fn run(&mut self) -> Result<(), T::Error> {
87        while self.memory.code() != self.memory.null() {
88            let instruction = self.memory.cdr(self.memory.code()).assume_cons();
89
90            trace!("instruction", instruction.tag());
91
92            match instruction.tag() {
93                Instruction::CONSTANT => self.constant()?,
94                Instruction::GET => self.get()?,
95                Instruction::SET => self.set(),
96                Instruction::IF => self.r#if(),
97                code => self.call(instruction, code as usize - Instruction::CALL as usize)?,
98            }
99
100            self.advance_code();
101
102            trace_memory!(self);
103        }
104
105        Ok(())
106    }
107
108    #[inline]
109    fn constant(&mut self) -> Result<(), Error> {
110        let constant = self.operand();
111
112        trace!("constant", constant);
113
114        self.memory.push(constant)?;
115
116        Ok(())
117    }
118
119    #[inline]
120    fn get(&mut self) -> Result<(), Error> {
121        let operand = self.operand_cons();
122        let value = self.memory.car(operand);
123
124        trace!("operand", operand);
125        trace!("value", value);
126
127        self.memory.push(value)?;
128
129        Ok(())
130    }
131
132    #[inline]
133    fn set(&mut self) {
134        let operand = self.operand_cons();
135        let value = self.memory.pop();
136
137        trace!("operand", operand);
138        trace!("value", value);
139
140        self.memory.set_car(operand, value);
141    }
142
143    #[inline]
144    fn r#if(&mut self) {
145        let cons = self.memory.stack();
146
147        if self.memory.pop() != self.memory.boolean(false).into() {
148            self.memory.set_cdr(cons, self.operand());
149            self.memory.set_code(cons);
150        }
151    }
152
153    #[inline]
154    fn call(&mut self, instruction: Cons, arity: usize) -> Result<(), T::Error> {
155        let r#return = instruction == self.memory.null();
156        let procedure = self.procedure();
157
158        trace!("procedure", procedure);
159        trace!("return", r#return);
160
161        if self.environment(procedure).tag() != Type::Procedure as _ {
162            return Err(Error::ProcedureExpected.into());
163        }
164
165        match self.code(procedure).to_typed() {
166            TypedValue::Cons(code) => {
167                #[cfg(feature = "profile")]
168                self.profile_call(self.memory.code(), r#return);
169
170                let arguments = Self::parse_arity(arity);
171                let parameters =
172                    Self::parse_arity(self.memory.car(code).assume_number().to_i64() as usize);
173
174                trace!("argument count", arguments.count);
175                trace!("argument variadic", arguments.variadic);
176                trace!("parameter count", parameters.count);
177                trace!("parameter variadic", parameters.variadic);
178
179                self.memory.set_register(procedure);
180
181                let mut list = if arguments.variadic {
182                    self.memory.pop().assume_cons()
183                } else {
184                    self.memory.null()
185                };
186
187                for _ in 0..arguments.count {
188                    let value = self.memory.pop();
189                    list = self.memory.cons(value, list)?;
190                }
191
192                // Use a `code` field as an escape cell for a procedure.
193                let code = self.memory.code();
194                self.memory.set_code(self.memory.register());
195                self.memory.set_register(list);
196
197                let continuation = if r#return {
198                    self.continuation()
199                } else {
200                    self.memory
201                        .allocate(code.into(), self.memory.stack().into())?
202                };
203                let stack = self.memory.allocate(
204                    continuation.into(),
205                    self.environment(self.memory.code())
206                        .set_tag(StackSlot::Frame as _)
207                        .into(),
208                )?;
209                self.memory.set_stack(stack);
210                self.memory
211                    .set_code(self.code(self.memory.code()).assume_cons());
212
213                for _ in 0..parameters.count {
214                    if self.memory.register() == self.memory.null() {
215                        return Err(Error::ArgumentCount.into());
216                    }
217
218                    self.memory.push(self.memory.car(self.memory.register()))?;
219                    self.memory
220                        .set_register(self.memory.cdr(self.memory.register()).assume_cons());
221                }
222
223                if parameters.variadic {
224                    self.memory.push(self.memory.register().into())?;
225                } else if self.memory.register() != self.memory.null() {
226                    return Err(Error::ArgumentCount.into());
227                }
228            }
229            TypedValue::Number(primitive) => {
230                if Self::parse_arity(arity).variadic {
231                    let list = self.memory.pop().assume_cons();
232                    self.memory.set_register(list);
233
234                    while self.memory.register() != self.memory.null() {
235                        self.memory.push(self.memory.car(self.memory.register()))?;
236                        self.memory
237                            .set_register(self.memory.cdr(self.memory.register()).assume_cons());
238                    }
239                }
240
241                self.primitive_set
242                    .operate(&mut self.memory, primitive.to_i64() as _)?;
243            }
244        }
245
246        Ok(())
247    }
248
249    #[inline]
250    const fn parse_arity(info: usize) -> Arity {
251        Arity {
252            count: info / 2,
253            variadic: info % 2 == 1,
254        }
255    }
256
257    #[inline]
258    fn advance_code(&mut self) {
259        let mut code = self.memory.cdr(self.memory.code()).assume_cons();
260
261        if code == self.memory.null() {
262            #[cfg(feature = "profile")]
263            self.profile_return();
264
265            let continuation = self.continuation();
266            // Keep a value at the top of a stack.
267            self.memory
268                .set_cdr(self.memory.stack(), self.memory.cdr(continuation));
269
270            code = self
271                .memory
272                .cdr(self.memory.car(continuation).assume_cons())
273                .assume_cons();
274        }
275
276        self.memory.set_code(code);
277    }
278
279    const fn operand(&self) -> Value {
280        self.memory.car(self.memory.code())
281    }
282
283    const fn operand_cons(&self) -> Cons {
284        match self.operand().to_typed() {
285            TypedValue::Cons(cons) => cons,
286            TypedValue::Number(index) => self.memory.tail(self.memory.stack(), index.to_i64() as _),
287        }
288    }
289
290    // (code . environment)
291    const fn procedure(&self) -> Cons {
292        self.memory.car(self.operand_cons()).assume_cons()
293    }
294
295    // (parameter-count . instruction-list) | primitive-id
296    const fn code(&self, procedure: Cons) -> Value {
297        self.memory.car(procedure)
298    }
299
300    const fn environment(&self, procedure: Cons) -> Cons {
301        self.memory.cdr(procedure).assume_cons()
302    }
303
304    // (code . stack)
305    const fn continuation(&self) -> Cons {
306        let mut stack = self.memory.stack();
307
308        while self.memory.cdr(stack).assume_cons().tag() != StackSlot::Frame as _ {
309            stack = self.memory.cdr(stack).assume_cons();
310        }
311
312        self.memory.car(stack).assume_cons()
313    }
314
315    // Profiling
316
317    #[cfg(feature = "profile")]
318    fn profile_call(&self, call_code: Cons, r#return: bool) {
319        if let Some(profiler) = &self.profiler {
320            profiler
321                .borrow_mut()
322                .profile_call(&self.memory, call_code, r#return);
323        }
324    }
325
326    #[cfg(feature = "profile")]
327    fn profile_return(&self) {
328        if let Some(profiler) = &self.profiler {
329            profiler.borrow_mut().profile_return(&self.memory);
330        }
331    }
332
333    #[cfg(feature = "profile")]
334    fn profile_event(&self, name: &str) {
335        if let Some(profiler) = &self.profiler {
336            profiler.borrow_mut().profile_event(name);
337        }
338    }
339
340    /// Initializes a virtual machine with bytecodes of a program.
341    pub fn initialize(&mut self, input: impl IntoIterator<Item = u8>) -> Result<(), super::Error> {
342        profile_event!(self, "initialization_start");
343        profile_event!(self, "decode_start");
344
345        let program = self.decode_ribs(&mut input.into_iter())?;
346        self.memory
347            .set_false(self.memory.car(program).assume_cons());
348        self.memory.set_code(self.memory.cdr(program).assume_cons());
349
350        profile_event!(self, "decode_end");
351
352        // Initialize an implicit top-level frame.
353        let codes = self
354            .memory
355            .cons(Number::default().into(), self.memory.null())?
356            .into();
357        let continuation = self.memory.cons(codes, self.memory.null())?.into();
358        let stack = self.memory.allocate(
359            continuation,
360            self.memory.null().set_tag(StackSlot::Frame as _).into(),
361        )?;
362        self.memory.set_stack(stack);
363        self.memory.set_register(NEVER);
364
365        profile_event!(self, "initialization_end");
366
367        Ok(())
368    }
369
370    fn decode_ribs(&mut self, input: &mut impl Iterator<Item = u8>) -> Result<Cons, Error> {
371        while let Some(head) = input.next() {
372            if head & 1 == 0 {
373                let cdr = self.memory.pop();
374                let cons = self
375                    .memory
376                    .allocate(Number::from_i64((head >> 1) as _).into(), cdr)?;
377                self.memory.push(cons.into())?;
378            } else if head & 0b10 == 0 {
379                let head = head >> 2;
380
381                if head == 0 {
382                    let value = self.memory.top();
383                    let cons = self.memory.cons(value, self.memory.code())?;
384                    self.memory.set_code(cons);
385                } else {
386                    let integer = Self::decode_integer_tail(input, head - 1, SHARE_BASE)?;
387                    let index = integer >> 1;
388
389                    if index > 0 {
390                        let cons = self.memory.tail(self.memory.code(), index as usize - 1);
391                        let head = self.memory.cdr(cons).assume_cons();
392                        let tail = self.memory.cdr(head);
393                        self.memory.set_cdr(head, self.memory.code().into());
394                        self.memory.set_cdr(cons, tail);
395                        self.memory.set_code(head);
396                    }
397
398                    let value = self.memory.car(self.memory.code());
399
400                    if integer & 1 == 0 {
401                        self.memory
402                            .set_code(self.memory.cdr(self.memory.code()).assume_cons());
403                    }
404
405                    self.memory.push(value)?;
406                }
407            } else if head & 0b100 == 0 {
408                let cdr = self.memory.pop();
409                let car = self.memory.pop();
410                let r#type = Self::decode_integer_tail(input, head >> 3, TAG_BASE)?;
411                let cons = self.memory.allocate(car, cdr.set_tag(r#type as _))?;
412                self.memory.push(cons.into())?;
413            } else {
414                self.memory.push(
415                    Self::decode_number(Self::decode_integer_tail(input, head >> 3, NUMBER_BASE)?)
416                        .into(),
417                )?;
418            }
419        }
420
421        self.memory.pop().to_cons().ok_or(Error::BytecodeEnd)
422    }
423
424    fn decode_number(integer: u128) -> Number {
425        if integer & 1 == 0 {
426            Number::from_i64((integer >> 1) as _)
427        } else if integer & 0b10 == 0 {
428            Number::from_i64(-((integer >> 2) as i64))
429        } else {
430            let integer = integer >> 2;
431            let mantissa = if integer % 2 == 0 { 1.0 } else { -1.0 } * (integer >> 12) as f64;
432            let exponent = ((integer >> 1) % (1 << 11)) as isize - 1023;
433
434            Number::from_f64(if exponent < 0 {
435                mantissa / (1u64 << exponent.abs()) as f64
436            } else {
437                mantissa * (1u64 << exponent) as f64
438            })
439        }
440    }
441
442    fn decode_integer_tail(
443        input: &mut impl Iterator<Item = u8>,
444        mut x: u8,
445        mut base: u128,
446    ) -> Result<u128, Error> {
447        let mut y = (x >> 1) as u128;
448
449        while x & 1 != 0 {
450            x = input.next().ok_or(Error::BytecodeEnd)?;
451            y += (x as u128 >> 1) * base;
452            base *= INTEGER_BASE;
453        }
454
455        Ok(y)
456    }
457}
458
459impl<T: PrimitiveSet> Display for Vm<'_, T> {
460    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
461        write!(formatter, "{}", &self.memory)
462    }
463}