slvm/vm/
macros.rs

1#[macro_export]
2macro_rules! inc_ip {
3    ($code_ip:expr) => {{
4        #[allow(clippy::macro_metavars_in_unsafe)]
5        unsafe {
6            // SAFETY: `$code_ip` must be a valid pointer to at least 1 byte of memory.
7            let r = *$code_ip;
8            $code_ip = $code_ip.offset(1);
9            r
10        }
11    }};
12}
13
14#[macro_export]
15macro_rules! get_code {
16    ($chunk:expr) => {{ $chunk.code.as_ptr() }};
17}
18
19#[macro_export]
20macro_rules! get_code_at {
21    ($chunk:expr, $idx:expr) => {{
22        let code = $chunk.code.as_ptr();
23        let idx: isize = $idx as isize;
24        unsafe { code.offset(idx) }
25    }};
26}
27
28#[macro_export]
29macro_rules! decode_u8 {
30    ($code:expr) => {{ $crate::inc_ip!($code) }};
31}
32
33#[macro_export]
34macro_rules! decode_u16 {
35    ($code:expr) => {{
36        #[allow(clippy::macro_metavars_in_unsafe)]
37        unsafe {
38            // SAFETY: `$code` must be a valid pointer to at least 2 bytes of memory.
39            let idx1 = *$code;
40            let idx2 = *$code.offset(1);
41            $code = $code.offset(2);
42            ((idx1 as u16) << 8) | (idx2 as u16)
43        }
44    }};
45}
46
47#[macro_export]
48macro_rules! decode_u32 {
49    ($code:expr) => {{
50        #[allow(clippy::macro_metavars_in_unsafe)]
51        unsafe {
52            // SAFETY: `$code` must be a valid pointer to at least 4 bytes of memory.
53            let idx1 = *$code;
54            let idx2 = *$code.offset(1);
55            let idx3 = *$code.offset(2);
56            let idx4 = *$code.offset(3);
57            $code = $code.offset(4);
58            ((idx1 as u32) << 24) | ((idx2 as u32) << 16) | ((idx3 as u32) << 8) | (idx4 as u32)
59        }
60    }};
61}
62
63#[macro_export]
64macro_rules! decode1 {
65    ($code:expr, $wide:expr) => {{
66        if $wide {
67            decode_u16!($code)
68        } else {
69            $crate::inc_ip!($code) as u16
70        }
71    }};
72}
73
74#[macro_export]
75macro_rules! decode2 {
76    ($code:expr, $wide:expr) => {{
77        if $wide {
78            (decode_u16!($code), decode_u16!($code))
79        } else {
80            //(crate::inc_ip!($code) as u16, crate::inc_ip!($code) as u16)
81            #[allow(clippy::macro_metavars_in_unsafe)]
82            unsafe {
83                // SAFETY: $code must be a valid pointer to at least 2 bytes of memory.
84                let r = (*$code as u16, *$code.offset(1) as u16);
85                $code = $code.offset(2);
86                r
87            }
88        }
89    }};
90}
91
92#[macro_export]
93macro_rules! decode3 {
94    ($code:expr, $wide:expr) => {{
95        if $wide {
96            (decode_u16!($code), decode_u16!($code), decode_u16!($code))
97        } else {
98            #[allow(clippy::macro_metavars_in_unsafe)]
99            unsafe {
100                // SAFETY: $code must be a valid pointer to at least 3 bytes of memory.
101                let r = (
102                    *$code as u16,
103                    *$code.offset(1) as u16,
104                    *$code.offset(2) as u16,
105                );
106                $code = $code.offset(3);
107                r
108            }
109        }
110    }};
111}
112
113/// Special macro for NUMEQ that uses approximate equality for floats
114macro_rules! compare_numeric_eq {
115    ($vm:expr, $chunk:expr, $code:expr, $wide:expr) => {{
116        let (dest, reg1, reg2) = decode3!($code, $wide);
117        let mut val = false;
118        for reg in reg1..reg2 {
119            let op1 = $vm.register_unref(reg as usize);
120            let op2 = $vm.register_unref(reg as usize + 1);
121            val = if matches!(op1, $crate::Value::Float(_))
122                || matches!(op2, $crate::Value::Float(_))
123            {
124                // For float equality, use approximate comparison
125                // Convert both values to F56 for comparison
126                let f56_1 = match op1 {
127                    $crate::Value::Float(f) => f,
128                    $crate::Value::Byte(b) => $crate::float::F56::from(b as f64),
129                    $crate::Value::Int(i) => $crate::float::F56::from($crate::from_i56(&i) as f64),
130                    _ => {
131                        return Err((
132                            $crate::VMError::new_value(format!(
133                                "Not a number: {}",
134                                op1.display_value($vm)
135                            )),
136                            $chunk.clone(),
137                        ));
138                    }
139                };
140                let f56_2 = match op2 {
141                    $crate::Value::Float(f) => f,
142                    $crate::Value::Byte(b) => $crate::float::F56::from(b as f64),
143                    $crate::Value::Int(i) => $crate::float::F56::from($crate::from_i56(&i) as f64),
144                    _ => {
145                        return Err((
146                            $crate::VMError::new_value(format!(
147                                "Not a number: {}",
148                                op2.display_value($vm)
149                            )),
150                            $chunk.clone(),
151                        ));
152                    }
153                };
154                f56_1.roughly_equal_using_relative_difference(&f56_2)
155            } else {
156                // Both operands are treated as integers - exact comparison
157                let i1 = get_primitive_int!($vm, op1).map_err(|e| (e, $chunk.clone()))?;
158                let i2 = get_primitive_int!($vm, op2).map_err(|e| (e, $chunk.clone()))?;
159                i1 == i2
160            };
161            if !val {
162                break;
163            }
164        }
165        let val = if val {
166            $crate::Value::True
167        } else {
168            $crate::Value::False
169        };
170        *$vm.register_mut(dest as usize) = val;
171    }};
172}
173
174macro_rules! compare_numeric {
175    ($vm:expr, $chunk:expr, $code:expr, $comp_fn:expr, $wide:expr) => {{
176        let (dest, reg1, reg2) = decode3!($code, $wide);
177        let mut val = false;
178        for reg in reg1..reg2 {
179            let op1 = $vm.register_unref(reg as usize);
180            let op2 = $vm.register_unref(reg as usize + 1);
181            val = if matches!(op1, $crate::Value::Float(_))
182                || matches!(op2, $crate::Value::Float(_))
183            {
184                // Both operands are floats.
185                // The macro expansion trips this.
186                #[allow(clippy::redundant_closure_call)]
187                $comp_fn(
188                    get_primitive_float!($vm, op1).map_err(|e| (e, $chunk.clone()))?,
189                    get_primitive_float!($vm, op2).map_err(|e| (e, $chunk.clone()))?,
190                )
191            } else {
192                // Both operands are treated as integers.
193                // The macro expansion trips this.
194                #[allow(clippy::redundant_closure_call)]
195                $comp_fn(
196                    get_primitive_int!($vm, op1).map_err(|e| (e, $chunk.clone()))?,
197                    get_primitive_int!($vm, op2).map_err(|e| (e, $chunk.clone()))?,
198                )
199            };
200            if !val {
201                break;
202            }
203        }
204        let val = if val {
205            $crate::Value::True
206        } else {
207            $crate::Value::False
208        };
209        *$vm.register_mut(dest as usize) = val;
210    }};
211}
212
213/// Convert a Value into an i64 integer primitive if possible
214macro_rules! get_primitive_int {
215    ($vm:expr, $val:expr) => {{
216        match $val {
217            $crate::Value::Byte(b) => Ok(b as i64),
218            $crate::Value::Int(i) => Ok($crate::from_i56(&i)),
219            _ => Err($crate::VMError::new_value(format!(
220                "Not an integer: {}",
221                $val.display_value($vm)
222            ))),
223        }
224    }};
225}
226
227/// Convert a numeric Value into an f64 float primitive
228macro_rules! get_primitive_float {
229    ($vm:expr, $val:expr) => {{
230        match $val {
231            $crate::Value::Byte(b) => Ok(b as f64),
232            $crate::Value::Int(i) => Ok(crate::from_i56(&i) as f64),
233            $crate::Value::Float(f) => Ok(f64::from(f)),
234            _ => Err($crate::VMError::new_value(format!(
235                "Not a float: {:?}",
236                $val
237            ))),
238        }
239    }};
240}
241
242macro_rules! binary_math {
243    ($vm:expr, $chunk:expr, $code:expr, $bin_fn:expr, $wide:expr) => {{
244        let (dest, op2) = decode2!($code, $wide);
245        let op1 = $vm.register(dest as usize);
246        let op2 = $vm.register(op2 as usize);
247        match (op1, op2) {
248            ($crate::Value::Float(op1_f), $crate::Value::Float(op2_f)) => {
249                *$vm.register_mut(dest as usize) =
250                    $bin_fn(f64::from(op1_f), f64::from(op2_f)).into();
251            }
252            ($crate::Value::Float(op1_f), _) => {
253                *$vm.register_mut(dest as usize) = $bin_fn(
254                    f64::from(op1_f),
255                    get_primitive_float!($vm, op2).map_err(|e| (e, $chunk.clone()))?,
256                )
257                .into();
258            }
259            (_, $crate::Value::Float(op2_f)) => {
260                *$vm.register_mut(dest as usize) = $bin_fn(
261                    get_primitive_float!($vm, op1).map_err(|e| (e, $chunk.clone()))?,
262                    f64::from(op2_f),
263                )
264                .into();
265            }
266            (_, _) => {
267                *$vm.register_mut(dest as usize) = $bin_fn(
268                    get_primitive_int!($vm, op1).map_err(|e| (e, $chunk.clone()))?,
269                    get_primitive_int!($vm, op2).map_err(|e| (e, $chunk.clone()))?,
270                )
271                .into();
272            }
273        }
274    }};
275}
276
277macro_rules! div_math {
278    ($vm:expr, $chunk:expr, $code:expr, $wide:expr) => {{
279        let (dest, op2) = decode2!($code, $wide);
280        let op1 = $vm.register(dest as usize);
281        let op2 = $vm.register(op2 as usize);
282        match (op1, op2) {
283            ($crate::Value::Float(op1_f), $crate::Value::Float(op2_f)) => {
284                let op1 = f64::from(op1_f);
285                let op2 = f64::from(op2_f);
286                if op2 == 0.0 {
287                    return Err(($crate::VMError::new_vm("Divide by zero error."), $chunk));
288                }
289                *$vm.register_mut(dest as usize) = (op1 / op2).into();
290            }
291            ($crate::Value::Float(op1_f), _) => {
292                let op1 = f64::from(op1_f);
293                let op2 = get_primitive_float!($vm, op2).map_err(|e| (e, $chunk.clone()))? as f64;
294                if op2 == 0.0 {
295                    return Err(($crate::VMError::new_vm("Divide by zero error."), $chunk));
296                }
297                *$vm.register_mut(dest as usize) = (op1 / op2).into();
298            }
299            (_, $crate::Value::Float(op2_f)) => {
300                let op1 = get_primitive_float!($vm, op1).map_err(|e| (e, $chunk.clone()))? as f64;
301                let op2 = f64::from(op2_f);
302                if op2 == 0.0 {
303                    return Err(($crate::VMError::new_vm("Divide by zero error."), $chunk));
304                }
305                *$vm.register_mut(dest as usize) = (op1 / op2).into();
306            }
307            (_, _) => {
308                let op1 = get_primitive_int!($vm, op1).map_err(|e| (e, $chunk.clone()))?;
309                let op2 = get_primitive_int!($vm, op2).map_err(|e| (e, $chunk.clone()))?;
310                if op2 == 0 {
311                    return Err(($crate::VMError::new_vm("Divide by zero error."), $chunk));
312                }
313                let val = op1 / op2;
314                *$vm.register_mut(dest as usize) = val.into();
315            }
316        }
317    }};
318}
319
320#[macro_export]
321macro_rules! set_register {
322    ($vm:expr, $idx:expr, $val:expr) => {{
323        match (&$vm.register($idx as usize), $val) {
324            ($crate::Value::Value(_), $crate::Value::Value(_)) => {
325                panic!("Do not set recursive Values...")
326            }
327            ($crate::Value::Value(handle), _) => {
328                *($vm.heap_mut().get_value_mut(*handle)) = $val;
329            }
330            _ => *$vm.register_mut($idx) = $val,
331        }
332    }};
333}
334
335#[macro_export]
336macro_rules! mov_register {
337    ($vm:expr, $idx:expr, $val:expr) => {{
338        *$vm.register_mut($idx) = $val;
339    }};
340}