Skip to main content

fidget_core/vm/
mod.rs

1//! Simple virtual machine for shape evaluation
2use crate::{
3    Context,
4    compiler::RegOp,
5    context::{BadNode, Node},
6    eval::{
7        BulkEvalError, BulkEvaluator, BulkOutput, Function, MathFunction, Tape,
8        Trace, TracingEvalError, TracingEvaluator,
9    },
10    render::{RenderHints, TileSizes},
11    shape::Shape,
12    types::{Grad, Interval},
13    var::VarMap,
14};
15use std::sync::Arc;
16
17mod choice;
18mod data;
19
20pub use choice::Choice;
21use data::BadChoiceSlice;
22pub use data::{VmData, VmWorkspace};
23
24////////////////////////////////////////////////////////////////////////////////
25
26/// Function which uses the VM backend for evaluation
27///
28/// Internally, the [`VmFunction`] stores an [`Arc<VmData>`](VmData), and
29/// iterates over a [`Vec<RegOp>`](RegOp) to perform evaluation.
30///
31/// All of the associated [`Tape`] types simply clone the internal `Arc`;
32/// there's no separate planning required to generate a tape.
33pub type VmFunction = GenericVmFunction<{ u8::MAX as usize }>;
34
35/// Shape that uses the [`VmFunction`] backend for evaluation
36pub type VmShape = Shape<VmFunction>;
37
38/// Tape storage type which indicates that there's no actual backing storage
39#[derive(Default)]
40pub struct EmptyTapeStorage;
41
42/// Tape which uses the VM backend for evaluation
43///
44/// This tape type is equivalent to a [`GenericVmFunction`], but implements
45/// different traits ([`Tape`] instead of [`Function`]).
46#[derive(Clone)]
47pub struct GenericVmTape<const N: usize>(Arc<VmData<N>>);
48
49impl<const N: usize> GenericVmTape<N> {
50    /// Returns a handle to the inner [`VmData`] used by the tape
51    pub fn data(&self) -> &VmData<N> {
52        &self.0
53    }
54}
55
56impl<const N: usize> Tape for GenericVmTape<N> {
57    type Storage = EmptyTapeStorage;
58    fn recycle(self) -> Option<Self::Storage> {
59        Some(EmptyTapeStorage)
60    }
61
62    fn vars(&self) -> &VarMap {
63        &self.0.vars
64    }
65
66    fn output_count(&self) -> usize {
67        self.0.output_count()
68    }
69}
70
71/// A trace captured by a VM evaluation
72///
73/// This is a thin wrapper around a [`Vec<Choice>`](Choice).
74#[derive(Clone, Default, Eq, PartialEq)]
75pub struct VmTrace(Vec<Choice>);
76
77impl VmTrace {
78    /// Fills the trace with the given value
79    pub fn fill(&mut self, v: Choice) {
80        self.0.fill(v);
81    }
82    /// Resizes the trace, using the new value if it needs to be extended
83    pub fn resize(&mut self, n: usize, v: Choice) {
84        self.0.resize(n, v);
85    }
86    /// Returns the inner choice slice
87    pub fn as_slice(&self) -> &[Choice] {
88        self.0.as_slice()
89    }
90    /// Returns the inner choice slice as a mutable reference
91    pub fn as_mut_slice(&mut self) -> &mut [Choice] {
92        self.0.as_mut_slice()
93    }
94    /// Returns a pointer to the allocated choice array
95    pub fn as_mut_ptr(&mut self) -> *mut Choice {
96        self.0.as_mut_ptr()
97    }
98}
99
100impl Trace for VmTrace {
101    fn copy_from(&mut self, other: &VmTrace) {
102        self.0.resize(other.0.len(), Choice::Unknown);
103        self.0.copy_from_slice(&other.0);
104    }
105}
106
107#[cfg(any(test, feature = "eval-tests"))]
108impl From<Vec<Choice>> for VmTrace {
109    fn from(v: Vec<Choice>) -> Self {
110        Self(v)
111    }
112}
113
114#[cfg(any(test, feature = "eval-tests"))]
115impl AsRef<[Choice]> for VmTrace {
116    fn as_ref(&self) -> &[Choice] {
117        &self.0
118    }
119}
120
121/// VM-backed shape with a configurable number of registers
122///
123/// You are unlikely to use this directly; [`VmShape`] should be used for
124/// VM-based evaluation.
125#[derive(Clone)]
126pub struct GenericVmFunction<const N: usize>(Arc<VmData<N>>);
127
128impl<const N: usize> From<VmData<N>> for GenericVmFunction<N> {
129    fn from(d: VmData<N>) -> Self {
130        Self(d.into())
131    }
132}
133
134impl<const N: usize> GenericVmFunction<N> {
135    /// Returns a characteristic size (the length of the inner assembly tape)
136    pub fn size(&self) -> usize {
137        self.0.len()
138    }
139
140    /// Reclaim the inner `VmData` if there's only a single reference
141    pub fn recycle(self) -> Option<VmData<N>> {
142        Arc::try_unwrap(self.0).ok()
143    }
144
145    /// Borrows the inner [`VmData`]
146    pub fn data(&self) -> &VmData<N> {
147        self.0.as_ref()
148    }
149
150    /// Returns a [`GenericVmTape`] for the given function
151    pub fn tape(&self) -> GenericVmTape<N> {
152        GenericVmTape(self.0.clone())
153    }
154
155    /// Returns the number of choices (i.e. `min` and `max` nodes) in the tape
156    pub fn choice_count(&self) -> usize {
157        self.0.choice_count()
158    }
159
160    /// Returns the number of outputs in the tape
161    pub fn output_count(&self) -> usize {
162        self.0.output_count()
163    }
164
165    /// Simplifies the function with the given trace and a new register count
166    pub fn simplify_with<const M: usize>(
167        &self,
168        trace: &VmTrace,
169        storage: VmData<M>,
170        workspace: &mut VmWorkspace<M>,
171    ) -> Result<GenericVmFunction<M>, BadTrace> {
172        let d = self.0.simplify::<M>(trace.as_slice(), workspace, storage)?;
173        Ok(GenericVmFunction(Arc::new(d)))
174    }
175}
176
177/// Error type for simplification
178#[derive(thiserror::Error, Debug)]
179#[error(transparent)]
180pub struct BadTrace(#[from] pub BadChoiceSlice);
181
182impl<const N: usize> Function for GenericVmFunction<N> {
183    type Storage = VmData<N>;
184    type Workspace = VmWorkspace<N>;
185
186    type TapeStorage = EmptyTapeStorage;
187
188    type FloatSliceEval = VmFloatSliceEval<N>;
189    type GradSliceEval = VmGradSliceEval<N>;
190    type PointEval = VmPointEval<N>;
191    type IntervalEval = VmIntervalEval<N>;
192    type Trace = VmTrace;
193
194    #[inline]
195    fn float_slice_tape(&self, _storage: EmptyTapeStorage) -> GenericVmTape<N> {
196        self.tape()
197    }
198
199    #[inline]
200    fn grad_slice_tape(&self, _storage: EmptyTapeStorage) -> GenericVmTape<N> {
201        self.tape()
202    }
203
204    #[inline]
205    fn point_tape(&self, _storage: EmptyTapeStorage) -> GenericVmTape<N> {
206        self.tape()
207    }
208
209    #[inline]
210    fn interval_tape(&self, _storage: EmptyTapeStorage) -> GenericVmTape<N> {
211        self.tape()
212    }
213
214    #[inline]
215    fn simplify(
216        &self,
217        trace: &Self::Trace,
218        storage: Self::Storage,
219        workspace: &mut Self::Workspace,
220    ) -> Result<Self, BadTrace> {
221        self.simplify_with(trace, storage, workspace)
222    }
223
224    #[inline]
225    fn recycle(self) -> Option<Self::Storage> {
226        GenericVmFunction::recycle(self)
227    }
228
229    #[inline]
230    fn size(&self) -> usize {
231        GenericVmFunction::size(self)
232    }
233
234    #[inline]
235    fn vars(&self) -> &VarMap {
236        &self.0.vars
237    }
238
239    #[inline]
240    fn can_simplify(&self) -> bool {
241        self.0.choice_count() > 0
242    }
243
244    #[inline]
245    fn output_count(&self) -> usize {
246        self.0.output_count()
247    }
248}
249
250impl<const N: usize> RenderHints for GenericVmFunction<N> {
251    fn tile_sizes_3d() -> TileSizes {
252        TileSizes::new(&[128, 64, 32, 16, 8]).unwrap()
253    }
254
255    fn tile_sizes_2d() -> TileSizes {
256        TileSizes::new(&[128, 32, 8]).unwrap()
257    }
258}
259
260impl<const N: usize> MathFunction for GenericVmFunction<N> {
261    fn new(ctx: &Context, nodes: &[Node]) -> Result<Self, BadNode> {
262        let d = VmData::new(ctx, nodes)?;
263        Ok(Self(d.into()))
264    }
265}
266
267////////////////////////////////////////////////////////////////////////////////
268
269/// Helper struct to reduce boilerplate conversions
270struct SlotArray<'a, T>(&'a mut [T]);
271impl<T> std::ops::Index<u8> for SlotArray<'_, T> {
272    type Output = T;
273    fn index(&self, i: u8) -> &Self::Output {
274        &self.0[i as usize]
275    }
276}
277impl<T> std::ops::IndexMut<u8> for SlotArray<'_, T> {
278    fn index_mut(&mut self, i: u8) -> &mut T {
279        &mut self.0[i as usize]
280    }
281}
282impl<T> std::ops::Index<u32> for SlotArray<'_, T> {
283    type Output = T;
284    fn index(&self, i: u32) -> &Self::Output {
285        &self.0[i as usize]
286    }
287}
288impl<T> std::ops::IndexMut<u32> for SlotArray<'_, T> {
289    fn index_mut(&mut self, i: u32) -> &mut T {
290        &mut self.0[i as usize]
291    }
292}
293
294////////////////////////////////////////////////////////////////////////////////
295
296/// Generic VM evaluator for tracing evaluation
297struct TracingVmEval<T> {
298    slots: Vec<T>,
299    out: Vec<T>,
300    choices: VmTrace,
301}
302
303impl<T> Default for TracingVmEval<T> {
304    fn default() -> Self {
305        Self {
306            slots: Vec::default(),
307            out: Vec::default(),
308            choices: VmTrace::default(),
309        }
310    }
311}
312
313impl<T: From<f32> + Clone> TracingVmEval<T> {
314    fn resize_slots<const N: usize>(&mut self, tape: &VmData<N>) {
315        self.slots.resize(tape.slot_count(), f32::NAN.into());
316        self.choices.resize(tape.choice_count(), Choice::Unknown);
317        self.out.resize(tape.output_count(), f32::NAN.into());
318        self.choices.fill(Choice::Unknown);
319    }
320}
321
322/// VM-based tracing evaluator for intervals
323#[derive(Default)]
324pub struct VmIntervalEval<const N: usize>(TracingVmEval<Interval>);
325impl<const N: usize> TracingEvaluator for VmIntervalEval<N> {
326    type Data = Interval;
327    type Tape = GenericVmTape<N>;
328    type Trace = VmTrace;
329    type TapeStorage = EmptyTapeStorage;
330
331    #[inline]
332    fn eval(
333        &mut self,
334        tape: &Self::Tape,
335        vars: &[Interval],
336    ) -> Result<(&[Interval], Option<&VmTrace>), TracingEvalError> {
337        tape.vars().check_tracing_arguments(vars)?;
338        let tape = tape.data();
339        self.0.resize_slots(tape);
340
341        let mut simplify = false;
342        let mut v = SlotArray(&mut self.0.slots);
343        let mut choices = self.0.choices.as_mut_slice().iter_mut();
344        for op in tape.iter_asm() {
345            match op {
346                RegOp::Output(arg, i) => {
347                    self.0.out[i as usize] = v[arg];
348                }
349                RegOp::Input(out, i) => {
350                    v[out] = vars[i as usize];
351                }
352                RegOp::NegReg(out, arg) => {
353                    v[out] = -v[arg];
354                }
355                RegOp::AbsReg(out, arg) => {
356                    v[out] = v[arg].abs();
357                }
358                RegOp::RecipReg(out, arg) => {
359                    v[out] = v[arg].recip();
360                }
361                RegOp::SqrtReg(out, arg) => {
362                    v[out] = v[arg].sqrt();
363                }
364                RegOp::SquareReg(out, arg) => {
365                    v[out] = v[arg].square();
366                }
367                RegOp::FloorReg(out, arg) => {
368                    v[out] = v[arg].floor();
369                }
370                RegOp::CeilReg(out, arg) => {
371                    v[out] = v[arg].ceil();
372                }
373                RegOp::RoundReg(out, arg) => {
374                    v[out] = v[arg].round();
375                }
376                RegOp::SinReg(out, arg) => {
377                    v[out] = v[arg].sin();
378                }
379                RegOp::CosReg(out, arg) => {
380                    v[out] = v[arg].cos();
381                }
382                RegOp::TanReg(out, arg) => {
383                    v[out] = v[arg].tan();
384                }
385                RegOp::AsinReg(out, arg) => {
386                    v[out] = v[arg].asin();
387                }
388                RegOp::AcosReg(out, arg) => {
389                    v[out] = v[arg].acos();
390                }
391                RegOp::AtanReg(out, arg) => {
392                    v[out] = v[arg].atan();
393                }
394                RegOp::ExpReg(out, arg) => {
395                    v[out] = v[arg].exp();
396                }
397                RegOp::LnReg(out, arg) => {
398                    v[out] = v[arg].ln();
399                }
400                RegOp::NotReg(out, arg) => {
401                    v[out] = if !v[arg].contains(0.0) && !v[arg].has_nan() {
402                        Interval::new(0.0, 0.0)
403                    } else if v[arg].lower() == 0.0 && v[arg].upper() == 0.0 {
404                        Interval::new(1.0, 1.0)
405                    } else {
406                        Interval::new(0.0, 1.0)
407                    };
408                }
409                RegOp::CopyReg(out, arg) => v[out] = v[arg],
410                RegOp::AddRegImm(out, arg, imm) => {
411                    v[out] = v[arg] + imm.into();
412                }
413                RegOp::MulRegImm(out, arg, imm) => {
414                    v[out] = v[arg] * imm;
415                }
416                RegOp::DivRegImm(out, arg, imm) => {
417                    v[out] = v[arg] / imm.into();
418                }
419                RegOp::DivImmReg(out, arg, imm) => {
420                    let imm: Interval = imm.into();
421                    v[out] = imm / v[arg];
422                }
423                RegOp::AtanRegImm(out, arg, imm) => {
424                    v[out] = v[arg].atan2(imm.into());
425                }
426                RegOp::AtanImmReg(out, arg, imm) => {
427                    let imm: Interval = imm.into();
428                    v[out] = imm.atan2(v[arg]);
429                }
430                RegOp::AtanRegReg(out, lhs, rhs) => {
431                    v[out] = v[lhs].atan2(v[rhs]);
432                }
433                RegOp::SubImmReg(out, arg, imm) => {
434                    v[out] = Interval::from(imm) - v[arg];
435                }
436                RegOp::SubRegImm(out, arg, imm) => {
437                    v[out] = v[arg] - imm.into();
438                }
439                RegOp::MinRegImm(out, arg, imm) => {
440                    let (value, choice) = v[arg].min_choice(imm.into());
441                    v[out] = value;
442                    *choices.next().unwrap() |= choice;
443                    simplify |= choice != Choice::Both;
444                }
445                RegOp::MaxRegImm(out, arg, imm) => {
446                    let (value, choice) = v[arg].max_choice(imm.into());
447                    v[out] = value;
448                    *choices.next().unwrap() |= choice;
449                    simplify |= choice != Choice::Both;
450                }
451                RegOp::AndRegReg(out, lhs, rhs) => {
452                    let (value, choice) = v[lhs].and_choice(v[rhs]);
453                    v[out] = value;
454                    *choices.next().unwrap() |= choice;
455                    simplify |= choice != Choice::Both;
456                }
457                RegOp::AndRegImm(out, arg, imm) => {
458                    let (value, choice) = v[arg].and_choice(imm.into());
459                    v[out] = value;
460                    *choices.next().unwrap() |= choice;
461                    simplify |= choice != Choice::Both;
462                }
463                RegOp::OrRegReg(out, lhs, rhs) => {
464                    let (value, choice) = v[lhs].or_choice(v[rhs]);
465                    v[out] = value;
466                    *choices.next().unwrap() |= choice;
467                    simplify |= choice != Choice::Both;
468                }
469                RegOp::OrRegImm(out, arg, imm) => {
470                    let (value, choice) = v[arg].or_choice(imm.into());
471                    v[out] = value;
472                    *choices.next().unwrap() |= choice;
473                    simplify |= choice != Choice::Both;
474                }
475                RegOp::ModRegReg(out, lhs, rhs) => {
476                    v[out] = v[lhs].rem_euclid(v[rhs]);
477                }
478                RegOp::ModRegImm(out, arg, imm) => {
479                    v[out] = v[arg].rem_euclid(imm.into());
480                }
481                RegOp::ModImmReg(out, arg, imm) => {
482                    v[out] = Interval::from(imm).rem_euclid(v[arg]);
483                }
484                RegOp::AddRegReg(out, lhs, rhs) => v[out] = v[lhs] + v[rhs],
485                RegOp::MulRegReg(out, lhs, rhs) => v[out] = v[lhs] * v[rhs],
486                RegOp::DivRegReg(out, lhs, rhs) => v[out] = v[lhs] / v[rhs],
487                RegOp::SubRegReg(out, lhs, rhs) => v[out] = v[lhs] - v[rhs],
488                RegOp::CompareRegReg(out, lhs, rhs) => {
489                    v[out] = if v[lhs].has_nan() || v[rhs].has_nan() {
490                        f32::NAN.into()
491                    } else if v[lhs].upper() < v[rhs].lower() {
492                        Interval::from(-1.0)
493                    } else if v[lhs].lower() > v[rhs].upper() {
494                        Interval::from(1.0)
495                    } else {
496                        Interval::new(-1.0, 1.0)
497                    };
498                }
499                RegOp::CompareRegImm(out, arg, imm) => {
500                    v[out] = if v[arg].has_nan() || imm.is_nan() {
501                        f32::NAN.into()
502                    } else if v[arg].upper() < imm {
503                        Interval::from(-1.0)
504                    } else if v[arg].lower() > imm {
505                        Interval::from(1.0)
506                    } else {
507                        Interval::new(-1.0, 1.0)
508                    };
509                }
510                RegOp::CompareImmReg(out, arg, imm) => {
511                    v[out] = if v[arg].has_nan() || imm.is_nan() {
512                        f32::NAN.into()
513                    } else if imm < v[arg].lower() {
514                        Interval::from(-1.0)
515                    } else if imm > v[arg].upper() {
516                        Interval::from(1.0)
517                    } else {
518                        Interval::new(-1.0, 1.0)
519                    };
520                }
521                RegOp::MinRegReg(out, lhs, rhs) => {
522                    let (value, choice) = v[lhs].min_choice(v[rhs]);
523                    v[out] = value;
524                    *choices.next().unwrap() |= choice;
525                    simplify |= choice != Choice::Both;
526                }
527                RegOp::MaxRegReg(out, lhs, rhs) => {
528                    let (value, choice) = v[lhs].max_choice(v[rhs]);
529                    v[out] = value;
530                    *choices.next().unwrap() |= choice;
531                    simplify |= choice != Choice::Both;
532                }
533                RegOp::CopyImm(out, imm) => {
534                    v[out] = imm.into();
535                }
536                RegOp::Load(out, mem) => {
537                    v[out] = v[mem];
538                }
539                RegOp::Store(out, mem) => {
540                    v[mem] = v[out];
541                }
542            }
543        }
544        Ok((
545            &self.0.out,
546            if simplify {
547                Some(&self.0.choices)
548            } else {
549                None
550            },
551        ))
552    }
553}
554
555/// VM-based tracing evaluator for single points
556#[derive(Default)]
557pub struct VmPointEval<const N: usize>(TracingVmEval<f32>);
558impl<const N: usize> TracingEvaluator for VmPointEval<N> {
559    type Data = f32;
560    type Tape = GenericVmTape<N>;
561    type Trace = VmTrace;
562    type TapeStorage = EmptyTapeStorage;
563
564    #[inline]
565    fn eval(
566        &mut self,
567        tape: &Self::Tape,
568        vars: &[f32],
569    ) -> Result<(&[f32], Option<&VmTrace>), TracingEvalError> {
570        tape.vars().check_tracing_arguments(vars)?;
571        let tape = tape.data();
572        self.0.resize_slots(tape);
573
574        let mut choices = self.0.choices.as_mut_slice().iter_mut();
575        let mut simplify = false;
576        let mut v = SlotArray(&mut self.0.slots);
577        for op in tape.iter_asm() {
578            match op {
579                RegOp::Output(arg, i) => {
580                    self.0.out[i as usize] = v[arg];
581                }
582                RegOp::Input(out, i) => {
583                    v[out] = vars[i as usize];
584                }
585                RegOp::NegReg(out, arg) => {
586                    v[out] = -v[arg];
587                }
588                RegOp::AbsReg(out, arg) => {
589                    v[out] = v[arg].abs();
590                }
591                RegOp::RecipReg(out, arg) => {
592                    v[out] = 1.0 / v[arg];
593                }
594                RegOp::SqrtReg(out, arg) => {
595                    v[out] = v[arg].sqrt();
596                }
597                RegOp::SquareReg(out, arg) => {
598                    let s = v[arg];
599                    v[out] = s * s;
600                }
601                RegOp::FloorReg(out, arg) => {
602                    v[out] = v[arg].floor();
603                }
604                RegOp::CeilReg(out, arg) => {
605                    v[out] = v[arg].ceil();
606                }
607                RegOp::RoundReg(out, arg) => {
608                    v[out] = v[arg].round();
609                }
610                RegOp::SinReg(out, arg) => {
611                    v[out] = v[arg].sin();
612                }
613                RegOp::CosReg(out, arg) => {
614                    v[out] = v[arg].cos();
615                }
616                RegOp::TanReg(out, arg) => {
617                    v[out] = v[arg].tan();
618                }
619                RegOp::AsinReg(out, arg) => {
620                    v[out] = v[arg].asin();
621                }
622                RegOp::AcosReg(out, arg) => {
623                    v[out] = v[arg].acos();
624                }
625                RegOp::AtanReg(out, arg) => {
626                    v[out] = v[arg].atan();
627                }
628                RegOp::ExpReg(out, arg) => {
629                    v[out] = v[arg].exp();
630                }
631                RegOp::LnReg(out, arg) => {
632                    v[out] = v[arg].ln();
633                }
634                RegOp::NotReg(out, arg) => v[out] = (v[arg] == 0.0).into(),
635                RegOp::CopyReg(out, arg) => {
636                    v[out] = v[arg];
637                }
638                RegOp::AddRegImm(out, arg, imm) => {
639                    v[out] = v[arg] + imm;
640                }
641                RegOp::MulRegImm(out, arg, imm) => {
642                    v[out] = v[arg] * imm;
643                }
644                RegOp::DivRegImm(out, arg, imm) => {
645                    v[out] = v[arg] / imm;
646                }
647                RegOp::DivImmReg(out, arg, imm) => {
648                    v[out] = imm / v[arg];
649                }
650                RegOp::AtanRegImm(out, arg, imm) => {
651                    v[out] = v[arg].atan2(imm);
652                }
653                RegOp::AtanImmReg(out, arg, imm) => {
654                    v[out] = imm.atan2(v[arg]);
655                }
656                RegOp::AtanRegReg(out, lhs, rhs) => {
657                    v[out] = v[lhs].atan2(v[rhs]);
658                }
659                RegOp::SubImmReg(out, arg, imm) => {
660                    v[out] = imm - v[arg];
661                }
662                RegOp::SubRegImm(out, arg, imm) => {
663                    v[out] = v[arg] - imm;
664                }
665                RegOp::MinRegImm(out, arg, imm) => {
666                    let a = v[arg];
667                    let (choice, value) = if a < imm {
668                        (Choice::Left, a)
669                    } else if imm < a {
670                        (Choice::Right, imm)
671                    } else {
672                        (
673                            Choice::Both,
674                            if a.is_nan() || imm.is_nan() {
675                                f32::NAN
676                            } else {
677                                imm
678                            },
679                        )
680                    };
681                    v[out] = value;
682                    *choices.next().unwrap() |= choice;
683                    simplify |= choice != Choice::Both;
684                }
685                RegOp::MaxRegImm(out, arg, imm) => {
686                    let a = v[arg];
687                    let (choice, value) = if a > imm {
688                        (Choice::Left, a)
689                    } else if imm > a {
690                        (Choice::Right, imm)
691                    } else {
692                        (
693                            Choice::Both,
694                            if a.is_nan() || imm.is_nan() {
695                                f32::NAN
696                            } else {
697                                imm
698                            },
699                        )
700                    };
701                    v[out] = value;
702                    *choices.next().unwrap() |= choice;
703                    simplify |= choice != Choice::Both;
704                }
705                RegOp::AndRegImm(out, arg, imm) => {
706                    let a = v[arg];
707                    let (choice, value) = if a == 0.0 {
708                        (Choice::Left, a)
709                    } else {
710                        (Choice::Right, imm)
711                    };
712                    v[out] = value;
713                    *choices.next().unwrap() |= choice;
714                    simplify |= choice != Choice::Both;
715                }
716                RegOp::OrRegImm(out, arg, imm) => {
717                    let a = v[arg];
718                    let (choice, value) = if a != 0.0 {
719                        (Choice::Left, a)
720                    } else {
721                        (Choice::Right, imm)
722                    };
723                    v[out] = value;
724                    *choices.next().unwrap() |= choice;
725                    simplify |= choice != Choice::Both;
726                }
727                RegOp::ModRegReg(out, lhs, rhs) => {
728                    v[out] = v[lhs].rem_euclid(v[rhs]);
729                }
730                RegOp::ModRegImm(out, arg, imm) => {
731                    v[out] = v[arg].rem_euclid(imm);
732                }
733                RegOp::ModImmReg(out, arg, imm) => {
734                    v[out] = imm.rem_euclid(v[arg]);
735                }
736                RegOp::AddRegReg(out, lhs, rhs) => {
737                    v[out] = v[lhs] + v[rhs];
738                }
739                RegOp::MulRegReg(out, lhs, rhs) => {
740                    v[out] = v[lhs] * v[rhs];
741                }
742                RegOp::DivRegReg(out, lhs, rhs) => {
743                    v[out] = v[lhs] / v[rhs];
744                }
745                RegOp::CompareRegReg(out, lhs, rhs) => {
746                    v[out] = v[lhs]
747                        .partial_cmp(&v[rhs])
748                        .map(|c| c as i8 as f32)
749                        .unwrap_or(f32::NAN)
750                }
751                RegOp::CompareRegImm(out, arg, imm) => {
752                    v[out] = v[arg]
753                        .partial_cmp(&imm)
754                        .map(|c| c as i8 as f32)
755                        .unwrap_or(f32::NAN)
756                }
757                RegOp::CompareImmReg(out, arg, imm) => {
758                    v[out] = imm
759                        .partial_cmp(&v[arg])
760                        .map(|c| c as i8 as f32)
761                        .unwrap_or(f32::NAN)
762                }
763                RegOp::SubRegReg(out, lhs, rhs) => {
764                    v[out] = v[lhs] - v[rhs];
765                }
766                RegOp::MinRegReg(out, lhs, rhs) => {
767                    let a = v[lhs];
768                    let b = v[rhs];
769                    let (choice, value) = if a < b {
770                        (Choice::Left, a)
771                    } else if b < a {
772                        (Choice::Right, b)
773                    } else {
774                        (
775                            Choice::Both,
776                            if a.is_nan() || b.is_nan() {
777                                f32::NAN
778                            } else {
779                                b
780                            },
781                        )
782                    };
783                    v[out] = value;
784                    *choices.next().unwrap() |= choice;
785                    simplify |= choice != Choice::Both;
786                }
787                RegOp::MaxRegReg(out, lhs, rhs) => {
788                    let a = v[lhs];
789                    let b = v[rhs];
790                    let (choice, value) = if a > b {
791                        (Choice::Left, a)
792                    } else if b > a {
793                        (Choice::Right, b)
794                    } else {
795                        (
796                            Choice::Both,
797                            if a.is_nan() || b.is_nan() {
798                                f32::NAN
799                            } else {
800                                b
801                            },
802                        )
803                    };
804                    v[out] = value;
805                    *choices.next().unwrap() |= choice;
806                    simplify |= choice != Choice::Both;
807                }
808                RegOp::AndRegReg(out, lhs, rhs) => {
809                    let a = v[lhs];
810                    let b = v[rhs];
811                    let (choice, value) = if a == 0.0 {
812                        (Choice::Left, a)
813                    } else {
814                        (Choice::Right, b)
815                    };
816                    v[out] = value;
817                    *choices.next().unwrap() |= choice;
818                    simplify |= choice != Choice::Both;
819                }
820                RegOp::OrRegReg(out, lhs, rhs) => {
821                    let a = v[lhs];
822                    let b = v[rhs];
823                    let (choice, value) = if a != 0.0 {
824                        (Choice::Left, a)
825                    } else {
826                        (Choice::Right, b)
827                    };
828                    v[out] = value;
829                    *choices.next().unwrap() |= choice;
830                    simplify |= choice != Choice::Both;
831                }
832                RegOp::CopyImm(out, imm) => {
833                    v[out] = imm;
834                }
835                RegOp::Load(out, mem) => {
836                    v[out] = v[mem];
837                }
838                RegOp::Store(out, mem) => {
839                    v[mem] = v[out];
840                }
841            }
842        }
843        Ok((
844            &self.0.out,
845            if simplify {
846                Some(&self.0.choices)
847            } else {
848                None
849            },
850        ))
851    }
852}
853
854////////////////////////////////////////////////////////////////////////////////
855
856/// Bulk evaluator for VM tapes
857#[derive(Default)]
858struct BulkVmEval<T> {
859    /// Workspace for data
860    slots: Vec<Vec<T>>,
861
862    /// Output array
863    out: Vec<Vec<T>>,
864}
865
866impl<T: From<f32> + Clone> BulkVmEval<T> {
867    /// Reserves slots for the given tape and slice size
868    fn resize_slots<const N: usize>(&mut self, tape: &VmData<N>, size: usize) {
869        self.slots
870            .resize_with(tape.slot_count(), || vec![f32::NAN.into(); size]);
871        for s in self.slots.iter_mut() {
872            s.resize(size, f32::NAN.into());
873        }
874
875        self.out
876            .resize_with(tape.output_count(), || vec![f32::NAN.into(); size]);
877        for o in self.out.iter_mut() {
878            o.resize(size, f32::NAN.into());
879        }
880    }
881}
882
883/// VM-based bulk evaluator for arrays of points, yielding point values
884#[derive(Default)]
885pub struct VmFloatSliceEval<const N: usize>(BulkVmEval<f32>);
886impl<const N: usize> BulkEvaluator for VmFloatSliceEval<N> {
887    type Data = f32;
888    type Tape = GenericVmTape<N>;
889    type TapeStorage = EmptyTapeStorage;
890
891    #[inline]
892    fn eval<V: std::ops::Deref<Target = [Self::Data]>>(
893        &mut self,
894        tape: &Self::Tape,
895        vars: &[V],
896    ) -> Result<BulkOutput<'_, f32>, BulkEvalError> {
897        tape.vars().check_bulk_arguments(vars)?;
898        let tape = tape.data();
899
900        let size = vars.first().map(|v| v.len()).unwrap_or(0);
901        self.0.resize_slots(tape, size);
902
903        let mut v = SlotArray(&mut self.0.slots);
904        for op in tape.iter_asm() {
905            match op {
906                RegOp::Output(arg, i) => {
907                    self.0.out[i as usize][0..size]
908                        .copy_from_slice(&v[arg][0..size]);
909                }
910                RegOp::Input(out, i) => {
911                    v[out][0..size].copy_from_slice(&vars[i as usize]);
912                }
913                RegOp::NegReg(out, arg) => {
914                    for i in 0..size {
915                        v[out][i] = -v[arg][i];
916                    }
917                }
918                RegOp::AbsReg(out, arg) => {
919                    for i in 0..size {
920                        v[out][i] = v[arg][i].abs();
921                    }
922                }
923                RegOp::RecipReg(out, arg) => {
924                    for i in 0..size {
925                        v[out][i] = 1.0 / v[arg][i];
926                    }
927                }
928                RegOp::SqrtReg(out, arg) => {
929                    for i in 0..size {
930                        v[out][i] = v[arg][i].sqrt();
931                    }
932                }
933                RegOp::SquareReg(out, arg) => {
934                    for i in 0..size {
935                        let s = v[arg][i];
936                        v[out][i] = s * s;
937                    }
938                }
939                RegOp::FloorReg(out, arg) => {
940                    for i in 0..size {
941                        v[out][i] = v[arg][i].floor();
942                    }
943                }
944                RegOp::CeilReg(out, arg) => {
945                    for i in 0..size {
946                        v[out][i] = v[arg][i].ceil();
947                    }
948                }
949                RegOp::RoundReg(out, arg) => {
950                    for i in 0..size {
951                        v[out][i] = v[arg][i].round();
952                    }
953                }
954                RegOp::SinReg(out, arg) => {
955                    for i in 0..size {
956                        v[out][i] = v[arg][i].sin();
957                    }
958                }
959                RegOp::CosReg(out, arg) => {
960                    for i in 0..size {
961                        v[out][i] = v[arg][i].cos();
962                    }
963                }
964                RegOp::TanReg(out, arg) => {
965                    for i in 0..size {
966                        v[out][i] = v[arg][i].tan();
967                    }
968                }
969                RegOp::AsinReg(out, arg) => {
970                    for i in 0..size {
971                        v[out][i] = v[arg][i].asin();
972                    }
973                }
974                RegOp::AcosReg(out, arg) => {
975                    for i in 0..size {
976                        v[out][i] = v[arg][i].acos();
977                    }
978                }
979                RegOp::AtanReg(out, arg) => {
980                    for i in 0..size {
981                        v[out][i] = v[arg][i].atan();
982                    }
983                }
984                RegOp::ExpReg(out, arg) => {
985                    for i in 0..size {
986                        v[out][i] = v[arg][i].exp();
987                    }
988                }
989                RegOp::LnReg(out, arg) => {
990                    for i in 0..size {
991                        v[out][i] = v[arg][i].ln();
992                    }
993                }
994                RegOp::NotReg(out, arg) => {
995                    for i in 0..size {
996                        v[out][i] = (v[arg][i] == 0.0).into();
997                    }
998                }
999                RegOp::CopyReg(out, arg) => {
1000                    for i in 0..size {
1001                        v[out][i] = v[arg][i];
1002                    }
1003                }
1004                RegOp::AddRegImm(out, arg, imm) => {
1005                    for i in 0..size {
1006                        v[out][i] = v[arg][i] + imm;
1007                    }
1008                }
1009                RegOp::MulRegImm(out, arg, imm) => {
1010                    for i in 0..size {
1011                        v[out][i] = v[arg][i] * imm;
1012                    }
1013                }
1014                RegOp::DivRegImm(out, arg, imm) => {
1015                    for i in 0..size {
1016                        v[out][i] = v[arg][i] / imm;
1017                    }
1018                }
1019                RegOp::DivImmReg(out, arg, imm) => {
1020                    for i in 0..size {
1021                        v[out][i] = imm / v[arg][i];
1022                    }
1023                }
1024                RegOp::AtanRegImm(out, arg, imm) => {
1025                    for i in 0..size {
1026                        v[out][i] = v[arg][i].atan2(imm);
1027                    }
1028                }
1029                RegOp::AtanImmReg(out, arg, imm) => {
1030                    for i in 0..size {
1031                        v[out][i] = imm.atan2(v[arg][i]);
1032                    }
1033                }
1034                RegOp::AtanRegReg(out, lhs, rhs) => {
1035                    for i in 0..size {
1036                        v[out][i] = v[lhs][i].atan2(v[rhs][i]);
1037                    }
1038                }
1039                RegOp::SubImmReg(out, arg, imm) => {
1040                    for i in 0..size {
1041                        v[out][i] = imm - v[arg][i];
1042                    }
1043                }
1044                RegOp::SubRegImm(out, arg, imm) => {
1045                    for i in 0..size {
1046                        v[out][i] = v[arg][i] - imm;
1047                    }
1048                }
1049                RegOp::CompareImmReg(out, arg, imm) => {
1050                    for i in 0..size {
1051                        v[out][i] = imm
1052                            .partial_cmp(&v[arg][i])
1053                            .map(|c| c as i8 as f32)
1054                            .unwrap_or(f32::NAN)
1055                    }
1056                }
1057                RegOp::CompareRegImm(out, arg, imm) => {
1058                    for i in 0..size {
1059                        v[out][i] = v[arg][i]
1060                            .partial_cmp(&imm)
1061                            .map(|c| c as i8 as f32)
1062                            .unwrap_or(f32::NAN)
1063                    }
1064                }
1065                RegOp::MinRegImm(out, arg, imm) => {
1066                    for i in 0..size {
1067                        v[out][i] = if v[arg][i].is_nan() || imm.is_nan() {
1068                            f32::NAN
1069                        } else {
1070                            v[arg][i].min(imm)
1071                        };
1072                    }
1073                }
1074                RegOp::MaxRegImm(out, arg, imm) => {
1075                    for i in 0..size {
1076                        v[out][i] = if v[arg][i].is_nan() || imm.is_nan() {
1077                            f32::NAN
1078                        } else {
1079                            v[arg][i].max(imm)
1080                        };
1081                    }
1082                }
1083                RegOp::AndRegImm(out, arg, imm) => {
1084                    for i in 0..size {
1085                        v[out][i] =
1086                            if v[arg][i] == 0.0 { v[arg][i] } else { imm };
1087                    }
1088                }
1089                RegOp::OrRegImm(out, arg, imm) => {
1090                    for i in 0..size {
1091                        v[out][i] =
1092                            if v[arg][i] != 0.0 { v[arg][i] } else { imm };
1093                    }
1094                }
1095                RegOp::ModRegReg(out, lhs, rhs) => {
1096                    for i in 0..size {
1097                        v[out][i] = v[lhs][i].rem_euclid(v[rhs][i]);
1098                    }
1099                }
1100                RegOp::ModRegImm(out, arg, imm) => {
1101                    for i in 0..size {
1102                        v[out][i] = v[arg][i].rem_euclid(imm);
1103                    }
1104                }
1105                RegOp::ModImmReg(out, arg, imm) => {
1106                    for i in 0..size {
1107                        v[out][i] = imm.rem_euclid(v[arg][i]);
1108                    }
1109                }
1110                RegOp::AddRegReg(out, lhs, rhs) => {
1111                    for i in 0..size {
1112                        v[out][i] = v[lhs][i] + v[rhs][i];
1113                    }
1114                }
1115                RegOp::MulRegReg(out, lhs, rhs) => {
1116                    for i in 0..size {
1117                        v[out][i] = v[lhs][i] * v[rhs][i];
1118                    }
1119                }
1120                RegOp::DivRegReg(out, lhs, rhs) => {
1121                    for i in 0..size {
1122                        v[out][i] = v[lhs][i] / v[rhs][i];
1123                    }
1124                }
1125                RegOp::SubRegReg(out, lhs, rhs) => {
1126                    for i in 0..size {
1127                        v[out][i] = v[lhs][i] - v[rhs][i];
1128                    }
1129                }
1130                RegOp::CompareRegReg(out, lhs, rhs) => {
1131                    for i in 0..size {
1132                        v[out][i] = v[lhs][i]
1133                            .partial_cmp(&v[rhs][i])
1134                            .map(|c| c as i8 as f32)
1135                            .unwrap_or(f32::NAN)
1136                    }
1137                }
1138                RegOp::MinRegReg(out, lhs, rhs) => {
1139                    for i in 0..size {
1140                        v[out][i] = if v[lhs][i].is_nan() || v[rhs][i].is_nan()
1141                        {
1142                            f32::NAN
1143                        } else {
1144                            v[lhs][i].min(v[rhs][i])
1145                        };
1146                    }
1147                }
1148                RegOp::MaxRegReg(out, lhs, rhs) => {
1149                    for i in 0..size {
1150                        v[out][i] = if v[lhs][i].is_nan() || v[rhs][i].is_nan()
1151                        {
1152                            f32::NAN
1153                        } else {
1154                            v[lhs][i].max(v[rhs][i])
1155                        };
1156                    }
1157                }
1158                RegOp::AndRegReg(out, lhs, rhs) => {
1159                    for i in 0..size {
1160                        v[out][i] = if v[lhs][i] == 0.0 {
1161                            v[lhs][i]
1162                        } else {
1163                            v[rhs][i]
1164                        };
1165                    }
1166                }
1167                RegOp::OrRegReg(out, lhs, rhs) => {
1168                    for i in 0..size {
1169                        v[out][i] = if v[lhs][i] != 0.0 {
1170                            v[lhs][i]
1171                        } else {
1172                            v[rhs][i]
1173                        };
1174                    }
1175                }
1176                RegOp::CopyImm(out, imm) => {
1177                    for i in 0..size {
1178                        v[out][i] = imm;
1179                    }
1180                }
1181                RegOp::Load(out, mem) => {
1182                    for i in 0..size {
1183                        v[out][i] = v[mem][i];
1184                    }
1185                }
1186                RegOp::Store(out, mem) => {
1187                    for i in 0..size {
1188                        v[mem][i] = v[out][i];
1189                    }
1190                }
1191            }
1192        }
1193        Ok(BulkOutput::new(&self.0.out, size))
1194    }
1195}
1196
1197/// VM-based bulk evaluator for arrays of points, yielding gradient values
1198#[derive(Default)]
1199pub struct VmGradSliceEval<const N: usize>(BulkVmEval<Grad>);
1200impl<const N: usize> BulkEvaluator for VmGradSliceEval<N> {
1201    type Data = Grad;
1202    type Tape = GenericVmTape<N>;
1203    type TapeStorage = EmptyTapeStorage;
1204
1205    #[inline]
1206    fn eval<V: std::ops::Deref<Target = [Self::Data]>>(
1207        &mut self,
1208        tape: &Self::Tape,
1209        vars: &[V],
1210    ) -> Result<BulkOutput<'_, Grad>, BulkEvalError> {
1211        tape.vars().check_bulk_arguments(vars)?;
1212        let tape = tape.data();
1213        let size = vars.first().map(|v| v.len()).unwrap_or(0);
1214        self.0.resize_slots(tape, size);
1215
1216        let mut v = SlotArray(&mut self.0.slots);
1217        for op in tape.iter_asm() {
1218            match op {
1219                RegOp::Output(arg, i) => {
1220                    self.0.out[i as usize][0..size]
1221                        .copy_from_slice(&v[arg][0..size]);
1222                }
1223                RegOp::Input(out, i) => {
1224                    v[out][0..size].copy_from_slice(&vars[i as usize]);
1225                }
1226                RegOp::NegReg(out, arg) => {
1227                    for i in 0..size {
1228                        v[out][i] = -v[arg][i];
1229                    }
1230                }
1231                RegOp::AbsReg(out, arg) => {
1232                    for i in 0..size {
1233                        v[out][i] = v[arg][i].abs();
1234                    }
1235                }
1236                RegOp::RecipReg(out, arg) => {
1237                    let one: Grad = 1.0.into();
1238                    for i in 0..size {
1239                        v[out][i] = one / v[arg][i];
1240                    }
1241                }
1242                RegOp::SqrtReg(out, arg) => {
1243                    for i in 0..size {
1244                        v[out][i] = v[arg][i].sqrt();
1245                    }
1246                }
1247                RegOp::SquareReg(out, arg) => {
1248                    for i in 0..size {
1249                        let s = v[arg][i];
1250                        v[out][i] = s * s;
1251                    }
1252                }
1253                RegOp::FloorReg(out, arg) => {
1254                    for i in 0..size {
1255                        v[out][i] = v[arg][i].floor();
1256                    }
1257                }
1258                RegOp::CeilReg(out, arg) => {
1259                    for i in 0..size {
1260                        v[out][i] = v[arg][i].ceil();
1261                    }
1262                }
1263                RegOp::RoundReg(out, arg) => {
1264                    for i in 0..size {
1265                        v[out][i] = v[arg][i].round();
1266                    }
1267                }
1268                RegOp::SinReg(out, arg) => {
1269                    for i in 0..size {
1270                        v[out][i] = v[arg][i].sin();
1271                    }
1272                }
1273                RegOp::CosReg(out, arg) => {
1274                    for i in 0..size {
1275                        v[out][i] = v[arg][i].cos();
1276                    }
1277                }
1278                RegOp::TanReg(out, arg) => {
1279                    for i in 0..size {
1280                        v[out][i] = v[arg][i].tan();
1281                    }
1282                }
1283                RegOp::AsinReg(out, arg) => {
1284                    for i in 0..size {
1285                        v[out][i] = v[arg][i].asin();
1286                    }
1287                }
1288                RegOp::AcosReg(out, arg) => {
1289                    for i in 0..size {
1290                        v[out][i] = v[arg][i].acos();
1291                    }
1292                }
1293                RegOp::AtanReg(out, arg) => {
1294                    for i in 0..size {
1295                        v[out][i] = v[arg][i].atan();
1296                    }
1297                }
1298                RegOp::ExpReg(out, arg) => {
1299                    for i in 0..size {
1300                        v[out][i] = v[arg][i].exp();
1301                    }
1302                }
1303                RegOp::LnReg(out, arg) => {
1304                    for i in 0..size {
1305                        v[out][i] = v[arg][i].ln();
1306                    }
1307                }
1308                RegOp::NotReg(out, arg) => {
1309                    for i in 0..size {
1310                        v[out][i] = f32::from(v[arg][i].v == 0.0).into();
1311                    }
1312                }
1313                RegOp::CopyReg(out, arg) => {
1314                    for i in 0..size {
1315                        v[out][i] = v[arg][i];
1316                    }
1317                }
1318                RegOp::AddRegImm(out, arg, imm) => {
1319                    for i in 0..size {
1320                        v[out][i] = v[arg][i] + imm.into();
1321                    }
1322                }
1323                RegOp::MulRegImm(out, arg, imm) => {
1324                    for i in 0..size {
1325                        v[out][i] = v[arg][i] * imm;
1326                    }
1327                }
1328                RegOp::DivRegImm(out, arg, imm) => {
1329                    for i in 0..size {
1330                        v[out][i] = v[arg][i] / imm.into();
1331                    }
1332                }
1333                RegOp::DivImmReg(out, arg, imm) => {
1334                    let imm = Grad::from(imm);
1335                    for i in 0..size {
1336                        v[out][i] = imm / v[arg][i];
1337                    }
1338                }
1339                RegOp::AtanRegImm(out, arg, imm) => {
1340                    let imm = Grad::from(imm);
1341                    for i in 0..size {
1342                        v[out][i] = v[arg][i].atan2(imm);
1343                    }
1344                }
1345                RegOp::AtanImmReg(out, arg, imm) => {
1346                    let imm = Grad::from(imm);
1347                    for i in 0..size {
1348                        v[out][i] = imm.atan2(v[arg][i]);
1349                    }
1350                }
1351                RegOp::AtanRegReg(out, lhs, rhs) => {
1352                    for i in 0..size {
1353                        v[out][i] = v[lhs][i].atan2(v[rhs][i]);
1354                    }
1355                }
1356                RegOp::SubImmReg(out, arg, imm) => {
1357                    let imm: Grad = imm.into();
1358                    for i in 0..size {
1359                        v[out][i] = imm - v[arg][i];
1360                    }
1361                }
1362                RegOp::SubRegImm(out, arg, imm) => {
1363                    let imm: Grad = imm.into();
1364                    for i in 0..size {
1365                        v[out][i] = v[arg][i] - imm;
1366                    }
1367                }
1368                RegOp::CompareImmReg(out, arg, imm) => {
1369                    for i in 0..size {
1370                        let p = imm
1371                            .partial_cmp(&v[arg][i].v)
1372                            .map(|c| c as i8 as f32)
1373                            .unwrap_or(f32::NAN);
1374                        v[out][i] = Grad::new(p, 0.0, 0.0, 0.0);
1375                    }
1376                }
1377                RegOp::CompareRegImm(out, arg, imm) => {
1378                    for i in 0..size {
1379                        let p = v[arg][i]
1380                            .v
1381                            .partial_cmp(&imm)
1382                            .map(|c| c as i8 as f32)
1383                            .unwrap_or(f32::NAN);
1384                        v[out][i] = Grad::new(p, 0.0, 0.0, 0.0);
1385                    }
1386                }
1387                RegOp::MinRegImm(out, arg, imm) => {
1388                    let imm: Grad = imm.into();
1389                    for i in 0..size {
1390                        v[out][i] = if v[arg][i].v.is_nan() || imm.v.is_nan() {
1391                            f32::NAN.into()
1392                        } else {
1393                            v[arg][i].min(imm)
1394                        };
1395                    }
1396                }
1397                RegOp::MaxRegImm(out, arg, imm) => {
1398                    let imm: Grad = imm.into();
1399                    for i in 0..size {
1400                        v[out][i] = if v[arg][i].v.is_nan() || imm.v.is_nan() {
1401                            f32::NAN.into()
1402                        } else {
1403                            v[arg][i].max(imm)
1404                        };
1405                    }
1406                }
1407                RegOp::ModRegReg(out, lhs, rhs) => {
1408                    for i in 0..size {
1409                        v[out][i] = v[lhs][i].rem_euclid(v[rhs][i]);
1410                    }
1411                }
1412                RegOp::ModRegImm(out, arg, imm) => {
1413                    for i in 0..size {
1414                        v[out][i] = v[arg][i].rem_euclid(imm.into());
1415                    }
1416                }
1417                RegOp::ModImmReg(out, arg, imm) => {
1418                    for i in 0..size {
1419                        v[out][i] = Grad::from(imm).rem_euclid(v[arg][i]);
1420                    }
1421                }
1422                RegOp::AddRegReg(out, lhs, rhs) => {
1423                    for i in 0..size {
1424                        v[out][i] = v[lhs][i] + v[rhs][i];
1425                    }
1426                }
1427                RegOp::MulRegReg(out, lhs, rhs) => {
1428                    for i in 0..size {
1429                        v[out][i] = v[lhs][i] * v[rhs][i];
1430                    }
1431                }
1432                RegOp::AndRegReg(out, lhs, rhs) => {
1433                    for i in 0..size {
1434                        v[out][i] = if v[lhs][i].v == 0.0 {
1435                            v[lhs][i]
1436                        } else {
1437                            v[rhs][i]
1438                        };
1439                    }
1440                }
1441                RegOp::AndRegImm(out, arg, imm) => {
1442                    for i in 0..size {
1443                        v[out][i] = if v[arg][i].v == 0.0 {
1444                            v[arg][i]
1445                        } else {
1446                            imm.into()
1447                        };
1448                    }
1449                }
1450                RegOp::OrRegReg(out, lhs, rhs) => {
1451                    for i in 0..size {
1452                        v[out][i] = if v[lhs][i].v != 0.0 {
1453                            v[lhs][i]
1454                        } else {
1455                            v[rhs][i]
1456                        };
1457                    }
1458                }
1459                RegOp::OrRegImm(out, arg, imm) => {
1460                    for i in 0..size {
1461                        v[out][i] = if v[arg][i].v != 0.0 {
1462                            v[arg][i]
1463                        } else {
1464                            imm.into()
1465                        };
1466                    }
1467                }
1468                RegOp::DivRegReg(out, lhs, rhs) => {
1469                    for i in 0..size {
1470                        v[out][i] = v[lhs][i] / v[rhs][i];
1471                    }
1472                }
1473                RegOp::SubRegReg(out, lhs, rhs) => {
1474                    for i in 0..size {
1475                        v[out][i] = v[lhs][i] - v[rhs][i];
1476                    }
1477                }
1478                RegOp::CompareRegReg(out, lhs, rhs) => {
1479                    for i in 0..size {
1480                        let p = v[lhs][i]
1481                            .v
1482                            .partial_cmp(&v[rhs][i].v)
1483                            .map(|c| c as i8 as f32)
1484                            .unwrap_or(f32::NAN);
1485                        v[out][i] = Grad::new(p, 0.0, 0.0, 0.0);
1486                    }
1487                }
1488                RegOp::MinRegReg(out, lhs, rhs) => {
1489                    for i in 0..size {
1490                        v[out][i] =
1491                            if v[lhs][i].v.is_nan() || v[rhs][i].v.is_nan() {
1492                                f32::NAN.into()
1493                            } else {
1494                                v[lhs][i].min(v[rhs][i])
1495                            };
1496                    }
1497                }
1498                RegOp::MaxRegReg(out, lhs, rhs) => {
1499                    for i in 0..size {
1500                        v[out][i] =
1501                            if v[lhs][i].v.is_nan() || v[rhs][i].v.is_nan() {
1502                                f32::NAN.into()
1503                            } else {
1504                                v[lhs][i].max(v[rhs][i])
1505                            };
1506                    }
1507                }
1508                RegOp::CopyImm(out, imm) => {
1509                    let imm: Grad = imm.into();
1510                    for i in 0..size {
1511                        v[out][i] = imm;
1512                    }
1513                }
1514                RegOp::Load(out, mem) => {
1515                    for i in 0..size {
1516                        v[out][i] = v[mem][i];
1517                    }
1518                }
1519                RegOp::Store(out, mem) => {
1520                    for i in 0..size {
1521                        v[mem][i] = v[out][i];
1522                    }
1523                }
1524            }
1525        }
1526        Ok(BulkOutput::new(&self.0.out, size))
1527    }
1528}
1529
1530#[cfg(test)]
1531mod test {
1532    use super::*;
1533    crate::grad_slice_tests!(VmFunction);
1534    crate::interval_tests!(VmFunction);
1535    crate::float_slice_tests!(VmFunction);
1536    crate::point_tests!(VmFunction);
1537}