summaryrefslogtreecommitdiffstats
path: root/contrib/restricted/wavm/Lib/IR/RandomModule.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/restricted/wavm/Lib/IR/RandomModule.cpp')
-rw-r--r--contrib/restricted/wavm/Lib/IR/RandomModule.cpp1363
1 files changed, 1363 insertions, 0 deletions
diff --git a/contrib/restricted/wavm/Lib/IR/RandomModule.cpp b/contrib/restricted/wavm/Lib/IR/RandomModule.cpp
new file mode 100644
index 00000000000..be6a4492512
--- /dev/null
+++ b/contrib/restricted/wavm/Lib/IR/RandomModule.cpp
@@ -0,0 +1,1363 @@
+#include "WAVM/IR/RandomModule.h"
+#include <vector>
+#include "WAVM/IR/IR.h"
+#include "WAVM/IR/Module.h"
+#include "WAVM/IR/OperatorSignatures.h"
+#include "WAVM/IR/Operators.h"
+#include "WAVM/IR/Types.h"
+#include "WAVM/IR/Validate.h"
+#include "WAVM/Inline/Assert.h"
+#include "WAVM/Inline/BasicTypes.h"
+#include "WAVM/Inline/HashMap.h"
+#include "WAVM/Inline/HashSet.h"
+#include "WAVM/Inline/RandomStream.h"
+
+using namespace WAVM;
+using namespace WAVM::IR;
+
+constexpr Uptr softMaxStackDepthInControlContext = 6;
+constexpr Uptr softMaxInstructionsInFunction = 15;
+
+struct ModuleState
+{
+ Module& module;
+ std::vector<Uptr> declaredFunctionIndices;
+ std::vector<ElemSegmentAndTableImm> validElemSegmentAndTableImms;
+ HashMap<FunctionType, Uptr> functionTypeMap;
+
+ RandomStream& random;
+
+ ModuleState(Module& inModule, RandomStream& inRandom) : module(inModule), random(inRandom) {}
+};
+
+using CodeStream = CodeValidationProxyStream<OperatorEncoderStream>;
+
+struct FunctionState
+{
+ struct ControlContext
+ {
+ enum class Type : U8
+ {
+ function,
+ block,
+ ifThen,
+ ifElse,
+ loop,
+ try_,
+ catch_
+ };
+
+ Type type;
+ Uptr outerStackSize;
+
+ TypeTuple params;
+ TypeTuple results;
+
+ TypeTuple elseParams;
+ };
+
+ ModuleState& moduleState;
+ ModuleValidationState& moduleValidationState;
+ const Module& module;
+ const FunctionType functionType;
+ FunctionDef& functionDef;
+
+ const Uptr numLocals;
+ Uptr numInstructions = 0;
+ bool allowStackGrowth = false;
+
+ std::vector<ControlContext> controlStack;
+ std::vector<ValueType> stack;
+
+ Serialization::ArrayOutputStream codeByteStream;
+ OperatorEncoderStream opEncoder;
+ CodeStream codeStream;
+
+ FunctionState(ModuleState& inModuleState,
+ ModuleValidationState& inModuleValidationState,
+ FunctionDef& inFunctionDef)
+ : moduleState(inModuleState)
+ , moduleValidationState(inModuleValidationState)
+ , module(inModuleState.module)
+ , functionType(inModuleState.module.types[inFunctionDef.type.index])
+ , functionDef(inFunctionDef)
+ , numLocals(functionType.params().size() + functionDef.nonParameterLocalTypes.size())
+ , opEncoder(codeByteStream)
+ , codeStream(inModuleValidationState, inFunctionDef, opEncoder)
+ {
+ }
+
+ template<typename TypeTupleOrOpTypeTuple>
+ bool doesStackMatchParams(const TypeTupleOrOpTypeTuple& params,
+ Uptr offsetFromTopOfStack = 0) const
+ {
+ // Ensure the stack has enough values for the operator's parameters.
+ if(params.size() + offsetFromTopOfStack > stack.size() - controlStack.back().outerStackSize)
+ { return false; }
+
+ // Check that the types of values on top of the stack are the right type for the
+ // operator's parameters.
+ for(Uptr paramIndex = 0; paramIndex < params.size(); ++paramIndex)
+ {
+ if(!isSubtype(stack[stack.size() - offsetFromTopOfStack - params.size() + paramIndex],
+ params[paramIndex]))
+ { return false; }
+ }
+ return true;
+ };
+
+ bool isOpSignatureAllowed(const OpSignature& sig) const
+ {
+ // If the random stream has run out of entropy, only consider operators that result
+ // in fewer operands on the stack.
+ if(!allowStackGrowth && sig.results.size() >= sig.params.size()) { return false; }
+
+ return doesStackMatchParams(sig.params);
+ }
+
+ void applyOpSignature(const OpSignature& sig)
+ {
+ // Remove the operator's parameters from the top of the stack.
+ stack.resize(stack.size() - sig.params.size());
+
+ // Push the operator's results onto the stack.
+ for(ValueType result : sig.results) { stack.push_back(result); }
+ }
+
+ void emitControlEnd()
+ {
+ ControlContext& controlContext = controlStack.back();
+ if(controlContext.type == ControlContext::Type::ifThen)
+ {
+ // Emit the else operator.
+ codeStream.else_();
+
+ stack.resize(controlContext.outerStackSize);
+ for(ValueType elseParam : controlContext.elseParams) { stack.push_back(elseParam); }
+
+ // Change the current control context type to an else clause.
+ controlContext.type = ControlContext::Type::ifElse;
+ }
+ else
+ {
+ if(controlContext.type == ControlContext::Type::try_
+ || controlContext.type == ControlContext::Type::catch_)
+ {
+ // TODO: catch
+ WAVM_UNREACHABLE();
+ }
+
+ codeStream.end();
+ stack.resize(controlContext.outerStackSize);
+ for(ValueType result : controlContext.results) { stack.push_back(result); }
+ controlStack.pop_back();
+ }
+ }
+
+ void generateFunction(RandomStream& random);
+};
+
+template<typename Imm> struct ImmTypeAsValue
+{
+};
+
+using OperatorEmitFunc = std::function<void(RandomStream&)>;
+
+template<typename Imm> bool isImmValid(const FunctionState&, ImmTypeAsValue<Imm>) { return true; }
+
+static void generateImm(const FunctionState& state, RandomStream& random, NoImm& outImm) {}
+
+static bool isImmValid(const FunctionState& state, ImmTypeAsValue<FunctionRefImm>)
+{
+ return state.moduleState.declaredFunctionIndices.size();
+}
+static void generateImm(const FunctionState& state, RandomStream& random, FunctionRefImm& outImm)
+{
+ outImm.functionIndex = state.moduleState.declaredFunctionIndices[random.get(
+ state.moduleState.declaredFunctionIndices.size() - 1)];
+}
+
+static void generateImm(const FunctionState& state, RandomStream& random, LiteralImm<I32>& outImm)
+{
+ outImm.value = I32(random.get(UINT32_MAX));
+}
+
+static void generateImm(const FunctionState& state, RandomStream& random, LiteralImm<I64>& outImm)
+{
+ outImm.value = I64(random.get(UINT64_MAX));
+}
+
+static void generateImm(const FunctionState& state, RandomStream& random, LiteralImm<F32>& outImm)
+{
+ const U32 u32 = random.get(UINT32_MAX);
+ memcpy(&outImm.value, &u32, sizeof(U32));
+}
+
+static void generateImm(const FunctionState& state, RandomStream& random, LiteralImm<F64>& outImm)
+{
+ const U64 u64 = random.get(UINT64_MAX);
+ memcpy(&outImm.value, &u64, sizeof(U64));
+}
+
+static void generateImm(const FunctionState& state, RandomStream& random, LiteralImm<V128>& outImm)
+{
+ outImm.value.u64x2[0] = random.get(UINT64_MAX);
+ outImm.value.u64x2[1] = random.get(UINT64_MAX);
+}
+
+static void generateImm(const FunctionState& state, RandomStream& random, AtomicFenceImm& outImm)
+{
+ outImm.order = MemoryOrder::sequentiallyConsistent;
+}
+
+template<Uptr numLanes>
+static void generateImm(const FunctionState& state,
+ RandomStream& random,
+ LaneIndexImm<numLanes>& outImm)
+{
+ outImm.laneIndex = random.get<U8>(numLanes - 1);
+}
+
+template<Uptr numLanes>
+static void generateImm(const FunctionState& state,
+ RandomStream& random,
+ ShuffleImm<numLanes>& outImm)
+{
+ for(Uptr laneIndex = 0; laneIndex < numLanes; ++laneIndex)
+ { outImm.laneIndices[laneIndex] = random.get<U8>(numLanes * 2 - 1); }
+}
+
+static bool isImmValid(const FunctionState& state, ImmTypeAsValue<DataSegmentImm>)
+{
+ return state.module.dataSegments.size();
+}
+static void generateImm(const FunctionState& state, RandomStream& random, DataSegmentImm& outImm)
+{
+ WAVM_ASSERT(state.module.dataSegments.size());
+ outImm.dataSegmentIndex = random.get(state.module.dataSegments.size() - 1);
+}
+
+static bool isImmValid(const FunctionState& state, ImmTypeAsValue<ElemSegmentImm>)
+{
+ return state.module.elemSegments.size();
+}
+static void generateImm(const FunctionState& state, RandomStream& random, ElemSegmentImm& outImm)
+{
+ WAVM_ASSERT(state.module.elemSegments.size());
+ outImm.elemSegmentIndex = random.get(state.module.elemSegments.size() - 1);
+}
+
+template<typename Imm>
+void getValidEmitters(FunctionState& state,
+ std::vector<OperatorEmitFunc>& outValidOpEmitters,
+ void (CodeStream::*emitOp)(Imm),
+ const OpSignature& sig)
+{
+ if(isImmValid(state, ImmTypeAsValue<Imm>()) && state.isOpSignatureAllowed(sig))
+ {
+ outValidOpEmitters.push_back([&state, emitOp, &sig](RandomStream& random) {
+ Imm imm;
+ generateImm(state, random, imm);
+ (state.codeStream.*emitOp)(imm);
+ state.applyOpSignature(sig);
+ });
+ }
+}
+
+void getValidEmitters(FunctionState& state,
+ std::vector<OperatorEmitFunc>& outValidOpEmitters,
+ void (CodeStream::*emitOp)(MemoryImm),
+ OpSignature (*sigFromImm)(const Module&, const MemoryImm&))
+{
+ for(Uptr memoryIndex = 0; memoryIndex < state.module.memories.size(); ++memoryIndex)
+ {
+ MemoryImm imm;
+ imm.memoryIndex = memoryIndex;
+ const OpSignature sig = (*sigFromImm)(state.module, imm);
+ if(state.isOpSignatureAllowed(sig))
+ {
+ outValidOpEmitters.push_back([&state, emitOp, sig, imm](RandomStream& random) {
+ (state.codeStream.*emitOp)(imm);
+ state.applyOpSignature(sig);
+ });
+ }
+ }
+}
+
+void getValidEmitters(FunctionState& state,
+ std::vector<OperatorEmitFunc>& outValidOpEmitters,
+ void (CodeStream::*emitOp)(MemoryCopyImm),
+ OpSignature (*sigFromImm)(const Module&, const MemoryCopyImm&))
+{
+ for(Uptr destMemoryIndex = 0; destMemoryIndex < state.module.memories.size(); ++destMemoryIndex)
+ {
+ for(Uptr sourceMemoryIndex = 0; sourceMemoryIndex < state.module.memories.size();
+ ++sourceMemoryIndex)
+ {
+ MemoryCopyImm imm;
+ imm.destMemoryIndex = destMemoryIndex;
+ imm.sourceMemoryIndex = sourceMemoryIndex;
+ const OpSignature sig = (*sigFromImm)(state.module, imm);
+ if(state.isOpSignatureAllowed(sig))
+ {
+ outValidOpEmitters.push_back([&state, emitOp, sig, imm](RandomStream& random) {
+ (state.codeStream.*emitOp)(imm);
+ state.applyOpSignature(sig);
+ });
+ }
+ }
+ }
+}
+
+void getValidEmitters(FunctionState& state,
+ std::vector<OperatorEmitFunc>& outValidOpEmitters,
+ void (CodeStream::*emitOp)(TableImm),
+ OpSignature (*sigFromImm)(const Module&, const TableImm&))
+{
+ for(Uptr tableIndex = 0; tableIndex < state.module.tables.size(); ++tableIndex)
+ {
+ TableImm imm;
+ imm.tableIndex = tableIndex;
+ const OpSignature sig = (*sigFromImm)(state.module, imm);
+ if(state.isOpSignatureAllowed(sig))
+ {
+ outValidOpEmitters.push_back([&state, emitOp, sig, imm](RandomStream& random) {
+ (state.codeStream.*emitOp)(imm);
+ state.applyOpSignature(sig);
+ });
+ }
+ }
+}
+
+void getValidEmitters(FunctionState& state,
+ std::vector<OperatorEmitFunc>& outValidOpEmitters,
+ void (CodeStream::*emitOp)(TableCopyImm),
+ OpSignature (*sigFromImm)(const Module&, const TableCopyImm&))
+{
+ for(Uptr destTableIndex = 0; destTableIndex < state.module.tables.size(); ++destTableIndex)
+ {
+ for(Uptr sourceTableIndex = 0; sourceTableIndex < state.module.tables.size();
+ ++sourceTableIndex)
+ {
+ const TableType& destTableType = state.module.tables.getType(destTableIndex);
+ const TableType& sourceTableType = state.module.tables.getType(sourceTableIndex);
+ if(isSubtype(sourceTableType.elementType, destTableType.elementType))
+ {
+ TableCopyImm imm;
+ imm.destTableIndex = destTableIndex;
+ imm.sourceTableIndex = sourceTableIndex;
+ const OpSignature sig = (*sigFromImm)(state.module, imm);
+ if(state.isOpSignatureAllowed(sig))
+ {
+ outValidOpEmitters.push_back([&state, emitOp, sig, imm](RandomStream& random) {
+ (state.codeStream.*emitOp)(imm);
+ state.applyOpSignature(sig);
+ });
+ }
+ }
+ }
+ }
+}
+
+template<Uptr naturalAlignmentLog2>
+void getValidEmitters(FunctionState& state,
+ std::vector<OperatorEmitFunc>& outValidOpEmitters,
+ void (CodeStream::*emitOp)(LoadOrStoreImm<naturalAlignmentLog2>),
+ OpSignature (*sigFromImm)(const Module&, const BaseLoadOrStoreImm&))
+{
+ for(Uptr memoryIndex = 0; memoryIndex < state.module.memories.size(); ++memoryIndex)
+ {
+ LoadOrStoreImm<naturalAlignmentLog2> sigImm;
+ sigImm.memoryIndex = memoryIndex;
+ sigImm.alignmentLog2 = 0;
+ sigImm.offset = 0;
+ const OpSignature sig = (*sigFromImm)(state.module, sigImm);
+ if(state.isOpSignatureAllowed(sig))
+ {
+ outValidOpEmitters.push_back([&state, emitOp, sig, memoryIndex](RandomStream& random) {
+ LoadOrStoreImm<naturalAlignmentLog2> imm;
+ imm.memoryIndex = memoryIndex;
+ imm.alignmentLog2 = random.get<U8>(naturalAlignmentLog2);
+ imm.offset = random.get(state.module.memories.getType(imm.memoryIndex).indexType
+ == IndexType::i32
+ ? UINT32_MAX
+ : UINT64_MAX);
+ (state.codeStream.*emitOp)(imm);
+ state.applyOpSignature(sig);
+ });
+ }
+ }
+}
+
+template<Uptr naturalAlignmentLog2, Uptr numLanes>
+void getValidEmitters(
+ FunctionState& state,
+ std::vector<OperatorEmitFunc>& outValidOpEmitters,
+ void (CodeStream::*emitOp)(LoadOrStoreLaneImm<naturalAlignmentLog2, numLanes>),
+ OpSignature (*sigFromImm)(const Module&,
+ const LoadOrStoreLaneImm<naturalAlignmentLog2, numLanes>&))
+{
+ for(Uptr memoryIndex = 0; memoryIndex < state.module.memories.size(); ++memoryIndex)
+ {
+ LoadOrStoreLaneImm<naturalAlignmentLog2, numLanes> sigImm;
+ sigImm.memoryIndex = memoryIndex;
+ sigImm.alignmentLog2 = 0;
+ sigImm.offset = 0;
+ sigImm.laneIndex = 0;
+ const OpSignature sig = (*sigFromImm)(state.module, sigImm);
+ if(state.isOpSignatureAllowed(sig))
+ {
+ outValidOpEmitters.push_back([&state, emitOp, sig, memoryIndex](RandomStream& random) {
+ LoadOrStoreLaneImm<naturalAlignmentLog2, numLanes> imm;
+ imm.memoryIndex = memoryIndex;
+ imm.alignmentLog2 = random.get<U8>(naturalAlignmentLog2);
+ imm.offset = random.get(state.module.memories.getType(imm.memoryIndex).indexType
+ == IndexType::i32
+ ? UINT32_MAX
+ : UINT64_MAX);
+ imm.laneIndex = random.get<U8>(numLanes - 1);
+ (state.codeStream.*emitOp)(imm);
+ state.applyOpSignature(sig);
+ });
+ }
+ }
+}
+
+template<Uptr naturalAlignmentLog2>
+void getValidEmitters(FunctionState& state,
+ std::vector<OperatorEmitFunc>& outValidOpEmitters,
+ void (CodeStream::*emitOp)(AtomicLoadOrStoreImm<naturalAlignmentLog2>),
+ OpSignature (*sigFromImm)(const Module&, const BaseLoadOrStoreImm&))
+{
+ for(Uptr memoryIndex = 0; memoryIndex < state.module.memories.size(); ++memoryIndex)
+ {
+ AtomicLoadOrStoreImm<naturalAlignmentLog2> sigImm;
+ sigImm.memoryIndex = memoryIndex;
+ sigImm.alignmentLog2 = 0;
+ sigImm.offset = 0;
+ const OpSignature sig = (*sigFromImm)(state.module, sigImm);
+ if(state.isOpSignatureAllowed(sig))
+ {
+ outValidOpEmitters.push_back([&state, emitOp, sig, memoryIndex](RandomStream& random) {
+ AtomicLoadOrStoreImm<naturalAlignmentLog2> imm;
+ imm.memoryIndex = memoryIndex;
+ imm.alignmentLog2 = naturalAlignmentLog2;
+ imm.offset = random.get(state.module.memories.getType(imm.memoryIndex).indexType
+ == IndexType::i32
+ ? UINT32_MAX
+ : UINT64_MAX);
+ (state.codeStream.*emitOp)(imm);
+ state.applyOpSignature(sig);
+ });
+ }
+ }
+}
+
+void getValidEmitters(FunctionState& state,
+ std::vector<OperatorEmitFunc>& outValidOpEmitters,
+ void (CodeStream::*emitOp)(DataSegmentAndMemImm),
+ OpSignature (*sigFromImm)(const Module&, const DataSegmentAndMemImm&))
+{
+ for(Uptr segmentIndex = 0; segmentIndex < state.module.dataSegments.size(); ++segmentIndex)
+ {
+ for(Uptr memoryIndex = 0; memoryIndex < state.module.memories.size(); ++memoryIndex)
+ {
+ DataSegmentAndMemImm imm;
+ imm.dataSegmentIndex = segmentIndex;
+ imm.memoryIndex = memoryIndex;
+ const OpSignature sig = (*sigFromImm)(state.module, imm);
+ if(state.isOpSignatureAllowed(sig))
+ {
+ outValidOpEmitters.push_back([&state, emitOp, sig, imm](RandomStream& random) {
+ (state.codeStream.*emitOp)(imm);
+ state.applyOpSignature(sig);
+ });
+ }
+ }
+ }
+}
+
+void getValidEmitters(FunctionState& state,
+ std::vector<OperatorEmitFunc>& outValidOpEmitters,
+ void (CodeStream::*emitOp)(ElemSegmentAndTableImm),
+ OpSignature (*sigFromImm)(const Module&, const ElemSegmentAndTableImm&))
+{
+ for(const ElemSegmentAndTableImm& imm : state.moduleState.validElemSegmentAndTableImms)
+ {
+ const OpSignature sig = (*sigFromImm)(state.module, imm);
+ if(state.isOpSignatureAllowed(sig))
+ {
+ outValidOpEmitters.push_back([&state, emitOp, sig, imm](RandomStream& random) {
+ (state.codeStream.*emitOp)(imm);
+ state.applyOpSignature(sig);
+ });
+ }
+ }
+}
+
+// Build a table with information about non-parametric operators.
+struct OperatorInfo
+{
+ const char* name;
+ void (*getValidEmitters)(FunctionState& state,
+ std::vector<OperatorEmitFunc>& outValidOpEmitters);
+};
+
+static const OperatorInfo operatorInfos[]{
+#define VISIT_OP(encoding, name, nameString, Imm, Signature, ...) \
+ {nameString, [](FunctionState& state, std::vector<OperatorEmitFunc>& outValidOpEmitters) { \
+ getValidEmitters(state, outValidOpEmitters, &CodeStream::name, OpSignatures::Signature); \
+ }},
+ WAVM_ENUM_NONCONTROL_NONPARAMETRIC_OPERATORS(VISIT_OP)
+ WAVM_ENUM_INDEX_POLYMORPHIC_OPERATORS(VISIT_OP)
+#undef VISIT_OP
+};
+static constexpr Uptr numNonParametricOps = sizeof(operatorInfos) / sizeof(OperatorInfo);
+
+static ValueType generateValueType(RandomStream& random)
+{
+ switch(random.get(6))
+ {
+ case 0: return ValueType::i32;
+ case 1: return ValueType::i64;
+ case 2: return ValueType::f32;
+ case 3: return ValueType::f64;
+ case 4: return ValueType::v128;
+ case 5: return ValueType::externref;
+ case 6: return ValueType::funcref;
+ default: WAVM_UNREACHABLE();
+ }
+}
+
+static FunctionType generateFunctionType(RandomStream& random)
+{
+ std::vector<ValueType> functionParams;
+ const Uptr numParams = random.get(4);
+ for(Uptr paramIndex = 0; paramIndex < numParams; ++paramIndex)
+ { functionParams.push_back(generateValueType(random)); };
+
+ std::vector<ValueType> functionResults;
+ const Uptr numResults = random.get(2);
+ for(Uptr resultIndex = 0; resultIndex < numResults; ++resultIndex)
+ { functionResults.push_back(generateValueType(random)); }
+
+ return FunctionType({functionResults}, {functionParams});
+}
+
+static IndexType generateIndexType(RandomStream& random)
+{
+ switch(random.get(1))
+ {
+ case 0: return IndexType::i32;
+ case 1: return IndexType::i64;
+ default: WAVM_UNREACHABLE();
+ };
+}
+
+static ReferenceType generateRefType(RandomStream& random)
+{
+ switch(random.get(1))
+ {
+ case 0: return ReferenceType::externref;
+ case 1: return ReferenceType::funcref;
+ default: WAVM_UNREACHABLE();
+ };
+}
+
+static ExternKind generateExternKind(RandomStream& random)
+{
+ switch(random.get(4))
+ {
+ case 0: return ExternKind::function;
+ case 1: return ExternKind::table;
+ case 2: return ExternKind::memory;
+ case 3: return ExternKind::global;
+ case 4: return ExternKind::exceptionType;
+ default: WAVM_UNREACHABLE();
+ };
+}
+
+FunctionType generateBlockSig(RandomStream& random, TypeTuple params)
+{
+ const Uptr maxResults = 4;
+ ValueType results[maxResults];
+ const Uptr numResults = random.get(4);
+ for(Uptr resultIndex = 0; resultIndex < numResults; ++resultIndex)
+ { results[resultIndex] = generateValueType(random); }
+
+ return FunctionType(TypeTuple(results, numResults), params);
+}
+
+IndexedBlockType getIndexedBlockType(ModuleState& moduleState, const FunctionType sig)
+{
+ if(sig.params().size() || sig.results().size() > 1)
+ {
+ IndexedBlockType result;
+ result.format = IndexedBlockType::functionType;
+ result.index = moduleState.functionTypeMap.getOrAdd(sig, moduleState.module.types.size());
+ if(result.index == moduleState.module.types.size())
+ { moduleState.module.types.push_back(sig); }
+ return result;
+ }
+ else
+ {
+ return sig.results().size() == 1
+ ? IndexedBlockType{IndexedBlockType::Format::oneResult, {sig.results()[0]}}
+ : IndexedBlockType{IndexedBlockType::Format::noParametersOrResult, {}};
+ }
+}
+
+void FunctionState::generateFunction(RandomStream& random)
+{
+ controlStack.push_back({ControlContext::Type::function,
+ 0,
+ functionType.results(),
+ functionType.results(),
+ TypeTuple()});
+
+ std::vector<OperatorEmitFunc> validOpEmitters;
+ while(controlStack.size())
+ {
+ const ControlContext& controlContext = controlStack.back();
+ allowStackGrowth
+ = stack.size() - controlContext.outerStackSize <= softMaxStackDepthInControlContext
+ && numInstructions < softMaxInstructionsInFunction;
+
+ validOpEmitters.clear();
+
+ if(stack.size() < controlContext.outerStackSize + controlContext.results.size())
+ {
+ // If there aren't enough params on the stack to end the current control context, allow
+ // instructions that grow the stack even if we've exceeded the
+ allowStackGrowth = true;
+ }
+ else if(stack.size() == controlContext.outerStackSize + controlContext.results.size())
+ {
+ // Check whether the current state has valid results to end the current control context.
+ if(doesStackMatchParams(controlContext.results))
+ {
+ if(controlContext.type == ControlContext::Type::ifThen)
+ {
+ // Enter an if-else clause.
+ validOpEmitters.push_back([this](RandomStream& random) {
+ // Emit the else operator.
+ codeStream.else_();
+
+ stack.resize(controlStack.back().outerStackSize);
+ for(ValueType elseParam : controlStack.back().elseParams)
+ { stack.push_back(elseParam); }
+
+ // Change the current control context type to an else clause.
+ controlStack.back().type = ControlContext::Type::ifElse;
+ });
+ }
+
+ if(controlContext.type != ControlContext::Type::try_
+ && (controlContext.type != ControlContext::Type::ifThen
+ || controlContext.elseParams == controlContext.results))
+ {
+ // End the current control structure.
+ validOpEmitters.push_back([this](RandomStream& random) {
+ // Emit the end operator.
+ codeStream.end();
+
+ // Push the control context's results on the stack.
+ stack.resize(controlStack.back().outerStackSize);
+ const TypeTuple& results = controlStack.back().results;
+ stack.insert(stack.end(), results.begin(), results.end());
+
+ // Pop the control stack.
+ controlStack.pop_back();
+ });
+ }
+ }
+ }
+
+ // Build a list of the non-parametric operators that are valid given the module and the
+ // current state of the stack.
+ for(Uptr opIndex = 0; opIndex < numNonParametricOps; ++opIndex)
+ {
+ const OperatorInfo& opInfo = operatorInfos[opIndex];
+
+ opInfo.getValidEmitters(*this, validOpEmitters);
+ }
+
+ // Build a list of the parametric operators that are valid given the current state of
+ // the stack.
+
+ for(Uptr localIndex = 0; localIndex < numLocals; ++localIndex)
+ {
+ const ValueType localType
+ = localIndex < functionType.params().size()
+ ? functionType.params()[localIndex]
+ : functionDef
+ .nonParameterLocalTypes[localIndex - functionType.params().size()];
+
+ if(stack.size() > controlContext.outerStackSize && isSubtype(stack.back(), localType))
+ {
+ // local.set
+ validOpEmitters.push_back([this, localIndex](RandomStream& random) {
+ codeStream.local_set({localIndex});
+ stack.pop_back();
+ });
+
+ // local.tee
+ if(allowStackGrowth)
+ {
+ validOpEmitters.push_back([this, localIndex](RandomStream& random) {
+ codeStream.local_tee({localIndex});
+ });
+ }
+ }
+
+ // local.get
+ if(allowStackGrowth)
+ {
+ validOpEmitters.push_back([this, localIndex, localType](RandomStream& random) {
+ codeStream.local_get({localIndex});
+ stack.push_back(localType);
+ });
+ }
+ }
+
+ for(Uptr globalIndex = 0; globalIndex < module.globals.size(); ++globalIndex)
+ {
+ const GlobalType globalType = module.globals.getType(globalIndex);
+
+ if(stack.size() > controlStack.back().outerStackSize
+ && isSubtype(stack.back(), globalType.valueType) && globalType.isMutable)
+ {
+ // global.set
+ validOpEmitters.push_back([this, globalIndex](RandomStream& random) {
+ codeStream.global_set({globalIndex});
+ stack.pop_back();
+ });
+ }
+
+ if(allowStackGrowth)
+ {
+ // global.get
+ validOpEmitters.push_back([this, globalIndex, globalType](RandomStream& random) {
+ codeStream.global_get({globalIndex});
+ stack.push_back(globalType.valueType);
+ });
+ }
+ }
+
+ for(Uptr tableIndex = 0; tableIndex < module.tables.size(); ++tableIndex)
+ {
+ const TableType& tableType = module.tables.getType(tableIndex);
+
+ // TODO: table.grow and table.fill
+
+ if(stack.size() - controlStack.back().outerStackSize >= 2
+ && stack[stack.size() - 2] == asValueType(tableType.indexType)
+ && isSubtype(stack.back(), asValueType(tableType.elementType)))
+ {
+ // table.set
+ validOpEmitters.push_back([this, tableIndex](RandomStream& random) {
+ codeStream.table_set({tableIndex});
+ stack.resize(stack.size() - 2);
+ });
+ }
+
+ if(stack.size() > controlStack.back().outerStackSize
+ && stack.back() == asValueType(tableType.indexType))
+ {
+ // table.get
+ validOpEmitters.push_back([this, tableIndex, tableType](RandomStream& random) {
+ codeStream.table_get({tableIndex});
+ stack.pop_back();
+ stack.push_back(asValueType(tableType.elementType));
+ });
+
+ if(tableType.elementType == ReferenceType::funcref)
+ {
+ // call_indirect
+ for(Uptr typeIndex = 0; typeIndex < module.types.size(); ++typeIndex)
+ {
+ const FunctionType calleeType = module.types[typeIndex];
+ const TypeTuple params = calleeType.params();
+ const TypeTuple results = calleeType.results();
+
+ // If the random stream has run out of entropy, only consider operators
+ // that result in fewer operands on the stack.
+ if(!allowStackGrowth && results.size() >= params.size() + 1) { continue; }
+
+ // Ensure the stack has enough values for the operator's parameters.
+ if(params.size() + 1 > stack.size() - controlStack.back().outerStackSize)
+ { continue; }
+
+ // Check whether the top of the stack is compatible with function's
+ // parameters.
+ if(doesStackMatchParams(params, /*offsetFromTopOfStack*/ 1))
+ {
+ validOpEmitters.push_back(
+ [this, calleeType, typeIndex, tableIndex](RandomStream& random) {
+ codeStream.call_indirect({{typeIndex}, tableIndex});
+
+ // Remove the function's parameters and the table index from
+ // the top of the stack.
+ stack.resize(stack.size() - calleeType.params().size() - 1);
+
+ // Push the function's results onto the stack.
+ for(ValueType result : calleeType.results())
+ { stack.push_back(result); }
+ });
+ }
+ }
+ }
+ }
+ }
+
+ if(allowStackGrowth)
+ {
+ const Uptr maxArity = stack.size() - controlStack.back().outerStackSize;
+ for(Uptr arity = 0; arity < maxArity; ++arity)
+ {
+ // Enter a block control structure.
+ validOpEmitters.push_back([this, arity](RandomStream& random) {
+ const FunctionType blockSig = generateBlockSig(
+ random, TypeTuple(stack.data() + stack.size() - arity, arity));
+ stack.resize(stack.size() - arity);
+ stack.insert(stack.end(), blockSig.params().begin(), blockSig.params().end());
+ codeStream.block({getIndexedBlockType(moduleState, blockSig)});
+ controlStack.push_back({ControlContext::Type::block,
+ stack.size() - arity,
+ blockSig.results(),
+ blockSig.results(),
+ TypeTuple()});
+ });
+
+ // Enter a loop control structure.
+ validOpEmitters.push_back([this, arity](RandomStream& random) {
+ const FunctionType loopSig = generateBlockSig(
+ random, TypeTuple(stack.data() + stack.size() - arity, arity));
+ stack.resize(stack.size() - arity);
+ stack.insert(stack.end(), loopSig.params().begin(), loopSig.params().end());
+ codeStream.loop({getIndexedBlockType(moduleState, loopSig)});
+ controlStack.push_back({ControlContext::Type::loop,
+ stack.size() - arity,
+ loopSig.params(),
+ loopSig.results(),
+ TypeTuple()});
+ });
+ }
+ }
+
+ // Enter an if control structure.
+ if(allowStackGrowth && stack.size() > controlStack.back().outerStackSize
+ && stack.back() == ValueType::i32)
+ {
+ const Uptr maxArity = stack.size() - controlStack.back().outerStackSize - 1;
+ for(Uptr arity = 0; arity < maxArity; ++arity)
+ {
+ validOpEmitters.push_back([this, arity](RandomStream& random) {
+ const FunctionType ifSig = generateBlockSig(
+ random, TypeTuple(stack.data() + stack.size() - arity - 1, arity));
+ stack.resize(stack.size() - arity - 1);
+ stack.insert(stack.end(), ifSig.params().begin(), ifSig.params().end());
+ codeStream.if_({getIndexedBlockType(moduleState, ifSig)});
+ controlStack.push_back({ControlContext::Type::ifThen,
+ stack.size() - arity,
+ ifSig.results(),
+ ifSig.results(),
+ ifSig.params()});
+ });
+ }
+ }
+
+ // TODO: try/catch/catch_all/throw/rethrow
+
+ for(Uptr branchTargetDepth = 0; branchTargetDepth < controlStack.size();
+ ++branchTargetDepth)
+ {
+ const ControlContext& targetContext
+ = controlStack[controlStack.size() - branchTargetDepth - 1];
+ const TypeTuple params = targetContext.params;
+
+ if(params.size() > stack.size() - controlStack.back().outerStackSize) { continue; }
+
+ // Check whether the top of the stack is compatible with branch target's parameters.
+ if(doesStackMatchParams(params))
+ {
+ // br
+ validOpEmitters.push_back([this, branchTargetDepth](RandomStream& random) {
+ codeStream.br({U32(branchTargetDepth)});
+ emitControlEnd();
+ });
+
+ if(branchTargetDepth == controlStack.size() - 1)
+ {
+ // return
+ validOpEmitters.push_back([this](RandomStream& random) {
+ codeStream.return_();
+ emitControlEnd();
+ });
+ }
+ }
+ }
+
+ // br_if
+ if(stack.size() > controlStack.back().outerStackSize && stack.back() == ValueType::i32)
+ {
+ for(Uptr branchTargetDepth = 0; branchTargetDepth < controlStack.size();
+ ++branchTargetDepth)
+ {
+ const ControlContext& targetContext
+ = controlStack[controlStack.size() - branchTargetDepth - 1];
+ const TypeTuple params = targetContext.params;
+
+ if(params.size() + 1 > stack.size() - controlStack.back().outerStackSize)
+ { continue; }
+
+ // Check whether the top of the stack is compatible with branch target's parameters.
+ if(doesStackMatchParams(params, /*offsetFromTopOfStack*/ 1))
+ {
+ validOpEmitters.push_back([this, branchTargetDepth](RandomStream& random) {
+ stack.pop_back();
+ codeStream.br_if({U32(branchTargetDepth)});
+ });
+ }
+ }
+ }
+
+ // unreachable
+ validOpEmitters.push_back([this](RandomStream& random) {
+ codeStream.unreachable();
+ emitControlEnd();
+ });
+
+ // TODO: br_table
+
+ if(stack.size() - controlStack.back().outerStackSize >= 3 && stack.back() == ValueType::i32)
+ {
+ const ValueType trueValueType = stack[stack.size() - 3];
+ const ValueType falseValueType = stack[stack.size() - 2];
+ if(trueValueType == falseValueType)
+ {
+ const ValueType joinType = trueValueType;
+ if(isReferenceType(joinType))
+ {
+ validOpEmitters.push_back([this, joinType](RandomStream& random) {
+ stack.resize(stack.size() - 3);
+ stack.push_back(joinType);
+ codeStream.select({joinType});
+ });
+ }
+ else
+ {
+ // Non-typed select
+ validOpEmitters.push_back([this, joinType](RandomStream& random) {
+ stack.resize(stack.size() - 3);
+ stack.push_back(joinType);
+ codeStream.select({ValueType::any});
+ });
+ }
+ }
+ }
+
+ if(stack.size() > controlStack.back().outerStackSize)
+ {
+ // drop
+ validOpEmitters.push_back([this](RandomStream& random) {
+ codeStream.drop();
+ stack.pop_back();
+ });
+ }
+
+ // call
+ for(Uptr functionIndex = 0; functionIndex < module.functions.size(); ++functionIndex)
+ {
+ const FunctionType calleeType
+ = module.types[module.functions.getType(functionIndex).index];
+ const TypeTuple params = calleeType.params();
+ const TypeTuple results = calleeType.results();
+
+ // If the random stream has run out of entropy, only consider operators that result
+ // in fewer operands on the stack.
+ if(!allowStackGrowth && results.size() >= params.size()) { continue; }
+
+ // Ensure the stack has enough values for the operator's parameters.
+ if(params.size() > stack.size() - controlStack.back().outerStackSize) { continue; }
+
+ // Check whether the top of the stack is compatible with function's parameters.
+ if(doesStackMatchParams(params))
+ {
+ validOpEmitters.push_back([this, functionIndex](RandomStream& random) {
+ const FunctionType calleeType
+ = moduleState.module
+ .types[moduleState.module.functions.getType(functionIndex).index];
+
+ codeStream.call({functionIndex});
+
+ // Remove the function's parameters from the top of the stack.
+ stack.resize(stack.size() - calleeType.params().size());
+
+ // Push the function's results onto the stack.
+ for(ValueType result : calleeType.results()) { stack.push_back(result); }
+ });
+ }
+ }
+
+ // ref.null
+ validOpEmitters.push_back([this](RandomStream& random) {
+ const ReferenceType nullReferenceType = generateRefType(random);
+ codeStream.ref_null({nullReferenceType});
+ stack.push_back(asValueType(nullReferenceType));
+ });
+
+ // ref.is_null
+ if(stack.size() > controlStack.back().outerStackSize && isReferenceType(stack.back()))
+ {
+ validOpEmitters.push_back([this](RandomStream& random) {
+ codeStream.ref_is_null();
+ stack.back() = ValueType::i32;
+ });
+ }
+
+ // Emit a random operator.
+ WAVM_ASSERT(validOpEmitters.size());
+ const Uptr randomOpIndex = random.get(validOpEmitters.size() - 1);
+ validOpEmitters[randomOpIndex](random);
+ ++numInstructions;
+ };
+
+ codeStream.finishValidation();
+
+ functionDef.code = codeByteStream.getBytes();
+};
+
+static InitializerExpression generateInitializerExpression(Module& module,
+ RandomStream& random,
+ ValueType type)
+{
+ switch(type)
+ {
+ case ValueType::i32: return InitializerExpression(I32(random.get(UINT32_MAX)));
+ case ValueType::i64: return InitializerExpression(I64(random.get(UINT64_MAX)));
+ case ValueType::f32: return InitializerExpression(F32(random.get(UINT32_MAX)));
+ case ValueType::f64: return InitializerExpression(F64(random.get(UINT64_MAX)));
+ case ValueType::v128: {
+ V128 v128;
+ v128.u64x2[0] = random.get(UINT64_MAX);
+ v128.u64x2[1] = random.get(UINT64_MAX);
+ return InitializerExpression(v128);
+ }
+ case ValueType::externref: {
+ return InitializerExpression(ReferenceType::externref);
+ }
+ case ValueType::funcref: {
+ const Uptr functionIndex = random.get(module.functions.size());
+ return functionIndex == module.functions.size()
+ ? InitializerExpression(ReferenceType::funcref)
+ : InitializerExpression(InitializerExpression::Type::ref_func, functionIndex);
+ }
+
+ case ValueType::none:
+ case ValueType::any:
+ default: WAVM_UNREACHABLE();
+ }
+}
+
+void IR::generateValidModule(Module& module, RandomStream& random)
+{
+ ModuleState moduleState(module, random);
+
+ WAVM_ASSERT(module.featureSpec.simd);
+ WAVM_ASSERT(module.featureSpec.atomics);
+ WAVM_ASSERT(module.featureSpec.exceptionHandling);
+ WAVM_ASSERT(module.featureSpec.multipleResultsAndBlockParams);
+ WAVM_ASSERT(module.featureSpec.bulkMemoryOperations);
+ WAVM_ASSERT(module.featureSpec.referenceTypes);
+ WAVM_ASSERT(module.featureSpec.sharedTables);
+
+ // Generate some memories.
+ const Uptr numMemories = random.get(3);
+ for(Uptr memoryIndex = 0; memoryIndex < numMemories; ++memoryIndex)
+ {
+ MemoryType type;
+ type.isShared = !!random.get(1);
+ type.indexType = generateIndexType(random);
+ type.size.min = random.get<U64>(100);
+ type.size.max = type.size.min + random.get<U64>(IR::maxMemory32Pages - type.size.min);
+
+ if(random.get(1)) { module.memories.defs.push_back({type}); }
+ else
+ {
+ module.imports.push_back({ExternKind::memory, module.memories.imports.size()});
+ module.memories.imports.push_back({type, "env", "memory"});
+ }
+ }
+
+ // Generate some tables.
+ const Uptr numTables = random.get(3);
+ for(Uptr tableIndex = 0; tableIndex < numTables; ++tableIndex)
+ {
+ TableType type;
+ type.elementType = generateRefType(random);
+ type.isShared = !!random.get(1);
+ type.indexType = generateIndexType(random);
+ type.size.min = random.get<U64>(100);
+ type.size.max = IR::maxTable32Elems;
+
+ if(random.get(1)) { module.tables.defs.push_back({type}); }
+ else
+ {
+ module.imports.push_back({ExternKind::table, module.tables.imports.size()});
+ module.tables.imports.push_back({type, "env", "table"});
+ }
+ }
+
+ // Generate some globals.
+ const Uptr numGlobals = random.get(10);
+ for(Uptr globalIndex = 0; globalIndex < numGlobals; ++globalIndex)
+ {
+ const ValueType globalValueType = generateValueType(random);
+
+ const bool isMutable = random.get(1);
+ const GlobalType globalType{globalValueType, isMutable};
+ if(random.get(1))
+ {
+ module.imports.push_back({ExternKind::global, module.globals.imports.size()});
+ module.globals.imports.push_back(
+ {globalType, "env", "global" + std::to_string(globalIndex)});
+ }
+ else
+ {
+ InitializerExpression initializer
+ = generateInitializerExpression(module, random, globalValueType);
+ module.globals.defs.push_back({globalType, initializer});
+ }
+ };
+
+ // Generate some data segments.
+ Uptr numDataSegments = random.get(2);
+ for(Uptr segmentIndex = 0; segmentIndex < numDataSegments; ++segmentIndex)
+ {
+ const Uptr numSegmentBytes = random.get(100);
+ std::vector<U8> bytes;
+ for(Uptr byteIndex = 0; byteIndex < numSegmentBytes; ++byteIndex)
+ { bytes.push_back(random.get<U8>(255)); }
+ if(!module.memories.size() || random.get(1))
+ {
+ module.dataSegments.push_back(
+ {false, UINTPTR_MAX, {}, std::make_shared<std::vector<U8>>(std::move(bytes))});
+ }
+ else
+ {
+ const Uptr memoryIndex = random.get(module.memories.size() - 1);
+ const MemoryType& memoryType = module.memories.getType(memoryIndex);
+ module.dataSegments.push_back(
+ {true,
+ memoryIndex,
+ generateInitializerExpression(module, random, asValueType(memoryType.indexType)),
+ std::make_shared<std::vector<U8>>(std::move(bytes))});
+ }
+ };
+
+ // Create some function imports/defs
+ const Uptr numFunctions = 1 + random.get(4);
+ while(module.functions.size() < numFunctions)
+ {
+ // Generate a signature.
+ FunctionType functionType = generateFunctionType(random);
+ const Uptr functionTypeIndex
+ = moduleState.functionTypeMap.getOrAdd(functionType, module.types.size());
+ if(functionTypeIndex == module.types.size()) { module.types.push_back(functionType); }
+
+ if(random.get(1))
+ {
+ // Generate a function import.
+ module.imports.push_back({ExternKind::function, module.functions.imports.size()});
+ module.functions.imports.push_back(
+ {{functionTypeIndex},
+ "env",
+ "func" + std::to_string(module.functions.imports.size())});
+ }
+ else
+ {
+ // Generate a FunctionDef, but don't generate its code until we have generated
+ // all declarations.
+ FunctionDef functionDef;
+ functionDef.type.index = functionTypeIndex;
+
+ // Generate locals.
+ const Uptr numNonParameterLocals = random.get(4);
+ for(Uptr localIndex = 0; localIndex < numNonParameterLocals; ++localIndex)
+ { functionDef.nonParameterLocalTypes.push_back(generateValueType(random)); }
+
+ module.functions.defs.push_back(std::move(functionDef));
+ }
+ };
+
+ // Generate some elem segments.
+ HashSet<Uptr> declaredFunctionIndexSet;
+ Uptr numElemSegments = random.get(2);
+ for(Uptr segmentIndex = 0; segmentIndex < numElemSegments; ++segmentIndex)
+ {
+ auto contents = std::make_shared<ElemSegment::Contents>();
+ contents->encoding
+ = random.get(1) ? ElemSegment::Encoding::expr : ElemSegment::Encoding::index;
+
+ ReferenceType segmentElemType;
+ const Uptr numSegmentElements = random.get(100);
+ switch(contents->encoding)
+ {
+ case ElemSegment::Encoding::expr: {
+ segmentElemType = contents->elemType = generateRefType(random);
+ for(Uptr index = 0; index < numSegmentElements; ++index)
+ {
+ switch(contents->elemType)
+ {
+ case ReferenceType::externref: {
+ contents->elemExprs.push_back(ElemExpr(ReferenceType::externref));
+ break;
+ }
+ case ReferenceType::funcref: {
+ const Uptr functionIndex = random.get(module.functions.size());
+ if(functionIndex == module.functions.size())
+ { contents->elemExprs.push_back(ElemExpr(ReferenceType::funcref)); }
+ else
+ {
+ contents->elemExprs.push_back(
+ ElemExpr(ElemExpr::Type::ref_func, functionIndex));
+ if(declaredFunctionIndexSet.add(functionIndex))
+ { moduleState.declaredFunctionIndices.push_back(functionIndex); }
+ }
+ break;
+ }
+
+ case ReferenceType::none:
+ default: WAVM_UNREACHABLE();
+ };
+ }
+ break;
+ }
+ case ElemSegment::Encoding::index: {
+ contents->externKind = generateExternKind(random);
+ segmentElemType = asReferenceType(contents->externKind);
+ for(Uptr index = 0; index < numSegmentElements; ++index)
+ {
+ switch(contents->externKind)
+ {
+ case ExternKind::function:
+ if(module.functions.size())
+ {
+ const Uptr functionIndex = random.get(module.functions.size() - 1);
+ contents->elemIndices.push_back(functionIndex);
+ if(declaredFunctionIndexSet.add(functionIndex))
+ { moduleState.declaredFunctionIndices.push_back(functionIndex); }
+ }
+ break;
+ case ExternKind::table:
+ if(module.tables.size())
+ { contents->elemIndices.push_back(random.get(module.tables.size() - 1)); }
+ break;
+ case ExternKind::memory:
+ if(module.memories.size())
+ { contents->elemIndices.push_back(random.get(module.memories.size() - 1)); }
+ break;
+ case ExternKind::global:
+ if(module.globals.size())
+ { contents->elemIndices.push_back(random.get(module.globals.size() - 1)); }
+ break;
+ case ExternKind::exceptionType:
+ if(module.exceptionTypes.size())
+ {
+ contents->elemIndices.push_back(
+ random.get(module.exceptionTypes.size() - 1));
+ }
+ break;
+
+ case ExternKind::invalid:
+ default: WAVM_UNREACHABLE();
+ };
+ }
+ break;
+ }
+ default: WAVM_UNREACHABLE();
+ };
+
+ std::vector<Uptr> validTableIndices;
+ for(Uptr tableIndex = 0; tableIndex < module.tables.size(); ++tableIndex)
+ {
+ const ReferenceType tableElemType = module.tables.getType(tableIndex).elementType;
+ if(isSubtype(segmentElemType, tableElemType))
+ { validTableIndices.push_back(tableIndex); }
+ }
+
+ ElemSegment::Type elemSegmentType = ElemSegment::Type::passive;
+ if(!validTableIndices.size())
+ {
+ elemSegmentType
+ = random.get(1) ? ElemSegment::Type::passive : ElemSegment::Type::declared;
+ }
+ else
+ {
+ switch(random.get(2))
+ {
+ case 0: elemSegmentType = ElemSegment::Type::passive; break;
+ case 1: elemSegmentType = ElemSegment::Type::active; break;
+ case 2: elemSegmentType = ElemSegment::Type::declared; break;
+ default: WAVM_UNREACHABLE();
+ };
+ }
+
+ switch(elemSegmentType)
+ {
+ case ElemSegment::Type::passive: {
+ module.elemSegments.push_back({ElemSegment::Type::passive,
+ UINTPTR_MAX,
+ InitializerExpression(),
+ std::move(contents)});
+ break;
+ }
+ case ElemSegment::Type::active: {
+ const Uptr validTableIndex = random.get(validTableIndices.size() - 1);
+ const TableType& tableType = module.tables.getType(validTableIndices[validTableIndex]);
+ module.elemSegments.push_back(
+ {ElemSegment::Type::active,
+ validTableIndices[validTableIndex],
+ generateInitializerExpression(module, random, asValueType(tableType.indexType)),
+ std::move(contents)});
+ break;
+ }
+ case ElemSegment::Type::declared: {
+ module.elemSegments.push_back({ElemSegment::Type::declared,
+ UINTPTR_MAX,
+ InitializerExpression(),
+ std::move(contents)});
+ break;
+ }
+ default: WAVM_UNREACHABLE();
+ };
+
+ // Precalculate a list of element-table pairs that are valid for a table.init
+ for(Uptr tableIndex = 0; tableIndex < module.tables.size(); ++tableIndex)
+ {
+ if(isSubtype(segmentElemType, module.tables.getType(tableIndex).elementType))
+ {
+ moduleState.validElemSegmentAndTableImms.push_back(
+ ElemSegmentAndTableImm{segmentIndex, tableIndex});
+ }
+ }
+ };
+
+ std::shared_ptr<ModuleValidationState> moduleValidationState
+ = createModuleValidationState(module);
+
+ validatePreCodeSections(*moduleValidationState);
+
+ // Generate a few functions.
+ for(FunctionDef& functionDef : module.functions.defs)
+ {
+ FunctionState functionState(moduleState, *moduleValidationState, functionDef);
+ functionState.generateFunction(random);
+ }
+
+ // Generating functions might have added some block types, so revalidate the type section.
+ validateTypes(*moduleValidationState);
+
+ validatePostCodeSections(*moduleValidationState);
+}