vihaco_stdlib/execution_state/
storage.rs1use super::{ExecutionState, function_entry};
7use vihaco_abi::{frame::Frame, traits::Reset, word::*};
8use vihaco_module::module::LocalModule;
9use vihaco_module::{PopStack, PushStack};
10
11impl ExecutionState {
12 pub fn stack(&self) -> &[Word] {
14 &self.stack
15 }
16
17 pub fn frames(&self) -> &[Frame] {
19 &self.frames
20 }
21
22 pub fn is_halted(&self) -> bool {
24 self.halted
25 }
26
27 pub fn start<I, V, Ty, Info>(
37 &mut self,
38 module: &LocalModule<I, V, Ty, Info>,
39 arguments: &[Word],
40 ) -> eyre::Result<()> {
41 eyre::ensure!(
42 self.halted && !self.executing,
43 "a function is already active"
44 );
45 let function = module
46 .main_function
47 .ok_or_else(|| eyre::eyre!("module has no main function"))?;
48 let (arity, locals, target) = function_entry(module, function as usize)?;
49 eyre::ensure!(arguments.len() == arity, "entry argument count mismatch");
50 self.stack
51 .try_reserve(locals.saturating_sub(self.stack.len()))?;
52 if self.frames.capacity() == 0 {
53 self.frames.try_reserve(1)?;
54 }
55 self.stack.clear();
56 self.stack.extend_from_slice(arguments);
57 self.stack.resize(locals, 0);
58 self.frames.clear();
59 self.frames.push(Frame {
60 base: 0,
61 local_count: locals,
62 span: (0, 0, 0),
65 function: Some(function as usize),
66 ret_pc: 0,
67 });
68 self.pc = target;
69 self.halted = false;
70 Ok(())
71 }
72
73 pub fn pc(&self) -> u32 {
75 self.pc
76 }
77
78 #[inline(always)]
79 pub(super) fn ensure_running(&self) -> eyre::Result<()> {
80 eyre::ensure!(
81 !self.halted && !self.frames.is_empty(),
82 "program is not running"
83 );
84 Ok(())
85 }
86
87 #[inline]
88 pub(super) fn require_operands(&self, count: usize) -> eyre::Result<()> {
89 self.ensure_running()?;
90 let frame = self
91 .frames
92 .last()
93 .ok_or_else(|| eyre::eyre!("no current frame"))?;
94 eyre::ensure!(
95 self.stack.len().saturating_sub(frame.operands_index()) >= count,
96 "stack underflow"
97 );
98 Ok(())
99 }
100
101 #[inline]
102 pub(super) fn peek_operand(&self) -> eyre::Result<Word> {
103 self.require_operands(1)?;
104 self.stack
105 .last()
106 .copied()
107 .ok_or_else(|| eyre::eyre!("stack underflow"))
108 }
109
110 #[inline]
111 pub(super) fn local_address(&self, index: u32) -> eyre::Result<usize> {
112 self.ensure_running()?;
113 let frame = self
114 .frames
115 .last()
116 .ok_or_else(|| eyre::eyre!("no current frame"))?;
117 eyre::ensure!(
118 (index as usize) < frame.local_count,
119 "local index out of bounds"
120 );
121 let address = frame
122 .base
123 .checked_add(index as usize)
124 .ok_or_else(|| eyre::eyre!("local address overflow"))?;
125 eyre::ensure!(address < self.stack.len(), "local index out of bounds");
126 Ok(address)
127 }
128}
129
130impl Reset for ExecutionState {
131 fn reset(&mut self) {
132 self.stack.clear();
133 self.frames.clear();
134 self.pc = 0;
135 self.halted = true;
136 }
137}
138
139impl PopStack<Word> for ExecutionState {
140 #[inline]
141 fn pop(&mut self) -> eyre::Result<Word> {
142 self.require_operands(1)?;
143 self.stack
144 .pop()
145 .ok_or_else(|| eyre::eyre!("stack underflow"))
146 }
147}
148
149impl PushStack<Word> for ExecutionState {
152 #[inline]
153 fn push(&mut self, value: Word) {
154 self.stack.push(value);
155 }
156}
157
158macro_rules! float_access {
159 ($ty:ty, $decode:ident, $encode:ident) => {
160 impl PopStack<$ty> for ExecutionState {
161 #[inline]
162 fn pop(&mut self) -> eyre::Result<$ty> {
163 <Self as PopStack<Word>>::pop(self).map($decode)
164 }
165 }
166 impl PushStack<$ty> for ExecutionState {
167 #[inline]
168 fn push(&mut self, value: $ty) {
169 <Self as PushStack<Word>>::push(self, $encode(value));
170 }
171 }
172 };
173}
174
175float_access!(f32, decode_f32, encode_f32);
176
177float_access!(f64, decode_f64, encode_f64);