Skip to content

Commit 508eec4

Browse files
committed
Combine instructions immediately
No functional changes intended
1 parent a9292d8 commit 508eec4

File tree

2 files changed

+80
-148
lines changed

2 files changed

+80
-148
lines changed

compiler/rustc_middle/src/mir/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ mod type_foldable;
5454
pub mod visit;
5555

5656
/// Types for locals
57-
type LocalDecls<'tcx> = IndexVec<Local, LocalDecl<'tcx>>;
57+
pub type LocalDecls<'tcx> = IndexVec<Local, LocalDecl<'tcx>>;
5858

5959
pub trait HasLocalDecls<'tcx> {
6060
fn local_decls(&self) -> &LocalDecls<'tcx>;

compiler/rustc_mir/src/transform/instcombine.rs

+79-147
Original file line numberDiff line numberDiff line change
@@ -1,190 +1,122 @@
11
//! Performs various peephole optimizations.
22
33
use crate::transform::MirPass;
4-
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
54
use rustc_hir::Mutability;
6-
use rustc_index::vec::Idx;
7-
use rustc_middle::mir::visit::{MutVisitor, Visitor};
85
use rustc_middle::mir::{
9-
BinOp, Body, Constant, Local, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue,
6+
BinOp, Body, Constant, LocalDecls, Operand, Place, ProjectionElem, Rvalue, SourceInfo,
7+
StatementKind,
108
};
119
use rustc_middle::ty::{self, TyCtxt};
12-
use std::mem;
1310

1411
pub struct InstCombine;
1512

1613
impl<'tcx> MirPass<'tcx> for InstCombine {
1714
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
18-
// First, find optimization opportunities. This is done in a pre-pass to keep the MIR
19-
// read-only so that we can do global analyses on the MIR in the process (e.g.
20-
// `Place::ty()`).
21-
let optimizations = {
22-
let mut optimization_finder = OptimizationFinder::new(body, tcx);
23-
optimization_finder.visit_body(body);
24-
optimization_finder.optimizations
25-
};
26-
27-
if !optimizations.is_empty() {
28-
// Then carry out those optimizations.
29-
MutVisitor::visit_body(&mut InstCombineVisitor { optimizations, tcx }, body);
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);
24+
}
25+
_ => {}
26+
}
27+
}
3028
}
3129
}
3230
}
3331

34-
pub struct InstCombineVisitor<'tcx> {
35-
optimizations: OptimizationList<'tcx>,
32+
struct InstCombineContext<'tcx, 'a> {
3633
tcx: TyCtxt<'tcx>,
34+
local_decls: &'a LocalDecls<'tcx>,
3735
}
3836

39-
impl<'tcx> InstCombineVisitor<'tcx> {
40-
fn should_combine(&self, rvalue: &Rvalue<'tcx>, location: Location) -> bool {
37+
impl<'tcx, 'a> InstCombineContext<'tcx, 'a> {
38+
fn should_combine(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool {
4139
self.tcx.consider_optimizing(|| {
42-
format!("InstCombine - Rvalue: {:?} Location: {:?}", rvalue, location)
40+
format!("InstCombine - Rvalue: {:?} SourceInfo: {:?}", rvalue, source_info)
4341
})
4442
}
45-
}
46-
47-
impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> {
48-
fn tcx(&self) -> TyCtxt<'tcx> {
49-
self.tcx
50-
}
5143

52-
fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) {
53-
if self.optimizations.and_stars.remove(&location) && self.should_combine(rvalue, location) {
54-
debug!("replacing `&*`: {:?}", rvalue);
55-
let new_place = match rvalue {
56-
Rvalue::Ref(_, _, place) => {
57-
if let &[ref proj_l @ .., proj_r] = place.projection.as_ref() {
58-
place.projection = self.tcx().intern_place_elems(&[proj_r]);
59-
60-
Place {
61-
// Replace with dummy
62-
local: mem::replace(&mut place.local, Local::new(0)),
63-
projection: self.tcx().intern_place_elems(proj_l),
64-
}
65-
} else {
66-
unreachable!();
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);
6772
}
6873
}
69-
_ => bug!("Detected `&*` but didn't find `&*`!"),
70-
};
71-
*rvalue = Rvalue::Use(Operand::Copy(new_place))
72-
}
73-
74-
if let Some(constant) = self.optimizations.arrays_lengths.remove(&location) {
75-
if self.should_combine(rvalue, location) {
76-
debug!("replacing `Len([_; N])`: {:?}", rvalue);
77-
*rvalue = Rvalue::Use(Operand::Constant(box constant));
7874
}
79-
}
8075

81-
if let Some(operand) = self.optimizations.unneeded_equality_comparison.remove(&location) {
82-
if self.should_combine(rvalue, location) {
83-
debug!("replacing {:?} with {:?}", rvalue, operand);
84-
*rvalue = Rvalue::Use(operand);
85-
}
76+
_ => {}
8677
}
87-
88-
// We do not call super_rvalue as we are not interested in any other parts of the tree
89-
}
90-
}
91-
92-
/// Finds optimization opportunities on the MIR.
93-
struct OptimizationFinder<'b, 'tcx> {
94-
body: &'b Body<'tcx>,
95-
tcx: TyCtxt<'tcx>,
96-
optimizations: OptimizationList<'tcx>,
97-
}
98-
99-
impl OptimizationFinder<'b, 'tcx> {
100-
fn new(body: &'b Body<'tcx>, tcx: TyCtxt<'tcx>) -> OptimizationFinder<'b, 'tcx> {
101-
OptimizationFinder { body, tcx, optimizations: OptimizationList::default() }
10278
}
10379

104-
fn find_unneeded_equality_comparison(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
105-
// find Ne(_place, false) or Ne(false, _place)
106-
// or Eq(_place, true) or Eq(true, _place)
107-
if let Rvalue::BinaryOp(op, l, r) = rvalue {
108-
let const_to_find = if *op == BinOp::Ne {
109-
false
110-
} else if *op == BinOp::Eq {
111-
true
112-
} else {
113-
return;
114-
};
115-
// (const, _place)
116-
if let Some(o) = self.find_operand_in_equality_comparison_pattern(l, r, const_to_find) {
117-
self.optimizations.unneeded_equality_comparison.insert(location, o.clone());
118-
}
119-
// (_place, const)
120-
else if let Some(o) =
121-
self.find_operand_in_equality_comparison_pattern(r, l, const_to_find)
122-
{
123-
self.optimizations.unneeded_equality_comparison.insert(location, o.clone());
124-
}
125-
}
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 }
12683
}
12784

128-
fn find_operand_in_equality_comparison_pattern(
129-
&self,
130-
l: &Operand<'tcx>,
131-
r: &'a Operand<'tcx>,
132-
const_to_find: bool,
133-
) -> Option<&'a Operand<'tcx>> {
134-
let const_ = l.constant()?;
135-
if const_.literal.ty == self.tcx.types.bool
136-
&& const_.literal.val.try_to_bool() == Some(const_to_find)
137-
{
138-
if r.place().is_some() {
139-
return Some(r);
140-
}
141-
}
142-
143-
None
144-
}
145-
}
146-
147-
impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> {
148-
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
85+
/// Transform "&(*a)" ==> "a".
86+
fn combine_ref_deref(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
14987
if let Rvalue::Ref(_, _, place) = rvalue {
150-
if let Some((place_base, ProjectionElem::Deref)) = place.as_ref().last_projection() {
151-
// The dereferenced place must have type `&_`.
152-
let ty = place_base.ty(self.body, self.tcx).ty;
153-
if let ty::Ref(_, _, Mutability::Not) = ty.kind() {
154-
self.optimizations.and_stars.insert(location);
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;
15595
}
156-
}
157-
}
15896

159-
if let Rvalue::Len(ref place) = *rvalue {
160-
let place_ty = place.ty(&self.body.local_decls, self.tcx).ty;
161-
if let ty::Array(_, len) = place_ty.kind() {
162-
let span = self.body.source_info(location).span;
163-
let constant = Constant { span, literal: len, user_ty: None };
164-
self.optimizations.arrays_lengths.insert(location, constant);
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+
}));
165105
}
166106
}
167-
168-
self.find_unneeded_equality_comparison(rvalue, location);
169-
170-
// We do not call super_rvalue as we are not interested in any other parts of the tree
171107
}
172-
}
173108

174-
#[derive(Default)]
175-
struct OptimizationList<'tcx> {
176-
and_stars: FxHashSet<Location>,
177-
arrays_lengths: FxHashMap<Location, Constant<'tcx>>,
178-
unneeded_equality_comparison: FxHashMap<Location, Operand<'tcx>>,
179-
}
109+
/// Transform "Len([_; N])" ==> "N".
110+
fn combine_len(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) {
111+
if let Rvalue::Len(ref place) = *rvalue {
112+
let place_ty = place.ty(self.local_decls, self.tcx).ty;
113+
if let ty::Array(_, len) = place_ty.kind() {
114+
if !self.should_combine(source_info, rvalue) {
115+
return;
116+
}
180117

181-
impl<'tcx> OptimizationList<'tcx> {
182-
fn is_empty(&self) -> bool {
183-
match self {
184-
OptimizationList { and_stars, arrays_lengths, unneeded_equality_comparison } => {
185-
and_stars.is_empty()
186-
&& arrays_lengths.is_empty()
187-
&& unneeded_equality_comparison.is_empty()
118+
let constant = Constant { span: source_info.span, literal: len, user_ty: None };
119+
*rvalue = Rvalue::Use(Operand::Constant(box constant));
188120
}
189121
}
190122
}

0 commit comments

Comments
 (0)