diff options
-rw-r--r-- | src/tools/fuzzing.h | 1 | ||||
-rw-r--r-- | src/tools/fuzzing/fuzzing.cpp | 124 | ||||
-rw-r--r-- | test/passes/translate-to-fuzz_all-features_metrics_noprint.txt | 80 |
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 |