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
72            // RegImm opcodes (without a choice)
73            #[doc = "Add a register and an immediate"]
74            AddRegImm($t, $t, f32),
75            #[doc = "Multiply a register and an immediate"]
76            MulRegImm($t, $t, f32),
77            #[doc = "Divides a register and an immediate"]
78            DivRegImm($t, $t, f32),
79            #[doc = "Divides an immediate by a register"]
80            DivImmReg($t, $t, f32),
81            #[doc = "Subtract a register from an immediate"]
82            SubImmReg($t, $t, f32),
83            #[doc = "Subtract an immediate from a register"]
84            SubRegImm($t, $t, f32),
85            #[doc = "Take the module (least nonnegative remainder) of two registers"]
86            ModRegReg($t, $t, $t),
87            #[doc = "Take the module (least nonnegative remainder) of a register and an immediate"]
88            ModRegImm($t, $t, f32),
89            #[doc = "atan2 of a position `(y, x)` specified as register, immediate"]
90            AtanRegImm($t, $t, f32),
91            #[doc = "Compares a register with an immediate"]
92            CompareRegImm($t, $t, f32),
93
94            // RegImm opcodes (with a choice)
95            #[doc = "Compute the minimum of a register and an immediate"]
96            MinRegImm($t, $t, f32),
97            #[doc = "Compute the maximum of a register and an immediate"]
98            MaxRegImm($t, $t, f32),
99            #[doc = "Logical `AND` (short-circuiting)\n\nThis is equivalent to `if lhs == 0 { lhs } else { imm }`"]
100            AndRegImm($t, $t, f32),
101            #[doc = "Logical `OR` (short-circuiting)\n\nThis is equivalent to `if lhs != 0 { lhs } else { imm }`"]
102            OrRegImm($t, $t, f32),
103
104            // ImmReg opcodes (without a choice)
105            #[doc = "Take the module (least nonnegative remainder) of an immediate and a register"]
106            ModImmReg($t, $t, f32),
107            #[doc = "atan2 of a position `(y, x)` specified as immediate, register"]
108            AtanImmReg($t, $t, f32),
109            #[doc = "Compares an immediate with a register"]
110            CompareImmReg($t, $t, f32),
111
112            // RegReg opcodes (without a choice)
113            #[doc = "Add two registers"]
114            AddRegReg($t, $t, $t),
115            #[doc = "Multiply two registers"]
116            MulRegReg($t, $t, $t),
117            #[doc = "Divides two registers"]
118            DivRegReg($t, $t, $t),
119            #[doc = "Subtract one register from another"]
120            SubRegReg($t, $t, $t),
121            #[doc = "Compares two registers"]
122            CompareRegReg($t, $t, $t),
123            #[doc = "atan2 of a position `(y, x)` specified as register, register"]
124            AtanRegReg($t, $t, $t),
125
126            // RegReg opcodes (with a choice)
127            #[doc = "Take the minimum of two registers"]
128            MinRegReg($t, $t, $t),
129            #[doc = "Take the maximum of two registers"]
130            MaxRegReg($t, $t, $t),
131            #[doc = "Logical `AND` (short-circuiting)\n\nThis is equivalent to `if lhs == 0 { lhs } else { rhs }`"]
132            AndRegReg($t, $t, $t),
133            #[doc = "Logical `OR` (short-circuiting)\n\nThis is equivalent to `if lhs != 0 { lhs } else { rhs }`"]
134            OrRegReg($t, $t, $t),
135
136            $(
137                $(#[$($a)*])*
138                $foo($($i),*)
139             ),*
140        }
141    };
142}
143
144opcodes!(
145    /// Basic operations that can be performed in a tape
146    ///
147    /// Arguments, in order, are
148    /// - Output register
149    /// - LHS register (or input slot for [`Input`](SsaOp::Input))
150    /// - RHS register (or immediate for `*Imm`)
151    ///
152    /// Each "register" represents an SSA slot, which is never reused.
153    #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
154    pub enum SsaOp<u32> {
155        // default variants
156    }
157);
158
159impl SsaOp {
160    /// Returns the output pseudo-register
161    pub fn output(&self) -> Option<u32> {
162        match self {
163            SsaOp::Input(out, ..)
164            | SsaOp::CopyImm(out, ..)
165            | SsaOp::NegReg(out, ..)
166            | SsaOp::AbsReg(out, ..)
167            | SsaOp::RecipReg(out, ..)
168            | SsaOp::SqrtReg(out, ..)
169            | SsaOp::SquareReg(out, ..)
170            | SsaOp::FloorReg(out, ..)
171            | SsaOp::CeilReg(out, ..)
172            | SsaOp::RoundReg(out, ..)
173            | SsaOp::CopyReg(out, ..)
174            | SsaOp::SinReg(out, ..)
175            | SsaOp::CosReg(out, ..)
176            | SsaOp::TanReg(out, ..)
177            | SsaOp::AsinReg(out, ..)
178            | SsaOp::AcosReg(out, ..)
179            | SsaOp::AtanReg(out, ..)
180            | SsaOp::ExpReg(out, ..)
181            | SsaOp::LnReg(out, ..)
182            | SsaOp::NotReg(out, ..)
183            | SsaOp::AddRegImm(out, ..)
184            | SsaOp::MulRegImm(out, ..)
185            | SsaOp::DivRegImm(out, ..)
186            | SsaOp::DivImmReg(out, ..)
187            | SsaOp::SubImmReg(out, ..)
188            | SsaOp::SubRegImm(out, ..)
189            | SsaOp::AddRegReg(out, ..)
190            | SsaOp::MulRegReg(out, ..)
191            | SsaOp::DivRegReg(out, ..)
192            | SsaOp::SubRegReg(out, ..)
193            | SsaOp::AtanRegReg(out, ..)
194            | SsaOp::AtanRegImm(out, ..)
195            | SsaOp::AtanImmReg(out, ..)
196            | SsaOp::MinRegImm(out, ..)
197            | SsaOp::MaxRegImm(out, ..)
198            | SsaOp::MinRegReg(out, ..)
199            | SsaOp::MaxRegReg(out, ..)
200            | SsaOp::CompareRegReg(out, ..)
201            | SsaOp::CompareRegImm(out, ..)
202            | SsaOp::CompareImmReg(out, ..)
203            | SsaOp::ModRegReg(out, ..)
204            | SsaOp::ModRegImm(out, ..)
205            | SsaOp::ModImmReg(out, ..)
206            | SsaOp::AndRegImm(out, ..)
207            | SsaOp::AndRegReg(out, ..)
208            | SsaOp::OrRegImm(out, ..)
209            | SsaOp::OrRegReg(out, ..) => Some(*out),
210            SsaOp::Output(..) => None,
211        }
212    }
213    /// Returns true if the given opcode is associated with a choice
214    pub fn has_choice(&self) -> bool {
215        match self {
216            SsaOp::Input(..)
217            | SsaOp::Output(..)
218            | SsaOp::CopyImm(..)
219            | SsaOp::NegReg(..)
220            | SsaOp::AbsReg(..)
221            | SsaOp::RecipReg(..)
222            | SsaOp::SqrtReg(..)
223            | SsaOp::SquareReg(..)
224            | SsaOp::FloorReg(..)
225            | SsaOp::CeilReg(..)
226            | SsaOp::RoundReg(..)
227            | SsaOp::CopyReg(..)
228            | SsaOp::SinReg(..)
229            | SsaOp::CosReg(..)
230            | SsaOp::TanReg(..)
231            | SsaOp::AsinReg(..)
232            | SsaOp::AcosReg(..)
233            | SsaOp::AtanReg(..)
234            | SsaOp::ExpReg(..)
235            | SsaOp::LnReg(..)
236            | SsaOp::NotReg(..)
237            | SsaOp::AddRegImm(..)
238            | SsaOp::MulRegImm(..)
239            | SsaOp::SubRegImm(..)
240            | SsaOp::SubImmReg(..)
241            | SsaOp::AddRegReg(..)
242            | SsaOp::MulRegReg(..)
243            | SsaOp::SubRegReg(..)
244            | SsaOp::DivRegReg(..)
245            | SsaOp::DivRegImm(..)
246            | SsaOp::DivImmReg(..)
247            | SsaOp::AtanRegReg(..)
248            | SsaOp::AtanRegImm(..)
249            | SsaOp::AtanImmReg(..)
250            | SsaOp::CompareRegReg(..)
251            | SsaOp::CompareRegImm(..)
252            | SsaOp::CompareImmReg(..)
253            | SsaOp::ModRegReg(..)
254            | SsaOp::ModRegImm(..)
255            | SsaOp::ModImmReg(..) => false,
256            SsaOp::MinRegImm(..)
257            | SsaOp::MaxRegImm(..)
258            | SsaOp::MinRegReg(..)
259            | SsaOp::MaxRegReg(..)
260            | SsaOp::AndRegImm(..)
261            | SsaOp::AndRegReg(..)
262            | SsaOp::OrRegImm(..)
263            | SsaOp::OrRegReg(..) => true,
264        }
265    }
266}
267
268opcodes!(
269    /// Operations used in register-allocated tapes
270    ///
271    /// Arguments, in order, are
272    /// - Output register
273    /// - LHS register (or input slot for [`Input`](RegOp::Input))
274    /// - RHS register (or immediate for `*Imm`)
275    ///
276    /// We have a maximum of 256 registers, though some tapes (e.g. ones
277    /// targeting physical hardware) may choose to use fewer.
278    ///
279    /// Note that memory slots are not zero-indexed; they start at `N` (where
280    /// `N` is the register count).
281    #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
282    pub enum RegOp<u8> {
283        // default variants
284        /// Read from a memory slot to a register
285        Load(u8, u32),
286
287        /// Write from a register to a memory slot
288        Store(u8, u32),
289    }
290);
291
292impl RegOp {
293    /// Apply a mutating function to every register in the op
294    ///
295    /// Both inputs and outputs are visited
296    pub fn visit_regs_mut<F: FnMut(&mut u8)>(&mut self, mut f: F) {
297        match self {
298            RegOp::CopyImm(out, imm) => {
299                let _: f32 = *imm;
300                f(out)
301            }
302            RegOp::NegReg(out, arg)
303            | RegOp::AbsReg(out, arg)
304            | RegOp::RecipReg(out, arg)
305            | RegOp::SqrtReg(out, arg)
306            | RegOp::SquareReg(out, arg)
307            | RegOp::FloorReg(out, arg)
308            | RegOp::CeilReg(out, arg)
309            | RegOp::RoundReg(out, arg)
310            | RegOp::CopyReg(out, arg)
311            | RegOp::SinReg(out, arg)
312            | RegOp::CosReg(out, arg)
313            | RegOp::TanReg(out, arg)
314            | RegOp::AsinReg(out, arg)
315            | RegOp::AcosReg(out, arg)
316            | RegOp::AtanReg(out, arg)
317            | RegOp::ExpReg(out, arg)
318            | RegOp::LnReg(out, arg)
319            | RegOp::NotReg(out, arg) => {
320                f(out);
321                f(arg);
322            }
323            RegOp::AddRegImm(out, arg, imm)
324            | RegOp::MulRegImm(out, arg, imm)
325            | RegOp::DivRegImm(out, arg, imm)
326            | RegOp::DivImmReg(out, arg, imm)
327            | RegOp::SubImmReg(out, arg, imm)
328            | RegOp::SubRegImm(out, arg, imm)
329            | RegOp::AtanRegImm(out, arg, imm)
330            | RegOp::AtanImmReg(out, arg, imm)
331            | RegOp::MinRegImm(out, arg, imm)
332            | RegOp::MaxRegImm(out, arg, imm)
333            | RegOp::CompareRegImm(out, arg, imm)
334            | RegOp::CompareImmReg(out, arg, imm)
335            | RegOp::ModRegImm(out, arg, imm)
336            | RegOp::ModImmReg(out, arg, imm)
337            | RegOp::AndRegImm(out, arg, imm)
338            | RegOp::OrRegImm(out, arg, imm) => {
339                let _: f32 = *imm; // type-checking pattern
340                f(out);
341                f(arg);
342            }
343
344            RegOp::AddRegReg(out, lhs, rhs)
345            | RegOp::MulRegReg(out, lhs, rhs)
346            | RegOp::DivRegReg(out, lhs, rhs)
347            | RegOp::SubRegReg(out, lhs, rhs)
348            | RegOp::AtanRegReg(out, lhs, rhs)
349            | RegOp::MinRegReg(out, lhs, rhs)
350            | RegOp::MaxRegReg(out, lhs, rhs)
351            | RegOp::CompareRegReg(out, lhs, rhs)
352            | RegOp::ModRegReg(out, lhs, rhs)
353            | RegOp::AndRegReg(out, lhs, rhs)
354            | RegOp::OrRegReg(out, lhs, rhs) => {
355                f(out);
356                f(lhs);
357                f(rhs);
358            }
359
360            RegOp::Output(reg, imm)
361            | RegOp::Input(reg, imm)
362            | RegOp::Store(reg, imm)
363            | RegOp::Load(reg, imm) => {
364                let _: u32 = *imm; // type-checking pattern
365                f(reg)
366            }
367        }
368    }
369
370    /// Apply a function to every register in the op
371    ///
372    /// Both inputs and outputs are visited
373    pub fn visit_regs<F: FnMut(u8)>(&self, mut f: F) {
374        match self {
375            RegOp::CopyImm(out, imm) => {
376                let _: f32 = *imm;
377                f(*out)
378            }
379            RegOp::NegReg(out, arg)
380            | RegOp::AbsReg(out, arg)
381            | RegOp::RecipReg(out, arg)
382            | RegOp::SqrtReg(out, arg)
383            | RegOp::SquareReg(out, arg)
384            | RegOp::FloorReg(out, arg)
385            | RegOp::CeilReg(out, arg)
386            | RegOp::RoundReg(out, arg)
387            | RegOp::CopyReg(out, arg)
388            | RegOp::SinReg(out, arg)
389            | RegOp::CosReg(out, arg)
390            | RegOp::TanReg(out, arg)
391            | RegOp::AsinReg(out, arg)
392            | RegOp::AcosReg(out, arg)
393            | RegOp::AtanReg(out, arg)
394            | RegOp::ExpReg(out, arg)
395            | RegOp::LnReg(out, arg)
396            | RegOp::NotReg(out, arg) => {
397                f(*out);
398                f(*arg);
399            }
400            RegOp::AddRegImm(out, arg, imm)
401            | RegOp::MulRegImm(out, arg, imm)
402            | RegOp::DivRegImm(out, arg, imm)
403            | RegOp::DivImmReg(out, arg, imm)
404            | RegOp::SubImmReg(out, arg, imm)
405            | RegOp::SubRegImm(out, arg, imm)
406            | RegOp::AtanRegImm(out, arg, imm)
407            | RegOp::AtanImmReg(out, arg, imm)
408            | RegOp::MinRegImm(out, arg, imm)
409            | RegOp::MaxRegImm(out, arg, imm)
410            | RegOp::CompareRegImm(out, arg, imm)
411            | RegOp::CompareImmReg(out, arg, imm)
412            | RegOp::ModRegImm(out, arg, imm)
413            | RegOp::ModImmReg(out, arg, imm)
414            | RegOp::AndRegImm(out, arg, imm)
415            | RegOp::OrRegImm(out, arg, imm) => {
416                let _: f32 = *imm; // type-checking pattern
417                f(*out);
418                f(*arg);
419            }
420
421            RegOp::AddRegReg(out, lhs, rhs)
422            | RegOp::MulRegReg(out, lhs, rhs)
423            | RegOp::DivRegReg(out, lhs, rhs)
424            | RegOp::SubRegReg(out, lhs, rhs)
425            | RegOp::AtanRegReg(out, lhs, rhs)
426            | RegOp::MinRegReg(out, lhs, rhs)
427            | RegOp::MaxRegReg(out, lhs, rhs)
428            | RegOp::CompareRegReg(out, lhs, rhs)
429            | RegOp::ModRegReg(out, lhs, rhs)
430            | RegOp::AndRegReg(out, lhs, rhs)
431            | RegOp::OrRegReg(out, lhs, rhs) => {
432                f(*out);
433                f(*lhs);
434                f(*rhs);
435            }
436
437            RegOp::Output(reg, imm)
438            | RegOp::Input(reg, imm)
439            | RegOp::Store(reg, imm)
440            | RegOp::Load(reg, imm) => {
441                let _: u32 = *imm; // type-checking pattern
442                f(*reg)
443            }
444        }
445    }
446}