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(&mut self) -> Result<(), T::Error> {
94 block_on!(self.run_async())
95 }
96
97 #[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 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 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 const fn procedure(&self) -> Cons {
367 self.memory.car(self.operand_cons()).assume_cons()
368 }
369
370 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 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 #[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 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 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}