Skip to main content

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    heap::Heap,
8    instruction::Instruction,
9    memory::Memory,
10    number::Number,
11    primitive_set::PrimitiveSet,
12    r#type::Type,
13    value::{TypedValue, Value},
14};
15#[cfg(feature = "profile")]
16use core::cell::RefCell;
17use core::{
18    fmt::{self, Display, Formatter, Write},
19    marker::PhantomData,
20};
21use stak_lzss::{Lzss, MAX_WINDOW_SIZE};
22use stak_util::block_on;
23use winter_maybe_async::{maybe_async, maybe_await};
24
25macro_rules! trace {
26    ($prefix:literal, $data:expr) => {
27        #[cfg(feature = "trace_instruction")]
28        std::eprintln!("{}: {}", $prefix, $data);
29    };
30}
31
32macro_rules! trace_memory {
33    ($self:expr) => {
34        #[cfg(feature = "trace_memory")]
35        std::eprintln!("{}", $self);
36    };
37}
38
39macro_rules! profile_event {
40    ($self:expr, $name:literal) => {
41        #[cfg(feature = "profile")]
42        (&$self).profile_event($name)?;
43    };
44}
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47struct Arity {
48    // A count does not include a variadic argument.
49    count: usize,
50    variadic: bool,
51}
52
53/// A virtual machine.
54pub struct Vm<'a, T: PrimitiveSet<H>, H = &'a mut [Value]> {
55    primitive_set: T,
56    memory: Memory<H>,
57    #[cfg(feature = "profile")]
58    profiler: Option<RefCell<&'a mut dyn Profiler<H>>>,
59    _profiler: PhantomData<&'a ()>,
60}
61
62// Note that some routines look unnecessarily complicated as we need to mark all
63// volatile variables live across garbage collections.
64impl<'a, T: PrimitiveSet<H>, H: Heap> Vm<'a, T, H> {
65    /// Creates a virtual machine.
66    pub fn new(heap: H, primitive_set: T) -> Result<Self, Error> {
67        Ok(Self {
68            primitive_set,
69            memory: Memory::new(heap)?,
70            #[cfg(feature = "profile")]
71            profiler: None,
72            _profiler: Default::default(),
73        })
74    }
75
76    /// Sets a profiler.
77    #[cfg(feature = "profile")]
78    pub fn with_profiler(self, profiler: &'a mut dyn Profiler<H>) -> Self {
79        Self {
80            profiler: Some(profiler.into()),
81            ..self
82        }
83    }
84
85    /// Returns a reference to a primitive set.
86    pub const fn primitive_set(&self) -> &T {
87        &self.primitive_set
88    }
89
90    /// Returns a mutable reference to a primitive set.
91    pub const fn primitive_set_mut(&mut self) -> &mut T {
92        &mut self.primitive_set
93    }
94
95    /// Runs bytecode on a virtual machine synchronously.
96    ///
97    /// # Panics
98    ///
99    /// Panics if asynchronous operations occur during the run.
100    pub fn run(&mut self, input: impl IntoIterator<Item = u8>) -> Result<(), T::Error> {
101        block_on!(self.run_async(input))
102    }
103
104    /// Runs bytecode on a virtual machine.
105    #[cfg_attr(not(feature = "async"), doc(hidden))]
106    #[maybe_async]
107    pub fn run_async(&mut self, input: impl IntoIterator<Item = u8>) -> Result<(), T::Error> {
108        self.initialize(input)?;
109
110        while let Err(error) = maybe_await!(self.run_with_continuation()) {
111            if error.is_critical() {
112                return Err(error);
113            }
114
115            let Some(continuation) = self.memory.cdr(self.memory.null()?)?.to_cons() else {
116                return Err(error);
117            };
118
119            if self.memory.cdr(continuation)?.tag() != Type::Procedure as _ {
120                return Err(error);
121            }
122
123            self.memory.set_register(continuation);
124            let string = self.memory.build_string("")?;
125            let symbol = self.memory.allocate(
126                self.memory.register().into(),
127                string.set_tag(Type::Symbol as _).into(),
128            )?;
129            let code = self.memory.allocate(
130                symbol.into(),
131                self.memory
132                    .code()
133                    .set_tag(
134                        Instruction::Call as u16
135                            + Self::build_arity(Arity {
136                                count: 1,
137                                variadic: false,
138                            }) as u16,
139                    )
140                    .into(),
141            )?;
142            self.memory.set_code(code);
143
144            self.memory.set_register(self.memory.null()?);
145            write!(&mut self.memory, "{error}").map_err(Error::from)?;
146            let code = self.memory.allocate(
147                self.memory.register().into(),
148                self.memory
149                    .code()
150                    .set_tag(Instruction::Constant as _)
151                    .into(),
152            )?;
153            self.memory.set_code(code);
154        }
155
156        Ok(())
157    }
158
159    #[maybe_async]
160    fn run_with_continuation(&mut self) -> Result<(), T::Error> {
161        while self.memory.code() != self.memory.null()? {
162            let instruction = self.memory.cdr(self.memory.code())?.assume_cons();
163
164            trace!("instruction", instruction.tag());
165
166            match instruction.tag() {
167                Instruction::CONSTANT => self.constant()?,
168                Instruction::GET => self.get()?,
169                Instruction::SET => self.set()?,
170                Instruction::IF => self.r#if()?,
171                code => maybe_await!(
172                    self.call(instruction, code as usize - Instruction::CALL as usize)
173                )?,
174            }
175
176            self.advance_code()?;
177
178            trace_memory!(self);
179        }
180
181        Ok(())
182    }
183
184    fn constant(&mut self) -> Result<(), Error> {
185        let constant = self.operand()?;
186
187        trace!("constant", constant);
188
189        self.memory.push(constant)?;
190
191        Ok(())
192    }
193
194    fn get(&mut self) -> Result<(), Error> {
195        let operand = self.operand_cons()?;
196        let value = self.memory.car(operand)?;
197
198        trace!("operand", operand);
199        trace!("value", value);
200
201        self.memory.push(value)?;
202
203        Ok(())
204    }
205
206    fn set(&mut self) -> Result<(), Error> {
207        let operand = self.operand_cons()?;
208        let value = self.memory.pop()?;
209
210        trace!("operand", operand);
211        trace!("value", value);
212
213        self.memory.set_car(operand, value)?;
214
215        Ok(())
216    }
217
218    fn r#if(&mut self) -> Result<(), Error> {
219        let cons = self.memory.stack();
220
221        if self.memory.pop()? != self.memory.boolean(false)?.into() {
222            self.memory.set_cdr(cons, self.operand()?)?;
223            self.memory.set_code(cons);
224        }
225
226        Ok(())
227    }
228
229    #[maybe_async]
230    fn call(&mut self, instruction: Cons, arity: usize) -> Result<(), T::Error> {
231        let procedure = self.procedure()?;
232
233        trace!("procedure", procedure);
234
235        if self.environment(procedure)?.tag() != Type::Procedure as _ {
236            return Err(Error::ProcedureExpected.into());
237        }
238
239        let arguments = Self::parse_arity(arity);
240        let r#return = instruction == self.memory.null()?;
241
242        trace!("return", r#return);
243
244        match self.code(procedure)?.to_typed() {
245            TypedValue::Cons(code) => {
246                #[cfg(feature = "profile")]
247                self.profile_call(self.memory.code(), r#return)?;
248
249                let parameters =
250                    Self::parse_arity(self.memory.car(code)?.assume_number().to_i64() as usize);
251
252                trace!("argument count", arguments.count);
253                trace!("argument variadic", arguments.variadic);
254                trace!("parameter count", parameters.count);
255                trace!("parameter variadic", parameters.variadic);
256
257                self.memory.set_register(procedure);
258
259                let mut list = if arguments.variadic {
260                    self.memory.pop()?.assume_cons()
261                } else {
262                    self.memory.null()?
263                };
264
265                for _ in 0..arguments.count {
266                    let value = self.memory.pop()?;
267                    list = self.memory.cons(value, list)?;
268                }
269
270                // Use a `code` field as an escape cell for a procedure.
271                let code = self.memory.code();
272                self.memory.set_code(self.memory.register());
273                self.memory.set_register(list);
274
275                let continuation = if r#return {
276                    self.continuation()?
277                } else {
278                    self.memory
279                        .allocate(code.into(), self.memory.stack().into())?
280                };
281                let stack = self.memory.allocate(
282                    continuation.into(),
283                    self.environment(self.memory.code())?
284                        .set_tag(StackSlot::Frame as _)
285                        .into(),
286                )?;
287                self.memory.set_stack(stack);
288                self.memory
289                    .set_code(self.code(self.memory.code())?.assume_cons());
290
291                for _ in 0..parameters.count {
292                    if self.memory.register() == self.memory.null()? {
293                        return Err(Error::ArgumentCount.into());
294                    }
295
296                    self.memory.push(self.memory.car(self.memory.register())?)?;
297                    self.memory
298                        .set_register(self.memory.cdr(self.memory.register())?.assume_cons());
299                }
300
301                if parameters.variadic {
302                    self.memory.push(self.memory.register().into())?;
303                } else if self.memory.register() != self.memory.null()? {
304                    return Err(Error::ArgumentCount.into());
305                }
306            }
307            TypedValue::Number(primitive) => {
308                if arguments.variadic {
309                    let list = self.memory.pop()?.assume_cons();
310                    self.memory.set_register(list);
311
312                    while self.memory.register() != self.memory.null()? {
313                        self.memory.push(self.memory.car(self.memory.register())?)?;
314                        self.memory
315                            .set_register(self.memory.cdr(self.memory.register())?.assume_cons());
316                    }
317                }
318
319                maybe_await!(
320                    self.primitive_set
321                        .operate(&mut self.memory, primitive.to_i64() as _)
322                )?;
323            }
324        }
325
326        Ok(())
327    }
328
329    const fn parse_arity(info: usize) -> Arity {
330        Arity {
331            count: info / 2,
332            variadic: info % 2 == 1,
333        }
334    }
335
336    const fn build_arity(arity: Arity) -> usize {
337        2 * arity.count + arity.variadic as usize
338    }
339
340    fn advance_code(&mut self) -> Result<(), Error> {
341        let mut code = self.memory.cdr(self.memory.code())?.assume_cons();
342
343        if code == self.memory.null()? {
344            #[cfg(feature = "profile")]
345            self.profile_return()?;
346
347            let continuation = self.continuation()?;
348            // Keep a value at the top of a stack.
349            self.memory
350                .set_cdr(self.memory.stack(), self.memory.cdr(continuation)?)?;
351
352            code = self
353                .memory
354                .cdr(self.memory.car(continuation)?.assume_cons())?
355                .assume_cons();
356        }
357
358        self.memory.set_code(code);
359
360        Ok(())
361    }
362
363    fn operand(&self) -> Result<Value, Error> {
364        self.memory.car(self.memory.code())
365    }
366
367    fn operand_cons(&self) -> Result<Cons, Error> {
368        Ok(match self.operand()?.to_typed() {
369            TypedValue::Cons(cons) => cons,
370            TypedValue::Number(index) => {
371                self.memory.tail(self.memory.stack(), index.to_i64() as _)?
372            }
373        })
374    }
375
376    // (code . environment)
377    fn procedure(&self) -> Result<Cons, Error> {
378        Ok(self.memory.car(self.operand_cons()?)?.assume_cons())
379    }
380
381    // (parameter-count . instruction-list) | primitive-id
382    fn code(&self, procedure: Cons) -> Result<Value, Error> {
383        self.memory.car(procedure)
384    }
385
386    fn environment(&self, procedure: Cons) -> Result<Cons, Error> {
387        Ok(self.memory.cdr(procedure)?.assume_cons())
388    }
389
390    // (code . stack)
391    fn continuation(&self) -> Result<Cons, Error> {
392        let mut stack = self.memory.stack();
393
394        while self.memory.cdr(stack)?.assume_cons().tag() != StackSlot::Frame as _ {
395            stack = self.memory.cdr(stack)?.assume_cons();
396        }
397
398        Ok(self.memory.car(stack)?.assume_cons())
399    }
400
401    // Profiling
402
403    #[cfg(feature = "profile")]
404    fn profile_call(&self, call_code: Cons, r#return: bool) -> Result<(), Error> {
405        if let Some(profiler) = &self.profiler {
406            profiler
407                .borrow_mut()
408                .profile_call(&self.memory, call_code, r#return)?;
409        }
410
411        Ok(())
412    }
413
414    #[cfg(feature = "profile")]
415    fn profile_return(&self) -> Result<(), Error> {
416        if let Some(profiler) = &self.profiler {
417            profiler.borrow_mut().profile_return(&self.memory)?;
418        }
419
420        Ok(())
421    }
422
423    #[cfg(feature = "profile")]
424    fn profile_event(&self, name: &str) -> Result<(), Error> {
425        if let Some(profiler) = &self.profiler {
426            profiler.borrow_mut().profile_event(name)?;
427        }
428
429        Ok(())
430    }
431
432    // This function is public only for benchmarking.
433    #[doc(hidden)]
434    pub fn initialize(&mut self, input: impl IntoIterator<Item = u8>) -> Result<(), super::Error> {
435        profile_event!(self, "initialization_start");
436        profile_event!(self, "decode_start");
437
438        let program = self.decode_ribs(input.into_iter())?;
439        self.memory
440            .set_false(self.memory.car(program)?.assume_cons())?;
441        self.memory
442            .set_code(self.memory.cdr(program)?.assume_cons());
443
444        profile_event!(self, "decode_end");
445
446        // Initialize an implicit top-level frame.
447        let codes = self
448            .memory
449            .cons(Number::default().into(), self.memory.null()?)?
450            .into();
451        let continuation = self.memory.cons(codes, self.memory.null()?)?.into();
452        let stack = self.memory.allocate(
453            continuation,
454            self.memory.null()?.set_tag(StackSlot::Frame as _).into(),
455        )?;
456        self.memory.set_stack(stack);
457        self.memory.set_register(NEVER);
458
459        profile_event!(self, "initialization_end");
460
461        Ok(())
462    }
463
464    fn decode_ribs(&mut self, input: impl Iterator<Item = u8>) -> Result<Cons, Error> {
465        let mut input = input.decompress::<{ MAX_WINDOW_SIZE }>();
466
467        while let Some(head) = input.next() {
468            if head & 0b1 == 0 {
469                let head = head >> 1;
470
471                if head == 0 {
472                    let cons = self.memory.cons(self.memory.top()?, self.memory.code())?;
473                    self.memory.set_code(cons);
474                } else {
475                    let integer = Self::decode_integer_tail(&mut input, head - 1, SHARE_BASE)?;
476                    let index = integer >> 1;
477
478                    if index > 0 {
479                        let cons = self.memory.tail(self.memory.code(), index as usize - 1)?;
480                        let head = self.memory.cdr(cons)?.assume_cons();
481                        self.memory.set_cdr(cons, self.memory.cdr(head)?)?;
482                        self.memory.set_cdr(head, self.memory.code().into())?;
483                        self.memory.set_code(head);
484                    }
485
486                    let value = self.memory.car(self.memory.code())?;
487
488                    if integer & 1 == 0 {
489                        self.memory
490                            .set_code(self.memory.cdr(self.memory.code())?.assume_cons());
491                    }
492
493                    self.memory.push(value)?;
494                }
495            } else if head & 0b10 == 0 {
496                let head = head >> 2;
497                let cdr = self.memory.pop()?;
498                let car = self.memory.pop()?;
499
500                if head == 0 {
501                    let cons = self.memory.top()?.assume_cons();
502                    self.memory.set_car(cons, car)?;
503                    self.memory.set_cdr(cons, cdr)?;
504                } else {
505                    let cons =
506                        self.memory.allocate(
507                            car,
508                            cdr.set_tag(
509                                Self::decode_integer_tail(&mut input, head - 1, TAG_BASE)? as _
510                            ),
511                        )?;
512                    self.memory.push(cons.into())?;
513                }
514            } else {
515                self.memory
516                    .push(Self::decode_number(&mut input, head)?.into())?;
517            }
518        }
519
520        self.memory.pop()?.to_cons().ok_or(Error::BytecodeEnd)
521    }
522
523    fn decode_number(input: &mut impl Iterator<Item = u8>, head: u8) -> Result<Number, Error> {
524        let integer = Self::decode_integer_tail(input, head >> 2, NUMBER_BASE)?;
525
526        Ok(if integer & 1 == 0 {
527            Number::from_i64((integer >> 1) as _)
528        } else if integer & 0b10 == 0 {
529            Number::from_i64(-((integer >> 2) as i64))
530        } else {
531            let integer = integer >> 2;
532            let head = input.next().ok_or(Error::BytecodeEnd)?;
533            let mantissa = Self::decode_integer_tail(input, head, INTEGER_BASE)?;
534
535            Number::from_f64(
536                if integer.is_multiple_of(2) { 1.0 } else { -1.0 }
537                    * mantissa as f64
538                    * f64::from_bits(((integer as u64 >> 1) % (1 << 11)) << 52),
539            )
540        })
541    }
542
543    fn decode_integer_tail(
544        input: &mut impl Iterator<Item = u8>,
545        mut x: u8,
546        mut base: u128,
547    ) -> Result<u128, Error> {
548        let mut y = (x >> 1) as u128;
549
550        while x & 1 != 0 {
551            x = input.next().ok_or(Error::BytecodeEnd)?;
552            y += (x as u128 >> 1) * base;
553            base *= INTEGER_BASE;
554        }
555
556        Ok(y)
557    }
558}
559
560impl<T: PrimitiveSet<H>, H: Heap> Display for Vm<'_, T, H> {
561    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
562        write!(formatter, "{}", &self.memory)
563    }
564}
565
566#[cfg(test)]
567mod tests {
568    use super::*;
569    #[cfg(feature = "float")]
570    use alloc::{vec, vec::Vec};
571
572    struct FakePrimitiveSet {}
573
574    impl<H: Heap> PrimitiveSet<H> for FakePrimitiveSet {
575        type Error = Error;
576
577        #[maybe_async]
578        fn operate(
579            &mut self,
580            _memory: &mut Memory<H>,
581            _primitive: usize,
582        ) -> Result<(), Self::Error> {
583            Ok(())
584        }
585    }
586
587    type VoidVm = Vm<'static, FakePrimitiveSet>;
588
589    #[test]
590    fn arity() {
591        for arity in [
592            Arity {
593                count: 0,
594                variadic: false,
595            },
596            Arity {
597                count: 1,
598                variadic: false,
599            },
600            Arity {
601                count: 2,
602                variadic: false,
603            },
604            Arity {
605                count: 0,
606                variadic: true,
607            },
608            Arity {
609                count: 1,
610                variadic: true,
611            },
612            Arity {
613                count: 2,
614                variadic: true,
615            },
616        ] {
617            assert_eq!(VoidVm::parse_arity(VoidVm::build_arity(arity)), arity);
618        }
619    }
620
621    #[cfg(feature = "float")]
622    #[test]
623    fn decode_float() {
624        fn encode_number(value: f64) -> Vec<u8> {
625            fn encode_integer(bytes: &mut Vec<u8>, mut integer: u128, base: u128) {
626                let digit = (integer % base) as u8;
627                integer /= base;
628                bytes.push(2 * digit + u8::from(integer != 0));
629
630                while integer != 0 {
631                    let digit = (integer % INTEGER_BASE) as u8;
632                    integer /= INTEGER_BASE;
633                    bytes.push(2 * digit + u8::from(integer != 0));
634                }
635            }
636
637            let mut mantissa = value.abs();
638            let mut exponent = 0i64;
639
640            while mantissa != mantissa.floor() {
641                mantissa *= 2.0;
642                exponent -= 1;
643            }
644
645            let mut bytes = vec![];
646
647            encode_integer(
648                &mut bytes,
649                3 + 4 * (u128::from(value < 0.0) + 2 * (exponent + 1023) as u128),
650                NUMBER_BASE,
651            );
652            encode_integer(&mut bytes, mantissa as u128, INTEGER_BASE);
653            bytes[0] = 3 + 4 * bytes[0];
654
655            bytes
656        }
657
658        for value in [
659            0.5,
660            -0.5,
661            0.75,
662            -0.75,
663            0.125,
664            1.5,
665            -2.25,
666            12.5,
667            -40.5,
668            // Small magnitudes with large negative exponents.
669            0.001953125,
670            -0.0009765625,
671            // Mantissas beyond the former 38-bit threshold.
672            137438953471.5,
673            -137438953471.5,
674            2251799813685247.5,
675            // Full 53-bit mantissas.
676            4503599627370495.5,
677            -4503599627370495.5,
678            // The minimum normal magnitude.
679            f64::MIN_POSITIVE,
680        ] {
681            let mut input = encode_number(value).into_iter();
682            let head = input.next().unwrap();
683
684            assert_eq!(
685                VoidVm::decode_number(&mut input, head).unwrap(),
686                Number::from_f64(value),
687            );
688        }
689    }
690
691    fn literals<const N: usize>(bytes: [u8; N]) -> [u8; N] {
692        bytes.map(|byte| byte * 2)
693    }
694
695    fn decode<const N: usize>(bytes: [u8; N]) -> (Cons, Memory<[Value; 1 << 9]>) {
696        let mut vm = Vm::new([Default::default(); 1 << 9], FakePrimitiveSet {}).unwrap();
697        let cons = vm.decode_ribs(literals(bytes).into_iter()).unwrap();
698
699        (cons, vm.memory)
700    }
701
702    #[test]
703    fn decode_shared_value() {
704        // A pair whose `car` and `cdr` are the same memoized rib.
705        let (rib, memory) = decode([
706            // Build a placeholder pair of two zeros.
707            3, 3, 5, // Memoize it into a dictionary.
708            0, // Reference it twice, keeping it the first time.
709            6, 2, // Build a pair of the two references.
710            5,
711        ]);
712
713        let car = memory.car(rib).unwrap().assume_cons();
714
715        assert_eq!(memory.cdr(rib).unwrap(), car.into());
716        assert_eq!(memory.car(car).unwrap(), Number::from_i64(0).into());
717        assert_eq!(memory.cdr(car).unwrap(), Number::from_i64(0).into());
718    }
719
720    #[test]
721    fn decode_self_loop() {
722        // A pair whose `cdr` points back to itself with a `car` of zero.
723        let (rib, memory) = decode([
724            // Build a placeholder pair of two zeros.
725            3, 3, 5, // Memoize it into a dictionary.
726            0, // A zero for its `car` and a reference to itself for its `cdr`.
727            3, 2, // Fill the placeholder, keeping its pair tag.
728            1,
729        ]);
730
731        assert_eq!(memory.car(rib).unwrap(), Number::from_i64(0).into());
732        assert_eq!(memory.cdr(rib).unwrap(), rib.into());
733    }
734
735    #[test]
736    fn decode_swapped_reference() {
737        // Two mutually referencing pairs. Filling each pair references the other
738        // one while it sits behind in the dictionary, exercising the
739        // move-to-front of a non-front entry.
740        let (first, memory) = decode([
741            // Build and memoize the first placeholder.
742            3, 3, 5, 0, // Build and memoize the second placeholder.
743            3, 3, 5, 0, // Fill the second pair with a zero and the first one.
744            3, 14, 1, // Bring the first pair to the front, then fill it with a
745            // zero and the second one.
746            6, 3, 10, 1,
747        ]);
748
749        let second = memory.cdr(first).unwrap().assume_cons();
750
751        assert_eq!(memory.car(first).unwrap(), Number::from_i64(0).into());
752        assert_eq!(memory.car(second).unwrap(), Number::from_i64(0).into());
753        assert_eq!(memory.cdr(second).unwrap(), first.into());
754    }
755}