diff options
Diffstat (limited to 'src/tools/fuzzing.h')
-rw-r--r-- | src/tools/fuzzing.h | 1554 |
1 files changed, 1042 insertions, 512 deletions
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index b7056f964..f5b548ec6 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -25,12 +25,14 @@ high chance for set at start of loop high chance of a tee in that case => loop var */ -#include <wasm-builder.h> +#include "ir/memory-utils.h" #include <ir/find_all.h> #include <ir/literal-utils.h> #include <ir/manipulation.h> -#include "ir/memory-utils.h" #include <ir/utils.h> +#include <support/file.h> +#include <tools/optimization-options.h> +#include <wasm-builder.h> namespace wasm { @@ -38,32 +40,35 @@ namespace wasm { // evaluation, avoiding UB struct ThreeArgs { - Expression *a; - Expression *b; - Expression *c; + Expression* a; + Expression* b; + Expression* c; }; struct UnaryArgs { UnaryOp a; - Expression *b; + Expression* b; }; struct BinaryArgs { BinaryOp a; - Expression *b; - Expression *c; + Expression* b; + Expression* c; }; // main reader class TranslateToFuzzReader { public: - TranslateToFuzzReader(Module& wasm, std::string& filename) : wasm(wasm), builder(wasm) { - auto input(read_file<std::vector<char>>(filename, Flags::Binary, Flags::Release)); + TranslateToFuzzReader(Module& wasm, std::string& filename) + : wasm(wasm), builder(wasm) { + auto input( + read_file<std::vector<char>>(filename, Flags::Binary, Flags::Release)); readData(input); } - TranslateToFuzzReader(Module& wasm, std::vector<char> input) : wasm(wasm), builder(wasm) { + TranslateToFuzzReader(Module& wasm, std::vector<char> input) + : wasm(wasm), builder(wasm) { readData(input); } @@ -80,38 +85,91 @@ public: options.passOptions.shrinkLevel = upTo(4); break; } - case 5: options.passes.push_back("coalesce-locals"); break; - case 6: options.passes.push_back("code-pushing"); break; - case 7: options.passes.push_back("code-folding"); break; - case 8: options.passes.push_back("dce"); break; - case 9: options.passes.push_back("duplicate-function-elimination"); break; - case 10: options.passes.push_back("flatten"); break; - case 11: options.passes.push_back("inlining"); break; - case 12: options.passes.push_back("inlining-optimizing"); break; - case 13: options.passes.push_back("local-cse"); break; - case 14: options.passes.push_back("memory-packing"); break; - case 15: options.passes.push_back("merge-blocks"); break; - case 16: options.passes.push_back("optimize-instructions"); break; - case 17: options.passes.push_back("pick-load-signs"); break; - case 18: options.passes.push_back("precompute"); break; - case 19: options.passes.push_back("precompute-propagate"); break; - case 20: options.passes.push_back("remove-unused-brs"); break; - case 21: options.passes.push_back("remove-unused-module-elements"); break; - case 22: options.passes.push_back("remove-unused-names"); break; - case 23: options.passes.push_back("reorder-functions"); break; - case 24: options.passes.push_back("reorder-locals"); break; + case 5: + options.passes.push_back("coalesce-locals"); + break; + case 6: + options.passes.push_back("code-pushing"); + break; + case 7: + options.passes.push_back("code-folding"); + break; + case 8: + options.passes.push_back("dce"); + break; + case 9: + options.passes.push_back("duplicate-function-elimination"); + break; + case 10: + options.passes.push_back("flatten"); + break; + case 11: + options.passes.push_back("inlining"); + break; + case 12: + options.passes.push_back("inlining-optimizing"); + break; + case 13: + options.passes.push_back("local-cse"); + break; + case 14: + options.passes.push_back("memory-packing"); + break; + case 15: + options.passes.push_back("merge-blocks"); + break; + case 16: + options.passes.push_back("optimize-instructions"); + break; + case 17: + options.passes.push_back("pick-load-signs"); + break; + case 18: + options.passes.push_back("precompute"); + break; + case 19: + options.passes.push_back("precompute-propagate"); + break; + case 20: + options.passes.push_back("remove-unused-brs"); + break; + case 21: + options.passes.push_back("remove-unused-module-elements"); + break; + case 22: + options.passes.push_back("remove-unused-names"); + break; + case 23: + options.passes.push_back("reorder-functions"); + break; + case 24: + options.passes.push_back("reorder-locals"); + break; case 25: { options.passes.push_back("flatten"); options.passes.push_back("rereloop"); break; } - case 26: options.passes.push_back("simplify-locals"); break; - case 27: options.passes.push_back("simplify-locals-notee"); break; - case 28: options.passes.push_back("simplify-locals-nostructure"); break; - case 29: options.passes.push_back("simplify-locals-notee-nostructure"); break; - case 30: options.passes.push_back("ssa"); break; - case 31: options.passes.push_back("vacuum"); break; - default: WASM_UNREACHABLE(); + case 26: + options.passes.push_back("simplify-locals"); + break; + case 27: + options.passes.push_back("simplify-locals-notee"); + break; + case 28: + options.passes.push_back("simplify-locals-nostructure"); + break; + case 29: + options.passes.push_back("simplify-locals-notee-nostructure"); + break; + case 30: + options.passes.push_back("ssa"); + break; + case 31: + options.passes.push_back("vacuum"); + break; + default: + WASM_UNREACHABLE(); } } if (oneIn(2)) { @@ -124,13 +182,9 @@ public: std::cout << "shrink level: " << options.passOptions.shrinkLevel << '\n'; } - void setAllowNaNs(bool allowNaNs_) { - allowNaNs = allowNaNs_; - } + void setAllowNaNs(bool allowNaNs_) { allowNaNs = allowNaNs_; } - void setAllowMemory(bool allowMemory_) { - allowMemory = allowMemory_; - } + void setAllowMemory(bool allowMemory_) { allowMemory = allowMemory_; } void build() { if (allowMemory) { @@ -157,8 +211,10 @@ private: Module& wasm; Builder builder; std::vector<char> bytes; // the input bytes - size_t pos; // the position in the input - bool finishedInput; // whether we already cycled through all the input (if so, we should try to finish things off) + size_t pos; // the position in the input + // whether we already cycled through all the input (if so, we should try to + // finish things off) + bool finishedInput; // The maximum amount of params to each function. static const int MAX_PARAMS = 10; @@ -176,8 +232,8 @@ private: static const int BLOCK_FACTOR = 5; // the memory that we use, a small portion so that we have a good chance of - // looking at writes (we also look outside of this region with small probability) - // this should be a power of 2 + // looking at writes (we also look outside of this region with small + // probability) this should be a power of 2 static const int USABLE_MEMORY = 16; // the number of runtime iterations (function calls, loop backbranches) we @@ -239,13 +295,9 @@ private: return temp | uint64_t(get32()); } - float getFloat() { - return Literal(get32()).reinterpretf32(); - } + float getFloat() { return Literal(get32()).reinterpretf32(); } - double getDouble() { - return Literal(get64()).reinterpretf64(); - } + double getDouble() { return Literal(get64()).reinterpretf64(); } void setupMemory() { // Add memory itself @@ -274,10 +326,12 @@ private: auto num = upTo(USABLE_MEMORY * 2); for (size_t i = 0; i < num; i++) { auto value = upTo(512); - wasm.memory.segments[0].data.push_back(value >= 256 ? 0 : (value & 0xff)); + wasm.memory.segments[0].data.push_back(value >= 256 ? 0 + : (value & 0xff)); } } - // Add memory hasher helper (for the hash, see hash.h). The function looks like: + // Add memory hasher helper (for the hash, see hash.h). The function looks + // like: // function hashMemory() { // hash = 5381; // hash = ((hash << 5) + hash) ^ mem[0]; @@ -287,31 +341,28 @@ private: // } std::vector<Expression*> contents; contents.push_back( - builder.makeSetLocal(0, builder.makeConst(Literal(uint32_t(5381)))) - ); + builder.makeSetLocal(0, builder.makeConst(Literal(uint32_t(5381))))); for (Index i = 0; i < USABLE_MEMORY; i++) { - contents.push_back( - builder.makeSetLocal(0, - builder.makeBinary(XorInt32, - builder.makeBinary(AddInt32, - builder.makeBinary(ShlInt32, - builder.makeGetLocal(0, i32), - builder.makeConst(Literal(uint32_t(5))) - ), - builder.makeGetLocal(0, i32) - ), - builder.makeLoad(1, false, i, 1, builder.makeConst(Literal(uint32_t(0))), i32) - ) - ) - ); - } - contents.push_back( - builder.makeGetLocal(0, i32) - ); + contents.push_back(builder.makeSetLocal( + 0, + builder.makeBinary( + XorInt32, + builder.makeBinary( + AddInt32, + builder.makeBinary(ShlInt32, + builder.makeGetLocal(0, i32), + builder.makeConst(Literal(uint32_t(5)))), + builder.makeGetLocal(0, i32)), + builder.makeLoad( + 1, false, i, 1, builder.makeConst(Literal(uint32_t(0))), i32)))); + } + contents.push_back(builder.makeGetLocal(0, i32)); auto* body = builder.makeBlock(contents); - auto* hasher = wasm.addFunction(builder.makeFunction("hashMemory", std::vector<Type>{}, i32, { i32 }, body)); + auto* hasher = wasm.addFunction(builder.makeFunction( + "hashMemory", std::vector<Type>{}, i32, {i32}, body)); hasher->type = ensureFunctionType(getSig(hasher), &wasm)->name; - wasm.addExport(builder.makeExport(hasher->name, hasher->name, ExternalKind::Function)); + wasm.addExport( + builder.makeExport(hasher->name, hasher->name, ExternalKind::Function)); } void setupTable() { @@ -323,15 +374,14 @@ private: void setupGlobals() { size_t index = 0; - for (auto type : { i32, i64, f32, f64 }) { + for (auto type : {i32, i64, f32, f64}) { auto num = upTo(3); for (size_t i = 0; i < num; i++) { - auto* glob = builder.makeGlobal( - std::string("global$") + std::to_string(index++), - type, - makeConst(type), - Builder::Mutable - ); + auto* glob = + builder.makeGlobal(std::string("global$") + std::to_string(index++), + type, + makeConst(type), + Builder::Mutable); wasm.addGlobal(glob); globalsByType[type].push_back(glob->name); } @@ -340,26 +390,25 @@ private: void finalizeTable() { wasm.table.initial = wasm.table.segments[0].data.size(); - wasm.table.max = oneIn(2) ? Address(Table::kUnlimitedSize) : wasm.table.initial; + wasm.table.max = + oneIn(2) ? Address(Table::kUnlimitedSize) : wasm.table.initial; } const Name HANG_LIMIT_GLOBAL = "hangLimit"; void addHangLimitSupport() { - auto* glob = builder.makeGlobal( - HANG_LIMIT_GLOBAL, - i32, - builder.makeConst(Literal(int32_t(HANG_LIMIT))), - Builder::Mutable - ); + auto* glob = + builder.makeGlobal(HANG_LIMIT_GLOBAL, + i32, + builder.makeConst(Literal(int32_t(HANG_LIMIT))), + Builder::Mutable); wasm.addGlobal(glob); auto* func = new Function; func->name = "hangLimitInitializer"; func->result = none; - func->body = builder.makeSetGlobal(glob->name, - builder.makeConst(Literal(int32_t(HANG_LIMIT))) - ); + func->body = builder.makeSetGlobal( + glob->name, builder.makeConst(Literal(int32_t(HANG_LIMIT)))); wasm.addFunction(func); auto* export_ = new Export; @@ -370,7 +419,7 @@ private: } void addImportLoggingSupport() { - for (auto type : { i32, i64, f32, f64 }) { + for (auto type : {i32, i64, f32, f64}) { auto* func = new Function; Name name = std::string("log-") + printType(type); func->name = name; @@ -386,21 +435,14 @@ private: Expression* makeHangLimitCheck() { return builder.makeSequence( builder.makeIf( - builder.makeUnary( - UnaryOp::EqZInt32, - builder.makeGetGlobal(HANG_LIMIT_GLOBAL, i32) - ), - makeTrivial(unreachable) - ), + builder.makeUnary(UnaryOp::EqZInt32, + builder.makeGetGlobal(HANG_LIMIT_GLOBAL, i32)), + makeTrivial(unreachable)), builder.makeSetGlobal( HANG_LIMIT_GLOBAL, - builder.makeBinary( - BinaryOp::SubInt32, - builder.makeGetGlobal(HANG_LIMIT_GLOBAL, i32), - builder.makeConst(Literal(int32_t(1))) - ) - ) - ); + builder.makeBinary(BinaryOp::SubInt32, + builder.makeGetGlobal(HANG_LIMIT_GLOBAL, i32), + builder.makeConst(Literal(int32_t(1)))))); } void addDeNanSupport() { @@ -411,13 +453,9 @@ private: func->result = type; func->body = builder.makeIf( builder.makeBinary( - op, - builder.makeGetLocal(0, type), - builder.makeGetLocal(0, type) - ), + op, builder.makeGetLocal(0, type), builder.makeGetLocal(0, type)), builder.makeGetLocal(0, type), - builder.makeConst(literal) - ); + builder.makeConst(literal)); wasm.addFunction(func); }; add("deNan32", f32, Literal(float(0)), EqFloat32); @@ -425,11 +463,12 @@ private: } Expression* makeDeNanOp(Expression* expr) { - if (allowNaNs) return expr; + if (allowNaNs) + return expr; if (expr->type == f32) { - return builder.makeCall("deNan32", { expr }, f32); + return builder.makeCall("deNan32", {expr}, f32); } else if (expr->type == f64) { - return builder.makeCall("deNan64", { expr }, f64); + return builder.makeCall("deNan64", {expr}, f64); } return expr; // unreachable etc. is fine } @@ -444,7 +483,8 @@ private: // which we try to minimize the risk of std::vector<Expression*> hangStack; - std::map<Type, std::vector<Index>> typeLocals; // type => list of locals with that type + std::map<Type, std::vector<Index>> + typeLocals; // type => list of locals with that type Function* addFunction() { LOGGING_PERCENT = upToSquared(100); @@ -518,23 +558,19 @@ private: // loop limit FindAll<Loop> loops(func->body); for (auto* loop : loops.list) { - loop->body = builder.makeSequence( - makeHangLimitCheck(), - loop->body - ); + loop->body = builder.makeSequence(makeHangLimitCheck(), loop->body); } // recursion limit - func->body = builder.makeSequence( - makeHangLimitCheck(), - func->body - ); + func->body = builder.makeSequence(makeHangLimitCheck(), func->body); } void recombine(Function* func) { // Don't always do this. - if (oneIn(2)) return; + if (oneIn(2)) + return; // First, scan and group all expressions by type. - struct Scanner : public PostWalker<Scanner, UnifiedExpressionVisitor<Scanner>> { + struct Scanner + : public PostWalker<Scanner, UnifiedExpressionVisitor<Scanner>> { // A map of all expressions, categorized by type. std::map<Type, std::vector<Expression*>> exprsByType; @@ -544,10 +580,11 @@ private: }; Scanner scanner; scanner.walk(func->body); - // Potentially trim the list of possible picks, so replacements are more likely - // to collide. + // Potentially trim the list of possible picks, so replacements are more + // likely to collide. for (auto& pair : scanner.exprsByType) { - if (oneIn(2)) continue; + if (oneIn(2)) + continue; auto& list = pair.second; std::vector<Expression*> trimmed; size_t num = upToSquared(list.size()); @@ -568,19 +605,22 @@ private: // Second, with some probability replace an item with another item having // the same type. (This is not always valid due to nesting of labels, but // we'll fix that up later.) - struct Modder : public PostWalker<Modder, UnifiedExpressionVisitor<Modder>> { + struct Modder + : public PostWalker<Modder, UnifiedExpressionVisitor<Modder>> { Module& wasm; Scanner& scanner; TranslateToFuzzReader& parent; - Modder(Module& wasm, Scanner& scanner, TranslateToFuzzReader& parent) : wasm(wasm), scanner(scanner), parent(parent) {} + Modder(Module& wasm, Scanner& scanner, TranslateToFuzzReader& parent) + : wasm(wasm), scanner(scanner), parent(parent) {} void visitExpression(Expression* curr) { if (parent.oneIn(10)) { // Replace it! auto& candidates = scanner.exprsByType[curr->type]; assert(!candidates.empty()); // this expression itself must be there - replaceCurrent(ExpressionManipulator::copy(parent.vectorPick(candidates), wasm)); + replaceCurrent( + ExpressionManipulator::copy(parent.vectorPick(candidates), wasm)); } } }; @@ -590,12 +630,15 @@ private: void mutate(Function* func) { // Don't always do this. - if (oneIn(2)) return; - struct Modder : public PostWalker<Modder, UnifiedExpressionVisitor<Modder>> { + if (oneIn(2)) + return; + struct Modder + : public PostWalker<Modder, UnifiedExpressionVisitor<Modder>> { Module& wasm; TranslateToFuzzReader& parent; - Modder(Module& wasm, TranslateToFuzzReader& parent) : wasm(wasm), parent(parent) {} + Modder(Module& wasm, TranslateToFuzzReader& parent) + : wasm(wasm), parent(parent) {} void visitExpression(Expression* curr) { if (parent.oneIn(10)) { @@ -617,7 +660,8 @@ private: Module& wasm; TranslateToFuzzReader& parent; - Fixer(Module& wasm, TranslateToFuzzReader& parent) : wasm(wasm), parent(parent) {} + Fixer(Module& wasm, TranslateToFuzzReader& parent) + : wasm(wasm), parent(parent) {} // Track seen names to find duplication, which is invalid. std::set<Name> seen; @@ -644,14 +688,13 @@ private: void visitSwitch(Switch* curr) { for (auto name : curr->targets) { - if (replaceIfInvalid(name)) return; + if (replaceIfInvalid(name)) + return; } replaceIfInvalid(curr->default_); } - void visitBreak(Break* curr) { - replaceIfInvalid(curr->name); - } + void visitBreak(Break* curr) { replaceIfInvalid(curr->name); } bool replaceIfInvalid(Name target) { if (!hasBreakTarget(target)) { @@ -662,24 +705,26 @@ private: return false; } - void replace() { - replaceCurrent(parent.makeTrivial(getCurrent()->type)); - } + void replace() { replaceCurrent(parent.makeTrivial(getCurrent()->type)); } bool hasBreakTarget(Name name) { - if (controlFlowStack.empty()) return false; + if (controlFlowStack.empty()) + return false; Index i = controlFlowStack.size() - 1; while (1) { auto* curr = controlFlowStack[i]; if (Block* block = curr->template dynCast<Block>()) { - if (name == block->name) return true; + if (name == block->name) + return true; } else if (Loop* loop = curr->template dynCast<Loop>()) { - if (name == loop->name) return true; + if (name == loop->name) + return true; } else { // an if, ignorable assert(curr->template is<If>()); } - if (i == 0) return false; + if (i == 0) + return false; i--; } } @@ -709,7 +754,8 @@ private: invocations.push_back(makeMemoryHashLogging()); } } - if (invocations.empty()) return; + if (invocations.empty()) + return; auto* invoker = new Function; invoker->name = func->name.str + std::string("_invoker"); invoker->result = none; @@ -733,8 +779,7 @@ private: Expression* make(Type type) { // when we should stop, emit something small (but not necessarily trivial) - if (finishedInput || - nesting >= 5 * NESTING_LIMIT || // hard limit + if (finishedInput || nesting >= 5 * NESTING_LIMIT || // hard limit (nesting >= NESTING_LIMIT && !oneIn(3))) { if (isConcreteType(type)) { if (oneIn(2)) { @@ -759,9 +804,15 @@ private: case i64: case f32: case f64: - case v128: ret = _makeConcrete(type); break; - case none: ret = _makenone(); break; - case unreachable: ret = _makeunreachable(); break; + case v128: + ret = _makeConcrete(type); + break; + case none: + ret = _makenone(); + break; + case unreachable: + ret = _makeunreachable(); + break; } assert(ret->type == type); // we should create the right type of thing nesting--; @@ -770,31 +821,38 @@ private: Expression* _makeConcrete(Type type) { auto choice = upTo(100); - if (choice < 10) return makeConst(type); - if (choice < 30) return makeSetLocal(type); - if (choice < 50) return makeGetLocal(type); - if (choice < 60) return makeBlock(type); - if (choice < 70) return makeIf(type); - if (choice < 80) return makeLoop(type); - if (choice < 90) return makeBreak(type); + if (choice < 10) + return makeConst(type); + if (choice < 30) + return makeSetLocal(type); + if (choice < 50) + return makeGetLocal(type); + if (choice < 60) + return makeBlock(type); + if (choice < 70) + return makeIf(type); + if (choice < 80) + return makeLoop(type); + if (choice < 90) + return makeBreak(type); using Self = TranslateToFuzzReader; auto options = FeatureOptions<Expression* (Self::*)(Type)>() - .add(FeatureSet::MVP, - &Self::makeBlock, - &Self::makeIf, - &Self::makeLoop, - &Self::makeBreak, - &Self::makeCall, - &Self::makeCallIndirect, - &Self::makeGetLocal, - &Self::makeSetLocal, - &Self::makeLoad, - &Self::makeConst, - &Self::makeUnary, - &Self::makeBinary, - &Self::makeSelect, - &Self::makeGetGlobal) - .add(FeatureSet::SIMD, &Self::makeSIMD); + .add(FeatureSet::MVP, + &Self::makeBlock, + &Self::makeIf, + &Self::makeLoop, + &Self::makeBreak, + &Self::makeCall, + &Self::makeCallIndirect, + &Self::makeGetLocal, + &Self::makeSetLocal, + &Self::makeLoad, + &Self::makeConst, + &Self::makeUnary, + &Self::makeBinary, + &Self::makeSelect, + &Self::makeGetGlobal) + .add(FeatureSet::SIMD, &Self::makeSIMD); if (type == i32 || type == i64) { options.add(FeatureSet::Atomics, &Self::makeAtomic); } @@ -811,46 +869,66 @@ private: } } choice = upTo(100); - if (choice < 50) return makeSetLocal(none); - if (choice < 60) return makeBlock(none); - if (choice < 70) return makeIf(none); - if (choice < 80) return makeLoop(none); - if (choice < 90) return makeBreak(none); + if (choice < 50) + return makeSetLocal(none); + if (choice < 60) + return makeBlock(none); + if (choice < 70) + return makeIf(none); + if (choice < 80) + return makeLoop(none); + if (choice < 90) + return makeBreak(none); using Self = TranslateToFuzzReader; auto options = FeatureOptions<Expression* (Self::*)(Type)>() - .add(FeatureSet::MVP, - &Self::makeBlock, - &Self::makeIf, - &Self::makeLoop, - &Self::makeBreak, - &Self::makeCall, - &Self::makeCallIndirect, - &Self::makeSetLocal, - &Self::makeStore, - &Self::makeDrop, - &Self::makeNop, - &Self::makeSetGlobal) - .add(FeatureSet::BulkMemory, &Self::makeBulkMemory); + .add(FeatureSet::MVP, + &Self::makeBlock, + &Self::makeIf, + &Self::makeLoop, + &Self::makeBreak, + &Self::makeCall, + &Self::makeCallIndirect, + &Self::makeSetLocal, + &Self::makeStore, + &Self::makeDrop, + &Self::makeNop, + &Self::makeSetGlobal) + .add(FeatureSet::BulkMemory, &Self::makeBulkMemory); return (this->*pick(options))(none); } Expression* _makeunreachable() { switch (upTo(15)) { - case 0: return makeBlock(unreachable); - case 1: return makeIf(unreachable); - case 2: return makeLoop(unreachable); - case 3: return makeBreak(unreachable); - case 4: return makeCall(unreachable); - case 5: return makeCallIndirect(unreachable); - case 6: return makeSetLocal(unreachable); - case 7: return makeStore(unreachable); - case 8: return makeUnary(unreachable); - case 9: return makeBinary(unreachable); - case 10: return makeSelect(unreachable); - case 11: return makeSwitch(unreachable); - case 12: return makeDrop(unreachable); - case 13: return makeReturn(unreachable); - case 14: return makeUnreachable(unreachable); + case 0: + return makeBlock(unreachable); + case 1: + return makeIf(unreachable); + case 2: + return makeLoop(unreachable); + case 3: + return makeBreak(unreachable); + case 4: + return makeCall(unreachable); + case 5: + return makeCallIndirect(unreachable); + case 6: + return makeSetLocal(unreachable); + case 7: + return makeStore(unreachable); + case 8: + return makeUnary(unreachable); + case 9: + return makeBinary(unreachable); + case 10: + return makeSelect(unreachable); + case 11: + return makeSwitch(unreachable); + case 12: + return makeDrop(unreachable); + case 13: + return makeReturn(unreachable); + case 14: + return makeUnreachable(unreachable); } WASM_UNREACHABLE(); } @@ -932,7 +1010,8 @@ private: // ensure a branch back. also optionally create some loop vars std::vector<Expression*> list; list.push_back(makeMaybeBlock(none)); // primary contents - list.push_back(builder.makeBreak(ret->name, nullptr, makeCondition())); // possible branch back + // possible branch back + list.push_back(builder.makeBreak(ret->name, nullptr, makeCondition())); list.push_back(make(type)); // final element, so we have the right type ret->body = builder.makeBlock(list); } @@ -970,13 +1049,15 @@ private: Expression* makeIf(Type type) { auto* condition = makeCondition(); hangStack.push_back(nullptr); - auto* ret = buildIf({ condition, makeMaybeBlock(type), makeMaybeBlock(type) }); + auto* ret = + buildIf({condition, makeMaybeBlock(type), makeMaybeBlock(type)}); hangStack.pop_back(); return ret; } Expression* makeBreak(Type type) { - if (breakableStack.empty()) return makeTrivial(type); + if (breakableStack.empty()) + return makeTrivial(type); Expression* condition = nullptr; if (type != unreachable) { hangStack.push_back(nullptr); @@ -1029,15 +1110,18 @@ private: } switch (conditions) { case 0: { - if (!oneIn(4)) continue; + if (!oneIn(4)) + continue; break; } case 1: { - if (!oneIn(2)) continue; + if (!oneIn(2)) + continue; break; } default: { - if (oneIn(conditions + 1)) continue; + if (oneIn(conditions + 1)) + continue; } } return builder.makeBreak(name); @@ -1058,7 +1142,8 @@ private: if (!wasm.functions.empty() && !oneIn(wasm.functions.size())) { target = vectorPick(wasm.functions).get(); } - if (target->result != type) continue; + if (target->result != type) + continue; // we found one! std::vector<Expression*> args; for (auto argType : target->params) { @@ -1072,7 +1157,8 @@ private: Expression* makeCallIndirect(Type type) { auto& data = wasm.table.segments[0].data; - if (data.empty()) return make(type); + if (data.empty()) + return make(type); // look for a call target with the right type Index start = upTo(data.size()); Index i = start; @@ -1084,8 +1170,10 @@ private: break; } i++; - if (i == data.size()) i = 0; - if (i == start) return make(type); + if (i == data.size()) + i = 0; + if (i == start) + return make(type); } // with high probability, make sure the type is valid otherwise, most are // going to trap @@ -1100,17 +1188,13 @@ private: args.push_back(make(type)); } func->type = ensureFunctionType(getSig(func), &wasm)->name; - return builder.makeCallIndirect( - func->type, - target, - args, - func->result - ); + return builder.makeCallIndirect(func->type, target, args, func->result); } Expression* makeGetLocal(Type type) { auto& locals = typeLocals[type]; - if (locals.empty()) return makeConst(type); + if (locals.empty()) + return makeConst(type); return builder.makeGetLocal(vectorPick(locals), type); } @@ -1123,7 +1207,8 @@ private: valueType = getConcreteType(); } auto& locals = typeLocals[valueType]; - if (locals.empty()) return makeTrivial(type); + if (locals.empty()) + return makeTrivial(type); auto* value = make(valueType); if (tee) { return builder.makeTeeLocal(vectorPick(locals), value); @@ -1134,7 +1219,8 @@ private: Expression* makeGetGlobal(Type type) { auto& globals = globalsByType[type]; - if (globals.empty()) return makeConst(type); + if (globals.empty()) + return makeConst(type); return builder.makeGetGlobal(vectorPick(globals), type); } @@ -1142,7 +1228,8 @@ private: assert(type == none); type = getConcreteType(); auto& globals = globalsByType[type]; - if (globals.empty()) return makeTrivial(none); + if (globals.empty()) + return makeTrivial(none); auto* value = make(type); return builder.makeSetGlobal(vectorPick(globals), value); } @@ -1153,10 +1240,8 @@ private: // range. otherwise, most pointers are going to be out of range and // most memory ops will just trap if (!oneIn(10)) { - ret = builder.makeBinary(AndInt32, - ret, - builder.makeConst(Literal(int32_t(USABLE_MEMORY - 1))) - ); + ret = builder.makeBinary( + AndInt32, ret, builder.makeConst(Literal(int32_t(USABLE_MEMORY - 1)))); } return ret; } @@ -1168,19 +1253,29 @@ private: case i32: { bool signed_ = get() & 1; switch (upTo(3)) { - case 0: return builder.makeLoad(1, signed_, offset, 1, ptr, type); - case 1: return builder.makeLoad(2, signed_, offset, pick(1, 2), ptr, type); - case 2: return builder.makeLoad(4, signed_, offset, pick(1, 2, 4), ptr, type); + case 0: + return builder.makeLoad(1, signed_, offset, 1, ptr, type); + case 1: + return builder.makeLoad(2, signed_, offset, pick(1, 2), ptr, type); + case 2: + return builder.makeLoad( + 4, signed_, offset, pick(1, 2, 4), ptr, type); } WASM_UNREACHABLE(); } case i64: { bool signed_ = get() & 1; switch (upTo(4)) { - case 0: return builder.makeLoad(1, signed_, offset, 1, ptr, type); - case 1: return builder.makeLoad(2, signed_, offset, pick(1, 2), ptr, type); - case 2: return builder.makeLoad(4, signed_, offset, pick(1, 2, 4), ptr, type); - case 3: return builder.makeLoad(8, signed_, offset, pick(1, 2, 4, 8), ptr, type); + case 0: + return builder.makeLoad(1, signed_, offset, 1, ptr, type); + case 1: + return builder.makeLoad(2, signed_, offset, pick(1, 2), ptr, type); + case 2: + return builder.makeLoad( + 4, signed_, offset, pick(1, 2, 4), ptr, type); + case 3: + return builder.makeLoad( + 8, signed_, offset, pick(1, 2, 4, 8), ptr, type); } WASM_UNREACHABLE(); } @@ -1194,19 +1289,24 @@ private: if (!wasm.features.hasSIMD()) { return makeTrivial(type); } - return builder.makeLoad(16, false, offset, pick(1, 2, 4, 8, 16), ptr, type); + return builder.makeLoad( + 16, false, offset, pick(1, 2, 4, 8, 16), ptr, type); } case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } Expression* makeLoad(Type type) { - if (!allowMemory) return makeTrivial(type); + if (!allowMemory) + return makeTrivial(type); auto* ret = makeNonAtomicLoad(type); - if (type != i32 && type != i64) return ret; - if (!wasm.features.hasAtomics() || oneIn(2)) return ret; + if (type != i32 && type != i64) + return ret; + if (!wasm.features.hasAtomics() || oneIn(2)) + return ret; // make it atomic auto* load = ret->cast<Load>(); wasm.memory.shared = true; @@ -1221,11 +1321,19 @@ private: // make a normal store, then make it unreachable auto* ret = makeNonAtomicStore(getConcreteType()); auto* store = ret->dynCast<Store>(); - if (!store) return ret; + if (!store) + return ret; switch (upTo(3)) { - case 0: store->ptr = make(unreachable); break; - case 1: store->value = make(unreachable); break; - case 2: store->ptr = make(unreachable); store->value = make(unreachable); break; + case 0: + store->ptr = make(unreachable); + break; + case 1: + store->value = make(unreachable); + break; + case 2: + store->ptr = make(unreachable); + store->value = make(unreachable); + break; } store->finalize(); return store; @@ -1241,18 +1349,28 @@ private: switch (type) { case i32: { switch (upTo(3)) { - case 0: return builder.makeStore(1, offset, 1, ptr, value, type); - case 1: return builder.makeStore(2, offset, pick(1, 2), ptr, value, type); - case 2: return builder.makeStore(4, offset, pick(1, 2, 4), ptr, value, type); + case 0: + return builder.makeStore(1, offset, 1, ptr, value, type); + case 1: + return builder.makeStore(2, offset, pick(1, 2), ptr, value, type); + case 2: + return builder.makeStore( + 4, offset, pick(1, 2, 4), ptr, value, type); } WASM_UNREACHABLE(); } case i64: { switch (upTo(4)) { - case 0: return builder.makeStore(1, offset, 1, ptr, value, type); - case 1: return builder.makeStore(2, offset, pick(1, 2), ptr, value, type); - case 2: return builder.makeStore(4, offset, pick(1, 2, 4), ptr, value, type); - case 3: return builder.makeStore(8, offset, pick(1, 2, 4, 8), ptr, value, type); + case 0: + return builder.makeStore(1, offset, 1, ptr, value, type); + case 1: + return builder.makeStore(2, offset, pick(1, 2), ptr, value, type); + case 2: + return builder.makeStore( + 4, offset, pick(1, 2, 4), ptr, value, type); + case 3: + return builder.makeStore( + 8, offset, pick(1, 2, 4, 8), ptr, value, type); } WASM_UNREACHABLE(); } @@ -1266,21 +1384,27 @@ private: if (!wasm.features.hasSIMD()) { return makeTrivial(type); } - return builder.makeStore(16, offset, pick(1, 2, 4, 8, 16), ptr, value, type); + return builder.makeStore( + 16, offset, pick(1, 2, 4, 8, 16), ptr, value, type); } case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } Expression* makeStore(Type type) { - if (!allowMemory) return makeTrivial(type); + if (!allowMemory) + return makeTrivial(type); auto* ret = makeNonAtomicStore(type); auto* store = ret->dynCast<Store>(); - if (!store) return ret; - if (store->value->type != i32 && store->value->type != i64) return store; - if (!wasm.features.hasAtomics() || oneIn(2)) return store; + if (!store) + return ret; + if (store->value->type != i32 && store->value->type != i64) + return store; + if (!wasm.features.hasAtomics() || oneIn(2)) + return store; // make it atomic wasm.memory.shared = true; store->isAtomic = true; @@ -1292,25 +1416,50 @@ private: if (type == v128) { // generate each lane individually for random lane interpretation switch (upTo(6)) { - case 0: return Literal( - std::array<Literal, 16>{{ - makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), - makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), - makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), - makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32) - }} - ); - case 1: return Literal( - std::array<Literal, 8>{{ - makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), - makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32) - }} - ); - case 2: return Literal(std::array<Literal, 4>{{makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32)}}); - case 3: return Literal(std::array<Literal, 2>{{makeLiteral(i64), makeLiteral(i64)}}); - case 4: return Literal(std::array<Literal, 4>{{makeLiteral(f32), makeLiteral(f32), makeLiteral(f32), makeLiteral(f32)}}); - case 5: return Literal(std::array<Literal, 2>{{makeLiteral(f64), makeLiteral(f64)}}); - default: WASM_UNREACHABLE(); + case 0: + return Literal(std::array<Literal, 16>{{makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32)}}); + case 1: + return Literal(std::array<Literal, 8>{{makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32)}}); + case 2: + return Literal(std::array<Literal, 4>{{makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32), + makeLiteral(i32)}}); + case 3: + return Literal( + std::array<Literal, 2>{{makeLiteral(i64), makeLiteral(i64)}}); + case 4: + return Literal(std::array<Literal, 4>{{makeLiteral(f32), + makeLiteral(f32), + makeLiteral(f32), + makeLiteral(f32)}}); + case 5: + return Literal( + std::array<Literal, 2>{{makeLiteral(f64), makeLiteral(f64)}}); + default: + WASM_UNREACHABLE(); } } @@ -1318,13 +1467,18 @@ private: case 0: { // totally random, entire range switch (type) { - case i32: return Literal(get32()); - case i64: return Literal(get64()); - case f32: return Literal(getFloat()); - case f64: return Literal(getDouble()); + case i32: + return Literal(get32()); + case i64: + return Literal(get64()); + case f32: + return Literal(getFloat()); + case f64: + return Literal(getDouble()); case v128: case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } break; } @@ -1332,22 +1486,40 @@ private: // small range int64_t small; switch (upTo(6)) { - case 0: small = int8_t(get()); break; - case 1: small = uint8_t(get()); break; - case 2: small = int16_t(get16()); break; - case 3: small = uint16_t(get16()); break; - case 4: small = int32_t(get32()); break; - case 5: small = uint32_t(get32()); break; - default: WASM_UNREACHABLE(); + case 0: + small = int8_t(get()); + break; + case 1: + small = uint8_t(get()); + break; + case 2: + small = int16_t(get16()); + break; + case 3: + small = uint16_t(get16()); + break; + case 4: + small = int32_t(get32()); + break; + case 5: + small = uint32_t(get32()); + break; + default: + WASM_UNREACHABLE(); } switch (type) { - case i32: return Literal(int32_t(small)); - case i64: return Literal(int64_t(small)); - case f32: return Literal(float(small)); - case f64: return Literal(double(small)); + case i32: + return Literal(int32_t(small)); + case i64: + return Literal(int64_t(small)); + case f32: + return Literal(float(small)); + case f64: + return Literal(double(small)); case v128: case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } break; } @@ -1355,38 +1527,63 @@ private: // special values Literal value; switch (type) { - case i32: value = Literal(pick<int32_t>(0, - std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max(), - std::numeric_limits<int16_t>::min(), std::numeric_limits<int16_t>::max(), - std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max(), - std::numeric_limits<uint8_t>::max(), - std::numeric_limits<uint16_t>::max(), - std::numeric_limits<uint32_t>::max())); break; - case i64: value = Literal(pick<int64_t>(0, - std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max(), - std::numeric_limits<int16_t>::min(), std::numeric_limits<int16_t>::max(), - std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max(), - std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max(), - std::numeric_limits<uint8_t>::max(), - std::numeric_limits<uint16_t>::max(), - std::numeric_limits<uint32_t>::max(), - std::numeric_limits<uint64_t>::max())); break; - case f32: value = Literal(pick<float>(0, - std::numeric_limits<float>::min(), std::numeric_limits<float>::max(), - std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max(), - std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max(), - std::numeric_limits<uint32_t>::max(), - std::numeric_limits<uint64_t>::max())); break; - case f64: value = Literal(pick<double>(0, - std::numeric_limits<float>::min(), std::numeric_limits<float>::max(), - std::numeric_limits<double>::min(), std::numeric_limits<double>::max(), - std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max(), - std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max(), - std::numeric_limits<uint32_t>::max(), - std::numeric_limits<uint64_t>::max())); break; + case i32: + value = + Literal(pick<int32_t>(0, + std::numeric_limits<int8_t>::min(), + std::numeric_limits<int8_t>::max(), + std::numeric_limits<int16_t>::min(), + std::numeric_limits<int16_t>::max(), + std::numeric_limits<int32_t>::min(), + std::numeric_limits<int32_t>::max(), + std::numeric_limits<uint8_t>::max(), + std::numeric_limits<uint16_t>::max(), + std::numeric_limits<uint32_t>::max())); + break; + case i64: + value = + Literal(pick<int64_t>(0, + std::numeric_limits<int8_t>::min(), + std::numeric_limits<int8_t>::max(), + std::numeric_limits<int16_t>::min(), + std::numeric_limits<int16_t>::max(), + std::numeric_limits<int32_t>::min(), + std::numeric_limits<int32_t>::max(), + std::numeric_limits<int64_t>::min(), + std::numeric_limits<int64_t>::max(), + std::numeric_limits<uint8_t>::max(), + std::numeric_limits<uint16_t>::max(), + std::numeric_limits<uint32_t>::max(), + std::numeric_limits<uint64_t>::max())); + break; + case f32: + value = Literal(pick<float>(0, + std::numeric_limits<float>::min(), + std::numeric_limits<float>::max(), + std::numeric_limits<int32_t>::min(), + std::numeric_limits<int32_t>::max(), + std::numeric_limits<int64_t>::min(), + std::numeric_limits<int64_t>::max(), + std::numeric_limits<uint32_t>::max(), + std::numeric_limits<uint64_t>::max())); + break; + case f64: + value = Literal(pick<double>(0, + std::numeric_limits<float>::min(), + std::numeric_limits<float>::max(), + std::numeric_limits<double>::min(), + std::numeric_limits<double>::max(), + std::numeric_limits<int32_t>::min(), + std::numeric_limits<int32_t>::max(), + std::numeric_limits<int64_t>::min(), + std::numeric_limits<int64_t>::max(), + std::numeric_limits<uint32_t>::max(), + std::numeric_limits<uint64_t>::max())); + break; case v128: case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } // tweak around special values if (oneIn(3)) { // +- 1 @@ -1401,13 +1598,22 @@ private: // powers of 2 Literal value; switch (type) { - case i32: value = Literal(int32_t(1) << upTo(32)); break; - case i64: value = Literal(int64_t(1) << upTo(64)); break; - case f32: value = Literal(float(int64_t(1) << upTo(64))); break; - case f64: value = Literal(double(int64_t(1) << upTo(64))); break; + case i32: + value = Literal(int32_t(1) << upTo(32)); + break; + case i64: + value = Literal(int64_t(1) << upTo(64)); + break; + case f32: + value = Literal(float(int64_t(1) << upTo(64))); + break; + case f64: + value = Literal(double(int64_t(1) << upTo(64))); + break; case v128: case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } // maybe negative if (oneIn(2)) { @@ -1452,105 +1658,178 @@ private: case i32: { auto op = pick( FeatureOptions<UnaryOp>() - .add(FeatureSet::MVP, EqZInt32, ClzInt32, CtzInt32, PopcntInt32) - .add(FeatureSet::Atomics, ExtendS8Int32, ExtendS16Int32) - ); - return buildUnary({ op, make(i32) }); + .add(FeatureSet::MVP, EqZInt32, ClzInt32, CtzInt32, PopcntInt32) + .add(FeatureSet::Atomics, ExtendS8Int32, ExtendS16Int32)); + return buildUnary({op, make(i32)}); } - case i64: return buildUnary({ pick(EqZInt64, WrapInt64), make(i64) }); + case i64: + return buildUnary({pick(EqZInt64, WrapInt64), make(i64)}); case f32: { - auto op = pick( - FeatureOptions<UnaryOp>() - .add(FeatureSet::MVP, TruncSFloat32ToInt32, TruncUFloat32ToInt32, ReinterpretFloat32) - .add(FeatureSet::TruncSat, TruncSatSFloat32ToInt32, TruncSatUFloat32ToInt32) - ); - return buildUnary({ op, make(f32) }); + auto op = pick(FeatureOptions<UnaryOp>() + .add(FeatureSet::MVP, + TruncSFloat32ToInt32, + TruncUFloat32ToInt32, + ReinterpretFloat32) + .add(FeatureSet::TruncSat, + TruncSatSFloat32ToInt32, + TruncSatUFloat32ToInt32)); + return buildUnary({op, make(f32)}); } case f64: { - auto op = pick( - FeatureOptions<UnaryOp>() - .add(FeatureSet::MVP, TruncSFloat64ToInt32, TruncUFloat64ToInt32) - .add(FeatureSet::TruncSat, TruncSatSFloat64ToInt32, TruncSatUFloat64ToInt32) - ); - return buildUnary({ op, make(f64) }); + auto op = pick(FeatureOptions<UnaryOp>() + .add(FeatureSet::MVP, + TruncSFloat64ToInt32, + TruncUFloat64ToInt32) + .add(FeatureSet::TruncSat, + TruncSatSFloat64ToInt32, + TruncSatUFloat64ToInt32)); + return buildUnary({op, make(f64)}); } case v128: { assert(wasm.features.hasSIMD()); - return buildUnary({ pick(AnyTrueVecI8x16, AllTrueVecI8x16, AnyTrueVecI16x8, AllTrueVecI16x8, - AnyTrueVecI32x4, AllTrueVecI32x4, AnyTrueVecI64x2, AllTrueVecI64x2), - make(v128) }); + return buildUnary({pick(AnyTrueVecI8x16, + AllTrueVecI8x16, + AnyTrueVecI16x8, + AllTrueVecI16x8, + AnyTrueVecI32x4, + AllTrueVecI32x4, + AnyTrueVecI64x2, + AllTrueVecI64x2), + make(v128)}); } case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } case i64: { switch (upTo(4)) { case 0: { - auto op = pick( - FeatureOptions<UnaryOp>() - .add(FeatureSet::MVP, ClzInt64, CtzInt64, PopcntInt64) - .add(FeatureSet::Atomics, ExtendS8Int64, ExtendS16Int64, ExtendS32Int64) - ); - return buildUnary({ op, make(i64) }); + auto op = + pick(FeatureOptions<UnaryOp>() + .add(FeatureSet::MVP, ClzInt64, CtzInt64, PopcntInt64) + .add(FeatureSet::Atomics, + ExtendS8Int64, + ExtendS16Int64, + ExtendS32Int64)); + return buildUnary({op, make(i64)}); } - case 1: return buildUnary({ pick(ExtendSInt32, ExtendUInt32), make(i32) }); + case 1: + return buildUnary({pick(ExtendSInt32, ExtendUInt32), make(i32)}); case 2: { - auto op = pick( - FeatureOptions<UnaryOp>() - .add(FeatureSet::MVP, TruncSFloat32ToInt64, TruncUFloat32ToInt64) - .add(FeatureSet::TruncSat, TruncSatSFloat32ToInt64, TruncSatUFloat32ToInt64) - ); - return buildUnary({ op, make(f32) }); + auto op = pick(FeatureOptions<UnaryOp>() + .add(FeatureSet::MVP, + TruncSFloat32ToInt64, + TruncUFloat32ToInt64) + .add(FeatureSet::TruncSat, + TruncSatSFloat32ToInt64, + TruncSatUFloat32ToInt64)); + return buildUnary({op, make(f32)}); } case 3: { - auto op = pick( - FeatureOptions<UnaryOp>() - .add(FeatureSet::MVP, TruncSFloat64ToInt64, TruncUFloat64ToInt64, ReinterpretFloat64) - .add(FeatureSet::TruncSat, TruncSatSFloat64ToInt64, TruncSatUFloat64ToInt64) - ); - return buildUnary({ op, make(f64) }); + auto op = pick(FeatureOptions<UnaryOp>() + .add(FeatureSet::MVP, + TruncSFloat64ToInt64, + TruncUFloat64ToInt64, + ReinterpretFloat64) + .add(FeatureSet::TruncSat, + TruncSatSFloat64ToInt64, + TruncSatUFloat64ToInt64)); + return buildUnary({op, make(f64)}); } } WASM_UNREACHABLE(); } case f32: { switch (upTo(4)) { - case 0: return makeDeNanOp(buildUnary({ pick(NegFloat32, AbsFloat32, CeilFloat32, FloorFloat32, TruncFloat32, NearestFloat32, SqrtFloat32), make(f32) })); - case 1: return makeDeNanOp(buildUnary({ pick(ConvertUInt32ToFloat32, ConvertSInt32ToFloat32, ReinterpretInt32), make(i32) })); - case 2: return makeDeNanOp(buildUnary({ pick(ConvertUInt64ToFloat32, ConvertSInt64ToFloat32), make(i64) })); - case 3: return makeDeNanOp(buildUnary({ DemoteFloat64, make(f64) })); + case 0: + return makeDeNanOp(buildUnary({pick(NegFloat32, + AbsFloat32, + CeilFloat32, + FloorFloat32, + TruncFloat32, + NearestFloat32, + SqrtFloat32), + make(f32)})); + case 1: + return makeDeNanOp(buildUnary({pick(ConvertUInt32ToFloat32, + ConvertSInt32ToFloat32, + ReinterpretInt32), + make(i32)})); + case 2: + return makeDeNanOp( + buildUnary({pick(ConvertUInt64ToFloat32, ConvertSInt64ToFloat32), + make(i64)})); + case 3: + return makeDeNanOp(buildUnary({DemoteFloat64, make(f64)})); } WASM_UNREACHABLE(); } case f64: { switch (upTo(4)) { - case 0: return makeDeNanOp(buildUnary({ pick(NegFloat64, AbsFloat64, CeilFloat64, FloorFloat64, TruncFloat64, NearestFloat64, SqrtFloat64), make(f64) })); - case 1: return makeDeNanOp(buildUnary({ pick(ConvertUInt32ToFloat64, ConvertSInt32ToFloat64), make(i32) })); - case 2: return makeDeNanOp(buildUnary({ pick(ConvertUInt64ToFloat64, ConvertSInt64ToFloat64, ReinterpretInt64), make(i64) })); - case 3: return makeDeNanOp(buildUnary({ PromoteFloat32, make(f32) })); + case 0: + return makeDeNanOp(buildUnary({pick(NegFloat64, + AbsFloat64, + CeilFloat64, + FloorFloat64, + TruncFloat64, + NearestFloat64, + SqrtFloat64), + make(f64)})); + case 1: + return makeDeNanOp( + buildUnary({pick(ConvertUInt32ToFloat64, ConvertSInt32ToFloat64), + make(i32)})); + case 2: + return makeDeNanOp(buildUnary({pick(ConvertUInt64ToFloat64, + ConvertSInt64ToFloat64, + ReinterpretInt64), + make(i64)})); + case 3: + return makeDeNanOp(buildUnary({PromoteFloat32, make(f32)})); } WASM_UNREACHABLE(); } case v128: { assert(wasm.features.hasSIMD()); switch (upTo(5)) { - case 0: return buildUnary({ pick(SplatVecI8x16, SplatVecI16x8, SplatVecI32x4), make(i32) }); - case 1: return buildUnary({ SplatVecI64x2, make(i64) }); - case 2: return buildUnary({ SplatVecF32x4, make(f32) }); - case 3: return buildUnary({ SplatVecF64x2, make(f64) }); - case 4: return buildUnary({ - pick(NotVec128, NegVecI8x16, NegVecI16x8, NegVecI32x4, NegVecI64x2, - AbsVecF32x4, NegVecF32x4, SqrtVecF32x4, AbsVecF64x2, NegVecF64x2, SqrtVecF64x2, - TruncSatSVecF32x4ToVecI32x4, TruncSatUVecF32x4ToVecI32x4, TruncSatSVecF64x2ToVecI64x2, TruncSatUVecF64x2ToVecI64x2, - ConvertSVecI32x4ToVecF32x4, ConvertUVecI32x4ToVecF32x4, ConvertSVecI64x2ToVecF64x2, ConvertUVecI64x2ToVecF64x2), - make(v128) }); + case 0: + return buildUnary( + {pick(SplatVecI8x16, SplatVecI16x8, SplatVecI32x4), make(i32)}); + case 1: + return buildUnary({SplatVecI64x2, make(i64)}); + case 2: + return buildUnary({SplatVecF32x4, make(f32)}); + case 3: + return buildUnary({SplatVecF64x2, make(f64)}); + case 4: + return buildUnary({pick(NotVec128, + NegVecI8x16, + NegVecI16x8, + NegVecI32x4, + NegVecI64x2, + AbsVecF32x4, + NegVecF32x4, + SqrtVecF32x4, + AbsVecF64x2, + NegVecF64x2, + SqrtVecF64x2, + TruncSatSVecF32x4ToVecI32x4, + TruncSatUVecF32x4ToVecI32x4, + TruncSatSVecF64x2ToVecI64x2, + TruncSatUVecF64x2ToVecI64x2, + ConvertSVecI32x4ToVecF32x4, + ConvertUVecI32x4ToVecF32x4, + ConvertSVecI64x2ToVecF64x2, + ConvertUVecI64x2ToVecF64x2), + make(v128)}); } WASM_UNREACHABLE(); } case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } @@ -1562,7 +1841,8 @@ private: Expression* makeBinary(Type type) { if (type == unreachable) { if (auto* binary = makeBinary(getConcreteType())->dynCast<Binary>()) { - return makeDeNanOp(buildBinary({ binary->op, make(unreachable), make(unreachable) })); + return makeDeNanOp( + buildBinary({binary->op, make(unreachable), make(unreachable)})); } // give up return makeTrivial(type); @@ -1570,37 +1850,193 @@ private: switch (type) { case i32: { switch (upTo(4)) { - case 0: return buildBinary({ pick(AddInt32, SubInt32, MulInt32, DivSInt32, DivUInt32, RemSInt32, RemUInt32, AndInt32, OrInt32, XorInt32, ShlInt32, ShrUInt32, ShrSInt32, RotLInt32, RotRInt32, EqInt32, NeInt32, LtSInt32, LtUInt32, LeSInt32, LeUInt32, GtSInt32, GtUInt32, GeSInt32, GeUInt32), make(i32), make(i32) }); - case 1: return buildBinary({ pick(EqInt64, NeInt64, LtSInt64, LtUInt64, LeSInt64, LeUInt64, GtSInt64, GtUInt64, GeSInt64, GeUInt64), make(i64), make(i64) }); - case 2: return buildBinary({ pick(EqFloat32, NeFloat32, LtFloat32, LeFloat32, GtFloat32, GeFloat32), make(f32), make(f32) }); - case 3: return buildBinary({ pick(EqFloat64, NeFloat64, LtFloat64, LeFloat64, GtFloat64, GeFloat64), make(f64), make(f64) }); + case 0: + return buildBinary({pick(AddInt32, + SubInt32, + MulInt32, + DivSInt32, + DivUInt32, + RemSInt32, + RemUInt32, + AndInt32, + OrInt32, + XorInt32, + ShlInt32, + ShrUInt32, + ShrSInt32, + RotLInt32, + RotRInt32, + EqInt32, + NeInt32, + LtSInt32, + LtUInt32, + LeSInt32, + LeUInt32, + GtSInt32, + GtUInt32, + GeSInt32, + GeUInt32), + make(i32), + make(i32)}); + case 1: + return buildBinary({pick(EqInt64, + NeInt64, + LtSInt64, + LtUInt64, + LeSInt64, + LeUInt64, + GtSInt64, + GtUInt64, + GeSInt64, + GeUInt64), + make(i64), + make(i64)}); + case 2: + return buildBinary({pick(EqFloat32, + NeFloat32, + LtFloat32, + LeFloat32, + GtFloat32, + GeFloat32), + make(f32), + make(f32)}); + case 3: + return buildBinary({pick(EqFloat64, + NeFloat64, + LtFloat64, + LeFloat64, + GtFloat64, + GeFloat64), + make(f64), + make(f64)}); } WASM_UNREACHABLE(); } case i64: { - return buildBinary({ pick(AddInt64, SubInt64, MulInt64, DivSInt64, DivUInt64, RemSInt64, RemUInt64, AndInt64, OrInt64, XorInt64, ShlInt64, ShrUInt64, ShrSInt64, RotLInt64, RotRInt64), make(i64), make(i64) }); + return buildBinary({pick(AddInt64, + SubInt64, + MulInt64, + DivSInt64, + DivUInt64, + RemSInt64, + RemUInt64, + AndInt64, + OrInt64, + XorInt64, + ShlInt64, + ShrUInt64, + ShrSInt64, + RotLInt64, + RotRInt64), + make(i64), + make(i64)}); } case f32: { - return makeDeNanOp(buildBinary({ pick(AddFloat32, SubFloat32, MulFloat32, DivFloat32, CopySignFloat32, MinFloat32, MaxFloat32), make(f32), make(f32) })); + return makeDeNanOp(buildBinary({pick(AddFloat32, + SubFloat32, + MulFloat32, + DivFloat32, + CopySignFloat32, + MinFloat32, + MaxFloat32), + make(f32), + make(f32)})); } case f64: { - return makeDeNanOp(buildBinary({ pick(AddFloat64, SubFloat64, MulFloat64, DivFloat64, CopySignFloat64, MinFloat64, MaxFloat64), make(f64), make(f64) })); + return makeDeNanOp(buildBinary({pick(AddFloat64, + SubFloat64, + MulFloat64, + DivFloat64, + CopySignFloat64, + MinFloat64, + MaxFloat64), + make(f64), + make(f64)})); } case v128: { assert(wasm.features.hasSIMD()); - return buildBinary({ - pick(EqVecI8x16, NeVecI8x16, LtSVecI8x16, LtUVecI8x16, GtSVecI8x16, GtUVecI8x16, LeSVecI8x16, LeUVecI8x16, GeSVecI8x16, GeUVecI8x16, - EqVecI16x8, NeVecI16x8, LtSVecI16x8, LtUVecI16x8, GtSVecI16x8, GtUVecI16x8, LeSVecI16x8, LeUVecI16x8, GeSVecI16x8, GeUVecI16x8, - EqVecI32x4, NeVecI32x4, LtSVecI32x4, LtUVecI32x4, GtSVecI32x4, GtUVecI32x4, LeSVecI32x4, LeUVecI32x4, GeSVecI32x4, GeUVecI32x4, - EqVecF32x4, NeVecF32x4, LtVecF32x4, GtVecF32x4, LeVecF32x4, GeVecF32x4, EqVecF64x2, NeVecF64x2, LtVecF64x2, GtVecF64x2, LeVecF64x2, GeVecF64x2, - AndVec128, OrVec128, XorVec128, AddVecI8x16, AddSatSVecI8x16, AddSatUVecI8x16, SubVecI8x16, SubSatSVecI8x16, SubSatUVecI8x16, MulVecI8x16, - AddVecI16x8, AddSatSVecI16x8, AddSatUVecI16x8, SubVecI16x8, SubSatSVecI16x8, SubSatUVecI16x8, MulVecI16x8, AddVecI32x4, SubVecI32x4, MulVecI32x4, - AddVecI64x2, SubVecI64x2, AddVecF32x4, SubVecF32x4, MulVecF32x4, DivVecF32x4, MinVecF32x4, MaxVecF32x4, - AddVecF64x2, SubVecF64x2, MulVecF64x2, DivVecF64x2, MinVecF64x2, MaxVecF64x2), - make(v128), make(v128) }); + return buildBinary({pick(EqVecI8x16, + NeVecI8x16, + LtSVecI8x16, + LtUVecI8x16, + GtSVecI8x16, + GtUVecI8x16, + LeSVecI8x16, + LeUVecI8x16, + GeSVecI8x16, + GeUVecI8x16, + EqVecI16x8, + NeVecI16x8, + LtSVecI16x8, + LtUVecI16x8, + GtSVecI16x8, + GtUVecI16x8, + LeSVecI16x8, + LeUVecI16x8, + GeSVecI16x8, + GeUVecI16x8, + EqVecI32x4, + NeVecI32x4, + LtSVecI32x4, + LtUVecI32x4, + GtSVecI32x4, + GtUVecI32x4, + LeSVecI32x4, + LeUVecI32x4, + GeSVecI32x4, + GeUVecI32x4, + EqVecF32x4, + NeVecF32x4, + LtVecF32x4, + GtVecF32x4, + LeVecF32x4, + GeVecF32x4, + EqVecF64x2, + NeVecF64x2, + LtVecF64x2, + GtVecF64x2, + LeVecF64x2, + GeVecF64x2, + AndVec128, + OrVec128, + XorVec128, + AddVecI8x16, + AddSatSVecI8x16, + AddSatUVecI8x16, + SubVecI8x16, + SubSatSVecI8x16, + SubSatUVecI8x16, + MulVecI8x16, + AddVecI16x8, + AddSatSVecI16x8, + AddSatUVecI16x8, + SubVecI16x8, + SubSatSVecI16x8, + SubSatUVecI16x8, + MulVecI16x8, + AddVecI32x4, + SubVecI32x4, + MulVecI32x4, + AddVecI64x2, + SubVecI64x2, + AddVecF32x4, + SubVecF32x4, + MulVecF32x4, + DivVecF32x4, + MinVecF32x4, + MaxVecF32x4, + AddVecF64x2, + SubVecF64x2, + MulVecF64x2, + DivVecF64x2, + MinVecF64x2, + MaxVecF64x2), + make(v128), + make(v128)}); } case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } @@ -1610,12 +2046,13 @@ private: } Expression* makeSelect(Type type) { - return makeDeNanOp(buildSelect({ make(i32), make(type), make(type) })); + return makeDeNanOp(buildSelect({make(i32), make(type), make(type)})); } Expression* makeSwitch(Type type) { assert(type == unreachable); - if (breakableStack.empty()) return make(type); + if (breakableStack.empty()) + return make(type); // we need to find proper targets to break to; try a bunch int tries = TRIES; std::vector<Name> names; @@ -1639,16 +2076,19 @@ private: } auto default_ = names.back(); names.pop_back(); - auto temp1 = make(i32), temp2 = isConcreteType(valueType) ? make(valueType) : nullptr; + auto temp1 = make(i32), + temp2 = isConcreteType(valueType) ? make(valueType) : nullptr; return builder.makeSwitch(names, default_, temp1, temp2); } Expression* makeDrop(Type type) { - return builder.makeDrop(make(type == unreachable ? type : getConcreteType())); + return builder.makeDrop( + make(type == unreachable ? type : getConcreteType())); } Expression* makeReturn(Type type) { - return builder.makeReturn(isConcreteType(func->result) ? make(func->result) : nullptr); + return builder.makeReturn(isConcreteType(func->result) ? make(func->result) + : nullptr); } Expression* makeNop(Type type) { @@ -1663,7 +2103,8 @@ private: Expression* makeAtomic(Type type) { assert(wasm.features.hasAtomics()); - if (!allowMemory) return makeTrivial(type); + if (!allowMemory) + return makeTrivial(type); wasm.memory.shared = true; if (type == i32 && oneIn(2)) { if (ATOMIC_WAITS && oneIn(2)) { @@ -1671,7 +2112,8 @@ private: auto expectedType = pick(i32, i64); auto* expected = make(expectedType); auto* timeout = make(i64); - return builder.makeAtomicWait(ptr, expected, timeout, expectedType, logify(get())); + return builder.makeAtomicWait( + ptr, expected, timeout, expectedType, logify(get())); } else { auto* ptr = makePointer(); auto* count = make(i32); @@ -1682,35 +2124,62 @@ private: switch (type) { case i32: { switch (upTo(3)) { - case 0: bytes = 1; break; - case 1: bytes = pick(1, 2); break; - case 2: bytes = pick(1, 2, 4); break; - default: WASM_UNREACHABLE(); + case 0: + bytes = 1; + break; + case 1: + bytes = pick(1, 2); + break; + case 2: + bytes = pick(1, 2, 4); + break; + default: + WASM_UNREACHABLE(); } break; } case i64: { switch (upTo(4)) { - case 0: bytes = 1; break; - case 1: bytes = pick(1, 2); break; - case 2: bytes = pick(1, 2, 4); break; - case 3: bytes = pick(1, 2, 4, 8); break; - default: WASM_UNREACHABLE(); + case 0: + bytes = 1; + break; + case 1: + bytes = pick(1, 2); + break; + case 2: + bytes = pick(1, 2, 4); + break; + case 3: + bytes = pick(1, 2, 4, 8); + break; + default: + WASM_UNREACHABLE(); } break; } - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } auto offset = logify(get()); auto* ptr = makePointer(); if (oneIn(2)) { auto* value = make(type); - return builder.makeAtomicRMW(pick(AtomicRMWOp::Add, AtomicRMWOp::Sub, AtomicRMWOp::And, AtomicRMWOp::Or, AtomicRMWOp::Xor, AtomicRMWOp::Xchg), - bytes, offset, ptr, value, type); + return builder.makeAtomicRMW(pick(AtomicRMWOp::Add, + AtomicRMWOp::Sub, + AtomicRMWOp::And, + AtomicRMWOp::Or, + AtomicRMWOp::Xor, + AtomicRMWOp::Xchg), + bytes, + offset, + ptr, + value, + type); } else { auto* expected = make(type); auto* replacement = make(type); - return builder.makeAtomicCmpxchg(bytes, offset, ptr, expected, replacement, type); + return builder.makeAtomicCmpxchg( + bytes, offset, ptr, expected, replacement, type); } } @@ -1720,12 +2189,18 @@ private: return makeSIMDExtract(type); } switch (upTo(6)) { - case 0: return makeUnary(v128); - case 1: return makeBinary(v128); - case 2: return makeSIMDReplace(); - case 3: return makeSIMDShuffle(); - case 4: return makeSIMDBitselect(); - case 5: return makeSIMDShift(); + case 0: + return makeUnary(v128); + case 1: + return makeBinary(v128); + case 2: + return makeSIMDReplace(); + case 3: + return makeSIMDShuffle(); + case 4: + return makeSIMDBitselect(); + case 5: + return makeSIMDShift(); } WASM_UNREACHABLE(); } @@ -1733,43 +2208,87 @@ private: Expression* makeSIMDExtract(Type type) { auto op = static_cast<SIMDExtractOp>(0); switch (type) { - case i32: op = pick(ExtractLaneSVecI8x16, ExtractLaneUVecI8x16, ExtractLaneSVecI16x8, ExtractLaneUVecI16x8, ExtractLaneVecI32x4); break; - case i64: op = ExtractLaneVecI64x2; break; - case f32: op = ExtractLaneVecF32x4; break; - case f64: op = ExtractLaneVecF64x2; break; + case i32: + op = pick(ExtractLaneSVecI8x16, + ExtractLaneUVecI8x16, + ExtractLaneSVecI16x8, + ExtractLaneUVecI16x8, + ExtractLaneVecI32x4); + break; + case i64: + op = ExtractLaneVecI64x2; + break; + case f32: + op = ExtractLaneVecF32x4; + break; + case f64: + op = ExtractLaneVecF64x2; + break; case v128: case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } Expression* vec = make(v128); uint8_t index = 0; switch (op) { case ExtractLaneSVecI8x16: - case ExtractLaneUVecI8x16: index = upTo(16); break; + case ExtractLaneUVecI8x16: + index = upTo(16); + break; case ExtractLaneSVecI16x8: - case ExtractLaneUVecI16x8: index = upTo(8); break; + case ExtractLaneUVecI16x8: + index = upTo(8); + break; case ExtractLaneVecI32x4: - case ExtractLaneVecF32x4: index = upTo(4); break; + case ExtractLaneVecF32x4: + index = upTo(4); + break; case ExtractLaneVecI64x2: - case ExtractLaneVecF64x2: index = upTo(2); break; + case ExtractLaneVecF64x2: + index = upTo(2); + break; } return builder.makeSIMDExtract(op, vec, index); } Expression* makeSIMDReplace() { - SIMDReplaceOp op = pick(ReplaceLaneVecI8x16, ReplaceLaneVecI16x8, ReplaceLaneVecI32x4, - ReplaceLaneVecI64x2, ReplaceLaneVecF32x4, ReplaceLaneVecF64x2); + SIMDReplaceOp op = pick(ReplaceLaneVecI8x16, + ReplaceLaneVecI16x8, + ReplaceLaneVecI32x4, + ReplaceLaneVecI64x2, + ReplaceLaneVecF32x4, + ReplaceLaneVecF64x2); Expression* vec = make(v128); uint8_t index; Type lane_t; switch (op) { - case ReplaceLaneVecI8x16: index = upTo(16); lane_t = i32; break; - case ReplaceLaneVecI16x8: index = upTo(8); lane_t = i32; break; - case ReplaceLaneVecI32x4: index = upTo(4); lane_t = i32; break; - case ReplaceLaneVecI64x2: index = upTo(2); lane_t = i64; break; - case ReplaceLaneVecF32x4: index = upTo(4); lane_t = f32; break; - case ReplaceLaneVecF64x2: index = upTo(2); lane_t = f64; break; - default: WASM_UNREACHABLE(); + case ReplaceLaneVecI8x16: + index = upTo(16); + lane_t = i32; + break; + case ReplaceLaneVecI16x8: + index = upTo(8); + lane_t = i32; + break; + case ReplaceLaneVecI32x4: + index = upTo(4); + lane_t = i32; + break; + case ReplaceLaneVecI64x2: + index = upTo(2); + lane_t = i64; + break; + case ReplaceLaneVecF32x4: + index = upTo(4); + lane_t = f32; + break; + case ReplaceLaneVecF64x2: + index = upTo(2); + lane_t = f64; + break; + default: + WASM_UNREACHABLE(); } Expression* value = make(lane_t); return builder.makeSIMDReplace(op, vec, index, value); @@ -1793,28 +2312,44 @@ private: } Expression* makeSIMDShift() { - SIMDShiftOp op = pick(ShlVecI8x16, ShrSVecI8x16, ShrUVecI8x16, ShlVecI16x8, ShrSVecI16x8, ShrUVecI16x8, - ShlVecI32x4, ShrSVecI32x4, ShrUVecI32x4, ShlVecI64x2, ShrSVecI64x2, ShrUVecI64x2); + SIMDShiftOp op = pick(ShlVecI8x16, + ShrSVecI8x16, + ShrUVecI8x16, + ShlVecI16x8, + ShrSVecI16x8, + ShrUVecI16x8, + ShlVecI32x4, + ShrSVecI32x4, + ShrUVecI32x4, + ShlVecI64x2, + ShrSVecI64x2, + ShrUVecI64x2); Expression* vec = make(v128); Expression* shift = make(i32); return builder.makeSIMDShift(op, vec, shift); } Expression* makeBulkMemory(Type type) { - if (!allowMemory) return makeTrivial(type); + if (!allowMemory) + return makeTrivial(type); assert(wasm.features.hasBulkMemory()); assert(type == none); switch (upTo(4)) { - case 0: return makeMemoryInit(); - case 1: return makeDataDrop(); - case 2: return makeMemoryCopy(); - case 3: return makeMemoryFill(); + case 0: + return makeMemoryInit(); + case 1: + return makeDataDrop(); + case 2: + return makeMemoryCopy(); + case 3: + return makeMemoryFill(); } WASM_UNREACHABLE(); } Expression* makeMemoryInit() { - if (!allowMemory) return makeTrivial(none); + if (!allowMemory) + return makeTrivial(none); uint32_t segment = upTo(wasm.memory.segments.size()); size_t totalSize = wasm.memory.segments[segment].data.size(); size_t offsetVal = upTo(totalSize); @@ -1826,12 +2361,14 @@ private: } Expression* makeDataDrop() { - if (!allowMemory) return makeTrivial(none); + if (!allowMemory) + return makeTrivial(none); return builder.makeDataDrop(upTo(wasm.memory.segments.size())); } Expression* makeMemoryCopy() { - if (!allowMemory) return makeTrivial(none); + if (!allowMemory) + return makeTrivial(none); Expression* dest = makePointer(); Expression* source = makePointer(); Expression* size = make(i32); @@ -1839,7 +2376,8 @@ private: } Expression* makeMemoryFill() { - if (!allowMemory) return makeTrivial(none); + if (!allowMemory) + return makeTrivial(none); Expression* dest = makePointer(); Expression* value = makePointer(); Expression* size = make(i32); @@ -1850,32 +2388,33 @@ private: Expression* makeLogging() { auto type = pick(i32, i64, f32, f64); - return builder.makeCall(std::string("log-") + printType(type), { make(type) }, none); + return builder.makeCall( + std::string("log-") + printType(type), {make(type)}, none); } Expression* makeMemoryHashLogging() { auto* hash = builder.makeCall(std::string("hashMemory"), {}, i32); - return builder.makeCall(std::string("log-i32"), { hash }, none); + return builder.makeCall(std::string("log-i32"), {hash}, none); } // special getters Type getType() { return pick(FeatureOptions<Type>() - .add(FeatureSet::MVP, i32, i64, f32, f64, none, unreachable) - .add(FeatureSet::SIMD, v128)); + .add(FeatureSet::MVP, i32, i64, f32, f64, none, unreachable) + .add(FeatureSet::SIMD, v128)); } Type getReachableType() { return pick(FeatureOptions<Type>() - .add(FeatureSet::MVP, i32, i64, f32, f64, none) - .add(FeatureSet::SIMD, v128)); + .add(FeatureSet::MVP, i32, i64, f32, f64, none) + .add(FeatureSet::SIMD, v128)); } Type getConcreteType() { return pick(FeatureOptions<Type>() - .add(FeatureSet::MVP, i32, i64, f32, f64) - .add(FeatureSet::SIMD, v128)); + .add(FeatureSet::MVP, i32, i64, f32, f64) + .add(FeatureSet::SIMD, v128)); } // statistical distributions @@ -1889,7 +2428,8 @@ private: // this isn't a perfectly uniform distribution, but it's fast // and reasonable Index upTo(Index x) { - if (x == 0) return 0; + if (x == 0) + return 0; Index raw; if (x <= 255) { raw = get(); @@ -1904,9 +2444,7 @@ private: return ret; } - bool oneIn(Index x) { - return upTo(x) == 0; - } + bool oneIn(Index x) { return upTo(x) == 0; } bool onceEvery(Index x) { static int counter = 0; @@ -1916,69 +2454,60 @@ private: // apply upTo twice, generating a skewed distribution towards // low values - Index upToSquared(Index x) { - return upTo(upTo(x)); - } + Index upToSquared(Index x) { return upTo(upTo(x)); } // pick from a vector - template<typename T> - const T& vectorPick(const std::vector<T>& vec) { + template<typename T> const T& vectorPick(const std::vector<T>& vec) { assert(!vec.empty()); auto index = upTo(vec.size()); return vec[index]; } // pick from a fixed list - template<typename T, typename... Args> - T pick(T first, Args... args) { + template<typename T, typename... Args> T pick(T first, Args... args) { auto num = sizeof...(Args) + 1; auto temp = upTo(num); return pickGivenNum<T>(temp, first, args...); } - template<typename T> - T pickGivenNum(size_t num, T first) { + template<typename T> T pickGivenNum(size_t num, T first) { assert(num == 0); return first; } - // Trick to avoid a bug in GCC 7.x. - // Upstream bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82800 - #define GCC_VERSION (__GNUC__ * 10000 \ - + __GNUC_MINOR__ * 100 \ - + __GNUC_PATCHLEVEL__) - #if GCC_VERSION > 70000 && GCC_VERSION < 70300 - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" - #endif +// Trick to avoid a bug in GCC 7.x. +// Upstream bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82800 +#define GCC_VERSION \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#if GCC_VERSION > 70000 && GCC_VERSION < 70300 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif template<typename T, typename... Args> T pickGivenNum(size_t num, T first, Args... args) { - if (num == 0) return first; + if (num == 0) + return first; return pickGivenNum<T>(num - 1, args...); } - #if GCC_VERSION > 70000 && GCC_VERSION < 70300 - #pragma GCC diagnostic pop - #endif +#if GCC_VERSION > 70000 && GCC_VERSION < 70300 +#pragma GCC diagnostic pop +#endif - template<typename T> - struct FeatureOptions { - template<typename ...Ts> + template<typename T> struct FeatureOptions { + template<typename... Ts> FeatureOptions<T>& add(FeatureSet::Feature feature, T option, Ts... rest) { options[feature].push_back(option); return add(feature, rest...); } - FeatureOptions<T>& add(FeatureSet::Feature feature) { - return *this; - } + FeatureOptions<T>& add(FeatureSet::Feature feature) { return *this; } std::map<FeatureSet::Feature, std::vector<T>> options; }; - template<typename T> - const T pick(FeatureOptions<T>& picker) { + template<typename T> const T pick(FeatureOptions<T>& picker) { std::vector<T> matches; for (const auto& item : picker.options) { if (wasm.features.has(item.first)) { @@ -2012,6 +2541,7 @@ private: } // namespace wasm -// XXX Switch class has a condition?! is it real? should the node type be the value type if it exists?! +// XXX Switch class has a condition?! is it real? should the node type be the +// value type if it exists?! // TODO copy an existing function and replace just one node in it |