| //@ 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]] = copy [[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 = copy [[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]] = copy [[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:_.*]] = copy [[b1]]; | |
| // CHECK: [[c2:_.*]] = copy (*[[b2]]); | |
| // CHECK: [[b3:_.*]] = copy [[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 = copy [[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]] = copy [[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 = copy [[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]] = copy [[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 = copy [[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 |