rust/tests/mir-opt/gvn.rs
bors d2d24e395a Auto merge of #123602 - cjgillot:gvn-borrowed, r=oli-obk
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
2024-05-03 21:50:13 +00:00

841 lines
26 KiB
Rust

//@ test-mir-pass: GVN
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
//@ only-64bit
#![feature(raw_ref_op)]
#![feature(rustc_attrs)]
#![feature(custom_mir)]
#![feature(core_intrinsics)]
#![feature(freeze)]
#![allow(unconditional_panic)]
use std::intrinsics::mir::*;
use std::marker::Freeze;
use std::mem::transmute;
struct S<T>(T);
fn subexpression_elimination(x: u64, y: u64, mut z: u64) {
// CHECK-LABEL: fn subexpression_elimination(
// CHECK: [[add:_.*]] = Add(_1, _2);
// CHECK: opaque::<u64>([[add]])
opaque(x + y);
// CHECK: [[mul:_.*]] = Mul(_1, _2);
// CHECK: opaque::<u64>([[mul]])
opaque(x * y);
// CHECK: [[sub:_.*]] = Sub(_1, _2);
// CHECK: opaque::<u64>([[sub]])
opaque(x - y);
// CHECK: [[div:_.*]] = Div(_1, _2);
// CHECK: opaque::<u64>([[div]])
opaque(x / y);
// CHECK: [[rem:_.*]] = Rem(_1, _2);
// CHECK: opaque::<u64>([[rem]])
opaque(x % y);
// CHECK: [[and:_.*]] = BitAnd(_1, _2);
// CHECK: opaque::<u64>([[and]])
opaque(x & y);
// CHECK: [[or:_.*]] = BitOr(_1, _2);
// CHECK: opaque::<u64>([[or]])
opaque(x | y);
// CHECK: [[xor:_.*]] = BitXor(_1, _2);
// CHECK: opaque::<u64>([[xor]])
opaque(x ^ y);
// CHECK: [[shl:_.*]] = Shl(_1, _2);
// CHECK: opaque::<u64>([[shl]])
opaque(x << y);
// CHECK: [[shr:_.*]] = Shr(_1, _2);
// CHECK: opaque::<u64>([[shr]])
opaque(x >> y);
// CHECK: [[int:_.*]] = _1 as u32 (IntToInt);
// CHECK: opaque::<u32>([[int]])
opaque(x as u32);
// CHECK: [[float:_.*]] = _1 as f32 (IntToFloat);
// CHECK: opaque::<f32>([[float]])
opaque(x as f32);
// CHECK: [[wrap:_.*]] = S::<u64>(_1);
// CHECK: opaque::<S<u64>>([[wrap]])
opaque(S(x));
// CHECK: opaque::<u64>(_1)
opaque(S(x).0);
// Those are duplicates to substitute somehow.
// CHECK: opaque::<u64>([[add]])
opaque(x + y);
// CHECK: opaque::<u64>([[mul]])
opaque(x * y);
// CHECK: opaque::<u64>([[sub]])
opaque(x - y);
// CHECK: opaque::<u64>([[div]])
opaque(x / y);
// CHECK: opaque::<u64>([[rem]])
opaque(x % y);
// CHECK: opaque::<u64>([[and]])
opaque(x & y);
// CHECK: opaque::<u64>([[or]])
opaque(x | y);
// CHECK: opaque::<u64>([[xor]])
opaque(x ^ y);
// CHECK: opaque::<u64>([[shl]])
opaque(x << y);
// CHECK: opaque::<u64>([[shr]])
opaque(x >> y);
// CHECK: opaque::<u32>([[int]])
opaque(x as u32);
// CHECK: opaque::<f32>([[float]])
opaque(x as f32);
// CHECK: opaque::<S<u64>>([[wrap]])
opaque(S(x));
// CHECK: opaque::<u64>(_1)
opaque(S(x).0);
// We can substitute through a complex expression.
// CHECK: [[compound:_.*]] = Sub([[mul]], _2);
// CHECK: opaque::<u64>([[compound]])
// CHECK: opaque::<u64>([[compound]])
opaque((x * y) - y);
opaque((x * y) - y);
// We can substitute through an immutable reference too.
// CHECK: [[ref:_.*]] = &_3;
// CHECK: [[deref:_.*]] = (*[[ref]]);
// CHECK: [[addref:_.*]] = Add([[deref]], _1);
// CHECK: opaque::<u64>([[addref]])
// CHECK: opaque::<u64>([[addref]])
let a = &z;
opaque(*a + x);
opaque(*a + x);
// But not through a mutable reference or a pointer.
// CHECK: [[mut:_.*]] = &mut _3;
// CHECK: [[addmut:_.*]] = Add(
// CHECK: opaque::<u64>(move [[addmut]])
// CHECK: [[addmut2:_.*]] = Add(
// CHECK: opaque::<u64>(move [[addmut2]])
let b = &mut z;
opaque(*b + x);
opaque(*b + x);
unsafe {
// CHECK: [[raw:_.*]] = &raw const _3;
// CHECK: [[addraw:_.*]] = Add(
// CHECK: opaque::<u64>(move [[addraw]])
// CHECK: [[addraw2:_.*]] = Add(
// CHECK: opaque::<u64>(move [[addraw2]])
let c = &raw const z;
opaque(*c + x);
opaque(*c + x);
// CHECK: [[ptr:_.*]] = &raw mut _3;
// CHECK: [[addptr:_.*]] = Add(
// CHECK: opaque::<u64>(move [[addptr]])
// CHECK: [[addptr2:_.*]] = Add(
// CHECK: opaque::<u64>(move [[addptr2]])
let d = &raw mut z;
opaque(*d + x);
opaque(*d + x);
}
// We can substitute again, but not with the earlier computations.
// Important: `e` is not `a`!
// CHECK: [[ref2:_.*]] = &_3;
// CHECK: [[deref2:_.*]] = (*[[ref2]]);
// CHECK: [[addref2:_.*]] = Add([[deref2]], _1);
// CHECK: opaque::<u64>([[addref2]])
// CHECK: opaque::<u64>([[addref2]])
let e = &z;
opaque(*e + x);
opaque(*e + x);
}
fn wrap_unwrap<T: Copy>(x: T) -> T {
// CHECK-LABEL: fn wrap_unwrap(
// CHECK: [[some:_.*]] = Option::<T>::Some(_1);
// CHECK: switchInt(const 1_isize)
// CHECK: _0 = _1;
match Some(x) {
Some(y) => y,
None => panic!(),
}
}
fn repeated_index<T: Copy, const N: usize>(x: T, idx: usize) {
// CHECK-LABEL: fn repeated_index(
// CHECK: [[a:_.*]] = [_1; N];
let a = [x; N];
// CHECK: opaque::<T>(_1)
opaque(a[0]);
// CHECK: opaque::<T>(_1)
opaque(a[idx]);
}
fn unary(x: i64) {
// CHECK-LABEL: fn unary(
// CHECK: opaque::<i64>(_1)
opaque(--x); // This is `x`.
// CHECK: [[b:_.*]] = Lt(_1, const 13_i64);
// CHECK: opaque::<bool>([[b]])
let b = x < 13;
opaque(!!b); // This is `b`.
// Both lines should test the same thing.
// CHECK: [[c:_.*]] = Ne(_1, const 15_i64);
// CHECK: opaque::<bool>([[c]])
// CHECK: opaque::<bool>([[c]])
opaque(x != 15);
opaque(!(x == 15));
// Both lines should test the same thing.
// CHECK: [[d:_.*]] = Eq(_1, const 35_i64);
// CHECK: opaque::<bool>([[d]])
// CHECK: opaque::<bool>([[d]])
opaque(x == 35);
opaque(!(x != 35));
}
/// Verify symbolic integer arithmetic simplifications.
fn arithmetic(x: u64) {
// CHECK-LABEL: fn arithmetic(
// CHECK: opaque::<u64>(_1)
opaque(x + 0);
// CHECK: opaque::<u64>(_1)
opaque(x - 0);
// CHECK: opaque::<u64>(const 0_u64)
opaque(x - x);
// CHECK: opaque::<u64>(const 0_u64)
opaque(x * 0);
// CHECK: opaque::<u64>(_1)
opaque(x * 1);
// CHECK: assert(!const true, "attempt to divide `{}` by zero",
// CHECK: [[div0:_.*]] = Div(_1, const 0_u64);
// CHECK: opaque::<u64>(move [[div0]])
opaque(x / 0);
// CHECK: opaque::<u64>(_1)
opaque(x / 1);
// CHECK: opaque::<u64>(const 0_u64)
opaque(0 / x);
// CHECK: [[odiv:_.*]] = Div(const 1_u64, _1);
// CHECK: opaque::<u64>(move [[odiv]])
opaque(1 / x);
// CHECK: assert(!const true, "attempt to calculate the remainder of `{}` with a divisor of zero"
// CHECK: [[rem0:_.*]] = Rem(_1, const 0_u64);
// CHECK: opaque::<u64>(move [[rem0]])
opaque(x % 0);
// CHECK: opaque::<u64>(const 0_u64)
opaque(x % 1);
// CHECK: opaque::<u64>(const 0_u64)
opaque(0 % x);
// CHECK: [[orem:_.*]] = Rem(const 1_u64, _1);
// CHECK: opaque::<u64>(move [[orem]])
opaque(1 % x);
// CHECK: opaque::<u64>(const 0_u64)
opaque(x & 0);
// CHECK: opaque::<u64>(_1)
opaque(x & u64::MAX);
// CHECK: opaque::<u64>(_1)
opaque(x | 0);
// CHECK: opaque::<u64>(const u64::MAX)
opaque(x | u64::MAX);
// CHECK: opaque::<u64>(_1)
opaque(x ^ 0);
// CHECK: opaque::<u64>(const 0_u64)
opaque(x ^ x);
// CHECK: opaque::<u64>(_1)
opaque(x >> 0);
// CHECK: opaque::<u64>(_1)
opaque(x << 0);
}
fn comparison(x: u64, y: u64) {
// CHECK-LABEL: fn comparison(
// CHECK: opaque::<bool>(const true)
opaque(x == x);
// CHECK: opaque::<bool>(const false)
opaque(x != x);
// CHECK: [[eqxy:_.*]] = Eq(_1, _2);
// CHECK: opaque::<bool>(move [[eqxy]])
opaque(x == y);
// CHECK: [[nexy:_.*]] = Ne(_1, _2);
// CHECK: opaque::<bool>(move [[nexy]])
opaque(x != y);
}
/// Verify symbolic integer arithmetic simplifications on checked ops.
#[rustc_inherit_overflow_checks]
fn arithmetic_checked(x: u64) {
// CHECK-LABEL: fn arithmetic_checked(
// CHECK: assert(!const false,
// CHECK: opaque::<u64>(_1)
opaque(x + 0);
// CHECK: assert(!const false,
// CHECK: opaque::<u64>(_1)
opaque(x - 0);
// CHECK: assert(!const false,
// CHECK: opaque::<u64>(const 0_u64)
opaque(x - x);
// CHECK: assert(!const false,
// CHECK: opaque::<u64>(const 0_u64)
opaque(x * 0);
// CHECK: assert(!const false,
// CHECK: opaque::<u64>(_1)
opaque(x * 1);
}
/// Verify that we do not apply arithmetic simplifications on floats.
fn arithmetic_float(x: f64) {
// CHECK-LABEL: fn arithmetic_float(
// CHECK: [[add:_.*]] = Add(_1, const 0f64);
// CHECK: opaque::<f64>(move [[add]])
opaque(x + 0.);
// CHECK: [[sub:_.*]] = Sub(_1, const 0f64);
// CHECK: opaque::<f64>(move [[sub]])
opaque(x - 0.);
// CHECK: [[mul:_.*]] = Mul(_1, const 0f64);
// CHECK: opaque::<f64>(move [[mul]])
opaque(x * 0.);
// CHECK: [[div0:_.*]] = Div(_1, const 0f64);
// CHECK: opaque::<f64>(move [[div0]])
opaque(x / 0.);
// CHECK: [[zdiv:_.*]] = Div(const 0f64, _1);
// CHECK: opaque::<f64>(move [[zdiv]])
opaque(0. / x);
// CHECK: [[rem0:_.*]] = Rem(_1, const 0f64);
// CHECK: opaque::<f64>(move [[rem0]])
opaque(x % 0.);
// CHECK: [[zrem:_.*]] = Rem(const 0f64, _1);
// CHECK: opaque::<f64>(move [[zrem]])
opaque(0. % x);
// Those are not simplifiable to `true`/`false`, thanks to NaNs.
// CHECK: [[eq:_.*]] = Eq(_1, _1);
// CHECK: opaque::<bool>(move [[eq]])
opaque(x == x);
// CHECK: [[ne:_.*]] = Ne(_1, _1);
// CHECK: opaque::<bool>(move [[ne]])
opaque(x != x);
}
fn cast() {
// CHECK-LABEL: fn cast(
let i = 1_i64;
let u = 1_u64;
let f = 1_f64;
// CHECK: opaque::<u8>(const 1_u8)
opaque(i as u8);
// CHECK: opaque::<u16>(const 1_u16)
opaque(i as u16);
// CHECK: opaque::<u32>(const 1_u32)
opaque(i as u32);
// CHECK: opaque::<u64>(const 1_u64)
opaque(i as u64);
// CHECK: opaque::<i8>(const 1_i8)
opaque(i as i8);
// CHECK: opaque::<i16>(const 1_i16)
opaque(i as i16);
// CHECK: opaque::<i32>(const 1_i32)
opaque(i as i32);
// CHECK: opaque::<i64>(const 1_i64)
opaque(i as i64);
// CHECK: opaque::<f32>(const 1f32)
opaque(i as f32);
// CHECK: opaque::<f64>(const 1f64)
opaque(i as f64);
// CHECK: opaque::<u8>(const 1_u8)
opaque(u as u8);
// CHECK: opaque::<u16>(const 1_u16)
opaque(u as u16);
// CHECK: opaque::<u32>(const 1_u32)
opaque(u as u32);
// CHECK: opaque::<u64>(const 1_u64)
opaque(u as u64);
// CHECK: opaque::<i8>(const 1_i8)
opaque(u as i8);
// CHECK: opaque::<i16>(const 1_i16)
opaque(u as i16);
// CHECK: opaque::<i32>(const 1_i32)
opaque(u as i32);
// CHECK: opaque::<i64>(const 1_i64)
opaque(u as i64);
// CHECK: opaque::<f32>(const 1f32)
opaque(u as f32);
// CHECK: opaque::<f64>(const 1f64)
opaque(u as f64);
// CHECK: opaque::<u8>(const 1_u8)
opaque(f as u8);
// CHECK: opaque::<u16>(const 1_u16)
opaque(f as u16);
// CHECK: opaque::<u32>(const 1_u32)
opaque(f as u32);
// CHECK: opaque::<u64>(const 1_u64)
opaque(f as u64);
// CHECK: opaque::<i8>(const 1_i8)
opaque(f as i8);
// CHECK: opaque::<i16>(const 1_i16)
opaque(f as i16);
// CHECK: opaque::<i32>(const 1_i32)
opaque(f as i32);
// CHECK: opaque::<i64>(const 1_i64)
opaque(f as i64);
// CHECK: opaque::<f32>(const 1f32)
opaque(f as f32);
// CHECK: opaque::<f64>(const 1f64)
opaque(f as f64);
}
fn multiple_branches(t: bool, x: u8, y: u8) {
// CHECK-LABEL: fn multiple_branches(
// CHECK: switchInt(_1) -> [0: [[bbf:bb.*]], otherwise: [[bbt:bb.*]]];
if t {
// CHECK: [[bbt]]: {
// CHECK: [[a:_.*]] = Add(_2, _3);
// CHECK: opaque::<u8>([[a]])
// CHECK: opaque::<u8>([[a]])
// CHECK: goto -> [[bbc:bb.*]];
opaque(x + y);
opaque(x + y);
} else {
// CHECK: [[bbf]]: {
// CHECK: [[b:_.*]] = Add(_2, _3);
// CHECK: opaque::<u8>([[b]])
// CHECK: opaque::<u8>([[b]])
// CHECK: goto -> [[bbc:bb.*]];
opaque(x + y);
opaque(x + y);
}
// Neither `a` nor `b` dominate `c`, so we cannot reuse any of them.
// CHECK: [[bbc]]: {
// CHECK: [[c:_.*]] = Add(_2, _3);
// CHECK: opaque::<u8>([[c]])
opaque(x + y);
// `c` dominates both calls, so we can reuse it.
if t {
// CHECK: opaque::<u8>([[c]])
opaque(x + y);
} else {
// CHECK: opaque::<u8>([[c]])
opaque(x + y);
}
}
/// Verify that we do not reuse a `&raw? mut?` rvalue.
fn references(mut x: impl Sized) {
// CHECK-LABEL: fn references(
// CHECK: [[ref1:_.*]] = &_1;
// CHECK: opaque::<&impl Sized>(move [[ref1]])
opaque(&x);
// CHECK: [[ref2:_.*]] = &_1;
// CHECK: opaque::<&impl Sized>(move [[ref2]])
opaque(&x);
// CHECK: [[ref3:_.*]] = &mut _1;
// CHECK: opaque::<&mut impl Sized>(move [[ref3]])
opaque(&mut x);
// CHECK: [[ref4:_.*]] = &mut _1;
// CHECK: opaque::<&mut impl Sized>(move [[ref4]])
opaque(&mut x);
// CHECK: [[ref5:_.*]] = &raw const _1;
// CHECK: opaque::<*const impl Sized>(move [[ref5]])
opaque(&raw const x);
// CHECK: [[ref6:_.*]] = &raw const _1;
// CHECK: opaque::<*const impl Sized>(move [[ref6]])
opaque(&raw const x);
// CHECK: [[ref7:_.*]] = &raw mut _1;
// CHECK: opaque::<*mut impl Sized>(move [[ref7]])
opaque(&raw mut x);
// CHECK: [[ref8:_.*]] = &raw mut _1;
// CHECK: opaque::<*mut impl Sized>(move [[ref8]])
opaque(&raw mut x);
let r = &mut x;
let s = S(r).0; // Obfuscate `r`. Following lines should still reborrow `r`.
// CHECK: [[ref9:_.*]] = &mut _1;
// CHECK: [[ref10:_.*]] = &(*[[ref9]]);
// CHECK: opaque::<&impl Sized>(move [[ref10]])
opaque(&*s);
// CHECK: [[ref11:_.*]] = &mut (*[[ref9]]);
// CHECK: opaque::<&mut impl Sized>(move [[ref11]])
opaque(&mut *s);
// CHECK: [[ref12:_.*]] = &raw const (*[[ref9]]);
// CHECK: opaque::<*const impl Sized>(move [[ref12]])
opaque(&raw const *s);
// CHECK: [[ref12:_.*]] = &raw mut (*[[ref9]]);
// CHECK: opaque::<*mut impl Sized>(move [[ref12]])
opaque(&raw mut *s);
}
fn dereferences(t: &mut u32, u: &impl Copy, s: &S<u32>) {
// CHECK-LABEL: fn dereferences(
// Do not reuse dereferences of `&mut`.
// CHECK: [[st1:_.*]] = (*_1);
// CHECK: opaque::<u32>(move [[st1]])
// CHECK: [[st2:_.*]] = (*_1);
// CHECK: opaque::<u32>(move [[st2]])
opaque(*t);
opaque(*t);
// Do not reuse dereferences of `*const`.
// CHECK: [[raw:_.*]] = &raw const (*_1);
// CHECK: [[st3:_.*]] = (*[[raw]]);
// CHECK: opaque::<u32>(move [[st3]])
// CHECK: [[st4:_.*]] = (*[[raw]]);
// CHECK: opaque::<u32>(move [[st4]])
let z = &raw const *t;
unsafe { opaque(*z) };
unsafe { opaque(*z) };
// Do not reuse dereferences of `*mut`.
// CHECK: [[ptr:_.*]] = &raw mut (*_1);
// CHECK: [[st5:_.*]] = (*[[ptr]]);
// CHECK: opaque::<u32>(move [[st5]])
// CHECK: [[st6:_.*]] = (*[[ptr]]);
// CHECK: opaque::<u32>(move [[st6]])
let z = &raw mut *t;
unsafe { opaque(*z) };
unsafe { opaque(*z) };
// We can reuse dereferences of `&Freeze`.
// CHECK: [[ref:_.*]] = &(*_1);
// CHECK: [[st7:_.*]] = (*[[ref]]);
// CHECK: opaque::<u32>([[st7]])
// CHECK: opaque::<u32>([[st7]])
let z = &*t;
opaque(*z);
opaque(*z);
// But not in reborrows.
// CHECK: [[reborrow:_.*]] = &(*[[ref]]);
// CHECK: opaque::<&u32>(move [[reborrow]])
opaque(&*z);
// `*u` is not Freeze, so we cannot reuse.
// CHECK: [[st8:_.*]] = (*_2);
// CHECK: opaque::<impl Copy>(move [[st8]])
// CHECK: [[st9:_.*]] = (*_2);
// CHECK: opaque::<impl Copy>(move [[st9]])
opaque(*u);
opaque(*u);
// `*s` is not Copy, by `(*s).0` is, so we can reuse.
// CHECK: [[st10:_.*]] = ((*_3).0: u32);
// CHECK: opaque::<u32>([[st10]])
// CHECK: opaque::<u32>([[st10]])
opaque(s.0);
opaque(s.0);
}
fn slices() {
// CHECK-LABEL: fn slices(
// CHECK: {{_.*}} = const "
// CHECK-NOT: {{_.*}} = const "
let s = "my favourite slice"; // This is a `Const::Slice` in MIR.
opaque(s);
let t = s; // This should be the same pointer, so cannot be a `Const::Slice`.
opaque(t);
assert_eq!(s.as_ptr(), t.as_ptr());
let u = unsafe { transmute::<&str, &[u8]>(s) };
opaque(u);
assert_eq!(s.as_ptr(), u.as_ptr());
}
#[custom_mir(dialect = "analysis")]
fn duplicate_slice() -> (bool, bool) {
// CHECK-LABEL: fn duplicate_slice(
mir!(
let au: u128;
let bu: u128;
let cu: u128;
let du: u128;
let c: &str;
let d: &str;
{
// CHECK: [[a:_.*]] = (const "a",);
// CHECK: [[au:_.*]] = ([[a]].0: &str) as u128 (Transmute);
let a = ("a",);
Call(au = transmute::<_, u128>(a.0), ReturnTo(bb1), UnwindContinue())
}
bb1 = {
// CHECK: [[c:_.*]] = identity::<&str>(([[a]].0: &str))
Call(c = identity(a.0), ReturnTo(bb2), UnwindContinue())
}
bb2 = {
// CHECK: [[cu:_.*]] = [[c]] as u128 (Transmute);
Call(cu = transmute::<_, u128>(c), ReturnTo(bb3), UnwindContinue())
}
bb3 = {
// This slice is different from `a.0`. Hence `bu` is not `au`.
// CHECK: [[b:_.*]] = const "a";
// CHECK: [[bu:_.*]] = [[b]] as u128 (Transmute);
let b = "a";
Call(bu = transmute::<_, u128>(b), ReturnTo(bb4), UnwindContinue())
}
bb4 = {
// This returns a copy of `b`, which is not `a`.
// CHECK: [[d:_.*]] = identity::<&str>([[b]])
Call(d = identity(b), ReturnTo(bb5), UnwindContinue())
}
bb5 = {
// CHECK: [[du:_.*]] = [[d]] as u128 (Transmute);
Call(du = transmute::<_, u128>(d), ReturnTo(bb6), UnwindContinue())
}
bb6 = {
// `direct` must not fold to `true`, as `indirect` will not.
// CHECK: = Eq([[au]], [[bu]]);
// CHECK: = Eq([[cu]], [[du]]);
let direct = au == bu;
let indirect = cu == du;
RET = (direct, indirect);
Return()
}
)
}
fn repeat() {
// CHECK-LABEL: fn repeat(
// CHECK: = [const 5_i32; 10];
let val = 5;
let array = [val, val, val, val, val, val, val, val, val, val];
}
/// Verify that we do not merge fn pointers created by casts.
fn fn_pointers() {
// CHECK-LABEL: fn fn_pointers(
// CHECK: [[f:_.*]] = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer
// CHECK: opaque::<fn(u8) -> u8>([[f]])
let f = identity as fn(u8) -> u8;
opaque(f);
// CHECK: [[g:_.*]] = identity::<u8> as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer
// CHECK: opaque::<fn(u8) -> u8>([[g]])
let g = identity as fn(u8) -> u8;
opaque(g);
// CHECK: [[cf:_.*]] = const {{.*}} as fn() (PointerCoercion(ClosureFnPointer
// CHECK: opaque::<fn()>([[cf]])
let closure = || {};
let cf = closure as fn();
opaque(cf);
// CHECK: [[cg:_.*]] = const {{.*}} as fn() (PointerCoercion(ClosureFnPointer
// CHECK: opaque::<fn()>([[cg]])
let cg = closure as fn();
opaque(cg);
}
/// Verify that we do not create a `ConstValue::Indirect` backed by a static's AllocId.
#[custom_mir(dialect = "analysis")]
fn indirect_static() {
static A: Option<u8> = None;
mir!({
let ptr = Static(A);
let out = Field::<u8>(Variant(*ptr, 1), 0);
Return()
})
}
/// Verify that having constant index `u64::MAX` does not yield to an overflow in rustc.
fn constant_index_overflow<T: Copy>(x: &[T]) {
// CHECK-LABEL: fn constant_index_overflow(
// CHECK: debug a => [[a:_.*]];
// CHECK: debug b => [[b:_.*]];
// CHECK: [[a]] = const usize::MAX;
// CHECK-NOT: = (*_1)[{{.*}} of 0];
// CHECK: [[b]] = (*_1)[[[a]]];
// CHECK-NOT: = (*_1)[{{.*}} of 0];
// CHECK: [[b]] = (*_1)[0 of 1];
// CHECK-NOT: = (*_1)[{{.*}} of 0];
let a = u64::MAX as usize;
let b = if a < x.len() { x[a] } else { x[0] };
opaque(b)
}
/// Check that we do not attempt to simplify anything when there is provenance.
fn wide_ptr_provenance() {
// CHECK-LABEL: fn wide_ptr_provenance(
let a: *const dyn Send = &1 as &dyn Send;
let b: *const dyn Send = &1 as &dyn Send;
// CHECK: [[eqp:_.*]] = Eq([[a:_.*]], [[b:_.*]]);
// CHECK: opaque::<bool>(move [[eqp]])
opaque(a == b);
// CHECK: [[nep:_.*]] = Ne([[a]], [[b]]);
// CHECK: opaque::<bool>(move [[nep]])
opaque(a != b);
// CHECK: [[ltp:_.*]] = Lt([[a]], [[b]]);
// CHECK: opaque::<bool>(move [[ltp]])
opaque(a < b);
// CHECK: [[lep:_.*]] = Le([[a]], [[b]]);
// CHECK: opaque::<bool>(move [[lep]])
opaque(a <= b);
// CHECK: [[gtp:_.*]] = Gt([[a]], [[b]]);
// CHECK: opaque::<bool>(move [[gtp]])
opaque(a > b);
// CHECK: [[gep:_.*]] = Ge([[a]], [[b]]);
// CHECK: opaque::<bool>(move [[gep]])
opaque(a >= b);
}
/// Both pointers come form the same allocation, so we could probably fold the comparisons.
fn wide_ptr_same_provenance() {
// CHECK-LABEL: fn wide_ptr_same_provenance(
let slice = &[1, 2];
let a: *const dyn Send = &slice[0] as &dyn Send;
let b: *const dyn Send = &slice[1] as &dyn Send;
// CHECK: [[eqp:_.*]] = Eq([[a:_.*]], [[b:_.*]]);
// CHECK: opaque::<bool>(move [[eqp]])
opaque(a == b);
// CHECK: [[nep:_.*]] = Ne([[a]], [[b]]);
// CHECK: opaque::<bool>(move [[nep]])
opaque(a != b);
// CHECK: [[ltp:_.*]] = Lt([[a]], [[b]]);
// CHECK: opaque::<bool>(move [[ltp]])
opaque(a < b);
// CHECK: [[lep:_.*]] = Le([[a]], [[b]]);
// CHECK: opaque::<bool>(move [[lep]])
opaque(a <= b);
// CHECK: [[gtp:_.*]] = Gt([[a]], [[b]]);
// CHECK: opaque::<bool>(move [[gtp]])
opaque(a > b);
// CHECK: [[gep:_.*]] = Ge([[a]], [[b]]);
// CHECK: opaque::<bool>(move [[gep]])
opaque(a >= b);
}
/// Check that we do simplify when there is no provenance, and do not ICE.
fn wide_ptr_integer() {
// CHECK-LABEL: fn wide_ptr_integer(
// CHECK: debug a => [[a:_.*]];
// CHECK: debug b => [[b:_.*]];
let a: *const [u8] = unsafe { transmute((1usize, 1usize)) };
let b: *const [u8] = unsafe { transmute((1usize, 2usize)) };
// CHECK: opaque::<bool>(const false)
opaque(a == b);
// CHECK: opaque::<bool>(const true)
opaque(a != b);
// CHECK: opaque::<bool>(const true)
opaque(a < b);
// CHECK: opaque::<bool>(const true)
opaque(a <= b);
// CHECK: opaque::<bool>(const false)
opaque(a > b);
// CHECK: opaque::<bool>(const false)
opaque(a >= b);
}
#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
fn borrowed<T: Copy + Freeze>(x: T) {
// CHECK-LABEL: fn borrowed(
// CHECK: bb0: {
// CHECK-NEXT: _2 = _1;
// CHECK-NEXT: _3 = &_1;
// CHECK-NEXT: _0 = opaque::<&T>(_3)
// CHECK: bb1: {
// CHECK-NEXT: _0 = opaque::<T>(_1)
// CHECK: bb2: {
// CHECK-NEXT: _0 = opaque::<T>(_1)
mir!(
{
let a = x;
let r1 = &x;
Call(RET = opaque(r1), ReturnTo(next), UnwindContinue())
}
next = {
Call(RET = opaque(a), ReturnTo(deref), UnwindContinue())
}
deref = {
Call(RET = opaque(*r1), ReturnTo(ret), UnwindContinue())
}
ret = {
Return()
}
)
}
/// Generic type `T` is not known to be `Freeze`, so shared borrows may be mutable.
#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
fn non_freeze<T: Copy>(x: T) {
// CHECK-LABEL: fn non_freeze(
// CHECK: bb0: {
// CHECK-NEXT: _2 = _1;
// CHECK-NEXT: _3 = &_1;
// CHECK-NEXT: _0 = opaque::<&T>(_3)
// CHECK: bb1: {
// CHECK-NEXT: _0 = opaque::<T>(_2)
// CHECK: bb2: {
// CHECK-NEXT: _0 = opaque::<T>((*_3))
mir!(
{
let a = x;
let r1 = &x;
Call(RET = opaque(r1), ReturnTo(next), UnwindContinue())
}
next = {
Call(RET = opaque(a), ReturnTo(deref), UnwindContinue())
}
deref = {
Call(RET = opaque(*r1), ReturnTo(ret), UnwindContinue())
}
ret = {
Return()
}
)
}
fn main() {
subexpression_elimination(2, 4, 5);
wrap_unwrap(5);
repeated_index::<u32, 7>(5, 3);
unary(i64::MIN);
arithmetic(5);
comparison(5, 6);
arithmetic_checked(5);
arithmetic_float(5.);
cast();
multiple_branches(true, 5, 9);
references(5);
dereferences(&mut 5, &6, &S(7));
slices();
let (direct, indirect) = duplicate_slice();
assert_eq!(direct, indirect);
repeat();
fn_pointers();
indirect_static();
constant_index_overflow(&[5, 3]);
wide_ptr_provenance();
wide_ptr_integer();
borrowed(5);
non_freeze(5);
}
#[inline(never)]
fn opaque(_: impl Sized) {}
#[inline(never)]
fn identity<T>(x: T) -> T {
x
}
// EMIT_MIR gvn.subexpression_elimination.GVN.diff
// EMIT_MIR gvn.wrap_unwrap.GVN.diff
// EMIT_MIR gvn.repeated_index.GVN.diff
// EMIT_MIR gvn.unary.GVN.diff
// EMIT_MIR gvn.arithmetic.GVN.diff
// EMIT_MIR gvn.comparison.GVN.diff
// EMIT_MIR gvn.arithmetic_checked.GVN.diff
// EMIT_MIR gvn.arithmetic_float.GVN.diff
// EMIT_MIR gvn.cast.GVN.diff
// EMIT_MIR gvn.multiple_branches.GVN.diff
// EMIT_MIR gvn.references.GVN.diff
// EMIT_MIR gvn.dereferences.GVN.diff
// EMIT_MIR gvn.slices.GVN.diff
// EMIT_MIR gvn.duplicate_slice.GVN.diff
// EMIT_MIR gvn.repeat.GVN.diff
// EMIT_MIR gvn.fn_pointers.GVN.diff
// EMIT_MIR gvn.indirect_static.GVN.diff
// EMIT_MIR gvn.constant_index_overflow.GVN.diff
// EMIT_MIR gvn.wide_ptr_provenance.GVN.diff
// EMIT_MIR gvn.wide_ptr_same_provenance.GVN.diff
// EMIT_MIR gvn.wide_ptr_integer.GVN.diff
// EMIT_MIR gvn.borrowed.GVN.diff
// EMIT_MIR gvn.non_freeze.GVN.diff