Skip to content

Commit 6001030

Browse files
Merge
2 parents 8d546dc + ca0f641 commit 6001030

6 files changed

Lines changed: 179 additions & 26 deletions

File tree

src/ir/child-typer.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1038,10 +1038,19 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
10381038
}
10391039
ht = curr->ref->type.getHeapType();
10401040
}
1041+
const auto& fields = ht->getStruct().fields;
1042+
if (curr->index >= fields.size()) {
1043+
self().noteUnknown();
1044+
return;
1045+
}
10411046

10421047
note(&curr->ref, Type(*ht, Nullable));
10431048
note(&curr->waitqueue, Type(HeapTypes::sharedWaitqueue, Nullable));
1044-
note(&curr->expected, Type(Type::BasicType::i32));
1049+
auto expectedType = fields[curr->index].type;
1050+
if (expectedType.isRef()) {
1051+
expectedType = Type(HeapTypes::eq.getBasic(Shared), Nullable);
1052+
}
1053+
note(&curr->expected, expectedType);
10451054
note(&curr->timeout, Type(Type::BasicType::i64));
10461055
}
10471056

src/wasm-interpreter.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2324,6 +2324,7 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
23242324

23252325
Flow visitStructWait(StructWait* curr) {
23262326
VISIT(ref, curr->ref)
2327+
VISIT(waitqueue, curr->waitqueue)
23272328
VISIT(expected, curr->expected)
23282329
VISIT(timeout, curr->timeout)
23292330

@@ -2335,8 +2336,11 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
23352336
if (!data) {
23362337
trap("null ref");
23372338
}
2339+
if (!waitqueue.getSingleValue().getGCData()) {
2340+
trap("null ref");
2341+
}
23382342
auto& field = data->values[curr->index];
2339-
if (field.geti32() != expected.getSingleValue().geti32()) {
2343+
if (field != expected.getSingleValue()) {
23402344
return Literal(int32_t{1}); // not equal
23412345
}
23422346
// TODO: Add threads support. For now, report a host limit here, as there

src/wasm/wasm-ir-builder.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2426,6 +2426,7 @@ Result<> IRBuilder::makeStructWait(HeapType type, Index index) {
24262426
}
24272427

24282428
StructWait curr(wasm.allocator);
2429+
curr.index = index;
24292430
CHECK_ERR(ChildPopper{*this}.visitStructWait(&curr, type));
24302431
CHECK_ERR(validateTypeAnnotation(type, curr.ref));
24312432
push(builder.makeStructWait(

src/wasm/wasm-validator.cpp

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,8 @@ struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> {
600600
bool shouldBeTrue(bool result, T curr, const char* text) {
601601
return info.shouldBeTrue(result, curr, text, getFunction());
602602
}
603+
604+
// Returns true if the assertion was met, i.e. returns !result.
603605
template<typename T>
604606
bool shouldBeFalse(bool result, T curr, const char* text) {
605607
return info.shouldBeFalse(result, curr, text, getFunction());
@@ -3679,6 +3681,10 @@ void FunctionValidator::visitStructCmpxchg(StructCmpxchg* curr) {
36793681
}
36803682

36813683
void FunctionValidator::visitStructWait(StructWait* curr) {
3684+
// In IRBuilder, we check that the struct ref matches the type immediate.
3685+
// We can't check this here because we've already discarded the type immediate
3686+
// at this point. All other validations are here.
3687+
36823688
shouldBeTrue(
36833689
!getModule() || getModule()->features.hasSharedEverything(),
36843690
curr,
@@ -3688,20 +3694,44 @@ void FunctionValidator::visitStructWait(StructWait* curr) {
36883694
Type(HeapTypes::sharedWaitqueue, Nullable),
36893695
curr,
36903696
"struct.wait waitqueue must be a shared waitqueue reference");
3691-
shouldBeEqual(curr->expected->type,
3692-
Type(Type::BasicType::i32),
3693-
curr,
3694-
"struct.wait expected must be an i32");
3695-
shouldBeEqual(curr->timeout->type,
3696-
Type(Type::BasicType::i64),
3697-
curr,
3698-
"struct.wait timeout must be an i64");
3697+
shouldBeEqualOrFirstIsUnreachable(curr->timeout->type,
3698+
Type(Type::BasicType::i64),
3699+
curr,
3700+
"struct.wait timeout must be an i64");
3701+
3702+
if (curr->ref->type == Type::unreachable || curr->ref->type.isNull()) {
3703+
return;
3704+
}
3705+
if (!shouldBeTrue(curr->ref->type.isStruct(),
3706+
curr->ref,
3707+
"struct.wait ref must be a struct")) {
3708+
return;
3709+
}
3710+
const auto& fields = curr->ref->type.getHeapType().getStruct().fields;
3711+
if (!shouldBeTrue(
3712+
curr->index < fields.size(), curr, "out of bounds struct.wait field")) {
3713+
return;
3714+
}
3715+
auto& field = fields[curr->index];
3716+
if (!shouldBeFalse(
3717+
field.isPacked(), curr, "struct.wait field must not be packed")) {
3718+
return;
3719+
}
3720+
3721+
if (
3722+
!shouldBeTrue(
3723+
field.type == Type::i32 || field.type == Type::i64 ||
3724+
Type::isSubType(field.type,
3725+
Type(HeapTypes::eq.getBasic(Shared), Nullable)),
3726+
curr,
3727+
R"(struct.wait control word field must be i32, i64 or a subtype of (ref null (shared eq)))")) {
3728+
return;
3729+
}
36993730

3700-
// Checks to the ref argument's type are done in IRBuilder where we have the
3701-
// type annotation immediate available. We check that
3702-
// * The reference arg is a subtype of the type immediate
3703-
// * The index immediate is a valid field index of the type immediate (and
3704-
// thus valid for the reference's type too)
3731+
shouldBeSubType(curr->expected->type,
3732+
field.type,
3733+
curr,
3734+
"struct.wait expected value must match the field immediate");
37053735
}
37063736

37073737
void FunctionValidator::visitWaitqueueNew(WaitqueueNew* curr) {
@@ -3722,10 +3752,10 @@ void FunctionValidator::visitWaitqueueNotify(WaitqueueNotify* curr) {
37223752
Type(HeapTypes::sharedWaitqueue, Nullable),
37233753
curr,
37243754
"waitqueue.notify waitqueue must be a shared waitqueue reference");
3725-
shouldBeEqual(curr->count->type,
3726-
Type(Type::BasicType::i32),
3727-
curr,
3728-
"waitqueue.notify count must be an i32");
3755+
shouldBeEqualOrFirstIsUnreachable(curr->count->type,
3756+
Type(Type::BasicType::i32),
3757+
curr,
3758+
"waitqueue.notify count must be an i32");
37293759
}
37303760

37313761
void FunctionValidator::visitArrayNew(ArrayNew* curr) {

test/lit/validation/waitqueue.wast

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
;; RUN: not wasm-opt --enable-reference-types --enable-gc %s 2>&1 | filecheck %s
2+
3+
;; Tests feature-related validations.
4+
;; Other validations are in the spec test spec/waitqueue.wast.
25
(module
36
(type $struct (struct (field i32)))
47
;; CHECK: waitqueue.new requires shared-everything [--enable-shared-everything]
5-
(func
8+
(func $new
69
(drop (waitqueue.new))
710
)
11+
;; CHECK: struct.wait requires shared-everything [--enable-shared-everything]
12+
(func $wait-no-feature (param $ref (ref $struct)) (param $wq (ref null (shared waitqueue)))
13+
(drop (struct.wait $struct 0 (local.get $ref) (local.get $wq) (i32.const 0) (i64.const 0)))
14+
)
815
)
9-

test/spec/waitqueue.wast

Lines changed: 108 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
(func (param $expected i32) (param $timeout i64) (result i32)
1515
(struct.wait $t 2 (ref.null $t) (global.get $wq) (local.get $expected) (local.get $timeout))
1616
)
17-
) "struct index out of bounds"
17+
) "out of bounds struct.wait field"
1818
)
1919

2020
(assert_invalid
@@ -25,7 +25,29 @@
2525
(func (param $expected i32) (param $timeout i64) (result i32)
2626
(struct.wait $t 0 (global.get $g) (global.get $wq) (i64.const 0) (local.get $timeout))
2727
)
28-
) "struct.wait expected must be an i32"
28+
) "struct.wait expected value must match the field immediate"
29+
)
30+
31+
(assert_invalid
32+
(module
33+
(type $t (shared (struct (field f32))))
34+
(global $g (ref $t) (struct.new $t (f32.const 0)))
35+
(global $wq (ref (shared waitqueue)) (waitqueue.new))
36+
(func (param $expected f32) (param $timeout i64) (result i32)
37+
(struct.wait $t 0 (global.get $g) (global.get $wq) (local.get $expected) (local.get $timeout))
38+
)
39+
) "struct.wait control word field must be i32, i64 or a subtype of (ref null (shared eq))"
40+
)
41+
42+
(assert_invalid
43+
(module
44+
(type $t (shared (struct (field i8))))
45+
(global $g (ref $t) (struct.new $t (i32.const 0)))
46+
(global $wq (ref (shared waitqueue)) (waitqueue.new))
47+
(func (param $expected i32) (param $timeout i64) (result i32)
48+
(struct.wait $t 0 (global.get $g) (global.get $wq) (local.get $expected) (local.get $timeout))
49+
)
50+
) "struct.wait field must not be packed"
2951
)
3052

3153
(assert_invalid
@@ -41,8 +63,6 @@
4163

4264
(assert_invalid
4365
(module
44-
(type $t (shared (struct (field i32))))
45-
(global $wq (ref (shared waitqueue)) (waitqueue.new))
4666
(func (param $count i32) (result i32)
4767
(waitqueue.notify (ref.null waitqueue) (local.get $count))
4868
)
@@ -62,18 +82,29 @@
6282
;; unreachable is allowed
6383
(module
6484
(type $t (shared (struct (field i32))))
85+
(global $g (ref $t) (struct.new $t (i32.const 0)))
6586
(global $wq (ref (shared waitqueue)) (waitqueue.new))
6687
(func (param $expected i32) (param $timeout i64) (result i32)
6788
(struct.wait $t 0 (unreachable) (global.get $wq) (local.get $expected) (local.get $timeout))
6889
)
6990
(func (param $expected i32) (param $timeout i64) (result i32)
70-
(struct.wait $t 0 (ref.null $t) (unreachable) (local.get $expected) (local.get $timeout))
91+
(struct.wait $t 0 (global.get $g) (unreachable) (local.get $expected) (local.get $timeout))
92+
)
93+
(func (param $timeout i64) (result i32)
94+
(struct.wait $t 0 (global.get $g) (global.get $wq) (unreachable) (local.get $timeout))
95+
)
96+
(func (param $expected i32) (result i32)
97+
(struct.wait $t 0 (global.get $g) (global.get $wq) (local.get $expected) (unreachable))
7198
)
7299
(func (param $count i32) (result i32)
73100
(waitqueue.notify (unreachable) (local.get $count))
74101
)
102+
(func (result i32)
103+
(waitqueue.notify (global.get $wq) (unreachable))
104+
)
75105
)
76106

107+
;; i32 control word
77108
(module
78109
(type $t (shared (struct (field (mut i32)))))
79110

@@ -119,6 +150,78 @@
119150
(assert_trap (invoke "struct.wait" (i32.const 0) (i64.const 0)) "null ref")
120151
(assert_trap (invoke "waitqueue.notify" (i32.const 0)) "null ref")
121152

153+
;; i64 control word
154+
(module
155+
(type $t (shared (struct (field (mut i64)))))
156+
157+
(global $g (mut (ref null $t)) (struct.new $t (i64.const 0)))
158+
(global $wq (mut (ref (shared waitqueue))) (waitqueue.new))
159+
160+
(func (export "struct.wait") (param $expected i64) (param $timeout i64) (result i32)
161+
(struct.wait $t 0 (global.get $g) (global.get $wq) (local.get $expected) (local.get $timeout))
162+
)
163+
164+
(func (export "struct.set") (param $val i64)
165+
(struct.set $t 0 (global.get $g) (local.get $val))
166+
)
167+
168+
(func (export "struct.get") (result i64)
169+
(struct.get $t 0 (global.get $g))
170+
)
171+
)
172+
173+
(invoke "struct.set" (i64.const 42))
174+
(assert_return (invoke "struct.get") (i64.const 42))
175+
(assert_return (invoke "struct.wait" (i64.const 0) (i64.const 100)) (i32.const 1))
176+
(assert_return (invoke "struct.wait" (i64.const 42) (i64.const 0)) (i32.const 2))
177+
178+
;; (ref null (shared eq)) control word
179+
(module
180+
(type $control (shared (struct)))
181+
182+
(type $t (shared (struct
183+
(field (mut (ref null (shared eq))))
184+
)))
185+
186+
(global $control1 (ref $control) (struct.new $control))
187+
(global $control2 (ref $control) (struct.new $control))
188+
189+
(global $g (mut (ref null $t)) (struct.new $t
190+
(global.get $control1)
191+
))
192+
193+
(global $wq (mut (ref null (shared waitqueue))) (waitqueue.new))
194+
195+
(func (export "wait_control1") (result i32)
196+
(struct.wait $t 0 (global.get $g) (global.get $wq) (global.get $control1) (i64.const 0))
197+
)
198+
199+
(func (export "wait_control2") (result i32)
200+
(struct.wait $t 0 (global.get $g) (global.get $wq) (global.get $control2) (i64.const 0))
201+
)
202+
203+
(func (export "wait_null") (result i32)
204+
(struct.wait $t 0 (global.get $g) (global.get $wq) (ref.null (shared eq)) (i64.const 0))
205+
)
206+
207+
(func (export "set_control_to_null")
208+
(struct.set $t 0 (global.get $g) (ref.null (shared eq)))
209+
)
210+
)
211+
212+
;; $control1 is the control word, wait 0ns and return 2.
213+
(assert_return (invoke "wait_control1") (i32.const 2))
214+
;; $control2 is not the control word, don't wait and return 1.
215+
(assert_return (invoke "wait_control2") (i32.const 1))
216+
;; ditto for null.
217+
(assert_return (invoke "wait_null") (i32.const 1))
218+
219+
(invoke "set_control_to_null")
220+
221+
;; null is now the control word.
222+
(assert_return (invoke "wait_null") (i32.const 2))
223+
(assert_return (invoke "wait_control1") (i32.const 1))
224+
122225
;; Binary format test for waitqueue and nowaitqueue.
123226
(module binary
124227
"\00asm\01\00\00\00" ;; Wasm header

0 commit comments

Comments
 (0)