Skip to content

Commit b82be0f

Browse files
authored
LocalCSE: ignore traps (#4085)
If we replace A A A with (local.set A) (local.get) (local.get) then it is ok for A to trap (so long as it does so deterministically), as if it does trap then the first appearance will do so, and the others not be reached anyhow. This helps GC code as often there are repeated struct.gets and such that may trap.
1 parent d1bea49 commit b82be0f

File tree

3 files changed

+59
-9
lines changed

3 files changed

+59
-9
lines changed

src/passes/LocalCSE.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,15 @@ struct Checker
422422
// away repeated apperances if it has any.
423423
EffectAnalyzer effects(options, getModule()->features, curr);
424424

425+
// We can ignore traps here, as we replace a repeating expression with a
426+
// single appearance of it, a store to a local, and gets in the other
427+
// locations, and so if the expression traps then the first appearance -
428+
// that we keep around - would trap, and the others are never reached
429+
// anyhow. (The other checks we perform here, including invalidation and
430+
// determinism, will ensure that either all of the appearances trap, or
431+
// none of them.)
432+
effects.trap = false;
433+
425434
// We also cannot optimize away something that is intrinsically
426435
// nondeterministic: even if it has no side effects, if it may return a
427436
// different result each time, then we cannot optimize away repeats.

test/lit/passes/local-cse.wast

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -266,20 +266,21 @@
266266
)
267267

268268
;; CHECK: (func $loads
269+
;; CHECK-NEXT: (local $0 i32)
269270
;; CHECK-NEXT: (drop
270-
;; CHECK-NEXT: (i32.load
271-
;; CHECK-NEXT: (i32.const 10)
271+
;; CHECK-NEXT: (local.tee $0
272+
;; CHECK-NEXT: (i32.load
273+
;; CHECK-NEXT: (i32.const 10)
274+
;; CHECK-NEXT: )
272275
;; CHECK-NEXT: )
273276
;; CHECK-NEXT: )
274277
;; CHECK-NEXT: (drop
275-
;; CHECK-NEXT: (i32.load
276-
;; CHECK-NEXT: (i32.const 10)
277-
;; CHECK-NEXT: )
278+
;; CHECK-NEXT: (local.get $0)
278279
;; CHECK-NEXT: )
279280
;; CHECK-NEXT: )
280281
(func $loads
281-
;; The possible trap on loads prevents optimization.
282-
;; TODO: optimize that too
282+
;; The possible trap on loads does not prevent optimization, since if we
283+
;; trap then it doesn't matter that we replaced the later expression.
283284
(drop
284285
(i32.load (i32.const 10))
285286
)

test/lit/passes/local-cse_all-features.wast

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,48 @@
6565
;; CHECK: (type $B (array (mut i32)))
6666
(type $B (array (mut i32)))
6767

68+
69+
;; CHECK: (type $ref?|$A|_=>_none (func (param (ref null $A))))
70+
6871
;; CHECK: (type $none_=>_none (func))
6972

73+
;; CHECK: (func $struct-gets-nullable (param $ref (ref null $A))
74+
;; CHECK-NEXT: (local $1 i32)
75+
;; CHECK-NEXT: (drop
76+
;; CHECK-NEXT: (local.tee $1
77+
;; CHECK-NEXT: (struct.get $A 0
78+
;; CHECK-NEXT: (local.get $ref)
79+
;; CHECK-NEXT: )
80+
;; CHECK-NEXT: )
81+
;; CHECK-NEXT: )
82+
;; CHECK-NEXT: (drop
83+
;; CHECK-NEXT: (local.get $1)
84+
;; CHECK-NEXT: )
85+
;; CHECK-NEXT: (drop
86+
;; CHECK-NEXT: (local.get $1)
87+
;; CHECK-NEXT: )
88+
;; CHECK-NEXT: )
89+
(func $struct-gets-nullable (param $ref (ref null $A))
90+
;; Repeated loads from a struct can be optimized, even with a nullable
91+
;; reference: if we trap, it does not matter that we replaced the later
92+
;; expressions).
93+
(drop
94+
(struct.get $A 0
95+
(local.get $ref)
96+
)
97+
)
98+
(drop
99+
(struct.get $A 0
100+
(local.get $ref)
101+
)
102+
)
103+
(drop
104+
(struct.get $A 0
105+
(local.get $ref)
106+
)
107+
)
108+
)
109+
70110
;; CHECK: (func $struct-gets (param $ref (ref $A))
71111
;; CHECK-NEXT: (local $1 i32)
72112
;; CHECK-NEXT: (drop
@@ -86,8 +126,8 @@
86126
(func $struct-gets (param $ref (ref $A))
87127
;; Repeated loads from a struct can be optimized.
88128
;;
89-
;; Note that these struct.gets cannot trap as the reference is non-nullable,
90-
;; so there are no side effects here, and we can optimize.
129+
;; A potential trap would not stop us (see previous testcase), but here
130+
;; there is also no trap possible anyhow, and we should optimize.
91131
(drop
92132
(struct.get $A 0
93133
(local.get $ref)

0 commit comments

Comments
 (0)