Skip to content

Commit 2c9103e

Browse files
authored
Fix HeapStoreOptimization on struct.new traps due to null descriptors (#7761)
Fixes #7759
1 parent f8e164d commit 2c9103e

File tree

3 files changed

+90
-0
lines changed

3 files changed

+90
-0
lines changed

scripts/test/fuzzing.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
'remove-unused-brs-desc.wast',
133133
'vacuum-desc.wast',
134134
'j2cl-merge-itables-desc.wast',
135+
'heap-store-optimization-desc.wast',
135136
# TODO: fix split_wast() on tricky escaping situations like a string ending
136137
# in \\" (the " is not escaped - there is an escaped \ before it)
137138
'string-lifting-section.wast',

src/passes/HeapStoreOptimization.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,16 @@ struct HeapStoreOptimization
248248
}
249249
}
250250

251+
// We also cannot reorder if the struct.new itself has effects (which it can
252+
// in the case of a descriptor) that interact with X' (from the comment
253+
// above), as e.g. a struct.new trap happens before effects in X', but after
254+
// the optimization X' would happen first.
255+
ShallowEffectAnalyzer structNewEffects(
256+
getPassOptions(), *getModule(), new_);
257+
if (structNewEffects.invalidates(setValueEffects)) {
258+
return false;
259+
}
260+
251261
// We must also be careful of branches out from the value that skip the
252262
// local.set, see below.
253263
if (canSkipLocalSet(set, setValueEffects, localSet)) {
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
2+
;; RUN: wasm-opt %s --heap-store-optimization -all -S -o - \
3+
;; RUN: | filecheck %s
4+
5+
(module
6+
(rec
7+
;; CHECK: (rec
8+
;; CHECK-NEXT: (type $struct (descriptor $desc (struct (field (mut i32)))))
9+
(type $struct (sub final (descriptor $desc (struct (field (mut i32))))))
10+
;; CHECK: (type $desc (sub (describes $struct (struct))))
11+
(type $desc (sub (describes $struct (struct))))
12+
)
13+
14+
;; CHECK: (import "" "" (func $effect (type $2)))
15+
(import "" "" (func $effect))
16+
17+
;; CHECK: (func $no-reorder (type $2)
18+
;; CHECK-NEXT: (local $struct (ref $struct))
19+
;; CHECK-NEXT: (local.set $struct
20+
;; CHECK-NEXT: (struct.new_default $struct
21+
;; CHECK-NEXT: (ref.null none)
22+
;; CHECK-NEXT: )
23+
;; CHECK-NEXT: )
24+
;; CHECK-NEXT: (struct.set $struct 0
25+
;; CHECK-NEXT: (local.get $struct)
26+
;; CHECK-NEXT: (block $block (result i32)
27+
;; CHECK-NEXT: (call $effect)
28+
;; CHECK-NEXT: (i32.const 0)
29+
;; CHECK-NEXT: )
30+
;; CHECK-NEXT: )
31+
;; CHECK-NEXT: )
32+
(func $no-reorder
33+
(local $struct (ref $struct))
34+
(local.set $struct
35+
;; This traps on the null descriptor, so we cannot optimize the struct.set
36+
;; into the struct.new (which would call $effect before the trap).
37+
(struct.new_default $struct
38+
(ref.null none)
39+
)
40+
)
41+
(struct.set $struct 0
42+
(local.get $struct)
43+
(block $block (result i32)
44+
(call $effect)
45+
(i32.const 0)
46+
)
47+
)
48+
)
49+
50+
;; CHECK: (func $yes-reorder (type $2)
51+
;; CHECK-NEXT: (local $struct (ref $struct))
52+
;; CHECK-NEXT: (local.set $struct
53+
;; CHECK-NEXT: (struct.new $struct
54+
;; CHECK-NEXT: (block $block (result i32)
55+
;; CHECK-NEXT: (call $effect)
56+
;; CHECK-NEXT: (i32.const 0)
57+
;; CHECK-NEXT: )
58+
;; CHECK-NEXT: (struct.new_default $desc)
59+
;; CHECK-NEXT: )
60+
;; CHECK-NEXT: )
61+
;; CHECK-NEXT: (nop)
62+
;; CHECK-NEXT: )
63+
(func $yes-reorder
64+
(local $struct (ref $struct))
65+
;; As above, but now the descriptor does not trap, so we optimize.
66+
(local.set $struct
67+
(struct.new_default $struct
68+
(struct.new $desc)
69+
)
70+
)
71+
(struct.set $struct 0
72+
(local.get $struct)
73+
(block $block (result i32)
74+
(call $effect)
75+
(i32.const 0)
76+
)
77+
)
78+
)
79+
)

0 commit comments

Comments
 (0)