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