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/test/fuzzing.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
'remove-unused-types-exact.wast',
'coalesce-locals-exact.wast',
'remove-unused-brs-exact.wast',
'exact.wast',
]


Expand Down
5 changes: 2 additions & 3 deletions src/ir/manipulation.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,9 @@ template<typename InputType> inline Nop* nop(InputType* target) {
}

template<typename InputType>
inline RefNull* refNull(InputType* target, Type type) {
assert(type.isNullable() && type.getHeapType().isBottom());
inline RefNull* refNull(InputType* target, HeapType type) {
auto* ret = convert<InputType, RefNull>(target);
ret->finalize(type);
ret->finalize(Type(type.getBottom(), Nullable, Exact));
return ret;
}

Expand Down
1 change: 1 addition & 0 deletions src/ir/type-updating.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ Type GlobalTypeRewriter::getTempType(Type type) {
if (type.isRef()) {
auto heapType = type.getHeapType();
if (auto it = typeIndices.find(heapType); it != typeIndices.end()) {
// TODO: Handle exactness.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fuzzer does not seem to trip over this (yet), so I left it unfixed for now. Eventually I expect us to trip over this bug, at which point we will be able to reduce the reproducer to a test case and ensure we have test coverage.

return typeBuilder.getTempRefType(typeBuilder[it->second],
type.getNullability());
}
Expand Down
1 change: 1 addition & 0 deletions src/ir/type-updating.h
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ class TypeMapper : public GlobalTypeRewriter {
auto heapType = type.getHeapType();
auto iter = mapping.find(heapType);
if (iter != mapping.end()) {
// TODO: Handle exactness.
return getTempType(Type(iter->second, type.getNullability()));
}
return getTempType(type);
Expand Down
2 changes: 1 addition & 1 deletion src/literal.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ class Literal {
}
}
static Literal makeNull(HeapType type) {
return Literal(Type(type.getBottom(), Nullable));
return Literal(Type(type.getBottom(), Nullable, Exact));
}
static Literal makeFunc(Name func, HeapType type) {
return Literal(func, type);
Expand Down
5 changes: 0 additions & 5 deletions src/passes/OptimizeInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2297,11 +2297,6 @@ struct OptimizeInstructions
// we need to check exactness. We can replace the cast with a drop
// followed by a direct return of the value, though.
if (ref->type.isNull()) {
// TODO: Remove this once we type ref.null as exact.
if (needsExactCast) {
return;
}

// We can materialize the resulting null value directly.
//
// The type must be nullable for us to do that, which it normally
Expand Down
6 changes: 6 additions & 0 deletions src/tools/fuzzing.h
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,10 @@ class TranslateToFuzzReader {
// instruction for EH is supposed to exist only at the beginning of a 'catch'
// block, so it shouldn't be moved around or deleted freely.
bool canBeArbitrarilyReplaced(Expression* curr) {
// TODO: Remove this once we better support exact references.
if (curr->type.isExact()) {
return false;
}
return curr->type.isDefaultable() &&
!EHUtils::containsValidDanglingPop(curr);
}
Expand Down Expand Up @@ -521,7 +525,9 @@ class TranslateToFuzzReader {
Type getLoggableType();
bool isLoggableType(Type type);
Nullability getNullability();
Exactness getExactness();
Nullability getSubType(Nullability nullability);
Exactness getSubType(Exactness exactness);
HeapType getSubType(HeapType type);
Type getSubType(Type type);
Nullability getSuperType(Nullability nullability);
Expand Down
75 changes: 59 additions & 16 deletions src/tools/fuzzing/fuzzing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1570,20 +1570,22 @@ void TranslateToFuzzReader::recombine(Function* func) {
}

std::vector<Type> ret;
auto heapType = type.getHeapType();
auto nullability = type.getNullability();
ret.push_back(type);

if (nullability == NonNullable) {
ret = getRelevantTypes(Type(heapType, Nullable));
if (type.isNonNullable()) {
auto nullable = getRelevantTypes(type.with(Nullable));
ret.insert(ret.end(), nullable.begin(), nullable.end());
}
if (type.isExact()) {
auto inexact = getRelevantTypes(type.with(Inexact));
ret.insert(ret.end(), inexact.begin(), inexact.end());
// Do not consider exact references to supertypes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because (ref exact $super) is not a supertype of (ref exact $sub).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right 😆

return ret;
}

while (1) {
ret.push_back(Type(heapType, nullability));
auto super = heapType.getSuperType();
if (!super) {
break;
}
heapType = *super;
for (auto heapType = type.getHeapType().getSuperType(); heapType;
heapType = heapType->getSuperType()) {
ret.push_back(type.with(*heapType));
}

return ret;
Expand Down Expand Up @@ -4906,9 +4908,17 @@ static auto makeArrayBoundsCheck(Expression* ref,
Function* func,
Builder& builder,
Expression* length = nullptr) {
auto tempRef = builder.addVar(func, ref->type);
// The reference might be a RefNull, in which case its type is exact. But we
// want to avoid creating exact-typed locals until we support them more widely
// in the fuzzer, so adjust the type. TODO: remove this once exact references
// are better supported.
Type refType = ref->type;
if (refType.isExact()) {
refType = refType.with(Inexact);
}
auto tempRef = builder.addVar(func, refType);
auto tempIndex = builder.addVar(func, index->type);
auto* teeRef = builder.makeLocalTee(tempRef, ref, ref->type);
auto* teeRef = builder.makeLocalTee(tempRef, ref, refType);
auto* teeIndex = builder.makeLocalTee(tempIndex, index, index->type);
auto* getSize = builder.makeArrayLen(teeRef);

Expand All @@ -4935,7 +4945,7 @@ static auto makeArrayBoundsCheck(Expression* ref,
// An additional use of the length, if it was provided.
Expression* getLength = nullptr;
} result = {builder.makeBinary(LtUInt32, effectiveIndex, getSize),
builder.makeLocalGet(tempRef, ref->type),
builder.makeLocalGet(tempRef, refType),
builder.makeLocalGet(tempIndex, index->type),
getLength};
return result;
Expand Down Expand Up @@ -5324,13 +5334,37 @@ Nullability TranslateToFuzzReader::getNullability() {
return Nullable;
}

Exactness TranslateToFuzzReader::getExactness() {
// Without GC, the only heap types are func and extern, neither of which is
// exactly inhabitable. To avoid introducing uninhabitable types, only
// generate exact references when GC is enabled. We don't need custom
// descriptors to be enabled even though that is the feature that introduces
// exact references because the binary writer can always generalize the exact
// reference types away.
//
// if (wasm.features.hasGC() && oneIn(8)) {
// return Exact;
// }
//
// However, we cannot yet handle creating exact references in general, so for
// now we always generate inexact references when given the choice. TODO.
return Inexact;
}

Nullability TranslateToFuzzReader::getSubType(Nullability nullability) {
if (nullability == NonNullable) {
return NonNullable;
}
return getNullability();
}

Exactness TranslateToFuzzReader::getSubType(Exactness exactness) {
if (exactness == Exact) {
return Exact;
}
return getExactness();
}

HeapType TranslateToFuzzReader::getSubType(HeapType type) {
if (oneIn(3)) {
return type;
Expand Down Expand Up @@ -5422,9 +5456,18 @@ Type TranslateToFuzzReader::getSubType(Type type) {
if (!funcContext && heapType.isMaybeShared(HeapType::exn)) {
return type;
}
heapType = getSubType(heapType);
if (type.isExact()) {
// The only other possible heap type is bottom, but we don't want to
// generate too many bottom types.
if (!heapType.isBottom() && oneIn(20)) {
heapType = heapType.getBottom();
}
} else {
heapType = getSubType(heapType);
}
auto nullability = getSubType(type.getNullability());
auto subType = Type(heapType, nullability);
auto exactness = getSubType(type.getExactness());
auto subType = Type(heapType, nullability, exactness);
// We don't want to emit lots of uninhabitable types like (ref none), so
// avoid them with high probability. Specifically, if the original type was
// inhabitable then return that; avoid adding more uninhabitability.
Expand Down
10 changes: 5 additions & 5 deletions src/wasm-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -674,11 +674,11 @@ class Builder {
}
RefNull* makeRefNull(HeapType type) {
auto* ret = wasm.allocator.alloc<RefNull>();
ret->finalize(Type(type.getBottom(), Nullable));
ret->finalize(Type(type.getBottom(), Nullable, Exact));
return ret;
}
RefNull* makeRefNull(Type type) {
assert(type.isNullable() && type.isNull());
assert(type.isNullable() && type.isNull() && type.isExact());
auto* ret = wasm.allocator.alloc<RefNull>();
ret->finalize(type);
return ret;
Expand Down Expand Up @@ -1274,7 +1274,7 @@ class Builder {
return makeConst(value);
}
if (value.isNull()) {
return makeRefNull(type);
return makeRefNull(type.getHeapType());
}
if (type.isFunction()) {
return makeRefFunc(value.getFunc(), type.getHeapType());
Expand Down Expand Up @@ -1439,8 +1439,8 @@ class Builder {
return maybeWrap(makeConstantExpression(Literal::makeZeros(curr->type)));
}
if (curr->type.isNullable()) {
return maybeWrap(ExpressionManipulator::refNull(
curr, Type(curr->type.getHeapType().getBottom(), Nullable)));
return maybeWrap(
ExpressionManipulator::refNull(curr, curr->type.getHeapType()));
}
if (curr->type.isRef() &&
curr->type.getHeapType().isMaybeShared(HeapType::i31)) {
Expand Down
4 changes: 3 additions & 1 deletion src/wasm/literal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ Literal::Literal(const uint8_t init[16]) : type(Type::v128) {
}

Literal::Literal(std::shared_ptr<GCData> gcData, HeapType type)
: gcData(gcData), type(type, gcData ? NonNullable : Nullable) {
: gcData(gcData),
type(type, gcData ? NonNullable : Nullable, gcData ? Inexact : Exact) {
// TODO: Use exact types for more than just nulls.
// The type must be a proper type for GC data: either a struct, array, or
// string; or an externalized version of the same; or a null.
assert((isData() && gcData) ||
Expand Down
4 changes: 4 additions & 0 deletions src/wasm/wasm-validator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2288,6 +2288,10 @@ void FunctionValidator::visitRefNull(RefNull* curr) {
curr->type.isNullable(), curr, "ref.null types must be nullable")) {
return;
}
if (!shouldBeTrue(
curr->type.isExact(), curr, "ref.null types must be exact")) {
return;
}
shouldBeTrue(
curr->type.isNull(), curr, "ref.null must have a bottom heap type");
}
Expand Down
3 changes: 1 addition & 2 deletions src/wasm/wasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -800,8 +800,7 @@ void MemoryGrow::finalize() {

void RefNull::finalize(HeapType heapType) {
assert(heapType.isBottom());
// TODO: Make this exact.
type = Type(heapType, Nullable);
type = Type(heapType, Nullable, Exact);
}

void RefNull::finalize(Type type_) { type = type_; }
Expand Down
16 changes: 8 additions & 8 deletions test/lit/basic/reference-types.wast
Original file line number Diff line number Diff line change
Expand Up @@ -961,7 +961,7 @@
;; CHECK-BIN-NEXT: )
;; CHECK-BIN-NEXT: (drop
;; CHECK-BIN-NEXT: (block $block2 (result eqref)
;; CHECK-BIN-NEXT: (ref.cast nullref
;; CHECK-BIN-NEXT: (ref.cast (exact nullref)
;; CHECK-BIN-NEXT: (br_if $block2
;; CHECK-BIN-NEXT: (ref.null none)
;; CHECK-BIN-NEXT: (i32.const 1)
Expand All @@ -987,7 +987,7 @@
;; CHECK-BIN-NEXT: )
;; CHECK-BIN-NEXT: (drop
;; CHECK-BIN-NEXT: (block $block5 (result funcref)
;; CHECK-BIN-NEXT: (ref.cast nullfuncref
;; CHECK-BIN-NEXT: (ref.cast (exact nullfuncref)
;; CHECK-BIN-NEXT: (br_if $block5
;; CHECK-BIN-NEXT: (ref.null nofunc)
;; CHECK-BIN-NEXT: (i32.const 1)
Expand Down Expand Up @@ -1023,7 +1023,7 @@
;; CHECK-BIN-NEXT: )
;; CHECK-BIN-NEXT: (drop
;; CHECK-BIN-NEXT: (block $block9 (result anyref)
;; CHECK-BIN-NEXT: (ref.cast nullref
;; CHECK-BIN-NEXT: (ref.cast (exact nullref)
;; CHECK-BIN-NEXT: (br_if $block9
;; CHECK-BIN-NEXT: (ref.null none)
;; CHECK-BIN-NEXT: (i32.const 1)
Expand All @@ -1043,7 +1043,7 @@
;; CHECK-BIN-NEXT: )
;; CHECK-BIN-NEXT: (drop
;; CHECK-BIN-NEXT: (block $block11 (result anyref)
;; CHECK-BIN-NEXT: (ref.cast nullref
;; CHECK-BIN-NEXT: (ref.cast (exact nullref)
;; CHECK-BIN-NEXT: (br_if $block11
;; CHECK-BIN-NEXT: (ref.null none)
;; CHECK-BIN-NEXT: (i32.const 1)
Expand Down Expand Up @@ -2298,7 +2298,7 @@
;; CHECK-BIN-NODEBUG-NEXT: )
;; CHECK-BIN-NODEBUG-NEXT: (drop
;; CHECK-BIN-NODEBUG-NEXT: (block $block2 (result eqref)
;; CHECK-BIN-NODEBUG-NEXT: (ref.cast nullref
;; CHECK-BIN-NODEBUG-NEXT: (ref.cast (exact nullref)
;; CHECK-BIN-NODEBUG-NEXT: (br_if $block2
;; CHECK-BIN-NODEBUG-NEXT: (ref.null none)
;; CHECK-BIN-NODEBUG-NEXT: (i32.const 1)
Expand All @@ -2324,7 +2324,7 @@
;; CHECK-BIN-NODEBUG-NEXT: )
;; CHECK-BIN-NODEBUG-NEXT: (drop
;; CHECK-BIN-NODEBUG-NEXT: (block $block5 (result funcref)
;; CHECK-BIN-NODEBUG-NEXT: (ref.cast nullfuncref
;; CHECK-BIN-NODEBUG-NEXT: (ref.cast (exact nullfuncref)
;; CHECK-BIN-NODEBUG-NEXT: (br_if $block5
;; CHECK-BIN-NODEBUG-NEXT: (ref.null nofunc)
;; CHECK-BIN-NODEBUG-NEXT: (i32.const 1)
Expand Down Expand Up @@ -2360,7 +2360,7 @@
;; CHECK-BIN-NODEBUG-NEXT: )
;; CHECK-BIN-NODEBUG-NEXT: (drop
;; CHECK-BIN-NODEBUG-NEXT: (block $block9 (result anyref)
;; CHECK-BIN-NODEBUG-NEXT: (ref.cast nullref
;; CHECK-BIN-NODEBUG-NEXT: (ref.cast (exact nullref)
;; CHECK-BIN-NODEBUG-NEXT: (br_if $block9
;; CHECK-BIN-NODEBUG-NEXT: (ref.null none)
;; CHECK-BIN-NODEBUG-NEXT: (i32.const 1)
Expand All @@ -2380,7 +2380,7 @@
;; CHECK-BIN-NODEBUG-NEXT: )
;; CHECK-BIN-NODEBUG-NEXT: (drop
;; CHECK-BIN-NODEBUG-NEXT: (block $block11 (result anyref)
;; CHECK-BIN-NODEBUG-NEXT: (ref.cast nullref
;; CHECK-BIN-NODEBUG-NEXT: (ref.cast (exact nullref)
;; CHECK-BIN-NODEBUG-NEXT: (br_if $block11
;; CHECK-BIN-NODEBUG-NEXT: (ref.null none)
;; CHECK-BIN-NODEBUG-NEXT: (i32.const 1)
Expand Down
26 changes: 26 additions & 0 deletions test/lit/ctor-eval/materialize-null-local.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.

;; RUN: wasm-ctor-eval %s -all --ctors=test --kept-exports=test --ignore-external-input -S -o - \
;; RUN: | filecheck %s

;; Check that materializing a non-data null local, which at time of writing uses
;; Builder::makeConstantExpression, does not trigger an assertion failure.

(module
(func $test (export "test") (param $0 externref)
(local $3 anyref)
(local.set $3
(any.convert_extern
(local.get $0)
)
)
)
)
;; CHECK: (type $0 (func (param externref)))

;; CHECK: (export "test" (func $test_1))

;; CHECK: (func $test_1 (type $0) (param $0 externref)
;; CHECK-NEXT: (local $3 anyref)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
21 changes: 21 additions & 0 deletions test/lit/exec/exact.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
;; NOTE: Assertions have been generated by update_lit_checks.py --output=fuzz-exec and should not be edited.

;; RUN: wasm-opt %s -all --fuzz-exec -q -o /dev/null 2>&1 | filecheck %s

(module
;; CHECK: [fuzz-exec] calling convert-null-extern
;; CHECK-NEXT: [fuzz-exec] note result: convert-null-extern => null
(func $convert-null-extern (export "convert-null-extern") (result (exact nullref))
(local externref)
;; The value produced by this cast must be exact to avoid triggering an
;; assertion.
(ref.cast (exact nullref)
(any.convert_extern
(local.get 0)
)
)
)
)
;; CHECK: [fuzz-exec] calling convert-null-extern
;; CHECK-NEXT: [fuzz-exec] note result: convert-null-extern => null
;; CHECK-NEXT: [fuzz-exec] comparing convert-null-extern
Loading