summaryrefslogtreecommitdiff
path: root/src/tools/fuzzing.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/fuzzing.h')
-rw-r--r--src/tools/fuzzing.h278
1 files changed, 170 insertions, 108 deletions
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index 24e1d6e8e..c1a81a896 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -228,6 +228,12 @@ private:
// The maximum amount of vars in each function.
static const int MAX_VARS = 20;
+ // The maximum number of globals in a module.
+ static const int MAX_GLOBALS = 20;
+
+ // The maximum number of tuple elements.
+ static const int MAX_TUPLE_SIZE = 6;
+
// some things require luck, try them a few times
static const int TRIES = 10;
@@ -309,22 +315,29 @@ private:
double getDouble() { return Literal(get64()).reinterpretf64(); }
- SmallVector<Type, 2> getSubTypes(Type type) {
- SmallVector<Type, 2> ret;
- ret.push_back(type); // includes itself
+ Type getSubType(Type type) {
+ if (type.isMulti()) {
+ std::vector<Type> types;
+ for (auto t : type.expand()) {
+ types.push_back(getSubType(t));
+ }
+ return Type(types);
+ }
+ SmallVector<Type, 2> options;
+ options.push_back(type); // includes itself
switch (type.getSingle()) {
case Type::anyref:
- ret.push_back(Type::funcref);
- ret.push_back(Type::exnref);
+ options.push_back(Type::funcref);
+ options.push_back(Type::exnref);
// falls through
case Type::funcref:
case Type::exnref:
- ret.push_back(Type::nullref);
+ options.push_back(Type::nullref);
break;
default:
break;
}
- return ret;
+ return pick(options);
}
void setupMemory() {
@@ -406,34 +419,25 @@ private:
std::map<Type, std::vector<Name>> globalsByType;
void setupGlobals() {
- size_t index = 0;
- for (auto type : getConcreteTypes()) {
- auto num = upTo(3);
- for (size_t i = 0; i < num; i++) {
- auto* glob =
- builder.makeGlobal(std::string("global$") + std::to_string(index++),
- type,
- makeConst(type),
- Builder::Mutable);
- wasm.addGlobal(glob);
- globalsByType[type].push_back(glob->name);
- }
+ for (size_t index = upTo(MAX_GLOBALS); index > 0; --index) {
+ auto type = getConcreteType();
+ auto* global =
+ builder.makeGlobal(std::string("global$") + std::to_string(index),
+ type,
+ makeConst(type),
+ Builder::Mutable);
+ wasm.addGlobal(global);
+ globalsByType[type].push_back(global->name);
}
}
void setupEvents() {
Index num = upTo(3);
for (size_t i = 0; i < num; i++) {
- // Events should have void return type and at least one param type
- std::vector<Type> params;
- Index numValues =
- wasm.features.hasMultivalue() ? upToSquared(MAX_PARAMS) : upTo(2);
- for (Index i = 0; i < numValues; i++) {
- params.push_back(getConcreteType());
- }
- auto* event = builder.makeEvent(std::string("event$") + std::to_string(i),
- WASM_EVENT_ATTRIBUTE_EXCEPTION,
- Signature(Type(params), Type::none));
+ auto* event =
+ builder.makeEvent(std::string("event$") + std::to_string(i),
+ WASM_EVENT_ATTRIBUTE_EXCEPTION,
+ Signature(getControlFlowType(), Type::none));
wasm.addEvent(event);
}
}
@@ -544,11 +548,11 @@ private:
std::vector<Type> params;
params.reserve(numParams);
for (Index i = 0; i < numParams; i++) {
- auto type = getConcreteType();
+ auto type = getSingleConcreteType();
typeLocals[type].push_back(params.size());
params.push_back(type);
}
- func->sig = Signature(Type(params), getReachableType());
+ func->sig = Signature(Type(params), getControlFlowType());
Index numVars = upToSquared(MAX_VARS);
for (Index i = 0; i < numVars; i++) {
auto type = getConcreteType();
@@ -859,32 +863,22 @@ private:
}
nesting++;
Expression* ret = nullptr;
- switch (type.getSingle()) {
- case Type::i32:
- case Type::i64:
- case Type::f32:
- case Type::f64:
- case Type::v128:
- case Type::funcref:
- case Type::anyref:
- case Type::nullref:
- case Type::exnref:
- ret = _makeConcrete(type);
- break;
- case Type::none:
- ret = _makenone();
- break;
- case Type::unreachable:
- ret = _makeunreachable();
- break;
+ if (type.isConcrete()) {
+ ret = _makeConcrete(type);
+ } else if (type == Type::none) {
+ ret = _makenone();
+ } else {
+ assert(type == Type::unreachable);
+ ret = _makeunreachable();
}
// we should create the right type of thing
assert(Type::isSubType(ret->type, type));
nesting--;
return ret;
}
-
Expression* _makeConcrete(Type type) {
+ bool canMakeControlFlow =
+ !type.isMulti() || wasm.features.has(FeatureSet::Multivalue);
auto choice = upTo(100);
if (choice < 10) {
return makeConst(type);
@@ -895,42 +889,55 @@ private:
if (choice < 50) {
return makeLocalGet(type);
}
- if (choice < 60) {
+ if (choice < 60 && canMakeControlFlow) {
return makeBlock(type);
}
- if (choice < 70) {
+ if (choice < 70 && canMakeControlFlow) {
return makeIf(type);
}
- if (choice < 80) {
+ if (choice < 80 && canMakeControlFlow) {
return makeLoop(type);
}
- if (choice < 90) {
+ if (choice < 90 && canMakeControlFlow) {
return makeBreak(type);
}
using Self = TranslateToFuzzReader;
- auto options = FeatureOptions<Expression* (Self::*)(Type)>()
- .add(FeatureSet::MVP,
- &Self::makeBlock,
- &Self::makeIf,
- &Self::makeLoop,
- &Self::makeBreak,
- &Self::makeCall,
- &Self::makeCallIndirect,
- &Self::makeLocalGet,
- &Self::makeLocalSet,
- &Self::makeLoad,
- &Self::makeConst,
- &Self::makeUnary,
- &Self::makeBinary,
- &Self::makeSelect,
- &Self::makeGlobalGet)
- .add(FeatureSet::SIMD, &Self::makeSIMD);
- if (type == Type::i32 || type == Type::i64) {
+ FeatureOptions<Expression* (Self::*)(Type)> options;
+ options.add(FeatureSet::MVP,
+ &Self::makeLocalGet,
+ &Self::makeLocalSet,
+ &Self::makeGlobalGet,
+ &Self::makeConst);
+ if (canMakeControlFlow) {
+ options.add(FeatureSet::MVP,
+ &Self::makeBlock,
+ &Self::makeIf,
+ &Self::makeLoop,
+ &Self::makeBreak,
+ &Self::makeCall,
+ &Self::makeCallIndirect);
+ }
+ if (type.isSingle()) {
+ options
+ .add(FeatureSet::MVP,
+ &Self::makeUnary,
+ &Self::makeBinary,
+ &Self::makeSelect)
+ .add(FeatureSet::Multivalue, &Self::makeTupleExtract);
+ }
+ if (type.isSingle() && !type.isRef()) {
+ options.add(FeatureSet::MVP, &Self::makeLoad);
+ options.add(FeatureSet::SIMD, &Self::makeSIMD);
+ }
+ if (type.isInteger()) {
options.add(FeatureSet::Atomics, &Self::makeAtomic);
}
if (type == Type::i32) {
options.add(FeatureSet::ReferenceTypes, &Self::makeRefIsNull);
}
+ if (type.isMulti()) {
+ options.add(FeatureSet::Multivalue, &Self::makeTupleMake);
+ }
return (this->*pick(options))(type);
}
@@ -1314,22 +1321,60 @@ private:
}
Expression* makeGlobalGet(Type type) {
- auto& globals = globalsByType[type];
- if (globals.empty()) {
+ auto it = globalsByType.find(type);
+ if (it == globalsByType.end() || it->second.empty()) {
return makeConst(type);
}
- return builder.makeGlobalGet(pick(globals), type);
+ return builder.makeGlobalGet(pick(it->second), type);
}
Expression* makeGlobalSet(Type type) {
assert(type == Type::none);
type = getConcreteType();
- auto& globals = globalsByType[type];
- if (globals.empty()) {
+ auto it = globalsByType.find(type);
+ if (it == globalsByType.end() || it->second.empty()) {
return makeTrivial(Type::none);
}
auto* value = make(type);
- return builder.makeGlobalSet(pick(globals), value);
+ return builder.makeGlobalSet(pick(it->second), value);
+ }
+
+ Expression* makeTupleMake(Type type) {
+ assert(wasm.features.hasMultivalue());
+ assert(type.isMulti());
+ std::vector<Expression*> elements;
+ for (auto t : type.expand()) {
+ elements.push_back(make(t));
+ }
+ return builder.makeTupleMake(std::move(elements));
+ }
+
+ Expression* makeTupleExtract(Type type) {
+ assert(wasm.features.hasMultivalue());
+ assert(type.isSingle() && type.isConcrete());
+ Type tupleType = getTupleType();
+ auto& elements = tupleType.expand();
+
+ // Find indices from which we can extract `type`
+ std::vector<size_t> extractIndices;
+ for (size_t i = 0; i < elements.size(); ++i) {
+ if (elements[i] == type) {
+ extractIndices.push_back(i);
+ }
+ }
+
+ // If there are none, inject one
+ if (extractIndices.size() == 0) {
+ auto newElements = elements;
+ size_t injected = upTo(elements.size());
+ newElements[injected] = type;
+ tupleType = Type(newElements);
+ extractIndices.push_back(injected);
+ }
+
+ Index index = pick(extractIndices);
+ Expression* child = make(tupleType);
+ return builder.makeTupleExtract(child, index);
}
Expression* makePointer() {
@@ -1782,6 +1827,13 @@ private:
}
return builder.makeRefNull();
}
+ if (type.isMulti()) {
+ std::vector<Expression*> operands;
+ for (auto t : type.expand()) {
+ operands.push_back(makeConst(t));
+ }
+ return builder.makeTupleMake(std::move(operands));
+ }
auto* ret = wasm.allocator.alloc<Const>();
ret->value = makeLiteral(type);
ret->type = type;
@@ -1793,8 +1845,9 @@ private:
}
Expression* makeUnary(Type type) {
+ assert(!type.isMulti());
if (type == Type::unreachable) {
- if (auto* unary = makeUnary(getConcreteType())->dynCast<Unary>()) {
+ if (auto* unary = makeUnary(getSingleConcreteType())->dynCast<Unary>()) {
return makeDeNanOp(
builder.makeUnary(unary->op, make(Type::unreachable)));
}
@@ -1808,7 +1861,7 @@ private:
switch (type.getSingle()) {
case Type::i32: {
- switch (getConcreteType().getSingle()) {
+ switch (getSingleConcreteType().getSingle()) {
case Type::i32: {
auto op = pick(
FeatureOptions<UnaryOp>()
@@ -2012,8 +2065,10 @@ private:
}
Expression* makeBinary(Type type) {
+ assert(!type.isMulti());
if (type == Type::unreachable) {
- if (auto* binary = makeBinary(getConcreteType())->dynCast<Binary>()) {
+ if (auto* binary =
+ makeBinary(getSingleConcreteType())->dynCast<Binary>()) {
return makeDeNanOp(buildBinary(
{binary->op, make(Type::unreachable), make(Type::unreachable)}));
}
@@ -2247,8 +2302,8 @@ private:
}
Expression* makeSelect(Type type) {
- Type subType1 = pick(getSubTypes(type));
- Type subType2 = pick(getSubTypes(type));
+ Type subType1 = getSubType(type);
+ Type subType2 = getSubType(type);
return makeDeNanOp(
buildSelect({make(Type::i32), make(subType1), make(subType2)}, type));
}
@@ -2677,16 +2732,10 @@ private:
}
// special getters
-
- std::vector<Type> getReachableTypes() {
+ std::vector<Type> getSingleConcreteTypes() {
return items(
FeatureOptions<Type>()
- .add(FeatureSet::MVP,
- Type::i32,
- Type::i64,
- Type::f32,
- Type::f64,
- Type::none)
+ .add(FeatureSet::MVP, Type::i32, Type::i64, Type::f32, Type::f64)
.add(FeatureSet::SIMD, Type::v128)
.add(FeatureSet::ReferenceTypes,
Type::funcref,
@@ -2695,34 +2744,46 @@ private:
.add(FeatureSet::ReferenceTypes | FeatureSet::ExceptionHandling,
Type::exnref));
}
- Type getReachableType() { return pick(getReachableTypes()); }
- std::vector<Type> getConcreteTypes() {
- return items(
- FeatureOptions<Type>()
- .add(FeatureSet::MVP, Type::i32, Type::i64, Type::f32, Type::f64)
- .add(FeatureSet::SIMD, Type::v128)
- .add(FeatureSet::ReferenceTypes,
- Type::funcref,
- Type::anyref,
- Type::nullref)
- .add(FeatureSet::ReferenceTypes | FeatureSet::ExceptionHandling,
- Type::exnref));
+ Type getSingleConcreteType() { return pick(getSingleConcreteTypes()); }
+
+ Type getTupleType() {
+ std::vector<Type> elements;
+ size_t numElements = 2 + upTo(MAX_TUPLE_SIZE - 1);
+ elements.resize(numElements);
+ for (size_t i = 0; i < numElements; ++i) {
+ elements[i] = getSingleConcreteType();
+ }
+ return Type(elements);
}
- Type getConcreteType() { return pick(getConcreteTypes()); }
- // Get types that can be stored in memory
- std::vector<Type> getStorableTypes() {
- return items(
+ Type getConcreteType() {
+ if (wasm.features.hasMultivalue() && oneIn(5)) {
+ return getTupleType();
+ } else {
+ return getSingleConcreteType();
+ }
+ }
+
+ Type getControlFlowType() {
+ if (oneIn(10)) {
+ return Type::none;
+ } else {
+ return getConcreteType();
+ }
+ }
+
+ Type getStorableType() {
+ return pick(
FeatureOptions<Type>()
.add(FeatureSet::MVP, Type::i32, Type::i64, Type::f32, Type::f64)
.add(FeatureSet::SIMD, Type::v128));
}
- Type getStorableType() { return pick(getStorableTypes()); }
// - funcref cannot be logged because referenced functions can be inlined or
// removed during optimization
// - there's no point in logging anyref because it is opaque
+ // - don't bother logging tuples
std::vector<Type> getLoggableTypes() {
return items(
FeatureOptions<Type>()
@@ -2732,6 +2793,7 @@ private:
.add(FeatureSet::ReferenceTypes | FeatureSet::ExceptionHandling,
Type::exnref));
}
+
Type getLoggableType() { return pick(getLoggableTypes()); }
// statistical distributions