jyafn/op/
arithmetic.rs

1use serde_derive::{Deserialize, Serialize};
2
3use crate::{impl_op, Graph, Ref, Type};
4
5use super::{unique_for, Op};
6
7/// Implements `a + b`.
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9pub struct Add;
10
11#[typetag::serde]
12impl Op for Add {
13    impl_op! {}
14
15    fn annotate(&mut self, self_id: usize, graph: &Graph, args: &[Type]) -> Option<Type> {
16        Some(match args {
17            [Type::Float, Type::Float] => Type::Float,
18            _ => return None,
19        })
20    }
21
22    fn render_into(
23        &self,
24        graph: &Graph,
25        output: qbe::Value,
26        args: &[Ref],
27        func: &mut qbe::Function,
28        namespace: &str,
29    ) {
30        func.assign_instr(
31            output,
32            Type::Float.render(),
33            qbe::Instr::Add(args[0].render(), args[1].render()),
34        )
35    }
36
37    fn const_eval(&self, graph: &Graph, args: &[Ref]) -> Option<Ref> {
38        if Ref::from(0.0) == args[0] {
39            return Some(args[1]);
40        }
41
42        if Ref::from(0.0) == args[1] {
43            return Some(args[0]);
44        }
45
46        if let Some((x, y)) = args[0].as_f64().zip(args[1].as_f64()) {
47            return Some((x + y).into());
48        }
49
50        None
51    }
52}
53
54/// Implements `a - b`.
55#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
56pub struct Sub;
57
58#[typetag::serde]
59impl Op for Sub {
60    impl_op! {}
61
62    fn annotate(&mut self, self_id: usize, graph: &Graph, args: &[Type]) -> Option<Type> {
63        Some(match args {
64            [Type::Float, Type::Float] => Type::Float,
65            _ => return None,
66        })
67    }
68
69    fn render_into(
70        &self,
71        graph: &Graph,
72        output: qbe::Value,
73        args: &[Ref],
74        func: &mut qbe::Function,
75        namespace: &str,
76    ) {
77        func.assign_instr(
78            output,
79            Type::Float.render(),
80            qbe::Instr::Sub(args[0].render(), args[1].render()),
81        )
82    }
83
84    fn const_eval(&self, graph: &Graph, args: &[Ref]) -> Option<Ref> {
85        if let Ref::Const(Type::Float, 0) = args[1] {
86            return Some(args[0]);
87        }
88
89        if let Some((x, y)) = args[0].as_f64().zip(args[1].as_f64()) {
90            return Some((x - y).into());
91        }
92
93        None
94    }
95}
96
97/// Implements `a * b`.
98#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
99pub struct Mul;
100
101#[typetag::serde]
102impl Op for Mul {
103    impl_op! {}
104
105    fn annotate(&mut self, self_id: usize, graph: &Graph, args: &[Type]) -> Option<Type> {
106        Some(match args {
107            [Type::Float, Type::Float] => Type::Float,
108            _ => return None,
109        })
110    }
111
112    fn render_into(
113        &self,
114        graph: &Graph,
115        output: qbe::Value,
116        args: &[Ref],
117        func: &mut qbe::Function,
118        namespace: &str,
119    ) {
120        func.assign_instr(
121            output,
122            Type::Float.render(),
123            qbe::Instr::Mul(args[0].render(), args[1].render()),
124        )
125    }
126
127    fn const_eval(&self, graph: &Graph, args: &[Ref]) -> Option<Ref> {
128        if Ref::from(1.0) == args[0] {
129            return Some(args[1]);
130        }
131
132        if Ref::from(1.0) == args[1] {
133            return Some(args[0]);
134        }
135
136        if let Some((x, y)) = args[0].as_f64().zip(args[1].as_f64()) {
137            return Some((x * y).into());
138        }
139
140        None
141    }
142}
143
144/// Implements `a / b`.
145#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
146pub struct Div;
147
148#[typetag::serde]
149impl Op for Div {
150    impl_op! {}
151
152    fn annotate(&mut self, self_id: usize, graph: &Graph, args: &[Type]) -> Option<Type> {
153        Some(match args {
154            [Type::Float, Type::Float] => Type::Float,
155            _ => return None,
156        })
157    }
158
159    fn render_into(
160        &self,
161        graph: &Graph,
162        output: qbe::Value,
163        args: &[Ref],
164        func: &mut qbe::Function,
165        namespace: &str,
166    ) {
167        func.assign_instr(
168            output,
169            Type::Float.render(),
170            qbe::Instr::Div(args[0].render(), args[1].render()),
171        )
172    }
173
174    fn const_eval(&self, graph: &Graph, args: &[Ref]) -> Option<Ref> {
175        if Ref::from(1.0) == args[1] {
176            return Some(args[0]);
177        }
178
179        if let Some((x, y)) = args[0].as_f64().zip(args[1].as_f64()) {
180            return Some((x / y).into());
181        }
182
183        None
184    }
185}
186
187/// Implements `a % b`.
188#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
189pub struct Rem;
190
191#[typetag::serde]
192impl Op for Rem {
193    impl_op! {}
194
195    fn annotate(&mut self, self_id: usize, graph: &Graph, args: &[Type]) -> Option<Type> {
196        Some(match args {
197            [Type::Float, Type::Float] => Type::Float,
198            _ => return None,
199        })
200    }
201
202    fn render_into(
203        &self,
204        graph: &Graph,
205        output: qbe::Value,
206        args: &[Ref],
207        func: &mut qbe::Function,
208        namespace: &str,
209    ) {
210        // `rem` does not work for floats in QBE. So, we need to resort to pfuncs!
211        super::call::Call("rem".to_string()).render_into(graph, output, args, func, namespace)
212    }
213
214    fn const_eval(&self, graph: &Graph, args: &[Ref]) -> Option<Ref> {
215        if let Some((x, y)) = args[0].as_f64().zip(args[1].as_f64()) {
216            return Some((x % y).into());
217        }
218
219        None
220    }
221}
222
223/// Implements `-a`.
224#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
225pub struct Neg;
226
227#[typetag::serde]
228impl Op for Neg {
229    impl_op! {}
230
231    fn annotate(&mut self, self_id: usize, graph: &Graph, args: &[Type]) -> Option<Type> {
232        Some(match args {
233            [Type::Float] => Type::Float,
234            _ => return None,
235        })
236    }
237
238    fn render_into(
239        &self,
240        graph: &Graph,
241        output: qbe::Value,
242        args: &[Ref],
243        func: &mut qbe::Function,
244        namespace: &str,
245    ) {
246        func.assign_instr(
247            output,
248            Type::Float.render(),
249            qbe::Instr::Neg(args[0].render()),
250        )
251    }
252
253    fn const_eval(&self, graph: &Graph, args: &[Ref]) -> Option<Ref> {
254        if let Some(x) = args[0].as_f64() {
255            return Some((-x).into());
256        }
257
258        None
259    }
260}
261
262/// Implements `|a|`.
263#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
264pub struct Abs;
265
266#[typetag::serde]
267impl Op for Abs {
268    impl_op! {}
269
270    fn annotate(&mut self, self_id: usize, graph: &Graph, args: &[Type]) -> Option<Type> {
271        Some(match args {
272            [Type::Float] => Type::Float,
273            _ => return None,
274        })
275    }
276
277    fn render_into(
278        &self,
279        graph: &Graph,
280        output: qbe::Value,
281        args: &[Ref],
282        func: &mut qbe::Function,
283        namespace: &str,
284    ) {
285        let test_temp = qbe::Value::Temporary(unique_for(output.clone(), "abs.test"));
286        func.assign_instr(
287            test_temp.clone(),
288            qbe::Type::Byte,
289            qbe::Instr::Cmp(
290                Type::Float.render(),
291                qbe::Cmp::Ge,
292                args[0].render(),
293                qbe::Value::Const(0),
294            ),
295        );
296
297        let true_side = unique_for(output.clone(), "abs.if.true");
298        let false_side = unique_for(output.clone(), "abs.if.false");
299        let end_side = unique_for(output.clone(), "abs.if.end");
300
301        func.add_instr(qbe::Instr::Jnz(
302            test_temp,
303            true_side.clone(),
304            false_side.clone(),
305        ));
306
307        func.add_block(true_side);
308        func.assign_instr(
309            output.clone(),
310            Type::Float.render(),
311            qbe::Instr::Copy(args[0].render()),
312        );
313        func.add_instr(qbe::Instr::Jmp(end_side.clone()));
314
315        func.add_block(false_side);
316        func.assign_instr(
317            output,
318            Type::Float.render(),
319            qbe::Instr::Neg(args[0].render()),
320        );
321
322        func.add_block(end_side);
323    }
324
325    fn const_eval(&self, graph: &Graph, args: &[Ref]) -> Option<Ref> {
326        if let Some(x) = args[0].as_f64() {
327            return Some(x.abs().into());
328        }
329
330        None
331    }
332}