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