Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/ir/child-typer.h
Original file line number Diff line number Diff line change
Expand Up @@ -1036,9 +1036,18 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
ht = curr->ref->type.getHeapType();
}

const auto& fields = ht->getStruct().fields;
assert(curr->index < fields.size());
auto type = fields[curr->index].type;
auto expectedType = type;
if (expectedType.isRef()) {
expectedType =
Type(HeapTypes::eq.getBasic(type.getHeapType().getShared()), Nullable);
}

note(&curr->ref, Type(*ht, Nullable));
note(&curr->waitqueue, Type(HeapTypes::sharedWaitqueue, Nullable));
note(&curr->expected, Type(Type::BasicType::i32));
note(&curr->expected, expectedType);
note(&curr->timeout, Type(Type::BasicType::i64));
}

Expand Down
2 changes: 1 addition & 1 deletion src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -2336,7 +2336,7 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
trap("null ref");
}
auto& field = data->values[curr->index];
if (field.geti32() != expected.getSingleValue().geti32()) {
if (field != expected.getSingleValue()) {
return Literal(int32_t{1}); // not equal
}
// TODO: Add threads support. For now, report a host limit here, as there
Expand Down
33 changes: 29 additions & 4 deletions src/wasm/wasm-validator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3652,10 +3652,35 @@ void FunctionValidator::visitStructWait(StructWait* curr) {
Type(HeapTypes::sharedWaitqueue, Nullable),
curr,
"struct.wait waitqueue must be a shared waitqueue reference");
shouldBeEqual(curr->expected->type,
Type(Type::BasicType::i32),
curr,
"struct.wait expected must be an i32");

if (curr->ref->type != Type::unreachable && curr->ref->type.isRef() &&
!curr->ref->type.getHeapType().isMaybeShared(HeapType::none) &&
curr->ref->type.getHeapType().isStruct()) {
auto type = curr->ref->type.getHeapType();
const auto& fields = type.getStruct().fields;
if (curr->index < fields.size()) {
auto& field = fields[curr->index];
Type expectedExpectedType;
if (field.type == Type::i32) {
expectedExpectedType = Type::i32;
} else if (field.type == Type::i64) {
expectedExpectedType = Type::i64;
} else if (field.type.isRef()) {
expectedExpectedType =
Type(HeapTypes::eq.getBasic(field.type.getHeapType().getShared()),
Nullable);
} else {
shouldBeTrue(
false, curr, "struct.wait field type invalid for operation");
return;
}
shouldBeSubType(curr->expected->type,
expectedExpectedType,
curr,
"struct.wait expected value must have the proper type");
}
}

shouldBeEqual(curr->timeout->type,
Type(Type::BasicType::i64),
curr,
Expand Down
76 changes: 76 additions & 0 deletions test/lit/waitqueue.wast
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@
;; RTRIP: (type $t (shared (struct (field (mut i32)))))
(type $t (shared (struct (field (mut i32)))))

;; CHECK: (type $t64 (shared (struct (field (mut i64)))))

;; CHECK: (type $tref (shared (struct (field (mut (ref null (shared eq)))))))

;; CHECK: (global $g (ref $t) (struct.new $t
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: ))
;; RTRIP: (type $t64 (shared (struct (field (mut i64)))))

;; RTRIP: (type $tref (shared (struct (field (mut (ref null (shared eq)))))))

;; RTRIP: (global $g (ref $t) (struct.new $t
;; RTRIP-NEXT: (i32.const 0)
;; RTRIP-NEXT: ))
Expand All @@ -22,6 +30,14 @@
;; RTRIP: (global $nwq (mut (ref null (shared nowaitqueue))) (ref.null (shared nowaitqueue)))
(global $nwq (mut (ref null (shared nowaitqueue))) (ref.null (shared nowaitqueue)))

;; CHECK: (global $g64 (ref $t64) (struct.new $t64
;; CHECK-NEXT: (i64.const 0)
;; CHECK-NEXT: ))

;; CHECK: (global $gref (ref $tref) (struct.new $tref
;; CHECK-NEXT: (ref.null (shared none))
;; CHECK-NEXT: ))

;; CHECK: (func $wait (type $0)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.wait $t 0
Expand All @@ -32,6 +48,14 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; RTRIP: (global $g64 (ref $t64) (struct.new $t64
;; RTRIP-NEXT: (i64.const 0)
;; RTRIP-NEXT: ))

;; RTRIP: (global $gref (ref $tref) (struct.new $tref
;; RTRIP-NEXT: (ref.null (shared none))
;; RTRIP-NEXT: ))

;; RTRIP: (func $wait (type $0)
;; RTRIP-NEXT: (drop
;; RTRIP-NEXT: (struct.wait $t 0
Expand All @@ -46,6 +70,58 @@
(drop (struct.wait $t 0 (global.get $g) (global.get $wq) (i32.const 0) (i64.const 0)))
)

(type $t64 (shared (struct (field (mut i64)))))
(global $g64 (ref $t64) (struct.new $t64 (i64.const 0)))
;; CHECK: (func $wait_i64 (type $0)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.wait $t64 0
;; CHECK-NEXT: (global.get $g64)
;; CHECK-NEXT: (global.get $wq)
;; CHECK-NEXT: (i64.const 0)
;; CHECK-NEXT: (i64.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; RTRIP: (func $wait_i64 (type $0)
;; RTRIP-NEXT: (drop
;; RTRIP-NEXT: (struct.wait $t64 0
;; RTRIP-NEXT: (global.get $g64)
;; RTRIP-NEXT: (global.get $wq)
;; RTRIP-NEXT: (i64.const 0)
;; RTRIP-NEXT: (i64.const 0)
;; RTRIP-NEXT: )
;; RTRIP-NEXT: )
;; RTRIP-NEXT: )
(func $wait_i64
(drop (struct.wait $t64 0 (global.get $g64) (global.get $wq) (i64.const 0) (i64.const 0)))
)

(type $tref (shared (struct (field (mut (ref null (shared eq)))))))
(global $gref (ref $tref) (struct.new $tref (ref.null (shared eq))))
;; CHECK: (func $wait_ref (type $0)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.wait $tref 0
;; CHECK-NEXT: (global.get $gref)
;; CHECK-NEXT: (global.get $wq)
;; CHECK-NEXT: (ref.null (shared none))
;; CHECK-NEXT: (i64.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; RTRIP: (func $wait_ref (type $0)
;; RTRIP-NEXT: (drop
;; RTRIP-NEXT: (struct.wait $tref 0
;; RTRIP-NEXT: (global.get $gref)
;; RTRIP-NEXT: (global.get $wq)
;; RTRIP-NEXT: (ref.null (shared none))
;; RTRIP-NEXT: (i64.const 0)
;; RTRIP-NEXT: )
;; RTRIP-NEXT: )
;; RTRIP-NEXT: )
(func $wait_ref
(drop (struct.wait $tref 0 (global.get $gref) (global.get $wq) (ref.null (shared eq)) (i64.const 0)))
)

;; CHECK: (func $notify (type $0)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (waitqueue.notify
Expand Down
24 changes: 23 additions & 1 deletion test/spec/waitqueue.wast
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
(func (param $expected i32) (param $timeout i64) (result i32)
(struct.wait $t 0 (global.get $g) (global.get $wq) (i64.const 0) (local.get $timeout))
)
) "struct.wait expected must be an i32"
) "struct.wait expected value must have the proper type"
)

(assert_invalid
Expand Down Expand Up @@ -76,19 +76,33 @@

(module
(type $t (shared (struct (field (mut i32)))))
(type $t64 (shared (struct (field (mut i64)))))
(type $tref (shared (struct (field (mut (ref null (shared eq)))))))

(global $g (mut (ref null $t)) (struct.new $t (i32.const 0)))
(global $g64 (mut (ref null $t64)) (struct.new $t64 (i64.const 0)))
(global $gref (mut (ref null $tref)) (struct.new $tref (ref.null (shared eq))))
(global $wq (mut (ref null (shared waitqueue))) (waitqueue.new))

(func (export "setToNull")
(global.set $g (ref.null $t))
(global.set $g64 (ref.null $t64))
(global.set $gref (ref.null $tref))
(global.set $wq (ref.null (shared waitqueue)))
)

(func (export "struct.wait") (param $expected i32) (param $timeout i64) (result i32)
(struct.wait $t 0 (global.get $g) (global.get $wq) (local.get $expected) (local.get $timeout))
)

(func (export "struct.wait_i64") (param $expected i64) (param $timeout i64) (result i32)
(struct.wait $t64 0 (global.get $g64) (global.get $wq) (local.get $expected) (local.get $timeout))
)

(func (export "struct.wait_ref") (param $expected (ref null (shared eq))) (param $timeout i64) (result i32)
(struct.wait $tref 0 (global.get $gref) (global.get $wq) (local.get $expected) (local.get $timeout))
)

(func (export "waitqueue.notify") (param $count i32) (result i32)
(waitqueue.notify (global.get $wq) (local.get $count))
)
Expand All @@ -111,12 +125,20 @@
;; Control word matched, wait 0ns and return 2.
(assert_return (invoke "struct.wait" (i32.const 1) (i64.const 0)) (i32.const 2))

(assert_return (invoke "struct.wait_i64" (i64.const 1) (i64.const 100)) (i32.const 1))
(assert_return (invoke "struct.wait_i64" (i64.const 0) (i64.const 0)) (i32.const 2))

(assert_return (invoke "struct.wait_ref" (ref.i31 (i32.const 0)) (i64.const 100)) (i32.const 1))
(assert_return (invoke "struct.wait_ref" (ref.null (shared eq)) (i64.const 0)) (i32.const 2))

;; Try to wake up 1 thread, but no-one was waiting.
(assert_return (invoke "waitqueue.notify" (i32.const 1)) (i32.const 0))

(invoke "setToNull")

(assert_trap (invoke "struct.wait" (i32.const 0) (i64.const 0)) "null ref")
(assert_trap (invoke "struct.wait_i64" (i64.const 0) (i64.const 0)) "null ref")
(assert_trap (invoke "struct.wait_ref" (ref.null (shared eq)) (i64.const 0)) "null ref")
(assert_trap (invoke "waitqueue.notify" (i32.const 0)) "null ref")

;; Binary format test for waitqueue and nowaitqueue.
Expand Down
Loading