Skip to content

Commit 7dbb670

Browse files
authored
[Custom Descriptors] Interpret descriptor casts (#7677)
Descriptor casts must check that the expected descriptor is not null, check whether the cast value is null, and if so, whether the cast allows null values through, and finally must check whether the descriptor on a non-null cast value matches the expected descriptor. This is fairly different than the implementation of a normal cast, but the possible results are the same. Add a new `doDescCast` helper mirroring the existing `doCast` helper and use it to implement the various descriptor casts.
1 parent 9bd8871 commit 7dbb670

File tree

3 files changed

+299
-8
lines changed

3 files changed

+299
-8
lines changed

src/wasm-interpreter.h

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,6 +1651,36 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
16511651
return typename Cast::Failure{val};
16521652
}
16531653
}
1654+
template<typename T> Cast doDescCast(T* curr) {
1655+
Flow ref = self()->visit(curr->ref);
1656+
if (ref.breaking()) {
1657+
return typename Cast::Breaking{ref};
1658+
}
1659+
Flow desc = self()->visit(curr->desc);
1660+
if (desc.breaking()) {
1661+
return typename Cast::Breaking{ref};
1662+
}
1663+
auto expected = desc.getSingleValue().getGCData();
1664+
if (!expected) {
1665+
trap("null descriptor");
1666+
}
1667+
Literal val = ref.getSingleValue();
1668+
auto data = val.getGCData();
1669+
if (!data) {
1670+
// Check whether null is allowed.
1671+
if (curr->getCastType().isNullable()) {
1672+
return typename Cast::Success{val};
1673+
} else {
1674+
return typename Cast::Failure{val};
1675+
}
1676+
}
1677+
// The cast succeeds if we have the expected descriptor.
1678+
if (data->desc.getGCData() == expected) {
1679+
return typename Cast::Success{val};
1680+
} else {
1681+
return typename Cast::Failure{val};
1682+
}
1683+
}
16541684

16551685
Flow visitRefTest(RefTest* curr) {
16561686
NOTE_ENTER("RefTest");
@@ -1663,7 +1693,7 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
16631693
}
16641694
Flow visitRefCast(RefCast* curr) {
16651695
NOTE_ENTER("RefCast");
1666-
auto cast = doCast(curr);
1696+
auto cast = curr->desc ? doDescCast(curr) : doCast(curr);
16671697
if (auto* breaking = cast.getBreaking()) {
16681698
return *breaking;
16691699
} else if (auto* result = cast.getSuccess()) {
@@ -1690,29 +1720,28 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
16901720
// BrOnCast* uses the casting infrastructure, so handle them first.
16911721
switch (curr->op) {
16921722
case BrOnCast:
1693-
case BrOnCastFail: {
1694-
auto cast = doCast(curr);
1723+
case BrOnCastFail:
1724+
case BrOnCastDesc:
1725+
case BrOnCastDescFail: {
1726+
auto cast = curr->desc ? doDescCast(curr) : doCast(curr);
16951727
if (auto* breaking = cast.getBreaking()) {
16961728
return *breaking;
16971729
} else if (auto* original = cast.getFailure()) {
1698-
if (curr->op == BrOnCast) {
1730+
if (curr->op == BrOnCast || curr->op == BrOnCastDesc) {
16991731
return *original;
17001732
} else {
17011733
return Flow(curr->name, *original);
17021734
}
17031735
} else {
17041736
auto* result = cast.getSuccess();
17051737
assert(result);
1706-
if (curr->op == BrOnCast) {
1738+
if (curr->op == BrOnCast || curr->op == BrOnCastDesc) {
17071739
return Flow(curr->name, *result);
17081740
} else {
17091741
return *result;
17101742
}
17111743
}
17121744
}
1713-
case BrOnCastDesc:
1714-
case BrOnCastDescFail:
1715-
WASM_UNREACHABLE("TODO");
17161745
case BrOnNull:
17171746
case BrOnNonNull: {
17181747
// Otherwise we are just checking for null.

test/spec/br_on_cast_desc.wast

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@
77
(type $sub.desc (sub $super.desc (describes $sub (struct))))
88
)
99

10+
(global $super.desc1 (ref (exact $super.desc)) (struct.new $super.desc))
11+
(global $super.desc2 (ref (exact $super.desc)) (struct.new $super.desc))
12+
(global $super1 (ref $super) (struct.new $super (global.get $super.desc1)))
13+
14+
(global $sub.desc (ref (exact $sub.desc)) (struct.new $sub.desc))
15+
(global $sub (ref $sub) (struct.new $sub (global.get $sub.desc)))
16+
1017
;; br_on_cast_desc
1118

1219
(func $br_on_cast_desc-unreachable (result anyref)
@@ -33,6 +40,78 @@
3340
)
3441
(unreachable)
3542
)
43+
(func (export "cast-success") (result i32)
44+
(drop
45+
(block $l (result anyref)
46+
(br_on_cast_desc $l anyref (ref null $super)
47+
(global.get $super1)
48+
(global.get $super.desc1)
49+
)
50+
(return (i32.const 0))
51+
)
52+
)
53+
(i32.const 1)
54+
)
55+
(func (export "cast-success-supertype") (result i32)
56+
(drop
57+
(block $l (result anyref)
58+
(br_on_cast_desc $l anyref (ref null $super)
59+
(global.get $sub)
60+
(global.get $sub.desc)
61+
)
62+
(return (i32.const 0))
63+
)
64+
)
65+
(i32.const 1)
66+
)
67+
(func (export "cast-success-null") (result i32)
68+
(drop
69+
(block $l (result anyref)
70+
(br_on_cast_desc $l anyref (ref null $super)
71+
(ref.null none)
72+
(global.get $super.desc1)
73+
)
74+
(return (i32.const 0))
75+
)
76+
)
77+
(i32.const 1)
78+
)
79+
(func (export "cast-fail-null") (result i32)
80+
(drop
81+
(block $l (result anyref)
82+
(br_on_cast_desc $l anyref (ref $super)
83+
(ref.null none)
84+
(global.get $super.desc1)
85+
)
86+
(return (i32.const 0))
87+
)
88+
)
89+
(i32.const 1)
90+
)
91+
(func (export "cast-fail-wrong-desc") (result i32)
92+
(drop
93+
(block $l (result anyref)
94+
(br_on_cast_desc $l anyref (ref null $super)
95+
(global.get $super1)
96+
(global.get $super.desc2)
97+
)
98+
(return (i32.const 0))
99+
)
100+
)
101+
(i32.const 1)
102+
)
103+
(func (export "cast-fail-null-desc") (result i32)
104+
(drop
105+
(block $l (result anyref)
106+
(br_on_cast_desc $l anyref (ref null $super)
107+
(global.get $super1)
108+
(ref.null none)
109+
)
110+
(return (i32.const 0))
111+
)
112+
)
113+
(i32.const 1)
114+
)
36115

37116
;; br_on_cast_desc_fail
38117

@@ -61,8 +140,93 @@
61140
)
62141
)
63142
)
143+
(func (export "fail-cast-success") (result i32)
144+
(drop
145+
(block $l (result anyref)
146+
(br_on_cast_desc_fail $l anyref (ref null $super)
147+
(global.get $super1)
148+
(global.get $super.desc1)
149+
)
150+
(return (i32.const 0))
151+
)
152+
)
153+
(i32.const 1)
154+
)
155+
(func (export "fail-cast-success-supertype") (result i32)
156+
(drop
157+
(block $l (result anyref)
158+
(br_on_cast_desc_fail $l anyref (ref null $super)
159+
(global.get $sub)
160+
(global.get $sub.desc)
161+
)
162+
(return (i32.const 0))
163+
)
164+
)
165+
(i32.const 1)
166+
)
167+
(func (export "fail-cast-success-null") (result i32)
168+
(drop
169+
(block $l (result anyref)
170+
(br_on_cast_desc_fail $l anyref (ref null $super)
171+
(ref.null none)
172+
(global.get $super.desc1)
173+
)
174+
(return (i32.const 0))
175+
)
176+
)
177+
(i32.const 1)
178+
)
179+
(func (export "fail-cast-fail-null") (result i32)
180+
(drop
181+
(block $l (result anyref)
182+
(br_on_cast_desc_fail $l anyref (ref $super)
183+
(ref.null none)
184+
(global.get $super.desc1)
185+
)
186+
(return (i32.const 0))
187+
)
188+
)
189+
(i32.const 1)
190+
)
191+
(func (export "fail-cast-fail-wrong-desc") (result i32)
192+
(drop
193+
(block $l (result anyref)
194+
(br_on_cast_desc_fail $l anyref (ref null $super)
195+
(global.get $super1)
196+
(global.get $super.desc2)
197+
)
198+
(return (i32.const 0))
199+
)
200+
)
201+
(i32.const 1)
202+
)
203+
(func (export "fail-cast-fail-null-desc") (result i32)
204+
(drop
205+
(block $l (result anyref)
206+
(br_on_cast_desc_fail $l anyref (ref null $super)
207+
(global.get $super1)
208+
(ref.null none)
209+
)
210+
(return (i32.const 0))
211+
)
212+
)
213+
(i32.const 1)
214+
)
64215
)
65216

217+
(assert_return (invoke "cast-success") (i32.const 1))
218+
(assert_return (invoke "cast-success-supertype") (i32.const 1))
219+
(assert_return (invoke "cast-success-null") (i32.const 1))
220+
(assert_return (invoke "cast-fail-null") (i32.const 0))
221+
(assert_return (invoke "cast-fail-wrong-desc") (i32.const 0))
222+
(assert_trap (invoke "cast-fail-null-desc") "null descriptor")
223+
(assert_return (invoke "fail-cast-success") (i32.const 0))
224+
(assert_return (invoke "fail-cast-success-supertype") (i32.const 0))
225+
(assert_return (invoke "fail-cast-success-null") (i32.const 0))
226+
(assert_return (invoke "fail-cast-fail-null") (i32.const 1))
227+
(assert_return (invoke "fail-cast-fail-wrong-desc") (i32.const 1))
228+
(assert_trap (invoke "fail-cast-fail-null-desc") "null descriptor")
229+
66230
(assert_malformed
67231
;; Input type must be a reference.
68232
(module quote "(module (rec (type $struct (descriptor $desc (struct))) (type $desc (describes $struct (struct)))) (func (result anyref) (unreachable) (br_on_cast_desc 0 i32 (ref null $struct))))")

test/spec/ref.cast_desc.wast

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@
77
(type $sub.desc (sub $super.desc (describes $sub (struct))))
88
)
99

10+
(global $super.desc1 (ref (exact $super.desc)) (struct.new $super.desc))
11+
(global $super.desc2 (ref (exact $super.desc)) (struct.new $super.desc))
12+
(global $super1 (ref $super) (struct.new $super (global.get $super.desc1)))
13+
14+
(global $sub.desc (ref (exact $sub.desc)) (struct.new $sub.desc))
15+
(global $sub (ref $sub) (struct.new $sub (global.get $sub.desc)))
16+
1017
;; ref.cast_desc (ref null ht)
1118

1219
(func $ref.cast_desc-null-unreachable (result anyref)
@@ -32,6 +39,46 @@
3239
(local.get $super.desc)
3340
)
3441
)
42+
(func (export "cast-success")
43+
(drop
44+
(ref.cast_desc (ref null $super)
45+
(global.get $super1)
46+
(global.get $super.desc1)
47+
)
48+
)
49+
)
50+
(func (export "cast-success-supertype")
51+
(drop
52+
(ref.cast_desc (ref null $super)
53+
(global.get $sub)
54+
(global.get $sub.desc)
55+
)
56+
)
57+
)
58+
(func (export "cast-success-null")
59+
(drop
60+
(ref.cast_desc (ref null $super)
61+
(ref.null none)
62+
(global.get $super.desc1)
63+
)
64+
)
65+
)
66+
(func (export "cast-fail-wrong-desc")
67+
(drop
68+
(ref.cast_desc (ref null $super)
69+
(global.get $super1)
70+
(global.get $super.desc2)
71+
)
72+
)
73+
)
74+
(func (export "cast-fail-null-desc")
75+
(drop
76+
(ref.cast_desc (ref null $super)
77+
(global.get $super1)
78+
(ref.null none)
79+
)
80+
)
81+
)
3582

3683
;; ref.cast_desc (ref ht)
3784

@@ -58,8 +105,59 @@
58105
(local.get $super.desc)
59106
)
60107
)
108+
(func (export "cast-nn-success")
109+
(drop
110+
(ref.cast_desc (ref $super)
111+
(global.get $super1)
112+
(global.get $super.desc1)
113+
)
114+
)
115+
)
116+
(func (export "cast-nn-success-supertype")
117+
(drop
118+
(ref.cast_desc (ref $super)
119+
(global.get $sub)
120+
(global.get $sub.desc)
121+
)
122+
)
123+
)
124+
(func (export "cast-nn-fail-null")
125+
(drop
126+
(ref.cast_desc (ref $super)
127+
(ref.null none)
128+
(global.get $super.desc1)
129+
)
130+
)
131+
)
132+
(func (export "cast-nn-fail-wrong-desc")
133+
(drop
134+
(ref.cast_desc (ref $super)
135+
(global.get $super1)
136+
(global.get $super.desc2)
137+
)
138+
)
139+
)
140+
(func (export "cast-nn-fail-null-desc")
141+
(drop
142+
(ref.cast_desc (ref $super)
143+
(global.get $super1)
144+
(ref.null none)
145+
)
146+
)
147+
)
61148
)
62149

150+
(assert_return (invoke "cast-success"))
151+
(assert_return (invoke "cast-success-supertype"))
152+
(assert_return (invoke "cast-success-null"))
153+
(assert_trap (invoke "cast-fail-wrong-desc") "cast error")
154+
(assert_trap (invoke "cast-fail-null-desc") "null descriptor")
155+
(assert_return (invoke "cast-nn-success"))
156+
(assert_return (invoke "cast-nn-success-supertype"))
157+
(assert_trap (invoke "cast-nn-fail-null") "cast error")
158+
(assert_trap (invoke "cast-nn-fail-wrong-desc") "cast error")
159+
(assert_trap (invoke "cast-nn-fail-null-desc") "null descriptor")
160+
63161
(assert_malformed
64162
;; Cast type must be a reference.
65163
(module quote "(module (func (unreachable) (ref.cast_desc i32) (unreachable)))")

0 commit comments

Comments
 (0)