1 //! Performs various peephole optimizations.
4 use rustc_hir
::Mutability
;
5 use rustc_middle
::mir
::{
6 BinOp
, Body
, Constant
, ConstantKind
, LocalDecls
, Operand
, Place
, ProjectionElem
, Rvalue
,
7 SourceInfo
, Statement
, StatementKind
, Terminator
, TerminatorKind
, UnOp
,
9 use rustc_middle
::ty
::{self, TyCtxt}
;
11 pub struct InstCombine
;
13 impl<'tcx
> MirPass
<'tcx
> for InstCombine
{
14 fn is_enabled(&self, sess
: &rustc_session
::Session
) -> bool
{
15 sess
.mir_opt_level() > 0
18 fn run_pass(&self, tcx
: TyCtxt
<'tcx
>, body
: &mut Body
<'tcx
>) {
19 let (basic_blocks
, local_decls
) = body
.basic_blocks_and_local_decls_mut();
20 let ctx
= InstCombineContext { tcx, local_decls }
;
21 for block
in basic_blocks
.iter_mut() {
22 for statement
in block
.statements
.iter_mut() {
23 match statement
.kind
{
24 StatementKind
::Assign(box (_place
, ref mut rvalue
)) => {
25 ctx
.combine_bool_cmp(&statement
.source_info
, rvalue
);
26 ctx
.combine_ref_deref(&statement
.source_info
, rvalue
);
27 ctx
.combine_len(&statement
.source_info
, rvalue
);
33 ctx
.combine_primitive_clone(
34 &mut block
.terminator
.as_mut().unwrap(),
35 &mut block
.statements
,
41 struct InstCombineContext
<'tcx
, 'a
> {
43 local_decls
: &'a LocalDecls
<'tcx
>,
46 impl<'tcx
> InstCombineContext
<'tcx
, '_
> {
47 fn should_combine(&self, source_info
: &SourceInfo
, rvalue
: &Rvalue
<'tcx
>) -> bool
{
48 self.tcx
.consider_optimizing(|| {
49 format
!("InstCombine - Rvalue: {:?} SourceInfo: {:?}", rvalue
, source_info
)
53 /// Transform boolean comparisons into logical operations.
54 fn combine_bool_cmp(&self, source_info
: &SourceInfo
, rvalue
: &mut Rvalue
<'tcx
>) {
56 Rvalue
::BinaryOp(op @
(BinOp
::Eq
| BinOp
::Ne
), box (a
, b
)) => {
57 let new
= match (op
, self.try_eval_bool(a
), self.try_eval_bool(b
)) {
58 // Transform "Eq(a, true)" ==> "a"
59 (BinOp
::Eq
, _
, Some(true)) => Some(Rvalue
::Use(a
.clone())),
61 // Transform "Ne(a, false)" ==> "a"
62 (BinOp
::Ne
, _
, Some(false)) => Some(Rvalue
::Use(a
.clone())),
64 // Transform "Eq(true, b)" ==> "b"
65 (BinOp
::Eq
, Some(true), _
) => Some(Rvalue
::Use(b
.clone())),
67 // Transform "Ne(false, b)" ==> "b"
68 (BinOp
::Ne
, Some(false), _
) => Some(Rvalue
::Use(b
.clone())),
70 // Transform "Eq(false, b)" ==> "Not(b)"
71 (BinOp
::Eq
, Some(false), _
) => Some(Rvalue
::UnaryOp(UnOp
::Not
, b
.clone())),
73 // Transform "Ne(true, b)" ==> "Not(b)"
74 (BinOp
::Ne
, Some(true), _
) => Some(Rvalue
::UnaryOp(UnOp
::Not
, b
.clone())),
76 // Transform "Eq(a, false)" ==> "Not(a)"
77 (BinOp
::Eq
, _
, Some(false)) => Some(Rvalue
::UnaryOp(UnOp
::Not
, a
.clone())),
79 // Transform "Ne(a, true)" ==> "Not(a)"
80 (BinOp
::Ne
, _
, Some(true)) => Some(Rvalue
::UnaryOp(UnOp
::Not
, a
.clone())),
85 if let Some(new
) = new
&& self.should_combine(source_info
, rvalue
) {
94 fn try_eval_bool(&self, a
: &Operand
<'_
>) -> Option
<bool
> {
95 let a
= a
.constant()?
;
96 if a
.literal
.ty().is_bool() { a.literal.try_to_bool() }
else { None }
99 /// Transform "&(*a)" ==> "a".
100 fn combine_ref_deref(&self, source_info
: &SourceInfo
, rvalue
: &mut Rvalue
<'tcx
>) {
101 if let Rvalue
::Ref(_
, _
, place
) = rvalue
{
102 if let Some((base
, ProjectionElem
::Deref
)) = place
.as_ref().last_projection() {
103 if let ty
::Ref(_
, _
, Mutability
::Not
) =
104 base
.ty(self.local_decls
, self.tcx
).ty
.kind()
106 // The dereferenced place must have type `&_`, so that we don't copy `&mut _`.
111 if !self.should_combine(source_info
, rvalue
) {
115 *rvalue
= Rvalue
::Use(Operand
::Copy(Place
{
117 projection
: self.tcx
.intern_place_elems(base
.projection
),
123 /// Transform "Len([_; N])" ==> "N".
124 fn combine_len(&self, source_info
: &SourceInfo
, rvalue
: &mut Rvalue
<'tcx
>) {
125 if let Rvalue
::Len(ref place
) = *rvalue
{
126 let place_ty
= place
.ty(self.local_decls
, self.tcx
).ty
;
127 if let ty
::Array(_
, len
) = *place_ty
.kind() {
128 if !self.should_combine(source_info
, rvalue
) {
132 let literal
= ConstantKind
::from_const(len
, self.tcx
);
133 let constant
= Constant { span: source_info.span, literal, user_ty: None }
;
134 *rvalue
= Rvalue
::Use(Operand
::Constant(Box
::new(constant
)));
139 fn combine_primitive_clone(
141 terminator
: &mut Terminator
<'tcx
>,
142 statements
: &mut Vec
<Statement
<'tcx
>>,
144 let TerminatorKind
::Call { func, args, destination, target, .. }
= &mut terminator
.kind
147 // It's definitely not a clone if there are multiple arguments
152 let Some(destination_block
) = *target
155 // Only bother looking more if it's easy to know what we're calling
156 let Some((fn_def_id
, fn_substs
)) = func
.const_fn_def()
159 // Clone needs one subst, so we can cheaply rule out other stuff
160 if fn_substs
.len() != 1 {
164 // These types are easily available from locals, so check that before
165 // doing DefId lookups to figure out what we're actually calling.
166 let arg_ty
= args
[0].ty(self.local_decls
, self.tcx
);
168 let ty
::Ref(_region
, inner_ty
, Mutability
::Not
) = *arg_ty
.kind()
171 if !inner_ty
.is_trivially_pure_clone_copy() {
175 let trait_def_id
= self.tcx
.trait_of_item(fn_def_id
);
176 if trait_def_id
.is_none() || trait_def_id
!= self.tcx
.lang_items().clone_trait() {
180 if !self.tcx
.consider_optimizing(|| {
182 "InstCombine - Call: {:?} SourceInfo: {:?}",
183 (fn_def_id
, fn_substs
),
184 terminator
.source_info
190 let Some(arg_place
) = args
.pop().unwrap().place()
193 statements
.push(Statement
{
194 source_info
: terminator
.source_info
,
195 kind
: StatementKind
::Assign(Box
::new((
197 Rvalue
::Use(Operand
::Copy(
198 arg_place
.project_deeper(&[ProjectionElem
::Deref
], self.tcx
),
202 terminator
.kind
= TerminatorKind
::Goto { target: destination_block }
;