rustc_mir_transform/
canonicalization.rs1use rustc_middle::mir::*;
2use rustc_middle::ty::{Ty, TyCtxt};
3
4pub(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}