1use eyre::Result;
5use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Rem, Shl, Shr, Sub};
6
7use crate::StepOutcome;
8use crate::data::CPU;
9use crate::instruction::RuntimeInstruction;
10use vihaco::Effects;
11use vihaco::program::{Type, Value};
12use vihaco::{Execute, Execution, StepResult, frame::Frame, traits::*};
13
14impl Reset for CPU {
15 fn reset(&mut self) {
16 self.frames.clear();
17 self.heap.clear();
18 self.stack.clear();
19 self.span = (0, 0, 0);
20 self.pending_pc = None;
21 self.current_pc = 0;
22 self.return_values.clear();
23 }
24}
25
26impl CPU {
27 pub fn execute_instruction(&mut self, inst: RuntimeInstruction) -> eyre::Result<StepOutcome> {
28 self.clear_pending_pc();
29 use RuntimeInstruction::*;
30 match inst {
31 Span(file, start, end) => self.op_span(file, start, end),
32 Label | FunctionStart | FunctionEnd => Ok(StepOutcome::Continue),
33 Breakpoint => Ok(StepOutcome::Breakpoint),
34 Branch(target) => self.op_branch(target),
35 ConditionalBranch(true_target, false_target) => {
36 self.op_conditional_branch(true_target, false_target)
37 }
38 Return(keep) => self.op_return(keep),
39 Call(arity, target) => self.op_call(arity, target),
40 IndirectCall => self.op_indirect_call(),
41 Halt => Ok(StepOutcome::Halt),
42 Print => Err(eyre::eyre!(
43 "Print must be handled via execute with CPUMessage::Print"
44 )),
45 Load(ty, addr) => self.op_load(ty, addr),
46 Store(ty, addr) => self.op_store(ty, addr),
47 Dup => self.op_dup(),
48 HeapAlloc(n_elements) => self.op_heap_alloc(n_elements),
49 GetItem => self.op_get_item(),
50 HeapDealloc => self.op_heap_dealloc(),
51 Const(v) => self.op_const(v),
52 Add(ty) => self.op_add(ty),
53 Sub(ty) => self.op_sub(ty),
54 Mul(ty) => self.op_mul(ty),
55 Div(ty) => self.op_div(ty),
56 Rem(ty) => self.op_rem(ty),
57 Neg(ty) => self.op_neg(ty),
58 Shl(ty) => self.op_shl(ty),
59 Shr(ty) => self.op_shr(ty),
60 Rol(ty) => self.op_rol(ty),
61 Ror(ty) => self.op_ror(ty),
62 BitAnd(ty) => self.op_bitand(ty),
63 BitOr(ty) => self.op_bitor(ty),
64 BitXor(ty) => self.op_bitxor(ty),
65 Not => self.op_not(),
66 And => self.op_and(),
67 Or => self.op_or(),
68 Xor => self.op_xor(),
69 Eq(ty) => self.op_eq(ty),
70 Ne(ty) => self.op_ne(ty),
71 Lt(ty) => self.op_lt(ty),
72 Gt(ty) => self.op_gt(ty),
73 Le(ty) => self.op_le(ty),
74 Ge(ty) => self.op_ge(ty),
75 }
76 }
77}
78
79#[derive(Debug, Clone, PartialEq)]
80pub enum CPUMessage {
81 None,
82 FunctionInfo { arity: u32, start_address: u32 },
83 Print(String),
84}
85
86impl vihaco::Message for CPUMessage {}
87
88impl CPU {
89 fn execute(
90 &mut self,
91 inst: RuntimeInstruction,
92 msg: CPUMessage,
93 ) -> eyre::Result<Effects<StepOutcome>> {
94 use RuntimeInstruction::*;
95 match (inst, msg) {
96 (Print, CPUMessage::Print(text)) => {
97 self.stack_pop()?;
98 drop(text);
99 Ok(Effects::one(StepOutcome::Continue))
100 }
101 (Print, _) => Err(eyre::eyre!("Print requires CPUMessage::Print")),
102 (_, CPUMessage::Print(_)) => Err(eyre::eyre!(
103 "CPUMessage::Print is only valid for Print instruction"
104 )),
105 (
106 inst,
107 CPUMessage::FunctionInfo {
108 arity,
109 start_address,
110 },
111 ) => {
112 self.stack_push(arity);
113 self.stack_push(start_address);
114 self.execute_instruction(inst).map(Effects::one)
115 }
116 (inst, CPUMessage::None) => self.execute_instruction(inst).map(Effects::one),
117 }
118 }
119}
120
121impl Execute<RuntimeInstruction> for CPU {
122 type Message = CPUMessage;
123 type Effect = StepOutcome;
124 type Fault = eyre::Report;
125
126 fn execute(
127 &mut self,
128 inst: &RuntimeInstruction,
129 msg: Self::Message,
130 ) -> eyre::Result<StepResult<Self::Effect>> {
131 Ok(StepResult {
132 effects: self.execute(inst.clone(), msg)?,
133 execution: Execution::Complete,
134 })
135 }
136}
137
138impl CPU {
139 pub fn op_span(&mut self, file: u32, start: u32, end: u32) -> eyre::Result<StepOutcome> {
140 self.span = (file, start, end);
141 Ok(StepOutcome::Continue)
142 }
143
144 pub fn op_branch(&mut self, target: u32) -> eyre::Result<StepOutcome> {
145 self.set_pending_pc(target);
146 Ok(StepOutcome::Continue)
147 }
148
149 pub fn op_conditional_branch(
150 &mut self,
151 true_target: u32,
152 false_target: u32,
153 ) -> eyre::Result<StepOutcome> {
154 let cond = self.stack.pop().ok_or(eyre::eyre!("stack underflow"))?;
155 match cond {
156 Value::Bool(true) => {
157 self.set_pending_pc(true_target);
158 Ok(StepOutcome::Continue)
159 }
160 Value::Bool(false) => {
161 self.set_pending_pc(false_target);
162 Ok(StepOutcome::Continue)
163 }
164 _ => Err(eyre::eyre!("type error: expected bool on stack")),
165 }
166 }
167
168 pub fn op_return(&mut self, keep: u32) -> eyre::Result<StepOutcome> {
169 let frame = self.pop_frame()?;
170 if self.stack.len() - frame.base < (keep as usize) {
171 return Err(eyre::eyre!("not enough values to return"));
172 }
173
174 let top = self.stack.len() - keep as usize;
176 let return_values: Vec<Value> = self.stack[top..].to_vec();
177 self.stack.drain(frame.base..top);
178
179 if self.get_frame().is_err() {
180 self.set_return_values(return_values);
182 Ok(StepOutcome::Return)
183 } else {
184 self.set_pending_pc(frame.ret_pc);
185 Ok(StepOutcome::Continue)
186 }
187 }
188
189 pub fn op_call(&mut self, arity: u32, target: u32) -> eyre::Result<StepOutcome> {
190 if self.stack.len() < (arity as usize) {
191 return Err(eyre::eyre!(
192 "not enough arguments on stack to call function"
193 ));
194 }
195
196 let base = self.stack.len() - (arity as usize);
197 let frame = Frame {
198 base,
199 span: self.span,
200 function: None,
201 ret_pc: self.current_pc + 1,
202 };
203 self.push_frame(frame);
204 self.set_pending_pc(target);
205 Ok(StepOutcome::Continue)
206 }
207
208 pub fn op_indirect_call(&mut self) -> eyre::Result<StepOutcome> {
209 let target: u32 = self.stack_pop()?.try_into()?;
211 let arity: u32 = self.stack_pop()?.try_into()?;
212 let f = self.stack_pop()?.get_function_ref()?;
213
214 if self.stack.len() < (arity as usize) {
215 return Err(eyre::eyre!(
216 "not enough arguments on stack to call function"
217 ));
218 }
219
220 let base = self.stack.len() - (arity as usize);
221 let frame = Frame {
222 base,
223 span: self.span,
224 function: Some(f as usize),
225 ret_pc: self.current_pc + 1,
226 };
227 self.push_frame(frame);
228 self.set_pending_pc(target);
229 Ok(StepOutcome::Continue)
230 }
231
232 fn op_load(&mut self, ty: Type, addr: u32) -> eyre::Result<StepOutcome> {
233 let value = self.get_local(addr as usize)?;
235 if value.type_of() != ty {
236 return Err(eyre::eyre!(format!(
237 "type error: expected {:?} at address {}, got {:?}",
238 ty,
239 addr,
240 value.type_of()
241 )));
242 }
243 self.stack_push(*value);
244 Ok(StepOutcome::Continue)
245 }
246
247 pub fn op_store(&mut self, ty: Type, addr: u32) -> Result<StepOutcome> {
248 let v: Value = self.stack_pop()?;
249 log::debug!("store value {:?} at addr {}", v, addr);
250 if !v.is_undefined() && v.type_of() != ty {
251 return Err(eyre::eyre!("Type mismatch"));
252 }
253 *self.get_local_mut(addr as usize)? = v;
254 Ok(StepOutcome::Continue)
255 }
256
257 pub fn op_dup(&mut self) -> Result<StepOutcome> {
258 let v = *self.stack_top()?;
259 self.stack.push(v);
260 Ok(StepOutcome::Continue)
261 }
262
263 pub fn op_heap_alloc(&mut self, n_elements: u32) -> Result<StepOutcome> {
264 let n: usize = n_elements as usize;
265 if self.stack.len() < n {
266 return Err(eyre::eyre!("stack underflow"));
267 }
268 let start = self.stack.len() - n;
269 let values: Box<[Value]> = self.stack.drain(start..).collect();
270 let heap_id = self.push_heap_object(values);
271 self.stack_push(Value::HeapRef(heap_id));
272 Ok(StepOutcome::Continue)
273 }
274
275 pub fn op_get_item(&mut self) -> Result<StepOutcome> {
276 let index = Self::heap_index(self.stack_pop()?)?;
277 let heap_id = self.stack_pop()?.get_heap_ref()?;
278 let value = *self
279 .heap_object(heap_id)?
280 .get(index)
281 .ok_or_else(|| eyre::eyre!("heap index {} out of bounds", index))?;
282 self.stack_push(value);
283 Ok(StepOutcome::Continue)
284 }
285
286 pub fn op_heap_dealloc(&mut self) -> Result<StepOutcome> {
287 let id = self.stack_pop()?.get_heap_ref()?;
288 self.dealloc_heap_object(id)?;
289 Ok(StepOutcome::Continue)
290 }
291
292 pub fn op_const(&mut self, v: Value) -> Result<StepOutcome> {
293 self.stack.push(v);
294 Ok(StepOutcome::Continue)
295 }
296
297 fn heap_index(value: Value) -> Result<usize> {
298 match value {
299 Value::U32(index) => Ok(index as usize),
300 Value::U64(index) => usize::try_from(index)
301 .map_err(|_| eyre::eyre!("heap index {} does not fit in usize", index)),
302 Value::I64(index) if index >= 0 => usize::try_from(index)
303 .map_err(|_| eyre::eyre!("heap index {} does not fit in usize", index)),
304 Value::I64(index) => Err(eyre::eyre!(
305 "heap index must be non-negative, got {}",
306 index
307 )),
308 _ => Err(eyre::eyre!(
309 "type error: expected integer heap index, got {:?}",
310 value.type_of()
311 )),
312 }
313 }
314}
315
316#[cfg(test)]
317#[allow(clippy::items_after_test_module)]
318mod tests {
319 use super::*;
320 use vihaco::{Effects, Execute, frame::Frame, instruction::OpCode, traits::StackMemory};
321
322 fn execute(
323 cpu: &mut CPU,
324 instruction: RuntimeInstruction,
325 message: CPUMessage,
326 ) -> eyre::Result<Effects<StepOutcome>> {
327 Execute::execute(cpu, &instruction, message).map(|result| result.effects)
328 }
329
330 #[test]
331 fn cpu_generated_component_executes_instruction_without_message() {
332 let mut cpu = CPU::default();
333
334 execute(
335 &mut cpu,
336 RuntimeInstruction::Const(Value::I64(7)),
337 CPUMessage::None,
338 )
339 .unwrap();
340
341 assert_eq!(cpu.stack(), &vec![Value::I64(7)]);
342 }
343
344 #[test]
345 fn execute_instruction_applies_control_flow_without_action() {
346 let mut cpu = CPU::default();
347
348 let branch = cpu
349 .execute_instruction(RuntimeInstruction::Branch(9))
350 .unwrap();
351 assert_eq!(branch, StepOutcome::Continue);
352 assert_eq!(cpu.take_pending_pc(), Some(9));
353
354 let halt = cpu.execute_instruction(RuntimeInstruction::Halt).unwrap();
355 assert_eq!(halt, StepOutcome::Halt);
356 assert_eq!(cpu.take_pending_pc(), None);
357 }
358
359 #[test]
360 fn op_return_stores_terminal_values_in_runtime_state() {
361 let mut cpu = CPU::default();
362 cpu.push_frame(Frame {
363 base: 0,
364 span: (0, 0, 0),
365 function: None,
366 ret_pc: 0,
367 });
368 cpu.stack_push(Value::I64(7));
369
370 let outcome = cpu
371 .execute_instruction(RuntimeInstruction::Return(1))
372 .unwrap();
373
374 assert_eq!(outcome, StepOutcome::Return);
375 assert_eq!(cpu.return_values(), &[Value::I64(7)]);
376 }
377
378 #[test]
379 fn op_return_restores_callers_pc() {
380 let mut cpu = CPU {
381 current_pc: 10,
382 ..Default::default()
383 };
384 cpu.push_frame(Frame {
386 base: 0,
387 span: (0, 0, 0),
388 function: None,
389 ret_pc: 0,
390 });
391
392 cpu.execute_instruction(RuntimeInstruction::Call(0, 100))
395 .unwrap();
396 assert_eq!(cpu.take_pending_pc(), Some(100));
397 assert_eq!(cpu.frames[1].ret_pc, 11);
398
399 let outcome = cpu
402 .execute_instruction(RuntimeInstruction::Return(0))
403 .unwrap();
404 assert_eq!(outcome, StepOutcome::Continue);
405 assert_eq!(cpu.take_pending_pc(), Some(11),);
406 }
407
408 #[test]
409 fn op_indirect_call_records_return_pc_after_call_site() {
410 let mut cpu = CPU {
411 current_pc: 10,
412 ..Default::default()
413 };
414 cpu.push_frame(Frame {
415 base: 0,
416 span: (0, 0, 0),
417 function: None,
418 ret_pc: 0,
419 });
420
421 cpu.stack_push(Value::FunctionRef(7));
423 cpu.stack_push(Value::U32(0));
424 cpu.stack_push(Value::U32(100));
425
426 cpu.execute_instruction(RuntimeInstruction::IndirectCall)
427 .unwrap();
428 assert_eq!(cpu.take_pending_pc(), Some(100));
429 assert_eq!(cpu.frames[1].ret_pc, 11);
430
431 let outcome = cpu
432 .execute_instruction(RuntimeInstruction::Return(0))
433 .unwrap();
434 assert_eq!(outcome, StepOutcome::Continue);
435 assert_eq!(cpu.take_pending_pc(), Some(11));
436 }
437
438 #[test]
439 fn op_return_keeps_bottom_of_frame_when_callee_leaves_scratch() {
440 let mut cpu = CPU::default();
441 cpu.push_frame(Frame {
443 base: 0,
444 span: (0, 0, 0),
445 function: None,
446 ret_pc: 0,
447 });
448
449 cpu.push_frame(Frame {
452 base: 0,
453 span: (0, 0, 0),
454 function: None,
455 ret_pc: 0,
456 });
457 cpu.stack_push(Value::I64(111)); cpu.stack_push(Value::I64(222)); cpu.stack_push(Value::I64(999)); let outcome = cpu
462 .execute_instruction(RuntimeInstruction::Return(1))
463 .unwrap();
464 assert_eq!(outcome, StepOutcome::Continue);
465
466 assert_eq!(cpu.stack(), &vec![Value::I64(999)],);
467 }
468
469 #[test]
470 fn op_heap_alloc_preserves_natural_push_order_and_returns_heap_ref() {
471 let mut cpu = CPU::default();
472 cpu.stack_push(Value::I64(10));
473 cpu.stack_push(Value::I64(20));
474 cpu.stack_push(Value::I64(30));
475
476 let outcome = cpu
477 .execute_instruction(RuntimeInstruction::HeapAlloc(3))
478 .unwrap();
479
480 assert_eq!(outcome, StepOutcome::Continue);
481 assert_eq!(cpu.stack(), &vec![Value::HeapRef(0)]);
482 assert_eq!(
483 cpu.heap.get(0).unwrap(),
484 &[Value::I64(10), Value::I64(20), Value::I64(30)]
485 );
486 }
487
488 #[test]
489 fn op_heap_alloc_supports_empty_heap_objects() {
490 let mut cpu = CPU::default();
491
492 let outcome = cpu
493 .execute_instruction(RuntimeInstruction::HeapAlloc(0))
494 .unwrap();
495
496 assert_eq!(outcome, StepOutcome::Continue);
497 assert_eq!(cpu.stack(), &vec![Value::HeapRef(0)]);
498 assert_eq!(cpu.heap.get(0).unwrap(), &[] as &[Value]);
499 }
500
501 #[test]
502 fn op_get_item_reads_heap_value() {
503 let mut cpu = CPU::default();
504 cpu.stack_push(Value::I64(10));
505 cpu.stack_push(Value::I64(20));
506 cpu.stack_push(Value::I64(30));
507 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(3))
508 .unwrap();
509 cpu.stack_push(Value::U32(1));
510
511 let outcome = cpu
512 .execute_instruction(RuntimeInstruction::GetItem)
513 .unwrap();
514
515 assert_eq!(outcome, StepOutcome::Continue);
516 assert_eq!(cpu.stack(), &vec![Value::I64(20)]);
517 }
518
519 #[test]
520 fn op_get_item_rejects_non_heap_refs() {
521 let mut cpu = CPU::default();
522 cpu.stack_push(Value::I64(7));
523 cpu.stack_push(Value::U32(0));
524
525 let err = cpu
526 .execute_instruction(RuntimeInstruction::GetItem)
527 .unwrap_err();
528
529 assert!(err.to_string().contains("HeapRef"));
530 }
531
532 #[test]
533 fn op_get_item_rejects_invalid_heap_ids() {
534 let mut cpu = CPU::default();
535 cpu.stack_push(Value::HeapRef(99));
536 cpu.stack_push(Value::U32(0));
537
538 let err = cpu
539 .execute_instruction(RuntimeInstruction::GetItem)
540 .unwrap_err();
541
542 assert!(err.to_string().contains("heap"));
543 }
544
545 #[test]
546 fn op_get_item_rejects_out_of_bounds_indices() {
547 let mut cpu = CPU::default();
548 cpu.stack_push(Value::I64(10));
549 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(1))
550 .unwrap();
551 cpu.stack_push(Value::U32(3));
552
553 let err = cpu
554 .execute_instruction(RuntimeInstruction::GetItem)
555 .unwrap_err();
556
557 assert!(err.to_string().contains("index"));
558 }
559
560 #[test]
561 fn reset_clears_heap_allocations() {
562 let mut cpu = CPU::default();
563 cpu.stack_push(Value::I64(10));
564 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(1))
565 .unwrap();
566
567 cpu.reset();
568
569 assert!(cpu.heap.is_empty());
570 assert!(cpu.stack().is_empty());
571 }
572
573 #[test]
574 fn cpu_instruction_opcodes_follow_variant_order_without_explicit_attributes() {
575 assert_eq!(RuntimeInstruction::Span(0, 0, 0).opcode(), 0);
576 assert_eq!(RuntimeInstruction::Label.opcode(), 1);
577 assert_eq!(RuntimeInstruction::FunctionStart.opcode(), 2);
578 assert_eq!(RuntimeInstruction::HeapAlloc(1).opcode(), 15);
579 assert_eq!(RuntimeInstruction::Const(Value::I64(1)).opcode(), 18);
580 assert_eq!(RuntimeInstruction::Ge(Type::I64).opcode(), 41);
581 }
582
583 #[test]
584 fn execute_generated_dispatches_instruction_without_message() {
585 let mut cpu = CPU::default();
586 cpu.push_frame(Frame {
587 base: 0,
588 span: (0, 0, 0),
589 function: None,
590 ret_pc: 0,
591 });
592
593 let outcome = execute(
594 &mut cpu,
595 RuntimeInstruction::Const(Value::I64(99)),
596 CPUMessage::None,
597 )
598 .unwrap();
599
600 assert_eq!(outcome, Effects::one(StepOutcome::Continue));
601 assert_eq!(cpu.stack(), &vec![Value::I64(99)]);
602 }
603
604 #[test]
605 fn execute_generated_function_info_pushes_arity_and_start_address() {
606 let mut cpu = CPU::default();
607 cpu.push_frame(Frame {
608 base: 0,
609 span: (0, 0, 0),
610 function: None,
611 ret_pc: 0,
612 });
613
614 let outcome = execute(
615 &mut cpu,
616 RuntimeInstruction::Label,
617 CPUMessage::FunctionInfo {
618 arity: 2,
619 start_address: 42,
620 },
621 )
622 .unwrap();
623
624 assert_eq!(outcome, Effects::one(StepOutcome::Continue));
625 assert_eq!(cpu.stack(), &vec![Value::U32(2), Value::U32(42)]);
627 }
628
629 #[test]
630 fn execute_generated_print_returns_control_effect_and_pops_stack() {
631 let mut cpu = CPU::default();
632 cpu.push_frame(Frame {
633 base: 0,
634 span: (0, 0, 0),
635 function: None,
636 ret_pc: 0,
637 });
638 cpu.stack_push(Value::I64(42));
639
640 let outcome = execute(
641 &mut cpu,
642 RuntimeInstruction::Print,
643 CPUMessage::Print("hello".into()),
644 )
645 .unwrap();
646
647 assert_eq!(outcome, Effects::one(StepOutcome::Continue));
648 assert!(cpu.stack().is_empty());
649 }
650
651 #[test]
652 fn execute_generated_print_rejects_wrong_message() {
653 let mut cpu = CPU::default();
654 cpu.push_frame(Frame {
655 base: 0,
656 span: (0, 0, 0),
657 function: None,
658 ret_pc: 0,
659 });
660 cpu.stack_push(Value::I64(42));
661
662 let err = execute(&mut cpu, RuntimeInstruction::Print, CPUMessage::None).unwrap_err();
663
664 assert!(err.to_string().contains("Print requires"));
665 }
666
667 #[test]
668 fn op_heap_dealloc_marks_slot_dead() {
669 let mut cpu = CPU::default();
670 cpu.stack_push(Value::I64(42));
671 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(1))
672 .unwrap();
673 cpu.stack_push(Value::HeapRef(0));
674
675 cpu.execute_instruction(RuntimeInstruction::HeapDealloc)
676 .unwrap();
677
678 assert!(
679 cpu.heap
680 .get(0)
681 .unwrap_err()
682 .to_string()
683 .contains("deallocated")
684 );
685 }
686
687 #[test]
688 fn op_heap_dealloc_slot_is_reused_on_next_alloc() {
689 let mut cpu = CPU::default();
690 cpu.stack_push(Value::I64(1));
691 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(1))
692 .unwrap();
693 cpu.execute_instruction(RuntimeInstruction::HeapDealloc)
694 .unwrap();
695
696 cpu.stack_push(Value::I64(2));
697 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(1))
698 .unwrap();
699
700 assert_eq!(cpu.stack(), &vec![Value::HeapRef(0)]);
701 assert_eq!(cpu.heap.get(0).unwrap(), &[Value::I64(2)]);
702 }
703
704 #[test]
705 fn op_heap_dealloc_rejects_double_free() {
706 let mut cpu = CPU::default();
707 cpu.stack_push(Value::I64(1));
708 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(1))
709 .unwrap();
710 cpu.stack_push(Value::HeapRef(0));
711 cpu.execute_instruction(RuntimeInstruction::HeapDealloc)
712 .unwrap();
713
714 cpu.stack_push(Value::HeapRef(0));
715 let err = cpu
716 .execute_instruction(RuntimeInstruction::HeapDealloc)
717 .unwrap_err();
718
719 assert!(err.to_string().contains("double-free"));
720 }
721
722 #[test]
723 fn op_heap_dealloc_rejects_invalid_id() {
724 let mut cpu = CPU::default();
725 cpu.stack_push(Value::HeapRef(99));
726
727 let err = cpu
728 .execute_instruction(RuntimeInstruction::HeapDealloc)
729 .unwrap_err();
730
731 assert!(err.to_string().contains("invalid heap object id"));
732 }
733
734 #[test]
735 fn reset_clears_free_list() {
736 let mut cpu = CPU::default();
737 cpu.stack_push(Value::I64(1));
738 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(1))
739 .unwrap();
740 cpu.stack_push(Value::HeapRef(0));
741 cpu.execute_instruction(RuntimeInstruction::HeapDealloc)
742 .unwrap();
743
744 cpu.reset();
745
746 assert!(cpu.heap.is_empty());
747 }
748}
749
750macro_rules! impl_op_num_binary {
751 ($name:ident, $op:ident) => {
752 pub fn $name(&mut self, ty: Type) -> Result<StepOutcome> {
753 let lhs: Value = self.stack_pop()?;
754 let rhs: Value = self.stack_pop()?;
755 if lhs.type_of() != ty {
756 return Err(eyre::eyre!(
757 "Type mismatch, expected {} got {} for lhs",
758 ty,
759 lhs.type_of()
760 ));
761 }
762
763 if rhs.type_of() != ty {
764 return Err(eyre::eyre!(
765 "Type mismatch, expected {} got {} for rhs",
766 ty,
767 rhs.type_of()
768 ));
769 }
770
771 let output = match (lhs, rhs) {
772 (Value::I64(l), Value::I64(r)) => Value::I64(l.$op(r)),
773 (Value::U32(l), Value::U32(r)) => Value::U32(l.$op(r)),
774 (Value::U64(l), Value::U64(r)) => Value::U64(l.$op(r)),
775 (Value::F64(l), Value::F64(r)) => Value::F64(l.$op(r)),
776 _ => {
777 return Err(eyre::eyre!(
778 "cannot {} {} and {}",
779 stringify!($op),
780 lhs.type_of(),
781 rhs.type_of()
782 ))
783 }
784 };
785 self.stack.push(output);
786 Ok(StepOutcome::Continue)
787 }
788 };
789}
790
791impl CPU {
792 impl_op_num_binary!(op_add, add);
793 impl_op_num_binary!(op_sub, sub);
794 impl_op_num_binary!(op_mul, mul);
795 impl_op_num_binary!(op_div, div);
796 impl_op_num_binary!(op_rem, rem);
797
798 pub fn op_neg(&mut self, ty: Type) -> Result<StepOutcome> {
799 let v: Value = self.stack_pop()?;
800 if v.type_of() != ty {
801 return Err(eyre::eyre!(format!(
802 "Type mismatch, expected {:?} got {:?}",
803 ty,
804 v.type_of()
805 )));
806 }
807
808 let output = match v {
809 Value::I64(i) => Value::I64(-i),
810 Value::F64(f) => Value::F64(-f),
811 _ => return Err(eyre::eyre!(format!("cannot negate {}", v.type_of()))),
812 };
813 self.stack.push(output);
814 Ok(StepOutcome::Continue)
815 }
816}
817
818macro_rules! impl_op_shift {
819 ($name:ident, $op:ident) => {
820 pub fn $name(&mut self, ty: Type) -> Result<StepOutcome> {
821 let rhs: Value = self.stack_pop()?;
822 let lhs: Value = self.stack_pop()?;
823 if lhs.type_of() != ty {
824 return Err(eyre::eyre!(
825 "Type mismatch, expected {} got {} for lhs",
826 ty,
827 lhs.type_of()
828 ));
829 }
830
831 if rhs.type_of() != ty {
832 return Err(eyre::eyre!(
833 "Type mismatch, expected {} got {} for rhs",
834 ty,
835 rhs.type_of()
836 ));
837 }
838 let output = match (lhs, rhs) {
839 (Value::I64(l), Value::I64(r)) => Value::I64(l.$op(r)),
840 (Value::U32(l), Value::U32(r)) => Value::U32(l.$op(r)),
841 (Value::U64(l), Value::U64(r)) => Value::U64(l.$op(r)),
842 _ => {
843 return Err(eyre::eyre!(format!(
844 "cannot {} {} and {}",
845 stringify!($op),
846 lhs.type_of(),
847 rhs.type_of()
848 )))
849 }
850 };
851 self.stack.push(output);
852 Ok(StepOutcome::Continue)
853 }
854 };
855}
856
857impl CPU {
858 impl_op_shift!(op_shl, shl);
859 impl_op_shift!(op_shr, shr);
860}
861
862macro_rules! impl_op_rotate {
863 ($name:ident, $op:ident) => {
864 pub fn $name(&mut self, ty: Type) -> Result<StepOutcome> {
865 let rhs: Value = self.stack_pop()?;
866 let lhs: Value = self.stack_pop()?;
867 if lhs.type_of() != ty {
868 return Err(eyre::eyre!(
869 "Type mismatch, expected {} got {} for lhs",
870 ty,
871 lhs.type_of()
872 ));
873 }
874
875 if rhs.type_of() != Type::U32 {
876 return Err(eyre::eyre!(
877 "Type mismatch, expected {} got {} for rhs",
878 Type::U32,
879 rhs.type_of()
880 ));
881 }
882 let output = match (lhs, rhs) {
883 (Value::I64(l), Value::U32(r)) => Value::I64(l.$op(r)),
884 (Value::U32(l), Value::U32(r)) => Value::U32(l.$op(r)),
885 (Value::U64(l), Value::U32(r)) => Value::U64(l.$op(r)),
886 _ => {
887 return Err(eyre::eyre!(format!(
888 "cannot {} {} and {}",
889 stringify!($op),
890 lhs.type_of(),
891 rhs.type_of()
892 )));
893 }
894 };
895 self.stack.push(output);
896 Ok(StepOutcome::Continue)
897 }
898 };
899}
900
901impl CPU {
902 impl_op_rotate!(op_rol, rotate_left);
903 impl_op_rotate!(op_ror, rotate_right);
904}
905
906macro_rules! impl_op_bitwise {
907 ($name:ident, $op:ident) => {
908 pub fn $name(&mut self, ty: Type) -> Result<StepOutcome> {
909 let rhs: Value = self.stack_pop()?;
910 let lhs: Value = self.stack_pop()?;
911 if lhs.type_of() != ty {
912 return Err(eyre::eyre!(
913 "Type mismatch, expected {} got {} for lhs",
914 ty,
915 lhs.type_of()
916 ));
917 }
918
919 if rhs.type_of() != ty {
920 return Err(eyre::eyre!(
921 "Type mismatch, expected {} got {} for rhs",
922 ty,
923 rhs.type_of()
924 ));
925 }
926 let output = match (lhs, rhs) {
927 (Value::I64(l), Value::I64(r)) => Value::I64(l.$op(r)),
928 (Value::U32(l), Value::U32(r)) => Value::U32(l.$op(r)),
929 (Value::U64(l), Value::U64(r)) => Value::U64(l.$op(r)),
930 _ => {
931 return Err(eyre::eyre!(format!(
932 "cannot {} {} and {}",
933 stringify!($op),
934 lhs.type_of(),
935 rhs.type_of()
936 )))
937 }
938 };
939 self.stack.push(output);
940 Ok(StepOutcome::Continue)
941 }
942 };
943}
944
945impl CPU {
946 impl_op_bitwise!(op_bitand, bitand);
947 impl_op_bitwise!(op_bitor, bitor);
948 impl_op_bitwise!(op_bitxor, bitxor);
949}
950
951macro_rules! impl_boolean_binary {
952 ($name:ident, $op:ident) => {
953 pub fn $name(&mut self) -> Result<StepOutcome> {
954 let rhs: bool = self.stack_pop()?.try_into()?;
955 let lhs: bool = self.stack_pop()?.try_into()?;
956 let output = lhs.$op(rhs);
957 self.stack_push(output);
958 Ok(StepOutcome::Continue)
959 }
960 };
961}
962
963impl CPU {
964 pub fn op_not(&mut self) -> Result<StepOutcome> {
965 let v: bool = self.stack_pop()?.try_into()?;
966 self.stack_push(!v);
967 Ok(StepOutcome::Continue)
968 }
969
970 impl_boolean_binary!(op_and, bitand);
971 impl_boolean_binary!(op_or, bitor);
972 impl_boolean_binary!(op_xor, bitxor);
973}
974
975macro_rules! impl_eq {
976 ($name:ident, $op:ident) => {
977 pub fn $name(&mut self, ty: Type) -> Result<StepOutcome> {
978 let rhs: Value = self.stack_pop()?;
979 let lhs: Value = self.stack_pop()?;
980 if lhs.type_of() != ty {
981 return Err(eyre::eyre!(
982 "Type mismatch, expected {} got {} for lhs",
983 ty,
984 lhs.type_of()
985 ));
986 }
987
988 if rhs.type_of() != ty {
989 return Err(eyre::eyre!(
990 "Type mismatch, expected {} got {} for rhs",
991 ty,
992 rhs.type_of()
993 ));
994 }
995 let output = lhs.$op(&rhs);
996 self.stack_push(output);
997 Ok(StepOutcome::Continue)
998 }
999 };
1000}
1001
1002impl CPU {
1003 impl_eq!(op_eq, eq);
1004 impl_eq!(op_ne, ne);
1005}
1006
1007macro_rules! impl_ordering {
1008 ($name:ident, $op:ident) => {
1009 pub fn $name(&mut self, ty: Type) -> Result<StepOutcome> {
1010 let rhs: Value = self.stack_pop()?;
1011 let lhs: Value = self.stack_pop()?;
1012 if lhs.type_of() != ty {
1013 return Err(eyre::eyre!(
1014 "Type mismatch, expected {} got {} for lhs",
1015 ty,
1016 lhs.type_of()
1017 ));
1018 }
1019
1020 if rhs.type_of() != ty {
1021 return Err(eyre::eyre!(
1022 "Type mismatch, expected {} got {} for rhs",
1023 ty,
1024 rhs.type_of()
1025 ));
1026 }
1027
1028 let output = match (lhs, rhs) {
1029 (Value::Bool(l), Value::Bool(r)) => l.$op(&r),
1030 (Value::I64(l), Value::I64(r)) => l.$op(&r),
1031 (Value::U32(l), Value::U32(r)) => l.$op(&r),
1032 (Value::U64(l), Value::U64(r)) => l.$op(&r),
1033 (Value::F64(l), Value::F64(r)) => l.$op(&r),
1034 _ => {
1035 return Err(eyre::eyre!(format!(
1036 "cannot compare {} and {}",
1037 lhs.type_of(),
1038 rhs.type_of()
1039 )))
1040 }
1041 };
1042 self.stack_push(output);
1043 Ok(StepOutcome::Continue)
1044 }
1045 };
1046}
1047
1048impl CPU {
1049 impl_ordering!(op_lt, lt);
1050 impl_ordering!(op_le, le);
1051 impl_ordering!(op_gt, gt);
1052 impl_ordering!(op_ge, ge);
1053}