Repository: aptos-labs/aptos-core (language: python)

Issue:
[Bug][compiler-v2] MOVELOC_EXISTS_BORROW_ERROR raised when generating non-opt code for copy_loc_borrowed_indirect 
# 🐛 Bug

When compiling the following code using V2:

```
module 0x8675309::Tester {
    fun t() {
        let x = 0;
        let y = 0;
        let r1 = foo(&x, &y);
        let r2 = foo(&x, &y);
        x + copy x;
        y + copy y;
        r1;
        r2;
    }

    fun foo(_r: &u64, r2: &u64): &u64 {
        r2
    }
}
```

The no-opt.exp file will contain a bytecode verification error while the exp file does not:

```
Diagnostics:
bug: BYTECODE VERIFICATION FAILED
  ┌─ tests/reference-safety/v1-borrow-tests/copy_loc_borrowed_indirect.move:7:9
  │
7 │         x + copy x;
  │         ^^^^^^^^^^ ICE failed bytecode verifier: VMError {
    major_status: MOVELOC_EXISTS_BORROW_ERROR,
    sub_status: None,
    message: None,
    exec_state: None,
    location: Module(
        ModuleId {
            address: 0000000000000000000000000000000000000000000000000000000008675309,
            name: Identifier(
                "Tester",
            ),
        },
    ),
    indices: [
        (
            FunctionDefinition,
            1,
        ),
    ],
    offsets: [
        (
            FunctionDefinitionIndex(1),
            12,
        ),
    ],
}
```






Discussion / hints:
Also happened for the test `copy_loc_borrowed`
Confirmed that bytecode verification error disappears if `AST_SIMPLIFY` is on.
For the following stackless bytecode:
```
fun Tester::t() {
     var $t0: u64
     var $t1: &u64
     var $t2: &u64
     var $t3: u64
     var $t4: u64
     var $t5: &u64
     var $t6: &u64
  0: $t0 := 0
  1: $t1 := borrow_local($t0)
  2: $t2 := borrow_local($t0)
  3: $t4 := copy($t0)
  4: $t3 := +($t0, $t4)
  5: $t5 := infer($t1)
  6: $t6 := infer($t2)
  7: return ()
}
```

The following bytecode is generated:
```
t() /* def_idx: 0 */ {
L0:	loc0: u64
L1:	loc1: u64
L2:	loc2: &u64
L3:	loc3: &u64
L4:	loc4: &u64
L5:	loc5: &u64
B0:
	0: LdU64(0)
	1: StLoc[0](loc0: u64)
	2: ImmBorrowLoc[0](loc0: u64)
	3: ImmBorrowLoc[0](loc0: u64)
	4: CopyLoc[0](loc0: u64)
	5: StLoc[1](loc1: u64)
	6: MoveLoc[0](loc0: u64)
	7: MoveLoc[1](loc1: u64)
	8: Add
	9: Pop
	10: StLoc[2](loc2: &u64)
	11: StLoc[3](loc3: &u64)
	12: MoveLoc[3](loc3: &u64)
	13: StLoc[4](loc4: &u64)
	14: MoveLoc[4](loc4: &u64)
	15: Pop
	16: MoveLoc[2](loc2: &u64)
	17: StLoc[5](loc5: &u64)
	18: MoveLoc[5](loc5: &u64)
	19: Pop
	20: Ret
}
```

At instr 6, `Copy` should be used instead of `Move`



Fix patch:
diff --git a/third_party/move/move-compiler-v2/src/pipeline/ability_processor.rs b/third_party/move/move-compiler-v2/src/pipeline/ability_processor.rs
index ca0621a61aa9b..f86b35fbe6e78 100644
--- a/third_party/move/move-compiler-v2/src/pipeline/ability_processor.rs
+++ b/third_party/move/move-compiler-v2/src/pipeline/ability_processor.rs
@@ -9,19 +9,18 @@
 //! - It infers the `AssignKind` in the assign statement. This will be `Move` if
 //!   the source is not used after the assignment and is not borrowed. It will
 //!   be Copy otherwise.
-//! - It inserts a `Copy` assignment for every function argument which is used later or borrowed
-//!   (same condition as above)
+//! - It inserts a `Copy` assignment for every function or operator argument which is
+//!   borrowed.
 //! - It inserts a `Drop` instruction for values which go out of scope and are not
 //!   consumed by any call and no longer borrowed.
 //!
 //! For the checking part, consider the transformation to have happened,
 //! then:
 //!
-//! - Every copied value must have the `copy` ability
+//! - Every copied value must have the `copy` ability. This includes values for which we do
+//!   not generate a `Copy` instruction, but for every temporary which is used later
+//!   or is borrowed.
 //! - Every dropped value must have the `drop` ability
-//! - Every type used in storage operations must have the `key` ability (TODO(#12036): this check should
-//!   go the frontend where also `store` is checked)
-//! - All type instantiations in the program must satisfy ability constraints (TODO: also frontend)
 //!
 //! Precondition: LiveVarAnnotation, LifetimeAnnotation, ExitStateAnnotation
 
@@ -161,11 +160,6 @@ impl<'a> TransferFunctions for CopyDropAnalysis<'a> {
         let live_var = self.live_var.get_info_at(offset);
         let lifetime = self.lifetime.get_info_at(offset);
         let exit_state = self.exit_state.get_state_at(offset);
-        // Only non-primitive types need a copy
-        let type_needs_copy = |temp: &TempIndex| {
-            let ty = self.target.get_local_type(*temp);
-            !ty.is_primitive()
-        };
         // Only temps which are used after or borrowed need a copy
         let temp_needs_copy = |temp, instr| {
             live_var.is_temp_used_after(temp, instr) || lifetime.borrow_kind_before(*temp).is_some()
@@ -190,18 +184,15 @@ impl<'a> TransferFunctions for CopyDropAnalysis<'a> {
                 // Operation does not consume operands.
             },
             Call(_, _, op, srcs, ..) => {
-                // If this is an equality we need to check drop for the operands, even though we do not need
-                // to emit a drop.
+                // If this is an equality we need to check drop for the operands,
+                // even though we do not need to emit a drop.
                 if matches!(op, Operation::Eq | Operation::Neq) {
                     state.check_drop.extend(srcs.iter().cloned())
                 }
-                // For arguments, we also need to check the case that a src, even if not used after this program
-                // point, is again used in the argument list. Also, in difference to assign inference, we only need
-                // to copy the argument if its not primitive.
+                // For arguments, we also need to check the case that a src, even
+                // if not used after this program point, is again used in the argument list.
                 for (i, src) in srcs.iter().enumerate() {
-                    if (temp_needs_copy(src, instr) || srcs[i + 1..].contains(src))
-                        && type_needs_copy(src)
-                    {
+                    if temp_needs_copy(src, instr) || srcs[i + 1..].contains(src) {
                         state.needs_copy.insert(*src);
                     } else {
                         state.moved.insert(*src);
@@ -295,14 +286,8 @@ impl<'a> Transformer<'a> {
                 },
             },
             Call(id, dests, op, srcs, ai) => {
-                use Operation::*;
-                match &op {
-                    Function(..) => {
-                        let new_srcs = self.copy_args_if_needed(code_offset, id, srcs);
-                        self.check_and_emit_bytecode(code_offset, Call(id, dests, op, new_srcs, ai))
-                    },
-                    _ => self.check_and_emit_bytecode(code_offset, bc.clone()),
-                }
+                let new_srcs = self.copy_args_if_needed(code_offset, id, srcs);
+                self.check_and_emit_bytecode(code_offset, Call(id, dests, op, new_srcs, ai))
             },
             _ => self.check_and_emit_bytecode(code_offset, bc.clone()),
         }
@@ -377,10 +362,22 @@ impl<'a> Transformer<'a> {
         for src in srcs.iter() {
             if copy_drop_at.needs_copy.contains(src) {
                 self.check_implicit_copy(code_offset, id, *src);
-                let ty = self.builder.get_local_type(*src);
-                let temp = self.builder.new_temp(ty);
-                self.builder.emit(Assign(id, temp, *src, AssignKind::Copy));
-                new_srcs.push(temp)
+                // Only need to perform the actual copy if src is borrowed, as this
+                // information cannot be determined from live-var analysis in later
+                // phases.
+                if self
+                    .lifetime
+                    .get_info_at(code_offset)
+                    .borrow_kind_after(*src)
+                    .is_some()
+                {
+                    let ty = self.builder.get_local_type(*src);
+                    let temp = self.builder.new_temp(ty);
+                    self.builder.emit(Assign(id, temp, *src, AssignKind::Copy));
+                    new_srcs.push(temp)
+                } else {
+                    new_srcs.push(*src)
+                }
             } else {
                 new_srcs.push(*src)
             }
diff --git a/third_party/move/move-compiler-v2/src/pipeline/reference_safety/reference_safety_processor_v3.rs b/third_party/move/move-compiler-v2/src/pipeline/reference_safety/reference_safety_processor_v3.rs
index 987b0a2480e23..12eeb06bb2b24 100644
--- a/third_party/move/move-compiler-v2/src/pipeline/reference_safety/reference_safety_processor_v3.rs
+++ b/third_party/move/move-compiler-v2/src/pipeline/reference_safety/reference_safety_processor_v3.rs
@@ -1170,17 +1170,22 @@ impl LifetimeInfo for LifetimeState {
 // ===============================================================================
 // Display
 
-struct LabelDisplay<'a>(&'a FunctionTarget<'a>, &'a Label);
+struct LabelDisplay<'a>(&'a FunctionTarget<'a>, &'a Label, /*raw*/ bool);
 
 impl Label {
     fn display<'a>(&'a self, fun: &'a FunctionTarget) -> LabelDisplay<'a> {
-        LabelDisplay(fun, self)
+        LabelDisplay(fun, self, false)
+    }
+
+    fn display_raw<'a>(&'a self, fun: &'a FunctionTarget) -> LabelDisplay<'a> {
+        LabelDisplay(fun, self, false) // TODO: turn on in different commit
     }
 }
 
 impl<'a> Display for LabelDisplay<'a> {
     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
         match self.1 {
+            Label::Local(local) if /*raw*/self.2 => write!(f, "$t{}", local),
             Label::Local(local) => write!(f, "{}", self.0.get_local_name_for_error_message(*local)),
             Label::Global(struct_id) => {
                 let struct_env = self.0.global_env().get_struct(*struct_id);
@@ -1271,10 +1276,10 @@ impl<'a> Display for LifetimeStateDisplay<'a> {
                         if is_strong { "=" } else { "-" },
                         mut_str,
                         fmt_ref_id(target),
-                        path.iter().map(|l| l.display(self.0)).join(", "),
+                        path.iter().map(|l| l.display_raw(self.0)).join(", "),
                         self.0
                             .get_bytecode_loc(code_id)
-                            .display_line_only(self.0.global_env())
+                            .display_line_only(self.0.global_env()),
                     )?
                 }
             }


Test patch:
diff --git a/third_party/move/move-compiler-v2/tests/ability-check/bug_14189.exp b/third_party/move/move-compiler-v2/tests/ability-check/bug_14189.exp
index 639eb3130c251..9be1b39f7a68d 100644
--- a/third_party/move/move-compiler-v2/tests/ability-check/bug_14189.exp
+++ b/third_party/move/move-compiler-v2/tests/ability-check/bug_14189.exp
@@ -1,9 +1,7 @@
 
-============ bytecode verification failed ========
-
 Diagnostics:
-bug: bytecode verification failed with unexpected status code `MOVELOC_UNAVAILABLE_ERROR`. This is a compiler bug, consider reporting it.
+error: local `x1` of type `test::S2` does not have the `copy` ability
    ┌─ tests/ability-check/bug_14189.move:34:18
    │
 34 │         let x2 = S3 { x: x1, y: x0, z: x1 };
-   │                  ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   │                  ^^^^^^^^^^^^^^^^^^^^^^^^^^ copy needed here because value is still in use
diff --git a/third_party/move/move-compiler-v2/tests/ability-check/bug_14227.exp b/third_party/move/move-compiler-v2/tests/ability-check/bug_14227.exp
index ca911432632e1..fd998cd08ec9e 100644
--- a/third_party/move/move-compiler-v2/tests/ability-check/bug_14227.exp
+++ b/third_party/move/move-compiler-v2/tests/ability-check/bug_14227.exp
@@ -1,13 +1,10 @@
 
-============ bytecode verification failed ========
-
 Diagnostics:
-bug: bytecode verification failed with unexpected status code `MOVELOC_UNAVAILABLE_ERROR`. This is a compiler bug, consider reporting it.
-   ┌─ tests/ability-check/bug_14227.move:19:32
+error: local `_common_fields` of type `m::CommonFields` does not have the `copy` ability
+   ┌─ tests/ability-check/bug_14227.move:21:16
    │
-19 │           let _common_vector_2 = CommonFieldsVector::Bar {
-   │ ╭────────────────────────────────^
-20 │ │             x: vector[2],
-21 │ │             y: vector[_common_fields]
-22 │ │         };
-   │ ╰─────────^
+21 │             y: vector[_common_fields]
+   │                ^^^^^^ copy needed here because value is still in use
+   ·
+25 │             y: vector[_common_fields]
+   │                ------ used here
diff --git a/third_party/move/move-compiler-v2/tests/ability-transform/foreach_mut_expanded.exp b/third_party/move/move-compiler-v2/tests/ability-transform/foreach_mut_expanded.exp
index 9a445c98e625f..7ab9fcebe971c 100644
--- a/third_party/move/move-compiler-v2/tests/ability-transform/foreach_mut_expanded.exp
+++ b/third_party/move/move-compiler-v2/tests/ability-transform/foreach_mut_expanded.exp
@@ -672,7 +672,6 @@ fun m::test_for_each_mut() {
      var $t10: bool
      var $t11: vector<u64>
      var $t12: u64
-     var $t13: &mut vector<u64>
   0: $t0 := ["1", "2", "3"]
   1: $t1 := 0
   2: $t3 := borrow_local($t0)
@@ -680,30 +679,29 @@ fun m::test_for_each_mut() {
   4: $t4 := borrow_local($t0)
   5: label L0
   6: $t5 := <($t1, $t2)
-  7: if ($t5) goto 8 else goto 17
+  7: if ($t5) goto 8 else goto 16
   8: label L2
-  9: $t13 := copy($t4)
- 10: $t6 := vector::borrow_mut<u64>($t13, $t1)
- 11: $t7 := 2
- 12: write_ref($t6, $t7)
- 13: $t9 := 1
- 14: $t8 := +($t1, $t9)
- 15: $t1 := move($t8)
- 16: goto 20
- 17: label L3
- 18: drop($t4)
- 19: goto 22
- 20: label L4
- 21: goto 5
- 22: label L1
- 23: $t11 := ["2", "3", "4"]
- 24: $t10 := ==($t0, $t11)
- 25: if ($t10) goto 26 else goto 28
- 26: label L5
- 27: goto 31
- 28: label L6
- 29: $t12 := 0
- 30: abort($t12)
- 31: label L7
- 32: return ()
+  9: $t6 := vector::borrow_mut<u64>($t4, $t1)
+ 10: $t7 := 2
+ 11: write_ref($t6, $t7)
+ 12: $t9 := 1
+ 13: $t8 := +($t1, $t9)
+ 14: $t1 := move($t8)
+ 15: goto 19
+ 16: label L3
+ 17: drop($t4)
+ 18: goto 21
+ 19: label L4
+ 20: goto 5
+ 21: label L1
+ 22: $t11 := ["2", "3", "4"]
+ 23: $t10 := ==($t0, $t11)
+ 24: if ($t10) goto 25 else goto 27
+ 25: label L5
+ 26: goto 30
+ 27: label L6
+ 28: $t12 := 0
+ 29: abort($t12)
+ 30: label L7
+ 31: return ()
 }
diff --git a/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488.no-opt.exp b/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488.no-opt.exp
deleted file mode 100644
index e877a2fbfef45..0000000000000
--- a/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488.no-opt.exp
+++ /dev/null
@@ -1,9 +0,0 @@
-
-============ bytecode verification failed ========
-
-Diagnostics:
-bug: bytecode verification failed with unexpected status code `MOVELOC_EXISTS_BORROW_ERROR`. This is a compiler bug, consider reporting it.
-  ┌─ tests/reference-safety/bug_13488.move:8:9
-  │
-8 │         x + copy x;
-  │         ^^^^^^^^^^
diff --git a/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488.exp b/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_for_add.exp
similarity index 100%
rename from third_party/move/move-compiler-v2/tests/reference-safety/bug_13488.exp
rename to third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_for_add.exp
diff --git a/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488.move b/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_for_add.move
similarity index 72%
rename from third_party/move/move-compiler-v2/tests/reference-safety/bug_13488.move
rename to third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_for_add.move
index fe1b0e871d3ec..be29aae6d6ab3 100644
--- a/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488.move
+++ b/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_for_add.move
@@ -1,4 +1,3 @@
-// TODO(#13488): after fix, rename file to reflect issue (`bug_nnn.move` to `bug_nnn_<issue>.move`)
 module 0x8675309::Tester {
     fun t() {
         let x = 0;
diff --git a/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488.old.exp b/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_for_add.no-opt.exp
similarity index 100%
rename from third_party/move/move-compiler-v2/tests/reference-safety/bug_13488.old.exp
rename to third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_for_add.no-opt.exp
diff --git a/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_for_add.old.exp b/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_for_add.old.exp
new file mode 100644
index 0000000000000..90b32906711cc
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_for_add.old.exp
@@ -0,0 +1,2 @@
+
+============ bytecode verification succeeded ========
diff --git a/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_more_ops.exp b/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_more_ops.exp
new file mode 100644
index 0000000000000..90b32906711cc
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_more_ops.exp
@@ -0,0 +1,2 @@
+
+============ bytecode verification succeeded ========
diff --git a/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_more_ops.move b/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_more_ops.move
new file mode 100644
index 0000000000000..f9705e8607975
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_more_ops.move
@@ -0,0 +1,44 @@
+module 0xc0ffee::m {
+
+    fun test() {
+        let x = 0;
+        let r1 = &x;
+        let r2 = &x;
+        x + copy x;
+        r1;
+        r2;
+        assert!(*r1 == 0, 1);
+        assert!(*r2 == 0, 1);
+    }
+
+    fun test2() {
+        let x = 2;
+        let _r1 = &x;
+        let _r2 = &x;
+        assert!(x + copy x == 4, 1);
+        _r1;
+        _r2;
+    }
+
+    fun test3() {
+        use std::vector;
+        let x = 1;
+        let _r1 = &x;
+        let v = vector[];
+        vector::push_back(&mut v, x);
+        _r1;
+        assert!(*vector::borrow(&v, 0) == 1, 0);
+    }
+
+    fun t(x: u64): u64 {
+        x
+    }
+
+    fun test5() {
+        let x = 0;
+        let _r1 = &x;
+        let y = t(x);
+        _r1;
+        assert!(y == 0, 0);
+    }
+}
diff --git a/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_more_ops.no-opt.exp b/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_more_ops.no-opt.exp
new file mode 100644
index 0000000000000..90b32906711cc
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_more_ops.no-opt.exp
@@ -0,0 +1,2 @@
+
+============ bytecode verification succeeded ========
diff --git a/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_more_ops.old.exp b/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_more_ops.old.exp
new file mode 100644
index 0000000000000..90b32906711cc
--- /dev/null
+++ b/third_party/move/move-compiler-v2/tests/reference-safety/bug_13488_missing_copy_more_ops.old.exp
@@ -0,0 +1,2 @@
+
+============ bytecode verification succeeded ========
diff --git a/third_party/move/move-compiler-v2/tests/reference-safety/v1-borrow-tests/copy_loc_borrowed.no-opt.exp b/third_party/move/move-compiler-v2/tests/reference-safety/v1-borrow-tests/copy_loc_borrowed.no-opt.exp
index db9447438234d..90b32906711cc 100644
--- a/third_party/move/move-compiler-v2/tests/reference-safety/v1-borrow-tests/copy_loc_borrowed.no-opt.exp
+++ b/third_party/move/move-compiler-v2/tests/reference-safety/v1-borrow-tests/copy_loc_borrowed.no-opt.exp
@@ -1,9 +1,2 @@
 
-============ bytecode verification failed ========
-
-Diagnostics:
-bug: bytecode verification failed with unexpected status code `MOVELOC_EXISTS_BORROW_ERROR`. This is a compiler bug, consider reporting it.
-  ┌─ tests/reference-safety/v1-borrow-tests/copy_loc_borrowed.move:6:9
-  │
-6 │         x + copy x;
-  │         ^^^^^^^^^^
+============ bytecode verification succeeded ========
diff --git a/third_party/move/move-compiler-v2/tests/reference-safety/v1-borrow-tests/copy_loc_borrowed_indirect.no-opt.exp b/third_party/move/move-compiler-v2/tests/reference-safety/v1-borrow-tests/copy_loc_borrowed_indirect.no-opt.exp
index 212abcc5fcddd..90b32906711cc 100644
--- a/third_party/move/move-compiler-v2/tests/reference-safety/v1-borrow-tests/copy_loc_borrowed_indirect.no-opt.exp
+++ b/third_party/move/move-compiler-v2/tests/reference-safety/v1-borrow-tests/copy_loc_borrowed_indirect.no-opt.exp
@@ -1,9 +1,2 @@
 
-============ bytecode verification failed ========
-
-Diagnostics:
-bug: bytecode verification failed with unexpected status code `MOVELOC_EXISTS_BORROW_ERROR`. This is a compiler bug, consider reporting it.
-  ┌─ tests/reference-safety/v1-borrow-tests/copy_loc_borrowed_indirect.move:7:9
-  │
-7 │         x + copy x;
-  │         ^^^^^^^^^^
+============ bytecode verification succeeded ========
diff --git a/third_party/move/move-compiler-v2/tests/testsuite.rs b/third_party/move/move-compiler-v2/tests/testsuite.rs
index 9d7d3254bb257..d5b17091780b4 100644
--- a/third_party/move/move-compiler-v2/tests/testsuite.rs
+++ b/third_party/move/move-compiler-v2/tests/testsuite.rs
@@ -332,6 +332,7 @@ const TEST_CONFIGS: Lazy<BTreeMap<&str, TestConfig>> = Lazy::new(|| {
             Some(vec![
                 INITIAL_BYTECODE_STAGE,
                 "ReferenceSafetyProcessor",
+                "AbilityProcessor",
                 "DeadStoreElimination",
                 FILE_FORMAT_STAGE,
             ]),
diff --git a/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_2.exp b/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_2.exp
index 2f06956cfca9e..d80d47d848cef 100644
--- a/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_2.exp
+++ b/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_2.exp
@@ -58,12 +58,10 @@ public fun m::test1($t0: u64) {
 [variant baseline]
 public fun m::test2($t0: m::W) {
      var $t1: m::W
-     var $t2: m::W
   0: $t1 := move($t0)
-  1: $t2 := copy($t1)
-  2: m::consume_($t2)
-  3: m::consume_($t1)
-  4: return ()
+  1: m::consume_($t1)
+  2: m::consume_($t1)
+  3: return ()
 }
 
 ============ after VariableCoalescingAnnotator: ================
@@ -103,21 +101,16 @@ public fun m::test1($t0: u64) {
 [variant baseline]
 public fun m::test2($t0: m::W) {
      var $t1: m::W
-     var $t2: m::W
      # live vars: $t0
      # events: b:$t0, e:$t0, b:$t1
   0: $t1 := move($t0)
      # live vars: $t1
-     # events: b:$t2
-  1: $t2 := copy($t1)
-     # live vars: $t1, $t2
-     # events: e:$t2
-  2: m::consume_($t2)
+  1: m::consume_($t1)
      # live vars: $t1
      # events: e:$t1
-  3: m::consume_($t1)
+  2: m::consume_($t1)
      # live vars:
-  4: return ()
+  3: return ()
 }
 
 ============ after VariableCoalescingTransformer: ================
@@ -147,12 +140,10 @@ public fun m::test1($t0: u64) {
 [variant baseline]
 public fun m::test2($t0: m::W) {
      var $t1: m::W [unused]
-     var $t2: m::W
   0: $t0 := move($t0)
-  1: $t2 := copy($t0)
-  2: m::consume_($t2)
-  3: m::consume_($t0)
-  4: return ()
+  1: m::consume_($t0)
+  2: m::consume_($t0)
+  3: return ()
 }
 
 ============ after DeadStoreElimination: ================
@@ -181,11 +172,9 @@ public fun m::test1($t0: u64) {
 [variant baseline]
 public fun m::test2($t0: m::W) {
      var $t1: m::W [unused]
-     var $t2: m::W
-  0: $t2 := copy($t0)
-  1: m::consume_($t2)
-  2: m::consume_($t0)
-  3: return ()
+  0: m::consume_($t0)
+  1: m::consume_($t0)
+  2: return ()
 }
 
 
@@ -213,7 +202,6 @@ B0:
 	4: Ret
 }
 public test2(Arg0: W) /* def_idx: 3 */ {
-L1:	loc0: W
 B0:
 	0: CopyLoc[0](Arg0: W)
 	1: Call consume_(W)
diff --git a/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_2.opt.exp b/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_2.opt.exp
index 2f06956cfca9e..d80d47d848cef 100644
--- a/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_2.opt.exp
+++ b/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_2.opt.exp
@@ -58,12 +58,10 @@ public fun m::test1($t0: u64) {
 [variant baseline]
 public fun m::test2($t0: m::W) {
      var $t1: m::W
-     var $t2: m::W
   0: $t1 := move($t0)
-  1: $t2 := copy($t1)
-  2: m::consume_($t2)
-  3: m::consume_($t1)
-  4: return ()
+  1: m::consume_($t1)
+  2: m::consume_($t1)
+  3: return ()
 }
 
 ============ after VariableCoalescingAnnotator: ================
@@ -103,21 +101,16 @@ public fun m::test1($t0: u64) {
 [variant baseline]
 public fun m::test2($t0: m::W) {
      var $t1: m::W
-     var $t2: m::W
      # live vars: $t0
      # events: b:$t0, e:$t0, b:$t1
   0: $t1 := move($t0)
      # live vars: $t1
-     # events: b:$t2
-  1: $t2 := copy($t1)
-     # live vars: $t1, $t2
-     # events: e:$t2
-  2: m::consume_($t2)
+  1: m::consume_($t1)
      # live vars: $t1
      # events: e:$t1
-  3: m::consume_($t1)
+  2: m::consume_($t1)
      # live vars:
-  4: return ()
+  3: return ()
 }
 
 ============ after VariableCoalescingTransformer: ================
@@ -147,12 +140,10 @@ public fun m::test1($t0: u64) {
 [variant baseline]
 public fun m::test2($t0: m::W) {
      var $t1: m::W [unused]
-     var $t2: m::W
   0: $t0 := move($t0)
-  1: $t2 := copy($t0)
-  2: m::consume_($t2)
-  3: m::consume_($t0)
-  4: return ()
+  1: m::consume_($t0)
+  2: m::consume_($t0)
+  3: return ()
 }
 
 ============ after DeadStoreElimination: ================
@@ -181,11 +172,9 @@ public fun m::test1($t0: u64) {
 [variant baseline]
 public fun m::test2($t0: m::W) {
      var $t1: m::W [unused]
-     var $t2: m::W
-  0: $t2 := copy($t0)
-  1: m::consume_($t2)
-  2: m::consume_($t0)
-  3: return ()
+  0: m::consume_($t0)
+  1: m::consume_($t0)
+  2: return ()
 }
 
 
@@ -213,7 +202,6 @@ B0:
 	4: Ret
 }
 public test2(Arg0: W) /* def_idx: 3 */ {
-L1:	loc0: W
 B0:
 	0: CopyLoc[0](Arg0: W)
 	1: Call consume_(W)
diff --git a/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_5.exp b/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_5.exp
index e85dc3bd778ff..9e53ac40122e5 100644
--- a/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_5.exp
+++ b/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_5.exp
@@ -101,27 +101,23 @@ public fun m::test($t0: bool, $t1: u32) {
 public fun m::test_struct($t0: bool, $t1: m::W) {
      var $t2: m::W
      var $t3: bool
-     var $t4: m::W
-     var $t5: m::W
   0: $t2 := copy($t1)
-  1: if ($t0) goto 2 else goto 6
+  1: if ($t0) goto 2 else goto 5
   2: label L0
-  3: $t4 := copy($t2)
-  4: m::consume_($t4)
-  5: goto 9
-  6: label L1
-  7: $t5 := copy($t1)
-  8: m::consume_($t5)
-  9: label L2
- 10: $t3 := !($t0)
- 11: if ($t3) goto 12 else goto 15
- 12: label L3
- 13: m::consume_($t1)
- 14: goto 17
- 15: label L4
- 16: m::consume_($t2)
- 17: label L5
- 18: return ()
+  3: m::consume_($t2)
+  4: goto 7
+  5: label L1
+  6: m::consume_($t1)
+  7: label L2
+  8: $t3 := !($t0)
+  9: if ($t3) goto 10 else goto 13
+ 10: label L3
+ 11: m::consume_($t1)
+ 12: goto 15
+ 13: label L4
+ 14: m::consume_($t2)
+ 15: label L5
+ 16: return ()
 }
 
 ============ after VariableCoalescingAnnotator: ================
@@ -192,55 +188,45 @@ public fun m::test($t0: bool, $t1: u32) {
 public fun m::test_struct($t0: bool, $t1: m::W) {
      var $t2: m::W
      var $t3: bool
-     var $t4: m::W
-     var $t5: m::W
      # live vars: $t0, $t1
      # events: b:$t0, b:$t1, b:$t2
   0: $t2 := copy($t1)
      # live vars: $t0, $t1, $t2
-  1: if ($t0) goto 2 else goto 6
+  1: if ($t0) goto 2 else goto 5
      # live vars: $t0, $t1, $t2
   2: label L0
      # live vars: $t0, $t1, $t2
-     # events: b:$t4
-  3: $t4 := copy($t2)
-     # live vars: $t0, $t1, $t2, $t4
-     # events: e:$t4
-  4: m::consume_($t4)
+  3: m::consume_($t2)
      # live vars: $t0, $t1, $t2
-  5: goto 9
+  4: goto 7
      # live vars: $t0, $t1, $t2
-  6: label L1
+  5: label L1
      # live vars: $t0, $t1, $t2
-     # events: b:$t5
-  7: $t5 := copy($t1)
-     # live vars: $t0, $t1, $t2, $t5
-     # events: e:$t5
-  8: m::consume_($t5)
+  6: m::consume_($t1)
      # live vars: $t0, $t1, $t2
-  9: label L2
+  7: label L2
      # live vars: $t0, $t1, $t2
      # events: e:$t0, b:$t3
- 10: $t3 := !($t0)
+  8: $t3 := !($t0)
      # live vars: $t1, $t2, $t3
      # events: e:$t3
- 11: if ($t3) goto 12 else goto 15
+  9: if ($t3) goto 10 else goto 13
      # live vars: $t1, $t2
- 12: label L3
+ 10: label L3
      # live vars: $t1
- 13: m::consume_($t1)
+ 11: m::consume_($t1)
      # live vars:
- 14: goto 17
+ 12: goto 15
      # live vars: $t1, $t2
      # events: e:$t1
- 15: label L4
+ 13: label L4
      # live vars: $t2
      # events: e:$t2
- 16: m::consume_($t2)
+ 14: m::consume_($t2)
      # live vars:
- 17: label L5
+ 15: label L5
      # live vars:
- 18: return ()
+ 16: return ()
 }
 
 ============ after VariableCoalescingTransformer: ================
@@ -285,27 +271,23 @@ public fun m::test($t0: bool, $t1: u32) {
 public fun m::test_struct($t0: bool, $t1: m::W) {
      var $t2: m::W
      var $t3: bool [unused]
-     var $t4: m::W
-     var $t5: m::W [unused]
   0: $t2 := copy($t1)
-  1: if ($t0) goto 2 else goto 6
+  1: if ($t0) goto 2 else goto 5
   2: label L0
-  3: $t4 := copy($t2)
-  4: m::consume_($t4)
-  5: goto 9
-  6: label L1
-  7: $t4 := copy($t1)
-  8: m::consume_($t4)
-  9: label L2
- 10: $t0 := !($t0)
- 11: if ($t0) goto 12 else goto 15
- 12: label L3
- 13: m::consume_($t1)
- 14: goto 17
- 15: label L4
- 16: m::consume_($t2)
- 17: label L5
- 18: return ()
+  3: m::consume_($t2)
+  4: goto 7
+  5: label L1
+  6: m::consume_($t1)
+  7: label L2
+  8: $t0 := !($t0)
+  9: if ($t0) goto 10 else goto 13
+ 10: label L3
+ 11: m::consume_($t1)
+ 12: goto 15
+ 13: label L4
+ 14: m::consume_($t2)
+ 15: label L5
+ 16: return ()
 }
 
 ============ after DeadStoreElimination: ================
@@ -350,27 +332,23 @@ public fun m::test($t0: bool, $t1: u32) {
 public fun m::test_struct($t0: bool, $t1: m::W) {
      var $t2: m::W
      var $t3: bool [unused]
-     var $t4: m::W
-     var $t5: m::W [unused]
   0: $t2 := copy($t1)
-  1: if ($t0) goto 2 else goto 6
+  1: if ($t0) goto 2 else goto 5
   2: label L0
-  3: $t4 := copy($t2)
-  4: m::consume_($t4)
-  5: goto 9
-  6: label L1
-  7: $t4 := copy($t1)
-  8: m::consume_($t4)
-  9: label L2
- 10: $t0 := !($t0)
- 11: if ($t0) goto 12 else goto 15
- 12: label L3
- 13: m::consume_($t1)
- 14: goto 17
- 15: label L4
- 16: m::consume_($t2)
- 17: label L5
- 18: return ()
+  3: m::consume_($t2)
+  4: goto 7
+  5: label L1
+  6: m::consume_($t1)
+  7: label L2
+  8: $t0 := !($t0)
+  9: if ($t0) goto 10 else goto 13
+ 10: label L3
+ 11: m::consume_($t1)
+ 12: goto 15
+ 13: label L4
+ 14: m::consume_($t2)
+ 15: label L5
+ 16: return ()
 }
 
 
@@ -418,7 +396,6 @@ B6:
 }
 public test_struct(Arg0: bool, Arg1: W) /* def_idx: 3 */ {
 L2:	loc0: W
-L3:	loc1: W
 B0:
 	0: CopyLoc[1](Arg1: W)
 	1: StLoc[2](loc0: W)
diff --git a/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_5.opt.exp b/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_5.opt.exp
index e85dc3bd778ff..9e53ac40122e5 100644
--- a/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_5.opt.exp
+++ b/third_party/move/move-compiler-v2/tests/variable-coalescing/consume_5.opt.exp
@@ -101,27 +101,23 @@ public fun m::test($t0: bool, $t1: u32) {
 public fun m::test_struct($t0: bool, $t1: m::W) {
      var $t2: m::W
      var $t3: bool
-     var $t4: m::W
-     var $t5: m::W
   0: $t2 := copy($t1)
-  1: if ($t0) goto 2 else goto 6
+  1: if ($t0) goto 2 else goto 5
   2: label L0
-  3: $t4 := copy($t2)
-  4: m::consume_($t4)
-  5: goto 9
-  6: label L1
-  7: $t5 := copy($t1)
-  8: m::consume_($t5)
-  9: label L2
- 10: $t3 := !($t0)
- 11: if ($t3) goto 12 else goto 15
- 12: label L3
- 13: m::consume_($t1)
- 14: goto 17
- 15: label L4
- 16: m::consume_($t2)
- 17: label L5
- 18: return ()
+  3: m::consume_($t2)
+  4: goto 7
+  5: label L1
+  6: m::consume_($t1)
+  7: label L2
+  8: $t3 := !($t0)
+  9: if ($t3) goto 10 else goto 13
+ 10: label L3
+ 11: m::consume_($t1)
+ 12: goto 15
+ 13: label L4
+ 14: m::consume_($t2)
+ 15: label L5
+ 16: return ()
 }
 
 ============ after VariableCoalescingAnnotator: ================
@@ -192,55 +188,45 @@ public fun m::test($t0: bool, $t1: u32) {
 public fun m::test_struct($t0: bool, $t1: m::W) {
      var $t2: m::W
      var $t3: bool
-     var $t4: m::W
-     var $t5: m::W
      # live vars: $t0, $t1
      # events: b:$t0, b:$t1, b:$t2
   0: $t2 := copy($t1)
      # live vars: $t0, $t1, $t2
-  1: if ($t0) goto 2 else goto 6
+  1: if ($t0) goto 2 else goto 5
      # live vars: $t0, $t1, $t2
   2: label L0
      # live vars: $t0, $t1, $t2
-     # events: b:$t4
-  3: $t4 := copy($t2)
-     # live vars: $t0, $t1, $t2, $t4
-     # events: e:$t4
-  4: m::consume_($t4)
+  3: m::consume_($t2)
      # live vars: $t0, $t1, $t2
-  5: goto 9
+  4: goto 7
      # live vars: $t0, $t1, $t2
-  6: label L1
+  5: label L1
      # live vars: $t0, $t1, $t2
-     # events: b:$t5
-  7: $t5 := copy($t1)
-     # live vars: $t0, $t1, $t2, $t5
-     # events: e:$t5
-  8: m::consume_($t5)
+  6: m::consume_($t1)
      # live vars: $t0, $t1, $t2
-  9: label L2
+  7: label L2
      # live vars: $t0, $t1, $t2
      # events: e:$t0, b:$t3
- 10: $t3 := !($t0)
+  8: $t3 := !($t0)
      # live vars: $t1, $t2, $t3
      # events: e:$t3
- 11: if ($t3) goto 12 else goto 15
+  9: if ($t3) goto 10 else goto 13
      # live vars: $t1, $t2
- 12: label L3
+ 10: label L3
      # live vars: $t1
- 13: m::consume_($t1)
+ 11: m::consume_($t1)
      # live vars:
- 14: goto 17
+ 12: goto 15
      # live vars: $t1, $t2
      # events: e:$t1
- 15: label L4
+ 13: label L4
      # live vars: $t2
      # events: e:$t2
- 16: m::consume_($t2)
+ 14: m::consume_($t2)
      # live vars:
- 17: label L5
+ 15: label L5
      # live vars:
- 18: return ()
+ 16: return ()
 }
 
 ============ after VariableCoalescingTransformer: ================
@@ -285,27 +271,23 @@ public fun m::test($t0: bool, $t1: u32) {
 public fun m::test_struct($t0: bool, $t1: m::W) {
      var $t2: m::W
      var $t3: bool [unused]
-     var $t4: m::W
-     var $t5: m::W [unused]
   0: $t2 := copy($t1)
-  1: if ($t0) goto 2 else goto 6
+  1: if ($t0) goto 2 else goto 5
   2: label L0
-  3: $t4 := copy($t2)
-  4: m::consume_($t4)
-  5: goto 9
-  6: label L1
-  7: $t4 := copy($t1)
-  8: m::consume_($t4)
-  9: label L2
- 10: $t0 := !($t0)
- 11: if ($t0) goto 12 else goto 15
- 12: label L3
- 13: m::consume_($t1)
- 14: goto 17
- 15: label L4
- 16: m::consume_($t2)
- 17: label L5
- 18: return ()
+  3: m::consume_($t2)
+  4: goto 7
+  5: label L1
+  6: m::consume_($t1)
+  7: label L2
+  8: $t0 := !($t0)
+  9: if ($t0) goto 10 else goto 13
+ 10: label L3
+ 11: m::consume_($t1)
+ 12: goto 15
+ 13: label L4
+ 14: m::consume_($t2)
+ 15: label L5
+ 16: return ()
 }
 
 ============ after DeadStoreElimination: ================
@@ -350,27 +332,23 @@ public fun m::test($t0: bool, $t1: u32) {
 public fun m::test_struct($t0: bool, $t1: m::W) {
      var $t2: m::W
      var $t3: bool [unused]
-     var $t4: m::W
-     var $t5: m::W [unused]
   0: $t2 := copy($t1)
-  1: if ($t0) goto 2 else goto 6
+  1: if ($t0) goto 2 else goto 5
   2: label L0
-  3: $t4 := copy($t2)
-  4: m::consume_($t4)
-  5: goto 9
-  6: label L1
-  7: $t4 := copy($t1)
-  8: m::consume_($t4)
-  9: label L2
- 10: $t0 := !($t0)
- 11: if ($t0) goto 12 else goto 15
- 12: label L3
- 13: m::consume_($t1)
- 14: goto 17
- 15: label L4
- 16: m::consume_($t2)
- 17: label L5
- 18: return ()
+  3: m::consume_($t2)
+  4: goto 7
+  5: label L1
+  6: m::consume_($t1)
+  7: label L2
+  8: $t0 := !($t0)
+  9: if ($t0) goto 10 else goto 13
+ 10: label L3
+ 11: m::consume_($t1)
+ 12: goto 15
+ 13: label L4
+ 14: m::consume_($t2)
+ 15: label L5
+ 16: return ()
 }
 
 
@@ -418,7 +396,6 @@ B6:
 }
 public test_struct(Arg0: bool, Arg1: W) /* def_idx: 3 */ {
 L2:	loc0: W
-L3:	loc1: W
 B0:
 	0: CopyLoc[1](Arg1: W)
 	1: StLoc[2](loc0: W)
diff --git a/third_party/move/move-compiler-v2/transactional-tests/tests/misc/bug_14243_stack_size.no-optimize.exp b/third_party/move/move-compiler-v2/transactional-tests/tests/misc/bug_14243_stack_size.no-optimize.exp
index c9e006770cba4..626523b1b0547 100644
--- a/third_party/move/move-compiler-v2/transactional-tests/tests/misc/bug_14243_stack_size.no-optimize.exp
+++ b/third_party/move/move-compiler-v2/transactional-tests/tests/misc/bug_14243_stack_size.no-optimize.exp
@@ -19,29 +19,22 @@ comparison between v1 and v2 failed:
 = t0() /* def_idx: 1 */ {
 = L0:	loc0: u64
 = L1:	loc1: &mut u64
-+ L2:	loc2: &mut u64
 = B0:
 = 	0: LdU64(0)
 = 	1: StLoc[0](loc0: u64)
 = 	2: MutBorrowLoc[0](loc0: u64)
 = 	3: StLoc[1](loc1: &mut u64)
 = 	4: CopyLoc[1](loc1: &mut u64)
-- 	5: Call id_mut<u64>(&mut u64): &mut u64
-- 	6: ReadRef
+= 	5: Call id_mut<u64>(&mut u64): &mut u64
+= 	6: ReadRef
 - 	7: Pop
 - 	8: MoveLoc[1](loc1: &mut u64)
 - 	9: ReadRef
-- 	10: Pop
-- 	11: Ret
-+ 	5: StLoc[2](loc2: &mut u64)
-+ 	6: MoveLoc[2](loc2: &mut u64)
-+ 	7: Call id_mut<u64>(&mut u64): &mut u64
++ 	7: MoveLoc[1](loc1: &mut u64)
 + 	8: ReadRef
-+ 	9: MoveLoc[1](loc1: &mut u64)
-+ 	10: ReadRef
-+ 	11: Pop
-+ 	12: Pop
-+ 	13: Ret
++ 	9: Pop
+= 	10: Pop
+= 	11: Ret
 = }
 = }
 = 
diff --git a/third_party/move/move-compiler-v2/transactional-tests/tests/misc/bug_14243_stack_size.optimize-no-simplify.exp b/third_party/move/move-compiler-v2/transactional-tests/tests/misc/bug_14243_stack_size.optimize-no-simplify.exp
index 91e1a5328d361..b0e838a9d8e19 100644
--- a/third_party/move/move-compiler-v2/transactional-tests/tests/misc/bug_14243_stack_size.optimize-no-simplify.exp
+++ b/third_party/move/move-compiler-v2/transactional-tests/tests/misc/bug_14243_stack_size.optimize-no-simplify.exp
@@ -15,7 +15,6 @@ comparison between v1 and v2 failed:
 = t0() /* def_idx: 1 */ {
 = L0:	loc0: u64
 = L1:	loc1: &mut u64
-+ L2:	loc2: &mut u64
 = B0:
 = 	0: LdU64(0)
 = 	1: StLoc[0](loc0: u64)
diff --git a/third_party/move/move-compiler-v2/transactional-tests/tests/misc/bug_14243_stack_size.optimize.exp b/third_party/move/move-compiler-v2/transactional-tests/tests/misc/bug_14243_stack_size.optimize.exp
index 91e1a5328d361..b0e838a9d8e19 100644
--- a/third_party/move/move-compiler-v2/transactional-tests/tests/misc/bug_14243_stack_size.optimize.exp
+++ b/third_party/move/move-compiler-v2/transactional-tests/tests/misc/bug_14243_stack_size.optimize.exp
@@ -15,7 +15,6 @@ comparison between v1 and v2 failed:
 = t0() /* def_idx: 1 */ {
 = L0:	loc0: u64
 = L1:	loc1: &mut u64
-+ L2:	loc2: &mut u64
 = B0:
 = 	0: LdU64(0)
 = 	1: StLoc[0](loc0: u64)
diff --git a/third_party/move/move-compiler-v2/transactional-tests/tests/optimization/bug_13488_missing_copy.exp b/third_party/move/move-compiler-v2/transactional-tests/tests/optimization/bug_13488_missing_copy.exp
new file mode 100644
index 0000000000000..15b4a3261910f
--- /dev/null
+++ b/third_party/move/move-compiler-v2/transactional-tests/tests/optimization/bug_13488_missing_copy.exp
@@ -0,0 +1,3 @@
+processed 3 tasks
+
+==> Compiler v2 delivered same results!
diff --git a/third_party/move/move-compiler-v2/transactional-tests/tests/optimization/bug_13488_missing_copy.move b/third_party/move/move-compiler-v2/transactional-tests/tests/optimization/bug_13488_missing_copy.move
new file mode 100644
index 0000000000000..bd5a2b6ca2402
--- /dev/null
+++ b/third_party/move/move-compiler-v2/transactional-tests/tests/optimization/bug_13488_missing_copy.move
@@ -0,0 +1,28 @@
+//# publish
+module 0xc0ffee::m {
+
+    fun test() {
+        let x = 0;
+        let r1 = &x;
+        let r2 = &x;
+        x + copy x;
+        r1;
+        r2;
+        assert!(*r1 == 0, 1);
+        assert!(*r2 == 0, 1);
+    }
+
+    fun test2() {
+        let x = 2;
+        let _r1 = &x;
+        let _r2 = &x;
+        assert!(x + copy x == 4, 1);
+        _r1;
+        _r2;
+    }
+
+}
+
+//# run 0xc0ffee::m::test
+
+//# run 0xc0ffee::m::test2
diff --git a/third_party/move/move-compiler-v2/transactional-tests/tests/tests.rs b/third_party/move/move-compiler-v2/transactional-tests/tests/tests.rs
index 5d08aeb892aa7..e62c4d203d8a4 100644
--- a/third_party/move/move-compiler-v2/transactional-tests/tests/tests.rs
+++ b/third_party/move/move-compiler-v2/transactional-tests/tests/tests.rs
@@ -99,7 +99,7 @@ const SEPARATE_BASELINE: &[&str] = &[
     "bug_14243_stack_size.move",
     // The output of the tests could be different depending on the language version
     "/operator_eval/",
-    // Creates different code if optimized
+    // Creates different code if optimized or not
     "no-v1-comparison/enum/enum_field_select.move",
     "no-v1-comparison/enum/enum_field_select_different_offsets.move",
 ];
diff --git a/third_party/move/move-prover/tests/sources/functional/choice.v2_exp b/third_party/move/move-prover/tests/sources/functional/choice.v2_exp
index ccf3752616dc3..88889bebfffec 100644
--- a/third_party/move/move-prover/tests/sources/functional/choice.v2_exp
+++ b/third_party/move/move-prover/tests/sources/functional/choice.v2_exp
@@ -49,12 +49,8 @@ error: post-condition does not hold
    =         <redacted> = <redacted>
    =         v_ref = <redacted>
    =     at tests/sources/functional/choice.move:79: test_not_using_min_incorrect
-   =         <redacted> = <redacted>
    =     at tests/sources/functional/choice.move:80: test_not_using_min_incorrect
-   =         <redacted> = <redacted>
    =     at tests/sources/functional/choice.move:81: test_not_using_min_incorrect
-   =         <redacted> = <redacted>
-   =     at tests/sources/functional/choice.move:82: test_not_using_min_incorrect
    =         v = <redacted>
    =         result = <redacted>
    =     at tests/sources/functional/choice.move:84: test_not_using_min_incorrect
diff --git a/third_party/move/move-prover/tests/sources/functional/emits.v2_exp b/third_party/move/move-prover/tests/sources/functional/emits.v2_exp
index 0d16bacdb3c2f..fc5f3c931bad3 100644
--- a/third_party/move/move-prover/tests/sources/functional/emits.v2_exp
+++ b/third_party/move/move-prover/tests/sources/functional/emits.v2_exp
@@ -188,6 +188,7 @@ error: function does not emit the expected event
     =     at tests/sources/functional/emits.move:288: opaque_incorrect
     =     at tests/sources/functional/emits.move:289: opaque_incorrect
     =         <redacted> = <redacted>
+    =     at tests/sources/functional/emits.move:286: opaque_incorrect
     =         handle = <redacted>
     =     at tests/sources/functional/emits.move:290: opaque_incorrect
     =     at tests/sources/functional/emits.move:292: opaque_incorrect (spec)
@@ -213,6 +214,7 @@ error: emitted event not covered by any of the `emits` clauses
     =     at tests/sources/functional/emits.move:311: opaque_completeness_incorrect
     =     at tests/sources/functional/emits.move:312: opaque_completeness_incorrect
     =         <redacted> = <redacted>
+    =     at tests/sources/functional/emits.move:309: opaque_completeness_incorrect
     =         handle = <redacted>
     =     at tests/sources/functional/emits.move:313: opaque_completeness_incorrect
     =     at tests/sources/functional/emits.move:315: opaque_completeness_incorrect (spec)
@@ -239,6 +241,7 @@ error: emitted event not covered by any of the `emits` clauses
     =     at tests/sources/functional/emits.move:352: opaque_partial_incorrect
     =     at tests/sources/functional/emits.move:353: opaque_partial_incorrect
     =         <redacted> = <redacted>
+    =     at tests/sources/functional/emits.move:350: opaque_partial_incorrect
     =         handle = <redacted>
     =     at tests/sources/functional/emits.move:354: opaque_partial_incorrect
     =     at tests/sources/functional/emits.move:356: opaque_partial_incorrect (spec)
diff --git a/third_party/move/move-prover/tests/sources/functional/global_invariants.v2_exp b/third_party/move/move-prover/tests/sources/functional/global_invariants.v2_exp
index 3424ce5a899f2..6d7b62ce06171 100644
--- a/third_party/move/move-prover/tests/sources/functional/global_invariants.v2_exp
+++ b/third_party/move/move-prover/tests/sources/functional/global_invariants.v2_exp
@@ -28,12 +28,12 @@ error: global memory invariant does not hold
    =         result = <redacted>
    =     at ../move-stdlib/sources/signer.move:14: address_of
    =     at tests/sources/functional/global_invariants.move:55: remove_S_invalid
+   =     at tests/sources/functional/global_invariants.move:56: remove_S_invalid
    =     at ../move-stdlib/sources/signer.move:12: address_of
    =         s = <redacted>
    =     at ../move-stdlib/sources/signer.move:13: address_of
    =         result = <redacted>
    =     at ../move-stdlib/sources/signer.move:14: address_of
-   =     at tests/sources/functional/global_invariants.move:56: remove_S_invalid
    =     at tests/sources/functional/global_invariants.move:18
 
 error: global memory invariant does not hold
@@ -51,6 +51,7 @@ error: global memory invariant does not hold
    =         result = <redacted>
    =     at ../move-stdlib/sources/signer.move:14: address_of
    =     at tests/sources/functional/global_invariants.move:64: remove_R_invalid
+   =     at tests/sources/functional/global_invariants.move:65: remove_R_invalid
    =     at ../move-stdlib/sources/signer.move:12: address_of
    =         s = <redacted>
    =     at ../move-stdlib/sources/signer.move:13: address_of
diff --git a/third_party/move/move-prover/tests/sources/functional/loops_with_memory_ops.v2_exp b/third_party/move/move-prover/tests/sources/functional/loops_with_memory_ops.v2_exp
index d16421b96824a..00ce62137346b 100644
--- a/third_party/move/move-prover/tests/sources/functional/loops_with_memory_ops.v2_exp
+++ b/third_party/move/move-prover/tests/sources/functional/loops_with_memory_ops.v2_exp
@@ -15,10 +15,8 @@ error: induction case of the loop invariant does not hold
    =     at tests/sources/functional/loops_with_memory_ops.move:62: nested_loop2
    =         i = <redacted>
    =     at tests/sources/functional/loops_with_memory_ops.move:63: nested_loop2
-   =         <redacted> = <redacted>
    =         x = <redacted>
    =     at tests/sources/functional/loops_with_memory_ops.move:64: nested_loop2
-   =         <redacted> = <redacted>
    =         y = <redacted>
    =     at tests/sources/functional/loops_with_memory_ops.move:66: nested_loop2
    =     at tests/sources/functional/loops_with_memory_ops.move:67: nested_loop2
@@ -45,15 +43,14 @@ error: induction case of the loop invariant does not hold
    =     at tests/sources/functional/loops_with_memory_ops.move:81: nested_loop2
    =         a = <redacted>
    =         b = <redacted>
+   =     at tests/sources/functional/loops_with_memory_ops.move:81: nested_loop2
    =     at tests/sources/functional/loops_with_memory_ops.move:85: nested_loop2
    =         <redacted> = <redacted>
    =         i = <redacted>
    =     at tests/sources/functional/loops_with_memory_ops.move:86: nested_loop2
    =     at tests/sources/functional/loops_with_memory_ops.move:89: nested_loop2
-   =         <redacted> = <redacted>
    =         x = <redacted>
    =     at tests/sources/functional/loops_with_memory_ops.move:90: nested_loop2
-   =         <redacted> = <redacted>
    =         y = <redacted>
    =     at tests/sources/functional/loops_with_memory_ops.move:65: nested_loop2
    =     at tests/sources/functional/loops_with_memory_ops.move:67: nested_loop2
@@ -77,10 +74,8 @@ error: unknown assertion failed
    =     at tests/sources/functional/loops_with_memory_ops.move:62: nested_loop2
    =         i = <redacted>
    =     at tests/sources/functional/loops_with_memory_ops.move:63: nested_loop2
-   =         <redacted> = <redacted>
    =         x = <redacted>
    =     at tests/sources/functional/loops_with_memory_ops.move:64: nested_loop2
-   =         <redacted> = <redacted>
    =         y = <redacted>
    =     at tests/sources/functional/loops_with_memory_ops.move:66: nested_loop2
    =     at tests/sources/functional/loops_with_memory_ops.move:67: nested_loop2
diff --git a/third_party/move/move-prover/tests/sources/functional/references.v2_exp b/third_party/move/move-prover/tests/sources/functional/references.v2_exp
index 9c36421055ba0..ef8dc12e6f107 100644
--- a/third_party/move/move-prover/tests/sources/functional/references.v2_exp
+++ b/third_party/move/move-prover/tests/sources/functional/references.v2_exp
@@ -16,7 +16,6 @@ error: function does not abort under this condition
    =     at tests/sources/functional/references.move:50: mut_b
    =         b = <redacted>
    =     at tests/sources/functional/references.move:52: mut_b
-   =         b = <redacted>
    =     at tests/sources/functional/references.move:72: mut_ref_incorrect
    =         b = <redacted>
    =     at tests/sources/functional/references.move:73: mut_ref_incorrect
diff --git a/third_party/move/move-prover/tests/sources/regression/type_param_bug_121721.v2_exp b/third_party/move/move-prover/tests/sources/regression/type_param_bug_121721.v2_exp
index 554b38047cf53..aa9b2951d4375 100644
--- a/third_party/move/move-prover/tests/sources/regression/type_param_bug_121721.v2_exp
+++ b/third_party/move/move-prover/tests/sources/regression/type_param_bug_121721.v2_exp
@@ -9,7 +9,6 @@ error: global memory invariant does not hold
     =         ballot_account = <redacted>
     =         proposal = <redacted>
     =     at tests/sources/regression/type_param_bug_121721.move:84: create_ballot
-    =         <redacted> = <redacted>
     =     at ../move-stdlib/sources/signer.move:12: address_of
     =         s = <redacted>
     =     at ../move-stdlib/sources/signer.move:13: address_of