255 lines
5.7 KiB
Rust
255 lines
5.7 KiB
Rust
// skip-filecheck
|
|
// unit-test: GVN
|
|
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
|
|
|
#![feature(raw_ref_op)]
|
|
#![feature(rustc_attrs)]
|
|
#![allow(unconditional_panic)]
|
|
|
|
struct S<T>(T);
|
|
|
|
fn subexpression_elimination(x: u64, y: u64, mut z: u64) {
|
|
opaque(x + y);
|
|
opaque(x * y);
|
|
opaque(x - y);
|
|
opaque(x / y);
|
|
opaque(x % y);
|
|
opaque(x & y);
|
|
opaque(x | y);
|
|
opaque(x ^ y);
|
|
opaque(x << y);
|
|
opaque(x >> y);
|
|
opaque(x as u32);
|
|
opaque(x as f32);
|
|
opaque(S(x));
|
|
opaque(S(x).0);
|
|
|
|
// Those are duplicates to substitute somehow.
|
|
opaque((x + y) + z);
|
|
opaque((x * y) + z);
|
|
opaque((x - y) + z);
|
|
opaque((x / y) + z);
|
|
opaque((x % y) + z);
|
|
opaque((x & y) + z);
|
|
opaque((x | y) + z);
|
|
opaque((x ^ y) + z);
|
|
opaque((x << y) + z);
|
|
opaque((x >> y) + z);
|
|
opaque(S(x));
|
|
opaque(S(x).0);
|
|
|
|
// We can substitute through an immutable reference too.
|
|
let a = &z;
|
|
opaque(*a + x);
|
|
opaque(*a + x);
|
|
|
|
// But not through a mutable reference or a pointer.
|
|
let b = &mut z;
|
|
opaque(*b + x);
|
|
opaque(*b + x);
|
|
unsafe {
|
|
let c = &raw const z;
|
|
opaque(*c + x);
|
|
opaque(*c + x);
|
|
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`!
|
|
let e = &z;
|
|
opaque(*e + x);
|
|
opaque(*e + x);
|
|
|
|
}
|
|
|
|
fn wrap_unwrap<T: Copy>(x: T) -> T {
|
|
match Some(x) {
|
|
Some(y) => y,
|
|
None => panic!(),
|
|
}
|
|
}
|
|
|
|
fn repeated_index<T: Copy, const N: usize>(x: T, idx: usize) {
|
|
let a = [x; N];
|
|
opaque(a[0]);
|
|
opaque(a[idx]);
|
|
}
|
|
|
|
fn arithmetic(x: u64) {
|
|
opaque(x + 0);
|
|
opaque(x - 0);
|
|
opaque(x * 0);
|
|
opaque(x * 1);
|
|
opaque(x / 0);
|
|
opaque(x / 1);
|
|
opaque(0 / x);
|
|
opaque(1 / x);
|
|
opaque(x % 0);
|
|
opaque(x % 1);
|
|
opaque(0 % x);
|
|
opaque(1 % x);
|
|
opaque(x & 0);
|
|
opaque(x | 0);
|
|
opaque(x ^ 0);
|
|
opaque(x >> 0);
|
|
opaque(x << 0);
|
|
}
|
|
|
|
#[rustc_inherit_overflow_checks]
|
|
fn arithmetic_checked(x: u64) {
|
|
opaque(x + 0);
|
|
opaque(x - 0);
|
|
opaque(x * 0);
|
|
opaque(x * 1);
|
|
opaque(x / 0);
|
|
opaque(x / 1);
|
|
opaque(0 / x);
|
|
opaque(1 / x);
|
|
opaque(x % 0);
|
|
opaque(x % 1);
|
|
opaque(0 % x);
|
|
opaque(1 % x);
|
|
opaque(x & 0);
|
|
opaque(x | 0);
|
|
opaque(x ^ 0);
|
|
opaque(x >> 0);
|
|
opaque(x << 0);
|
|
}
|
|
|
|
fn arithmetic_float(x: f64) {
|
|
opaque(x + 0.);
|
|
opaque(x - 0.);
|
|
opaque(x * 0.);
|
|
opaque(x / 0.);
|
|
opaque(0. / x);
|
|
opaque(x % 0.);
|
|
opaque(0. % x);
|
|
// Those are not simplifiable to `true`/`false`, thanks to NaNs.
|
|
opaque(x == x);
|
|
opaque(x != x);
|
|
}
|
|
|
|
fn cast() {
|
|
let i = 1_i64;
|
|
let u = 1_u64;
|
|
let f = 1_f64;
|
|
opaque(i as u8);
|
|
opaque(i as u16);
|
|
opaque(i as u32);
|
|
opaque(i as u64);
|
|
opaque(i as i8);
|
|
opaque(i as i16);
|
|
opaque(i as i32);
|
|
opaque(i as i64);
|
|
opaque(i as f32);
|
|
opaque(i as f64);
|
|
opaque(u as u8);
|
|
opaque(u as u16);
|
|
opaque(u as u32);
|
|
opaque(u as u64);
|
|
opaque(u as i8);
|
|
opaque(u as i16);
|
|
opaque(u as i32);
|
|
opaque(u as i64);
|
|
opaque(u as f32);
|
|
opaque(u as f64);
|
|
opaque(f as u8);
|
|
opaque(f as u16);
|
|
opaque(f as u32);
|
|
opaque(f as u64);
|
|
opaque(f as i8);
|
|
opaque(f as i16);
|
|
opaque(f as i32);
|
|
opaque(f as i64);
|
|
opaque(f as f32);
|
|
opaque(f as f64);
|
|
}
|
|
|
|
fn multiple_branches(t: bool, x: u8, y: u8) {
|
|
if t {
|
|
opaque(x + y); // a
|
|
opaque(x + y); // should reuse a
|
|
} else {
|
|
opaque(x + y); // b
|
|
opaque(x + y); // shoud reuse b
|
|
}
|
|
opaque(x + y); // c
|
|
if t {
|
|
opaque(x + y); // should reuse c
|
|
} else {
|
|
opaque(x + y); // should reuse c
|
|
}
|
|
}
|
|
|
|
fn references(mut x: impl Sized) {
|
|
opaque(&x);
|
|
opaque(&x); // should not reuse a
|
|
opaque(&mut x);
|
|
opaque(&mut x); // should not reuse a
|
|
opaque(&raw const x);
|
|
opaque(&raw const x); // should not reuse a
|
|
opaque(&raw mut x);
|
|
opaque(&raw mut x); // should not reuse a
|
|
}
|
|
|
|
fn dereferences(t: &mut u32, u: &impl Copy, s: &S<u32>) {
|
|
opaque(*t);
|
|
opaque(*t); // this cannot reuse a, as x is &mut.
|
|
let z = &raw const *t;
|
|
unsafe { opaque(*z) };
|
|
unsafe { opaque(*z) }; // this cannot reuse a, as x is *const.
|
|
let z = &raw mut *t;
|
|
unsafe { opaque(*z) };
|
|
unsafe { opaque(*z) }; // this cannot reuse a, as x is *mut.
|
|
let z = &*t;
|
|
opaque(*z);
|
|
opaque(*z); // this can reuse, as `z` is immutable ref, Freeze and Copy.
|
|
opaque(&*z); // but not for a reborrow.
|
|
opaque(*u);
|
|
opaque(*u); // this cannot reuse, as `z` is not Freeze.
|
|
opaque(s.0);
|
|
opaque(s.0); // *s is not Copy, by (*s).0 is, so we can reuse.
|
|
}
|
|
|
|
fn slices() {
|
|
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 { std::mem::transmute::<&str, &[u8]>(s) };
|
|
opaque(u);
|
|
assert_eq!(s.as_ptr(), u.as_ptr());
|
|
}
|
|
|
|
fn main() {
|
|
subexpression_elimination(2, 4, 5);
|
|
wrap_unwrap(5);
|
|
repeated_index::<u32, 7>(5, 3);
|
|
arithmetic(5);
|
|
arithmetic_checked(5);
|
|
arithmetic_float(5.);
|
|
cast();
|
|
multiple_branches(true, 5, 9);
|
|
references(5);
|
|
dereferences(&mut 5, &6, &S(7));
|
|
slices();
|
|
}
|
|
|
|
#[inline(never)]
|
|
fn opaque(_: impl Sized) {}
|
|
|
|
// EMIT_MIR gvn.subexpression_elimination.GVN.diff
|
|
// EMIT_MIR gvn.wrap_unwrap.GVN.diff
|
|
// EMIT_MIR gvn.repeated_index.GVN.diff
|
|
// EMIT_MIR gvn.arithmetic.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
|