|
1 | | -/// More generic macros for various instructions |
2 | | -/// |
3 | | -/// These macros are used to generate the actual instruction implementations. |
4 | | -/// |
5 | | -/// A bunch of these could be simplified, but some copy-paste is sometimes just simpler - this is way nicer for debugging. |
6 | | -/// Might also be nicer for the compiler to not have closures everywhere, the assembly from this in godbolt is pretty good like this. |
| 1 | +//! More generic macros for various instructions |
| 2 | +//! |
| 3 | +//! These macros are used to generate the actual instruction implementations. |
7 | 4 |
|
8 | | -/// Add two values from the stack |
9 | | -macro_rules! add_instr { |
10 | | - ($ty:ty, $stack:ident) => {{ |
11 | | - let [a, b] = $stack.values.pop_n_const::<2>()?; |
12 | | - let a: $ty = a.into(); |
13 | | - let b: $ty = b.into(); |
14 | | - $stack.values.push((a + b).into()); |
15 | | - }}; |
16 | | -} |
17 | | - |
18 | | -/// Subtract the top two values on the stack |
19 | | -macro_rules! sub_instr { |
20 | | - ($ty:ty, $stack:ident) => {{ |
21 | | - let [a, b] = $stack.values.pop_n_const::<2>()?; |
22 | | - let a: $ty = a.into(); |
23 | | - let b: $ty = b.into(); |
24 | | - $stack.values.push((a - b).into()); |
25 | | - }}; |
26 | | -} |
27 | | - |
28 | | -/// Divide the top two values on the stack |
29 | | -macro_rules! checked_divs_instr { |
30 | | - ($ty:ty, $stack:ident) => {{ |
31 | | - let [a, b] = $stack.values.pop_n_const::<2>()?; |
32 | | - let a: $ty = a.into(); |
33 | | - let b: $ty = b.into(); |
34 | | - let Some(res) = a.checked_div(b) else { |
35 | | - return Err(Error::Trap(crate::Trap::DivisionByZero)); |
36 | | - }; |
37 | | - |
38 | | - $stack.values.push(res.into()); |
39 | | - }}; |
40 | | -} |
41 | | - |
42 | | -/// Divide the top two values on the stack |
43 | | -macro_rules! checked_divu_instr { |
44 | | - ($ty:ty, $uty:ty, $stack:ident) => {{ |
45 | | - let [a, b] = $stack.values.pop_n_const::<2>()?; |
46 | | - let a: $ty = a.into(); |
47 | | - let b: $ty = b.into(); |
48 | | - let Some(res) = (a as $uty).checked_div(b as $uty) else { |
49 | | - return Err(Error::Trap(crate::Trap::DivisionByZero)); |
50 | | - }; |
51 | | - |
52 | | - $stack.values.push((res as $ty).into()); |
53 | | - }}; |
54 | | -} |
55 | | - |
56 | | -/// Divide the top two values on the stack |
57 | | -macro_rules! div_instr { |
58 | | - ($ty:ty, $stack:ident) => {{ |
59 | | - let [a, b] = $stack.values.pop_n_const::<2>()?; |
60 | | - let a: $ty = a.into(); |
61 | | - let b: $ty = b.into(); |
62 | | - $stack.values.push((a / b).into()); |
63 | | - }}; |
64 | | -} |
65 | | - |
66 | | -/// Less than signed instruction |
67 | | -macro_rules! lts_instr { |
68 | | - ($ty:ty, $stack:ident) => {{ |
69 | | - let [a, b] = $stack.values.pop_n_const::<2>()?; |
70 | | - let a: $ty = a.into(); |
71 | | - let b: $ty = b.into(); |
72 | | - $stack.values.push(((a < b) as i32).into()); |
| 5 | +/// Convert the top value on the stack to a specific type |
| 6 | +macro_rules! conv_1 { |
| 7 | + ($from:ty, $to:ty, $stack:ident) => {{ |
| 8 | + let a: $from = $stack.values.pop()?.into(); |
| 9 | + $stack.values.push((a as $to).into()); |
73 | 10 | }}; |
74 | 11 | } |
75 | 12 |
|
76 | | -/// Less than unsigned instruction |
77 | | -macro_rules! ltu_instr { |
78 | | - ($ty:ty, $uty:ty, $stack:ident) => {{ |
79 | | - let [a, b] = $stack.values.pop_n_const::<2>()?; |
80 | | - let a: $ty = a.into(); |
81 | | - let b: $ty = b.into(); |
82 | | - // Cast to unsigned type before comparison |
83 | | - let a_unsigned: $uty = a as $uty; |
84 | | - let b_unsigned: $uty = b as $uty; |
85 | | - $stack.values.push(((a_unsigned < b_unsigned) as i32).into()); |
| 13 | +/// Convert the unsigned value on the top of the stack to a specific type |
| 14 | +macro_rules! conv_2 { |
| 15 | + ($ty:ty, $uty:ty, $to:ty, $stack:ident) => {{ |
| 16 | + let a: $ty = $stack.values.pop()?.into(); |
| 17 | + $stack.values.push((a as $uty as $to).into()); |
86 | 18 | }}; |
87 | 19 | } |
88 | 20 |
|
89 | | -/// Less than equal signed instruction |
90 | | -macro_rules! les_instr { |
91 | | - ($ty:ty, $stack:ident) => {{ |
| 21 | +/// Compare two values on the stack |
| 22 | +macro_rules! comp { |
| 23 | + ($op:tt, $ty:ty, $stack:ident) => {{ |
92 | 24 | let [a, b] = $stack.values.pop_n_const::<2>()?; |
93 | 25 | let a: $ty = a.into(); |
94 | 26 | let b: $ty = b.into(); |
95 | | - |
96 | | - $stack.values.push(((a <= b) as i32).into()); |
| 27 | + $stack.values.push(((a $op b) as i32).into()); |
97 | 28 | }}; |
98 | 29 | } |
99 | 30 |
|
100 | | -/// Less than equal unsigned instruction |
101 | | -macro_rules! leu_instr { |
102 | | - ($ty:ty, $uty:ty, $stack:ident) => {{ |
| 31 | +/// Compare two values on the stack (cast to ty2 before comparison) |
| 32 | +macro_rules! comp_cast { |
| 33 | + ($op:tt, $ty:ty, $ty2:ty, $stack:ident) => {{ |
103 | 34 | let [a, b] = $stack.values.pop_n_const::<2>()?; |
104 | 35 | let a: $ty = a.into(); |
105 | 36 | let b: $ty = b.into(); |
106 | 37 |
|
107 | 38 | // Cast to unsigned type before comparison |
108 | | - let a_unsigned: $uty = a as $uty; |
109 | | - let b_unsigned: $uty = b as $uty; |
110 | | - $stack.values.push(((a_unsigned <= b_unsigned) as i32).into()); |
111 | | - }}; |
112 | | -} |
113 | | - |
114 | | -/// Multiply the top two values on the stack |
115 | | -macro_rules! mul_instr { |
116 | | - ($ty:ty, $stack:ident) => {{ |
117 | | - let [a, b] = $stack.values.pop_n_const::<2>()?; |
118 | | - let a: $ty = a.into(); |
119 | | - let b: $ty = b.into(); |
120 | | - $stack.values.push((a * b).into()); |
| 39 | + let a_unsigned: $ty2 = a as $ty2; |
| 40 | + let b_unsigned: $ty2 = b as $ty2; |
| 41 | + $stack.values.push(((a_unsigned $op b_unsigned) as i32).into()); |
121 | 42 | }}; |
122 | 43 | } |
123 | 44 |
|
124 | | -/// Compare the top two values on the stack for equality |
125 | | -macro_rules! eq_instr { |
126 | | - ($ty:ty, $stack:ident) => {{ |
127 | | - let [a, b] = $stack.values.pop_n_const::<2>()?; |
128 | | - let a: $ty = a.into(); |
129 | | - let b: $ty = b.into(); |
130 | | - $stack.values.push(((a == b) as i32).into()); |
131 | | - }}; |
132 | | -} |
133 | | - |
134 | | -/// Compare the top value on the stack for equality with zero |
135 | | -macro_rules! eqz_instr { |
136 | | - ($ty:ty, $stack:ident) => {{ |
| 45 | +/// Compare a value on the stack to zero |
| 46 | +macro_rules! comp_zero { |
| 47 | + ($op:tt, $ty:ty, $stack:ident) => {{ |
137 | 48 | let a: $ty = $stack.values.pop()?.into(); |
138 | | - $stack.values.push(((a == 0) as i32).into()); |
| 49 | + $stack.values.push(((a $op 0) as i32).into()); |
139 | 50 | }}; |
140 | 51 | } |
141 | 52 |
|
142 | | -/// Compare the top two values on the stack for inequality |
143 | | -macro_rules! ne_instr { |
144 | | - ($ty:ty, $stack:ident) => {{ |
| 53 | +/// Apply an arithmetic operation to two values on the stack |
| 54 | +macro_rules! arithmetic { |
| 55 | + ($op:tt, $ty:ty, $stack:ident) => {{ |
145 | 56 | let [a, b] = $stack.values.pop_n_const::<2>()?; |
146 | 57 | let a: $ty = a.into(); |
147 | 58 | let b: $ty = b.into(); |
148 | | - $stack.values.push(((a != b) as i32).into()); |
| 59 | + $stack.values.push((a $op b).into()); |
149 | 60 | }}; |
150 | 61 | } |
151 | 62 |
|
152 | | -/// Greater or equal than signed instruction |
153 | | -macro_rules! ges_instr { |
154 | | - ($ty:ty, $stack:ident) => {{ |
| 63 | +/// Apply an arithmetic operation to two values on the stack |
| 64 | +macro_rules! checked_arithmetic { |
| 65 | + ($op:ident, $ty:ty, $stack:ident, $trap:expr) => {{ |
155 | 66 | let [a, b] = $stack.values.pop_n_const::<2>()?; |
156 | 67 | let a: $ty = a.into(); |
157 | 68 | let b: $ty = b.into(); |
158 | | - $stack.values.push(((a >= b) as i32).into()); |
| 69 | + let result = a.$op(b).ok_or_else(|| Error::Trap($trap))?; |
| 70 | + $stack.values.push(result.into()); |
159 | 71 | }}; |
160 | 72 | } |
161 | 73 |
|
162 | | -/// Greater or equal than unsigned instruction |
163 | | -macro_rules! geu_instr { |
164 | | - ($ty:ty, $uty:ty, $stack:ident) => {{ |
| 74 | +/// Apply an arithmetic operation to two values on the stack (cast to ty2 before operation) |
| 75 | +macro_rules! checked_arithmetic_cast { |
| 76 | + ($op:ident, $ty:ty, $ty2:ty, $stack:ident, $trap:expr) => {{ |
165 | 77 | let [a, b] = $stack.values.pop_n_const::<2>()?; |
166 | 78 | let a: $ty = a.into(); |
167 | 79 | let b: $ty = b.into(); |
168 | | - // Cast to unsigned type before comparison |
169 | | - let a_unsigned: $uty = a as $uty; |
170 | | - let b_unsigned: $uty = b as $uty; |
171 | | - $stack.values.push(((a_unsigned >= b_unsigned) as i32).into()); |
172 | | - }}; |
173 | | -} |
174 | 80 |
|
175 | | -/// Greater than instruction |
176 | | -macro_rules! gts_instr { |
177 | | - ($ty:ty, $stack:ident) => {{ |
178 | | - let [a, b] = $stack.values.pop_n_const::<2>()?; |
179 | | - let a: $ty = a.into(); |
180 | | - let b: $ty = b.into(); |
| 81 | + // Cast to unsigned type before operation |
| 82 | + let a_unsigned: $ty2 = a as $ty2; |
| 83 | + let b_unsigned: $ty2 = b as $ty2; |
181 | 84 |
|
182 | | - $stack.values.push(((a > b) as i32).into()); |
183 | | - }}; |
184 | | -} |
185 | | - |
186 | | -/// Greater than instruction (convert to unsigned before comparison) |
187 | | -macro_rules! gtu_instr { |
188 | | - ($ty:ty, $uty:ty, $stack:ident) => {{ |
189 | | - let [a, b] = $stack.values.pop_n_const::<2>()?; |
190 | | - let a: $ty = a.into(); |
191 | | - let b: $ty = b.into(); |
192 | | - // Cast to unsigned type before comparison |
193 | | - let a_unsigned: $uty = a as $uty; |
194 | | - let b_unsigned: $uty = b as $uty; |
195 | | - $stack.values.push(((a_unsigned > b_unsigned) as i32).into()); |
196 | | - }}; |
197 | | -} |
198 | | - |
199 | | -/// Convert the top value on the stack to a specific type |
200 | | -macro_rules! conv_1 { |
201 | | - ($from:ty, $to:ty, $stack:ident) => {{ |
202 | | - let a: $from = $stack.values.pop()?.into(); |
203 | | - $stack.values.push((a as $to).into()); |
204 | | - }}; |
205 | | -} |
206 | | - |
207 | | -/// Convert the unsigned value on the top of the stack to a specific type |
208 | | -macro_rules! conv_2 { |
209 | | - ($ty:ty, $uty:ty, $to:ty, $stack:ident) => {{ |
210 | | - let a: $ty = $stack.values.pop()?.into(); |
211 | | - $stack.values.push((a as $uty as $to).into()); |
| 85 | + let result = a_unsigned.$op(b_unsigned).ok_or_else(|| Error::Trap($trap))?; |
| 86 | + $stack.values.push((result as $ty).into()); |
212 | 87 | }}; |
213 | 88 | } |
214 | 89 |
|
215 | | -pub(super) use add_instr; |
216 | | -pub(super) use checked_divs_instr; |
217 | | -pub(super) use checked_divu_instr; |
| 90 | +pub(super) use arithmetic; |
| 91 | +pub(super) use checked_arithmetic; |
| 92 | +pub(super) use checked_arithmetic_cast; |
| 93 | +pub(super) use comp; |
| 94 | +pub(super) use comp_cast; |
| 95 | +pub(super) use comp_zero; |
218 | 96 | pub(super) use conv_1; |
219 | 97 | pub(super) use conv_2; |
220 | | -pub(super) use div_instr; |
221 | | -pub(super) use eq_instr; |
222 | | -pub(super) use eqz_instr; |
223 | | -pub(super) use ges_instr; |
224 | | -pub(super) use geu_instr; |
225 | | -pub(super) use gts_instr; |
226 | | -pub(super) use gtu_instr; |
227 | | -pub(super) use les_instr; |
228 | | -pub(super) use leu_instr; |
229 | | -pub(super) use lts_instr; |
230 | | -pub(super) use ltu_instr; |
231 | | -pub(super) use mul_instr; |
232 | | -pub(super) use ne_instr; |
233 | | -pub(super) use sub_instr; |
0 commit comments