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