Rollup merge of #135099 - Shunpoco:116971-mir-opt-copy-prop, r=davidtwco

Add FileCheck annotations to mir-opt/copy-prop

This resolves a part of https://github.com/rust-lang/rust/issues/116971 .

This PR adds FileCheck annotations to test files under mir-opt/copy-prop.
This commit is contained in:
Guillaume Gomez 2025-11-03 17:20:31 +01:00 committed by GitHub
commit c97bde7df7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 110 additions and 89 deletions

View file

@ -1,4 +1,3 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
//! Tests that we bail out when there are multiple assignments to the same local.
//@ test-mir-pass: CopyProp
@ -12,6 +11,14 @@ fn cond() -> bool {
// EMIT_MIR branch.foo.CopyProp.diff
fn foo() -> i32 {
// CHECK-LABEL: fn foo(
// CHECK: debug x => [[x:_.*]];
// CHECK: debug y => [[y:_.*]];
// CHECK: bb3: {
// CHECK: [[y]] = copy [[x]];
// CHECK: bb5: {
// CHECK: [[y]] = copy [[x]];
// CHECK: _0 = copy [[y]];
let x = val();
let y = if cond() {

View file

@ -1,4 +1,3 @@
// skip-filecheck
// Check that CopyProp does propagate return values of call terminators.
//@ test-mir-pass: CopyProp
//@ needs-unwind
@ -13,6 +12,13 @@ fn dummy(x: u8) -> u8 {
// EMIT_MIR calls.nrvo.CopyProp.diff
fn nrvo() -> u8 {
// CHECK-LABEL: fn nrvo(
// CHECK: debug y => _0;
// CHECK-NOT: StorageLive(_1);
// CHECK-NOT: _1 = dummy(const 5_u8)
// CHECK: _0 = dummy(const 5_u8)
// CHECK-NOT: _0 = copy _1;
// CHECK-NOT: StorageDead(_1);
let y = dummy(5); // this should get NRVO
y
}
@ -20,6 +26,11 @@ fn nrvo() -> u8 {
// EMIT_MIR calls.multiple_edges.CopyProp.diff
#[custom_mir(dialect = "runtime", phase = "initial")]
fn multiple_edges(t: bool) -> u8 {
// CHECK-LABEL: fn multiple_edges(
// CHECK: bb1: {
// CHECK: _2 = dummy(const 13_u8)
// CHECK: bb2: {
// CHECK: _0 = copy _2;
mir! {
let x: u8;
{

View file

@ -1,4 +1,3 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
// Check that CopyProp does not propagate an assignment to a function argument
// (doing so can break usages of the original argument value)
@ -9,25 +8,46 @@ fn dummy(x: u8) -> u8 {
// EMIT_MIR copy_propagation_arg.foo.CopyProp.diff
fn foo(mut x: u8) {
// CHECK-LABEL: fn foo(
// CHECK: debug x => [[x:_.*]];
// CHECK: [[three:_.*]] = copy [[x]];
// CHECK: [[two:_.*]] = dummy(move [[three]])
// CHECK: [[x]] = move [[two]];
// calling `dummy` to make a use of `x` that copyprop cannot eliminate
x = dummy(x); // this will assign a local to `x`
}
// EMIT_MIR copy_propagation_arg.bar.CopyProp.diff
fn bar(mut x: u8) {
// CHECK-LABEL: fn bar(
// CHECK: debug x => [[x:_.*]];
// CHECK: [[three:_.*]] = copy [[x]];
// CHECK: dummy(move [[three]])
// CHECK: [[x]] = const 5_u8;
dummy(x);
x = 5;
}
// EMIT_MIR copy_propagation_arg.baz.CopyProp.diff
fn baz(mut x: i32) -> i32 {
// self-assignment to a function argument should be eliminated
// CHECK-LABEL: fn baz(
// CHECK: debug x => [[x:_.*]];
// CHECK: [[x2:_.*]] = copy [[x]];
// CHECK: [[x]] = move [[x2]];
// CHECK: _0 = copy [[x]];
// In the original case for DestProp, the self-assignment to a function argument is eliminated,
// but in CopyProp it is not eliminated.
x = x;
x
}
// EMIT_MIR copy_propagation_arg.arg_src.CopyProp.diff
fn arg_src(mut x: i32) -> i32 {
// CHECK-LABEL: fn arg_src(
// CHECK: debug x => [[x:_.*]];
// CHECK: debug y => [[y:_.*]];
// CHECK: [[y]] = copy [[x]];
// CHECK: [[x]] = const 123_i32;
let y = x;
x = 123; // Don't propagate this assignment to `y`
y

View file

@ -1,4 +1,3 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
//@ test-mir-pass: CopyProp
@ -12,6 +11,13 @@ struct NotCopy(bool);
// EMIT_MIR custom_move_arg.f.CopyProp.diff
#[custom_mir(dialect = "runtime")]
fn f(_1: NotCopy) {
// CHECK-LABEL: fn f(
// CHECK: bb0: {
// CHECK-NOT: _2 = copy _1;
// CHECK: _0 = opaque::<NotCopy>(copy _1)
// CHECK: bb1: {
// CHECK-NOT: _3 = move _2;
// CHECK: _0 = opaque::<NotCopy>(copy _1)
mir! {
{
let _2 = _1;

View file

@ -1,4 +1,3 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
//! Tests that cyclic assignments don't hang CopyProp, and result in reasonable code.
//@ test-mir-pass: CopyProp
@ -8,6 +7,18 @@ fn val() -> i32 {
// EMIT_MIR cycle.main.CopyProp.diff
fn main() {
// CHECK-LABEL: fn main(
// CHECK: debug x => [[x:_.*]];
// CHECK: debug y => [[y:_.*]];
// CHECK: debug z => [[y]];
// CHECK-NOT: StorageLive([[y]]);
// CHECK: [[y]] = copy [[x]];
// CHECK-NOT: StorageLive(_3);
// CHECK-NOT: _3 = copy [[y]];
// CHECK-NOT: StorageLive(_4);
// CHECK-NOT: _4 = copy _3;
// CHECK-NOT: _1 = move _4;
// CHECK: [[x]] = copy [[y]];
let mut x = val();
let y = x;
let z = y;

View file

@ -1,4 +1,3 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
//@ test-mir-pass: CopyProp
@ -8,6 +7,14 @@ fn id<T>(x: T) -> T {
// EMIT_MIR dead_stores_79191.f.CopyProp.after.mir
fn f(mut a: usize) -> usize {
// CHECK-LABEL: fn f(
// CHECK: debug a => [[a:_.*]];
// CHECK: debug b => [[b:_.*]];
// CHECK: [[b]] = copy [[a]];
// CHECK: [[a]] = const 5_usize;
// CHECK: [[a]] = copy [[b]];
// CHECK: [[c:_.*]] = copy [[a]]
// CHECK: id::<usize>(move [[c]])
let b = a;
a = 5;
a = b;

View file

@ -1,26 +0,0 @@
// MIR for `f` after CopyProp
fn f(_1: usize) -> usize {
debug a => _1;
let mut _0: usize;
let _2: usize;
let mut _3: usize;
let mut _4: usize;
scope 1 {
debug b => _2;
}
bb0: {
_2 = copy _1;
_1 = const 5_usize;
_1 = copy _2;
StorageLive(_4);
_4 = copy _1;
_0 = id::<usize>(move _4) -> [return: bb1, unwind unreachable];
}
bb1: {
StorageDead(_4);
return;
}
}

View file

@ -1,26 +0,0 @@
// MIR for `f` after CopyProp
fn f(_1: usize) -> usize {
debug a => _1;
let mut _0: usize;
let _2: usize;
let mut _3: usize;
let mut _4: usize;
scope 1 {
debug b => _2;
}
bb0: {
_2 = copy _1;
_1 = const 5_usize;
_1 = copy _2;
StorageLive(_4);
_4 = copy _1;
_0 = id::<usize>(move _4) -> [return: bb1, unwind continue];
}
bb1: {
StorageDead(_4);
return;
}
}

View file

@ -1,23 +0,0 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
// This is a copy of the `dead_stores_79191` test, except that we turn on DSE. This demonstrates
// that that pass enables this one to do more optimizations.
//@ test-mir-pass: CopyProp
//@ compile-flags: -Zmir-enable-passes=+DeadStoreElimination
fn id<T>(x: T) -> T {
x
}
// EMIT_MIR dead_stores_better.f.CopyProp.after.mir
pub fn f(mut a: usize) -> usize {
let b = a;
a = 5;
a = b;
id(a)
}
fn main() {
f(0);
}

View file

@ -1,9 +1,12 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
//@ test-mir-pass: CopyProp
// EMIT_MIR issue_107511.main.CopyProp.diff
fn main() {
// CHECK-LABEL: fn main(
// CHECK: debug i => [[i:_.*]];
// CHECK-NOT: StorageLive([[i]]);
// CHECK-NOT: StorageDead([[i]]);
let mut sum = 0;
let a = [0, 10, 20, 30];

View file

@ -1,10 +1,13 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
// Test that we do not move multiple times from the same local.
//@ test-mir-pass: CopyProp
// EMIT_MIR move_arg.f.CopyProp.diff
pub fn f<T: Copy>(a: T) {
// CHECK-LABEL: fn f(
// CHECK: debug a => [[a:_.*]];
// CHECK: debug b => [[a]];
// CHECK: g::<T>(copy [[a]], copy [[a]])
let b = a;
g(a, b);
}

View file

@ -1,4 +1,3 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
//@ test-mir-pass: CopyProp
@ -15,6 +14,15 @@ struct Foo(u8);
#[custom_mir(dialect = "runtime")]
fn f(a: Foo) -> bool {
// CHECK-LABEL: fn f(
// CHECK-SAME: [[a:_.*]]: Foo)
// CHECK: bb0: {
// CHECK-NOT: _2 = copy [[a]];
// CHECK-NOT: _3 = move (_2.0: u8);
// CHECK: [[c:_.*]] = copy ([[a]].0: u8);
// CHECK: _0 = opaque::<Foo>(copy [[a]])
// CHECK: bb1: {
// CHECK: _0 = opaque::<u8>(move [[c]])
mir! {
{
let b = a;

View file

@ -1,4 +1,3 @@
// skip-filecheck
//@ test-mir-pass: CopyProp
//
// This attempts to mutate `a` via a pointer derived from `addr_of!(a)`. That is UB
@ -18,6 +17,10 @@ use core::intrinsics::mir::*;
#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
fn f(c: bool) -> bool {
// CHECK-LABEL: fn f(
// CHECK: _2 = copy _1;
// CHECK-NOT: _3 = &raw const _1;
// CHECK: _3 = &raw const _2;
mir! {
{
let a = c;

View file

@ -1,4 +1,3 @@
// skip-filecheck
//@ test-mir-pass: CopyProp
#![feature(custom_mir, core_intrinsics)]
@ -8,6 +7,11 @@ use core::intrinsics::mir::*;
#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
fn f(c: bool) -> bool {
// CHECK-LABEL: fn f(
// CHECK: bb2: {
// CHECK: _2 = copy _3;
// CHECK: bb3: {
// CHECK: _0 = copy _2;
mir! {
let a: bool;
let b: bool;

View file

@ -1,4 +1,3 @@
// skip-filecheck
//@ test-mir-pass: CopyProp
// Verify that we do not ICE on partial initializations.
@ -9,6 +8,9 @@ use core::intrinsics::mir::*;
// EMIT_MIR partial_init.main.CopyProp.diff
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
pub fn main() {
// CHECK-LABEL: fn main(
// CHECK: let mut [[x:_.*]]: (isize,);
// CHECK: ([[x]].0: isize) = const 1_isize;
mir! (
let x: (isize, );
{

View file

@ -1,4 +1,3 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
// Check that CopyProp considers reborrows as not mutating the pointer.
//@ test-mir-pass: CopyProp
@ -8,6 +7,9 @@ fn opaque(_: impl Sized) {}
// EMIT_MIR reborrow.remut.CopyProp.diff
fn remut(mut x: u8) {
// CHECK-LABEL: fn remut(
// CHECK: debug a => [[a:_.*]];
// CHECK: debug c => [[a]];
let a = &mut x;
let b = &mut *a; //< this cannot mutate a.
let c = a; //< so `c` and `a` can be merged.
@ -16,6 +18,9 @@ fn remut(mut x: u8) {
// EMIT_MIR reborrow.reraw.CopyProp.diff
fn reraw(mut x: u8) {
// CHECK-LABEL: fn reraw(
// CHECK: debug a => [[a:_.*]];
// CHECK: debug c => [[a]];
let a = &mut x;
let b = &raw mut *a; //< this cannot mutate a.
let c = a; //< so `c` and `a` can be merged.
@ -24,6 +29,9 @@ fn reraw(mut x: u8) {
// EMIT_MIR reborrow.miraw.CopyProp.diff
fn miraw(mut x: u8) {
// CHECK-LABEL: fn miraw(
// CHECK: debug a => [[a:_.*]];
// CHECK: debug c => [[a]];
let a = &raw mut x;
let b = unsafe { &raw mut *a }; //< this cannot mutate a.
let c = a; //< so `c` and `a` can be merged.
@ -32,6 +40,9 @@ fn miraw(mut x: u8) {
// EMIT_MIR reborrow.demiraw.CopyProp.diff
fn demiraw(mut x: u8) {
// CHECK-LABEL: fn demiraw(
// CHECK: debug a => [[a:_.*]];
// CHECK: debug c => [[a]];
let a = &raw mut x;
let b = unsafe { &mut *a }; //< this cannot mutate a.
let c = a; //< so `c` and `a` can be merged.