Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable MIR reference propagation by default #109025

Merged
merged 7 commits into from
Jul 14, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,14 +553,14 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
&normalize_array_len::NormalizeArrayLen, // has to run after `slice::len` lowering
&const_goto::ConstGoto,
&remove_unneeded_drops::RemoveUnneededDrops,
&ref_prop::ReferencePropagation,
&sroa::ScalarReplacementOfAggregates,
&match_branches::MatchBranchSimplification,
// inst combine is after MatchBranchSimplification to clean up Ne(_1, false)
&multiple_return_terminators::MultipleReturnTerminators,
&instsimplify::InstSimplify,
&simplify::SimplifyLocals::BeforeConstProp,
&copy_prop::CopyProp,
&ref_prop::ReferencePropagation,
// Perform `SeparateConstSwitch` after SSA-based analyses, as cloning blocks may
// destroy the SSA property. It should still happen before const-propagation, so the
// latter pass will leverage the created opportunities.
Expand Down
42 changes: 27 additions & 15 deletions compiler/rustc_mir_transform/src/ref_prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub struct ReferencePropagation;

impl<'tcx> MirPass<'tcx> for ReferencePropagation {
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
sess.mir_opt_level() >= 4
sess.mir_opt_level() >= 2
}

#[instrument(level = "trace", skip(self, tcx, body))]
Expand Down Expand Up @@ -355,7 +355,10 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
}

fn visit_var_debug_info(&mut self, debuginfo: &mut VarDebugInfo<'tcx>) {
if let VarDebugInfoContents::Place(ref mut place) = debuginfo.value
// If the debuginfo is a pointer to another place:
// - if it's a reborrow, see through it;
// - if it's a direct borrow, increase `debuginfo.references`.
while let VarDebugInfoContents::Place(ref mut place) = debuginfo.value
&& place.projection.is_empty()
&& let Value::Pointer(target, _) = self.targets[place.local]
&& target.projection.iter().all(|p| p.can_use_in_debuginfo())
Expand All @@ -369,28 +372,37 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
debuginfo.references = references;
*place = target;
self.any_replacement = true;
} else {
break
}
}

// Simplify eventual projections left inside `debuginfo`.
self.super_var_debug_info(debuginfo);
}

fn visit_place(&mut self, place: &mut Place<'tcx>, ctxt: PlaceContext, loc: Location) {
if place.projection.first() != Some(&PlaceElem::Deref) {
return;
}

loop {
if let Value::Pointer(target, _) = self.targets[place.local] {
let perform_opt = matches!(ctxt, PlaceContext::NonUse(_))
|| self.allowed_replacements.contains(&(target.local, loc));

if perform_opt {
*place = target.project_deeper(&place.projection[1..], self.tcx);
self.any_replacement = true;
continue;
if place.projection.first() != Some(&PlaceElem::Deref) {
return;
}

let Value::Pointer(target, _) = self.targets[place.local] else { return };

let perform_opt = match ctxt {
PlaceContext::NonUse(NonUseContext::VarDebugInfo) => {
target.projection.iter().all(|p| p.can_use_in_debuginfo())
}
PlaceContext::NonUse(_) => true,
_ => self.allowed_replacements.contains(&(target.local, loc)),
};

if !perform_opt {
return;
}

break;
*place = target.project_deeper(&place.projection[1..], self.tcx);
self.any_replacement = true;
}
}

Expand Down
16 changes: 10 additions & 6 deletions tests/codegen/slice-init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub fn zero_sized_elem() {
// CHECK-NOT: br label %repeat_loop_header{{.*}}
// CHECK-NOT: call void @llvm.memset.p0
let x = [(); 4];
drop(&x);
opaque(&x);
}

// CHECK-LABEL: @zero_len_array
Expand All @@ -17,7 +17,7 @@ pub fn zero_len_array() {
// CHECK-NOT: br label %repeat_loop_header{{.*}}
// CHECK-NOT: call void @llvm.memset.p0
let x = [4; 0];
drop(&x);
opaque(&x);
}

// CHECK-LABEL: @byte_array
Expand All @@ -26,7 +26,7 @@ pub fn byte_array() {
// CHECK: call void @llvm.memset.{{.+}}({{i8\*|ptr}} {{.*}}, i8 7, i{{[0-9]+}} 4
// CHECK-NOT: br label %repeat_loop_header{{.*}}
let x = [7u8; 4];
drop(&x);
opaque(&x);
}

#[allow(dead_code)]
Expand All @@ -42,7 +42,7 @@ pub fn byte_enum_array() {
// CHECK: call void @llvm.memset.{{.+}}({{i8\*|ptr}} {{.*}}, i8 {{.*}}, i{{[0-9]+}} 4
// CHECK-NOT: br label %repeat_loop_header{{.*}}
let x = [Init::Memset; 4];
drop(&x);
opaque(&x);
}

// CHECK-LABEL: @zeroed_integer_array
Expand All @@ -51,7 +51,7 @@ pub fn zeroed_integer_array() {
// CHECK: call void @llvm.memset.{{.+}}({{i8\*|ptr}} {{.*}}, i8 0, i{{[0-9]+}} 16
// CHECK-NOT: br label %repeat_loop_header{{.*}}
let x = [0u32; 4];
drop(&x);
opaque(&x);
}

// CHECK-LABEL: @nonzero_integer_array
Expand All @@ -60,5 +60,9 @@ pub fn nonzero_integer_array() {
// CHECK: br label %repeat_loop_header{{.*}}
// CHECK-NOT: call void @llvm.memset.p0
let x = [0x1a_2b_3c_4d_u32; 4];
drop(&x);
opaque(&x);
}

// Use an opaque function to prevent rustc from removing useless drops.
#[inline(never)]
pub fn opaque(_: impl Sized) {}
32 changes: 16 additions & 16 deletions tests/codegen/slice-ref-equality.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,48 +44,48 @@ pub fn is_zero_array(data: &[u8; 4]) -> bool {
// equality for non-byte types also just emit a `bcmp`, not a loop.

// CHECK-LABEL: @eq_slice_of_nested_u8(
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1
// CHECK-SAME: [[USIZE]] noundef %3
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
// CHECK-SAME: [[USIZE]] noundef %y.1
#[no_mangle]
fn eq_slice_of_nested_u8(x: &[[u8; 3]], y: &[[u8; 3]]) -> bool {
// CHECK: icmp eq [[USIZE]] %1, %3
// CHECK: %[[BYTES:.+]] = mul nsw [[USIZE]] %1, 3
// CHECK: icmp eq [[USIZE]] %x.1, %y.1
// CHECK: %[[BYTES:.+]] = mul nsw [[USIZE]] %x.1, 3
// CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}({{i8\*|ptr}}
// CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
x == y
}

// CHECK-LABEL: @eq_slice_of_i32(
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1
// CHECK-SAME: [[USIZE]] noundef %3
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
// CHECK-SAME: [[USIZE]] noundef %y.1
#[no_mangle]
fn eq_slice_of_i32(x: &[i32], y: &[i32]) -> bool {
// CHECK: icmp eq [[USIZE]] %1, %3
// CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 2
// CHECK: icmp eq [[USIZE]] %x.1, %y.1
// CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %x.1, 2
// CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}({{i32\*|ptr}}
// CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
x == y
}

// CHECK-LABEL: @eq_slice_of_nonzero(
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1
// CHECK-SAME: [[USIZE]] noundef %3
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
// CHECK-SAME: [[USIZE]] noundef %y.1
#[no_mangle]
fn eq_slice_of_nonzero(x: &[NonZeroU32], y: &[NonZeroU32]) -> bool {
// CHECK: icmp eq [[USIZE]] %1, %3
// CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 2
// CHECK: icmp eq [[USIZE]] %x.1, %y.1
// CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %x.1, 2
// CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}({{i32\*|ptr}}
// CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
x == y
}

// CHECK-LABEL: @eq_slice_of_option_of_nonzero(
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1
// CHECK-SAME: [[USIZE]] noundef %3
// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %x.1
// CHECK-SAME: [[USIZE]] noundef %y.1
#[no_mangle]
fn eq_slice_of_option_of_nonzero(x: &[Option<NonZeroI16>], y: &[Option<NonZeroI16>]) -> bool {
// CHECK: icmp eq [[USIZE]] %1, %3
// CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 1
// CHECK: icmp eq [[USIZE]] %x.1, %y.1
// CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %x.1, 1
// CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}({{i16\*|ptr}}
// CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]])
x == y
Expand Down
2 changes: 1 addition & 1 deletion tests/incremental/hashes/call_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ pub fn change_to_ufcs() {
}

#[cfg(not(any(cfail1,cfail4)))]
#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,typeck")]
#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,typeck")]
#[rustc_clean(cfg="cfail3")]
#[rustc_clean(cfg="cfail5", except="hir_owner_nodes,optimized_mir,typeck")]
#[rustc_clean(cfg="cfail6")]
Expand Down
2 changes: 1 addition & 1 deletion tests/mir-opt/casts.redundant.InstSimplify.diff
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
let mut _2: *const &u8;
let mut _3: *const &u8;
scope 1 (inlined generic_cast::<&u8, &u8>) {
debug x => _3;
debug x => _1;
}

bb0: {
Expand Down
2 changes: 2 additions & 0 deletions tests/mir-opt/copy-prop/mutate_through_pointer.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// unit-test: CopyProp
//
// This attempts to mutate `a` via a pointer derived from `addr_of!(a)`. That is UB
// according to Miri. However, the decision to make this UB - and to allow
// rustc to rely on that fact for the purpose of optimizations - has not been
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
let mut _2: std::option::Option<T>;
+ scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) {
+ debug self => _2;
+ let mut _3: &std::option::Option<T>;
+ let mut _4: isize;
+ let mut _3: isize;
+ scope 2 {
+ debug val => _0;
+ }
Expand All @@ -21,17 +20,16 @@
+ }
+ }
+ scope 4 (inlined Option::<T>::is_some) {
+ debug self => _3;
+ debug self => &_2;
+ }
+ }

bb0: {
StorageLive(_2);
_2 = move _1;
- _0 = Option::<T>::unwrap_unchecked(move _2) -> [return: bb1, unwind unreachable];
+ StorageLive(_3);
+ _4 = discriminant(_2);
+ switchInt(move _4) -> [1: bb2, otherwise: bb1];
+ _3 = discriminant(_2);
+ switchInt(move _3) -> [1: bb2, otherwise: bb1];
}

bb1: {
Expand All @@ -40,7 +38,6 @@
+
+ bb2: {
+ _0 = move ((_2 as Some).0: T);
+ StorageDead(_3);
StorageDead(_2);
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
let mut _2: std::option::Option<T>;
+ scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) {
+ debug self => _2;
+ let mut _3: &std::option::Option<T>;
+ let mut _4: isize;
+ let mut _3: isize;
+ scope 2 {
+ debug val => _0;
+ }
Expand All @@ -21,17 +20,16 @@
+ }
+ }
+ scope 4 (inlined Option::<T>::is_some) {
+ debug self => _3;
+ debug self => &_2;
+ }
+ }

bb0: {
StorageLive(_2);
_2 = move _1;
- _0 = Option::<T>::unwrap_unchecked(move _2) -> [return: bb1, unwind: bb2];
+ StorageLive(_3);
+ _4 = discriminant(_2);
+ switchInt(move _4) -> [1: bb2, otherwise: bb1];
+ _3 = discriminant(_2);
+ switchInt(move _3) -> [1: bb2, otherwise: bb1];
}

bb1: {
Expand All @@ -44,7 +42,6 @@
- resume;
+ bb2: {
+ _0 = move ((_2 as Some).0: T);
+ StorageDead(_3);
+ StorageDead(_2);
+ return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) {
debug self => _1;
let mut _2: isize;
let mut _3: &std::option::Option<T>;
scope 2 {
debug val => _0;
}
Expand All @@ -19,19 +18,17 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
}
}
scope 4 (inlined Option::<T>::is_some) {
debug self => _3;
debug self => &_1;
}
}

bb0: {
StorageLive(_3);
_2 = discriminant(_1);
switchInt(move _2) -> [1: bb1, otherwise: bb2];
}

bb1: {
_0 = move ((_1 as Some).0: T);
StorageDead(_3);
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) {
debug self => _1;
let mut _2: isize;
let mut _3: &std::option::Option<T>;
scope 2 {
debug val => _0;
}
Expand All @@ -19,19 +18,17 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
}
}
scope 4 (inlined Option::<T>::is_some) {
debug self => _3;
debug self => &_1;
}
}

bb0: {
StorageLive(_3);
_2 = discriminant(_1);
switchInt(move _2) -> [1: bb1, otherwise: bb2];
}

bb1: {
_0 = move ((_1 as Some).0: T);
StorageDead(_3);
return;
}

Expand Down
Loading