Skip to main content

fidget_core/compiler/
op.rs

1use serde::{Deserialize, Serialize};
2
3/// Macro to generate a set of opcodes, using the given type for registers
4macro_rules! opcodes {
5    (
6        $(#[$($attrss:meta)*])*
7        pub enum $name:ident<$t:ty> {}
8    ) => {
9        opcodes!(
10            $(#[$($attrss)*])*
11            pub enum $name<$t> {,}
12        );
13    };
14    (
15        $(#[$($attrss:meta)*])*
16        pub enum $name:ident<$t:ty> {
17            $(
18                $(#[$($a:meta)*])*
19                $foo:ident($($i:ty),*)
20             ),*
21            ,
22        }
23    ) => {
24        $(#[$($attrss)*])*
25        pub enum $name {
26            // Special unary opcodes
27            #[doc = "Writes an output variable by index"]
28            Output($t, u32),
29            #[doc = "Read an input variable by index"]
30            Input($t, u32),
31            #[doc = "Copies the given register"]
32            CopyReg($t, $t),
33            #[doc = "Copy an immediate to a register"]
34            CopyImm($t, f32),
35
36            // Normal unary opcodes
37            #[doc = "Negate the given register"]
38            NegReg($t, $t),
39            #[doc = "Take the absolute value of the given register"]
40            AbsReg($t, $t),
41            #[doc = "Take the reciprocal of the given register (1.0 / value)"]
42            RecipReg($t, $t),
43            #[doc = "Take the square root of the given register"]
44            SqrtReg($t, $t),
45            #[doc = "Square the given register"]
46            SquareReg($t, $t),
47            #[doc = "Returns the largest integer less than or equal to `self`"]
48            FloorReg($t, $t),
49            #[doc = "Returns the smallest integer greater than or equal to `self`"]
50            CeilReg($t, $t),
51            #[doc = "Returns the nearest integer to `self`. If a value is half-way between two integers, round away from `0.0`."]
52            RoundReg($t, $t),
53            #[doc = "Computes the sine of the given register (in radians)"]
54            SinReg($t, $t),
55            #[doc = "Computes the cosine of the given register (in radians)"]
56            CosReg($t, $t),
57            #[doc = "Computes the tangent of the given register (in radians)"]
58            TanReg($t, $t),
59            #[doc = "Computes the arcsin of the given register (in radians)"]
60            AsinReg($t, $t),
61            #[doc = "Computes the arccos of the given register (in radians)"]
62            AcosReg($t, $t),
63            #[doc = "Computes the arctangent of the given register (in radians)"]
64            AtanReg($t, $t),
65            #[doc = "Computes the exponential function of the given register"]
66            ExpReg($t, $t),
67            #[doc = "Computes the natural log of the given register"]
68            LnReg($t, $t),
69            #[doc = "Computes the logical negation of the given register\n\nEquivalent to `if arg == 0 { 1 } else { 0 }`"]
70            NotReg($t, $t),
71            #[doc = "Returns a pseudo-random value in the 0.0-1.0 range from a given seed"]
72            RandReg($t, $t),
73
74            // RegImm opcodes (without a choice)
75            #[doc = "Add a register and an immediate"]
76            AddRegImm($t, $t, f32),
77            #[doc = "Multiply a register and an immediate"]
78            MulRegImm($t, $t, f32),
79            #[doc = "Divides a register and an immediate"]
80            DivRegImm($t, $t, f32),
81            #[doc = "Divides an immediate by a register"]
82            DivImmReg($t, $t, f32),
83            #[doc = "Subtract a register from an immediate"]
84            SubImmReg($t, $t, f32),
85            #[doc = "Subtract an immediate from a register"]
86            SubRegImm($t, $t, f32),
87            #[doc = "Take the module (least nonnegative remainder) of two registers"]
88            ModRegReg($t, $t, $t),
89            #[doc = "Take the module (least nonnegative remainder) of a register and an immediate"]
90            ModRegImm($t, $t, f32),
91            #[doc = "atan2 of a position `(y, x)` specified as register, immediate"]
92            AtanRegImm($t, $t, f32),
93            #[doc = "Compares a register with an immediate"]
94            CompareRegImm($t, $t, f32),
95            #[doc = "Pseudo-random mixing of a register with an immediate"]
96            MixRegImm($t, $t, f32),
97
98            // RegImm opcodes (with a choice)
99            #[doc = "Compute the minimum of a register and an immediate"]
100            MinRegImm($t, $t, f32),
101            #[doc = "Compute the maximum of a register and an immediate"]
102            MaxRegImm($t, $t, f32),
103            #[doc = "Logical `AND` (short-circuiting)\n\nThis is equivalent to `if lhs == 0 { lhs } else { imm }`"]
104            AndRegImm($t, $t, f32),
105            #[doc = "Logical `OR` (short-circuiting)\n\nThis is equivalent to `if lhs != 0 { lhs } else { imm }`"]
106            OrRegImm($t, $t, f32),
107
108            // ImmReg opcodes (without a choice)
109            #[doc = "Take the module (least nonnegative remainder) of an immediate and a register"]
110            ModImmReg($t, $t, f32),
111            #[doc = "atan2 of a position `(y, x)` specified as immediate, register"]
112            AtanImmReg($t, $t, f32),
113            #[doc = "Compares an immediate with a register"]
114            CompareImmReg($t, $t, f32),
115            #[doc = "Pseudo-random mixing of an immediate and a register"]
116            MixImmReg($t, $t, f32),
117
118            // RegReg opcodes (without a choice)
119            #[doc = "Add two registers"]
120            AddRegReg($t, $t, $t),
121            #[doc = "Multiply two registers"]
122            MulRegReg($t, $t, $t),
123            #[doc = "Divides two registers"]
124            DivRegReg($t, $t, $t),
125            #[doc = "Subtract one register from another"]
126            SubRegReg($t, $t, $t),
127            #[doc = "Compares two registers"]
128            CompareRegReg($t, $t, $t),
129            #[doc = "atan2 of a position `(y, x)` specified as register, register"]
130            AtanRegReg($t, $t, $t),
131            #[doc = "Pseudo-random mixing of two registers"]
132            MixRegReg($t, $t, $t),
133
134            // RegReg opcodes (with a choice)
135            #[doc = "Take the minimum of two registers"]
136            MinRegReg($t, $t, $t),
137            #[doc = "Take the maximum of two registers"]
138            MaxRegReg($t, $t, $t),
139            #[doc = "Logical `AND` (short-circuiting)\n\nThis is equivalent to `if lhs == 0 { lhs } else { rhs }`"]
140            AndRegReg($t, $t, $t),
141            #[doc = "Logical `OR` (short-circuiting)\n\nThis is equivalent to `if lhs != 0 { lhs } else { rhs }`"]
142            OrRegReg($t, $t, $t),
143
144            $(
145                $(#[$($a)*])*
146                $foo($($i),*)
147             ),*
148        }
149    };
150}
151
152opcodes!(
153    /// Basic operations that can be performed in a tape
154    ///
155    /// Arguments, in order, are
156    /// - Output register
157    /// - LHS register (or input slot for [`Input`](SsaOp::Input))
158    /// - RHS register (or immediate for `*Imm`)
159    ///
160    /// Each "register" represents an SSA slot, which is never reused.
161    #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
162    pub enum SsaOp<u32> {
163        // default variants
164    }
165);
166
167impl SsaOp {
168    /// Returns the output pseudo-register
169    pub fn output(&self) -> Option<u32> {
170        match self {
171            SsaOp::Input(out, ..)
172            | SsaOp::CopyImm(out, ..)
173            | SsaOp::NegReg(out, ..)
174            | SsaOp::AbsReg(out, ..)
175            | SsaOp::RecipReg(out, ..)
176            | SsaOp::SqrtReg(out, ..)
177            | SsaOp::SquareReg(out, ..)
178            | SsaOp::FloorReg(out, ..)
179            | SsaOp::CeilReg(out, ..)
180            | SsaOp::RoundReg(out, ..)
181            | SsaOp::CopyReg(out, ..)
182            | SsaOp::SinReg(out, ..)
183            | SsaOp::CosReg(out, ..)
184            | SsaOp::TanReg(out, ..)
185            | SsaOp::AsinReg(out, ..)
186            | SsaOp::AcosReg(out, ..)
187            | SsaOp::AtanReg(out, ..)
188            | SsaOp::ExpReg(out, ..)
189            | SsaOp::LnReg(out, ..)
190            | SsaOp::NotReg(out, ..)
191            | SsaOp::RandReg(out, ..)
192            | SsaOp::AddRegImm(out, ..)
193            | SsaOp::MulRegImm(out, ..)
194            | SsaOp::DivRegImm(out, ..)
195            | SsaOp::DivImmReg(out, ..)
196            | SsaOp::SubImmReg(out, ..)
197            | SsaOp::SubRegImm(out, ..)
198            | SsaOp::AddRegReg(out, ..)
199            | SsaOp::MulRegReg(out, ..)
200            | SsaOp::DivRegReg(out, ..)
201            | SsaOp::SubRegReg(out, ..)
202            | SsaOp::AtanRegReg(out, ..)
203            | SsaOp::AtanRegImm(out, ..)
204            | SsaOp::AtanImmReg(out, ..)
205            | SsaOp::MinRegImm(out, ..)
206            | SsaOp::MaxRegImm(out, ..)
207            | SsaOp::MinRegReg(out, ..)
208            | SsaOp::MaxRegReg(out, ..)
209            | SsaOp::CompareRegReg(out, ..)
210            | SsaOp::CompareRegImm(out, ..)
211            | SsaOp::CompareImmReg(out, ..)
212            | SsaOp::MixRegReg(out, ..)
213            | SsaOp::MixRegImm(out, ..)
214            | SsaOp::MixImmReg(out, ..)
215            | SsaOp::ModRegReg(out, ..)
216            | SsaOp::ModRegImm(out, ..)
217            | SsaOp::ModImmReg(out, ..)
218            | SsaOp::AndRegImm(out, ..)
219            | SsaOp::AndRegReg(out, ..)
220            | SsaOp::OrRegImm(out, ..)
221            | SsaOp::OrRegReg(out, ..) => Some(*out),
222            SsaOp::Output(..) => None,
223        }
224    }
225    /// Returns true if the given opcode is associated with a choice
226    pub fn has_choice(&self) -> bool {
227        match self {
228            SsaOp::Input(..)
229            | SsaOp::Output(..)
230            | SsaOp::CopyImm(..)
231            | SsaOp::NegReg(..)
232            | SsaOp::AbsReg(..)
233            | SsaOp::RecipReg(..)
234            | SsaOp::SqrtReg(..)
235            | SsaOp::SquareReg(..)
236            | SsaOp::FloorReg(..)
237            | SsaOp::CeilReg(..)
238            | SsaOp::RoundReg(..)
239            | SsaOp::CopyReg(..)
240            | SsaOp::SinReg(..)
241            | SsaOp::CosReg(..)
242            | SsaOp::TanReg(..)
243            | SsaOp::AsinReg(..)
244            | SsaOp::AcosReg(..)
245            | SsaOp::AtanReg(..)
246            | SsaOp::ExpReg(..)
247            | SsaOp::LnReg(..)
248            | SsaOp::NotReg(..)
249            | SsaOp::RandReg(..)
250            | SsaOp::AddRegImm(..)
251            | SsaOp::MulRegImm(..)
252            | SsaOp::SubRegImm(..)
253            | SsaOp::SubImmReg(..)
254            | SsaOp::AddRegReg(..)
255            | SsaOp::MulRegReg(..)
256            | SsaOp::SubRegReg(..)
257            | SsaOp::DivRegReg(..)
258            | SsaOp::DivRegImm(..)
259            | SsaOp::DivImmReg(..)
260            | SsaOp::AtanRegReg(..)
261            | SsaOp::AtanRegImm(..)
262            | SsaOp::AtanImmReg(..)
263            | SsaOp::CompareRegReg(..)
264            | SsaOp::CompareRegImm(..)
265            | SsaOp::CompareImmReg(..)
266            | SsaOp::MixRegReg(..)
267            | SsaOp::MixRegImm(..)
268            | SsaOp::MixImmReg(..)
269            | SsaOp::ModRegReg(..)
270            | SsaOp::ModRegImm(..)
271            | SsaOp::ModImmReg(..) => false,
272            SsaOp::MinRegImm(..)
273            | SsaOp::MaxRegImm(..)
274            | SsaOp::MinRegReg(..)
275            | SsaOp::MaxRegReg(..)
276            | SsaOp::AndRegImm(..)
277            | SsaOp::AndRegReg(..)
278            | SsaOp::OrRegImm(..)
279            | SsaOp::OrRegReg(..) => true,
280        }
281    }
282}
283
284opcodes!(
285    /// Operations used in register-allocated tapes
286    ///
287    /// Arguments, in order, are
288    /// - Output register
289    /// - LHS register (or input slot for [`Input`](RegOp::Input))
290    /// - RHS register (or immediate for `*Imm`)
291    ///
292    /// We have a maximum of 256 registers, though some tapes (e.g. ones
293    /// targeting physical hardware) may choose to use fewer.
294    ///
295    /// Note that memory slots are not zero-indexed; they start at `N` (where
296    /// `N` is the register count).
297    #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
298    pub enum RegOp<u8> {
299        // default variants
300        /// Read from a memory slot to a register
301        Load(u8, u32),
302
303        /// Write from a register to a memory slot
304        Store(u8, u32),
305    }
306);
307
308impl RegOp {
309    /// Apply a mutating function to every register in the op
310    ///
311    /// Both inputs and outputs are visited
312    pub fn visit_regs_mut<F: FnMut(&mut u8)>(&mut self, mut f: F) {
313        match self {
314            RegOp::CopyImm(out, imm) => {
315                let _: f32 = *imm;
316                f(out)
317            }
318            RegOp::NegReg(out, arg)
319            | RegOp::AbsReg(out, arg)
320            | RegOp::RecipReg(out, arg)
321            | RegOp::SqrtReg(out, arg)
322            | RegOp::SquareReg(out, arg)
323            | RegOp::FloorReg(out, arg)
324            | RegOp::CeilReg(out, arg)
325            | RegOp::RoundReg(out, arg)
326            | RegOp::CopyReg(out, arg)
327            | RegOp::SinReg(out, arg)
328            | RegOp::CosReg(out, arg)
329            | RegOp::TanReg(out, arg)
330            | RegOp::AsinReg(out, arg)
331            | RegOp::AcosReg(out, arg)
332            | RegOp::AtanReg(out, arg)
333            | RegOp::ExpReg(out, arg)
334            | RegOp::LnReg(out, arg)
335            | RegOp::NotReg(out, arg)
336            | RegOp::RandReg(out, arg) => {
337                f(out);
338                f(arg);
339            }
340            RegOp::AddRegImm(out, arg, imm)
341            | RegOp::MulRegImm(out, arg, imm)
342            | RegOp::DivRegImm(out, arg, imm)
343            | RegOp::DivImmReg(out, arg, imm)
344            | RegOp::SubImmReg(out, arg, imm)
345            | RegOp::SubRegImm(out, arg, imm)
346            | RegOp::AtanRegImm(out, arg, imm)
347            | RegOp::AtanImmReg(out, arg, imm)
348            | RegOp::MinRegImm(out, arg, imm)
349            | RegOp::MaxRegImm(out, arg, imm)
350            | RegOp::CompareRegImm(out, arg, imm)
351            | RegOp::CompareImmReg(out, arg, imm)
352            | RegOp::MixRegImm(out, arg, imm)
353            | RegOp::MixImmReg(out, arg, imm)
354            | RegOp::ModRegImm(out, arg, imm)
355            | RegOp::ModImmReg(out, arg, imm)
356            | RegOp::AndRegImm(out, arg, imm)
357            | RegOp::OrRegImm(out, arg, imm) => {
358                let _: f32 = *imm; // type-checking pattern
359                f(out);
360                f(arg);
361            }
362
363            RegOp::AddRegReg(out, lhs, rhs)
364            | RegOp::MulRegReg(out, lhs, rhs)
365            | RegOp::DivRegReg(out, lhs, rhs)
366            | RegOp::SubRegReg(out, lhs, rhs)
367            | RegOp::AtanRegReg(out, lhs, rhs)
368            | RegOp::MinRegReg(out, lhs, rhs)
369            | RegOp::MaxRegReg(out, lhs, rhs)
370            | RegOp::CompareRegReg(out, lhs, rhs)
371            | RegOp::MixRegReg(out, lhs, rhs)
372            | RegOp::ModRegReg(out, lhs, rhs)
373            | RegOp::AndRegReg(out, lhs, rhs)
374            | RegOp::OrRegReg(out, lhs, rhs) => {
375                f(out);
376                f(lhs);
377                f(rhs);
378            }
379
380            RegOp::Output(reg, imm)
381            | RegOp::Input(reg, imm)
382            | RegOp::Store(reg, imm)
383            | RegOp::Load(reg, imm) => {
384                let _: u32 = *imm; // type-checking pattern
385                f(reg)
386            }
387        }
388    }
389
390    /// Apply a function to every register in the op
391    ///
392    /// Both inputs and outputs are visited
393    pub fn visit_regs<F: FnMut(u8)>(&self, mut f: F) {
394        match self {
395            RegOp::CopyImm(out, imm) => {
396                let _: f32 = *imm;
397                f(*out)
398            }
399            RegOp::NegReg(out, arg)
400            | RegOp::AbsReg(out, arg)
401            | RegOp::RecipReg(out, arg)
402            | RegOp::SqrtReg(out, arg)
403            | RegOp::SquareReg(out, arg)
404            | RegOp::FloorReg(out, arg)
405            | RegOp::CeilReg(out, arg)
406            | RegOp::RoundReg(out, arg)
407            | RegOp::CopyReg(out, arg)
408            | RegOp::SinReg(out, arg)
409            | RegOp::CosReg(out, arg)
410            | RegOp::TanReg(out, arg)
411            | RegOp::AsinReg(out, arg)
412            | RegOp::AcosReg(out, arg)
413            | RegOp::AtanReg(out, arg)
414            | RegOp::ExpReg(out, arg)
415            | RegOp::LnReg(out, arg)
416            | RegOp::NotReg(out, arg)
417            | RegOp::RandReg(out, arg) => {
418                f(*out);
419                f(*arg);
420            }
421            RegOp::AddRegImm(out, arg, imm)
422            | RegOp::MulRegImm(out, arg, imm)
423            | RegOp::DivRegImm(out, arg, imm)
424            | RegOp::DivImmReg(out, arg, imm)
425            | RegOp::SubImmReg(out, arg, imm)
426            | RegOp::SubRegImm(out, arg, imm)
427            | RegOp::AtanRegImm(out, arg, imm)
428            | RegOp::AtanImmReg(out, arg, imm)
429            | RegOp::MinRegImm(out, arg, imm)
430            | RegOp::MaxRegImm(out, arg, imm)
431            | RegOp::CompareRegImm(out, arg, imm)
432            | RegOp::CompareImmReg(out, arg, imm)
433            | RegOp::MixRegImm(out, arg, imm)
434            | RegOp::MixImmReg(out, arg, imm)
435            | RegOp::ModRegImm(out, arg, imm)
436            | RegOp::ModImmReg(out, arg, imm)
437            | RegOp::AndRegImm(out, arg, imm)
438            | RegOp::OrRegImm(out, arg, imm) => {
439                let _: f32 = *imm; // type-checking pattern
440                f(*out);
441                f(*arg);
442            }
443
444            RegOp::AddRegReg(out, lhs, rhs)
445            | RegOp::MulRegReg(out, lhs, rhs)
446            | RegOp::DivRegReg(out, lhs, rhs)
447            | RegOp::SubRegReg(out, lhs, rhs)
448            | RegOp::AtanRegReg(out, lhs, rhs)
449            | RegOp::MinRegReg(out, lhs, rhs)
450            | RegOp::MaxRegReg(out, lhs, rhs)
451            | RegOp::CompareRegReg(out, lhs, rhs)
452            | RegOp::MixRegReg(out, lhs, rhs)
453            | RegOp::ModRegReg(out, lhs, rhs)
454            | RegOp::AndRegReg(out, lhs, rhs)
455            | RegOp::OrRegReg(out, lhs, rhs) => {
456                f(*out);
457                f(*lhs);
458                f(*rhs);
459            }
460
461            RegOp::Output(reg, imm)
462            | RegOp::Input(reg, imm)
463            | RegOp::Store(reg, imm)
464            | RegOp::Load(reg, imm) => {
465                let _: u32 = *imm; // type-checking pattern
466                f(*reg)
467            }
468        }
469    }
470}