summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/struct-utils.h4
-rw-r--r--src/passes/RemoveUnusedModuleElements.cpp5
-rw-r--r--src/tools/fuzzing.h9
-rw-r--r--src/tools/fuzzing/fuzzing.cpp62
-rw-r--r--test/passes/translate-to-fuzz_all-features_metrics_noprint.txt56
5 files changed, 105 insertions, 31 deletions
diff --git a/src/ir/struct-utils.h b/src/ir/struct-utils.h
index 9f880985f..dc87ed574 100644
--- a/src/ir/struct-utils.h
+++ b/src/ir/struct-utils.h
@@ -22,6 +22,10 @@
namespace wasm {
+// A pair of a struct type and a field index, together defining a field in a
+// particular type.
+using StructField = std::pair<HeapType, Index>;
+
namespace StructUtils {
// A vector of a template type's values. One such vector will be used per struct
diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp
index 228b3afc1..6842c852e 100644
--- a/src/passes/RemoveUnusedModuleElements.cpp
+++ b/src/passes/RemoveUnusedModuleElements.cpp
@@ -41,6 +41,7 @@
#include "ir/find_all.h"
#include "ir/intrinsics.h"
#include "ir/module-utils.h"
+#include "ir/struct-utils.h"
#include "ir/subtypes.h"
#include "ir/utils.h"
#include "pass.h"
@@ -63,10 +64,6 @@ enum class ModuleElementKind {
// name of the particular element.
using ModuleElement = std::pair<ModuleElementKind, Name>;
-// A pair of a struct type and a field index, together defining a field in a
-// particular type.
-using StructField = std::pair<HeapType, Index>;
-
// Visit or walk an expression to find what things are referenced.
struct ReferenceFinder : public PostWalker<ReferenceFinder> {
// Our findings are placed in these data structures, which the user of this
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index e53665e14..24f08dc53 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -29,6 +29,7 @@ high chance for set at start of loop
#include "ir/branch-utils.h"
#include "ir/memory-utils.h"
+#include "ir/struct-utils.h"
#include "support/insert_ordered.h"
#include "tools/fuzzing/random.h"
#include <ir/eh-utils.h>
@@ -117,6 +118,12 @@ private:
// subtypes of it.
std::unordered_map<HeapType, std::vector<HeapType>> interestingHeapSubTypes;
+ // Type => list of struct fields that have that type.
+ std::unordered_map<Type, std::vector<StructField>> typeStructFields;
+
+ // Type => list of array types that have that type.
+ std::unordered_map<Type, std::vector<HeapType>> typeArrays;
+
Index numAddedFunctions = 0;
// RAII helper for managing the state used to create a single function.
@@ -326,6 +333,8 @@ private:
Expression* makeRefEq(Type type);
Expression* makeRefTest(Type type);
Expression* makeRefCast(Type type);
+ Expression* makeStructGet(Type type);
+ Expression* makeArrayGet(Type type);
Expression* makeI31Get(Type type);
Expression* makeMemoryInit();
Expression* makeDataDrop();
diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp
index 02902fb15..fd8722224 100644
--- a/src/tools/fuzzing/fuzzing.cpp
+++ b/src/tools/fuzzing/fuzzing.cpp
@@ -295,6 +295,18 @@ void TranslateToFuzzReader::setupHeapTypes() {
interestingHeapSubTypes[HeapType::func].push_back(type);
}
}
+
+ // Compute struct and array fields.
+ for (auto type : interestingHeapTypes) {
+ if (type.isStruct()) {
+ auto& fields = type.getStruct().fields;
+ for (Index i = 0; i < fields.size(); i++) {
+ typeStructFields[fields[i].type].push_back(StructField{type, i});
+ }
+ } else if (type.isArray()) {
+ typeArrays[type.getArray().element.type].push_back(type);
+ }
+ }
}
// TODO(reference-types): allow the fuzzer to create multiple tables
@@ -1129,6 +1141,16 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) {
options.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
&Self::makeRefCast);
}
+ if (wasm.features.hasGC()) {
+ if (typeStructFields.find(type) != typeStructFields.end()) {
+ options.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
+ &Self::makeStructGet);
+ }
+ if (typeArrays.find(type) != typeArrays.end()) {
+ options.add(FeatureSet::ReferenceTypes | FeatureSet::GC,
+ &Self::makeArrayGet);
+ }
+ }
// TODO: struct.get and other GC things
return (this->*pick(options))(type);
}
@@ -3224,6 +3246,46 @@ Expression* TranslateToFuzzReader::makeRefCast(Type type) {
return builder.makeRefCast(make(refType), type, RefCast::Safe);
}
+Expression* TranslateToFuzzReader::makeStructGet(Type type) {
+ auto& structFields = typeStructFields[type];
+ assert(!structFields.empty());
+ auto [structType, fieldIndex] = pick(structFields);
+ // TODO: also nullable ones? that would increase the risk of traps
+ auto* ref = make(Type(structType, NonNullable));
+ // TODO: fuzz signed and unsigned
+ return builder.makeStructGet(fieldIndex, ref, type);
+}
+
+Expression* TranslateToFuzzReader::makeArrayGet(Type type) {
+ auto& arrays = typeArrays[type];
+ assert(!arrays.empty());
+ auto arrayType = pick(arrays);
+ // TODO: also nullable ones? that would increase the risk of traps
+ auto* ref = make(Type(arrayType, NonNullable));
+ auto* index = make(Type::i32);
+ // Only rarely emit a plain get which might trap. See related logic in
+ // ::makePointer().
+ if (allowOOB && oneIn(10)) {
+ // TODO: fuzz signed and unsigned, and also below
+ return builder.makeArrayGet(ref, index, type);
+ }
+ // To avoid a trap, check the length dynamically using this pattern:
+ //
+ // index < array.len ? array[index] : ..some fallback value..
+ //
+ auto tempRef = builder.addVar(funcContext->func, ref->type);
+ auto tempIndex = builder.addVar(funcContext->func, index->type);
+ auto* teeRef = builder.makeLocalTee(tempRef, ref, ref->type);
+ auto* teeIndex = builder.makeLocalTee(tempIndex, index, index->type);
+ auto* getSize = builder.makeArrayLen(teeRef);
+ auto* condition = builder.makeBinary(LtUInt32, teeIndex, getSize);
+ auto* get = builder.makeArrayGet(builder.makeLocalGet(tempRef, ref->type),
+ builder.makeLocalGet(tempIndex, index->type),
+ type);
+ auto* fallback = makeTrivial(type);
+ return builder.makeIf(condition, get, fallback);
+}
+
Expression* TranslateToFuzzReader::makeI31Get(Type type) {
assert(type == Type::i32);
assert(wasm.features.hasReferenceTypes() && wasm.features.hasGC());
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 cd33b98f6..71e0b852f 100644
--- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
+++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
@@ -1,40 +1,42 @@
total
- [exports] : 4
- [funcs] : 13
+ [exports] : 3
+ [funcs] : 11
[globals] : 16
[imports] : 5
[memories] : 1
[memory-data] : 20
- [table-data] : 4
+ [table-data] : 7
[tables] : 1
[tags] : 2
- [total] : 592
- [vars] : 27
- ArrayNew : 1
- AtomicNotify : 1
- Binary : 71
- Block : 72
- Break : 10
- Call : 20
- Const : 129
+ [total] : 593
+ [vars] : 14
+ ArrayNew : 3
+ AtomicCmpxchg : 1
+ AtomicFence : 2
+ AtomicRMW : 1
+ Binary : 72
+ Block : 77
+ Break : 5
+ Call : 14
+ CallRef : 1
+ Const : 130
Drop : 4
- GlobalGet : 33
- GlobalSet : 33
+ GlobalGet : 34
+ GlobalSet : 34
I31New : 1
If : 24
- Load : 19
- LocalGet : 44
- LocalSet : 25
- Loop : 4
- Nop : 7
- RefAs : 3
- RefEq : 1
- RefFunc : 10
- RefIsNull : 1
- RefNull : 10
- RefTest : 1
- Return : 4
- Store : 2
+ Load : 18
+ LocalGet : 34
+ LocalSet : 17
+ Loop : 7
+ Nop : 17
+ RefAs : 5
+ RefFunc : 13
+ RefNull : 12
+ Return : 1
+ SIMDExtract : 1
+ Store : 1
+ StructGet : 2
StructNew : 4
TupleExtract : 2
TupleMake : 11