237 lines
6.4 KiB
Rust
237 lines
6.4 KiB
Rust
//@ test-mir-pass: ScalarReplacementOfAggregates
|
|
//@ compile-flags: -Cpanic=abort
|
|
//@ no-prefer-dynamic
|
|
|
|
struct Tag(usize);
|
|
|
|
#[repr(C)]
|
|
struct S(Tag, Tag, Tag);
|
|
|
|
impl Drop for Tag {
|
|
#[inline(never)]
|
|
fn drop(&mut self) {}
|
|
}
|
|
|
|
/// Check that SROA excludes structs with a `Drop` implementation.
|
|
pub fn dropping() {
|
|
// CHECK-LABEL: fn dropping(
|
|
|
|
// CHECK: [[aggregate:_[0-9]+]]: S;
|
|
|
|
// CHECK: bb0: {
|
|
// CHECK: [[aggregate]] = S
|
|
S(Tag(0), Tag(1), Tag(2)).1;
|
|
}
|
|
|
|
/// Check that SROA excludes enums.
|
|
pub fn enums(a: usize) -> usize {
|
|
// CHECK-LABEL: fn enums(
|
|
|
|
// CHECK: [[enum:_[0-9]+]]: std::option::Option<usize>;
|
|
|
|
// CHECK: bb0: {
|
|
// CHECK: [[enum]] = Option::<usize>::Some
|
|
// CHECK: _5 = copy (([[enum]] as Some).0: usize)
|
|
// CHECK: _0 = copy _5
|
|
if let Some(a) = Some(a) { a } else { 0 }
|
|
}
|
|
|
|
/// Check that SROA destructures `U`.
|
|
pub fn structs(a: f32) -> f32 {
|
|
// CHECK-LABEL: fn structs(
|
|
struct U {
|
|
_foo: usize,
|
|
a: f32,
|
|
}
|
|
// CHECK: [[ret:_0]]: f32;
|
|
// CHECK: [[struct:_[0-9]+]]: structs::U;
|
|
// CHECK: [[a_tmp:_[0-9]+]]: f32;
|
|
// CHECK: [[foo:_[0-9]+]]: usize;
|
|
// CHECK: [[a_ret:_[0-9]+]]: f32;
|
|
|
|
// CHECK: bb0: {
|
|
// CHECK-NOT: [[struct]]
|
|
// CHECK: [[a_tmp]] = copy _1;
|
|
// CHECK-NOT: [[struct]]
|
|
// CHECK: [[foo]] = const 0_usize;
|
|
// CHECK-NOT: [[struct]]
|
|
// CHECK: [[a_ret]] = move [[a_tmp]];
|
|
// CHECK-NOT: [[struct]]
|
|
// CHECK: _0 = copy [[a_ret]];
|
|
// CHECK-NOT: [[struct]]
|
|
U { _foo: 0, a }.a
|
|
}
|
|
|
|
/// Check that SROA excludes unions.
|
|
pub fn unions(a: f32) -> u32 {
|
|
// CHECK-LABEL: fn unions(
|
|
union Repr {
|
|
f: f32,
|
|
u: u32,
|
|
}
|
|
// CHECK: [[union:_[0-9]+]]: unions::Repr;
|
|
|
|
// CHECK: bb0: {
|
|
// CHECK: [[union]] = Repr {
|
|
// CHECK: _0 = copy ([[union]].1: u32)
|
|
unsafe { Repr { f: a }.u }
|
|
}
|
|
|
|
#[derive(Copy, Clone)]
|
|
struct Foo {
|
|
a: u8,
|
|
b: (),
|
|
c: &'static str,
|
|
d: Option<isize>,
|
|
}
|
|
|
|
/// Check that non-escaping uses of a struct are destructured.
|
|
pub fn flat() {
|
|
// CHECK-LABEL: fn flat(
|
|
|
|
// CHECK: [[struct:_[0-9]+]]: Foo;
|
|
|
|
// CHECK: bb0: {
|
|
// CHECK: [[init_unit:_[0-9]+]] = ();
|
|
// CHECK: [[init_opt_isize:_[0-9]+]] = Option::<isize>::Some
|
|
|
|
// CHECK: [[destr_five:_[0-9]+]] = const 5_u8;
|
|
// CHECK: [[destr_unit:_[0-9]+]] = move [[init_unit]];
|
|
// CHECK: [[destr_a:_[0-9]+]] = const "a";
|
|
// CHECK: [[destr_opt_isize:_[0-9]+]] = move [[init_opt_isize]];
|
|
|
|
let Foo { a, b, c, d } = Foo { a: 5, b: (), c: "a", d: Some(-4) };
|
|
let _ = a;
|
|
let _ = b;
|
|
let _ = c;
|
|
let _ = d;
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct Escaping {
|
|
a: u32,
|
|
b: u32,
|
|
c: u32,
|
|
}
|
|
|
|
fn g() -> u32 {
|
|
3
|
|
}
|
|
|
|
fn f(a: *const u32) {
|
|
println!("{}", unsafe { *a.add(2) });
|
|
}
|
|
|
|
// `f` uses the `&e.a` to access `e.c`. This is UB according to Miri today; however,
|
|
// T-opsem has not finalized that decision and as such rustc should not rely on
|
|
// it. If SROA were to rely on it, it would be (almost) correct to turn `e` into
|
|
// three distinct locals - one for each field - and pass a reference to only one
|
|
// of them to `f`. However, this would lead to a miscompilation because `b` and `c`
|
|
// might no longer appear right after `a` in memory.
|
|
pub fn escaping() {
|
|
// CHECK-LABEL: fn escaping(
|
|
|
|
// CHECK: [[ptr:_[0-9]+]]: *const u32;
|
|
// CHECK: [[ref:_[0-9]+]]: &u32;
|
|
// CHECK: [[struct:_[0-9]+]]: Escaping;
|
|
// CHECK: [[a:_[0-9]+]]: u32;
|
|
|
|
// CHECK: bb0: {
|
|
// CHECK: [[struct]] = Escaping {
|
|
// CHECK: [[ref]] = &([[struct]].0
|
|
// CHECK: [[ptr]] = &raw const (*[[ref]]);
|
|
f(&Escaping { a: 1, b: 2, c: g() }.a);
|
|
}
|
|
|
|
/// Check that copies from an internal struct are destructured and reassigned to
|
|
/// the original struct.
|
|
fn copies(x: Foo) {
|
|
// CHECK-LABEL: fn copies(
|
|
|
|
// CHECK: [[external:_[0-9]+]]: Foo) ->
|
|
// CHECK: [[internal:_[0-9]+]]: Foo;
|
|
// CHECK: [[byte:_[0-9]+]]: u8;
|
|
// CHECK: [[unit:_[0-9]+]]: ();
|
|
// CHECK: [[str:_[0-9]+]]: &str;
|
|
// CHECK: [[opt_isize:_[0-9]+]]: std::option::Option<isize>;
|
|
|
|
// CHECK: bb0: {
|
|
// CHECK: [[byte]] = copy ([[external]].0
|
|
// CHECK: [[unit]] = copy ([[external]].1
|
|
// CHECK: [[str]] = copy ([[external]].2
|
|
// CHECK: [[opt_isize]] = copy ([[external]].3
|
|
|
|
let y = x;
|
|
let t = y.a;
|
|
let u = y.c;
|
|
let z = y;
|
|
let a = z.b;
|
|
}
|
|
|
|
/// Check that copies from an internal struct are destructured and reassigned to
|
|
/// the original struct.
|
|
fn ref_copies(x: &Foo) {
|
|
// CHECK-LABEL: fn ref_copies(
|
|
|
|
// CHECK: [[external:_[0-9]+]]: &Foo) ->
|
|
// CHECK: [[internal:_[0-9]+]]: Foo;
|
|
// CHECK: [[byte:_[0-9]+]]: u8;
|
|
// CHECK: [[unit:_[0-9]+]]: ();
|
|
// CHECK: [[str:_[0-9]+]]: &str;
|
|
// CHECK: [[opt_isize:_[0-9]+]]: std::option::Option<isize>;
|
|
|
|
// CHECK: bb0: {
|
|
// CHECK: [[byte]] = copy ((*[[external]]).0
|
|
// CHECK: [[unit]] = copy ((*[[external]]).1
|
|
// CHECK: [[str]] = copy ((*[[external]]).2
|
|
// CHECK: [[opt_isize]] = copy ((*[[external]]).3
|
|
|
|
let y = *x;
|
|
let t = y.a;
|
|
let u = y.c;
|
|
}
|
|
|
|
/// Check that deaggregated assignments from constants are placed after the constant's
|
|
/// assignment. Also check that copying field accesses from the copy of the constant are
|
|
/// reassigned to copy from the constant.
|
|
fn constant() {
|
|
// CHECK-LABEL: constant(
|
|
|
|
// CHECK: [[constant:_[0-9]+]]: (usize, u8);
|
|
// CHECK: [[t:_[0-9]+]]: usize;
|
|
// CHECK: [[u:_[0-9]+]]: u8;
|
|
|
|
// CHECK: bb0: {
|
|
// CHECK-NOT: [[constant]]
|
|
// CHECK: [[constant]] = const
|
|
// CHECK: [[t]] = move ([[constant]].0: usize)
|
|
// CHECK: [[u]] = move ([[constant]].1: u8)
|
|
const U: (usize, u8) = (5, 9);
|
|
let y = U;
|
|
let t = y.0;
|
|
let u = y.1;
|
|
}
|
|
|
|
fn main() {
|
|
// CHECK-LABEL: fn main(
|
|
dropping();
|
|
enums(5);
|
|
structs(5.);
|
|
unions(5.);
|
|
flat();
|
|
escaping();
|
|
copies(Foo { a: 5, b: (), c: "a", d: Some(-4) });
|
|
ref_copies(&Foo { a: 5, b: (), c: "a", d: Some(-4) });
|
|
constant();
|
|
}
|
|
|
|
// EMIT_MIR structs.dropping.ScalarReplacementOfAggregates.diff
|
|
// EMIT_MIR structs.enums.ScalarReplacementOfAggregates.diff
|
|
// EMIT_MIR structs.structs.ScalarReplacementOfAggregates.diff
|
|
// EMIT_MIR structs.unions.ScalarReplacementOfAggregates.diff
|
|
// EMIT_MIR structs.flat.ScalarReplacementOfAggregates.diff
|
|
// EMIT_MIR structs.escaping.ScalarReplacementOfAggregates.diff
|
|
// EMIT_MIR structs.copies.ScalarReplacementOfAggregates.diff
|
|
// EMIT_MIR structs.ref_copies.ScalarReplacementOfAggregates.diff
|
|
// EMIT_MIR structs.constant.ScalarReplacementOfAggregates.diff
|