summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/tools/fuzzing.h4
-rw-r--r--src/tools/fuzzing/fuzzing.cpp89
-rw-r--r--test/passes/translate-to-fuzz_all-features_metrics_noprint.txt66
3 files changed, 120 insertions, 39 deletions
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index c67d6c902..ebc77ab43 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -318,6 +318,7 @@ private:
Expression* makeRefIsNull(Type type);
Expression* makeRefEq(Type type);
Expression* makeRefTest(Type type);
+ Expression* makeRefCast(Type type);
Expression* makeI31New(Type type);
Expression* makeI31Get(Type type);
Expression* makeMemoryInit();
@@ -340,6 +341,9 @@ private:
Nullability getSubType(Nullability nullability);
HeapType getSubType(HeapType type);
Type getSubType(Type type);
+ Nullability getSuperType(Nullability nullability);
+ HeapType getSuperType(HeapType type);
+ Type getSuperType(Type type);
// Utilities
Name getTargetName(Expression* target);
diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp
index 45cfbde0f..0a4f8e581 100644
--- a/src/tools/fuzzing/fuzzing.cpp
+++ b/src/tools/fuzzing/fuzzing.cpp
@@ -1088,8 +1088,13 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) {
if (type.isTuple()) {
options.add(FeatureSet::Multivalue, &Self::makeTupleMake);
}
- if (type.isRef() && type.getHeapType() == HeapType::i31) {
- options.add(FeatureSet::ReferenceTypes | FeatureSet::GC, &Self::makeI31New);
+ if (type.isRef()) {
+ if (type.getHeapType() == HeapType::i31) {
+ options.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
+ &Self::makeI31New);
+ }
+ options.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
+ &Self::makeRefCast);
}
// TODO: struct.get and other GC things
return (this->*pick(options))(type);
@@ -3136,6 +3141,41 @@ Expression* TranslateToFuzzReader::makeRefTest(Type type) {
return builder.makeRefTest(make(refType), castType);
}
+Expression* TranslateToFuzzReader::makeRefCast(Type type) {
+ assert(type.isRef());
+ assert(wasm.features.hasReferenceTypes() && wasm.features.hasGC());
+ // As with RefTest, use possibly related types. Unlike there, we are given the
+ // output type, which is the cast type, so just generate the ref's type.
+ Type refType;
+ switch (upTo(3)) {
+ case 0:
+ // Totally random.
+ refType = getReferenceType();
+ // They must share a bottom type in order to validate.
+ if (refType.getHeapType().getBottom() == type.getHeapType().getBottom()) {
+ break;
+ }
+ // Otherwise, fall through and generate things in a way that is
+ // guaranteed to validate.
+ [[fallthrough]];
+ case 1: {
+ // Cast is a subtype of ref. We can't modify |type|, so find a supertype
+ // for the ref.
+ refType = getSuperType(type);
+ break;
+ }
+ case 2:
+ // Ref is a subtype of cast.
+ refType = getSubType(type);
+ break;
+ default:
+ // This unreachable avoids a warning on refType being possibly undefined.
+ WASM_UNREACHABLE("bad case");
+ }
+ // TODO: Fuzz unsafe casts?
+ return builder.makeRefCast(make(refType), type, RefCast::Safe);
+}
+
Expression* TranslateToFuzzReader::makeI31New(Type type) {
assert(type.isRef() && type.getHeapType() == HeapType::i31);
assert(wasm.features.hasReferenceTypes() && wasm.features.hasGC());
@@ -3408,6 +3448,10 @@ HeapType TranslateToFuzzReader::getSubType(HeapType type) {
return type;
}
+static bool isUninhabitable(Type type) {
+ return type.isNonNullable() && type.getHeapType().isBottom();
+}
+
Type TranslateToFuzzReader::getSubType(Type type) {
if (type.isTuple()) {
std::vector<Type> types;
@@ -3418,16 +3462,14 @@ Type TranslateToFuzzReader::getSubType(Type type) {
} else if (type.isRef()) {
auto heapType = getSubType(type.getHeapType());
auto nullability = getSubType(type.getNullability());
+ auto subType = Type(heapType, nullability);
// 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.
- auto uninhabitable = nullability == NonNullable && heapType.isBottom();
- auto originalUninhabitable =
- type.isNonNullable() && type.getHeapType().isBottom();
- if (uninhabitable && !originalUninhabitable && !oneIn(20)) {
+ if (isUninhabitable(subType) && !isUninhabitable(type) && !oneIn(20)) {
return type;
}
- return Type(heapType, nullability);
+ return subType;
} else {
// This is an MVP type without subtypes.
assert(type.isBasic());
@@ -3435,6 +3477,39 @@ Type TranslateToFuzzReader::getSubType(Type type) {
}
}
+Nullability TranslateToFuzzReader::getSuperType(Nullability nullability) {
+ if (nullability == Nullable) {
+ return Nullable;
+ }
+ return getNullability();
+}
+
+HeapType TranslateToFuzzReader::getSuperType(HeapType type) {
+ // TODO cache these?
+ std::vector<HeapType> supers;
+ while (1) {
+ supers.push_back(type);
+ if (auto super = type.getSuperType()) {
+ type = *super;
+ } else {
+ break;
+ }
+ }
+ return pick(supers);
+}
+
+Type TranslateToFuzzReader::getSuperType(Type type) {
+ auto heapType = getSuperType(type.getHeapType());
+ auto nullability = getSuperType(type.getNullability());
+ auto superType = Type(heapType, nullability);
+ // As with getSubType, we want to avoid returning an uninhabitable type where
+ // possible. Here all we can do is flip the super's nullability to nullable.
+ if (isUninhabitable(superType)) {
+ superType = Type(heapType, Nullable);
+ }
+ return superType;
+}
+
Name TranslateToFuzzReader::getTargetName(Expression* target) {
if (auto* block = target->dynCast<Block>()) {
return block->name;
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 1cafe1b28..0e4351b47 100644
--- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
+++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
@@ -8,40 +8,42 @@ total
[table-data] : 2
[tables] : 1
[tags] : 0
- [total] : 682
- [vars] : 9
- ArrayNew : 4
- ArrayNewFixed : 5
- AtomicCmpxchg : 1
- AtomicNotify : 1
+ [total] : 563
+ [vars] : 7
+ ArrayNew : 6
+ ArrayNewFixed : 6
+ AtomicRMW : 1
Binary : 78
- Block : 88
- Break : 11
- Call : 28
- Const : 145
- Drop : 8
- GlobalGet : 35
- GlobalSet : 35
+ Block : 63
+ Break : 5
+ Call : 18
+ CallIndirect : 1
+ CallRef : 1
+ Const : 119
+ DataDrop : 1
+ Drop : 6
+ GlobalGet : 28
+ GlobalSet : 26
I31Get : 1
- I31New : 4
- If : 30
- Load : 18
- LocalGet : 44
- LocalSet : 24
- Loop : 10
- MemoryFill : 2
+ I31New : 2
+ If : 21
+ Load : 22
+ LocalGet : 43
+ LocalSet : 26
+ Loop : 4
MemoryInit : 1
- Nop : 14
- RefAs : 1
- RefEq : 1
- RefFunc : 10
- RefNull : 8
- RefTest : 2
- Return : 4
+ Nop : 10
+ RefAs : 2
+ RefCast : 1
+ RefEq : 2
+ RefFunc : 7
+ RefNull : 6
+ RefTest : 1
+ Return : 8
+ SIMDExtract : 1
Select : 1
Store : 3
- StructNew : 5
- TupleExtract : 1
- TupleMake : 8
- Unary : 32
- Unreachable : 19
+ StructNew : 2
+ TupleMake : 6
+ Unary : 20
+ Unreachable : 14