Skip to content

Commit 1d65888

Browse files
committed
Optimize MIR for comparison of references
1 parent 37998ab commit 1d65888

File tree

4 files changed

+155
-0
lines changed

4 files changed

+155
-0
lines changed

compiler/rustc_mir_transform/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ mod multiple_return_terminators;
8686
mod normalize_array_len;
8787
mod nrvo;
8888
mod prettify;
89+
mod ref_cmp_simplify;
8990
mod ref_prop;
9091
mod remove_noop_landing_pads;
9192
mod remove_storage_markers;
@@ -561,6 +562,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
561562
&instsimplify::InstSimplify,
562563
&simplify::SimplifyLocals::BeforeConstProp,
563564
&copy_prop::CopyProp,
565+
&ref_cmp_simplify::RefCmpSimplify,
564566
&ref_prop::ReferencePropagation,
565567
// Perform `SeparateConstSwitch` after SSA-based analyses, as cloning blocks may
566568
// destroy the SSA property. It should still happen before const-propagation, so the
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use crate::MirPass;
2+
use rustc_middle::mir::patch::MirPatch;
3+
use rustc_middle::mir::*;
4+
use rustc_middle::ty::TyCtxt;
5+
6+
pub struct RefCmpSimplify;
7+
8+
impl<'tcx> MirPass<'tcx> for RefCmpSimplify {
9+
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
10+
self.simplify_ref_cmp(tcx, body)
11+
}
12+
}
13+
14+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15+
enum MatchState {
16+
Empty,
17+
Deref { src_statement_idx: usize, dst: Local, src: Local },
18+
CopiedFrom { src_statement_idx: usize, dst: Local, real_src: Local },
19+
Completed { src_statement_idx: usize, dst: Local, real_src: Local },
20+
}
21+
22+
impl RefCmpSimplify {
23+
fn simplify_ref_cmp<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
24+
debug!("body: {:#?}", body);
25+
26+
let n_bbs = body.basic_blocks.len() as u32;
27+
for bb in 0..n_bbs {
28+
let bb = BasicBlock::from_u32(bb);
29+
let mut max = Local::MAX;
30+
'repeat: loop {
31+
let mut state = MatchState::Empty;
32+
let bb_data = &body.basic_blocks[bb];
33+
for (i, stmt) in bb_data.statements.iter().enumerate().rev() {
34+
state = match (state, &stmt.kind) {
35+
(
36+
MatchState::Empty,
37+
StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Copy(rhs)))),
38+
) if rhs.has_deref() && lhs.ty(body, tcx).ty.is_primitive() => {
39+
let Some(dst) = lhs.as_local() else {
40+
continue
41+
};
42+
let Some(src) = rhs.local_or_deref_local() else {
43+
continue;
44+
};
45+
if max <= dst {
46+
continue;
47+
}
48+
max = dst;
49+
MatchState::Deref { dst, src, src_statement_idx: i }
50+
}
51+
(
52+
MatchState::Deref { src, dst, src_statement_idx },
53+
StatementKind::Assign(box (lhs, Rvalue::CopyForDeref(rhs))),
54+
) if lhs.as_local() == Some(src) && rhs.has_deref() => {
55+
let Some(real_src) = rhs.local_or_deref_local() else{
56+
continue;
57+
};
58+
MatchState::CopiedFrom { src_statement_idx, dst, real_src }
59+
}
60+
(
61+
MatchState::CopiedFrom { src_statement_idx, dst, real_src },
62+
StatementKind::Assign(box (
63+
lhs,
64+
Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Shallow, rhs),
65+
)),
66+
) if lhs.as_local() == Some(real_src) => {
67+
let Some(real_src) = rhs.as_local() else {
68+
continue;
69+
};
70+
MatchState::Completed { dst, real_src, src_statement_idx }
71+
}
72+
_ => continue,
73+
};
74+
if let MatchState::Completed { dst, real_src, src_statement_idx } = state {
75+
let mut patch = MirPatch::new(&body);
76+
let src = Place::from(real_src);
77+
let src = src.project_deeper(&[PlaceElem::Deref], tcx);
78+
let dst = Place::from(dst);
79+
let new_stmt =
80+
StatementKind::Assign(Box::new((dst, Rvalue::Use(Operand::Copy(src)))));
81+
patch.add_statement(
82+
Location { block: bb, statement_index: src_statement_idx + 1 },
83+
new_stmt,
84+
);
85+
patch.apply(body);
86+
continue 'repeat;
87+
}
88+
}
89+
break;
90+
}
91+
}
92+
}
93+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
- // MIR for `opt1` before RefCmpSimplify
2+
+ // MIR for `opt1` after RefCmpSimplify
3+
4+
fn opt1(_1: &u8, _2: &u8) -> bool {
5+
debug x => _1; // in scope 0 at $DIR/ref_int_cmp.rs:+0:13: +0:14
6+
debug y => _2; // in scope 0 at $DIR/ref_int_cmp.rs:+0:21: +0:22
7+
let mut _0: bool; // return place in scope 0 at $DIR/ref_int_cmp.rs:+0:32: +0:36
8+
let mut _3: &&u8; // in scope 0 at $DIR/ref_int_cmp.rs:+1:3: +1:4
9+
let mut _4: &&u8; // in scope 0 at $DIR/ref_int_cmp.rs:+1:7: +1:8
10+
let _5: &u8; // in scope 0 at $DIR/ref_int_cmp.rs:+1:7: +1:8
11+
let mut _8: &u8; // in scope 0 at $SRC_DIR/core/src/cmp.rs:LL:COL
12+
let mut _9: &u8; // in scope 0 at $SRC_DIR/core/src/cmp.rs:LL:COL
13+
scope 1 (inlined cmp::impls::<impl PartialOrd for &u8>::lt) { // at $DIR/ref_int_cmp.rs:5:3: 5:8
14+
debug self => _3; // in scope 1 at $SRC_DIR/core/src/cmp.rs:LL:COL
15+
debug other => _4; // in scope 1 at $SRC_DIR/core/src/cmp.rs:LL:COL
16+
let mut _6: &u8; // in scope 1 at $SRC_DIR/core/src/cmp.rs:LL:COL
17+
let mut _7: &u8; // in scope 1 at $SRC_DIR/core/src/cmp.rs:LL:COL
18+
scope 2 (inlined cmp::impls::<impl PartialOrd for u8>::lt) { // at $SRC_DIR/core/src/cmp.rs:LL:COL
19+
debug self => _6; // in scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
20+
debug other => _7; // in scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
21+
let mut _10: u8; // in scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
22+
let mut _11: u8; // in scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
23+
}
24+
}
25+
26+
bb0: {
27+
StorageLive(_3); // scope 0 at $DIR/ref_int_cmp.rs:+1:3: +1:4
28+
_3 = &_1; // scope 0 at $DIR/ref_int_cmp.rs:+1:3: +1:4
29+
StorageLive(_4); // scope 0 at $DIR/ref_int_cmp.rs:+1:7: +1:8
30+
StorageLive(_5); // scope 0 at $DIR/ref_int_cmp.rs:+1:7: +1:8
31+
_5 = _2; // scope 0 at $DIR/ref_int_cmp.rs:+1:7: +1:8
32+
_4 = &_5; // scope 0 at $DIR/ref_int_cmp.rs:+1:7: +1:8
33+
_6 = deref_copy (*_3); // scope 1 at $SRC_DIR/core/src/cmp.rs:LL:COL
34+
_7 = deref_copy (*_4); // scope 1 at $SRC_DIR/core/src/cmp.rs:LL:COL
35+
StorageLive(_10); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
36+
_10 = (*_6); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
37+
+ _10 = (*_1); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
38+
StorageLive(_11); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
39+
_11 = (*_7); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
40+
+ _11 = (*_5); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
41+
_0 = Lt(move _10, move _11); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
42+
StorageDead(_11); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
43+
StorageDead(_10); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
44+
StorageDead(_4); // scope 0 at $DIR/ref_int_cmp.rs:+1:7: +1:8
45+
StorageDead(_3); // scope 0 at $DIR/ref_int_cmp.rs:+1:7: +1:8
46+
StorageDead(_5); // scope 0 at $DIR/ref_int_cmp.rs:+2:1: +2:2
47+
return; // scope 0 at $DIR/ref_int_cmp.rs:+2:2: +2:2
48+
}
49+
}
50+

tests/mir-opt/ref_int_cmp.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// compile-flags: -O -Zmir-opt-level=3
2+
3+
// EMIT_MIR ref_int_cmp.opt1.RefCmpSimplify.diff
4+
pub fn opt1(x: &u8, y: &u8) -> bool {
5+
x < y
6+
}
7+
8+
fn main() {
9+
opt1(&1, &2);
10+
}

0 commit comments

Comments
 (0)