summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/tools/fuzzing.h1
-rw-r--r--src/tools/fuzzing/fuzzing.cpp124
-rw-r--r--test/passes/translate-to-fuzz_all-features_metrics_noprint.txt80
3 files changed, 158 insertions, 47 deletions
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index f4400e39e..92e997913 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -366,6 +366,7 @@ private:
Expression* makeRefEq(Type type);
Expression* makeRefTest(Type type);
Expression* makeRefCast(Type type);
+ Expression* makeBrOn(Type type);
// Decide to emit a signed Struct/ArrayGet sometimes, when the field is
// packed.
diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp
index abc6a63d5..d3f52d3b0 100644
--- a/src/tools/fuzzing/fuzzing.cpp
+++ b/src/tools/fuzzing/fuzzing.cpp
@@ -1367,7 +1367,8 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) {
&Self::makeCallIndirect)
.add(FeatureSet::ExceptionHandling, &Self::makeTry)
.add(FeatureSet::ExceptionHandling, &Self::makeTryTable)
- .add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeCallRef);
+ .add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeCallRef)
+ .add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeBrOn);
}
if (type.isSingle()) {
options
@@ -1454,10 +1455,11 @@ Expression* TranslateToFuzzReader::_makenone() {
.add(FeatureSet::Atomics, &Self::makeAtomic)
.add(FeatureSet::ExceptionHandling, &Self::makeTry)
.add(FeatureSet::ExceptionHandling, &Self::makeTryTable)
- .add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeCallRef)
- .add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeStructSet)
- .add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeArraySet)
- .add(FeatureSet::GC | FeatureSet::ReferenceTypes,
+ .add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeCallRef)
+ .add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeStructSet)
+ .add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeArraySet)
+ .add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeBrOn)
+ .add(FeatureSet::ReferenceTypes | FeatureSet::GC,
&Self::makeArrayBulkMemoryOp);
return (this->*pick(options))(Type::none);
}
@@ -1484,7 +1486,7 @@ Expression* TranslateToFuzzReader::_makeunreachable() {
&Self::makeDrop,
&Self::makeReturn)
.add(FeatureSet::ExceptionHandling, &Self::makeThrow)
- .add(FeatureSet::GC | FeatureSet::ReferenceTypes, &Self::makeCallRef);
+ .add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeCallRef);
return (this->*pick(options))(Type::unreachable);
}
@@ -3944,6 +3946,116 @@ Expression* TranslateToFuzzReader::makeRefCast(Type type) {
return builder.makeRefCast(make(refType), type);
}
+Expression* TranslateToFuzzReader::makeBrOn(Type type) {
+ if (funcContext->breakableStack.empty()) {
+ return makeTrivial(type);
+ }
+ // We need to find a proper target to break to; try a few times. Finding the
+ // target is harder than flowing out the proper type, so focus on the target,
+ // and fix up the flowing type later. That is, once we find a target to break
+ // to, we can then either drop ourselves or wrap ourselves in a block +
+ // another value, so that we return the proper thing here (which is done below
+ // in fixFlowingType).
+ int tries = TRIES;
+ Name targetName;
+ Type targetType;
+ while (--tries >= 0) {
+ auto* target = pick(funcContext->breakableStack);
+ targetName = getTargetName(target);
+ targetType = getTargetType(target);
+ // We can send any reference type, or no value at all, but nothing else.
+ if (targetType.isRef() || targetType == Type::none) {
+ break;
+ }
+ }
+ if (tries < 0) {
+ return makeTrivial(type);
+ }
+
+ auto fixFlowingType = [&](Expression* brOn) -> Expression* {
+ if (Type::isSubType(brOn->type, type)) {
+ // Already of the proper type.
+ return brOn;
+ }
+ if (type == Type::none) {
+ // We just need to drop whatever it is.
+ return builder.makeDrop(brOn);
+ }
+ // We need to replace the type with something else. Drop the BrOn if we need
+ // to, and append a value with the proper type.
+ if (brOn->type != Type::none) {
+ brOn = builder.makeDrop(brOn);
+ }
+ return builder.makeSequence(brOn, make(type));
+ };
+
+ // We found something to break to. Figure out which BrOn variants we can
+ // send.
+ if (targetType == Type::none) {
+ // BrOnNull is the only variant that sends no value.
+ return fixFlowingType(
+ builder.makeBrOn(BrOnNull, targetName, make(getReferenceType())));
+ }
+
+ // We are sending a reference type to the target. All other BrOn variants can
+ // do that.
+ assert(targetType.isRef());
+ auto op = pick(BrOnNonNull, BrOnCast, BrOnCastFail);
+ Type castType = Type::none;
+ Type refType;
+ switch (op) {
+ case BrOnNonNull: {
+ // The sent type is the non-nullable version of the reference, so any ref
+ // of that type is ok, nullable or not.
+ refType = Type(targetType.getHeapType(), getNullability());
+ break;
+ }
+ case BrOnCast: {
+ // The sent type is the heap type we cast to, with the input type's
+ // nullability, so the combination of the two must be a subtype of
+ // targetType.
+ castType = getSubType(targetType);
+ // The ref's type must be castable to castType, or we'd not validate. But
+ // it can also be a subtype, which will trivially also succeed (so do that
+ // more rarely). Pick subtypes rarely, as they make the cast trivial.
+ refType = oneIn(5) ? getSubType(castType) : getSuperType(castType);
+ if (targetType.isNonNullable()) {
+ // And it must have the right nullability for the target, as mentioned
+ // above: if the target type is non-nullable then either the ref or the
+ // cast types must be.
+ if (!refType.isNonNullable() && !castType.isNonNullable()) {
+ // Pick one to make non-nullable.
+ if (oneIn(2)) {
+ refType = Type(refType.getHeapType(), NonNullable);
+ } else {
+ castType = Type(castType.getHeapType(), NonNullable);
+ }
+ }
+ }
+ break;
+ }
+ case BrOnCastFail: {
+ // The sent type is the ref's type, with adjusted nullability (if the cast
+ // allows nulls then no null can fail the cast, and what is sent is non-
+ // nullable). First, pick a ref type that we can send to the target.
+ refType = getSubType(targetType);
+ // See above on BrOnCast, but flipped.
+ castType = oneIn(5) ? getSuperType(refType) : getSubType(refType);
+ // There is no nullability to adjust: if targetType is non-nullable then
+ // both refType and castType are as well, as subtypes of it. But we can
+ // also allow castType to be nullable (it is not sent to the target).
+ if (castType.isNonNullable() && oneIn(2)) {
+ castType = Type(castType.getHeapType(), Nullable);
+ }
+ } break;
+ default: {
+ WASM_UNREACHABLE("bad br_on op");
+ }
+ }
+ return fixFlowingType(
+ builder.makeBrOn(op, targetName, make(refType), castType));
+}
+
bool TranslateToFuzzReader::maybeSignedGet(const Field& field) {
if (field.isPacked()) {
return oneIn(2);
diff --git a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
index e58a50830..3cc841610 100644
--- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
+++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
@@ -1,53 +1,51 @@
Metrics
total
- [exports] : 3
- [funcs] : 5
+ [exports] : 4
+ [funcs] : 7
[globals] : 26
[imports] : 5
[memories] : 1
[memory-data] : 20
- [table-data] : 0
+ [table-data] : 2
[tables] : 1
[tags] : 2
- [total] : 499
- [vars] : 20
+ [total] : 510
+ [vars] : 15
ArrayNew : 14
ArrayNewFixed : 2
- AtomicCmpxchg : 1
- AtomicNotify : 1
- AtomicRMW : 1
- Binary : 69
- Block : 42
- Break : 8
- Call : 6
- Const : 126
- Drop : 2
- GlobalGet : 27
+ AtomicFence : 1
+ Binary : 64
+ Block : 45
+ Break : 2
+ Call : 8
+ CallRef : 2
+ Const : 127
+ Drop : 5
+ GlobalGet : 28
GlobalSet : 16
- I31Get : 1
- If : 10
+ If : 12
Load : 18
- LocalGet : 43
- LocalSet : 22
- Loop : 5
- Nop : 3
- Pop : 3
- RefAs : 2
- RefFunc : 2
- RefI31 : 1
- RefNull : 8
- RefTest : 1
- Return : 1
- Select : 1
- Store : 3
- StringConst : 9
- StringEncode : 1
- StringEq : 3
- StructNew : 12
- StructSet : 1
- Try : 3
- TryTable : 2
- TupleExtract : 1
- TupleMake : 4
- Unary : 13
- Unreachable : 11
+ LocalGet : 46
+ LocalSet : 29
+ Loop : 3
+ MemoryCopy : 1
+ MemoryInit : 1
+ Nop : 4
+ Pop : 2
+ RefCast : 1
+ RefFunc : 7
+ RefNull : 6
+ Return : 9
+ SIMDExtract : 1
+ Store : 1
+ StringConst : 7
+ StringEq : 1
+ StringMeasure : 1
+ StringSliceWTF : 1
+ StructNew : 14
+ Try : 2
+ TryTable : 1
+ TupleExtract : 3
+ TupleMake : 6
+ Unary : 9
+ Unreachable : 10