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::{FloatExt, 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] = v[arg].not();
402                }
403                RegOp::RandReg(out, arg) => {
404                    v[out] = v[arg].rand();
405                }
406                RegOp::CopyReg(out, arg) => v[out] = v[arg],
407                RegOp::AddRegImm(out, arg, imm) => {
408                    v[out] = v[arg] + imm.into();
409                }
410                RegOp::MulRegImm(out, arg, imm) => {
411                    v[out] = v[arg] * imm;
412                }
413                RegOp::DivRegImm(out, arg, imm) => {
414                    v[out] = v[arg] / imm.into();
415                }
416                RegOp::DivImmReg(out, arg, imm) => {
417                    let imm: Interval = imm.into();
418                    v[out] = imm / v[arg];
419                }
420                RegOp::AtanRegImm(out, arg, imm) => {
421                    v[out] = v[arg].atan2(imm.into());
422                }
423                RegOp::AtanImmReg(out, arg, imm) => {
424                    let imm: Interval = imm.into();
425                    v[out] = imm.atan2(v[arg]);
426                }
427                RegOp::AtanRegReg(out, lhs, rhs) => {
428                    v[out] = v[lhs].atan2(v[rhs]);
429                }
430                RegOp::SubImmReg(out, arg, imm) => {
431                    v[out] = Interval::from(imm) - v[arg];
432                }
433                RegOp::SubRegImm(out, arg, imm) => {
434                    v[out] = v[arg] - imm.into();
435                }
436                RegOp::MinRegImm(out, arg, imm) => {
437                    let (value, choice) = v[arg].min_choice(imm.into());
438                    v[out] = value;
439                    *choices.next().unwrap() |= choice;
440                    simplify |= choice != Choice::Both;
441                }
442                RegOp::MaxRegImm(out, arg, imm) => {
443                    let (value, choice) = v[arg].max_choice(imm.into());
444                    v[out] = value;
445                    *choices.next().unwrap() |= choice;
446                    simplify |= choice != Choice::Both;
447                }
448                RegOp::AndRegReg(out, lhs, rhs) => {
449                    let (value, choice) = v[lhs].and_choice(v[rhs]);
450                    v[out] = value;
451                    *choices.next().unwrap() |= choice;
452                    simplify |= choice != Choice::Both;
453                }
454                RegOp::AndRegImm(out, arg, imm) => {
455                    let (value, choice) = v[arg].and_choice(imm.into());
456                    v[out] = value;
457                    *choices.next().unwrap() |= choice;
458                    simplify |= choice != Choice::Both;
459                }
460                RegOp::OrRegReg(out, lhs, rhs) => {
461                    let (value, choice) = v[lhs].or_choice(v[rhs]);
462                    v[out] = value;
463                    *choices.next().unwrap() |= choice;
464                    simplify |= choice != Choice::Both;
465                }
466                RegOp::OrRegImm(out, arg, imm) => {
467                    let (value, choice) = v[arg].or_choice(imm.into());
468                    v[out] = value;
469                    *choices.next().unwrap() |= choice;
470                    simplify |= choice != Choice::Both;
471                }
472                RegOp::ModRegReg(out, lhs, rhs) => {
473                    v[out] = v[lhs].rem_euclid(v[rhs]);
474                }
475                RegOp::ModRegImm(out, arg, imm) => {
476                    v[out] = v[arg].rem_euclid(imm.into());
477                }
478                RegOp::ModImmReg(out, arg, imm) => {
479                    v[out] = Interval::from(imm).rem_euclid(v[arg]);
480                }
481                RegOp::AddRegReg(out, lhs, rhs) => v[out] = v[lhs] + v[rhs],
482                RegOp::MulRegReg(out, lhs, rhs) => v[out] = v[lhs] * v[rhs],
483                RegOp::DivRegReg(out, lhs, rhs) => v[out] = v[lhs] / v[rhs],
484                RegOp::SubRegReg(out, lhs, rhs) => v[out] = v[lhs] - v[rhs],
485                RegOp::CompareRegReg(out, lhs, rhs) => {
486                    v[out] = Interval::compare(v[lhs], v[rhs]);
487                }
488                RegOp::CompareRegImm(out, arg, imm) => {
489                    v[out] = Interval::compare(v[arg], imm);
490                }
491                RegOp::CompareImmReg(out, arg, imm) => {
492                    v[out] = Interval::compare(imm, v[arg]);
493                }
494                RegOp::MixRegReg(out, lhs, rhs) => {
495                    v[out] = v[lhs].mix(v[rhs]);
496                }
497                RegOp::MixRegImm(out, arg, imm) => {
498                    v[out] = v[arg].mix(imm.into());
499                }
500                RegOp::MixImmReg(out, arg, imm) => {
501                    v[out] = Interval::from(imm).mix(v[arg]);
502                }
503
504                RegOp::MinRegReg(out, lhs, rhs) => {
505                    let (value, choice) = v[lhs].min_choice(v[rhs]);
506                    v[out] = value;
507                    *choices.next().unwrap() |= choice;
508                    simplify |= choice != Choice::Both;
509                }
510                RegOp::MaxRegReg(out, lhs, rhs) => {
511                    let (value, choice) = v[lhs].max_choice(v[rhs]);
512                    v[out] = value;
513                    *choices.next().unwrap() |= choice;
514                    simplify |= choice != Choice::Both;
515                }
516                RegOp::CopyImm(out, imm) => {
517                    v[out] = imm.into();
518                }
519                RegOp::Load(out, mem) => {
520                    v[out] = v[mem];
521                }
522                RegOp::Store(out, mem) => {
523                    v[mem] = v[out];
524                }
525            }
526        }
527        Ok((
528            &self.0.out,
529            if simplify {
530                Some(&self.0.choices)
531            } else {
532                None
533            },
534        ))
535    }
536}
537
538/// VM-based tracing evaluator for single points
539#[derive(Default)]
540pub struct VmPointEval<const N: usize>(TracingVmEval<f32>);
541impl<const N: usize> TracingEvaluator for VmPointEval<N> {
542    type Data = f32;
543    type Tape = GenericVmTape<N>;
544    type Trace = VmTrace;
545    type TapeStorage = EmptyTapeStorage;
546
547    #[inline]
548    fn eval(
549        &mut self,
550        tape: &Self::Tape,
551        vars: &[f32],
552    ) -> Result<(&[f32], Option<&VmTrace>), TracingEvalError> {
553        tape.vars().check_tracing_arguments(vars)?;
554        let tape = tape.data();
555        self.0.resize_slots(tape);
556
557        let mut choices = self.0.choices.as_mut_slice().iter_mut();
558        let mut simplify = false;
559        let mut v = SlotArray(&mut self.0.slots);
560        for op in tape.iter_asm() {
561            match op {
562                RegOp::Output(arg, i) => {
563                    self.0.out[i as usize] = v[arg];
564                }
565                RegOp::Input(out, i) => {
566                    v[out] = vars[i as usize];
567                }
568                RegOp::NegReg(out, arg) => {
569                    v[out] = -v[arg];
570                }
571                RegOp::AbsReg(out, arg) => {
572                    v[out] = v[arg].abs();
573                }
574                RegOp::RecipReg(out, arg) => {
575                    v[out] = 1.0 / v[arg];
576                }
577                RegOp::SqrtReg(out, arg) => {
578                    v[out] = v[arg].sqrt();
579                }
580                RegOp::SquareReg(out, arg) => {
581                    let s = v[arg];
582                    v[out] = s * s;
583                }
584                RegOp::FloorReg(out, arg) => {
585                    v[out] = v[arg].floor();
586                }
587                RegOp::CeilReg(out, arg) => {
588                    v[out] = v[arg].ceil();
589                }
590                RegOp::RoundReg(out, arg) => {
591                    v[out] = v[arg].round();
592                }
593                RegOp::SinReg(out, arg) => {
594                    v[out] = v[arg].sin();
595                }
596                RegOp::CosReg(out, arg) => {
597                    v[out] = v[arg].cos();
598                }
599                RegOp::TanReg(out, arg) => {
600                    v[out] = v[arg].tan();
601                }
602                RegOp::AsinReg(out, arg) => {
603                    v[out] = v[arg].asin();
604                }
605                RegOp::AcosReg(out, arg) => {
606                    v[out] = v[arg].acos();
607                }
608                RegOp::AtanReg(out, arg) => {
609                    v[out] = v[arg].atan();
610                }
611                RegOp::ExpReg(out, arg) => {
612                    v[out] = v[arg].exp();
613                }
614                RegOp::LnReg(out, arg) => {
615                    v[out] = v[arg].ln();
616                }
617                RegOp::NotReg(out, arg) => v[out] = (v[arg] == 0.0).into(),
618                RegOp::RandReg(out, arg) => {
619                    v[out] = v[arg].rand();
620                }
621                RegOp::CopyReg(out, arg) => {
622                    v[out] = v[arg];
623                }
624                RegOp::AddRegImm(out, arg, imm) => {
625                    v[out] = v[arg] + imm;
626                }
627                RegOp::MulRegImm(out, arg, imm) => {
628                    v[out] = v[arg] * imm;
629                }
630                RegOp::DivRegImm(out, arg, imm) => {
631                    v[out] = v[arg] / imm;
632                }
633                RegOp::DivImmReg(out, arg, imm) => {
634                    v[out] = imm / v[arg];
635                }
636                RegOp::AtanRegImm(out, arg, imm) => {
637                    v[out] = v[arg].atan2(imm);
638                }
639                RegOp::AtanImmReg(out, arg, imm) => {
640                    v[out] = imm.atan2(v[arg]);
641                }
642                RegOp::AtanRegReg(out, lhs, rhs) => {
643                    v[out] = v[lhs].atan2(v[rhs]);
644                }
645                RegOp::SubImmReg(out, arg, imm) => {
646                    v[out] = imm - v[arg];
647                }
648                RegOp::SubRegImm(out, arg, imm) => {
649                    v[out] = v[arg] - imm;
650                }
651                RegOp::MinRegImm(out, arg, imm) => {
652                    let (value, choice) = v[arg].min_choice(imm);
653                    v[out] = value;
654                    *choices.next().unwrap() |= choice;
655                    simplify |= choice != Choice::Both;
656                }
657                RegOp::MaxRegImm(out, arg, imm) => {
658                    let (value, choice) = v[arg].max_choice(imm);
659                    v[out] = value;
660                    *choices.next().unwrap() |= choice;
661                    simplify |= choice != Choice::Both;
662                }
663                RegOp::AndRegImm(out, arg, imm) => {
664                    let (value, choice) = v[arg].and_choice(imm);
665                    v[out] = value;
666                    *choices.next().unwrap() |= choice;
667                    simplify |= choice != Choice::Both;
668                }
669                RegOp::OrRegImm(out, arg, imm) => {
670                    let (value, choice) = v[arg].or_choice(imm);
671                    v[out] = value;
672                    *choices.next().unwrap() |= choice;
673                    simplify |= choice != Choice::Both;
674                }
675                RegOp::ModRegReg(out, lhs, rhs) => {
676                    v[out] = v[lhs].rem_euclid(v[rhs]);
677                }
678                RegOp::ModRegImm(out, arg, imm) => {
679                    v[out] = v[arg].rem_euclid(imm);
680                }
681                RegOp::ModImmReg(out, arg, imm) => {
682                    v[out] = imm.rem_euclid(v[arg]);
683                }
684                RegOp::AddRegReg(out, lhs, rhs) => {
685                    v[out] = v[lhs] + v[rhs];
686                }
687                RegOp::MulRegReg(out, lhs, rhs) => {
688                    v[out] = v[lhs] * v[rhs];
689                }
690                RegOp::DivRegReg(out, lhs, rhs) => {
691                    v[out] = v[lhs] / v[rhs];
692                }
693                RegOp::CompareRegReg(out, lhs, rhs) => {
694                    v[out] = v[lhs].compare(v[rhs]);
695                }
696                RegOp::CompareRegImm(out, arg, imm) => {
697                    v[out] = v[arg].compare(imm);
698                }
699                RegOp::CompareImmReg(out, arg, imm) => {
700                    v[out] = imm.compare(v[arg]);
701                }
702                RegOp::MixRegReg(out, lhs, rhs) => {
703                    v[out] = v[lhs].mix(v[rhs]);
704                }
705                RegOp::MixRegImm(out, arg, imm) => {
706                    v[out] = v[arg].mix(imm);
707                }
708                RegOp::MixImmReg(out, arg, imm) => {
709                    v[out] = imm.mix(v[arg]);
710                }
711                RegOp::SubRegReg(out, lhs, rhs) => {
712                    v[out] = v[lhs] - v[rhs];
713                }
714                RegOp::MinRegReg(out, lhs, rhs) => {
715                    let (value, choice) = v[lhs].min_choice(v[rhs]);
716                    v[out] = value;
717                    *choices.next().unwrap() |= choice;
718                    simplify |= choice != Choice::Both;
719                }
720                RegOp::MaxRegReg(out, lhs, rhs) => {
721                    let (value, choice) = v[lhs].max_choice(v[rhs]);
722                    v[out] = value;
723                    *choices.next().unwrap() |= choice;
724                    simplify |= choice != Choice::Both;
725                }
726                RegOp::AndRegReg(out, lhs, rhs) => {
727                    let (value, choice) = v[lhs].and_choice(v[rhs]);
728                    v[out] = value;
729                    *choices.next().unwrap() |= choice;
730                    simplify |= choice != Choice::Both;
731                }
732                RegOp::OrRegReg(out, lhs, rhs) => {
733                    let (value, choice) = v[lhs].or_choice(v[rhs]);
734                    v[out] = value;
735                    *choices.next().unwrap() |= choice;
736                    simplify |= choice != Choice::Both;
737                }
738                RegOp::CopyImm(out, imm) => {
739                    v[out] = imm;
740                }
741                RegOp::Load(out, mem) => {
742                    v[out] = v[mem];
743                }
744                RegOp::Store(out, mem) => {
745                    v[mem] = v[out];
746                }
747            }
748        }
749        Ok((
750            &self.0.out,
751            if simplify {
752                Some(&self.0.choices)
753            } else {
754                None
755            },
756        ))
757    }
758}
759
760////////////////////////////////////////////////////////////////////////////////
761
762/// Bulk evaluator for VM tapes
763#[derive(Default)]
764struct BulkVmEval<T> {
765    /// Workspace for data
766    slots: Vec<Vec<T>>,
767
768    /// Output array
769    out: Vec<Vec<T>>,
770}
771
772impl<T: From<f32> + Clone> BulkVmEval<T> {
773    /// Reserves slots for the given tape and slice size
774    fn resize_slots<const N: usize>(&mut self, tape: &VmData<N>, size: usize) {
775        self.slots
776            .resize_with(tape.slot_count(), || vec![f32::NAN.into(); size]);
777        for s in self.slots.iter_mut() {
778            s.resize(size, f32::NAN.into());
779        }
780
781        self.out
782            .resize_with(tape.output_count(), || vec![f32::NAN.into(); size]);
783        for o in self.out.iter_mut() {
784            o.resize(size, f32::NAN.into());
785        }
786    }
787}
788
789/// VM-based bulk evaluator for arrays of points, yielding point values
790#[derive(Default)]
791pub struct VmFloatSliceEval<const N: usize>(BulkVmEval<f32>);
792impl<const N: usize> BulkEvaluator for VmFloatSliceEval<N> {
793    type Data = f32;
794    type Tape = GenericVmTape<N>;
795    type TapeStorage = EmptyTapeStorage;
796
797    #[inline]
798    fn eval<V: std::ops::Deref<Target = [Self::Data]>>(
799        &mut self,
800        tape: &Self::Tape,
801        vars: &[V],
802    ) -> Result<BulkOutput<'_, f32>, BulkEvalError> {
803        tape.vars().check_bulk_arguments(vars)?;
804        let tape = tape.data();
805
806        let size = vars.first().map(|v| v.len()).unwrap_or(0);
807        self.0.resize_slots(tape, size);
808
809        let mut v = SlotArray(&mut self.0.slots);
810        for op in tape.iter_asm() {
811            match op {
812                RegOp::Output(arg, i) => {
813                    self.0.out[i as usize][0..size]
814                        .copy_from_slice(&v[arg][0..size]);
815                }
816                RegOp::Input(out, i) => {
817                    v[out][0..size].copy_from_slice(&vars[i as usize]);
818                }
819                RegOp::NegReg(out, arg) => {
820                    for i in 0..size {
821                        v[out][i] = -v[arg][i];
822                    }
823                }
824                RegOp::AbsReg(out, arg) => {
825                    for i in 0..size {
826                        v[out][i] = v[arg][i].abs();
827                    }
828                }
829                RegOp::RecipReg(out, arg) => {
830                    for i in 0..size {
831                        v[out][i] = 1.0 / v[arg][i];
832                    }
833                }
834                RegOp::SqrtReg(out, arg) => {
835                    for i in 0..size {
836                        v[out][i] = v[arg][i].sqrt();
837                    }
838                }
839                RegOp::SquareReg(out, arg) => {
840                    for i in 0..size {
841                        let s = v[arg][i];
842                        v[out][i] = s * s;
843                    }
844                }
845                RegOp::FloorReg(out, arg) => {
846                    for i in 0..size {
847                        v[out][i] = v[arg][i].floor();
848                    }
849                }
850                RegOp::CeilReg(out, arg) => {
851                    for i in 0..size {
852                        v[out][i] = v[arg][i].ceil();
853                    }
854                }
855                RegOp::RoundReg(out, arg) => {
856                    for i in 0..size {
857                        v[out][i] = v[arg][i].round();
858                    }
859                }
860                RegOp::SinReg(out, arg) => {
861                    for i in 0..size {
862                        v[out][i] = v[arg][i].sin();
863                    }
864                }
865                RegOp::CosReg(out, arg) => {
866                    for i in 0..size {
867                        v[out][i] = v[arg][i].cos();
868                    }
869                }
870                RegOp::TanReg(out, arg) => {
871                    for i in 0..size {
872                        v[out][i] = v[arg][i].tan();
873                    }
874                }
875                RegOp::AsinReg(out, arg) => {
876                    for i in 0..size {
877                        v[out][i] = v[arg][i].asin();
878                    }
879                }
880                RegOp::AcosReg(out, arg) => {
881                    for i in 0..size {
882                        v[out][i] = v[arg][i].acos();
883                    }
884                }
885                RegOp::AtanReg(out, arg) => {
886                    for i in 0..size {
887                        v[out][i] = v[arg][i].atan();
888                    }
889                }
890                RegOp::ExpReg(out, arg) => {
891                    for i in 0..size {
892                        v[out][i] = v[arg][i].exp();
893                    }
894                }
895                RegOp::LnReg(out, arg) => {
896                    for i in 0..size {
897                        v[out][i] = v[arg][i].ln();
898                    }
899                }
900                RegOp::NotReg(out, arg) => {
901                    for i in 0..size {
902                        v[out][i] = (v[arg][i] == 0.0).into();
903                    }
904                }
905                RegOp::RandReg(out, arg) => {
906                    for i in 0..size {
907                        v[out][i] = v[arg][i].rand();
908                    }
909                }
910                RegOp::CopyReg(out, arg) => {
911                    for i in 0..size {
912                        v[out][i] = v[arg][i];
913                    }
914                }
915                RegOp::AddRegImm(out, arg, imm) => {
916                    for i in 0..size {
917                        v[out][i] = v[arg][i] + imm;
918                    }
919                }
920                RegOp::MulRegImm(out, arg, imm) => {
921                    for i in 0..size {
922                        v[out][i] = v[arg][i] * imm;
923                    }
924                }
925                RegOp::DivRegImm(out, arg, imm) => {
926                    for i in 0..size {
927                        v[out][i] = v[arg][i] / imm;
928                    }
929                }
930                RegOp::DivImmReg(out, arg, imm) => {
931                    for i in 0..size {
932                        v[out][i] = imm / v[arg][i];
933                    }
934                }
935                RegOp::AtanRegImm(out, arg, imm) => {
936                    for i in 0..size {
937                        v[out][i] = v[arg][i].atan2(imm);
938                    }
939                }
940                RegOp::AtanImmReg(out, arg, imm) => {
941                    for i in 0..size {
942                        v[out][i] = imm.atan2(v[arg][i]);
943                    }
944                }
945                RegOp::AtanRegReg(out, lhs, rhs) => {
946                    for i in 0..size {
947                        v[out][i] = v[lhs][i].atan2(v[rhs][i]);
948                    }
949                }
950                RegOp::SubImmReg(out, arg, imm) => {
951                    for i in 0..size {
952                        v[out][i] = imm - v[arg][i];
953                    }
954                }
955                RegOp::SubRegImm(out, arg, imm) => {
956                    for i in 0..size {
957                        v[out][i] = v[arg][i] - imm;
958                    }
959                }
960                RegOp::CompareImmReg(out, arg, imm) => {
961                    for i in 0..size {
962                        v[out][i] = imm.compare(v[arg][i]);
963                    }
964                }
965                RegOp::CompareRegImm(out, arg, imm) => {
966                    for i in 0..size {
967                        v[out][i] = v[arg][i].compare(imm);
968                    }
969                }
970                RegOp::MixRegReg(out, lhs, rhs) => {
971                    for i in 0..size {
972                        v[out][i] = v[lhs][i].mix(v[rhs][i]);
973                    }
974                }
975                RegOp::MixRegImm(out, arg, imm) => {
976                    for i in 0..size {
977                        v[out][i] = v[arg][i].mix(imm);
978                    }
979                }
980                RegOp::MixImmReg(out, arg, imm) => {
981                    for i in 0..size {
982                        v[out][i] = imm.mix(v[arg][i]);
983                    }
984                }
985                RegOp::MinRegImm(out, arg, imm) => {
986                    for i in 0..size {
987                        v[out][i] = v[arg][i].min_choice(imm).0;
988                    }
989                }
990                RegOp::MaxRegImm(out, arg, imm) => {
991                    for i in 0..size {
992                        v[out][i] = v[arg][i].max_choice(imm).0;
993                    }
994                }
995                RegOp::AndRegImm(out, arg, imm) => {
996                    for i in 0..size {
997                        v[out][i] = v[arg][i].and_choice(imm).0;
998                    }
999                }
1000                RegOp::OrRegImm(out, arg, imm) => {
1001                    for i in 0..size {
1002                        v[out][i] = v[arg][i].or_choice(imm).0;
1003                    }
1004                }
1005                RegOp::ModRegReg(out, lhs, rhs) => {
1006                    for i in 0..size {
1007                        v[out][i] = v[lhs][i].rem_euclid(v[rhs][i]);
1008                    }
1009                }
1010                RegOp::ModRegImm(out, arg, imm) => {
1011                    for i in 0..size {
1012                        v[out][i] = v[arg][i].rem_euclid(imm);
1013                    }
1014                }
1015                RegOp::ModImmReg(out, arg, imm) => {
1016                    for i in 0..size {
1017                        v[out][i] = imm.rem_euclid(v[arg][i]);
1018                    }
1019                }
1020                RegOp::AddRegReg(out, lhs, rhs) => {
1021                    for i in 0..size {
1022                        v[out][i] = v[lhs][i] + v[rhs][i];
1023                    }
1024                }
1025                RegOp::MulRegReg(out, lhs, rhs) => {
1026                    for i in 0..size {
1027                        v[out][i] = v[lhs][i] * v[rhs][i];
1028                    }
1029                }
1030                RegOp::DivRegReg(out, lhs, rhs) => {
1031                    for i in 0..size {
1032                        v[out][i] = v[lhs][i] / v[rhs][i];
1033                    }
1034                }
1035                RegOp::SubRegReg(out, lhs, rhs) => {
1036                    for i in 0..size {
1037                        v[out][i] = v[lhs][i] - v[rhs][i];
1038                    }
1039                }
1040                RegOp::CompareRegReg(out, lhs, rhs) => {
1041                    for i in 0..size {
1042                        v[out][i] = v[lhs][i].compare(v[rhs][i]);
1043                    }
1044                }
1045                RegOp::MinRegReg(out, lhs, rhs) => {
1046                    for i in 0..size {
1047                        v[out][i] = v[lhs][i].min_choice(v[rhs][i]).0;
1048                    }
1049                }
1050                RegOp::MaxRegReg(out, lhs, rhs) => {
1051                    for i in 0..size {
1052                        v[out][i] = v[lhs][i].max_choice(v[rhs][i]).0;
1053                    }
1054                }
1055                RegOp::AndRegReg(out, lhs, rhs) => {
1056                    for i in 0..size {
1057                        v[out][i] = v[lhs][i].and_choice(v[rhs][i]).0;
1058                    }
1059                }
1060                RegOp::OrRegReg(out, lhs, rhs) => {
1061                    for i in 0..size {
1062                        v[out][i] = v[lhs][i].or_choice(v[rhs][i]).0;
1063                    }
1064                }
1065                RegOp::CopyImm(out, imm) => {
1066                    for i in 0..size {
1067                        v[out][i] = imm;
1068                    }
1069                }
1070                RegOp::Load(out, mem) => {
1071                    for i in 0..size {
1072                        v[out][i] = v[mem][i];
1073                    }
1074                }
1075                RegOp::Store(out, mem) => {
1076                    for i in 0..size {
1077                        v[mem][i] = v[out][i];
1078                    }
1079                }
1080            }
1081        }
1082        Ok(BulkOutput::new(&self.0.out, size))
1083    }
1084}
1085
1086/// VM-based bulk evaluator for arrays of points, yielding gradient values
1087#[derive(Default)]
1088pub struct VmGradSliceEval<const N: usize>(BulkVmEval<Grad>);
1089impl<const N: usize> BulkEvaluator for VmGradSliceEval<N> {
1090    type Data = Grad;
1091    type Tape = GenericVmTape<N>;
1092    type TapeStorage = EmptyTapeStorage;
1093
1094    #[inline]
1095    fn eval<V: std::ops::Deref<Target = [Self::Data]>>(
1096        &mut self,
1097        tape: &Self::Tape,
1098        vars: &[V],
1099    ) -> Result<BulkOutput<'_, Grad>, BulkEvalError> {
1100        tape.vars().check_bulk_arguments(vars)?;
1101        let tape = tape.data();
1102        let size = vars.first().map(|v| v.len()).unwrap_or(0);
1103        self.0.resize_slots(tape, size);
1104
1105        let mut v = SlotArray(&mut self.0.slots);
1106        for op in tape.iter_asm() {
1107            match op {
1108                RegOp::Output(arg, i) => {
1109                    self.0.out[i as usize][0..size]
1110                        .copy_from_slice(&v[arg][0..size]);
1111                }
1112                RegOp::Input(out, i) => {
1113                    v[out][0..size].copy_from_slice(&vars[i as usize]);
1114                }
1115                RegOp::NegReg(out, arg) => {
1116                    for i in 0..size {
1117                        v[out][i] = -v[arg][i];
1118                    }
1119                }
1120                RegOp::AbsReg(out, arg) => {
1121                    for i in 0..size {
1122                        v[out][i] = v[arg][i].abs();
1123                    }
1124                }
1125                RegOp::RecipReg(out, arg) => {
1126                    let one: Grad = 1.0.into();
1127                    for i in 0..size {
1128                        v[out][i] = one / v[arg][i];
1129                    }
1130                }
1131                RegOp::SqrtReg(out, arg) => {
1132                    for i in 0..size {
1133                        v[out][i] = v[arg][i].sqrt();
1134                    }
1135                }
1136                RegOp::SquareReg(out, arg) => {
1137                    for i in 0..size {
1138                        let s = v[arg][i];
1139                        v[out][i] = s * s;
1140                    }
1141                }
1142                RegOp::FloorReg(out, arg) => {
1143                    for i in 0..size {
1144                        v[out][i] = v[arg][i].floor();
1145                    }
1146                }
1147                RegOp::CeilReg(out, arg) => {
1148                    for i in 0..size {
1149                        v[out][i] = v[arg][i].ceil();
1150                    }
1151                }
1152                RegOp::RoundReg(out, arg) => {
1153                    for i in 0..size {
1154                        v[out][i] = v[arg][i].round();
1155                    }
1156                }
1157                RegOp::SinReg(out, arg) => {
1158                    for i in 0..size {
1159                        v[out][i] = v[arg][i].sin();
1160                    }
1161                }
1162                RegOp::CosReg(out, arg) => {
1163                    for i in 0..size {
1164                        v[out][i] = v[arg][i].cos();
1165                    }
1166                }
1167                RegOp::TanReg(out, arg) => {
1168                    for i in 0..size {
1169                        v[out][i] = v[arg][i].tan();
1170                    }
1171                }
1172                RegOp::AsinReg(out, arg) => {
1173                    for i in 0..size {
1174                        v[out][i] = v[arg][i].asin();
1175                    }
1176                }
1177                RegOp::AcosReg(out, arg) => {
1178                    for i in 0..size {
1179                        v[out][i] = v[arg][i].acos();
1180                    }
1181                }
1182                RegOp::AtanReg(out, arg) => {
1183                    for i in 0..size {
1184                        v[out][i] = v[arg][i].atan();
1185                    }
1186                }
1187                RegOp::ExpReg(out, arg) => {
1188                    for i in 0..size {
1189                        v[out][i] = v[arg][i].exp();
1190                    }
1191                }
1192                RegOp::LnReg(out, arg) => {
1193                    for i in 0..size {
1194                        v[out][i] = v[arg][i].ln();
1195                    }
1196                }
1197                RegOp::NotReg(out, arg) => {
1198                    for i in 0..size {
1199                        v[out][i] = v[arg][i].not();
1200                    }
1201                }
1202                RegOp::RandReg(out, arg) => {
1203                    for i in 0..size {
1204                        v[out][i] = v[arg][i].rand();
1205                    }
1206                }
1207                RegOp::CopyReg(out, arg) => {
1208                    for i in 0..size {
1209                        v[out][i] = v[arg][i];
1210                    }
1211                }
1212                RegOp::AddRegImm(out, arg, imm) => {
1213                    for i in 0..size {
1214                        v[out][i] = v[arg][i] + imm.into();
1215                    }
1216                }
1217                RegOp::MulRegImm(out, arg, imm) => {
1218                    for i in 0..size {
1219                        v[out][i] = v[arg][i] * imm;
1220                    }
1221                }
1222                RegOp::DivRegImm(out, arg, imm) => {
1223                    for i in 0..size {
1224                        v[out][i] = v[arg][i] / imm.into();
1225                    }
1226                }
1227                RegOp::DivImmReg(out, arg, imm) => {
1228                    let imm = Grad::from(imm);
1229                    for i in 0..size {
1230                        v[out][i] = imm / v[arg][i];
1231                    }
1232                }
1233                RegOp::AtanRegImm(out, arg, imm) => {
1234                    let imm = Grad::from(imm);
1235                    for i in 0..size {
1236                        v[out][i] = v[arg][i].atan2(imm);
1237                    }
1238                }
1239                RegOp::AtanImmReg(out, arg, imm) => {
1240                    let imm = Grad::from(imm);
1241                    for i in 0..size {
1242                        v[out][i] = imm.atan2(v[arg][i]);
1243                    }
1244                }
1245                RegOp::AtanRegReg(out, lhs, rhs) => {
1246                    for i in 0..size {
1247                        v[out][i] = v[lhs][i].atan2(v[rhs][i]);
1248                    }
1249                }
1250                RegOp::SubImmReg(out, arg, imm) => {
1251                    let imm: Grad = imm.into();
1252                    for i in 0..size {
1253                        v[out][i] = imm - v[arg][i];
1254                    }
1255                }
1256                RegOp::SubRegImm(out, arg, imm) => {
1257                    let imm: Grad = imm.into();
1258                    for i in 0..size {
1259                        v[out][i] = v[arg][i] - imm;
1260                    }
1261                }
1262                RegOp::CompareImmReg(out, arg, imm) => {
1263                    let imm: Grad = imm.into();
1264                    for i in 0..size {
1265                        v[out][i] = imm.compare(v[arg][i]);
1266                    }
1267                }
1268                RegOp::MixRegReg(out, lhs, rhs) => {
1269                    for i in 0..size {
1270                        v[out][i] = v[lhs][i].mix(v[rhs][i]);
1271                    }
1272                }
1273                RegOp::MixRegImm(out, arg, imm) => {
1274                    let imm = imm.into();
1275                    for i in 0..size {
1276                        v[out][i] = v[arg][i].mix(imm);
1277                    }
1278                }
1279                RegOp::MixImmReg(out, arg, imm) => {
1280                    let imm = Grad::from(imm);
1281                    for i in 0..size {
1282                        v[out][i] = imm.mix(v[arg][i]);
1283                    }
1284                }
1285                RegOp::CompareRegImm(out, arg, imm) => {
1286                    let imm = imm.into();
1287                    for i in 0..size {
1288                        v[out][i] = v[arg][i].compare(imm);
1289                    }
1290                }
1291                RegOp::MinRegImm(out, arg, imm) => {
1292                    let imm: Grad = imm.into();
1293                    for i in 0..size {
1294                        v[out][i] = v[arg][i].min(imm);
1295                    }
1296                }
1297                RegOp::MaxRegImm(out, arg, imm) => {
1298                    let imm: Grad = imm.into();
1299                    for i in 0..size {
1300                        v[out][i] = v[arg][i].max(imm)
1301                    }
1302                }
1303                RegOp::ModRegReg(out, lhs, rhs) => {
1304                    for i in 0..size {
1305                        v[out][i] = v[lhs][i].rem_euclid(v[rhs][i]);
1306                    }
1307                }
1308                RegOp::ModRegImm(out, arg, imm) => {
1309                    for i in 0..size {
1310                        v[out][i] = v[arg][i].rem_euclid(imm.into());
1311                    }
1312                }
1313                RegOp::ModImmReg(out, arg, imm) => {
1314                    for i in 0..size {
1315                        v[out][i] = Grad::from(imm).rem_euclid(v[arg][i]);
1316                    }
1317                }
1318                RegOp::AddRegReg(out, lhs, rhs) => {
1319                    for i in 0..size {
1320                        v[out][i] = v[lhs][i] + v[rhs][i];
1321                    }
1322                }
1323                RegOp::MulRegReg(out, lhs, rhs) => {
1324                    for i in 0..size {
1325                        v[out][i] = v[lhs][i] * v[rhs][i];
1326                    }
1327                }
1328                RegOp::AndRegReg(out, lhs, rhs) => {
1329                    for i in 0..size {
1330                        v[out][i] = v[lhs][i].and(v[rhs][i]);
1331                    }
1332                }
1333                RegOp::AndRegImm(out, arg, imm) => {
1334                    let imm = imm.into();
1335                    for i in 0..size {
1336                        v[out][i] = v[arg][i].and(imm);
1337                    }
1338                }
1339                RegOp::OrRegReg(out, lhs, rhs) => {
1340                    for i in 0..size {
1341                        v[out][i] = v[lhs][i].or(v[rhs][i]);
1342                    }
1343                }
1344                RegOp::OrRegImm(out, arg, imm) => {
1345                    let imm = imm.into();
1346                    for i in 0..size {
1347                        v[out][i] = v[arg][i].or(imm);
1348                    }
1349                }
1350                RegOp::DivRegReg(out, lhs, rhs) => {
1351                    for i in 0..size {
1352                        v[out][i] = v[lhs][i] / v[rhs][i];
1353                    }
1354                }
1355                RegOp::SubRegReg(out, lhs, rhs) => {
1356                    for i in 0..size {
1357                        v[out][i] = v[lhs][i] - v[rhs][i];
1358                    }
1359                }
1360                RegOp::CompareRegReg(out, lhs, rhs) => {
1361                    for i in 0..size {
1362                        v[out][i] = v[lhs][i].compare(v[rhs][i]);
1363                    }
1364                }
1365                RegOp::MinRegReg(out, lhs, rhs) => {
1366                    for i in 0..size {
1367                        v[out][i] = v[lhs][i].min(v[rhs][i]);
1368                    }
1369                }
1370                RegOp::MaxRegReg(out, lhs, rhs) => {
1371                    for i in 0..size {
1372                        v[out][i] = v[lhs][i].max(v[rhs][i]);
1373                    }
1374                }
1375                RegOp::CopyImm(out, imm) => {
1376                    let imm: Grad = imm.into();
1377                    for i in 0..size {
1378                        v[out][i] = imm;
1379                    }
1380                }
1381                RegOp::Load(out, mem) => {
1382                    for i in 0..size {
1383                        v[out][i] = v[mem][i];
1384                    }
1385                }
1386                RegOp::Store(out, mem) => {
1387                    for i in 0..size {
1388                        v[mem][i] = v[out][i];
1389                    }
1390                }
1391            }
1392        }
1393        Ok(BulkOutput::new(&self.0.out, size))
1394    }
1395}
1396
1397#[cfg(test)]
1398mod test {
1399    use super::*;
1400    crate::grad_slice_tests!(VmFunction);
1401    crate::interval_tests!(VmFunction);
1402    crate::float_slice_tests!(VmFunction);
1403    crate::point_tests!(VmFunction);
1404}