stak_vm/
vm.rs

1#[cfg(feature = "profile")]
2use crate::profiler::Profiler;
3use crate::{
4    Error, Exception, 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, Write};
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
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40struct Arity {
41    // A count does not include a variadic argument.
42    count: usize,
43    variadic: bool,
44}
45
46/// A virtual machine.
47pub struct Vm<'a, T: PrimitiveSet> {
48    primitive_set: T,
49    memory: Memory<'a>,
50    #[cfg(feature = "profile")]
51    profiler: Option<RefCell<&'a mut dyn Profiler>>,
52}
53
54// Note that some routines look unnecessarily complicated as we need to mark all
55// volatile variables live across garbage collections.
56impl<'a, T: PrimitiveSet> Vm<'a, T> {
57    /// Creates a virtual machine.
58    pub fn new(heap: &'a mut [Value], primitive_set: T) -> Result<Self, Error> {
59        Ok(Self {
60            primitive_set,
61            memory: Memory::new(heap)?,
62            #[cfg(feature = "profile")]
63            profiler: None,
64        })
65    }
66
67    /// Sets a profiler.
68    #[cfg(feature = "profile")]
69    pub fn with_profiler(self, profiler: &'a mut dyn Profiler) -> Self {
70        Self {
71            profiler: Some(profiler.into()),
72            ..self
73        }
74    }
75
76    /// Returns a reference to a primitive set.
77    pub const fn primitive_set(&self) -> &T {
78        &self.primitive_set
79    }
80
81    /// Returns a mutable reference to a primitive set.
82    pub const fn primitive_set_mut(&mut self) -> &mut T {
83        &mut self.primitive_set
84    }
85
86    /// Runs bytecodes on a virtual machine.
87    pub fn run(&mut self) -> Result<(), T::Error> {
88        while let Err(error) = self.run_with_continuation() {
89            if error.is_critical() {
90                return Err(error);
91            }
92
93            let Some(continuation) = self.memory.cdr(self.memory.null()).to_cons() else {
94                return Err(error);
95            };
96
97            if self.memory.cdr(continuation).tag() != Type::Procedure as _ {
98                return Err(error);
99            }
100
101            self.memory.set_register(continuation);
102            let string = self.memory.build_string("")?;
103            let symbol = self.memory.allocate(
104                self.memory.register().into(),
105                string.set_tag(Type::Symbol as _).into(),
106            )?;
107            let code = self.memory.allocate(
108                symbol.into(),
109                self.memory
110                    .code()
111                    .set_tag(
112                        Instruction::Call as u16
113                            + Self::build_arity(Arity {
114                                count: 1,
115                                variadic: false,
116                            }) as u16,
117                    )
118                    .into(),
119            )?;
120            self.memory.set_code(code);
121
122            self.memory.set_register(self.memory.null());
123            write!(&mut self.memory, "{error}").map_err(Error::from)?;
124            let code = self.memory.allocate(
125                self.memory.register().into(),
126                self.memory
127                    .code()
128                    .set_tag(Instruction::Constant as _)
129                    .into(),
130            )?;
131            self.memory.set_code(code);
132        }
133
134        Ok(())
135    }
136
137    fn run_with_continuation(&mut self) -> Result<(), T::Error> {
138        while self.memory.code() != self.memory.null() {
139            let instruction = self.memory.cdr(self.memory.code()).assume_cons();
140
141            trace!("instruction", instruction.tag());
142
143            match instruction.tag() {
144                Instruction::CONSTANT => self.constant()?,
145                Instruction::GET => self.get()?,
146                Instruction::SET => self.set(),
147                Instruction::IF => self.r#if(),
148                code => self.call(instruction, code as usize - Instruction::CALL as usize)?,
149            }
150
151            self.advance_code();
152
153            trace_memory!(self);
154        }
155
156        Ok(())
157    }
158
159    #[inline]
160    fn constant(&mut self) -> Result<(), Error> {
161        let constant = self.operand();
162
163        trace!("constant", constant);
164
165        self.memory.push(constant)?;
166
167        Ok(())
168    }
169
170    #[inline]
171    fn get(&mut self) -> Result<(), Error> {
172        let operand = self.operand_cons();
173        let value = self.memory.car(operand);
174
175        trace!("operand", operand);
176        trace!("value", value);
177
178        self.memory.push(value)?;
179
180        Ok(())
181    }
182
183    #[inline]
184    fn set(&mut self) {
185        let operand = self.operand_cons();
186        let value = self.memory.pop();
187
188        trace!("operand", operand);
189        trace!("value", value);
190
191        self.memory.set_car(operand, value);
192    }
193
194    #[inline]
195    fn r#if(&mut self) {
196        let cons = self.memory.stack();
197
198        if self.memory.pop() != self.memory.boolean(false).into() {
199            self.memory.set_cdr(cons, self.operand());
200            self.memory.set_code(cons);
201        }
202    }
203
204    #[inline]
205    fn call(&mut self, instruction: Cons, arity: usize) -> Result<(), T::Error> {
206        let r#return = instruction == self.memory.null();
207        let procedure = self.procedure();
208
209        trace!("procedure", procedure);
210        trace!("return", r#return);
211
212        if self.environment(procedure).tag() != Type::Procedure as _ {
213            return Err(Error::ProcedureExpected.into());
214        }
215
216        match self.code(procedure).to_typed() {
217            TypedValue::Cons(code) => {
218                #[cfg(feature = "profile")]
219                self.profile_call(self.memory.code(), r#return);
220
221                let arguments = Self::parse_arity(arity);
222                let parameters =
223                    Self::parse_arity(self.memory.car(code).assume_number().to_i64() as usize);
224
225                trace!("argument count", arguments.count);
226                trace!("argument variadic", arguments.variadic);
227                trace!("parameter count", parameters.count);
228                trace!("parameter variadic", parameters.variadic);
229
230                self.memory.set_register(procedure);
231
232                let mut list = if arguments.variadic {
233                    self.memory.pop().assume_cons()
234                } else {
235                    self.memory.null()
236                };
237
238                for _ in 0..arguments.count {
239                    let value = self.memory.pop();
240                    list = self.memory.cons(value, list)?;
241                }
242
243                // Use a `code` field as an escape cell for a procedure.
244                let code = self.memory.code();
245                self.memory.set_code(self.memory.register());
246                self.memory.set_register(list);
247
248                let continuation = if r#return {
249                    self.continuation()
250                } else {
251                    self.memory
252                        .allocate(code.into(), self.memory.stack().into())?
253                };
254                let stack = self.memory.allocate(
255                    continuation.into(),
256                    self.environment(self.memory.code())
257                        .set_tag(StackSlot::Frame as _)
258                        .into(),
259                )?;
260                self.memory.set_stack(stack);
261                self.memory
262                    .set_code(self.code(self.memory.code()).assume_cons());
263
264                for _ in 0..parameters.count {
265                    if self.memory.register() == self.memory.null() {
266                        return Err(Error::ArgumentCount.into());
267                    }
268
269                    self.memory.push(self.memory.car(self.memory.register()))?;
270                    self.memory
271                        .set_register(self.memory.cdr(self.memory.register()).assume_cons());
272                }
273
274                if parameters.variadic {
275                    self.memory.push(self.memory.register().into())?;
276                } else if self.memory.register() != self.memory.null() {
277                    return Err(Error::ArgumentCount.into());
278                }
279            }
280            TypedValue::Number(primitive) => {
281                if Self::parse_arity(arity).variadic {
282                    let list = self.memory.pop().assume_cons();
283                    self.memory.set_register(list);
284
285                    while self.memory.register() != self.memory.null() {
286                        self.memory.push(self.memory.car(self.memory.register()))?;
287                        self.memory
288                            .set_register(self.memory.cdr(self.memory.register()).assume_cons());
289                    }
290                }
291
292                self.primitive_set
293                    .operate(&mut self.memory, primitive.to_i64() as _)?;
294            }
295        }
296
297        Ok(())
298    }
299
300    #[inline]
301    const fn parse_arity(info: usize) -> Arity {
302        Arity {
303            count: info / 2,
304            variadic: info % 2 == 1,
305        }
306    }
307
308    #[inline]
309    const fn build_arity(arity: Arity) -> usize {
310        2 * arity.count + arity.variadic as usize
311    }
312
313    #[inline]
314    fn advance_code(&mut self) {
315        let mut code = self.memory.cdr(self.memory.code()).assume_cons();
316
317        if code == self.memory.null() {
318            #[cfg(feature = "profile")]
319            self.profile_return();
320
321            let continuation = self.continuation();
322            // Keep a value at the top of a stack.
323            self.memory
324                .set_cdr(self.memory.stack(), self.memory.cdr(continuation));
325
326            code = self
327                .memory
328                .cdr(self.memory.car(continuation).assume_cons())
329                .assume_cons();
330        }
331
332        self.memory.set_code(code);
333    }
334
335    const fn operand(&self) -> Value {
336        self.memory.car(self.memory.code())
337    }
338
339    const fn operand_cons(&self) -> Cons {
340        match self.operand().to_typed() {
341            TypedValue::Cons(cons) => cons,
342            TypedValue::Number(index) => self.memory.tail(self.memory.stack(), index.to_i64() as _),
343        }
344    }
345
346    // (code . environment)
347    const fn procedure(&self) -> Cons {
348        self.memory.car(self.operand_cons()).assume_cons()
349    }
350
351    // (parameter-count . instruction-list) | primitive-id
352    const fn code(&self, procedure: Cons) -> Value {
353        self.memory.car(procedure)
354    }
355
356    const fn environment(&self, procedure: Cons) -> Cons {
357        self.memory.cdr(procedure).assume_cons()
358    }
359
360    // (code . stack)
361    const fn continuation(&self) -> Cons {
362        let mut stack = self.memory.stack();
363
364        while self.memory.cdr(stack).assume_cons().tag() != StackSlot::Frame as _ {
365            stack = self.memory.cdr(stack).assume_cons();
366        }
367
368        self.memory.car(stack).assume_cons()
369    }
370
371    // Profiling
372
373    #[cfg(feature = "profile")]
374    fn profile_call(&self, call_code: Cons, r#return: bool) {
375        if let Some(profiler) = &self.profiler {
376            profiler
377                .borrow_mut()
378                .profile_call(&self.memory, call_code, r#return);
379        }
380    }
381
382    #[cfg(feature = "profile")]
383    fn profile_return(&self) {
384        if let Some(profiler) = &self.profiler {
385            profiler.borrow_mut().profile_return(&self.memory);
386        }
387    }
388
389    #[cfg(feature = "profile")]
390    fn profile_event(&self, name: &str) {
391        if let Some(profiler) = &self.profiler {
392            profiler.borrow_mut().profile_event(name);
393        }
394    }
395
396    /// Initializes a virtual machine with bytecodes of a program.
397    pub fn initialize(&mut self, input: impl IntoIterator<Item = u8>) -> Result<(), super::Error> {
398        profile_event!(self, "initialization_start");
399        profile_event!(self, "decode_start");
400
401        let program = self.decode_ribs(&mut input.into_iter())?;
402        self.memory
403            .set_false(self.memory.car(program).assume_cons());
404        self.memory.set_code(self.memory.cdr(program).assume_cons());
405
406        profile_event!(self, "decode_end");
407
408        // Initialize an implicit top-level frame.
409        let codes = self
410            .memory
411            .cons(Number::default().into(), self.memory.null())?
412            .into();
413        let continuation = self.memory.cons(codes, self.memory.null())?.into();
414        let stack = self.memory.allocate(
415            continuation,
416            self.memory.null().set_tag(StackSlot::Frame as _).into(),
417        )?;
418        self.memory.set_stack(stack);
419        self.memory.set_register(NEVER);
420
421        profile_event!(self, "initialization_end");
422
423        Ok(())
424    }
425
426    fn decode_ribs(&mut self, input: &mut impl Iterator<Item = u8>) -> Result<Cons, Error> {
427        while let Some(head) = input.next() {
428            if head & 1 == 0 {
429                let cdr = self.memory.pop();
430                let cons = self
431                    .memory
432                    .allocate(Number::from_i64((head >> 1) as _).into(), cdr)?;
433                self.memory.push(cons.into())?;
434            } else if head & 0b10 == 0 {
435                let head = head >> 2;
436
437                if head == 0 {
438                    let value = self.memory.top();
439                    let cons = self.memory.cons(value, self.memory.code())?;
440                    self.memory.set_code(cons);
441                } else {
442                    let integer = Self::decode_integer_tail(input, head - 1, SHARE_BASE)?;
443                    let index = integer >> 1;
444
445                    if index > 0 {
446                        let cons = self.memory.tail(self.memory.code(), index as usize - 1);
447                        let head = self.memory.cdr(cons).assume_cons();
448                        let tail = self.memory.cdr(head);
449                        self.memory.set_cdr(head, self.memory.code().into());
450                        self.memory.set_cdr(cons, tail);
451                        self.memory.set_code(head);
452                    }
453
454                    let value = self.memory.car(self.memory.code());
455
456                    if integer & 1 == 0 {
457                        self.memory
458                            .set_code(self.memory.cdr(self.memory.code()).assume_cons());
459                    }
460
461                    self.memory.push(value)?;
462                }
463            } else if head & 0b100 == 0 {
464                let cdr = self.memory.pop();
465                let car = self.memory.pop();
466                let r#type = Self::decode_integer_tail(input, head >> 3, TAG_BASE)?;
467                let cons = self.memory.allocate(car, cdr.set_tag(r#type as _))?;
468                self.memory.push(cons.into())?;
469            } else {
470                self.memory.push(
471                    Self::decode_number(Self::decode_integer_tail(input, head >> 3, NUMBER_BASE)?)
472                        .into(),
473                )?;
474            }
475        }
476
477        self.memory.pop().to_cons().ok_or(Error::BytecodeEnd)
478    }
479
480    fn decode_number(integer: u128) -> Number {
481        if integer & 1 == 0 {
482            Number::from_i64((integer >> 1) as _)
483        } else if integer & 0b10 == 0 {
484            Number::from_i64(-((integer >> 2) as i64))
485        } else {
486            let integer = integer >> 2;
487            let mantissa = if integer % 2 == 0 { 1.0 } else { -1.0 } * (integer >> 12) as f64;
488            let exponent = ((integer >> 1) % (1 << 11)) as isize - 1023;
489
490            Number::from_f64(if exponent < 0 {
491                mantissa / (1u64 << exponent.abs()) as f64
492            } else {
493                mantissa * (1u64 << exponent) as f64
494            })
495        }
496    }
497
498    fn decode_integer_tail(
499        input: &mut impl Iterator<Item = u8>,
500        mut x: u8,
501        mut base: u128,
502    ) -> Result<u128, Error> {
503        let mut y = (x >> 1) as u128;
504
505        while x & 1 != 0 {
506            x = input.next().ok_or(Error::BytecodeEnd)?;
507            y += (x as u128 >> 1) * base;
508            base *= INTEGER_BASE;
509        }
510
511        Ok(y)
512    }
513}
514
515impl<T: PrimitiveSet> Display for Vm<'_, T> {
516    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
517        write!(formatter, "{}", &self.memory)
518    }
519}
520
521#[cfg(test)]
522mod tests {
523    use super::*;
524
525    struct FakePrimitiveSet {}
526
527    impl PrimitiveSet for FakePrimitiveSet {
528        type Error = Error;
529
530        fn operate(
531            &mut self,
532            _memory: &mut Memory<'_>,
533            _primitive: usize,
534        ) -> Result<(), Self::Error> {
535            Ok(())
536        }
537    }
538
539    type VoidVm = Vm<'static, FakePrimitiveSet>;
540
541    #[test]
542    fn arity() {
543        for arity in [
544            Arity {
545                count: 0,
546                variadic: false,
547            },
548            Arity {
549                count: 1,
550                variadic: false,
551            },
552            Arity {
553                count: 2,
554                variadic: false,
555            },
556            Arity {
557                count: 0,
558                variadic: true,
559            },
560            Arity {
561                count: 1,
562                variadic: true,
563            },
564            Arity {
565                count: 2,
566                variadic: true,
567            },
568        ] {
569            assert_eq!(VoidVm::parse_arity(VoidVm::build_arity(arity)), arity);
570        }
571    }
572}