summaryrefslogtreecommitdiffstats
path: root/tests/mir-opt/copy-prop
diff options
context:
space:
mode:
Diffstat (limited to 'tests/mir-opt/copy-prop')
-rw-r--r--tests/mir-opt/copy-prop/borrowed_local.f.CopyProp.diff34
-rw-r--r--tests/mir-opt/copy-prop/borrowed_local.rs39
-rw-r--r--tests/mir-opt/copy-prop/branch.foo.CopyProp.diff65
-rw-r--r--tests/mir-opt/copy-prop/branch.rs27
-rw-r--r--tests/mir-opt/copy-prop/copy_propagation_arg.arg_src.CopyProp.diff21
-rw-r--r--tests/mir-opt/copy-prop/copy_propagation_arg.bar.CopyProp.diff28
-rw-r--r--tests/mir-opt/copy-prop/copy_propagation_arg.baz.CopyProp.diff18
-rw-r--r--tests/mir-opt/copy-prop/copy_propagation_arg.foo.CopyProp.diff28
-rw-r--r--tests/mir-opt/copy-prop/copy_propagation_arg.rs40
-rw-r--r--tests/mir-opt/copy-prop/custom_move_arg.f.CopyProp.diff31
-rw-r--r--tests/mir-opt/copy-prop/custom_move_arg.rs32
-rw-r--r--tests/mir-opt/copy-prop/cycle.main.CopyProp.diff60
-rw-r--r--tests/mir-opt/copy-prop/cycle.rs15
-rw-r--r--tests/mir-opt/copy-prop/dead_stores_79191.f.CopyProp.after.mir29
-rw-r--r--tests/mir-opt/copy-prop/dead_stores_79191.rs17
-rw-r--r--tests/mir-opt/copy-prop/dead_stores_better.f.CopyProp.after.mir29
-rw-r--r--tests/mir-opt/copy-prop/dead_stores_better.rs21
-rw-r--r--tests/mir-opt/copy-prop/issue_107511.main.CopyProp.diff138
-rw-r--r--tests/mir-opt/copy-prop/issue_107511.rs13
-rw-r--r--tests/mir-opt/copy-prop/move_arg.f.CopyProp.diff40
-rw-r--r--tests/mir-opt/copy-prop/move_arg.rs15
-rw-r--r--tests/mir-opt/copy-prop/move_projection.f.CopyProp.diff31
-rw-r--r--tests/mir-opt/copy-prop/move_projection.rs34
-rw-r--r--tests/mir-opt/copy-prop/mutate_through_pointer.f.CopyProp.diff19
-rw-r--r--tests/mir-opt/copy-prop/mutate_through_pointer.rs32
-rw-r--r--tests/mir-opt/copy-prop/non_dominate.f.CopyProp.diff29
-rw-r--r--tests/mir-opt/copy-prop/non_dominate.rs26
27 files changed, 911 insertions, 0 deletions
diff --git a/tests/mir-opt/copy-prop/borrowed_local.f.CopyProp.diff b/tests/mir-opt/copy-prop/borrowed_local.f.CopyProp.diff
new file mode 100644
index 000000000..b183865a9
--- /dev/null
+++ b/tests/mir-opt/copy-prop/borrowed_local.f.CopyProp.diff
@@ -0,0 +1,34 @@
+- // MIR for `f` before CopyProp
++ // MIR for `f` after CopyProp
+
+ fn f() -> bool {
+ let mut _0: bool; // return place in scope 0 at $DIR/borrowed_local.rs:+0:11: +0:15
+ let mut _1: u8; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ let mut _2: &u8; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ let mut _3: u8; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ let mut _4: &u8; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+ bb0: {
+ _1 = const 5_u8; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _2 = &_1; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _3 = _1; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _4 = &_3; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _0 = cmp_ref(_2, _4) -> bb1; // scope 0 at $DIR/borrowed_local.rs:+8:13: +8:45
+ // mir::Constant
+ // + span: $DIR/borrowed_local.rs:23:29: 23:36
+ // + literal: Const { ty: for<'a, 'b> fn(&'a u8, &'b u8) -> bool {cmp_ref}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+- _0 = opaque::<u8>(_3) -> bb2; // scope 0 at $DIR/borrowed_local.rs:+12:13: +12:38
++ _0 = opaque::<u8>(_1) -> bb2; // scope 0 at $DIR/borrowed_local.rs:+12:13: +12:38
+ // mir::Constant
+ // + span: $DIR/borrowed_local.rs:27:28: 27:34
+ // + literal: Const { ty: fn(u8) -> bool {opaque::<u8>}, val: Value(<ZST>) }
+ }
+
+ bb2: {
+ return; // scope 0 at $DIR/borrowed_local.rs:+15:13: +15:21
+ }
+ }
+
diff --git a/tests/mir-opt/copy-prop/borrowed_local.rs b/tests/mir-opt/copy-prop/borrowed_local.rs
new file mode 100644
index 000000000..c4b980e2b
--- /dev/null
+++ b/tests/mir-opt/copy-prop/borrowed_local.rs
@@ -0,0 +1,39 @@
+// unit-test: CopyProp
+
+#![feature(custom_mir, core_intrinsics)]
+#![allow(unused_assignments)]
+extern crate core;
+use core::intrinsics::mir::*;
+
+fn opaque(_: impl Sized) -> bool { true }
+
+fn cmp_ref(a: &u8, b: &u8) -> bool {
+ std::ptr::eq(a as *const u8, b as *const u8)
+}
+
+#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
+fn f() -> bool {
+ mir!(
+ {
+ let a = 5_u8;
+ let r1 = &a;
+ let b = a;
+ // We cannot propagate the place `a`.
+ let r2 = &b;
+ Call(RET, next, cmp_ref(r1, r2))
+ }
+ next = {
+ // But we can propagate the value `a`.
+ Call(RET, ret, opaque(b))
+ }
+ ret = {
+ Return()
+ }
+ )
+}
+
+fn main() {
+ assert!(!f());
+}
+
+// EMIT_MIR borrowed_local.f.CopyProp.diff
diff --git a/tests/mir-opt/copy-prop/branch.foo.CopyProp.diff b/tests/mir-opt/copy-prop/branch.foo.CopyProp.diff
new file mode 100644
index 000000000..8b116532d
--- /dev/null
+++ b/tests/mir-opt/copy-prop/branch.foo.CopyProp.diff
@@ -0,0 +1,65 @@
+- // MIR for `foo` before CopyProp
++ // MIR for `foo` after CopyProp
+
+ fn foo() -> i32 {
+ let mut _0: i32; // return place in scope 0 at $DIR/branch.rs:+0:13: +0:16
+ let _1: i32; // in scope 0 at $DIR/branch.rs:+1:9: +1:10
+ let mut _3: bool; // in scope 0 at $DIR/branch.rs:+3:16: +3:22
+ let _4: i32; // in scope 0 at $DIR/branch.rs:+6:9: +6:14
+ scope 1 {
+ debug x => _1; // in scope 1 at $DIR/branch.rs:+1:9: +1:10
+ let _2: i32; // in scope 1 at $DIR/branch.rs:+3:9: +3:10
+ scope 2 {
+ debug y => _2; // in scope 2 at $DIR/branch.rs:+3:9: +3:10
+ }
+ }
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/branch.rs:+1:9: +1:10
+ _1 = val() -> bb1; // scope 0 at $DIR/branch.rs:+1:13: +1:18
+ // mir::Constant
+ // + span: $DIR/branch.rs:13:13: 13:16
+ // + literal: Const { ty: fn() -> i32 {val}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageLive(_2); // scope 1 at $DIR/branch.rs:+3:9: +3:10
+ StorageLive(_3); // scope 1 at $DIR/branch.rs:+3:16: +3:22
+ _3 = cond() -> bb2; // scope 1 at $DIR/branch.rs:+3:16: +3:22
+ // mir::Constant
+ // + span: $DIR/branch.rs:15:16: 15:20
+ // + literal: Const { ty: fn() -> bool {cond}, val: Value(<ZST>) }
+ }
+
+ bb2: {
+ switchInt(move _3) -> [0: bb4, otherwise: bb3]; // scope 1 at $DIR/branch.rs:+3:16: +3:22
+ }
+
+ bb3: {
+ _2 = _1; // scope 1 at $DIR/branch.rs:+4:9: +4:10
+ goto -> bb6; // scope 1 at $DIR/branch.rs:+3:13: +8:6
+ }
+
+ bb4: {
+ StorageLive(_4); // scope 1 at $DIR/branch.rs:+6:9: +6:14
+ _4 = val() -> bb5; // scope 1 at $DIR/branch.rs:+6:9: +6:14
+ // mir::Constant
+ // + span: $DIR/branch.rs:18:9: 18:12
+ // + literal: Const { ty: fn() -> i32 {val}, val: Value(<ZST>) }
+ }
+
+ bb5: {
+ StorageDead(_4); // scope 1 at $DIR/branch.rs:+6:14: +6:15
+ _2 = _1; // scope 1 at $DIR/branch.rs:+7:9: +7:10
+ goto -> bb6; // scope 1 at $DIR/branch.rs:+3:13: +8:6
+ }
+
+ bb6: {
+ StorageDead(_3); // scope 1 at $DIR/branch.rs:+8:5: +8:6
+ _0 = _2; // scope 2 at $DIR/branch.rs:+10:5: +10:6
+ StorageDead(_2); // scope 1 at $DIR/branch.rs:+11:1: +11:2
+ StorageDead(_1); // scope 0 at $DIR/branch.rs:+11:1: +11:2
+ return; // scope 0 at $DIR/branch.rs:+11:2: +11:2
+ }
+ }
+
diff --git a/tests/mir-opt/copy-prop/branch.rs b/tests/mir-opt/copy-prop/branch.rs
new file mode 100644
index 000000000..50b1e00fa
--- /dev/null
+++ b/tests/mir-opt/copy-prop/branch.rs
@@ -0,0 +1,27 @@
+//! Tests that we bail out when there are multiple assignments to the same local.
+// unit-test: CopyProp
+fn val() -> i32 {
+ 1
+}
+
+fn cond() -> bool {
+ true
+}
+
+// EMIT_MIR branch.foo.CopyProp.diff
+fn foo() -> i32 {
+ let x = val();
+
+ let y = if cond() {
+ x
+ } else {
+ val();
+ x
+ };
+
+ y
+}
+
+fn main() {
+ foo();
+}
diff --git a/tests/mir-opt/copy-prop/copy_propagation_arg.arg_src.CopyProp.diff b/tests/mir-opt/copy-prop/copy_propagation_arg.arg_src.CopyProp.diff
new file mode 100644
index 000000000..69acebf76
--- /dev/null
+++ b/tests/mir-opt/copy-prop/copy_propagation_arg.arg_src.CopyProp.diff
@@ -0,0 +1,21 @@
+- // MIR for `arg_src` before CopyProp
++ // MIR for `arg_src` after CopyProp
+
+ fn arg_src(_1: i32) -> i32 {
+ debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:+0:12: +0:17
+ let mut _0: i32; // return place in scope 0 at $DIR/copy_propagation_arg.rs:+0:27: +0:30
+ let _2: i32; // in scope 0 at $DIR/copy_propagation_arg.rs:+1:9: +1:10
+ scope 1 {
+ debug y => _2; // in scope 1 at $DIR/copy_propagation_arg.rs:+1:9: +1:10
+ }
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+1:9: +1:10
+ _2 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+1:13: +1:14
+ _1 = const 123_i32; // scope 1 at $DIR/copy_propagation_arg.rs:+2:5: +2:12
+ _0 = _2; // scope 1 at $DIR/copy_propagation_arg.rs:+3:5: +3:6
+ StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+4:1: +4:2
+ return; // scope 0 at $DIR/copy_propagation_arg.rs:+4:2: +4:2
+ }
+ }
+
diff --git a/tests/mir-opt/copy-prop/copy_propagation_arg.bar.CopyProp.diff b/tests/mir-opt/copy-prop/copy_propagation_arg.bar.CopyProp.diff
new file mode 100644
index 000000000..ac4e9a2bf
--- /dev/null
+++ b/tests/mir-opt/copy-prop/copy_propagation_arg.bar.CopyProp.diff
@@ -0,0 +1,28 @@
+- // MIR for `bar` before CopyProp
++ // MIR for `bar` after CopyProp
+
+ fn bar(_1: u8) -> () {
+ debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:+0:8: +0:13
+ let mut _0: (); // return place in scope 0 at $DIR/copy_propagation_arg.rs:+0:19: +0:19
+ let _2: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:+1:5: +1:13
+ let mut _3: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:+1:11: +1:12
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+1:5: +1:13
+ StorageLive(_3); // scope 0 at $DIR/copy_propagation_arg.rs:+1:11: +1:12
+ _3 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+1:11: +1:12
+ _2 = dummy(move _3) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:+1:5: +1:13
+ // mir::Constant
+ // + span: $DIR/copy_propagation_arg.rs:16:5: 16:10
+ // + literal: Const { ty: fn(u8) -> u8 {dummy}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_3); // scope 0 at $DIR/copy_propagation_arg.rs:+1:12: +1:13
+ StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+1:13: +1:14
+ _1 = const 5_u8; // scope 0 at $DIR/copy_propagation_arg.rs:+2:5: +2:10
+ _0 = const (); // scope 0 at $DIR/copy_propagation_arg.rs:+0:19: +3:2
+ return; // scope 0 at $DIR/copy_propagation_arg.rs:+3:2: +3:2
+ }
+ }
+
diff --git a/tests/mir-opt/copy-prop/copy_propagation_arg.baz.CopyProp.diff b/tests/mir-opt/copy-prop/copy_propagation_arg.baz.CopyProp.diff
new file mode 100644
index 000000000..7ab6ebb7d
--- /dev/null
+++ b/tests/mir-opt/copy-prop/copy_propagation_arg.baz.CopyProp.diff
@@ -0,0 +1,18 @@
+- // MIR for `baz` before CopyProp
++ // MIR for `baz` after CopyProp
+
+ fn baz(_1: i32) -> i32 {
+ debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:+0:8: +0:13
+ let mut _0: i32; // return place in scope 0 at $DIR/copy_propagation_arg.rs:+0:23: +0:26
+ let mut _2: i32; // in scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10
+ _2 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10
+ _1 = move _2; // scope 0 at $DIR/copy_propagation_arg.rs:+2:5: +2:10
+ StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10
+ _0 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+3:5: +3:6
+ return; // scope 0 at $DIR/copy_propagation_arg.rs:+4:2: +4:2
+ }
+ }
+
diff --git a/tests/mir-opt/copy-prop/copy_propagation_arg.foo.CopyProp.diff b/tests/mir-opt/copy-prop/copy_propagation_arg.foo.CopyProp.diff
new file mode 100644
index 000000000..0a3e985e7
--- /dev/null
+++ b/tests/mir-opt/copy-prop/copy_propagation_arg.foo.CopyProp.diff
@@ -0,0 +1,28 @@
+- // MIR for `foo` before CopyProp
++ // MIR for `foo` after CopyProp
+
+ fn foo(_1: u8) -> () {
+ debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:+0:8: +0:13
+ let mut _0: (); // return place in scope 0 at $DIR/copy_propagation_arg.rs:+0:19: +0:19
+ let mut _2: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:17
+ let mut _3: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:+2:15: +2:16
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:17
+ StorageLive(_3); // scope 0 at $DIR/copy_propagation_arg.rs:+2:15: +2:16
+ _3 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+2:15: +2:16
+ _2 = dummy(move _3) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:17
+ // mir::Constant
+ // + span: $DIR/copy_propagation_arg.rs:11:9: 11:14
+ // + literal: Const { ty: fn(u8) -> u8 {dummy}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_3); // scope 0 at $DIR/copy_propagation_arg.rs:+2:16: +2:17
+ _1 = move _2; // scope 0 at $DIR/copy_propagation_arg.rs:+2:5: +2:17
+ StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:16: +2:17
+ _0 = const (); // scope 0 at $DIR/copy_propagation_arg.rs:+0:19: +3:2
+ return; // scope 0 at $DIR/copy_propagation_arg.rs:+3:2: +3:2
+ }
+ }
+
diff --git a/tests/mir-opt/copy-prop/copy_propagation_arg.rs b/tests/mir-opt/copy-prop/copy_propagation_arg.rs
new file mode 100644
index 000000000..cc98985f1
--- /dev/null
+++ b/tests/mir-opt/copy-prop/copy_propagation_arg.rs
@@ -0,0 +1,40 @@
+// Check that CopyProp does not propagate an assignment to a function argument
+// (doing so can break usages of the original argument value)
+// unit-test: CopyProp
+fn dummy(x: u8) -> u8 {
+ x
+}
+
+// EMIT_MIR copy_propagation_arg.foo.CopyProp.diff
+fn foo(mut x: u8) {
+ // 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) {
+ 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
+ x = x;
+ x
+}
+
+// EMIT_MIR copy_propagation_arg.arg_src.CopyProp.diff
+fn arg_src(mut x: i32) -> i32 {
+ let y = x;
+ x = 123; // Don't propagate this assignment to `y`
+ y
+}
+
+fn main() {
+ // Make sure the function actually gets instantiated.
+ foo(0);
+ bar(0);
+ baz(0);
+ arg_src(0);
+}
diff --git a/tests/mir-opt/copy-prop/custom_move_arg.f.CopyProp.diff b/tests/mir-opt/copy-prop/custom_move_arg.f.CopyProp.diff
new file mode 100644
index 000000000..6ca73ffdd
--- /dev/null
+++ b/tests/mir-opt/copy-prop/custom_move_arg.f.CopyProp.diff
@@ -0,0 +1,31 @@
+- // MIR for `f` before CopyProp
++ // MIR for `f` after CopyProp
+
+ fn f(_1: NotCopy) -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/custom_move_arg.rs:+0:19: +0:19
+ let mut _2: NotCopy; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ let mut _3: NotCopy; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+ bb0: {
+- _2 = _1; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+- _0 = opaque::<NotCopy>(move _1) -> bb1; // scope 0 at $DIR/custom_move_arg.rs:+3:9: +3:41
++ _0 = opaque::<NotCopy>(_1) -> bb1; // scope 0 at $DIR/custom_move_arg.rs:+3:9: +3:41
+ // mir::Constant
+ // + span: $DIR/custom_move_arg.rs:15:24: 15:30
+ // + literal: Const { ty: fn(NotCopy) {opaque::<NotCopy>}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+- _3 = move _2; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+- _0 = opaque::<NotCopy>(_3) -> bb2; // scope 0 at $DIR/custom_move_arg.rs:+7:9: +7:35
++ _0 = opaque::<NotCopy>(_1) -> bb2; // scope 0 at $DIR/custom_move_arg.rs:+7:9: +7:35
+ // mir::Constant
+ // + span: $DIR/custom_move_arg.rs:19:24: 19:30
+ // + literal: Const { ty: fn(NotCopy) {opaque::<NotCopy>}, val: Value(<ZST>) }
+ }
+
+ bb2: {
+ return; // scope 0 at $DIR/custom_move_arg.rs:+10:9: +10:17
+ }
+ }
+
diff --git a/tests/mir-opt/copy-prop/custom_move_arg.rs b/tests/mir-opt/copy-prop/custom_move_arg.rs
new file mode 100644
index 000000000..4a591146e
--- /dev/null
+++ b/tests/mir-opt/copy-prop/custom_move_arg.rs
@@ -0,0 +1,32 @@
+// unit-test: CopyProp
+
+#![feature(custom_mir, core_intrinsics)]
+#![allow(unused_assignments)]
+extern crate core;
+use core::intrinsics::mir::*;
+
+struct NotCopy(bool);
+
+// EMIT_MIR custom_move_arg.f.CopyProp.diff
+#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
+fn f(_1: NotCopy) {
+ mir!({
+ let _2 = _1;
+ Call(RET, bb1, opaque(Move(_1)))
+ }
+ bb1 = {
+ let _3 = Move(_2);
+ Call(RET, bb2, opaque(_3))
+ }
+ bb2 = {
+ Return()
+ })
+}
+
+#[inline(never)]
+fn opaque<T>(_t: T) {}
+
+fn main() {
+ f(NotCopy(true));
+ println!("hi");
+}
diff --git a/tests/mir-opt/copy-prop/cycle.main.CopyProp.diff b/tests/mir-opt/copy-prop/cycle.main.CopyProp.diff
new file mode 100644
index 000000000..3e61869e8
--- /dev/null
+++ b/tests/mir-opt/copy-prop/cycle.main.CopyProp.diff
@@ -0,0 +1,60 @@
+- // MIR for `main` before CopyProp
++ // MIR for `main` after CopyProp
+
+ fn main() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/cycle.rs:+0:11: +0:11
+ let mut _1: i32; // in scope 0 at $DIR/cycle.rs:+1:9: +1:14
+ let mut _4: i32; // in scope 0 at $DIR/cycle.rs:+4:9: +4:10
+ let _5: (); // in scope 0 at $DIR/cycle.rs:+6:5: +6:12
+ let mut _6: i32; // in scope 0 at $DIR/cycle.rs:+6:10: +6:11
+ scope 1 {
+ debug x => _1; // in scope 1 at $DIR/cycle.rs:+1:9: +1:14
+ let _2: i32; // in scope 1 at $DIR/cycle.rs:+2:9: +2:10
+ scope 2 {
+ debug y => _2; // in scope 2 at $DIR/cycle.rs:+2:9: +2:10
+ let _3: i32; // in scope 2 at $DIR/cycle.rs:+3:9: +3:10
+ scope 3 {
+- debug z => _3; // in scope 3 at $DIR/cycle.rs:+3:9: +3:10
++ debug z => _2; // in scope 3 at $DIR/cycle.rs:+3:9: +3:10
+ }
+ }
+ }
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/cycle.rs:+1:9: +1:14
+ _1 = val() -> bb1; // scope 0 at $DIR/cycle.rs:+1:17: +1:22
+ // mir::Constant
+ // + span: $DIR/cycle.rs:9:17: 9:20
+ // + literal: Const { ty: fn() -> i32 {val}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+- StorageLive(_2); // scope 1 at $DIR/cycle.rs:+2:9: +2:10
+ _2 = _1; // scope 1 at $DIR/cycle.rs:+2:13: +2:14
+- StorageLive(_3); // scope 2 at $DIR/cycle.rs:+3:9: +3:10
+- _3 = _2; // scope 2 at $DIR/cycle.rs:+3:13: +3:14
+- StorageLive(_4); // scope 3 at $DIR/cycle.rs:+4:9: +4:10
+- _4 = _3; // scope 3 at $DIR/cycle.rs:+4:9: +4:10
+- _1 = move _4; // scope 3 at $DIR/cycle.rs:+4:5: +4:10
+- StorageDead(_4); // scope 3 at $DIR/cycle.rs:+4:9: +4:10
++ _1 = _2; // scope 3 at $DIR/cycle.rs:+4:5: +4:10
+ StorageLive(_5); // scope 3 at $DIR/cycle.rs:+6:5: +6:12
+ StorageLive(_6); // scope 3 at $DIR/cycle.rs:+6:10: +6:11
+ _6 = _1; // scope 3 at $DIR/cycle.rs:+6:10: +6:11
+ _5 = std::mem::drop::<i32>(move _6) -> bb2; // scope 3 at $DIR/cycle.rs:+6:5: +6:12
+ // mir::Constant
+ // + span: $DIR/cycle.rs:14:5: 14:9
+ // + literal: Const { ty: fn(i32) {std::mem::drop::<i32>}, val: Value(<ZST>) }
+ }
+
+ bb2: {
+ StorageDead(_6); // scope 3 at $DIR/cycle.rs:+6:11: +6:12
+ StorageDead(_5); // scope 3 at $DIR/cycle.rs:+6:12: +6:13
+ _0 = const (); // scope 0 at $DIR/cycle.rs:+0:11: +7:2
+- StorageDead(_3); // scope 2 at $DIR/cycle.rs:+7:1: +7:2
+- StorageDead(_2); // scope 1 at $DIR/cycle.rs:+7:1: +7:2
+ StorageDead(_1); // scope 0 at $DIR/cycle.rs:+7:1: +7:2
+ return; // scope 0 at $DIR/cycle.rs:+7:2: +7:2
+ }
+ }
+
diff --git a/tests/mir-opt/copy-prop/cycle.rs b/tests/mir-opt/copy-prop/cycle.rs
new file mode 100644
index 000000000..b74c39726
--- /dev/null
+++ b/tests/mir-opt/copy-prop/cycle.rs
@@ -0,0 +1,15 @@
+//! Tests that cyclic assignments don't hang CopyProp, and result in reasonable code.
+// unit-test: CopyProp
+fn val() -> i32 {
+ 1
+}
+
+// EMIT_MIR cycle.main.CopyProp.diff
+fn main() {
+ let mut x = val();
+ let y = x;
+ let z = y;
+ x = z;
+
+ drop(x);
+}
diff --git a/tests/mir-opt/copy-prop/dead_stores_79191.f.CopyProp.after.mir b/tests/mir-opt/copy-prop/dead_stores_79191.f.CopyProp.after.mir
new file mode 100644
index 000000000..d48b04e2d
--- /dev/null
+++ b/tests/mir-opt/copy-prop/dead_stores_79191.f.CopyProp.after.mir
@@ -0,0 +1,29 @@
+// MIR for `f` after CopyProp
+
+fn f(_1: usize) -> usize {
+ debug a => _1; // in scope 0 at $DIR/dead_stores_79191.rs:+0:6: +0:11
+ let mut _0: usize; // return place in scope 0 at $DIR/dead_stores_79191.rs:+0:23: +0:28
+ let _2: usize; // in scope 0 at $DIR/dead_stores_79191.rs:+1:9: +1:10
+ let mut _3: usize; // in scope 0 at $DIR/dead_stores_79191.rs:+3:9: +3:10
+ let mut _4: usize; // in scope 0 at $DIR/dead_stores_79191.rs:+4:8: +4:9
+ scope 1 {
+ debug b => _2; // in scope 1 at $DIR/dead_stores_79191.rs:+1:9: +1:10
+ }
+
+ bb0: {
+ _2 = _1; // scope 0 at $DIR/dead_stores_79191.rs:+1:13: +1:14
+ _1 = const 5_usize; // scope 1 at $DIR/dead_stores_79191.rs:+2:5: +2:10
+ _1 = _2; // scope 1 at $DIR/dead_stores_79191.rs:+3:5: +3:10
+ StorageLive(_4); // scope 1 at $DIR/dead_stores_79191.rs:+4:8: +4:9
+ _4 = _1; // scope 1 at $DIR/dead_stores_79191.rs:+4:8: +4:9
+ _0 = id::<usize>(move _4) -> bb1; // scope 1 at $DIR/dead_stores_79191.rs:+4:5: +4:10
+ // mir::Constant
+ // + span: $DIR/dead_stores_79191.rs:12:5: 12:7
+ // + literal: Const { ty: fn(usize) -> usize {id::<usize>}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_4); // scope 1 at $DIR/dead_stores_79191.rs:+4:9: +4:10
+ return; // scope 0 at $DIR/dead_stores_79191.rs:+5:2: +5:2
+ }
+}
diff --git a/tests/mir-opt/copy-prop/dead_stores_79191.rs b/tests/mir-opt/copy-prop/dead_stores_79191.rs
new file mode 100644
index 000000000..e3493b8b7
--- /dev/null
+++ b/tests/mir-opt/copy-prop/dead_stores_79191.rs
@@ -0,0 +1,17 @@
+// unit-test: CopyProp
+
+fn id<T>(x: T) -> T {
+ x
+}
+
+// EMIT_MIR dead_stores_79191.f.CopyProp.after.mir
+fn f(mut a: usize) -> usize {
+ let b = a;
+ a = 5;
+ a = b;
+ id(a)
+}
+
+fn main() {
+ f(0);
+}
diff --git a/tests/mir-opt/copy-prop/dead_stores_better.f.CopyProp.after.mir b/tests/mir-opt/copy-prop/dead_stores_better.f.CopyProp.after.mir
new file mode 100644
index 000000000..727791f50
--- /dev/null
+++ b/tests/mir-opt/copy-prop/dead_stores_better.f.CopyProp.after.mir
@@ -0,0 +1,29 @@
+// MIR for `f` after CopyProp
+
+fn f(_1: usize) -> usize {
+ debug a => _1; // in scope 0 at $DIR/dead_stores_better.rs:+0:10: +0:15
+ let mut _0: usize; // return place in scope 0 at $DIR/dead_stores_better.rs:+0:27: +0:32
+ let _2: usize; // in scope 0 at $DIR/dead_stores_better.rs:+1:9: +1:10
+ let mut _3: usize; // in scope 0 at $DIR/dead_stores_better.rs:+3:9: +3:10
+ let mut _4: usize; // in scope 0 at $DIR/dead_stores_better.rs:+4:8: +4:9
+ scope 1 {
+ debug b => _2; // in scope 1 at $DIR/dead_stores_better.rs:+1:9: +1:10
+ }
+
+ bb0: {
+ _2 = _1; // scope 0 at $DIR/dead_stores_better.rs:+1:13: +1:14
+ _1 = const 5_usize; // scope 1 at $DIR/dead_stores_better.rs:+2:5: +2:10
+ _1 = _2; // scope 1 at $DIR/dead_stores_better.rs:+3:5: +3:10
+ StorageLive(_4); // scope 1 at $DIR/dead_stores_better.rs:+4:8: +4:9
+ _4 = _1; // scope 1 at $DIR/dead_stores_better.rs:+4:8: +4:9
+ _0 = id::<usize>(move _4) -> bb1; // scope 1 at $DIR/dead_stores_better.rs:+4:5: +4:10
+ // mir::Constant
+ // + span: $DIR/dead_stores_better.rs:16:5: 16:7
+ // + literal: Const { ty: fn(usize) -> usize {id::<usize>}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_4); // scope 1 at $DIR/dead_stores_better.rs:+4:9: +4:10
+ return; // scope 0 at $DIR/dead_stores_better.rs:+5:2: +5:2
+ }
+}
diff --git a/tests/mir-opt/copy-prop/dead_stores_better.rs b/tests/mir-opt/copy-prop/dead_stores_better.rs
new file mode 100644
index 000000000..8465b3c98
--- /dev/null
+++ b/tests/mir-opt/copy-prop/dead_stores_better.rs
@@ -0,0 +1,21 @@
+// 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.
+
+// unit-test: 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);
+}
diff --git a/tests/mir-opt/copy-prop/issue_107511.main.CopyProp.diff b/tests/mir-opt/copy-prop/issue_107511.main.CopyProp.diff
new file mode 100644
index 000000000..97d0a01e0
--- /dev/null
+++ b/tests/mir-opt/copy-prop/issue_107511.main.CopyProp.diff
@@ -0,0 +1,138 @@
+- // MIR for `main` before CopyProp
++ // MIR for `main` after CopyProp
+
+ fn main() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/issue_107511.rs:+0:11: +0:11
+ let mut _1: i32; // in scope 0 at $DIR/issue_107511.rs:+1:9: +1:16
+ let mut _3: std::ops::Range<usize>; // in scope 0 at $DIR/issue_107511.rs:+6:14: +6:24
+ let mut _4: std::ops::Range<usize>; // in scope 0 at $DIR/issue_107511.rs:+6:14: +6:24
+ let mut _5: usize; // in scope 0 at $DIR/issue_107511.rs:+6:17: +6:24
+ let mut _6: &[i32]; // in scope 0 at $DIR/issue_107511.rs:+6:17: +6:24
+ let mut _7: &[i32; 4]; // in scope 0 at $DIR/issue_107511.rs:+6:17: +6:24
+ let mut _9: (); // in scope 0 at $DIR/issue_107511.rs:+0:1: +9:2
+ let _10: (); // in scope 0 at $DIR/issue_107511.rs:+6:14: +6:24
+ let mut _11: std::option::Option<usize>; // in scope 0 at $DIR/issue_107511.rs:+6:14: +6:24
+ let mut _12: &mut std::ops::Range<usize>; // in scope 0 at $DIR/issue_107511.rs:+6:14: +6:24
+ let mut _13: &mut std::ops::Range<usize>; // in scope 0 at $DIR/issue_107511.rs:+6:14: +6:24
+ let mut _14: isize; // in scope 0 at $DIR/issue_107511.rs:+6:5: +8:6
+ let mut _15: !; // in scope 0 at $DIR/issue_107511.rs:+6:5: +8:6
+ let mut _17: i32; // in scope 0 at $DIR/issue_107511.rs:+7:16: +7:20
+ let _18: usize; // in scope 0 at $DIR/issue_107511.rs:+7:18: +7:19
+ let mut _19: usize; // in scope 0 at $DIR/issue_107511.rs:+7:16: +7:20
+ let mut _20: bool; // in scope 0 at $DIR/issue_107511.rs:+7:16: +7:20
+ scope 1 {
+ debug sum => _1; // in scope 1 at $DIR/issue_107511.rs:+1:9: +1:16
+ let _2: [i32; 4]; // in scope 1 at $DIR/issue_107511.rs:+2:9: +2:10
+ scope 2 {
+ debug a => _2; // in scope 2 at $DIR/issue_107511.rs:+2:9: +2:10
+ let mut _8: std::ops::Range<usize>; // in scope 2 at $DIR/issue_107511.rs:+6:14: +6:24
+ scope 3 {
+ debug iter => _8; // in scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ let _16: usize; // in scope 3 at $DIR/issue_107511.rs:+6:9: +6:10
+ scope 4 {
+ debug i => _16; // in scope 4 at $DIR/issue_107511.rs:+6:9: +6:10
+ }
+ }
+ }
+ }
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/issue_107511.rs:+1:9: +1:16
+ _1 = const 0_i32; // scope 0 at $DIR/issue_107511.rs:+1:19: +1:20
+ StorageLive(_2); // scope 1 at $DIR/issue_107511.rs:+2:9: +2:10
+ _2 = [const 0_i32, const 10_i32, const 20_i32, const 30_i32]; // scope 1 at $DIR/issue_107511.rs:+2:13: +2:28
+ StorageLive(_3); // scope 2 at $DIR/issue_107511.rs:+6:14: +6:24
+ StorageLive(_4); // scope 2 at $DIR/issue_107511.rs:+6:14: +6:24
+ StorageLive(_5); // scope 2 at $DIR/issue_107511.rs:+6:17: +6:24
+ StorageLive(_6); // scope 2 at $DIR/issue_107511.rs:+6:17: +6:24
+ StorageLive(_7); // scope 2 at $DIR/issue_107511.rs:+6:17: +6:24
+ _7 = &_2; // scope 2 at $DIR/issue_107511.rs:+6:17: +6:24
+ _6 = move _7 as &[i32] (Pointer(Unsize)); // scope 2 at $DIR/issue_107511.rs:+6:17: +6:24
+ StorageDead(_7); // scope 2 at $DIR/issue_107511.rs:+6:17: +6:18
+ _5 = core::slice::<impl [i32]>::len(move _6) -> bb1; // scope 2 at $DIR/issue_107511.rs:+6:17: +6:24
+ // mir::Constant
+ // + span: $DIR/issue_107511.rs:10:19: 10:22
+ // + literal: Const { ty: for<'a> fn(&'a [i32]) -> usize {core::slice::<impl [i32]>::len}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_6); // scope 2 at $DIR/issue_107511.rs:+6:23: +6:24
+ _4 = std::ops::Range::<usize> { start: const 0_usize, end: move _5 }; // scope 2 at $DIR/issue_107511.rs:+6:14: +6:24
+ StorageDead(_5); // scope 2 at $DIR/issue_107511.rs:+6:23: +6:24
+ _3 = <std::ops::Range<usize> as IntoIterator>::into_iter(move _4) -> bb2; // scope 2 at $DIR/issue_107511.rs:+6:14: +6:24
+ // mir::Constant
+ // + span: $DIR/issue_107511.rs:10:14: 10:24
+ // + literal: Const { ty: fn(std::ops::Range<usize>) -> <std::ops::Range<usize> as IntoIterator>::IntoIter {<std::ops::Range<usize> as IntoIterator>::into_iter}, val: Value(<ZST>) }
+ }
+
+ bb2: {
+ StorageDead(_4); // scope 2 at $DIR/issue_107511.rs:+6:23: +6:24
+ StorageLive(_8); // scope 2 at $DIR/issue_107511.rs:+6:14: +6:24
+ _8 = move _3; // scope 2 at $DIR/issue_107511.rs:+6:14: +6:24
+ goto -> bb3; // scope 3 at $DIR/issue_107511.rs:+6:5: +8:6
+ }
+
+ bb3: {
+- StorageLive(_10); // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ StorageLive(_11); // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ StorageLive(_12); // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ StorageLive(_13); // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ _13 = &mut _8; // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ _12 = &mut (*_13); // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ _11 = <std::ops::Range<usize> as Iterator>::next(move _12) -> bb4; // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ // mir::Constant
+ // + span: $DIR/issue_107511.rs:10:14: 10:24
+ // + literal: Const { ty: for<'a> fn(&'a mut std::ops::Range<usize>) -> Option<<std::ops::Range<usize> as Iterator>::Item> {<std::ops::Range<usize> as Iterator>::next}, val: Value(<ZST>) }
+ }
+
+ bb4: {
+ StorageDead(_12); // scope 3 at $DIR/issue_107511.rs:+6:23: +6:24
+ _14 = discriminant(_11); // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ switchInt(move _14) -> [0: bb7, 1: bb5, otherwise: bb6]; // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ }
+
+ bb5: {
+- StorageLive(_16); // scope 3 at $DIR/issue_107511.rs:+6:9: +6:10
+ _16 = ((_11 as Some).0: usize); // scope 3 at $DIR/issue_107511.rs:+6:9: +6:10
+ StorageLive(_17); // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
+- StorageLive(_18); // scope 4 at $DIR/issue_107511.rs:+7:18: +7:19
+- _18 = _16; // scope 4 at $DIR/issue_107511.rs:+7:18: +7:19
+ _19 = Len(_2); // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
+- _20 = Lt(_18, _19); // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
+- assert(move _20, "index out of bounds: the length is {} but the index is {}", move _19, _18) -> bb8; // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
++ _20 = Lt(_16, _19); // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
++ assert(move _20, "index out of bounds: the length is {} but the index is {}", move _19, _16) -> bb8; // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
+ }
+
+ bb6: {
+ unreachable; // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ }
+
+ bb7: {
+ _0 = const (); // scope 3 at $DIR/issue_107511.rs:+6:5: +8:6
+ StorageDead(_13); // scope 3 at $DIR/issue_107511.rs:+8:5: +8:6
+ StorageDead(_11); // scope 3 at $DIR/issue_107511.rs:+8:5: +8:6
+- StorageDead(_10); // scope 3 at $DIR/issue_107511.rs:+8:5: +8:6
+ StorageDead(_8); // scope 2 at $DIR/issue_107511.rs:+8:5: +8:6
+ StorageDead(_3); // scope 2 at $DIR/issue_107511.rs:+8:5: +8:6
+ StorageDead(_2); // scope 1 at $DIR/issue_107511.rs:+9:1: +9:2
+ StorageDead(_1); // scope 0 at $DIR/issue_107511.rs:+9:1: +9:2
+ return; // scope 0 at $DIR/issue_107511.rs:+9:2: +9:2
+ }
+
+ bb8: {
+- _17 = _2[_18]; // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
++ _17 = _2[_16]; // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
+ _1 = Add(_1, move _17); // scope 4 at $DIR/issue_107511.rs:+7:9: +7:20
+ StorageDead(_17); // scope 4 at $DIR/issue_107511.rs:+7:19: +7:20
+- StorageDead(_18); // scope 4 at $DIR/issue_107511.rs:+7:20: +7:21
+- _10 = const (); // scope 4 at $DIR/issue_107511.rs:+6:25: +8:6
+- StorageDead(_16); // scope 3 at $DIR/issue_107511.rs:+8:5: +8:6
+ StorageDead(_13); // scope 3 at $DIR/issue_107511.rs:+8:5: +8:6
+ StorageDead(_11); // scope 3 at $DIR/issue_107511.rs:+8:5: +8:6
+- StorageDead(_10); // scope 3 at $DIR/issue_107511.rs:+8:5: +8:6
+- _9 = const (); // scope 3 at $DIR/issue_107511.rs:+6:5: +8:6
+ goto -> bb3; // scope 3 at $DIR/issue_107511.rs:+6:5: +8:6
+ }
+ }
+
diff --git a/tests/mir-opt/copy-prop/issue_107511.rs b/tests/mir-opt/copy-prop/issue_107511.rs
new file mode 100644
index 000000000..d593f2872
--- /dev/null
+++ b/tests/mir-opt/copy-prop/issue_107511.rs
@@ -0,0 +1,13 @@
+// unit-test: CopyProp
+
+// EMIT_MIR issue_107511.main.CopyProp.diff
+fn main() {
+ let mut sum = 0;
+ let a = [0, 10, 20, 30];
+
+ // `i` is assigned in a loop. Only removing its `StorageDead` would mean that
+ // execution sees repeated `StorageLive`. This would be UB.
+ for i in 0..a.len() {
+ sum += a[i];
+ }
+}
diff --git a/tests/mir-opt/copy-prop/move_arg.f.CopyProp.diff b/tests/mir-opt/copy-prop/move_arg.f.CopyProp.diff
new file mode 100644
index 000000000..d76bf1cfe
--- /dev/null
+++ b/tests/mir-opt/copy-prop/move_arg.f.CopyProp.diff
@@ -0,0 +1,40 @@
+- // MIR for `f` before CopyProp
++ // MIR for `f` after CopyProp
+
+ fn f(_1: T) -> () {
+ debug a => _1; // in scope 0 at $DIR/move_arg.rs:+0:19: +0:20
+ let mut _0: (); // return place in scope 0 at $DIR/move_arg.rs:+0:25: +0:25
+ let _2: T; // in scope 0 at $DIR/move_arg.rs:+1:9: +1:10
+ let _3: (); // in scope 0 at $DIR/move_arg.rs:+2:5: +2:12
+ let mut _4: T; // in scope 0 at $DIR/move_arg.rs:+2:7: +2:8
+ let mut _5: T; // in scope 0 at $DIR/move_arg.rs:+2:10: +2:11
+ scope 1 {
+- debug b => _2; // in scope 1 at $DIR/move_arg.rs:+1:9: +1:10
++ debug b => _1; // in scope 1 at $DIR/move_arg.rs:+1:9: +1:10
+ }
+
+ bb0: {
+- StorageLive(_2); // scope 0 at $DIR/move_arg.rs:+1:9: +1:10
+- _2 = _1; // scope 0 at $DIR/move_arg.rs:+1:13: +1:14
+ StorageLive(_3); // scope 1 at $DIR/move_arg.rs:+2:5: +2:12
+- StorageLive(_4); // scope 1 at $DIR/move_arg.rs:+2:7: +2:8
+- _4 = _1; // scope 1 at $DIR/move_arg.rs:+2:7: +2:8
+- StorageLive(_5); // scope 1 at $DIR/move_arg.rs:+2:10: +2:11
+- _5 = _2; // scope 1 at $DIR/move_arg.rs:+2:10: +2:11
+- _3 = g::<T>(move _4, move _5) -> bb1; // scope 1 at $DIR/move_arg.rs:+2:5: +2:12
++ _3 = g::<T>(_1, _1) -> bb1; // scope 1 at $DIR/move_arg.rs:+2:5: +2:12
+ // mir::Constant
+ // + span: $DIR/move_arg.rs:7:5: 7:6
+ // + literal: Const { ty: fn(T, T) {g::<T>}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+- StorageDead(_5); // scope 1 at $DIR/move_arg.rs:+2:11: +2:12
+- StorageDead(_4); // scope 1 at $DIR/move_arg.rs:+2:11: +2:12
+ StorageDead(_3); // scope 1 at $DIR/move_arg.rs:+2:12: +2:13
+ _0 = const (); // scope 0 at $DIR/move_arg.rs:+0:25: +3:2
+- StorageDead(_2); // scope 0 at $DIR/move_arg.rs:+3:1: +3:2
+ return; // scope 0 at $DIR/move_arg.rs:+3:2: +3:2
+ }
+ }
+
diff --git a/tests/mir-opt/copy-prop/move_arg.rs b/tests/mir-opt/copy-prop/move_arg.rs
new file mode 100644
index 000000000..40ae1d8f4
--- /dev/null
+++ b/tests/mir-opt/copy-prop/move_arg.rs
@@ -0,0 +1,15 @@
+// Test that we do not move multiple times from the same local.
+// unit-test: CopyProp
+
+// EMIT_MIR move_arg.f.CopyProp.diff
+pub fn f<T: Copy>(a: T) {
+ let b = a;
+ g(a, b);
+}
+
+#[inline(never)]
+pub fn g<T: Copy>(_: T, _: T) {}
+
+fn main() {
+ f(5)
+}
diff --git a/tests/mir-opt/copy-prop/move_projection.f.CopyProp.diff b/tests/mir-opt/copy-prop/move_projection.f.CopyProp.diff
new file mode 100644
index 000000000..02308beb8
--- /dev/null
+++ b/tests/mir-opt/copy-prop/move_projection.f.CopyProp.diff
@@ -0,0 +1,31 @@
+- // MIR for `f` before CopyProp
++ // MIR for `f` after CopyProp
+
+ fn f(_1: Foo) -> bool {
+ let mut _0: bool; // return place in scope 0 at $DIR/move_projection.rs:+0:17: +0:21
+ let mut _2: Foo; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ let mut _3: u8; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+ bb0: {
+- _2 = _1; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+- _3 = move (_2.0: u8); // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+- _0 = opaque::<Foo>(move _1) -> bb1; // scope 0 at $DIR/move_projection.rs:+6:13: +6:44
++ _3 = (_1.0: u8); // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
++ _0 = opaque::<Foo>(_1) -> bb1; // scope 0 at $DIR/move_projection.rs:+6:13: +6:44
+ // mir::Constant
+ // + span: $DIR/move_projection.rs:19:28: 19:34
+ // + literal: Const { ty: fn(Foo) -> bool {opaque::<Foo>}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ _0 = opaque::<u8>(move _3) -> bb2; // scope 0 at $DIR/move_projection.rs:+9:13: +9:44
+ // mir::Constant
+ // + span: $DIR/move_projection.rs:22:28: 22:34
+ // + literal: Const { ty: fn(u8) -> bool {opaque::<u8>}, val: Value(<ZST>) }
+ }
+
+ bb2: {
+ return; // scope 0 at $DIR/move_projection.rs:+12:13: +12:21
+ }
+ }
+
diff --git a/tests/mir-opt/copy-prop/move_projection.rs b/tests/mir-opt/copy-prop/move_projection.rs
new file mode 100644
index 000000000..2a1bbae99
--- /dev/null
+++ b/tests/mir-opt/copy-prop/move_projection.rs
@@ -0,0 +1,34 @@
+// unit-test: CopyProp
+
+#![feature(custom_mir, core_intrinsics)]
+#![allow(unused_assignments)]
+extern crate core;
+use core::intrinsics::mir::*;
+
+fn opaque(_: impl Sized) -> bool { true }
+
+struct Foo(u8);
+
+#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
+fn f(a: Foo) -> bool {
+ mir!(
+ {
+ let b = a;
+ // This is a move out of a copy, so must become a copy of `a.0`.
+ let c = Move(b.0);
+ Call(RET, bb1, opaque(Move(a)))
+ }
+ bb1 = {
+ Call(RET, ret, opaque(Move(c)))
+ }
+ ret = {
+ Return()
+ }
+ )
+}
+
+fn main() {
+ assert!(f(Foo(0)));
+}
+
+// EMIT_MIR move_projection.f.CopyProp.diff
diff --git a/tests/mir-opt/copy-prop/mutate_through_pointer.f.CopyProp.diff b/tests/mir-opt/copy-prop/mutate_through_pointer.f.CopyProp.diff
new file mode 100644
index 000000000..61fdd6f8c
--- /dev/null
+++ b/tests/mir-opt/copy-prop/mutate_through_pointer.f.CopyProp.diff
@@ -0,0 +1,19 @@
+- // MIR for `f` before CopyProp
++ // MIR for `f` after CopyProp
+
+ fn f(_1: bool) -> bool {
+ let mut _0: bool; // return place in scope 0 at $DIR/mutate_through_pointer.rs:+0:18: +0:22
+ let mut _2: bool; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ let mut _3: *const bool; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ let mut _4: *mut bool; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+ bb0: {
+ _2 = _1; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _3 = &raw const _2; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _4 = &raw mut (*_3); // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ (*_4) = const false; // scope 0 at $DIR/mutate_through_pointer.rs:+5:9: +5:20
+ _0 = _1; // scope 0 at $DIR/mutate_through_pointer.rs:+6:9: +6:16
+ return; // scope 0 at $DIR/mutate_through_pointer.rs:+7:9: +7:17
+ }
+ }
+
diff --git a/tests/mir-opt/copy-prop/mutate_through_pointer.rs b/tests/mir-opt/copy-prop/mutate_through_pointer.rs
new file mode 100644
index 000000000..da142e339
--- /dev/null
+++ b/tests/mir-opt/copy-prop/mutate_through_pointer.rs
@@ -0,0 +1,32 @@
+// This attempts to mutate `a` via a pointer derived from `addr_of!(a)`. That is UB
+// according to Miri. However, the decision to make this UB - and to allow
+// rustc to rely on that fact for the purpose of optimizations - has not been
+// finalized.
+//
+// As such, we include this test to ensure that copy prop does not rely on that
+// fact. Specifically, if `addr_of!(a)` could not be used to modify a, it would
+// be correct for CopyProp to replace all occurrences of `a` with `c` - but that
+// would cause `f(true)` to output `false` instead of `true`.
+
+#![feature(custom_mir, core_intrinsics)]
+#![allow(unused_assignments)]
+extern crate core;
+use core::intrinsics::mir::*;
+
+#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
+fn f(c: bool) -> bool {
+ mir!({
+ let a = c;
+ let p = core::ptr::addr_of!(a);
+ let p2 = core::ptr::addr_of_mut!(*p);
+ *p2 = false;
+ RET = c;
+ Return()
+ })
+}
+
+fn main() {
+ assert_eq!(true, f(true));
+}
+
+// EMIT_MIR mutate_through_pointer.f.CopyProp.diff
diff --git a/tests/mir-opt/copy-prop/non_dominate.f.CopyProp.diff b/tests/mir-opt/copy-prop/non_dominate.f.CopyProp.diff
new file mode 100644
index 000000000..9760fd374
--- /dev/null
+++ b/tests/mir-opt/copy-prop/non_dominate.f.CopyProp.diff
@@ -0,0 +1,29 @@
+- // MIR for `f` before CopyProp
++ // MIR for `f` after CopyProp
+
+ fn f(_1: bool) -> bool {
+ let mut _0: bool; // return place in scope 0 at $DIR/non_dominate.rs:+0:18: +0:22
+ let mut _2: bool; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ let mut _3: bool; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+ bb0: {
+ goto -> bb1; // scope 0 at $DIR/non_dominate.rs:+4:11: +4:20
+ }
+
+ bb1: {
+ _3 = _1; // scope 0 at $DIR/non_dominate.rs:+5:17: +5:22
+ switchInt(_3) -> [0: bb3, otherwise: bb2]; // scope 0 at $DIR/non_dominate.rs:+5:24: +5:58
+ }
+
+ bb2: {
+ _2 = _3; // scope 0 at $DIR/non_dominate.rs:+8:17: +8:22
+ _1 = const false; // scope 0 at $DIR/non_dominate.rs:+8:24: +8:33
+ goto -> bb1; // scope 0 at $DIR/non_dominate.rs:+8:35: +8:44
+ }
+
+ bb3: {
+ _0 = _2; // scope 0 at $DIR/non_dominate.rs:+9:17: +9:24
+ return; // scope 0 at $DIR/non_dominate.rs:+9:26: +9:34
+ }
+ }
+
diff --git a/tests/mir-opt/copy-prop/non_dominate.rs b/tests/mir-opt/copy-prop/non_dominate.rs
new file mode 100644
index 000000000..c0ea838e1
--- /dev/null
+++ b/tests/mir-opt/copy-prop/non_dominate.rs
@@ -0,0 +1,26 @@
+// unit-test: CopyProp
+
+#![feature(custom_mir, core_intrinsics)]
+#![allow(unused_assignments)]
+extern crate core;
+use core::intrinsics::mir::*;
+
+#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
+fn f(c: bool) -> bool {
+ mir!(
+ let a: bool;
+ let b: bool;
+ { Goto(bb1) }
+ bb1 = { b = c; match b { false => bb3, _ => bb2 }}
+ // This assignment to `a` does not dominate the use in `bb3`.
+ // It should not be replaced by `b`.
+ bb2 = { a = b; c = false; Goto(bb1) }
+ bb3 = { RET = a; Return() }
+ )
+}
+
+fn main() {
+ assert_eq!(true, f(true));
+}
+
+// EMIT_MIR non_dominate.f.CopyProp.diff