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::Instruction;
10use vihaco::Effects;
11use vihaco::value::Value;
12use vihaco::{component, frame::Frame, traits::*, value::Type};
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: Instruction) -> eyre::Result<StepOutcome> {
28 self.clear_pending_pc();
29 use Instruction::*;
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 = Instruction, message = CPUMessage, effect = StepOutcome)]
87impl CPU {
88 fn execute(
89 &mut self,
90 inst: Instruction,
91 msg: CPUMessage,
92 ) -> eyre::Result<Effects<StepOutcome>> {
93 use Instruction::*;
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 Instruction::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.execute_instruction(Instruction::Branch(9)).unwrap();
325 assert_eq!(branch, StepOutcome::Continue);
326 assert_eq!(cpu.take_pending_pc(), Some(9));
327
328 let halt = cpu.execute_instruction(Instruction::Halt).unwrap();
329 assert_eq!(halt, StepOutcome::Halt);
330 assert_eq!(cpu.take_pending_pc(), None);
331 }
332
333 #[test]
334 fn op_return_stores_terminal_values_in_runtime_state() {
335 let mut cpu = CPU::default();
336 cpu.push_frame(Frame {
337 base: 0,
338 span: (0, 0, 0),
339 function: None,
340 ret_pc: 0,
341 });
342 cpu.stack_push(Value::I64(7));
343
344 let outcome = cpu.execute_instruction(Instruction::Return(1)).unwrap();
345
346 assert_eq!(outcome, StepOutcome::Return);
347 assert_eq!(cpu.return_values(), &[Value::I64(7)]);
348 }
349
350 #[test]
351 fn op_return_restores_callers_pc() {
352 let mut cpu = CPU {
353 current_pc: 10,
354 ..Default::default()
355 };
356 cpu.push_frame(Frame {
358 base: 0,
359 span: (0, 0, 0),
360 function: None,
361 ret_pc: 0,
362 });
363
364 cpu.execute_instruction(Instruction::Call(0, 100)).unwrap();
367 assert_eq!(cpu.take_pending_pc(), Some(100));
368 assert_eq!(cpu.frames[1].ret_pc, 11);
369
370 let outcome = cpu.execute_instruction(Instruction::Return(0)).unwrap();
373 assert_eq!(outcome, StepOutcome::Continue);
374 assert_eq!(cpu.take_pending_pc(), Some(11),);
375 }
376
377 #[test]
378 fn op_indirect_call_records_return_pc_after_call_site() {
379 let mut cpu = CPU {
380 current_pc: 10,
381 ..Default::default()
382 };
383 cpu.push_frame(Frame {
384 base: 0,
385 span: (0, 0, 0),
386 function: None,
387 ret_pc: 0,
388 });
389
390 cpu.stack_push(Value::FunctionRef(7));
392 cpu.stack_push(Value::U32(0));
393 cpu.stack_push(Value::U32(100));
394
395 cpu.execute_instruction(Instruction::IndirectCall).unwrap();
396 assert_eq!(cpu.take_pending_pc(), Some(100));
397 assert_eq!(cpu.frames[1].ret_pc, 11);
398
399 let outcome = cpu.execute_instruction(Instruction::Return(0)).unwrap();
400 assert_eq!(outcome, StepOutcome::Continue);
401 assert_eq!(cpu.take_pending_pc(), Some(11));
402 }
403
404 #[test]
405 fn op_return_keeps_bottom_of_frame_when_callee_leaves_scratch() {
406 let mut cpu = CPU::default();
407 cpu.push_frame(Frame {
409 base: 0,
410 span: (0, 0, 0),
411 function: None,
412 ret_pc: 0,
413 });
414
415 cpu.push_frame(Frame {
418 base: 0,
419 span: (0, 0, 0),
420 function: None,
421 ret_pc: 0,
422 });
423 cpu.stack_push(Value::I64(111)); cpu.stack_push(Value::I64(222)); cpu.stack_push(Value::I64(999)); let outcome = cpu.execute_instruction(Instruction::Return(1)).unwrap();
428 assert_eq!(outcome, StepOutcome::Continue);
429
430 assert_eq!(cpu.stack(), &vec![Value::I64(999)],);
431 }
432
433 #[test]
434 fn op_heap_alloc_preserves_natural_push_order_and_returns_heap_ref() {
435 let mut cpu = CPU::default();
436 cpu.stack_push(Value::I64(10));
437 cpu.stack_push(Value::I64(20));
438 cpu.stack_push(Value::I64(30));
439
440 let outcome = cpu.execute_instruction(Instruction::HeapAlloc(3)).unwrap();
441
442 assert_eq!(outcome, StepOutcome::Continue);
443 assert_eq!(cpu.stack(), &vec![Value::HeapRef(0)]);
444 assert_eq!(
445 cpu.heap.get(0).unwrap(),
446 &[Value::I64(10), Value::I64(20), Value::I64(30)]
447 );
448 }
449
450 #[test]
451 fn op_heap_alloc_supports_empty_heap_objects() {
452 let mut cpu = CPU::default();
453
454 let outcome = cpu.execute_instruction(Instruction::HeapAlloc(0)).unwrap();
455
456 assert_eq!(outcome, StepOutcome::Continue);
457 assert_eq!(cpu.stack(), &vec![Value::HeapRef(0)]);
458 assert_eq!(cpu.heap.get(0).unwrap(), &[] as &[Value]);
459 }
460
461 #[test]
462 fn op_get_item_reads_heap_value() {
463 let mut cpu = CPU::default();
464 cpu.stack_push(Value::I64(10));
465 cpu.stack_push(Value::I64(20));
466 cpu.stack_push(Value::I64(30));
467 cpu.execute_instruction(Instruction::HeapAlloc(3)).unwrap();
468 cpu.stack_push(Value::U32(1));
469
470 let outcome = cpu.execute_instruction(Instruction::GetItem).unwrap();
471
472 assert_eq!(outcome, StepOutcome::Continue);
473 assert_eq!(cpu.stack(), &vec![Value::I64(20)]);
474 }
475
476 #[test]
477 fn op_get_item_rejects_non_heap_refs() {
478 let mut cpu = CPU::default();
479 cpu.stack_push(Value::I64(7));
480 cpu.stack_push(Value::U32(0));
481
482 let err = cpu.execute_instruction(Instruction::GetItem).unwrap_err();
483
484 assert!(err.to_string().contains("HeapRef"));
485 }
486
487 #[test]
488 fn op_get_item_rejects_invalid_heap_ids() {
489 let mut cpu = CPU::default();
490 cpu.stack_push(Value::HeapRef(99));
491 cpu.stack_push(Value::U32(0));
492
493 let err = cpu.execute_instruction(Instruction::GetItem).unwrap_err();
494
495 assert!(err.to_string().contains("heap"));
496 }
497
498 #[test]
499 fn op_get_item_rejects_out_of_bounds_indices() {
500 let mut cpu = CPU::default();
501 cpu.stack_push(Value::I64(10));
502 cpu.execute_instruction(Instruction::HeapAlloc(1)).unwrap();
503 cpu.stack_push(Value::U32(3));
504
505 let err = cpu.execute_instruction(Instruction::GetItem).unwrap_err();
506
507 assert!(err.to_string().contains("index"));
508 }
509
510 #[test]
511 fn reset_clears_heap_allocations() {
512 let mut cpu = CPU::default();
513 cpu.stack_push(Value::I64(10));
514 cpu.execute_instruction(Instruction::HeapAlloc(1)).unwrap();
515
516 cpu.reset();
517
518 assert!(cpu.heap.is_empty());
519 assert!(cpu.stack().is_empty());
520 }
521
522 #[test]
523 fn cpu_instruction_opcodes_follow_variant_order_without_explicit_attributes() {
524 assert_eq!(Instruction::Span(0, 0, 0).opcode(), 0);
525 assert_eq!(Instruction::Label.opcode(), 1);
526 assert_eq!(Instruction::FunctionStart.opcode(), 2);
527 assert_eq!(Instruction::HeapAlloc(1).opcode(), 15);
528 assert_eq!(Instruction::Const(Value::I64(1)).opcode(), 18);
529 assert_eq!(Instruction::Ge(Type::I64).opcode(), 41);
530 }
531
532 #[test]
533 fn execute_generated_dispatches_instruction_without_message() {
534 let mut cpu = CPU::default();
535 cpu.push_frame(Frame {
536 base: 0,
537 span: (0, 0, 0),
538 function: None,
539 ret_pc: 0,
540 });
541
542 let outcome = GeneratedComponent::execute_generated(
543 &mut cpu,
544 Instruction::Const(Value::I64(99)),
545 CPUMessage::None,
546 )
547 .unwrap();
548
549 assert_eq!(outcome, Effects::one(StepOutcome::Continue));
550 assert_eq!(cpu.stack(), &vec![Value::I64(99)]);
551 }
552
553 #[test]
554 fn execute_generated_function_info_pushes_arity_and_start_address() {
555 let mut cpu = CPU::default();
556 cpu.push_frame(Frame {
557 base: 0,
558 span: (0, 0, 0),
559 function: None,
560 ret_pc: 0,
561 });
562
563 let outcome = GeneratedComponent::execute_generated(
564 &mut cpu,
565 Instruction::Label,
566 CPUMessage::FunctionInfo {
567 arity: 2,
568 start_address: 42,
569 },
570 )
571 .unwrap();
572
573 assert_eq!(outcome, Effects::one(StepOutcome::Continue));
574 assert_eq!(cpu.stack(), &vec![Value::U32(2), Value::U32(42)]);
576 }
577
578 #[test]
579 fn execute_generated_print_returns_control_effect_and_pops_stack() {
580 let mut cpu = CPU::default();
581 cpu.push_frame(Frame {
582 base: 0,
583 span: (0, 0, 0),
584 function: None,
585 ret_pc: 0,
586 });
587 cpu.stack_push(Value::I64(42));
588
589 let outcome = GeneratedComponent::execute_generated(
590 &mut cpu,
591 Instruction::Print,
592 CPUMessage::Print("hello".into()),
593 )
594 .unwrap();
595
596 assert_eq!(outcome, Effects::one(StepOutcome::Continue));
597 assert!(cpu.stack().is_empty());
598 }
599
600 #[test]
601 fn execute_generated_print_rejects_wrong_message() {
602 let mut cpu = CPU::default();
603 cpu.push_frame(Frame {
604 base: 0,
605 span: (0, 0, 0),
606 function: None,
607 ret_pc: 0,
608 });
609 cpu.stack_push(Value::I64(42));
610
611 let err =
612 GeneratedComponent::execute_generated(&mut cpu, Instruction::Print, CPUMessage::None)
613 .unwrap_err();
614
615 assert!(err.to_string().contains("Print requires"));
616 }
617
618 #[test]
619 fn op_heap_dealloc_marks_slot_dead() {
620 let mut cpu = CPU::default();
621 cpu.stack_push(Value::I64(42));
622 cpu.execute_instruction(Instruction::HeapAlloc(1)).unwrap();
623 cpu.stack_push(Value::HeapRef(0));
624
625 cpu.execute_instruction(Instruction::HeapDealloc).unwrap();
626
627 assert!(
628 cpu.heap
629 .get(0)
630 .unwrap_err()
631 .to_string()
632 .contains("deallocated")
633 );
634 }
635
636 #[test]
637 fn op_heap_dealloc_slot_is_reused_on_next_alloc() {
638 let mut cpu = CPU::default();
639 cpu.stack_push(Value::I64(1));
640 cpu.execute_instruction(Instruction::HeapAlloc(1)).unwrap();
641 cpu.execute_instruction(Instruction::HeapDealloc).unwrap();
642
643 cpu.stack_push(Value::I64(2));
644 cpu.execute_instruction(Instruction::HeapAlloc(1)).unwrap();
645
646 assert_eq!(cpu.stack(), &vec![Value::HeapRef(0)]);
647 assert_eq!(cpu.heap.get(0).unwrap(), &[Value::I64(2)]);
648 }
649
650 #[test]
651 fn op_heap_dealloc_rejects_double_free() {
652 let mut cpu = CPU::default();
653 cpu.stack_push(Value::I64(1));
654 cpu.execute_instruction(Instruction::HeapAlloc(1)).unwrap();
655 cpu.stack_push(Value::HeapRef(0));
656 cpu.execute_instruction(Instruction::HeapDealloc).unwrap();
657
658 cpu.stack_push(Value::HeapRef(0));
659 let err = cpu
660 .execute_instruction(Instruction::HeapDealloc)
661 .unwrap_err();
662
663 assert!(err.to_string().contains("double-free"));
664 }
665
666 #[test]
667 fn op_heap_dealloc_rejects_invalid_id() {
668 let mut cpu = CPU::default();
669 cpu.stack_push(Value::HeapRef(99));
670
671 let err = cpu
672 .execute_instruction(Instruction::HeapDealloc)
673 .unwrap_err();
674
675 assert!(err.to_string().contains("invalid heap object id"));
676 }
677
678 #[test]
679 fn reset_clears_free_list() {
680 let mut cpu = CPU::default();
681 cpu.stack_push(Value::I64(1));
682 cpu.execute_instruction(Instruction::HeapAlloc(1)).unwrap();
683 cpu.stack_push(Value::HeapRef(0));
684 cpu.execute_instruction(Instruction::HeapDealloc).unwrap();
685
686 cpu.reset();
687
688 assert!(cpu.heap.is_empty());
689 }
690}
691
692macro_rules! impl_op_num_binary {
693 ($name:ident, $op:ident) => {
694 pub fn $name(&mut self, ty: Type) -> Result<StepOutcome> {
695 let lhs: Value = self.stack_pop()?;
696 let rhs: Value = self.stack_pop()?;
697 if lhs.type_of() != ty {
698 return Err(eyre::eyre!(
699 "Type mismatch, expected {} got {} for lhs",
700 ty,
701 lhs.type_of()
702 ));
703 }
704
705 if rhs.type_of() != ty {
706 return Err(eyre::eyre!(
707 "Type mismatch, expected {} got {} for rhs",
708 ty,
709 rhs.type_of()
710 ));
711 }
712
713 let output = match (lhs, rhs) {
714 (Value::I64(l), Value::I64(r)) => Value::I64(l.$op(r)),
715 (Value::U32(l), Value::U32(r)) => Value::U32(l.$op(r)),
716 (Value::U64(l), Value::U64(r)) => Value::U64(l.$op(r)),
717 (Value::F64(l), Value::F64(r)) => Value::F64(l.$op(r)),
718 _ => {
719 return Err(eyre::eyre!(
720 "cannot {} {} and {}",
721 stringify!($op),
722 lhs.type_of(),
723 rhs.type_of()
724 ))
725 }
726 };
727 self.stack.push(output);
728 Ok(StepOutcome::Continue)
729 }
730 };
731}
732
733impl CPU {
734 impl_op_num_binary!(op_add, add);
735 impl_op_num_binary!(op_sub, sub);
736 impl_op_num_binary!(op_mul, mul);
737 impl_op_num_binary!(op_div, div);
738 impl_op_num_binary!(op_rem, rem);
739
740 pub fn op_neg(&mut self, ty: Type) -> Result<StepOutcome> {
741 let v: Value = self.stack_pop()?;
742 if v.type_of() != ty {
743 return Err(eyre::eyre!(format!(
744 "Type mismatch, expected {:?} got {:?}",
745 ty,
746 v.type_of()
747 )));
748 }
749
750 let output = match v {
751 Value::I64(i) => Value::I64(-i),
752 Value::F64(f) => Value::F64(-f),
753 _ => return Err(eyre::eyre!(format!("cannot negate {}", v.type_of()))),
754 };
755 self.stack.push(output);
756 Ok(StepOutcome::Continue)
757 }
758}
759
760macro_rules! impl_op_shift {
761 ($name:ident, $op:ident) => {
762 pub fn $name(&mut self, ty: Type) -> Result<StepOutcome> {
763 let rhs: Value = self.stack_pop()?;
764 let lhs: Value = self.stack_pop()?;
765 if lhs.type_of() != ty {
766 return Err(eyre::eyre!(
767 "Type mismatch, expected {} got {} for lhs",
768 ty,
769 lhs.type_of()
770 ));
771 }
772
773 if rhs.type_of() != ty {
774 return Err(eyre::eyre!(
775 "Type mismatch, expected {} got {} for rhs",
776 ty,
777 rhs.type_of()
778 ));
779 }
780 let output = match (lhs, rhs) {
781 (Value::I64(l), Value::I64(r)) => Value::I64(l.$op(r)),
782 (Value::U32(l), Value::U32(r)) => Value::U32(l.$op(r)),
783 (Value::U64(l), Value::U64(r)) => Value::U64(l.$op(r)),
784 _ => {
785 return Err(eyre::eyre!(format!(
786 "cannot {} {} and {}",
787 stringify!($op),
788 lhs.type_of(),
789 rhs.type_of()
790 )))
791 }
792 };
793 self.stack.push(output);
794 Ok(StepOutcome::Continue)
795 }
796 };
797}
798
799impl CPU {
800 impl_op_shift!(op_shl, shl);
801 impl_op_shift!(op_shr, shr);
802}
803
804macro_rules! impl_op_rotate {
805 ($name:ident, $op:ident) => {
806 pub fn $name(&mut self, ty: Type) -> Result<StepOutcome> {
807 let rhs: Value = self.stack_pop()?;
808 let lhs: Value = self.stack_pop()?;
809 if lhs.type_of() != ty {
810 return Err(eyre::eyre!(
811 "Type mismatch, expected {} got {} for lhs",
812 ty,
813 lhs.type_of()
814 ));
815 }
816
817 if rhs.type_of() != Type::U32 {
818 return Err(eyre::eyre!(
819 "Type mismatch, expected {} got {} for rhs",
820 Type::U32,
821 rhs.type_of()
822 ));
823 }
824 let output = match (lhs, rhs) {
825 (Value::I64(l), Value::U32(r)) => Value::I64(l.$op(r)),
826 (Value::U32(l), Value::U32(r)) => Value::U32(l.$op(r)),
827 (Value::U64(l), Value::U32(r)) => Value::U64(l.$op(r)),
828 _ => {
829 return Err(eyre::eyre!(format!(
830 "cannot {} {} and {}",
831 stringify!($op),
832 lhs.type_of(),
833 rhs.type_of()
834 )));
835 }
836 };
837 self.stack.push(output);
838 Ok(StepOutcome::Continue)
839 }
840 };
841}
842
843impl CPU {
844 impl_op_rotate!(op_rol, rotate_left);
845 impl_op_rotate!(op_ror, rotate_right);
846}
847
848macro_rules! impl_op_bitwise {
849 ($name:ident, $op:ident) => {
850 pub fn $name(&mut self, ty: Type) -> Result<StepOutcome> {
851 let rhs: Value = self.stack_pop()?;
852 let lhs: Value = self.stack_pop()?;
853 if lhs.type_of() != ty {
854 return Err(eyre::eyre!(
855 "Type mismatch, expected {} got {} for lhs",
856 ty,
857 lhs.type_of()
858 ));
859 }
860
861 if rhs.type_of() != ty {
862 return Err(eyre::eyre!(
863 "Type mismatch, expected {} got {} for rhs",
864 ty,
865 rhs.type_of()
866 ));
867 }
868 let output = match (lhs, rhs) {
869 (Value::I64(l), Value::I64(r)) => Value::I64(l.$op(r)),
870 (Value::U32(l), Value::U32(r)) => Value::U32(l.$op(r)),
871 (Value::U64(l), Value::U64(r)) => Value::U64(l.$op(r)),
872 _ => {
873 return Err(eyre::eyre!(format!(
874 "cannot {} {} and {}",
875 stringify!($op),
876 lhs.type_of(),
877 rhs.type_of()
878 )))
879 }
880 };
881 self.stack.push(output);
882 Ok(StepOutcome::Continue)
883 }
884 };
885}
886
887impl CPU {
888 impl_op_bitwise!(op_bitand, bitand);
889 impl_op_bitwise!(op_bitor, bitor);
890 impl_op_bitwise!(op_bitxor, bitxor);
891}
892
893macro_rules! impl_boolean_binary {
894 ($name:ident, $op:ident) => {
895 pub fn $name(&mut self) -> Result<StepOutcome> {
896 let rhs: bool = self.stack_pop()?.try_into()?;
897 let lhs: bool = self.stack_pop()?.try_into()?;
898 let output = lhs.$op(rhs);
899 self.stack_push(output);
900 Ok(StepOutcome::Continue)
901 }
902 };
903}
904
905impl CPU {
906 pub fn op_not(&mut self) -> Result<StepOutcome> {
907 let v: bool = self.stack_pop()?.try_into()?;
908 self.stack_push(!v);
909 Ok(StepOutcome::Continue)
910 }
911
912 impl_boolean_binary!(op_and, bitand);
913 impl_boolean_binary!(op_or, bitor);
914 impl_boolean_binary!(op_xor, bitxor);
915}
916
917macro_rules! impl_eq {
918 ($name:ident, $op:ident) => {
919 pub fn $name(&mut self, ty: Type) -> Result<StepOutcome> {
920 let rhs: Value = self.stack_pop()?;
921 let lhs: Value = self.stack_pop()?;
922 if lhs.type_of() != ty {
923 return Err(eyre::eyre!(
924 "Type mismatch, expected {} got {} for lhs",
925 ty,
926 lhs.type_of()
927 ));
928 }
929
930 if rhs.type_of() != ty {
931 return Err(eyre::eyre!(
932 "Type mismatch, expected {} got {} for rhs",
933 ty,
934 rhs.type_of()
935 ));
936 }
937 let output = lhs.$op(&rhs);
938 self.stack_push(output);
939 Ok(StepOutcome::Continue)
940 }
941 };
942}
943
944impl CPU {
945 impl_eq!(op_eq, eq);
946 impl_eq!(op_ne, ne);
947}
948
949macro_rules! impl_ordering {
950 ($name:ident, $op:ident) => {
951 pub fn $name(&mut self, ty: Type) -> Result<StepOutcome> {
952 let rhs: Value = self.stack_pop()?;
953 let lhs: Value = self.stack_pop()?;
954 if lhs.type_of() != ty {
955 return Err(eyre::eyre!(
956 "Type mismatch, expected {} got {} for lhs",
957 ty,
958 lhs.type_of()
959 ));
960 }
961
962 if rhs.type_of() != ty {
963 return Err(eyre::eyre!(
964 "Type mismatch, expected {} got {} for rhs",
965 ty,
966 rhs.type_of()
967 ));
968 }
969
970 let output = match (lhs, rhs) {
971 (Value::Bool(l), Value::Bool(r)) => l.$op(&r),
972 (Value::I64(l), Value::I64(r)) => l.$op(&r),
973 (Value::U32(l), Value::U32(r)) => l.$op(&r),
974 (Value::U64(l), Value::U64(r)) => l.$op(&r),
975 (Value::F64(l), Value::F64(r)) => l.$op(&r),
976 _ => {
977 return Err(eyre::eyre!(format!(
978 "cannot compare {} and {}",
979 lhs.type_of(),
980 rhs.type_of()
981 )))
982 }
983 };
984 self.stack_push(output);
985 Ok(StepOutcome::Continue)
986 }
987 };
988}
989
990impl CPU {
991 impl_ordering!(op_lt, lt);
992 impl_ordering!(op_le, le);
993 impl_ordering!(op_gt, gt);
994 impl_ordering!(op_ge, ge);
995}