| //@ compile-flags: -Zunsound-mir-opts |
| // FIXME: see <https://github.com/rust-lang/rust/issues/132353. |
| //@ test-mir-pass: GVN |
| //@ compile-flags: -Cpanic=abort |
| |
| #![feature(core_intrinsics, custom_mir)] |
| #![allow(internal_features)] |
| |
| use std::intrinsics::mir::*; |
| |
| struct AllCopy { |
| a: i32, |
| b: u64, |
| c: [i8; 3], |
| } |
| |
| // EMIT_MIR gvn_copy_aggregate.all_copy.GVN.diff |
| fn all_copy(v: &AllCopy) -> AllCopy { |
| // CHECK-LABEL: fn all_copy( |
| // CHECK: bb0: { |
| // CHECK-NOT: = AllCopy { {{.*}} }; |
| // CHECK: _0 = copy (*_1); |
| let a = v.a; |
| let b = v.b; |
| let c = v.c; |
| AllCopy { a, b, c } |
| } |
| |
| // EMIT_MIR gvn_copy_aggregate.all_copy_2.GVN.diff |
| fn all_copy_2(v: &&AllCopy) -> AllCopy { |
| // CHECK-LABEL: fn all_copy_2( |
| // CHECK: bb0: { |
| // CHECK-NOT: = AllCopy { {{.*}} }; |
| // CHECK: [[V1:_.*]] = copy (*_1); |
| // CHECK: _0 = copy (*[[V1]]); |
| let a = v.a; |
| let b = v.b; |
| let c = v.c; |
| AllCopy { a, b, c } |
| } |
| |
| // EMIT_MIR gvn_copy_aggregate.all_copy_move.GVN.diff |
| fn all_copy_move(v: AllCopy) -> AllCopy { |
| // CHECK-LABEL: fn all_copy_move( |
| // CHECK: bb0: { |
| // CHECK-NOT: = AllCopy { {{.*}} }; |
| // CHECK: _0 = copy _1; |
| let a = v.a; |
| let b = v.b; |
| let c = v.c; |
| AllCopy { a, b, c } |
| } |
| |
| // EMIT_MIR gvn_copy_aggregate.all_copy_ret_2.GVN.diff |
| fn all_copy_ret_2(v: &AllCopy) -> (AllCopy, AllCopy) { |
| // CHECK-LABEL: fn all_copy_ret_2( |
| // CHECK: bb0: { |
| // CHECK-NOT: = AllCopy { {{.*}} }; |
| // CHECK: [[V1:_.*]] = copy (*_1); |
| // CHECK: [[V2:_.*]] = copy [[V1]]; |
| // CHECK: _0 = (copy [[V1]], copy [[V1]]); |
| let a = v.a; |
| let b = v.b; |
| let c = v.c; |
| (AllCopy { a, b, c }, AllCopy { a, b, c }) |
| } |
| |
| struct AllCopy2 { |
| a: i32, |
| b: u64, |
| c: [i8; 3], |
| } |
| |
| // EMIT_MIR gvn_copy_aggregate.all_copy_different_type.GVN.diff |
| fn all_copy_different_type(v: &AllCopy) -> AllCopy2 { |
| // CHECK-LABEL: fn all_copy_different_type( |
| // CHECK: bb0: { |
| // CHECK: _0 = AllCopy2 { {{.*}} }; |
| let a = v.a; |
| let b = v.b; |
| let c = v.c; |
| AllCopy2 { a, b, c } |
| } |
| |
| struct SameType { |
| a: i32, |
| b: i32, |
| } |
| |
| // EMIT_MIR gvn_copy_aggregate.same_type_different_index.GVN.diff |
| fn same_type_different_index(v: &SameType) -> SameType { |
| // CHECK-LABEL: fn same_type_different_index( |
| // CHECK: bb0: { |
| // CHECK: _0 = SameType { {{.*}} }; |
| let a = v.b; |
| let b = v.a; |
| SameType { a, b } |
| } |
| |
| // EMIT_MIR gvn_copy_aggregate.all_copy_has_changed.GVN.diff |
| fn all_copy_has_changed(v: &mut AllCopy) -> AllCopy { |
| // CHECK-LABEL: fn all_copy_has_changed( |
| // CHECK: bb0: { |
| // CHECK: _0 = AllCopy { {{.*}} }; |
| let a = v.a; |
| let b = v.b; |
| let c = v.c; |
| v.a = 1; |
| AllCopy { a, b, c } |
| } |
| |
| // FIXME: This can be simplified to `Copy`. |
| // EMIT_MIR gvn_copy_aggregate.all_copy_use_changed.GVN.diff |
| fn all_copy_use_changed(v: &mut AllCopy) -> AllCopy { |
| // CHECK-LABEL: fn all_copy_use_changed( |
| // CHECK: bb0: { |
| // CHECK-NOT: _0 = copy (*_1); |
| // CHECK: = AllCopy { {{.*}} }; |
| let mut a = v.a; |
| v.a = 1; |
| a = v.a; |
| let b = v.b; |
| let c = v.c; |
| AllCopy { a, b, c } |
| } |
| |
| // FIXME: This can be simplified to `Copy`. |
| // EMIT_MIR gvn_copy_aggregate.all_copy_use_changed_2.GVN.diff |
| fn all_copy_use_changed_2(v: &mut AllCopy) -> AllCopy { |
| // CHECK-LABEL: fn all_copy_use_changed_2( |
| // CHECK: bb0: { |
| // CHECK-NOT: _0 = (*_1); |
| // CHECK: = AllCopy { {{.*}} }; |
| let mut a = v.a; |
| let b = v.b; |
| let c = v.c; |
| v.a = 1; |
| a = v.a; |
| AllCopy { a, b, c } |
| } |
| |
| struct NestCopy { |
| d: i32, |
| all_copy: AllCopy, |
| } |
| |
| // EMIT_MIR gvn_copy_aggregate.nest_copy.GVN.diff |
| fn nest_copy(v: &NestCopy) -> NestCopy { |
| // CHECK-LABEL: fn nest_copy( |
| // CHECK: bb0: { |
| // CHECK-NOT: = AllCopy { {{.*}} }; |
| // CHECK-NOT: = NestCopy { {{.*}} }; |
| let a = v.all_copy.a; |
| let b = v.all_copy.b; |
| let c = v.all_copy.c; |
| let all_copy = AllCopy { a, b, c }; |
| let d = v.d; |
| NestCopy { d, all_copy } |
| } |
| |
| enum Enum1 { |
| A(AllCopy), |
| B(AllCopy), |
| } |
| |
| // EMIT_MIR gvn_copy_aggregate.enum_identical_variant.GVN.diff |
| fn enum_identical_variant(v: &Enum1) -> Enum1 { |
| // CHECK-LABEL: fn enum_identical_variant( |
| // CHECK-NOT: = AllCopy { {{.*}} }; |
| // CHECK: _0 = copy (*_1); |
| // CHECK-NOT: = AllCopy { {{.*}} }; |
| // CHECK: _0 = copy (*_1); |
| match v { |
| Enum1::A(v) => { |
| let a = v.a; |
| let b = v.b; |
| let c = v.c; |
| let all_copy = AllCopy { a, b, c }; |
| Enum1::A(all_copy) |
| } |
| Enum1::B(v) => { |
| let a = v.a; |
| let b = v.b; |
| let c = v.c; |
| let all_copy = AllCopy { a, b, c }; |
| Enum1::B(all_copy) |
| } |
| } |
| } |
| |
| // EMIT_MIR gvn_copy_aggregate.enum_different_variant.GVN.diff |
| fn enum_different_variant(v: &Enum1) -> Enum1 { |
| // CHECK-LABEL: fn enum_different_variant( |
| // CHECK-NOT: = AllCopy { {{.*}} }; |
| // CHECK: [[V1:_.*]] = copy (((*_1) as [[VARIANT1:.*]]).0: AllCopy); |
| // CHECK: _0 = Enum1::[[VARIANT2:.*]](copy [[V1]]); |
| // CHECK-NOT: = AllCopy { {{.*}} }; |
| // CHECK: [[V2:_.*]] = copy (((*_1) as [[VARIANT2]]).0: AllCopy); |
| // CHECK: _0 = Enum1::[[VARIANT1]](copy [[V2]]); |
| match v { |
| Enum1::A(v) => { |
| let a = v.a; |
| let b = v.b; |
| let c = v.c; |
| let all_copy = AllCopy { a, b, c }; |
| Enum1::B(all_copy) |
| } |
| Enum1::B(v) => { |
| let a = v.a; |
| let b = v.b; |
| let c = v.c; |
| let all_copy = AllCopy { a, b, c }; |
| Enum1::A(all_copy) |
| } |
| } |
| } |
| |
| enum AlwaysSome<T> { |
| Some(T), |
| } |
| |
| // Ensure that we do not access this local after `StorageDead`. |
| // EMIT_MIR gvn_copy_aggregate.remove_storage_dead.GVN.diff |
| fn remove_storage_dead<T>(f: fn() -> AlwaysSome<T>) -> AlwaysSome<T> { |
| // CHECK-LABEL: fn remove_storage_dead( |
| // CHECK: [[V1:_.*]] = copy _1() -> [return: [[BB1:bb.*]], |
| // CHECK: [[BB1]]: { |
| // CHECK-NOT: StorageDead([[V1]]); |
| // CHECK: _0 = copy [[V1]]; |
| let v = { |
| match f() { |
| AlwaysSome::Some(v) => v, |
| } |
| }; |
| AlwaysSome::Some(v) |
| } |
| |
| // EMIT_MIR gvn_copy_aggregate.remove_storage_dead_from_index.GVN.diff |
| #[custom_mir(dialect = "analysis")] |
| fn remove_storage_dead_from_index(f: fn() -> usize, v: [SameType; 5]) -> SameType { |
| // CHECK-LABEL: fn remove_storage_dead_from_index( |
| // CHECK: [[V1:_.*]] = copy _1() -> [return: [[BB1:bb.*]], |
| // CHECK: [[BB1]]: { |
| // CHECK-NOT: StorageDead([[V1]]); |
| // CHECK-NOT: = SameType { {{.*}} }; |
| // CHECK: _0 = copy _2[[[V1]]]; |
| mir! { |
| let index: usize; |
| let a: i32; |
| let b: i32; |
| { |
| StorageLive(index); |
| Call(index = f(), ReturnTo(bb1), UnwindUnreachable()) |
| } |
| bb1 = { |
| a = v[index].a; |
| b = v[index].b; |
| StorageDead(index); |
| RET = SameType { a, b }; |
| Return() |
| } |
| } |
| } |