stak_vm/
vm.rs

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