diff --git a/src/ir/child-typer.h b/src/ir/child-typer.h index 148cb7219ba..a7789ba5f33 100644 --- a/src/ir/child-typer.h +++ b/src/ir/child-typer.h @@ -1036,9 +1036,18 @@ template struct ChildTyper : OverriddenVisitor { 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)); } diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 6e046152252..2be6b0e0057 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -2336,7 +2336,7 @@ class ExpressionRunner : public OverriddenVisitor { 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 diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 3193e6539e4..6f8e9a6cdec 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -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, diff --git a/test/lit/waitqueue.wast b/test/lit/waitqueue.wast index 9bc1b40220d..c66a5be4261 100644 --- a/test/lit/waitqueue.wast +++ b/test/lit/waitqueue.wast @@ -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: )) @@ -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 @@ -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 @@ -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 diff --git a/test/spec/waitqueue.wast b/test/spec/waitqueue.wast index 6317a39d970..b28183ec032 100644 --- a/test/spec/waitqueue.wast +++ b/test/spec/waitqueue.wast @@ -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 @@ -76,12 +76,18 @@ (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))) ) @@ -89,6 +95,14 @@ (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)) ) @@ -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.