]>
Commit | Line | Data |
---|---|---|
1b1a35ee XL |
1 | //! Performs various peephole optimizations. |
2 | ||
29967ef6 | 3 | use crate::transform::MirPass; |
1b1a35ee | 4 | use rustc_hir::Mutability; |
1b1a35ee | 5 | use rustc_middle::mir::{ |
5869c6ff XL |
6 | BinOp, Body, Constant, LocalDecls, Operand, Place, ProjectionElem, Rvalue, SourceInfo, |
7 | StatementKind, | |
1b1a35ee XL |
8 | }; |
9 | use rustc_middle::ty::{self, TyCtxt}; | |
1b1a35ee XL |
10 | |
11 | pub struct InstCombine; | |
12 | ||
13 | impl<'tcx> MirPass<'tcx> for InstCombine { | |
29967ef6 | 14 | fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
5869c6ff XL |
15 | let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); |
16 | let ctx = InstCombineContext { tcx, local_decls }; | |
17 | for block in basic_blocks.iter_mut() { | |
18 | for statement in block.statements.iter_mut() { | |
19 | match statement.kind { | |
20 | StatementKind::Assign(box (_place, ref mut rvalue)) => { | |
21 | ctx.combine_bool_cmp(&statement.source_info, rvalue); | |
22 | ctx.combine_ref_deref(&statement.source_info, rvalue); | |
23 | ctx.combine_len(&statement.source_info, rvalue); | |
1b1a35ee | 24 | } |
5869c6ff | 25 | _ => {} |
1b1a35ee | 26 | } |
fc512014 | 27 | } |
1b1a35ee | 28 | } |
1b1a35ee XL |
29 | } |
30 | } | |
31 | ||
5869c6ff | 32 | struct InstCombineContext<'tcx, 'a> { |
1b1a35ee | 33 | tcx: TyCtxt<'tcx>, |
5869c6ff | 34 | local_decls: &'a LocalDecls<'tcx>, |
1b1a35ee XL |
35 | } |
36 | ||
5869c6ff XL |
37 | impl<'tcx, 'a> InstCombineContext<'tcx, 'a> { |
38 | fn should_combine(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool { | |
39 | self.tcx.consider_optimizing(|| { | |
40 | format!("InstCombine - Rvalue: {:?} SourceInfo: {:?}", rvalue, source_info) | |
41 | }) | |
1b1a35ee XL |
42 | } |
43 | ||
5869c6ff XL |
44 | /// Transform boolean comparisons into logical operations. |
45 | fn combine_bool_cmp(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { | |
46 | match rvalue { | |
47 | Rvalue::BinaryOp(op @ (BinOp::Eq | BinOp::Ne), a, b) => { | |
48 | let new = match (op, self.try_eval_bool(a), self.try_eval_bool(b)) { | |
49 | // Transform "Eq(a, true)" ==> "a" | |
50 | (BinOp::Eq, _, Some(true)) => Some(a.clone()), | |
51 | ||
52 | // Transform "Ne(a, false)" ==> "a" | |
53 | (BinOp::Ne, _, Some(false)) => Some(a.clone()), | |
54 | ||
55 | // Transform "Eq(true, b)" ==> "b" | |
56 | (BinOp::Eq, Some(true), _) => Some(b.clone()), | |
57 | ||
58 | // Transform "Ne(false, b)" ==> "b" | |
59 | (BinOp::Ne, Some(false), _) => Some(b.clone()), | |
60 | ||
61 | // FIXME: Consider combining remaining comparisons into logical operations: | |
62 | // Transform "Eq(false, b)" ==> "Not(b)" | |
63 | // Transform "Ne(true, b)" ==> "Not(b)" | |
64 | // Transform "Eq(a, false)" ==> "Not(a)" | |
65 | // Transform "Ne(a, true)" ==> "Not(a)" | |
66 | _ => None, | |
67 | }; | |
68 | ||
69 | if let Some(new) = new { | |
70 | if self.should_combine(source_info, rvalue) { | |
71 | *rvalue = Rvalue::Use(new); | |
1b1a35ee XL |
72 | } |
73 | } | |
74 | } | |
1b1a35ee | 75 | |
5869c6ff | 76 | _ => {} |
1b1a35ee XL |
77 | } |
78 | } | |
79 | ||
5869c6ff XL |
80 | fn try_eval_bool(&self, a: &Operand<'_>) -> Option<bool> { |
81 | let a = a.constant()?; | |
82 | if a.literal.ty.is_bool() { a.literal.val.try_to_bool() } else { None } | |
1b1a35ee | 83 | } |
1b1a35ee | 84 | |
5869c6ff XL |
85 | /// Transform "&(*a)" ==> "a". |
86 | fn combine_ref_deref(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { | |
1b1a35ee | 87 | if let Rvalue::Ref(_, _, place) = rvalue { |
5869c6ff XL |
88 | if let Some((base, ProjectionElem::Deref)) = place.as_ref().last_projection() { |
89 | if let ty::Ref(_, _, Mutability::Not) = | |
90 | base.ty(self.local_decls, self.tcx).ty.kind() | |
91 | { | |
92 | // The dereferenced place must have type `&_`, so that we don't copy `&mut _`. | |
93 | } else { | |
94 | return; | |
1b1a35ee | 95 | } |
5869c6ff XL |
96 | |
97 | if !self.should_combine(source_info, rvalue) { | |
98 | return; | |
99 | } | |
100 | ||
101 | *rvalue = Rvalue::Use(Operand::Copy(Place { | |
102 | local: base.local, | |
103 | projection: self.tcx.intern_place_elems(base.projection), | |
104 | })); | |
1b1a35ee XL |
105 | } |
106 | } | |
5869c6ff | 107 | } |
1b1a35ee | 108 | |
5869c6ff XL |
109 | /// Transform "Len([_; N])" ==> "N". |
110 | fn combine_len(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { | |
1b1a35ee | 111 | if let Rvalue::Len(ref place) = *rvalue { |
5869c6ff | 112 | let place_ty = place.ty(self.local_decls, self.tcx).ty; |
1b1a35ee | 113 | if let ty::Array(_, len) = place_ty.kind() { |
5869c6ff XL |
114 | if !self.should_combine(source_info, rvalue) { |
115 | return; | |
116 | } | |
117 | ||
118 | let constant = Constant { span: source_info.span, literal: len, user_ty: None }; | |
119 | *rvalue = Rvalue::Use(Operand::Constant(box constant)); | |
1b1a35ee XL |
120 | } |
121 | } | |
1b1a35ee XL |
122 | } |
123 | } |