diff options
-rwxr-xr-x | scripts/fuzz_opt.py | 1 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 9 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 131 | ||||
-rw-r--r-- | test/spec/shared-array.wast | 46 | ||||
-rw-r--r-- | test/spec/shared-polymorphism.wast | 12 | ||||
-rw-r--r-- | test/spec/shared-struct.wast | 23 |
6 files changed, 150 insertions, 72 deletions
diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index 0804c0387..192c0d362 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -353,6 +353,7 @@ INITIAL_CONTENTS_IGNORE = [ # Shared types implementation in progress 'type-merging-shared.wast', 'shared-types.wast', + 'shared-polymorphism.wast', 'shared-struct.wast', ] diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 1644c2950..c4aaec5e9 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -2272,11 +2272,16 @@ void WasmBinaryReader::readTypes() { TypeBuilder builder(getU32LEB()); BYN_TRACE("num: " << builder.size() << std::endl); - auto readHeapType = [&]() { + auto readHeapType = [&]() -> HeapType { int64_t htCode = getS64LEB(); // TODO: Actually s33 + auto share = Unshared; + if (htCode == BinaryConsts::EncodedType::Shared) { + share = Shared; + htCode = getS64LEB(); // TODO: Actually s33 + } HeapType ht; if (getBasicHeapType(htCode, ht)) { - return ht; + return ht.getBasic(share); } if (size_t(htCode) >= builder.size()) { throwError("invalid type index: " + std::to_string(htCode)); diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 13993c825..281f3ec45 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -209,6 +209,18 @@ struct ValidationInfo { fail(text, curr, func); return false; } + + bool shouldBeSubTypeIgnoringShared(Type left, + Type right, + Expression* curr, + const char* text, + Function* func = nullptr) { + assert(right.isRef() && right.getHeapType().isBasic()); + auto share = left.isRef() ? left.getHeapType().getShared() : Unshared; + auto ht = right.getHeapType(); + auto matchedRight = Type(ht.getBasic(share), right.getNullability()); + return shouldBeSubType(left, matchedRight, curr, text, func); + } }; struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> { @@ -526,6 +538,13 @@ private: return info.shouldBeSubType(left, right, curr, text, getFunction()); } + bool shouldBeSubTypeIgnoringShared(Type left, + Type right, + Expression* curr, + const char* text) { + return info.shouldBeSubTypeIgnoringShared(left, right, curr, text); + } + void validateOffset(Address offset, Memory* mem, Expression* curr); void validateAlignment( size_t align, Type type, Index bytes, bool isAtomic, Expression* curr); @@ -2256,14 +2275,16 @@ void FunctionValidator::visitRefEq(RefEq* curr) { Type eqref = Type(HeapType::eq, Nullable); shouldBeTrue( getModule()->features.hasGC(), curr, "ref.eq requires gc [--enable-gc]"); - shouldBeSubType(curr->left->type, - eqref, - curr->left, - "ref.eq's left argument should be a subtype of eqref"); - shouldBeSubType(curr->right->type, - eqref, - curr->right, - "ref.eq's right argument should be a subtype of eqref"); + shouldBeSubTypeIgnoringShared( + curr->left->type, + eqref, + curr->left, + "ref.eq's left argument should be a subtype of eqref"); + shouldBeSubTypeIgnoringShared( + curr->right->type, + eqref, + curr->right, + "ref.eq's right argument should be a subtype of eqref"); } void FunctionValidator::visitTableGet(TableGet* curr) { @@ -2689,10 +2710,10 @@ void FunctionValidator::visitI31Get(I31Get* curr) { shouldBeTrue(getModule()->features.hasGC(), curr, "i31.get_s/u requires gc [--enable-gc]"); - shouldBeSubType(curr->i31->type, - Type(HeapType::i31, Nullable), - curr->i31, - "i31.get_s/u's argument should be i31ref"); + shouldBeSubTypeIgnoringShared(curr->i31->type, + Type(HeapType::i31, Nullable), + curr->i31, + "i31.get_s/u's argument should be i31ref"); } void FunctionValidator::visitRefTest(RefTest* curr) { @@ -3008,24 +3029,15 @@ void FunctionValidator::visitArrayGet(ArrayGet* curr) { getModule()->features.hasGC(), curr, "array.get requires gc [--enable-gc]"); shouldBeEqualOrFirstIsUnreachable( curr->index->type, Type(Type::i32), curr, "array.get index must be an i32"); - if (curr->type == Type::unreachable) { - return; - } - if (!shouldBeSubType(curr->ref->type, - Type(HeapType::array, Nullable), - curr, - "array.get target should be an array reference")) { + const char* mustBeArray = + "array.get target should be a specific array reference"; + if (curr->type == Type::unreachable || + !shouldBeTrue(curr->ref->type.isRef(), curr, mustBeArray) || + curr->ref->type.getHeapType().isBottom() || + !shouldBeTrue(curr->ref->type.isArray(), curr, mustBeArray)) { return; } auto heapType = curr->ref->type.getHeapType(); - if (heapType == HeapType::none) { - return; - } - if (!shouldBeTrue(heapType != HeapType::array, - curr, - "array.get target should be a specific array reference")) { - return; - } const auto& element = heapType.getArray().element; // If the type is not packed, it must be marked internally as unsigned, by // convention. @@ -3044,19 +3056,11 @@ void FunctionValidator::visitArraySet(ArraySet* curr) { if (curr->type == Type::unreachable) { return; } - if (!shouldBeSubType(curr->ref->type, - Type(HeapType::array, Nullable), - curr, - "array.set target should be an array reference")) { - return; - } - auto heapType = curr->ref->type.getHeapType(); - if (heapType == HeapType::none) { - return; - } - if (!shouldBeTrue(heapType != HeapType::array, - curr, - "array.set target should be a specific array reference")) { + const char* mustBeArray = "array.set target should be an array reference"; + if (curr->type == Type::unreachable || + !shouldBeTrue(curr->ref->type.isRef(), curr, mustBeArray) || + curr->ref->type.getHeapType().isBottom() || + !shouldBeTrue(curr->ref->type.isArray(), curr, mustBeArray)) { return; } const auto& element = curr->ref->type.getHeapType().getArray().element; @@ -3072,10 +3076,11 @@ void FunctionValidator::visitArrayLen(ArrayLen* curr) { getModule()->features.hasGC(), curr, "array.len requires gc [--enable-gc]"); shouldBeEqualOrFirstIsUnreachable( curr->type, Type(Type::i32), curr, "array.len result must be an i32"); - shouldBeSubType(curr->ref->type, - Type(HeapType::array, Nullable), - curr, - "array.len argument must be an array reference"); + shouldBeSubTypeIgnoringShared( + curr->ref->type, + Type(HeapType::array, Nullable), + curr, + "array.len argument must be an array reference"); } void FunctionValidator::visitArrayCopy(ArrayCopy* curr) { @@ -3145,22 +3150,15 @@ void FunctionValidator::visitArrayFill(ArrayFill* curr) { "array.fill index must be an i32"); shouldBeEqualOrFirstIsUnreachable( curr->size->type, Type(Type::i32), curr, "array.fill size must be an i32"); - if (curr->type == Type::unreachable) { - return; - } - if (!shouldBeSubType(curr->ref->type, - Type(HeapType::array, Nullable), - curr, - "array.fill destination should be an array reference")) { + const char* mustBeArray = + "array.fill destination should be an array reference"; + if (curr->type == Type::unreachable || + !shouldBeTrue(curr->ref->type.isRef(), curr, mustBeArray) || + curr->ref->type.getHeapType().isBottom() || + !shouldBeTrue(curr->ref->type.isArray(), curr, mustBeArray)) { return; } auto heapType = curr->ref->type.getHeapType(); - if (heapType == HeapType::none || - !shouldBeTrue(heapType.isArray(), - curr, - "array.fill destination should be an array reference")) { - return; - } auto element = heapType.getArray().element; shouldBeSubType(curr->value->type, element.type, @@ -3187,22 +3185,15 @@ void FunctionValidator::visitArrayInit(ArrayInit* curr) { Type(Type::i32), curr, "array.init_* size must be an i32"); - if (curr->type == Type::unreachable) { - return; - } - if (!shouldBeSubType(curr->ref->type, - Type(HeapType::array, Nullable), - curr, - "array.init_* destination must be an array reference")) { + const char* mustBeArray = + "array.init_* destination must be an array reference"; + if (curr->type == Type::unreachable || + !shouldBeTrue(curr->ref->type.isRef(), curr, mustBeArray) || + curr->ref->type.getHeapType().isBottom() || + !shouldBeTrue(curr->ref->type.isArray(), curr, mustBeArray)) { return; } auto heapType = curr->ref->type.getHeapType(); - if (heapType == HeapType::none || - !shouldBeTrue(heapType.isArray(), - curr, - "array.init_* destination must be an array reference")) { - return; - } auto element = heapType.getArray().element; shouldBeTrue( element.mutable_, curr, "array.init_* destination must be mutable"); diff --git a/test/spec/shared-array.wast b/test/spec/shared-array.wast index a687c1d36..7aa387731 100644 --- a/test/spec/shared-array.wast +++ b/test/spec/shared-array.wast @@ -67,3 +67,49 @@ (module (type (array (ref null (shared any)))) ) + +;; Array instructions work on shared arrays. +(module + (type $i8 (shared (array (mut i8)))) + (type $i32 (shared (array (mut i32)))) + (type $unshared (array (mut i8))) + + (data) + (elem) + + (func (array.new $i8 (i32.const 0) (i32.const 0)) (drop)) + + (func (array.new_default $i8 (i32.const 0)) (drop)) + + (func (array.new_fixed $i8 0) (drop)) + + (func (param (ref null $i8)) + (array.get_s $i8 (local.get 0) (i32.const 0)) (drop)) + + (func (param (ref null $i8)) + (array.get_u $i8 (local.get 0) (i32.const 0)) (drop)) + + (func (param (ref null $i32)) + (array.get $i32 (local.get 0) (i32.const 0)) (drop)) + + (func (param (ref null $i8)) + (array.set $i8 (local.get 0) (i32.const 0) (i32.const 0))) + + (func (param (ref null $i8) (ref null $i8)) + (array.copy $i8 $i8 (local.get 0) (i32.const 0) (local.get 1) (i32.const 0) (i32.const 0))) + + (func (param (ref null $i8) (ref null $unshared)) + (array.copy $i8 $unshared (local.get 0) (i32.const 0) (local.get 1) (i32.const 0) (i32.const 0))) + + (func (param (ref null $unshared) (ref null $i8)) + (array.copy $unshared $i8 (local.get 0) (i32.const 0) (local.get 1) (i32.const 0) (i32.const 0))) + + (func (param (ref null $i8)) + (array.fill $i8 (local.get 0) (i32.const 0) (i32.const 0) (i32.const 0))) + + (func (param (ref null $i8)) + (array.init_data $i8 0 (local.get 0) (i32.const 0) (i32.const 0) (i32.const 0))) + + (func (param (ref null $i8)) + (array.init_data $i8 0 (local.get 0) (i32.const 0) (i32.const 0) (i32.const 0))) +) diff --git a/test/spec/shared-polymorphism.wast b/test/spec/shared-polymorphism.wast new file mode 100644 index 000000000..547829f11 --- /dev/null +++ b/test/spec/shared-polymorphism.wast @@ -0,0 +1,12 @@ +;; Some instructions are shared-polymorphic and work with shared or unshared +;; references. +(module + (func (drop (ref.eq (ref.null (shared none)) (ref.null (shared none))))) + (func (drop (ref.eq (ref.null (shared none)) (ref.null none)))) + (func (drop (ref.eq (ref.null none) (ref.null (shared none))))) + + (func (param (ref null (shared i31))) (drop (i31.get_s (local.get 0)))) + (func (param (ref null (shared i31))) (drop (i31.get_u (local.get 0)))) + + (func (param (ref null (shared array))) (drop (array.len (local.get 0)))) +) diff --git a/test/spec/shared-struct.wast b/test/spec/shared-struct.wast index b2c82caff..28139bb28 100644 --- a/test/spec/shared-struct.wast +++ b/test/spec/shared-struct.wast @@ -67,3 +67,26 @@ (module (type (struct (ref null (shared any)))) ) + +;; Struct instructions work on shared structs. +(module + (type $i8 (shared (struct (mut i8)))) + (type $i32 (shared (struct (mut i32)))) + (type $unshared (struct (mut i8))) + + (func (struct.new $i8 (i32.const 0)) (drop)) + + (func (struct.new_default $i8) (drop)) + + (func (param (ref null $i8)) + (struct.get_s $i8 0 (local.get 0)) (drop)) + + (func (param (ref null $i8)) + (struct.get_u $i8 0 (local.get 0)) (drop)) + + (func (param (ref null $i32)) + (struct.get $i32 0 (local.get 0)) (drop)) + + (func (param (ref null $i8)) + (struct.set $i8 0 (local.get 0) (i32.const 0))) +) |