mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-29 18:23:49 +00:00
d2d24e395a
Account for immutably borrowed locals in MIR copy-prop and GVN For the most part, we consider that immutably borrowed `Freeze` locals still fulfill SSA conditions. As the borrow is immutable, any use of the local will have the value given by the single assignment, and there can be no surprise. This allows copy-prop to merge a non-borrowed local with a borrowed local. We chose to keep copy-classes heads unborrowed, as those may be easier to optimize in later passes. This also allows to GVN the value behind an immutable borrow. If a SSA local is borrowed, dereferencing that borrow is equivalent to copying the local's value: re-executing the assignment between the borrow and the dereference would be UB. r? `@ghost` for perf
895 lines
26 KiB
Rust
895 lines
26 KiB
Rust
//@ compile-flags: -Zlint-mir=no
|
|
//@ test-mir-pass: ReferencePropagation
|
|
//@ needs-unwind
|
|
|
|
#![feature(raw_ref_op)]
|
|
#![feature(core_intrinsics, custom_mir)]
|
|
|
|
#[inline(never)]
|
|
fn opaque(_: impl Sized) {}
|
|
|
|
fn reference_propagation<'a, T: Copy>(single: &'a T, mut multiple: &'a T) {
|
|
// CHECK-LABEL: fn reference_propagation(
|
|
|
|
// Propagation through a reference.
|
|
{
|
|
// CHECK: bb0: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &[[a]];
|
|
// CHECK: [[c:_.*]] = [[a]];
|
|
|
|
let a = 5_usize;
|
|
let b = &a; // This borrow is only used once.
|
|
let c = *b; // This should be optimized.
|
|
opaque(()); // We use opaque to separate cases into basic blocks in the MIR.
|
|
}
|
|
|
|
// Propagation through two references.
|
|
{
|
|
// CHECK: bb1: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[a2:_.*]] = const 7_usize;
|
|
// CHECK: [[b:_.*]] = &[[a]];
|
|
// CHECK: [[btmp:_.*]] = &[[a2]];
|
|
// CHECK: [[b]] = move [[btmp]];
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let a = 5_usize;
|
|
let a2 = 7_usize;
|
|
let mut b = &a;
|
|
b = &a2;
|
|
// `b` is assigned twice, so we cannot propagate it.
|
|
let c = *b;
|
|
opaque(());
|
|
}
|
|
|
|
// Propagation through a borrowed reference.
|
|
{
|
|
// CHECK: bb2: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &[[a]];
|
|
// CHECK: [[d:_.*]] = &[[b]];
|
|
// CHECK: [[c:_.*]] = [[a]];
|
|
|
|
let a = 5_usize;
|
|
let b = &a;
|
|
let d = &b;
|
|
let c = *b; // `b` is immutably borrowed, we know its value, but do not propagate it
|
|
opaque(d); // prevent `d` from being removed.
|
|
}
|
|
|
|
// Propagation through a mutably borrowed reference.
|
|
{
|
|
// CHECK: bb3: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &[[a]];
|
|
// CHECK: [[d:_.*]] = &raw mut [[b]];
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let a = 5_usize;
|
|
let mut b = &a;
|
|
let d = &raw mut b;
|
|
let c = *b; // `b` is mutably borrowed, we cannot know its value.
|
|
opaque(d); // prevent `d` from being removed.
|
|
}
|
|
|
|
// Propagation through an escaping borrow.
|
|
{
|
|
// CHECK: bb4: {
|
|
// CHECK: [[a:_.*]] = const 7_usize;
|
|
// CHECK: [[b:_.*]] = &[[a]];
|
|
// CHECK: [[c:_.*]] = [[a]];
|
|
|
|
let a = 7_usize;
|
|
let b = &a;
|
|
let c = *b;
|
|
opaque(b); // `b` escapes here, but we can still replace immutable borrow
|
|
}
|
|
|
|
// Propagation through a transitively escaping borrow.
|
|
{
|
|
// CHECK: bb5: {
|
|
// CHECK: [[a:_.*]] = const 7_usize;
|
|
// CHECK: [[b1:_.*]] = &[[a]];
|
|
// CHECK: [[c:_.*]] = [[a]];
|
|
// CHECK: [[b2:_.*]] = [[b1]];
|
|
// CHECK: [[c2:_.*]] = [[a]];
|
|
// CHECK: [[b3:_.*]] = [[b2]];
|
|
|
|
let a = 7_usize;
|
|
let b1 = &a;
|
|
let c = *b1;
|
|
let b2 = b1;
|
|
let c2 = *b2;
|
|
let b3 = b2;
|
|
// `b3` escapes here, so we can only replace immutable borrow,
|
|
// for either `b`, `b2` or `b3`.
|
|
opaque(b3);
|
|
}
|
|
|
|
// Propagation a reborrow of an argument.
|
|
{
|
|
// CHECK: bb6: {
|
|
// CHECK-NOT: {{_.*}} = &(*_1);
|
|
// CHECK: [[b:_.*]] = (*_1);
|
|
|
|
let a = &*single;
|
|
let b = *a; // This should be optimized as `*single`.
|
|
opaque(());
|
|
}
|
|
|
|
// Propagation a reborrow of a mutated argument.
|
|
{
|
|
// CHECK: bb7: {
|
|
// CHECK: [[a:_.*]] = &(*_2);
|
|
// CHECK: [[tmp:_.*]] = &(*_1);
|
|
// CHECK: _2 = move [[tmp]];
|
|
// CHECK: [[b:_.*]] = (*[[a]]);
|
|
|
|
let a = &*multiple;
|
|
multiple = &*single;
|
|
let b = *a; // This should not be optimized.
|
|
opaque(());
|
|
}
|
|
|
|
// Fixed-point propagation through a borrowed reference.
|
|
{
|
|
// CHECK: bb8: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &[[a]];
|
|
// CHECK: [[d:_.*]] = &[[b]];
|
|
// CHECK: [[c:_.*]] = [[a]];
|
|
|
|
let a = 5_usize;
|
|
let b = &a;
|
|
let d = &b; // first round promotes debuginfo for `d`
|
|
let c = *b; // second round propagates this dereference
|
|
opaque(());
|
|
}
|
|
|
|
// Fixed-point propagation through a mutably borrowed reference.
|
|
{
|
|
// CHECK: bb9: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &[[a]];
|
|
// CHECK: [[d:_.*]] = &mut [[b]];
|
|
// FIXME this could be [[a]]
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let a = 5_usize;
|
|
let mut b = &a;
|
|
let d = &mut b; // first round promotes debuginfo for `d`
|
|
let c = *b; // second round propagates this dereference
|
|
opaque(());
|
|
}
|
|
}
|
|
|
|
fn reference_propagation_mut<'a, T: Copy>(single: &'a mut T, mut multiple: &'a mut T) {
|
|
// CHECK-LABEL: fn reference_propagation_mut(
|
|
|
|
// Propagation through a reference.
|
|
{
|
|
// CHECK: bb0: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &mut [[a]];
|
|
// CHECK: [[c:_.*]] = [[a]];
|
|
|
|
let mut a = 5_usize;
|
|
let b = &mut a; // This borrow is only used once.
|
|
let c = *b; // This should be optimized.
|
|
opaque(());
|
|
}
|
|
|
|
// Propagation through two references.
|
|
{
|
|
// CHECK: bb1: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[a2:_.*]] = const 7_usize;
|
|
// CHECK: [[b:_.*]] = &mut [[a]];
|
|
// CHECK: [[btmp:_.*]] = &mut [[a2]];
|
|
// CHECK: [[b]] = move [[btmp]];
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let mut a = 5_usize;
|
|
let mut a2 = 7_usize;
|
|
let mut b = &mut a;
|
|
b = &mut a2;
|
|
// `b` is assigned twice, so we cannot propagate it.
|
|
let c = *b;
|
|
opaque(());
|
|
}
|
|
|
|
// Propagation through a borrowed reference.
|
|
{
|
|
// CHECK: bb2: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &mut [[a]];
|
|
// CHECK: [[d:_.*]] = &[[b]];
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let mut a = 5_usize;
|
|
let b = &mut a;
|
|
let d = &b;
|
|
let c = *b; // `b` is immutably borrowed, we know its value, but cannot be removed.
|
|
opaque(d); // prevent `d` from being removed.
|
|
}
|
|
|
|
// Propagation through a mutably borrowed reference.
|
|
{
|
|
// CHECK: bb3: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &mut [[a]];
|
|
// CHECK: [[d:_.*]] = &raw mut [[b]];
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let mut a = 5_usize;
|
|
let mut b = &mut a;
|
|
let d = &raw mut b;
|
|
let c = *b; // `b` is mutably borrowed, we cannot know its value.
|
|
opaque(d); // prevent `d` from being removed.
|
|
}
|
|
|
|
// Propagation through an escaping borrow.
|
|
{
|
|
// CHECK: bb4: {
|
|
// CHECK: [[a:_.*]] = const 7_usize;
|
|
// CHECK: [[b:_.*]] = &mut [[a]];
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let mut a = 7_usize;
|
|
let b = &mut a;
|
|
let c = *b;
|
|
opaque(b); // `b` escapes here, so we can only replace immutable borrow
|
|
}
|
|
|
|
// Propagation through a transitively escaping borrow.
|
|
{
|
|
// CHECK: bb5: {
|
|
// CHECK: [[a:_.*]] = const 7_usize;
|
|
// CHECK: [[b1:_.*]] = &mut [[a]];
|
|
// CHECK: [[c:_.*]] = (*[[b1]]);
|
|
// CHECK: [[b2:_.*]] = move [[b1]];
|
|
// CHECK: [[c2:_.*]] = (*[[b2]]);
|
|
// CHECK: [[b3:_.*]] = move [[b2]];
|
|
|
|
let mut a = 7_usize;
|
|
let b1 = &mut a;
|
|
let c = *b1;
|
|
let b2 = b1;
|
|
let c2 = *b2;
|
|
let b3 = b2;
|
|
// `b3` escapes here, so we can only replace immutable borrow,
|
|
// for either `b`, `b2` or `b3`.
|
|
opaque(b3);
|
|
}
|
|
|
|
// Propagation a reborrow of an argument.
|
|
{
|
|
// CHECK: bb6: {
|
|
// CHECK-NOT: {{_.*}} = &(*_1);
|
|
// CHECK: [[b:_.*]] = (*_1);
|
|
|
|
let a = &mut *single;
|
|
let b = *a; // This should be optimized as `*single`.
|
|
opaque(());
|
|
}
|
|
|
|
// Propagation a reborrow of a mutated argument.
|
|
{
|
|
// CHECK: bb7: {
|
|
// CHECK: [[a:_.*]] = &mut (*_2);
|
|
// CHECK: [[tmp:_.*]] = &mut (*_1);
|
|
// CHECK: _2 = move [[tmp]];
|
|
// CHECK: [[b:_.*]] = (*[[a]]);
|
|
|
|
let a = &mut *multiple;
|
|
multiple = &mut *single;
|
|
let b = *a; // This should not be optimized.
|
|
opaque(());
|
|
}
|
|
|
|
// Fixed-point propagation through a borrowed reference.
|
|
{
|
|
// CHECK: bb8: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &mut [[a]];
|
|
// CHECK: [[d:_.*]] = &[[b]];
|
|
// FIXME this could be [[a]]
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let mut a = 5_usize;
|
|
let b = &mut a;
|
|
let d = &b; // first round promotes debuginfo for `d`
|
|
let c = *b; // second round propagates this dereference
|
|
opaque(());
|
|
}
|
|
|
|
// Fixed-point propagation through a mutably borrowed reference.
|
|
{
|
|
// CHECK: bb9: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &mut [[a]];
|
|
// CHECK: [[d:_.*]] = &mut [[b]];
|
|
// FIXME this could be [[a]]
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let mut a = 5_usize;
|
|
let mut b = &mut a;
|
|
let d = &mut b; // first round promotes debuginfo for `d`
|
|
let c = *b; // second round propagates this dereference
|
|
opaque(());
|
|
}
|
|
}
|
|
|
|
fn reference_propagation_const_ptr<T: Copy>(single: *const T, mut multiple: *const T) {
|
|
// CHECK-LABEL: fn reference_propagation_const_ptr(
|
|
|
|
// Propagation through a reference.
|
|
unsafe {
|
|
// CHECK: bb0: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &raw const [[a]];
|
|
// CHECK: [[c:_.*]] = [[a]];
|
|
|
|
let a = 5_usize;
|
|
let b = &raw const a; // This borrow is only used once.
|
|
let c = *b; // This should be optimized.
|
|
opaque(());
|
|
}
|
|
|
|
// Propagation through two references.
|
|
unsafe {
|
|
// CHECK: bb1: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[a2:_.*]] = const 7_usize;
|
|
// CHECK: [[b:_.*]] = &raw const [[a]];
|
|
// CHECK: [[btmp:_.*]] = &raw const [[a2]];
|
|
// CHECK: [[b]] = move [[btmp]];
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let a = 5_usize;
|
|
let a2 = 7_usize;
|
|
let mut b = &raw const a;
|
|
b = &raw const a2;
|
|
// `b` is assigned twice, so we cannot propagate it.
|
|
let c = *b;
|
|
opaque(());
|
|
}
|
|
|
|
// Propagation through a borrowed reference.
|
|
unsafe {
|
|
// CHECK: bb2: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &raw const [[a]];
|
|
// CHECK: [[d:_.*]] = &[[b]];
|
|
// CHECK: [[c:_.*]] = [[a]];
|
|
|
|
let a = 5_usize;
|
|
let b = &raw const a;
|
|
let d = &b;
|
|
let c = *b; // `b` is immutably borrowed, we know its value, but cannot be removed.
|
|
opaque(d); // prevent `d` from being removed.
|
|
}
|
|
|
|
// Propagation through a mutably borrowed reference.
|
|
unsafe {
|
|
// CHECK: bb3: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &raw const [[a]];
|
|
// CHECK: [[d:_.*]] = &raw mut [[b]];
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let a = 5_usize;
|
|
let mut b = &raw const a;
|
|
let d = &raw mut b;
|
|
let c = *b; // `b` is mutably borrowed, we cannot know its value.
|
|
opaque(d); // prevent `d` from being removed.
|
|
}
|
|
|
|
// Propagation through an escaping borrow.
|
|
unsafe {
|
|
// CHECK: bb4: {
|
|
// CHECK: [[a:_.*]] = const 7_usize;
|
|
// CHECK: [[b:_.*]] = &raw const [[a]];
|
|
// CHECK: [[c:_.*]] = [[a]];
|
|
|
|
let a = 7_usize;
|
|
let b = &raw const a;
|
|
let c = *b;
|
|
opaque(b); // `b` escapes here, so we can only replace immutable borrow
|
|
}
|
|
|
|
// Propagation through a transitively escaping borrow.
|
|
unsafe {
|
|
// CHECK: bb5: {
|
|
// CHECK: [[a:_.*]] = const 7_usize;
|
|
// CHECK: [[b1:_.*]] = &raw const [[a]];
|
|
// CHECK: [[c:_.*]] = [[a]];
|
|
// CHECK: [[b2:_.*]] = [[b1]];
|
|
// CHECK: [[c2:_.*]] = [[a]];
|
|
// CHECK: [[b3:_.*]] = [[b2]];
|
|
|
|
let a = 7_usize;
|
|
let b1 = &raw const a;
|
|
let c = *b1;
|
|
let b2 = b1;
|
|
let c2 = *b2;
|
|
let b3 = b2;
|
|
// `b3` escapes here, so we can only replace immutable borrow,
|
|
// for either `b`, `b2` or `b3`.
|
|
opaque(b3);
|
|
}
|
|
|
|
// Propagation a reborrow of an argument.
|
|
unsafe {
|
|
// CHECK: bb6: {
|
|
// CHECK-NOT: {{_.*}} = &(*_1);
|
|
// CHECK: [[b:_.*]] = (*_1);
|
|
|
|
let a = &raw const *single;
|
|
let b = *a; // This should be optimized as `*single`.
|
|
opaque(());
|
|
}
|
|
|
|
// Propagation a reborrow of a mutated argument.
|
|
unsafe {
|
|
// CHECK: bb7: {
|
|
// CHECK: [[a:_.*]] = &raw const (*_2);
|
|
// CHECK: [[tmp:_.*]] = &raw const (*_1);
|
|
// CHECK: _2 = move [[tmp]];
|
|
// CHECK: [[b:_.*]] = (*[[a]]);
|
|
|
|
let a = &raw const *multiple;
|
|
multiple = &raw const *single;
|
|
let b = *a; // This should not be optimized.
|
|
opaque(());
|
|
}
|
|
|
|
// Propagation through a reborrow.
|
|
unsafe {
|
|
// CHECK: bb8: {
|
|
// CHECK: [[a:_.*]] = const 13_usize;
|
|
// CHECK: [[b:_.*]] = &raw const [[a]];
|
|
// CHECK: [[d:_.*]] = &raw const [[a]];
|
|
// CHECK: [[c:_.*]] = [[a]];
|
|
|
|
let a = 13_usize;
|
|
let b = &raw const a;
|
|
let c = &raw const *b;
|
|
let e = *c;
|
|
opaque(());
|
|
}
|
|
|
|
// Fixed-point propagation through a borrowed reference.
|
|
unsafe {
|
|
// CHECK: bb9: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &raw const [[a]];
|
|
// CHECK: [[d:_.*]] = &[[b]];
|
|
// CHECK: [[c:_.*]] = [[a]];
|
|
|
|
let a = 5_usize;
|
|
let b = &raw const a;
|
|
let d = &b; // first round promotes debuginfo for `d`
|
|
let c = *b; // second round propagates this dereference
|
|
opaque(());
|
|
}
|
|
|
|
// Fixed-point propagation through a borrowed reference.
|
|
unsafe {
|
|
// CHECK: bb10: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &raw const [[a]];
|
|
// CHECK: [[d:_.*]] = &mut [[b]];
|
|
// FIXME this could be [[a]]
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let a = 5_usize;
|
|
let mut b = &raw const a;
|
|
let d = &mut b; // first round promotes debuginfo for `d`
|
|
let c = *b; // second round propagates this dereference
|
|
opaque(());
|
|
}
|
|
}
|
|
|
|
fn reference_propagation_mut_ptr<T: Copy>(single: *mut T, mut multiple: *mut T) {
|
|
// CHECK-LABEL: fn reference_propagation_mut_ptr(
|
|
|
|
// Propagation through a reference.
|
|
unsafe {
|
|
// CHECK: bb0: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &raw mut [[a]];
|
|
// CHECK: [[c:_.*]] = [[a]];
|
|
|
|
let mut a = 5_usize;
|
|
let b = &raw mut a; // This borrow is only used once.
|
|
let c = *b; // This should be optimized.
|
|
opaque(());
|
|
}
|
|
|
|
// Propagation through two references.
|
|
unsafe {
|
|
// CHECK: bb1: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[a2:_.*]] = const 7_usize;
|
|
// CHECK: [[b:_.*]] = &raw mut [[a]];
|
|
// CHECK: [[btmp:_.*]] = &raw mut [[a2]];
|
|
// CHECK: [[b]] = move [[btmp]];
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let mut a = 5_usize;
|
|
let mut a2 = 7_usize;
|
|
let mut b = &raw mut a;
|
|
b = &raw mut a2;
|
|
// `b` is assigned twice, so we cannot propagate it.
|
|
let c = *b;
|
|
opaque(());
|
|
}
|
|
|
|
// Propagation through a borrowed reference.
|
|
unsafe {
|
|
// CHECK: bb2: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &raw mut [[a]];
|
|
// CHECK: [[d:_.*]] = &[[b]];
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let mut a = 5_usize;
|
|
let b = &raw mut a;
|
|
let d = &b;
|
|
let c = *b; // `b` is immutably borrowed, we know its value, but cannot be removed.
|
|
opaque(d); // prevent `d` from being removed.
|
|
}
|
|
|
|
// Propagation through a mutably borrowed reference.
|
|
unsafe {
|
|
// CHECK: bb3: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &raw mut [[a]];
|
|
// CHECK: [[d:_.*]] = &raw mut [[b]];
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let mut a = 5_usize;
|
|
let mut b = &raw mut a;
|
|
let d = &raw mut b;
|
|
let c = *b; // `b` is mutably borrowed, we cannot know its value.
|
|
opaque(d); // prevent `d` from being removed.
|
|
}
|
|
|
|
// Propagation through an escaping borrow.
|
|
unsafe {
|
|
// CHECK: bb4: {
|
|
// CHECK: [[a:_.*]] = const 7_usize;
|
|
// CHECK: [[b:_.*]] = &raw mut [[a]];
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let mut a = 7_usize;
|
|
let b = &raw mut a;
|
|
let c = *b;
|
|
opaque(b); // `b` escapes here, so we can only replace immutable borrow
|
|
}
|
|
|
|
// Propagation through a transitively escaping borrow.
|
|
unsafe {
|
|
// CHECK: bb5: {
|
|
// CHECK: [[a:_.*]] = const 7_usize;
|
|
// CHECK: [[b1:_.*]] = &raw mut [[a]];
|
|
// CHECK: [[c:_.*]] = (*[[b1]]);
|
|
// CHECK: [[b2:_.*]] = [[b1]];
|
|
// CHECK: [[c2:_.*]] = (*[[b2]]);
|
|
// CHECK: [[b3:_.*]] = [[b2]];
|
|
|
|
let mut a = 7_usize;
|
|
let b1 = &raw mut a;
|
|
let c = *b1;
|
|
let b2 = b1;
|
|
let c2 = *b2;
|
|
let b3 = b2;
|
|
// `b3` escapes here, so we can only replace immutable borrow,
|
|
// for either `b`, `b2` or `b3`.
|
|
opaque(b3);
|
|
}
|
|
|
|
// Propagation a reborrow of an argument.
|
|
unsafe {
|
|
// CHECK: bb6: {
|
|
// CHECK-NOT: {{_.*}} = &(*_1);
|
|
// CHECK: [[b:_.*]] = (*_1);
|
|
|
|
let a = &raw mut *single;
|
|
let b = *a; // This should be optimized as `*single`.
|
|
opaque(());
|
|
}
|
|
|
|
// Propagation a reborrow of a mutated argument.
|
|
unsafe {
|
|
// CHECK: bb7: {
|
|
// CHECK: [[a:_.*]] = &raw mut (*_2);
|
|
// CHECK: [[tmp:_.*]] = &raw mut (*_1);
|
|
// CHECK: _2 = move [[tmp]];
|
|
// CHECK: [[b:_.*]] = (*[[a]]);
|
|
|
|
let a = &raw mut *multiple;
|
|
multiple = &raw mut *single;
|
|
let b = *a; // This should not be optimized.
|
|
opaque(());
|
|
}
|
|
|
|
// Fixed-point propagation through a borrowed reference.
|
|
unsafe {
|
|
// CHECK: bb8: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &raw mut [[a]];
|
|
// CHECK: [[d:_.*]] = &[[b]];
|
|
// FIXME this could be [[a]]
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let mut a = 5_usize;
|
|
let b = &raw mut a;
|
|
let d = &b; // first round promotes debuginfo for `d`
|
|
let c = *b; // second round propagates this dereference
|
|
opaque(());
|
|
}
|
|
|
|
// Fixed-point propagation through a mutably borrowed reference.
|
|
unsafe {
|
|
// CHECK: bb9: {
|
|
// CHECK: [[a:_.*]] = const 5_usize;
|
|
// CHECK: [[b:_.*]] = &raw mut [[a]];
|
|
// CHECK: [[d:_.*]] = &mut [[b]];
|
|
// FIXME this could be [[a]]
|
|
// CHECK: [[c:_.*]] = (*[[b]]);
|
|
|
|
let mut a = 5_usize;
|
|
let mut b = &raw mut a;
|
|
let d = &mut b; // first round promotes debuginfo for `d`
|
|
let c = *b; // second round propagates this dereference
|
|
opaque(());
|
|
}
|
|
}
|
|
|
|
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
|
|
fn read_through_raw(x: &mut usize) -> usize {
|
|
// CHECK-LABEL: read_through_raw
|
|
// CHECK: bb0: {
|
|
// CHECK-NEXT: _0 = (*_1);
|
|
// CHECK-NEXT: _0 = (*_1);
|
|
// CHECK-NEXT: return;
|
|
|
|
use std::intrinsics::mir::*;
|
|
mir!(
|
|
let r1: &mut usize;
|
|
let r2: &mut usize;
|
|
let p1: *mut usize;
|
|
let p2: *mut usize;
|
|
|
|
{
|
|
r1 = &mut *x;
|
|
r2 = &mut *r1;
|
|
p1 = &raw mut *r1;
|
|
p2 = &raw mut *r2;
|
|
|
|
RET = *p1;
|
|
RET = *p2;
|
|
Return()
|
|
}
|
|
)
|
|
}
|
|
|
|
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
|
|
fn multiple_storage() {
|
|
// CHECK-LABEL: multiple_storage
|
|
// CHECK: _3 = (*_2);
|
|
|
|
use std::intrinsics::mir::*;
|
|
mir!(
|
|
let x: i32;
|
|
{
|
|
StorageLive(x);
|
|
x = 5;
|
|
let z = &x;
|
|
StorageDead(x);
|
|
StorageLive(x);
|
|
// As there are multiple `StorageLive` statements for `x`, we cannot know if this `z`'s
|
|
// pointer address is the address of `x`, so do nothing.
|
|
let y = *z;
|
|
Call(RET = opaque(y), ReturnTo(retblock), UnwindContinue())
|
|
}
|
|
|
|
retblock = {
|
|
Return()
|
|
}
|
|
)
|
|
}
|
|
|
|
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
|
|
fn dominate_storage() {
|
|
// CHECK-LABEL: dominate_storage
|
|
// CHECK: _5 = (*_2);
|
|
|
|
use std::intrinsics::mir::*;
|
|
mir!(
|
|
let x: i32;
|
|
let r: &i32;
|
|
let c: i32;
|
|
let d: bool;
|
|
{ Goto(bb0) }
|
|
bb0 = {
|
|
x = 5;
|
|
r = &x;
|
|
Goto(bb1)
|
|
}
|
|
bb1 = {
|
|
let c = *r;
|
|
Call(RET = opaque(c), ReturnTo(bb2), UnwindContinue())
|
|
}
|
|
bb2 = {
|
|
StorageDead(x);
|
|
StorageLive(x);
|
|
let d = true;
|
|
match d { false => bb2, _ => bb0 }
|
|
}
|
|
)
|
|
}
|
|
|
|
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
|
|
fn maybe_dead(m: bool) {
|
|
// CHECK-LABEL: fn maybe_dead(
|
|
// CHECK: (*_5) = const 7_i32;
|
|
|
|
use std::intrinsics::mir::*;
|
|
mir!(
|
|
let x: i32;
|
|
let y: i32;
|
|
{
|
|
StorageLive(x);
|
|
StorageLive(y);
|
|
x = 5;
|
|
y = 5;
|
|
let a = &x;
|
|
let b = &mut y;
|
|
// As we don't replace `b` in `bb2`, we cannot replace it here either.
|
|
*b = 7;
|
|
// But this can still be replaced.
|
|
let u = *a;
|
|
match m { true => bb1, _ => bb2 }
|
|
}
|
|
bb1 = {
|
|
StorageDead(x);
|
|
StorageDead(y);
|
|
Call(RET = opaque(u), ReturnTo(bb2), UnwindContinue())
|
|
}
|
|
bb2 = {
|
|
// As `x` may be `StorageDead`, `a` may be dangling, so we do nothing.
|
|
let z = *a;
|
|
Call(RET = opaque(z), ReturnTo(bb3), UnwindContinue())
|
|
}
|
|
bb3 = {
|
|
// As `y` may be `StorageDead`, `b` may be dangling, so we do nothing.
|
|
// This implies that we also do not substitute `b` in `bb0`.
|
|
let t = *b;
|
|
Call(RET = opaque(t), ReturnTo(retblock), UnwindContinue())
|
|
}
|
|
retblock = {
|
|
Return()
|
|
}
|
|
)
|
|
}
|
|
|
|
fn mut_raw_then_mut_shr() -> (i32, i32) {
|
|
// CHECK-LABEL: fn mut_raw_then_mut_shr(
|
|
// CHECK-NOT: (*{{_.*}})
|
|
|
|
let mut x = 2;
|
|
let xref = &mut x;
|
|
let xraw = &mut *xref as *mut _;
|
|
let xshr = &*xref;
|
|
// Verify that we completely replace with `x` in both cases.
|
|
let a = *xshr;
|
|
unsafe { *xraw = 4; }
|
|
(a, x)
|
|
}
|
|
|
|
fn unique_with_copies() {
|
|
// CHECK-LABEL: fn unique_with_copies(
|
|
// CHECK: [[a:_.*]] = const 0_i32;
|
|
// CHECK: [[x:_.*]] = &raw mut [[a]];
|
|
// CHECK-NOT: [[a]]
|
|
// CHECK: [[tmp:_.*]] = (*[[x]]);
|
|
// CHECK-NEXT: opaque::<i32>(move [[tmp]])
|
|
// CHECK-NOT: [[a]]
|
|
// CHECK: StorageDead([[a]]);
|
|
// CHECK-NOT: [[a]]
|
|
// CHECK: [[tmp:_.*]] = (*[[x]]);
|
|
// CHECK-NEXT: opaque::<i32>(move [[tmp]])
|
|
|
|
let y = {
|
|
let mut a = 0;
|
|
let x = &raw mut a;
|
|
// `*y` is not replacable below, so we must not replace `*x`.
|
|
unsafe { opaque(*x) };
|
|
x
|
|
};
|
|
// But rewriting as `*x` is ok.
|
|
unsafe { opaque(*y) };
|
|
}
|
|
|
|
fn debuginfo() {
|
|
// CHECK-LABEL: fn debuginfo(
|
|
// FIXME: This features waits for DWARF implicit pointers in LLVM.
|
|
// CHECK: debug ref_mut_u8 => _{{.*}};
|
|
// CHECK: debug field => _{{.*}};
|
|
// CHECK: debug reborrow => _{{.*}};
|
|
// CHECK: debug variant_field => _{{.*}};
|
|
// CHECK: debug constant_index => _{{.*}};
|
|
// CHECK: debug subslice => _{{.*}};
|
|
// CHECK: debug constant_index_from_end => _{{.*}};
|
|
// CHECK: debug multiple_borrow => _{{.*}};
|
|
|
|
struct T(u8);
|
|
|
|
let ref_mut_u8 = &mut 5_u8;
|
|
let field = &T(0).0;
|
|
|
|
// Verify that we don't emit `&*` in debuginfo.
|
|
let reborrow = &*ref_mut_u8;
|
|
|
|
match Some(0) {
|
|
None => {}
|
|
Some(ref variant_field) => {}
|
|
}
|
|
|
|
// `constant_index_from_end` and `subslice` should not be promoted, as their value depends
|
|
// on the slice length.
|
|
if let [_, ref constant_index, subslice @ .., ref constant_index_from_end] = &[6; 10][..] {
|
|
}
|
|
|
|
let multiple_borrow = &&&mut T(6).0;
|
|
}
|
|
|
|
fn many_debuginfo() {
|
|
// CHECK-LABEL: fn many_debuginfo(
|
|
// FIXME: This features waits for DWARF implicit pointers in LLVM.
|
|
// CHECK: debug many_borrow => _{{.*}};
|
|
|
|
let a = 0;
|
|
|
|
// Verify that we do not ICE on deeply nested borrows.
|
|
let many_borrow =
|
|
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
|
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
|
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
|
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|
|
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&a;
|
|
}
|
|
|
|
fn main() {
|
|
let mut x = 5_usize;
|
|
let mut y = 7_usize;
|
|
reference_propagation(&x, &y);
|
|
reference_propagation_mut(&mut x, &mut y);
|
|
reference_propagation_const_ptr(&raw const x, &raw const y);
|
|
reference_propagation_mut_ptr(&raw mut x, &raw mut y);
|
|
read_through_raw(&mut x);
|
|
multiple_storage();
|
|
dominate_storage();
|
|
maybe_dead(true);
|
|
mut_raw_then_mut_shr();
|
|
unique_with_copies();
|
|
debuginfo();
|
|
many_debuginfo();
|
|
}
|
|
|
|
// EMIT_MIR reference_prop.reference_propagation.ReferencePropagation.diff
|
|
// EMIT_MIR reference_prop.reference_propagation_mut.ReferencePropagation.diff
|
|
// EMIT_MIR reference_prop.reference_propagation_const_ptr.ReferencePropagation.diff
|
|
// EMIT_MIR reference_prop.reference_propagation_mut_ptr.ReferencePropagation.diff
|
|
// EMIT_MIR reference_prop.read_through_raw.ReferencePropagation.diff
|
|
// EMIT_MIR reference_prop.multiple_storage.ReferencePropagation.diff
|
|
// EMIT_MIR reference_prop.dominate_storage.ReferencePropagation.diff
|
|
// EMIT_MIR reference_prop.maybe_dead.ReferencePropagation.diff
|
|
// EMIT_MIR reference_prop.mut_raw_then_mut_shr.ReferencePropagation.diff
|
|
// EMIT_MIR reference_prop.unique_with_copies.ReferencePropagation.diff
|
|
// EMIT_MIR reference_prop.debuginfo.ReferencePropagation.diff
|