Skip to content
Merged
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
1 change: 1 addition & 0 deletions scripts/fuzz_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ def is_git_repo():
# Shared types implementation in progress
'type-merging-shared.wast',
'shared-types.wast',
'shared-polymorphism.wast',
'shared-struct.wast',
]

Expand Down
9 changes: 7 additions & 2 deletions src/wasm/wasm-binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
131 changes: 61 additions & 70 deletions src/wasm/wasm-validator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>> {
Expand Down Expand Up @@ -526,6 +538,13 @@ struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> {
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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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.
Expand All @@ -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;
Expand All @@ -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) {
Expand Down Expand Up @@ -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,
Expand All @@ -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");
Expand Down
46 changes: 46 additions & 0 deletions test/spec/shared-array.wast
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
)
12 changes: 12 additions & 0 deletions test/spec/shared-polymorphism.wast
Original file line number Diff line number Diff line change
@@ -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))))
)
23 changes: 23 additions & 0 deletions test/spec/shared-struct.wast
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
)