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
26 changes: 9 additions & 17 deletions scripts/fuzz_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,11 @@ def randomize_feature_opts():
# 2/3 of the remaining 90% use them all. This is useful to maximize
# coverage, as enabling more features enables more optimizations and
# code paths, and also allows all initial contents to run.
pass

# The shared-everything feature is new and we want to fuzz it, but it
# also currently disables fuzzing V8, so disable it most of the time.
if random.random() < 0.9:
FEATURE_OPTS.append('--disable-shared-everything')

print('randomized feature opts:', '\n ' + '\n '.join(FEATURE_OPTS))

Expand Down Expand Up @@ -350,21 +354,6 @@ def is_git_repo():
'exception-handling.wast',
'translate-to-new-eh.wast',
'rse-eh.wast',
# Shared types implementation in progress
'type-merging-shared.wast',
'shared-types.wast',
'shared-polymorphism.wast',
'shared-struct.wast',
'shared-array.wast',
'shared-i31.wast',
'shared-null.wast',
'shared-absheaptype.wast',
'type-ssa-shared.wast',
'shared-ref_eq.wast',
'shared-types-no-gc.wast',
'shared-ref-i31.wast',
'table-type.wast',
'elem-type.wast',
]


Expand Down Expand Up @@ -847,7 +836,10 @@ def run(self, wasm, extra_d8_flags=[]):
return run_vm([shared.V8, FUZZ_SHELL_JS] + shared.V8_OPTS + extra_d8_flags + ['--', wasm])

def can_run(self, wasm):
return True
# V8 does not support shared memories when running with
# shared-everything enabled, so do not fuzz shared-everything
# for now.
return all_disallowed(['shared-everything'])

def can_compare_to_self(self):
# With nans, VM differences can confuse us, so only very simple VMs
Expand Down
141 changes: 88 additions & 53 deletions src/tools/fuzzing/fuzzing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,11 +246,8 @@ void TranslateToFuzzReader::setupHeapTypes() {

// For GC, also generate random types.
if (wasm.features.hasGC()) {
// Do not generate shared types until the fuzzer can be updated to handle
// them.
auto features = wasm.features - FeatureSet::SharedEverything;
auto generator =
HeapTypeGenerator::create(random, features, upTo(MAX_NEW_GC_TYPES));
HeapTypeGenerator::create(random, wasm.features, upTo(MAX_NEW_GC_TYPES));
auto result = generator.builder.build();
if (auto* err = result.getError()) {
Fatal() << "Failed to build heap types: " << err->reason << " at index "
Expand Down Expand Up @@ -288,10 +285,16 @@ void TranslateToFuzzReader::setupHeapTypes() {
}
// Basic types must be handled directly, since subTypes doesn't look at
// those.
auto share = type.getShared();
auto struct_ = HeapTypes::struct_.getBasic(share);
auto array = HeapTypes::array.getBasic(share);
auto eq = HeapTypes::eq.getBasic(share);
auto any = HeapTypes::any.getBasic(share);
auto func = HeapTypes::func.getBasic(share);
if (type.isStruct()) {
interestingHeapSubTypes[HeapType::struct_].push_back(type);
interestingHeapSubTypes[HeapType::eq].push_back(type);
interestingHeapSubTypes[HeapType::any].push_back(type);
interestingHeapSubTypes[struct_].push_back(type);
interestingHeapSubTypes[eq].push_back(type);
interestingHeapSubTypes[any].push_back(type);

// Note the mutable fields.
auto& fields = type.getStruct().fields;
Expand All @@ -301,15 +304,15 @@ void TranslateToFuzzReader::setupHeapTypes() {
}
}
} else if (type.isArray()) {
interestingHeapSubTypes[HeapType::array].push_back(type);
interestingHeapSubTypes[HeapType::eq].push_back(type);
interestingHeapSubTypes[HeapType::any].push_back(type);
interestingHeapSubTypes[array].push_back(type);
interestingHeapSubTypes[eq].push_back(type);
interestingHeapSubTypes[any].push_back(type);

if (type.getArray().element.mutable_) {
mutableArrays.push_back(type);
}
} else if (type.isSignature()) {
interestingHeapSubTypes[HeapType::func].push_back(type);
interestingHeapSubTypes[func].push_back(type);
}
}

Expand Down Expand Up @@ -2468,11 +2471,13 @@ Literal TranslateToFuzzReader::makeLiteral(Type type) {

Expression* TranslateToFuzzReader::makeRefFuncConst(Type type) {
auto heapType = type.getHeapType();
auto share = heapType.getShared();
if (heapType.isBasic()) {
assert(heapType.getBasic(Unshared) == HeapType::func);
// With high probability, use the last created function if possible.
// Otherwise, continue on to select some other function.
if (funcContext && !oneIn(4)) {
if (funcContext && funcContext->func->type.getShared() == share &&
!oneIn(4)) {
auto* target = funcContext->func;
return builder.makeRefFunc(target->name, target->type);
}
Expand All @@ -2496,7 +2501,7 @@ Expression* TranslateToFuzzReader::makeRefFuncConst(Type type) {
// here).
if ((type.isNullable() && oneIn(2)) ||
(type.isNonNullable() && oneIn(16) && funcContext)) {
Expression* ret = builder.makeRefNull(HeapType::nofunc);
Expression* ret = builder.makeRefNull(HeapTypes::nofunc.getBasic(share));
if (!type.isNullable()) {
assert(funcContext);
ret = builder.makeRefAs(RefAsNonNull, ret);
Expand All @@ -2511,7 +2516,10 @@ Expression* TranslateToFuzzReader::makeRefFuncConst(Type type) {
if (heapType.isBasic()) {
// We need a specific signature type to create a function. Pick an arbitrary
// signature if we only had generic 'func' here.
heapType = Signature(Type::none, Type::none);
TypeBuilder builder(1);
builder[0] = Signature(Type::none, Type::none);
builder[0].setShared(share);
heapType = (*builder.build())[0];
}
auto* body = heapType.getSignature().results == Type::none
? (Expression*)builder.makeNop()
Expand Down Expand Up @@ -2553,10 +2561,10 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
auto heapType = type.getHeapType();
assert(heapType.isBasic());
assert(wasm.features.hasReferenceTypes());
assert(!heapType.isShared() && "TODO: handle shared types");
auto share = heapType.getShared();
switch (heapType.getBasic(Unshared)) {
case HeapType::ext: {
auto null = builder.makeRefNull(HeapType::ext);
auto null = builder.makeRefNull(HeapTypes::ext.getBasic(share));
// TODO: support actual non-nullable externrefs via imported globals or
// similar.
if (!type.isNullable()) {
Expand All @@ -2575,12 +2583,16 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
// Choose a subtype we can materialize a constant for. We cannot
// materialize non-nullable refs to func or i31 in global contexts.
Nullability nullability = getSubType(type.getNullability());
auto subtype = pick(FeatureOptions<HeapType>()
.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
HeapType::i31,
HeapType::struct_,
HeapType::array)
.add(FeatureSet::Strings, HeapType::string));
auto subtypeOpts = FeatureOptions<HeapType>().add(
FeatureSet::ReferenceTypes | FeatureSet::GC,
HeapType::i31,
HeapType::struct_,
HeapType::array);
if (share == Unshared) {
// Shared strings not yet supported.
subtypeOpts.add(FeatureSet::Strings, HeapType::string);
}
auto subtype = pick(subtypeOpts).getBasic(share);
return makeConst(Type(subtype, nullability));
}
case HeapType::eq: {
Expand All @@ -2589,7 +2601,7 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
// a subtype of anyref, but we cannot create constants of it, except
// for null.
assert(type.isNullable());
return builder.makeRefNull(HeapType::none);
return builder.makeRefNull(HeapTypes::none.getBasic(share));
}
auto nullability = getSubType(type.getNullability());
// ref.i31 is not allowed in initializer expressions.
Expand All @@ -2605,14 +2617,14 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
subtype = HeapType::array;
break;
}
return makeConst(Type(subtype, nullability));
return makeConst(Type(subtype.getBasic(share), nullability));
}
case HeapType::i31: {
assert(wasm.features.hasGC());
if (type.isNullable() && oneIn(4)) {
return builder.makeRefNull(HeapType::none);
return builder.makeRefNull(HeapTypes::none.getBasic(share));
}
return builder.makeRefI31(makeConst(Type::i32));
return builder.makeRefI31(makeConst(Type::i32), share);
}
case HeapType::struct_: {
assert(wasm.features.hasGC());
Expand All @@ -2621,15 +2633,29 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
// Use a local static to avoid the expense of canonicalizing a new type
// every time.
static HeapType trivialStruct = HeapType(Struct());
return builder.makeStructNew(trivialStruct, std::vector<Expression*>{});
static HeapType sharedTrivialStruct = []() {
TypeBuilder builder(1);
builder[0] = Struct{};
builder[0].setShared();
return (*builder.build())[0];
}();
auto ht = share == Shared ? sharedTrivialStruct : trivialStruct;
Comment on lines +2636 to +2642
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
static HeapType sharedTrivialStruct = []() {
TypeBuilder builder(1);
builder[0] = Struct{};
builder[0].setShared();
return (*builder.build())[0];
}();
auto ht = share == Shared ? sharedTrivialStruct : trivialStruct;
static HeapType makeSharedTrivialStruct = []() {
TypeBuilder builder(1);
builder[0] = Struct{};
builder[0].setShared();
return (*builder.build())[0];
}();
auto ht = share == Shared ? makeSharedTrivialStruct() : trivialStruct;

Copy link
Member

Choose a reason for hiding this comment

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

(also below, if this makes sense)

Copy link
Member Author

Choose a reason for hiding this comment

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

Note the () after the lambda expression making this an IIFE meant to be executed once when the static local is initialized.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks, I missed the static.

return builder.makeStructNew(ht, std::vector<Expression*>{});
}
case HeapType::array: {
static HeapType trivialArray =
HeapType(Array(Field(Field::PackedType::i8, Immutable)));
return builder.makeArrayNewFixed(trivialArray, {});
static HeapType sharedTrivialArray = []() {
TypeBuilder builder(1);
builder[0] = Array(Field(Field::PackedType::i8, Immutable));
builder[0].setShared();
return (*builder.build())[0];
}();
auto ht = share == Shared ? sharedTrivialArray : trivialArray;
return builder.makeArrayNewFixed(ht, {});
}
case HeapType::exn: {
auto null = builder.makeRefNull(HeapType::exn);
auto null = builder.makeRefNull(HeapTypes::exn.getBasic(share));
if (!type.isNullable()) {
assert(funcContext);
return builder.makeRefAs(RefAsNonNull, null);
Expand All @@ -2638,6 +2664,7 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
}
case HeapType::string: {
// In non-function contexts all we can do is string.const.
assert(share == Unshared && "shared strings not supported");
if (!funcContext) {
return makeStringConst();
}
Expand Down Expand Up @@ -2671,7 +2698,7 @@ Expression* TranslateToFuzzReader::makeBasicRef(Type type) {
case HeapType::nofunc:
case HeapType::nocont:
case HeapType::noexn: {
auto null = builder.makeRefNull(heapType);
auto null = builder.makeRefNull(heapType.getBasic(share));
if (!type.isNullable()) {
assert(funcContext);
return builder.makeRefAs(RefAsNonNull, null);
Expand Down Expand Up @@ -4231,48 +4258,56 @@ HeapType TranslateToFuzzReader::getSubType(HeapType type) {
return type;
}
if (type.isBasic() && oneIn(2)) {
assert(!type.isShared() && "TODO: handle shared types");
auto share = type.getShared();
switch (type.getBasic(Unshared)) {
case HeapType::func:
// TODO: Typed function references.
return pick(FeatureOptions<HeapType>()
.add(FeatureSet::ReferenceTypes, HeapType::func)
.add(FeatureSet::GC, HeapType::nofunc));
.add(FeatureSet::GC, HeapType::nofunc))
.getBasic(share);
case HeapType::cont:
return pick(HeapType::cont, HeapType::nocont);
return pick(HeapTypes::cont, HeapTypes::nocont).getBasic(share);
case HeapType::ext:
return pick(FeatureOptions<HeapType>()
.add(FeatureSet::ReferenceTypes, HeapType::ext)
.add(FeatureSet::GC, HeapType::noext));
case HeapType::any:
.add(FeatureSet::GC, HeapType::noext))
.getBasic(share);
case HeapType::any: {
assert(wasm.features.hasReferenceTypes());
assert(wasm.features.hasGC());
return pick(FeatureOptions<HeapType>()
.add(FeatureSet::GC,
HeapType::any,
HeapType::eq,
HeapType::i31,
HeapType::struct_,
HeapType::array,
HeapType::none)
.add(FeatureSet::Strings, HeapType::string));
auto options = FeatureOptions<HeapType>().add(FeatureSet::GC,
HeapType::any,
HeapType::eq,
HeapType::i31,
HeapType::struct_,
HeapType::array,
HeapType::none);
if (share == Unshared) {
// Shared strings not yet supported.
options.add(FeatureSet::Strings, HeapType::string);
}
return pick(options).getBasic(share);
}
case HeapType::eq:
assert(wasm.features.hasReferenceTypes());
assert(wasm.features.hasGC());
return pick(HeapType::eq,
HeapType::i31,
HeapType::struct_,
HeapType::array,
HeapType::none);
return pick(HeapTypes::eq,
HeapTypes::i31,
HeapTypes::struct_,
HeapTypes::array,
HeapTypes::none)
.getBasic(share);
case HeapType::i31:
return pick(HeapType::i31, HeapType::none);
return pick(HeapTypes::i31, HeapTypes::none).getBasic(share);
case HeapType::struct_:
return pick(HeapType::struct_, HeapType::none);
return pick(HeapTypes::struct_, HeapTypes::none).getBasic(share);
case HeapType::array:
return pick(HeapType::array, HeapType::none);
return pick(HeapTypes::array, HeapTypes::none).getBasic(share);
case HeapType::exn:
return HeapType::exn;
return HeapTypes::exn.getBasic(share);
case HeapType::string:
assert(share == Unshared);
return HeapType::string;
case HeapType::none:
case HeapType::noext:
Expand Down
3 changes: 2 additions & 1 deletion src/tools/fuzzing/heap-types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,8 @@ void Inhabitator::markExternRefsNullable() {
auto children = type.getTypeChildren();
for (size_t i = 0; i < children.size(); ++i) {
auto child = children[i];
if (child.isRef() && child.getHeapType() == HeapType::ext &&
if (child.isRef() && child.getHeapType().isBasic() &&
child.getHeapType().getBasic(Unshared) == HeapType::ext &&
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps we should have a helper for this common pattern of x.isBasic() && x.getHeapType.getBasic(Unshared) == Y, something like x.isEqualToUnsharedBasic(Y)?

Copy link
Member Author

Choose a reason for hiding this comment

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

How about doing this as a follow-up? It could apply more broadly than just in these files.

Copy link
Member

Choose a reason for hiding this comment

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

sgtm

child.isNonNullable()) {
markNullable({type, i});
}
Expand Down
Loading