1use crate::RuntimeInstruction;
5use crate::StepOutcome;
6use crate::Word;
7use crate::data::CPU;
8use crate::word::*;
9use eyre::Result;
10use vihaco::Effects;
11use vihaco::{dispatch, frame::Frame, traits::*};
12
13impl Reset for CPU {
14 fn reset(&mut self) {
15 self.frames.clear();
16 self.heap.clear();
17 self.stack.clear();
18 self.span = (0, 0, 0);
19 self.pending_pc = None;
20 self.current_pc = 0;
21 self.return_values.clear();
22 }
23}
24
25impl CPU {
26 #[inline(always)]
27 fn execute_generated(
28 &mut self,
29 inst: &RuntimeInstruction,
30 msg: CPUMessage,
31 ) -> eyre::Result<Effects<StepOutcome>> {
32 use RuntimeInstruction::*;
33
34 self.clear_pending_pc();
35 match (inst, msg) {
36 (Print, CPUMessage::Print(text)) => {
37 self.stack_pop()?;
38 drop(text);
39 return Ok(Effects::one(StepOutcome::Continue));
40 }
41 (Print, _) => return Err(eyre::eyre!("Print requires CPUMessage::Print")),
42 (_, CPUMessage::Print(_)) => {
43 return Err(eyre::eyre!(
44 "CPUMessage::Print is only valid for Print instruction"
45 ));
46 }
47 (Call(arity, target), CPUMessage::FunctionInfo { local_count, .. }) => {
48 return self.op_call(*arity, *target, local_count).map(Effects::one);
49 }
50 (
51 IndirectCall,
52 CPUMessage::FunctionInfo {
53 arity,
54 start_address,
55 local_count,
56 },
57 ) => {
58 return self
59 .op_indirect_call(arity, start_address, local_count)
60 .map(Effects::one);
61 }
62 (_, CPUMessage::FunctionInfo { .. }) => {
63 return Err(eyre::eyre!(
64 "CPUMessage::FunctionInfo is only valid for call instructions"
65 ));
66 }
67 (_, CPUMessage::None) => {}
68 }
69
70 let outcome = match inst {
71 Span(file, start, end) => self.op_span(*file, *start, *end),
72 Label(_) | FunctionStart | FunctionEnd => Ok(StepOutcome::Continue),
73 Breakpoint => Ok(StepOutcome::Breakpoint),
74 Branch(target) => self.op_branch(*target),
75 ConditionalBranch(true_target, false_target) => {
76 self.op_conditional_branch(*true_target, *false_target)
77 }
78 Return(keep) => self.op_return(*keep),
79 Call(..) | IndirectCall => Err(eyre::eyre!("call requires CPUMessage::FunctionInfo")),
80 Halt => Ok(StepOutcome::Halt),
81 Print => Err(eyre::eyre!(
82 "Print must be handled via execute with CPUMessage::Print"
83 )),
84 LoadI32(addr) => self.op_load(*addr),
85 LoadI64(addr) => self.op_load(*addr),
86 LoadU32(addr) => self.op_load(*addr),
87 LoadU64(addr) => self.op_load(*addr),
88 LoadF32(addr) => self.op_load(*addr),
89 LoadF64(addr) => self.op_load(*addr),
90 LoadBool(addr) => self.op_load(*addr),
91 StoreI32(addr) => self.op_store(*addr),
92 StoreI64(addr) => self.op_store(*addr),
93 StoreU32(addr) => self.op_store(*addr),
94 StoreU64(addr) => self.op_store(*addr),
95 StoreF32(addr) => self.op_store(*addr),
96 StoreF64(addr) => self.op_store(*addr),
97 StoreBool(addr) => self.op_store(*addr),
98 Dup => self.op_dup(),
99 HeapAlloc(n_elements) => self.op_heap_alloc(*n_elements),
100 GetItem => self.op_get_item(),
101 HeapDealloc => self.op_heap_dealloc(),
102 HeapReserve => self.op_heap_reserve(),
103 HeapPush => self.op_heap_push(),
104 HeapLen => self.op_heap_len(),
105 ConstI32(v) | ConstI64(v) | ConstU32(v) | ConstU64(v) | ConstF32(v) | ConstF64(v)
106 | ConstBool(v) | ConstString(v) | ConstFunctionRef(v) | ConstHeapRef(v) => {
107 self.op_const(*v)
108 }
109 AddI32 => self.add_i32(),
110 AddI64 => self.add_i64(),
111 AddU32 => self.add_u32(),
112 AddU64 => self.add_u64(),
113 AddF32 => self.add_f32(),
114 AddF64 => self.add_f64(),
115 SubI32 => self.sub_i32(),
116 SubI64 => self.sub_i64(),
117 SubU32 => self.sub_u32(),
118 SubU64 => self.sub_u64(),
119 SubF32 => self.sub_f32(),
120 SubF64 => self.sub_f64(),
121 MulI32 => self.mul_i32(),
122 MulI64 => self.mul_i64(),
123 MulU32 => self.mul_u32(),
124 MulU64 => self.mul_u64(),
125 MulF32 => self.mul_f32(),
126 MulF64 => self.mul_f64(),
127 DivI32 => self.div_i32(),
128 DivI64 => self.div_i64(),
129 DivU32 => self.div_u32(),
130 DivU64 => self.div_u64(),
131 DivF32 => self.div_f32(),
132 DivF64 => self.div_f64(),
133 RemI32 => self.rem_i32(),
134 RemI64 => self.rem_i64(),
135 RemU32 => self.rem_u32(),
136 RemU64 => self.rem_u64(),
137 RemF32 => self.rem_f32(),
138 RemF64 => self.rem_f64(),
139 NegI32 => self.neg_i32(),
140 NegI64 => self.neg_i64(),
141 NegF32 => self.neg_f32(),
142 NegF64 => self.neg_f64(),
143 ShlI32 => self.shl_i32(),
144 ShlI64 => self.shl_i64(),
145 ShlU32 => self.shl_u32(),
146 ShlU64 => self.shl_u64(),
147 ShrI32 => self.shr_i32(),
148 ShrI64 => self.shr_i64(),
149 ShrU32 => self.shr_u32(),
150 ShrU64 => self.shr_u64(),
151 RolI32 => self.rol_i32(),
152 RolI64 => self.rol_i64(),
153 RolU32 => self.rol_u32(),
154 RolU64 => self.rol_u64(),
155 RorI32 => self.ror_i32(),
156 RorI64 => self.ror_i64(),
157 RorU32 => self.ror_u32(),
158 RorU64 => self.ror_u64(),
159 BitAndI32 => self.bitand_i32(),
160 BitAndI64 => self.bitand_i64(),
161 BitAndU32 => self.bitand_u32(),
162 BitAndU64 => self.bitand_u64(),
163 BitOrI32 => self.bitor_i32(),
164 BitOrI64 => self.bitor_i64(),
165 BitOrU32 => self.bitor_u32(),
166 BitOrU64 => self.bitor_u64(),
167 BitXorI32 => self.bitxor_i32(),
168 BitXorI64 => self.bitxor_i64(),
169 BitXorU32 => self.bitxor_u32(),
170 BitXorU64 => self.bitxor_u64(),
171 Not => self.op_not(),
172 And => self.op_and(),
173 Or => self.op_or(),
174 Xor => self.op_xor(),
175 EqI32 => self.eq_i32(),
176 EqI64 => self.eq_i64(),
177 EqU32 => self.eq_u32(),
178 EqU64 => self.eq_u64(),
179 EqF32 => self.eq_f32(),
180 EqF64 => self.eq_f64(),
181 NeI32 => self.ne_i32(),
182 NeI64 => self.ne_i64(),
183 NeU32 => self.ne_u32(),
184 NeU64 => self.ne_u64(),
185 NeF32 => self.ne_f32(),
186 NeF64 => self.ne_f64(),
187 LtI32 => self.lt_i32(),
188 LtI64 => self.lt_i64(),
189 LtU32 => self.lt_u32(),
190 LtU64 => self.lt_u64(),
191 LtF32 => self.lt_f32(),
192 LtF64 => self.lt_f64(),
193 GtI32 => self.gt_i32(),
194 GtI64 => self.gt_i64(),
195 GtU32 => self.gt_u32(),
196 GtU64 => self.gt_u64(),
197 GtF32 => self.gt_f32(),
198 GtF64 => self.gt_f64(),
199 LeI32 => self.le_i32(),
200 LeI64 => self.le_i64(),
201 LeU32 => self.le_u32(),
202 LeU64 => self.le_u64(),
203 LeF32 => self.le_f32(),
204 LeF64 => self.le_f64(),
205 GeI32 => self.ge_i32(),
206 GeI64 => self.ge_i64(),
207 GeU32 => self.ge_u32(),
208 GeU64 => self.ge_u64(),
209 GeF32 => self.ge_f32(),
210 GeF64 => self.ge_f64(),
211 }?;
212 Ok(Effects::one(outcome))
213 }
214}
215
216#[derive(Debug, Clone, PartialEq, vihaco::Message)]
217pub enum CPUMessage {
218 None,
219 FunctionInfo {
221 arity: u32,
222 start_address: u32,
223 local_count: u32,
224 },
225 Print(String),
226}
227
228#[dispatch(instruction = RuntimeInstruction, message = CPUMessage, effect = StepOutcome)]
229impl CPU {
230 fn execute(
231 &mut self,
232 inst: &RuntimeInstruction,
233 msg: CPUMessage,
234 ) -> eyre::Result<Effects<StepOutcome>> {
235 self.execute_generated(inst, msg)
236 }
237}
238
239impl CPU {
240 pub fn op_span(&mut self, file: u32, start: u32, end: u32) -> eyre::Result<StepOutcome> {
241 self.span = (file, start, end);
242 Ok(StepOutcome::Continue)
243 }
244
245 pub fn op_branch(&mut self, target: u32) -> eyre::Result<StepOutcome> {
246 self.set_pending_pc(target);
247 Ok(StepOutcome::Continue)
248 }
249
250 pub fn op_conditional_branch(
251 &mut self,
252 true_target: u32,
253 false_target: u32,
254 ) -> eyre::Result<StepOutcome> {
255 let cond = self.stack_pop()?;
256 match canonical_bool(cond)? {
257 true => {
258 self.set_pending_pc(true_target);
259 Ok(StepOutcome::Continue)
260 }
261 false => {
262 self.set_pending_pc(false_target);
263 Ok(StepOutcome::Continue)
264 }
265 }
266 }
267
268 pub fn op_return(&mut self, keep: u32) -> eyre::Result<StepOutcome> {
269 let frame = *self.get_frame()?;
270 let available = self
271 .stack
272 .len()
273 .checked_sub(frame.operands_index())
274 .ok_or_else(|| eyre::eyre!("frame locals out of bounds"))?;
275 if available < keep as usize {
276 return Err(eyre::eyre!("not enough values to return"));
277 }
278
279 self.pop_frame()?;
280 let top = self.stack.len() - keep as usize;
282 let return_values: Vec<Word> = self.stack[top..].to_vec();
283 self.stack.drain(frame.base..top);
284
285 if self.get_frame().is_err() {
286 self.set_return_values(return_values);
288 Ok(StepOutcome::Return)
289 } else {
290 self.set_pending_pc(frame.ret_pc);
291 Ok(StepOutcome::Continue)
292 }
293 }
294
295 pub fn enter_function(
305 &mut self,
306 arity: u32,
307 target: u32,
308 local_count: u32,
309 function: Option<usize>,
310 ) -> eyre::Result<StepOutcome> {
311 self.require_operands(arity as usize)?;
312 self.ensure_local_count_is_at_least_arity(arity, local_count)?;
313 let base = self.stack.len() - arity as usize;
314 let end = base
315 .checked_add(local_count as usize)
316 .ok_or_else(|| eyre::eyre!("frame size overflow"))?;
317 let ret_pc = if self.frames.is_empty() {
318 0
319 } else {
320 self.current_pc
321 .checked_add(1)
322 .ok_or_else(|| eyre::eyre!("return address overflow"))?
323 };
324 self.stack.resize(end, 0);
325 self.push_frame(Frame {
326 base,
327 local_count: local_count as usize,
328 span: self.span,
329 function,
330 ret_pc,
331 });
332 self.set_pending_pc(target);
333 Ok(StepOutcome::Continue)
334 }
335
336 pub fn op_call(
337 &mut self,
338 arity: u32,
339 target: u32,
340 local_count: u32,
341 ) -> eyre::Result<StepOutcome> {
342 self.enter_function(arity, target, local_count, None)
343 }
344
345 pub fn op_indirect_call(
346 &mut self,
347 arity: u32,
348 target: u32,
349 local_count: u32,
350 ) -> eyre::Result<StepOutcome> {
351 let required = (arity as usize)
353 .checked_add(1)
354 .ok_or_else(|| eyre::eyre!("argument count overflow"))?;
355 self.require_operands(required)?;
356 let function = decode_function_ref(*self.stack_top()?);
357 self.stack_pop()?;
358 self.enter_function(arity, target, local_count, Some(function as usize))
359 }
360
361 fn op_load(&mut self, addr: u32) -> eyre::Result<StepOutcome> {
362 let value = self.get_local(addr as usize)?;
364 self.stack_push(*value);
365 Ok(StepOutcome::Continue)
366 }
367
368 pub fn op_store(&mut self, addr: u32) -> Result<StepOutcome> {
369 let address = self.local_address(addr as usize)?;
370 let value: Word = self.stack_pop()?;
371 *self
372 .stack
373 .get_mut(address)
374 .ok_or_else(|| eyre::eyre!("local index out of bounds"))? = value;
375 Ok(StepOutcome::Continue)
376 }
377
378 pub fn op_dup(&mut self) -> Result<StepOutcome> {
379 let v = *self.stack_top()?;
380 self.stack.push(v);
381 Ok(StepOutcome::Continue)
382 }
383
384 pub fn op_heap_alloc(&mut self, n_elements: u32) -> Result<StepOutcome> {
385 let n: usize = n_elements as usize;
386 self.require_operands(n)?;
387 let start = self.stack.len() - n;
388 let values: Vec<Word> = self.stack.drain(start..).collect();
389 let heap_id = self.push_heap_object(values);
390 self.stack_push(encode_heap_ref(heap_id));
391 Ok(StepOutcome::Continue)
392 }
393
394 pub fn op_get_item(&mut self) -> Result<StepOutcome> {
395 let index = Self::heap_index(self.stack_pop()?)?;
396 let heap_id = decode_heap_ref(self.stack_pop()?);
397 let value = *self
398 .heap_object(heap_id)?
399 .get(index)
400 .ok_or_else(|| eyre::eyre!("heap index {} out of bounds", index))?;
401 self.stack_push(value);
402 Ok(StepOutcome::Continue)
403 }
404
405 pub fn op_heap_dealloc(&mut self) -> Result<StepOutcome> {
406 let id = decode_heap_ref(self.stack_pop()?);
407 self.dealloc_heap_object(id)?;
408 Ok(StepOutcome::Continue)
409 }
410
411 pub fn op_heap_reserve(&mut self) -> Result<StepOutcome> {
412 let capacity = decode_u64(self.stack_pop()?);
413 let capacity = usize::try_from(capacity)
414 .map_err(|_| eyre::eyre!("heap capacity {} does not fit in usize", capacity))?;
415 let heap_id = self.heap.reserve(capacity)?;
416 self.stack_push(encode_heap_ref(heap_id));
417 Ok(StepOutcome::Continue)
418 }
419
420 pub fn op_heap_push(&mut self) -> Result<StepOutcome> {
421 self.require_operands(2)?;
422 let value = self.stack_pop()?;
423 let heap_ref = self.stack_pop()?;
424 self.heap.push(decode_heap_ref(heap_ref), value)?;
425 self.stack_push(heap_ref);
426 Ok(StepOutcome::Continue)
427 }
428
429 pub fn op_heap_len(&mut self) -> Result<StepOutcome> {
430 let heap_id = decode_heap_ref(self.stack_pop()?);
431 let len = u64::try_from(self.heap_object(heap_id)?.len())?;
432 self.stack_push(encode_u64(len));
433 Ok(StepOutcome::Continue)
434 }
435
436 pub fn op_const(&mut self, v: Word) -> Result<StepOutcome> {
437 self.stack.push(v);
438 Ok(StepOutcome::Continue)
439 }
440
441 fn heap_index(value: Word) -> Result<usize> {
442 match decode_i64(value) {
443 index if index >= 0 => usize::try_from(index)
444 .map_err(|_| eyre::eyre!("heap index {} does not fit in usize", index)),
445 index => Err(eyre::eyre!(
446 "heap index must be non-negative, got {}",
447 index
448 )),
449 }
450 }
451}
452
453#[cfg(test)]
454#[allow(clippy::items_after_test_module)]
455mod tests {
456 use super::*;
457 use vihaco::{Effects, GeneratedComponent, frame::Frame, traits::StackMemory};
458 use vihaco_parser::Ident;
459
460 trait ExecuteInstruction {
461 fn execute_instruction(&mut self, instruction: RuntimeInstruction) -> Result<StepOutcome>;
462 }
463
464 impl ExecuteInstruction for CPU {
465 fn execute_instruction(&mut self, instruction: RuntimeInstruction) -> Result<StepOutcome> {
466 vihaco::expect_exactly_one_effect(GeneratedComponent::execute_generated(
467 self,
468 &instruction,
469 CPUMessage::None,
470 )?)
471 }
472 }
473
474 #[test]
475 fn cpu_generated_component_executes_instruction_without_message() {
476 let mut cpu = CPU::default();
477
478 GeneratedComponent::execute_generated(
479 &mut cpu,
480 &RuntimeInstruction::ConstI64(encode_i64(7)),
481 CPUMessage::None,
482 )
483 .unwrap();
484
485 assert_eq!(cpu.stack(), &vec![encode_i64(7)]);
486 }
487
488 #[test]
489 fn execute_instruction_applies_control_flow_without_action() {
490 let mut cpu = CPU::default();
491
492 let branch = cpu
493 .execute_instruction(RuntimeInstruction::Branch(9))
494 .unwrap();
495 assert_eq!(branch, StepOutcome::Continue);
496 assert_eq!(cpu.take_pending_pc(), Some(9));
497
498 let halt = cpu.execute_instruction(RuntimeInstruction::Halt).unwrap();
499 assert_eq!(halt, StepOutcome::Halt);
500 assert_eq!(cpu.take_pending_pc(), None);
501 }
502
503 #[test]
504 fn op_return_stores_terminal_values_in_runtime_state() {
505 let mut cpu = CPU::default();
506 cpu.push_frame(Frame {
507 base: 0,
508 local_count: 0,
509 span: (0, 0, 0),
510 function: None,
511 ret_pc: 0,
512 });
513 cpu.stack_push(encode_i64(7));
514
515 let outcome = cpu
516 .execute_instruction(RuntimeInstruction::Return(1))
517 .unwrap();
518
519 assert_eq!(outcome, StepOutcome::Return);
520 assert_eq!(cpu.return_values(), &[encode_i64(7)]);
521 }
522
523 #[test]
524 fn op_return_restores_callers_pc() {
525 let mut cpu = CPU {
526 current_pc: 10,
527 ..Default::default()
528 };
529 cpu.push_frame(Frame {
531 base: 0,
532 local_count: 0,
533 span: (0, 0, 0),
534 function: None,
535 ret_pc: 0,
536 });
537
538 GeneratedComponent::execute_generated(
541 &mut cpu,
542 &RuntimeInstruction::Call(0, 100),
543 CPUMessage::FunctionInfo {
544 arity: 0,
545 start_address: 100,
546 local_count: 0,
547 },
548 )
549 .unwrap();
550 assert_eq!(cpu.take_pending_pc(), Some(100));
551 assert_eq!(cpu.frames[1].ret_pc, 11);
552
553 let outcome = cpu
556 .execute_instruction(RuntimeInstruction::Return(0))
557 .unwrap();
558 assert_eq!(outcome, StepOutcome::Continue);
559 assert_eq!(cpu.take_pending_pc(), Some(11),);
560 }
561
562 #[test]
563 fn op_indirect_call_records_return_pc_after_call_site() {
564 let mut cpu = CPU {
565 current_pc: 10,
566 ..Default::default()
567 };
568 cpu.push_frame(Frame {
569 base: 0,
570 local_count: 0,
571 span: (0, 0, 0),
572 function: None,
573 ret_pc: 0,
574 });
575
576 cpu.stack_push(encode_function_ref(7));
578 GeneratedComponent::execute_generated(
579 &mut cpu,
580 &RuntimeInstruction::IndirectCall,
581 CPUMessage::FunctionInfo {
582 arity: 0,
583 start_address: 100,
584 local_count: 0,
585 },
586 )
587 .unwrap();
588 assert_eq!(cpu.take_pending_pc(), Some(100));
589 assert_eq!(cpu.frames[1].ret_pc, 11);
590
591 let outcome = cpu
592 .execute_instruction(RuntimeInstruction::Return(0))
593 .unwrap();
594 assert_eq!(outcome, StepOutcome::Continue);
595 assert_eq!(cpu.take_pending_pc(), Some(11));
596 }
597
598 #[test]
599 fn op_return_keeps_bottom_of_frame_when_callee_leaves_scratch() {
600 let mut cpu = CPU::default();
601 cpu.push_frame(Frame {
603 base: 0,
604 local_count: 0,
605 span: (0, 0, 0),
606 function: None,
607 ret_pc: 0,
608 });
609
610 cpu.push_frame(Frame {
613 base: 0,
614 local_count: 0,
615 span: (0, 0, 0),
616 function: None,
617 ret_pc: 0,
618 });
619 cpu.stack_push(encode_i64(111)); cpu.stack_push(encode_i64(222)); cpu.stack_push(encode_i64(999)); let outcome = cpu
624 .execute_instruction(RuntimeInstruction::Return(1))
625 .unwrap();
626 assert_eq!(outcome, StepOutcome::Continue);
627
628 assert_eq!(cpu.stack(), &vec![encode_i64(999)],);
629 }
630
631 #[test]
632 fn op_heap_alloc_preserves_natural_push_order_and_returns_heap_ref() {
633 let mut cpu = CPU::default();
634 cpu.stack_push(encode_i64(10));
635 cpu.stack_push(encode_i64(20));
636 cpu.stack_push(encode_i64(30));
637
638 let outcome = cpu
639 .execute_instruction(RuntimeInstruction::HeapAlloc(3))
640 .unwrap();
641
642 assert_eq!(outcome, StepOutcome::Continue);
643 assert_eq!(cpu.stack(), &vec![encode_heap_ref(0)]);
644 assert_eq!(
645 cpu.heap.get(0).unwrap(),
646 &[encode_i64(10), encode_i64(20), encode_i64(30)]
647 );
648 }
649
650 #[test]
651 fn op_heap_alloc_supports_empty_heap_objects() {
652 let mut cpu = CPU::default();
653
654 let outcome = cpu
655 .execute_instruction(RuntimeInstruction::HeapAlloc(0))
656 .unwrap();
657
658 assert_eq!(outcome, StepOutcome::Continue);
659 assert_eq!(cpu.stack(), &vec![encode_heap_ref(0)]);
660 assert_eq!(cpu.heap.get(0).unwrap(), &[] as &[Word]);
661 }
662
663 #[test]
664 fn reserved_heap_appends_in_order_and_returns_same_reference() {
665 let mut cpu = CPU::default();
666 cpu.stack_push(encode_u32(2));
667 assert_eq!(
668 cpu.execute_instruction(RuntimeInstruction::HeapReserve)
669 .unwrap(),
670 StepOutcome::Continue
671 );
672 let heap_ref = *cpu.stack_top().unwrap();
673 assert_eq!(cpu.heap_object(decode_heap_ref(heap_ref)).unwrap(), &[]);
674
675 for value in [10, 20] {
676 cpu.stack_push(encode_i64(value));
677 assert_eq!(
678 cpu.execute_instruction(RuntimeInstruction::HeapPush)
679 .unwrap(),
680 StepOutcome::Continue
681 );
682 assert_eq!(cpu.stack(), &[heap_ref]);
683 }
684 assert_eq!(
685 cpu.heap_object(decode_heap_ref(heap_ref)).unwrap(),
686 &[encode_i64(10), encode_i64(20)]
687 );
688
689 cpu.stack_push(encode_i64(30));
690 let err = cpu
691 .execute_instruction(RuntimeInstruction::HeapPush)
692 .unwrap_err();
693 assert!(err.to_string().contains("full"));
694 assert_eq!(
695 cpu.heap_object(decode_heap_ref(heap_ref)).unwrap(),
696 &[encode_i64(10), encode_i64(20)]
697 );
698
699 cpu.stack_push(heap_ref);
700 cpu.stack_push(encode_u32(1));
701 cpu.execute_instruction(RuntimeInstruction::GetItem)
702 .unwrap();
703 assert_eq!(cpu.stack(), &[encode_i64(20)]);
704 }
705
706 #[test]
707 fn heap_len_consumes_reference_and_preserves_other_stack_values() {
708 for values in [vec![], vec![encode_i64(10), encode_i64(20)]] {
709 let mut cpu = CPU::default();
710 let heap_id = cpu.push_heap_object(values.clone());
711 cpu.stack_push(encode_i64(42));
712 cpu.stack_push(encode_heap_ref(heap_id));
713
714 let outcome = cpu
715 .execute_instruction(RuntimeInstruction::HeapLen)
716 .unwrap();
717
718 assert_eq!(outcome, StepOutcome::Continue);
719 assert_eq!(
720 cpu.stack(),
721 &[encode_i64(42), encode_u64(values.len() as u64)]
722 );
723 assert_eq!(cpu.heap_object(heap_id).unwrap(), values);
724 }
725 }
726
727 #[test]
728 fn heap_len_reports_current_length_of_reserved_object() {
729 let mut cpu = CPU::default();
730 cpu.stack_push(encode_u64(3));
731 cpu.execute_instruction(RuntimeInstruction::HeapReserve)
732 .unwrap();
733 let heap_ref = *cpu.stack_top().unwrap();
734
735 cpu.execute_instruction(RuntimeInstruction::HeapLen)
736 .unwrap();
737 assert_eq!(cpu.stack_pop().unwrap(), encode_u64(0));
738
739 cpu.stack_push(heap_ref);
740 cpu.stack_push(encode_i64(10));
741 cpu.execute_instruction(RuntimeInstruction::HeapPush)
742 .unwrap();
743 cpu.execute_instruction(RuntimeInstruction::HeapLen)
744 .unwrap();
745 assert_eq!(cpu.stack(), &[encode_u64(1)]);
746 }
747
748 #[test]
749 fn heap_len_rejects_invalid_and_deallocated_objects() {
750 let mut cpu = CPU::default();
751 let heap_id = cpu.push_heap_object(vec![]);
752 cpu.dealloc_heap_object(heap_id).unwrap();
753 for (id, message) in [(heap_id, "deallocated"), (42, "invalid heap object id")] {
754 cpu.stack_push(encode_heap_ref(id));
755 let err = cpu
756 .execute_instruction(RuntimeInstruction::HeapLen)
757 .unwrap_err();
758 assert!(err.to_string().contains(message));
759 }
760 }
761
762 #[test]
763 fn heap_len_requires_operand() {
764 let mut cpu = CPU::default();
765 assert!(
766 cpu.execute_instruction(RuntimeInstruction::HeapLen)
767 .is_err()
768 );
769 }
770
771 #[test]
772 fn reserved_heap_get_item_checks_current_length() {
773 let mut cpu = CPU::default();
774 cpu.stack_push(encode_u64(3));
775 cpu.execute_instruction(RuntimeInstruction::HeapReserve)
776 .unwrap();
777 cpu.stack_push(encode_i64(10));
778 cpu.execute_instruction(RuntimeInstruction::HeapPush)
779 .unwrap();
780 cpu.stack_push(encode_u32(1));
781 let err = cpu
782 .execute_instruction(RuntimeInstruction::GetItem)
783 .unwrap_err();
784 assert!(err.to_string().contains("out of bounds"));
785 }
786
787 #[test]
788 fn heap_push_rejects_allocated_and_zero_capacity_objects() {
789 for instruction in [
790 RuntimeInstruction::HeapAlloc(1),
791 RuntimeInstruction::HeapReserve,
792 ] {
793 let mut cpu = CPU::default();
794 cpu.stack_push(encode_u64(0));
795 cpu.execute_instruction(instruction).unwrap();
796 cpu.stack_push(encode_i64(42));
797 let err = cpu
798 .execute_instruction(RuntimeInstruction::HeapPush)
799 .unwrap_err();
800 assert!(err.to_string().contains("full"));
801 }
802 }
803
804 #[test]
805 fn heap_push_rejects_invalid_and_deallocated_objects() {
806 let mut cpu = CPU::default();
807 cpu.stack_push(encode_u64(1));
808 cpu.execute_instruction(RuntimeInstruction::HeapReserve)
809 .unwrap();
810 cpu.execute_instruction(RuntimeInstruction::HeapDealloc)
811 .unwrap();
812 for (id, message) in [(0, "deallocated"), (42, "invalid heap object id")] {
813 cpu.stack_push(encode_heap_ref(id));
814 cpu.stack_push(encode_i64(1));
815 let err = cpu
816 .execute_instruction(RuntimeInstruction::HeapPush)
817 .unwrap_err();
818 assert!(err.to_string().contains(message));
819 }
820 cpu.stack_push(encode_u64(1));
821 cpu.execute_instruction(RuntimeInstruction::HeapReserve)
822 .unwrap();
823 assert_eq!(cpu.stack(), &[encode_heap_ref(0)]);
824 cpu.stack_push(encode_i64(7));
825 cpu.execute_instruction(RuntimeInstruction::HeapPush)
826 .unwrap();
827 assert_eq!(cpu.heap_object(0).unwrap(), &[encode_i64(7)]);
828 }
829
830 #[test]
831 fn heap_reserve_rejects_unrepresentable_allocation() {
832 let mut cpu = CPU::default();
833 cpu.stack_push(encode_u64(u64::MAX));
834 assert!(
835 cpu.execute_instruction(RuntimeInstruction::HeapReserve)
836 .is_err()
837 );
838 assert!(cpu.heap.is_empty());
839 }
840
841 #[test]
842 fn heap_append_instructions_require_operands() {
843 let mut cpu = CPU::default();
844 assert!(
845 cpu.execute_instruction(RuntimeInstruction::HeapReserve)
846 .is_err()
847 );
848 assert!(
849 cpu.execute_instruction(RuntimeInstruction::HeapPush)
850 .is_err()
851 );
852 cpu.stack_push(encode_heap_ref(0));
853 assert!(
854 cpu.execute_instruction(RuntimeInstruction::HeapPush)
855 .is_err()
856 );
857 assert_eq!(cpu.stack(), &[encode_heap_ref(0)]);
858 }
859
860 #[test]
861 fn op_get_item_reads_heap_value() {
862 let mut cpu = CPU::default();
863 cpu.stack_push(encode_i64(10));
864 cpu.stack_push(encode_i64(20));
865 cpu.stack_push(encode_i64(30));
866 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(3))
867 .unwrap();
868 cpu.stack_push(encode_u32(1));
869
870 let outcome = cpu
871 .execute_instruction(RuntimeInstruction::GetItem)
872 .unwrap();
873
874 assert_eq!(outcome, StepOutcome::Continue);
875 assert_eq!(cpu.stack(), &vec![encode_i64(20)]);
876 }
877
878 #[test]
879 fn op_get_item_rejects_non_heap_refs() {
880 let mut cpu = CPU::default();
881 cpu.stack_push(encode_i64(7));
882 cpu.stack_push(encode_u32(0));
883
884 let err = cpu
885 .execute_instruction(RuntimeInstruction::GetItem)
886 .unwrap_err();
887
888 assert!(err.to_string().contains("heap"));
889 }
890
891 #[test]
892 fn op_get_item_rejects_invalid_heap_ids() {
893 let mut cpu = CPU::default();
894 cpu.stack_push(encode_heap_ref(99));
895 cpu.stack_push(encode_u32(0));
896
897 let err = cpu
898 .execute_instruction(RuntimeInstruction::GetItem)
899 .unwrap_err();
900
901 assert!(err.to_string().contains("heap"));
902 }
903
904 #[test]
905 fn op_get_item_rejects_out_of_bounds_indices() {
906 let mut cpu = CPU::default();
907 cpu.stack_push(encode_i64(10));
908 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(1))
909 .unwrap();
910 cpu.stack_push(encode_u32(3));
911
912 let err = cpu
913 .execute_instruction(RuntimeInstruction::GetItem)
914 .unwrap_err();
915
916 assert!(err.to_string().contains("index"));
917 }
918
919 #[test]
920 fn reset_clears_heap_allocations() {
921 let mut cpu = CPU::default();
922 cpu.stack_push(encode_i64(10));
923 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(1))
924 .unwrap();
925
926 cpu.reset();
927
928 assert!(cpu.heap.is_empty());
929 assert!(cpu.stack().is_empty());
930 }
931
932 #[test]
933 fn execute_generated_dispatches_instruction_without_message() {
934 let mut cpu = CPU::default();
935 cpu.push_frame(Frame {
936 base: 0,
937 local_count: 0,
938 span: (0, 0, 0),
939 function: None,
940 ret_pc: 0,
941 });
942
943 let outcome = GeneratedComponent::execute_generated(
944 &mut cpu,
945 &RuntimeInstruction::ConstI64(encode_i64(99)),
946 CPUMessage::None,
947 )
948 .unwrap();
949
950 assert_eq!(outcome, Effects::one(StepOutcome::Continue));
951 assert_eq!(cpu.stack(), &vec![encode_i64(99)]);
952 }
953
954 #[test]
955 fn execute_generated_function_info_is_only_accepted_for_calls() {
956 let mut cpu = CPU::default();
957 cpu.push_frame(Frame {
958 base: 0,
959 local_count: 0,
960 span: (0, 0, 0),
961 function: None,
962 ret_pc: 0,
963 });
964
965 let outcome = GeneratedComponent::execute_generated(
966 &mut cpu,
967 &RuntimeInstruction::Label(Ident("label".to_owned())),
968 CPUMessage::FunctionInfo {
969 arity: 2,
970 start_address: 42,
971 local_count: 2,
972 },
973 )
974 .unwrap_err();
975
976 assert!(outcome.to_string().contains("only valid for call"));
977 assert!(cpu.stack().is_empty());
978 }
979
980 #[test]
981 fn execute_generated_print_returns_control_effect_and_pops_stack() {
982 let mut cpu = CPU::default();
983 cpu.push_frame(Frame {
984 base: 0,
985 local_count: 0,
986 span: (0, 0, 0),
987 function: None,
988 ret_pc: 0,
989 });
990 cpu.stack_push(encode_i64(42));
991
992 let outcome = GeneratedComponent::execute_generated(
993 &mut cpu,
994 &RuntimeInstruction::Print,
995 CPUMessage::Print("hello".into()),
996 )
997 .unwrap();
998
999 assert_eq!(outcome, Effects::one(StepOutcome::Continue));
1000 assert!(cpu.stack().is_empty());
1001 }
1002
1003 #[test]
1004 fn execute_generated_print_rejects_wrong_message() {
1005 let mut cpu = CPU::default();
1006 cpu.push_frame(Frame {
1007 base: 0,
1008 local_count: 0,
1009 span: (0, 0, 0),
1010 function: None,
1011 ret_pc: 0,
1012 });
1013 cpu.stack_push(encode_i64(42));
1014
1015 let err = GeneratedComponent::execute_generated(
1016 &mut cpu,
1017 &RuntimeInstruction::Print,
1018 CPUMessage::None,
1019 )
1020 .unwrap_err();
1021
1022 assert!(err.to_string().contains("Print requires"));
1023 }
1024
1025 #[test]
1026 fn op_heap_dealloc_marks_slot_dead() {
1027 let mut cpu = CPU::default();
1028 cpu.stack_push(encode_i64(42));
1029 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(1))
1030 .unwrap();
1031 cpu.stack_push(encode_heap_ref(0));
1032
1033 cpu.execute_instruction(RuntimeInstruction::HeapDealloc)
1034 .unwrap();
1035
1036 assert!(
1037 cpu.heap
1038 .get(0)
1039 .unwrap_err()
1040 .to_string()
1041 .contains("deallocated")
1042 );
1043 }
1044
1045 #[test]
1046 fn op_heap_dealloc_slot_is_reused_on_next_alloc() {
1047 let mut cpu = CPU::default();
1048 cpu.stack_push(encode_i64(1));
1049 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(1))
1050 .unwrap();
1051 cpu.execute_instruction(RuntimeInstruction::HeapDealloc)
1052 .unwrap();
1053
1054 cpu.stack_push(encode_i64(2));
1055 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(1))
1056 .unwrap();
1057
1058 assert_eq!(cpu.stack(), &vec![encode_heap_ref(0)]);
1059 assert_eq!(cpu.heap.get(0).unwrap(), &[encode_i64(2)]);
1060 }
1061
1062 #[test]
1063 fn op_heap_dealloc_rejects_double_free() {
1064 let mut cpu = CPU::default();
1065 cpu.stack_push(encode_i64(1));
1066 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(1))
1067 .unwrap();
1068 cpu.stack_push(encode_heap_ref(0));
1069 cpu.execute_instruction(RuntimeInstruction::HeapDealloc)
1070 .unwrap();
1071
1072 cpu.stack_push(encode_heap_ref(0));
1073 let err = cpu
1074 .execute_instruction(RuntimeInstruction::HeapDealloc)
1075 .unwrap_err();
1076
1077 assert!(err.to_string().contains("double-free"));
1078 }
1079
1080 #[test]
1081 fn op_heap_dealloc_rejects_invalid_id() {
1082 let mut cpu = CPU::default();
1083 cpu.stack_push(encode_heap_ref(99));
1084
1085 let err = cpu
1086 .execute_instruction(RuntimeInstruction::HeapDealloc)
1087 .unwrap_err();
1088
1089 assert!(err.to_string().contains("invalid heap object id"));
1090 }
1091
1092 #[test]
1093 fn reset_clears_free_list() {
1094 let mut cpu = CPU::default();
1095 cpu.stack_push(encode_i64(1));
1096 cpu.execute_instruction(RuntimeInstruction::HeapAlloc(1))
1097 .unwrap();
1098 cpu.stack_push(encode_heap_ref(0));
1099 cpu.execute_instruction(RuntimeInstruction::HeapDealloc)
1100 .unwrap();
1101
1102 cpu.reset();
1103
1104 assert!(cpu.heap.is_empty());
1105 }
1106
1107 #[test]
1108 fn typed_word_arithmetic_canonicalizes_narrow_results() {
1109 let mut cpu = CPU::default();
1110 cpu.stack_push(encode_i32(i32::MAX));
1111 cpu.stack_push(encode_i32(1));
1112 cpu.execute_instruction(RuntimeInstruction::AddI32).unwrap();
1113 assert_eq!(cpu.stack_pop().unwrap(), encode_i32(i32::MIN));
1114
1115 cpu.stack_push(encode_u32(u32::MAX));
1116 cpu.stack_push(encode_u32(1));
1117 cpu.execute_instruction(RuntimeInstruction::AddU32).unwrap();
1118 assert_eq!(cpu.stack_pop().unwrap(), 0);
1119
1120 cpu.stack_push(encode_f32(1.5));
1121 cpu.stack_push(encode_f32(2.0));
1122 cpu.execute_instruction(RuntimeInstruction::MulF32).unwrap();
1123 assert_eq!(decode_f32(cpu.stack_pop().unwrap()), 3.0);
1124 }
1125
1126 #[test]
1127 fn integer_division_and_remainder_report_errors() {
1128 let mut cpu = CPU::default();
1129 cpu.stack_push(encode_i64(7));
1130 cpu.stack_push(encode_i64(0));
1131 assert!(cpu.execute_instruction(RuntimeInstruction::DivI64).is_err());
1132
1133 cpu.stack_push(encode_u32(7));
1134 cpu.stack_push(encode_u32(0));
1135 assert!(cpu.execute_instruction(RuntimeInstruction::RemU32).is_err());
1136 }
1137
1138 #[test]
1139 fn boolean_words_must_be_canonical() {
1140 let mut cpu = CPU::default();
1141 cpu.stack_push(2u64);
1142 assert!(cpu.execute_instruction(RuntimeInstruction::Not).is_err());
1143
1144 cpu.stack_push(2u64);
1145 assert!(
1146 cpu.execute_instruction(RuntimeInstruction::ConditionalBranch(1, 2))
1147 .is_err()
1148 );
1149 }
1150}
1151
1152fn canonical_bool(value: Word) -> Result<bool> {
1153 match value {
1154 0 => Ok(false),
1155 1 => Ok(true),
1156 other => Err(eyre::eyre!("invalid boolean word {}", other)),
1157 }
1158}
1159
1160macro_rules! int_wrapping {
1161 ($($name:ident {
1162 decode: $decode:ident,
1163 encode: $encode:ident,
1164 operation: $op:ident
1165 });+ $(;)?) => {$ (
1166 #[inline(always)]
1167 fn $name(&mut self) -> Result<StepOutcome> {
1168 let rhs = $decode(self.stack_pop()?);
1169 let lhs = $decode(self.stack_pop()?);
1170 self.stack_push($encode(lhs.$op(rhs)));
1171 Ok(StepOutcome::Continue)
1172 }
1173 )+ };
1174}
1175
1176macro_rules! int_checked {
1177 ($($name:ident {
1178 decode: $decode:ident,
1179 encode: $encode:ident,
1180 operation: $op:ident,
1181 error: $message:literal
1182 });+ $(;)?) => {$ (
1183 #[inline(always)]
1184 fn $name(&mut self) -> Result<StepOutcome> {
1185 let rhs = $decode(self.stack_pop()?);
1186 let lhs = $decode(self.stack_pop()?);
1187 let value = lhs.$op(rhs).ok_or_else(|| eyre::eyre!($message))?;
1188 self.stack_push($encode(value));
1189 Ok(StepOutcome::Continue)
1190 }
1191 )+ };
1192}
1193
1194macro_rules! float_binary {
1195 ($($name:ident {
1196 decode: $decode:ident,
1197 encode: $encode:ident,
1198 operator: $op:tt
1199 });+ $(;)?) => {$ (
1200 #[inline(always)]
1201 fn $name(&mut self) -> Result<StepOutcome> {
1202 let rhs = $decode(self.stack_pop()?);
1203 let lhs = $decode(self.stack_pop()?);
1204 self.stack_push($encode(lhs $op rhs));
1205 Ok(StepOutcome::Continue)
1206 }
1207 )+ };
1208}
1209
1210macro_rules! shift {
1211 ($($name:ident {
1212 decode: $decode:ident,
1213 encode: $encode:ident,
1214 operation: $op:ident,
1215 count_mask: $mask:expr
1216 });+ $(;)?) => {$ (
1217 #[inline(always)]
1218 fn $name(&mut self) -> Result<StepOutcome> {
1219 let rhs = decode_u32(self.stack_pop()?);
1220 let lhs = $decode(self.stack_pop()?);
1221 self.stack_push($encode(lhs.$op(rhs & $mask)));
1222 Ok(StepOutcome::Continue)
1223 }
1224 )+ };
1225}
1226
1227macro_rules! rotate {
1228 ($($name:ident {
1229 decode: $decode:ident,
1230 encode: $encode:ident,
1231 operation: $op:ident
1232 });+ $(;)?) => {$ (
1233 #[inline(always)]
1234 fn $name(&mut self) -> Result<StepOutcome> {
1235 let rhs = decode_u32(self.stack_pop()?);
1236 let lhs = $decode(self.stack_pop()?);
1237 self.stack_push($encode(lhs.$op(rhs)));
1238 Ok(StepOutcome::Continue)
1239 }
1240 )+ };
1241}
1242
1243macro_rules! bitwise {
1244 ($($name:ident {
1245 decode: $decode:ident,
1246 encode: $encode:ident,
1247 operator: $op:tt
1248 });+ $(;)?) => {$ (
1249 #[inline(always)]
1250 fn $name(&mut self) -> Result<StepOutcome> {
1251 let rhs = $decode(self.stack_pop()?);
1252 let lhs = $decode(self.stack_pop()?);
1253 self.stack_push($encode(lhs $op rhs));
1254 Ok(StepOutcome::Continue)
1255 }
1256 )+ };
1257}
1258
1259macro_rules! compare {
1260 ($($name:ident {
1261 decode: $decode:ident,
1262 operator: $op:tt
1263 });+ $(;)?) => {$ (
1264 #[inline(always)]
1265 fn $name(&mut self) -> Result<StepOutcome> {
1266 let rhs = $decode(self.stack_pop()?);
1267 let lhs = $decode(self.stack_pop()?);
1268 self.stack_push(encode_bool(lhs $op rhs));
1269 Ok(StepOutcome::Continue)
1270 }
1271 )+ };
1272}
1273
1274impl CPU {
1275 int_wrapping! {
1276 add_i32 { decode: decode_i32, encode: encode_i32, operation: wrapping_add };
1277 add_i64 { decode: decode_i64, encode: encode_i64, operation: wrapping_add };
1278 add_u32 { decode: decode_u32, encode: encode_u32, operation: wrapping_add };
1279 add_u64 { decode: decode_u64, encode: encode_u64, operation: wrapping_add };
1280 sub_i32 { decode: decode_i32, encode: encode_i32, operation: wrapping_sub };
1281 sub_i64 { decode: decode_i64, encode: encode_i64, operation: wrapping_sub };
1282 sub_u32 { decode: decode_u32, encode: encode_u32, operation: wrapping_sub };
1283 sub_u64 { decode: decode_u64, encode: encode_u64, operation: wrapping_sub };
1284 mul_i32 { decode: decode_i32, encode: encode_i32, operation: wrapping_mul };
1285 mul_i64 { decode: decode_i64, encode: encode_i64, operation: wrapping_mul };
1286 mul_u32 { decode: decode_u32, encode: encode_u32, operation: wrapping_mul };
1287 mul_u64 { decode: decode_u64, encode: encode_u64, operation: wrapping_mul };
1288 }
1289 int_checked! {
1290 div_i32 { decode: decode_i32, encode: encode_i32, operation: checked_div, error: "integer division error" };
1291 div_i64 { decode: decode_i64, encode: encode_i64, operation: checked_div, error: "integer division error" };
1292 div_u32 { decode: decode_u32, encode: encode_u32, operation: checked_div, error: "integer division error" };
1293 div_u64 { decode: decode_u64, encode: encode_u64, operation: checked_div, error: "integer division error" };
1294 rem_i32 { decode: decode_i32, encode: encode_i32, operation: checked_rem, error: "integer remainder error" };
1295 rem_i64 { decode: decode_i64, encode: encode_i64, operation: checked_rem, error: "integer remainder error" };
1296 rem_u32 { decode: decode_u32, encode: encode_u32, operation: checked_rem, error: "integer remainder error" };
1297 rem_u64 { decode: decode_u64, encode: encode_u64, operation: checked_rem, error: "integer remainder error" };
1298 }
1299 float_binary! {
1300 add_f32 { decode: decode_f32, encode: encode_f32, operator: + };
1301 add_f64 { decode: decode_f64, encode: encode_f64, operator: + };
1302 sub_f32 { decode: decode_f32, encode: encode_f32, operator: - };
1303 sub_f64 { decode: decode_f64, encode: encode_f64, operator: - };
1304 mul_f32 { decode: decode_f32, encode: encode_f32, operator: * };
1305 mul_f64 { decode: decode_f64, encode: encode_f64, operator: * };
1306 div_f32 { decode: decode_f32, encode: encode_f32, operator: / };
1307 div_f64 { decode: decode_f64, encode: encode_f64, operator: / };
1308 rem_f32 { decode: decode_f32, encode: encode_f32, operator: % };
1309 rem_f64 { decode: decode_f64, encode: encode_f64, operator: % };
1310 }
1311
1312 #[inline(always)]
1313 fn neg_i32(&mut self) -> Result<StepOutcome> {
1314 let value = decode_i32(self.stack_pop()?).wrapping_neg();
1315 self.stack_push(encode_i32(value));
1316 Ok(StepOutcome::Continue)
1317 }
1318 #[inline(always)]
1319 fn neg_i64(&mut self) -> Result<StepOutcome> {
1320 let value = decode_i64(self.stack_pop()?).wrapping_neg();
1321 self.stack_push(encode_i64(value));
1322 Ok(StepOutcome::Continue)
1323 }
1324 #[inline(always)]
1325 fn neg_f32(&mut self) -> Result<StepOutcome> {
1326 let value = -decode_f32(self.stack_pop()?);
1327 self.stack_push(encode_f32(value));
1328 Ok(StepOutcome::Continue)
1329 }
1330 #[inline(always)]
1331 fn neg_f64(&mut self) -> Result<StepOutcome> {
1332 let value = -decode_f64(self.stack_pop()?);
1333 self.stack_push(encode_f64(value));
1334 Ok(StepOutcome::Continue)
1335 }
1336
1337 shift! {
1338 shl_i32 { decode: decode_i32, encode: encode_i32, operation: wrapping_shl, count_mask: 31 };
1339 shl_i64 { decode: decode_i64, encode: encode_i64, operation: wrapping_shl, count_mask: 63 };
1340 shl_u32 { decode: decode_u32, encode: encode_u32, operation: wrapping_shl, count_mask: 31 };
1341 shl_u64 { decode: decode_u64, encode: encode_u64, operation: wrapping_shl, count_mask: 63 };
1342 shr_i32 { decode: decode_i32, encode: encode_i32, operation: wrapping_shr, count_mask: 31 };
1343 shr_i64 { decode: decode_i64, encode: encode_i64, operation: wrapping_shr, count_mask: 63 };
1344 shr_u32 { decode: decode_u32, encode: encode_u32, operation: wrapping_shr, count_mask: 31 };
1345 shr_u64 { decode: decode_u64, encode: encode_u64, operation: wrapping_shr, count_mask: 63 };
1346 }
1347 rotate! {
1348 rol_i32 { decode: decode_i32, encode: encode_i32, operation: rotate_left };
1349 rol_i64 { decode: decode_i64, encode: encode_i64, operation: rotate_left };
1350 rol_u32 { decode: decode_u32, encode: encode_u32, operation: rotate_left };
1351 rol_u64 { decode: decode_u64, encode: encode_u64, operation: rotate_left };
1352 ror_i32 { decode: decode_i32, encode: encode_i32, operation: rotate_right };
1353 ror_i64 { decode: decode_i64, encode: encode_i64, operation: rotate_right };
1354 ror_u32 { decode: decode_u32, encode: encode_u32, operation: rotate_right };
1355 ror_u64 { decode: decode_u64, encode: encode_u64, operation: rotate_right };
1356 }
1357 bitwise! {
1358 bitand_i32 { decode: decode_i32, encode: encode_i32, operator: & };
1359 bitand_i64 { decode: decode_i64, encode: encode_i64, operator: & };
1360 bitand_u32 { decode: decode_u32, encode: encode_u32, operator: & };
1361 bitand_u64 { decode: decode_u64, encode: encode_u64, operator: & };
1362 bitor_i32 { decode: decode_i32, encode: encode_i32, operator: | };
1363 bitor_i64 { decode: decode_i64, encode: encode_i64, operator: | };
1364 bitor_u32 { decode: decode_u32, encode: encode_u32, operator: | };
1365 bitor_u64 { decode: decode_u64, encode: encode_u64, operator: | };
1366 bitxor_i32 { decode: decode_i32, encode: encode_i32, operator: ^ };
1367 bitxor_i64 { decode: decode_i64, encode: encode_i64, operator: ^ };
1368 bitxor_u32 { decode: decode_u32, encode: encode_u32, operator: ^ };
1369 bitxor_u64 { decode: decode_u64, encode: encode_u64, operator: ^ };
1370 }
1371 compare! {
1372 eq_i32 { decode: decode_i32, operator: == };
1373 eq_i64 { decode: decode_i64, operator: == };
1374 eq_u32 { decode: decode_u32, operator: == };
1375 eq_u64 { decode: decode_u64, operator: == };
1376 eq_f32 { decode: decode_f32, operator: == };
1377 eq_f64 { decode: decode_f64, operator: == };
1378 ne_i32 { decode: decode_i32, operator: != };
1379 ne_i64 { decode: decode_i64, operator: != };
1380 ne_u32 { decode: decode_u32, operator: != };
1381 ne_u64 { decode: decode_u64, operator: != };
1382 ne_f32 { decode: decode_f32, operator: != };
1383 ne_f64 { decode: decode_f64, operator: != };
1384 lt_i32 { decode: decode_i32, operator: < };
1385 lt_i64 { decode: decode_i64, operator: < };
1386 lt_u32 { decode: decode_u32, operator: < };
1387 lt_u64 { decode: decode_u64, operator: < };
1388 lt_f32 { decode: decode_f32, operator: < };
1389 lt_f64 { decode: decode_f64, operator: < };
1390 gt_i32 { decode: decode_i32, operator: > };
1391 gt_i64 { decode: decode_i64, operator: > };
1392 gt_u32 { decode: decode_u32, operator: > };
1393 gt_u64 { decode: decode_u64, operator: > };
1394 gt_f32 { decode: decode_f32, operator: > };
1395 gt_f64 { decode: decode_f64, operator: > };
1396 le_i32 { decode: decode_i32, operator: <= };
1397 le_i64 { decode: decode_i64, operator: <= };
1398 le_u32 { decode: decode_u32, operator: <= };
1399 le_u64 { decode: decode_u64, operator: <= };
1400 le_f32 { decode: decode_f32, operator: <= };
1401 le_f64 { decode: decode_f64, operator: <= };
1402 ge_i32 { decode: decode_i32, operator: >= };
1403 ge_i64 { decode: decode_i64, operator: >= };
1404 ge_u32 { decode: decode_u32, operator: >= };
1405 ge_u64 { decode: decode_u64, operator: >= };
1406 ge_f32 { decode: decode_f32, operator: >= };
1407 ge_f64 { decode: decode_f64, operator: >= };
1408 }
1409
1410 fn op_not(&mut self) -> Result<StepOutcome> {
1411 let value = !canonical_bool(self.stack_pop()?)?;
1412 self.stack_push(encode_bool(value));
1413 Ok(StepOutcome::Continue)
1414 }
1415 fn op_and(&mut self) -> Result<StepOutcome> {
1416 let rhs = canonical_bool(self.stack_pop()?)?;
1417 let lhs = canonical_bool(self.stack_pop()?)?;
1418 self.stack_push(encode_bool(lhs && rhs));
1419 Ok(StepOutcome::Continue)
1420 }
1421 fn op_or(&mut self) -> Result<StepOutcome> {
1422 let rhs = canonical_bool(self.stack_pop()?)?;
1423 let lhs = canonical_bool(self.stack_pop()?)?;
1424 self.stack_push(encode_bool(lhs || rhs));
1425 Ok(StepOutcome::Continue)
1426 }
1427 fn op_xor(&mut self) -> Result<StepOutcome> {
1428 let rhs = canonical_bool(self.stack_pop()?)?;
1429 let lhs = canonical_bool(self.stack_pop()?)?;
1430 self.stack_push(encode_bool(lhs ^ rhs));
1431 Ok(StepOutcome::Continue)
1432 }
1433}