Skip to content

Commit 355fae6

Browse files
authored
Always allow trivial exact casts (#7516)
We usually disallow casts to exact types when custom descriptors are disallowed to prevent the cast semantics changing when the binary writer makes the cast types inexact. At the same time, we plan to allow exact types to appear elsewhere in the IR even when custom descriptors are not enabled. This means that the input to a cast may have an exact type. Finalization of casts sets the cast target to be the GLB of the old cast target and the input type, so this could produce invalid exact casts. However, this situation only arises when the cast will trivially succeed because the cast target is a supertype of the input type. If the cast input were exact and the cast target were not a supertype of the input type, then the GLB would be an inexact bottom type, so there would be no problem. Since these trivial casts would remain trivial after binary writing removes exactness, it is safe to allow them to validate even when custom descriptors is disabled. Make this change to avoid finalization producing invalid casts. The alternative would be to add logic to cast finalization to avoid introducing exactness, but that would both be more complicated and would lose type information, harming optimization power.
1 parent da4652b commit 355fae6

File tree

3 files changed

+106
-15
lines changed

3 files changed

+106
-15
lines changed

scripts/test/fuzzing.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
'exact-references.wast',
116116
'exact-references-lowering.wast',
117117
'exact-casts.wast',
118+
'exact-casts-trivial.wast',
118119
'optimize-instructions-exact.wast',
119120
'local-subtyping-exact.wast',
120121
'remove-unused-types-exact.wast',

src/wasm/wasm-validator.cpp

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2904,11 +2904,17 @@ void FunctionValidator::visitRefTest(RefTest* curr) {
29042904
curr,
29052905
"ref.test target type and ref type must have a common supertype");
29062906

2907-
shouldBeTrue(curr->castType.isInexact() ||
2908-
getModule()->features.hasCustomDescriptors(),
2909-
curr,
2910-
"ref.test of exact type requires custom descriptors "
2911-
"[--enable-custom-descriptors]");
2907+
// If custom descriptors is not enabled, only trivial exact casts are allowed,
2908+
// i.e. those where the operand has the same exact type. The result of these
2909+
// trivial exact casts does not change when the types are made inexact during
2910+
// binary writing.
2911+
if (!getModule()->features.hasCustomDescriptors()) {
2912+
shouldBeTrue(curr->castType.isInexact() ||
2913+
curr->castType == curr->ref->type,
2914+
curr,
2915+
"ref.test of exact type requires custom descriptors "
2916+
"[--enable-custom-descriptors]");
2917+
}
29122918
}
29132919

29142920
void FunctionValidator::visitRefCast(RefCast* curr) {
@@ -2948,11 +2954,13 @@ void FunctionValidator::visitRefCast(RefCast* curr) {
29482954
curr,
29492955
"ref.cast null of non-nullable references are not allowed");
29502956

2951-
shouldBeTrue(curr->type.isInexact() ||
2952-
getModule()->features.hasCustomDescriptors(),
2953-
curr,
2954-
"ref.cast to exact type requires custom descriptors "
2955-
"[--enable-custom-descriptors]");
2957+
// See comment about exactness on visitRefTest.
2958+
if (!getModule()->features.hasCustomDescriptors()) {
2959+
shouldBeTrue(curr->type.isInexact() || curr->type == curr->ref->type,
2960+
curr,
2961+
"ref.cast to exact type requires custom descriptors "
2962+
"[--enable-custom-descriptors]");
2963+
}
29562964
}
29572965

29582966
void FunctionValidator::visitBrOn(BrOn* curr) {
@@ -2982,11 +2990,14 @@ void FunctionValidator::visitBrOn(BrOn* curr) {
29822990
curr->ref->type,
29832991
curr,
29842992
"br_on_cast* target type must be a subtype of its input type");
2985-
shouldBeTrue(curr->castType.isInexact() ||
2986-
getModule()->features.hasCustomDescriptors(),
2987-
curr,
2988-
"br_on_cast* to exact type requires custom descriptors "
2989-
"[--enable-custom-descriptors]");
2993+
// See comment about exactness on visitRefTest.
2994+
if (!getModule()->features.hasCustomDescriptors()) {
2995+
shouldBeTrue(curr->castType.isInexact() ||
2996+
curr->castType == curr->ref->type,
2997+
curr,
2998+
"br_on_cast* to exact type requires custom descriptors "
2999+
"[--enable-custom-descriptors]");
3000+
}
29903001
} else {
29913002
shouldBeEqual(curr->castType,
29923003
Type(Type::none),
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+
3+
;; Test that trivial exact casts are ok even when custom descriptors is
4+
;; disabled.
5+
6+
;; RUN: wasm-opt %s -all --disable-custom-descriptors -S -o - | filecheck %s
7+
8+
(module
9+
;; CHECK: (type $foo (struct))
10+
(type $foo (struct))
11+
12+
;; CHECK: (func $ref.cast (type $1) (param $0 (ref null (exact $foo)))
13+
;; CHECK-NEXT: (drop
14+
;; CHECK-NEXT: (ref.cast (ref null (exact $foo))
15+
;; CHECK-NEXT: (local.get $0)
16+
;; CHECK-NEXT: )
17+
;; CHECK-NEXT: )
18+
;; CHECK-NEXT: )
19+
(func $ref.cast (param (ref null (exact $foo)))
20+
(drop
21+
(ref.cast anyref
22+
(local.get 0)
23+
)
24+
)
25+
)
26+
27+
;; CHECK: (func $ref.test (type $2) (param $0 (ref (exact $foo)))
28+
;; CHECK-NEXT: (drop
29+
;; CHECK-NEXT: (ref.test (ref (exact $foo))
30+
;; CHECK-NEXT: (local.get $0)
31+
;; CHECK-NEXT: )
32+
;; CHECK-NEXT: )
33+
;; CHECK-NEXT: )
34+
(func $ref.test (param (ref (exact $foo)))
35+
(drop
36+
(ref.test anyref
37+
(local.get 0)
38+
)
39+
)
40+
)
41+
42+
;; CHECK: (func $br_on_cast (type $1) (param $0 (ref null (exact $foo)))
43+
;; CHECK-NEXT: (drop
44+
;; CHECK-NEXT: (block $block (result anyref)
45+
;; CHECK-NEXT: (br_on_cast $block (ref null (exact $foo)) (ref null (exact $foo))
46+
;; CHECK-NEXT: (local.get $0)
47+
;; CHECK-NEXT: )
48+
;; CHECK-NEXT: )
49+
;; CHECK-NEXT: )
50+
;; CHECK-NEXT: )
51+
(func $br_on_cast (param (ref null (exact $foo)))
52+
(drop
53+
(block (result anyref)
54+
(br_on_cast 0 anyref anyref
55+
(local.get 0)
56+
)
57+
)
58+
)
59+
)
60+
61+
;; CHECK: (func $br_on_cast_fail (type $2) (param $0 (ref (exact $foo)))
62+
;; CHECK-NEXT: (drop
63+
;; CHECK-NEXT: (block $block (result anyref)
64+
;; CHECK-NEXT: (br_on_cast_fail $block (ref (exact $foo)) (ref (exact $foo))
65+
;; CHECK-NEXT: (local.get $0)
66+
;; CHECK-NEXT: )
67+
;; CHECK-NEXT: )
68+
;; CHECK-NEXT: )
69+
;; CHECK-NEXT: )
70+
(func $br_on_cast_fail (param (ref (exact $foo)))
71+
(drop
72+
(block (result anyref)
73+
(br_on_cast_fail 0 anyref anyref
74+
(local.get 0)
75+
)
76+
)
77+
)
78+
)
79+
)

0 commit comments

Comments
 (0)