Skip to main content

vihaco_cpu/
data.rs

1// SPDX-FileCopyrightText: 2026 The vihaco Authors
2// SPDX-License-Identifier: MIT
3
4use crate::{Word, instruction::SurfaceValue};
5use vihaco::{
6    frame::Frame,
7    traits::{FrameMemory, StackFrame, StackMemory},
8};
9use vihaco_parser::Ident;
10
11vihaco::component! {
12    #[derive(Default, Debug)]
13    pub component CPU {
14        pub(crate) frames: Vec<Frame>,
15        pub(crate) heap: Heap,
16        pub(crate) stack: Vec<Word>,
17        pub(crate) span: (u32, u32, u32),
18        pub(crate) pending_pc: Option<u32>,
19        pub(crate) current_pc: u32,
20        pub(crate) return_values: Vec<Word>,
21    }
22
23    type Type = vihaco::Type;
24    value Word = crate::Word;
25
26    instruction {
27        #[pattern = "'span $0 $1 $2"]
28        Span(u32, u32, u32),
29
30        #[pattern = "'label `@` $0"]
31        Label(Ident),
32
33        #[pattern = "'func_start"]
34        FunctionStart,
35
36        #[pattern = "'func_end"]
37        FunctionEnd,
38
39        Breakpoint,
40
41        #[pattern = "'br `@` $0"]
42        Branch(Ident => u32),
43
44        #[pattern = "'cond_br `@` $0 `,` `@` $1"]
45        ConditionalBranch(Ident => u32, Ident => u32),
46
47        #[pattern = "'ret $0"]
48        Return(u32),
49
50        #[pattern = "'call_indirect"]
51        IndirectCall,
52
53        Call(u32, Ident => u32),
54
55        Halt,
56
57        Print,
58
59        #[pattern = "'load_i32 $0"]
60        LoadI32(u32),
61        #[pattern = "'load_i64 $0"]
62        LoadI64(u32),
63        #[pattern = "'load_u32 $0"]
64        LoadU32(u32),
65        #[pattern = "'load_u64 $0"]
66        LoadU64(u32),
67        #[pattern = "'load_f32 $0"]
68        LoadF32(u32),
69        #[pattern = "'load_f64 $0"]
70        LoadF64(u32),
71        #[pattern = "'load_bool $0"]
72        LoadBool(u32),
73        #[pattern = "'store_i32 $0"]
74        StoreI32(u32),
75        #[pattern = "'store_i64 $0"]
76        StoreI64(u32),
77        #[pattern = "'store_u32 $0"]
78        StoreU32(u32),
79        #[pattern = "'store_u64 $0"]
80        StoreU64(u32),
81        #[pattern = "'store_f32 $0"]
82        StoreF32(u32),
83        #[pattern = "'store_f64 $0"]
84        StoreF64(u32),
85        #[pattern = "'store_bool $0"]
86        StoreBool(u32),
87
88        Dup,
89
90        #[pattern = "'heap_alloc $0"]
91        HeapAlloc(u32),
92
93        #[pattern = "'get_item"]
94        GetItem,
95
96        #[pattern = "'heap_dealloc"]
97        HeapDealloc,
98
99        #[pattern = "'const_i32 $0"]
100        ConstI32(SurfaceValue => Word),
101        #[pattern = "'const_i64 $0"]
102        ConstI64(SurfaceValue => Word),
103        #[pattern = "'const_u32 $0"]
104        ConstU32(SurfaceValue => Word),
105        #[pattern = "'const_u64 $0"]
106        ConstU64(SurfaceValue => Word),
107        #[pattern = "'const_f32 $0"]
108        ConstF32(SurfaceValue => Word),
109        #[pattern = "'const_f64 $0"]
110        ConstF64(SurfaceValue => Word),
111        #[pattern = "'const_bool $0"]
112        ConstBool(SurfaceValue => Word),
113        #[pattern = "'const_string $0"]
114        ConstString(SurfaceValue => Word),
115        #[pattern = "'const_fn_ref $0"]
116        ConstFunctionRef(SurfaceValue => Word),
117        #[pattern = "'const_heap_ref $0"]
118        ConstHeapRef(SurfaceValue => Word),
119
120        #[pattern = "'add_i32"]
121        AddI32,
122        #[pattern = "'add_f32"]
123        AddF32,
124        #[pattern = "'add_i64"]
125        AddI64,
126        #[pattern = "'add_u32"]
127        AddU32,
128        #[pattern = "'add_u64"]
129        AddU64,
130        #[pattern = "'add_f64"]
131        AddF64,
132        #[pattern = "'sub_i32"]
133        SubI32,
134        #[pattern = "'sub_i64"]
135        SubI64,
136        #[pattern = "'sub_u32"]
137        SubU32,
138        #[pattern = "'sub_u64"]
139        SubU64,
140        #[pattern = "'sub_f32"]
141        SubF32,
142        #[pattern = "'sub_f64"]
143        SubF64,
144        #[pattern = "'mul_i32"]
145        MulI32,
146        #[pattern = "'mul_i64"]
147        MulI64,
148        #[pattern = "'mul_u32"]
149        MulU32,
150        #[pattern = "'mul_u64"]
151        MulU64,
152        #[pattern = "'mul_f32"]
153        MulF32,
154        #[pattern = "'mul_f64"]
155        MulF64,
156        #[pattern = "'div_i32"]
157        DivI32,
158        #[pattern = "'div_i64"]
159        DivI64,
160        #[pattern = "'div_u32"]
161        DivU32,
162        #[pattern = "'div_u64"]
163        DivU64,
164        #[pattern = "'div_f32"]
165        DivF32,
166        #[pattern = "'div_f64"]
167        DivF64,
168        #[pattern = "'rem_i32"]
169        RemI32,
170        #[pattern = "'rem_i64"]
171        RemI64,
172        #[pattern = "'rem_u32"]
173        RemU32,
174        #[pattern = "'rem_u64"]
175        RemU64,
176        #[pattern = "'rem_f32"]
177        RemF32,
178        #[pattern = "'rem_f64"]
179        RemF64,
180        #[pattern = "'neg_i32"]
181        NegI32,
182        #[pattern = "'neg_i64"]
183        NegI64,
184        #[pattern = "'neg_f32"]
185        NegF32,
186        #[pattern = "'neg_f64"]
187        NegF64,
188        #[pattern = "'shl_i32"]
189        ShlI32,
190        #[pattern = "'shl_i64"]
191        ShlI64,
192        #[pattern = "'shl_u32"]
193        ShlU32,
194        #[pattern = "'shl_u64"]
195        ShlU64,
196        #[pattern = "'shr_i32"]
197        ShrI32,
198        #[pattern = "'shr_i64"]
199        ShrI64,
200        #[pattern = "'shr_u32"]
201        ShrU32,
202        #[pattern = "'shr_u64"]
203        ShrU64,
204        #[pattern = "'rol_i32"]
205        RolI32,
206        #[pattern = "'rol_i64"]
207        RolI64,
208        #[pattern = "'rol_u32"]
209        RolU32,
210        #[pattern = "'rol_u64"]
211        RolU64,
212        #[pattern = "'ror_i32"]
213        RorI32,
214        #[pattern = "'ror_i64"]
215        RorI64,
216        #[pattern = "'ror_u32"]
217        RorU32,
218        #[pattern = "'ror_u64"]
219        RorU64,
220        #[pattern = "'bitand_i32"]
221        BitAndI32,
222        #[pattern = "'bitand_i64"]
223        BitAndI64,
224        #[pattern = "'bitand_u32"]
225        BitAndU32,
226        #[pattern = "'bitand_u64"]
227        BitAndU64,
228        #[pattern = "'bitor_i32"]
229        BitOrI32,
230        #[pattern = "'bitor_i64"]
231        BitOrI64,
232        #[pattern = "'bitor_u32"]
233        BitOrU32,
234        #[pattern = "'bitor_u64"]
235        BitOrU64,
236        #[pattern = "'bitxor_i32"]
237        BitXorI32,
238        #[pattern = "'bitxor_i64"]
239        BitXorI64,
240        #[pattern = "'bitxor_u32"]
241        BitXorU32,
242        #[pattern = "'bitxor_u64"]
243        BitXorU64,
244
245        Not,
246
247        And,
248
249        Or,
250
251        Xor,
252
253        #[pattern = "'eq_i32"]
254        EqI32,
255        #[pattern = "'eq_i64"]
256        EqI64,
257        #[pattern = "'eq_u32"]
258        EqU32,
259        #[pattern = "'eq_u64"]
260        EqU64,
261        #[pattern = "'eq_f32"]
262        EqF32,
263        #[pattern = "'eq_f64"]
264        EqF64,
265        #[pattern = "'ne_i32"]
266        NeI32,
267        #[pattern = "'ne_i64"]
268        NeI64,
269        #[pattern = "'ne_u32"]
270        NeU32,
271        #[pattern = "'ne_u64"]
272        NeU64,
273        #[pattern = "'ne_f32"]
274        NeF32,
275        #[pattern = "'ne_f64"]
276        NeF64,
277        #[pattern = "'lt_i32"]
278        LtI32,
279        #[pattern = "'lt_i64"]
280        LtI64,
281        #[pattern = "'lt_u32"]
282        LtU32,
283        #[pattern = "'lt_u64"]
284        LtU64,
285        #[pattern = "'lt_f32"]
286        LtF32,
287        #[pattern = "'lt_f64"]
288        LtF64,
289        #[pattern = "'gt_i32"]
290        GtI32,
291        #[pattern = "'gt_i64"]
292        GtI64,
293        #[pattern = "'gt_u32"]
294        GtU32,
295        #[pattern = "'gt_u64"]
296        GtU64,
297        #[pattern = "'gt_f32"]
298        GtF32,
299        #[pattern = "'gt_f64"]
300        GtF64,
301        #[pattern = "'le_i32"]
302        LeI32,
303        #[pattern = "'le_i64"]
304        LeI64,
305        #[pattern = "'le_u32"]
306        LeU32,
307        #[pattern = "'le_u64"]
308        LeU64,
309        #[pattern = "'le_f32"]
310        LeF32,
311        #[pattern = "'le_f64"]
312        LeF64,
313        #[pattern = "'ge_i32"]
314        GeI32,
315        #[pattern = "'ge_i64"]
316        GeI64,
317        #[pattern = "'ge_u32"]
318        GeU32,
319        #[pattern = "'ge_u64"]
320        GeU64,
321        #[pattern = "'ge_f32"]
322        GeF32,
323        #[pattern = "'ge_f64"]
324        GeF64,
325    }
326}
327
328pub use cpu::CPU;
329pub use cpu::runtime::Instruction as RuntimeInstruction;
330pub use cpu::syntax::Instruction as SurfaceInstruction;
331
332type HeapSlot = Option<Box<[Word]>>;
333
334#[derive(Debug, Clone, Default)]
335pub struct Heap {
336    slots: Vec<HeapSlot>,
337    free_list: Vec<u32>,
338}
339
340impl Heap {
341    pub fn alloc(&mut self, values: Box<[Word]>) -> u32 {
342        if let Some(id) = self.free_list.pop() {
343            self.slots[id as usize] = Some(values);
344            id
345        } else {
346            let id = self.slots.len() as u32;
347            self.slots.push(Some(values));
348            id
349        }
350    }
351
352    pub fn dealloc(&mut self, id: u32) -> eyre::Result<()> {
353        match self.slots.get_mut(id as usize) {
354            Some(slot @ Some(_)) => {
355                *slot = None;
356                self.free_list.push(id);
357                Ok(())
358            }
359            Some(None) => Err(eyre::eyre!(
360                "double-free: heap object {} already deallocated",
361                id
362            )),
363            None => Err(eyre::eyre!("invalid heap object id {}", id)),
364        }
365    }
366
367    pub fn get(&self, id: u32) -> eyre::Result<&[Word]> {
368        match self.slots.get(id as usize) {
369            Some(Some(v)) => Ok(v),
370            Some(None) => Err(eyre::eyre!("heap object {} has been deallocated", id)),
371            None => Err(eyre::eyre!("invalid heap object id {}", id)),
372        }
373    }
374
375    pub fn clear(&mut self) {
376        self.slots.clear();
377        self.free_list.clear();
378    }
379
380    #[cfg(test)]
381    pub fn is_empty(&self) -> bool {
382        self.slots.is_empty()
383    }
384}
385
386impl StackMemory for CPU {
387    type Value = Word;
388
389    fn stack(&self) -> &Vec<Self::Value> {
390        &self.stack
391    }
392
393    fn stack_mut(&mut self) -> &mut Vec<Self::Value> {
394        &mut self.stack
395    }
396
397    fn stack_is_empty(&self) -> bool {
398        self.stack.is_empty()
399    }
400
401    fn stack_len(&self) -> usize {
402        self.stack.len()
403    }
404
405    fn stack_get(&self, pos: usize) -> eyre::Result<&Self::Value> {
406        self.stack
407            .get(pos)
408            .ok_or_else(|| eyre::eyre!("stack underflow"))
409    }
410
411    fn stack_get_mut(&mut self, pos: usize) -> eyre::Result<&mut Self::Value> {
412        self.stack
413            .get_mut(pos)
414            .ok_or_else(|| eyre::eyre!("stack underflow"))
415    }
416
417    fn stack_pop(&mut self) -> eyre::Result<Self::Value> {
418        self.require_operands(1)?;
419        self.stack
420            .pop()
421            .ok_or_else(|| eyre::eyre!("stack underflow"))
422    }
423
424    fn stack_push<T: Into<Self::Value>>(&mut self, v: T) {
425        self.stack.push(v.into());
426    }
427
428    fn stack_top(&self) -> eyre::Result<&Self::Value> {
429        self.require_operands(1)?;
430        self.stack_get(self.stack.len() - 1)
431    }
432
433    fn stack_top_mut(&mut self) -> eyre::Result<&mut Self::Value> {
434        self.require_operands(1)?;
435        self.stack_get_mut(self.stack.len() - 1)
436    }
437}
438
439impl StackFrame for CPU {
440    fn get_frame(&self) -> eyre::Result<&Frame> {
441        self.frames
442            .last()
443            .ok_or_else(|| eyre::eyre!("no current frame"))
444    }
445
446    fn get_frame_mut(&mut self) -> eyre::Result<&mut Frame> {
447        self.frames
448            .last_mut()
449            .ok_or_else(|| eyre::eyre!("no current frame"))
450    }
451
452    fn push_frame(&mut self, frame: Frame) {
453        self.frames.push(frame);
454    }
455
456    fn pop_frame(&mut self) -> eyre::Result<Frame> {
457        self.frames
458            .pop()
459            .ok_or_else(|| eyre::eyre!("no frame to pop"))
460    }
461}
462
463impl FrameMemory for CPU {
464    fn frame_base(&self) -> eyre::Result<usize> {
465        self.get_frame().map(|f| f.base)
466    }
467
468    fn get_local(&self, index: usize) -> eyre::Result<&Self::Value> {
469        let address = self.local_address(index)?;
470        self.stack
471            .get(address)
472            .ok_or_else(|| eyre::eyre!("local index out of bounds"))
473    }
474
475    fn get_local_mut(&mut self, index: usize) -> eyre::Result<&mut Self::Value> {
476        let address = self.local_address(index)?;
477        self.stack
478            .get_mut(address)
479            .ok_or_else(|| eyre::eyre!("local index out of bounds"))
480    }
481}
482
483impl CPU {
484    /// Number of operands above the current frame's reserved locals.
485    /// Before entry, all stack values are available as operands/arguments.
486    pub fn operand_count(&self) -> usize {
487        let start = self.frames.last().map_or(0, Frame::operands_index);
488        self.stack.len().saturating_sub(start)
489    }
490
491    #[inline(always)]
492    pub(crate) fn require_operands(&self, count: usize) -> eyre::Result<()> {
493        eyre::ensure!(self.operand_count() >= count, "stack underflow");
494        Ok(())
495    }
496
497    pub(crate) fn ensure_local_count_is_at_least_arity(
498        &self,
499        arity: u32,
500        count: u32,
501    ) -> eyre::Result<()> {
502        eyre::ensure!(
503            count >= arity,
504            "local count includes arity, count must be at least arity"
505        );
506        Ok(())
507    }
508
509    pub(crate) fn local_address(&self, index: usize) -> eyre::Result<usize> {
510        let frame = self.get_frame()?;
511        eyre::ensure!(index < frame.local_count, "local index out of bounds");
512        frame
513            .base
514            .checked_add(index)
515            .ok_or_else(|| eyre::eyre!("local address overflow"))
516    }
517
518    pub fn push_heap_object(&mut self, values: Box<[Word]>) -> u32 {
519        self.heap.alloc(values)
520    }
521
522    pub fn heap_object(&self, id: u32) -> eyre::Result<&[Word]> {
523        self.heap.get(id)
524    }
525
526    pub fn dealloc_heap_object(&mut self, id: u32) -> eyre::Result<()> {
527        self.heap.dealloc(id)
528    }
529
530    pub fn take_pending_pc(&mut self) -> Option<u32> {
531        self.pending_pc.take()
532    }
533
534    pub fn set_pending_pc(&mut self, pc: u32) {
535        self.pending_pc = Some(pc);
536    }
537
538    pub fn clear_pending_pc(&mut self) {
539        self.pending_pc = None;
540    }
541
542    pub fn set_current_pc(&mut self, pc: u32) {
543        self.current_pc = pc;
544    }
545
546    pub fn return_values(&self) -> &[Word] {
547        &self.return_values
548    }
549
550    pub fn set_return_values(&mut self, values: Vec<Word>) {
551        self.return_values = values;
552    }
553}