diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/field_access.move b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/field_access.move new file mode 100644 index 0000000000000..1c5f34528b151 --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/field_access.move @@ -0,0 +1,56 @@ +module 0x99::FiledAccess { + struct Inner has copy, drop { + value: u64, + } + + struct Outer has copy, drop { + inner: Inner, + } + + fun get_value(outer: &Outer): u64 { + outer.inner.value + } + + fun set_value(outer: &mut Outer, new_value: u64) { + outer.inner.value = new_value; + } + + fun get_inner_value1(inner: &Inner): u64 { + inner.value + } + + fun get_inner_value2(inner: &Inner): u64 { + inner.value + } + + // `&arg1.inner` cannot be reused + // perf_gain: 1 borrow_loc + 1 borrow_field eliminated + // new_cost: + // - `u64` flushed and copied twice + // perf_gain < new_cost, so this optimization is not applied + fun test_field_access_with_function(arg1: Outer): u64 { + get_inner_value1(&arg1.inner) + get_inner_value2(&arg1.inner) + } + + // `arg1.inner.value` cannot be reused due to the mutation between the two accesses + fun test_field_access(arg1: Outer, arg2: u64): u64 { + let x = arg1.inner.value; + arg1.inner.value += 1; + x + arg2 + arg1.inner.value + } + + // `arg1.inner.value` cannot be reused due to the mutation between the two accesses + fun test_field_access_ref(arg1: Outer, arg2: u64): u64 { + let x = arg1.inner.value; + let ref = &mut arg1.inner.value; + *ref += 1; + x + arg2 + arg1.inner.value + } + + // `set_value(&mut arg1, arg2)` may modify `arg1.inner.value`, so the two calls cannot be reused + fun test_field_access_mut_ref(arg1: Outer, arg2: u64): u64 { + set_value(&mut arg1, arg2); + set_value(&mut arg1, arg2); + arg1.inner.value + } +} diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/field_access.off.exp b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/field_access.off.exp new file mode 100644 index 0000000000000..625db64c8229f --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/field_access.off.exp @@ -0,0 +1,135 @@ + +============ disassembled file-format ================== +// Bytecode version v9 +module 0x99::FiledAccess +struct Inner has copy + drop + value: u64 + +struct Outer has copy + drop + inner: Inner + +// Function definition at index 0 +fun get_inner_value1(l0: &Inner): u64 + move_loc l0 + borrow_field Inner, value + read_ref + ret + +// Function definition at index 1 +fun get_inner_value2(l0: &Inner): u64 + move_loc l0 + borrow_field Inner, value + read_ref + ret + +// Function definition at index 2 +fun get_value(l0: &Outer): u64 + move_loc l0 + borrow_field Outer, inner + borrow_field Inner, value + read_ref + ret + +// Function definition at index 3 +fun set_value(l0: &mut Outer, l1: u64) + local l2: &mut u64 + move_loc l0 + mut_borrow_field Outer, inner + mut_borrow_field Inner, value + st_loc l2 + move_loc l1 + // @5 + move_loc l2 + write_ref + ret + +// Function definition at index 4 +fun test_field_access(l0: Outer, l1: u64): u64 + local l2: &mut u64 + borrow_loc l0 + borrow_field Outer, inner + borrow_field Inner, value + read_ref + mut_borrow_loc l0 + // @5 + mut_borrow_field Outer, inner + mut_borrow_field Inner, value + st_loc l2 + copy_loc l2 + read_ref + // @10 + ld_u64 1 + add + move_loc l2 + write_ref + move_loc l1 + // @15 + add + borrow_loc l0 + borrow_field Outer, inner + borrow_field Inner, value + read_ref + // @20 + add + ret + +// Function definition at index 5 +fun test_field_access_mut_ref(l0: Outer, l1: u64): u64 + mut_borrow_loc l0 + copy_loc l1 + call set_value + mut_borrow_loc l0 + move_loc l1 + // @5 + call set_value + borrow_loc l0 + borrow_field Outer, inner + borrow_field Inner, value + read_ref + // @10 + ret + +// Function definition at index 6 +fun test_field_access_ref(l0: Outer, l1: u64): u64 + local l2: &mut u64 + borrow_loc l0 + borrow_field Outer, inner + borrow_field Inner, value + read_ref + mut_borrow_loc l0 + // @5 + mut_borrow_field Outer, inner + mut_borrow_field Inner, value + st_loc l2 + copy_loc l2 + read_ref + // @10 + ld_u64 1 + add + move_loc l2 + write_ref + move_loc l1 + // @15 + add + borrow_loc l0 + borrow_field Outer, inner + borrow_field Inner, value + read_ref + // @20 + add + ret + +// Function definition at index 7 +fun test_field_access_with_function(l0: Outer): u64 + borrow_loc l0 + borrow_field Outer, inner + call get_inner_value1 + borrow_loc l0 + borrow_field Outer, inner + // @5 + call get_inner_value2 + add + ret + + +============ bytecode verification succeeded ======== diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/field_access.on.exp b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/field_access.on.exp new file mode 100644 index 0000000000000..5ec600c5763dd --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/field_access.on.exp @@ -0,0 +1,930 @@ +============ after ReachingDefProcessor: ================ + +[variant baseline] +fun FiledAccess::get_inner_value1($t0: &0x99::FiledAccess::Inner): u64 { + var $t1: u64 + var $t2: &u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t2 := borrow_field<0x99::FiledAccess::Inner>.value($t0) + # live vars: $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [$t2 => #2] + # #2 + # + # #root + # + # + 1: $t1 := read_ref($t2) + # live vars: $t1 + # reaching instruction #2: `t1` @ {1}, `t2` @ {0} + # refs: [] + # + 2: return $t1 +} + + +[variant baseline] +fun FiledAccess::get_inner_value2($t0: &0x99::FiledAccess::Inner): u64 { + var $t1: u64 + var $t2: &u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t2 := borrow_field<0x99::FiledAccess::Inner>.value($t0) + # live vars: $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [$t2 => #2] + # #2 + # + # #root + # + # + 1: $t1 := read_ref($t2) + # live vars: $t1 + # reaching instruction #2: `t1` @ {1}, `t2` @ {0} + # refs: [] + # + 2: return $t1 +} + + +[variant baseline] +fun FiledAccess::get_value($t0: &0x99::FiledAccess::Outer): u64 { + var $t1: u64 + var $t2: &0x99::FiledAccess::Inner + var $t3: &u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t2 := borrow_field<0x99::FiledAccess::Outer>.inner($t0) + # live vars: $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [$t2 => #2] + # #2 + # + # #root + # + # + 1: $t3 := borrow_field<0x99::FiledAccess::Inner>.value($t2) + # live vars: $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [$t3 => #3] + # #3 + # + # #root + # + # + 2: $t1 := read_ref($t3) + # live vars: $t1 + # reaching instruction #3: `t1` @ {2}, `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 3: return $t1 +} + + +[variant baseline] +fun FiledAccess::set_value($t0: &mut 0x99::FiledAccess::Outer, $t1: u64) { + var $t2: &mut u64 + var $t3: &mut 0x99::FiledAccess::Inner + # live vars: $t0, $t1 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t3 := borrow_field<0x99::FiledAccess::Outer>.inner($t0) + # flush: $t1 + # live vars: $t1, $t3 + # reaching instruction #1: `t3` @ {0} + # refs: [$t3 => #3] + # #3 + # + # #root + # + # + 1: $t2 := borrow_field<0x99::FiledAccess::Inner>.value($t3) + # live vars: $t1, $t2 + # reaching instruction #2: `t2` @ {1}, `t3` @ {0} + # refs: [$t2 => #2] + # #2 + # + # #root + # + # + 2: write_ref($t2, $t1) + # live vars: + # reaching instruction #3: `t2` @ {1}, `t3` @ {0} + # refs: [] + # + 3: return () +} + + +[variant baseline] +fun FiledAccess::test_field_access($t0: 0x99::FiledAccess::Outer, $t1: u64): u64 { + var $t2: u64 + var $t3: u64 + var $t4: &0x99::FiledAccess::Inner + var $t5: &0x99::FiledAccess::Outer + var $t6: &u64 + var $t7: &mut u64 + var $t8: &mut 0x99::FiledAccess::Inner + var $t9: &mut 0x99::FiledAccess::Outer + var $t10: u64 + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: u64 + var $t16: &0x99::FiledAccess::Inner + var $t17: &0x99::FiledAccess::Outer + var $t18: &u64 + # live vars: $t0, $t1 + # reaching instruction #0: + # refs: [] + # + 0: $t5 := borrow_local($t0) + # live vars: $t0, $t1, $t5 + # reaching instruction #1: `t5` @ {0} + # refs: [$t5 => #5] + # #5 + # + # #root + # => #5 via [local `arg1`] at line 37 + # + 1: $t4 := borrow_field<0x99::FiledAccess::Outer>.inner($t5) + # live vars: $t0, $t1, $t4 + # reaching instruction #2: `t4` @ {1}, `t5` @ {0} + # refs: [$t4 => #4] + # #4 + # + # #root + # => #4 via [local `arg1`, field `inner`] at line 37 + # + 2: $t6 := borrow_field<0x99::FiledAccess::Inner>.value($t4) + # live vars: $t0, $t1, $t6 + # reaching instruction #3: `t4` @ {1}, `t5` @ {0}, `t6` @ {2} + # refs: [$t6 => #6] + # #6 + # + # #root + # => #6 via [local `arg1`, field `inner`, field `value`] at line 37 + # + 3: $t3 := read_ref($t6) + # live vars: $t0, $t1, $t3 + # reaching instruction #4: `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2} + # refs: [] + # + 4: $t9 := borrow_local($t0) + # live vars: $t0, $t1, $t3, $t9 + # reaching instruction #5: `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t9` @ {4} + # refs: [$t9 => #9] + # #9 + # + # #root + # => (mut) #9 via [local `arg1`] at line 38 + # + 5: $t8 := borrow_field<0x99::FiledAccess::Outer>.inner($t9) + # flush: $t7, $t10 + # live vars: $t0, $t1, $t3, $t8 + # reaching instruction #6: `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t8` @ {5}, `t9` @ {4} + # refs: [$t8 => #8] + # #8 + # + # #root + # => (mut) #8 via [local `arg1`, field `inner`] at line 38 + # + 6: $t7 := borrow_field<0x99::FiledAccess::Inner>.value($t8) + # live vars: $t0, $t1, $t3, $t7 + # reaching instruction #7: `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t7` @ {6}, `t8` @ {5}, `t9` @ {4} + # refs: [$t7 => #7] + # #7 + # + # #root + # => (mut) #7 via [local `arg1`, field `inner`, field `value`] at line 38 + # + 7: $t11 := read_ref($t7) + # live vars: $t0, $t1, $t3, $t7, $t11 + # reaching instruction #8: `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t7` @ {6}, `t8` @ {5}, `t9` @ {4}, `t11` @ {7} + # refs: [$t7 => #7] + # #7 + # + # #root + # => (mut) #7 via [local `arg1`, field `inner`, field `value`] at line 38 + # + 8: $t12 := 1 + # live vars: $t0, $t1, $t3, $t7, $t11, $t12 + # reaching instruction #9: `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t7` @ {6}, `t8` @ {5}, `t9` @ {4}, `t11` @ {7}, `t12` @ {8} + # refs: [$t7 => #7] + # #7 + # + # #root + # => (mut) #7 via [local `arg1`, field `inner`, field `value`] at line 38 + # + 9: $t10 := +($t11, $t12) + # live vars: $t0, $t1, $t3, $t7, $t10 + # reaching instruction #10: `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t7` @ {6}, `t8` @ {5}, `t9` @ {4}, `t10` @ {9}, `t11` @ {7}, `t12` @ {8} + # refs: [$t7 => #7] + # #7 + # + # #root + # => (mut) #7 via [local `arg1`, field `inner`, field `value`] at line 38 + # + 10: write_ref($t7, $t10) + # live vars: $t0, $t1, $t3 + # reaching instruction #11: `t0` @ {10}, `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t7` @ {6}, `t8` @ {5}, `t9` @ {4}, `t10` @ {9}, `t11` @ {7}, `t12` @ {8} + # refs: [] + # + 11: $t14 := infer($t3) + # live vars: $t0, $t1, $t14 + # reaching instruction #12: `t0` @ {10}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t7` @ {6}, `t8` @ {5}, `t9` @ {4}, `t10` @ {9}, `t11` @ {7}, `t12` @ {8}, `t14` @ {11} + # refs: [] + # + 12: $t13 := +($t14, $t1) + # live vars: $t0, $t13 + # reaching instruction #13: `t0` @ {10}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t7` @ {6}, `t8` @ {5}, `t9` @ {4}, `t10` @ {9}, `t11` @ {7}, `t12` @ {8}, `t13` @ {12}, `t14` @ {11} + # refs: [] + # + 13: $t17 := borrow_local($t0) + # live vars: $t13, $t17 + # reaching instruction #14: `t0` @ {10}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t7` @ {6}, `t8` @ {5}, `t9` @ {4}, `t10` @ {9}, `t11` @ {7}, `t12` @ {8}, `t13` @ {12}, `t14` @ {11}, `t17` @ {13} + # refs: [$t17 => #17] + # #17 + # + # #root + # => #17 via [local `arg1`] at line 39 + # + 14: $t16 := borrow_field<0x99::FiledAccess::Outer>.inner($t17) + # live vars: $t13, $t16 + # reaching instruction #15: `t0` @ {10}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t7` @ {6}, `t8` @ {5}, `t9` @ {4}, `t10` @ {9}, `t11` @ {7}, `t12` @ {8}, `t13` @ {12}, `t14` @ {11}, `t16` @ {14}, `t17` @ {13} + # refs: [$t16 => #16] + # #16 + # + # #root + # => #16 via [local `arg1`, field `inner`] at line 39 + # + 15: $t18 := borrow_field<0x99::FiledAccess::Inner>.value($t16) + # live vars: $t13, $t18 + # reaching instruction #16: `t0` @ {10}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t7` @ {6}, `t8` @ {5}, `t9` @ {4}, `t10` @ {9}, `t11` @ {7}, `t12` @ {8}, `t13` @ {12}, `t14` @ {11}, `t16` @ {14}, `t17` @ {13}, `t18` @ {15} + # refs: [$t18 => #18] + # #18 + # + # #root + # => #18 via [local `arg1`, field `inner`, field `value`] at line 39 + # + 16: $t15 := read_ref($t18) + # live vars: $t13, $t15 + # reaching instruction #17: `t0` @ {10}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t7` @ {6}, `t8` @ {5}, `t9` @ {4}, `t10` @ {9}, `t11` @ {7}, `t12` @ {8}, `t13` @ {12}, `t14` @ {11}, `t15` @ {16}, `t16` @ {14}, `t17` @ {13}, `t18` @ {15} + # refs: [] + # + 17: $t2 := +($t13, $t15) + # live vars: $t2 + # reaching instruction #18: `t0` @ {10}, `t2` @ {17}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t7` @ {6}, `t8` @ {5}, `t9` @ {4}, `t10` @ {9}, `t11` @ {7}, `t12` @ {8}, `t13` @ {12}, `t14` @ {11}, `t15` @ {16}, `t16` @ {14}, `t17` @ {13}, `t18` @ {15} + # refs: [] + # + 18: return $t2 +} + + +[variant baseline] +fun FiledAccess::test_field_access_mut_ref($t0: 0x99::FiledAccess::Outer, $t1: u64): u64 { + var $t2: u64 + var $t3: &mut 0x99::FiledAccess::Outer + var $t4: &mut 0x99::FiledAccess::Outer + var $t5: &0x99::FiledAccess::Inner + var $t6: &0x99::FiledAccess::Outer + var $t7: &u64 + # live vars: $t0, $t1 + # reaching instruction #0: + # refs: [] + # + 0: $t3 := borrow_local($t0) + # live vars: $t0, $t1, $t3 + # reaching instruction #1: `t3` @ {0} + # refs: [$t3 => #3] + # #3 + # + # #root + # => (mut) #3 via [local `arg1`] at line 52 + # + 1: FiledAccess::set_value($t3, $t1) + # live vars: $t0, $t1 + # reaching instruction #2: `t0` @ {1}, `t3` @ {0} + # refs: [] + # + 2: $t4 := borrow_local($t0) + # live vars: $t0, $t1, $t4 + # reaching instruction #3: `t0` @ {1}, `t3` @ {0}, `t4` @ {2} + # refs: [$t4 => #4] + # #4 + # + # #root + # => (mut) #4 via [local `arg1`] at line 53 + # + 3: FiledAccess::set_value($t4, $t1) + # live vars: $t0 + # reaching instruction #4: `t0` @ {3}, `t3` @ {0}, `t4` @ {2} + # refs: [] + # + 4: $t6 := borrow_local($t0) + # live vars: $t6 + # reaching instruction #5: `t0` @ {3}, `t3` @ {0}, `t4` @ {2}, `t6` @ {4} + # refs: [$t6 => #6] + # #6 + # + # #root + # => #6 via [local `arg1`] at line 54 + # + 5: $t5 := borrow_field<0x99::FiledAccess::Outer>.inner($t6) + # live vars: $t5 + # reaching instruction #6: `t0` @ {3}, `t3` @ {0}, `t4` @ {2}, `t5` @ {5}, `t6` @ {4} + # refs: [$t5 => #5] + # #5 + # + # #root + # => #5 via [local `arg1`, field `inner`] at line 54 + # + 6: $t7 := borrow_field<0x99::FiledAccess::Inner>.value($t5) + # live vars: $t7 + # reaching instruction #7: `t0` @ {3}, `t3` @ {0}, `t4` @ {2}, `t5` @ {5}, `t6` @ {4}, `t7` @ {6} + # refs: [$t7 => #7] + # #7 + # + # #root + # => #7 via [local `arg1`, field `inner`, field `value`] at line 54 + # + 7: $t2 := read_ref($t7) + # live vars: $t2 + # reaching instruction #8: `t0` @ {3}, `t2` @ {7}, `t3` @ {0}, `t4` @ {2}, `t5` @ {5}, `t6` @ {4}, `t7` @ {6} + # refs: [] + # + 8: return $t2 +} + + +[variant baseline] +fun FiledAccess::test_field_access_ref($t0: 0x99::FiledAccess::Outer, $t1: u64): u64 { + var $t2: u64 + var $t3: u64 + var $t4: &0x99::FiledAccess::Inner + var $t5: &0x99::FiledAccess::Outer + var $t6: &u64 + var $t7: &mut u64 + var $t8: &mut 0x99::FiledAccess::Inner + var $t9: &mut 0x99::FiledAccess::Outer + var $t10: &mut u64 + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: u64 + var $t16: u64 + var $t17: &0x99::FiledAccess::Inner + var $t18: &0x99::FiledAccess::Outer + var $t19: &u64 + # live vars: $t0, $t1 + # reaching instruction #0: + # refs: [] + # + 0: $t5 := borrow_local($t0) + # live vars: $t0, $t1, $t5 + # reaching instruction #1: `t5` @ {0} + # refs: [$t5 => #5] + # #5 + # + # #root + # => #5 via [local `arg1`] at line 44 + # + 1: $t4 := borrow_field<0x99::FiledAccess::Outer>.inner($t5) + # live vars: $t0, $t1, $t4 + # reaching instruction #2: `t4` @ {1}, `t5` @ {0} + # refs: [$t4 => #4] + # #4 + # + # #root + # => #4 via [local `arg1`, field `inner`] at line 44 + # + 2: $t6 := borrow_field<0x99::FiledAccess::Inner>.value($t4) + # live vars: $t0, $t1, $t6 + # reaching instruction #3: `t4` @ {1}, `t5` @ {0}, `t6` @ {2} + # refs: [$t6 => #6] + # #6 + # + # #root + # => #6 via [local `arg1`, field `inner`, field `value`] at line 44 + # + 3: $t3 := read_ref($t6) + # live vars: $t0, $t1, $t3 + # reaching instruction #4: `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2} + # refs: [] + # + 4: $t9 := borrow_local($t0) + # live vars: $t0, $t1, $t3, $t9 + # reaching instruction #5: `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t9` @ {4} + # refs: [$t9 => #9] + # #9 + # + # #root + # => (mut) #9 via [local `arg1`] at line 45 + # + 5: $t8 := borrow_field<0x99::FiledAccess::Outer>.inner($t9) + # live vars: $t0, $t1, $t3, $t8 + # reaching instruction #6: `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t8` @ {5}, `t9` @ {4} + # refs: [$t8 => #8] + # #8 + # + # #root + # => (mut) #8 via [local `arg1`, field `inner`] at line 45 + # + 6: $t7 := borrow_field<0x99::FiledAccess::Inner>.value($t8) + # flush: $t10, $t11 + # live vars: $t0, $t1, $t3, $t7 + # reaching instruction #7: `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t7` @ {6}, `t8` @ {5}, `t9` @ {4} + # refs: [$t7 => #7] + # #7 + # + # #root + # => (mut) #7 via [local `arg1`, field `inner`, field `value`] at line 45 + # + 7: $t10 := infer($t7) + # live vars: $t0, $t1, $t3, $t10 + # reaching instruction #8: `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t8` @ {5}, `t9` @ {4}, `t10` @ {7} + # refs: [$t10 => #10] + # #10 + # + # #root + # => (mut) #10 via [local `arg1`, field `inner`, field `value`] at line 45 + # + 8: $t12 := read_ref($t10) + # live vars: $t0, $t1, $t3, $t10, $t12 + # reaching instruction #9: `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t8` @ {5}, `t9` @ {4}, `t10` @ {7}, `t12` @ {8} + # refs: [$t10 => #10] + # #10 + # + # #root + # => (mut) #10 via [local `arg1`, field `inner`, field `value`] at line 45 + # + 9: $t13 := 1 + # live vars: $t0, $t1, $t3, $t10, $t12, $t13 + # reaching instruction #10: `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t8` @ {5}, `t9` @ {4}, `t10` @ {7}, `t12` @ {8}, `t13` @ {9} + # refs: [$t10 => #10] + # #10 + # + # #root + # => (mut) #10 via [local `arg1`, field `inner`, field `value`] at line 45 + # + 10: $t11 := +($t12, $t13) + # live vars: $t0, $t1, $t3, $t10, $t11 + # reaching instruction #11: `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t8` @ {5}, `t9` @ {4}, `t10` @ {7}, `t11` @ {10}, `t12` @ {8}, `t13` @ {9} + # refs: [$t10 => #10] + # #10 + # + # #root + # => (mut) #10 via [local `arg1`, field `inner`, field `value`] at line 45 + # + 11: write_ref($t10, $t11) + # live vars: $t0, $t1, $t3 + # reaching instruction #12: `t0` @ {11}, `t3` @ {3}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t8` @ {5}, `t9` @ {4}, `t10` @ {7}, `t11` @ {10}, `t12` @ {8}, `t13` @ {9} + # refs: [] + # + 12: $t15 := infer($t3) + # live vars: $t0, $t1, $t15 + # reaching instruction #13: `t0` @ {11}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t8` @ {5}, `t9` @ {4}, `t10` @ {7}, `t11` @ {10}, `t12` @ {8}, `t13` @ {9}, `t15` @ {12} + # refs: [] + # + 13: $t14 := +($t15, $t1) + # live vars: $t0, $t14 + # reaching instruction #14: `t0` @ {11}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t8` @ {5}, `t9` @ {4}, `t10` @ {7}, `t11` @ {10}, `t12` @ {8}, `t13` @ {9}, `t14` @ {13}, `t15` @ {12} + # refs: [] + # + 14: $t18 := borrow_local($t0) + # live vars: $t14, $t18 + # reaching instruction #15: `t0` @ {11}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t8` @ {5}, `t9` @ {4}, `t10` @ {7}, `t11` @ {10}, `t12` @ {8}, `t13` @ {9}, `t14` @ {13}, `t15` @ {12}, `t18` @ {14} + # refs: [$t18 => #18] + # #18 + # + # #root + # => #18 via [local `arg1`] at line 47 + # + 15: $t17 := borrow_field<0x99::FiledAccess::Outer>.inner($t18) + # live vars: $t14, $t17 + # reaching instruction #16: `t0` @ {11}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t8` @ {5}, `t9` @ {4}, `t10` @ {7}, `t11` @ {10}, `t12` @ {8}, `t13` @ {9}, `t14` @ {13}, `t15` @ {12}, `t17` @ {15}, `t18` @ {14} + # refs: [$t17 => #17] + # #17 + # + # #root + # => #17 via [local `arg1`, field `inner`] at line 47 + # + 16: $t19 := borrow_field<0x99::FiledAccess::Inner>.value($t17) + # live vars: $t14, $t19 + # reaching instruction #17: `t0` @ {11}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t8` @ {5}, `t9` @ {4}, `t10` @ {7}, `t11` @ {10}, `t12` @ {8}, `t13` @ {9}, `t14` @ {13}, `t15` @ {12}, `t17` @ {15}, `t18` @ {14}, `t19` @ {16} + # refs: [$t19 => #19] + # #19 + # + # #root + # => #19 via [local `arg1`, field `inner`, field `value`] at line 47 + # + 17: $t16 := read_ref($t19) + # live vars: $t14, $t16 + # reaching instruction #18: `t0` @ {11}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t8` @ {5}, `t9` @ {4}, `t10` @ {7}, `t11` @ {10}, `t12` @ {8}, `t13` @ {9}, `t14` @ {13}, `t15` @ {12}, `t16` @ {17}, `t17` @ {15}, `t18` @ {14}, `t19` @ {16} + # refs: [] + # + 18: $t2 := +($t14, $t16) + # live vars: $t2 + # reaching instruction #19: `t0` @ {11}, `t2` @ {18}, `t4` @ {1}, `t5` @ {0}, `t6` @ {2}, `t8` @ {5}, `t9` @ {4}, `t10` @ {7}, `t11` @ {10}, `t12` @ {8}, `t13` @ {9}, `t14` @ {13}, `t15` @ {12}, `t16` @ {17}, `t17` @ {15}, `t18` @ {14}, `t19` @ {16} + # refs: [] + # + 19: return $t2 +} + + +[variant baseline] +fun FiledAccess::test_field_access_with_function($t0: 0x99::FiledAccess::Outer): u64 { + var $t1: u64 + var $t2: u64 + var $t3: &0x99::FiledAccess::Inner + var $t4: &0x99::FiledAccess::Outer + var $t5: u64 + var $t6: &0x99::FiledAccess::Inner + var $t7: &0x99::FiledAccess::Outer + # live vars: $t0 + # reaching instruction #0: + # refs: [] + # + 0: $t4 := borrow_local($t0) + # live vars: $t0, $t4 + # reaching instruction #1: `t4` @ {0} + # refs: [$t4 => #4] + # #4 + # + # #root + # => #4 via [local `arg1`] at line 32 + # + 1: $t3 := borrow_field<0x99::FiledAccess::Outer>.inner($t4) + # live vars: $t0, $t3 + # reaching instruction #2: `t3` @ {1}, `t4` @ {0} + # refs: [$t3 => #3] + # #3 + # + # #root + # => #3 via [local `arg1`, field `inner`] at line 32 + # + 2: $t2 := FiledAccess::get_inner_value1($t3) + # live vars: $t0, $t2 + # reaching instruction #3: `t2` @ {2}, `t3` @ {1}, `t4` @ {0} + # refs: [] + # + 3: $t7 := borrow_local($t0) + # live vars: $t2, $t7 + # reaching instruction #4: `t2` @ {2}, `t3` @ {1}, `t4` @ {0}, `t7` @ {3} + # refs: [$t7 => #7] + # #7 + # + # #root + # => #7 via [local `arg1`] at line 32 + # + 4: $t6 := borrow_field<0x99::FiledAccess::Outer>.inner($t7) + # live vars: $t2, $t6 + # reaching instruction #5: `t2` @ {2}, `t3` @ {1}, `t4` @ {0}, `t6` @ {4}, `t7` @ {3} + # refs: [$t6 => #6] + # #6 + # + # #root + # => #6 via [local `arg1`, field `inner`] at line 32 + # + 5: $t5 := FiledAccess::get_inner_value2($t6) + # live vars: $t2, $t5 + # reaching instruction #6: `t2` @ {2}, `t3` @ {1}, `t4` @ {0}, `t5` @ {5}, `t6` @ {4}, `t7` @ {3} + # refs: [] + # + 6: $t1 := +($t2, $t5) + # live vars: $t1 + # reaching instruction #7: `t1` @ {6}, `t2` @ {2}, `t3` @ {1}, `t4` @ {0}, `t5` @ {5}, `t6` @ {4}, `t7` @ {3} + # refs: [] + # + 7: return $t1 +} + +============ after CommonSubexpElimination: ================ + +[variant baseline] +fun FiledAccess::get_inner_value1($t0: &0x99::FiledAccess::Inner): u64 { + var $t1: u64 + var $t2: &u64 + 0: $t2 := borrow_field<0x99::FiledAccess::Inner>.value($t0) + 1: $t1 := read_ref($t2) + 2: return $t1 +} + + +[variant baseline] +fun FiledAccess::get_inner_value2($t0: &0x99::FiledAccess::Inner): u64 { + var $t1: u64 + var $t2: &u64 + 0: $t2 := borrow_field<0x99::FiledAccess::Inner>.value($t0) + 1: $t1 := read_ref($t2) + 2: return $t1 +} + + +[variant baseline] +fun FiledAccess::get_value($t0: &0x99::FiledAccess::Outer): u64 { + var $t1: u64 + var $t2: &0x99::FiledAccess::Inner + var $t3: &u64 + 0: $t2 := borrow_field<0x99::FiledAccess::Outer>.inner($t0) + 1: $t3 := borrow_field<0x99::FiledAccess::Inner>.value($t2) + 2: $t1 := read_ref($t3) + 3: return $t1 +} + + +[variant baseline] +fun FiledAccess::set_value($t0: &mut 0x99::FiledAccess::Outer, $t1: u64) { + var $t2: &mut u64 + var $t3: &mut 0x99::FiledAccess::Inner + 0: $t3 := borrow_field<0x99::FiledAccess::Outer>.inner($t0) + 1: $t2 := borrow_field<0x99::FiledAccess::Inner>.value($t3) + 2: write_ref($t2, $t1) + 3: return () +} + + +[variant baseline] +fun FiledAccess::test_field_access($t0: 0x99::FiledAccess::Outer, $t1: u64): u64 { + var $t2: u64 + var $t3: u64 + var $t4: &0x99::FiledAccess::Inner + var $t5: &0x99::FiledAccess::Outer + var $t6: &u64 + var $t7: &mut u64 + var $t8: &mut 0x99::FiledAccess::Inner + var $t9: &mut 0x99::FiledAccess::Outer + var $t10: u64 + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: u64 + var $t16: &0x99::FiledAccess::Inner + var $t17: &0x99::FiledAccess::Outer + var $t18: &u64 + 0: $t5 := borrow_local($t0) + 1: $t4 := borrow_field<0x99::FiledAccess::Outer>.inner($t5) + 2: $t6 := borrow_field<0x99::FiledAccess::Inner>.value($t4) + 3: $t3 := read_ref($t6) + 4: $t9 := borrow_local($t0) + 5: $t8 := borrow_field<0x99::FiledAccess::Outer>.inner($t9) + 6: $t7 := borrow_field<0x99::FiledAccess::Inner>.value($t8) + 7: $t11 := read_ref($t7) + 8: $t12 := 1 + 9: $t10 := +($t11, $t12) + 10: write_ref($t7, $t10) + 11: $t14 := infer($t3) + 12: $t13 := +($t14, $t1) + 13: $t17 := borrow_local($t0) + 14: $t16 := borrow_field<0x99::FiledAccess::Outer>.inner($t17) + 15: $t18 := borrow_field<0x99::FiledAccess::Inner>.value($t16) + 16: $t15 := read_ref($t18) + 17: $t2 := +($t13, $t15) + 18: return $t2 +} + + +[variant baseline] +fun FiledAccess::test_field_access_mut_ref($t0: 0x99::FiledAccess::Outer, $t1: u64): u64 { + var $t2: u64 + var $t3: &mut 0x99::FiledAccess::Outer + var $t4: &mut 0x99::FiledAccess::Outer + var $t5: &0x99::FiledAccess::Inner + var $t6: &0x99::FiledAccess::Outer + var $t7: &u64 + 0: $t3 := borrow_local($t0) + 1: FiledAccess::set_value($t3, $t1) + 2: $t4 := borrow_local($t0) + 3: FiledAccess::set_value($t4, $t1) + 4: $t6 := borrow_local($t0) + 5: $t5 := borrow_field<0x99::FiledAccess::Outer>.inner($t6) + 6: $t7 := borrow_field<0x99::FiledAccess::Inner>.value($t5) + 7: $t2 := read_ref($t7) + 8: return $t2 +} + + +[variant baseline] +fun FiledAccess::test_field_access_ref($t0: 0x99::FiledAccess::Outer, $t1: u64): u64 { + var $t2: u64 + var $t3: u64 + var $t4: &0x99::FiledAccess::Inner + var $t5: &0x99::FiledAccess::Outer + var $t6: &u64 + var $t7: &mut u64 + var $t8: &mut 0x99::FiledAccess::Inner + var $t9: &mut 0x99::FiledAccess::Outer + var $t10: &mut u64 + var $t11: u64 + var $t12: u64 + var $t13: u64 + var $t14: u64 + var $t15: u64 + var $t16: u64 + var $t17: &0x99::FiledAccess::Inner + var $t18: &0x99::FiledAccess::Outer + var $t19: &u64 + 0: $t5 := borrow_local($t0) + 1: $t4 := borrow_field<0x99::FiledAccess::Outer>.inner($t5) + 2: $t6 := borrow_field<0x99::FiledAccess::Inner>.value($t4) + 3: $t3 := read_ref($t6) + 4: $t9 := borrow_local($t0) + 5: $t8 := borrow_field<0x99::FiledAccess::Outer>.inner($t9) + 6: $t7 := borrow_field<0x99::FiledAccess::Inner>.value($t8) + 7: $t10 := infer($t7) + 8: $t12 := read_ref($t10) + 9: $t13 := 1 + 10: $t11 := +($t12, $t13) + 11: write_ref($t10, $t11) + 12: $t15 := infer($t3) + 13: $t14 := +($t15, $t1) + 14: $t18 := borrow_local($t0) + 15: $t17 := borrow_field<0x99::FiledAccess::Outer>.inner($t18) + 16: $t19 := borrow_field<0x99::FiledAccess::Inner>.value($t17) + 17: $t16 := read_ref($t19) + 18: $t2 := +($t14, $t16) + 19: return $t2 +} + + +[variant baseline] +fun FiledAccess::test_field_access_with_function($t0: 0x99::FiledAccess::Outer): u64 { + var $t1: u64 + var $t2: u64 + var $t3: &0x99::FiledAccess::Inner + var $t4: &0x99::FiledAccess::Outer + var $t5: u64 + var $t6: &0x99::FiledAccess::Inner + var $t7: &0x99::FiledAccess::Outer + 0: $t4 := borrow_local($t0) + 1: $t3 := borrow_field<0x99::FiledAccess::Outer>.inner($t4) + 2: $t2 := FiledAccess::get_inner_value1($t3) + 3: $t7 := borrow_local($t0) + 4: $t6 := borrow_field<0x99::FiledAccess::Outer>.inner($t7) + 5: $t5 := FiledAccess::get_inner_value2($t6) + 6: $t1 := +($t2, $t5) + 7: return $t1 +} + + +============ disassembled file-format ================== +// Bytecode version v9 +module 0x99::FiledAccess +struct Inner has copy + drop + value: u64 + +struct Outer has copy + drop + inner: Inner + +// Function definition at index 0 +fun get_inner_value1(l0: &Inner): u64 + move_loc l0 + borrow_field Inner, value + read_ref + ret + +// Function definition at index 1 +fun get_inner_value2(l0: &Inner): u64 + move_loc l0 + borrow_field Inner, value + read_ref + ret + +// Function definition at index 2 +fun get_value(l0: &Outer): u64 + move_loc l0 + borrow_field Outer, inner + borrow_field Inner, value + read_ref + ret + +// Function definition at index 3 +fun set_value(l0: &mut Outer, l1: u64) + local l2: &mut u64 + move_loc l0 + mut_borrow_field Outer, inner + mut_borrow_field Inner, value + st_loc l2 + move_loc l1 + // @5 + move_loc l2 + write_ref + ret + +// Function definition at index 4 +fun test_field_access(l0: Outer, l1: u64): u64 + local l2: &mut u64 + borrow_loc l0 + borrow_field Outer, inner + borrow_field Inner, value + read_ref + mut_borrow_loc l0 + // @5 + mut_borrow_field Outer, inner + mut_borrow_field Inner, value + st_loc l2 + copy_loc l2 + read_ref + // @10 + ld_u64 1 + add + move_loc l2 + write_ref + move_loc l1 + // @15 + add + borrow_loc l0 + borrow_field Outer, inner + borrow_field Inner, value + read_ref + // @20 + add + ret + +// Function definition at index 5 +fun test_field_access_mut_ref(l0: Outer, l1: u64): u64 + mut_borrow_loc l0 + copy_loc l1 + call set_value + mut_borrow_loc l0 + move_loc l1 + // @5 + call set_value + borrow_loc l0 + borrow_field Outer, inner + borrow_field Inner, value + read_ref + // @10 + ret + +// Function definition at index 6 +fun test_field_access_ref(l0: Outer, l1: u64): u64 + local l2: &mut u64 + borrow_loc l0 + borrow_field Outer, inner + borrow_field Inner, value + read_ref + mut_borrow_loc l0 + // @5 + mut_borrow_field Outer, inner + mut_borrow_field Inner, value + st_loc l2 + copy_loc l2 + read_ref + // @10 + ld_u64 1 + add + move_loc l2 + write_ref + move_loc l1 + // @15 + add + borrow_loc l0 + borrow_field Outer, inner + borrow_field Inner, value + read_ref + // @20 + add + ret + +// Function definition at index 7 +fun test_field_access_with_function(l0: Outer): u64 + borrow_loc l0 + borrow_field Outer, inner + call get_inner_value1 + borrow_loc l0 + borrow_field Outer, inner + // @5 + call get_inner_value2 + add + ret + + +============ bytecode verification succeeded ======== diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/function_call.move b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/function_call.move new file mode 100644 index 0000000000000..edebc0b68d012 --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/function_call.move @@ -0,0 +1,31 @@ +module 0x99::FunctionCall { + + struct S has key { + val: u64, + } + + fun foo_ref(x: &u64): u64 { + *x + 1 + } + + fun foo_mut_ref(x: &mut u64): u64 { + *x + 1 + } + + fun foo_global(account: &signer, x: &u64): u64 { + move_to(account, S { val: 42 }); + *x + 1 + } + + // `foo_mut_ref(y)` cannot be reused because `ref` is a mutable reference + fun bar_mut_ref(y: u64): u64 { + let ref = &mut y; + foo_mut_ref(ref) + foo_mut_ref(ref) + } + + // `foo_global(y)` cannot be reused because `foo_global` access global storage + fun bar_global(account: &signer, y: u64): u64 { + let ref = &y; + foo_global(account, ref) + foo_global(account, ref) + } +} diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/function_call.off.exp b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/function_call.off.exp new file mode 100644 index 0000000000000..8ae7cb7b6e314 --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/function_call.off.exp @@ -0,0 +1,66 @@ + +============ disassembled file-format ================== +// Bytecode version v9 +module 0x99::FunctionCall +struct S has key + val: u64 + +// Function definition at index 0 +fun bar_global(l0: &signer, l1: u64): u64 + local l2: &u64 + borrow_loc l1 + st_loc l2 + copy_loc l0 + copy_loc l2 + call foo_global + // @5 + move_loc l0 + move_loc l2 + call foo_global + add + ret + +// Function definition at index 1 +fun bar_mut_ref(l0: u64): u64 + local l1: &mut u64 + mut_borrow_loc l0 + st_loc l1 + copy_loc l1 + call foo_mut_ref + move_loc l1 + // @5 + call foo_mut_ref + add + ret + +// Function definition at index 2 +fun foo_global(l0: &signer, l1: &u64): u64 + move_loc l0 + ld_u64 42 + pack S + move_to S + move_loc l1 + // @5 + read_ref + ld_u64 1 + add + ret + +// Function definition at index 3 +fun foo_mut_ref(l0: &mut u64): u64 + move_loc l0 + read_ref + ld_u64 1 + add + ret + +// Function definition at index 4 +fun foo_ref(l0: &u64): u64 + move_loc l0 + read_ref + ld_u64 1 + add + ret + + +============ bytecode verification succeeded ======== diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/function_call.on.exp b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/function_call.on.exp new file mode 100644 index 0000000000000..edbc373c40fbf --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/function_call.on.exp @@ -0,0 +1,406 @@ +============ after ReachingDefProcessor: ================ + +[variant baseline] +fun FunctionCall::bar_global($t0: &signer, $t1: u64): u64 { + var $t2: u64 + var $t3: &u64 + var $t4: u64 + var $t5: &signer + var $t6: u64 + var $t7: &signer + # flush: $t3 + # live vars: $t0, $t1 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t3 := borrow_local($t1) + # live vars: $t0, $t3 + # reaching instruction #1: `t3` @ {0} + # refs: [$t0 => #0, $t3 => #3] + # #0 + # + # #3 + # + # #root + # => #3 via [local `y`] at line 28 + # + 1: $t5 := infer($t0) + # live vars: $t0, $t3, $t5 + # reaching instruction #2: `t3` @ {0}, `t5` @ {1} + # refs: [$t0 => #0, $t3 => #3, $t5 => #5] + # #0 + # => #5 via [] at line 29 + # #3 + # + # #5 + # + # #root + # => #3 via [local `y`] at line 28 + # + 2: $t4 := FunctionCall::foo_global($t5, $t3) + # live vars: $t0, $t3, $t4 + # reaching instruction #3: `t3` @ {0}, `t4` @ {2}, `t5` @ {1}, `Struct QualifiedId { module_id: ModuleId(1), id: StructId(Symbol(133)) }` @ {2} + # refs: [$t0 => #0, $t3 => #3] + # #0 + # + # #3 + # + # #root + # => #3 via [local `y`] at line 28 + # + 3: $t7 := infer($t0) + # live vars: $t3, $t4, $t7 + # reaching instruction #4: `t3` @ {0}, `t4` @ {2}, `t5` @ {1}, `t7` @ {3}, `Struct QualifiedId { module_id: ModuleId(1), id: StructId(Symbol(133)) }` @ {2} + # refs: [$t3 => #3, $t7 => #7] + # #3 + # + # #7 + # + # #root + # => #3 via [local `y`] at line 28 + # + 4: $t6 := FunctionCall::foo_global($t7, $t3) + # live vars: $t4, $t6 + # reaching instruction #5: `t3` @ {0}, `t4` @ {2}, `t5` @ {1}, `t6` @ {4}, `t7` @ {3}, `Struct QualifiedId { module_id: ModuleId(1), id: StructId(Symbol(133)) }` @ {4} + # refs: [] + # + 5: $t2 := +($t4, $t6) + # live vars: $t2 + # reaching instruction #6: `t2` @ {5}, `t3` @ {0}, `t4` @ {2}, `t5` @ {1}, `t6` @ {4}, `t7` @ {3}, `Struct QualifiedId { module_id: ModuleId(1), id: StructId(Symbol(133)) }` @ {4} + # refs: [] + # + 6: return $t2 +} + + +[variant baseline] +fun FunctionCall::bar_mut_ref($t0: u64): u64 { + var $t1: u64 + var $t2: &mut u64 + var $t3: u64 + var $t4: u64 + # flush: $t2 + # live vars: $t0 + # reaching instruction #0: + # refs: [] + # + 0: $t2 := borrow_local($t0) + # live vars: $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [$t2 => #2] + # #2 + # + # #root + # => (mut) #2 via [local `y`] at line 22 + # + 1: $t3 := FunctionCall::foo_mut_ref($t2) + # live vars: $t2, $t3 + # reaching instruction #2: `t0` @ {1}, `t2` @ {0}, `t3` @ {1} + # refs: [$t2 => #2] + # #2 + # + # #root + # => (mut) #2 via [local `y`] at line 22 + # + 2: $t4 := FunctionCall::foo_mut_ref($t2) + # live vars: $t3, $t4 + # reaching instruction #3: `t0` @ {2}, `t2` @ {0}, `t3` @ {1}, `t4` @ {2} + # refs: [] + # + 3: $t1 := +($t3, $t4) + # live vars: $t1 + # reaching instruction #4: `t0` @ {2}, `t1` @ {3}, `t2` @ {0}, `t3` @ {1}, `t4` @ {2} + # refs: [] + # + 4: return $t1 +} + + +[variant baseline] +fun FunctionCall::foo_global($t0: &signer, $t1: &u64): u64 { + var $t2: u64 + var $t3: &signer + var $t4: 0x99::FunctionCall::S + var $t5: u64 + var $t6: u64 + var $t7: u64 + # live vars: $t0, $t1 + # reaching instruction #0: + # refs: [$t0 => #0, $t1 => #1] + # #0 + # + # #1 + # + # #root + # + # + 0: $t3 := infer($t0) + # live vars: $t1, $t3 + # reaching instruction #1: `t3` @ {0} + # refs: [$t1 => #1, $t3 => #3] + # #1 + # + # #3 + # + # #root + # + # + 1: $t5 := 42 + # live vars: $t1, $t3, $t5 + # reaching instruction #2: `t3` @ {0}, `t5` @ {1} + # refs: [$t1 => #1, $t3 => #3] + # #1 + # + # #3 + # + # #root + # + # + 2: $t4 := pack 0x99::FunctionCall::S($t5) + # live vars: $t1, $t3, $t4 + # reaching instruction #3: `t3` @ {0}, `t4` @ {2}, `t5` @ {1} + # refs: [$t1 => #1, $t3 => #3] + # #1 + # + # #3 + # + # #root + # + # + 3: move_to<0x99::FunctionCall::S>($t3, $t4) + # live vars: $t1 + # reaching instruction #4: `t3` @ {0}, `t4` @ {2}, `t5` @ {1}, `Struct QualifiedId { module_id: ModuleId(1), id: StructId(Symbol(133)) }` @ {3} + # refs: [$t1 => #1] + # #1 + # + # #root + # + # + 4: $t6 := read_ref($t1) + # live vars: $t6 + # reaching instruction #5: `t3` @ {0}, `t4` @ {2}, `t5` @ {1}, `t6` @ {4}, `Struct QualifiedId { module_id: ModuleId(1), id: StructId(Symbol(133)) }` @ {3} + # refs: [] + # + 5: $t7 := 1 + # live vars: $t6, $t7 + # reaching instruction #6: `t3` @ {0}, `t4` @ {2}, `t5` @ {1}, `t6` @ {4}, `t7` @ {5}, `Struct QualifiedId { module_id: ModuleId(1), id: StructId(Symbol(133)) }` @ {3} + # refs: [] + # + 6: $t2 := +($t6, $t7) + # live vars: $t2 + # reaching instruction #7: `t2` @ {6}, `t3` @ {0}, `t4` @ {2}, `t5` @ {1}, `t6` @ {4}, `t7` @ {5}, `Struct QualifiedId { module_id: ModuleId(1), id: StructId(Symbol(133)) }` @ {3} + # refs: [] + # + 7: return $t2 +} + + +[variant baseline] +fun FunctionCall::foo_mut_ref($t0: &mut u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t2 := read_ref($t0) + # live vars: $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [] + # + 1: $t3 := 1 + # live vars: $t2, $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 2: $t1 := +($t2, $t3) + # live vars: $t1 + # reaching instruction #3: `t1` @ {2}, `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 3: return $t1 +} + + +[variant baseline] +fun FunctionCall::foo_ref($t0: &u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t2 := read_ref($t0) + # live vars: $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [] + # + 1: $t3 := 1 + # live vars: $t2, $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 2: $t1 := +($t2, $t3) + # live vars: $t1 + # reaching instruction #3: `t1` @ {2}, `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 3: return $t1 +} + +============ after CommonSubexpElimination: ================ + +[variant baseline] +fun FunctionCall::bar_global($t0: &signer, $t1: u64): u64 { + var $t2: u64 + var $t3: &u64 + var $t4: u64 + var $t5: &signer + var $t6: u64 + var $t7: &signer + 0: $t3 := borrow_local($t1) + 1: $t5 := infer($t0) + 2: $t4 := FunctionCall::foo_global($t5, $t3) + 3: $t7 := infer($t0) + 4: $t6 := FunctionCall::foo_global($t7, $t3) + 5: $t2 := +($t4, $t6) + 6: return $t2 +} + + +[variant baseline] +fun FunctionCall::bar_mut_ref($t0: u64): u64 { + var $t1: u64 + var $t2: &mut u64 + var $t3: u64 + var $t4: u64 + 0: $t2 := borrow_local($t0) + 1: $t3 := FunctionCall::foo_mut_ref($t2) + 2: $t4 := FunctionCall::foo_mut_ref($t2) + 3: $t1 := +($t3, $t4) + 4: return $t1 +} + + +[variant baseline] +fun FunctionCall::foo_global($t0: &signer, $t1: &u64): u64 { + var $t2: u64 + var $t3: &signer + var $t4: 0x99::FunctionCall::S + var $t5: u64 + var $t6: u64 + var $t7: u64 + 0: $t3 := infer($t0) + 1: $t5 := 42 + 2: $t4 := pack 0x99::FunctionCall::S($t5) + 3: move_to<0x99::FunctionCall::S>($t3, $t4) + 4: $t6 := read_ref($t1) + 5: $t7 := 1 + 6: $t2 := +($t6, $t7) + 7: return $t2 +} + + +[variant baseline] +fun FunctionCall::foo_mut_ref($t0: &mut u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + 0: $t2 := read_ref($t0) + 1: $t3 := 1 + 2: $t1 := +($t2, $t3) + 3: return $t1 +} + + +[variant baseline] +fun FunctionCall::foo_ref($t0: &u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + 0: $t2 := read_ref($t0) + 1: $t3 := 1 + 2: $t1 := +($t2, $t3) + 3: return $t1 +} + + +============ disassembled file-format ================== +// Bytecode version v9 +module 0x99::FunctionCall +struct S has key + val: u64 + +// Function definition at index 0 +fun bar_global(l0: &signer, l1: u64): u64 + local l2: &u64 + borrow_loc l1 + st_loc l2 + copy_loc l0 + copy_loc l2 + call foo_global + // @5 + move_loc l0 + move_loc l2 + call foo_global + add + ret + +// Function definition at index 1 +fun bar_mut_ref(l0: u64): u64 + local l1: &mut u64 + mut_borrow_loc l0 + st_loc l1 + copy_loc l1 + call foo_mut_ref + move_loc l1 + // @5 + call foo_mut_ref + add + ret + +// Function definition at index 2 +fun foo_global(l0: &signer, l1: &u64): u64 + move_loc l0 + ld_u64 42 + pack S + move_to S + move_loc l1 + // @5 + read_ref + ld_u64 1 + add + ret + +// Function definition at index 3 +fun foo_mut_ref(l0: &mut u64): u64 + move_loc l0 + read_ref + ld_u64 1 + add + ret + +// Function definition at index 4 +fun foo_ref(l0: &u64): u64 + move_loc l0 + read_ref + ld_u64 1 + add + ret + + +============ bytecode verification succeeded ======== diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/global_access.move b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/global_access.move new file mode 100644 index 0000000000000..5952e785e5108 --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/global_access.move @@ -0,0 +1,68 @@ +module 0x99::GlobalAccess { + use std::signer; + use std::vector; + + struct S has key, drop { + val: u64, + } + + struct S1 has key, drop { + val: u64, + } + + fun move_to_S(account: &signer) { + move_to(account, S { val: 42 } ) + } + + fun move_from_S(account: &signer) { + move_from(signer::address_of(account)); + } + + fun mutable_borrow_S(account: &signer) { + *borrow_global_mut(signer::address_of(account)) = S { val: 100 }; + } + + fun dummy_func(): vector { + vector::empty() + } + + // `borrow_global(addr).val` cannot be reused + // because `move_to_S` may modify the global storage + fun test_global_borrow_v1(account: &signer): u64 { + let addr = signer::address_of(account); + let r1 = borrow_global(addr); + move_to_S(account); + let r2 = borrow_global(addr); + r1.val + r2.val + } + + // `exists(addr)` cannot be reused + // because `move_to_S` may modify the global storage + fun test_existence_check_v1(account: &signer): bool { + let addr = signer::address_of(account); + let b1 = exists(addr); + move_to_S(account); + let b2 = exists(addr); + b1 && b2 + } + + // `exists(addr)` cannot be reused + // because `move_from_S` may modify the global storage + fun test_existence_check_v2(account: &signer): bool { + let addr = signer::address_of(account); + let b1 = exists(addr); + move_from_S(account); + let b2 = exists(addr); + b1 && b2 + } + + // `exists(addr)` cannot be reused + // because `move_to_S` may modify the global storage + fun test_existence_check_v3(account: &signer): bool { + let addr = signer::address_of(account); + let b1 = exists(addr); + mutable_borrow_S(account); + let b2 = exists(addr); + b1 && b2 + } +} diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/global_access.off.exp b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/global_access.off.exp new file mode 100644 index 0000000000000..2c0428806ea59 --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/global_access.off.exp @@ -0,0 +1,136 @@ + +============ disassembled file-format ================== +// Bytecode version v9 +module 0x99::GlobalAccess +use 0x1::signer +struct S has drop + key + val: u64 + +struct S1 has drop + key + val: u64 + +// Function definition at index 0 +fun dummy_func(): vector + vec_pack , 0 + ret + +// Function definition at index 1 +fun move_from_S(l0: &signer) acquires S + move_loc l0 + call signer::address_of + move_from S + pop + ret + +// Function definition at index 2 +fun move_to_S(l0: &signer) + move_loc l0 + ld_u64 42 + pack S + move_to S + ret + +// Function definition at index 3 +fun mutable_borrow_S(l0: &signer) acquires S + ld_u64 100 + pack S + move_loc l0 + call signer::address_of + mut_borrow_global S + // @5 + write_ref + ret + +// Function definition at index 4 +fun test_existence_check_v1(l0: &signer): bool + local l1: address + local l2: bool + copy_loc l0 + call signer::address_of + st_loc l1 + copy_loc l1 + exists S + // @5 + move_loc l0 + call move_to_S + move_loc l1 + exists S + st_loc l2 + // @10 + br_false l0 + move_loc l2 + ret +l0: ld_false + ret + +// Function definition at index 5 +fun test_existence_check_v2(l0: &signer): bool acquires S + local l1: address + local l2: bool + copy_loc l0 + call signer::address_of + st_loc l1 + copy_loc l1 + exists S + // @5 + move_loc l0 + call move_from_S + move_loc l1 + exists S + st_loc l2 + // @10 + br_false l0 + move_loc l2 + ret +l0: ld_false + ret + +// Function definition at index 6 +fun test_existence_check_v3(l0: &signer): bool acquires S + local l1: address + local l2: bool + copy_loc l0 + call signer::address_of + st_loc l1 + copy_loc l1 + exists S + // @5 + move_loc l0 + call mutable_borrow_S + move_loc l1 + exists S + st_loc l2 + // @10 + br_false l0 + move_loc l2 + ret +l0: ld_false + ret + +// Function definition at index 7 +fun test_global_borrow_v1(l0: &signer): u64 acquires S + local l1: address + local l2: &S + copy_loc l0 + call signer::address_of + st_loc l1 + copy_loc l1 + borrow_global S + // @5 + move_loc l0 + call move_to_S + move_loc l1 + borrow_global S + st_loc l2 + // @10 + borrow_field S, val + read_ref + move_loc l2 + borrow_field S, val + read_ref + // @15 + add + ret + + +============ bytecode verification succeeded ======== diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/global_access.on.exp b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/global_access.on.exp new file mode 100644 index 0000000000000..2575af1ed29f7 --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/not-optimized/global_access.on.exp @@ -0,0 +1,777 @@ +============ after ReachingDefProcessor: ================ + +[variant baseline] +fun GlobalAccess::dummy_func(): vector { + var $t0: vector + # live vars: + # reaching instruction #0: + # refs: [] + # + 0: $t0 := vector::empty() + # live vars: $t0 + # reaching instruction #1: `t0` @ {0} + # refs: [] + # + 1: return $t0 +} + + +[variant baseline] +fun GlobalAccess::move_from_S($t0: &signer) { + var $t1: 0x99::GlobalAccess::S + var $t2: address + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t2 := signer::address_of($t0) + # flush: $t1 + # live vars: $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [] + # + 1: $t1 := move_from<0x99::GlobalAccess::S>($t2) + # live vars: + # reaching instruction #2: `t1` @ {1}, `t2` @ {0}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {1} + # refs: [] + # + 2: return () +} + + +[variant baseline] +fun GlobalAccess::move_to_S($t0: &signer) { + var $t1: &signer + var $t2: 0x99::GlobalAccess::S + var $t3: u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t1 := infer($t0) + # live vars: $t1 + # reaching instruction #1: `t1` @ {0} + # refs: [$t1 => #1] + # #1 + # + # #root + # + # + 1: $t3 := 42 + # live vars: $t1, $t3 + # reaching instruction #2: `t1` @ {0}, `t3` @ {1} + # refs: [$t1 => #1] + # #1 + # + # #root + # + # + 2: $t2 := pack 0x99::GlobalAccess::S($t3) + # live vars: $t1, $t2 + # reaching instruction #3: `t1` @ {0}, `t2` @ {2}, `t3` @ {1} + # refs: [$t1 => #1] + # #1 + # + # #root + # + # + 3: move_to<0x99::GlobalAccess::S>($t1, $t2) + # live vars: + # reaching instruction #4: `t1` @ {0}, `t2` @ {2}, `t3` @ {1}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {3} + # refs: [] + # + 4: return () +} + + +[variant baseline] +fun GlobalAccess::mutable_borrow_S($t0: &signer) { + var $t1: 0x99::GlobalAccess::S + var $t2: u64 + var $t3: &mut 0x99::GlobalAccess::S + var $t4: address + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t2 := 100 + # live vars: $t0, $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 1: $t1 := pack 0x99::GlobalAccess::S($t2) + # live vars: $t0, $t1 + # reaching instruction #2: `t1` @ {1}, `t2` @ {0} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 2: $t4 := signer::address_of($t0) + # live vars: $t1, $t4 + # reaching instruction #3: `t1` @ {1}, `t2` @ {0}, `t4` @ {2} + # refs: [] + # + 3: $t3 := borrow_global<0x99::GlobalAccess::S>($t4) + # live vars: $t1, $t3 + # reaching instruction #4: `t1` @ {1}, `t2` @ {0}, `t3` @ {3}, `t4` @ {2} + # refs: [$t3 => #3] + # #3 + # + # #root + # -> (mut) #3 via [struct `GlobalAccess::S`] at line 22 + # + 4: write_ref($t3, $t1) + # live vars: + # reaching instruction #5: `t1` @ {1}, `t2` @ {0}, `t3` @ {3}, `t4` @ {2}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {4} + # refs: [] + # + 5: return () +} + + +[variant baseline] +fun GlobalAccess::test_existence_check_v1($t0: &signer): bool { + var $t1: bool + var $t2: address + var $t3: bool + var $t4: bool + # flush: $t2 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t2 := signer::address_of($t0) + # live vars: $t0, $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 1: $t3 := exists<0x99::GlobalAccess::S>($t2) + # live vars: $t0, $t2, $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 2: GlobalAccess::move_to_S($t0) + # flush: $t4 + # live vars: $t2, $t3 + # reaching instruction #3: `t2` @ {0}, `t3` @ {1}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 3: $t4 := exists<0x99::GlobalAccess::S>($t2) + # live vars: $t3, $t4 + # reaching instruction #4: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 4: if ($t3) goto 5 else goto 8 + # live vars: $t4 + # reaching instruction #5: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 5: label L0 + # flush: $t1 + # live vars: $t4 + # reaching instruction #6: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 6: $t1 := infer($t4) + # live vars: $t1 + # reaching instruction #7: `t1` @ {6}, `t2` @ {0}, `t3` @ {1}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 7: goto 10 + # live vars: $t4 + # reaching instruction #8: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 8: label L1 + # flush: $t1 + # live vars: + # reaching instruction #9: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 9: $t1 := false + # live vars: $t1 + # reaching instruction #10: `t1` @ {6, 9}, `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 10: label L2 + # live vars: $t1 + # reaching instruction #11: `t1` @ {6, 9}, `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 11: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_existence_check_v2($t0: &signer): bool { + var $t1: bool + var $t2: address + var $t3: bool + var $t4: bool + # flush: $t2 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t2 := signer::address_of($t0) + # live vars: $t0, $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 1: $t3 := exists<0x99::GlobalAccess::S>($t2) + # live vars: $t0, $t2, $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 2: GlobalAccess::move_from_S($t0) + # flush: $t4 + # live vars: $t2, $t3 + # reaching instruction #3: `t2` @ {0}, `t3` @ {1}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 3: $t4 := exists<0x99::GlobalAccess::S>($t2) + # live vars: $t3, $t4 + # reaching instruction #4: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 4: if ($t3) goto 5 else goto 8 + # live vars: $t4 + # reaching instruction #5: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 5: label L0 + # flush: $t1 + # live vars: $t4 + # reaching instruction #6: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 6: $t1 := infer($t4) + # live vars: $t1 + # reaching instruction #7: `t1` @ {6}, `t2` @ {0}, `t3` @ {1}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 7: goto 10 + # live vars: $t4 + # reaching instruction #8: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 8: label L1 + # flush: $t1 + # live vars: + # reaching instruction #9: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 9: $t1 := false + # live vars: $t1 + # reaching instruction #10: `t1` @ {6, 9}, `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 10: label L2 + # live vars: $t1 + # reaching instruction #11: `t1` @ {6, 9}, `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 11: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_existence_check_v3($t0: &signer): bool { + var $t1: bool + var $t2: address + var $t3: bool + var $t4: bool + # flush: $t2 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t2 := signer::address_of($t0) + # live vars: $t0, $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 1: $t3 := exists<0x99::GlobalAccess::S>($t2) + # live vars: $t0, $t2, $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 2: GlobalAccess::mutable_borrow_S($t0) + # flush: $t4 + # live vars: $t2, $t3 + # reaching instruction #3: `t2` @ {0}, `t3` @ {1}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 3: $t4 := exists<0x99::GlobalAccess::S>($t2) + # live vars: $t3, $t4 + # reaching instruction #4: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 4: if ($t3) goto 5 else goto 8 + # live vars: $t4 + # reaching instruction #5: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 5: label L0 + # flush: $t1 + # live vars: $t4 + # reaching instruction #6: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 6: $t1 := infer($t4) + # live vars: $t1 + # reaching instruction #7: `t1` @ {6}, `t2` @ {0}, `t3` @ {1}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 7: goto 10 + # live vars: $t4 + # reaching instruction #8: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 8: label L1 + # flush: $t1 + # live vars: + # reaching instruction #9: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 9: $t1 := false + # live vars: $t1 + # reaching instruction #10: `t1` @ {6, 9}, `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 10: label L2 + # live vars: $t1 + # reaching instruction #11: `t1` @ {6, 9}, `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 11: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_global_borrow_v1($t0: &signer): u64 { + var $t1: u64 + var $t2: address + var $t3: &0x99::GlobalAccess::S + var $t4: &0x99::GlobalAccess::S + var $t5: u64 + var $t6: &u64 + var $t7: u64 + var $t8: &u64 + # flush: $t2 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t2 := signer::address_of($t0) + # live vars: $t0, $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 1: $t3 := borrow_global<0x99::GlobalAccess::S>($t2) + # live vars: $t0, $t2, $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [$t0 => #0, $t3 => #3] + # #0 + # + # #3 + # + # #root + # -> #3 via [struct `GlobalAccess::S`] at line 33 + # + 2: GlobalAccess::move_to_S($t0) + # flush: $t4 + # live vars: $t2, $t3 + # reaching instruction #3: `t2` @ {0}, `t3` @ {1}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [$t3 => #3] + # #3 + # + # #root + # -> #3 via [struct `GlobalAccess::S`] at line 33 + # + 3: $t4 := borrow_global<0x99::GlobalAccess::S>($t2) + # live vars: $t3, $t4 + # reaching instruction #4: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [$t3 => #3, $t4 => #4] + # #3 + # + # #4 + # + # #root + # -> #3 via [struct `GlobalAccess::S`] at line 33 + # -> #4 via [struct `GlobalAccess::S`] at line 35 + # + 4: $t6 := borrow_field<0x99::GlobalAccess::S>.val($t3) + # live vars: $t4, $t6 + # reaching instruction #5: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `t6` @ {4}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [$t4 => #4, $t6 => #6] + # #4 + # + # #6 + # + # #root + # -> #4 via [struct `GlobalAccess::S`] at line 35 + # -> #6 via [struct `GlobalAccess::S`] at line 36 + # + 5: $t5 := read_ref($t6) + # live vars: $t4, $t5 + # reaching instruction #6: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `t5` @ {5}, `t6` @ {4}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [$t4 => #4] + # #4 + # + # #root + # -> #4 via [struct `GlobalAccess::S`] at line 35 + # + 6: $t8 := borrow_field<0x99::GlobalAccess::S>.val($t4) + # live vars: $t5, $t8 + # reaching instruction #7: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `t5` @ {5}, `t6` @ {4}, `t8` @ {6}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [$t8 => #8] + # #8 + # + # #root + # -> #8 via [struct `GlobalAccess::S`] at line 36 + # + 7: $t7 := read_ref($t8) + # live vars: $t5, $t7 + # reaching instruction #8: `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `t5` @ {5}, `t6` @ {4}, `t7` @ {7}, `t8` @ {6}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 8: $t1 := +($t5, $t7) + # live vars: $t1 + # reaching instruction #9: `t1` @ {8}, `t2` @ {0}, `t3` @ {1}, `t4` @ {3}, `t5` @ {5}, `t6` @ {4}, `t7` @ {7}, `t8` @ {6}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(140)) }` @ {2} + # refs: [] + # + 9: return $t1 +} + +============ after CommonSubexpElimination: ================ + +[variant baseline] +fun GlobalAccess::dummy_func(): vector { + var $t0: vector + 0: $t0 := vector::empty() + 1: return $t0 +} + + +[variant baseline] +fun GlobalAccess::move_from_S($t0: &signer) { + var $t1: 0x99::GlobalAccess::S + var $t2: address + 0: $t2 := signer::address_of($t0) + 1: $t1 := move_from<0x99::GlobalAccess::S>($t2) + 2: return () +} + + +[variant baseline] +fun GlobalAccess::move_to_S($t0: &signer) { + var $t1: &signer + var $t2: 0x99::GlobalAccess::S + var $t3: u64 + 0: $t1 := infer($t0) + 1: $t3 := 42 + 2: $t2 := pack 0x99::GlobalAccess::S($t3) + 3: move_to<0x99::GlobalAccess::S>($t1, $t2) + 4: return () +} + + +[variant baseline] +fun GlobalAccess::mutable_borrow_S($t0: &signer) { + var $t1: 0x99::GlobalAccess::S + var $t2: u64 + var $t3: &mut 0x99::GlobalAccess::S + var $t4: address + 0: $t2 := 100 + 1: $t1 := pack 0x99::GlobalAccess::S($t2) + 2: $t4 := signer::address_of($t0) + 3: $t3 := borrow_global<0x99::GlobalAccess::S>($t4) + 4: write_ref($t3, $t1) + 5: return () +} + + +[variant baseline] +fun GlobalAccess::test_existence_check_v1($t0: &signer): bool { + var $t1: bool + var $t2: address + var $t3: bool + var $t4: bool + 0: $t2 := signer::address_of($t0) + 1: $t3 := exists<0x99::GlobalAccess::S>($t2) + 2: GlobalAccess::move_to_S($t0) + 3: $t4 := exists<0x99::GlobalAccess::S>($t2) + 4: if ($t3) goto 5 else goto 8 + 5: label L0 + 6: $t1 := infer($t4) + 7: goto 10 + 8: label L1 + 9: $t1 := false + 10: label L2 + 11: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_existence_check_v2($t0: &signer): bool { + var $t1: bool + var $t2: address + var $t3: bool + var $t4: bool + 0: $t2 := signer::address_of($t0) + 1: $t3 := exists<0x99::GlobalAccess::S>($t2) + 2: GlobalAccess::move_from_S($t0) + 3: $t4 := exists<0x99::GlobalAccess::S>($t2) + 4: if ($t3) goto 5 else goto 8 + 5: label L0 + 6: $t1 := infer($t4) + 7: goto 10 + 8: label L1 + 9: $t1 := false + 10: label L2 + 11: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_existence_check_v3($t0: &signer): bool { + var $t1: bool + var $t2: address + var $t3: bool + var $t4: bool + 0: $t2 := signer::address_of($t0) + 1: $t3 := exists<0x99::GlobalAccess::S>($t2) + 2: GlobalAccess::mutable_borrow_S($t0) + 3: $t4 := exists<0x99::GlobalAccess::S>($t2) + 4: if ($t3) goto 5 else goto 8 + 5: label L0 + 6: $t1 := infer($t4) + 7: goto 10 + 8: label L1 + 9: $t1 := false + 10: label L2 + 11: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_global_borrow_v1($t0: &signer): u64 { + var $t1: u64 + var $t2: address + var $t3: &0x99::GlobalAccess::S + var $t4: &0x99::GlobalAccess::S + var $t5: u64 + var $t6: &u64 + var $t7: u64 + var $t8: &u64 + 0: $t2 := signer::address_of($t0) + 1: $t3 := borrow_global<0x99::GlobalAccess::S>($t2) + 2: GlobalAccess::move_to_S($t0) + 3: $t4 := borrow_global<0x99::GlobalAccess::S>($t2) + 4: $t6 := borrow_field<0x99::GlobalAccess::S>.val($t3) + 5: $t5 := read_ref($t6) + 6: $t8 := borrow_field<0x99::GlobalAccess::S>.val($t4) + 7: $t7 := read_ref($t8) + 8: $t1 := +($t5, $t7) + 9: return $t1 +} + + +============ disassembled file-format ================== +// Bytecode version v9 +module 0x99::GlobalAccess +use 0x1::signer +struct S has drop + key + val: u64 + +struct S1 has drop + key + val: u64 + +// Function definition at index 0 +fun dummy_func(): vector + vec_pack , 0 + ret + +// Function definition at index 1 +fun move_from_S(l0: &signer) acquires S + move_loc l0 + call signer::address_of + move_from S + pop + ret + +// Function definition at index 2 +fun move_to_S(l0: &signer) + move_loc l0 + ld_u64 42 + pack S + move_to S + ret + +// Function definition at index 3 +fun mutable_borrow_S(l0: &signer) acquires S + ld_u64 100 + pack S + move_loc l0 + call signer::address_of + mut_borrow_global S + // @5 + write_ref + ret + +// Function definition at index 4 +fun test_existence_check_v1(l0: &signer): bool + local l1: address + local l2: bool + copy_loc l0 + call signer::address_of + st_loc l1 + copy_loc l1 + exists S + // @5 + move_loc l0 + call move_to_S + move_loc l1 + exists S + st_loc l2 + // @10 + br_false l0 + move_loc l2 + ret +l0: ld_false + ret + +// Function definition at index 5 +fun test_existence_check_v2(l0: &signer): bool acquires S + local l1: address + local l2: bool + copy_loc l0 + call signer::address_of + st_loc l1 + copy_loc l1 + exists S + // @5 + move_loc l0 + call move_from_S + move_loc l1 + exists S + st_loc l2 + // @10 + br_false l0 + move_loc l2 + ret +l0: ld_false + ret + +// Function definition at index 6 +fun test_existence_check_v3(l0: &signer): bool acquires S + local l1: address + local l2: bool + copy_loc l0 + call signer::address_of + st_loc l1 + copy_loc l1 + exists S + // @5 + move_loc l0 + call mutable_borrow_S + move_loc l1 + exists S + st_loc l2 + // @10 + br_false l0 + move_loc l2 + ret +l0: ld_false + ret + +// Function definition at index 7 +fun test_global_borrow_v1(l0: &signer): u64 acquires S + local l1: address + local l2: &S + copy_loc l0 + call signer::address_of + st_loc l1 + copy_loc l1 + borrow_global S + // @5 + move_loc l0 + call move_to_S + move_loc l1 + borrow_global S + st_loc l2 + // @10 + borrow_field S, val + read_ref + move_loc l2 + borrow_field S, val + read_ref + // @15 + add + ret + + +============ bytecode verification succeeded ======== diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/field_access.move b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/field_access.move new file mode 100644 index 0000000000000..3f365299b096f --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/field_access.move @@ -0,0 +1,16 @@ +module 0x99::FiledAccess { + struct Inner has copy, drop { + value: u64, + } + + struct Outer has copy, drop { + inner: Inner, + } + + // `arg1.inner.value` can be reused + // perf_gain: 2 field accesses + 1 readref eliminated + // new_cost: `u64` flushed and copied twice + fun test_field_access(arg1: Outer, arg2: u64): u64 { + arg1.inner.value + arg2 + arg1.inner.value + } +} diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/field_access.off.exp b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/field_access.off.exp new file mode 100644 index 0000000000000..2133af4aa24e0 --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/field_access.off.exp @@ -0,0 +1,29 @@ + +============ disassembled file-format ================== +// Bytecode version v9 +module 0x99::FiledAccess +struct Inner has copy + drop + value: u64 + +struct Outer has copy + drop + inner: Inner + +// Function definition at index 0 +fun test_field_access(l0: Outer, l1: u64): u64 + borrow_loc l0 + borrow_field Outer, inner + borrow_field Inner, value + read_ref + move_loc l1 + // @5 + add + borrow_loc l0 + borrow_field Outer, inner + borrow_field Inner, value + read_ref + // @10 + add + ret + + +============ bytecode verification succeeded ======== diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/field_access.on.exp b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/field_access.on.exp new file mode 100644 index 0000000000000..0424e28f450fa --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/field_access.on.exp @@ -0,0 +1,148 @@ +============ after ReachingDefProcessor: ================ + +[variant baseline] +fun FiledAccess::test_field_access($t0: 0x99::FiledAccess::Outer, $t1: u64): u64 { + var $t2: u64 + var $t3: u64 + var $t4: u64 + var $t5: &0x99::FiledAccess::Inner + var $t6: &0x99::FiledAccess::Outer + var $t7: &u64 + var $t8: u64 + var $t9: &0x99::FiledAccess::Inner + var $t10: &0x99::FiledAccess::Outer + var $t11: &u64 + # live vars: $t0, $t1 + # reaching instruction #0: + # refs: [] + # + 0: $t6 := borrow_local($t0) + # live vars: $t0, $t1, $t6 + # reaching instruction #1: `t6` @ {0} + # refs: [$t6 => #6] + # #6 + # + # #root + # => #6 via [local `arg1`] at line 14 + # + 1: $t5 := borrow_field<0x99::FiledAccess::Outer>.inner($t6) + # live vars: $t0, $t1, $t5 + # reaching instruction #2: `t5` @ {1}, `t6` @ {0} + # refs: [$t5 => #5] + # #5 + # + # #root + # => #5 via [local `arg1`, field `inner`] at line 14 + # + 2: $t7 := borrow_field<0x99::FiledAccess::Inner>.value($t5) + # live vars: $t0, $t1, $t7 + # reaching instruction #3: `t5` @ {1}, `t6` @ {0}, `t7` @ {2} + # refs: [$t7 => #7] + # #7 + # + # #root + # => #7 via [local `arg1`, field `inner`, field `value`] at line 14 + # + 3: $t4 := read_ref($t7) + # live vars: $t0, $t1, $t4 + # reaching instruction #4: `t4` @ {3}, `t5` @ {1}, `t6` @ {0}, `t7` @ {2} + # refs: [] + # + 4: $t3 := +($t4, $t1) + # live vars: $t0, $t3 + # reaching instruction #5: `t3` @ {4}, `t4` @ {3}, `t5` @ {1}, `t6` @ {0}, `t7` @ {2} + # refs: [] + # + 5: $t10 := borrow_local($t0) + # live vars: $t3, $t10 + # reaching instruction #6: `t3` @ {4}, `t4` @ {3}, `t5` @ {1}, `t6` @ {0}, `t7` @ {2}, `t10` @ {5} + # refs: [$t10 => #10] + # #10 + # + # #root + # => #10 via [local `arg1`] at line 14 + # + 6: $t9 := borrow_field<0x99::FiledAccess::Outer>.inner($t10) + # live vars: $t3, $t9 + # reaching instruction #7: `t3` @ {4}, `t4` @ {3}, `t5` @ {1}, `t6` @ {0}, `t7` @ {2}, `t9` @ {6}, `t10` @ {5} + # refs: [$t9 => #9] + # #9 + # + # #root + # => #9 via [local `arg1`, field `inner`] at line 14 + # + 7: $t11 := borrow_field<0x99::FiledAccess::Inner>.value($t9) + # live vars: $t3, $t11 + # reaching instruction #8: `t3` @ {4}, `t4` @ {3}, `t5` @ {1}, `t6` @ {0}, `t7` @ {2}, `t9` @ {6}, `t10` @ {5}, `t11` @ {7} + # refs: [$t11 => #11] + # #11 + # + # #root + # => #11 via [local `arg1`, field `inner`, field `value`] at line 14 + # + 8: $t8 := read_ref($t11) + # live vars: $t3, $t8 + # reaching instruction #9: `t3` @ {4}, `t4` @ {3}, `t5` @ {1}, `t6` @ {0}, `t7` @ {2}, `t8` @ {8}, `t9` @ {6}, `t10` @ {5}, `t11` @ {7} + # refs: [] + # + 9: $t2 := +($t3, $t8) + # live vars: $t2 + # reaching instruction #10: `t2` @ {9}, `t3` @ {4}, `t4` @ {3}, `t5` @ {1}, `t6` @ {0}, `t7` @ {2}, `t8` @ {8}, `t9` @ {6}, `t10` @ {5}, `t11` @ {7} + # refs: [] + # + 10: return $t2 +} + +============ after CommonSubexpElimination: ================ + +[variant baseline] +fun FiledAccess::test_field_access($t0: 0x99::FiledAccess::Outer, $t1: u64): u64 { + var $t2: u64 + var $t3: u64 + var $t4: u64 + var $t5: &0x99::FiledAccess::Inner + var $t6: &0x99::FiledAccess::Outer + var $t7: &u64 + var $t8: u64 + var $t9: &0x99::FiledAccess::Inner [unused] + var $t10: &0x99::FiledAccess::Outer [unused] + var $t11: &u64 [unused] + 0: $t6 := borrow_local($t0) + 1: $t5 := borrow_field<0x99::FiledAccess::Outer>.inner($t6) + 2: $t7 := borrow_field<0x99::FiledAccess::Inner>.value($t5) + 3: $t4 := read_ref($t7) + 4: $t3 := +($t4, $t1) + 5: $t8 := infer($t4) + 6: $t2 := +($t3, $t8) + 7: return $t2 +} + + +============ disassembled file-format ================== +// Bytecode version v9 +module 0x99::FiledAccess +struct Inner has copy + drop + value: u64 + +struct Outer has copy + drop + inner: Inner + +// Function definition at index 0 +fun test_field_access(l0: Outer, l1: u64): u64 + local l2: u64 + borrow_loc l0 + borrow_field Outer, inner + borrow_field Inner, value + read_ref + st_loc l2 + // @5 + copy_loc l2 + move_loc l1 + add + move_loc l2 + add + // @10 + ret + + +============ bytecode verification succeeded ======== diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/function_call.move b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/function_call.move new file mode 100644 index 0000000000000..3f412a951b722 --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/function_call.move @@ -0,0 +1,103 @@ +module 0x99::FunctionCall { + use std::vector; + use std::bit_vector; + + fun foo(x: u64): u64 { + x + 1 + } + + fun foo_ref(x: &u64): u64 { + *x + 1 + } + + fun foo_vec(): vector { + vector::empty() + } + + fun foo_grand_child(x: &mut u64): u64 { + *x + 1 + } + + fun foo_child(x: u64): u64 { + let ref = &mut x; + foo_grand_child(ref) + } + + fun foo_with_external_call(x: u64): u64 { + // simulate an external call by calling a native function + let bv = bit_vector::new(x); + bit_vector::length(&bv) + } + + + // `foo(y)` can be reused + // perf_gain: 1 function call eliminated + // new_cost: + // - `u64` flushed and copied twice + // - the result of the second call to `foo` needs to be adjust on stack (one st_loc and one move_loc) + fun bar(y: u64): u64 { + foo(y) + foo(y) + } + + // `foo(y)` can be reused + // perf_gain: 1 function call eliminated + // new_cost: + // - `u64` copied once for reuse + fun bar_optimal(y: u64): u64 { + let temp = foo(y); + temp + foo(y) + temp + } + + // `foo_ref(ref)` can be reused + // perf_gain: 1 function call eliminated + // new_cost: + // - `&u64` flushed and copied twice + // - the result of the second call to `foo` needs to be adjust on stack (one st_loc and one move_loc) + fun bar_ref(y: u64): u64 { + let ref = &y; + foo_ref(ref) + foo_ref(ref) + } + + // `foo_ref(ref1)` can be reused + // even when there is a mutable reference `mut_ref` in between + // (here, `mut_ref` does not change the value of `y`) + // perf_gain: 1 function call eliminated + // new_cost: + // - `&u64` flushed and copied twice + // - the result of the second call to `foo` needs to be adjust on stack (one st_loc and one move_loc) + fun bar_ref2(y: u64): u64 { + let ref1 = &y; + let v1 = foo_ref(ref1); + let mut_ref = &mut y; + let ref2 = &y; + let v2 = foo_ref(ref2); + v1 + v2 + } + + // `foo_vec()` can be reused + // perf_gain: 1 function call eliminated + // new_cost: none + fun bar_vec(): vector { + foo_vec(); + foo_vec() + } + + // `foo_child(y)` can be reused + // perf_gain: 1 function call eliminated + // new_cost: + // - `u64` flushed and copied twice + // - the result of the second call to `foo_child` needs to be adjust on stack (one st_loc and one move_loc) + fun bar_recursive(y: u64): u64 { + foo_child(y) + foo_child(y) + } + + // `foo_with_external_call(y)` can be reused + // perf_gain: 1 function call eliminated + // new_cost: + // - `u64` flushed and copied twice + // - the result of the second call to `foo_with_external_call` needs to be adjust on stack (one st_loc and one move_loc) + fun bar_with_external_call(y: u64): u64 { + foo_with_external_call(y) + + foo_with_external_call(y) + } +} diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/function_call.off.exp b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/function_call.off.exp new file mode 100644 index 0000000000000..487c018c2380e --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/function_call.off.exp @@ -0,0 +1,140 @@ + +Diagnostics: +warning: This assignment/binding to the left-hand-side variable `mut_ref` is unused. Consider removing this assignment/binding, or prefixing the left-hand-side variable with an underscore (e.g., `_mut_ref`), or renaming to `_` + ┌─ tests/common-subexp-elimination/optimized/function_call.move:71:23 + │ +71 │ let mut_ref = &mut y; + │ ^^^^^^ + + +============ disassembled file-format ================== +// Bytecode version v9 +module 0x99::FunctionCall +use 0x1::bit_vector +// Function definition at index 0 +fun bar(l0: u64): u64 + copy_loc l0 + call foo + move_loc l0 + call foo + add + // @5 + ret + +// Function definition at index 1 +fun bar_optimal(l0: u64): u64 + local l1: u64 + copy_loc l0 + call foo + st_loc l1 + copy_loc l1 + move_loc l0 + // @5 + call foo + add + move_loc l1 + add + ret + +// Function definition at index 2 +fun bar_recursive(l0: u64): u64 + copy_loc l0 + call foo_child + move_loc l0 + call foo_child + add + // @5 + ret + +// Function definition at index 3 +fun bar_ref(l0: u64): u64 + local l1: &u64 + borrow_loc l0 + st_loc l1 + copy_loc l1 + call foo_ref + move_loc l1 + // @5 + call foo_ref + add + ret + +// Function definition at index 4 +fun bar_ref2(l0: u64): u64 + local l1: &mut u64 + local l2: u64 + local l3: u64 + borrow_loc l0 + call foo_ref + mut_borrow_loc l0 + pop + borrow_loc l0 + // @5 + call foo_ref + add + ret + +// Function definition at index 5 +fun bar_vec(): vector + call foo_vec + pop + call foo_vec + ret + +// Function definition at index 6 +fun bar_with_external_call(l0: u64): u64 + copy_loc l0 + call foo_with_external_call + move_loc l0 + call foo_with_external_call + add + // @5 + ret + +// Function definition at index 7 +fun foo(l0: u64): u64 + move_loc l0 + ld_u64 1 + add + ret + +// Function definition at index 8 +fun foo_child(l0: u64): u64 + mut_borrow_loc l0 + call foo_grand_child + ret + +// Function definition at index 9 +fun foo_grand_child(l0: &mut u64): u64 + move_loc l0 + read_ref + ld_u64 1 + add + ret + +// Function definition at index 10 +fun foo_ref(l0: &u64): u64 + move_loc l0 + read_ref + ld_u64 1 + add + ret + +// Function definition at index 11 +fun foo_vec(): vector + vec_pack , 0 + ret + +// Function definition at index 12 +fun foo_with_external_call(l0: u64): u64 + local l1: bit_vector::BitVector + move_loc l0 + call bit_vector::new + st_loc l1 + borrow_loc l1 + call bit_vector::length + // @5 + ret + + +============ bytecode verification succeeded ======== diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/function_call.on.exp b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/function_call.on.exp new file mode 100644 index 0000000000000..82bcb30b8cf23 --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/function_call.on.exp @@ -0,0 +1,736 @@ + +Diagnostics: +warning: This assignment/binding to the left-hand-side variable `mut_ref` is unused. Consider removing this assignment/binding, or prefixing the left-hand-side variable with an underscore (e.g., `_mut_ref`), or renaming to `_` + ┌─ tests/common-subexp-elimination/optimized/function_call.move:71:23 + │ +71 │ let mut_ref = &mut y; + │ ^^^^^^ + +============ after ReachingDefProcessor: ================ + +[variant baseline] +fun FunctionCall::bar($t0: u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [] + # + 0: $t2 := FunctionCall::foo($t0) + # live vars: $t0, $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [] + # + 1: $t3 := FunctionCall::foo($t0) + # live vars: $t2, $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 2: $t1 := +($t2, $t3) + # live vars: $t1 + # reaching instruction #3: `t1` @ {2}, `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 3: return $t1 +} + + +[variant baseline] +fun FunctionCall::bar_optimal($t0: u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + var $t4: u64 + var $t5: u64 + # flush: $t2 + # live vars: $t0 + # reaching instruction #0: + # refs: [] + # + 0: $t2 := FunctionCall::foo($t0) + # live vars: $t0, $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [] + # + 1: $t4 := infer($t2) + # live vars: $t0, $t2, $t4 + # reaching instruction #2: `t2` @ {0}, `t4` @ {1} + # refs: [] + # + 2: $t5 := FunctionCall::foo($t0) + # live vars: $t2, $t4, $t5 + # reaching instruction #3: `t2` @ {0}, `t4` @ {1}, `t5` @ {2} + # refs: [] + # + 3: $t3 := +($t4, $t5) + # live vars: $t2, $t3 + # reaching instruction #4: `t2` @ {0}, `t3` @ {3}, `t4` @ {1}, `t5` @ {2} + # refs: [] + # + 4: $t1 := +($t3, $t2) + # live vars: $t1 + # reaching instruction #5: `t1` @ {4}, `t2` @ {0}, `t3` @ {3}, `t4` @ {1}, `t5` @ {2} + # refs: [] + # + 5: return $t1 +} + + +[variant baseline] +fun FunctionCall::bar_recursive($t0: u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [] + # + 0: $t2 := FunctionCall::foo_child($t0) + # live vars: $t0, $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [] + # + 1: $t3 := FunctionCall::foo_child($t0) + # live vars: $t2, $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 2: $t1 := +($t2, $t3) + # live vars: $t1 + # reaching instruction #3: `t1` @ {2}, `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 3: return $t1 +} + + +[variant baseline] +fun FunctionCall::bar_ref($t0: u64): u64 { + var $t1: u64 + var $t2: &u64 + var $t3: u64 + var $t4: u64 + # flush: $t2 + # live vars: $t0 + # reaching instruction #0: + # refs: [] + # + 0: $t2 := borrow_local($t0) + # live vars: $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [$t2 => #2] + # #2 + # + # #root + # => #2 via [local `y`] at line 57 + # + 1: $t3 := FunctionCall::foo_ref($t2) + # live vars: $t2, $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [$t2 => #2] + # #2 + # + # #root + # => #2 via [local `y`] at line 57 + # + 2: $t4 := FunctionCall::foo_ref($t2) + # live vars: $t3, $t4 + # reaching instruction #3: `t2` @ {0}, `t3` @ {1}, `t4` @ {2} + # refs: [] + # + 3: $t1 := +($t3, $t4) + # live vars: $t1 + # reaching instruction #4: `t1` @ {3}, `t2` @ {0}, `t3` @ {1}, `t4` @ {2} + # refs: [] + # + 4: return $t1 +} + + +[variant baseline] +fun FunctionCall::bar_ref2($t0: u64): u64 { + var $t1: u64 + var $t2: &u64 + var $t3: u64 + var $t4: &mut u64 + var $t5: &u64 + var $t6: u64 + var $t7: u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [] + # + 0: $t2 := borrow_local($t0) + # live vars: $t0, $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [$t2 => #2] + # #2 + # + # #root + # => #2 via [local `y`] at line 69 + # + 1: $t3 := FunctionCall::foo_ref($t2) + # flush: $t4 + # live vars: $t0, $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 2: $t4 := borrow_local($t0) + # live vars: $t0, $t3 + # reaching instruction #3: `t2` @ {0}, `t3` @ {1}, `t4` @ {2} + # refs: [] + # + 3: $t5 := borrow_local($t0) + # flush: $t6 + # live vars: $t3, $t5 + # reaching instruction #4: `t2` @ {0}, `t3` @ {1}, `t4` @ {2}, `t5` @ {3} + # refs: [$t5 => #5] + # #5 + # + # #root + # => #5 via [local `y`] at line 72 + # + 4: $t6 := FunctionCall::foo_ref($t5) + # live vars: $t3, $t6 + # reaching instruction #5: `t2` @ {0}, `t3` @ {1}, `t4` @ {2}, `t5` @ {3}, `t6` @ {4} + # refs: [] + # + 5: $t7 := infer($t3) + # live vars: $t6, $t7 + # reaching instruction #6: `t2` @ {0}, `t4` @ {2}, `t5` @ {3}, `t6` @ {4}, `t7` @ {5} + # refs: [] + # + 6: $t1 := +($t7, $t6) + # live vars: $t1 + # reaching instruction #7: `t1` @ {6}, `t2` @ {0}, `t4` @ {2}, `t5` @ {3}, `t6` @ {4}, `t7` @ {5} + # refs: [] + # + 7: return $t1 +} + + +[variant baseline] +fun FunctionCall::bar_vec(): vector { + var $t0: vector + var $t1: vector + # flush: $t1 + # live vars: + # reaching instruction #0: + # refs: [] + # + 0: $t1 := FunctionCall::foo_vec() + # live vars: + # reaching instruction #1: `t1` @ {0} + # refs: [] + # + 1: $t0 := FunctionCall::foo_vec() + # live vars: $t0 + # reaching instruction #2: `t0` @ {1}, `t1` @ {0} + # refs: [] + # + 2: return $t0 +} + + +[variant baseline] +fun FunctionCall::bar_with_external_call($t0: u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [] + # + 0: $t2 := FunctionCall::foo_with_external_call($t0) + # live vars: $t0, $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [] + # + 1: $t3 := FunctionCall::foo_with_external_call($t0) + # live vars: $t2, $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 2: $t1 := +($t2, $t3) + # live vars: $t1 + # reaching instruction #3: `t1` @ {2}, `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 3: return $t1 +} + + +[variant baseline] +fun FunctionCall::foo($t0: u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [] + # + 0: $t2 := infer($t0) + # live vars: $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [] + # + 1: $t3 := 1 + # live vars: $t2, $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 2: $t1 := +($t2, $t3) + # live vars: $t1 + # reaching instruction #3: `t1` @ {2}, `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 3: return $t1 +} + + +[variant baseline] +fun FunctionCall::foo_child($t0: u64): u64 { + var $t1: u64 + var $t2: &mut u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [] + # + 0: $t2 := borrow_local($t0) + # live vars: $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [$t2 => #2] + # #2 + # + # #root + # => (mut) #2 via [local `x`] at line 22 + # + 1: $t1 := FunctionCall::foo_grand_child($t2) + # live vars: $t1 + # reaching instruction #2: `t0` @ {1}, `t1` @ {1}, `t2` @ {0} + # refs: [] + # + 2: return $t1 +} + + +[variant baseline] +fun FunctionCall::foo_grand_child($t0: &mut u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t2 := read_ref($t0) + # live vars: $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [] + # + 1: $t3 := 1 + # live vars: $t2, $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 2: $t1 := +($t2, $t3) + # live vars: $t1 + # reaching instruction #3: `t1` @ {2}, `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 3: return $t1 +} + + +[variant baseline] +fun FunctionCall::foo_ref($t0: &u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t2 := read_ref($t0) + # live vars: $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [] + # + 1: $t3 := 1 + # live vars: $t2, $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 2: $t1 := +($t2, $t3) + # live vars: $t1 + # reaching instruction #3: `t1` @ {2}, `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 3: return $t1 +} + + +[variant baseline] +fun FunctionCall::foo_vec(): vector { + var $t0: vector + # live vars: + # reaching instruction #0: + # refs: [] + # + 0: $t0 := vector::empty() + # live vars: $t0 + # reaching instruction #1: `t0` @ {0} + # refs: [] + # + 1: return $t0 +} + + +[variant baseline] +fun FunctionCall::foo_with_external_call($t0: u64): u64 { + var $t1: u64 + var $t2: 0x1::bit_vector::BitVector + var $t3: &0x1::bit_vector::BitVector + # live vars: $t0 + # reaching instruction #0: + # refs: [] + # + 0: $t2 := bit_vector::new($t0) + # live vars: $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [] + # + 1: $t3 := borrow_local($t2) + # live vars: $t3 + # reaching instruction #2: `t2` @ {0}, `t3` @ {1} + # refs: [$t3 => #3] + # #3 + # + # #root + # => #3 via [local `bv`] at line 29 + # + 2: $t1 := bit_vector::length($t3) + # live vars: $t1 + # reaching instruction #3: `t1` @ {2}, `t2` @ {0}, `t3` @ {1} + # refs: [] + # + 3: return $t1 +} + +============ after CommonSubexpElimination: ================ + +[variant baseline] +fun FunctionCall::bar($t0: u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + 0: $t2 := FunctionCall::foo($t0) + 1: $t3 := infer($t2) + 2: $t1 := +($t2, $t3) + 3: return $t1 +} + + +[variant baseline] +fun FunctionCall::bar_optimal($t0: u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + var $t4: u64 + var $t5: u64 + 0: $t2 := FunctionCall::foo($t0) + 1: $t4 := infer($t2) + 2: $t5 := infer($t2) + 3: $t3 := +($t4, $t5) + 4: $t1 := +($t3, $t2) + 5: return $t1 +} + + +[variant baseline] +fun FunctionCall::bar_recursive($t0: u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + 0: $t2 := FunctionCall::foo_child($t0) + 1: $t3 := infer($t2) + 2: $t1 := +($t2, $t3) + 3: return $t1 +} + + +[variant baseline] +fun FunctionCall::bar_ref($t0: u64): u64 { + var $t1: u64 + var $t2: &u64 + var $t3: u64 + var $t4: u64 + 0: $t2 := borrow_local($t0) + 1: $t3 := FunctionCall::foo_ref($t2) + 2: $t4 := infer($t3) + 3: $t1 := +($t3, $t4) + 4: return $t1 +} + + +[variant baseline] +fun FunctionCall::bar_ref2($t0: u64): u64 { + var $t1: u64 + var $t2: &u64 + var $t3: u64 + var $t4: &mut u64 + var $t5: &u64 [unused] + var $t6: u64 + var $t7: u64 + 0: $t2 := borrow_local($t0) + 1: $t3 := FunctionCall::foo_ref($t2) + 2: $t4 := borrow_local($t0) + 3: $t6 := infer($t3) + 4: $t7 := infer($t3) + 5: $t1 := +($t7, $t6) + 6: return $t1 +} + + +[variant baseline] +fun FunctionCall::bar_vec(): vector { + var $t0: vector + var $t1: vector + 0: $t1 := FunctionCall::foo_vec() + 1: $t0 := infer($t1) + 2: return $t0 +} + + +[variant baseline] +fun FunctionCall::bar_with_external_call($t0: u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + 0: $t2 := FunctionCall::foo_with_external_call($t0) + 1: $t3 := infer($t2) + 2: $t1 := +($t2, $t3) + 3: return $t1 +} + + +[variant baseline] +fun FunctionCall::foo($t0: u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + 0: $t2 := infer($t0) + 1: $t3 := 1 + 2: $t1 := +($t2, $t3) + 3: return $t1 +} + + +[variant baseline] +fun FunctionCall::foo_child($t0: u64): u64 { + var $t1: u64 + var $t2: &mut u64 + 0: $t2 := borrow_local($t0) + 1: $t1 := FunctionCall::foo_grand_child($t2) + 2: return $t1 +} + + +[variant baseline] +fun FunctionCall::foo_grand_child($t0: &mut u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + 0: $t2 := read_ref($t0) + 1: $t3 := 1 + 2: $t1 := +($t2, $t3) + 3: return $t1 +} + + +[variant baseline] +fun FunctionCall::foo_ref($t0: &u64): u64 { + var $t1: u64 + var $t2: u64 + var $t3: u64 + 0: $t2 := read_ref($t0) + 1: $t3 := 1 + 2: $t1 := +($t2, $t3) + 3: return $t1 +} + + +[variant baseline] +fun FunctionCall::foo_vec(): vector { + var $t0: vector + 0: $t0 := vector::empty() + 1: return $t0 +} + + +[variant baseline] +fun FunctionCall::foo_with_external_call($t0: u64): u64 { + var $t1: u64 + var $t2: 0x1::bit_vector::BitVector + var $t3: &0x1::bit_vector::BitVector + 0: $t2 := bit_vector::new($t0) + 1: $t3 := borrow_local($t2) + 2: $t1 := bit_vector::length($t3) + 3: return $t1 +} + + +============ disassembled file-format ================== +// Bytecode version v9 +module 0x99::FunctionCall +use 0x1::bit_vector +// Function definition at index 0 +fun bar(l0: u64): u64 + local l1: u64 + move_loc l0 + call foo + st_loc l0 + copy_loc l0 + st_loc l1 + // @5 + move_loc l0 + move_loc l1 + add + ret + +// Function definition at index 1 +fun bar_optimal(l0: u64): u64 + move_loc l0 + call foo + st_loc l0 + copy_loc l0 + copy_loc l0 + // @5 + add + move_loc l0 + add + ret + +// Function definition at index 2 +fun bar_recursive(l0: u64): u64 + local l1: u64 + move_loc l0 + call foo_child + st_loc l0 + copy_loc l0 + st_loc l1 + // @5 + move_loc l0 + move_loc l1 + add + ret + +// Function definition at index 3 +fun bar_ref(l0: u64): u64 + local l1: u64 + local l2: u64 + borrow_loc l0 + call foo_ref + st_loc l1 + copy_loc l1 + st_loc l2 + // @5 + move_loc l1 + move_loc l2 + add + ret + +// Function definition at index 4 +fun bar_ref2(l0: u64): u64 + local l1: u64 + local l2: &mut u64 + local l3: u64 + borrow_loc l0 + call foo_ref + st_loc l1 + mut_borrow_loc l0 + pop + // @5 + copy_loc l1 + st_loc l3 + move_loc l1 + move_loc l3 + add + // @10 + ret + +// Function definition at index 5 +fun bar_vec(): vector + call foo_vec + ret + +// Function definition at index 6 +fun bar_with_external_call(l0: u64): u64 + local l1: u64 + move_loc l0 + call foo_with_external_call + st_loc l0 + copy_loc l0 + st_loc l1 + // @5 + move_loc l0 + move_loc l1 + add + ret + +// Function definition at index 7 +fun foo(l0: u64): u64 + move_loc l0 + ld_u64 1 + add + ret + +// Function definition at index 8 +fun foo_child(l0: u64): u64 + mut_borrow_loc l0 + call foo_grand_child + ret + +// Function definition at index 9 +fun foo_grand_child(l0: &mut u64): u64 + move_loc l0 + read_ref + ld_u64 1 + add + ret + +// Function definition at index 10 +fun foo_ref(l0: &u64): u64 + move_loc l0 + read_ref + ld_u64 1 + add + ret + +// Function definition at index 11 +fun foo_vec(): vector + vec_pack , 0 + ret + +// Function definition at index 12 +fun foo_with_external_call(l0: u64): u64 + local l1: bit_vector::BitVector + move_loc l0 + call bit_vector::new + st_loc l1 + borrow_loc l1 + call bit_vector::length + // @5 + ret + + +============ bytecode verification succeeded ======== diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/global_access.move b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/global_access.move new file mode 100644 index 0000000000000..ef5a7299d9f09 --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/global_access.move @@ -0,0 +1,78 @@ +module 0x99::GlobalAccess { + use std::signer; + use std::vector; + + struct S has key { + val: u64, + } + + struct S1 has key, drop { + val: u64, + } + + fun move_from_S1(account: &signer): S1 { + move_from(signer::address_of(account)) + } + + fun dummy_func(): vector { + vector::empty() + } + + // `borrow_global(addr).val` can be reused + // perf_gain: 1 call to `address_of` + 1 global access + 1 readref eliminated + // new_cost: + // - `u64` flushed and copied twice + // - the result of `r2.val` needs to be adjust on stack (one st_loc and one move_loc) + fun test_global_borrow(account: &signer): u64 { + let r1 = borrow_global(signer::address_of(account)); + let r2 = borrow_global(signer::address_of(account)); + r1.val + r2.val + } + + // `borrow_global(addr)` can be reused + // perf_gain: 1 call to `address_of` + 1 global access + 1 readref eliminated + // new_cost: + // - `u64` flushed and copied twice + // - the result of `r2.val` needs to be adjust on stack (one st_loc and one move_loc) + fun test_global_borrow_v2(account: &signer): u64 { + let r1 = borrow_global(signer::address_of(account)); + move_from(signer::address_of(account)); + let r2 = borrow_global(signer::address_of(account)); + r1.val + r2.val + } + + // `borrow_global(addr)` can be reused + // perf_gain: 1 call to `address_of` + 1 global access + 1 readref eliminated + // new_cost: + // - `u64` flushed and copied twice + // - the result of `r2.val` needs to be adjust on stack (one st_loc and one move_loc) + fun test_global_borrow_v3(account: &signer): u64 { + let r1 = borrow_global(signer::address_of(account)); + move_from_S1(account); + let r2 = borrow_global(signer::address_of(account)); + r1.val + r2.val + } + + // `borrow_global(addr)` can be reused + // perf_gain: 1 call to `address_of` + 1 global access + 1 readref eliminated + // new_cost: + // - `u64` flushed and copied twice + // - the result of `r2.val` needs to be adjust on stack (one st_loc and one move_loc) + fun test_global_borrow_v4(account: &signer): u64 { + let r1 = borrow_global(signer::address_of(account)); + dummy_func(); + let r2 = borrow_global(signer::address_of(account)); + r1.val + r2.val + } + + // `exists(addr)` can be reused + // perf_gain: 1 call to `address_of` and 1 global existence check eliminated + // new_cost: + // - `bool` flushed and copied twice + fun test_existence_check(account: &signer): bool { + let b1 = exists(signer::address_of(account)); + borrow_global(signer::address_of(account)); + let b2 = exists(signer::address_of(account)); + b1 && b2 + } +} diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/global_access.off.exp b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/global_access.off.exp new file mode 100644 index 0000000000000..20834f69a1a3e --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/global_access.off.exp @@ -0,0 +1,141 @@ + +============ disassembled file-format ================== +// Bytecode version v9 +module 0x99::GlobalAccess +use 0x1::signer +struct S has key + val: u64 + +struct S1 has drop + key + val: u64 + +// Function definition at index 0 +fun dummy_func(): vector + vec_pack , 0 + ret + +// Function definition at index 1 +fun move_from_S1(l0: &signer): S1 acquires S1 + move_loc l0 + call signer::address_of + move_from S1 + ret + +// Function definition at index 2 +fun test_existence_check(l0: &signer): bool acquires S + local l1: &S + local l2: bool + copy_loc l0 + call signer::address_of + exists S + copy_loc l0 + call signer::address_of + // @5 + borrow_global S + pop + move_loc l0 + call signer::address_of + exists S + // @10 + st_loc l2 + br_false l0 + move_loc l2 + ret +l0: ld_false + // @15 + ret + +// Function definition at index 3 +fun test_global_borrow(l0: &signer): u64 acquires S + local l1: &S + copy_loc l0 + call signer::address_of + borrow_global S + move_loc l0 + call signer::address_of + // @5 + borrow_global S + st_loc l1 + borrow_field S, val + read_ref + move_loc l1 + // @10 + borrow_field S, val + read_ref + add + ret + +// Function definition at index 4 +fun test_global_borrow_v2(l0: &signer): u64 acquires S, S1 + local l1: &S + copy_loc l0 + call signer::address_of + borrow_global S + copy_loc l0 + call signer::address_of + // @5 + move_from S1 + pop + move_loc l0 + call signer::address_of + borrow_global S + // @10 + st_loc l1 + borrow_field S, val + read_ref + move_loc l1 + borrow_field S, val + // @15 + read_ref + add + ret + +// Function definition at index 5 +fun test_global_borrow_v3(l0: &signer): u64 acquires S, S1 + local l1: &S + copy_loc l0 + call signer::address_of + borrow_global S + copy_loc l0 + call move_from_S1 + // @5 + pop + move_loc l0 + call signer::address_of + borrow_global S + st_loc l1 + // @10 + borrow_field S, val + read_ref + move_loc l1 + borrow_field S, val + read_ref + // @15 + add + ret + +// Function definition at index 6 +fun test_global_borrow_v4(l0: &signer): u64 acquires S + local l1: &S + copy_loc l0 + call signer::address_of + borrow_global S + call dummy_func + pop + // @5 + move_loc l0 + call signer::address_of + borrow_global S + st_loc l1 + borrow_field S, val + // @10 + read_ref + move_loc l1 + borrow_field S, val + read_ref + add + // @15 + ret + + +============ bytecode verification succeeded ======== diff --git a/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/global_access.on.exp b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/global_access.on.exp new file mode 100644 index 0000000000000..3a43f0a2c4e03 --- /dev/null +++ b/third_party/move/move-compiler-v2/tests/common-subexp-elimination/optimized/global_access.on.exp @@ -0,0 +1,896 @@ +============ after ReachingDefProcessor: ================ + +[variant baseline] +fun GlobalAccess::dummy_func(): vector { + var $t0: vector + # live vars: + # reaching instruction #0: + # refs: [] + # + 0: $t0 := vector::empty() + # live vars: $t0 + # reaching instruction #1: `t0` @ {0} + # refs: [] + # + 1: return $t0 +} + + +[variant baseline] +fun GlobalAccess::move_from_S1($t0: &signer): 0x99::GlobalAccess::S1 { + var $t1: 0x99::GlobalAccess::S1 + var $t2: address + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t2 := signer::address_of($t0) + # live vars: $t2 + # reaching instruction #1: `t2` @ {0} + # refs: [] + # + 1: $t1 := move_from<0x99::GlobalAccess::S1>($t2) + # live vars: $t1 + # reaching instruction #2: `t1` @ {1}, `t2` @ {0}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {1} + # refs: [] + # + 2: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_existence_check($t0: &signer): bool { + var $t1: bool + var $t2: bool + var $t3: address + var $t4: &0x99::GlobalAccess::S + var $t5: address + var $t6: bool + var $t7: address + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t3 := signer::address_of($t0) + # live vars: $t0, $t3 + # reaching instruction #1: `t3` @ {0} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 1: $t2 := exists<0x99::GlobalAccess::S>($t3) + # live vars: $t0, $t2 + # reaching instruction #2: `t2` @ {1}, `t3` @ {0} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 2: $t5 := signer::address_of($t0) + # flush: $t4 + # live vars: $t0, $t2, $t5 + # reaching instruction #3: `t2` @ {1}, `t3` @ {0}, `t5` @ {2} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 3: $t4 := borrow_global<0x99::GlobalAccess::S>($t5) + # live vars: $t0, $t2 + # reaching instruction #4: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 4: $t7 := signer::address_of($t0) + # flush: $t6 + # live vars: $t2, $t7 + # reaching instruction #5: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t7` @ {4} + # refs: [] + # + 5: $t6 := exists<0x99::GlobalAccess::S>($t7) + # live vars: $t2, $t6 + # reaching instruction #6: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4} + # refs: [] + # + 6: if ($t2) goto 7 else goto 10 + # live vars: $t6 + # reaching instruction #7: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4} + # refs: [] + # + 7: label L0 + # flush: $t1 + # live vars: $t6 + # reaching instruction #8: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4} + # refs: [] + # + 8: $t1 := infer($t6) + # live vars: $t1 + # reaching instruction #9: `t1` @ {8}, `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t7` @ {4} + # refs: [] + # + 9: goto 12 + # live vars: $t6 + # reaching instruction #10: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4} + # refs: [] + # + 10: label L1 + # flush: $t1 + # live vars: + # reaching instruction #11: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4} + # refs: [] + # + 11: $t1 := false + # live vars: $t1 + # reaching instruction #12: `t1` @ {8, 11}, `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4} + # refs: [] + # + 12: label L2 + # live vars: $t1 + # reaching instruction #13: `t1` @ {8, 11}, `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4} + # refs: [] + # + 13: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_global_borrow($t0: &signer): u64 { + var $t1: u64 + var $t2: &0x99::GlobalAccess::S + var $t3: address + var $t4: &0x99::GlobalAccess::S + var $t5: address + var $t6: u64 + var $t7: &u64 + var $t8: u64 + var $t9: &u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t3 := signer::address_of($t0) + # live vars: $t0, $t3 + # reaching instruction #1: `t3` @ {0} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 1: $t2 := borrow_global<0x99::GlobalAccess::S>($t3) + # live vars: $t0, $t2 + # reaching instruction #2: `t2` @ {1}, `t3` @ {0} + # refs: [$t0 => #0, $t2 => #2] + # #0 + # + # #2 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 27 + # + 2: $t5 := signer::address_of($t0) + # flush: $t4 + # live vars: $t2, $t5 + # reaching instruction #3: `t2` @ {1}, `t3` @ {0}, `t5` @ {2} + # refs: [$t2 => #2] + # #2 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 27 + # + 3: $t4 := borrow_global<0x99::GlobalAccess::S>($t5) + # live vars: $t2, $t4 + # reaching instruction #4: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2} + # refs: [$t2 => #2, $t4 => #4] + # #2 + # + # #4 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 27 + # -> #4 via [struct `GlobalAccess::S`] at line 28 + # + 4: $t7 := borrow_field<0x99::GlobalAccess::S>.val($t2) + # live vars: $t4, $t7 + # reaching instruction #5: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t7` @ {4} + # refs: [$t4 => #4, $t7 => #7] + # #4 + # + # #7 + # + # #root + # -> #4 via [struct `GlobalAccess::S`] at line 28 + # -> #7 via [struct `GlobalAccess::S`] at line 29 + # + 5: $t6 := read_ref($t7) + # live vars: $t4, $t6 + # reaching instruction #6: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4} + # refs: [$t4 => #4] + # #4 + # + # #root + # -> #4 via [struct `GlobalAccess::S`] at line 28 + # + 6: $t9 := borrow_field<0x99::GlobalAccess::S>.val($t4) + # live vars: $t6, $t9 + # reaching instruction #7: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4}, `t9` @ {6} + # refs: [$t9 => #9] + # #9 + # + # #root + # -> #9 via [struct `GlobalAccess::S`] at line 29 + # + 7: $t8 := read_ref($t9) + # live vars: $t6, $t8 + # reaching instruction #8: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4}, `t8` @ {7}, `t9` @ {6} + # refs: [] + # + 8: $t1 := +($t6, $t8) + # live vars: $t1 + # reaching instruction #9: `t1` @ {8}, `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4}, `t8` @ {7}, `t9` @ {6} + # refs: [] + # + 9: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_global_borrow_v2($t0: &signer): u64 { + var $t1: u64 + var $t2: &0x99::GlobalAccess::S + var $t3: address + var $t4: 0x99::GlobalAccess::S1 + var $t5: address + var $t6: &0x99::GlobalAccess::S + var $t7: address + var $t8: u64 + var $t9: &u64 + var $t10: u64 + var $t11: &u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t3 := signer::address_of($t0) + # live vars: $t0, $t3 + # reaching instruction #1: `t3` @ {0} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 1: $t2 := borrow_global<0x99::GlobalAccess::S>($t3) + # live vars: $t0, $t2 + # reaching instruction #2: `t2` @ {1}, `t3` @ {0} + # refs: [$t0 => #0, $t2 => #2] + # #0 + # + # #2 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 38 + # + 2: $t5 := signer::address_of($t0) + # flush: $t4 + # live vars: $t0, $t2, $t5 + # reaching instruction #3: `t2` @ {1}, `t3` @ {0}, `t5` @ {2} + # refs: [$t0 => #0, $t2 => #2] + # #0 + # + # #2 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 38 + # + 3: $t4 := move_from<0x99::GlobalAccess::S1>($t5) + # live vars: $t0, $t2 + # reaching instruction #4: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {3} + # refs: [$t0 => #0, $t2 => #2] + # #0 + # + # #2 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 38 + # + 4: $t7 := signer::address_of($t0) + # flush: $t6 + # live vars: $t2, $t7 + # reaching instruction #5: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t7` @ {4}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {3} + # refs: [$t2 => #2] + # #2 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 38 + # + 5: $t6 := borrow_global<0x99::GlobalAccess::S>($t7) + # live vars: $t2, $t6 + # reaching instruction #6: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {3} + # refs: [$t2 => #2, $t6 => #6] + # #2 + # + # #6 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 38 + # -> #6 via [struct `GlobalAccess::S`] at line 40 + # + 6: $t9 := borrow_field<0x99::GlobalAccess::S>.val($t2) + # live vars: $t6, $t9 + # reaching instruction #7: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4}, `t9` @ {6}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {3} + # refs: [$t6 => #6, $t9 => #9] + # #6 + # + # #9 + # + # #root + # -> #6 via [struct `GlobalAccess::S`] at line 40 + # -> #9 via [struct `GlobalAccess::S`] at line 41 + # + 7: $t8 := read_ref($t9) + # live vars: $t6, $t8 + # reaching instruction #8: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4}, `t8` @ {7}, `t9` @ {6}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {3} + # refs: [$t6 => #6] + # #6 + # + # #root + # -> #6 via [struct `GlobalAccess::S`] at line 40 + # + 8: $t11 := borrow_field<0x99::GlobalAccess::S>.val($t6) + # live vars: $t8, $t11 + # reaching instruction #9: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4}, `t8` @ {7}, `t9` @ {6}, `t11` @ {8}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {3} + # refs: [$t11 => #11] + # #11 + # + # #root + # -> #11 via [struct `GlobalAccess::S`] at line 41 + # + 9: $t10 := read_ref($t11) + # live vars: $t8, $t10 + # reaching instruction #10: `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4}, `t8` @ {7}, `t9` @ {6}, `t10` @ {9}, `t11` @ {8}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {3} + # refs: [] + # + 10: $t1 := +($t8, $t10) + # live vars: $t1 + # reaching instruction #11: `t1` @ {10}, `t2` @ {1}, `t3` @ {0}, `t4` @ {3}, `t5` @ {2}, `t6` @ {5}, `t7` @ {4}, `t8` @ {7}, `t9` @ {6}, `t10` @ {9}, `t11` @ {8}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {3} + # refs: [] + # + 11: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_global_borrow_v3($t0: &signer): u64 { + var $t1: u64 + var $t2: &0x99::GlobalAccess::S + var $t3: address + var $t4: 0x99::GlobalAccess::S1 + var $t5: &0x99::GlobalAccess::S + var $t6: address + var $t7: u64 + var $t8: &u64 + var $t9: u64 + var $t10: &u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t3 := signer::address_of($t0) + # live vars: $t0, $t3 + # reaching instruction #1: `t3` @ {0} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 1: $t2 := borrow_global<0x99::GlobalAccess::S>($t3) + # flush: $t4 + # live vars: $t0, $t2 + # reaching instruction #2: `t2` @ {1}, `t3` @ {0} + # refs: [$t0 => #0, $t2 => #2] + # #0 + # + # #2 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 50 + # + 2: $t4 := GlobalAccess::move_from_S1($t0) + # live vars: $t0, $t2 + # reaching instruction #3: `t2` @ {1}, `t3` @ {0}, `t4` @ {2}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {2} + # refs: [$t0 => #0, $t2 => #2] + # #0 + # + # #2 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 50 + # + 3: $t6 := signer::address_of($t0) + # flush: $t5 + # live vars: $t2, $t6 + # reaching instruction #4: `t2` @ {1}, `t3` @ {0}, `t4` @ {2}, `t6` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {2} + # refs: [$t2 => #2] + # #2 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 50 + # + 4: $t5 := borrow_global<0x99::GlobalAccess::S>($t6) + # live vars: $t2, $t5 + # reaching instruction #5: `t2` @ {1}, `t3` @ {0}, `t4` @ {2}, `t5` @ {4}, `t6` @ {3}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {2} + # refs: [$t2 => #2, $t5 => #5] + # #2 + # + # #5 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 50 + # -> #5 via [struct `GlobalAccess::S`] at line 52 + # + 5: $t8 := borrow_field<0x99::GlobalAccess::S>.val($t2) + # live vars: $t5, $t8 + # reaching instruction #6: `t2` @ {1}, `t3` @ {0}, `t4` @ {2}, `t5` @ {4}, `t6` @ {3}, `t8` @ {5}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {2} + # refs: [$t5 => #5, $t8 => #8] + # #5 + # + # #8 + # + # #root + # -> #5 via [struct `GlobalAccess::S`] at line 52 + # -> #8 via [struct `GlobalAccess::S`] at line 53 + # + 6: $t7 := read_ref($t8) + # live vars: $t5, $t7 + # reaching instruction #7: `t2` @ {1}, `t3` @ {0}, `t4` @ {2}, `t5` @ {4}, `t6` @ {3}, `t7` @ {6}, `t8` @ {5}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {2} + # refs: [$t5 => #5] + # #5 + # + # #root + # -> #5 via [struct `GlobalAccess::S`] at line 52 + # + 7: $t10 := borrow_field<0x99::GlobalAccess::S>.val($t5) + # live vars: $t7, $t10 + # reaching instruction #8: `t2` @ {1}, `t3` @ {0}, `t4` @ {2}, `t5` @ {4}, `t6` @ {3}, `t7` @ {6}, `t8` @ {5}, `t10` @ {7}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {2} + # refs: [$t10 => #10] + # #10 + # + # #root + # -> #10 via [struct `GlobalAccess::S`] at line 53 + # + 8: $t9 := read_ref($t10) + # live vars: $t7, $t9 + # reaching instruction #9: `t2` @ {1}, `t3` @ {0}, `t4` @ {2}, `t5` @ {4}, `t6` @ {3}, `t7` @ {6}, `t8` @ {5}, `t9` @ {8}, `t10` @ {7}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {2} + # refs: [] + # + 9: $t1 := +($t7, $t9) + # live vars: $t1 + # reaching instruction #10: `t1` @ {9}, `t2` @ {1}, `t3` @ {0}, `t4` @ {2}, `t5` @ {4}, `t6` @ {3}, `t7` @ {6}, `t8` @ {5}, `t9` @ {8}, `t10` @ {7}, `Struct QualifiedId { module_id: ModuleId(2), id: StructId(Symbol(141)) }` @ {2} + # refs: [] + # + 10: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_global_borrow_v4($t0: &signer): u64 { + var $t1: u64 + var $t2: &0x99::GlobalAccess::S + var $t3: address + var $t4: vector + var $t5: &0x99::GlobalAccess::S + var $t6: address + var $t7: u64 + var $t8: &u64 + var $t9: u64 + var $t10: &u64 + # live vars: $t0 + # reaching instruction #0: + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 0: $t3 := signer::address_of($t0) + # live vars: $t0, $t3 + # reaching instruction #1: `t3` @ {0} + # refs: [$t0 => #0] + # #0 + # + # #root + # + # + 1: $t2 := borrow_global<0x99::GlobalAccess::S>($t3) + # flush: $t4 + # live vars: $t0, $t2 + # reaching instruction #2: `t2` @ {1}, `t3` @ {0} + # refs: [$t0 => #0, $t2 => #2] + # #0 + # + # #2 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 62 + # + 2: $t4 := GlobalAccess::dummy_func() + # live vars: $t0, $t2 + # reaching instruction #3: `t2` @ {1}, `t3` @ {0}, `t4` @ {2} + # refs: [$t0 => #0, $t2 => #2] + # #0 + # + # #2 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 62 + # + 3: $t6 := signer::address_of($t0) + # flush: $t5 + # live vars: $t2, $t6 + # reaching instruction #4: `t2` @ {1}, `t3` @ {0}, `t4` @ {2}, `t6` @ {3} + # refs: [$t2 => #2] + # #2 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 62 + # + 4: $t5 := borrow_global<0x99::GlobalAccess::S>($t6) + # live vars: $t2, $t5 + # reaching instruction #5: `t2` @ {1}, `t3` @ {0}, `t4` @ {2}, `t5` @ {4}, `t6` @ {3} + # refs: [$t2 => #2, $t5 => #5] + # #2 + # + # #5 + # + # #root + # -> #2 via [struct `GlobalAccess::S`] at line 62 + # -> #5 via [struct `GlobalAccess::S`] at line 64 + # + 5: $t8 := borrow_field<0x99::GlobalAccess::S>.val($t2) + # live vars: $t5, $t8 + # reaching instruction #6: `t2` @ {1}, `t3` @ {0}, `t4` @ {2}, `t5` @ {4}, `t6` @ {3}, `t8` @ {5} + # refs: [$t5 => #5, $t8 => #8] + # #5 + # + # #8 + # + # #root + # -> #5 via [struct `GlobalAccess::S`] at line 64 + # -> #8 via [struct `GlobalAccess::S`] at line 65 + # + 6: $t7 := read_ref($t8) + # live vars: $t5, $t7 + # reaching instruction #7: `t2` @ {1}, `t3` @ {0}, `t4` @ {2}, `t5` @ {4}, `t6` @ {3}, `t7` @ {6}, `t8` @ {5} + # refs: [$t5 => #5] + # #5 + # + # #root + # -> #5 via [struct `GlobalAccess::S`] at line 64 + # + 7: $t10 := borrow_field<0x99::GlobalAccess::S>.val($t5) + # live vars: $t7, $t10 + # reaching instruction #8: `t2` @ {1}, `t3` @ {0}, `t4` @ {2}, `t5` @ {4}, `t6` @ {3}, `t7` @ {6}, `t8` @ {5}, `t10` @ {7} + # refs: [$t10 => #10] + # #10 + # + # #root + # -> #10 via [struct `GlobalAccess::S`] at line 65 + # + 8: $t9 := read_ref($t10) + # live vars: $t7, $t9 + # reaching instruction #9: `t2` @ {1}, `t3` @ {0}, `t4` @ {2}, `t5` @ {4}, `t6` @ {3}, `t7` @ {6}, `t8` @ {5}, `t9` @ {8}, `t10` @ {7} + # refs: [] + # + 9: $t1 := +($t7, $t9) + # live vars: $t1 + # reaching instruction #10: `t1` @ {9}, `t2` @ {1}, `t3` @ {0}, `t4` @ {2}, `t5` @ {4}, `t6` @ {3}, `t7` @ {6}, `t8` @ {5}, `t9` @ {8}, `t10` @ {7} + # refs: [] + # + 10: return $t1 +} + +============ after CommonSubexpElimination: ================ + +[variant baseline] +fun GlobalAccess::dummy_func(): vector { + var $t0: vector + 0: $t0 := vector::empty() + 1: return $t0 +} + + +[variant baseline] +fun GlobalAccess::move_from_S1($t0: &signer): 0x99::GlobalAccess::S1 { + var $t1: 0x99::GlobalAccess::S1 + var $t2: address + 0: $t2 := signer::address_of($t0) + 1: $t1 := move_from<0x99::GlobalAccess::S1>($t2) + 2: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_existence_check($t0: &signer): bool { + var $t1: bool + var $t2: bool + var $t3: address + var $t4: &0x99::GlobalAccess::S + var $t5: address + var $t6: bool + var $t7: address [unused] + 0: $t3 := signer::address_of($t0) + 1: $t2 := exists<0x99::GlobalAccess::S>($t3) + 2: $t5 := infer($t3) + 3: $t4 := borrow_global<0x99::GlobalAccess::S>($t5) + 4: $t6 := infer($t2) + 5: if ($t2) goto 6 else goto 9 + 6: label L0 + 7: $t1 := infer($t6) + 8: goto 11 + 9: label L1 + 10: $t1 := false + 11: label L2 + 12: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_global_borrow($t0: &signer): u64 { + var $t1: u64 + var $t2: &0x99::GlobalAccess::S + var $t3: address + var $t4: &0x99::GlobalAccess::S [unused] + var $t5: address [unused] + var $t6: u64 + var $t7: &u64 + var $t8: u64 + var $t9: &u64 [unused] + 0: $t3 := signer::address_of($t0) + 1: $t2 := borrow_global<0x99::GlobalAccess::S>($t3) + 2: $t7 := borrow_field<0x99::GlobalAccess::S>.val($t2) + 3: $t6 := read_ref($t7) + 4: $t8 := infer($t6) + 5: $t1 := +($t6, $t8) + 6: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_global_borrow_v2($t0: &signer): u64 { + var $t1: u64 + var $t2: &0x99::GlobalAccess::S + var $t3: address + var $t4: 0x99::GlobalAccess::S1 + var $t5: address + var $t6: &0x99::GlobalAccess::S [unused] + var $t7: address [unused] + var $t8: u64 + var $t9: &u64 + var $t10: u64 + var $t11: &u64 [unused] + 0: $t3 := signer::address_of($t0) + 1: $t2 := borrow_global<0x99::GlobalAccess::S>($t3) + 2: $t5 := infer($t3) + 3: $t4 := move_from<0x99::GlobalAccess::S1>($t5) + 4: $t9 := borrow_field<0x99::GlobalAccess::S>.val($t2) + 5: $t8 := read_ref($t9) + 6: $t10 := infer($t8) + 7: $t1 := +($t8, $t10) + 8: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_global_borrow_v3($t0: &signer): u64 { + var $t1: u64 + var $t2: &0x99::GlobalAccess::S + var $t3: address + var $t4: 0x99::GlobalAccess::S1 + var $t5: &0x99::GlobalAccess::S [unused] + var $t6: address [unused] + var $t7: u64 + var $t8: &u64 + var $t9: u64 + var $t10: &u64 [unused] + 0: $t3 := signer::address_of($t0) + 1: $t2 := borrow_global<0x99::GlobalAccess::S>($t3) + 2: $t4 := GlobalAccess::move_from_S1($t0) + 3: $t8 := borrow_field<0x99::GlobalAccess::S>.val($t2) + 4: $t7 := read_ref($t8) + 5: $t9 := infer($t7) + 6: $t1 := +($t7, $t9) + 7: return $t1 +} + + +[variant baseline] +fun GlobalAccess::test_global_borrow_v4($t0: &signer): u64 { + var $t1: u64 + var $t2: &0x99::GlobalAccess::S + var $t3: address + var $t4: vector + var $t5: &0x99::GlobalAccess::S [unused] + var $t6: address [unused] + var $t7: u64 + var $t8: &u64 + var $t9: u64 + var $t10: &u64 [unused] + 0: $t3 := signer::address_of($t0) + 1: $t2 := borrow_global<0x99::GlobalAccess::S>($t3) + 2: $t4 := GlobalAccess::dummy_func() + 3: $t8 := borrow_field<0x99::GlobalAccess::S>.val($t2) + 4: $t7 := read_ref($t8) + 5: $t9 := infer($t7) + 6: $t1 := +($t7, $t9) + 7: return $t1 +} + + +============ disassembled file-format ================== +// Bytecode version v9 +module 0x99::GlobalAccess +use 0x1::signer +struct S has key + val: u64 + +struct S1 has drop + key + val: u64 + +// Function definition at index 0 +fun dummy_func(): vector + vec_pack , 0 + ret + +// Function definition at index 1 +fun move_from_S1(l0: &signer): S1 acquires S1 + move_loc l0 + call signer::address_of + move_from S1 + ret + +// Function definition at index 2 +fun test_existence_check(l0: &signer): bool acquires S + local l1: address + local l2: bool + local l3: &S + local l4: bool + move_loc l0 + call signer::address_of + st_loc l1 + copy_loc l1 + exists S + // @5 + st_loc l2 + move_loc l1 + borrow_global S + pop + copy_loc l2 + // @10 + st_loc l4 + move_loc l2 + br_false l0 + move_loc l4 + ret + // @15 +l0: ld_false + ret + +// Function definition at index 3 +fun test_global_borrow(l0: &signer): u64 acquires S + local l1: u64 + local l2: u64 + move_loc l0 + call signer::address_of + borrow_global S + borrow_field S, val + read_ref + // @5 + st_loc l1 + copy_loc l1 + st_loc l2 + move_loc l1 + move_loc l2 + // @10 + add + ret + +// Function definition at index 4 +fun test_global_borrow_v2(l0: &signer): u64 acquires S, S1 + local l1: address + local l2: u64 + local l3: u64 + move_loc l0 + call signer::address_of + st_loc l1 + copy_loc l1 + borrow_global S + // @5 + move_loc l1 + move_from S1 + pop + borrow_field S, val + read_ref + // @10 + st_loc l2 + copy_loc l2 + st_loc l3 + move_loc l2 + move_loc l3 + // @15 + add + ret + +// Function definition at index 5 +fun test_global_borrow_v3(l0: &signer): u64 acquires S, S1 + local l1: u64 + local l2: u64 + copy_loc l0 + call signer::address_of + borrow_global S + move_loc l0 + call move_from_S1 + // @5 + pop + borrow_field S, val + read_ref + st_loc l1 + copy_loc l1 + // @10 + st_loc l2 + move_loc l1 + move_loc l2 + add + ret + +// Function definition at index 6 +fun test_global_borrow_v4(l0: &signer): u64 acquires S + local l1: u64 + local l2: u64 + move_loc l0 + call signer::address_of + borrow_global S + call dummy_func + pop + // @5 + borrow_field S, val + read_ref + st_loc l1 + copy_loc l1 + st_loc l2 + // @10 + move_loc l1 + move_loc l2 + add + ret + + +============ 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 6d178306c3404..bdea04c6bec68 100644 --- a/third_party/move/move-compiler-v2/tests/testsuite.rs +++ b/third_party/move/move-compiler-v2/tests/testsuite.rs @@ -459,6 +459,25 @@ const TEST_CONFIGS: Lazy> = Lazy::new(|| { // For testing .exp(Experiment::VARIABLE_COALESCING_ANNOTATE) }, + // Common exbpression elimination tests + TestConfig { + name: "common-subexp-elim-on", + runner: |p| run_test(p, get_config_by_name("common-subexp-elim-on")), + include: vec!["/common-subexp-elimination/"], + exp_suffix: Some("on.exp"), + dump_bytecode: DumpLevel::AllStages, + dump_bytecode_filter: Some(vec!["ReachingDefProcessor", "CommonSubexpElimination", FILE_FORMAT_STAGE]), + ..config().exp(Experiment::COMMON_SUBEXP_ELIMINATION) + }, + TestConfig { + name: "common-subexp-elim-off", + runner: |p| run_test(p, get_config_by_name("common-subexp-elim-off")), + include: vec!["/common-subexp-elimination/"], + exp_suffix: Some("off.exp"), + dump_bytecode: DumpLevel::AllStages, + dump_bytecode_filter: Some(vec![FILE_FORMAT_STAGE]), + ..config().exp_off(Experiment::COMMON_SUBEXP_ELIMINATION) + }, // Flush writes processor tests TestConfig { name: "flush-writes-on",