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_lzss::{Lzss, MAX_WINDOW_SIZE};
21use stak_util::block_on;
22use winter_maybe_async::{maybe_async, maybe_await};
23
24macro_rules! trace {
25 ($prefix:literal, $data:expr) => {
26 #[cfg(feature = "trace_instruction")]
27 std::eprintln!("{}: {}", $prefix, $data);
28 };
29}
30
31macro_rules! trace_memory {
32 ($self:expr) => {
33 #[cfg(feature = "trace_memory")]
34 std::eprintln!("{}", $self);
35 };
36}
37
38macro_rules! profile_event {
39 ($self:expr, $name:literal) => {
40 #[cfg(feature = "profile")]
41 (&$self).profile_event($name)?;
42 };
43}
44
45#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46struct Arity {
47 count: usize,
49 variadic: bool,
50}
51
52pub struct Vm<'a, T: PrimitiveSet<H>, H = &'a mut [Value]> {
54 primitive_set: T,
55 memory: Memory<H>,
56 #[cfg(feature = "profile")]
57 profiler: Option<RefCell<&'a mut dyn Profiler<H>>>,
58 _profiler: PhantomData<&'a ()>,
59}
60
61impl<'a, T: PrimitiveSet<H>, H: Heap> Vm<'a, T, H> {
64 pub fn new(heap: H, primitive_set: T) -> Result<Self, Error> {
66 Ok(Self {
67 primitive_set,
68 memory: Memory::new(heap)?,
69 #[cfg(feature = "profile")]
70 profiler: None,
71 _profiler: Default::default(),
72 })
73 }
74
75 #[cfg(feature = "profile")]
77 pub fn with_profiler(self, profiler: &'a mut dyn Profiler<H>) -> Self {
78 Self {
79 profiler: Some(profiler.into()),
80 ..self
81 }
82 }
83
84 pub const fn primitive_set(&self) -> &T {
86 &self.primitive_set
87 }
88
89 pub const fn primitive_set_mut(&mut self) -> &mut T {
91 &mut self.primitive_set
92 }
93
94 pub fn run(&mut self, input: impl IntoIterator<Item = u8>) -> Result<(), T::Error> {
100 block_on!(self.run_async(input))
101 }
102
103 #[cfg_attr(not(feature = "async"), doc(hidden))]
105 #[maybe_async]
106 pub fn run_async(&mut self, input: impl IntoIterator<Item = u8>) -> Result<(), T::Error> {
107 self.initialize(input)?;
108
109 while let Err(error) = maybe_await!(self.run_with_continuation()) {
110 if error.is_critical() {
111 return Err(error);
112 }
113
114 let Some(continuation) = self.memory.cdr(self.memory.null()?)?.to_cons() else {
115 return Err(error);
116 };
117
118 if self.memory.cdr(continuation)?.tag() != Type::Procedure as _ {
119 return Err(error);
120 }
121
122 self.memory.set_register(continuation);
123 let string = self.memory.build_string("")?;
124 let symbol = self.memory.allocate(
125 self.memory.register().into(),
126 string.set_tag(Type::Symbol as _).into(),
127 )?;
128 let code = self.memory.allocate(
129 symbol.into(),
130 self.memory
131 .code()
132 .set_tag(
133 Instruction::Call as u16
134 + Self::build_arity(Arity {
135 count: 1,
136 variadic: false,
137 }) as u16,
138 )
139 .into(),
140 )?;
141 self.memory.set_code(code);
142
143 self.memory.set_register(self.memory.null()?);
144 write!(&mut self.memory, "{error}").map_err(Error::from)?;
145 let code = self.memory.allocate(
146 self.memory.register().into(),
147 self.memory
148 .code()
149 .set_tag(Instruction::Constant as _)
150 .into(),
151 )?;
152 self.memory.set_code(code);
153 }
154
155 Ok(())
156 }
157
158 #[maybe_async]
159 fn run_with_continuation(&mut self) -> Result<(), T::Error> {
160 while self.memory.code() != self.memory.null()? {
161 let instruction = self.memory.cdr(self.memory.code())?.assume_cons();
162
163 trace!("instruction", instruction.tag());
164
165 match instruction.tag() {
166 Instruction::CONSTANT => self.constant()?,
167 Instruction::GET => self.get()?,
168 Instruction::SET => self.set()?,
169 Instruction::IF => self.r#if()?,
170 code => maybe_await!(
171 self.call(instruction, code as usize - Instruction::CALL as usize)
172 )?,
173 }
174
175 self.advance_code()?;
176
177 trace_memory!(self);
178 }
179
180 Ok(())
181 }
182
183 fn constant(&mut self) -> Result<(), Error> {
184 let constant = self.operand()?;
185
186 trace!("constant", constant);
187
188 self.memory.push(constant)?;
189
190 Ok(())
191 }
192
193 fn get(&mut self) -> Result<(), Error> {
194 let operand = self.operand_cons()?;
195 let value = self.memory.car(operand)?;
196
197 trace!("operand", operand);
198 trace!("value", value);
199
200 self.memory.push(value)?;
201
202 Ok(())
203 }
204
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 fn r#if(&mut self) -> Result<(), Error> {
218 let cons = self.memory.stack();
219
220 if self.memory.pop()? != self.memory.boolean(false)?.into() {
221 self.memory.set_cdr(cons, self.operand()?)?;
222 self.memory.set_code(cons);
223 }
224
225 Ok(())
226 }
227
228 #[maybe_async]
229 fn call(&mut self, instruction: Cons, arity: usize) -> Result<(), T::Error> {
230 let procedure = self.procedure()?;
231
232 trace!("procedure", procedure);
233
234 if self.environment(procedure)?.tag() != Type::Procedure as _ {
235 return Err(Error::ProcedureExpected.into());
236 }
237
238 let arguments = Self::parse_arity(arity);
239 let r#return = instruction == self.memory.null()?;
240
241 trace!("return", r#return);
242
243 match self.code(procedure)?.to_typed() {
244 TypedValue::Cons(code) => {
245 #[cfg(feature = "profile")]
246 self.profile_call(self.memory.code(), r#return)?;
247
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 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 arguments.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 const fn parse_arity(info: usize) -> Arity {
329 Arity {
330 count: info / 2,
331 variadic: info % 2 == 1,
332 }
333 }
334
335 const fn build_arity(arity: Arity) -> usize {
336 2 * arity.count + arity.variadic as usize
337 }
338
339 fn advance_code(&mut self) -> Result<(), Error> {
340 let mut code = self.memory.cdr(self.memory.code())?.assume_cons();
341
342 if code == self.memory.null()? {
343 #[cfg(feature = "profile")]
344 self.profile_return()?;
345
346 let continuation = self.continuation()?;
347 self.memory
349 .set_cdr(self.memory.stack(), self.memory.cdr(continuation)?)?;
350
351 code = self
352 .memory
353 .cdr(self.memory.car(continuation)?.assume_cons())?
354 .assume_cons();
355 }
356
357 self.memory.set_code(code);
358
359 Ok(())
360 }
361
362 fn operand(&self) -> Result<Value, Error> {
363 self.memory.car(self.memory.code())
364 }
365
366 fn operand_cons(&self) -> Result<Cons, Error> {
367 Ok(match self.operand()?.to_typed() {
368 TypedValue::Cons(cons) => cons,
369 TypedValue::Number(index) => {
370 self.memory.tail(self.memory.stack(), index.to_i64() as _)?
371 }
372 })
373 }
374
375 fn procedure(&self) -> Result<Cons, Error> {
377 Ok(self.memory.car(self.operand_cons()?)?.assume_cons())
378 }
379
380 fn code(&self, procedure: Cons) -> Result<Value, Error> {
382 self.memory.car(procedure)
383 }
384
385 fn environment(&self, procedure: Cons) -> Result<Cons, Error> {
386 Ok(self.memory.cdr(procedure)?.assume_cons())
387 }
388
389 fn continuation(&self) -> Result<Cons, Error> {
391 let mut stack = self.memory.stack();
392
393 while self.memory.cdr(stack)?.assume_cons().tag() != StackSlot::Frame as _ {
394 stack = self.memory.cdr(stack)?.assume_cons();
395 }
396
397 Ok(self.memory.car(stack)?.assume_cons())
398 }
399
400 #[cfg(feature = "profile")]
403 fn profile_call(&self, call_code: Cons, r#return: bool) -> Result<(), Error> {
404 if let Some(profiler) = &self.profiler {
405 profiler
406 .borrow_mut()
407 .profile_call(&self.memory, call_code, r#return)?;
408 }
409
410 Ok(())
411 }
412
413 #[cfg(feature = "profile")]
414 fn profile_return(&self) -> Result<(), Error> {
415 if let Some(profiler) = &self.profiler {
416 profiler.borrow_mut().profile_return(&self.memory)?;
417 }
418
419 Ok(())
420 }
421
422 #[cfg(feature = "profile")]
423 fn profile_event(&self, name: &str) -> Result<(), Error> {
424 if let Some(profiler) = &self.profiler {
425 profiler.borrow_mut().profile_event(name)?;
426 }
427
428 Ok(())
429 }
430
431 #[doc(hidden)]
433 pub fn initialize(&mut self, input: impl IntoIterator<Item = u8>) -> Result<(), super::Error> {
434 profile_event!(self, "initialization_start");
435 profile_event!(self, "decode_start");
436
437 let program = self.decode_ribs(input.into_iter())?;
438 self.memory
439 .set_false(self.memory.car(program)?.assume_cons());
440 self.memory
441 .set_code(self.memory.cdr(program)?.assume_cons());
442
443 profile_event!(self, "decode_end");
444
445 let codes = self
447 .memory
448 .cons(Number::default().into(), self.memory.null()?)?
449 .into();
450 let continuation = self.memory.cons(codes, self.memory.null()?)?.into();
451 let stack = self.memory.allocate(
452 continuation,
453 self.memory.null()?.set_tag(StackSlot::Frame as _).into(),
454 )?;
455 self.memory.set_stack(stack);
456 self.memory.set_register(NEVER);
457
458 profile_event!(self, "initialization_end");
459
460 Ok(())
461 }
462
463 fn decode_ribs(&mut self, input: impl Iterator<Item = u8>) -> Result<Cons, Error> {
464 let mut input = input.decompress::<{ MAX_WINDOW_SIZE }>();
465
466 while let Some(head) = input.next() {
467 if head & 0b1 == 0 {
468 let head = head >> 1;
469
470 if head == 0 {
471 let value = self.memory.top()?;
472 let cons = self.memory.cons(value, self.memory.code())?;
473 self.memory.set_code(cons);
474 } else {
475 let integer = Self::decode_integer_tail(&mut input, head - 1, SHARE_BASE)?;
476 let index = integer >> 1;
477
478 if index > 0 {
479 let cons = self.memory.tail(self.memory.code(), index as usize - 1)?;
480 let head = self.memory.cdr(cons)?.assume_cons();
481 let tail = self.memory.cdr(head)?;
482 self.memory.set_cdr(head, self.memory.code().into())?;
483 self.memory.set_cdr(cons, tail)?;
484 self.memory.set_code(head);
485 }
486
487 let value = self.memory.car(self.memory.code())?;
488
489 if integer & 1 == 0 {
490 self.memory
491 .set_code(self.memory.cdr(self.memory.code())?.assume_cons());
492 }
493
494 self.memory.push(value)?;
495 }
496 } else if head & 0b10 == 0 {
497 let cons = self.memory.stack();
498 let cdr = self.memory.pop()?;
499 let car = self.memory.top()?;
500 let tag = Self::decode_integer_tail(&mut input, head >> 2, TAG_BASE)?;
501 self.memory.set_car(cons, car)?;
502 self.memory.set_raw_cdr(cons, cdr.set_tag(tag as _))?;
503 self.memory.set_top(cons.into())?;
504 } else {
505 self.memory.push(
506 Self::decode_number(Self::decode_integer_tail(
507 &mut input,
508 head >> 2,
509 NUMBER_BASE,
510 )?)
511 .into(),
512 )?;
513 }
514 }
515
516 self.memory.pop()?.to_cons().ok_or(Error::BytecodeEnd)
517 }
518
519 fn decode_number(integer: u128) -> Number {
520 if integer & 1 == 0 {
521 Number::from_i64((integer >> 1) as _)
522 } else if integer & 0b10 == 0 {
523 Number::from_i64(-((integer >> 2) as i64))
524 } else {
525 let integer = integer >> 2;
526 let mantissa =
527 if integer.is_multiple_of(2) { 1.0 } else { -1.0 } * (integer >> 12) as f64;
528 let exponent = ((integer >> 1) % (1 << 11)) as isize - 1023;
529
530 Number::from_f64(if exponent < 0 {
531 mantissa / (1u64 << exponent.abs()) as f64
532 } else {
533 mantissa * (1u64 << exponent) as f64
534 })
535 }
536 }
537
538 fn decode_integer_tail(
539 input: &mut impl Iterator<Item = u8>,
540 mut x: u8,
541 mut base: u128,
542 ) -> Result<u128, Error> {
543 let mut y = (x >> 1) as u128;
544
545 while x & 1 != 0 {
546 x = input.next().ok_or(Error::BytecodeEnd)?;
547 y += (x as u128 >> 1) * base;
548 base *= INTEGER_BASE;
549 }
550
551 Ok(y)
552 }
553}
554
555impl<T: PrimitiveSet<H>, H: Heap> Display for Vm<'_, T, H> {
556 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
557 write!(formatter, "{}", &self.memory)
558 }
559}
560
561#[cfg(test)]
562mod tests {
563 use super::*;
564
565 struct FakePrimitiveSet {}
566
567 impl<H: Heap> PrimitiveSet<H> for FakePrimitiveSet {
568 type Error = Error;
569
570 #[maybe_async]
571 fn operate(
572 &mut self,
573 _memory: &mut Memory<H>,
574 _primitive: usize,
575 ) -> Result<(), Self::Error> {
576 Ok(())
577 }
578 }
579
580 type VoidVm = Vm<'static, FakePrimitiveSet>;
581
582 #[test]
583 fn arity() {
584 for arity in [
585 Arity {
586 count: 0,
587 variadic: false,
588 },
589 Arity {
590 count: 1,
591 variadic: false,
592 },
593 Arity {
594 count: 2,
595 variadic: false,
596 },
597 Arity {
598 count: 0,
599 variadic: true,
600 },
601 Arity {
602 count: 1,
603 variadic: true,
604 },
605 Arity {
606 count: 2,
607 variadic: true,
608 },
609 ] {
610 assert_eq!(VoidVm::parse_arity(VoidVm::build_arity(arity)), arity);
611 }
612 }
613}