Skip to main content

vihaco_cpu/
component.rs

1// SPDX-FileCopyrightText: 2026 The vihaco Authors
2// SPDX-License-Identifier: MIT
3
4use 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        // Collect return values before truncating
157        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            // No more frames - program is returning
163            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        // simliar order to op_call but from the stack
192        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        // addr should be local to frame.
216        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        // Outer ("main") frame so the inner Return takes the Continue branch.
357        cpu.push_frame(Frame {
358            base: 0,
359            span: (0, 0, 0),
360            function: None,
361            ret_pc: 0,
362        });
363
364        // Caller would be executing `call 0, 100` at some PC; op_call sets
365        // pending_pc to the callee target.
366        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        // Callee returns immediately. pending_pc should be restored to the
371        // instruction after the call.
372        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        // IndirectCall pops (top → bottom): target, arity, FunctionRef.
391        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        // Outer frame so Return takes the Continue branch.
408        cpu.push_frame(Frame {
409            base: 0,
410            span: (0, 0, 0),
411            function: None,
412            ret_pc: 0,
413        });
414
415        // Simulate a callee frame holding [scratch_a, scratch_b, return_val]
416        // where only `return_val` (the top) should survive `ret 1`.
417        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)); // scratch — bottom of callee frame
424        cpu.stack_push(Value::I64(222)); // scratch — middle
425        cpu.stack_push(Value::I64(999)); // intended return value — top
426
427        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        // arity pushed first, then start_address
575        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}