mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
79611d90b6
Added "copy" to Debug fmt for copy operands In MIR's debug mode (--emit mir) the printing for Operands is slightly inconsistent. The RValues - values on the right side of an Assign - are usually printed with their Operand when they are Places. Example: _2 = move _3 But for arguments, the operand is omitted. _2 = _1 I propose a change be made, to display the place with the operand. _2 = copy _1 Move and copy have different semantics, meaning this difference is important and helpful to the user. It also adds consistency to the pretty printing. -- EDIT -- Consider this example Rust program and its MIR output with the **updated pretty printer.** This was generated with the arguments --emit mir --crate-type lib -Zmir-opt-level=0 (Otherwise, it's optimised away since it's a junk program). ```rust fn main(foo: i32) { let v = 10; if v == 20 { foo; } else { v; } } ``` ```MIR // WARNING: This output format is intended for human consumers only // and is subject to change without notice. Knock yourself out. fn main(_1: i32) -> () { debug foo => _1; let mut _0: (); let _2: i32; let mut _3: bool; let mut _4: i32; let _5: i32; let _6: i32; scope 1 { debug v => _2; } bb0: { StorageLive(_2); _2 = const 10_i32; StorageLive(_3); StorageLive(_4); _4 = copy _2; _3 = Eq(move _4, const 20_i32); switchInt(move _3) -> [0: bb2, otherwise: bb1]; } bb1: { StorageDead(_4); StorageLive(_5); _5 = copy _1; StorageDead(_5); _0 = const (); goto -> bb3; } bb2: { StorageDead(_4); StorageLive(_6); _6 = copy _2; StorageDead(_6); _0 = const (); goto -> bb3; } bb3: { StorageDead(_3); StorageDead(_2); return; } } ``` In this example program, we can see that when we move a place, it is preceded by "move". e.g. ``` _3 = Eq(move _4, const 20_i32);```. However, when we copy a place such as ```_5 = _1;```, it is not preceded by the operand in the original printout. I propose to change the print to include the copy ```_5 = copy _1``` as in this example. Regarding the arguments part. When I originally submitted this PR, I was under the impression this only affected the print for arguments to a function, but actually, it affects anything that uses a copy. This is preferable anyway with regard to consistency. The PR is about making ```copy``` explicit.
895 lines
26 KiB
Rust
895 lines
26 KiB
Rust
//@ compile-flags: -Zlint-mir=no
|
|
//@ test-mir-pass: ReferencePropagation
|
|
//@ needs-unwind
|
|
|
|
#![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:_.*]] = copy [[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:_.*]] = copy (*[[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:_.*]] = copy [[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:_.*]] = copy (*[[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:_.*]] = copy [[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:_.*]] = copy [[a]];
|
|
// CHECK: [[b2:_.*]] = copy [[b1]];
|
|
// CHECK: [[c2:_.*]] = copy [[a]];
|
|
// CHECK: [[b3:_.*]] = copy [[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:_.*]] = copy (*_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:_.*]] = copy (*[[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:_.*]] = copy [[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:_.*]] = copy (*[[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:_.*]] = copy [[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:_.*]] = copy (*[[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:_.*]] = copy (*[[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:_.*]] = copy (*[[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:_.*]] = copy (*[[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:_.*]] = copy (*[[b1]]);
|
|
// CHECK: [[b2:_.*]] = move [[b1]];
|
|
// CHECK: [[c2:_.*]] = copy (*[[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:_.*]] = copy (*_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:_.*]] = copy (*[[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:_.*]] = copy (*[[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:_.*]] = copy (*[[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:_.*]] = copy [[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:_.*]] = copy (*[[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:_.*]] = copy [[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:_.*]] = copy (*[[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:_.*]] = copy [[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:_.*]] = copy [[a]];
|
|
// CHECK: [[b2:_.*]] = copy [[b1]];
|
|
// CHECK: [[c2:_.*]] = copy [[a]];
|
|
// CHECK: [[b3:_.*]] = copy [[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:_.*]] = copy (*_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:_.*]] = copy (*[[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:_.*]] = copy [[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:_.*]] = copy [[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:_.*]] = copy (*[[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:_.*]] = copy [[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:_.*]] = copy (*[[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:_.*]] = copy (*[[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:_.*]] = copy (*[[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:_.*]] = copy (*[[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:_.*]] = copy (*[[b1]]);
|
|
// CHECK: [[b2:_.*]] = copy [[b1]];
|
|
// CHECK: [[c2:_.*]] = copy (*[[b2]]);
|
|
// CHECK: [[b3:_.*]] = copy [[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:_.*]] = copy (*_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:_.*]] = copy (*[[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:_.*]] = copy (*[[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:_.*]] = copy (*[[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 = copy (*_1);
|
|
// CHECK-NEXT: _0 = copy (*_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 = copy (*_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 = copy (*_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:_.*]] = copy (*[[x]]);
|
|
// CHECK-NEXT: opaque::<i32>(move [[tmp]])
|
|
// CHECK-NOT: [[a]]
|
|
// CHECK: StorageDead([[a]]);
|
|
// CHECK-NOT: [[a]]
|
|
// CHECK: [[tmp:_.*]] = copy (*[[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
|