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    #[derive(
279        Copy,
280        Clone,
281        Debug,
282        PartialEq,
283        Serialize,
284        Deserialize,
285        strum::EnumDiscriminants,
286    )]
287    #[strum_discriminants(derive(
288        strum::EnumIter,
289        strum::EnumCount,
290        strum::IntoStaticStr,
291        strum::FromRepr,
292    ))]
293    #[strum_discriminants(doc = "[`RegOp`] discriminant value")]
294    pub enum RegOp<u8> {
295        // default variants
296        /// Read from a memory slot to a register
297        Load(u8, u32),
298
299        /// Write from a register to a memory slot
300        Store(u8, u32),
301    }
302);