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::{Heap, 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::{
17    fmt::{self, Display, Formatter, Write},
18    marker::PhantomData,
19};
20use stak_lzss::Lzss;
21use stak_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) -> Result<(), T::Error> {
101        block_on!(self.run_async())
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) -> Result<(), T::Error> {
108        while let Err(error) = maybe_await!(self.run_with_continuation()) {
109            if error.is_critical() {
110                return Err(error);
111            }
112
113            let Some(continuation) = self.memory.cdr(self.memory.null()?)?.to_cons() else {
114                return Err(error);
115            };
116
117            if self.memory.cdr(continuation)?.tag() != Type::Procedure as _ {
118                return Err(error);
119            }
120
121            self.memory.set_register(continuation);
122            let string = self.memory.build_string("")?;
123            let symbol = self.memory.allocate(
124                self.memory.register().into(),
125                string.set_tag(Type::Symbol as _).into(),
126            )?;
127            let code = self.memory.allocate(
128                symbol.into(),
129                self.memory
130                    .code()
131                    .set_tag(
132                        Instruction::Call as u16
133                            + Self::build_arity(Arity {
134                                count: 1,
135                                variadic: false,
136                            }) as u16,
137                    )
138                    .into(),
139            )?;
140            self.memory.set_code(code);
141
142            self.memory.set_register(self.memory.null()?);
143            write!(&mut self.memory, "{error}").map_err(Error::from)?;
144            let code = self.memory.allocate(
145                self.memory.register().into(),
146                self.memory
147                    .code()
148                    .set_tag(Instruction::Constant as _)
149                    .into(),
150            )?;
151            self.memory.set_code(code);
152        }
153
154        Ok(())
155    }
156
157    #[maybe_async]
158    fn run_with_continuation(&mut self) -> Result<(), T::Error> {
159        while self.memory.code() != self.memory.null()? {
160            let instruction = self.memory.cdr(self.memory.code())?.assume_cons();
161
162            trace!("instruction", instruction.tag());
163
164            match instruction.tag() {
165                Instruction::CONSTANT => self.constant()?,
166                Instruction::GET => self.get()?,
167                Instruction::SET => self.set()?,
168                Instruction::IF => self.r#if()?,
169                code => maybe_await!(
170                    self.call(instruction, code as usize - Instruction::CALL as usize)
171                )?,
172            }
173
174            self.advance_code()?;
175
176            trace_memory!(self);
177        }
178
179        Ok(())
180    }
181
182    fn constant(&mut self) -> Result<(), Error> {
183        let constant = self.operand()?;
184
185        trace!("constant", constant);
186
187        self.memory.push(constant)?;
188
189        Ok(())
190    }
191
192    fn get(&mut self) -> Result<(), Error> {
193        let operand = self.operand_cons()?;
194        let value = self.memory.car(operand)?;
195
196        trace!("operand", operand);
197        trace!("value", value);
198
199        self.memory.push(value)?;
200
201        Ok(())
202    }
203
204    fn set(&mut self) -> Result<(), Error> {
205        let operand = self.operand_cons()?;
206        let value = self.memory.pop()?;
207
208        trace!("operand", operand);
209        trace!("value", value);
210
211        self.memory.set_car(operand, value)?;
212
213        Ok(())
214    }
215
216    fn r#if(&mut self) -> Result<(), Error> {
217        let cons = self.memory.stack();
218
219        if self.memory.pop()? != self.memory.boolean(false)?.into() {
220            self.memory.set_cdr(cons, self.operand()?)?;
221            self.memory.set_code(cons);
222        }
223
224        Ok(())
225    }
226
227    #[maybe_async]
228    fn call(&mut self, instruction: Cons, arity: usize) -> Result<(), T::Error> {
229        let procedure = self.procedure()?;
230
231        trace!("procedure", procedure);
232
233        if self.environment(procedure)?.tag() != Type::Procedure as _ {
234            return Err(Error::ProcedureExpected.into());
235        }
236
237        let arguments = Self::parse_arity(arity);
238        let r#return = instruction == self.memory.null()?;
239
240        trace!("return", r#return);
241
242        match self.code(procedure)?.to_typed() {
243            TypedValue::Cons(code) => {
244                #[cfg(feature = "profile")]
245                self.profile_call(self.memory.code(), r#return)?;
246
247                let parameters =
248                    Self::parse_arity(self.memory.car(code)?.assume_number().to_i64() as usize);
249
250                trace!("argument count", arguments.count);
251                trace!("argument variadic", arguments.variadic);
252                trace!("parameter count", parameters.count);
253                trace!("parameter variadic", parameters.variadic);
254
255                self.memory.set_register(procedure);
256
257                let mut list = if arguments.variadic {
258                    self.memory.pop()?.assume_cons()
259                } else {
260                    self.memory.null()?
261                };
262
263                for _ in 0..arguments.count {
264                    let value = self.memory.pop()?;
265                    list = self.memory.cons(value, list)?;
266                }
267
268                // Use a `code` field as an escape cell for a procedure.
269                let code = self.memory.code();
270                self.memory.set_code(self.memory.register());
271                self.memory.set_register(list);
272
273                let continuation = if r#return {
274                    self.continuation()?
275                } else {
276                    self.memory
277                        .allocate(code.into(), self.memory.stack().into())?
278                };
279                let stack = self.memory.allocate(
280                    continuation.into(),
281                    self.environment(self.memory.code())?
282                        .set_tag(StackSlot::Frame as _)
283                        .into(),
284                )?;
285                self.memory.set_stack(stack);
286                self.memory
287                    .set_code(self.code(self.memory.code())?.assume_cons());
288
289                for _ in 0..parameters.count {
290                    if self.memory.register() == self.memory.null()? {
291                        return Err(Error::ArgumentCount.into());
292                    }
293
294                    self.memory.push(self.memory.car(self.memory.register())?)?;
295                    self.memory
296                        .set_register(self.memory.cdr(self.memory.register())?.assume_cons());
297                }
298
299                if parameters.variadic {
300                    self.memory.push(self.memory.register().into())?;
301                } else if self.memory.register() != self.memory.null()? {
302                    return Err(Error::ArgumentCount.into());
303                }
304            }
305            TypedValue::Number(primitive) => {
306                if arguments.variadic {
307                    let list = self.memory.pop()?.assume_cons();
308                    self.memory.set_register(list);
309
310                    while self.memory.register() != self.memory.null()? {
311                        self.memory.push(self.memory.car(self.memory.register())?)?;
312                        self.memory
313                            .set_register(self.memory.cdr(self.memory.register())?.assume_cons());
314                    }
315                }
316
317                maybe_await!(
318                    self.primitive_set
319                        .operate(&mut self.memory, primitive.to_i64() as _)
320                )?;
321            }
322        }
323
324        Ok(())
325    }
326
327    const fn parse_arity(info: usize) -> Arity {
328        Arity {
329            count: info / 2,
330            variadic: info % 2 == 1,
331        }
332    }
333
334    const fn build_arity(arity: Arity) -> usize {
335        2 * arity.count + arity.variadic as usize
336    }
337
338    fn advance_code(&mut self) -> Result<(), Error> {
339        let mut code = self.memory.cdr(self.memory.code())?.assume_cons();
340
341        if code == self.memory.null()? {
342            #[cfg(feature = "profile")]
343            self.profile_return()?;
344
345            let continuation = self.continuation()?;
346            // Keep a value at the top of a stack.
347            self.memory
348                .set_cdr(self.memory.stack(), self.memory.cdr(continuation)?)?;
349
350            code = self
351                .memory
352                .cdr(self.memory.car(continuation)?.assume_cons())?
353                .assume_cons();
354        }
355
356        self.memory.set_code(code);
357
358        Ok(())
359    }
360
361    fn operand(&self) -> Result<Value, Error> {
362        self.memory.car(self.memory.code())
363    }
364
365    fn operand_cons(&self) -> Result<Cons, Error> {
366        Ok(match self.operand()?.to_typed() {
367            TypedValue::Cons(cons) => cons,
368            TypedValue::Number(index) => {
369                self.memory.tail(self.memory.stack(), index.to_i64() as _)?
370            }
371        })
372    }
373
374    // (code . environment)
375    fn procedure(&self) -> Result<Cons, Error> {
376        Ok(self.memory.car(self.operand_cons()?)?.assume_cons())
377    }
378
379    // (parameter-count . instruction-list) | primitive-id
380    fn code(&self, procedure: Cons) -> Result<Value, Error> {
381        self.memory.car(procedure)
382    }
383
384    fn environment(&self, procedure: Cons) -> Result<Cons, Error> {
385        Ok(self.memory.cdr(procedure)?.assume_cons())
386    }
387
388    // (code . stack)
389    fn continuation(&self) -> Result<Cons, Error> {
390        let mut stack = self.memory.stack();
391
392        while self.memory.cdr(stack)?.assume_cons().tag() != StackSlot::Frame as _ {
393            stack = self.memory.cdr(stack)?.assume_cons();
394        }
395
396        Ok(self.memory.car(stack)?.assume_cons())
397    }
398
399    // Profiling
400
401    #[cfg(feature = "profile")]
402    fn profile_call(&self, call_code: Cons, r#return: bool) -> Result<(), Error> {
403        if let Some(profiler) = &self.profiler {
404            profiler
405                .borrow_mut()
406                .profile_call(&self.memory, call_code, r#return)?;
407        }
408
409        Ok(())
410    }
411
412    #[cfg(feature = "profile")]
413    fn profile_return(&self) -> Result<(), Error> {
414        if let Some(profiler) = &self.profiler {
415            profiler.borrow_mut().profile_return(&self.memory)?;
416        }
417
418        Ok(())
419    }
420
421    #[cfg(feature = "profile")]
422    fn profile_event(&self, name: &str) -> Result<(), Error> {
423        if let Some(profiler) = &self.profiler {
424            profiler.borrow_mut().profile_event(name)?;
425        }
426
427        Ok(())
428    }
429
430    /// Initializes a virtual machine with bytecode of a program.
431    pub fn initialize(&mut self, input: impl IntoIterator<Item = u8>) -> Result<(), super::Error> {
432        profile_event!(self, "initialization_start");
433        profile_event!(self, "decode_start");
434
435        let program = self.decode_ribs(input.into_iter())?;
436        self.memory
437            .set_false(self.memory.car(program)?.assume_cons());
438        self.memory
439            .set_code(self.memory.cdr(program)?.assume_cons());
440
441        profile_event!(self, "decode_end");
442
443        // Initialize an implicit top-level frame.
444        let codes = self
445            .memory
446            .cons(Number::default().into(), self.memory.null()?)?
447            .into();
448        let continuation = self.memory.cons(codes, self.memory.null()?)?.into();
449        let stack = self.memory.allocate(
450            continuation,
451            self.memory.null()?.set_tag(StackSlot::Frame as _).into(),
452        )?;
453        self.memory.set_stack(stack);
454        self.memory.set_register(NEVER);
455
456        profile_event!(self, "initialization_end");
457
458        Ok(())
459    }
460
461    fn decode_ribs(&mut self, input: impl Iterator<Item = u8>) -> Result<Cons, Error> {
462        let mut input = input.decompress::<{ MAX_WINDOW_SIZE }>();
463
464        while let Some(head) = input.next() {
465            if head & 0b1 == 0 {
466                let head = head >> 1;
467
468                if head == 0 {
469                    let value = self.memory.top()?;
470                    let cons = self.memory.cons(value, self.memory.code())?;
471                    self.memory.set_code(cons);
472                } else {
473                    let integer = Self::decode_integer_tail(&mut input, head - 1, SHARE_BASE)?;
474                    let index = integer >> 1;
475
476                    if index > 0 {
477                        let cons = self.memory.tail(self.memory.code(), index as usize - 1)?;
478                        let head = self.memory.cdr(cons)?.assume_cons();
479                        let tail = self.memory.cdr(head)?;
480                        self.memory.set_cdr(head, self.memory.code().into())?;
481                        self.memory.set_cdr(cons, tail)?;
482                        self.memory.set_code(head);
483                    }
484
485                    let value = self.memory.car(self.memory.code())?;
486
487                    if integer & 1 == 0 {
488                        self.memory
489                            .set_code(self.memory.cdr(self.memory.code())?.assume_cons());
490                    }
491
492                    self.memory.push(value)?;
493                }
494            } else if head & 0b10 == 0 {
495                let cons = self.memory.stack();
496                let cdr = self.memory.pop()?;
497                let car = self.memory.top()?;
498                let tag = Self::decode_integer_tail(&mut input, head >> 2, TAG_BASE)?;
499                self.memory.set_car(cons, car)?;
500                self.memory.set_raw_cdr(cons, cdr.set_tag(tag as _))?;
501                self.memory.set_top(cons.into())?;
502            } else {
503                self.memory.push(
504                    Self::decode_number(Self::decode_integer_tail(
505                        &mut input,
506                        head >> 2,
507                        NUMBER_BASE,
508                    )?)
509                    .into(),
510                )?;
511            }
512        }
513
514        self.memory.pop()?.to_cons().ok_or(Error::BytecodeEnd)
515    }
516
517    fn decode_number(integer: u128) -> Number {
518        if integer & 1 == 0 {
519            Number::from_i64((integer >> 1) as _)
520        } else if integer & 0b10 == 0 {
521            Number::from_i64(-((integer >> 2) as i64))
522        } else {
523            let integer = integer >> 2;
524            let mantissa =
525                if integer.is_multiple_of(2) { 1.0 } else { -1.0 } * (integer >> 12) as f64;
526            let exponent = ((integer >> 1) % (1 << 11)) as isize - 1023;
527
528            Number::from_f64(if exponent < 0 {
529                mantissa / (1u64 << exponent.abs()) as f64
530            } else {
531                mantissa * (1u64 << exponent) as f64
532            })
533        }
534    }
535
536    fn decode_integer_tail(
537        input: &mut impl Iterator<Item = u8>,
538        mut x: u8,
539        mut base: u128,
540    ) -> Result<u128, Error> {
541        let mut y = (x >> 1) as u128;
542
543        while x & 1 != 0 {
544            x = input.next().ok_or(Error::BytecodeEnd)?;
545            y += (x as u128 >> 1) * base;
546            base *= INTEGER_BASE;
547        }
548
549        Ok(y)
550    }
551}
552
553impl<T: PrimitiveSet<H>, H: Heap> Display for Vm<'_, T, H> {
554    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
555        write!(formatter, "{}", &self.memory)
556    }
557}
558
559#[cfg(test)]
560mod tests {
561    use super::*;
562
563    struct FakePrimitiveSet {}
564
565    impl<H: Heap> PrimitiveSet<H> for FakePrimitiveSet {
566        type Error = Error;
567
568        #[maybe_async]
569        fn operate(
570            &mut self,
571            _memory: &mut Memory<H>,
572            _primitive: usize,
573        ) -> Result<(), Self::Error> {
574            Ok(())
575        }
576    }
577
578    type VoidVm = Vm<'static, FakePrimitiveSet>;
579
580    #[test]
581    fn arity() {
582        for arity in [
583            Arity {
584                count: 0,
585                variadic: false,
586            },
587            Arity {
588                count: 1,
589                variadic: false,
590            },
591            Arity {
592                count: 2,
593                variadic: false,
594            },
595            Arity {
596                count: 0,
597                variadic: true,
598            },
599            Arity {
600                count: 1,
601                variadic: true,
602            },
603            Arity {
604                count: 2,
605                variadic: true,
606            },
607        ] {
608            assert_eq!(VoidVm::parse_arity(VoidVm::build_arity(arity)), arity);
609        }
610    }
611}