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.stack
419            .pop()
420            .ok_or_else(|| eyre::eyre!("stack underflow"))
421    }
422
423    fn stack_push<T: Into<Self::Value>>(&mut self, v: T) {
424        self.stack.push(v.into());
425    }
426}
427
428impl StackFrame for CPU {
429    fn get_frame(&self) -> eyre::Result<&Frame> {
430        self.frames
431            .last()
432            .ok_or_else(|| eyre::eyre!("no current frame"))
433    }
434
435    fn get_frame_mut(&mut self) -> eyre::Result<&mut Frame> {
436        self.frames
437            .last_mut()
438            .ok_or_else(|| eyre::eyre!("no current frame"))
439    }
440
441    fn push_frame(&mut self, frame: Frame) {
442        self.frames.push(frame);
443    }
444
445    fn pop_frame(&mut self) -> eyre::Result<Frame> {
446        self.frames
447            .pop()
448            .ok_or_else(|| eyre::eyre!("no frame to pop"))
449    }
450}
451
452impl FrameMemory for CPU {
453    fn frame_base(&self) -> eyre::Result<usize> {
454        self.get_frame().map(|f| f.base)
455    }
456
457    fn get_local(&self, index: usize) -> eyre::Result<&Self::Value> {
458        let base = self.frame_base()?;
459        self.stack
460            .get(base + index)
461            .ok_or_else(|| eyre::eyre!("local index out of bounds"))
462    }
463
464    // TODO, this is wrong to extend the vector, because the stack is push from back, so it should be reversed
465    fn get_local_mut(&mut self, index: usize) -> eyre::Result<&mut Self::Value> {
466        let base = self.frame_base()?;
467        let idx = base + index;
468        let len = self.stack.len();
469
470        if len <= idx {
471            self.stack.resize(idx + 1, 0);
472        }
473        self.stack
474            .get_mut(idx)
475            .ok_or_else(|| eyre::eyre!("Invalid local address at {:?}, stack size: {:?}", idx, len))
476    }
477}
478
479impl CPU {
480    pub fn push_heap_object(&mut self, values: Box<[Word]>) -> u32 {
481        self.heap.alloc(values)
482    }
483
484    pub fn heap_object(&self, id: u32) -> eyre::Result<&[Word]> {
485        self.heap.get(id)
486    }
487
488    pub fn dealloc_heap_object(&mut self, id: u32) -> eyre::Result<()> {
489        self.heap.dealloc(id)
490    }
491
492    pub fn take_pending_pc(&mut self) -> Option<u32> {
493        self.pending_pc.take()
494    }
495
496    pub fn set_pending_pc(&mut self, pc: u32) {
497        self.pending_pc = Some(pc);
498    }
499
500    pub fn clear_pending_pc(&mut self) {
501        self.pending_pc = None;
502    }
503
504    pub fn set_current_pc(&mut self, pc: u32) {
505        self.current_pc = pc;
506    }
507
508    pub fn return_values(&self) -> &[Word] {
509        &self.return_values
510    }
511
512    pub fn set_return_values(&mut self, values: Vec<Word>) {
513        self.return_values = values;
514    }
515}