Skip to main content

rustc_mir_transform/
canonicalization.rs

1use rustc_middle::mir::*;
2use rustc_middle::ty::{Ty, TyCtxt};
3
4/// Canonicalize operands of binary operations.
5/// This only canonicalize their ordering for now.
6pub(super) fn canonicalize_binary_operands<'tcx, 'body>(
7    tcx: TyCtxt<'tcx>,
8    local_decls: &'body LocalDecls<'tcx>,
9    op: BinOp,
10    lhs: &mut Operand<'tcx>,
11    rhs: &mut Operand<'tcx>,
12) -> bool {
13    fn needs_swap<T: std::cmp::Ord>(lhs: &T, rhs: &T) -> Option<bool> {
14        use std::cmp::Ordering;
15
16        match lhs.cmp(rhs) {
17            Ordering::Less => Some(false),
18            Ordering::Equal => None,
19            Ordering::Greater => Some(true),
20        }
21    }
22
23    fn projection_rank<'tcx>(proj: &ProjectionElem<Local, Ty<'tcx>>) -> usize {
24        match proj {
25            ProjectionElem::Deref => 0,
26            ProjectionElem::Field(..) => 1,
27            ProjectionElem::Index(_) => 2,
28            ProjectionElem::ConstantIndex { .. } => 3,
29            ProjectionElem::Subslice { .. } => 4,
30            ProjectionElem::Downcast(..) => 5,
31            ProjectionElem::OpaqueCast(_) => 6,
32            ProjectionElem::UnwrapUnsafeBinder(_) => 7,
33            ProjectionElem::PhantomDeref => 8,
34        }
35    }
36
37    fn needs_swap_operands<'tcx>(lhs: &Operand<'tcx>, rhs: &Operand<'tcx>) -> bool {
38        let (lhs, rhs) = match (lhs, rhs) {
39            (Operand::Constant(_), Operand::Copy(_) | Operand::Move(_)) => return true,
40            (Operand::Copy(lhs) | Operand::Move(lhs), Operand::Copy(rhs) | Operand::Move(rhs)) => {
41                (lhs, rhs)
42            }
43            _ => return false,
44        };
45
46        if let Some(ns) = needs_swap(&lhs.local, &rhs.local) {
47            return ns;
48        }
49
50        let lhs_proj = lhs.projection;
51        let rhs_proj = rhs.projection;
52
53        if let Some(ns) = needs_swap(&lhs_proj.len(), &rhs_proj.len()) {
54            return ns;
55        }
56
57        for (lhs_proj, rhs_proj) in lhs_proj.iter().zip(rhs_proj.iter()) {
58            if let Some(ns) = match (lhs_proj, rhs_proj) {
59                (ProjectionElem::Field(lhs, _), ProjectionElem::Field(rhs, _)) => {
60                    needs_swap(&lhs, &rhs)
61                }
62
63                (ProjectionElem::Index(lhs), ProjectionElem::Index(rhs)) => needs_swap(&lhs, &rhs),
64
65                (
66                    ProjectionElem::ConstantIndex { offset: l0, min_length: l1, from_end: l2 },
67                    ProjectionElem::ConstantIndex { offset: r0, min_length: r1, from_end: r2 },
68                ) => needs_swap(&(l0, l1, l2), &(r0, r1, r2)),
69
70                (
71                    ProjectionElem::Subslice { from: l0, to: l1, from_end: l2 },
72                    ProjectionElem::Subslice { from: r0, to: r1, from_end: r2 },
73                ) => needs_swap(&(l0, l1, l2), &(r0, r1, r2)),
74
75                (ProjectionElem::Downcast(l0, l1), ProjectionElem::Downcast(r0, r1)) => {
76                    needs_swap(&(l0, l1), &(r0, r1))
77                }
78
79                _ => needs_swap(&projection_rank(&lhs_proj), &projection_rank(&rhs_proj)),
80            } {
81                return ns;
82            }
83        }
84
85        false
86    }
87
88    let needs_integral = match op {
89        BinOp::Eq | BinOp::Ne => false,
90
91        BinOp::Add
92        | BinOp::AddUnchecked
93        | BinOp::AddWithOverflow
94        | BinOp::Mul
95        | BinOp::MulUnchecked
96        | BinOp::MulWithOverflow
97        | BinOp::BitXor
98        | BinOp::BitAnd
99        | BinOp::BitOr => true,
100
101        BinOp::Sub
102        | BinOp::SubUnchecked
103        | BinOp::SubWithOverflow
104        | BinOp::Div
105        | BinOp::Rem
106        | BinOp::Shl
107        | BinOp::ShlUnchecked
108        | BinOp::Shr
109        | BinOp::ShrUnchecked
110        | BinOp::Lt
111        | BinOp::Le
112        | BinOp::Ge
113        | BinOp::Gt
114        | BinOp::Cmp
115        | BinOp::Offset => return false,
116    };
117
118    if needs_integral && !lhs.ty(local_decls, tcx).is_integral() {
119        return false;
120    }
121
122    if needs_swap_operands(lhs, rhs) {
123        std::mem::swap(lhs, rhs);
124        true
125    } else {
126        false
127    }
128}