Skip to content

Commit eb00324

Browse files
authored
Avoid invalid exact casts in TypeRefining (#7529)
TypeRefining (both the normal and GUFA variants) is able to infer that a field can be more precise than is immediately apparent from the type of an expression set to that field. In these cases, the pass inserts a cast to recover the more precise type information. When custom descriptors are not enabled, casts to exact types invalid, so avoid introducing new exactness that might need such invalid casts in that case.
1 parent 8c22aa5 commit eb00324

File tree

4 files changed

+199
-1
lines changed

4 files changed

+199
-1
lines changed

scripts/test/fuzzing.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@
122122
'signature-refining-exact.wast',
123123
'gufa-cast-all-exact.wast',
124124
'type-merging-exact.wast',
125+
'type-refining-exact.wast',
126+
'type-refining-gufa-exact.wast',
125127
# TODO: fuzzer support for custom descriptors
126128
'custom-descriptors.wast',
127129
]

src/passes/TypeRefining.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,13 @@ struct FieldInfoScanner
6161
HeapType type,
6262
Index index,
6363
FieldInfo& info) {
64-
info.note(expr->type);
64+
auto noted = expr->type;
65+
// Do not introduce new exact fields that might requires invalid
66+
// casts. Keep any existing exact fields, though.
67+
if (type.getStruct().fields[index].type.isInexact()) {
68+
noted = noted.withInexactIfNoCustomDescs(getModule()->features);
69+
}
70+
info.note(noted);
6571
}
6672

6773
void
@@ -185,6 +191,11 @@ struct TypeRefining : public Pass {
185191
auto& infos = finalInfos[type];
186192
for (Index i = 0; i < fields.size(); i++) {
187193
auto gufaType = oracle.getContents(DataLocation{type, i}).getType();
194+
// Do not introduce new exact fields that might requires invalid
195+
// casts. Keep any existing exact fields, though.
196+
if (!fields[i].type.isExact()) {
197+
gufaType = gufaType.withInexactIfNoCustomDescs(module->features);
198+
}
188199
infos[i] = LUBFinder(gufaType);
189200
}
190201
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
2+
3+
;; Check that we don't refine in ways that might require invalid exact casts
4+
;; when custom descriptors is disabled.
5+
6+
;; RUN: wasm-opt %s -all --closed-world --preserve-type-order \
7+
;; RUN: --type-refining -S -o - | filecheck %s
8+
9+
;; RUN: wasm-opt %s -all --disable-custom-descriptors --closed-world --preserve-type-order \
10+
;; RUN: --type-refining -S -o - | filecheck %s --check-prefix=NO_CD
11+
12+
(module
13+
;; CHECK: (rec
14+
;; CHECK-NEXT: (type $foo (struct))
15+
;; NO_CD: (rec
16+
;; NO_CD-NEXT: (type $foo (struct))
17+
(type $foo (struct))
18+
;; CHECK: (type $bar (struct (field (mut (ref (exact $foo))))))
19+
;; NO_CD: (type $bar (struct (field (mut (ref $foo)))))
20+
(type $bar (struct (field (mut (ref null $foo)))))
21+
22+
;; CHECK: (type $already-exact (struct (field (ref (exact $foo)))))
23+
;; NO_CD: (type $already-exact (struct (field (ref (exact $foo)))))
24+
(type $already-exact (struct (field (ref (exact $foo)))))
25+
26+
;; CHECK: (tag $e (type $3))
27+
;; NO_CD: (tag $e (type $3))
28+
(tag $e)
29+
30+
;; CHECK: (func $struct.new (type $4) (param $inexact (ref null $foo)) (param $exact (ref (exact $foo))) (result anyref)
31+
;; CHECK-NEXT: (struct.new $bar
32+
;; CHECK-NEXT: (ref.cast (ref (exact $foo))
33+
;; CHECK-NEXT: (try (result (ref null $foo))
34+
;; CHECK-NEXT: (do
35+
;; CHECK-NEXT: (struct.get $bar 0
36+
;; CHECK-NEXT: (struct.new $bar
37+
;; CHECK-NEXT: (local.get $exact)
38+
;; CHECK-NEXT: )
39+
;; CHECK-NEXT: )
40+
;; CHECK-NEXT: )
41+
;; CHECK-NEXT: (catch $e
42+
;; CHECK-NEXT: (local.get $inexact)
43+
;; CHECK-NEXT: )
44+
;; CHECK-NEXT: )
45+
;; CHECK-NEXT: )
46+
;; CHECK-NEXT: )
47+
;; CHECK-NEXT: )
48+
;; NO_CD: (func $struct.new (type $4) (param $inexact (ref null $foo)) (param $exact (ref (exact $foo))) (result anyref)
49+
;; NO_CD-NEXT: (struct.new $bar
50+
;; NO_CD-NEXT: (ref.cast (ref $foo)
51+
;; NO_CD-NEXT: (try (result (ref null $foo))
52+
;; NO_CD-NEXT: (do
53+
;; NO_CD-NEXT: (struct.get $bar 0
54+
;; NO_CD-NEXT: (struct.new $bar
55+
;; NO_CD-NEXT: (local.get $exact)
56+
;; NO_CD-NEXT: )
57+
;; NO_CD-NEXT: )
58+
;; NO_CD-NEXT: )
59+
;; NO_CD-NEXT: (catch $e
60+
;; NO_CD-NEXT: (local.get $inexact)
61+
;; NO_CD-NEXT: )
62+
;; NO_CD-NEXT: )
63+
;; NO_CD-NEXT: )
64+
;; NO_CD-NEXT: )
65+
;; NO_CD-NEXT: )
66+
(func $struct.new (param $inexact (ref null $foo)) (param $exact (ref (exact $foo))) (result anyref)
67+
(struct.new $bar
68+
(try (result (ref null $foo))
69+
(do
70+
(struct.get $bar 0
71+
(struct.new $bar
72+
(local.get $exact)
73+
)
74+
)
75+
)
76+
(catch $e
77+
(local.get $inexact)
78+
)
79+
)
80+
)
81+
)
82+
83+
;; CHECK: (func $make-already-exact (type $5) (param $0 (ref (exact $foo))) (result (ref (exact $foo)))
84+
;; CHECK-NEXT: (struct.get $already-exact 0
85+
;; CHECK-NEXT: (struct.new $already-exact
86+
;; CHECK-NEXT: (local.get $0)
87+
;; CHECK-NEXT: )
88+
;; CHECK-NEXT: )
89+
;; CHECK-NEXT: )
90+
;; NO_CD: (func $make-already-exact (type $5) (param $0 (ref (exact $foo))) (result (ref (exact $foo)))
91+
;; NO_CD-NEXT: (struct.get $already-exact 0
92+
;; NO_CD-NEXT: (struct.new $already-exact
93+
;; NO_CD-NEXT: (local.get $0)
94+
;; NO_CD-NEXT: )
95+
;; NO_CD-NEXT: )
96+
;; NO_CD-NEXT: )
97+
(func $make-already-exact (param (ref (exact $foo))) (result (ref (exact $foo)))
98+
;; We should not accidentally remove exactness from a field that is already exact.
99+
(struct.get $already-exact 0
100+
(struct.new $already-exact
101+
(local.get 0)
102+
)
103+
)
104+
)
105+
)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
2+
3+
;; Check that we don't refine in ways that might require invalid exact casts
4+
;; when custom descriptors is disabled.
5+
6+
;; RUN: wasm-opt %s -all --closed-world --preserve-type-order \
7+
;; RUN: --type-refining-gufa -S -o - | filecheck %s
8+
9+
;; RUN: wasm-opt %s -all --disable-custom-descriptors --closed-world --preserve-type-order \
10+
;; RUN: --type-refining-gufa -S -o - | filecheck %s --check-prefix=NO_CD
11+
12+
(module
13+
;; CHECK: (type $foo (struct))
14+
;; NO_CD: (type $foo (struct))
15+
(type $foo (struct))
16+
;; CHECK: (rec
17+
;; CHECK-NEXT: (type $bar (struct (field (ref (exact $foo)))))
18+
;; NO_CD: (rec
19+
;; NO_CD-NEXT: (type $bar (struct (field (ref $foo))))
20+
(type $bar (struct (field (ref null $foo))))
21+
22+
;; CHECK: (type $already-exact (struct (field (ref (exact $foo)))))
23+
;; NO_CD: (type $already-exact (struct (field (ref (exact $foo)))))
24+
(type $already-exact (struct (field (ref (exact $foo)))))
25+
26+
;; CHECK: (import "" "" (global $exact (ref (exact $foo))))
27+
;; NO_CD: (import "" "" (global $exact (ref (exact $foo))))
28+
(import "" "" (global $exact (ref (exact $foo))))
29+
30+
;; CHECK: (global $g (ref $foo) (global.get $exact))
31+
;; NO_CD: (global $g (ref $foo) (global.get $exact))
32+
(global $g (ref $foo) (global.get $exact))
33+
34+
;; CHECK: (func $make-bar (type $3)
35+
;; CHECK-NEXT: (drop
36+
;; CHECK-NEXT: (struct.new $bar
37+
;; CHECK-NEXT: (ref.cast (ref (exact $foo))
38+
;; CHECK-NEXT: (global.get $g)
39+
;; CHECK-NEXT: )
40+
;; CHECK-NEXT: )
41+
;; CHECK-NEXT: )
42+
;; CHECK-NEXT: )
43+
;; NO_CD: (func $make-bar (type $3)
44+
;; NO_CD-NEXT: (drop
45+
;; NO_CD-NEXT: (struct.new $bar
46+
;; NO_CD-NEXT: (global.get $g)
47+
;; NO_CD-NEXT: )
48+
;; NO_CD-NEXT: )
49+
;; NO_CD-NEXT: )
50+
(func $make-bar
51+
(drop
52+
(struct.new $bar
53+
(global.get $g)
54+
)
55+
)
56+
)
57+
58+
;; CHECK: (func $make-already-exact (type $4) (result (ref (exact $foo)))
59+
;; CHECK-NEXT: (struct.get $already-exact 0
60+
;; CHECK-NEXT: (struct.new $already-exact
61+
;; CHECK-NEXT: (global.get $exact)
62+
;; CHECK-NEXT: )
63+
;; CHECK-NEXT: )
64+
;; CHECK-NEXT: )
65+
;; NO_CD: (func $make-already-exact (type $4) (result (ref (exact $foo)))
66+
;; NO_CD-NEXT: (struct.get $already-exact 0
67+
;; NO_CD-NEXT: (struct.new $already-exact
68+
;; NO_CD-NEXT: (global.get $exact)
69+
;; NO_CD-NEXT: )
70+
;; NO_CD-NEXT: )
71+
;; NO_CD-NEXT: )
72+
(func $make-already-exact (result (ref (exact $foo)))
73+
;; We should not accidentally remove exactness from a field that is already exact.
74+
(struct.get $already-exact 0
75+
(struct.new $already-exact
76+
(global.get $exact)
77+
)
78+
)
79+
)
80+
)

0 commit comments

Comments
 (0)