1use 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"]
93 HeapAlloc(u32),
94
95 #[pattern = "'get_item"]
96 GetItem,
97 #[pattern = "'heap_dealloc"]
98 HeapDealloc,
99
100 #[pattern = "'heap_reserve"]
103 HeapReserve,
104
105 #[pattern = "'heap_push"]
107 HeapPush,
108
109 #[pattern = "'const_i32 $0"]
110 ConstI32(SurfaceValue => Word),
111 #[pattern = "'const_i64 $0"]
112 ConstI64(SurfaceValue => Word),
113 #[pattern = "'const_u32 $0"]
114 ConstU32(SurfaceValue => Word),
115 #[pattern = "'const_u64 $0"]
116 ConstU64(SurfaceValue => Word),
117 #[pattern = "'const_f32 $0"]
118 ConstF32(SurfaceValue => Word),
119 #[pattern = "'const_f64 $0"]
120 ConstF64(SurfaceValue => Word),
121 #[pattern = "'const_bool $0"]
122 ConstBool(SurfaceValue => Word),
123 #[pattern = "'const_string $0"]
124 ConstString(SurfaceValue => Word),
125 #[pattern = "'const_fn_ref $0"]
126 ConstFunctionRef(SurfaceValue => Word),
127 #[pattern = "'const_heap_ref $0"]
128 ConstHeapRef(SurfaceValue => Word),
129
130 #[pattern = "'add_i32"]
131 AddI32,
132 #[pattern = "'add_f32"]
133 AddF32,
134 #[pattern = "'add_i64"]
135 AddI64,
136 #[pattern = "'add_u32"]
137 AddU32,
138 #[pattern = "'add_u64"]
139 AddU64,
140 #[pattern = "'add_f64"]
141 AddF64,
142 #[pattern = "'sub_i32"]
143 SubI32,
144 #[pattern = "'sub_i64"]
145 SubI64,
146 #[pattern = "'sub_u32"]
147 SubU32,
148 #[pattern = "'sub_u64"]
149 SubU64,
150 #[pattern = "'sub_f32"]
151 SubF32,
152 #[pattern = "'sub_f64"]
153 SubF64,
154 #[pattern = "'mul_i32"]
155 MulI32,
156 #[pattern = "'mul_i64"]
157 MulI64,
158 #[pattern = "'mul_u32"]
159 MulU32,
160 #[pattern = "'mul_u64"]
161 MulU64,
162 #[pattern = "'mul_f32"]
163 MulF32,
164 #[pattern = "'mul_f64"]
165 MulF64,
166 #[pattern = "'div_i32"]
167 DivI32,
168 #[pattern = "'div_i64"]
169 DivI64,
170 #[pattern = "'div_u32"]
171 DivU32,
172 #[pattern = "'div_u64"]
173 DivU64,
174 #[pattern = "'div_f32"]
175 DivF32,
176 #[pattern = "'div_f64"]
177 DivF64,
178 #[pattern = "'rem_i32"]
179 RemI32,
180 #[pattern = "'rem_i64"]
181 RemI64,
182 #[pattern = "'rem_u32"]
183 RemU32,
184 #[pattern = "'rem_u64"]
185 RemU64,
186 #[pattern = "'rem_f32"]
187 RemF32,
188 #[pattern = "'rem_f64"]
189 RemF64,
190 #[pattern = "'neg_i32"]
191 NegI32,
192 #[pattern = "'neg_i64"]
193 NegI64,
194 #[pattern = "'neg_f32"]
195 NegF32,
196 #[pattern = "'neg_f64"]
197 NegF64,
198 #[pattern = "'shl_i32"]
199 ShlI32,
200 #[pattern = "'shl_i64"]
201 ShlI64,
202 #[pattern = "'shl_u32"]
203 ShlU32,
204 #[pattern = "'shl_u64"]
205 ShlU64,
206 #[pattern = "'shr_i32"]
207 ShrI32,
208 #[pattern = "'shr_i64"]
209 ShrI64,
210 #[pattern = "'shr_u32"]
211 ShrU32,
212 #[pattern = "'shr_u64"]
213 ShrU64,
214 #[pattern = "'rol_i32"]
215 RolI32,
216 #[pattern = "'rol_i64"]
217 RolI64,
218 #[pattern = "'rol_u32"]
219 RolU32,
220 #[pattern = "'rol_u64"]
221 RolU64,
222 #[pattern = "'ror_i32"]
223 RorI32,
224 #[pattern = "'ror_i64"]
225 RorI64,
226 #[pattern = "'ror_u32"]
227 RorU32,
228 #[pattern = "'ror_u64"]
229 RorU64,
230 #[pattern = "'bitand_i32"]
231 BitAndI32,
232 #[pattern = "'bitand_i64"]
233 BitAndI64,
234 #[pattern = "'bitand_u32"]
235 BitAndU32,
236 #[pattern = "'bitand_u64"]
237 BitAndU64,
238 #[pattern = "'bitor_i32"]
239 BitOrI32,
240 #[pattern = "'bitor_i64"]
241 BitOrI64,
242 #[pattern = "'bitor_u32"]
243 BitOrU32,
244 #[pattern = "'bitor_u64"]
245 BitOrU64,
246 #[pattern = "'bitxor_i32"]
247 BitXorI32,
248 #[pattern = "'bitxor_i64"]
249 BitXorI64,
250 #[pattern = "'bitxor_u32"]
251 BitXorU32,
252 #[pattern = "'bitxor_u64"]
253 BitXorU64,
254
255 Not,
256
257 And,
258
259 Or,
260
261 Xor,
262
263 #[pattern = "'eq_i32"]
264 EqI32,
265 #[pattern = "'eq_i64"]
266 EqI64,
267 #[pattern = "'eq_u32"]
268 EqU32,
269 #[pattern = "'eq_u64"]
270 EqU64,
271 #[pattern = "'eq_f32"]
272 EqF32,
273 #[pattern = "'eq_f64"]
274 EqF64,
275 #[pattern = "'ne_i32"]
276 NeI32,
277 #[pattern = "'ne_i64"]
278 NeI64,
279 #[pattern = "'ne_u32"]
280 NeU32,
281 #[pattern = "'ne_u64"]
282 NeU64,
283 #[pattern = "'ne_f32"]
284 NeF32,
285 #[pattern = "'ne_f64"]
286 NeF64,
287 #[pattern = "'lt_i32"]
288 LtI32,
289 #[pattern = "'lt_i64"]
290 LtI64,
291 #[pattern = "'lt_u32"]
292 LtU32,
293 #[pattern = "'lt_u64"]
294 LtU64,
295 #[pattern = "'lt_f32"]
296 LtF32,
297 #[pattern = "'lt_f64"]
298 LtF64,
299 #[pattern = "'gt_i32"]
300 GtI32,
301 #[pattern = "'gt_i64"]
302 GtI64,
303 #[pattern = "'gt_u32"]
304 GtU32,
305 #[pattern = "'gt_u64"]
306 GtU64,
307 #[pattern = "'gt_f32"]
308 GtF32,
309 #[pattern = "'gt_f64"]
310 GtF64,
311 #[pattern = "'le_i32"]
312 LeI32,
313 #[pattern = "'le_i64"]
314 LeI64,
315 #[pattern = "'le_u32"]
316 LeU32,
317 #[pattern = "'le_u64"]
318 LeU64,
319 #[pattern = "'le_f32"]
320 LeF32,
321 #[pattern = "'le_f64"]
322 LeF64,
323 #[pattern = "'ge_i32"]
324 GeI32,
325 #[pattern = "'ge_i64"]
326 GeI64,
327 #[pattern = "'ge_u32"]
328 GeU32,
329 #[pattern = "'ge_u64"]
330 GeU64,
331 #[pattern = "'ge_f32"]
332 GeF32,
333 #[pattern = "'ge_f64"]
334 GeF64,
335
336 #[pattern = "'heap_len"]
338 HeapLen,
339 }
340}
341
342pub use cpu::CPU;
343pub use cpu::runtime::Instruction as RuntimeInstruction;
344pub use cpu::syntax::Instruction as SurfaceInstruction;
345
346#[derive(Debug, Default)]
351struct HeapObject {
352 values: Vec<Word>,
353 capacity: usize,
354}
355
356impl Clone for HeapObject {
357 fn clone(&self) -> Self {
358 let mut values = Vec::with_capacity(self.capacity);
361 values.extend_from_slice(&self.values);
362 Self {
363 values,
364 capacity: self.capacity,
365 }
366 }
367}
368
369#[derive(Debug, Clone)]
370enum HeapSlot {
371 Alive(HeapObject),
372 Deallocated,
373}
374
375#[derive(Debug, Clone, Default)]
376pub struct Heap {
377 slots: Vec<HeapSlot>,
378 free_list: Vec<u32>,
379}
380
381impl Heap {
382 pub fn alloc(&mut self, values: impl Into<Vec<Word>>) -> u32 {
384 let values = values.into();
385 let capacity = values.len();
386 self.insert(HeapObject { values, capacity })
387 }
388
389 pub fn reserve(&mut self, capacity: usize) -> eyre::Result<u32> {
392 let mut values = Vec::new();
393 values.try_reserve_exact(capacity)?;
394 Ok(self.insert(HeapObject { values, capacity }))
395 }
396
397 pub fn push(&mut self, id: u32, value: Word) -> eyre::Result<()> {
400 let object = match self.slots.get_mut(id as usize) {
401 Some(HeapSlot::Alive(object)) => object,
402 Some(HeapSlot::Deallocated) => {
403 return Err(eyre::eyre!("heap object {} has been deallocated", id));
404 }
405 None => return Err(eyre::eyre!("invalid heap object id {}", id)),
406 };
407 eyre::ensure!(
408 object.values.len() < object.capacity,
409 "heap object {} is full (capacity {})",
410 id,
411 object.capacity
412 );
413 object.values.push(value);
414 Ok(())
415 }
416
417 fn insert(&mut self, object: HeapObject) -> u32 {
418 if let Some(id) = self.free_list.pop() {
419 self.slots[id as usize] = HeapSlot::Alive(object);
420 id
421 } else {
422 let id = self.slots.len() as u32;
423 self.slots.push(HeapSlot::Alive(object));
424 id
425 }
426 }
427
428 pub fn dealloc(&mut self, id: u32) -> eyre::Result<()> {
429 match self.slots.get_mut(id as usize) {
430 Some(slot @ HeapSlot::Alive(_)) => {
431 *slot = HeapSlot::Deallocated;
432 self.free_list.push(id);
433 Ok(())
434 }
435 Some(HeapSlot::Deallocated) => Err(eyre::eyre!(
436 "double-free: heap object {} already deallocated",
437 id
438 )),
439 None => Err(eyre::eyre!("invalid heap object id {}", id)),
440 }
441 }
442
443 pub fn get(&self, id: u32) -> eyre::Result<&[Word]> {
444 match self.slots.get(id as usize) {
445 Some(HeapSlot::Alive(object)) => Ok(&object.values),
446 Some(HeapSlot::Deallocated) => {
447 Err(eyre::eyre!("heap object {} has been deallocated", id))
448 }
449 None => Err(eyre::eyre!("invalid heap object id {}", id)),
450 }
451 }
452
453 pub fn clear(&mut self) {
454 self.slots.clear();
455 self.free_list.clear();
456 }
457
458 #[cfg(test)]
459 pub fn is_empty(&self) -> bool {
460 self.slots.is_empty()
461 }
462}
463
464impl StackMemory for CPU {
465 type Value = Word;
466
467 fn stack(&self) -> &Vec<Self::Value> {
468 &self.stack
469 }
470
471 fn stack_mut(&mut self) -> &mut Vec<Self::Value> {
472 &mut self.stack
473 }
474
475 fn stack_is_empty(&self) -> bool {
476 self.stack.is_empty()
477 }
478
479 fn stack_len(&self) -> usize {
480 self.stack.len()
481 }
482
483 fn stack_get(&self, pos: usize) -> eyre::Result<&Self::Value> {
484 self.stack
485 .get(pos)
486 .ok_or_else(|| eyre::eyre!("stack underflow"))
487 }
488
489 fn stack_get_mut(&mut self, pos: usize) -> eyre::Result<&mut Self::Value> {
490 self.stack
491 .get_mut(pos)
492 .ok_or_else(|| eyre::eyre!("stack underflow"))
493 }
494
495 fn stack_pop(&mut self) -> eyre::Result<Self::Value> {
496 self.require_operands(1)?;
497 self.stack
498 .pop()
499 .ok_or_else(|| eyre::eyre!("stack underflow"))
500 }
501
502 fn stack_push<T: Into<Self::Value>>(&mut self, v: T) {
503 self.stack.push(v.into());
504 }
505
506 fn stack_top(&self) -> eyre::Result<&Self::Value> {
507 self.require_operands(1)?;
508 self.stack_get(self.stack.len() - 1)
509 }
510
511 fn stack_top_mut(&mut self) -> eyre::Result<&mut Self::Value> {
512 self.require_operands(1)?;
513 self.stack_get_mut(self.stack.len() - 1)
514 }
515}
516
517impl StackFrame for CPU {
518 fn get_frame(&self) -> eyre::Result<&Frame> {
519 self.frames
520 .last()
521 .ok_or_else(|| eyre::eyre!("no current frame"))
522 }
523
524 fn get_frame_mut(&mut self) -> eyre::Result<&mut Frame> {
525 self.frames
526 .last_mut()
527 .ok_or_else(|| eyre::eyre!("no current frame"))
528 }
529
530 fn push_frame(&mut self, frame: Frame) {
531 self.frames.push(frame);
532 }
533
534 fn pop_frame(&mut self) -> eyre::Result<Frame> {
535 self.frames
536 .pop()
537 .ok_or_else(|| eyre::eyre!("no frame to pop"))
538 }
539}
540
541impl FrameMemory for CPU {
542 fn frame_base(&self) -> eyre::Result<usize> {
543 self.get_frame().map(|f| f.base)
544 }
545
546 fn get_local(&self, index: usize) -> eyre::Result<&Self::Value> {
547 let address = self.local_address(index)?;
548 self.stack
549 .get(address)
550 .ok_or_else(|| eyre::eyre!("local index out of bounds"))
551 }
552
553 fn get_local_mut(&mut self, index: usize) -> eyre::Result<&mut Self::Value> {
554 let address = self.local_address(index)?;
555 self.stack
556 .get_mut(address)
557 .ok_or_else(|| eyre::eyre!("local index out of bounds"))
558 }
559}
560
561impl CPU {
562 pub fn operand_count(&self) -> usize {
565 let start = self.frames.last().map_or(0, Frame::operands_index);
566 self.stack.len().saturating_sub(start)
567 }
568
569 #[inline(always)]
570 pub(crate) fn require_operands(&self, count: usize) -> eyre::Result<()> {
571 eyre::ensure!(self.operand_count() >= count, "stack underflow");
572 Ok(())
573 }
574
575 pub(crate) fn ensure_local_count_is_at_least_arity(
576 &self,
577 arity: u32,
578 count: u32,
579 ) -> eyre::Result<()> {
580 eyre::ensure!(
581 count >= arity,
582 "local count includes arity, count must be at least arity"
583 );
584 Ok(())
585 }
586
587 pub(crate) fn local_address(&self, index: usize) -> eyre::Result<usize> {
588 let frame = self.get_frame()?;
589 eyre::ensure!(index < frame.local_count, "local index out of bounds");
590 frame
591 .base
592 .checked_add(index)
593 .ok_or_else(|| eyre::eyre!("local address overflow"))
594 }
595
596 pub fn push_heap_object(&mut self, values: impl Into<Vec<Word>>) -> u32 {
597 self.heap.alloc(values)
598 }
599
600 pub fn heap_object(&self, id: u32) -> eyre::Result<&[Word]> {
601 self.heap.get(id)
602 }
603
604 pub fn dealloc_heap_object(&mut self, id: u32) -> eyre::Result<()> {
605 self.heap.dealloc(id)
606 }
607
608 pub fn take_pending_pc(&mut self) -> Option<u32> {
609 self.pending_pc.take()
610 }
611
612 pub fn set_pending_pc(&mut self, pc: u32) {
613 self.pending_pc = Some(pc);
614 }
615
616 pub fn clear_pending_pc(&mut self) {
617 self.pending_pc = None;
618 }
619
620 pub fn set_current_pc(&mut self, pc: u32) {
621 self.current_pc = pc;
622 }
623
624 pub fn return_values(&self) -> &[Word] {
625 &self.return_values
626 }
627
628 pub fn set_return_values(&mut self, values: Vec<Word>) {
629 self.return_values = values;
630 }
631}
632
633#[cfg(test)]
634mod heap_tests {
635 use super::Heap;
636
637 #[test]
638 fn allocated_objects_are_full_even_with_spare_vec_capacity() {
639 let mut heap = Heap::default();
640 let mut values = Vec::with_capacity(8);
641 values.push(1);
642 let id = heap.alloc(values);
643 assert!(heap.push(id, 2).unwrap_err().to_string().contains("full"));
644 assert_eq!(heap.get(id).unwrap(), &[1]);
645 }
646
647 #[test]
648 fn cloned_heap_preserves_append_capacity_without_reallocation() {
649 let mut heap = Heap::default();
650 let id = heap.reserve(3).unwrap();
651 heap.push(id, 1).unwrap();
652 let mut cloned = heap.clone();
653 for heap in [&mut heap, &mut cloned] {
654 let storage = heap.get(id).unwrap().as_ptr();
655 heap.push(id, 2).unwrap();
656 heap.push(id, 3).unwrap();
657 assert_eq!(heap.get(id).unwrap().as_ptr(), storage);
658 assert_eq!(heap.get(id).unwrap(), &[1, 2, 3]);
659 assert!(heap.push(id, 4).is_err());
660 }
661 }
662}