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