Skip to content

Commit 1a2c70e

Browse files
committed
When a memory's min and max are equal, it will never move
This allows us to set the `readonly` and `can_move` flags on the load of the memory's heap base, which allows the optimizer to perform GVN- and LICM-style deduplication and code motion on the heap base load, which ultimately results in better code.
1 parent 6ba6e13 commit 1a2c70e

File tree

3 files changed

+81
-4
lines changed

3 files changed

+81
-4
lines changed

crates/environ/src/types.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2019,6 +2019,12 @@ impl Memory {
20192019
return false;
20202020
}
20212021

2022+
// If its minimum and maximum are the same, then the memory will never
2023+
// be resized, and therefore will never move.
2024+
if self.limits.max.is_some_and(|max| self.limits.min == max) {
2025+
return false;
2026+
}
2027+
20222028
// If the maximum size of this memory is above the threshold of the
20232029
// initial memory reservation then the memory may move.
20242030
let max = self.maximum_byte_size().unwrap_or(u64::MAX);

tests/disas/fixed-size-memory.wat

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@
2626
;; gv2 = load.i64 notrap aligned gv1+16
2727
;; gv3 = vmctx
2828
;; gv4 = load.i64 notrap aligned gv3+72
29-
;; gv5 = load.i64 notrap aligned can_move checked gv3+64
29+
;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+64
3030
;; stack_limit = gv2
3131
;;
3232
;; block0(v0: i64, v1: i64, v2: i32, v3: i32):
3333
;; @0041 v4 = uextend.i64 v2
3434
;; @0041 v5 = iconst.i64 0x0001_0000
3535
;; @0041 v6 = icmp uge v4, v5 ; v5 = 0x0001_0000
3636
;; @0041 trapnz v6, heap_oob
37-
;; @0041 v7 = load.i64 notrap aligned can_move checked v0+64
37+
;; @0041 v7 = load.i64 notrap aligned readonly can_move checked v0+64
3838
;; @0041 v8 = iadd v7, v4
3939
;; @0041 istore8 little heap v3, v8
4040
;; @0044 jump block1
@@ -49,15 +49,15 @@
4949
;; gv2 = load.i64 notrap aligned gv1+16
5050
;; gv3 = vmctx
5151
;; gv4 = load.i64 notrap aligned gv3+72
52-
;; gv5 = load.i64 notrap aligned can_move checked gv3+64
52+
;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+64
5353
;; stack_limit = gv2
5454
;;
5555
;; block0(v0: i64, v1: i64, v2: i32):
5656
;; @0049 v4 = uextend.i64 v2
5757
;; @0049 v5 = iconst.i64 0x0001_0000
5858
;; @0049 v6 = icmp uge v4, v5 ; v5 = 0x0001_0000
5959
;; @0049 trapnz v6, heap_oob
60-
;; @0049 v7 = load.i64 notrap aligned can_move checked v0+64
60+
;; @0049 v7 = load.i64 notrap aligned readonly can_move checked v0+64
6161
;; @0049 v8 = iadd v7, v4
6262
;; @0049 v9 = uload8.i32 little heap v8
6363
;; @004c jump block1
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
;;! target = "x86_64"
2+
;;! test = "optimize"
3+
;;! flags = "-O memory-may-move=y -O memory-reservation=0"
4+
5+
(module
6+
(import "" "" (func $imp))
7+
8+
;; Minimum and maximum sizes are equal. Therefore, this memory should never
9+
;; move, regardless of any other memory-related settings.
10+
(memory 1 1)
11+
12+
;; And therefore, the heap base should be marked read-only and can-move and
13+
;; should get code-motioned up out of the loop below, even though we call some
14+
;; foreign function in the loop body.
15+
(func $f (param $base i32)
16+
(local $i i32)
17+
18+
(local.set $i (i32.const 0))
19+
20+
(loop
21+
;; Call a foreign function. This would usually otherwise defeat
22+
;; code-motioning the heap base out of the loop.
23+
(call $imp)
24+
25+
;; Do a memory operation, which should use the heap base, but should be
26+
;; code-motioned above the loop, because its load should be marked both
27+
;; read-only and can-move.
28+
(i32.store (i32.add (local.get $base) (local.get $i)) (i32.const 0))
29+
30+
;; Increment `i` and continue the loop.
31+
(local.set $i (i32.add (local.get $i) (i32.const 1)))
32+
(br 0)
33+
)
34+
)
35+
)
36+
;; function u0:1(i64 vmctx, i64, i32) tail {
37+
;; gv0 = vmctx
38+
;; gv1 = load.i64 notrap aligned readonly gv0+8
39+
;; gv2 = load.i64 notrap aligned gv1+16
40+
;; gv3 = vmctx
41+
;; gv4 = load.i64 notrap aligned gv3+72
42+
;; gv5 = load.i64 notrap aligned readonly can_move checked gv3+64
43+
;; sig0 = (i64 vmctx, i64) tail
44+
;; fn0 = u0:0 sig0
45+
;; stack_limit = gv2
46+
;;
47+
;; block0(v0: i64, v1: i64, v2: i32):
48+
;; @0028 v3 = iconst.i32 0
49+
;; @0030 v6 = load.i64 notrap aligned readonly can_move v0+80
50+
;; @0030 v7 = load.i64 notrap aligned readonly can_move v0+96
51+
;; @0039 v13 = iconst.i64 0x0001_0000
52+
;; @0039 v17 = iconst.i64 0
53+
;; @0039 v15 = load.i64 notrap aligned readonly can_move checked v0+64
54+
;; @003e v19 = iconst.i32 1
55+
;; @002e jump block2(v3) ; v3 = 0
56+
;;
57+
;; block2(v9: i32):
58+
;; @0030 call_indirect.i64 sig0, v6(v7, v0)
59+
;; v22 = iconst.i32 0
60+
;; @0036 v10 = iadd.i32 v2, v9
61+
;; @0039 v12 = uextend.i64 v10
62+
;; v23 = iconst.i64 0x0001_0000
63+
;; v24 = icmp ugt v12, v23 ; v23 = 0x0001_0000
64+
;; @0039 v16 = iadd.i64 v15, v12
65+
;; v25 = iconst.i64 0
66+
;; v26 = select_spectre_guard v24, v25, v16 ; v25 = 0
67+
;; @0039 store little heap v22, v26 ; v22 = 0
68+
;; v27 = iconst.i32 1
69+
;; v28 = iadd v9, v27 ; v27 = 1
70+
;; @0043 jump block2(v28)
71+
;; }

0 commit comments

Comments
 (0)