Skip to main content

rumba_core/
reduce.rs

1use rustc_hash::FxHashMap as HashMap;
2
3use crate::{expr::Expr, varint::make_mask};
4
5/// Distributes an expression
6fn distribute<F>(
7    v: &mut [Expr],
8    self_cst: fn(Vec<Expr>) -> Expr,
9    child_cst: fn(Vec<Expr>) -> Expr,
10    extractor: F,
11) -> Option<Expr>
12where
13    F: Fn(&mut [Expr]) -> Option<(usize, Vec<Expr>)>,
14{
15    if let Some((idx, terms)) = extractor(v) {
16        let mut distributed_terms: Vec<Expr> = Vec::with_capacity(terms.len());
17
18        for term in terms {
19            let mut copy = v.to_vec();
20            copy[idx] = term;
21            distributed_terms.push(self_cst(copy));
22        }
23
24        return Some(child_cst(distributed_terms));
25    }
26
27    None
28}
29
30macro_rules! distribute {
31    ($self:ident, $child:ident, $v:expr) => {
32        distribute($v, Expr::$self, Expr::$child, distribute!(@extractor $child))
33    };
34
35    (@extractor $variant:ident) => {
36        |v: &mut [Expr]| {
37            v.iter_mut().enumerate().find_map(|(i, e)| match e {
38                Expr::$variant(v) => Some((i, std::mem::take(v))),
39                _ => None,
40            })
41        }
42    };
43}
44
45/// Remove pairs of elements in a vector
46fn remove_pairs(v: Vec<Expr>) -> Vec<Expr> {
47    let mut reduced = Vec::with_capacity(v.len());
48    let mut iter = v.into_iter().peekable();
49
50    while let Some(current) = iter.next() {
51        let mut count = 1;
52
53        while let Some(next) = iter.peek() {
54            if *next == current {
55                count += 1;
56                iter.next();
57                continue;
58            }
59
60            break;
61        }
62
63        if (count & 1) == 1 {
64            reduced.push(current);
65        }
66    }
67
68    reduced
69}
70
71/// Deduplicates a list
72fn dedupe(mut v: Vec<Expr>) -> Vec<Expr> {
73    // TODO: Somehow using hashset here makes the program crash
74    // We need a better comparison function
75    v.sort();
76    v.dedup();
77    v
78}
79
80struct Reducer {
81    mask: u64,
82}
83
84/// The result enum for a flattening handler
85#[must_use]
86enum FlattenResult {
87    Expr(Expr),
88    Vec(Vec<Expr>),
89    None,
90}
91
92impl Reducer {
93    /// Flattens an expression by concatenating any same typed child expression into it
94    fn flatten<F>(&self, v: Vec<Expr>, mut handler: F) -> Vec<Expr>
95    where
96        F: FnMut(Expr) -> FlattenResult,
97    {
98        let mut flat = Vec::with_capacity(v.len());
99
100        let mut stack: Vec<_> = v.into_iter().map(|e| self.reduce_masked(e)).collect();
101
102        while let Some(e) = stack.pop() {
103            match handler(e) {
104                FlattenResult::Vec(mut v) => {
105                    stack.append(&mut v);
106                }
107
108                FlattenResult::Expr(e) => flat.push(e),
109
110                FlattenResult::None => {}
111            }
112        }
113
114        flat
115    }
116
117    pub fn group_terms(&self, exprs: Vec<Expr>) -> Expr {
118        let initial_len = exprs.len();
119
120        let mut map =
121            HashMap::<Expr, u64>::with_capacity_and_hasher(initial_len, Default::default());
122
123        for e in exprs.into_iter() {
124            if let Expr::Scale(c, e) = e {
125                let count = map.entry(*e).or_insert(0);
126                *count = count.wrapping_add(c);
127            } else {
128                let count = map.entry(e).or_insert(0);
129                *count = count.wrapping_add(1);
130            }
131        }
132
133        let mut out = Vec::with_capacity(map.len());
134
135        for (e, mut count) in map {
136            count &= self.mask;
137
138            if count == 0 {
139                continue;
140            }
141
142            out.push(Expr::scale(count, e));
143        }
144
145        out.sort();
146
147        if initial_len > out.len() {
148            self.reduce_masked(Expr::Add(out))
149        } else {
150            Expr::Add(out)
151        }
152    }
153
154    /// Reduces a not node
155    fn reduce_not(&self, expr: Expr) -> Expr {
156        match expr {
157            // !!x = x
158            Expr::Not(x) => self.reduce_masked(*x),
159
160            Expr::Const(v) => Expr::Const(!v),
161
162            // De Morgan's laws
163            Expr::And(exprs) => {
164                Expr::Or(exprs.into_iter().map(|e| self.reduce_masked(!e)).collect())
165            }
166
167            // De Morgan's laws
168            Expr::Or(exprs) => {
169                Expr::And(exprs.into_iter().map(|e| self.reduce_masked(!e)).collect())
170            }
171
172            _ => !self.reduce_masked(expr),
173        }
174    }
175
176    /// Reduces a scale node
177    fn reduce_scale(&self, scale: u64, expr: Expr) -> Expr {
178        let scale = scale & self.mask;
179
180        match scale {
181            0 => Expr::zero(),
182
183            1 => self.reduce_masked(expr),
184
185            _ => match self.reduce_masked(expr) {
186                Expr::Const(c2) => Expr::Const(scale.wrapping_mul(c2) & self.mask),
187
188                Expr::Scale(c2, e) => {
189                    let c = scale.wrapping_mul(c2) & self.mask;
190                    match c {
191                        0 => Expr::zero(),
192                        1 => *e,
193                        c => c * *e,
194                    }
195                }
196
197                Expr::Add(sum) => {
198                    // TODO: the arith reduce is unnecessary here
199                    self.reduce_masked(Expr::Add(sum.into_iter().map(|e| scale * e).collect()))
200                }
201
202                other => scale * other,
203            },
204        }
205    }
206
207    /// Reduces a and node
208    fn reduce_and(&self, exprs: Vec<Expr>) -> Expr {
209        let mut c: u64 = self.mask;
210
211        let mut flat = self.flatten(exprs, |e| match e {
212            Expr::And(v) => FlattenResult::Vec(v),
213            Expr::Const(v) => {
214                c &= v;
215                FlattenResult::None
216            }
217            _ => FlattenResult::Expr(e),
218        });
219
220        c &= self.mask;
221
222        if c == 0 {
223            return Expr::zero();
224        }
225
226        if c != self.mask {
227            flat.push(Expr::Const(c));
228        }
229
230        if let Some(distributed) = distribute!(And, Xor, &mut flat) {
231            return self.reduce_masked(distributed);
232        }
233
234        flat = dedupe(flat);
235
236        match flat.len() {
237            0 => Expr::Const(u64::MAX),
238            1 => flat.remove(0),
239            _ => Expr::And(flat),
240        }
241    }
242
243    fn reduce_or(&self, exprs: Vec<Expr>) -> Expr {
244        let mut c: u64 = 0;
245
246        let mut flat = self.flatten(exprs, |e| match e {
247            Expr::Or(v) => FlattenResult::Vec(v),
248            Expr::Const(v) => {
249                c |= v;
250                FlattenResult::None
251            }
252            _ => FlattenResult::Expr(e),
253        });
254
255        c &= self.mask;
256
257        if c != 0 {
258            flat.push(Expr::Const(c));
259        }
260
261        if let Some(distributed) = distribute!(Or, And, &mut flat) {
262            return self.reduce_masked(distributed);
263        }
264
265        flat = dedupe(flat);
266
267        match flat.len() {
268            0 => Expr::zero(),
269            1 => flat.remove(0),
270            _ => Expr::Or(flat),
271        }
272    }
273
274    fn reduce_xor(&self, exprs: Vec<Expr>) -> Expr {
275        let mut c: u64 = 0;
276
277        let mut flat = self.flatten(exprs, |e| match e {
278            Expr::Xor(v) => FlattenResult::Vec(v),
279            Expr::Const(v) => {
280                c ^= v;
281                FlattenResult::None
282            }
283            _ => FlattenResult::Expr(e),
284        });
285
286        c &= self.mask;
287
288        if c != 0 {
289            flat.push(Expr::Const(c));
290        }
291
292        flat = remove_pairs(flat);
293
294        match flat.len() {
295            0 => Expr::zero(),
296            1 => flat.remove(0),
297            _ => Expr::Xor(flat),
298        }
299    }
300
301    fn reduce_add(&self, exprs: Vec<Expr>) -> Expr {
302        // Used with dynamic masking
303        if self.mask == 1 {
304            return self.reduce_xor(exprs);
305        }
306
307        let mut c: u64 = 0;
308
309        let mut flat = self.flatten(exprs, |e| match e {
310            Expr::Add(v) => FlattenResult::Vec(v),
311            Expr::Const(v) => {
312                c = c.wrapping_add(v);
313                FlattenResult::None
314            }
315            _ => FlattenResult::Expr(e),
316        });
317
318        c &= self.mask;
319
320        if c != 0 {
321            flat.push(Expr::Const(c));
322        }
323
324        match flat.len() {
325            0 => Expr::zero(),
326            1 => flat.remove(0),
327            _ => self.group_terms(flat),
328        }
329    }
330
331    fn reduce_mul(&self, exprs: Vec<Expr>) -> Expr {
332        // Used with dynamic masking
333        if self.mask == 1 {
334            return self.reduce_and(exprs);
335        }
336
337        let mut c: u64 = 1;
338
339        let mut flat = self.flatten(exprs, |e| match e {
340            Expr::Mul(v) => FlattenResult::Vec(v),
341            Expr::Const(v) => {
342                c = c.wrapping_mul(v);
343                FlattenResult::None
344            }
345            Expr::Scale(s, v) => {
346                c = c.wrapping_mul(s);
347                FlattenResult::Expr(*v)
348            }
349            _ => FlattenResult::Expr(e),
350        });
351
352        c &= self.mask;
353
354        if c == 0 {
355            return Expr::zero();
356        }
357
358        if let Some(distributed) = distribute!(Mul, Add, &mut flat) {
359            return self.reduce_masked(Expr::scale(c, distributed));
360        }
361
362        flat.sort();
363
364        match flat.len() {
365            0 => Expr::Const(c),
366            1 => Expr::scale(c, flat.remove(0)),
367            _ => Expr::scale(c, Expr::Mul(flat)),
368        }
369    }
370
371    fn reduce_masked(&self, expr: Expr) -> Expr {
372        match expr {
373            Expr::Var(_) => expr,
374
375            Expr::Const(c) => Expr::Const(c & self.mask),
376
377            Expr::Not(expr) => self.reduce_not(*expr),
378
379            Expr::Scale(c, e) => self.reduce_scale(c, *e),
380
381            Expr::And(exprs) => self.reduce_and(exprs),
382
383            Expr::Or(exprs) => self.reduce_or(exprs),
384
385            Expr::Xor(exprs) => self.reduce_xor(exprs),
386
387            Expr::Add(exprs) => self.reduce_add(exprs),
388
389            Expr::Mul(exprs) => self.reduce_mul(exprs),
390        }
391    }
392
393    // fn reduce_(&self, expr: Expr) -> Expr {
394    //     let old = expr.clone();
395    //     let res = self.reduce_(expr);
396
397    //     if let Err((v, v1, v2)) = old.sem_equal_masked(&res, self.mask, 500) {
398    //         println!("Semantic error {} vs {}", old, res);
399    //         println!("Semantic error {} vs {}", v1, v2);
400    //         println!("{}\n\n", old.symbol(false));
401    //     }
402    //     res
403    // }
404}
405
406impl Expr {
407    pub(crate) fn reduce_masked(self, mask: u64) -> Self {
408        Reducer { mask }.reduce_masked(self)
409    }
410
411    /// Canonicalizes the expression on `n` bits (constant folding, flattening
412    /// and normalization), without attempting MBA simplification.
413    pub fn reduce(self, n: u8) -> Self {
414        self.reduce_masked(make_mask(n))
415    }
416}