summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xscripts/fuzz_opt.py1
-rw-r--r--src/wasm/wasm-binary.cpp9
-rw-r--r--src/wasm/wasm-validator.cpp131
-rw-r--r--test/spec/shared-array.wast46
-rw-r--r--test/spec/shared-polymorphism.wast12
-rw-r--r--test/spec/shared-struct.wast23
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)))
+)