diff options
Diffstat (limited to 'src')
198 files changed, 22447 insertions, 13655 deletions
diff --git a/src/abi/js.h b/src/abi/js.h index 6994a4291..8391346c8 100644 --- a/src/abi/js.h +++ b/src/abi/js.h @@ -17,17 +17,14 @@ #ifndef wasm_abi_abi_h #define wasm_abi_abi_h -#include "wasm.h" #include "asmjs/shared-constants.h" +#include "wasm.h" namespace wasm { namespace ABI { -enum class LegalizationLevel { - Full = 0, - Minimal = 1 -}; +enum class LegalizationLevel { Full = 0, Minimal = 1 }; inline std::string getLegalizationPass(LegalizationLevel level) { if (level == LegalizationLevel::Full) { @@ -39,51 +36,52 @@ inline std::string getLegalizationPass(LegalizationLevel level) { namespace wasm2js { -extern cashew::IString SCRATCH_LOAD_I32, - SCRATCH_STORE_I32, - SCRATCH_LOAD_I64, - SCRATCH_STORE_I64, - SCRATCH_LOAD_F32, - SCRATCH_STORE_F32, - SCRATCH_LOAD_F64, - SCRATCH_STORE_F64; +extern cashew::IString SCRATCH_LOAD_I32; +extern cashew::IString SCRATCH_STORE_I32; +extern cashew::IString SCRATCH_LOAD_I64; +extern cashew::IString SCRATCH_STORE_I64; +extern cashew::IString SCRATCH_LOAD_F32; +extern cashew::IString SCRATCH_STORE_F32; +extern cashew::IString SCRATCH_LOAD_F64; +extern cashew::IString SCRATCH_STORE_F64; // The wasm2js scratch memory helpers let us read and write to scratch memory // for purposes of implementing things like reinterpret, etc. // The optional "specific" parameter is a specific function we want. If not // provided, we create them all. -inline void ensureScratchMemoryHelpers(Module* wasm, cashew::IString specific = cashew::IString()) { - auto ensureImport = [&](Name name, const std::vector<Type> params, Type result) { - if (wasm->getFunctionOrNull(name)) return; - if (specific.is() && name != specific) return; - auto func = make_unique<Function>(); - func->name = name; - func->params = params; - func->result = result; - func->module = ENV; - func->base = name; - wasm->addFunction(std::move(func)); - }; +inline void +ensureScratchMemoryHelpers(Module* wasm, + cashew::IString specific = cashew::IString()) { + auto ensureImport = + [&](Name name, const std::vector<Type> params, Type result) { + if (wasm->getFunctionOrNull(name)) + return; + if (specific.is() && name != specific) + return; + auto func = make_unique<Function>(); + func->name = name; + func->params = params; + func->result = result; + func->module = ENV; + func->base = name; + wasm->addFunction(std::move(func)); + }; - ensureImport(SCRATCH_LOAD_I32, { i32 }, i32); - ensureImport(SCRATCH_STORE_I32, { i32, i32 }, none); + ensureImport(SCRATCH_LOAD_I32, {i32}, i32); + ensureImport(SCRATCH_STORE_I32, {i32, i32}, none); ensureImport(SCRATCH_LOAD_I64, {}, i64); - ensureImport(SCRATCH_STORE_I64, { i64 }, none); + ensureImport(SCRATCH_STORE_I64, {i64}, none); ensureImport(SCRATCH_LOAD_F32, {}, f32); - ensureImport(SCRATCH_STORE_F32, { f32 }, none); + ensureImport(SCRATCH_STORE_F32, {f32}, none); ensureImport(SCRATCH_LOAD_F64, {}, f64); - ensureImport(SCRATCH_STORE_F64, { f64 }, none); + ensureImport(SCRATCH_STORE_F64, {f64}, none); } inline bool isScratchMemoryHelper(cashew::IString name) { - return name == SCRATCH_LOAD_I32 || - name == SCRATCH_STORE_I32 || - name == SCRATCH_LOAD_I64 || - name == SCRATCH_STORE_I64 || - name == SCRATCH_LOAD_F32 || - name == SCRATCH_STORE_F32 || - name == SCRATCH_LOAD_F64 || - name == SCRATCH_STORE_F64; + return name == SCRATCH_LOAD_I32 || name == SCRATCH_STORE_I32 || + name == SCRATCH_LOAD_I64 || name == SCRATCH_STORE_I64 || + name == SCRATCH_LOAD_F32 || name == SCRATCH_STORE_F32 || + name == SCRATCH_LOAD_F64 || name == SCRATCH_STORE_F64; } } // namespace wasm2js diff --git a/src/abi/stack.h b/src/abi/stack.h index 77e166c2a..f84c15d90 100644 --- a/src/abi/stack.h +++ b/src/abi/stack.h @@ -17,31 +17,31 @@ #ifndef wasm_abi_stack_h #define wasm_abi_stack_h -#include "wasm.h" -#include "wasm-builder.h" -#include "shared-constants.h" +#include "abi.h" #include "asmjs/shared-constants.h" #include "ir/find_all.h" #include "ir/global-utils.h" -#include "abi.h" +#include "shared-constants.h" +#include "wasm-builder.h" +#include "wasm.h" namespace wasm { namespace ABI { -enum { - StackAlign = 16 -}; +enum { StackAlign = 16 }; inline Index stackAlign(Index size) { return (size + StackAlign - 1) & -StackAlign; } // Allocate some space on the stack, and assign it to a local. -// The local will have the same constant value in all the function, so you can just -// local.get it anywhere there. -inline void getStackSpace(Index local, Function* func, Index size, Module& wasm) { - auto* stackPointer = GlobalUtils::getGlobalInitializedToImport(wasm, ENV, "STACKTOP"); +// The local will have the same constant value in all the function, so you can +// just local.get it anywhere there. +inline void +getStackSpace(Index local, Function* func, Index size, Module& wasm) { + auto* stackPointer = + GlobalUtils::getGlobalInitializedToImport(wasm, ENV, "STACKTOP"); if (!stackPointer) { Fatal() << "getStackSpace: failed to find the stack pointer"; } @@ -50,34 +50,21 @@ inline void getStackSpace(Index local, Function* func, Index size, Module& wasm) // TODO: find existing stack usage, and add on top of that - carefully Builder builder(wasm); auto* block = builder.makeBlock(); - block->list.push_back( - builder.makeSetLocal( - local, - builder.makeGetGlobal(stackPointer->name, PointerType) - ) - ); + block->list.push_back(builder.makeSetLocal( + local, builder.makeGetGlobal(stackPointer->name, PointerType))); // TODO: add stack max check Expression* added; if (PointerType == i32) { - added = builder.makeBinary( - AddInt32, - builder.makeGetLocal(local, PointerType), - builder.makeConst(Literal(int32_t(size))) - ); + added = builder.makeBinary(AddInt32, + builder.makeGetLocal(local, PointerType), + builder.makeConst(Literal(int32_t(size)))); } else { WASM_UNREACHABLE(); } - block->list.push_back( - builder.makeSetGlobal( - stackPointer->name, - added - ) - ); + block->list.push_back(builder.makeSetGlobal(stackPointer->name, added)); auto makeStackRestore = [&]() { - return builder.makeSetGlobal( - stackPointer->name, - builder.makeGetLocal(local, PointerType) - ); + return builder.makeSetGlobal(stackPointer->name, + builder.makeGetLocal(local, PointerType)); }; // add stack restores to the returns FindAllPointers<Return> finder(func->body); @@ -89,9 +76,8 @@ inline void getStackSpace(Index local, Function* func, Index size, Module& wasm) auto temp = builder.addVar(func, ret->value->type); block->list.push_back(builder.makeSetLocal(temp, ret->value)); block->list.push_back(makeStackRestore()); - block->list.push_back(builder.makeReturn( - builder.makeGetLocal(temp, ret->value->type) - )); + block->list.push_back( + builder.makeReturn(builder.makeGetLocal(temp, ret->value->type))); block->finalize(); *ptr = block; } else { diff --git a/src/abi/wasm-object.h b/src/abi/wasm-object.h index 0ce814030..d12e1f581 100644 --- a/src/abi/wasm-object.h +++ b/src/abi/wasm-object.h @@ -25,14 +25,14 @@ namespace wasm { namespace ABI { - enum LinkType : unsigned { - WASM_STACK_POINTER = 0x1, - WASM_SYMBOL_INFO = 0x2, - WASM_DATA_SIZE = 0x3, - WASM_DATA_ALIGNMENT = 0x4, - WASM_SEGMENT_INFO = 0x5, - WASM_INIT_FUNCS = 0x6, - }; +enum LinkType : unsigned { + WASM_STACK_POINTER = 0x1, + WASM_SYMBOL_INFO = 0x2, + WASM_DATA_SIZE = 0x3, + WASM_DATA_ALIGNMENT = 0x4, + WASM_SEGMENT_INFO = 0x5, + WASM_INIT_FUNCS = 0x6, +}; } // namespace ABI } // namespace wasm diff --git a/src/asm2wasm.h b/src/asm2wasm.h index a981cb660..ba6a3df57 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -22,15 +22,10 @@ #ifndef wasm_asm2wasm_h #define wasm_asm2wasm_h -#include "wasm.h" -#include "emscripten-optimizer/optimizer.h" -#include "mixed_arena.h" -#include "shared-constants.h" -#include "asmjs/shared-constants.h" +#include "abi/js.h" #include "asm_v_wasm.h" -#include "passes/passes.h" -#include "pass.h" -#include "parsing.h" +#include "asmjs/shared-constants.h" +#include "emscripten-optimizer/optimizer.h" #include "ir/bits.h" #include "ir/branch-utils.h" #include "ir/function-type-utils.h" @@ -38,10 +33,15 @@ #include "ir/module-utils.h" #include "ir/trapping.h" #include "ir/utils.h" +#include "mixed_arena.h" +#include "parsing.h" +#include "pass.h" +#include "passes/passes.h" +#include "shared-constants.h" #include "wasm-builder.h" #include "wasm-emscripten.h" #include "wasm-module-building.h" -#include "abi/js.h" +#include "wasm.h" namespace wasm { @@ -49,90 +49,90 @@ using namespace cashew; // Names -Name I32_CTTZ("i32_cttz"), - I32_CTPOP("i32_ctpop"), - I32_BC2F("i32_bc2f"), - I32_BC2I("i32_bc2i"), - I64("i64"), - I64_CONST("i64_const"), - I64_ADD("i64_add"), - I64_SUB("i64_sub"), - I64_MUL("i64_mul"), - I64_UDIV("i64_udiv"), - I64_SDIV("i64_sdiv"), - I64_UREM("i64_urem"), - I64_SREM("i64_srem"), - I64_AND("i64_and"), - I64_OR("i64_or"), - I64_XOR("i64_xor"), - I64_SHL("i64_shl"), - I64_ASHR("i64_ashr"), - I64_LSHR("i64_lshr"), - I64_EQ("i64_eq"), - I64_NE("i64_ne"), - I64_ULE("i64_ule"), - I64_SLE("i64_sle"), - I64_UGE("i64_uge"), - I64_SGE("i64_sge"), - I64_ULT("i64_ult"), - I64_SLT("i64_slt"), - I64_UGT("i64_ugt"), - I64_SGT("i64_sgt"), - I64_TRUNC("i64_trunc"), - I64_SEXT("i64_sext"), - I64_ZEXT("i64_zext"), - I64_S2F("i64_s2f"), - I64_S2D("i64_s2d"), - I64_U2F("i64_u2f"), - I64_U2D("i64_u2d"), - I64_F2S("i64_f2s"), - I64_D2S("i64_d2s"), - I64_F2U("i64_f2u"), - I64_D2U("i64_d2u"), - I64_BC2D("i64_bc2d"), - I64_BC2I("i64_bc2i"), - I64_CTTZ("i64_cttz"), - I64_CTLZ("i64_ctlz"), - I64_CTPOP("i64_ctpop"), - F32_COPYSIGN("f32_copysign"), - F64_COPYSIGN("f64_copysign"), - LOAD1("load1"), - LOAD2("load2"), - LOAD4("load4"), - LOAD8("load8"), - LOADF("loadf"), - LOADD("loadd"), - STORE1("store1"), - STORE2("store2"), - STORE4("store4"), - STORE8("store8"), - STOREF("storef"), - STORED("stored"), - FTCALL("ftCall_"), - MFTCALL("mftCall_"), - MAX_("max"), - MIN_("min"), - ATOMICS("Atomics"), - ATOMICS_LOAD("load"), - ATOMICS_STORE("store"), - ATOMICS_EXCHANGE("exchange"), - ATOMICS_COMPARE_EXCHANGE("compareExchange"), - ATOMICS_ADD("add"), - ATOMICS_SUB("sub"), - ATOMICS_AND("and"), - ATOMICS_OR("or"), - ATOMICS_XOR("xor"), - I64_ATOMICS_LOAD("i64_atomics_load"), - I64_ATOMICS_STORE("i64_atomics_store"), - I64_ATOMICS_AND("i64_atomics_and"), - I64_ATOMICS_OR("i64_atomics_or"), - I64_ATOMICS_XOR("i64_atomics_xor"), - I64_ATOMICS_ADD("i64_atomics_add"), - I64_ATOMICS_SUB("i64_atomics_sub"), - I64_ATOMICS_EXCHANGE("i64_atomics_exchange"), - I64_ATOMICS_COMPAREEXCHANGE("i64_atomics_compareExchange"), - TEMP_DOUBLE_PTR("tempDoublePtr"), - EMSCRIPTEN_DEBUGINFO("emscripten_debuginfo"); +Name I32_CTTZ("i32_cttz"); +Name I32_CTPOP("i32_ctpop"); +Name I32_BC2F("i32_bc2f"); +Name I32_BC2I("i32_bc2i"); +Name I64("i64"); +Name I64_CONST("i64_const"); +Name I64_ADD("i64_add"); +Name I64_SUB("i64_sub"); +Name I64_MUL("i64_mul"); +Name I64_UDIV("i64_udiv"); +Name I64_SDIV("i64_sdiv"); +Name I64_UREM("i64_urem"); +Name I64_SREM("i64_srem"); +Name I64_AND("i64_and"); +Name I64_OR("i64_or"); +Name I64_XOR("i64_xor"); +Name I64_SHL("i64_shl"); +Name I64_ASHR("i64_ashr"); +Name I64_LSHR("i64_lshr"); +Name I64_EQ("i64_eq"); +Name I64_NE("i64_ne"); +Name I64_ULE("i64_ule"); +Name I64_SLE("i64_sle"); +Name I64_UGE("i64_uge"); +Name I64_SGE("i64_sge"); +Name I64_ULT("i64_ult"); +Name I64_SLT("i64_slt"); +Name I64_UGT("i64_ugt"); +Name I64_SGT("i64_sgt"); +Name I64_TRUNC("i64_trunc"); +Name I64_SEXT("i64_sext"); +Name I64_ZEXT("i64_zext"); +Name I64_S2F("i64_s2f"); +Name I64_S2D("i64_s2d"); +Name I64_U2F("i64_u2f"); +Name I64_U2D("i64_u2d"); +Name I64_F2S("i64_f2s"); +Name I64_D2S("i64_d2s"); +Name I64_F2U("i64_f2u"); +Name I64_D2U("i64_d2u"); +Name I64_BC2D("i64_bc2d"); +Name I64_BC2I("i64_bc2i"); +Name I64_CTTZ("i64_cttz"); +Name I64_CTLZ("i64_ctlz"); +Name I64_CTPOP("i64_ctpop"); +Name F32_COPYSIGN("f32_copysign"); +Name F64_COPYSIGN("f64_copysign"); +Name LOAD1("load1"); +Name LOAD2("load2"); +Name LOAD4("load4"); +Name LOAD8("load8"); +Name LOADF("loadf"); +Name LOADD("loadd"); +Name STORE1("store1"); +Name STORE2("store2"); +Name STORE4("store4"); +Name STORE8("store8"); +Name STOREF("storef"); +Name STORED("stored"); +Name FTCALL("ftCall_"); +Name MFTCALL("mftCall_"); +Name MAX_("max"); +Name MIN_("min"); +Name ATOMICS("Atomics"); +Name ATOMICS_LOAD("load"); +Name ATOMICS_STORE("store"); +Name ATOMICS_EXCHANGE("exchange"); +Name ATOMICS_COMPARE_EXCHANGE("compareExchange"); +Name ATOMICS_ADD("add"); +Name ATOMICS_SUB("sub"); +Name ATOMICS_AND("and"); +Name ATOMICS_OR("or"); +Name ATOMICS_XOR("xor"); +Name I64_ATOMICS_LOAD("i64_atomics_load"); +Name I64_ATOMICS_STORE("i64_atomics_store"); +Name I64_ATOMICS_AND("i64_atomics_and"); +Name I64_ATOMICS_OR("i64_atomics_or"); +Name I64_ATOMICS_XOR("i64_atomics_xor"); +Name I64_ATOMICS_ADD("i64_atomics_add"); +Name I64_ATOMICS_SUB("i64_atomics_sub"); +Name I64_ATOMICS_EXCHANGE("i64_atomics_exchange"); +Name I64_ATOMICS_COMPAREEXCHANGE("i64_atomics_compareExchange"); +Name TEMP_DOUBLE_PTR("tempDoublePtr"); +Name EMSCRIPTEN_DEBUGINFO("emscripten_debuginfo"); // Utilities @@ -147,22 +147,16 @@ static void abort_on(std::string why, IString element) { abort(); } -Index indexOr(Index x, Index y) { - return x ? x : y; -} +Index indexOr(Index x, Index y) { return x ? x : y; } // useful when we need to see our parent, in an asm.js expression stack struct AstStackHelper { static std::vector<Ref> astStack; - AstStackHelper(Ref curr) { - astStack.push_back(curr); - } - ~AstStackHelper() { - astStack.pop_back(); - } + AstStackHelper(Ref curr) { astStack.push_back(curr); } + ~AstStackHelper() { astStack.pop_back(); } Ref getParent() { if (astStack.size() >= 2) { - return astStack[astStack.size()-2]; + return astStack[astStack.size() - 2]; } else { return Ref(); } @@ -171,11 +165,14 @@ struct AstStackHelper { std::vector<Ref> AstStackHelper::astStack; -static bool startsWith(const char* string, const char *prefix) { +static bool startsWith(const char* string, const char* prefix) { while (1) { - if (*prefix == 0) return true; - if (*string == 0) return false; - if (*string++ != *prefix++) return false; + if (*prefix == 0) + return true; + if (*string == 0) + return false; + if (*string++ != *prefix++) + return false; } }; @@ -194,7 +191,8 @@ struct Asm2WasmPreProcessor { char* allocatedCopy = nullptr; ~Asm2WasmPreProcessor() { - if (allocatedCopy) free(allocatedCopy); + if (allocatedCopy) + free(allocatedCopy); } char* process(char* input) { @@ -211,31 +209,38 @@ struct Asm2WasmPreProcessor { input++; num--; } - char *end = input + num - 1; + char* end = input + num - 1; while (*end != '}') { *end = 0; end--; } } - // asm.js memory growth uses a quite elaborate pattern. Instead of parsing and - // matching it, we do a simpler detection on emscripten's asm.js output format + // asm.js memory growth uses a quite elaborate pattern. Instead of parsing + // and matching it, we do a simpler detection on emscripten's asm.js output + // format const char* START_FUNCS = "// EMSCRIPTEN_START_FUNCS"; - char *marker = strstr(input, START_FUNCS); + char* marker = strstr(input, START_FUNCS); if (marker) { - *marker = 0; // look for memory growth code just up to here, as an optimization + // look for memory growth code just up to here, as an optimization + *marker = 0; } - char *growthSign = strstr(input, "return true;"); // this can only show up in growth code, as normal asm.js lacks "true" + // this can only show up in growth code, as normal asm.js lacks "true" + char* growthSign = strstr(input, "return true;"); if (growthSign) { memoryGrowth = true; // clean out this function, we don't need it. first where it starts - char *growthFuncStart = growthSign; - while (*growthFuncStart != '{') growthFuncStart--; // skip body - while (*growthFuncStart != '(') growthFuncStart--; // skip params - while (*growthFuncStart != ' ') growthFuncStart--; // skip function name - while (*growthFuncStart != 'f') growthFuncStart--; // skip 'function' + char* growthFuncStart = growthSign; + while (*growthFuncStart != '{') + growthFuncStart--; // skip body + while (*growthFuncStart != '(') + growthFuncStart--; // skip params + while (*growthFuncStart != ' ') + growthFuncStart--; // skip function name + while (*growthFuncStart != 'f') + growthFuncStart--; // skip 'function' assert(strstr(growthFuncStart, "function ") == growthFuncStart); - char *growthFuncEnd = strchr(growthSign, '}'); + char* growthFuncEnd = strchr(growthSign, '}'); assert(growthFuncEnd > growthFuncStart + 5); growthFuncStart[0] = '/'; growthFuncStart[1] = '*'; @@ -257,8 +262,12 @@ struct Asm2WasmPreProcessor { // that, we can apply the debug info to the wasm node right // before it - this is guaranteed to be correct without opts, // and is usually decently accurate with them. - const auto SCALE_FACTOR = 1.25; // an upper bound on how much more space we need as a multiple of the original - const auto ADD_FACTOR = 100; // an upper bound on how much we write for each debug info element itself + + // an upper bound on how much more space we need as a multiple of the + // original + const auto SCALE_FACTOR = 1.25; + // an upper bound on how much we write for each debug info element itself + const auto ADD_FACTOR = 100; auto size = strlen(input); auto upperBound = Index(size * SCALE_FACTOR) + ADD_FACTOR; char* copy = allocatedCopy = (char*)malloc(upperBound); @@ -321,15 +330,18 @@ struct Asm2WasmPreProcessor { out += line.size(); *out++ = ')'; *out++ = ';'; - } else if (!seenUseAsm && (startsWith(input, "asm'") || startsWith(input, "asm\""))) { + } else if (!seenUseAsm && + (startsWith(input, "asm'") || startsWith(input, "asm\""))) { // end of "use asm" or "almost asm" - const auto SKIP = 5; // skip the end of "use asm"; (5 chars, a,s,m," or ',;) + // skip the end of "use asm"; (5 chars, a,s,m," or ',;) + const auto SKIP = 5; seenUseAsm = true; memcpy(out, input, SKIP); out += SKIP; input += SKIP; // add a fake import for the intrinsic, so the module validates - std::string import = "\n var emscripten_debuginfo = env.emscripten_debuginfo;"; + std::string import = + "\n var emscripten_debuginfo = env.emscripten_debuginfo;"; strcpy(out, import.c_str()); out += import.size(); } else { @@ -356,21 +368,21 @@ static Call* checkDebugInfo(Expression* curr) { return nullptr; } -// Debug info appears in the ast as calls to the debug intrinsic. These are usually -// after the relevant node. We adjust them to a position that is not dce-able, so that -// they are not trivially removed when optimizing. -struct AdjustDebugInfo : public WalkerPass<PostWalker<AdjustDebugInfo, Visitor<AdjustDebugInfo>>> { +// Debug info appears in the ast as calls to the debug intrinsic. These are +// usually after the relevant node. We adjust them to a position that is not +// dce-able, so that they are not trivially removed when optimizing. +struct AdjustDebugInfo + : public WalkerPass<PostWalker<AdjustDebugInfo, Visitor<AdjustDebugInfo>>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new AdjustDebugInfo(); } - AdjustDebugInfo() { - name = "adjust-debug-info"; - } + AdjustDebugInfo() { name = "adjust-debug-info"; } void visitBlock(Block* curr) { // look for a debug info call that is unreachable - if (curr->list.size() == 0) return; + if (curr->list.size() == 0) + return; auto* back = curr->list.back(); for (Index i = 1; i < curr->list.size(); i++) { if (checkDebugInfo(curr->list[i]) && !checkDebugInfo(curr->list[i - 1])) { @@ -393,7 +405,7 @@ class Asm2WasmBuilder { public: Module& wasm; - MixedArena &allocator; + MixedArena& allocator; Builder builder; @@ -403,15 +415,20 @@ public: struct MappedGlobal { Type type; - bool import; // if true, this is an import - we should read the value, not just set a zero + // if true, this is an import - we should read the value, not just set a + // zero + bool import; IString module, base; MappedGlobal() : type(none), import(false) {} MappedGlobal(Type type) : type(type), import(false) {} - MappedGlobal(Type type, bool import, IString module, IString base) : type(type), import(import), module(module), base(base) {} + MappedGlobal(Type type, bool import, IString module, IString base) + : type(type), import(import), module(module), base(base) {} }; // function table - std::map<IString, int> functionTableStarts; // each asm function table gets a range in the one wasm table, starting at a location + // each asm function table gets a range in the one wasm table, starting at a + // location + std::map<IString, int> functionTableStarts; Asm2WasmPreProcessor& preprocessor; bool debug; @@ -426,18 +443,14 @@ public: std::map<IString, MappedGlobal> mappedGlobals; private: - void allocateGlobal(IString name, Type type, Literal value=Literal()) { + void allocateGlobal(IString name, Type type, Literal value = Literal()) { assert(mappedGlobals.find(name) == mappedGlobals.end()); if (value.type == none) { value = Literal::makeZero(type); } mappedGlobals.emplace(name, MappedGlobal(type)); wasm.addGlobal(builder.makeGlobal( - name, - type, - builder.makeConst(value), - Builder::Mutable - )); + name, type, builder.makeConst(value), Builder::Mutable)); } struct View { @@ -445,7 +458,8 @@ private: bool integer, signed_; AsmType type; View() : bytes(0) {} - View(unsigned bytes, bool integer, bool signed_, AsmType type) : bytes(bytes), integer(integer), signed_(signed_), type(type) {} + View(unsigned bytes, bool integer, bool signed_, AsmType type) + : bytes(bytes), integer(integer), signed_(signed_), type(type) {} }; std::map<IString, View> views; // name (e.g. HEAP8) => view info @@ -489,18 +503,20 @@ private: assert(ast[0] == CALL && ast[1]->isString()); IString importName = ast[1]->getIString(); auto type = make_unique<FunctionType>(); - type->name = IString((std::string("type$") + importName.str).c_str(), false); // TODO: make a list of such types + type->name = IString((std::string("type$") + importName.str).c_str(), + false); // TODO: make a list of such types type->result = resultType; for (auto* operand : call->operands) { type->params.push_back(operand->type); } - // if we already saw this signature, verify it's the same (or else handle that) + // if we already saw this signature, verify it's the same (or else handle + // that) if (importedFunctionTypes.find(importName) != importedFunctionTypes.end()) { FunctionType* previous = importedFunctionTypes[importName].get(); if (*type != *previous) { - // merge it in. we'll add on extra 0 parameters for ones not actually used, and upgrade types to - // double where there is a conflict (which is ok since in JS, double can contain everything - // i32 and f32 can). + // merge it in. we'll add on extra 0 parameters for ones not actually + // used, and upgrade types to double where there is a conflict (which is + // ok since in JS, double can contain everything i32 and f32 can). for (size_t i = 0; i < type->params.size(); i++) { if (previous->params.size() > i) { if (previous->params[i] == none) { @@ -512,7 +528,8 @@ private: previous->params.push_back(type->params[i]); // add a new param } } - // we accept none and a concrete type, but two concrete types mean we need to use an f64 to contain anything + // we accept none and a concrete type, but two concrete types mean we + // need to use an f64 to contain anything if (previous->result == none) { previous->result = type->result; // use a more concrete type } else if (previous->result != type->result && type->result != none) { @@ -527,8 +544,9 @@ private: Type getResultTypeOfCallUsingParent(Ref parent, AsmData* data) { auto result = none; if (!!parent) { - // if the parent is a seq, we cannot be the last element in it (we would have a coercion, which would be - // the parent), so we must be (us, somethingElse), and so our return is void + // if the parent is a seq, we cannot be the last element in it (we would + // have a coercion, which would be the parent), so we must be (us, + // somethingElse), and so our return is void if (parent[0] != SEQ) { result = detectWasmType(parent, data); } @@ -536,29 +554,31 @@ private: return result; } - FunctionType* getFunctionType(Ref parent, ExpressionList& operands, AsmData* data) { + FunctionType* + getFunctionType(Ref parent, ExpressionList& operands, AsmData* data) { Type result = getResultTypeOfCallUsingParent(parent, data); return ensureFunctionType(getSig(result, operands), &wasm); } public: - Asm2WasmBuilder(Module& wasm, Asm2WasmPreProcessor& preprocessor, bool debug, TrapMode trapMode, PassOptions passOptions, bool legalizeJavaScriptFFI, bool runOptimizationPasses, bool wasmOnly) - : wasm(wasm), - allocator(wasm.allocator), - builder(wasm), - preprocessor(preprocessor), - debug(debug), - trapMode(trapMode), - trappingFunctions(trapMode, wasm, /* immediate = */ true), - passOptions(passOptions), - legalizeJavaScriptFFI(legalizeJavaScriptFFI), - runOptimizationPasses(runOptimizationPasses), - wasmOnly(wasmOnly) {} - - void processAsm(Ref ast); + Asm2WasmBuilder(Module& wasm, + Asm2WasmPreProcessor& preprocessor, + bool debug, + TrapMode trapMode, + PassOptions passOptions, + bool legalizeJavaScriptFFI, + bool runOptimizationPasses, + bool wasmOnly) + : wasm(wasm), allocator(wasm.allocator), builder(wasm), + preprocessor(preprocessor), debug(debug), trapMode(trapMode), + trappingFunctions(trapMode, wasm, /* immediate = */ true), + passOptions(passOptions), legalizeJavaScriptFFI(legalizeJavaScriptFFI), + runOptimizationPasses(runOptimizationPasses), wasmOnly(wasmOnly) {} + + void processAsm(Ref ast); private: - AsmType detectAsmType(Ref ast, AsmData *data) { + AsmType detectAsmType(Ref ast, AsmData* data) { if (ast->isString()) { IString name = ast->getIString(); if (!data->isLocal(name)) { @@ -576,7 +596,7 @@ private: return detectType(ast, data, false, Math_fround, wasmOnly); } - Type detectWasmType(Ref ast, AsmData *data) { + Type detectWasmType(Ref ast, AsmData* data) { return asmToWasmType(detectAsmType(ast, data)); } @@ -586,27 +606,53 @@ private: bool isParentUnsignedCoercion(Ref parent) { // parent may not exist, or may be a non-relevant node - if (!!parent && parent->isArray() && parent[0] == BINARY && isUnsignedCoercion(parent)) { + if (!!parent && parent->isArray() && parent[0] == BINARY && + isUnsignedCoercion(parent)) { return true; } return false; } - BinaryOp parseAsmBinaryOp(IString op, Ref left, Ref right, Expression* leftWasm, Expression* rightWasm) { + BinaryOp parseAsmBinaryOp(IString op, + Ref left, + Ref right, + Expression* leftWasm, + Expression* rightWasm) { Type leftType = leftWasm->type; bool isInteger = leftType == Type::i32; - if (op == PLUS) return isInteger ? BinaryOp::AddInt32 : (leftType == f32 ? BinaryOp::AddFloat32 : BinaryOp::AddFloat64); - if (op == MINUS) return isInteger ? BinaryOp::SubInt32 : (leftType == f32 ? BinaryOp::SubFloat32 : BinaryOp::SubFloat64); - if (op == MUL) return isInteger ? BinaryOp::MulInt32 : (leftType == f32 ? BinaryOp::MulFloat32 : BinaryOp::MulFloat64); - if (op == AND) return BinaryOp::AndInt32; - if (op == OR) return BinaryOp::OrInt32; - if (op == XOR) return BinaryOp::XorInt32; - if (op == LSHIFT) return BinaryOp::ShlInt32; - if (op == RSHIFT) return BinaryOp::ShrSInt32; - if (op == TRSHIFT) return BinaryOp::ShrUInt32; - if (op == EQ) return isInteger ? BinaryOp::EqInt32 : (leftType == f32 ? BinaryOp::EqFloat32 : BinaryOp::EqFloat64); - if (op == NE) return isInteger ? BinaryOp::NeInt32 : (leftType == f32 ? BinaryOp::NeFloat32 : BinaryOp::NeFloat64); + if (op == PLUS) + return isInteger ? BinaryOp::AddInt32 + : (leftType == f32 ? BinaryOp::AddFloat32 + : BinaryOp::AddFloat64); + if (op == MINUS) + return isInteger ? BinaryOp::SubInt32 + : (leftType == f32 ? BinaryOp::SubFloat32 + : BinaryOp::SubFloat64); + if (op == MUL) + return isInteger ? BinaryOp::MulInt32 + : (leftType == f32 ? BinaryOp::MulFloat32 + : BinaryOp::MulFloat64); + if (op == AND) + return BinaryOp::AndInt32; + if (op == OR) + return BinaryOp::OrInt32; + if (op == XOR) + return BinaryOp::XorInt32; + if (op == LSHIFT) + return BinaryOp::ShlInt32; + if (op == RSHIFT) + return BinaryOp::ShrSInt32; + if (op == TRSHIFT) + return BinaryOp::ShrUInt32; + if (op == EQ) + return isInteger + ? BinaryOp::EqInt32 + : (leftType == f32 ? BinaryOp::EqFloat32 : BinaryOp::EqFloat64); + if (op == NE) + return isInteger + ? BinaryOp::NeInt32 + : (leftType == f32 ? BinaryOp::NeFloat32 : BinaryOp::NeFloat64); bool isUnsigned = isUnsignedCoercion(left) || isUnsignedCoercion(right); @@ -620,7 +666,8 @@ private: if (isInteger) { return isUnsigned ? BinaryOp::RemUInt32 : BinaryOp::RemSInt32; } - return BinaryOp::RemSInt32; // XXX no floating-point remainder op, this must be handled by the caller + return BinaryOp::RemSInt32; // XXX no floating-point remainder op, this + // must be handled by the caller } if (op == GE) { if (isInteger) { @@ -652,10 +699,14 @@ private: int32_t bytesToShift(unsigned bytes) { switch (bytes) { - case 1: return 0; - case 2: return 1; - case 4: return 2; - case 8: return 3; + case 1: + return 0; + case 2: + return 1; + case 4: + return 2; + case 8: + return 3; default: {} } abort(); @@ -677,17 +728,22 @@ private: } if (ast[1] == MINUS && ast[2]->isNumber()) { double num = -ast[2]->getNumber(); - if (isSInteger32(num)) return Literal((int32_t)num); - if (isUInteger32(num)) return Literal((uint32_t)num); + if (isSInteger32(num)) + return Literal((int32_t)num); + if (isUInteger32(num)) + return Literal((uint32_t)num); assert(false && "expected signed or unsigned int32"); } - if (ast[1] == PLUS && ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == MINUS && ast[2][2]->isNumber()) { + if (ast[1] == PLUS && ast[2]->isArray(UNARY_PREFIX) && + ast[2][1] == MINUS && ast[2][2]->isNumber()) { return Literal((double)-ast[2][2]->getNumber()); } - if (ast[1] == MINUS && ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == PLUS && ast[2][2]->isNumber()) { + if (ast[1] == MINUS && ast[2]->isArray(UNARY_PREFIX) && + ast[2][1] == PLUS && ast[2][2]->isNumber()) { return Literal((double)-ast[2][2]->getNumber()); } - } else if (wasmOnly && ast->isArray(CALL) && ast[1]->isString() && ast[1] == I64_CONST) { + } else if (wasmOnly && ast->isArray(CALL) && ast[1]->isString() && + ast[1] == I64_CONST) { uint64_t low = ast[2][0]->getNumber(); uint64_t high = ast[2][1]->getNumber(); return Literal(uint64_t(low + (high << 32))); @@ -702,18 +758,25 @@ private: } void fixCallType(Expression* call, Type type) { - if (call->is<Call>()) call->cast<Call>()->type = type; - else if (call->is<CallIndirect>()) call->cast<CallIndirect>()->type = type; + if (call->is<Call>()) + call->cast<Call>()->type = type; + else if (call->is<CallIndirect>()) + call->cast<CallIndirect>()->type = type; } - FunctionType* getBuiltinFunctionType(Name module, Name base, ExpressionList* operands = nullptr) { + FunctionType* getBuiltinFunctionType(Name module, + Name base, + ExpressionList* operands = nullptr) { if (module == GLOBAL_MATH) { if (base == ABS) { assert(operands && operands->size() == 1); Type type = (*operands)[0]->type; - if (type == i32) return ensureFunctionType("ii", &wasm); - if (type == f32) return ensureFunctionType("ff", &wasm); - if (type == f64) return ensureFunctionType("dd", &wasm); + if (type == i32) + return ensureFunctionType("ii", &wasm); + if (type == f32) + return ensureFunctionType("ff", &wasm); + if (type == f64) + return ensureFunctionType("dd", &wasm); } } return nullptr; @@ -721,7 +784,8 @@ private: // ensure a nameless block Block* blockify(Expression* expression) { - if (expression->is<Block>() && !expression->cast<Block>()->name.is()) return expression->dynCast<Block>(); + if (expression->is<Block>() && !expression->cast<Block>()->name.is()) + return expression->dynCast<Block>(); auto ret = allocator.alloc<Block>(); ret->list.push_back(expression); ret->finalize(); @@ -733,8 +797,10 @@ private: } Expression* truncateToInt32(Expression* value) { - if (value->type == i64) return builder.makeUnary(UnaryOp::WrapInt64, value); - // either i32, or a call_import whose type we don't know yet (but would be legalized to i32 anyhow) + if (value->type == i64) + return builder.makeUnary(UnaryOp::WrapInt64, value); + // either i32, or a call_import whose type we don't know yet (but would be + // legalized to i32 anyhow) return value; } @@ -749,7 +815,9 @@ void Asm2WasmBuilder::processAsm(Ref ast) { Ref asmFunction = ast[1][0]; assert(asmFunction[0] == DEFUN); Ref body = asmFunction[3]; - assert(body[0][0] == STRING && (body[0][1]->getIString() == IString("use asm") || body[0][1]->getIString() == IString("almost asm"))); + assert(body[0][0] == STRING && + (body[0][1]->getIString() == IString("use asm") || + body[0][1]->getIString() == IString("almost asm"))); // extra functions that we add, that are not from the compiled code. we need // to make sure to optimize them normally (OptimizingIncrementalModuleBuilder @@ -885,7 +953,7 @@ void Asm2WasmBuilder::processAsm(Ref ast) { } std::string fullName = module[1]->getCString(); fullName += '.'; - fullName += + module[2]->getCString(); + fullName += +module[2]->getCString(); moduleName = IString(fullName.c_str(), false); } else { assert(module->isString()); @@ -895,7 +963,8 @@ void Asm2WasmBuilder::processAsm(Ref ast) { if (base == TEMP_DOUBLE_PTR) { assert(tempDoublePtr.isNull()); tempDoublePtr = name; - // we don't return here, as we can only optimize out some uses of tDP. So it remains imported + // we don't return here, as we can only optimize out some uses of tDP. + // So it remains imported } else if (base == LLVM_CTTZ_I32) { assert(llvm_cttz_i32.isNull()); llvm_cttz_i32 = name; @@ -916,19 +985,20 @@ void Asm2WasmBuilder::processAsm(Ref ast) { import->base = base; import->type = type; mappedGlobals.emplace(name, type); - // __table_base and __memory_base are used as segment/element offsets, and must be constant; - // otherwise, an asm.js import of a constant is mutable, e.g. STACKTOP + // __table_base and __memory_base are used as segment/element offsets, and + // must be constant; otherwise, an asm.js import of a constant is mutable, + // e.g. STACKTOP if (name != TABLE_BASE && name != MEMORY_BASE) { - // we need imported globals to be mutable, but wasm doesn't support that yet, so we must - // import an immutable and create a mutable global initialized to its value + // we need imported globals to be mutable, but wasm doesn't support that + // yet, so we must import an immutable and create a mutable global + // initialized to its value import->name = Name(std::string(import->name.str) + "$asm2wasm$import"); { - wasm.addGlobal(builder.makeGlobal( - name, - type, - builder.makeGetGlobal(import->name, type), - Builder::Mutable - )); + wasm.addGlobal( + builder.makeGlobal(name, + type, + builder.makeGetGlobal(import->name, type), + Builder::Mutable)); } } if ((name == TABLE_BASE || name == MEMORY_BASE) && @@ -946,34 +1016,44 @@ void Asm2WasmBuilder::processAsm(Ref ast) { } }; - IString Int8Array, Int16Array, Int32Array, UInt8Array, UInt16Array, UInt32Array, Float32Array, Float64Array; + IString Int8Array, Int16Array, Int32Array, UInt8Array, UInt16Array, + UInt32Array, Float32Array, Float64Array; // set up optimization if (runOptimizationPasses) { Index numFunctions = 0; for (unsigned i = 1; i < body->size(); i++) { - if (body[i][0] == DEFUN) numFunctions++; + if (body[i][0] == DEFUN) + numFunctions++; } - optimizingBuilder = make_unique<OptimizingIncrementalModuleBuilder>(&wasm, numFunctions, passOptions, [&](PassRunner& passRunner) { - // addPrePasses - passRunner.options.lowMemoryUnused = true; - if (debug) { - passRunner.setDebug(true); - passRunner.setValidateGlobally(false); - } - // run autodrop first, before optimizations - passRunner.add<AutoDrop>(); - if (preprocessor.debugInfo) { - // fix up debug info to better survive optimization - passRunner.add<AdjustDebugInfo>(); - } - // optimize relooper label variable usage at the wasm level, where it is easy - passRunner.add("relooper-jump-threading"); - }, debug, false /* do not validate globally yet */); + optimizingBuilder = make_unique<OptimizingIncrementalModuleBuilder>( + &wasm, + numFunctions, + passOptions, + [&](PassRunner& passRunner) { + // addPrePasses + passRunner.options.lowMemoryUnused = true; + if (debug) { + passRunner.setDebug(true); + passRunner.setValidateGlobally(false); + } + // run autodrop first, before optimizations + passRunner.add<AutoDrop>(); + if (preprocessor.debugInfo) { + // fix up debug info to better survive optimization + passRunner.add<AdjustDebugInfo>(); + } + // optimize relooper label variable usage at the wasm level, where it is + // easy + passRunner.add("relooper-jump-threading"); + }, + debug, + false /* do not validate globally yet */); } - // if we see no function tables in the processing below, then the table still exists and has size 0 + // if we see no function tables in the processing below, then the table still + // exists and has size 0 wasm.table.initial = wasm.table.max = 0; @@ -990,10 +1070,12 @@ void Asm2WasmBuilder::processAsm(Ref ast) { Ref value = pair[1]; if (value->isNumber()) { // global int - allocateGlobal(name, Type::i32, Literal(int32_t(value->getInteger()))); + allocateGlobal( + name, Type::i32, Literal(int32_t(value->getInteger()))); } else if (value[0] == BINARY) { // int import - assert(value[1] == OR && value[3]->isNumber() && value[3]->getNumber() == 0); + assert(value[1] == OR && value[3]->isNumber() && + value[3]->getNumber() == 0); Ref import = value[2]; // env.what addImport(name, import, Type::i32); } else if (value[0] == UNARY_PREFIX) { @@ -1009,7 +1091,8 @@ void Asm2WasmBuilder::processAsm(Ref ast) { addImport(name, import, Type::f64); } } else if (value[0] == CALL) { - assert(value[1]->isString() && value[1] == Math_fround && value[2][0]->isNumber() && value[2][0]->getNumber() == 0); + assert(value[1]->isString() && value[1] == Math_fround && + value[2][0]->isNumber() && value[2][0]->getNumber() == 0); allocateGlobal(name, Type::f32); } else if (value[0] == DOT) { // simple module.base import. can be a view, or a function. @@ -1049,21 +1132,45 @@ void Asm2WasmBuilder::processAsm(Ref ast) { if (constructor->isArray(DOT)) { // global.*Array IString heap = constructor[2]->getIString(); if (heap == INT8ARRAY) { - bytes = 1; integer = true; signed_ = true; asmType = ASM_INT; + bytes = 1; + integer = true; + signed_ = true; + asmType = ASM_INT; } else if (heap == INT16ARRAY) { - bytes = 2; integer = true; signed_ = true; asmType = ASM_INT; + bytes = 2; + integer = true; + signed_ = true; + asmType = ASM_INT; } else if (heap == INT32ARRAY) { - bytes = 4; integer = true; signed_ = true; asmType = ASM_INT; + bytes = 4; + integer = true; + signed_ = true; + asmType = ASM_INT; } else if (heap == UINT8ARRAY) { - bytes = 1; integer = true; signed_ = false; asmType = ASM_INT; + bytes = 1; + integer = true; + signed_ = false; + asmType = ASM_INT; } else if (heap == UINT16ARRAY) { - bytes = 2; integer = true; signed_ = false; asmType = ASM_INT; + bytes = 2; + integer = true; + signed_ = false; + asmType = ASM_INT; } else if (heap == UINT32ARRAY) { - bytes = 4; integer = true; signed_ = false; asmType = ASM_INT; + bytes = 4; + integer = true; + signed_ = false; + asmType = ASM_INT; } else if (heap == FLOAT32ARRAY) { - bytes = 4; integer = false; signed_ = true; asmType = ASM_FLOAT; + bytes = 4; + integer = false; + signed_ = true; + asmType = ASM_FLOAT; } else if (heap == FLOAT64ARRAY) { - bytes = 8; integer = false; signed_ = true; asmType = ASM_DOUBLE; + bytes = 8; + integer = false; + signed_ = true; + asmType = ASM_DOUBLE; } else { abort_on("invalid view import", heap); } @@ -1071,21 +1178,45 @@ void Asm2WasmBuilder::processAsm(Ref ast) { assert(constructor->isString()); IString viewName = constructor->getIString(); if (viewName == Int8Array) { - bytes = 1; integer = true; signed_ = true; asmType = ASM_INT; + bytes = 1; + integer = true; + signed_ = true; + asmType = ASM_INT; } else if (viewName == Int16Array) { - bytes = 2; integer = true; signed_ = true; asmType = ASM_INT; + bytes = 2; + integer = true; + signed_ = true; + asmType = ASM_INT; } else if (viewName == Int32Array) { - bytes = 4; integer = true; signed_ = true; asmType = ASM_INT; + bytes = 4; + integer = true; + signed_ = true; + asmType = ASM_INT; } else if (viewName == UInt8Array) { - bytes = 1; integer = true; signed_ = false; asmType = ASM_INT; + bytes = 1; + integer = true; + signed_ = false; + asmType = ASM_INT; } else if (viewName == UInt16Array) { - bytes = 2; integer = true; signed_ = false; asmType = ASM_INT; + bytes = 2; + integer = true; + signed_ = false; + asmType = ASM_INT; } else if (viewName == UInt32Array) { - bytes = 4; integer = true; signed_ = false; asmType = ASM_INT; + bytes = 4; + integer = true; + signed_ = false; + asmType = ASM_INT; } else if (viewName == Float32Array) { - bytes = 4; integer = false; signed_ = true; asmType = ASM_FLOAT; + bytes = 4; + integer = false; + signed_ = true; + asmType = ASM_FLOAT; } else if (viewName == Float64Array) { - bytes = 8; integer = false; signed_ = true; asmType = ASM_DOUBLE; + bytes = 8; + integer = false; + signed_ = true; + asmType = ASM_DOUBLE; } else { abort_on("invalid short view import", viewName); } @@ -1093,15 +1224,20 @@ void Asm2WasmBuilder::processAsm(Ref ast) { assert(views.find(name) == views.end()); views.emplace(name, View(bytes, integer, signed_, asmType)); } else if (value[0] == ARRAY) { - // function table. we merge them into one big table, so e.g. [foo, b1] , [b2, bar] => [foo, b1, b2, bar] - // TODO: when not using aliasing function pointers, we could merge them by noticing that - // index 0 in each table is the null func, and each other index should only have one - // non-null func. However, that breaks down when function pointer casts are emulated. + // function table. we merge them into one big table, so e.g. [foo, + // b1] , [b2, bar] => [foo, b1, b2, bar] + // TODO: when not using aliasing function pointers, we could merge + // them by noticing that + // index 0 in each table is the null func, and each other index + // should only have one non-null func. However, that breaks down + // when function pointer casts are emulated. if (wasm.table.segments.size() == 0) { - wasm.table.segments.emplace_back(builder.makeGetGlobal(Name(TABLE_BASE), i32)); + wasm.table.segments.emplace_back( + builder.makeGetGlobal(Name(TABLE_BASE), i32)); } auto& segment = wasm.table.segments[0]; - functionTableStarts[name] = segment.data.size(); // this table starts here + functionTableStarts[name] = + segment.data.size(); // this table starts here Ref contents = value[1]; for (unsigned k = 0; k < contents->size(); k++) { IString curr = contents[k]->getIString(); @@ -1124,7 +1260,8 @@ void Asm2WasmBuilder::processAsm(Ref ast) { // exporting a function IString value = pair[1]->getIString(); if (key == Name("_emscripten_replace_memory")) { - // asm.js memory growth provides this special non-asm function, which we don't need (we use grow_memory) + // asm.js memory growth provides this special non-asm function, + // which we don't need (we use grow_memory) assert(!wasm.getFunctionOrNull(value)); continue; } else if (key == UDIVMODDI4) { @@ -1133,7 +1270,8 @@ void Asm2WasmBuilder::processAsm(Ref ast) { getTempRet0 = value; } if (exported.count(key) > 0) { - // asm.js allows duplicate exports, but not wasm. use the last, like asm.js + // asm.js allows duplicate exports, but not wasm. use the last, like + // asm.js exported[key]->value = value; } else { auto* export_ = new Export; @@ -1148,12 +1286,11 @@ void Asm2WasmBuilder::processAsm(Ref ast) { assert(pair[1]->isNumber()); assert(exported.count(key) == 0); auto value = pair[1]->getInteger(); - auto* global = builder.makeGlobal( - key, - i32, - builder.makeConst(Literal(int32_t(value))), - Builder::Immutable - ); + auto* global = + builder.makeGlobal(key, + i32, + builder.makeConst(Literal(int32_t(value))), + Builder::Immutable); wasm.addGlobal(global); auto* export_ = new Export; export_->name = key; @@ -1185,10 +1322,10 @@ void Asm2WasmBuilder::processAsm(Ref ast) { if (runOptimizationPasses) { optimizingBuilder->finish(); - // if we added any helper functions (like non-trapping i32-div, etc.), then those - // have not been optimized (the optimizing builder has just been fed the asm.js - // functions). Optimize those now. Typically there are very few, just do it - // sequentially. + // if we added any helper functions (like non-trapping i32-div, etc.), then + // those have not been optimized (the optimizing builder has just been fed + // the asm.js functions). Optimize those now. Typically there are very few, + // just do it sequentially. PassRunner passRunner(&wasm, passOptions); passRunner.options.lowMemoryUnused = true; passRunner.addDefaultFunctionOptimizationPasses(); @@ -1210,14 +1347,18 @@ void Asm2WasmBuilder::processAsm(Ref ast) { IString name = import->name; if (importedFunctionTypes.find(name) != importedFunctionTypes.end()) { // special math builtins - FunctionType* builtin = getBuiltinFunctionType(import->module, import->base); + FunctionType* builtin = + getBuiltinFunctionType(import->module, import->base); if (builtin) { import->type = builtin->name; } else { - import->type = ensureFunctionType(getSig(importedFunctionTypes[name].get()), &wasm)->name; + import->type = + ensureFunctionType(getSig(importedFunctionTypes[name].get()), &wasm) + ->name; } } else if (import->module != ASM2WASM) { // special-case the special module - // never actually used, which means we don't know the function type since the usage tells us, so illegal for it to remain + // never actually used, which means we don't know the function type since + // the usage tells us, so illegal for it to remain toErase.push_back(name); } }); @@ -1255,55 +1396,70 @@ void Asm2WasmBuilder::processAsm(Ref ast) { numShown = make_unique<std::atomic<int>>(); numShown->store(0); } - if (numShown->load() >= MAX_SHOWN) return; - std::cerr << why << " in call from " << getFunction()->name << " to " << calledFunc->name << " (this is likely due to undefined behavior in C, like defining a function one way and calling it in another, which is important to fix)\n"; + if (numShown->load() >= MAX_SHOWN) + return; + std::cerr << why << " in call from " << getFunction()->name << " to " + << calledFunc->name + << " (this is likely due to undefined behavior in C, like " + "defining a function one way and calling it in another, " + "which is important to fix)\n"; (*numShown)++; if (numShown->load() >= MAX_SHOWN) { - std::cerr << "(" << numShown->load() << " such warnings shown; not showing any more)\n"; + std::cerr << "(" << numShown->load() + << " such warnings shown; not showing any more)\n"; } } void visitCall(Call* curr) { - // The call target may not exist if it is one of our special fake imports for callIndirect fixups + // The call target may not exist if it is one of our special fake imports + // for callIndirect fixups auto* calledFunc = getModule()->getFunctionOrNull(curr->target); if (calledFunc && !calledFunc->imported()) { - // The result type of the function being called is now known, and can be applied. + // The result type of the function being called is now known, and can be + // applied. auto result = calledFunc->result; if (curr->type != result) { curr->type = result; } - // Handle mismatched numbers of arguments. In clang, if a function is declared one way - // but called in another, it inserts bitcasts to make things work. Those end up - // working since it is "ok" to drop or add parameters in native platforms, even - // though it's undefined behavior. We warn about it here, but tolerate it, if there is - // a simple solution. + // Handle mismatched numbers of arguments. In clang, if a function is + // declared one way but called in another, it inserts bitcasts to make + // things work. Those end up working since it is "ok" to drop or add + // parameters in native platforms, even though it's undefined behavior. + // We warn about it here, but tolerate it, if there is a simple + // solution. if (curr->operands.size() < calledFunc->params.size()) { - notifyAboutWrongOperands("warning: asm2wasm adding operands", calledFunc); + notifyAboutWrongOperands("warning: asm2wasm adding operands", + calledFunc); while (curr->operands.size() < calledFunc->params.size()) { // Add params as necessary, with zeros. - curr->operands.push_back( - LiteralUtils::makeZero(calledFunc->params[curr->operands.size()], *getModule()) - ); + curr->operands.push_back(LiteralUtils::makeZero( + calledFunc->params[curr->operands.size()], *getModule())); } } if (curr->operands.size() > calledFunc->params.size()) { - notifyAboutWrongOperands("warning: asm2wasm dropping operands", calledFunc); + notifyAboutWrongOperands("warning: asm2wasm dropping operands", + calledFunc); curr->operands.resize(calledFunc->params.size()); } - // If the types are wrong, validation will fail later anyhow, but add a warning here, - // it may help people. + // If the types are wrong, validation will fail later anyhow, but add a + // warning here, it may help people. for (Index i = 0; i < curr->operands.size(); i++) { auto sent = curr->operands[i]->type; auto expected = calledFunc->params[i]; if (sent != unreachable && sent != expected) { - notifyAboutWrongOperands("error: asm2wasm seeing an invalid argument type at index " + std::to_string(i) + " (this will not validate)", calledFunc); + notifyAboutWrongOperands( + "error: asm2wasm seeing an invalid argument type at index " + + std::to_string(i) + " (this will not validate)", + calledFunc); } } } else { // A call to an import - // fill things out: add extra params as needed, etc. asm tolerates ffi overloading, wasm does not + // fill things out: add extra params as needed, etc. asm tolerates ffi + // overloading, wasm does not auto iter = parent->importedFunctionTypes.find(curr->target); - if (iter == parent->importedFunctionTypes.end()) return; // one of our fake imports for callIndirect fixups + if (iter == parent->importedFunctionTypes.end()) + return; // one of our fake imports for callIndirect fixups auto type = iter->second.get(); for (size_t i = 0; i < type->params.size(); i++) { if (i >= curr->operands.size()) { @@ -1312,28 +1468,40 @@ void Asm2WasmBuilder::processAsm(Ref ast) { val->type = val->value.type = type->params[i]; curr->operands.push_back(val); } else if (curr->operands[i]->type != type->params[i]) { - // if the param is used, then we have overloading here and the combined type must be f64; - // if this is an unreachable param, then it doesn't matter. - assert(type->params[i] == f64 || curr->operands[i]->type == unreachable); + // if the param is used, then we have overloading here and the + // combined type must be f64; if this is an unreachable param, then + // it doesn't matter. + assert(type->params[i] == f64 || + curr->operands[i]->type == unreachable); // overloaded, upgrade to f64 switch (curr->operands[i]->type) { - case i32: curr->operands[i] = parent->builder.makeUnary(ConvertSInt32ToFloat64, curr->operands[i]); break; - case f32: curr->operands[i] = parent->builder.makeUnary(PromoteFloat32, curr->operands[i]); break; + case i32: + curr->operands[i] = parent->builder.makeUnary( + ConvertSInt32ToFloat64, curr->operands[i]); + break; + case f32: + curr->operands[i] = + parent->builder.makeUnary(PromoteFloat32, curr->operands[i]); + break; default: {} // f64, unreachable, etc., are all good } } } Module* wasm = getModule(); - auto importResult = wasm->getFunctionType(wasm->getFunction(curr->target)->type)->result; + auto importResult = + wasm->getFunctionType(wasm->getFunction(curr->target)->type)->result; if (curr->type != importResult) { auto old = curr->type; curr->type = importResult; if (importResult == f64) { - // we use a JS f64 value which is the most general, and convert to it + // we use a JS f64 value which is the most general, and convert to + // it switch (old) { - case i32: { - Unary* trunc = parent->builder.makeUnary(TruncSFloat64ToInt32, curr); - replaceCurrent(makeTrappingUnary(trunc, parent->trappingFunctions)); + case i32: { + Unary* trunc = + parent->builder.makeUnary(TruncSFloat64ToInt32, curr); + replaceCurrent( + makeTrappingUnary(trunc, parent->trappingFunctions)); break; } case f32: { @@ -1341,24 +1509,27 @@ void Asm2WasmBuilder::processAsm(Ref ast) { break; } case none: { - // this function returns a value, but we are not using it, so it must be dropped. - // autodrop will do that for us. + // this function returns a value, but we are not using it, so it + // must be dropped. autodrop will do that for us. break; } - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } } else { assert(old == none); - // we don't want a return value here, but the import does provide one - // autodrop will do that for us. + // we don't want a return value here, but the import does provide + // one autodrop will do that for us. } } } } void visitCallIndirect(CallIndirect* curr) { - // we already call into target = something + offset, where offset is a callImport with the name of the table. replace that with the table offset - // note that for an ftCall or mftCall, we have no asm.js mask, so have nothing to do here + // we already call into target = something + offset, where offset is a + // callImport with the name of the table. replace that with the table + // offset note that for an ftCall or mftCall, we have no asm.js mask, so + // have nothing to do here auto* target = curr->target; // might be a block with a fallthrough if (auto* block = target->dynCast<Block>()) { @@ -1367,23 +1538,34 @@ void Asm2WasmBuilder::processAsm(Ref ast) { // the something might have been optimized out, leaving only the call if (auto* call = target->dynCast<Call>()) { auto tableName = call->target; - if (parent->functionTableStarts.find(tableName) == parent->functionTableStarts.end()) return; - curr->target = parent->builder.makeConst(Literal((int32_t)parent->functionTableStarts[tableName])); + if (parent->functionTableStarts.find(tableName) == + parent->functionTableStarts.end()) + return; + curr->target = parent->builder.makeConst( + Literal((int32_t)parent->functionTableStarts[tableName])); return; } auto* add = target->dynCast<Binary>(); - if (!add) return; + if (!add) + return; if (add->right->is<Call>()) { auto* offset = add->right->cast<Call>(); auto tableName = offset->target; - if (parent->functionTableStarts.find(tableName) == parent->functionTableStarts.end()) return; - add->right = parent->builder.makeConst(Literal((int32_t)parent->functionTableStarts[tableName])); + if (parent->functionTableStarts.find(tableName) == + parent->functionTableStarts.end()) + return; + add->right = parent->builder.makeConst( + Literal((int32_t)parent->functionTableStarts[tableName])); } else { auto* offset = add->left->dynCast<Call>(); - if (!offset) return; + if (!offset) + return; auto tableName = offset->target; - if (parent->functionTableStarts.find(tableName) == parent->functionTableStarts.end()) return; - add->left = parent->builder.makeConst(Literal((int32_t)parent->functionTableStarts[tableName])); + if (parent->functionTableStarts.find(tableName) == + parent->functionTableStarts.end()) + return; + add->left = parent->builder.makeConst( + Literal((int32_t)parent->functionTableStarts[tableName])); } } @@ -1394,15 +1576,17 @@ void Asm2WasmBuilder::processAsm(Ref ast) { } }; - // apply debug info, reducing intrinsic calls into annotations on the ast nodes - struct ApplyDebugInfo : public WalkerPass<ExpressionStackWalker<ApplyDebugInfo, UnifiedExpressionVisitor<ApplyDebugInfo>>> { + // apply debug info, reducing intrinsic calls into annotations on the ast + // nodes + struct ApplyDebugInfo + : public WalkerPass< + ExpressionStackWalker<ApplyDebugInfo, + UnifiedExpressionVisitor<ApplyDebugInfo>>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new ApplyDebugInfo(); } - ApplyDebugInfo() { - name = "apply-debug-info"; - } + ApplyDebugInfo() { name = "apply-debug-info"; } Call* lastDebugInfo = nullptr; @@ -1413,30 +1597,34 @@ void Asm2WasmBuilder::processAsm(Ref ast) { } else { if (lastDebugInfo) { auto& debugLocations = getFunction()->debugLocations; - uint32_t fileIndex = lastDebugInfo->operands[0]->cast<Const>()->value.geti32(); + uint32_t fileIndex = + lastDebugInfo->operands[0]->cast<Const>()->value.geti32(); assert(getModule()->debugInfoFileNames.size() > fileIndex); - uint32_t lineNumber = lastDebugInfo->operands[1]->cast<Const>()->value.geti32(); + uint32_t lineNumber = + lastDebugInfo->operands[1]->cast<Const>()->value.geti32(); // look up the stack, apply to the root expression Index i = expressionStack.size() - 1; while (1) { auto* exp = expressionStack[i]; - bool parentIsStructure = i > 0 && (expressionStack[i - 1]->is<Block>() || - expressionStack[i - 1]->is<Loop>() || - expressionStack[i - 1]->is<If>()); - if (i == 0 || parentIsStructure || exp->type == none || exp->type == unreachable) { + bool parentIsStructure = + i > 0 && (expressionStack[i - 1]->is<Block>() || + expressionStack[i - 1]->is<Loop>() || + expressionStack[i - 1]->is<If>()); + if (i == 0 || parentIsStructure || exp->type == none || + exp->type == unreachable) { if (debugLocations.count(exp) > 0) { // already present, so look back up i++; while (i < expressionStack.size()) { exp = expressionStack[i]; if (debugLocations.count(exp) == 0) { - debugLocations[exp] = { fileIndex, lineNumber, 0 }; + debugLocations[exp] = {fileIndex, lineNumber, 0}; break; } i++; } } else { - debugLocations[exp] = { fileIndex, lineNumber, 0 }; + debugLocations[exp] = {fileIndex, lineNumber, 0}; } break; } @@ -1454,13 +1642,12 @@ void Asm2WasmBuilder::processAsm(Ref ast) { passRunner.setDebug(true); passRunner.setValidateGlobally(false); } - // finalizeCalls also does autoDrop, which is crucial for the non-optimizing case, - // so that the output of the first pass is valid + // finalizeCalls also does autoDrop, which is crucial for the non-optimizing + // case, so that the output of the first pass is valid passRunner.add<FinalizeCalls>(this); - passRunner.add(ABI::getLegalizationPass( - legalizeJavaScriptFFI ? ABI::LegalizationLevel::Full - : ABI::LegalizationLevel::Minimal - )); + passRunner.add(ABI::getLegalizationPass(legalizeJavaScriptFFI + ? ABI::LegalizationLevel::Full + : ABI::LegalizationLevel::Minimal)); if (runOptimizationPasses) { // autodrop can add some garbage passRunner.add("vacuum"); @@ -1478,7 +1665,9 @@ void Asm2WasmBuilder::processAsm(Ref ast) { } if (preprocessor.debugInfo) { passRunner.add<ApplyDebugInfo>(); - passRunner.add("vacuum"); // FIXME maybe just remove the nops that were debuginfo nodes, if not optimizing? + // FIXME maybe just remove the nops that were debuginfo nodes, if not + // optimizing? + passRunner.add("vacuum"); } if (runOptimizationPasses) { // do final global optimizations after all function work is done @@ -1493,14 +1682,18 @@ void Asm2WasmBuilder::processAsm(Ref ast) { } if (udivmoddi4.is() && getTempRet0.is()) { - // generate a wasm-optimized __udivmoddi4 method, which we can do much more efficiently in wasm - // we can only do this if we know getTempRet0 as well since we use it to figure out which minified global is tempRet0 - // (getTempRet0 might be an import, if this is a shared module, so we can't optimize that case) + // generate a wasm-optimized __udivmoddi4 method, which we can do much more + // efficiently in wasm we can only do this if we know getTempRet0 as well + // since we use it to figure out which minified global is tempRet0 + // (getTempRet0 might be an import, if this is a shared module, so we can't + // optimize that case) Name tempRet0; { Expression* curr = wasm.getFunction(getTempRet0)->body; - if (curr->is<Block>()) curr = curr->cast<Block>()->list.back(); - if (curr->is<Return>()) curr = curr->cast<Return>()->value; + if (curr->is<Block>()) + curr = curr->cast<Block>()->list.back(); + if (curr->is<Return>()) + curr = curr->cast<Return>()->value; auto* get = curr->cast<GetGlobal>(); tempRet0 = get->name; } @@ -1510,47 +1703,35 @@ void Asm2WasmBuilder::processAsm(Ref ast) { auto* func = wasm.getFunction(udivmoddi4); assert(!func->type.is()); Builder::clearLocals(func); - Index xl = Builder::addParam(func, "xl", i32), - xh = Builder::addParam(func, "xh", i32), - yl = Builder::addParam(func, "yl", i32), - yh = Builder::addParam(func, "yh", i32), - r = Builder::addParam(func, "r", i32), + Index xl = Builder::addParam(func, "xl", i32), + xh = Builder::addParam(func, "xh", i32), + yl = Builder::addParam(func, "yl", i32), + yh = Builder::addParam(func, "yh", i32), + r = Builder::addParam(func, "r", i32), x64 = Builder::addVar(func, "x64", i64), y64 = Builder::addVar(func, "y64", i64); auto* body = allocator.alloc<Block>(); - body->list.push_back(builder.makeSetLocal(x64, I64Utilities::recreateI64(builder, xl, xh))); - body->list.push_back(builder.makeSetLocal(y64, I64Utilities::recreateI64(builder, yl, yh))); body->list.push_back( - builder.makeIf( - builder.makeGetLocal(r, i32), - builder.makeStore( - 8, 0, 8, - builder.makeGetLocal(r, i32), - builder.makeBinary( - RemUInt64, - builder.makeGetLocal(x64, i64), - builder.makeGetLocal(y64, i64) - ), - i64 - ) - ) - ); + builder.makeSetLocal(x64, I64Utilities::recreateI64(builder, xl, xh))); body->list.push_back( - builder.makeSetLocal( - x64, - builder.makeBinary( - DivUInt64, - builder.makeGetLocal(x64, i64), - builder.makeGetLocal(y64, i64) - ) - ) - ); + builder.makeSetLocal(y64, I64Utilities::recreateI64(builder, yl, yh))); + body->list.push_back(builder.makeIf( + builder.makeGetLocal(r, i32), + builder.makeStore(8, + 0, + 8, + builder.makeGetLocal(r, i32), + builder.makeBinary(RemUInt64, + builder.makeGetLocal(x64, i64), + builder.makeGetLocal(y64, i64)), + i64))); body->list.push_back( - builder.makeSetGlobal( - tempRet0, - I64Utilities::getI64High(builder, x64) - ) - ); + builder.makeSetLocal(x64, + builder.makeBinary(DivUInt64, + builder.makeGetLocal(x64, i64), + builder.makeGetLocal(y64, i64)))); + body->list.push_back( + builder.makeSetGlobal(tempRet0, I64Utilities::getI64High(builder, x64))); body->list.push_back(I64Utilities::getI64Low(builder, x64)); body->finalize(); func->body = body; @@ -1581,7 +1762,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { IStringSet functionVariables; // params or vars - IString parentLabel; // set in LABEL, then read in WHILE/DO/SWITCH + IString parentLabel; // set in LABEL, then read in WHILE/DO/SWITCH std::vector<IString> breakStack; // where a break will go std::vector<IString> continueStack; // where a continue will go @@ -1591,7 +1772,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { Ref curr = body[i]; auto* assign = curr->asAssignName(); IString name = assign->target(); - AsmType asmType = detectType(assign->value(), nullptr, false, Math_fround, wasmOnly); + AsmType asmType = + detectType(assign->value(), nullptr, false, Math_fround, wasmOnly); Builder::addParam(function, name, asmToWasmType(asmType)); functionVariables.insert(name); asmData.addParam(name, asmType); @@ -1602,7 +1784,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { for (unsigned j = 0; j < curr[1]->size(); j++) { Ref pair = curr[1][j]; IString name = pair[0]->getIString(); - AsmType asmType = detectType(pair[1], nullptr, true, Math_fround, wasmOnly); + AsmType asmType = + detectType(pair[1], nullptr, true, Math_fround, wasmOnly); Builder::addVar(function, name, asmToWasmType(asmType)); functionVariables.insert(name); asmData.addVar(name, asmType); @@ -1612,7 +1795,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { bool addedI32Temp = false; auto ensureI32Temp = [&]() { - if (addedI32Temp) return; + if (addedI32Temp) + return; addedI32Temp = true; Builder::addVar(function, I32_TEMP, i32); functionVariables.insert(I32_TEMP); @@ -1621,12 +1805,13 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { bool seenReturn = false; // function->result is updated if we see a return // processors - std::function<Expression* (Ref, unsigned)> processStatements; - std::function<Expression* (Ref, unsigned)> processUnshifted; - std::function<Expression* (Ref, unsigned)> processIgnoringShift; + std::function<Expression*(Ref, unsigned)> processStatements; + std::function<Expression*(Ref, unsigned)> processUnshifted; + std::function<Expression*(Ref, unsigned)> processIgnoringShift; - std::function<Expression* (Ref)> process = [&](Ref ast) -> Expression* { - AstStackHelper astStackHelper(ast); // TODO: only create one when we need it? + std::function<Expression*(Ref)> process = [&](Ref ast) -> Expression* { + AstStackHelper astStackHelper( + ast); // TODO: only create one when we need it? if (ast->isString()) { IString name = ast->getIString(); if (functionVariables.has(name)) { @@ -1637,7 +1822,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { return ret; } if (name == DEBUGGER) { - Call *call = allocator.alloc<Call>(); + Call* call = allocator.alloc<Call>(); call->target = DEBUGGER; call->type = none; static bool addedImport = false; @@ -1655,7 +1840,9 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { return call; } // global var - assert(mappedGlobals.find(name) != mappedGlobals.end() ? true : (std::cerr << name.str << '\n', false)); + assert(mappedGlobals.find(name) != mappedGlobals.end() + ? true + : (std::cerr << name.str << '\n', false)); MappedGlobal& global = mappedGlobals[name]; return builder.makeGetGlobal(name, global.type); } @@ -1688,10 +1875,13 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { Fatal() << "error: access of a non-existent global var " << name.str; } auto* ret = builder.makeSetGlobal(name, process(assign->value())); - // global.set does not return; if our value is trivially not used, don't emit a load (if nontrivially not used, opts get it later) + // global.set does not return; if our value is trivially not used, don't + // emit a load (if nontrivially not used, opts get it later) auto parent = astStackHelper.getParent(); - if (!parent || parent->isArray(BLOCK) || parent->isArray(IF)) return ret; - return builder.makeSequence(ret, builder.makeGetGlobal(name, ret->value->type)); + if (!parent || parent->isArray(BLOCK) || parent->isArray(IF)) + return ret; + return builder.makeSequence( + ret, builder.makeGetGlobal(name, ret->value->type)); } if (ast->isAssign()) { auto* assign = ast->asAssign(); @@ -1711,7 +1901,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { ret->valueType = asmToWasmType(view.type); ret->finalize(); if (ret->valueType != ret->value->type) { - // in asm.js we have some implicit coercions that we must do explicitly here + // in asm.js we have some implicit coercions that we must do explicitly + // here if (ret->valueType == f32 && ret->value->type == f64) { auto conv = allocator.alloc<Unary>(); conv->op = DemoteFloat64; @@ -1728,19 +1919,23 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } IString what = ast[0]->getIString(); if (what == BINARY) { - if ((ast[1] == OR || ast[1] == TRSHIFT) && ast[3]->isNumber() && ast[3]->getNumber() == 0) { - auto ret = process(ast[2]); // just look through the ()|0 or ()>>>0 coercion + if ((ast[1] == OR || ast[1] == TRSHIFT) && ast[3]->isNumber() && + ast[3]->getNumber() == 0) { + auto ret = + process(ast[2]); // just look through the ()|0 or ()>>>0 coercion fixCallType(ret, i32); return ret; } auto ret = allocator.alloc<Binary>(); ret->left = process(ast[2]); ret->right = process(ast[3]); - ret->op = parseAsmBinaryOp(ast[1]->getIString(), ast[2], ast[3], ret->left, ret->right); + ret->op = parseAsmBinaryOp( + ast[1]->getIString(), ast[2], ast[3], ret->left, ret->right); ret->finalize(); if (ret->op == BinaryOp::RemSInt32 && isFloatType(ret->type)) { - // WebAssembly does not have floating-point remainder, we have to emit a call to a special import of ours - Call *call = allocator.alloc<Call>(); + // WebAssembly does not have floating-point remainder, we have to emit a + // call to a special import of ours + Call* call = allocator.alloc<Call>(); call->target = F64_REM; call->operands.push_back(ensureDouble(ret->left)); call->operands.push_back(ensureDouble(ret->right)); @@ -1784,7 +1979,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { auto ret = process(ast[2]); // we are a +() coercion if (ret->type == i32) { auto conv = allocator.alloc<Unary>(); - conv->op = isUnsignedCoercion(ast[2]) ? ConvertUInt32ToFloat64 : ConvertSInt32ToFloat64; + conv->op = isUnsignedCoercion(ast[2]) ? ConvertUInt32ToFloat64 + : ConvertSInt32ToFloat64; conv->value = ret; conv->type = Type::f64; return conv; @@ -1795,7 +1991,9 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { fixCallType(ret, f64); return ret; } else if (ast[1] == MINUS) { - if (ast[2]->isNumber() || (ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == PLUS && ast[2][2]->isNumber())) { + if (ast[2]->isNumber() || + (ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == PLUS && + ast[2][2]->isNumber())) { auto ret = allocator.alloc<Const>(); ret->value = getLiteral(ast); ret->type = ret->value.type; @@ -1840,7 +2038,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } else { // !isSigned && !isF64 op = UnaryOp::TruncUFloat32ToInt32; } - return makeTrappingUnary(builder.makeUnary(op, expr), trappingFunctions); + return makeTrappingUnary(builder.makeUnary(op, expr), + trappingFunctions); } // no bitwise unary not, so do xor with -1 auto ret = allocator.alloc<Binary>(); @@ -1860,7 +2059,9 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } else if (what == IF) { auto* condition = process(ast[1]); auto* ifTrue = process(ast[2]); - return builder.makeIf(truncateToInt32(condition), ifTrue, !!ast[3] ? process(ast[3]) : nullptr); + return builder.makeIf(truncateToInt32(condition), + ifTrue, + !!ast[3] ? process(ast[3]) : nullptr); } else if (what == CALL) { if (ast[1]->isString()) { IString name = ast[1]->getIString(); @@ -1961,13 +2162,18 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { auto ret = allocator.alloc<Unary>(); ret->value = value; if (value->type == f32) { - ret->op = name == Math_floor ? FloorFloat32 : name == Math_ceil ? CeilFloat32 : SqrtFloat32; + ret->op = name == Math_floor + ? FloorFloat32 + : name == Math_ceil ? CeilFloat32 : SqrtFloat32; ret->type = value->type; } else if (value->type == f64) { - ret->op = name == Math_floor ? FloorFloat64 : name == Math_ceil ? CeilFloat64 : SqrtFloat64; + ret->op = name == Math_floor + ? FloorFloat64 + : name == Math_ceil ? CeilFloat64 : SqrtFloat64; ret->type = value->type; } else { - Fatal() << "floor/sqrt/ceil only work on float/double in asm.js and wasm"; + Fatal() + << "floor/sqrt/ceil only work on float/double in asm.js and wasm"; } return ret; } @@ -1987,15 +2193,10 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { ret->type = ret->left->type; return ret; } - if (name == Atomics_load || - name == Atomics_store || - name == Atomics_exchange || - name == Atomics_compareExchange || - name == Atomics_add || - name == Atomics_sub || - name == Atomics_and || - name == Atomics_or || - name == Atomics_xor) { + if (name == Atomics_load || name == Atomics_store || + name == Atomics_exchange || name == Atomics_compareExchange || + name == Atomics_add || name == Atomics_sub || name == Atomics_and || + name == Atomics_or || name == Atomics_xor) { // atomic operation Ref target = ast[2][0]; assert(target->isString()); @@ -2004,7 +2205,11 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { View& view = views[heap]; wasm.memory.shared = true; if (name == Atomics_load) { - Expression* ret = builder.makeAtomicLoad(view.bytes, 0, processUnshifted(ast[2][1], view.bytes), asmToWasmType(view.type)); + Expression* ret = + builder.makeAtomicLoad(view.bytes, + 0, + processUnshifted(ast[2][1], view.bytes), + asmToWasmType(view.type)); if (view.signed_) { // atomic loads are unsigned; add a signing ret = Bits::makeSignExt(ret, view.bytes, wasm); @@ -2015,26 +2220,71 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { auto type = asmToWasmType(view.type); auto temp = Builder::addVar(function, type); return builder.makeSequence( - builder.makeAtomicStore(view.bytes, 0, processUnshifted(ast[2][1], view.bytes), - builder.makeTeeLocal(temp, process(ast[2][2])), - type), - builder.makeGetLocal(temp, type) - ); + builder.makeAtomicStore( + view.bytes, + 0, + processUnshifted(ast[2][1], view.bytes), + builder.makeTeeLocal(temp, process(ast[2][2])), + type), + builder.makeGetLocal(temp, type)); } else if (name == Atomics_exchange) { - return builder.makeAtomicRMW(AtomicRMWOp::Xchg, view.bytes, 0, processUnshifted(ast[2][1], view.bytes), process(ast[2][2]), asmToWasmType(view.type)); + return builder.makeAtomicRMW( + AtomicRMWOp::Xchg, + view.bytes, + 0, + processUnshifted(ast[2][1], view.bytes), + process(ast[2][2]), + asmToWasmType(view.type)); } else if (name == Atomics_compareExchange) { - // cmpxchg is odd in fastcomp output - we must ignore the shift, a cmpxchg of a i8 will look like compareExchange(HEAP8, ptr >> 2) - return builder.makeAtomicCmpxchg(view.bytes, 0, processIgnoringShift(ast[2][1], view.bytes), process(ast[2][2]), process(ast[2][3]), asmToWasmType(view.type)); + // cmpxchg is odd in fastcomp output - we must ignore the shift, a + // cmpxchg of a i8 will look like compareExchange(HEAP8, ptr >> 2) + return builder.makeAtomicCmpxchg( + view.bytes, + 0, + processIgnoringShift(ast[2][1], view.bytes), + process(ast[2][2]), + process(ast[2][3]), + asmToWasmType(view.type)); } else if (name == Atomics_add) { - return builder.makeAtomicRMW(AtomicRMWOp::Add, view.bytes, 0, processUnshifted(ast[2][1], view.bytes), process(ast[2][2]), asmToWasmType(view.type)); + return builder.makeAtomicRMW( + AtomicRMWOp::Add, + view.bytes, + 0, + processUnshifted(ast[2][1], view.bytes), + process(ast[2][2]), + asmToWasmType(view.type)); } else if (name == Atomics_sub) { - return builder.makeAtomicRMW(AtomicRMWOp::Sub, view.bytes, 0, processUnshifted(ast[2][1], view.bytes), process(ast[2][2]), asmToWasmType(view.type)); + return builder.makeAtomicRMW( + AtomicRMWOp::Sub, + view.bytes, + 0, + processUnshifted(ast[2][1], view.bytes), + process(ast[2][2]), + asmToWasmType(view.type)); } else if (name == Atomics_and) { - return builder.makeAtomicRMW(AtomicRMWOp::And, view.bytes, 0, processUnshifted(ast[2][1], view.bytes), process(ast[2][2]), asmToWasmType(view.type)); + return builder.makeAtomicRMW( + AtomicRMWOp::And, + view.bytes, + 0, + processUnshifted(ast[2][1], view.bytes), + process(ast[2][2]), + asmToWasmType(view.type)); } else if (name == Atomics_or) { - return builder.makeAtomicRMW(AtomicRMWOp::Or, view.bytes, 0, processUnshifted(ast[2][1], view.bytes), process(ast[2][2]), asmToWasmType(view.type)); + return builder.makeAtomicRMW( + AtomicRMWOp::Or, + view.bytes, + 0, + processUnshifted(ast[2][1], view.bytes), + process(ast[2][2]), + asmToWasmType(view.type)); } else if (name == Atomics_xor) { - return builder.makeAtomicRMW(AtomicRMWOp::Xor, view.bytes, 0, processUnshifted(ast[2][1], view.bytes), process(ast[2][2]), asmToWasmType(view.type)); + return builder.makeAtomicRMW( + AtomicRMWOp::Xor, + view.bytes, + 0, + processUnshifted(ast[2][1], view.bytes), + process(ast[2][2]), + asmToWasmType(view.type)); } WASM_UNREACHABLE(); } @@ -2044,118 +2294,208 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { switch (name.str[0]) { case 'l': { auto align = num == 2 ? ast[2][1]->getInteger() : 0; - if (name == LOAD1) return builder.makeLoad(1, true, 0, 1, process(ast[2][0]), i32); - if (name == LOAD2) return builder.makeLoad(2, true, 0, indexOr(align, 2), process(ast[2][0]), i32); - if (name == LOAD4) return builder.makeLoad(4, true, 0, indexOr(align, 4), process(ast[2][0]), i32); - if (name == LOAD8) return builder.makeLoad(8, true, 0, indexOr(align, 8), process(ast[2][0]), i64); - if (name == LOADF) return builder.makeLoad(4, true, 0, indexOr(align, 4), process(ast[2][0]), f32); - if (name == LOADD) return builder.makeLoad(8, true, 0, indexOr(align, 8), process(ast[2][0]), f64); + if (name == LOAD1) + return builder.makeLoad(1, true, 0, 1, process(ast[2][0]), i32); + if (name == LOAD2) + return builder.makeLoad( + 2, true, 0, indexOr(align, 2), process(ast[2][0]), i32); + if (name == LOAD4) + return builder.makeLoad( + 4, true, 0, indexOr(align, 4), process(ast[2][0]), i32); + if (name == LOAD8) + return builder.makeLoad( + 8, true, 0, indexOr(align, 8), process(ast[2][0]), i64); + if (name == LOADF) + return builder.makeLoad( + 4, true, 0, indexOr(align, 4), process(ast[2][0]), f32); + if (name == LOADD) + return builder.makeLoad( + 8, true, 0, indexOr(align, 8), process(ast[2][0]), f64); break; } case 's': { auto align = num == 3 ? ast[2][2]->getInteger() : 0; - if (name == STORE1) return builder.makeStore(1, 0, 1, process(ast[2][0]), process(ast[2][1]), i32); - if (name == STORE2) return builder.makeStore(2, 0, indexOr(align, 2), process(ast[2][0]), process(ast[2][1]), i32); - if (name == STORE4) return builder.makeStore(4, 0, indexOr(align, 4), process(ast[2][0]), process(ast[2][1]), i32); - if (name == STORE8) return builder.makeStore(8, 0, indexOr(align, 8), process(ast[2][0]), process(ast[2][1]), i64); + if (name == STORE1) + return builder.makeStore( + 1, 0, 1, process(ast[2][0]), process(ast[2][1]), i32); + if (name == STORE2) + return builder.makeStore(2, + 0, + indexOr(align, 2), + process(ast[2][0]), + process(ast[2][1]), + i32); + if (name == STORE4) + return builder.makeStore(4, + 0, + indexOr(align, 4), + process(ast[2][0]), + process(ast[2][1]), + i32); + if (name == STORE8) + return builder.makeStore(8, + 0, + indexOr(align, 8), + process(ast[2][0]), + process(ast[2][1]), + i64); if (name == STOREF) { auto* value = process(ast[2][1]); if (value->type == f64) { - // asm.js allows storing a double to HEAPF32, we must cast here + // asm.js allows storing a double to HEAPF32, we must cast + // here value = builder.makeUnary(DemoteFloat64, value); } - return builder.makeStore(4, 0, indexOr(align, 4), process(ast[2][0]), value, f32); + return builder.makeStore( + 4, 0, indexOr(align, 4), process(ast[2][0]), value, f32); } - if (name == STORED) return builder.makeStore(8, 0, indexOr(align, 8), process(ast[2][0]), process(ast[2][1]), f64); + if (name == STORED) + return builder.makeStore(8, + 0, + indexOr(align, 8), + process(ast[2][0]), + process(ast[2][1]), + f64); break; } case 'i': { if (num == 1) { auto* value = process(ast[2][0]); if (name == I64) { - // no-op "coercion" / "cast", although we also tolerate i64(0) for constants that fit in i32 + // no-op "coercion" / "cast", although we also tolerate i64(0) + // for constants that fit in i32 if (value->type == i32) { - return builder.makeConst(Literal(int64_t(value->cast<Const>()->value.geti32()))); + return builder.makeConst( + Literal(int64_t(value->cast<Const>()->value.geti32()))); } else { fixCallType(value, i64); return value; } } - if (name == I32_CTTZ) return builder.makeUnary(UnaryOp::CtzInt32, value); - if (name == I32_CTPOP) return builder.makeUnary(UnaryOp::PopcntInt32, value); - if (name == I32_BC2F) return builder.makeUnary(UnaryOp::ReinterpretInt32, value); - if (name == I32_BC2I) return builder.makeUnary(UnaryOp::ReinterpretFloat32, value); - - if (name == I64_TRUNC) return builder.makeUnary(UnaryOp::WrapInt64, value); - if (name == I64_SEXT) return builder.makeUnary(UnaryOp::ExtendSInt32, value); - if (name == I64_ZEXT) return builder.makeUnary(UnaryOp::ExtendUInt32, value); - if (name == I64_S2F) return builder.makeUnary(UnaryOp::ConvertSInt64ToFloat32, value); - if (name == I64_S2D) return builder.makeUnary(UnaryOp::ConvertSInt64ToFloat64, value); - if (name == I64_U2F) return builder.makeUnary(UnaryOp::ConvertUInt64ToFloat32, value); - if (name == I64_U2D) return builder.makeUnary(UnaryOp::ConvertUInt64ToFloat64, value); + if (name == I32_CTTZ) + return builder.makeUnary(UnaryOp::CtzInt32, value); + if (name == I32_CTPOP) + return builder.makeUnary(UnaryOp::PopcntInt32, value); + if (name == I32_BC2F) + return builder.makeUnary(UnaryOp::ReinterpretInt32, value); + if (name == I32_BC2I) + return builder.makeUnary(UnaryOp::ReinterpretFloat32, value); + + if (name == I64_TRUNC) + return builder.makeUnary(UnaryOp::WrapInt64, value); + if (name == I64_SEXT) + return builder.makeUnary(UnaryOp::ExtendSInt32, value); + if (name == I64_ZEXT) + return builder.makeUnary(UnaryOp::ExtendUInt32, value); + if (name == I64_S2F) + return builder.makeUnary(UnaryOp::ConvertSInt64ToFloat32, + value); + if (name == I64_S2D) + return builder.makeUnary(UnaryOp::ConvertSInt64ToFloat64, + value); + if (name == I64_U2F) + return builder.makeUnary(UnaryOp::ConvertUInt64ToFloat32, + value); + if (name == I64_U2D) + return builder.makeUnary(UnaryOp::ConvertUInt64ToFloat64, + value); if (name == I64_F2S) { - Unary* conv = builder.makeUnary(UnaryOp::TruncSFloat32ToInt64, value); + Unary* conv = + builder.makeUnary(UnaryOp::TruncSFloat32ToInt64, value); return makeTrappingUnary(conv, trappingFunctions); } if (name == I64_D2S) { - Unary* conv = builder.makeUnary(UnaryOp::TruncSFloat64ToInt64, value); + Unary* conv = + builder.makeUnary(UnaryOp::TruncSFloat64ToInt64, value); return makeTrappingUnary(conv, trappingFunctions); } if (name == I64_F2U) { - Unary* conv = builder.makeUnary(UnaryOp::TruncUFloat32ToInt64, value); + Unary* conv = + builder.makeUnary(UnaryOp::TruncUFloat32ToInt64, value); return makeTrappingUnary(conv, trappingFunctions); } if (name == I64_D2U) { - Unary* conv = builder.makeUnary(UnaryOp::TruncUFloat64ToInt64, value); + Unary* conv = + builder.makeUnary(UnaryOp::TruncUFloat64ToInt64, value); return makeTrappingUnary(conv, trappingFunctions); } - if (name == I64_BC2D) return builder.makeUnary(UnaryOp::ReinterpretInt64, value); - if (name == I64_BC2I) return builder.makeUnary(UnaryOp::ReinterpretFloat64, value); - if (name == I64_CTTZ) return builder.makeUnary(UnaryOp::CtzInt64, value); - if (name == I64_CTLZ) return builder.makeUnary(UnaryOp::ClzInt64, value); - if (name == I64_CTPOP) return builder.makeUnary(UnaryOp::PopcntInt64, value); - if (name == I64_ATOMICS_LOAD) return builder.makeAtomicLoad(8, 0, value, i64); + if (name == I64_BC2D) + return builder.makeUnary(UnaryOp::ReinterpretInt64, value); + if (name == I64_BC2I) + return builder.makeUnary(UnaryOp::ReinterpretFloat64, value); + if (name == I64_CTTZ) + return builder.makeUnary(UnaryOp::CtzInt64, value); + if (name == I64_CTLZ) + return builder.makeUnary(UnaryOp::ClzInt64, value); + if (name == I64_CTPOP) + return builder.makeUnary(UnaryOp::PopcntInt64, value); + if (name == I64_ATOMICS_LOAD) + return builder.makeAtomicLoad(8, 0, value, i64); } else if (num == 2) { // 2 params,binary - if (name == I64_CONST) return builder.makeConst(getLiteral(ast)); + if (name == I64_CONST) + return builder.makeConst(getLiteral(ast)); auto* left = process(ast[2][0]); auto* right = process(ast[2][1]); // maths - if (name == I64_ADD) return builder.makeBinary(BinaryOp::AddInt64, left, right); - if (name == I64_SUB) return builder.makeBinary(BinaryOp::SubInt64, left, right); - if (name == I64_MUL) return builder.makeBinary(BinaryOp::MulInt64, left, right); + if (name == I64_ADD) + return builder.makeBinary(BinaryOp::AddInt64, left, right); + if (name == I64_SUB) + return builder.makeBinary(BinaryOp::SubInt64, left, right); + if (name == I64_MUL) + return builder.makeBinary(BinaryOp::MulInt64, left, right); if (name == I64_UDIV) { - Binary* div = builder.makeBinary(BinaryOp::DivUInt64, left, right); + Binary* div = + builder.makeBinary(BinaryOp::DivUInt64, left, right); return makeTrappingBinary(div, trappingFunctions); } if (name == I64_SDIV) { - Binary* div = builder.makeBinary(BinaryOp::DivSInt64, left, right); + Binary* div = + builder.makeBinary(BinaryOp::DivSInt64, left, right); return makeTrappingBinary(div, trappingFunctions); } if (name == I64_UREM) { - Binary* rem = builder.makeBinary(BinaryOp::RemUInt64, left, right); + Binary* rem = + builder.makeBinary(BinaryOp::RemUInt64, left, right); return makeTrappingBinary(rem, trappingFunctions); } if (name == I64_SREM) { - Binary* rem = builder.makeBinary(BinaryOp::RemSInt64, left, right); + Binary* rem = + builder.makeBinary(BinaryOp::RemSInt64, left, right); return makeTrappingBinary(rem, trappingFunctions); } - if (name == I64_AND) return builder.makeBinary(BinaryOp::AndInt64, left, right); - if (name == I64_OR) return builder.makeBinary(BinaryOp::OrInt64, left, right); - if (name == I64_XOR) return builder.makeBinary(BinaryOp::XorInt64, left, right); - if (name == I64_SHL) return builder.makeBinary(BinaryOp::ShlInt64, left, right); - if (name == I64_ASHR) return builder.makeBinary(BinaryOp::ShrSInt64, left, right); - if (name == I64_LSHR) return builder.makeBinary(BinaryOp::ShrUInt64, left, right); + if (name == I64_AND) + return builder.makeBinary(BinaryOp::AndInt64, left, right); + if (name == I64_OR) + return builder.makeBinary(BinaryOp::OrInt64, left, right); + if (name == I64_XOR) + return builder.makeBinary(BinaryOp::XorInt64, left, right); + if (name == I64_SHL) + return builder.makeBinary(BinaryOp::ShlInt64, left, right); + if (name == I64_ASHR) + return builder.makeBinary(BinaryOp::ShrSInt64, left, right); + if (name == I64_LSHR) + return builder.makeBinary(BinaryOp::ShrUInt64, left, right); // comps - if (name == I64_EQ) return builder.makeBinary(BinaryOp::EqInt64, left, right); - if (name == I64_NE) return builder.makeBinary(BinaryOp::NeInt64, left, right); - if (name == I64_ULE) return builder.makeBinary(BinaryOp::LeUInt64, left, right); - if (name == I64_SLE) return builder.makeBinary(BinaryOp::LeSInt64, left, right); - if (name == I64_UGE) return builder.makeBinary(BinaryOp::GeUInt64, left, right); - if (name == I64_SGE) return builder.makeBinary(BinaryOp::GeSInt64, left, right); - if (name == I64_ULT) return builder.makeBinary(BinaryOp::LtUInt64, left, right); - if (name == I64_SLT) return builder.makeBinary(BinaryOp::LtSInt64, left, right); - if (name == I64_UGT) return builder.makeBinary(BinaryOp::GtUInt64, left, right); - if (name == I64_SGT) return builder.makeBinary(BinaryOp::GtSInt64, left, right); + if (name == I64_EQ) + return builder.makeBinary(BinaryOp::EqInt64, left, right); + if (name == I64_NE) + return builder.makeBinary(BinaryOp::NeInt64, left, right); + if (name == I64_ULE) + return builder.makeBinary(BinaryOp::LeUInt64, left, right); + if (name == I64_SLE) + return builder.makeBinary(BinaryOp::LeSInt64, left, right); + if (name == I64_UGE) + return builder.makeBinary(BinaryOp::GeUInt64, left, right); + if (name == I64_SGE) + return builder.makeBinary(BinaryOp::GeSInt64, left, right); + if (name == I64_ULT) + return builder.makeBinary(BinaryOp::LtUInt64, left, right); + if (name == I64_SLT) + return builder.makeBinary(BinaryOp::LtSInt64, left, right); + if (name == I64_UGT) + return builder.makeBinary(BinaryOp::GtUInt64, left, right); + if (name == I64_SGT) + return builder.makeBinary(BinaryOp::GtSInt64, left, right); // atomics if (name == I64_ATOMICS_STORE) { wasm.memory.shared = true; @@ -2163,47 +2503,64 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } if (name == I64_ATOMICS_ADD) { wasm.memory.shared = true; - return builder.makeAtomicRMW(AtomicRMWOp::Add, 8, 0, left, right, i64); + return builder.makeAtomicRMW( + AtomicRMWOp::Add, 8, 0, left, right, i64); } if (name == I64_ATOMICS_SUB) { wasm.memory.shared = true; - return builder.makeAtomicRMW(AtomicRMWOp::Sub, 8, 0, left, right, i64); + return builder.makeAtomicRMW( + AtomicRMWOp::Sub, 8, 0, left, right, i64); } if (name == I64_ATOMICS_AND) { wasm.memory.shared = true; - return builder.makeAtomicRMW(AtomicRMWOp::And, 8, 0, left, right, i64); + return builder.makeAtomicRMW( + AtomicRMWOp::And, 8, 0, left, right, i64); } if (name == I64_ATOMICS_OR) { wasm.memory.shared = true; - return builder.makeAtomicRMW(AtomicRMWOp::Or, 8, 0, left, right, i64); + return builder.makeAtomicRMW( + AtomicRMWOp::Or, 8, 0, left, right, i64); } if (name == I64_ATOMICS_XOR) { wasm.memory.shared = true; - return builder.makeAtomicRMW(AtomicRMWOp::Xor, 8, 0, left, right, i64); + return builder.makeAtomicRMW( + AtomicRMWOp::Xor, 8, 0, left, right, i64); } if (name == I64_ATOMICS_EXCHANGE) { wasm.memory.shared = true; - return builder.makeAtomicRMW(AtomicRMWOp::Xchg, 8, 0, left, right, i64); + return builder.makeAtomicRMW( + AtomicRMWOp::Xchg, 8, 0, left, right, i64); } } else if (num == 3) { if (name == I64_ATOMICS_COMPAREEXCHANGE) { wasm.memory.shared = true; - return builder.makeAtomicCmpxchg(8, 0, process(ast[2][0]), process(ast[2][1]), process(ast[2][2]), i64); + return builder.makeAtomicCmpxchg(8, + 0, + process(ast[2][0]), + process(ast[2][1]), + process(ast[2][2]), + i64); } } break; } case 'f': { - if (name == F32_COPYSIGN) return builder.makeBinary(BinaryOp::CopySignFloat32, process(ast[2][0]), process(ast[2][1])); - if (name == F64_COPYSIGN) return builder.makeBinary(BinaryOp::CopySignFloat64, process(ast[2][0]), process(ast[2][1])); + if (name == F32_COPYSIGN) + return builder.makeBinary(BinaryOp::CopySignFloat32, + process(ast[2][0]), + process(ast[2][1])); + if (name == F64_COPYSIGN) + return builder.makeBinary(BinaryOp::CopySignFloat64, + process(ast[2][0]), + process(ast[2][1])); break; } default: {} } } - // ftCall_* and mftCall_* represent function table calls, either from the outside, or - // from the inside of the module. when compiling to wasm, we can just convert those - // into table calls + // ftCall_* and mftCall_* represent function table calls, either from + // the outside, or from the inside of the module. when compiling to + // wasm, we can just convert those into table calls if ((name.str[0] == 'f' && strncmp(name.str, FTCALL.str, 7) == 0) || (name.str[0] == 'm' && strncmp(name.str, MFTCALL.str, 8) == 0)) { tableCall = true; @@ -2234,22 +2591,26 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } if (tableCall) { auto specific = ret->dynCast<CallIndirect>(); - // note that we could also get the type from the suffix of the name, e.g., mftCall_vi - auto* fullType = getFunctionType(astStackHelper.getParent(), specific->operands, &asmData); + // note that we could also get the type from the suffix of the name, + // e.g., mftCall_vi + auto* fullType = getFunctionType( + astStackHelper.getParent(), specific->operands, &asmData); specific->fullType = fullType->name; specific->type = fullType->result; } if (callImport) { // apply the detected type from the parent - // note that this may not be complete, e.g. we may see f(); but f is an - // import which does return a value, and we use that elsewhere. finalizeCalls - // fixes that up. what we do here is wherever a value is used, we set the right - // value, which is enough to ensure that the wasm ast is valid for such uses. - // this is important as we run the optimizer on functions before we get - // to finalizeCalls (which we can only do once we've read all the functions, - // and we optimize in parallel starting earlier). + // note that this may not be complete, e.g. we may see f(); but f is + // an import which does return a value, and we use that elsewhere. + // finalizeCalls fixes that up. what we do here is wherever a value is + // used, we set the right value, which is enough to ensure that the + // wasm ast is valid for such uses. this is important as we run the + // optimizer on functions before we get to finalizeCalls (which we can + // only do once we've read all the functions, and we optimize in + // parallel starting earlier). auto* call = ret->cast<Call>(); - call->type = getResultTypeOfCallUsingParent(astStackHelper.getParent(), &asmData); + call->type = getResultTypeOfCallUsingParent( + astStackHelper.getParent(), &asmData); noteImportedFunctionCall(ast, call->type, call); } return ret; @@ -2257,17 +2618,26 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { // function pointers auto ret = allocator.alloc<CallIndirect>(); Ref target = ast[1]; - assert(target[0] == SUB && target[1]->isString() && target[2][0] == BINARY && target[2][1] == AND && target[2][3]->isNumber()); // FUNCTION_TABLE[(expr) & mask] - ret->target = process(target[2]); // TODO: as an optimization, we could look through the mask + assert(target[0] == SUB && target[1]->isString() && + target[2][0] == BINARY && target[2][1] == AND && + target[2][3]->isNumber()); // FUNCTION_TABLE[(expr) & mask] + // TODO: as an optimization, we could look through the mask + ret->target = process(target[2]); Ref args = ast[2]; for (unsigned i = 0; i < args->size(); i++) { ret->operands.push_back(process(args[i])); } - auto* fullType = getFunctionType(astStackHelper.getParent(), ret->operands, &asmData); + auto* fullType = + getFunctionType(astStackHelper.getParent(), ret->operands, &asmData); ret->fullType = fullType->name; ret->type = fullType->result; - // we don't know the table offset yet. emit target = target + callImport(tableName), which we fix up later when we know how asm function tables are layed out inside the wasm table. - ret->target = builder.makeBinary(BinaryOp::AddInt32, ret->target, builder.makeCall(target[1]->getIString(), {}, i32)); + // we don't know the table offset yet. emit target = target + + // callImport(tableName), which we fix up later when we know how asm + // function tables are layed out inside the wasm table. + ret->target = + builder.makeBinary(BinaryOp::AddInt32, + ret->target, + builder.makeCall(target[1]->getIString(), {}, i32)); return ret; } else if (what == RETURN) { Type type = !!ast[1] ? detectWasmType(ast[1], &asmData) : none; @@ -2306,12 +2676,17 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } else if (what == BREAK) { auto ret = allocator.alloc<Break>(); assert(breakStack.size() > 0); - ret->name = !!ast[1] ? nameMapper.sourceToUnique(getBreakLabelName(ast[1]->getIString())) : breakStack.back(); + ret->name = + !!ast[1] + ? nameMapper.sourceToUnique(getBreakLabelName(ast[1]->getIString())) + : breakStack.back(); return ret; } else if (what == CONTINUE) { auto ret = allocator.alloc<Break>(); assert(continueStack.size() > 0); - ret->name = !!ast[1] ? nameMapper.sourceToUnique(getContinueLabelName(ast[1]->getIString())) : continueStack.back(); + ret->name = !!ast[1] ? nameMapper.sourceToUnique( + getContinueLabelName(ast[1]->getIString())) + : continueStack.back(); return ret; } else if (what == WHILE) { bool forever = ast[1]->isNumber() && ast[1]->getInteger() == 1; @@ -2333,9 +2708,9 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { if (forever) { ret->body = process(ast[2]); } else { - Break *breakOut = allocator.alloc<Break>(); + Break* breakOut = allocator.alloc<Break>(); breakOut->name = out; - If *condition = allocator.alloc<If>(); + If* condition = allocator.alloc<If>(); condition->condition = builder.makeUnary(EqZInt32, process(ast[1])); condition->ifTrue = breakOut; condition->finalize(); @@ -2384,7 +2759,9 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { auto block = allocator.alloc<Block>(); block->list.push_back(child); if (isConcreteType(child->type)) { - block->list.push_back(builder.makeNop()); // ensure a nop at the end, so the block has guaranteed none type and no values fall through + // ensure a nop at the end, so the block has guaranteed none type + // and no values fall through + block->list.push_back(builder.makeNop()); } block->name = stop; block->finalize(); @@ -2418,19 +2795,16 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { breakStack.pop_back(); nameMapper.popLabelName(in); nameMapper.popLabelName(out); - Break *continuer = allocator.alloc<Break>(); + Break* continuer = allocator.alloc<Break>(); continuer->name = in; continuer->condition = process(ast[1]); continuer->finalize(); - Block *block = builder.blockifyWithName(loop->body, out, continuer); + Block* block = builder.blockifyWithName(loop->body, out, continuer); loop->body = block; loop->finalize(); return loop; } else if (what == FOR) { - Ref finit = ast[1], - fcond = ast[2], - finc = ast[3], - fbody = ast[4]; + Ref finit = ast[1], fcond = ast[2], finc = ast[3], fbody = ast[4]; auto ret = allocator.alloc<Loop>(); IString out, in; if (!parentLabel.isNull()) { @@ -2446,9 +2820,9 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { ret->name = in; breakStack.push_back(out); continueStack.push_back(in); - Break *breakOut = allocator.alloc<Break>(); + Break* breakOut = allocator.alloc<Break>(); breakOut->name = out; - If *condition = allocator.alloc<If>(); + If* condition = allocator.alloc<If>(); condition->condition = builder.makeUnary(EqZInt32, process(fcond)); condition->ifTrue = breakOut; condition->finalize(); @@ -2468,7 +2842,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { breakStack.pop_back(); nameMapper.popLabelName(in); nameMapper.popLabelName(out); - Block *outer = allocator.alloc<Block>(); + Block* outer = allocator.alloc<Block>(); // add an outer block for the init as well outer->list.push_back(process(finit)); outer->list.push_back(ret); @@ -2487,36 +2861,48 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { return ret; } else if (what == SEQ) { // Some (x, y) patterns can be optimized, like bitcasts, - // (HEAP32[tempDoublePtr >> 2] = i, Math_fround(HEAPF32[tempDoublePtr >> 2])); // i32->f32 - // (HEAP32[tempDoublePtr >> 2] = i, +HEAPF32[tempDoublePtr >> 2]); // i32->f32, no fround - // (HEAPF32[tempDoublePtr >> 2] = f, HEAP32[tempDoublePtr >> 2] | 0); // f32->i32 + // (HEAP32[tempDoublePtr >> 2] = i, + // Math_fround(HEAPF32[tempDoublePtr >> 2])); // i32->f32 + // (HEAP32[tempDoublePtr >> 2] = i, + // +HEAPF32[tempDoublePtr >> 2]); // i32->f32, no fround + // (HEAPF32[tempDoublePtr >> 2] = f, + // HEAP32[tempDoublePtr >> 2] | 0); // f32->i32 if (ast[1]->isAssign()) { auto* assign = ast[1]->asAssign(); Ref target = assign->target(); - if (target->isArray(SUB) && target[1]->isString() && target[2]->isArray(BINARY) && target[2][1] == RSHIFT && - target[2][2]->isString() && target[2][2] == tempDoublePtr && target[2][3]->isNumber() && target[2][3]->getNumber() == 2) { + if (target->isArray(SUB) && target[1]->isString() && + target[2]->isArray(BINARY) && target[2][1] == RSHIFT && + target[2][2]->isString() && target[2][2] == tempDoublePtr && + target[2][3]->isNumber() && target[2][3]->getNumber() == 2) { // (?[tempDoublePtr >> 2] = ?, ?) so far auto heap = target[1]->getIString(); if (views.find(heap) != views.end()) { AsmType writeType = views[heap].type; AsmType readType = ASM_NONE; Ref readValue; - if (ast[2]->isArray(BINARY) && ast[2][1] == OR && ast[2][3]->isNumber() && ast[2][3]->getNumber() == 0) { + if (ast[2]->isArray(BINARY) && ast[2][1] == OR && + ast[2][3]->isNumber() && ast[2][3]->getNumber() == 0) { readType = ASM_INT; readValue = ast[2][2]; } else if (ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == PLUS) { readType = ASM_DOUBLE; readValue = ast[2][2]; - } else if (ast[2]->isArray(CALL) && ast[2][1]->isString() && ast[2][1] == Math_fround) { + } else if (ast[2]->isArray(CALL) && ast[2][1]->isString() && + ast[2][1] == Math_fround) { readType = ASM_FLOAT; readValue = ast[2][2][0]; } if (readType != ASM_NONE) { - if (readValue->isArray(SUB) && readValue[1]->isString() && readValue[2]->isArray(BINARY) && readValue[2][1] == RSHIFT && - readValue[2][2]->isString() && readValue[2][2] == tempDoublePtr && readValue[2][3]->isNumber() && readValue[2][3]->getNumber() == 2) { + if (readValue->isArray(SUB) && readValue[1]->isString() && + readValue[2]->isArray(BINARY) && readValue[2][1] == RSHIFT && + readValue[2][2]->isString() && + readValue[2][2] == tempDoublePtr && + readValue[2][3]->isNumber() && + readValue[2][3]->getNumber() == 2) { // pattern looks right! Ref writtenValue = assign->value(); - if (writeType == ASM_INT && (readType == ASM_FLOAT || readType == ASM_DOUBLE)) { + if (writeType == ASM_INT && + (readType == ASM_FLOAT || readType == ASM_DOUBLE)) { auto conv = allocator.alloc<Unary>(); conv->op = ReinterpretInt32; conv->value = process(writtenValue); @@ -2574,8 +2960,10 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { min = index; max = index; } else { - if (index < min) min = index; - if (index > max) max = index; + if (index < min) + min = index; + if (index > max) + max = index; } } } @@ -2600,32 +2988,31 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { br->condition = offsetor; } else { assert(br->condition->type == i64); - // 64-bit condition. after offsetting it must be in a reasonable range, but the offsetting itself must be 64-bit + // 64-bit condition. after offsetting it must be in a reasonable + // range, but the offsetting itself must be 64-bit Binary* offsetor = allocator.alloc<Binary>(); offsetor->op = BinaryOp::SubInt64; offsetor->left = br->condition; offsetor->right = builder.makeConst(Literal(int64_t(min))); offsetor->type = i64; - // the switch itself can be 32-bit, as the range is in a reasonable range. so after - // offsetting, we need to make sure there are no high bits, then we can just look - // at the lower 32 bits + // the switch itself can be 32-bit, as the range is in a reasonable + // range. so after offsetting, we need to make sure there are no high + // bits, then we can just look at the lower 32 bits auto temp = Builder::addVar(function, i64); auto* block = builder.makeBlock(); block->list.push_back(builder.makeSetLocal(temp, offsetor)); - // if high bits, we can break to the default (we'll fill in the name later) - breakWhenNotMatching = builder.makeBreak(Name(), nullptr, + // if high bits, we can break to the default (we'll fill in the name + // later) + breakWhenNotMatching = builder.makeBreak( + Name(), + nullptr, builder.makeUnary( UnaryOp::WrapInt64, builder.makeBinary(BinaryOp::ShrUInt64, - builder.makeGetLocal(temp, i64), - builder.makeConst(Literal(int64_t(32))) - ) - ) - ); + builder.makeGetLocal(temp, i64), + builder.makeConst(Literal(int64_t(32)))))); block->list.push_back(breakWhenNotMatching); - block->list.push_back( - builder.makeGetLocal(temp, i64) - ); + block->list.push_back(builder.makeGetLocal(temp, i64)); block->finalize(); br->condition = builder.makeUnary(UnaryOp::WrapInt64, block); } @@ -2672,7 +3059,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { breakWhenNotMatching->name = br->default_; } for (size_t i = 0; i < br->targets.size(); i++) { - if (br->targets[i].isNull()) br->targets[i] = br->default_; + if (br->targets[i].isNull()) + br->targets[i] = br->default_; } } else { // we can't switch, make an if-chain instead of br_table @@ -2693,16 +3081,14 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } else { name = nameMapper.pushLabelName("switch-case"); auto* iff = builder.makeIf( - builder.makeBinary( - br->condition->type == i32 ? EqInt32 : EqInt64, - builder.makeGetLocal(var, br->condition->type), - builder.makeConst(getLiteral(condition)) - ), + builder.makeBinary(br->condition->type == i32 ? EqInt32 : EqInt64, + builder.makeGetLocal(var, br->condition->type), + builder.makeConst(getLiteral(condition))), builder.makeBreak(name), - chain - ); + chain); chain = iff; - if (!first) first = iff; + if (!first) + first = iff; } auto next = allocator.alloc<Block>(); top->name = name; @@ -2734,13 +3120,15 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { return (Expression*)nullptr; // avoid warning }; - // given HEAP32[addr >> 2], we need an absolute address, and would like to remove that shift. - // if there is a shift, we can just look through it, etc. + // given HEAP32[addr >> 2], we need an absolute address, and would like to + // remove that shift. if there is a shift, we can just look through it, etc. processUnshifted = [&](Ref ptr, unsigned bytes) { auto shifts = bytesToShift(bytes); // HEAP?[addr >> ?], or HEAP8[x | 0] - if ((ptr->isArray(BINARY) && ptr[1] == RSHIFT && ptr[3]->isNumber() && ptr[3]->getInteger() == shifts) || - (bytes == 1 && ptr->isArray(BINARY) && ptr[1] == OR && ptr[3]->isNumber() && ptr[3]->getInteger() == 0)) { + if ((ptr->isArray(BINARY) && ptr[1] == RSHIFT && ptr[3]->isNumber() && + ptr[3]->getInteger() == shifts) || + (bytes == 1 && ptr->isArray(BINARY) && ptr[1] == OR && + ptr[3]->isNumber() && ptr[3]->getInteger() == 0)) { return process(ptr[2]); // look through it } else if (ptr->isNumber()) { // constant, apply a shift (e.g. HEAP32[1] is address 4) @@ -2755,7 +3143,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { processIgnoringShift = [&](Ref ptr, unsigned bytes) { // If there is a shift here, no matter the size look through it. if ((ptr->isArray(BINARY) && ptr[1] == RSHIFT && ptr[3]->isNumber()) || - (bytes == 1 && ptr->isArray(BINARY) && ptr[1] == OR && ptr[3]->isNumber() && ptr[3]->getInteger() == 0)) { + (bytes == 1 && ptr->isArray(BINARY) && ptr[1] == OR && + ptr[3]->isNumber() && ptr[3]->getInteger() == 0)) { return process(ptr[2]); } // Otherwise do the same as processUnshifted. @@ -2764,8 +3153,10 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { processStatements = [&](Ref ast, unsigned from) -> Expression* { unsigned size = ast->size() - from; - if (size == 0) return allocator.alloc<Nop>(); - if (size == 1) return process(ast[from]); + if (size == 0) + return allocator.alloc<Nop>(); + if (size == 1) + return process(ast[from]); auto block = allocator.alloc<Block>(); for (unsigned i = from; i < ast->size(); i++) { block->list.push_back(process(ast[i])); diff --git a/src/asm_v_wasm.h b/src/asm_v_wasm.h index 08416ef80..66601cf10 100644 --- a/src/asm_v_wasm.h +++ b/src/asm_v_wasm.h @@ -17,8 +17,8 @@ #ifndef wasm_asm_v_wasm_h #define wasm_asm_v_wasm_h -#include "mixed_arena.h" #include "emscripten-optimizer/optimizer.h" +#include "mixed_arena.h" #include "wasm.h" namespace wasm { @@ -29,13 +29,14 @@ AsmType wasmToAsmType(Type type); char getSig(Type type); -std::string getSig(const FunctionType *type); +std::string getSig(const FunctionType* type); -std::string getSig(Function *func); +std::string getSig(Function* func); template<typename T, - typename std::enable_if<std::is_base_of<Expression, T>::value>::type* = nullptr> -std::string getSig(T *call) { + typename std::enable_if<std::is_base_of<Expression, T>::value>::type* = + nullptr> +std::string getSig(T* call) { std::string ret; ret += getSig(call->type); for (auto operand : call->operands) { diff --git a/src/asmjs/asm_v_wasm.cpp b/src/asmjs/asm_v_wasm.cpp index a0b3938fa..bbc7dabd9 100644 --- a/src/asmjs/asm_v_wasm.cpp +++ b/src/asmjs/asm_v_wasm.cpp @@ -17,52 +17,71 @@ #include "asm_v_wasm.h" #include "wasm.h" - namespace wasm { Type asmToWasmType(AsmType asmType) { switch (asmType) { - case ASM_INT: return Type::i32; - case ASM_DOUBLE: return Type::f64; - case ASM_FLOAT: return Type::f32; - case ASM_INT64: return Type::i64; - case ASM_NONE: return Type::none; + case ASM_INT: + return Type::i32; + case ASM_DOUBLE: + return Type::f64; + case ASM_FLOAT: + return Type::f32; + case ASM_INT64: + return Type::i64; + case ASM_NONE: + return Type::none; case ASM_FLOAT32X4: case ASM_FLOAT64X2: case ASM_INT8X16: case ASM_INT16X8: - case ASM_INT32X4: return Type::v128; + case ASM_INT32X4: + return Type::v128; } WASM_UNREACHABLE(); } AsmType wasmToAsmType(Type type) { switch (type) { - case i32: return ASM_INT; - case f32: return ASM_FLOAT; - case f64: return ASM_DOUBLE; - case i64: return ASM_INT64; - case v128: assert(false && "v128 not implemented yet"); - case none: return ASM_NONE; - case unreachable: WASM_UNREACHABLE(); + case i32: + return ASM_INT; + case f32: + return ASM_FLOAT; + case f64: + return ASM_DOUBLE; + case i64: + return ASM_INT64; + case v128: + assert(false && "v128 not implemented yet"); + case none: + return ASM_NONE; + case unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } char getSig(Type type) { switch (type) { - case i32: return 'i'; - case i64: return 'j'; - case f32: return 'f'; - case f64: return 'd'; - case v128: return 'V'; - case none: return 'v'; - case unreachable: WASM_UNREACHABLE(); + case i32: + return 'i'; + case i64: + return 'j'; + case f32: + return 'f'; + case f64: + return 'd'; + case v128: + return 'V'; + case none: + return 'v'; + case unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } -std::string getSig(const FunctionType *type) { +std::string getSig(const FunctionType* type) { std::string ret; ret += getSig(type->result); for (auto param : type->params) { @@ -71,7 +90,7 @@ std::string getSig(const FunctionType *type) { return ret; } -std::string getSig(Function *func) { +std::string getSig(Function* func) { std::string ret; ret += getSig(func->result); for (auto type : func->params) { @@ -82,13 +101,20 @@ std::string getSig(Function *func) { Type sigToType(char sig) { switch (sig) { - case 'i': return i32; - case 'j': return i64; - case 'f': return f32; - case 'd': return f64; - case 'V': return v128; - case 'v': return none; - default: abort(); + case 'i': + return i32; + case 'j': + return i64; + case 'f': + return f32; + case 'd': + return f64; + case 'V': + return v128; + case 'v': + return none; + default: + abort(); } } diff --git a/src/asmjs/asmangle.cpp b/src/asmjs/asmangle.cpp index 0453139b3..743603023 100644 --- a/src/asmjs/asmangle.cpp +++ b/src/asmjs/asmangle.cpp @@ -17,7 +17,6 @@ #include "asmjs/asmangle.h" #include <assert.h> - namespace wasm { std::string asmangle(std::string name) { @@ -49,10 +48,7 @@ std::string asmangle(std::string name) { break; } default: { - if ( - !(ch >= 'a' && ch <= 'z') && - !(ch >= 'A' && ch <= 'Z') - ) { + if (!(ch >= 'a' && ch <= 'z') && !(ch >= 'A' && ch <= 'Z')) { name = "$" + name.substr(1); mightBeKeyword = false; } @@ -79,10 +75,7 @@ std::string asmangle(std::string name) { break; } default: { - if ( - !(ch >= 'a' && ch <= 'z') && - !(ch >= 'A' && ch <= 'Z') - ) { + if (!(ch >= 'a' && ch <= 'z') && !(ch >= 'A' && ch <= 'Z')) { name = name.substr(0, i) + "_" + name.substr(i + 1); mightBeKeyword = false; } @@ -106,59 +99,36 @@ std::string asmangle(std::string name) { break; } case 'c': { - if ( - name == "case" || - name == "continue" || - name == "catch" || - name == "const" || - name == "class" - ) { + if (name == "case" || name == "continue" || name == "catch" || + name == "const" || name == "class") { goto mangleKeyword; } break; } case 'd': { - if ( - name == "do" || - name == "default" || - name == "debugger" - ) { + if (name == "do" || name == "default" || name == "debugger") { goto mangleKeyword; } break; } case 'e': { - if ( - name == "else" || - name == "enum" || - name == "eval" || // to be sure - name == "export" || - name == "extends" - ) { + if (name == "else" || name == "enum" || name == "eval" || // to be sure + name == "export" || name == "extends") { goto mangleKeyword; } break; } case 'f': { - if ( - name == "for" || - name == "false" || - name == "finally" || - name == "function" - ) { + if (name == "for" || name == "false" || name == "finally" || + name == "function") { goto mangleKeyword; } break; } case 'i': { - if ( - name == "if" || - name == "in" || - name == "import" || - name == "interface" || - name == "implements" || - name == "instanceof" - ) { + if (name == "if" || name == "in" || name == "import" || + name == "interface" || name == "implements" || + name == "instanceof") { goto mangleKeyword; } break; @@ -170,21 +140,14 @@ std::string asmangle(std::string name) { break; } case 'n': { - if ( - name == "new" || - name == "null" - ) { + if (name == "new" || name == "null") { goto mangleKeyword; } break; } case 'p': { - if ( - name == "public" || - name == "package" || - name == "private" || - name == "protected" - ) { + if (name == "public" || name == "package" || name == "private" || + name == "protected") { goto mangleKeyword; } break; @@ -196,41 +159,26 @@ std::string asmangle(std::string name) { break; } case 's': { - if ( - name == "super" || - name == "static" || - name == "switch" - ) { + if (name == "super" || name == "static" || name == "switch") { goto mangleKeyword; } break; } case 't': { - if ( - name == "try" || - name == "this" || - name == "true" || - name == "throw" || - name == "typeof" - ) { + if (name == "try" || name == "this" || name == "true" || + name == "throw" || name == "typeof") { goto mangleKeyword; } break; } case 'v': { - if ( - name == "var" || - name == "void" - ) { + if (name == "var" || name == "void") { goto mangleKeyword; } break; } case 'w': { - if ( - name == "with" || - name == "while" - ) { + if (name == "with" || name == "while") { goto mangleKeyword; } break; @@ -241,9 +189,7 @@ std::string asmangle(std::string name) { } break; } - mangleKeyword: { - name = name + "_"; - } + mangleKeyword : { name = name + "_"; } } } return name; diff --git a/src/asmjs/asmangle.h b/src/asmjs/asmangle.h index 161101c07..f0e96317b 100644 --- a/src/asmjs/asmangle.h +++ b/src/asmjs/asmangle.h @@ -19,11 +19,10 @@ #include <string> - namespace wasm { - // Mangles a WebAssembly name to a valid JavaScript identifier. - std::string asmangle(std::string name); +// Mangles a WebAssembly name to a valid JavaScript identifier. +std::string asmangle(std::string name); } // namespace wasm diff --git a/src/asmjs/shared-constants.cpp b/src/asmjs/shared-constants.cpp index ec04b63e7..f1123ba67 100644 --- a/src/asmjs/shared-constants.cpp +++ b/src/asmjs/shared-constants.cpp @@ -18,104 +18,104 @@ namespace wasm { -cashew::IString GLOBAL("global"), - NAN_("NaN"), - INFINITY_("Infinity"), - NAN__("nan"), - INFINITY__("infinity"), - TOPMOST("topmost"), - INT8ARRAY("Int8Array"), - INT16ARRAY("Int16Array"), - INT32ARRAY("Int32Array"), - UINT8ARRAY("Uint8Array"), - UINT16ARRAY("Uint16Array"), - UINT32ARRAY("Uint32Array"), - FLOAT32ARRAY("Float32Array"), - FLOAT64ARRAY("Float64Array"), - ARRAY_BUFFER("ArrayBuffer"), - ASM_MODULE("asmModule"), - IMPOSSIBLE_CONTINUE("impossible-continue"), - MATH("Math"), - IMUL("imul"), - CLZ32("clz32"), - FROUND("fround"), - ASM2WASM("asm2wasm"), - MIN("min"), - MAX("max"), - F64_REM("f64-rem"), - F64_TO_INT("f64-to-int"), - F64_TO_UINT("f64-to-uint"), - F64_TO_INT64("f64-to-int64"), - F64_TO_UINT64("f64-to-uint64"), - F32_TO_INT("f32-to-int"), - F32_TO_UINT("f32-to-uint"), - F32_TO_INT64("f32-to-int64"), - F32_TO_UINT64("f32-to-uint64"), - I32S_DIV("i32s-div"), - I32U_DIV("i32u-div"), - I32S_REM("i32s-rem"), - I32U_REM("i32u-rem"), - GLOBAL_MATH("global.Math"), - ABS("abs"), - FLOOR("floor"), - CEIL("ceil"), - SQRT("sqrt"), - POW("pow"), - I32_TEMP("asm2wasm_i32_temp"), - DEBUGGER("debugger"), - USE_ASM("use asm"), - ALMOST_ASM("almost asm"), - BUFFER("buffer"), - ENV("env"), - STACKTOP("STACKTOP"), - STACK_MAX("STACK_MAX"), - INSTRUMENT("instrument"), - MATH_IMUL("Math_imul"), - MATH_ABS("Math_abs"), - MATH_CEIL("Math_ceil"), - MATH_CLZ32("Math_clz32"), - MATH_FLOOR("Math_floor"), - MATH_TRUNC("Math_trunc"), - MATH_SQRT("Math_sqrt"), - MATH_MIN("Math_min"), - MATH_MAX("Math_max"), - WASM_CTZ32("__wasm_ctz_i32"), - WASM_CTZ64("__wasm_ctz_i64"), - WASM_CLZ32("__wasm_clz_i32"), - WASM_CLZ64("__wasm_clz_i64"), - WASM_POPCNT32("__wasm_popcnt_i32"), - WASM_POPCNT64("__wasm_popcnt_i64"), - WASM_ROTL32("__wasm_rotl_i32"), - WASM_ROTL64("__wasm_rotl_i64"), - WASM_ROTR32("__wasm_rotr_i32"), - WASM_ROTR64("__wasm_rotr_i64"), - WASM_GROW_MEMORY("__wasm_grow_memory"), - WASM_CURRENT_MEMORY("__wasm_current_memory"), - WASM_FETCH_HIGH_BITS("__wasm_fetch_high_bits"), - INT64_TO_32_HIGH_BITS("i64toi32_i32$HIGH_BITS"), - WASM_NEAREST_F32("__wasm_nearest_f32"), - WASM_NEAREST_F64("__wasm_nearest_f64"), - WASM_TRUNC_F32("__wasm_trunc_f32"), - WASM_TRUNC_F64("__wasm_trunc_f64"), - WASM_I64_MUL("__wasm_i64_mul"), - WASM_I64_SDIV("__wasm_i64_sdiv"), - WASM_I64_UDIV("__wasm_i64_udiv"), - WASM_I64_SREM("__wasm_i64_srem"), - WASM_I64_UREM("__wasm_i64_urem"); +cashew::IString GLOBAL("global"); +cashew::IString NAN_("NaN"); +cashew::IString INFINITY_("Infinity"); +cashew::IString NAN__("nan"); +cashew::IString INFINITY__("infinity"); +cashew::IString TOPMOST("topmost"); +cashew::IString INT8ARRAY("Int8Array"); +cashew::IString INT16ARRAY("Int16Array"); +cashew::IString INT32ARRAY("Int32Array"); +cashew::IString UINT8ARRAY("Uint8Array"); +cashew::IString UINT16ARRAY("Uint16Array"); +cashew::IString UINT32ARRAY("Uint32Array"); +cashew::IString FLOAT32ARRAY("Float32Array"); +cashew::IString FLOAT64ARRAY("Float64Array"); +cashew::IString ARRAY_BUFFER("ArrayBuffer"); +cashew::IString ASM_MODULE("asmModule"); +cashew::IString IMPOSSIBLE_CONTINUE("impossible-continue"); +cashew::IString MATH("Math"); +cashew::IString IMUL("imul"); +cashew::IString CLZ32("clz32"); +cashew::IString FROUND("fround"); +cashew::IString ASM2WASM("asm2wasm"); +cashew::IString MIN("min"); +cashew::IString MAX("max"); +cashew::IString F64_REM("f64-rem"); +cashew::IString F64_TO_INT("f64-to-int"); +cashew::IString F64_TO_UINT("f64-to-uint"); +cashew::IString F64_TO_INT64("f64-to-int64"); +cashew::IString F64_TO_UINT64("f64-to-uint64"); +cashew::IString F32_TO_INT("f32-to-int"); +cashew::IString F32_TO_UINT("f32-to-uint"); +cashew::IString F32_TO_INT64("f32-to-int64"); +cashew::IString F32_TO_UINT64("f32-to-uint64"); +cashew::IString I32S_DIV("i32s-div"); +cashew::IString I32U_DIV("i32u-div"); +cashew::IString I32S_REM("i32s-rem"); +cashew::IString I32U_REM("i32u-rem"); +cashew::IString GLOBAL_MATH("global.Math"); +cashew::IString ABS("abs"); +cashew::IString FLOOR("floor"); +cashew::IString CEIL("ceil"); +cashew::IString SQRT("sqrt"); +cashew::IString POW("pow"); +cashew::IString I32_TEMP("asm2wasm_i32_temp"); +cashew::IString DEBUGGER("debugger"); +cashew::IString USE_ASM("use asm"); +cashew::IString ALMOST_ASM("almost asm"); +cashew::IString BUFFER("buffer"); +cashew::IString ENV("env"); +cashew::IString STACKTOP("STACKTOP"); +cashew::IString STACK_MAX("STACK_MAX"); +cashew::IString INSTRUMENT("instrument"); +cashew::IString MATH_IMUL("Math_imul"); +cashew::IString MATH_ABS("Math_abs"); +cashew::IString MATH_CEIL("Math_ceil"); +cashew::IString MATH_CLZ32("Math_clz32"); +cashew::IString MATH_FLOOR("Math_floor"); +cashew::IString MATH_TRUNC("Math_trunc"); +cashew::IString MATH_SQRT("Math_sqrt"); +cashew::IString MATH_MIN("Math_min"); +cashew::IString MATH_MAX("Math_max"); +cashew::IString WASM_CTZ32("__wasm_ctz_i32"); +cashew::IString WASM_CTZ64("__wasm_ctz_i64"); +cashew::IString WASM_CLZ32("__wasm_clz_i32"); +cashew::IString WASM_CLZ64("__wasm_clz_i64"); +cashew::IString WASM_POPCNT32("__wasm_popcnt_i32"); +cashew::IString WASM_POPCNT64("__wasm_popcnt_i64"); +cashew::IString WASM_ROTL32("__wasm_rotl_i32"); +cashew::IString WASM_ROTL64("__wasm_rotl_i64"); +cashew::IString WASM_ROTR32("__wasm_rotr_i32"); +cashew::IString WASM_ROTR64("__wasm_rotr_i64"); +cashew::IString WASM_GROW_MEMORY("__wasm_grow_memory"); +cashew::IString WASM_CURRENT_MEMORY("__wasm_current_memory"); +cashew::IString WASM_FETCH_HIGH_BITS("__wasm_fetch_high_bits"); +cashew::IString INT64_TO_32_HIGH_BITS("i64toi32_i32$HIGH_BITS"); +cashew::IString WASM_NEAREST_F32("__wasm_nearest_f32"); +cashew::IString WASM_NEAREST_F64("__wasm_nearest_f64"); +cashew::IString WASM_TRUNC_F32("__wasm_trunc_f32"); +cashew::IString WASM_TRUNC_F64("__wasm_trunc_f64"); +cashew::IString WASM_I64_MUL("__wasm_i64_mul"); +cashew::IString WASM_I64_SDIV("__wasm_i64_sdiv"); +cashew::IString WASM_I64_UDIV("__wasm_i64_udiv"); +cashew::IString WASM_I64_SREM("__wasm_i64_srem"); +cashew::IString WASM_I64_UREM("__wasm_i64_urem"); namespace ABI { namespace wasm2js { -cashew::IString SCRATCH_LOAD_I32("wasm2js_scratch_load_i32"), - SCRATCH_STORE_I32("wasm2js_scratch_store_i32"), - SCRATCH_LOAD_I64("wasm2js_scratch_load_i64"), - SCRATCH_STORE_I64("wasm2js_scratch_store_i64"), - SCRATCH_LOAD_F32("wasm2js_scratch_load_f32"), - SCRATCH_STORE_F32("wasm2js_scratch_store_f32"), - SCRATCH_LOAD_F64("wasm2js_scratch_load_f64"), - SCRATCH_STORE_F64("wasm2js_scratch_store_f64"); +cashew::IString SCRATCH_LOAD_I32("wasm2js_scratch_load_i32"); +cashew::IString SCRATCH_STORE_I32("wasm2js_scratch_store_i32"); +cashew::IString SCRATCH_LOAD_I64("wasm2js_scratch_load_i64"); +cashew::IString SCRATCH_STORE_I64("wasm2js_scratch_store_i64"); +cashew::IString SCRATCH_LOAD_F32("wasm2js_scratch_load_f32"); +cashew::IString SCRATCH_STORE_F32("wasm2js_scratch_store_f32"); +cashew::IString SCRATCH_LOAD_F64("wasm2js_scratch_load_f64"); +cashew::IString SCRATCH_STORE_F64("wasm2js_scratch_store_f64"); } // namespace wasm2js } // namespace ABI -} +} // namespace wasm diff --git a/src/asmjs/shared-constants.h b/src/asmjs/shared-constants.h index b1f93336b..e8d5d5693 100644 --- a/src/asmjs/shared-constants.h +++ b/src/asmjs/shared-constants.h @@ -21,90 +21,90 @@ namespace wasm { -extern cashew::IString GLOBAL, - NAN_, - INFINITY_, - NAN__, - INFINITY__, - TOPMOST, - INT8ARRAY, - INT16ARRAY, - INT32ARRAY, - UINT8ARRAY, - UINT16ARRAY, - UINT32ARRAY, - FLOAT32ARRAY, - FLOAT64ARRAY, - ARRAY_BUFFER, - ASM_MODULE, - IMPOSSIBLE_CONTINUE, - MATH, - IMUL, - CLZ32, - FROUND, - ASM2WASM, - MIN, - MAX, - F64_REM, - F64_TO_INT, - F64_TO_UINT, - F64_TO_INT64, - F64_TO_UINT64, - F32_TO_INT, - F32_TO_UINT, - F32_TO_INT64, - F32_TO_UINT64, - I32S_DIV, - I32U_DIV, - I32S_REM, - I32U_REM, - GLOBAL_MATH, - ABS, - FLOOR, - CEIL, - SQRT, - POW, - I32_TEMP, - DEBUGGER, - USE_ASM, - ALMOST_ASM, - BUFFER, - ENV, - STACKTOP, - STACK_MAX, - INSTRUMENT, - MATH_IMUL, - MATH_ABS, - MATH_CEIL, - MATH_CLZ32, - MATH_FLOOR, - MATH_TRUNC, - MATH_SQRT, - MATH_MIN, - MATH_MAX, - WASM_CTZ32, - WASM_CTZ64, - WASM_CLZ32, - WASM_CLZ64, - WASM_POPCNT32, - WASM_POPCNT64, - WASM_ROTL32, - WASM_ROTL64, - WASM_ROTR32, - WASM_ROTR64, - WASM_GROW_MEMORY, - WASM_CURRENT_MEMORY, - WASM_FETCH_HIGH_BITS, - INT64_TO_32_HIGH_BITS, - WASM_NEAREST_F32, - WASM_NEAREST_F64, - WASM_TRUNC_F32, - WASM_TRUNC_F64, - WASM_I64_MUL, - WASM_I64_SDIV, - WASM_I64_UDIV, - WASM_I64_SREM, - WASM_I64_UREM; -} +extern cashew::IString GLOBAL; +extern cashew::IString NAN_; +extern cashew::IString INFINITY_; +extern cashew::IString NAN__; +extern cashew::IString INFINITY__; +extern cashew::IString TOPMOST; +extern cashew::IString INT8ARRAY; +extern cashew::IString INT16ARRAY; +extern cashew::IString INT32ARRAY; +extern cashew::IString UINT8ARRAY; +extern cashew::IString UINT16ARRAY; +extern cashew::IString UINT32ARRAY; +extern cashew::IString FLOAT32ARRAY; +extern cashew::IString FLOAT64ARRAY; +extern cashew::IString ARRAY_BUFFER; +extern cashew::IString ASM_MODULE; +extern cashew::IString IMPOSSIBLE_CONTINUE; +extern cashew::IString MATH; +extern cashew::IString IMUL; +extern cashew::IString CLZ32; +extern cashew::IString FROUND; +extern cashew::IString ASM2WASM; +extern cashew::IString MIN; +extern cashew::IString MAX; +extern cashew::IString F64_REM; +extern cashew::IString F64_TO_INT; +extern cashew::IString F64_TO_UINT; +extern cashew::IString F64_TO_INT64; +extern cashew::IString F64_TO_UINT64; +extern cashew::IString F32_TO_INT; +extern cashew::IString F32_TO_UINT; +extern cashew::IString F32_TO_INT64; +extern cashew::IString F32_TO_UINT64; +extern cashew::IString I32S_DIV; +extern cashew::IString I32U_DIV; +extern cashew::IString I32S_REM; +extern cashew::IString I32U_REM; +extern cashew::IString GLOBAL_MATH; +extern cashew::IString ABS; +extern cashew::IString FLOOR; +extern cashew::IString CEIL; +extern cashew::IString SQRT; +extern cashew::IString POW; +extern cashew::IString I32_TEMP; +extern cashew::IString DEBUGGER; +extern cashew::IString USE_ASM; +extern cashew::IString ALMOST_ASM; +extern cashew::IString BUFFER; +extern cashew::IString ENV; +extern cashew::IString STACKTOP; +extern cashew::IString STACK_MAX; +extern cashew::IString INSTRUMENT; +extern cashew::IString MATH_IMUL; +extern cashew::IString MATH_ABS; +extern cashew::IString MATH_CEIL; +extern cashew::IString MATH_CLZ32; +extern cashew::IString MATH_FLOOR; +extern cashew::IString MATH_TRUNC; +extern cashew::IString MATH_SQRT; +extern cashew::IString MATH_MIN; +extern cashew::IString MATH_MAX; +extern cashew::IString WASM_CTZ32; +extern cashew::IString WASM_CTZ64; +extern cashew::IString WASM_CLZ32; +extern cashew::IString WASM_CLZ64; +extern cashew::IString WASM_POPCNT32; +extern cashew::IString WASM_POPCNT64; +extern cashew::IString WASM_ROTL32; +extern cashew::IString WASM_ROTL64; +extern cashew::IString WASM_ROTR32; +extern cashew::IString WASM_ROTR64; +extern cashew::IString WASM_GROW_MEMORY; +extern cashew::IString WASM_CURRENT_MEMORY; +extern cashew::IString WASM_FETCH_HIGH_BITS; +extern cashew::IString INT64_TO_32_HIGH_BITS; +extern cashew::IString WASM_NEAREST_F32; +extern cashew::IString WASM_NEAREST_F64; +extern cashew::IString WASM_TRUNC_F32; +extern cashew::IString WASM_TRUNC_F64; +extern cashew::IString WASM_I64_MUL; +extern cashew::IString WASM_I64_SDIV; +extern cashew::IString WASM_I64_UDIV; +extern cashew::IString WASM_I64_SREM; +extern cashew::IString WASM_I64_UREM; +} // namespace wasm #endif // wasm_asmjs_shared_constants_h diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 87dd631d3..5842e5afe 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -21,19 +21,19 @@ #include <mutex> #include "binaryen-c.h" +#include "cfg/Relooper.h" +#include "ir/function-type-utils.h" +#include "ir/utils.h" #include "pass.h" -#include "wasm.h" +#include "shell-interface.h" #include "wasm-binary.h" #include "wasm-builder.h" #include "wasm-interpreter.h" #include "wasm-printing.h" #include "wasm-s-parser.h" #include "wasm-validator.h" +#include "wasm.h" #include "wasm2js.h" -#include "cfg/Relooper.h" -#include "ir/function-type-utils.h" -#include "ir/utils.h" -#include "shell-interface.h" #ifdef __EMSCRIPTEN__ #include <emscripten.h> @@ -43,35 +43,51 @@ using namespace wasm; // Literal utilities -static_assert(sizeof(BinaryenLiteral) == sizeof(Literal), "Binaryen C API literal must match wasm.h"); +static_assert(sizeof(BinaryenLiteral) == sizeof(Literal), + "Binaryen C API literal must match wasm.h"); BinaryenLiteral toBinaryenLiteral(Literal x) { BinaryenLiteral ret; ret.type = x.type; switch (x.type) { - case Type::i32: ret.i32 = x.geti32(); break; - case Type::i64: ret.i64 = x.geti64(); break; - case Type::f32: ret.i32 = x.reinterpreti32(); break; - case Type::f64: ret.i64 = x.reinterpreti64(); break; + case Type::i32: + ret.i32 = x.geti32(); + break; + case Type::i64: + ret.i64 = x.geti64(); + break; + case Type::f32: + ret.i32 = x.reinterpreti32(); + break; + case Type::f64: + ret.i64 = x.reinterpreti64(); + break; case Type::v128: { memcpy(&ret.v128, x.getv128Ptr(), 16); break; } case Type::none: - case Type::unreachable: WASM_UNREACHABLE(); + case Type::unreachable: + WASM_UNREACHABLE(); } return ret; } Literal fromBinaryenLiteral(BinaryenLiteral x) { switch (x.type) { - case Type::i32: return Literal(x.i32); - case Type::i64: return Literal(x.i64); - case Type::f32: return Literal(x.i32).castToF32(); - case Type::f64: return Literal(x.i64).castToF64(); - case Type::v128: return Literal(x.v128); + case Type::i32: + return Literal(x.i32); + case Type::i64: + return Literal(x.i64); + case Type::f32: + return Literal(x.i32).castToF32(); + case Type::f64: + return Literal(x.i64).castToF64(); + case Type::v128: + return Literal(x.v128); case Type::none: - case Type::unreachable: WASM_UNREACHABLE(); + case Type::unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } @@ -84,15 +100,18 @@ static std::mutex BinaryenFunctionMutex; static std::mutex BinaryenFunctionTypeMutex; // Optimization options -static PassOptions globalPassOptions = PassOptions::getWithDefaultOptimizationOptions(); +static PassOptions globalPassOptions = + PassOptions::getWithDefaultOptimizationOptions(); // Tracing support static int tracing = 0; -void traceNameOrNULL(const char* name, std::ostream &out = std::cout) { - if (name) out << "\"" << name << "\""; - else out << "NULL"; +void traceNameOrNULL(const char* name, std::ostream& out = std::cout) { + if (name) + out << "\"" << name << "\""; + else + out << "NULL"; } std::map<BinaryenFunctionTypeRef, size_t> functionTypes; @@ -115,27 +134,29 @@ std::string getTemp() { } template<typename T> -void printArg(std::ostream &setup, std::ostream& out, T arg) { +void printArg(std::ostream& setup, std::ostream& out, T arg) { out << arg; } template<> -void printArg(std::ostream &setup, std::ostream& out, BinaryenExpressionRef arg) { +void printArg(std::ostream& setup, + std::ostream& out, + BinaryenExpressionRef arg) { out << "expressions[" << expressions[arg] << "]"; } struct StringLit { const char* name; - StringLit(const char* name) : name(name) {}; + StringLit(const char* name) : name(name){}; }; template<> -void printArg(std::ostream &setup, std::ostream& out, StringLit arg) { +void printArg(std::ostream& setup, std::ostream& out, StringLit arg) { traceNameOrNULL(arg.name, out); } template<> -void printArg(std::ostream &setup, std::ostream &out, BinaryenType arg) { +void printArg(std::ostream& setup, std::ostream& out, BinaryenType arg) { if (arg == BinaryenTypeAuto()) { out << "BinaryenTypeAuto()"; } else { @@ -144,21 +165,29 @@ void printArg(std::ostream &setup, std::ostream &out, BinaryenType arg) { } template<> -void printArg(std::ostream &setup, std::ostream &out, BinaryenLiteral arg) { +void printArg(std::ostream& setup, std::ostream& out, BinaryenLiteral arg) { switch (arg.type) { - case Type::i32: out << "BinaryenLiteralInt32(" << arg.i32 << ")"; break; - case Type::i64: out << "BinaryenLiteralInt64(" << arg.i64 << ")"; break; + case Type::i32: + out << "BinaryenLiteralInt32(" << arg.i32 << ")"; + break; + case Type::i64: + out << "BinaryenLiteralInt64(" << arg.i64 << ")"; + break; case Type::f32: if (std::isnan(arg.f32)) { - out << "BinaryenLiteralFloat32(NAN)"; break; + out << "BinaryenLiteralFloat32(NAN)"; + break; } else { - out << "BinaryenLiteralFloat32(" << arg.f32 << ")"; break; + out << "BinaryenLiteralFloat32(" << arg.f32 << ")"; + break; } case Type::f64: if (std::isnan(arg.f64)) { - out << "BinaryenLiteralFloat64(NAN)"; break; + out << "BinaryenLiteralFloat64(NAN)"; + break; } else { - out << "BinaryenLiteralFloat64(" << arg.f64 << ")"; break; + out << "BinaryenLiteralFloat64(" << arg.f64 << ")"; + break; } case Type::v128: { std::string array = getTemp(); @@ -174,24 +203,28 @@ void printArg(std::ostream &setup, std::ostream &out, BinaryenLiteral arg) { break; } case Type::none: - case Type::unreachable: WASM_UNREACHABLE(); + case Type::unreachable: + WASM_UNREACHABLE(); } } template<typename T> -void traceArgs(std::ostream &setup, std::ostream &out, T arg) { +void traceArgs(std::ostream& setup, std::ostream& out, T arg) { printArg(setup, out, arg); } -template<typename T, typename S, typename ...Ts> -void traceArgs(std::ostream &setup, std::ostream &out, T arg, S next, Ts... rest) { +template<typename T, typename S, typename... Ts> +void traceArgs( + std::ostream& setup, std::ostream& out, T arg, S next, Ts... rest) { printArg(setup, out, arg); out << ", "; traceArgs(setup, out, next, rest...); } -template<typename ...Ts> -void traceExpression(BinaryenExpressionRef expr, const char* constructor, Ts... args) { +template<typename... Ts> +void traceExpression(BinaryenExpressionRef expr, + const char* constructor, + Ts... args) { auto id = noteExpression(expr); std::stringstream setup, out; out << "expressions[" << id << "] = " << constructor << "("; @@ -235,18 +268,30 @@ WASM_DEPRECATED BinaryenType BinaryenUndefined(void) { return uint32_t(-1); } // Expression ids -BinaryenExpressionId BinaryenInvalidId(void) { return Expression::Id::InvalidId; } +BinaryenExpressionId BinaryenInvalidId(void) { + return Expression::Id::InvalidId; +} BinaryenExpressionId BinaryenBlockId(void) { return Expression::Id::BlockId; } BinaryenExpressionId BinaryenIfId(void) { return Expression::Id::IfId; } BinaryenExpressionId BinaryenLoopId(void) { return Expression::Id::LoopId; } BinaryenExpressionId BinaryenBreakId(void) { return Expression::Id::BreakId; } BinaryenExpressionId BinaryenSwitchId(void) { return Expression::Id::SwitchId; } BinaryenExpressionId BinaryenCallId(void) { return Expression::Id::CallId; } -BinaryenExpressionId BinaryenCallIndirectId(void) { return Expression::Id::CallIndirectId; } -BinaryenExpressionId BinaryenGetLocalId(void) { return Expression::Id::GetLocalId; } -BinaryenExpressionId BinaryenSetLocalId(void) { return Expression::Id::SetLocalId; } -BinaryenExpressionId BinaryenGetGlobalId(void) { return Expression::Id::GetGlobalId; } -BinaryenExpressionId BinaryenSetGlobalId(void) { return Expression::Id::SetGlobalId; } +BinaryenExpressionId BinaryenCallIndirectId(void) { + return Expression::Id::CallIndirectId; +} +BinaryenExpressionId BinaryenGetLocalId(void) { + return Expression::Id::GetLocalId; +} +BinaryenExpressionId BinaryenSetLocalId(void) { + return Expression::Id::SetLocalId; +} +BinaryenExpressionId BinaryenGetGlobalId(void) { + return Expression::Id::GetGlobalId; +} +BinaryenExpressionId BinaryenSetGlobalId(void) { + return Expression::Id::SetGlobalId; +} BinaryenExpressionId BinaryenLoadId(void) { return Expression::Id::LoadId; } BinaryenExpressionId BinaryenStoreId(void) { return Expression::Id::StoreId; } BinaryenExpressionId BinaryenConstId(void) { return Expression::Id::ConstId; } @@ -257,27 +302,63 @@ BinaryenExpressionId BinaryenDropId(void) { return Expression::Id::DropId; } BinaryenExpressionId BinaryenReturnId(void) { return Expression::Id::ReturnId; } BinaryenExpressionId BinaryenHostId(void) { return Expression::Id::HostId; } BinaryenExpressionId BinaryenNopId(void) { return Expression::Id::NopId; } -BinaryenExpressionId BinaryenUnreachableId(void) { return Expression::Id::UnreachableId; } -BinaryenExpressionId BinaryenAtomicCmpxchgId(void) { return Expression::Id::AtomicCmpxchgId; } -BinaryenExpressionId BinaryenAtomicRMWId(void) { return Expression::Id::AtomicRMWId; } -BinaryenExpressionId BinaryenAtomicWaitId(void) { return Expression::Id::AtomicWaitId; } -BinaryenExpressionId BinaryenAtomicNotifyId(void) { return Expression::Id::AtomicNotifyId; } -BinaryenExpressionId BinaryenSIMDExtractId(void) { return Expression::Id::SIMDExtractId; } -BinaryenExpressionId BinaryenSIMDReplaceId(void) { return Expression::Id::SIMDReplaceId; } -BinaryenExpressionId BinaryenSIMDShuffleId(void) { return Expression::Id::SIMDShuffleId; } -BinaryenExpressionId BinaryenSIMDBitselectId(void) { return Expression::Id::SIMDBitselectId; } -BinaryenExpressionId BinaryenSIMDShiftId(void) { return Expression::Id::SIMDShiftId; } -BinaryenExpressionId BinaryenMemoryInitId(void) { return Expression::Id::MemoryInitId; } -BinaryenExpressionId BinaryenDataDropId(void) { return Expression::Id::DataDropId; } -BinaryenExpressionId BinaryenMemoryCopyId(void) { return Expression::Id::MemoryCopyId; } -BinaryenExpressionId BinaryenMemoryFillId(void) { return Expression::Id::MemoryFillId; } +BinaryenExpressionId BinaryenUnreachableId(void) { + return Expression::Id::UnreachableId; +} +BinaryenExpressionId BinaryenAtomicCmpxchgId(void) { + return Expression::Id::AtomicCmpxchgId; +} +BinaryenExpressionId BinaryenAtomicRMWId(void) { + return Expression::Id::AtomicRMWId; +} +BinaryenExpressionId BinaryenAtomicWaitId(void) { + return Expression::Id::AtomicWaitId; +} +BinaryenExpressionId BinaryenAtomicNotifyId(void) { + return Expression::Id::AtomicNotifyId; +} +BinaryenExpressionId BinaryenSIMDExtractId(void) { + return Expression::Id::SIMDExtractId; +} +BinaryenExpressionId BinaryenSIMDReplaceId(void) { + return Expression::Id::SIMDReplaceId; +} +BinaryenExpressionId BinaryenSIMDShuffleId(void) { + return Expression::Id::SIMDShuffleId; +} +BinaryenExpressionId BinaryenSIMDBitselectId(void) { + return Expression::Id::SIMDBitselectId; +} +BinaryenExpressionId BinaryenSIMDShiftId(void) { + return Expression::Id::SIMDShiftId; +} +BinaryenExpressionId BinaryenMemoryInitId(void) { + return Expression::Id::MemoryInitId; +} +BinaryenExpressionId BinaryenDataDropId(void) { + return Expression::Id::DataDropId; +} +BinaryenExpressionId BinaryenMemoryCopyId(void) { + return Expression::Id::MemoryCopyId; +} +BinaryenExpressionId BinaryenMemoryFillId(void) { + return Expression::Id::MemoryFillId; +} // External kinds -BinaryenExternalKind BinaryenExternalFunction(void) { return static_cast<BinaryenExternalKind>(ExternalKind::Function); } -BinaryenExternalKind BinaryenExternalTable(void) { return static_cast<BinaryenExternalKind>(ExternalKind::Table); } -BinaryenExternalKind BinaryenExternalMemory(void) { return static_cast<BinaryenExternalKind>(ExternalKind::Memory); } -BinaryenExternalKind BinaryenExternalGlobal(void) { return static_cast<BinaryenExternalKind>(ExternalKind::Global); } +BinaryenExternalKind BinaryenExternalFunction(void) { + return static_cast<BinaryenExternalKind>(ExternalKind::Function); +} +BinaryenExternalKind BinaryenExternalTable(void) { + return static_cast<BinaryenExternalKind>(ExternalKind::Table); +} +BinaryenExternalKind BinaryenExternalMemory(void) { + return static_cast<BinaryenExternalKind>(ExternalKind::Memory); +} +BinaryenExternalKind BinaryenExternalGlobal(void) { + return static_cast<BinaryenExternalKind>(ExternalKind::Global); +} // Modules @@ -312,11 +393,17 @@ void BinaryenModuleDispose(BinaryenModuleRef module) { // Function types -BinaryenFunctionTypeRef BinaryenAddFunctionType(BinaryenModuleRef module, const char* name, BinaryenType result, BinaryenType* paramTypes, BinaryenIndex numParams) { +BinaryenFunctionTypeRef BinaryenAddFunctionType(BinaryenModuleRef module, + const char* name, + BinaryenType result, + BinaryenType* paramTypes, + BinaryenIndex numParams) { auto* wasm = (Module*)module; auto ret = make_unique<FunctionType>(); - if (name) ret->name = name; - else ret->name = Name::fromInt(wasm->functionTypes.size()); + if (name) + ret->name = name; + else + ret->name = Name::fromInt(wasm->functionTypes.size()); ret->result = Type(result); for (BinaryenIndex i = 0; i < numParams; i++) { ret->params.push_back(Type(paramTypes[i])); @@ -326,13 +413,17 @@ BinaryenFunctionTypeRef BinaryenAddFunctionType(BinaryenModuleRef module, const std::cout << " {\n"; std::cout << " BinaryenType paramTypes[] = { "; for (BinaryenIndex i = 0; i < numParams; i++) { - if (i > 0) std::cout << ", "; + if (i > 0) + std::cout << ", "; std::cout << paramTypes[i]; } - if (numParams == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS + if (numParams == 0) + // ensure the array is not empty, otherwise a compiler error on VS + std::cout << "0"; std::cout << " };\n"; size_t id = functionTypes.size(); - std::cout << " functionTypes[" << id << "] = BinaryenAddFunctionType(the_module, "; + std::cout << " functionTypes[" << id + << "] = BinaryenAddFunctionType(the_module, "; functionTypes[ret.get()] = id; traceNameOrNULL(name); std::cout << ", " << result << ", paramTypes, " << numParams << ");\n"; @@ -362,13 +453,27 @@ void BinaryenRemoveFunctionType(BinaryenModuleRef module, const char* name) { } } -BinaryenLiteral BinaryenLiteralInt32(int32_t x) { return toBinaryenLiteral(Literal(x)); } -BinaryenLiteral BinaryenLiteralInt64(int64_t x) { return toBinaryenLiteral(Literal(x)); } -BinaryenLiteral BinaryenLiteralFloat32(float x) { return toBinaryenLiteral(Literal(x)); } -BinaryenLiteral BinaryenLiteralFloat64(double x) { return toBinaryenLiteral(Literal(x)); } -BinaryenLiteral BinaryenLiteralVec128(const uint8_t x[16]) { return toBinaryenLiteral(Literal(x)); } -BinaryenLiteral BinaryenLiteralFloat32Bits(int32_t x) { return toBinaryenLiteral(Literal(x).castToF32()); } -BinaryenLiteral BinaryenLiteralFloat64Bits(int64_t x) { return toBinaryenLiteral(Literal(x).castToF64()); } +BinaryenLiteral BinaryenLiteralInt32(int32_t x) { + return toBinaryenLiteral(Literal(x)); +} +BinaryenLiteral BinaryenLiteralInt64(int64_t x) { + return toBinaryenLiteral(Literal(x)); +} +BinaryenLiteral BinaryenLiteralFloat32(float x) { + return toBinaryenLiteral(Literal(x)); +} +BinaryenLiteral BinaryenLiteralFloat64(double x) { + return toBinaryenLiteral(Literal(x)); +} +BinaryenLiteral BinaryenLiteralVec128(const uint8_t x[16]) { + return toBinaryenLiteral(Literal(x)); +} +BinaryenLiteral BinaryenLiteralFloat32Bits(int32_t x) { + return toBinaryenLiteral(Literal(x).castToF32()); +} +BinaryenLiteral BinaryenLiteralFloat64Bits(int64_t x) { + return toBinaryenLiteral(Literal(x).castToF64()); +} // Expressions @@ -412,14 +517,30 @@ BinaryenOp BinaryenExtendS16Int32(void) { return ExtendS16Int32; } BinaryenOp BinaryenExtendS8Int64(void) { return ExtendS8Int64; } BinaryenOp BinaryenExtendS16Int64(void) { return ExtendS16Int64; } BinaryenOp BinaryenExtendS32Int64(void) { return ExtendS32Int64; } -BinaryenOp BinaryenConvertSInt32ToFloat32(void) { return ConvertSInt32ToFloat32; } -BinaryenOp BinaryenConvertSInt32ToFloat64(void) { return ConvertSInt32ToFloat64; } -BinaryenOp BinaryenConvertUInt32ToFloat32(void) { return ConvertUInt32ToFloat32; } -BinaryenOp BinaryenConvertUInt32ToFloat64(void) { return ConvertUInt32ToFloat64; } -BinaryenOp BinaryenConvertSInt64ToFloat32(void) { return ConvertSInt64ToFloat32; } -BinaryenOp BinaryenConvertSInt64ToFloat64(void) { return ConvertSInt64ToFloat64; } -BinaryenOp BinaryenConvertUInt64ToFloat32(void) { return ConvertUInt64ToFloat32; } -BinaryenOp BinaryenConvertUInt64ToFloat64(void) { return ConvertUInt64ToFloat64; } +BinaryenOp BinaryenConvertSInt32ToFloat32(void) { + return ConvertSInt32ToFloat32; +} +BinaryenOp BinaryenConvertSInt32ToFloat64(void) { + return ConvertSInt32ToFloat64; +} +BinaryenOp BinaryenConvertUInt32ToFloat32(void) { + return ConvertUInt32ToFloat32; +} +BinaryenOp BinaryenConvertUInt32ToFloat64(void) { + return ConvertUInt32ToFloat64; +} +BinaryenOp BinaryenConvertSInt64ToFloat32(void) { + return ConvertSInt64ToFloat32; +} +BinaryenOp BinaryenConvertSInt64ToFloat64(void) { + return ConvertSInt64ToFloat64; +} +BinaryenOp BinaryenConvertUInt64ToFloat32(void) { + return ConvertUInt64ToFloat32; +} +BinaryenOp BinaryenConvertUInt64ToFloat64(void) { + return ConvertUInt64ToFloat64; +} BinaryenOp BinaryenPromoteFloat32(void) { return PromoteFloat32; } BinaryenOp BinaryenDemoteFloat64(void) { return DemoteFloat64; } BinaryenOp BinaryenReinterpretInt32(void) { return ReinterpretInt32; } @@ -508,14 +629,30 @@ BinaryenOp BinaryenAtomicRMWAnd(void) { return AtomicRMWOp::And; } BinaryenOp BinaryenAtomicRMWOr(void) { return AtomicRMWOp::Or; } BinaryenOp BinaryenAtomicRMWXor(void) { return AtomicRMWOp::Xor; } BinaryenOp BinaryenAtomicRMWXchg(void) { return AtomicRMWOp::Xchg; } -BinaryenOp BinaryenTruncSatSFloat32ToInt32(void) { return TruncSatSFloat32ToInt32; } -BinaryenOp BinaryenTruncSatSFloat32ToInt64(void) { return TruncSatSFloat32ToInt64; } -BinaryenOp BinaryenTruncSatUFloat32ToInt32(void) { return TruncSatUFloat32ToInt32; } -BinaryenOp BinaryenTruncSatUFloat32ToInt64(void) { return TruncSatUFloat32ToInt64; } -BinaryenOp BinaryenTruncSatSFloat64ToInt32(void) { return TruncSatSFloat64ToInt32; } -BinaryenOp BinaryenTruncSatSFloat64ToInt64(void) { return TruncSatSFloat64ToInt64; } -BinaryenOp BinaryenTruncSatUFloat64ToInt32(void) { return TruncSatUFloat64ToInt32; } -BinaryenOp BinaryenTruncSatUFloat64ToInt64(void) { return TruncSatUFloat64ToInt64; } +BinaryenOp BinaryenTruncSatSFloat32ToInt32(void) { + return TruncSatSFloat32ToInt32; +} +BinaryenOp BinaryenTruncSatSFloat32ToInt64(void) { + return TruncSatSFloat32ToInt64; +} +BinaryenOp BinaryenTruncSatUFloat32ToInt32(void) { + return TruncSatUFloat32ToInt32; +} +BinaryenOp BinaryenTruncSatUFloat32ToInt64(void) { + return TruncSatUFloat32ToInt64; +} +BinaryenOp BinaryenTruncSatSFloat64ToInt32(void) { + return TruncSatSFloat64ToInt32; +} +BinaryenOp BinaryenTruncSatSFloat64ToInt64(void) { + return TruncSatSFloat64ToInt64; +} +BinaryenOp BinaryenTruncSatUFloat64ToInt32(void) { + return TruncSatUFloat64ToInt32; +} +BinaryenOp BinaryenTruncSatUFloat64ToInt64(void) { + return TruncSatUFloat64ToInt64; +} BinaryenOp BinaryenSplatVecI8x16(void) { return SplatVecI8x16; } BinaryenOp BinaryenExtractLaneSVecI8x16(void) { return ExtractLaneSVecI8x16; } BinaryenOp BinaryenExtractLaneUVecI8x16(void) { return ExtractLaneUVecI8x16; } @@ -643,41 +780,72 @@ BinaryenOp BinaryenMulVecF64x2(void) { return MulVecF64x2; } BinaryenOp BinaryenDivVecF64x2(void) { return DivVecF64x2; } BinaryenOp BinaryenMinVecF64x2(void) { return MinVecF64x2; } BinaryenOp BinaryenMaxVecF64x2(void) { return MaxVecF64x2; } -BinaryenOp BinaryenTruncSatSVecF32x4ToVecI32x4(void) { return TruncSatSVecF32x4ToVecI32x4; } -BinaryenOp BinaryenTruncSatUVecF32x4ToVecI32x4(void) { return TruncSatUVecF32x4ToVecI32x4; } -BinaryenOp BinaryenTruncSatSVecF64x2ToVecI64x2(void) { return TruncSatSVecF64x2ToVecI64x2; } -BinaryenOp BinaryenTruncSatUVecF64x2ToVecI64x2(void) { return TruncSatUVecF64x2ToVecI64x2; } -BinaryenOp BinaryenConvertSVecI32x4ToVecF32x4(void) { return ConvertSVecI32x4ToVecF32x4; } -BinaryenOp BinaryenConvertUVecI32x4ToVecF32x4(void) { return ConvertUVecI32x4ToVecF32x4; } -BinaryenOp BinaryenConvertSVecI64x2ToVecF64x2(void) { return ConvertSVecI64x2ToVecF64x2; } -BinaryenOp BinaryenConvertUVecI64x2ToVecF64x2(void) { return ConvertUVecI64x2ToVecF64x2; } - -BinaryenExpressionRef BinaryenBlock(BinaryenModuleRef module, const char* name, BinaryenExpressionRef* children, BinaryenIndex numChildren, BinaryenType type) { +BinaryenOp BinaryenTruncSatSVecF32x4ToVecI32x4(void) { + return TruncSatSVecF32x4ToVecI32x4; +} +BinaryenOp BinaryenTruncSatUVecF32x4ToVecI32x4(void) { + return TruncSatUVecF32x4ToVecI32x4; +} +BinaryenOp BinaryenTruncSatSVecF64x2ToVecI64x2(void) { + return TruncSatSVecF64x2ToVecI64x2; +} +BinaryenOp BinaryenTruncSatUVecF64x2ToVecI64x2(void) { + return TruncSatUVecF64x2ToVecI64x2; +} +BinaryenOp BinaryenConvertSVecI32x4ToVecF32x4(void) { + return ConvertSVecI32x4ToVecF32x4; +} +BinaryenOp BinaryenConvertUVecI32x4ToVecF32x4(void) { + return ConvertUVecI32x4ToVecF32x4; +} +BinaryenOp BinaryenConvertSVecI64x2ToVecF64x2(void) { + return ConvertSVecI64x2ToVecF64x2; +} +BinaryenOp BinaryenConvertUVecI64x2ToVecF64x2(void) { + return ConvertUVecI64x2ToVecF64x2; +} + +BinaryenExpressionRef BinaryenBlock(BinaryenModuleRef module, + const char* name, + BinaryenExpressionRef* children, + BinaryenIndex numChildren, + BinaryenType type) { auto* ret = ((Module*)module)->allocator.alloc<Block>(); - if (name) ret->name = name; + if (name) + ret->name = name; for (BinaryenIndex i = 0; i < numChildren; i++) { ret->list.push_back((Expression*)children[i]); } - if (type != BinaryenTypeAuto()) ret->finalize(Type(type)); - else ret->finalize(); + if (type != BinaryenTypeAuto()) + ret->finalize(Type(type)); + else + ret->finalize(); if (tracing) { std::cout << " {\n"; std::cout << " BinaryenExpressionRef children[] = { "; for (BinaryenIndex i = 0; i < numChildren; i++) { - if (i > 0) std::cout << ", "; - if (i % 6 == 5) std::cout << "\n "; // don't create hugely long lines + if (i > 0) + std::cout << ", "; + if (i % 6 == 5) + std::cout << "\n "; // don't create hugely long lines std::cout << "expressions[" << expressions[children[i]] << "]"; } - if (numChildren == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS + if (numChildren == 0) + // ensure the array is not empty, otherwise a compiler error on VS + std::cout << "0"; std::cout << " };\n "; - traceExpression(ret, "BinaryenBlock", StringLit(name), "children", numChildren, type); + traceExpression( + ret, "BinaryenBlock", StringLit(name), "children", numChildren, type); std::cout << " }\n"; } return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenIf(BinaryenModuleRef module, BinaryenExpressionRef condition, BinaryenExpressionRef ifTrue, BinaryenExpressionRef ifFalse) { +BinaryenExpressionRef BinaryenIf(BinaryenModuleRef module, + BinaryenExpressionRef condition, + BinaryenExpressionRef ifTrue, + BinaryenExpressionRef ifFalse) { auto* ret = ((Module*)module)->allocator.alloc<If>(); ret->condition = (Expression*)condition; ret->ifTrue = (Expression*)ifTrue; @@ -690,8 +858,11 @@ BinaryenExpressionRef BinaryenIf(BinaryenModuleRef module, BinaryenExpressionRef return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenLoop(BinaryenModuleRef module, const char* name, BinaryenExpressionRef body) { - auto* ret = Builder(*((Module*)module)).makeLoop(name ? Name(name) : Name(), (Expression*)body); +BinaryenExpressionRef BinaryenLoop(BinaryenModuleRef module, + const char* name, + BinaryenExpressionRef body) { + auto* ret = Builder(*((Module*)module)) + .makeLoop(name ? Name(name) : Name(), (Expression*)body); if (tracing) { traceExpression(ret, "BinaryenLoop", StringLit(name), body); @@ -699,8 +870,12 @@ BinaryenExpressionRef BinaryenLoop(BinaryenModuleRef module, const char* name, B return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenBreak(BinaryenModuleRef module, const char* name, BinaryenExpressionRef condition, BinaryenExpressionRef value) { - auto* ret = Builder(*((Module*)module)).makeBreak(name, (Expression*)value, (Expression*)condition); +BinaryenExpressionRef BinaryenBreak(BinaryenModuleRef module, + const char* name, + BinaryenExpressionRef condition, + BinaryenExpressionRef value) { + auto* ret = Builder(*((Module*)module)) + .makeBreak(name, (Expression*)value, (Expression*)condition); if (tracing) { traceExpression(ret, "BinaryenBreak", StringLit(name), condition, value); @@ -708,19 +883,33 @@ BinaryenExpressionRef BinaryenBreak(BinaryenModuleRef module, const char* name, return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenSwitch(BinaryenModuleRef module, const char** names, BinaryenIndex numNames, const char* defaultName, BinaryenExpressionRef condition, BinaryenExpressionRef value) { +BinaryenExpressionRef BinaryenSwitch(BinaryenModuleRef module, + const char** names, + BinaryenIndex numNames, + const char* defaultName, + BinaryenExpressionRef condition, + BinaryenExpressionRef value) { auto* ret = ((Module*)module)->allocator.alloc<Switch>(); if (tracing) { std::cout << " {\n"; std::cout << " const char* names[] = { "; for (BinaryenIndex i = 0; i < numNames; i++) { - if (i > 0) std::cout << ", "; + if (i > 0) + std::cout << ", "; std::cout << "\"" << names[i] << "\""; } - if (numNames == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS + if (numNames == 0) + std::cout << "0"; // ensure the array is not empty, otherwise a compiler + // error on VS std::cout << " };\n "; - traceExpression(ret, "BinaryenSwitch", "names", numNames, StringLit(defaultName), condition, value); + traceExpression(ret, + "BinaryenSwitch", + "names", + numNames, + StringLit(defaultName), + condition, + value); std::cout << " }\n"; } @@ -733,19 +922,31 @@ BinaryenExpressionRef BinaryenSwitch(BinaryenModuleRef module, const char** name ret->finalize(); return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenCall(BinaryenModuleRef module, const char* target, BinaryenExpressionRef* operands, BinaryenIndex numOperands, BinaryenType returnType) { +BinaryenExpressionRef BinaryenCall(BinaryenModuleRef module, + const char* target, + BinaryenExpressionRef* operands, + BinaryenIndex numOperands, + BinaryenType returnType) { auto* ret = ((Module*)module)->allocator.alloc<Call>(); if (tracing) { std::cout << " {\n"; std::cout << " BinaryenExpressionRef operands[] = { "; for (BinaryenIndex i = 0; i < numOperands; i++) { - if (i > 0) std::cout << ", "; + if (i > 0) + std::cout << ", "; std::cout << "expressions[" << expressions[operands[i]] << "]"; } - if (numOperands == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS + if (numOperands == 0) + // ensure the array is not empty, otherwise a compiler error on VS + std::cout << "0"; std::cout << " };\n "; - traceExpression(ret, "BinaryenCall", StringLit(target), "operands", numOperands, returnType); + traceExpression(ret, + "BinaryenCall", + StringLit(target), + "operands", + numOperands, + returnType); std::cout << " }\n"; } @@ -757,7 +958,11 @@ BinaryenExpressionRef BinaryenCall(BinaryenModuleRef module, const char* target, ret->finalize(); return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenCallIndirect(BinaryenModuleRef module, BinaryenExpressionRef target, BinaryenExpressionRef* operands, BinaryenIndex numOperands, const char* type) { +BinaryenExpressionRef BinaryenCallIndirect(BinaryenModuleRef module, + BinaryenExpressionRef target, + BinaryenExpressionRef* operands, + BinaryenIndex numOperands, + const char* type) { auto* wasm = (Module*)module; auto* ret = wasm->allocator.alloc<CallIndirect>(); @@ -765,12 +970,20 @@ BinaryenExpressionRef BinaryenCallIndirect(BinaryenModuleRef module, BinaryenExp std::cout << " {\n"; std::cout << " BinaryenExpressionRef operands[] = { "; for (BinaryenIndex i = 0; i < numOperands; i++) { - if (i > 0) std::cout << ", "; + if (i > 0) + std::cout << ", "; std::cout << "expressions[" << expressions[operands[i]] << "]"; } - if (numOperands == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS + if (numOperands == 0) + // ensure the array is not empty, otherwise a compiler error on VS + std::cout << "0"; std::cout << " };\n "; - traceExpression(ret, "BinaryenCallIndirect", target, "operands", numOperands, StringLit(type)); + traceExpression(ret, + "BinaryenCallIndirect", + target, + "operands", + numOperands, + StringLit(type)); std::cout << " }\n"; } @@ -783,7 +996,9 @@ BinaryenExpressionRef BinaryenCallIndirect(BinaryenModuleRef module, BinaryenExp ret->finalize(); return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenGetLocal(BinaryenModuleRef module, BinaryenIndex index, BinaryenType type) { +BinaryenExpressionRef BinaryenGetLocal(BinaryenModuleRef module, + BinaryenIndex index, + BinaryenType type) { auto* ret = ((Module*)module)->allocator.alloc<GetLocal>(); if (tracing) { @@ -795,7 +1010,9 @@ BinaryenExpressionRef BinaryenGetLocal(BinaryenModuleRef module, BinaryenIndex i ret->finalize(); return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenSetLocal(BinaryenModuleRef module, BinaryenIndex index, BinaryenExpressionRef value) { +BinaryenExpressionRef BinaryenSetLocal(BinaryenModuleRef module, + BinaryenIndex index, + BinaryenExpressionRef value) { auto* ret = ((Module*)module)->allocator.alloc<SetLocal>(); if (tracing) { @@ -808,7 +1025,9 @@ BinaryenExpressionRef BinaryenSetLocal(BinaryenModuleRef module, BinaryenIndex i ret->finalize(); return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenTeeLocal(BinaryenModuleRef module, BinaryenIndex index, BinaryenExpressionRef value) { +BinaryenExpressionRef BinaryenTeeLocal(BinaryenModuleRef module, + BinaryenIndex index, + BinaryenExpressionRef value) { auto* ret = ((Module*)module)->allocator.alloc<SetLocal>(); if (tracing) { @@ -821,7 +1040,9 @@ BinaryenExpressionRef BinaryenTeeLocal(BinaryenModuleRef module, BinaryenIndex i ret->finalize(); return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenGetGlobal(BinaryenModuleRef module, const char* name, BinaryenType type) { +BinaryenExpressionRef BinaryenGetGlobal(BinaryenModuleRef module, + const char* name, + BinaryenType type) { auto* ret = ((Module*)module)->allocator.alloc<GetGlobal>(); if (tracing) { @@ -833,7 +1054,9 @@ BinaryenExpressionRef BinaryenGetGlobal(BinaryenModuleRef module, const char* na ret->finalize(); return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenSetGlobal(BinaryenModuleRef module, const char* name, BinaryenExpressionRef value) { +BinaryenExpressionRef BinaryenSetGlobal(BinaryenModuleRef module, + const char* name, + BinaryenExpressionRef value) { auto* ret = ((Module*)module)->allocator.alloc<SetGlobal>(); if (tracing) { @@ -845,11 +1068,18 @@ BinaryenExpressionRef BinaryenSetGlobal(BinaryenModuleRef module, const char* na ret->finalize(); return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenLoad(BinaryenModuleRef module, uint32_t bytes, int8_t signed_, uint32_t offset, uint32_t align, BinaryenType type, BinaryenExpressionRef ptr) { +BinaryenExpressionRef BinaryenLoad(BinaryenModuleRef module, + uint32_t bytes, + int8_t signed_, + uint32_t offset, + uint32_t align, + BinaryenType type, + BinaryenExpressionRef ptr) { auto* ret = ((Module*)module)->allocator.alloc<Load>(); if (tracing) { - traceExpression(ret, "BinaryenLoad", bytes, int(signed_), offset, align, type, ptr); + traceExpression( + ret, "BinaryenLoad", bytes, int(signed_), offset, align, type, ptr); } ret->isAtomic = false; ret->bytes = bytes; @@ -861,11 +1091,18 @@ BinaryenExpressionRef BinaryenLoad(BinaryenModuleRef module, uint32_t bytes, int ret->finalize(); return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenStore(BinaryenModuleRef module, uint32_t bytes, uint32_t offset, uint32_t align, BinaryenExpressionRef ptr, BinaryenExpressionRef value, BinaryenType type) { +BinaryenExpressionRef BinaryenStore(BinaryenModuleRef module, + uint32_t bytes, + uint32_t offset, + uint32_t align, + BinaryenExpressionRef ptr, + BinaryenExpressionRef value, + BinaryenType type) { auto* ret = ((Module*)module)->allocator.alloc<Store>(); if (tracing) { - traceExpression(ret, "BinaryenStore", bytes, offset, align, ptr, value, type); + traceExpression( + ret, "BinaryenStore", bytes, offset, align, ptr, value, type); } ret->isAtomic = false; ret->bytes = bytes; @@ -877,15 +1114,19 @@ BinaryenExpressionRef BinaryenStore(BinaryenModuleRef module, uint32_t bytes, ui ret->finalize(); return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenConst(BinaryenModuleRef module, BinaryenLiteral value) { +BinaryenExpressionRef BinaryenConst(BinaryenModuleRef module, + BinaryenLiteral value) { auto* ret = Builder(*((Module*)module)).makeConst(fromBinaryenLiteral(value)); if (tracing) { traceExpression(ret, "BinaryenConst", value); } return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenUnary(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef value) { - auto* ret = Builder(*((Module*)module)).makeUnary(UnaryOp(op), (Expression*)value); +BinaryenExpressionRef BinaryenUnary(BinaryenModuleRef module, + BinaryenOp op, + BinaryenExpressionRef value) { + auto* ret = + Builder(*((Module*)module)).makeUnary(UnaryOp(op), (Expression*)value); if (tracing) { traceExpression(ret, "BinaryenUnary", op, value); @@ -893,8 +1134,13 @@ BinaryenExpressionRef BinaryenUnary(BinaryenModuleRef module, BinaryenOp op, Bin return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenBinary(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef left, BinaryenExpressionRef right) { - auto* ret = Builder(*((Module*)module)).makeBinary(BinaryOp(op), (Expression*)left, (Expression*)right); +BinaryenExpressionRef BinaryenBinary(BinaryenModuleRef module, + BinaryenOp op, + BinaryenExpressionRef left, + BinaryenExpressionRef right) { + auto* ret = + Builder(*((Module*)module)) + .makeBinary(BinaryOp(op), (Expression*)left, (Expression*)right); if (tracing) { traceExpression(ret, "BinaryenBinary", op, left, right); @@ -902,7 +1148,10 @@ BinaryenExpressionRef BinaryenBinary(BinaryenModuleRef module, BinaryenOp op, Bi return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenSelect(BinaryenModuleRef module, BinaryenExpressionRef condition, BinaryenExpressionRef ifTrue, BinaryenExpressionRef ifFalse) { +BinaryenExpressionRef BinaryenSelect(BinaryenModuleRef module, + BinaryenExpressionRef condition, + BinaryenExpressionRef ifTrue, + BinaryenExpressionRef ifFalse) { auto* ret = ((Module*)module)->allocator.alloc<Select>(); if (tracing) { @@ -915,7 +1164,8 @@ BinaryenExpressionRef BinaryenSelect(BinaryenModuleRef module, BinaryenExpressio ret->finalize(); return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenDrop(BinaryenModuleRef module, BinaryenExpressionRef value) { +BinaryenExpressionRef BinaryenDrop(BinaryenModuleRef module, + BinaryenExpressionRef value) { auto* ret = ((Module*)module)->allocator.alloc<Drop>(); if (tracing) { @@ -926,7 +1176,8 @@ BinaryenExpressionRef BinaryenDrop(BinaryenModuleRef module, BinaryenExpressionR ret->finalize(); return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenReturn(BinaryenModuleRef module, BinaryenExpressionRef value) { +BinaryenExpressionRef BinaryenReturn(BinaryenModuleRef module, + BinaryenExpressionRef value) { auto* ret = Builder(*((Module*)module)).makeReturn((Expression*)value); if (tracing) { @@ -935,24 +1186,33 @@ BinaryenExpressionRef BinaryenReturn(BinaryenModuleRef module, BinaryenExpressio return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenHost(BinaryenModuleRef module, BinaryenOp op, const char* name, BinaryenExpressionRef* operands, BinaryenIndex numOperands) { +BinaryenExpressionRef BinaryenHost(BinaryenModuleRef module, + BinaryenOp op, + const char* name, + BinaryenExpressionRef* operands, + BinaryenIndex numOperands) { auto* ret = ((Module*)module)->allocator.alloc<Host>(); if (tracing) { std::cout << " {\n"; std::cout << " BinaryenExpressionRef operands[] = { "; for (BinaryenIndex i = 0; i < numOperands; i++) { - if (i > 0) std::cout << ", "; + if (i > 0) + std::cout << ", "; std::cout << "expressions[" << expressions[operands[i]] << "]"; } - if (numOperands == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS + if (numOperands == 0) + // ensure the array is not empty, otherwise a compiler error on VS + std::cout << "0"; std::cout << " };\n "; - traceExpression(ret, "BinaryenHost", StringLit(name), "operands", numOperands); + traceExpression( + ret, "BinaryenHost", StringLit(name), "operands", numOperands); std::cout << " }\n"; } ret->op = HostOp(op); - if (name) ret->nameOperand = name; + if (name) + ret->nameOperand = name; for (BinaryenIndex i = 0; i < numOperands; i++) { ret->operands.push_back((Expression*)operands[i]); } @@ -977,8 +1237,13 @@ BinaryenExpressionRef BinaryenUnreachable(BinaryenModuleRef module) { return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenAtomicLoad(BinaryenModuleRef module, uint32_t bytes, uint32_t offset, BinaryenType type, BinaryenExpressionRef ptr) { - auto* ret = Builder(*((Module*)module)).makeAtomicLoad(bytes, offset, (Expression*)ptr, Type(type)); +BinaryenExpressionRef BinaryenAtomicLoad(BinaryenModuleRef module, + uint32_t bytes, + uint32_t offset, + BinaryenType type, + BinaryenExpressionRef ptr) { + auto* ret = Builder(*((Module*)module)) + .makeAtomicLoad(bytes, offset, (Expression*)ptr, Type(type)); if (tracing) { traceExpression(ret, "BinaryenAtomicLoad", bytes, offset, type, ptr); @@ -986,44 +1251,99 @@ BinaryenExpressionRef BinaryenAtomicLoad(BinaryenModuleRef module, uint32_t byte return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenAtomicStore(BinaryenModuleRef module, uint32_t bytes, uint32_t offset, BinaryenExpressionRef ptr, BinaryenExpressionRef value, BinaryenType type) { - auto* ret = Builder(*((Module*)module)).makeAtomicStore(bytes, offset, (Expression*)ptr, (Expression*)value, Type(type)); +BinaryenExpressionRef BinaryenAtomicStore(BinaryenModuleRef module, + uint32_t bytes, + uint32_t offset, + BinaryenExpressionRef ptr, + BinaryenExpressionRef value, + BinaryenType type) { + auto* ret = + Builder(*((Module*)module)) + .makeAtomicStore( + bytes, offset, (Expression*)ptr, (Expression*)value, Type(type)); if (tracing) { - traceExpression(ret, "BinaryenAtomicStore", bytes, offset, ptr, value, type); + traceExpression( + ret, "BinaryenAtomicStore", bytes, offset, ptr, value, type); } return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenAtomicRMW(BinaryenModuleRef module, BinaryenOp op, BinaryenIndex bytes, BinaryenIndex offset, BinaryenExpressionRef ptr, BinaryenExpressionRef value, BinaryenType type) { - auto* ret = Builder(*((Module*)module)).makeAtomicRMW(AtomicRMWOp(op), bytes, offset, (Expression*)ptr, (Expression*)value, Type(type)); +BinaryenExpressionRef BinaryenAtomicRMW(BinaryenModuleRef module, + BinaryenOp op, + BinaryenIndex bytes, + BinaryenIndex offset, + BinaryenExpressionRef ptr, + BinaryenExpressionRef value, + BinaryenType type) { + auto* ret = Builder(*((Module*)module)) + .makeAtomicRMW(AtomicRMWOp(op), + bytes, + offset, + (Expression*)ptr, + (Expression*)value, + Type(type)); if (tracing) { - traceExpression(ret, "BinaryenAtomicRMW", op, bytes, offset, ptr, value, type); + traceExpression( + ret, "BinaryenAtomicRMW", op, bytes, offset, ptr, value, type); } return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenAtomicCmpxchg(BinaryenModuleRef module, BinaryenIndex bytes, BinaryenIndex offset, BinaryenExpressionRef ptr, BinaryenExpressionRef expected, BinaryenExpressionRef replacement, BinaryenType type) { - auto* ret = Builder(*((Module*)module)).makeAtomicCmpxchg(bytes, offset, (Expression*)ptr, (Expression*)expected, (Expression*)replacement, Type(type)); - - if (tracing) { - traceExpression(ret, "BinaryenAtomicCmpxchg", bytes, offset, ptr, expected, replacement, type); +BinaryenExpressionRef BinaryenAtomicCmpxchg(BinaryenModuleRef module, + BinaryenIndex bytes, + BinaryenIndex offset, + BinaryenExpressionRef ptr, + BinaryenExpressionRef expected, + BinaryenExpressionRef replacement, + BinaryenType type) { + auto* ret = Builder(*((Module*)module)) + .makeAtomicCmpxchg(bytes, + offset, + (Expression*)ptr, + (Expression*)expected, + (Expression*)replacement, + Type(type)); + + if (tracing) { + traceExpression(ret, + "BinaryenAtomicCmpxchg", + bytes, + offset, + ptr, + expected, + replacement, + type); } return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenAtomicWait(BinaryenModuleRef module, BinaryenExpressionRef ptr, BinaryenExpressionRef expected, BinaryenExpressionRef timeout, BinaryenType expectedType) { - auto* ret = Builder(*((Module*)module)).makeAtomicWait((Expression*)ptr, (Expression*)expected, (Expression*)timeout, Type(expectedType), 0); +BinaryenExpressionRef BinaryenAtomicWait(BinaryenModuleRef module, + BinaryenExpressionRef ptr, + BinaryenExpressionRef expected, + BinaryenExpressionRef timeout, + BinaryenType expectedType) { + auto* ret = Builder(*((Module*)module)) + .makeAtomicWait((Expression*)ptr, + (Expression*)expected, + (Expression*)timeout, + Type(expectedType), + 0); if (tracing) { - traceExpression(ret, "BinaryenAtomicWait", ptr, expected, timeout, expectedType); + traceExpression( + ret, "BinaryenAtomicWait", ptr, expected, timeout, expectedType); } return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenAtomicNotify(BinaryenModuleRef module, BinaryenExpressionRef ptr, BinaryenExpressionRef notifyCount) { - auto* ret = Builder(*((Module*)module)).makeAtomicNotify((Expression*)ptr, (Expression*)notifyCount, 0); +BinaryenExpressionRef BinaryenAtomicNotify(BinaryenModuleRef module, + BinaryenExpressionRef ptr, + BinaryenExpressionRef notifyCount) { + auto* ret = + Builder(*((Module*)module)) + .makeAtomicNotify((Expression*)ptr, (Expression*)notifyCount, 0); if (tracing) { traceExpression(ret, "BinaryenAtomicNotify", ptr, notifyCount); @@ -1031,24 +1351,39 @@ BinaryenExpressionRef BinaryenAtomicNotify(BinaryenModuleRef module, BinaryenExp return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenSIMDExtract(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef vec, uint8_t index) { - auto* ret = Builder(*((Module*)module)).makeSIMDExtract(SIMDExtractOp(op), (Expression*) vec, index); +BinaryenExpressionRef BinaryenSIMDExtract(BinaryenModuleRef module, + BinaryenOp op, + BinaryenExpressionRef vec, + uint8_t index) { + auto* ret = Builder(*((Module*)module)) + .makeSIMDExtract(SIMDExtractOp(op), (Expression*)vec, index); if (tracing) { traceExpression(ret, "BinaryenSIMDExtract", op, vec, int(index)); } return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenSIMDReplace(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef vec, uint8_t index, BinaryenExpressionRef value) { - auto* ret = Builder(*((Module*)module)).makeSIMDReplace(SIMDReplaceOp(op), (Expression*) vec, index, (Expression*)value); +BinaryenExpressionRef BinaryenSIMDReplace(BinaryenModuleRef module, + BinaryenOp op, + BinaryenExpressionRef vec, + uint8_t index, + BinaryenExpressionRef value) { + auto* ret = + Builder(*((Module*)module)) + .makeSIMDReplace( + SIMDReplaceOp(op), (Expression*)vec, index, (Expression*)value); if (tracing) { traceExpression(ret, "BinaryenSIMDReplace", op, vec, int(index), value); } return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenSIMDShuffle(BinaryenModuleRef module, BinaryenExpressionRef left, BinaryenExpressionRef right, const uint8_t mask_[16]) { +BinaryenExpressionRef BinaryenSIMDShuffle(BinaryenModuleRef module, + BinaryenExpressionRef left, + BinaryenExpressionRef right, + const uint8_t mask_[16]) { std::array<uint8_t, 16> mask; memcpy(mask.data(), mask_, 16); - auto* ret = Builder(*((Module*)module)).makeSIMDShuffle((Expression*)left, (Expression*)right, mask); + auto* ret = Builder(*((Module*)module)) + .makeSIMDShuffle((Expression*)left, (Expression*)right, mask); if (tracing) { std::cout << " {\n"; std::cout << " uint8_t mask[] = {"; @@ -1064,29 +1399,47 @@ BinaryenExpressionRef BinaryenSIMDShuffle(BinaryenModuleRef module, BinaryenExpr } return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenSIMDBitselect(BinaryenModuleRef module, BinaryenExpressionRef left, BinaryenExpressionRef right, BinaryenExpressionRef cond) { - auto* ret = Builder(*((Module*)module)).makeSIMDBitselect((Expression*)left, (Expression*)right, (Expression*)cond); +BinaryenExpressionRef BinaryenSIMDBitselect(BinaryenModuleRef module, + BinaryenExpressionRef left, + BinaryenExpressionRef right, + BinaryenExpressionRef cond) { + auto* ret = Builder(*((Module*)module)) + .makeSIMDBitselect( + (Expression*)left, (Expression*)right, (Expression*)cond); if (tracing) { traceExpression(ret, "BinaryenSIMDBitselect", left, right, cond); } return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenSIMDShift(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef vec, BinaryenExpressionRef shift) { - auto* ret = Builder(*((Module*)module)).makeSIMDShift(SIMDShiftOp(op), (Expression*)vec, (Expression*)shift); +BinaryenExpressionRef BinaryenSIMDShift(BinaryenModuleRef module, + BinaryenOp op, + BinaryenExpressionRef vec, + BinaryenExpressionRef shift) { + auto* ret = + Builder(*((Module*)module)) + .makeSIMDShift(SIMDShiftOp(op), (Expression*)vec, (Expression*)shift); if (tracing) { traceExpression(ret, "BinaryenSIMDShift", op, vec, shift); } return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenMemoryInit(BinaryenModuleRef module, uint32_t segment, BinaryenExpressionRef dest, BinaryenExpressionRef offset, BinaryenExpressionRef size) { - auto* ret = Builder(*((Module*)module)).makeMemoryInit(segment, (Expression*)dest, (Expression*)offset, (Expression*)size); +BinaryenExpressionRef BinaryenMemoryInit(BinaryenModuleRef module, + uint32_t segment, + BinaryenExpressionRef dest, + BinaryenExpressionRef offset, + BinaryenExpressionRef size) { + auto* ret = + Builder(*((Module*)module)) + .makeMemoryInit( + segment, (Expression*)dest, (Expression*)offset, (Expression*)size); if (tracing) { traceExpression(ret, "BinaryenMemoryInit", segment, dest, offset, size); } return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenDataDrop(BinaryenModuleRef module, uint32_t segment) { +BinaryenExpressionRef BinaryenDataDrop(BinaryenModuleRef module, + uint32_t segment) { auto* ret = Builder(*((Module*)module)).makeDataDrop(segment); if (tracing) { traceExpression(ret, "BinaryenDataDrop", segment); @@ -1094,16 +1447,26 @@ BinaryenExpressionRef BinaryenDataDrop(BinaryenModuleRef module, uint32_t segmen return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenMemoryCopy(BinaryenModuleRef module, BinaryenExpressionRef dest, BinaryenExpressionRef source, BinaryenExpressionRef size) { - auto* ret = Builder(*((Module*)module)).makeMemoryCopy((Expression*)dest, (Expression*)source, (Expression*)size); +BinaryenExpressionRef BinaryenMemoryCopy(BinaryenModuleRef module, + BinaryenExpressionRef dest, + BinaryenExpressionRef source, + BinaryenExpressionRef size) { + auto* ret = Builder(*((Module*)module)) + .makeMemoryCopy( + (Expression*)dest, (Expression*)source, (Expression*)size); if (tracing) { traceExpression(ret, "BinaryenMemoryCopy", dest, source, size); } return static_cast<Expression*>(ret); } -BinaryenExpressionRef BinaryenMemoryFill(BinaryenModuleRef module, BinaryenExpressionRef dest, BinaryenExpressionRef value, BinaryenExpressionRef size) { - auto* ret = Builder(*((Module*)module)).makeMemoryFill((Expression*)dest, (Expression*)value, (Expression*)size); +BinaryenExpressionRef BinaryenMemoryFill(BinaryenModuleRef module, + BinaryenExpressionRef dest, + BinaryenExpressionRef value, + BinaryenExpressionRef size) { + auto* ret = + Builder(*((Module*)module)) + .makeMemoryFill((Expression*)dest, (Expression*)value, (Expression*)size); if (tracing) { traceExpression(ret, "BinaryenMemoryFill", dest, value, size); } @@ -1114,21 +1477,24 @@ BinaryenExpressionRef BinaryenMemoryFill(BinaryenModuleRef module, BinaryenExpre BinaryenExpressionId BinaryenExpressionGetId(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenExpressionGetId(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenExpressionGetId(expressions[" << expressions[expr] + << "]);\n"; } return ((Expression*)expr)->_id; } BinaryenType BinaryenExpressionGetType(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenExpressionGetType(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenExpressionGetType(expressions[" << expressions[expr] + << "]);\n"; } return ((Expression*)expr)->type; } void BinaryenExpressionPrint(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenExpressionPrint(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenExpressionPrint(expressions[" << expressions[expr] + << "]);\n"; } WasmPrinter::printExpression((Expression*)expr, std::cout); @@ -1140,7 +1506,8 @@ void BinaryenExpressionPrint(BinaryenExpressionRef expr) { // Block const char* BinaryenBlockGetName(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenBlockGetName(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenBlockGetName(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1149,16 +1516,19 @@ const char* BinaryenBlockGetName(BinaryenExpressionRef expr) { } BinaryenIndex BinaryenBlockGetNumChildren(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenBlockGetNumChildren(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenBlockGetNumChildren(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is<Block>()); return static_cast<Block*>(expression)->list.size(); } -BinaryenExpressionRef BinaryenBlockGetChild(BinaryenExpressionRef expr, BinaryenIndex index) { +BinaryenExpressionRef BinaryenBlockGetChild(BinaryenExpressionRef expr, + BinaryenIndex index) { if (tracing) { - std::cout << " BinaryenBlockGetChild(expressions[" << expressions[expr] << "], " << index << ");\n"; + std::cout << " BinaryenBlockGetChild(expressions[" << expressions[expr] + << "], " << index << ");\n"; } auto* expression = (Expression*)expr; @@ -1169,7 +1539,8 @@ BinaryenExpressionRef BinaryenBlockGetChild(BinaryenExpressionRef expr, Binaryen // If BinaryenExpressionRef BinaryenIfGetCondition(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenIfGetCondition(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenIfGetCondition(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1178,7 +1549,8 @@ BinaryenExpressionRef BinaryenIfGetCondition(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenIfGetIfTrue(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenIfGetIfTrue(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenIfGetIfTrue(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1187,7 +1559,8 @@ BinaryenExpressionRef BinaryenIfGetIfTrue(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenIfGetIfFalse(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenIfGetIfFalse(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenIfGetIfFalse(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1197,7 +1570,8 @@ BinaryenExpressionRef BinaryenIfGetIfFalse(BinaryenExpressionRef expr) { // Loop const char* BinaryenLoopGetName(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenLoopGetName(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenLoopGetName(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1206,7 +1580,8 @@ const char* BinaryenLoopGetName(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenLoopGetBody(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenLoopGetBody(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenLoopGetBody(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1216,7 +1591,8 @@ BinaryenExpressionRef BinaryenLoopGetBody(BinaryenExpressionRef expr) { // Break const char* BinaryenBreakGetName(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenBreakGetName(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenBreakGetName(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1225,7 +1601,8 @@ const char* BinaryenBreakGetName(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenBreakGetCondition(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenBreakGetCondition(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenBreakGetCondition(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1234,7 +1611,8 @@ BinaryenExpressionRef BinaryenBreakGetCondition(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenBreakGetValue(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenBreakGetValue(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenBreakGetValue(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1244,16 +1622,19 @@ BinaryenExpressionRef BinaryenBreakGetValue(BinaryenExpressionRef expr) { // Switch BinaryenIndex BinaryenSwitchGetNumNames(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSwitchGetNumNames(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSwitchGetNumNames(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is<Switch>()); return static_cast<Switch*>(expression)->targets.size(); } -const char* BinaryenSwitchGetName(BinaryenExpressionRef expr, BinaryenIndex index) { +const char* BinaryenSwitchGetName(BinaryenExpressionRef expr, + BinaryenIndex index) { if (tracing) { - std::cout << " BinaryenSwitchGetName(expressions[" << expressions[expr] << "], " << index << ");\n"; + std::cout << " BinaryenSwitchGetName(expressions[" << expressions[expr] + << "], " << index << ");\n"; } auto* expression = (Expression*)expr; @@ -1263,7 +1644,8 @@ const char* BinaryenSwitchGetName(BinaryenExpressionRef expr, BinaryenIndex inde } const char* BinaryenSwitchGetDefaultName(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSwitchGetDefaultName(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSwitchGetDefaultName(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1272,7 +1654,8 @@ const char* BinaryenSwitchGetDefaultName(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenSwitchGetCondition(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSwitchGetCondition(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSwitchGetCondition(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1281,7 +1664,8 @@ BinaryenExpressionRef BinaryenSwitchGetCondition(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenSwitchGetValue(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSwitchGetValue(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSwitchGetValue(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1291,7 +1675,8 @@ BinaryenExpressionRef BinaryenSwitchGetValue(BinaryenExpressionRef expr) { // Call const char* BinaryenCallGetTarget(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenCallGetTarget(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenCallGetTarget(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1300,16 +1685,19 @@ const char* BinaryenCallGetTarget(BinaryenExpressionRef expr) { } BinaryenIndex BinaryenCallGetNumOperands(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenCallGetNumOperands(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenCallGetNumOperands(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is<Call>()); return static_cast<Call*>(expression)->operands.size(); } -BinaryenExpressionRef BinaryenCallGetOperand(BinaryenExpressionRef expr, BinaryenIndex index) { +BinaryenExpressionRef BinaryenCallGetOperand(BinaryenExpressionRef expr, + BinaryenIndex index) { if (tracing) { - std::cout << " BinaryenCallGetOperand(expressions[" << expressions[expr] << "], " << index << ");\n"; + std::cout << " BinaryenCallGetOperand(expressions[" << expressions[expr] + << "], " << index << ");\n"; } auto* expression = (Expression*)expr; @@ -1318,9 +1706,11 @@ BinaryenExpressionRef BinaryenCallGetOperand(BinaryenExpressionRef expr, Binarye return static_cast<Call*>(expression)->operands[index]; } // CallIndirect -BinaryenExpressionRef BinaryenCallIndirectGetTarget(BinaryenExpressionRef expr) { +BinaryenExpressionRef +BinaryenCallIndirectGetTarget(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenCallIndirectGetTarget(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenCallIndirectGetTarget(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1329,16 +1719,19 @@ BinaryenExpressionRef BinaryenCallIndirectGetTarget(BinaryenExpressionRef expr) } BinaryenIndex BinaryenCallIndirectGetNumOperands(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenCallIndirectGetNumOperands(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenCallIndirectGetNumOperands(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is<CallIndirect>()); return static_cast<CallIndirect*>(expression)->operands.size(); } -BinaryenExpressionRef BinaryenCallIndirectGetOperand(BinaryenExpressionRef expr, BinaryenIndex index) { +BinaryenExpressionRef BinaryenCallIndirectGetOperand(BinaryenExpressionRef expr, + BinaryenIndex index) { if (tracing) { - std::cout << " BinaryenCallIndirectGetOperand(expressions[" << expressions[expr] << "], " << index << ");\n"; + std::cout << " BinaryenCallIndirectGetOperand(expressions[" + << expressions[expr] << "], " << index << ");\n"; } auto* expression = (Expression*)expr; @@ -1349,7 +1742,8 @@ BinaryenExpressionRef BinaryenCallIndirectGetOperand(BinaryenExpressionRef expr, // GetLocal BinaryenIndex BinaryenGetLocalGetIndex(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenGetLocalGetIndex(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenGetLocalGetIndex(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1359,7 +1753,8 @@ BinaryenIndex BinaryenGetLocalGetIndex(BinaryenExpressionRef expr) { // SetLocal int BinaryenSetLocalIsTee(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSetLocalIsTee(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSetLocalIsTee(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1368,7 +1763,8 @@ int BinaryenSetLocalIsTee(BinaryenExpressionRef expr) { } BinaryenIndex BinaryenSetLocalGetIndex(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSetLocalGetIndex(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSetLocalGetIndex(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1377,7 +1773,8 @@ BinaryenIndex BinaryenSetLocalGetIndex(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenSetLocalGetValue(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSetLocalGetValue(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSetLocalGetValue(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1387,7 +1784,8 @@ BinaryenExpressionRef BinaryenSetLocalGetValue(BinaryenExpressionRef expr) { // GetGlobal const char* BinaryenGetGlobalGetName(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenGetGlobalGetName(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenGetGlobalGetName(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1397,7 +1795,8 @@ const char* BinaryenGetGlobalGetName(BinaryenExpressionRef expr) { // SetGlobal const char* BinaryenSetGlobalGetName(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSetGlobalGetName(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSetGlobalGetName(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1406,7 +1805,8 @@ const char* BinaryenSetGlobalGetName(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenSetGlobalGetValue(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSetGlobalGetValue(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSetGlobalGetValue(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1416,7 +1816,8 @@ BinaryenExpressionRef BinaryenSetGlobalGetValue(BinaryenExpressionRef expr) { // Host BinaryenOp BinaryenHostGetOp(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenHostGetOp(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenHostGetOp(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1425,7 +1826,8 @@ BinaryenOp BinaryenHostGetOp(BinaryenExpressionRef expr) { } const char* BinaryenHostGetNameOperand(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenHostGetNameOperand(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenHostGetNameOperand(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1434,16 +1836,19 @@ const char* BinaryenHostGetNameOperand(BinaryenExpressionRef expr) { } BinaryenIndex BinaryenHostGetNumOperands(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenHostGetNumOperands(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenHostGetNumOperands(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is<Host>()); return static_cast<Host*>(expression)->operands.size(); } -BinaryenExpressionRef BinaryenHostGetOperand(BinaryenExpressionRef expr, BinaryenIndex index) { +BinaryenExpressionRef BinaryenHostGetOperand(BinaryenExpressionRef expr, + BinaryenIndex index) { if (tracing) { - std::cout << " BinaryenHostGetOperand(expressions[" << expressions[expr] << "], " << index << ");\n"; + std::cout << " BinaryenHostGetOperand(expressions[" << expressions[expr] + << "], " << index << ");\n"; } auto* expression = (Expression*)expr; @@ -1454,7 +1859,8 @@ BinaryenExpressionRef BinaryenHostGetOperand(BinaryenExpressionRef expr, Binarye // Load int BinaryenLoadIsAtomic(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenLoadIsAtomic(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenLoadIsAtomic(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1463,7 +1869,8 @@ int BinaryenLoadIsAtomic(BinaryenExpressionRef expr) { } int BinaryenLoadIsSigned(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenLoadIsSigned(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenLoadIsSigned(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1472,7 +1879,8 @@ int BinaryenLoadIsSigned(BinaryenExpressionRef expr) { } uint32_t BinaryenLoadGetBytes(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenLoadGetBytes(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenLoadGetBytes(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1481,7 +1889,8 @@ uint32_t BinaryenLoadGetBytes(BinaryenExpressionRef expr) { } uint32_t BinaryenLoadGetOffset(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenLoadGetOffset(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenLoadGetOffset(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1490,7 +1899,8 @@ uint32_t BinaryenLoadGetOffset(BinaryenExpressionRef expr) { } uint32_t BinaryenLoadGetAlign(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenLoadGetAlign(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenLoadGetAlign(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1499,7 +1909,8 @@ uint32_t BinaryenLoadGetAlign(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenLoadGetPtr(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenLoadGetPtr(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenLoadGetPtr(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1509,7 +1920,8 @@ BinaryenExpressionRef BinaryenLoadGetPtr(BinaryenExpressionRef expr) { // Store int BinaryenStoreIsAtomic(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenStoreIsAtomic(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenStoreIsAtomic(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1518,7 +1930,8 @@ int BinaryenStoreIsAtomic(BinaryenExpressionRef expr) { } uint32_t BinaryenStoreGetBytes(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenStoreGetBytes(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenStoreGetBytes(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1527,7 +1940,8 @@ uint32_t BinaryenStoreGetBytes(BinaryenExpressionRef expr) { } uint32_t BinaryenStoreGetOffset(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenStoreGetOffset(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenStoreGetOffset(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1536,7 +1950,8 @@ uint32_t BinaryenStoreGetOffset(BinaryenExpressionRef expr) { } uint32_t BinaryenStoreGetAlign(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenStoreGetAlign(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenStoreGetAlign(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1545,7 +1960,8 @@ uint32_t BinaryenStoreGetAlign(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenStoreGetPtr(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenStoreGetPtr(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenStoreGetPtr(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1554,7 +1970,8 @@ BinaryenExpressionRef BinaryenStoreGetPtr(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenStoreGetValue(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenStoreGetValue(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenStoreGetValue(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1564,7 +1981,8 @@ BinaryenExpressionRef BinaryenStoreGetValue(BinaryenExpressionRef expr) { // Const int32_t BinaryenConstGetValueI32(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenConstGetValueI32(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenConstGetValueI32(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1573,7 +1991,8 @@ int32_t BinaryenConstGetValueI32(BinaryenExpressionRef expr) { } int64_t BinaryenConstGetValueI64(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenConstGetValueI64(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenConstGetValueI64(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1582,16 +2001,19 @@ int64_t BinaryenConstGetValueI64(BinaryenExpressionRef expr) { } int32_t BinaryenConstGetValueI64Low(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenConstGetValueI64Low(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenConstGetValueI64Low(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is<Const>()); - return (int32_t)(static_cast<Const*>(expression)->value.geti64() & 0xffffffff); + return (int32_t)(static_cast<Const*>(expression)->value.geti64() & + 0xffffffff); } int32_t BinaryenConstGetValueI64High(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenConstGetValueI64High(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenConstGetValueI64High(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1600,7 +2022,8 @@ int32_t BinaryenConstGetValueI64High(BinaryenExpressionRef expr) { } float BinaryenConstGetValueF32(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenConstGetValueF32(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenConstGetValueF32(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1609,7 +2032,8 @@ float BinaryenConstGetValueF32(BinaryenExpressionRef expr) { } double BinaryenConstGetValueF64(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenConstGetValueF64(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenConstGetValueF64(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1618,7 +2042,8 @@ double BinaryenConstGetValueF64(BinaryenExpressionRef expr) { } void BinaryenConstGetValueV128(BinaryenExpressionRef expr, uint8_t* out) { if (tracing) { - std::cout << " BinaryenConstGetValueV128(expressions[" << expressions[expr] << "], " << out << ");\n"; + std::cout << " BinaryenConstGetValueV128(expressions[" << expressions[expr] + << "], " << out << ");\n"; } auto* expression = (Expression*)expr; @@ -1628,7 +2053,8 @@ void BinaryenConstGetValueV128(BinaryenExpressionRef expr, uint8_t* out) { // Unary BinaryenOp BinaryenUnaryGetOp(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenUnaryGetOp(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenUnaryGetOp(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1637,7 +2063,8 @@ BinaryenOp BinaryenUnaryGetOp(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenUnaryGetValue(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenUnaryGetValue(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenUnaryGetValue(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1647,7 +2074,8 @@ BinaryenExpressionRef BinaryenUnaryGetValue(BinaryenExpressionRef expr) { // Binary BinaryenOp BinaryenBinaryGetOp(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenBinaryGetOp(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenBinaryGetOp(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1656,7 +2084,8 @@ BinaryenOp BinaryenBinaryGetOp(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenBinaryGetLeft(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenBinaryGetLeft(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenBinaryGetLeft(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1665,7 +2094,8 @@ BinaryenExpressionRef BinaryenBinaryGetLeft(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenBinaryGetRight(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenBinaryGetRight(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenBinaryGetRight(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1675,7 +2105,8 @@ BinaryenExpressionRef BinaryenBinaryGetRight(BinaryenExpressionRef expr) { // Select BinaryenExpressionRef BinaryenSelectGetIfTrue(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSelectGetIfTrue(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSelectGetIfTrue(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1684,7 +2115,8 @@ BinaryenExpressionRef BinaryenSelectGetIfTrue(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenSelectGetIfFalse(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSelectGetIfFalse(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSelectGetIfFalse(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1693,7 +2125,8 @@ BinaryenExpressionRef BinaryenSelectGetIfFalse(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenSelectGetCondition(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSelectGetCondition(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSelectGetCondition(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1703,7 +2136,8 @@ BinaryenExpressionRef BinaryenSelectGetCondition(BinaryenExpressionRef expr) { // Drop BinaryenExpressionRef BinaryenDropGetValue(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenDropGetValue(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenDropGetValue(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1713,7 +2147,8 @@ BinaryenExpressionRef BinaryenDropGetValue(BinaryenExpressionRef expr) { // Return BinaryenExpressionRef BinaryenReturnGetValue(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenReturnGetValue(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenReturnGetValue(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1723,7 +2158,8 @@ BinaryenExpressionRef BinaryenReturnGetValue(BinaryenExpressionRef expr) { // AtomicRMW BinaryenOp BinaryenAtomicRMWGetOp(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicRMWGetOp(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicRMWGetOp(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1732,7 +2168,8 @@ BinaryenOp BinaryenAtomicRMWGetOp(BinaryenExpressionRef expr) { } uint32_t BinaryenAtomicRMWGetBytes(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicRMWGetBytes(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicRMWGetBytes(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1741,7 +2178,8 @@ uint32_t BinaryenAtomicRMWGetBytes(BinaryenExpressionRef expr) { } uint32_t BinaryenAtomicRMWGetOffset(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicRMWGetOffset(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicRMWGetOffset(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1750,7 +2188,8 @@ uint32_t BinaryenAtomicRMWGetOffset(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenAtomicRMWGetPtr(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicRMWGetPtr(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicRMWGetPtr(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1759,7 +2198,8 @@ BinaryenExpressionRef BinaryenAtomicRMWGetPtr(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenAtomicRMWGetValue(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicRMWGetValue(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicRMWGetValue(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1769,7 +2209,8 @@ BinaryenExpressionRef BinaryenAtomicRMWGetValue(BinaryenExpressionRef expr) { // AtomicCmpxchg uint32_t BinaryenAtomicCmpxchgGetBytes(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicCmpxchgGetBytes(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicCmpxchgGetBytes(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1778,7 +2219,8 @@ uint32_t BinaryenAtomicCmpxchgGetBytes(BinaryenExpressionRef expr) { } uint32_t BinaryenAtomicCmpxchgGetOffset(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicCmpxchgGetOffset(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicCmpxchgGetOffset(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1787,25 +2229,30 @@ uint32_t BinaryenAtomicCmpxchgGetOffset(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenAtomicCmpxchgGetPtr(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicCmpxchgGetPtr(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicCmpxchgGetPtr(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is<AtomicCmpxchg>()); return static_cast<AtomicCmpxchg*>(expression)->ptr; } -BinaryenExpressionRef BinaryenAtomicCmpxchgGetExpected(BinaryenExpressionRef expr) { +BinaryenExpressionRef +BinaryenAtomicCmpxchgGetExpected(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicCmpxchgGetExpected(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicCmpxchgGetExpected(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is<AtomicCmpxchg>()); return static_cast<AtomicCmpxchg*>(expression)->expected; } -BinaryenExpressionRef BinaryenAtomicCmpxchgGetReplacement(BinaryenExpressionRef expr) { +BinaryenExpressionRef +BinaryenAtomicCmpxchgGetReplacement(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicCmpxchgGetReplacement(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicCmpxchgGetReplacement(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1815,16 +2262,19 @@ BinaryenExpressionRef BinaryenAtomicCmpxchgGetReplacement(BinaryenExpressionRef // AtomicWait BinaryenExpressionRef BinaryenAtomicWaitGetPtr(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicWaitGetPtr(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicWaitGetPtr(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is<AtomicWait>()); return static_cast<AtomicWait*>(expression)->ptr; } -BinaryenExpressionRef BinaryenAtomicWaitGetExpected(BinaryenExpressionRef expr) { +BinaryenExpressionRef +BinaryenAtomicWaitGetExpected(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicWaitGetExpected(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicWaitGetExpected(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1833,7 +2283,8 @@ BinaryenExpressionRef BinaryenAtomicWaitGetExpected(BinaryenExpressionRef expr) } BinaryenExpressionRef BinaryenAtomicWaitGetTimeout(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicWaitGetTimeout(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicWaitGetTimeout(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1842,7 +2293,8 @@ BinaryenExpressionRef BinaryenAtomicWaitGetTimeout(BinaryenExpressionRef expr) { } BinaryenType BinaryenAtomicWaitGetExpectedType(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicWaitGetExpectedType(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicWaitGetExpectedType(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1852,16 +2304,19 @@ BinaryenType BinaryenAtomicWaitGetExpectedType(BinaryenExpressionRef expr) { // AtomicNotify BinaryenExpressionRef BinaryenAtomicNotifyGetPtr(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicNotifyGetPtr(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicNotifyGetPtr(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is<AtomicNotify>()); return static_cast<AtomicNotify*>(expression)->ptr; } -BinaryenExpressionRef BinaryenAtomicNotifyGetNotifyCount(BinaryenExpressionRef expr) { +BinaryenExpressionRef +BinaryenAtomicNotifyGetNotifyCount(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenAtomicNotifyGetNotifyCount(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenAtomicNotifyGetNotifyCount(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1871,7 +2326,8 @@ BinaryenExpressionRef BinaryenAtomicNotifyGetNotifyCount(BinaryenExpressionRef e // SIMDExtract BinaryenOp BinaryenSIMDExtractGetOp(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSIMDExtractGetOp(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSIMDExtractGetOp(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1880,7 +2336,8 @@ BinaryenOp BinaryenSIMDExtractGetOp(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenSIMDExtractGetVec(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSIMDExtractGetVec(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSIMDExtractGetVec(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1889,7 +2346,8 @@ BinaryenExpressionRef BinaryenSIMDExtractGetVec(BinaryenExpressionRef expr) { } uint8_t BinaryenSIMDExtractGetIndex(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSIMDExtractGetIndex(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSIMDExtractGetIndex(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1899,7 +2357,8 @@ uint8_t BinaryenSIMDExtractGetIndex(BinaryenExpressionRef expr) { // SIMDReplace BinaryenOp BinaryenSIMDReplaceGetOp(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSIMDReplaceGetOp(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSIMDReplaceGetOp(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1908,7 +2367,8 @@ BinaryenOp BinaryenSIMDReplaceGetOp(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenSIMDReplaceGetVec(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSIMDReplaceGetVec(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSIMDReplaceGetVec(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -1917,7 +2377,8 @@ BinaryenExpressionRef BinaryenSIMDReplaceGetVec(BinaryenExpressionRef expr) { } uint8_t BinaryenSIMDReplaceGetIndex(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSIMDReplaceGetIndex(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSIMDReplaceGetIndex(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1926,7 +2387,8 @@ uint8_t BinaryenSIMDReplaceGetIndex(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenSIMDReplaceGetValue(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSIMDReplaceGetValue(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSIMDReplaceGetValue(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1936,7 +2398,8 @@ BinaryenExpressionRef BinaryenSIMDReplaceGetValue(BinaryenExpressionRef expr) { // SIMDShuffle BinaryenExpressionRef BinaryenSIMDShuffleGetLeft(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSIMDShuffleGetLeft(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSIMDShuffleGetLeft(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1945,16 +2408,18 @@ BinaryenExpressionRef BinaryenSIMDShuffleGetLeft(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenSIMDShuffleGetRight(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSIMDShuffleGetRight(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSIMDShuffleGetRight(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is<SIMDShuffle>()); return static_cast<SIMDShuffle*>(expression)->right; } -void BinaryenSIMDShuffleGetMask(BinaryenExpressionRef expr, uint8_t *mask) { +void BinaryenSIMDShuffleGetMask(BinaryenExpressionRef expr, uint8_t* mask) { if (tracing) { - std::cout << " BinaryenSIMDShuffleGetMask(expressions[" << expressions[expr] << "], " << mask << ");\n"; + std::cout << " BinaryenSIMDShuffleGetMask(expressions[" + << expressions[expr] << "], " << mask << ");\n"; } auto* expression = (Expression*)expr; @@ -1964,16 +2429,19 @@ void BinaryenSIMDShuffleGetMask(BinaryenExpressionRef expr, uint8_t *mask) { // SIMDBitselect BinaryenExpressionRef BinaryenSIMDBitselectGetLeft(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSIMDBitselectGetLeft(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSIMDBitselectGetLeft(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is<SIMDBitselect>()); return static_cast<SIMDBitselect*>(expression)->left; } -BinaryenExpressionRef BinaryenSIMDBitselectGetRight(BinaryenExpressionRef expr) { +BinaryenExpressionRef +BinaryenSIMDBitselectGetRight(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSIMDBitselectGetRight(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSIMDBitselectGetRight(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1982,7 +2450,8 @@ BinaryenExpressionRef BinaryenSIMDBitselectGetRight(BinaryenExpressionRef expr) } BinaryenExpressionRef BinaryenSIMDBitselectGetCond(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSIMDBitselectGetCond(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSIMDBitselectGetCond(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -1992,7 +2461,8 @@ BinaryenExpressionRef BinaryenSIMDBitselectGetCond(BinaryenExpressionRef expr) { // SIMDShift BinaryenOp BinaryenSIMDShiftGetOp(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSIMDShiftGetOp(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSIMDShiftGetOp(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -2001,7 +2471,8 @@ BinaryenOp BinaryenSIMDShiftGetOp(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenSIMDShiftGetVec(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSIMDShiftGetVec(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSIMDShiftGetVec(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -2010,7 +2481,8 @@ BinaryenExpressionRef BinaryenSIMDShiftGetVec(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenSIMDShiftGetShift(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenSIMDShiftGetShift(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenSIMDShiftGetShift(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -2020,7 +2492,8 @@ BinaryenExpressionRef BinaryenSIMDShiftGetShift(BinaryenExpressionRef expr) { // MemoryInit uint32_t BinaryenMemoryInitGetSegment(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenMemoryInitGetSegment(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenMemoryInitGetSegment(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -2029,7 +2502,8 @@ uint32_t BinaryenMemoryInitGetSegment(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenMemoryInitGetDest(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenMemoryInitGetDest(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenMemoryInitGetDest(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -2038,7 +2512,8 @@ BinaryenExpressionRef BinaryenMemoryInitGetDest(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenMemoryInitGetOffset(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenMemoryInitGetOffset(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenMemoryInitGetOffset(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -2047,7 +2522,8 @@ BinaryenExpressionRef BinaryenMemoryInitGetOffset(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenMemoryInitGetSize(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenMemoryInitGetSize(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenMemoryInitGetSize(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -2057,7 +2533,8 @@ BinaryenExpressionRef BinaryenMemoryInitGetSize(BinaryenExpressionRef expr) { // DataDrop uint32_t BinaryenDataDropGetSegment(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenDataDropGetSegment(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenDataDropGetSegment(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -2067,7 +2544,8 @@ uint32_t BinaryenDataDropGetSegment(BinaryenExpressionRef expr) { // MemoryCopy BinaryenExpressionRef BinaryenMemoryCopyGetDest(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenMemoryCopyGetDest(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenMemoryCopyGetDest(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -2076,7 +2554,8 @@ BinaryenExpressionRef BinaryenMemoryCopyGetDest(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenMemoryCopyGetSource(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenMemoryCopyGetSource(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenMemoryCopyGetSource(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -2085,7 +2564,8 @@ BinaryenExpressionRef BinaryenMemoryCopyGetSource(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenMemoryCopyGetSize(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenMemoryCopyGetSize(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenMemoryCopyGetSize(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -2095,7 +2575,8 @@ BinaryenExpressionRef BinaryenMemoryCopyGetSize(BinaryenExpressionRef expr) { // MemoryFill BinaryenExpressionRef BinaryenMemoryFillGetDest(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenMemoryFillGetDest(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenMemoryFillGetDest(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -2104,7 +2585,8 @@ BinaryenExpressionRef BinaryenMemoryFillGetDest(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenMemoryFillGetValue(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenMemoryFillGetValue(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenMemoryFillGetValue(expressions[" + << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; @@ -2113,7 +2595,8 @@ BinaryenExpressionRef BinaryenMemoryFillGetValue(BinaryenExpressionRef expr) { } BinaryenExpressionRef BinaryenMemoryFillGetSize(BinaryenExpressionRef expr) { if (tracing) { - std::cout << " BinaryenMemoryFillGetSize(expressions[" << expressions[expr] << "]);\n"; + std::cout << " BinaryenMemoryFillGetSize(expressions[" << expressions[expr] + << "]);\n"; } auto* expression = (Expression*)expr; @@ -2123,7 +2606,12 @@ BinaryenExpressionRef BinaryenMemoryFillGetSize(BinaryenExpressionRef expr) { // Functions -BinaryenFunctionRef BinaryenAddFunction(BinaryenModuleRef module, const char* name, BinaryenFunctionTypeRef type, BinaryenType* varTypes, BinaryenIndex numVarTypes, BinaryenExpressionRef body) { +BinaryenFunctionRef BinaryenAddFunction(BinaryenModuleRef module, + const char* name, + BinaryenFunctionTypeRef type, + BinaryenType* varTypes, + BinaryenIndex numVarTypes, + BinaryenExpressionRef body) { auto* wasm = (Module*)module; auto* ret = new Function; @@ -2131,14 +2619,21 @@ BinaryenFunctionRef BinaryenAddFunction(BinaryenModuleRef module, const char* na std::cout << " {\n"; std::cout << " BinaryenType varTypes[] = { "; for (BinaryenIndex i = 0; i < numVarTypes; i++) { - if (i > 0) std::cout << ", "; + if (i > 0) + std::cout << ", "; std::cout << varTypes[i]; } - if (numVarTypes == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS + if (numVarTypes == 0) + // ensure the array is not empty, otherwise a compiler error on VS + std::cout << "0"; std::cout << " };\n"; auto id = functions.size(); functions[ret] = id; - std::cout << " functions[" << id << "] = BinaryenAddFunction(the_module, \"" << name << "\", functionTypes[" << functionTypes[type] << "], varTypes, " << numVarTypes << ", expressions[" << expressions[body] << "]);\n"; + std::cout << " functions[" << id + << "] = BinaryenAddFunction(the_module, \"" << name + << "\", functionTypes[" << functionTypes[type] << "], varTypes, " + << numVarTypes << ", expressions[" << expressions[body] + << "]);\n"; std::cout << " }\n"; } @@ -2161,7 +2656,8 @@ BinaryenFunctionRef BinaryenAddFunction(BinaryenModuleRef module, const char* na return ret; } -BinaryenFunctionRef BinaryenGetFunction(BinaryenModuleRef module, const char* name) { +BinaryenFunctionRef BinaryenGetFunction(BinaryenModuleRef module, + const char* name) { if (tracing) { std::cout << " BinaryenGetFunction(the_module, \"" << name << "\");\n"; } @@ -2178,9 +2674,15 @@ void BinaryenRemoveFunction(BinaryenModuleRef module, const char* name) { wasm->removeFunction(name); } -BinaryenGlobalRef BinaryenAddGlobal(BinaryenModuleRef module, const char* name, BinaryenType type, int8_t mutable_, BinaryenExpressionRef init) { +BinaryenGlobalRef BinaryenAddGlobal(BinaryenModuleRef module, + const char* name, + BinaryenType type, + int8_t mutable_, + BinaryenExpressionRef init) { if (tracing) { - std::cout << " BinaryenAddGlobal(the_module, \"" << name << "\", " << type << ", " << int(mutable_) << ", expressions[" << expressions[init] << "]);\n"; + std::cout << " BinaryenAddGlobal(the_module, \"" << name << "\", " << type + << ", " << int(mutable_) << ", expressions[" << expressions[init] + << "]);\n"; } auto* wasm = (Module*)module; @@ -2203,12 +2705,18 @@ void BinaryenRemoveGlobal(BinaryenModuleRef module, const char* name) { // Imports -void BinaryenAddFunctionImport(BinaryenModuleRef module, const char* internalName, const char* externalModuleName, const char* externalBaseName, BinaryenFunctionTypeRef functionType) { +void BinaryenAddFunctionImport(BinaryenModuleRef module, + const char* internalName, + const char* externalModuleName, + const char* externalBaseName, + BinaryenFunctionTypeRef functionType) { auto* wasm = (Module*)module; auto* ret = new Function(); if (tracing) { - std::cout << " BinaryenAddFunctionImport(the_module, \"" << internalName << "\", \"" << externalModuleName << "\", \"" << externalBaseName << "\", functionTypes[" << functionTypes[functionType] << "]);\n"; + std::cout << " BinaryenAddFunctionImport(the_module, \"" << internalName + << "\", \"" << externalModuleName << "\", \"" << externalBaseName + << "\", functionTypes[" << functionTypes[functionType] << "]);\n"; } ret->name = internalName; @@ -2218,33 +2726,50 @@ void BinaryenAddFunctionImport(BinaryenModuleRef module, const char* internalNam FunctionTypeUtils::fillFunction(ret, (FunctionType*)functionType); wasm->addFunction(ret); } -void BinaryenAddTableImport(BinaryenModuleRef module, const char* internalName, const char* externalModuleName, const char* externalBaseName) { +void BinaryenAddTableImport(BinaryenModuleRef module, + const char* internalName, + const char* externalModuleName, + const char* externalBaseName) { auto* wasm = (Module*)module; if (tracing) { - std::cout << " BinaryenAddTableImport(the_module, \"" << internalName << "\", \"" << externalModuleName << "\", \"" << externalBaseName << "\");\n"; + std::cout << " BinaryenAddTableImport(the_module, \"" << internalName + << "\", \"" << externalModuleName << "\", \"" << externalBaseName + << "\");\n"; } wasm->table.module = externalModuleName; wasm->table.base = externalBaseName; } -void BinaryenAddMemoryImport(BinaryenModuleRef module, const char* internalName, const char* externalModuleName, const char* externalBaseName, uint8_t shared) { +void BinaryenAddMemoryImport(BinaryenModuleRef module, + const char* internalName, + const char* externalModuleName, + const char* externalBaseName, + uint8_t shared) { auto* wasm = (Module*)module; if (tracing) { - std::cout << " BinaryenAddMemoryImport(the_module, \"" << internalName << "\", \"" << externalModuleName << "\", \"" << externalBaseName << "\", " << int(shared) << ");\n"; + std::cout << " BinaryenAddMemoryImport(the_module, \"" << internalName + << "\", \"" << externalModuleName << "\", \"" << externalBaseName + << "\", " << int(shared) << ");\n"; } wasm->memory.module = externalModuleName; wasm->memory.base = externalBaseName; wasm->memory.shared = shared; } -void BinaryenAddGlobalImport(BinaryenModuleRef module, const char* internalName, const char* externalModuleName, const char* externalBaseName, BinaryenType globalType) { +void BinaryenAddGlobalImport(BinaryenModuleRef module, + const char* internalName, + const char* externalModuleName, + const char* externalBaseName, + BinaryenType globalType) { auto* wasm = (Module*)module; auto* ret = new Global(); if (tracing) { - std::cout << " BinaryenAddGlobalImport(the_module, \"" << internalName << "\", \"" << externalModuleName << "\", \"" << externalBaseName << "\", " << globalType << ");\n"; + std::cout << " BinaryenAddGlobalImport(the_module, \"" << internalName + << "\", \"" << externalModuleName << "\", \"" << externalBaseName + << "\", " << globalType << ");\n"; } ret->name = internalName; @@ -2256,17 +2781,23 @@ void BinaryenAddGlobalImport(BinaryenModuleRef module, const char* internalName, // Exports -WASM_DEPRECATED BinaryenExportRef BinaryenAddExport(BinaryenModuleRef module, const char* internalName, const char* externalName) { +WASM_DEPRECATED BinaryenExportRef BinaryenAddExport(BinaryenModuleRef module, + const char* internalName, + const char* externalName) { return BinaryenAddFunctionExport(module, internalName, externalName); } -BinaryenExportRef BinaryenAddFunctionExport(BinaryenModuleRef module, const char* internalName, const char* externalName) { +BinaryenExportRef BinaryenAddFunctionExport(BinaryenModuleRef module, + const char* internalName, + const char* externalName) { auto* wasm = (Module*)module; auto* ret = new Export(); if (tracing) { auto id = exports.size(); exports[ret] = id; - std::cout << " exports[" << id << "] = BinaryenAddFunctionExport(the_module, \"" << internalName << "\", \"" << externalName << "\");\n"; + std::cout << " exports[" << id + << "] = BinaryenAddFunctionExport(the_module, \"" << internalName + << "\", \"" << externalName << "\");\n"; } ret->value = internalName; @@ -2275,14 +2806,18 @@ BinaryenExportRef BinaryenAddFunctionExport(BinaryenModuleRef module, const char wasm->addExport(ret); return ret; } -BinaryenExportRef BinaryenAddTableExport(BinaryenModuleRef module, const char* internalName, const char* externalName) { +BinaryenExportRef BinaryenAddTableExport(BinaryenModuleRef module, + const char* internalName, + const char* externalName) { auto* wasm = (Module*)module; auto* ret = new Export(); if (tracing) { auto id = exports.size(); exports[ret] = id; - std::cout << " exports[" << id << "] = BinaryenAddTableExport(the_module, \"" << internalName << "\", \"" << externalName << "\");\n"; + std::cout << " exports[" << id + << "] = BinaryenAddTableExport(the_module, \"" << internalName + << "\", \"" << externalName << "\");\n"; } ret->value = internalName; @@ -2291,14 +2826,18 @@ BinaryenExportRef BinaryenAddTableExport(BinaryenModuleRef module, const char* i wasm->addExport(ret); return ret; } -BinaryenExportRef BinaryenAddMemoryExport(BinaryenModuleRef module, const char* internalName, const char* externalName) { +BinaryenExportRef BinaryenAddMemoryExport(BinaryenModuleRef module, + const char* internalName, + const char* externalName) { auto* wasm = (Module*)module; auto* ret = new Export(); if (tracing) { auto id = exports.size(); exports[ret] = id; - std::cout << " exports[" << id << "] = BinaryenAddMemoryExport(the_module, \"" << internalName << "\", \"" << externalName << "\");\n"; + std::cout << " exports[" << id + << "] = BinaryenAddMemoryExport(the_module, \"" << internalName + << "\", \"" << externalName << "\");\n"; } ret->value = internalName; @@ -2307,14 +2846,18 @@ BinaryenExportRef BinaryenAddMemoryExport(BinaryenModuleRef module, const char* wasm->addExport(ret); return ret; } -BinaryenExportRef BinaryenAddGlobalExport(BinaryenModuleRef module, const char* internalName, const char* externalName) { +BinaryenExportRef BinaryenAddGlobalExport(BinaryenModuleRef module, + const char* internalName, + const char* externalName) { auto* wasm = (Module*)module; auto* ret = new Export(); if (tracing) { auto id = exports.size(); exports[ret] = id; - std::cout << " exports[" << id << "] = BinaryenAddGlobalExport(the_module, \"" << internalName << "\", \"" << externalName << "\");\n"; + std::cout << " exports[" << id + << "] = BinaryenAddGlobalExport(the_module, \"" << internalName + << "\", \"" << externalName << "\");\n"; } ret->value = internalName; @@ -2325,7 +2868,8 @@ BinaryenExportRef BinaryenAddGlobalExport(BinaryenModuleRef module, const char* } void BinaryenRemoveExport(BinaryenModuleRef module, const char* externalName) { if (tracing) { - std::cout << " BinaryenRemoveExport(the_module, \"" << externalName << "\");\n"; + std::cout << " BinaryenRemoveExport(the_module, \"" << externalName + << "\");\n"; } auto* wasm = (Module*)module; @@ -2334,21 +2878,28 @@ void BinaryenRemoveExport(BinaryenModuleRef module, const char* externalName) { // Function table. One per module -void BinaryenSetFunctionTable(BinaryenModuleRef module, BinaryenIndex initial, BinaryenIndex maximum, const char** funcNames, BinaryenIndex numFuncNames) { +void BinaryenSetFunctionTable(BinaryenModuleRef module, + BinaryenIndex initial, + BinaryenIndex maximum, + const char** funcNames, + BinaryenIndex numFuncNames) { if (tracing) { std::cout << " {\n"; std::cout << " const char* funcNames[] = { "; for (BinaryenIndex i = 0; i < numFuncNames; i++) { - if (i > 0) std::cout << ", "; + if (i > 0) + std::cout << ", "; std::cout << "\"" << funcNames[i] << "\""; } std::cout << " };\n"; - std::cout << " BinaryenSetFunctionTable(the_module, " << initial << ", " << maximum << ", funcNames, " << numFuncNames << ");\n"; + std::cout << " BinaryenSetFunctionTable(the_module, " << initial << ", " + << maximum << ", funcNames, " << numFuncNames << ");\n"; std::cout << " }\n"; } auto* wasm = (Module*)module; - Table::Segment segment(wasm->allocator.alloc<Const>()->set(Literal(int32_t(0)))); + Table::Segment segment( + wasm->allocator.alloc<Const>()->set(Literal(int32_t(0)))); for (BinaryenIndex i = 0; i < numFuncNames; i++) { segment.data.push_back(funcNames[i]); } @@ -2360,48 +2911,72 @@ void BinaryenSetFunctionTable(BinaryenModuleRef module, BinaryenIndex initial, B // Memory. One per module -void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, BinaryenIndex maximum, const char* exportName, const char** segments, int8_t* segmentPassive, BinaryenExpressionRef* segmentOffsets, BinaryenIndex* segmentSizes, BinaryenIndex numSegments, uint8_t shared) { +void BinaryenSetMemory(BinaryenModuleRef module, + BinaryenIndex initial, + BinaryenIndex maximum, + const char* exportName, + const char** segments, + int8_t* segmentPassive, + BinaryenExpressionRef* segmentOffsets, + BinaryenIndex* segmentSizes, + BinaryenIndex numSegments, + uint8_t shared) { if (tracing) { std::cout << " {\n"; for (BinaryenIndex i = 0; i < numSegments; i++) { std::cout << " const char segment" << i << "[] = { "; for (BinaryenIndex j = 0; j < segmentSizes[i]; j++) { - if (j > 0) std::cout << ", "; + if (j > 0) + std::cout << ", "; std::cout << int(segments[i][j]); } std::cout << " };\n"; } std::cout << " const char* segments[] = { "; for (BinaryenIndex i = 0; i < numSegments; i++) { - if (i > 0) std::cout << ", "; + if (i > 0) + std::cout << ", "; std::cout << "segment" << i; } - if (numSegments == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS + if (numSegments == 0) + // ensure the array is not empty, otherwise a compiler error on VS + std::cout << "0"; std::cout << " };\n"; std::cout << " int8_t segmentPassive[] = { "; for (BinaryenIndex i = 0; i < numSegments; i++) { - if (i > 0) std::cout << ", "; + if (i > 0) + std::cout << ", "; std::cout << int(segmentPassive[i]); } - if (numSegments == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS + if (numSegments == 0) + // ensure the array is not empty, otherwise a compiler error on VS + std::cout << "0"; std::cout << " };\n"; std::cout << " BinaryenExpressionRef segmentOffsets[] = { "; for (BinaryenIndex i = 0; i < numSegments; i++) { - if (i > 0) std::cout << ", "; + if (i > 0) + std::cout << ", "; std::cout << "expressions[" << expressions[segmentOffsets[i]] << "]"; } - if (numSegments == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS + if (numSegments == 0) + // ensure the array is not empty, otherwise a compiler error on VS + std::cout << "0"; std::cout << " };\n"; std::cout << " BinaryenIndex segmentSizes[] = { "; for (BinaryenIndex i = 0; i < numSegments; i++) { - if (i > 0) std::cout << ", "; + if (i > 0) + std::cout << ", "; std::cout << segmentSizes[i]; } - if (numSegments == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS + if (numSegments == 0) + // ensure the array is not empty, otherwise a compiler error on VS + std::cout << "0"; std::cout << " };\n"; - std::cout << " BinaryenSetMemory(the_module, " << initial << ", " << maximum << ", "; + std::cout << " BinaryenSetMemory(the_module, " << initial << ", " + << maximum << ", "; traceNameOrNULL(exportName); - std::cout << ", segments, segmentPassive, segmentOffsets, segmentSizes, " << numSegments << ", " << int(shared) << ");\n"; + std::cout << ", segments, segmentPassive, segmentOffsets, segmentSizes, " + << numSegments << ", " << int(shared) << ");\n"; std::cout << " }\n"; } @@ -2418,7 +2993,10 @@ void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, Binaryen wasm->addExport(memoryExport.release()); } for (BinaryenIndex i = 0; i < numSegments; i++) { - wasm->memory.segments.emplace_back(segmentPassive[i], (Expression*)segmentOffsets[i], segments[i], segmentSizes[i]); + wasm->memory.segments.emplace_back(segmentPassive[i], + (Expression*)segmentOffsets[i], + segments[i], + segmentSizes[i]); } } @@ -2426,7 +3004,8 @@ void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, Binaryen void BinaryenSetStart(BinaryenModuleRef module, BinaryenFunctionRef start) { if (tracing) { - std::cout << " BinaryenSetStart(the_module, functions[" << functions[start] << "]);\n"; + std::cout << " BinaryenSetStart(the_module, functions[" << functions[start] + << "]);\n"; } auto* wasm = (Module*)module; @@ -2551,16 +3130,20 @@ void BinaryenSetDebugInfo(int on) { globalPassOptions.debugInfo = on != 0; } -void BinaryenModuleRunPasses(BinaryenModuleRef module, const char** passes, BinaryenIndex numPasses) { +void BinaryenModuleRunPasses(BinaryenModuleRef module, + const char** passes, + BinaryenIndex numPasses) { if (tracing) { std::cout << " {\n"; std::cout << " const char* passes[] = { "; for (BinaryenIndex i = 0; i < numPasses; i++) { - if (i > 0) std::cout << ", "; + if (i > 0) + std::cout << ", "; std::cout << "\"" << passes[i] << "\""; } std::cout << " };\n"; - std::cout << " BinaryenModuleRunPasses(the_module, passes, " << numPasses << ");\n"; + std::cout << " BinaryenModuleRunPasses(the_module, passes, " << numPasses + << ");\n"; std::cout << " }\n"; } @@ -2585,7 +3168,12 @@ void BinaryenModuleAutoDrop(BinaryenModuleRef module) { passRunner.run(); } -static BinaryenBufferSizes writeModule(BinaryenModuleRef module, char* output, size_t outputSize, const char* sourceMapUrl, char* sourceMap, size_t sourceMapSize) { +static BinaryenBufferSizes writeModule(BinaryenModuleRef module, + char* output, + size_t outputSize, + const char* sourceMapUrl, + char* sourceMap, + size_t sourceMapSize) { Module* wasm = (Module*)module; BufferWithRandomAccess buffer(false); WasmBinaryWriter writer(wasm, buffer, false); @@ -2603,28 +3191,38 @@ static BinaryenBufferSizes writeModule(BinaryenModuleRef module, char* output, s sourceMapBytes = std::min(str.length(), sourceMapSize); std::copy_n(str.c_str(), sourceMapBytes, sourceMap); } - return { bytes, sourceMapBytes }; + return {bytes, sourceMapBytes}; } -size_t BinaryenModuleWrite(BinaryenModuleRef module, char* output, size_t outputSize) { +size_t +BinaryenModuleWrite(BinaryenModuleRef module, char* output, size_t outputSize) { if (tracing) { std::cout << " // BinaryenModuleWrite\n"; } - return writeModule((Module*)module, output, outputSize, nullptr, nullptr, 0).outputBytes; + return writeModule((Module*)module, output, outputSize, nullptr, nullptr, 0) + .outputBytes; } -BinaryenBufferSizes BinaryenModuleWriteWithSourceMap(BinaryenModuleRef module, const char* url, char* output, size_t outputSize, char* sourceMap, size_t sourceMapSize) { +BinaryenBufferSizes BinaryenModuleWriteWithSourceMap(BinaryenModuleRef module, + const char* url, + char* output, + size_t outputSize, + char* sourceMap, + size_t sourceMapSize) { if (tracing) { std::cout << " // BinaryenModuleWriteWithSourceMap\n"; } assert(url); assert(sourceMap); - return writeModule((Module*)module, output, outputSize, url, sourceMap, sourceMapSize); + return writeModule( + (Module*)module, output, outputSize, url, sourceMap, sourceMapSize); } -BinaryenModuleAllocateAndWriteResult BinaryenModuleAllocateAndWrite(BinaryenModuleRef module, const char* sourceMapUrl) { +BinaryenModuleAllocateAndWriteResult +BinaryenModuleAllocateAndWrite(BinaryenModuleRef module, + const char* sourceMapUrl) { if (tracing) { std::cout << " // BinaryenModuleAllocateAndWrite(the_module, "; traceNameOrNULL(sourceMapUrl); @@ -2648,7 +3246,7 @@ BinaryenModuleAllocateAndWriteResult BinaryenModuleAllocateAndWrite(BinaryenModu sourceMap = (char*)malloc(str.length() + 1); std::copy_n(str.c_str(), str.length() + 1, sourceMap); } - return { binary, buffer.size(), sourceMap }; + return {binary, buffer.size(), sourceMap}; } BinaryenModuleRef BinaryenModuleRead(char* input, size_t inputSize) { @@ -2680,9 +3278,11 @@ void BinaryenModuleInterpret(BinaryenModuleRef module) { ModuleInstance instance(*wasm, &interface); } -BinaryenIndex BinaryenModuleAddDebugInfoFileName(BinaryenModuleRef module, const char* filename) { +BinaryenIndex BinaryenModuleAddDebugInfoFileName(BinaryenModuleRef module, + const char* filename) { if (tracing) { - std::cout << " BinaryenModuleAddDebugInfoFileName(the_module, \"" << filename << "\");\n"; + std::cout << " BinaryenModuleAddDebugInfoFileName(the_module, \"" + << filename << "\");\n"; } Module* wasm = (Module*)module; @@ -2691,13 +3291,17 @@ BinaryenIndex BinaryenModuleAddDebugInfoFileName(BinaryenModuleRef module, const return index; } -const char* BinaryenModuleGetDebugInfoFileName(BinaryenModuleRef module, BinaryenIndex index) { +const char* BinaryenModuleGetDebugInfoFileName(BinaryenModuleRef module, + BinaryenIndex index) { if (tracing) { - std::cout << " BinaryenModuleGetDebugInfoFileName(the_module, \"" << index << "\");\n"; + std::cout << " BinaryenModuleGetDebugInfoFileName(the_module, \"" << index + << "\");\n"; } Module* wasm = (Module*)module; - return index < wasm->debugInfoFileNames.size() ? wasm->debugInfoFileNames.at(index).c_str() : nullptr; + return index < wasm->debugInfoFileNames.size() + ? wasm->debugInfoFileNames.at(index).c_str() + : nullptr; } // @@ -2706,21 +3310,25 @@ const char* BinaryenModuleGetDebugInfoFileName(BinaryenModuleRef module, Binarye const char* BinaryenFunctionTypeGetName(BinaryenFunctionTypeRef ftype) { if (tracing) { - std::cout << " BinaryenFunctionTypeGetName(functionsTypes[" << functions[ftype] << "]);\n"; + std::cout << " BinaryenFunctionTypeGetName(functionsTypes[" + << functions[ftype] << "]);\n"; } return ((FunctionType*)ftype)->name.c_str(); } BinaryenIndex BinaryenFunctionTypeGetNumParams(BinaryenFunctionTypeRef ftype) { if (tracing) { - std::cout << " BinaryenFunctionTypeGetNumParams(functionsTypes[" << functions[ftype] << "]);\n"; + std::cout << " BinaryenFunctionTypeGetNumParams(functionsTypes[" + << functions[ftype] << "]);\n"; } return ((FunctionType*)ftype)->params.size(); } -BinaryenType BinaryenFunctionTypeGetParam(BinaryenFunctionTypeRef ftype, BinaryenIndex index) { +BinaryenType BinaryenFunctionTypeGetParam(BinaryenFunctionTypeRef ftype, + BinaryenIndex index) { if (tracing) { - std::cout << " BinaryenFunctionTypeGetParam(functionsTypes[" << functions[ftype] << "], " << index << ");\n"; + std::cout << " BinaryenFunctionTypeGetParam(functionsTypes[" + << functions[ftype] << "], " << index << ");\n"; } auto* ft = (FunctionType*)ftype; @@ -2729,7 +3337,8 @@ BinaryenType BinaryenFunctionTypeGetParam(BinaryenFunctionTypeRef ftype, Binarye } BinaryenType BinaryenFunctionTypeGetResult(BinaryenFunctionTypeRef ftype) { if (tracing) { - std::cout << " BinaryenFunctionTypeGetResult(functionsTypes[" << functions[ftype] << "]);\n"; + std::cout << " BinaryenFunctionTypeGetResult(functionsTypes[" + << functions[ftype] << "]);\n"; } return ((FunctionType*)ftype)->result; @@ -2741,28 +3350,33 @@ BinaryenType BinaryenFunctionTypeGetResult(BinaryenFunctionTypeRef ftype) { const char* BinaryenFunctionGetName(BinaryenFunctionRef func) { if (tracing) { - std::cout << " BinaryenFunctionGetName(functions[" << functions[func] << "]);\n"; + std::cout << " BinaryenFunctionGetName(functions[" << functions[func] + << "]);\n"; } return ((Function*)func)->name.c_str(); } const char* BinaryenFunctionGetType(BinaryenFunctionRef func) { if (tracing) { - std::cout << " BinaryenFunctionGetType(functions[" << functions[func] << "]);\n"; + std::cout << " BinaryenFunctionGetType(functions[" << functions[func] + << "]);\n"; } return ((Function*)func)->type.c_str(); } BinaryenIndex BinaryenFunctionGetNumParams(BinaryenFunctionRef func) { if (tracing) { - std::cout << " BinaryenFunctionGetNumParams(functions[" << functions[func] << "]);\n"; + std::cout << " BinaryenFunctionGetNumParams(functions[" << functions[func] + << "]);\n"; } return ((Function*)func)->params.size(); } -BinaryenType BinaryenFunctionGetParam(BinaryenFunctionRef func, BinaryenIndex index) { +BinaryenType BinaryenFunctionGetParam(BinaryenFunctionRef func, + BinaryenIndex index) { if (tracing) { - std::cout << " BinaryenFunctionGetParam(functions[" << functions[func] << "], " << index << ");\n"; + std::cout << " BinaryenFunctionGetParam(functions[" << functions[func] + << "], " << index << ");\n"; } auto* fn = (Function*)func; @@ -2771,21 +3385,25 @@ BinaryenType BinaryenFunctionGetParam(BinaryenFunctionRef func, BinaryenIndex in } BinaryenType BinaryenFunctionGetResult(BinaryenFunctionRef func) { if (tracing) { - std::cout << " BinaryenFunctionGetResult(functions[" << functions[func] << "]);\n"; + std::cout << " BinaryenFunctionGetResult(functions[" << functions[func] + << "]);\n"; } return ((Function*)func)->result; } BinaryenIndex BinaryenFunctionGetNumVars(BinaryenFunctionRef func) { if (tracing) { - std::cout << " BinaryenFunctionGetNumVars(functions[" << functions[func] << "]);\n"; + std::cout << " BinaryenFunctionGetNumVars(functions[" << functions[func] + << "]);\n"; } return ((Function*)func)->vars.size(); } -BinaryenType BinaryenFunctionGetVar(BinaryenFunctionRef func, BinaryenIndex index) { +BinaryenType BinaryenFunctionGetVar(BinaryenFunctionRef func, + BinaryenIndex index) { if (tracing) { - std::cout << " BinaryenFunctionGetVar(functions[" << functions[func] << "], " << index << ");\n"; + std::cout << " BinaryenFunctionGetVar(functions[" << functions[func] + << "], " << index << ");\n"; } auto* fn = (Function*)func; @@ -2794,14 +3412,17 @@ BinaryenType BinaryenFunctionGetVar(BinaryenFunctionRef func, BinaryenIndex inde } BinaryenExpressionRef BinaryenFunctionGetBody(BinaryenFunctionRef func) { if (tracing) { - std::cout << " BinaryenFunctionGetBody(functions[" << functions[func] << "]);\n"; + std::cout << " BinaryenFunctionGetBody(functions[" << functions[func] + << "]);\n"; } return ((Function*)func)->body; } -void BinaryenFunctionOptimize(BinaryenFunctionRef func, BinaryenModuleRef module) { +void BinaryenFunctionOptimize(BinaryenFunctionRef func, + BinaryenModuleRef module) { if (tracing) { - std::cout << " BinaryenFunctionOptimize(functions[" << functions[func] << "], the_module);\n"; + std::cout << " BinaryenFunctionOptimize(functions[" << functions[func] + << "], the_module);\n"; } Module* wasm = (Module*)module; @@ -2810,16 +3431,21 @@ void BinaryenFunctionOptimize(BinaryenFunctionRef func, BinaryenModuleRef module passRunner.addDefaultOptimizationPasses(); passRunner.runOnFunction((Function*)func); } -void BinaryenFunctionRunPasses(BinaryenFunctionRef func, BinaryenModuleRef module, const char** passes, BinaryenIndex numPasses) { +void BinaryenFunctionRunPasses(BinaryenFunctionRef func, + BinaryenModuleRef module, + const char** passes, + BinaryenIndex numPasses) { if (tracing) { std::cout << " {\n"; std::cout << " const char* passes[] = { "; for (BinaryenIndex i = 0; i < numPasses; i++) { - if (i > 0) std::cout << ", "; + if (i > 0) + std::cout << ", "; std::cout << "\"" << passes[i] << "\""; } std::cout << " };\n"; - std::cout << " BinaryenFunctionRunPasses(functions[" << functions[func] << ", the_module, passes, " << numPasses << ");\n"; + std::cout << " BinaryenFunctionRunPasses(functions[" << functions[func] + << ", the_module, passes, " << numPasses << ");\n"; std::cout << " }\n"; } @@ -2831,9 +3457,16 @@ void BinaryenFunctionRunPasses(BinaryenFunctionRef func, BinaryenModuleRef modul } passRunner.runOnFunction((Function*)func); } -void BinaryenFunctionSetDebugLocation(BinaryenFunctionRef func, BinaryenExpressionRef expr, BinaryenIndex fileIndex, BinaryenIndex lineNumber, BinaryenIndex columnNumber) { +void BinaryenFunctionSetDebugLocation(BinaryenFunctionRef func, + BinaryenExpressionRef expr, + BinaryenIndex fileIndex, + BinaryenIndex lineNumber, + BinaryenIndex columnNumber) { if (tracing) { - std::cout << " BinaryenFunctionSetDebugLocation(functions[" << functions[func] << "], expressions[" << expressions[expr] << "], " << fileIndex << ", " << lineNumber << ", " << columnNumber << ");\n"; + std::cout << " BinaryenFunctionSetDebugLocation(functions[" + << functions[func] << "], expressions[" << expressions[expr] + << "], " << fileIndex << ", " << lineNumber << ", " + << columnNumber << ");\n"; } auto* fn = (Function*)func; @@ -2853,7 +3486,8 @@ void BinaryenFunctionSetDebugLocation(BinaryenFunctionRef func, BinaryenExpressi const char* BinaryenFunctionImportGetModule(BinaryenFunctionRef import) { if (tracing) { - std::cout << " BinaryenFunctionImportGetModule(functions[" << functions[import] << "]);\n"; + std::cout << " BinaryenFunctionImportGetModule(functions[" + << functions[import] << "]);\n"; } auto* func = (Function*)import; @@ -2865,7 +3499,8 @@ const char* BinaryenFunctionImportGetModule(BinaryenFunctionRef import) { } const char* BinaryenGlobalImportGetModule(BinaryenGlobalRef import) { if (tracing) { - std::cout << " BinaryenGlobalImportGetModule(globals[" << globals[import] << "]);\n"; + std::cout << " BinaryenGlobalImportGetModule(globals[" << globals[import] + << "]);\n"; } auto* global = (Global*)import; @@ -2877,7 +3512,8 @@ const char* BinaryenGlobalImportGetModule(BinaryenGlobalRef import) { } const char* BinaryenFunctionImportGetBase(BinaryenFunctionRef import) { if (tracing) { - std::cout << " BinaryenFunctionImportGetBase(functions[" << functions[import] << "]);\n"; + std::cout << " BinaryenFunctionImportGetBase(functions[" + << functions[import] << "]);\n"; } auto* func = (Function*)import; @@ -2889,7 +3525,8 @@ const char* BinaryenFunctionImportGetBase(BinaryenFunctionRef import) { } const char* BinaryenGlobalImportGetBase(BinaryenGlobalRef import) { if (tracing) { - std::cout << " BinaryenGlobalImportGetBase(globals[" << globals[import] << "]);\n"; + std::cout << " BinaryenGlobalImportGetBase(globals[" << globals[import] + << "]);\n"; } auto* global = (Global*)import; @@ -2906,21 +3543,24 @@ const char* BinaryenGlobalImportGetBase(BinaryenGlobalRef import) { BinaryenExternalKind BinaryenExportGetKind(BinaryenExportRef export_) { if (tracing) { - std::cout << " BinaryenExportGetKind(exports[" << exports[export_] << "]);\n"; + std::cout << " BinaryenExportGetKind(exports[" << exports[export_] + << "]);\n"; } return BinaryenExternalKind(((Export*)export_)->kind); } const char* BinaryenExportGetName(BinaryenExportRef export_) { if (tracing) { - std::cout << " BinaryenExportGetName(exports[" << exports[export_] << "]);\n"; + std::cout << " BinaryenExportGetName(exports[" << exports[export_] + << "]);\n"; } return ((Export*)export_)->name.c_str(); } const char* BinaryenExportGetValue(BinaryenExportRef export_) { if (tracing) { - std::cout << " BinaryenExportGetValue(exports[" << exports[export_] << "]);\n"; + std::cout << " BinaryenExportGetValue(exports[" << exports[export_] + << "]);\n"; } return ((Export*)export_)->value.c_str(); @@ -2939,23 +3579,32 @@ RelooperRef RelooperCreate(BinaryenModuleRef module) { return RelooperRef(new CFG::Relooper(wasm)); } -RelooperBlockRef RelooperAddBlock(RelooperRef relooper, BinaryenExpressionRef code) { +RelooperBlockRef RelooperAddBlock(RelooperRef relooper, + BinaryenExpressionRef code) { auto* R = (CFG::Relooper*)relooper; auto* ret = new CFG::Block((Expression*)code); if (tracing) { auto id = relooperBlocks.size(); relooperBlocks[ret] = id; - std::cout << " relooperBlocks[" << id << "] = RelooperAddBlock(the_relooper, expressions[" << expressions[code] << "]);\n"; + std::cout << " relooperBlocks[" << id + << "] = RelooperAddBlock(the_relooper, expressions[" + << expressions[code] << "]);\n"; } R->AddBlock(ret); return RelooperRef(ret); } -void RelooperAddBranch(RelooperBlockRef from, RelooperBlockRef to, BinaryenExpressionRef condition, BinaryenExpressionRef code) { +void RelooperAddBranch(RelooperBlockRef from, + RelooperBlockRef to, + BinaryenExpressionRef condition, + BinaryenExpressionRef code) { if (tracing) { - std::cout << " RelooperAddBranch(relooperBlocks[" << relooperBlocks[from] << "], relooperBlocks[" << relooperBlocks[to] << "], expressions[" << expressions[condition] << "], expressions[" << expressions[code] << "]);\n"; + std::cout << " RelooperAddBranch(relooperBlocks[" << relooperBlocks[from] + << "], relooperBlocks[" << relooperBlocks[to] << "], expressions[" + << expressions[condition] << "], expressions[" + << expressions[code] << "]);\n"; } auto* fromBlock = (CFG::Block*)from; @@ -2963,29 +3612,44 @@ void RelooperAddBranch(RelooperBlockRef from, RelooperBlockRef to, BinaryenExpre fromBlock->AddBranchTo(toBlock, (Expression*)condition, (Expression*)code); } -RelooperBlockRef RelooperAddBlockWithSwitch(RelooperRef relooper, BinaryenExpressionRef code, BinaryenExpressionRef condition) { +RelooperBlockRef RelooperAddBlockWithSwitch(RelooperRef relooper, + BinaryenExpressionRef code, + BinaryenExpressionRef condition) { auto* R = (CFG::Relooper*)relooper; auto* ret = new CFG::Block((Expression*)code, (Expression*)condition); if (tracing) { - std::cout << " relooperBlocks[" << relooperBlocks[ret] << "] = RelooperAddBlockWithSwitch(the_relooper, expressions[" << expressions[code] << "], expressions[" << expressions[condition] << "]);\n"; + std::cout << " relooperBlocks[" << relooperBlocks[ret] + << "] = RelooperAddBlockWithSwitch(the_relooper, expressions[" + << expressions[code] << "], expressions[" + << expressions[condition] << "]);\n"; } R->AddBlock(ret); return RelooperRef(ret); } -void RelooperAddBranchForSwitch(RelooperBlockRef from, RelooperBlockRef to, BinaryenIndex* indexes, BinaryenIndex numIndexes, BinaryenExpressionRef code) { +void RelooperAddBranchForSwitch(RelooperBlockRef from, + RelooperBlockRef to, + BinaryenIndex* indexes, + BinaryenIndex numIndexes, + BinaryenExpressionRef code) { if (tracing) { std::cout << " {\n"; std::cout << " BinaryenIndex indexes[] = { "; for (BinaryenIndex i = 0; i < numIndexes; i++) { - if (i > 0) std::cout << ", "; + if (i > 0) + std::cout << ", "; std::cout << indexes[i]; } - if (numIndexes == 0) std::cout << "0"; // ensure the array is not empty, otherwise a compiler error on VS + if (numIndexes == 0) + // ensure the array is not empty, otherwise a compiler error on VS + std::cout << "0"; std::cout << " };\n"; - std::cout << " RelooperAddBranchForSwitch(relooperBlocks[" << relooperBlocks[from] << "], relooperBlocks[" << relooperBlocks[to] << "], indexes, " << numIndexes << ", expressions[" << expressions[code] << "]);\n"; + std::cout << " RelooperAddBranchForSwitch(relooperBlocks[" + << relooperBlocks[from] << "], relooperBlocks[" + << relooperBlocks[to] << "], indexes, " << numIndexes + << ", expressions[" << expressions[code] << "]);\n"; std::cout << " }\n"; } @@ -2998,7 +3662,9 @@ void RelooperAddBranchForSwitch(RelooperBlockRef from, RelooperBlockRef to, Bina fromBlock->AddSwitchBranchTo(toBlock, std::move(values), (Expression*)code); } -BinaryenExpressionRef RelooperRenderAndDispose(RelooperRef relooper, RelooperBlockRef entry, BinaryenIndex labelHelper) { +BinaryenExpressionRef RelooperRenderAndDispose(RelooperRef relooper, + RelooperBlockRef entry, + BinaryenIndex labelHelper) { auto* R = (CFG::Relooper*)relooper; R->Calculate((CFG::Block*)entry); CFG::RelooperBuilder builder(*R->Module, labelHelper); @@ -3006,7 +3672,9 @@ BinaryenExpressionRef RelooperRenderAndDispose(RelooperRef relooper, RelooperBlo if (tracing) { auto id = noteExpression(ret); - std::cout << " expressions[" << id << "] = RelooperRenderAndDispose(the_relooper, relooperBlocks[" << relooperBlocks[entry] << "], " << labelHelper << ");\n"; + std::cout << " expressions[" << id + << "] = RelooperRenderAndDispose(the_relooper, relooperBlocks[" + << relooperBlocks[entry] << "], " << labelHelper << ");\n"; relooperBlocks.clear(); } @@ -3045,7 +3713,11 @@ void BinaryenSetAPITracing(int on) { // ========= Utilities ========= // -BinaryenFunctionTypeRef BinaryenGetFunctionTypeBySignature(BinaryenModuleRef module, BinaryenType result, BinaryenType* paramTypes, BinaryenIndex numParams) { +BinaryenFunctionTypeRef +BinaryenGetFunctionTypeBySignature(BinaryenModuleRef module, + BinaryenType result, + BinaryenType* paramTypes, + BinaryenIndex numParams) { if (tracing) { std::cout << " // BinaryenGetFunctionTypeBySignature\n"; } @@ -3074,17 +3746,13 @@ BinaryenFunctionTypeRef BinaryenGetFunctionTypeBySignature(BinaryenModuleRef mod #ifdef __EMSCRIPTEN__ // Override atexit - we don't need any global ctors to actually run, and // otherwise we get clutter in the output in debug builds -int atexit(void (*function)(void)) { - return 0; -} +int atexit(void (*function)(void)) { return 0; } // Internal binaryen.js APIs // Returns the size of a Literal object. EMSCRIPTEN_KEEPALIVE -size_t BinaryenSizeofLiteral(void) { - return sizeof(Literal); -} +size_t BinaryenSizeofLiteral(void) { return sizeof(Literal); } // Returns the size of an allocate and write result object. EMSCRIPTEN_KEEPALIVE diff --git a/src/binaryen-c.h b/src/binaryen-c.h index 7478a4642..9a57cac79 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -139,14 +139,14 @@ BinaryenExternalKind BinaryenExternalGlobal(void); // Modules // // Modules contain lists of functions, imports, exports, function types. The -// Add* methods create them on a module. The module owns them and will free their -// memory when the module is disposed of. +// Add* methods create them on a module. The module owns them and will free +// their memory when the module is disposed of. // -// Expressions are also allocated inside modules, and freed with the module. They -// are not created by Add* methods, since they are not added directly on the -// module, instead, they are arguments to other expressions (and then they are -// the children of that AST node), or to a function (and then they are the body -// of that function). +// Expressions are also allocated inside modules, and freed with the module. +// They are not created by Add* methods, since they are not added directly on +// the module, instead, they are arguments to other expressions (and then they +// are the children of that AST node), or to a function (and then they are the +// body of that function). // // A module can also contain a function table for indirect calls, a memory, // and a start method. @@ -162,7 +162,11 @@ typedef void* BinaryenFunctionTypeRef; // Add a new function type. This is thread-safe. // Note: name can be NULL, in which case we auto-generate a name -BinaryenFunctionTypeRef BinaryenAddFunctionType(BinaryenModuleRef module, const char* name, BinaryenType result, BinaryenType* paramTypes, BinaryenIndex numParams); +BinaryenFunctionTypeRef BinaryenAddFunctionType(BinaryenModuleRef module, + const char* name, + BinaryenType result, + BinaryenType* paramTypes, + BinaryenIndex numParams); // Removes a function type. void BinaryenRemoveFunctionType(BinaryenModuleRef module, const char* name); @@ -485,20 +489,45 @@ typedef void* BinaryenExpressionRef; // parameter indicates that the block's type shall be figured out // automatically instead of explicitly providing it. This conforms // to the behavior before the 'type' parameter has been introduced. -BinaryenExpressionRef BinaryenBlock(BinaryenModuleRef module, const char* name, BinaryenExpressionRef* children, BinaryenIndex numChildren, BinaryenType type); +BinaryenExpressionRef BinaryenBlock(BinaryenModuleRef module, + const char* name, + BinaryenExpressionRef* children, + BinaryenIndex numChildren, + BinaryenType type); // If: ifFalse can be NULL -BinaryenExpressionRef BinaryenIf(BinaryenModuleRef module, BinaryenExpressionRef condition, BinaryenExpressionRef ifTrue, BinaryenExpressionRef ifFalse); -BinaryenExpressionRef BinaryenLoop(BinaryenModuleRef module, const char* in, BinaryenExpressionRef body); +BinaryenExpressionRef BinaryenIf(BinaryenModuleRef module, + BinaryenExpressionRef condition, + BinaryenExpressionRef ifTrue, + BinaryenExpressionRef ifFalse); +BinaryenExpressionRef BinaryenLoop(BinaryenModuleRef module, + const char* in, + BinaryenExpressionRef body); // Break: value and condition can be NULL -BinaryenExpressionRef BinaryenBreak(BinaryenModuleRef module, const char* name, BinaryenExpressionRef condition, BinaryenExpressionRef value); +BinaryenExpressionRef BinaryenBreak(BinaryenModuleRef module, + const char* name, + BinaryenExpressionRef condition, + BinaryenExpressionRef value); // Switch: value can be NULL -BinaryenExpressionRef BinaryenSwitch(BinaryenModuleRef module, const char** names, BinaryenIndex numNames, const char* defaultName, BinaryenExpressionRef condition, BinaryenExpressionRef value); +BinaryenExpressionRef BinaryenSwitch(BinaryenModuleRef module, + const char** names, + BinaryenIndex numNames, + const char* defaultName, + BinaryenExpressionRef condition, + BinaryenExpressionRef value); // Call: Note the 'returnType' parameter. You must declare the // type returned by the function being called, as that // function might not have been created yet, so we don't // know what it is. -BinaryenExpressionRef BinaryenCall(BinaryenModuleRef module, const char* target, BinaryenExpressionRef* operands, BinaryenIndex numOperands, BinaryenType returnType); -BinaryenExpressionRef BinaryenCallIndirect(BinaryenModuleRef module, BinaryenExpressionRef target, BinaryenExpressionRef* operands, BinaryenIndex numOperands, const char* type); +BinaryenExpressionRef BinaryenCall(BinaryenModuleRef module, + const char* target, + BinaryenExpressionRef* operands, + BinaryenIndex numOperands, + BinaryenType returnType); +BinaryenExpressionRef BinaryenCallIndirect(BinaryenModuleRef module, + BinaryenExpressionRef target, + BinaryenExpressionRef* operands, + BinaryenIndex numOperands, + const char* type); // GetLocal: Note the 'type' parameter. It might seem redundant, since the // local at that index must have a type. However, this API lets you // build code "top-down": create a node, then its parents, and so @@ -513,41 +542,134 @@ BinaryenExpressionRef BinaryenCallIndirect(BinaryenModuleRef module, BinaryenExp // a var, that is, either a parameter to the function or a variable // declared when you call BinaryenAddFunction. See BinaryenAddFunction // for more details. -BinaryenExpressionRef BinaryenGetLocal(BinaryenModuleRef module, BinaryenIndex index, BinaryenType type); -BinaryenExpressionRef BinaryenSetLocal(BinaryenModuleRef module, BinaryenIndex index, BinaryenExpressionRef value); -BinaryenExpressionRef BinaryenTeeLocal(BinaryenModuleRef module, BinaryenIndex index, BinaryenExpressionRef value); -BinaryenExpressionRef BinaryenGetGlobal(BinaryenModuleRef module, const char* name, BinaryenType type); -BinaryenExpressionRef BinaryenSetGlobal(BinaryenModuleRef module, const char* name, BinaryenExpressionRef value); -// Load: align can be 0, in which case it will be the natural alignment (equal to bytes) -BinaryenExpressionRef BinaryenLoad(BinaryenModuleRef module, uint32_t bytes, int8_t signed_, uint32_t offset, uint32_t align, BinaryenType type, BinaryenExpressionRef ptr); -// Store: align can be 0, in which case it will be the natural alignment (equal to bytes) -BinaryenExpressionRef BinaryenStore(BinaryenModuleRef module, uint32_t bytes, uint32_t offset, uint32_t align, BinaryenExpressionRef ptr, BinaryenExpressionRef value, BinaryenType type); -BinaryenExpressionRef BinaryenConst(BinaryenModuleRef module, struct BinaryenLiteral value); -BinaryenExpressionRef BinaryenUnary(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef value); -BinaryenExpressionRef BinaryenBinary(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef left, BinaryenExpressionRef right); -BinaryenExpressionRef BinaryenSelect(BinaryenModuleRef module, BinaryenExpressionRef condition, BinaryenExpressionRef ifTrue, BinaryenExpressionRef ifFalse); -BinaryenExpressionRef BinaryenDrop(BinaryenModuleRef module, BinaryenExpressionRef value); +BinaryenExpressionRef BinaryenGetLocal(BinaryenModuleRef module, + BinaryenIndex index, + BinaryenType type); +BinaryenExpressionRef BinaryenSetLocal(BinaryenModuleRef module, + BinaryenIndex index, + BinaryenExpressionRef value); +BinaryenExpressionRef BinaryenTeeLocal(BinaryenModuleRef module, + BinaryenIndex index, + BinaryenExpressionRef value); +BinaryenExpressionRef BinaryenGetGlobal(BinaryenModuleRef module, + const char* name, + BinaryenType type); +BinaryenExpressionRef BinaryenSetGlobal(BinaryenModuleRef module, + const char* name, + BinaryenExpressionRef value); +// Load: align can be 0, in which case it will be the natural alignment (equal +// to bytes) +BinaryenExpressionRef BinaryenLoad(BinaryenModuleRef module, + uint32_t bytes, + int8_t signed_, + uint32_t offset, + uint32_t align, + BinaryenType type, + BinaryenExpressionRef ptr); +// Store: align can be 0, in which case it will be the natural alignment (equal +// to bytes) +BinaryenExpressionRef BinaryenStore(BinaryenModuleRef module, + uint32_t bytes, + uint32_t offset, + uint32_t align, + BinaryenExpressionRef ptr, + BinaryenExpressionRef value, + BinaryenType type); +BinaryenExpressionRef BinaryenConst(BinaryenModuleRef module, + struct BinaryenLiteral value); +BinaryenExpressionRef BinaryenUnary(BinaryenModuleRef module, + BinaryenOp op, + BinaryenExpressionRef value); +BinaryenExpressionRef BinaryenBinary(BinaryenModuleRef module, + BinaryenOp op, + BinaryenExpressionRef left, + BinaryenExpressionRef right); +BinaryenExpressionRef BinaryenSelect(BinaryenModuleRef module, + BinaryenExpressionRef condition, + BinaryenExpressionRef ifTrue, + BinaryenExpressionRef ifFalse); +BinaryenExpressionRef BinaryenDrop(BinaryenModuleRef module, + BinaryenExpressionRef value); // Return: value can be NULL -BinaryenExpressionRef BinaryenReturn(BinaryenModuleRef module, BinaryenExpressionRef value); +BinaryenExpressionRef BinaryenReturn(BinaryenModuleRef module, + BinaryenExpressionRef value); // Host: name may be NULL -BinaryenExpressionRef BinaryenHost(BinaryenModuleRef module, BinaryenOp op, const char* name, BinaryenExpressionRef* operands, BinaryenIndex numOperands); +BinaryenExpressionRef BinaryenHost(BinaryenModuleRef module, + BinaryenOp op, + const char* name, + BinaryenExpressionRef* operands, + BinaryenIndex numOperands); BinaryenExpressionRef BinaryenNop(BinaryenModuleRef module); BinaryenExpressionRef BinaryenUnreachable(BinaryenModuleRef module); -BinaryenExpressionRef BinaryenAtomicLoad(BinaryenModuleRef module, uint32_t bytes, uint32_t offset, BinaryenType type, BinaryenExpressionRef ptr); -BinaryenExpressionRef BinaryenAtomicStore(BinaryenModuleRef module, uint32_t bytes, uint32_t offset, BinaryenExpressionRef ptr, BinaryenExpressionRef value, BinaryenType type); -BinaryenExpressionRef BinaryenAtomicRMW(BinaryenModuleRef module, BinaryenOp op, BinaryenIndex bytes, BinaryenIndex offset, BinaryenExpressionRef ptr, BinaryenExpressionRef value, BinaryenType type); -BinaryenExpressionRef BinaryenAtomicCmpxchg(BinaryenModuleRef module, BinaryenIndex bytes, BinaryenIndex offset, BinaryenExpressionRef ptr, BinaryenExpressionRef expected, BinaryenExpressionRef replacement, BinaryenType type); -BinaryenExpressionRef BinaryenAtomicWait(BinaryenModuleRef module, BinaryenExpressionRef ptr, BinaryenExpressionRef expected, BinaryenExpressionRef timeout, BinaryenType type); -BinaryenExpressionRef BinaryenAtomicNotify(BinaryenModuleRef module, BinaryenExpressionRef ptr, BinaryenExpressionRef notifyCount); -BinaryenExpressionRef BinaryenSIMDExtract(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef vec, uint8_t index); -BinaryenExpressionRef BinaryenSIMDReplace(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef vec, uint8_t index, BinaryenExpressionRef value); -BinaryenExpressionRef BinaryenSIMDShuffle(BinaryenModuleRef module, BinaryenExpressionRef left, BinaryenExpressionRef right, const uint8_t mask[16]); -BinaryenExpressionRef BinaryenSIMDBitselect(BinaryenModuleRef module, BinaryenExpressionRef left, BinaryenExpressionRef right, BinaryenExpressionRef cond); -BinaryenExpressionRef BinaryenSIMDShift(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef vec, BinaryenExpressionRef shift); -BinaryenExpressionRef BinaryenMemoryInit(BinaryenModuleRef module, uint32_t segment, BinaryenExpressionRef dest, BinaryenExpressionRef offset, BinaryenExpressionRef size); -BinaryenExpressionRef BinaryenDataDrop(BinaryenModuleRef module, uint32_t segment); -BinaryenExpressionRef BinaryenMemoryCopy(BinaryenModuleRef module, BinaryenExpressionRef dest, BinaryenExpressionRef source, BinaryenExpressionRef size); -BinaryenExpressionRef BinaryenMemoryFill(BinaryenModuleRef module, BinaryenExpressionRef dest, BinaryenExpressionRef value, BinaryenExpressionRef size); +BinaryenExpressionRef BinaryenAtomicLoad(BinaryenModuleRef module, + uint32_t bytes, + uint32_t offset, + BinaryenType type, + BinaryenExpressionRef ptr); +BinaryenExpressionRef BinaryenAtomicStore(BinaryenModuleRef module, + uint32_t bytes, + uint32_t offset, + BinaryenExpressionRef ptr, + BinaryenExpressionRef value, + BinaryenType type); +BinaryenExpressionRef BinaryenAtomicRMW(BinaryenModuleRef module, + BinaryenOp op, + BinaryenIndex bytes, + BinaryenIndex offset, + BinaryenExpressionRef ptr, + BinaryenExpressionRef value, + BinaryenType type); +BinaryenExpressionRef BinaryenAtomicCmpxchg(BinaryenModuleRef module, + BinaryenIndex bytes, + BinaryenIndex offset, + BinaryenExpressionRef ptr, + BinaryenExpressionRef expected, + BinaryenExpressionRef replacement, + BinaryenType type); +BinaryenExpressionRef BinaryenAtomicWait(BinaryenModuleRef module, + BinaryenExpressionRef ptr, + BinaryenExpressionRef expected, + BinaryenExpressionRef timeout, + BinaryenType type); +BinaryenExpressionRef BinaryenAtomicNotify(BinaryenModuleRef module, + BinaryenExpressionRef ptr, + BinaryenExpressionRef notifyCount); +BinaryenExpressionRef BinaryenSIMDExtract(BinaryenModuleRef module, + BinaryenOp op, + BinaryenExpressionRef vec, + uint8_t index); +BinaryenExpressionRef BinaryenSIMDReplace(BinaryenModuleRef module, + BinaryenOp op, + BinaryenExpressionRef vec, + uint8_t index, + BinaryenExpressionRef value); +BinaryenExpressionRef BinaryenSIMDShuffle(BinaryenModuleRef module, + BinaryenExpressionRef left, + BinaryenExpressionRef right, + const uint8_t mask[16]); +BinaryenExpressionRef BinaryenSIMDBitselect(BinaryenModuleRef module, + BinaryenExpressionRef left, + BinaryenExpressionRef right, + BinaryenExpressionRef cond); +BinaryenExpressionRef BinaryenSIMDShift(BinaryenModuleRef module, + BinaryenOp op, + BinaryenExpressionRef vec, + BinaryenExpressionRef shift); +BinaryenExpressionRef BinaryenMemoryInit(BinaryenModuleRef module, + uint32_t segment, + BinaryenExpressionRef dest, + BinaryenExpressionRef offset, + BinaryenExpressionRef size); +BinaryenExpressionRef BinaryenDataDrop(BinaryenModuleRef module, + uint32_t segment); +BinaryenExpressionRef BinaryenMemoryCopy(BinaryenModuleRef module, + BinaryenExpressionRef dest, + BinaryenExpressionRef source, + BinaryenExpressionRef size); +BinaryenExpressionRef BinaryenMemoryFill(BinaryenModuleRef module, + BinaryenExpressionRef dest, + BinaryenExpressionRef value, + BinaryenExpressionRef size); BinaryenExpressionId BinaryenExpressionGetId(BinaryenExpressionRef expr); BinaryenType BinaryenExpressionGetType(BinaryenExpressionRef expr); @@ -555,7 +677,8 @@ void BinaryenExpressionPrint(BinaryenExpressionRef expr); const char* BinaryenBlockGetName(BinaryenExpressionRef expr); BinaryenIndex BinaryenBlockGetNumChildren(BinaryenExpressionRef expr); -BinaryenExpressionRef BinaryenBlockGetChild(BinaryenExpressionRef expr, BinaryenIndex index); +BinaryenExpressionRef BinaryenBlockGetChild(BinaryenExpressionRef expr, + BinaryenIndex index); BinaryenExpressionRef BinaryenIfGetCondition(BinaryenExpressionRef expr); BinaryenExpressionRef BinaryenIfGetIfTrue(BinaryenExpressionRef expr); @@ -569,18 +692,21 @@ BinaryenExpressionRef BinaryenBreakGetCondition(BinaryenExpressionRef expr); BinaryenExpressionRef BinaryenBreakGetValue(BinaryenExpressionRef expr); BinaryenIndex BinaryenSwitchGetNumNames(BinaryenExpressionRef expr); -const char* BinaryenSwitchGetName(BinaryenExpressionRef expr, BinaryenIndex index); +const char* BinaryenSwitchGetName(BinaryenExpressionRef expr, + BinaryenIndex index); const char* BinaryenSwitchGetDefaultName(BinaryenExpressionRef expr); BinaryenExpressionRef BinaryenSwitchGetCondition(BinaryenExpressionRef expr); BinaryenExpressionRef BinaryenSwitchGetValue(BinaryenExpressionRef expr); const char* BinaryenCallGetTarget(BinaryenExpressionRef expr); BinaryenIndex BinaryenCallGetNumOperands(BinaryenExpressionRef expr); -BinaryenExpressionRef BinaryenCallGetOperand(BinaryenExpressionRef expr, BinaryenIndex index); +BinaryenExpressionRef BinaryenCallGetOperand(BinaryenExpressionRef expr, + BinaryenIndex index); BinaryenExpressionRef BinaryenCallIndirectGetTarget(BinaryenExpressionRef expr); BinaryenIndex BinaryenCallIndirectGetNumOperands(BinaryenExpressionRef expr); -BinaryenExpressionRef BinaryenCallIndirectGetOperand(BinaryenExpressionRef expr, BinaryenIndex index); +BinaryenExpressionRef BinaryenCallIndirectGetOperand(BinaryenExpressionRef expr, + BinaryenIndex index); BinaryenIndex BinaryenGetLocalGetIndex(BinaryenExpressionRef expr); @@ -596,7 +722,8 @@ BinaryenExpressionRef BinaryenSetGlobalGetValue(BinaryenExpressionRef expr); BinaryenOp BinaryenHostGetOp(BinaryenExpressionRef expr); const char* BinaryenHostGetNameOperand(BinaryenExpressionRef expr); BinaryenIndex BinaryenHostGetNumOperands(BinaryenExpressionRef expr); -BinaryenExpressionRef BinaryenHostGetOperand(BinaryenExpressionRef expr, BinaryenIndex index); +BinaryenExpressionRef BinaryenHostGetOperand(BinaryenExpressionRef expr, + BinaryenIndex index); int BinaryenLoadIsAtomic(BinaryenExpressionRef expr); int BinaryenLoadIsSigned(BinaryenExpressionRef expr); @@ -644,8 +771,10 @@ BinaryenExpressionRef BinaryenAtomicRMWGetValue(BinaryenExpressionRef expr); uint32_t BinaryenAtomicCmpxchgGetBytes(BinaryenExpressionRef expr); uint32_t BinaryenAtomicCmpxchgGetOffset(BinaryenExpressionRef expr); BinaryenExpressionRef BinaryenAtomicCmpxchgGetPtr(BinaryenExpressionRef expr); -BinaryenExpressionRef BinaryenAtomicCmpxchgGetExpected(BinaryenExpressionRef expr); -BinaryenExpressionRef BinaryenAtomicCmpxchgGetReplacement(BinaryenExpressionRef expr); +BinaryenExpressionRef +BinaryenAtomicCmpxchgGetExpected(BinaryenExpressionRef expr); +BinaryenExpressionRef +BinaryenAtomicCmpxchgGetReplacement(BinaryenExpressionRef expr); BinaryenExpressionRef BinaryenAtomicWaitGetPtr(BinaryenExpressionRef expr); BinaryenExpressionRef BinaryenAtomicWaitGetExpected(BinaryenExpressionRef expr); @@ -653,7 +782,8 @@ BinaryenExpressionRef BinaryenAtomicWaitGetTimeout(BinaryenExpressionRef expr); BinaryenType BinaryenAtomicWaitGetExpectedType(BinaryenExpressionRef expr); BinaryenExpressionRef BinaryenAtomicNotifyGetPtr(BinaryenExpressionRef expr); -BinaryenExpressionRef BinaryenAtomicNotifyGetNotifyCount(BinaryenExpressionRef expr); +BinaryenExpressionRef +BinaryenAtomicNotifyGetNotifyCount(BinaryenExpressionRef expr); BinaryenOp BinaryenSIMDExtractGetOp(BinaryenExpressionRef expr); BinaryenExpressionRef BinaryenSIMDExtractGetVec(BinaryenExpressionRef expr); @@ -666,7 +796,7 @@ BinaryenExpressionRef BinaryenSIMDReplaceGetValue(BinaryenExpressionRef expr); BinaryenExpressionRef BinaryenSIMDShuffleGetLeft(BinaryenExpressionRef expr); BinaryenExpressionRef BinaryenSIMDShuffleGetRight(BinaryenExpressionRef expr); -void BinaryenSIMDShuffleGetMask(BinaryenExpressionRef expr, uint8_t *mask); +void BinaryenSIMDShuffleGetMask(BinaryenExpressionRef expr, uint8_t* mask); BinaryenExpressionRef BinaryenSIMDBitselectGetLeft(BinaryenExpressionRef expr); BinaryenExpressionRef BinaryenSIMDBitselectGetRight(BinaryenExpressionRef expr); @@ -703,48 +833,96 @@ typedef void* BinaryenFunctionRef; // and then vars, so if you have one param it will be at index // 0 (and written $0), and if you also have 2 vars they will be // at indexes 1 and 2, etc., that is, they share an index space. -BinaryenFunctionRef BinaryenAddFunction(BinaryenModuleRef module, const char* name, BinaryenFunctionTypeRef type, BinaryenType* varTypes, BinaryenIndex numVarTypes, BinaryenExpressionRef body); +BinaryenFunctionRef BinaryenAddFunction(BinaryenModuleRef module, + const char* name, + BinaryenFunctionTypeRef type, + BinaryenType* varTypes, + BinaryenIndex numVarTypes, + BinaryenExpressionRef body); // Gets a function reference by name. -BinaryenFunctionRef BinaryenGetFunction(BinaryenModuleRef module, const char* name); +BinaryenFunctionRef BinaryenGetFunction(BinaryenModuleRef module, + const char* name); // Removes a function by name. void BinaryenRemoveFunction(BinaryenModuleRef module, const char* name); // Imports -void BinaryenAddFunctionImport(BinaryenModuleRef module, const char* internalName, const char* externalModuleName, const char* externalBaseName, BinaryenFunctionTypeRef functionType); -void BinaryenAddTableImport(BinaryenModuleRef module, const char* internalName, const char* externalModuleName, const char* externalBaseName); -void BinaryenAddMemoryImport(BinaryenModuleRef module, const char* internalName, const char* externalModuleName, const char* externalBaseName, uint8_t shared); -void BinaryenAddGlobalImport(BinaryenModuleRef module, const char* internalName, const char* externalModuleName, const char* externalBaseName, BinaryenType globalType); +void BinaryenAddFunctionImport(BinaryenModuleRef module, + const char* internalName, + const char* externalModuleName, + const char* externalBaseName, + BinaryenFunctionTypeRef functionType); +void BinaryenAddTableImport(BinaryenModuleRef module, + const char* internalName, + const char* externalModuleName, + const char* externalBaseName); +void BinaryenAddMemoryImport(BinaryenModuleRef module, + const char* internalName, + const char* externalModuleName, + const char* externalBaseName, + uint8_t shared); +void BinaryenAddGlobalImport(BinaryenModuleRef module, + const char* internalName, + const char* externalModuleName, + const char* externalBaseName, + BinaryenType globalType); // Exports typedef void* BinaryenExportRef; -WASM_DEPRECATED BinaryenExportRef BinaryenAddExport(BinaryenModuleRef module, const char* internalName, const char* externalName); -BinaryenExportRef BinaryenAddFunctionExport(BinaryenModuleRef module, const char* internalName, const char* externalName); -BinaryenExportRef BinaryenAddTableExport(BinaryenModuleRef module, const char* internalName, const char* externalName); -BinaryenExportRef BinaryenAddMemoryExport(BinaryenModuleRef module, const char* internalName, const char* externalName); -BinaryenExportRef BinaryenAddGlobalExport(BinaryenModuleRef module, const char* internalName, const char* externalName); +WASM_DEPRECATED BinaryenExportRef BinaryenAddExport(BinaryenModuleRef module, + const char* internalName, + const char* externalName); +BinaryenExportRef BinaryenAddFunctionExport(BinaryenModuleRef module, + const char* internalName, + const char* externalName); +BinaryenExportRef BinaryenAddTableExport(BinaryenModuleRef module, + const char* internalName, + const char* externalName); +BinaryenExportRef BinaryenAddMemoryExport(BinaryenModuleRef module, + const char* internalName, + const char* externalName); +BinaryenExportRef BinaryenAddGlobalExport(BinaryenModuleRef module, + const char* internalName, + const char* externalName); void BinaryenRemoveExport(BinaryenModuleRef module, const char* externalName); // Globals typedef void* BinaryenGlobalRef; -BinaryenGlobalRef BinaryenAddGlobal(BinaryenModuleRef module, const char* name, BinaryenType type, int8_t mutable_, BinaryenExpressionRef init); +BinaryenGlobalRef BinaryenAddGlobal(BinaryenModuleRef module, + const char* name, + BinaryenType type, + int8_t mutable_, + BinaryenExpressionRef init); void BinaryenRemoveGlobal(BinaryenModuleRef module, const char* name); // Function table. One per module -void BinaryenSetFunctionTable(BinaryenModuleRef module, BinaryenIndex initial, BinaryenIndex maximum, const char** funcNames, BinaryenIndex numFuncNames); +void BinaryenSetFunctionTable(BinaryenModuleRef module, + BinaryenIndex initial, + BinaryenIndex maximum, + const char** funcNames, + BinaryenIndex numFuncNames); // Memory. One per module -// Each segment has data in segments, a start offset in segmentOffsets, and a size in segmentSizes. -// exportName can be NULL -void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex initial, BinaryenIndex maximum, const char* exportName, const char** segments, int8_t* segmentPassive, BinaryenExpressionRef* segmentOffsets, BinaryenIndex* segmentSizes, BinaryenIndex numSegments, uint8_t shared); +// Each segment has data in segments, a start offset in segmentOffsets, and a +// size in segmentSizes. exportName can be NULL +void BinaryenSetMemory(BinaryenModuleRef module, + BinaryenIndex initial, + BinaryenIndex maximum, + const char* exportName, + const char** segments, + int8_t* segmentPassive, + BinaryenExpressionRef* segmentOffsets, + BinaryenIndex* segmentSizes, + BinaryenIndex numSegments, + uint8_t shared); // Start function. One per module @@ -797,29 +975,41 @@ void BinaryenSetDebugInfo(int on); // Runs the specified passes on the module. Uses the currently set global // optimize and shrink level. -void BinaryenModuleRunPasses(BinaryenModuleRef module, const char** passes, BinaryenIndex numPasses); +void BinaryenModuleRunPasses(BinaryenModuleRef module, + const char** passes, + BinaryenIndex numPasses); -// Auto-generate drop() operations where needed. This lets you generate code without -// worrying about where they are needed. (It is more efficient to do it yourself, -// but simpler to use autodrop). +// Auto-generate drop() operations where needed. This lets you generate code +// without worrying about where they are needed. (It is more efficient to do it +// yourself, but simpler to use autodrop). void BinaryenModuleAutoDrop(BinaryenModuleRef module); -// Serialize a module into binary form. Uses the currently set global debugInfo option. -// @return how many bytes were written. This will be less than or equal to outputSize -size_t BinaryenModuleWrite(BinaryenModuleRef module, char* output, size_t outputSize); +// Serialize a module into binary form. Uses the currently set global debugInfo +// option. +// @return how many bytes were written. This will be less than or equal to +// outputSize +size_t +BinaryenModuleWrite(BinaryenModuleRef module, char* output, size_t outputSize); typedef struct BinaryenBufferSizes { size_t outputBytes; size_t sourceMapBytes; } BinaryenBufferSizes; -// Serialize a module into binary form including its source map. Uses the currently set -// global debugInfo option. -// @returns how many bytes were written. This will be less than or equal to outputSize -BinaryenBufferSizes BinaryenModuleWriteWithSourceMap(BinaryenModuleRef module, const char* url, char* output, size_t outputSize, char* sourceMap, size_t sourceMapSize); - -// Result structure of BinaryenModuleAllocateAndWrite. Contained buffers have been allocated -// using malloc() and the user is expected to free() them manually once not needed anymore. +// Serialize a module into binary form including its source map. Uses the +// currently set global debugInfo option. +// @returns how many bytes were written. This will be less than or equal to +// outputSize +BinaryenBufferSizes BinaryenModuleWriteWithSourceMap(BinaryenModuleRef module, + const char* url, + char* output, + size_t outputSize, + char* sourceMap, + size_t sourceMapSize); + +// Result structure of BinaryenModuleAllocateAndWrite. Contained buffers have +// been allocated using malloc() and the user is expected to free() them +// manually once not needed anymore. typedef struct BinaryenModuleAllocateAndWriteResult { void* binary; size_t binaryBytes; @@ -827,25 +1017,30 @@ typedef struct BinaryenModuleAllocateAndWriteResult { } BinaryenModuleAllocateAndWriteResult; // Serializes a module into binary form, optionally including its source map if -// sourceMapUrl has been specified. Uses the currently set global debugInfo option. -// Differs from BinaryenModuleWrite in that it implicitly allocates appropriate buffers -// using malloc(), and expects the user to free() them manually once not needed anymore. -BinaryenModuleAllocateAndWriteResult BinaryenModuleAllocateAndWrite(BinaryenModuleRef module, const char* sourceMapUrl); +// sourceMapUrl has been specified. Uses the currently set global debugInfo +// option. Differs from BinaryenModuleWrite in that it implicitly allocates +// appropriate buffers using malloc(), and expects the user to free() them +// manually once not needed anymore. +BinaryenModuleAllocateAndWriteResult +BinaryenModuleAllocateAndWrite(BinaryenModuleRef module, + const char* sourceMapUrl); // Deserialize a module from binary form. BinaryenModuleRef BinaryenModuleRead(char* input, size_t inputSize); // Execute a module in the Binaryen interpreter. This will create an instance of -// the module, run it in the interpreter - which means running the start method - -// and then destroying the instance. +// the module, run it in the interpreter - which means running the start method +// - and then destroying the instance. void BinaryenModuleInterpret(BinaryenModuleRef module); // Adds a debug info file name to the module and returns its index. -BinaryenIndex BinaryenModuleAddDebugInfoFileName(BinaryenModuleRef module, const char* filename); +BinaryenIndex BinaryenModuleAddDebugInfoFileName(BinaryenModuleRef module, + const char* filename); -// Gets the name of the debug info file at the specified index. Returns `NULL` if it -// does not exist. -const char* BinaryenModuleGetDebugInfoFileName(BinaryenModuleRef module, BinaryenIndex index); +// Gets the name of the debug info file at the specified index. Returns `NULL` +// if it does not exist. +const char* BinaryenModuleGetDebugInfoFileName(BinaryenModuleRef module, + BinaryenIndex index); // // ======== FunctionType Operations ======== @@ -855,8 +1050,10 @@ const char* BinaryenModuleGetDebugInfoFileName(BinaryenModuleRef module, Binarye const char* BinaryenFunctionTypeGetName(BinaryenFunctionTypeRef ftype); // Gets the number of parameters of the specified `FunctionType`. BinaryenIndex BinaryenFunctionTypeGetNumParams(BinaryenFunctionTypeRef ftype); -// Gets the type of the parameter at the specified index of the specified `FunctionType`. -BinaryenType BinaryenFunctionTypeGetParam(BinaryenFunctionTypeRef ftype, BinaryenIndex index); +// Gets the type of the parameter at the specified index of the specified +// `FunctionType`. +BinaryenType BinaryenFunctionTypeGetParam(BinaryenFunctionTypeRef ftype, + BinaryenIndex index); // Gets the result type of the specified `FunctionType`. BinaryenType BinaryenFunctionTypeGetResult(BinaryenFunctionTypeRef ftype); @@ -866,31 +1063,45 @@ BinaryenType BinaryenFunctionTypeGetResult(BinaryenFunctionTypeRef ftype); // Gets the name of the specified `Function`. const char* BinaryenFunctionGetName(BinaryenFunctionRef func); -// Gets the name of the `FunctionType` associated with the specified `Function`. May be `NULL` if the signature is implicit. +// Gets the name of the `FunctionType` associated with the specified `Function`. +// May be `NULL` if the signature is implicit. const char* BinaryenFunctionGetType(BinaryenFunctionRef func); // Gets the number of parameters of the specified `Function`. BinaryenIndex BinaryenFunctionGetNumParams(BinaryenFunctionRef func); -// Gets the type of the parameter at the specified index of the specified `Function`. -BinaryenType BinaryenFunctionGetParam(BinaryenFunctionRef func, BinaryenIndex index); +// Gets the type of the parameter at the specified index of the specified +// `Function`. +BinaryenType BinaryenFunctionGetParam(BinaryenFunctionRef func, + BinaryenIndex index); // Gets the result type of the specified `Function`. BinaryenType BinaryenFunctionGetResult(BinaryenFunctionRef func); // Gets the number of additional locals within the specified `Function`. BinaryenIndex BinaryenFunctionGetNumVars(BinaryenFunctionRef func); -// Gets the type of the additional local at the specified index within the specified `Function`. -BinaryenType BinaryenFunctionGetVar(BinaryenFunctionRef func, BinaryenIndex index); +// Gets the type of the additional local at the specified index within the +// specified `Function`. +BinaryenType BinaryenFunctionGetVar(BinaryenFunctionRef func, + BinaryenIndex index); // Gets the body of the specified `Function`. BinaryenExpressionRef BinaryenFunctionGetBody(BinaryenFunctionRef func); // Runs the standard optimization passes on the function. Uses the currently set // global optimize and shrink level. -void BinaryenFunctionOptimize(BinaryenFunctionRef func, BinaryenModuleRef module); +void BinaryenFunctionOptimize(BinaryenFunctionRef func, + BinaryenModuleRef module); // Runs the specified passes on the function. Uses the currently set global // optimize and shrink level. -void BinaryenFunctionRunPasses(BinaryenFunctionRef func, BinaryenModuleRef module, const char** passes, BinaryenIndex numPasses); - -// Sets the debug location of the specified `Expression` within the specified `Function`. -void BinaryenFunctionSetDebugLocation(BinaryenFunctionRef func, BinaryenExpressionRef expr, BinaryenIndex fileIndex, BinaryenIndex lineNumber, BinaryenIndex columnNumber); +void BinaryenFunctionRunPasses(BinaryenFunctionRef func, + BinaryenModuleRef module, + const char** passes, + BinaryenIndex numPasses); + +// Sets the debug location of the specified `Expression` within the specified +// `Function`. +void BinaryenFunctionSetDebugLocation(BinaryenFunctionRef func, + BinaryenExpressionRef expr, + BinaryenIndex fileIndex, + BinaryenIndex lineNumber, + BinaryenIndex columnNumber); // // ========== Import Operations ========== @@ -930,37 +1141,53 @@ typedef void* RelooperBlockRef; RelooperRef RelooperCreate(BinaryenModuleRef module); // Create a basic block that ends with nothing, or with some simple branching -RelooperBlockRef RelooperAddBlock(RelooperRef relooper, BinaryenExpressionRef code); +RelooperBlockRef RelooperAddBlock(RelooperRef relooper, + BinaryenExpressionRef code); // Create a branch to another basic block -// The branch can have code on it, that is executed as the branch happens. this is useful for phis. otherwise, code can be NULL -void RelooperAddBranch(RelooperBlockRef from, RelooperBlockRef to, BinaryenExpressionRef condition, BinaryenExpressionRef code); +// The branch can have code on it, that is executed as the branch happens. this +// is useful for phis. otherwise, code can be NULL +void RelooperAddBranch(RelooperBlockRef from, + RelooperBlockRef to, + BinaryenExpressionRef condition, + BinaryenExpressionRef code); // Create a basic block that ends a switch on a condition -RelooperBlockRef RelooperAddBlockWithSwitch(RelooperRef relooper, BinaryenExpressionRef code, BinaryenExpressionRef condition); - -// Create a switch-style branch to another basic block. The block's switch table will have these indexes going to that target -void RelooperAddBranchForSwitch(RelooperBlockRef from, RelooperBlockRef to, BinaryenIndex* indexes, BinaryenIndex numIndexes, BinaryenExpressionRef code); - -// Generate structed wasm control flow from the CFG of blocks and branches that were created -// on this relooper instance. This returns the rendered output, and also disposes of the -// relooper and its blocks and branches, as they are no longer needed. -// @param labelHelper To render irreducible control flow, we may need a helper variable to -// guide us to the right target label. This value should be an index of -// an i32 local variable that is free for us to use. -BinaryenExpressionRef RelooperRenderAndDispose(RelooperRef relooper, RelooperBlockRef entry, BinaryenIndex labelHelper); +RelooperBlockRef RelooperAddBlockWithSwitch(RelooperRef relooper, + BinaryenExpressionRef code, + BinaryenExpressionRef condition); + +// Create a switch-style branch to another basic block. The block's switch table +// will have these indexes going to that target +void RelooperAddBranchForSwitch(RelooperBlockRef from, + RelooperBlockRef to, + BinaryenIndex* indexes, + BinaryenIndex numIndexes, + BinaryenExpressionRef code); + +// Generate structed wasm control flow from the CFG of blocks and branches that +// were created on this relooper instance. This returns the rendered output, and +// also disposes of the relooper and its blocks and branches, as they are no +// longer needed. +// @param labelHelper To render irreducible control flow, we may need a helper +// variable to guide us to the right target label. This value should be +// an index of an i32 local variable that is free for us to use. +BinaryenExpressionRef RelooperRenderAndDispose(RelooperRef relooper, + RelooperBlockRef entry, + BinaryenIndex labelHelper); // // ========= Other APIs ========= // -// Sets whether API tracing is on or off. It is off by default. When on, each call -// to an API method will print out C code equivalent to it, which is useful for -// auto-generating standalone testcases from projects using the API. -// When calling this to turn on tracing, the prelude of the full program is printed, -// and when calling it to turn it off, the ending of the program is printed, giving -// you the full compilable testcase. -// TODO: compile-time option to enable/disable this feature entirely at build time? +// Sets whether API tracing is on or off. It is off by default. When on, each +// call to an API method will print out C code equivalent to it, which is useful +// for auto-generating standalone testcases from projects using the API. When +// calling this to turn on tracing, the prelude of the full program is printed, +// and when calling it to turn it off, the ending of the program is printed, +// giving you the full compilable testcase. +// TODO: compile-time option to enable/disable this feature entirely at build +// time? void BinaryenSetAPITracing(int on); // @@ -968,11 +1195,15 @@ void BinaryenSetAPITracing(int on); // // Note that this function has been added because there is no better alternative -// currently and is scheduled for removal once there is one. It takes the same set -// of parameters as BinaryenAddFunctionType but instead of adding a new function -// signature, it returns a pointer to the existing signature or NULL if there is no -// such signature yet. -BinaryenFunctionTypeRef BinaryenGetFunctionTypeBySignature(BinaryenModuleRef module, BinaryenType result, BinaryenType* paramTypes, BinaryenIndex numParams); +// currently and is scheduled for removal once there is one. It takes the same +// set of parameters as BinaryenAddFunctionType but instead of adding a new +// function signature, it returns a pointer to the existing signature or NULL if +// there is no such signature yet. +BinaryenFunctionTypeRef +BinaryenGetFunctionTypeBySignature(BinaryenModuleRef module, + BinaryenType result, + BinaryenType* paramTypes, + BinaryenIndex numParams); #ifdef __cplusplus } // extern "C" diff --git a/src/cfg/Relooper.cpp b/src/cfg/Relooper.cpp index 3acdabd52..8a15fe75c 100644 --- a/src/cfg/Relooper.cpp +++ b/src/cfg/Relooper.cpp @@ -16,8 +16,8 @@ #include "Relooper.h" -#include <string.h> #include <stdlib.h> +#include <string.h> #include <list> #include <stack> @@ -29,12 +29,13 @@ namespace CFG { -template<class T, class U> static bool contains(const T& container, const U& contained) { +template<class T, class U> +static bool contains(const T& container, const U& contained) { return !!container.count(contained); } #ifdef RELOOPER_DEBUG -static void PrintDebug(const char *Format, ...); +static void PrintDebug(const char* Format, ...); #define DebugDump(x, ...) Debugging::Dump(x, __VA_ARGS__) #else #define PrintDebug(x, ...) @@ -43,8 +44,12 @@ static void PrintDebug(const char *Format, ...); // Rendering utilities -static wasm::Expression* HandleFollowupMultiples(wasm::Expression* Ret, Shape* Parent, RelooperBuilder& Builder, bool InLoop) { - if (!Parent->Next) return Ret; +static wasm::Expression* HandleFollowupMultiples(wasm::Expression* Ret, + Shape* Parent, + RelooperBuilder& Builder, + bool InLoop) { + if (!Parent->Next) + return Ret; auto* Curr = Ret->dynCast<wasm::Block>(); if (!Curr || Curr->name.is()) { @@ -53,7 +58,8 @@ static wasm::Expression* HandleFollowupMultiples(wasm::Expression* Ret, Shape* P // for each multiple after us, we create a block target for breaks to reach while (Parent->Next) { auto* Multiple = Shape::IsMultiple(Parent->Next); - if (!Multiple) break; + if (!Multiple) + break; for (auto& iter : Multiple->InnerMap) { int Id = iter.first; Shape* Body = iter.second; @@ -66,9 +72,9 @@ static wasm::Expression* HandleFollowupMultiples(wasm::Expression* Ret, Shape* P } Parent->Next = Parent->Next->Next; } - // after the multiples is a simple or a loop, in both cases we must hit an entry - // block, and so this is the last one we need to take into account now (this - // is why we require that loops hit an entry). + // after the multiples is a simple or a loop, in both cases we must hit an + // entry block, and so this is the last one we need to take into account now + // (this is why we require that loops hit an entry). if (Parent->Next) { auto* Simple = Shape::IsSimple(Parent->Next); if (Simple) { @@ -99,19 +105,25 @@ static wasm::Expression* HandleFollowupMultiples(wasm::Expression* Ret, Shape* P // Branch -Branch::Branch(wasm::Expression* ConditionInit, wasm::Expression* CodeInit) : Condition(ConditionInit), Code(CodeInit) {} +Branch::Branch(wasm::Expression* ConditionInit, wasm::Expression* CodeInit) + : Condition(ConditionInit), Code(CodeInit) {} -Branch::Branch(std::vector<wasm::Index>&& ValuesInit, wasm::Expression* CodeInit) : Condition(nullptr), Code(CodeInit) { +Branch::Branch(std::vector<wasm::Index>&& ValuesInit, + wasm::Expression* CodeInit) + : Condition(nullptr), Code(CodeInit) { if (ValuesInit.size() > 0) { SwitchValues = wasm::make_unique<std::vector<wasm::Index>>(ValuesInit); } // otherwise, it is the default } -wasm::Expression* Branch::Render(RelooperBuilder& Builder, Block* Target, bool SetLabel) { +wasm::Expression* +Branch::Render(RelooperBuilder& Builder, Block* Target, bool SetLabel) { auto* Ret = Builder.makeBlock(); - if (Code) Ret->list.push_back(Code); - if (SetLabel) Ret->list.push_back(Builder.makeSetLabel(Target->Id)); + if (Code) + Ret->list.push_back(Code); + if (SetLabel) + Ret->list.push_back(Builder.makeSetLabel(Target->Id)); if (Type == Break) { Ret->list.push_back(Builder.makeBlockBreak(Target->Id)); } else if (Type == Continue) { @@ -124,7 +136,9 @@ wasm::Expression* Branch::Render(RelooperBuilder& Builder, Block* Target, bool S // Block -Block::Block(wasm::Expression* CodeInit, wasm::Expression* SwitchConditionInit) : Code(CodeInit), SwitchCondition(SwitchConditionInit), IsCheckedMultipleEntry(false) {} +Block::Block(wasm::Expression* CodeInit, wasm::Expression* SwitchConditionInit) + : Code(CodeInit), SwitchCondition(SwitchConditionInit), + IsCheckedMultipleEntry(false) {} Block::~Block() { for (auto& iter : ProcessedBranchesOut) { @@ -135,13 +149,19 @@ Block::~Block() { } } -void Block::AddBranchTo(Block* Target, wasm::Expression* Condition, wasm::Expression* Code) { - assert(!contains(BranchesOut, Target)); // cannot add more than one branch to the same target +void Block::AddBranchTo(Block* Target, + wasm::Expression* Condition, + wasm::Expression* Code) { + // cannot add more than one branch to the same target + assert(!contains(BranchesOut, Target)); BranchesOut[Target] = new Branch(Condition, Code); } -void Block::AddSwitchBranchTo(Block* Target, std::vector<wasm::Index>&& Values, wasm::Expression* Code) { - assert(!contains(BranchesOut, Target)); // cannot add more than one branch to the same target +void Block::AddSwitchBranchTo(Block* Target, + std::vector<wasm::Index>&& Values, + wasm::Expression* Code) { + // cannot add more than one branch to the same target + assert(!contains(BranchesOut, Target)); BranchesOut[Target] = new Branch(std::move(Values), Code); } @@ -150,14 +170,16 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) { if (IsCheckedMultipleEntry && InLoop) { Ret->list.push_back(Builder.makeSetLabel(0)); } - if (Code) Ret->list.push_back(Code); + if (Code) + Ret->list.push_back(Code); if (!ProcessedBranchesOut.size()) { Ret->finalize(); return Ret; } - bool SetLabel = true; // in some cases it is clear we can avoid setting label, see later + // in some cases it is clear we can avoid setting label, see later + bool SetLabel = true; // A setting of the label variable (label = x) is necessary if it can // cause an impact. The main case is where we set label to x, then elsewhere @@ -189,31 +211,40 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) { // When the Multiple has the same number of groups as we have branches, // they will all be fused, so it is safe to not set the label at all. // If a switch, then we can have multiple branches to the same target - // (in different table indexes), and so this check is not sufficient TODO: optimize - if (SetLabel && Fused->InnerMap.size() == ProcessedBranchesOut.size() && !SwitchCondition) { + // (in different table indexes), and so this check is not sufficient + // TODO: optimize + if (SetLabel && Fused->InnerMap.size() == ProcessedBranchesOut.size() && + !SwitchCondition) { SetLabel = false; } } - Block* DefaultTarget = nullptr; // The block we branch to without checking the condition, if none of the other conditions held. + // The block we branch to without checking the condition, if none of the other + // conditions held. + Block* DefaultTarget = nullptr; // Find the default target, the one without a condition for (auto& iter : ProcessedBranchesOut) { - if ((!SwitchCondition && !iter.second->Condition) || (SwitchCondition && !iter.second->SwitchValues)) { - assert(!DefaultTarget && "block has branches without a default (nullptr for the condition)"); // Must be exactly one default // nullptr + if ((!SwitchCondition && !iter.second->Condition) || + (SwitchCondition && !iter.second->SwitchValues)) { + assert(!DefaultTarget && + "block has branches without a default (nullptr for the " + "condition)"); // Must be exactly one default // nullptr DefaultTarget = iter.first; } } - assert(DefaultTarget); // Since each Block* must* branch somewhere, this must be set + // Since each Block* must* branch somewhere, this must be set + assert(DefaultTarget); - wasm::Expression* Root = nullptr; // root of the main part, that we are about to emit + // root of the main part, that we are about to emit + wasm::Expression* Root = nullptr; if (!SwitchCondition) { // We'll emit a chain of if-elses wasm::If* CurrIf = nullptr; - // we build an if, then add a child, then add a child to that, etc., so we must - // finalize them in reverse order + // we build an if, then add a child, then add a child to that, etc., so we + // must finalize them in reverse order std::vector<wasm::If*> finalizeStack; wasm::Expression* RemainingConditions = nullptr; @@ -223,9 +254,11 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) { Branch* Details; if (iter != ProcessedBranchesOut.end()) { Target = iter->first; - if (Target == DefaultTarget) continue; // done at the end + if (Target == DefaultTarget) + continue; // done at the end Details = iter->second; - assert(Details->Condition); // must have a condition if this is not the default target + // must have a condition if this is not the default target + assert(Details->Condition); } else { Target = DefaultTarget; Details = ProcessedBranchesOut[DefaultTarget]; @@ -238,10 +271,13 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) { } wasm::Expression* CurrContent = nullptr; bool IsDefault = iter == ProcessedBranchesOut.end(); - if (SetCurrLabel || Details->Type != Branch::Direct || HasFusedContent || Details->Code) { + if (SetCurrLabel || Details->Type != Branch::Direct || HasFusedContent || + Details->Code) { CurrContent = Details->Render(Builder, Target, SetCurrLabel); if (HasFusedContent) { - CurrContent = Builder.blockify(CurrContent, Fused->InnerMap.find(Target->Id)->second->Render(Builder, InLoop)); + CurrContent = Builder.blockify( + CurrContent, + Fused->InnerMap.find(Target->Id)->second->Render(Builder, InLoop)); } } // If there is nothing to show in this branch, omit the condition @@ -276,12 +312,14 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) { } else { auto* Now = Builder.makeUnary(wasm::EqZInt32, Details->Condition); if (RemainingConditions) { - RemainingConditions = Builder.makeBinary(wasm::AndInt32, RemainingConditions, Now); + RemainingConditions = + Builder.makeBinary(wasm::AndInt32, RemainingConditions, Now); } else { RemainingConditions = Now; } } - if (IsDefault) break; + if (IsDefault) + break; } // finalize the if-chains @@ -317,17 +355,21 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) { Details->Type = Branch::Direct; } wasm::Expression* CurrContent = nullptr; - if (SetCurrLabel || Details->Type != Branch::Direct || HasFusedContent || Details->Code) { + if (SetCurrLabel || Details->Type != Branch::Direct || HasFusedContent || + Details->Code) { CurrContent = Details->Render(Builder, Target, SetCurrLabel); if (HasFusedContent) { - CurrContent = Builder.blockify(CurrContent, Fused->InnerMap.find(Target->Id)->second->Render(Builder, InLoop)); + CurrContent = Builder.blockify( + CurrContent, + Fused->InnerMap.find(Target->Id)->second->Render(Builder, InLoop)); } } // generate a block to branch to, if we have content if (CurrContent) { auto* NextOuter = Builder.makeBlock(); NextOuter->list.push_back(Outer); - Outer->name = CurrName; // breaking on Outer leads to the content in NextOuter + // breaking on Outer leads to the content in NextOuter + Outer->name = CurrName; NextOuter->list.push_back(CurrContent); // if this is not a dead end, also need to break to the outside // this is both an optimization, and avoids incorrectness as adding @@ -340,23 +382,27 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) { } else { CurrName = SwitchLeave; // just go out straight from the table if (!Details->SwitchValues) { - // this is the default, and it has no content. So make the default be the leave + // this is the default, and it has no content. So make the default be + // the leave for (auto& Value : Table) { - if (Value == SwitchDefault) Value = SwitchLeave; + if (Value == SwitchDefault) + Value = SwitchLeave; } SwitchDefault = SwitchLeave; } } if (Details->SwitchValues) { for (auto Value : *Details->SwitchValues) { - while (Table.size() <= Value) Table.push_back(SwitchDefault); + while (Table.size() <= Value) + Table.push_back(SwitchDefault); Table[Value] = CurrName; } } } // finish up the whole pattern Outer->name = SwitchLeave; - Inner->list.push_back(Builder.makeSwitch(Table, SwitchDefault, SwitchCondition)); + Inner->list.push_back( + Builder.makeSwitch(Table, SwitchDefault, SwitchCondition)); Root = Outer; } @@ -379,7 +425,6 @@ wasm::Expression* SimpleShape::Render(RelooperBuilder& Builder, bool InLoop) { return Ret; } - // MultipleShape wasm::Expression* MultipleShape::Render(RelooperBuilder& Builder, bool InLoop) { @@ -389,10 +434,8 @@ wasm::Expression* MultipleShape::Render(RelooperBuilder& Builder, bool InLoop) { wasm::If* CurrIf = nullptr; std::vector<wasm::If*> finalizeStack; for (auto& iter : InnerMap) { - auto* Now = Builder.makeIf( - Builder.makeCheckLabel(iter.first), - iter.second->Render(Builder, InLoop) - ); + auto* Now = Builder.makeIf(Builder.makeCheckLabel(iter.first), + iter.second->Render(Builder, InLoop)); finalizeStack.push_back(Now); if (!CurrIf) { FirstIf = CurrIf = Now; @@ -418,7 +461,8 @@ wasm::Expression* MultipleShape::Render(RelooperBuilder& Builder, bool InLoop) { // LoopShape wasm::Expression* LoopShape::Render(RelooperBuilder& Builder, bool InLoop) { - wasm::Expression* Ret = Builder.makeLoop(Builder.getShapeContinueName(Id), Inner->Render(Builder, true)); + wasm::Expression* Ret = Builder.makeLoop(Builder.getShapeContinueName(Id), + Inner->Render(Builder, true)); Ret = HandleFollowupMultiples(Ret, this, Builder, InLoop); if (Next) { Ret = Builder.makeSequence(Ret, Next->Render(Builder, InLoop)); @@ -428,14 +472,16 @@ wasm::Expression* LoopShape::Render(RelooperBuilder& Builder, bool InLoop) { // Relooper -Relooper::Relooper(wasm::Module* ModuleInit) : - Module(ModuleInit), Root(nullptr), MinSize(false), - BlockIdCounter(1), ShapeIdCounter(0) { // block ID 0 is reserved for clearings +Relooper::Relooper(wasm::Module* ModuleInit) + : Module(ModuleInit), Root(nullptr), MinSize(false), BlockIdCounter(1), + ShapeIdCounter(0) { // block ID 0 is reserved for clearings } Relooper::~Relooper() { - for (unsigned i = 0; i < Blocks.size(); i++) delete Blocks[i]; - for (unsigned i = 0; i < Shapes.size(); i++) delete Shapes[i]; + for (unsigned i = 0; i < Blocks.size(); i++) + delete Blocks[i]; + for (unsigned i = 0; i < Shapes.size(); i++) + delete Shapes[i]; } void Relooper::AddBlock(Block* New, int Id) { @@ -462,7 +508,8 @@ struct Liveness : public RelooperRecursor { while (ToInvestigate.size() > 0) { Block* Curr = ToInvestigate.front(); ToInvestigate.pop_front(); - if (contains(Live, Curr)) continue; + if (contains(Live, Curr)) + continue; Live.insert(Curr); for (auto& iter : Curr->BranchesOut) { ToInvestigate.push_back(iter.first); @@ -475,7 +522,8 @@ typedef std::pair<Branch*, Block*> BranchBlock; struct Optimizer : public RelooperRecursor { Optimizer(Relooper* Parent) : RelooperRecursor(Parent) { - // TODO: there are likely some rare but possible O(N^2) cases with this looping + // TODO: there are likely some rare but possible O(N^2) cases with this + // looping bool More = true; #if RELOOPER_OPTIMIZER_DEBUG std::cout << "pre-optimize\n"; @@ -494,9 +542,9 @@ struct Optimizer : public RelooperRecursor { More = MergeEquivalentBranches() || More; More = UnSwitch() || More; More = MergeConsecutiveBlocks() || More; - // TODO: Merge identical blocks. This would avoid taking into account their - // position / how they are reached, which means that the merging - // may add overhead, so we do it carefully: + // TODO: Merge identical blocks. This would avoid taking into account + // their position / how they are reached, which means that the merging may + // add overhead, so we do it carefully: // * Merging a large-enough block is good for size, and we do it // in we are in MinSize mode, which means we can tolerate slightly // slower throughput. @@ -541,12 +589,12 @@ struct Optimizer : public RelooperRecursor { auto* First = Next; auto* Replacement = First; #if RELOOPER_OPTIMIZER_DEBUG - std::cout << " maybeskip from " << Block->Id << " to next=" << Next->Id << '\n'; + std::cout << " maybeskip from " << Block->Id << " to next=" << Next->Id + << '\n'; #endif std::unordered_set<decltype(Replacement)> Seen; while (1) { - if (IsEmpty(Next) && - Next->BranchesOut.size() == 1) { + if (IsEmpty(Next) && Next->BranchesOut.size() == 1) { auto iter = Next->BranchesOut.begin(); Block* NextNext = iter->first; Branch* NextNextBranch = iter->second; @@ -554,13 +602,13 @@ struct Optimizer : public RelooperRecursor { if (!NextNextBranch->Code) { // TODO: handle extra code too // We can skip through! Next = Replacement = NextNext; - // If we've already seen this, stop - it's an infinite loop of empty - // blocks we can skip through. + // If we've already seen this, stop - it's an infinite loop of + // empty blocks we can skip through. if (Seen.count(Replacement)) { - // Stop here. Note that if we started from X and ended up with X once - // more, then Replacement == First and so lower down we will not - // report that we did any work, avoiding an infinite loop due to - // always thinking there is more work to do. + // Stop here. Note that if we started from X and ended up with X + // once more, then Replacement == First and so lower down we + // will not report that we did any work, avoiding an infinite + // loop due to always thinking there is more work to do. break; } else { // Otherwise, keep going. @@ -573,12 +621,14 @@ struct Optimizer : public RelooperRecursor { } if (Replacement != First) { #if RELOOPER_OPTIMIZER_DEBUG - std::cout << " skip to replacement! " << CurrBlock->Id << " -> " << First->Id << " -> " << Replacement->Id << '\n'; + std::cout << " skip to replacement! " << CurrBlock->Id << " -> " + << First->Id << " -> " << Replacement->Id << '\n'; #endif Worked = true; } - // Add a branch to the target (which may be the unchanged original) in the set of new branches. - // If it's a replacement, it may collide, and we need to merge. + // Add a branch to the target (which may be the unchanged original) in + // the set of new branches. If it's a replacement, it may collide, and + // we need to merge. if (NewBranchesOut.count(Replacement)) { #if RELOOPER_OPTIMIZER_DEBUG std::cout << " merge\n"; @@ -588,7 +638,8 @@ struct Optimizer : public RelooperRecursor { NewBranchesOut[Replacement] = NextBranch; } } - CurrBlock->BranchesOut.swap(NewBranchesOut); // FIXME do we leak old unused Branches? + // FIXME do we leak old unused Branches? + CurrBlock->BranchesOut.swap(NewBranchesOut); } return Worked; } @@ -603,7 +654,8 @@ struct Optimizer : public RelooperRecursor { std::cout << "at parent " << ParentBlock->Id << '\n'; #endif if (ParentBlock->BranchesOut.size() >= 2) { - std::unordered_map<wasm::HashType, std::vector<BranchBlock>> HashedBranchesOut; + std::unordered_map<wasm::HashType, std::vector<BranchBlock>> + HashedBranchesOut; std::vector<Block*> BlocksToErase; for (auto& iter : ParentBlock->BranchesOut) { Block* CurrBlock = iter.first; @@ -633,7 +685,8 @@ struct Optimizer : public RelooperRecursor { } #if RELOOPER_OPTIMIZER_DEBUG else { - std::cout << " same hash, but not equiv to " << SiblingBlock->Id << '\n'; + std::cout << " same hash, but not equiv to " + << SiblingBlock->Id << '\n'; } #endif } @@ -672,9 +725,11 @@ struct Optimizer : public RelooperRecursor { wasm::Builder Builder(*Parent->Module); // Merge in code on the branch as well, if any. if (NextBranch->Code) { - CurrBlock->Code = Builder.makeSequence(CurrBlock->Code, NextBranch->Code); + CurrBlock->Code = + Builder.makeSequence(CurrBlock->Code, NextBranch->Code); } - CurrBlock->Code = Builder.makeSequence(CurrBlock->Code, NextBlock->Code); + CurrBlock->Code = + Builder.makeSequence(CurrBlock->Code, NextBlock->Code); // Use the next block's branching behavior CurrBlock->BranchesOut.swap(NextBlock->BranchesOut); NextBlock->BranchesOut.clear(); @@ -694,7 +749,9 @@ struct Optimizer : public RelooperRecursor { bool Worked = false; for (auto* ParentBlock : Parent->Blocks) { #if RELOOPER_OPTIMIZER_DEBUG - std::cout << "un-switching at " << ParentBlock->Id << ' ' << !!ParentBlock->SwitchCondition << ' ' << ParentBlock->BranchesOut.size() << '\n'; + std::cout << "un-switching at " << ParentBlock->Id << ' ' + << !!ParentBlock->SwitchCondition << ' ' + << ParentBlock->BranchesOut.size() << '\n'; #endif if (ParentBlock->SwitchCondition) { if (ParentBlock->BranchesOut.size() <= 1) { @@ -761,24 +818,25 @@ private: SeenUnreachableType = true; } }; - std::function<void (wasm::Block*)> FlattenIntoNewList = [&](wasm::Block* Curr) { - assert(!Curr->name.is()); - for (auto* Item : Curr->list) { - if (auto* Block = Item->dynCast<wasm::Block>()) { - if (Block->name.is()) { - // Leave it whole, it's not a trivial block. - Add(Block); + std::function<void(wasm::Block*)> FlattenIntoNewList = + [&](wasm::Block* Curr) { + assert(!Curr->name.is()); + for (auto* Item : Curr->list) { + if (auto* Block = Item->dynCast<wasm::Block>()) { + if (Block->name.is()) { + // Leave it whole, it's not a trivial block. + Add(Block); + } else { + FlattenIntoNewList(Block); + } } else { - FlattenIntoNewList(Block); + // A random item. + Add(Item); } - } else { - // A random item. - Add(Item); } - } - // All the items have been moved out. - Curr->list.clear(); - }; + // All the items have been moved out. + Curr->list.clear(); + }; FlattenIntoNewList(Outer); assert(Outer->list.empty()); Outer->list.swap(NewList); @@ -831,7 +889,8 @@ private: if (!IsPossibleCodeEquivalent(ABranch->Condition, BBranch->Condition)) { return false; } - if (!IsPossibleUniquePtrEquivalent(ABranch->SwitchValues, BBranch->SwitchValues)) { + if (!IsPossibleUniquePtrEquivalent(ABranch->SwitchValues, + BBranch->SwitchValues)) { return false; } if (!IsPossibleCodeEquivalent(ABranch->Code, BBranch->Code)) { @@ -841,18 +900,25 @@ private: return true; } - // Checks if values referred to by pointers are identical, allowing the code to also be nullptr + // Checks if values referred to by pointers are identical, allowing the code + // to also be nullptr template<typename T> - static bool IsPossibleUniquePtrEquivalent(std::unique_ptr<T>& A, std::unique_ptr<T>& B) { - if (A == B) return true; - if (!A || !B) return false; + static bool IsPossibleUniquePtrEquivalent(std::unique_ptr<T>& A, + std::unique_ptr<T>& B) { + if (A == B) + return true; + if (!A || !B) + return false; return *A == *B; } // Checks if code is equivalent, allowing the code to also be nullptr - static bool IsPossibleCodeEquivalent(wasm::Expression* A, wasm::Expression* B) { - if (A == B) return true; - if (!A || !B) return false; + static bool IsPossibleCodeEquivalent(wasm::Expression* A, + wasm::Expression* B) { + if (A == B) + return true; + if (!A || !B) + return false; return IsCodeEquivalent(A, B); } @@ -873,9 +939,9 @@ private: assert(!Into->Condition); // Merging into the already-default, nothing to do. } else { - Into->SwitchValues->insert( - Into->SwitchValues->end(), - Curr->SwitchValues->begin(), Curr->SwitchValues->end()); + Into->SwitchValues->insert(Into->SwitchValues->end(), + Curr->SwitchValues->begin(), + Curr->SwitchValues->end()); } } else { if (!Curr->Condition) { @@ -888,11 +954,9 @@ private: } else { assert(!Into->SwitchValues); // Merge them, checking both. - Into->Condition = wasm::Builder(*Parent->Module).makeBinary( - wasm::OrInt32, - Into->Condition, - Curr->Condition - ); + Into->Condition = + wasm::Builder(*Parent->Module) + .makeBinary(wasm::OrInt32, Into->Condition, Curr->Condition); } } if (!Curr->Code) { @@ -919,7 +983,8 @@ private: Ret = wasm::rehash(Ret, 2); for (auto& Pair : Curr->BranchesOut) { // Hash the Block* as a pointer TODO: full hash? - Ret = wasm::rehash(Ret, wasm::HashType(reinterpret_cast<size_t>(Pair.first))); + Ret = + wasm::rehash(Ret, wasm::HashType(reinterpret_cast<size_t>(Pair.first))); // Hash the Branch info properly Ret = wasm::rehash(Ret, Hash(Pair.second)); } @@ -960,7 +1025,8 @@ void Relooper::Calculate(Block* Entry) { // Add incoming branches from live blocks, ignoring dead code for (unsigned i = 0; i < Blocks.size(); i++) { Block* Curr = Blocks[i]; - if (!contains(Live.Live, Curr)) continue; + if (!contains(Live.Live, Curr)) + continue; for (auto& iter : Curr->BranchesOut) { iter.first->BranchesIn.insert(Curr); } @@ -977,9 +1043,11 @@ void Relooper::Calculate(Block* Entry) { Parent->Shapes.push_back(New); } - // Create a list of entries from a block. If LimitTo is provided, only results in that set - // will appear - void GetBlocksOut(Block* Source, BlockSet& Entries, BlockSet* LimitTo = nullptr) { + // Create a list of entries from a block. If LimitTo is provided, only + // results in that set will appear + void GetBlocksOut(Block* Source, + BlockSet& Entries, + BlockSet* LimitTo = nullptr) { for (auto& iter : Source->BranchesOut) { if (!LimitTo || contains(*LimitTo, iter.first)) { Entries.insert(iter.first); @@ -988,10 +1056,14 @@ void Relooper::Calculate(Block* Entry) { } // Converts/processes all branchings to a specific target - void Solipsize(Block* Target, Branch::FlowType Type, Shape* Ancestor, BlockSet &From) { + void Solipsize(Block* Target, + Branch::FlowType Type, + Shape* Ancestor, + BlockSet& From) { PrintDebug("Solipsizing branches into %d\n", Target->Id); DebugDump(From, " relevant to solipsize: "); - for (auto iter = Target->BranchesIn.begin(); iter != Target->BranchesIn.end();) { + for (auto iter = Target->BranchesIn.begin(); + iter != Target->BranchesIn.end();) { Block* Prior = *iter; if (!contains(From, Prior)) { iter++; @@ -1009,7 +1081,7 @@ void Relooper::Calculate(Block* Entry) { } } - Shape* MakeSimple(BlockSet &Blocks, Block* Inner, BlockSet &NextEntries) { + Shape* MakeSimple(BlockSet& Blocks, Block* Inner, BlockSet& NextEntries) { PrintDebug("creating simple block with block #%d\n", Inner->Id); SimpleShape* Simple = new SimpleShape; Notice(Simple); @@ -1027,9 +1099,10 @@ void Relooper::Calculate(Block* Entry) { return Simple; } - Shape* MakeLoop(BlockSet &Blocks, BlockSet& Entries, BlockSet &NextEntries) { - // Find the inner blocks in this loop. Proceed backwards from the entries until - // you reach a seen block, collecting as you go. + Shape* + MakeLoop(BlockSet& Blocks, BlockSet& Entries, BlockSet& NextEntries) { + // Find the inner blocks in this loop. Proceed backwards from the entries + // until you reach a seen block, collecting as you go. BlockSet InnerBlocks; BlockSet Queue = Entries; while (Queue.size() > 0) { @@ -1123,12 +1196,14 @@ void Relooper::Calculate(Block* Entry) { LoopShape* Loop = new LoopShape(); Notice(Loop); - // Solipsize the loop, replacing with break/continue and marking branches as Processed (will not affect later calculations) - // A. Branches to the loop entries become a continue to this shape + // Solipsize the loop, replacing with break/continue and marking branches + // as Processed (will not affect later calculations) A. Branches to the + // loop entries become a continue to this shape for (auto* Entry : Entries) { Solipsize(Entry, Branch::Continue, Loop, InnerBlocks); } - // B. Branches to outside the loop (a next entry) become breaks on this shape + // B. Branches to outside the loop (a next entry) become breaks on this + // shape for (auto* Next : NextEntries) { Solipsize(Next, Branch::Break, Loop, InnerBlocks); } @@ -1139,29 +1214,38 @@ void Relooper::Calculate(Block* Entry) { return Loop; } - // For each entry, find the independent group reachable by it. The independent group is - // the entry itself, plus all the blocks it can reach that cannot be directly reached by another entry. Note that we - // ignore directly reaching the entry itself by another entry. + // For each entry, find the independent group reachable by it. The + // independent group is the entry itself, plus all the blocks it can reach + // that cannot be directly reached by another entry. Note that we ignore + // directly reaching the entry itself by another entry. // @param Ignore - previous blocks that are irrelevant - void FindIndependentGroups(BlockSet &Entries, BlockBlockSetMap& IndependentGroups, BlockSet* Ignore = nullptr) { + void FindIndependentGroups(BlockSet& Entries, + BlockBlockSetMap& IndependentGroups, + BlockSet* Ignore = nullptr) { typedef std::map<Block*, Block*> BlockBlockMap; struct HelperClass { BlockBlockSetMap& IndependentGroups; - BlockBlockMap Ownership; // For each block, which entry it belongs to. We have reached it from there. + // For each block, which entry it belongs to. We have reached it from + // there. + BlockBlockMap Ownership; - HelperClass(BlockBlockSetMap& IndependentGroupsInit) : IndependentGroups(IndependentGroupsInit) {} + HelperClass(BlockBlockSetMap& IndependentGroupsInit) + : IndependentGroups(IndependentGroupsInit) {} void InvalidateWithChildren(Block* New) { // TODO: rename New - BlockList ToInvalidate; // Being in the list means you need to be invalidated + // Being in the list means you need to be invalidated + BlockList ToInvalidate; ToInvalidate.push_back(New); while (ToInvalidate.size() > 0) { Block* Invalidatee = ToInvalidate.front(); ToInvalidate.pop_front(); Block* Owner = Ownership[Invalidatee]; - if (contains(IndependentGroups, Owner)) { // Owner may have been invalidated, do not add to IndependentGroups! + // Owner may have been invalidated, do not add to IndependentGroups! + if (contains(IndependentGroups, Owner)) { IndependentGroups[Owner].erase(Invalidatee); } - if (Ownership[Invalidatee]) { // may have been seen before and invalidated already + // may have been seen before and invalidated already + if (Ownership[Invalidatee]) { Ownership[Invalidatee] = nullptr; for (auto& iter : Invalidatee->BranchesOut) { Block* Target = iter.first; @@ -1180,12 +1264,14 @@ void Relooper::Calculate(Block* Entry) { HelperClass Helper(IndependentGroups); // We flow out from each of the entries, simultaneously. - // When we reach a new block, we add it as belonging to the one we got to it from. - // If we reach a new block that is already marked as belonging to someone, it is reachable by - // two entries and is not valid for any of them. Remove it and all it can reach that have been - // visited. - - BlockList Queue; // Being in the queue means we just added this item, and we need to add its children + // When we reach a new block, we add it as belonging to the one we got to + // it from. If we reach a new block that is already marked as belonging to + // someone, it is reachable by two entries and is not valid for any of + // them. Remove it and all it can reach that have been visited. + + // Being in the queue means we just added this item, and we need to add + // its children + BlockList Queue; for (auto* Entry : Entries) { Helper.Ownership[Entry] = Entry; IndependentGroups[Entry].insert(Entry); @@ -1194,8 +1280,12 @@ void Relooper::Calculate(Block* Entry) { while (Queue.size() > 0) { Block* Curr = Queue.front(); Queue.pop_front(); - Block* Owner = Helper.Ownership[Curr]; // Curr must be in the ownership map if we are in the queue - if (!Owner) continue; // we have been invalidated meanwhile after being reached from two entries + // Curr must be in the ownership map if we are in the queue + Block* Owner = Helper.Ownership[Curr]; + if (!Owner) + // we have been invalidated meanwhile after being reached from two + // entries + continue; // Add all children for (auto& iter : Curr->BranchesOut) { Block* New = iter.first; @@ -1208,27 +1298,31 @@ void Relooper::Calculate(Block* Entry) { continue; } Block* NewOwner = Known->second; - if (!NewOwner) continue; // We reached an invalidated node + if (!NewOwner) + continue; // We reached an invalidated node if (NewOwner != Owner) { - // Invalidate this and all reachable that we have seen - we reached this from two locations + // Invalidate this and all reachable that we have seen - we reached + // this from two locations Helper.InvalidateWithChildren(New); } // otherwise, we have the same owner, so do nothing } } - // Having processed all the interesting blocks, we remain with just one potential issue: - // If a->b, and a was invalidated, but then b was later reached by someone else, we must - // invalidate b. To check for this, we go over all elements in the independent groups, - // if an element has a parent which does *not* have the same owner, we must remove it - // and all its children. + // Having processed all the interesting blocks, we remain with just one + // potential issue: If a->b, and a was invalidated, but then b was later + // reached by someone else, we must invalidate b. To check for this, we go + // over all elements in the independent groups, if an element has a parent + // which does *not* have the same owner, we must remove it and all its + // children. for (auto* Entry : Entries) { - BlockSet &CurrGroup = IndependentGroups[Entry]; + BlockSet& CurrGroup = IndependentGroups[Entry]; BlockList ToInvalidate; for (auto* Child : CurrGroup) { for (auto* Parent : Child->BranchesIn) { - if (Ignore && contains(*Ignore, Parent)) continue; + if (Ignore && contains(*Ignore, Parent)) + continue; if (Helper.Ownership[Parent] != Helper.Ownership[Child]) { ToInvalidate.push_back(Child); } @@ -1256,14 +1350,19 @@ void Relooper::Calculate(Block* Entry) { #endif } - Shape* MakeMultiple(BlockSet &Blocks, BlockSet& Entries, BlockBlockSetMap& IndependentGroups, BlockSet &NextEntries, bool IsCheckedMultiple) { - PrintDebug("creating multiple block with %d inner groups\n", IndependentGroups.size()); + Shape* MakeMultiple(BlockSet& Blocks, + BlockSet& Entries, + BlockBlockSetMap& IndependentGroups, + BlockSet& NextEntries, + bool IsCheckedMultiple) { + PrintDebug("creating multiple block with %d inner groups\n", + IndependentGroups.size()); MultipleShape* Multiple = new MultipleShape(); Notice(Multiple); BlockSet CurrEntries; for (auto& iter : IndependentGroups) { Block* CurrEntry = iter.first; - BlockSet &CurrBlocks = iter.second; + BlockSet& CurrBlocks = iter.second; PrintDebug(" multiple group with entry %d:\n", CurrEntry->Id); DebugDump(CurrBlocks, " "); // Create inner block @@ -1273,13 +1372,14 @@ void Relooper::Calculate(Block* Entry) { // Remove the block from the remaining blocks Blocks.erase(CurrInner); // Find new next entries and fix branches to them - for (auto iter = CurrInner->BranchesOut.begin(); iter != CurrInner->BranchesOut.end();) { + for (auto iter = CurrInner->BranchesOut.begin(); + iter != CurrInner->BranchesOut.end();) { Block* CurrTarget = iter->first; auto Next = iter; Next++; if (!contains(CurrBlocks, CurrTarget)) { NextEntries.insert(CurrTarget); - Solipsize(CurrTarget, Branch::Break, Multiple, CurrBlocks); + Solipsize(CurrTarget, Branch::Break, Multiple, CurrBlocks); } iter = Next; // increment carefully because Solipsize can remove us } @@ -1301,10 +1401,12 @@ void Relooper::Calculate(Block* Entry) { // Main function. // Process a set of blocks with specified entries, returns a shape - // The Make* functions receive a NextEntries. If they fill it with data, those are the entries for the - // ->Next block on them, and the blocks are what remains in Blocks (which Make* modify). In this way - // we avoid recursing on Next (imagine a long chain of Simples, if we recursed we could blow the stack). - Shape* Process(BlockSet &Blocks, BlockSet& InitialEntries) { + // The Make* functions receive a NextEntries. If they fill it with data, + // those are the entries for the + // ->Next block on them, and the blocks are what remains in Blocks (which + // Make* modify). In this way we avoid recursing on Next (imagine a long + // chain of Simples, if we recursed we could blow the stack). + Shape* Process(BlockSet& Blocks, BlockSet& InitialEntries) { PrintDebug("Process() called\n", 0); BlockSet* Entries = &InitialEntries; BlockSet TempEntries[2]; @@ -1312,24 +1414,30 @@ void Relooper::Calculate(Block* Entry) { BlockSet* NextEntries; Shape* Ret = nullptr; Shape* Prev = nullptr; - #define Make(call) \ - Shape* Temp = call; \ - if (Prev) Prev->Next = Temp; \ - if (!Ret) Ret = Temp; \ - if (!NextEntries->size()) { PrintDebug("Process() returning\n", 0); return Ret; } \ - Prev = Temp; \ - Entries = NextEntries; \ - continue; +#define Make(call) \ + Shape* Temp = call; \ + if (Prev) \ + Prev->Next = Temp; \ + if (!Ret) \ + Ret = Temp; \ + if (!NextEntries->size()) { \ + PrintDebug("Process() returning\n", 0); \ + return Ret; \ + } \ + Prev = Temp; \ + Entries = NextEntries; \ + continue; while (1) { PrintDebug("Process() running\n", 0); DebugDump(Blocks, " blocks : "); DebugDump(*Entries, " entries: "); - CurrTempIndex = 1-CurrTempIndex; + CurrTempIndex = 1 - CurrTempIndex; NextEntries = &TempEntries[CurrTempIndex]; NextEntries->clear(); - if (Entries->size() == 0) return Ret; + if (Entries->size() == 0) + return Ret; if (Entries->size() == 1) { Block* Curr = *(Entries->begin()); if (Curr->BranchesIn.size() == 0) { @@ -1341,41 +1449,53 @@ void Relooper::Calculate(Block* Entry) { } // More than one entry, try to eliminate through a Multiple groups of - // independent blocks from an entry/ies. It is important to remove through - // multiples as opposed to looping since the former is more performant. + // independent blocks from an entry/ies. It is important to remove + // through multiples as opposed to looping since the former is more + // performant. BlockBlockSetMap IndependentGroups; FindIndependentGroups(*Entries, IndependentGroups); PrintDebug("Independent groups: %d\n", IndependentGroups.size()); if (IndependentGroups.size() > 0) { - // We can handle a group in a multiple if its entry cannot be reached by another group. - // Note that it might be reachable by itself - a loop. But that is fine, we will create - // a loop inside the multiple block, which is both the performant order to do it, and - // preserves the property that a loop will always reach an entry. - for (auto iter = IndependentGroups.begin(); iter != IndependentGroups.end();) { + // We can handle a group in a multiple if its entry cannot be reached + // by another group. Note that it might be reachable by itself - a + // loop. But that is fine, we will create a loop inside the multiple + // block, which is both the performant order to do it, and preserves + // the property that a loop will always reach an entry. + for (auto iter = IndependentGroups.begin(); + iter != IndependentGroups.end();) { Block* Entry = iter->first; - BlockSet &Group = iter->second; + BlockSet& Group = iter->second; auto curr = iter++; // iterate carefully, we may delete - for (auto iterBranch = Entry->BranchesIn.begin(); iterBranch != Entry->BranchesIn.end(); iterBranch++) { + for (auto iterBranch = Entry->BranchesIn.begin(); + iterBranch != Entry->BranchesIn.end(); + iterBranch++) { Block* Origin = *iterBranch; if (!contains(Group, Origin)) { // Reached from outside the group, so we cannot handle this - PrintDebug("Cannot handle group with entry %d because of incoming branch from %d\n", Entry->Id, Origin->Id); + PrintDebug("Cannot handle group with entry %d because of " + "incoming branch from %d\n", + Entry->Id, + Origin->Id); IndependentGroups.erase(curr); break; } } } - // As an optimization, if we have 2 independent groups, and one is a small dead end, we can handle only that dead end. - // The other then becomes a Next - without nesting in the code and recursion in the analysis. + // As an optimization, if we have 2 independent groups, and one is a + // small dead end, we can handle only that dead end. The other then + // becomes a Next - without nesting in the code and recursion in the + // analysis. // TODO: if the larger is the only dead end, handle that too // TODO: handle >2 groups - // TODO: handle not just dead ends, but also that do not branch to the NextEntries. However, must be careful - // there since we create a Next, and that Next can prevent eliminating a break (since we no longer - // naturally reach the same place), which may necessitate a one-time loop, which makes the unnesting - // pointless. + // TODO: handle not just dead ends, but also that do not branch to the + // NextEntries. However, must be careful + // there since we create a Next, and that Next can prevent + // eliminating a break (since we no longer naturally reach the + // same place), which may necessitate a one-time loop, which + // makes the unnesting pointless. if (IndependentGroups.size() == 2) { // Find the smaller one auto iter = IndependentGroups.begin(); @@ -1384,15 +1504,19 @@ void Relooper::Calculate(Block* Entry) { iter++; Block* LargeEntry = iter->first; int LargeSize = iter->second.size(); - if (SmallSize != LargeSize) { // ignore the case where they are identical - keep things symmetrical there + // ignore the case where they are identical - keep things + // symmetrical there + if (SmallSize != LargeSize) { if (SmallSize > LargeSize) { Block* Temp = SmallEntry; SmallEntry = LargeEntry; - LargeEntry = Temp; // Note: we did not flip the Sizes too, they are now invalid. TODO: use the smaller size as a limit? + // Note: we did not flip the Sizes too, they are now invalid. + // TODO: use the smaller size as a limit? + LargeEntry = Temp; } // Check if dead end bool DeadEnd = true; - BlockSet &SmallGroup = IndependentGroups[SmallEntry]; + BlockSet& SmallGroup = IndependentGroups[SmallEntry]; for (auto* Curr : SmallGroup) { for (auto& iter : Curr->BranchesOut) { Block* Target = iter.first; @@ -1401,23 +1525,28 @@ void Relooper::Calculate(Block* Entry) { break; } } - if (!DeadEnd) break; + if (!DeadEnd) + break; } if (DeadEnd) { - PrintDebug("Removing nesting by not handling large group because small group is dead end\n", 0); + PrintDebug("Removing nesting by not handling large group " + "because small group is dead end\n", + 0); IndependentGroups.erase(LargeEntry); } } } - PrintDebug("Handleable independent groups: %d\n", IndependentGroups.size()); + PrintDebug("Handleable independent groups: %d\n", + IndependentGroups.size()); if (IndependentGroups.size() > 0) { // Some groups removable ==> Multiple - // This is a checked multiple if it has an entry that is an entry to this Process call, that is, - // if we can reach it from outside this set of blocks, then we must check the label variable - // to do so. Otherwise, if it is just internal blocks, those can always be jumped to forward, - // without using the label variable + // This is a checked multiple if it has an entry that is an entry to + // this Process call, that is, if we can reach it from outside this + // set of blocks, then we must check the label variable to do so. + // Otherwise, if it is just internal blocks, those can always be + // jumped to forward, without using the label variable bool Checked = false; for (auto* Entry : *Entries) { if (InitialEntries.count(Entry)) { @@ -1425,7 +1554,8 @@ void Relooper::Calculate(Block* Entry) { break; } } - Make(MakeMultiple(Blocks, *Entries, IndependentGroups, *NextEntries, Checked)); + Make(MakeMultiple( + Blocks, *Entries, IndependentGroups, *NextEntries, Checked)); } } // No independent groups, must be loopable ==> Loop @@ -1461,10 +1591,13 @@ wasm::Expression* Relooper::Render(RelooperBuilder& Builder) { #ifdef RELOOPER_DEBUG // Debugging -void Debugging::Dump(Block* Curr, const char *prefix) { - if (prefix) std::cout << prefix << ": "; - std::cout << Curr->Id << " [code " << *Curr->Code << "] [switch? " << !!Curr->SwitchCondition << "]\n"; - for (auto iter2 = Curr->BranchesOut.begin(); iter2 != Curr->BranchesOut.end(); iter2++) { +void Debugging::Dump(Block* Curr, const char* prefix) { + if (prefix) + std::cout << prefix << ": "; + std::cout << Curr->Id << " [code " << *Curr->Code << "] [switch? " + << !!Curr->SwitchCondition << "]\n"; + for (auto iter2 = Curr->BranchesOut.begin(); iter2 != Curr->BranchesOut.end(); + iter2++) { Block* Other = iter2->first; Branch* Br = iter2->second; std::cout << " -> " << Other->Id << ' '; @@ -1479,21 +1612,24 @@ void Debugging::Dump(Block* Curr, const char *prefix) { } else { std::cout << "[default] "; } - if (Br->Code) std::cout << "[phi " << *Br->Code << "] "; + if (Br->Code) + std::cout << "[phi " << *Br->Code << "] "; std::cout << '\n'; } std::cout << '\n'; } -void Debugging::Dump(BlockSet &Blocks, const char *prefix) { - if (prefix) std::cout << prefix << ": "; +void Debugging::Dump(BlockSet& Blocks, const char* prefix) { + if (prefix) + std::cout << prefix << ": "; for (auto* Curr : Blocks) { Dump(Curr); } } -void Debugging::Dump(Shape* S, const char *prefix) { - if (prefix) std::cout << prefix << ": "; +void Debugging::Dump(Shape* S, const char* prefix) { + if (prefix) + std::cout << prefix << ": "; if (!S) { printf(" (null)\n"); return; @@ -1513,7 +1649,7 @@ void Debugging::Dump(Shape* S, const char *prefix) { } } -static void PrintDebug(const char *Format, ...) { +static void PrintDebug(const char* Format, ...) { printf("// "); va_list Args; va_start(Args, Format); diff --git a/src/cfg/Relooper.h b/src/cfg/Relooper.h index 5773721cd..c38cb56b3 100644 --- a/src/cfg/Relooper.h +++ b/src/cfg/Relooper.h @@ -19,12 +19,16 @@ This is an optimized C++ implemention of the Relooper algorithm originally developed as part of Emscripten. This implementation includes optimizations added since the original academic paper [1] was published about it. -[1] Alon Zakai. 2011. Emscripten: an LLVM-to-JavaScript compiler. In Proceedings of the ACM international conference companion on Object oriented programming systems languages and applications companion (SPLASH '11). ACM, New York, NY, USA, 301-312. DOI=10.1145/2048147.2048224 http://doi.acm.org/10.1145/2048147.2048224 +[1] Alon Zakai. 2011. Emscripten: an LLVM-to-JavaScript compiler. In Proceedings +of the ACM international conference companion on Object oriented programming +systems languages and applications companion (SPLASH '11). ACM, New York, NY, +USA, 301-312. DOI=10.1145/2048147.2048224 +http://doi.acm.org/10.1145/2048147.2048224 */ #include <assert.h> -#include <stdio.h> #include <stdarg.h> +#include <stdio.h> #include <stdlib.h> #include <deque> @@ -33,8 +37,8 @@ added since the original academic paper [1] was published about it. #include <memory> #include <set> -#include "wasm.h" #include "wasm-builder.h" +#include "wasm.h" namespace CFG { @@ -42,7 +46,8 @@ class RelooperBuilder : public wasm::Builder { wasm::Index labelHelper; public: - RelooperBuilder(wasm::Module& wasm, wasm::Index labelHelper) : wasm::Builder(wasm), labelHelper(labelHelper) {} + RelooperBuilder(wasm::Module& wasm, wasm::Index labelHelper) + : wasm::Builder(wasm), labelHelper(labelHelper) {} wasm::GetLocal* makeGetLabel() { return makeGetLocal(labelHelper, wasm::i32); @@ -51,15 +56,17 @@ public: return makeSetLocal(labelHelper, makeConst(wasm::Literal(int32_t(value)))); } wasm::Binary* makeCheckLabel(wasm::Index value) { - return makeBinary(wasm::EqInt32, makeGetLabel(), makeConst(wasm::Literal(int32_t(value)))); + return makeBinary( + wasm::EqInt32, makeGetLabel(), makeConst(wasm::Literal(int32_t(value)))); } - // breaks are on blocks, as they can be specific, we make one wasm block per basic block + // breaks are on blocks, as they can be specific, we make one wasm block per + // basic block wasm::Break* makeBlockBreak(int id) { return wasm::Builder::makeBreak(getBlockBreakName(id)); } - // continues are on shapes, as there is one per loop, and if we have more than one - // going there, it is irreducible control flow anyhow + // continues are on shapes, as there is one per loop, and if we have more than + // one going there, it is irreducible control flow anyhow wasm::Break* makeShapeContinue(int id) { return wasm::Builder::makeBreak(getShapeContinueName(id)); } @@ -78,42 +85,49 @@ struct Shape; // Info about a branching from one block to another struct Branch { enum FlowType { - Direct = 0, // We will directly reach the right location through other means, no need for continue or break + Direct = 0, // We will directly reach the right location through other + // means, no need for continue or break Break = 1, Continue = 2 }; - Shape* Ancestor = nullptr; // If not NULL, this shape is the relevant one for purposes of getting to the target block. We break or continue on it - Branch::FlowType Type; // If Ancestor is not NULL, this says whether to break or continue - - // A branch either has a condition expression if the block ends in ifs, or if the block ends in a switch, then a list of indexes, which - // becomes the indexes in the table of the switch. If not a switch, the condition can be any expression (or nullptr for the - // branch taken when no other condition is true) - // A condition must not have side effects, as the Relooper can reorder or eliminate condition checking. - // This must not have side effects. + // If not NULL, this shape is the relevant one for purposes of getting to the + // target block. We break or continue on it + Shape* Ancestor = nullptr; + // If Ancestor is not NULL, this says whether to break or continue + Branch::FlowType Type; + + // A branch either has a condition expression if the block ends in ifs, or if + // the block ends in a switch, then a list of indexes, which becomes the + // indexes in the table of the switch. If not a switch, the condition can be + // any expression (or nullptr for the branch taken when no other condition is + // true) A condition must not have side effects, as the Relooper can reorder + // or eliminate condition checking. This must not have side effects. wasm::Expression* Condition; - // Switches are rare, so have just a pointer for their values. This contains the values - // for which the branch will be taken, or for the default it is simply not present. + // Switches are rare, so have just a pointer for their values. This contains + // the values for which the branch will be taken, or for the default it is + // simply not present. std::unique_ptr<std::vector<wasm::Index>> SwitchValues; - // If provided, code that is run right before the branch is taken. This is useful for phis. + // If provided, code that is run right before the branch is taken. This is + // useful for phis. wasm::Expression* Code; Branch(wasm::Expression* ConditionInit, wasm::Expression* CodeInit = nullptr); - Branch(std::vector<wasm::Index>&& ValuesInit, wasm::Expression* CodeInit = nullptr); + Branch(std::vector<wasm::Index>&& ValuesInit, + wasm::Expression* CodeInit = nullptr); // Emits code for branch - wasm::Expression* Render(RelooperBuilder& Builder, Block* Target, bool SetLabel); + wasm::Expression* + Render(RelooperBuilder& Builder, Block* Target, bool SetLabel); }; // like std::set, except that begin() -> end() iterates in the // order that elements were added to the set (not in the order // of operator<(T, T)) -template<typename T> -struct InsertOrderedSet -{ - std::map<T, typename std::list<T>::iterator> Map; - std::list<T> List; +template<typename T> struct InsertOrderedSet { + std::map<T, typename std::list<T>::iterator> Map; + std::list<T> List; typedef typename std::list<T>::iterator iterator; iterator begin() { return List.begin(); } @@ -152,9 +166,7 @@ struct InsertOrderedSet size_t count(const T& val) const { return Map.count(val); } InsertOrderedSet() = default; - InsertOrderedSet(const InsertOrderedSet& other) { - *this = other; - } + InsertOrderedSet(const InsertOrderedSet& other) { *this = other; } InsertOrderedSet& operator=(const InsertOrderedSet& other) { clear(); for (auto i : other.List) { @@ -167,11 +179,9 @@ struct InsertOrderedSet // like std::map, except that begin() -> end() iterates in the // order that elements were added to the map (not in the order // of operator<(Key, Key)) -template<typename Key, typename T> -struct InsertOrderedMap -{ - std::map<Key, typename std::list<std::pair<Key,T>>::iterator> Map; - std::list<std::pair<Key,T>> List; +template<typename Key, typename T> struct InsertOrderedMap { + std::map<Key, typename std::list<std::pair<Key, T>>::iterator> Map; + std::list<std::pair<Key, T>> List; T& operator[](const Key& k) { auto it = Map.find(k); @@ -184,7 +194,7 @@ struct InsertOrderedMap return it->second->second; } - typedef typename std::list<std::pair<Key,T>>::iterator iterator; + typedef typename std::list<std::pair<Key, T>>::iterator iterator; iterator begin() { return List.begin(); } iterator end() { return List.end(); } @@ -196,9 +206,7 @@ struct InsertOrderedMap } } - void erase(iterator position) { - erase(position->first); - } + void erase(iterator position) { erase(position->first); } void clear() { Map.clear(); @@ -224,50 +232,61 @@ struct InsertOrderedMap bool operator==(const InsertOrderedMap& other) { return Map == other.Map && List == other.List; } - bool operator!=(const InsertOrderedMap& other) { - return !(*this == other); - } + bool operator!=(const InsertOrderedMap& other) { return !(*this == other); } }; - typedef InsertOrderedSet<Block*> BlockSet; typedef InsertOrderedMap<Block*, Branch*> BlockBranchMap; // Represents a basic block of code - some instructions that end with a // control flow modifier (a branch, return or throw). struct Block { - // Branches become processed after we finish the shape relevant to them. For example, - // when we recreate a loop, branches to the loop start become continues and are now - // processed. When we calculate what shape to generate from a set of blocks, we ignore - // processed branches. - // Blocks own the Branch objects they use, and destroy them when done. + // Branches become processed after we finish the shape relevant to them. For + // example, when we recreate a loop, branches to the loop start become + // continues and are now processed. When we calculate what shape to generate + // from a set of blocks, we ignore processed branches. Blocks own the Branch + // objects they use, and destroy them when done. BlockBranchMap BranchesOut; BlockSet BranchesIn; BlockBranchMap ProcessedBranchesOut; BlockSet ProcessedBranchesIn; Shape* Parent = nullptr; // The shape we are directly inside int Id = -1; // A unique identifier, defined when added to relooper - wasm::Expression* Code; // The code in this block. This can be arbitrary wasm code, including internal control flow, it should just not branch to the outside - wasm::Expression* SwitchCondition; // If nullptr, then this block ends in ifs (or nothing). otherwise, this block ends in a switch, done on this condition - bool IsCheckedMultipleEntry; // If true, we are a multiple entry, so reaching us requires setting the label variable - - Block(wasm::Expression* CodeInit, wasm::Expression* SwitchConditionInit = nullptr); + // The code in this block. This can be arbitrary wasm code, including internal + // control flow, it should just not branch to the outside + wasm::Expression* Code; + // If nullptr, then this block ends in ifs (or nothing). otherwise, this block + // ends in a switch, done on this condition + wasm::Expression* SwitchCondition; + // If true, we are a multiple entry, so reaching us requires setting the label + // variable + bool IsCheckedMultipleEntry; + + Block(wasm::Expression* CodeInit, + wasm::Expression* SwitchConditionInit = nullptr); ~Block(); - // Add a branch: if the condition holds we branch (or if null, we branch if all others failed) - // Note that there can be only one branch from A to B (if you need multiple conditions for the branch, - // create a more interesting expression in the Condition). - // If a Block has no outgoing branches, the contents in Code must contain a terminating - // instruction, as the relooper doesn't know whether you want control flow to stop with - // an `unreachable` or a `return` or something else (if you forget to do this, control - // flow may continue into the block that happens to be emitted right after it). - // Internally, adding a branch only adds the outgoing branch. The matching incoming branch - // on the target is added by the Relooper itself as it works. - void AddBranchTo(Block* Target, wasm::Expression* Condition, wasm::Expression* Code = nullptr); - - // Add a switch branch: if the switch condition is one of these values, we branch (or if the list is empty, we are the default) - // Note that there can be only one branch from A to B (if you need multiple values for the branch, that's what the array and default are for). - void AddSwitchBranchTo(Block* Target, std::vector<wasm::Index>&& Values, wasm::Expression* Code = nullptr); + // Add a branch: if the condition holds we branch (or if null, we branch if + // all others failed) Note that there can be only one branch from A to B (if + // you need multiple conditions for the branch, create a more interesting + // expression in the Condition). If a Block has no outgoing branches, the + // contents in Code must contain a terminating instruction, as the relooper + // doesn't know whether you want control flow to stop with an `unreachable` or + // a `return` or something else (if you forget to do this, control flow may + // continue into the block that happens to be emitted right after it). + // Internally, adding a branch only adds the outgoing branch. The matching + // incoming branch on the target is added by the Relooper itself as it works. + void AddBranchTo(Block* Target, + wasm::Expression* Condition, + wasm::Expression* Code = nullptr); + + // Add a switch branch: if the switch condition is one of these values, we + // branch (or if the list is empty, we are the default) Note that there can be + // only one branch from A to B (if you need multiple values for the branch, + // that's what the array and default are for). + void AddSwitchBranchTo(Block* Target, + std::vector<wasm::Index>&& Values, + wasm::Expression* Code = nullptr); // Emit code for the block, including its contents and branchings out wasm::Expression* Render(RelooperBuilder& Builder, bool InLoop); @@ -296,15 +315,16 @@ struct MultipleShape; struct LoopShape; struct Shape { - int Id = -1; // A unique identifier. Used to identify loops, labels are Lx where x is the Id. Defined when added to relooper - Shape* Next = nullptr; // The shape that will appear in the code right after this one - Shape* Natural; // The shape that control flow gets to naturally (if there is Next, then this is Next) - - enum ShapeType { - Simple, - Multiple, - Loop - }; + // A unique identifier. Used to identify loops, labels are Lx where x is the + // Id. Defined when added to relooper + int Id = -1; + // The shape that will appear in the code right after this one + Shape* Next = nullptr; + // The shape that control flow gets to naturally (if there is Next, then this + // is Next) + Shape* Natural; + + enum ShapeType { Simple, Multiple, Loop }; ShapeType Type; Shape(ShapeType TypeInit) : Type(TypeInit) {} @@ -312,9 +332,15 @@ struct Shape { virtual wasm::Expression* Render(RelooperBuilder& Builder, bool InLoop) = 0; - static SimpleShape* IsSimple(Shape* It) { return It && It->Type == Simple ? (SimpleShape*)It : NULL; } - static MultipleShape* IsMultiple(Shape* It) { return It && It->Type == Multiple ? (MultipleShape*)It : NULL; } - static LoopShape* IsLoop(Shape* It) { return It && It->Type == Loop ? (LoopShape*)It : NULL; } + static SimpleShape* IsSimple(Shape* It) { + return It && It->Type == Simple ? (SimpleShape*)It : NULL; + } + static MultipleShape* IsMultiple(Shape* It) { + return It && It->Type == Multiple ? (MultipleShape*)It : NULL; + } + static LoopShape* IsLoop(Shape* It) { + return It && It->Type == Loop ? (LoopShape*)It : NULL; + } }; struct SimpleShape : public Shape { @@ -366,7 +392,7 @@ struct Relooper { Relooper(wasm::Module* ModuleInit); ~Relooper(); - void AddBlock(Block* New, int Id=-1); + void AddBlock(Block* New, int Id = -1); // Calculates the shapes void Calculate(Block* Entry); @@ -382,9 +408,9 @@ typedef InsertOrderedMap<Block*, BlockSet> BlockBlockSetMap; #ifdef RELOOPER_DEBUG struct Debugging { - static void Dump(Block* Curr, const char *prefix=NULL); - static void Dump(BlockSet &Blocks, const char *prefix=NULL); - static void Dump(Shape* S, const char *prefix=NULL); + static void Dump(Block* Curr, const char* prefix = NULL); + static void Dump(BlockSet& Blocks, const char* prefix = NULL); + static void Dump(Shape* S, const char* prefix = NULL); }; #endif diff --git a/src/cfg/cfg-traversal.h b/src/cfg/cfg-traversal.h index 73463eaa3..abaf63c5b 100644 --- a/src/cfg/cfg-traversal.h +++ b/src/cfg/cfg-traversal.h @@ -30,8 +30,8 @@ #ifndef cfg_traversal_h #define cfg_traversal_h -#include "wasm.h" #include "wasm-traversal.h" +#include "wasm.h" namespace wasm { @@ -47,21 +47,23 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> { BasicBlock* entry; // the entry block - BasicBlock* makeBasicBlock() { // override this with code to create a BasicBlock if necessary - return new BasicBlock(); - } + // override this with code to create a BasicBlock if necessary + BasicBlock* makeBasicBlock() { return new BasicBlock(); } // internal details std::vector<std::unique_ptr<BasicBlock>> basicBlocks; // all the blocks - std::vector<BasicBlock*> loopTops; // blocks that are the tops of loops, i.e., have backedges to them + // blocks that are the tops of loops, i.e., have backedges to them + std::vector<BasicBlock*> loopTops; // traversal state - BasicBlock* currBasicBlock; // the current block in play during traversal. can be nullptr if unreachable, - // but note that we don't do a deep unreachability analysis - just enough - // to avoid constructing obviously-unreachable blocks (we do a full reachability - // analysis on the CFG once it is constructed). - std::map<Expression*, std::vector<BasicBlock*>> branches; // a block or loop => its branches + // the current block in play during traversal. can be nullptr if unreachable, + // but note that we don't do a deep unreachability analysis - just enough to + // avoid constructing obviously-unreachable blocks (we do a full reachability + // analysis on the CFG once it is constructed). + BasicBlock* currBasicBlock; + // a block or loop => its branches + std::map<Expression*, std::vector<BasicBlock*>> branches; std::vector<BasicBlock*> ifStack; std::vector<BasicBlock*> loopStack; @@ -70,27 +72,29 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> { basicBlocks.push_back(std::unique_ptr<BasicBlock>(currBasicBlock)); } - void startUnreachableBlock() { - currBasicBlock = nullptr; - } + void startUnreachableBlock() { currBasicBlock = nullptr; } static void doStartUnreachableBlock(SubType* self, Expression** currp) { self->startUnreachableBlock(); } void link(BasicBlock* from, BasicBlock* to) { - if (!from || !to) return; // if one of them is not reachable, ignore + if (!from || !to) + return; // if one of them is not reachable, ignore from->out.push_back(to); to->in.push_back(from); } static void doEndBlock(SubType* self, Expression** currp) { auto* curr = (*currp)->cast<Block>(); - if (!curr->name.is()) return; + if (!curr->name.is()) + return; auto iter = self->branches.find(curr); - if (iter == self->branches.end()) return; + if (iter == self->branches.end()) + return; auto& origins = iter->second; - if (origins.size() == 0) return; + if (origins.size() == 0) + return; // we have branches to here, so we need a new block auto* last = self->currBasicBlock; self->startBasicBlock(); @@ -106,19 +110,22 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> { auto* last = self->currBasicBlock; self->startBasicBlock(); self->link(last, self->currBasicBlock); // ifTrue - self->ifStack.push_back(last); // the block before the ifTrue + self->ifStack.push_back(last); // the block before the ifTrue } static void doStartIfFalse(SubType* self, Expression** currp) { self->ifStack.push_back(self->currBasicBlock); // the ifTrue fallthrough self->startBasicBlock(); - self->link(self->ifStack[self->ifStack.size() - 2], self->currBasicBlock); // before if -> ifFalse + self->link(self->ifStack[self->ifStack.size() - 2], + self->currBasicBlock); // before if -> ifFalse } static void doEndIf(SubType* self, Expression** currp) { auto* last = self->currBasicBlock; self->startBasicBlock(); - self->link(last, self->currBasicBlock); // last one is ifFalse's fallthrough if there was one, otherwise it's the ifTrue fallthrough + // last one is ifFalse's fallthrough if there was one, otherwise it's the + // ifTrue fallthrough + self->link(last, self->currBasicBlock); if ((*currp)->cast<If>()->ifFalse) { // we just linked ifFalse, need to link ifTrue to the end self->link(self->ifStack.back(), self->currBasicBlock); @@ -133,7 +140,8 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> { static void doStartLoop(SubType* self, Expression** currp) { auto* last = self->currBasicBlock; self->startBasicBlock(); - self->loopTops.push_back(self->currBasicBlock); // a loop with no backedges would still be counted here, but oh well + // a loop with no backedges would still be counted here, but oh well + self->loopTops.push_back(self->currBasicBlock); self->link(last, self->currBasicBlock); self->loopStack.push_back(self->currBasicBlock); } @@ -157,7 +165,8 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> { static void doEndBreak(SubType* self, Expression** currp) { auto* curr = (*currp)->cast<Break>(); - self->branches[self->findBreakTarget(curr->name)].push_back(self->currBasicBlock); // branch to the target + self->branches[self->findBreakTarget(curr->name)].push_back( + self->currBasicBlock); // branch to the target if (curr->condition) { auto* last = self->currBasicBlock; self->startBasicBlock(); @@ -169,15 +178,18 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> { static void doEndSwitch(SubType* self, Expression** currp) { auto* curr = (*currp)->cast<Switch>(); - std::set<Name> seen; // we might see the same label more than once; do not spam branches + // we might see the same label more than once; do not spam branches + std::set<Name> seen; for (Name target : curr->targets) { if (!seen.count(target)) { - self->branches[self->findBreakTarget(target)].push_back(self->currBasicBlock); // branch to the target + self->branches[self->findBreakTarget(target)].push_back( + self->currBasicBlock); // branch to the target seen.insert(target); } } if (!seen.count(curr->default_)) { - self->branches[self->findBreakTarget(curr->default_)].push_back(self->currBasicBlock); // branch to the target + self->branches[self->findBreakTarget(curr->default_)].push_back( + self->currBasicBlock); // branch to the target } self->startUnreachableBlock(); } @@ -258,7 +270,8 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> { queue.erase(iter); alive.insert(curr); for (auto* out : curr->out) { - if (!alive.count(out)) queue.insert(out); + if (!alive.count(out)) + queue.insert(out); } } return alive; @@ -271,21 +284,29 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> { block->out.clear(); continue; } - block->in.erase(std::remove_if(block->in.begin(), block->in.end(), [&alive](BasicBlock* other) { - return !alive.count(other); - }), block->in.end()); - block->out.erase(std::remove_if(block->out.begin(), block->out.end(), [&alive](BasicBlock* other) { - return !alive.count(other); - }), block->out.end()); + block->in.erase(std::remove_if(block->in.begin(), + block->in.end(), + [&alive](BasicBlock* other) { + return !alive.count(other); + }), + block->in.end()); + block->out.erase(std::remove_if(block->out.begin(), + block->out.end(), + [&alive](BasicBlock* other) { + return !alive.count(other); + }), + block->out.end()); } } - // TODO: utility method for optimizing cfg, removing empty blocks depending on their .content + // TODO: utility method for optimizing cfg, removing empty blocks depending on + // their .content std::map<BasicBlock*, size_t> debugIds; void generateDebugIds() { - if (debugIds.size() > 0) return; + if (debugIds.size() > 0) + return; for (auto& block : basicBlocks) { debugIds[block.get()] = debugIds.size(); } @@ -300,12 +321,14 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> { block->contents.dump(static_cast<SubType*>(this)->getFunction()); for (auto& in : block->in) { assert(debugIds.count(in) > 0); - assert(std::find(in->out.begin(), in->out.end(), block.get()) != in->out.end()); // must be a parallel link back + assert(std::find(in->out.begin(), in->out.end(), block.get()) != + in->out.end()); // must be a parallel link back } for (auto& out : block->out) { assert(debugIds.count(out) > 0); std::cout << " out: " << debugIds[out] << "\n"; - assert(std::find(out->in.begin(), out->in.end(), block.get()) != out->in.end()); // must be a parallel link back + assert(std::find(out->in.begin(), out->in.end(), block.get()) != + out->in.end()); // must be a parallel link back } checkDuplicates(block->in); checkDuplicates(block->out); diff --git a/src/cfg/liveness-traversal.h b/src/cfg/liveness-traversal.h index edbe52fd6..b26898460 100644 --- a/src/cfg/liveness-traversal.h +++ b/src/cfg/liveness-traversal.h @@ -21,12 +21,12 @@ #ifndef liveness_traversal_h #define liveness_traversal_h +#include "cfg-traversal.h" +#include "ir/utils.h" #include "support/sorted_vector.h" -#include "wasm.h" #include "wasm-builder.h" #include "wasm-traversal.h" -#include "cfg-traversal.h" -#include "ir/utils.h" +#include "wasm.h" namespace wasm { @@ -41,20 +41,19 @@ typedef SortedVector LocalSet; // "other" which can be used for other purposes, to mark // their position in a block struct LivenessAction { - enum What { - Get = 0, - Set = 1, - Other = 2 - }; + enum What { Get = 0, Set = 1, Other = 2 }; What what; - Index index; // the local index read or written + Index index; // the local index read or written Expression** origin; // the origin bool effective; // whether a store is actually effective, i.e., may be read - LivenessAction(What what, Index index, Expression** origin) : what(what), index(index), origin(origin), effective(false) { + LivenessAction(What what, Index index, Expression** origin) + : what(what), index(index), origin(origin), effective(false) { assert(what != Other); - if (what == Get) assert((*origin)->is<GetLocal>()); - if (what == Set) assert((*origin)->is<SetLocal>()); + if (what == Get) + assert((*origin)->is<GetLocal>()); + if (what == Set) + assert((*origin)->is<SetLocal>()); } LivenessAction(Expression** origin) : what(Other), origin(origin) {} @@ -80,15 +79,18 @@ struct LivenessAction { // information about liveness in a basic block struct Liveness { - LocalSet start, end; // live locals at the start and end + LocalSet start, end; // live locals at the start and end std::vector<LivenessAction> actions; // actions occurring in this block #if LIVENESS_DEBUG void dump(Function* func) { - if (actions.empty()) return; + if (actions.empty()) + return; std::cout << " actions:\n"; for (auto& action : actions) { - std::cout << " " << (action.isGet() ? "get" : (action.isSet() ? "set" : "other")) << " " << func->getLocalName(action.index) << "\n"; + std::cout << " " + << (action.isGet() ? "get" : (action.isSet() ? "set" : "other")) + << " " << func->getLocalName(action.index) << "\n"; } } #endif // LIVENESS_DEBUG @@ -96,28 +98,35 @@ struct Liveness { template<typename SubType, typename VisitorType> struct LivenessWalker : public CFGWalker<SubType, VisitorType, Liveness> { - typedef typename CFGWalker<SubType, VisitorType, Liveness>::BasicBlock BasicBlock; + typedef + typename CFGWalker<SubType, VisitorType, Liveness>::BasicBlock BasicBlock; Index numLocals; std::unordered_set<BasicBlock*> liveBlocks; - std::vector<uint8_t> copies; // canonicalized - accesses should check (low, high) TODO: use a map for high N, as this tends to be sparse? or don't look at copies at all for big N? - std::vector<Index> totalCopies; // total # of copies for each local, with all others + // canonicalized - accesses should check (low, high) + // TODO: use a map for high N, as this tends to be sparse? or don't look at + // copies at all for big N? + std::vector<uint8_t> copies; + // total # of copies for each local, with all others + std::vector<Index> totalCopies; // cfg traversal work static void doVisitGetLocal(SubType* self, Expression** currp) { auto* curr = (*currp)->cast<GetLocal>(); - // if in unreachable code, ignore + // if in unreachable code, ignore if (!self->currBasicBlock) { *currp = Builder(*self->getModule()).replaceWithIdenticalType(curr); return; } - self->currBasicBlock->contents.actions.emplace_back(LivenessAction::Get, curr->index, currp); + self->currBasicBlock->contents.actions.emplace_back( + LivenessAction::Get, curr->index, currp); } static void doVisitSetLocal(SubType* self, Expression** currp) { auto* curr = (*currp)->cast<SetLocal>(); - // if in unreachable code, we don't need the tee (but might need the value, if it has side effects) + // if in unreachable code, we don't need the tee (but might need the value, + // if it has side effects) if (!self->currBasicBlock) { if (curr->isTee()) { *currp = curr->value; @@ -126,10 +135,12 @@ struct LivenessWalker : public CFGWalker<SubType, VisitorType, Liveness> { } return; } - self->currBasicBlock->contents.actions.emplace_back(LivenessAction::Set, curr->index, currp); + self->currBasicBlock->contents.actions.emplace_back( + LivenessAction::Set, curr->index, currp); // if this is a copy, note it if (auto* get = self->getCopy(curr)) { - // add 2 units, so that backedge prioritization can decide ties, but not much more + // add 2 units, so that backedge prioritization can decide ties, but not + // much more self->addCopy(curr->index, get->index); self->addCopy(curr->index, get->index); } @@ -137,16 +148,19 @@ struct LivenessWalker : public CFGWalker<SubType, VisitorType, Liveness> { // A simple copy is a set of a get. A more interesting copy // is a set of an if with a value, where one side a get. - // That can happen when we create an if value in simplify-locals. TODO: recurse into - // nested ifs, and block return values? Those cases are trickier, need to - // count to see if worth it. + // That can happen when we create an if value in simplify-locals. TODO: + // recurse into nested ifs, and block return values? Those cases are trickier, + // need to count to see if worth it. // TODO: an if can have two copies GetLocal* getCopy(SetLocal* set) { - if (auto* get = set->value->dynCast<GetLocal>()) return get; + if (auto* get = set->value->dynCast<GetLocal>()) + return get; if (auto* iff = set->value->dynCast<If>()) { - if (auto* get = iff->ifTrue->dynCast<GetLocal>()) return get; + if (auto* get = iff->ifTrue->dynCast<GetLocal>()) + return get; if (iff->ifFalse) { - if (auto* get = iff->ifFalse->dynCast<GetLocal>()) return get; + if (auto* get = iff->ifFalse->dynCast<GetLocal>()) + return get; } } return nullptr; @@ -162,7 +176,8 @@ struct LivenessWalker : public CFGWalker<SubType, VisitorType, Liveness> { std::fill(totalCopies.begin(), totalCopies.end(), 0); // create the CFG by walking the IR CFGWalker<SubType, VisitorType, Liveness>::doWalkFunction(func); - // ignore links to dead blocks, so they don't confuse us and we can see their stores are all ineffective + // ignore links to dead blocks, so they don't confuse us and we can see + // their stores are all ineffective liveBlocks = CFGWalker<SubType, VisitorType, Liveness>::findLiveBlocks(); CFGWalker<SubType, VisitorType, Liveness>::unlinkDeadBlocks(liveBlocks); // flow liveness across blocks @@ -173,24 +188,30 @@ struct LivenessWalker : public CFGWalker<SubType, VisitorType, Liveness> { // keep working while stuff is flowing std::unordered_set<BasicBlock*> queue; for (auto& curr : CFGWalker<SubType, VisitorType, Liveness>::basicBlocks) { - if (liveBlocks.count(curr.get()) == 0) continue; // ignore dead blocks + if (liveBlocks.count(curr.get()) == 0) + continue; // ignore dead blocks queue.insert(curr.get()); - // do the first scan through the block, starting with nothing live at the end, and updating the liveness at the start + // do the first scan through the block, starting with nothing live at the + // end, and updating the liveness at the start scanLivenessThroughActions(curr->contents.actions, curr->contents.start); } - // at every point in time, we assume we already noted interferences between things already known alive at the end, and scanned back through the block using that + // at every point in time, we assume we already noted interferences between + // things already known alive at the end, and scanned back through the block + // using that while (queue.size() > 0) { auto iter = queue.begin(); auto* curr = *iter; queue.erase(iter); LocalSet live; - if (!mergeStartsAndCheckChange(curr->out, curr->contents.end, live)) continue; + if (!mergeStartsAndCheckChange(curr->out, curr->contents.end, live)) + continue; assert(curr->contents.end.size() < live.size()); curr->contents.end = live; scanLivenessThroughActions(curr->contents.actions, live); // liveness is now calculated at the start. if something // changed, all predecessor blocks need recomputation - if (curr->contents.start == live) continue; + if (curr->contents.start == live) + continue; assert(curr->contents.start.size() < live.size()); curr->contents.start = live; for (auto* in : curr->in) { @@ -200,9 +221,13 @@ struct LivenessWalker : public CFGWalker<SubType, VisitorType, Liveness> { } // merge starts of a list of blocks. return - // whether anything changed vs an old state (which indicates further processing is necessary). - bool mergeStartsAndCheckChange(std::vector<BasicBlock*>& blocks, LocalSet& old, LocalSet& ret) { - if (blocks.size() == 0) return false; + // whether anything changed vs an old state (which indicates further + // processing is necessary). + bool mergeStartsAndCheckChange(std::vector<BasicBlock*>& blocks, + LocalSet& old, + LocalSet& ret) { + if (blocks.size() == 0) + return false; ret = blocks[0]->contents.start; if (blocks.size() > 1) { // more than one, so we must merge @@ -213,7 +238,8 @@ struct LivenessWalker : public CFGWalker<SubType, VisitorType, Liveness> { return old != ret; } - void scanLivenessThroughActions(std::vector<LivenessAction>& actions, LocalSet& live) { + void scanLivenessThroughActions(std::vector<LivenessAction>& actions, + LocalSet& live) { // move towards the front for (int i = int(actions.size()) - 1; i >= 0; i--) { auto& action = actions[i]; diff --git a/src/compiler-support.h b/src/compiler-support.h index e7cb4db8c..911d92ef5 100644 --- a/src/compiler-support.h +++ b/src/compiler-support.h @@ -18,26 +18,34 @@ #define wasm_compiler_support_h #ifndef __has_feature -# define __has_feature(x) 0 +#define __has_feature(x) 0 #endif #ifndef __has_builtin -# define __has_builtin(x) 0 +#define __has_builtin(x) 0 #endif // If control flow reaches the point of the WASM_UNREACHABLE(), the program is // undefined. #if __has_builtin(__builtin_unreachable) && defined(NDEBUG) -# define WASM_UNREACHABLE() __builtin_unreachable() +#define WASM_UNREACHABLE() __builtin_unreachable() #elif defined(_MSC_VER) -# define WASM_UNREACHABLE() __assume(false) +#define WASM_UNREACHABLE() __assume(false) #elif __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) -# include "sanitizer/common_interface_defs.h" -# define WASM_UNREACHABLE() do { __sanitizer_print_stack_trace(); __builtin_trap(); } while (0) +#include "sanitizer/common_interface_defs.h" +#define WASM_UNREACHABLE() \ + do { \ + __sanitizer_print_stack_trace(); \ + __builtin_trap(); \ + } while (0) #else -# include <assert.h> -# include <stdlib.h> -# define WASM_UNREACHABLE() do { assert(false); abort(); } while (0) +#include <assert.h> +#include <stdlib.h> +#define WASM_UNREACHABLE() \ + do { \ + assert(false); \ + abort(); \ + } while (0) #endif #ifdef __GNUC__ @@ -54,7 +62,11 @@ // The code might contain TODOs or stubs that read some values but do nothing // with them. The compiler might fail with [-Werror,-Wunused-variable]. // The WASM_UNUSED(varible) is a wrapper that helps to suppress the error. -#define WASM_UNUSED(expr) \ - do { if (sizeof expr) { (void)0; } } while (0) +#define WASM_UNUSED(expr) \ + do { \ + if (sizeof expr) { \ + (void)0; \ + } \ + } while (0) #endif // wasm_compiler_support_h diff --git a/src/dataflow/graph.h b/src/dataflow/graph.h index 85b37b7b0..f93621a91 100644 --- a/src/dataflow/graph.h +++ b/src/dataflow/graph.h @@ -25,11 +25,11 @@ #ifndef wasm_dataflow_graph_h #define wasm_dataflow_graph_h -#include "wasm.h" +#include "dataflow/node.h" #include "ir/abstract.h" #include "ir/iteration.h" #include "ir/literal-utils.h" -#include "dataflow/node.h" +#include "wasm.h" namespace wasm { @@ -99,7 +99,8 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { struct FlowState { Locals locals; Node* condition; - FlowState(Locals locals, Node* condition) : locals(locals), condition(condition) {} + FlowState(Locals locals, Node* condition) + : locals(locals), condition(condition) {} }; // API @@ -109,11 +110,13 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { module = moduleInit; auto numLocals = func->getNumLocals(); - if (numLocals == 0) return; // nothing to do + if (numLocals == 0) + return; // nothing to do // Set up initial local state IR. setInReachable(); for (Index i = 0; i < numLocals; i++) { - if (!isRelevantType(func->getLocalType(i))) continue; + if (!isRelevantType(func->getLocalType(i))) + continue; Node* node; auto type = func->getLocalType(i); if (func->isParam(i)) { @@ -141,7 +144,7 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { Node* makeConst(Literal value) { auto iter = constantNodes.find(value); - if (iter!= constantNodes.end()) { + if (iter != constantNodes.end()) { return iter->second; } // Create one for this literal. @@ -152,9 +155,7 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { return ret; } - Node* makeZero(wasm::Type type) { - return makeConst(Literal::makeZero(type)); - } + Node* makeZero(wasm::Type type) { return makeConst(Literal::makeZero(type)); } // Add a new node to our list of owned nodes. Node* addNode(Node* node) { @@ -166,34 +167,26 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { assert(!node->isBad()); Builder builder(*module); auto type = node->getWasmType(); - if (!isConcreteType(type)) return &bad; + if (!isConcreteType(type)) + return &bad; auto* zero = makeZero(type); auto* expr = builder.makeBinary( Abstract::getBinary(type, equal ? Abstract::Eq : Abstract::Ne), makeUse(node), - makeUse(zero) - ); + makeUse(zero)); auto* check = addNode(Node::makeExpr(expr, origin)); check->addValue(expandFromI1(node, origin)); check->addValue(zero); return check; } - void setInUnreachable() { - locals.clear(); - } + void setInUnreachable() { locals.clear(); } - void setInReachable() { - locals.resize(func->getNumLocals()); - } + void setInReachable() { locals.resize(func->getNumLocals()); } - bool isInUnreachable() { - return isInUnreachable(locals); - } + bool isInUnreachable() { return isInUnreachable(locals); } - bool isInUnreachable(const Locals& state) { - return state.empty(); - } + bool isInUnreachable(const Locals& state) { return state.empty(); } bool isInUnreachable(const FlowState& state) { return isInUnreachable(state.locals); @@ -320,7 +313,8 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { auto& breaks = breakStates[curr->name]; // Phis are possible, check for them. for (Index i = 0; i < numLocals; i++) { - if (!isRelevantType(func->getLocalType(i))) continue; + if (!isRelevantType(func->getLocalType(i))) + continue; bool needPhi = false; // We replaced the proper value with a Var. If it's still that // Var - or it's the original proper value, which can happen with @@ -413,9 +407,7 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { } return &bad; } - Node* doVisitConst(Const* curr) { - return makeConst(curr->value); - } + Node* doVisitConst(Const* curr) { return makeConst(curr->value); } Node* doVisitUnary(Unary* curr) { // First, check if we support this op. switch (curr->op) { @@ -428,7 +420,8 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { // These are ok as-is. // Check if our child is supported. auto* value = expandFromI1(visit(curr->value), curr); - if (value->isBad()) return value; + if (value->isBad()) + return value; // Great, we are supported! auto* ret = addNode(Node::makeExpr(curr, curr)); ret->addValue(value); @@ -439,7 +432,8 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { // These can be implemented using a binary. // Check if our child is supported. auto* value = expandFromI1(visit(curr->value), curr); - if (value->isBad()) return value; + if (value->isBad()) + return value; // Great, we are supported! return makeZeroComp(value, true, curr); } @@ -449,7 +443,7 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { } } } - Node* doVisitBinary(Binary *curr) { + Node* doVisitBinary(Binary* curr) { // First, check if we support this op. switch (curr->op) { case AddInt32: @@ -497,9 +491,11 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { // These are ok as-is. // Check if our children are supported. auto* left = expandFromI1(visit(curr->left), curr); - if (left->isBad()) return left; + if (left->isBad()) + return left; auto* right = expandFromI1(visit(curr->right), curr); - if (right->isBad()) return right; + if (right->isBad()) + return right; // Great, we are supported! auto* ret = addNode(Node::makeExpr(curr, curr)); ret->addValue(left); @@ -518,19 +514,37 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { Builder builder(*module); BinaryOp opposite; switch (curr->op) { - case GtSInt32: opposite = LtSInt32; break; - case GtSInt64: opposite = LtSInt64; break; - case GeSInt32: opposite = LeSInt32; break; - case GeSInt64: opposite = LeSInt64; break; - case GtUInt32: opposite = LtUInt32; break; - case GtUInt64: opposite = LtUInt64; break; - case GeUInt32: opposite = LeUInt32; break; - case GeUInt64: opposite = LeUInt64; break; - default: WASM_UNREACHABLE(); + case GtSInt32: + opposite = LtSInt32; + break; + case GtSInt64: + opposite = LtSInt64; + break; + case GeSInt32: + opposite = LeSInt32; + break; + case GeSInt64: + opposite = LeSInt64; + break; + case GtUInt32: + opposite = LtUInt32; + break; + case GtUInt64: + opposite = LtUInt64; + break; + case GeUInt32: + opposite = LeUInt32; + break; + case GeUInt64: + opposite = LeUInt64; + break; + default: + WASM_UNREACHABLE(); } - auto* ret = visitBinary(builder.makeBinary(opposite, curr->right, curr->left)); - // We just created a new binary node, but we need to set the origin properly - // to the original. + auto* ret = + visitBinary(builder.makeBinary(opposite, curr->right, curr->left)); + // We just created a new binary node, but we need to set the origin + // properly to the original. ret->origin = curr; return ret; } @@ -542,11 +556,14 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { } Node* doVisitSelect(Select* curr) { auto* ifTrue = expandFromI1(visit(curr->ifTrue), curr); - if (ifTrue->isBad()) return ifTrue; + if (ifTrue->isBad()) + return ifTrue; auto* ifFalse = expandFromI1(visit(curr->ifFalse), curr); - if (ifFalse->isBad()) return ifFalse; + if (ifFalse->isBad()) + return ifFalse; auto* condition = ensureI1(visit(curr->condition), curr); - if (condition->isBad()) return condition; + if (condition->isBad()) + return condition; // Great, we are supported! auto* ret = addNode(Node::makeExpr(curr, curr)); ret->addValue(condition); @@ -575,16 +592,18 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { // Helpers. - bool isRelevantType(wasm::Type type) { - return isIntegerType(type); - } + bool isRelevantType(wasm::Type type) { return isIntegerType(type); } bool isRelevantLocal(Index index) { return isRelevantType(func->getLocalType(index)); } // Merge local state for an if, also creating a block and conditions. - void mergeIf(Locals& aState, Locals& bState, Node* condition, Expression* expr, Locals& out) { + void mergeIf(Locals& aState, + Locals& bState, + Node* condition, + Expression* expr, + Locals& out) { // Create the conditions (if we can). Node* ifTrue; Node* ifFalse; @@ -642,7 +661,8 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { Index numLocals = func->getNumLocals(); Node* block = nullptr; for (Index i = 0; i < numLocals; i++) { - if (!isRelevantType(func->getLocalType(i))) continue; + if (!isRelevantType(func->getLocalType(i))) + continue; // Process the inputs. If any is bad, the phi is bad. bool bad = false; for (auto& state : states) { @@ -653,7 +673,8 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { break; } } - if (bad) continue; + if (bad) + continue; // Nothing is bad, proceed. Node* first = nullptr; for (auto& state : states) { @@ -703,14 +724,16 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> { // the set. SetLocal* getSet(Node* node) { auto iter = nodeParentMap.find(node); - if (iter == nodeParentMap.end()) return nullptr; + if (iter == nodeParentMap.end()) + return nullptr; return iter->second->dynCast<SetLocal>(); } // Given an expression, return the parent if such exists. Expression* getParent(Expression* curr) { auto iter = expressionParentMap.find(curr); - if (iter == expressionParentMap.end()) return nullptr; + if (iter == expressionParentMap.end()) + return nullptr; return iter->second; } diff --git a/src/dataflow/node.h b/src/dataflow/node.h index d6514588e..8977ab41c 100644 --- a/src/dataflow/node.h +++ b/src/dataflow/node.h @@ -25,6 +25,7 @@ #ifndef wasm_dataflow_node_h #define wasm_dataflow_node_h +#include "ir/utils.h" #include "wasm.h" namespace wasm { @@ -34,11 +35,11 @@ namespace DataFlow { // // The core IR representation in DataFlow: a Node. // -// We reuse the Binaryen IR as much as possible: when things are identical between -// the two IRs, we just create an Expr node, which stores the opcode and other -// details, and we can emit them to Souper by reading the Binaryen Expression. -// Other node types here are special things from Souper IR that we can't -// represent that way. +// We reuse the Binaryen IR as much as possible: when things are identical +// between the two IRs, we just create an Expr node, which stores the opcode and +// other details, and we can emit them to Souper by reading the Binaryen +// Expression. Other node types here are special things from Souper IR that we +// can't represent that way. // // * Souper comparisons return an i1. We extend them immediately if they are // going to be used as i32s or i64s. @@ -52,15 +53,17 @@ namespace DataFlow { struct Node { enum Type { - Var, // an unknown variable number (not to be confused with var/param/local in wasm) - Expr, // a value represented by a Binaryen Expression - Phi, // a phi from converging control flow - Cond, // a blockpc, representing one of the branchs for a Block + Var, // an unknown variable number (not to be confused with var/param/local + // in wasm) + Expr, // a value represented by a Binaryen Expression + Phi, // a phi from converging control flow + Cond, // a blockpc, representing one of the branchs for a Block Block, // a source of phis - Zext, // zero-extend an i1 (from an op where Souper returns i1 but wasm does not, - // and so we need a special way to get back to an i32/i64 if we operate - // on that value instead of just passing it straight to Souper). - Bad // something we can't handle and should ignore + Zext, // zero-extend an i1 (from an op where Souper returns i1 but wasm does + // not, and so we need a special way to get back to an i32/i64 if we + // operate on that value instead of just passing it straight to + // Souper). + Bad // something we can't handle and should ignore } type; Node(Type type) : type(type) {} @@ -144,50 +147,56 @@ struct Node { // Helpers - void addValue(Node* value) { - values.push_back(value); - } - Node* getValue(Index i) { - return values.at(i); - } + void addValue(Node* value) { values.push_back(value); } + Node* getValue(Index i) { return values.at(i); } // Gets the wasm type of the node. If there isn't a valid one, // return unreachable. wasm::Type getWasmType() { switch (type) { - case Var: return wasmType; - case Expr: return expr->type; - case Phi: return getValue(1)->getWasmType(); - case Zext: return getValue(0)->getWasmType(); - case Bad: return unreachable; - default: WASM_UNREACHABLE(); + case Var: + return wasmType; + case Expr: + return expr->type; + case Phi: + return getValue(1)->getWasmType(); + case Zext: + return getValue(0)->getWasmType(); + case Bad: + return unreachable; + default: + WASM_UNREACHABLE(); } } bool operator==(const Node& other) { - if (type != other.type) return false; + if (type != other.type) + return false; switch (type) { case Var: - case Block: return this == &other; + case Block: + return this == &other; case Expr: { if (!ExpressionAnalyzer::equal(expr, other.expr)) { return false; } break; } - case Cond: if (index != other.index) return false; + case Cond: + if (index != other.index) + return false; default: {} } - if (values.size() != other.values.size()) return false; + if (values.size() != other.values.size()) + return false; for (Index i = 0; i < values.size(); i++) { - if (*(values[i]) != *(other.values[i])) return false; + if (*(values[i]) != *(other.values[i])) + return false; } return true; } - bool operator!=(const Node& other) { - return !(*this == other); - } + bool operator!=(const Node& other) { return !(*this == other); } // As mentioned above, comparisons return i1. This checks // if an operation is of that sort. diff --git a/src/dataflow/users.h b/src/dataflow/users.h index ab9a2ccef..369d18bbb 100644 --- a/src/dataflow/users.h +++ b/src/dataflow/users.h @@ -89,14 +89,10 @@ public: } // Adds a new user to a node. Called when we add or change a value of a node. - void addUser(Node* node, Node* newUser) { - users[node].insert(newUser); - } + void addUser(Node* node, Node* newUser) { users[node].insert(newUser); } // Remove all uses of a node. Called when a node is being removed. - void removeAllUsesOf(Node* node) { - users.erase(node); - } + void removeAllUsesOf(Node* node) { users.erase(node); } }; } // namespace DataFlow diff --git a/src/dataflow/utils.h b/src/dataflow/utils.h index 5328e6ab7..337dfec5e 100644 --- a/src/dataflow/utils.h +++ b/src/dataflow/utils.h @@ -25,9 +25,9 @@ #ifndef wasm_dataflow_utils_h #define wasm_dataflow_utils_h -#include "wasm.h" -#include "wasm-printing.h" #include "dataflow/node.h" +#include "wasm-printing.h" +#include "wasm.h" namespace wasm { @@ -35,27 +35,39 @@ namespace DataFlow { inline std::ostream& dump(Node* node, std::ostream& o, size_t indent = 0) { auto doIndent = [&]() { - for (size_t i = 0; i < indent; i++) o << ' '; + for (size_t i = 0; i < indent; i++) + o << ' '; }; doIndent(); o << '[' << node << ' '; switch (node->type) { - case Node::Type::Var: o << "var " << printType(node->wasmType) << ' ' << node; break; - case Node::Type::Expr: { + case Node::Type::Var: + o << "var " << printType(node->wasmType) << ' ' << node; + break; + case Node::Type::Expr: { o << "expr "; WasmPrinter::printExpression(node->expr, o, true); break; } - case Node::Type::Phi: o << "phi " << node->index; break; - case Node::Type::Cond: o << "cond " << node->index; break; + case Node::Type::Phi: + o << "phi " << node->index; + break; + case Node::Type::Cond: + o << "cond " << node->index; + break; case Node::Type::Block: { // don't print the conds - they would recurse o << "block (" << node->values.size() << " conds)]\n"; return o; } - case Node::Type::Zext: o << "zext"; break; - case Node::Type::Bad: o << "bad"; break; - default: WASM_UNREACHABLE(); + case Node::Type::Zext: + o << "zext"; + break; + case Node::Type::Bad: + o << "bad"; + break; + default: + WASM_UNREACHABLE(); } if (!node->values.empty()) { o << '\n'; @@ -115,11 +127,9 @@ inline bool allInputsConstant(Node* node) { if (node->expr->is<Unary>()) { return node->getValue(0)->isConst(); } else if (node->expr->is<Binary>()) { - return node->getValue(0)->isConst() && - node->getValue(1)->isConst(); + return node->getValue(0)->isConst() && node->getValue(1)->isConst(); } else if (node->expr->is<Select>()) { - return node->getValue(0)->isConst() && - node->getValue(1)->isConst() && + return node->getValue(0)->isConst() && node->getValue(1)->isConst() && node->getValue(2)->isConst(); } break; diff --git a/src/emscripten-optimizer/istring.h b/src/emscripten-optimizer/istring.h index 5c3b094c3..320a3e590 100644 --- a/src/emscripten-optimizer/istring.h +++ b/src/emscripten-optimizer/istring.h @@ -14,20 +14,21 @@ * limitations under the License. */ -// Interned String type, 100% interned on creation. Comparisons are always just a pointer comparison +// Interned String type, 100% interned on creation. Comparisons are always just +// a pointer comparison #ifndef wasm_istring_h #define wasm_istring_h -#include <unordered_set> -#include <unordered_map> #include <set> +#include <unordered_map> +#include <unordered_set> -#include <string.h> +#include <assert.h> #include <stdint.h> -#include <stdlib.h> #include <stdio.h> -#include <assert.h> +#include <stdlib.h> +#include <string.h> #include "support/threads.h" #include "support/utilities.h" @@ -35,9 +36,10 @@ namespace cashew { struct IString { - const char *str = nullptr; + const char* str = nullptr; - static size_t hash_c(const char *str) { // see http://www.cse.yorku.ca/~oz/hash.html + static size_t + hash_c(const char* str) { // see http://www.cse.yorku.ca/~oz/hash.html unsigned int hash = 5381; int c; while ((c = *str++)) { @@ -46,27 +48,27 @@ struct IString { return (size_t)hash; } - class CStringHash : public std::hash<const char *> { + class CStringHash : public std::hash<const char*> { public: - size_t operator()(const char *str) const { - return IString::hash_c(str); - } + size_t operator()(const char* str) const { return IString::hash_c(str); } }; - class CStringEqual : public std::equal_to<const char *> { + class CStringEqual : public std::equal_to<const char*> { public: - bool operator()(const char *x, const char *y) const { + bool operator()(const char* x, const char* y) const { return strcmp(x, y) == 0; } }; IString() = default; - IString(const char *s, bool reuse=true) { // if reuse=true, then input is assumed to remain alive; not copied + // if reuse=true, then input is assumed to remain alive; not copied + IString(const char* s, bool reuse = true) { assert(s); set(s, reuse); } - void set(const char *s, bool reuse=true) { - typedef std::unordered_set<const char *, CStringHash, CStringEqual> StringSet; + void set(const char* s, bool reuse = true) { + typedef std::unordered_set<const char*, CStringHash, CStringEqual> + StringSet; // one global store of strings per thread, we must not access this // in parallel thread_local static StringSet strings; @@ -79,8 +81,8 @@ struct IString { // exactly once static std::mutex mutex; std::unique_lock<std::mutex> lock(mutex); - // a single global set contains the actual strings, so we allocate each one - // exactly once. + // a single global set contains the actual strings, so we allocate each + // one exactly once. static StringSet globalStrings; auto globalExisting = globalStrings.find(s); if (globalExisting == globalStrings.end()) { @@ -103,56 +105,51 @@ struct IString { str = s; } - void set(const IString &s) { - str = s.str; - } + void set(const IString& s) { str = s.str; } - void clear() { - str = nullptr; - } + void clear() { str = nullptr; } bool operator==(const IString& other) const { - //assert((str == other.str) == !strcmp(str, other.str)); + // assert((str == other.str) == !strcmp(str, other.str)); return str == other.str; // fast! } bool operator!=(const IString& other) const { - //assert((str == other.str) == !strcmp(str, other.str)); + // assert((str == other.str) == !strcmp(str, other.str)); return str != other.str; // fast! } bool operator<(const IString& other) const { return strcmp(str ? str : "", other.str ? other.str : "") < 0; } - char operator[](int x) const { - return str[x]; - } + char operator[](int x) const { return str[x]; } bool operator!() const { // no string, or empty string return !str || str[0] == 0; } - const char *c_str() const { return str; } - bool equals(const char *other) const { return !strcmp(str, other); } + const char* c_str() const { return str; } + bool equals(const char* other) const { return !strcmp(str, other); } - bool is() const { return str != nullptr; } + bool is() const { return str != nullptr; } bool isNull() const { return str == nullptr; } - const char* stripPrefix(const char *prefix) const { - const char *ptr = str; + const char* stripPrefix(const char* prefix) const { + const char* ptr = str; while (true) { - if (*prefix == 0) return ptr; - if (*ptr == 0) return nullptr; - if (*ptr++ != *prefix++) return nullptr; + if (*prefix == 0) + return ptr; + if (*ptr == 0) + return nullptr; + if (*ptr++ != *prefix++) + return nullptr; } } - bool startsWith(const char *prefix) const { + bool startsWith(const char* prefix) const { return stripPrefix(prefix) != nullptr; } - size_t size() const { - return str ? strlen(str) : 0; - } + size_t size() const { return str ? strlen(str) : 0; } }; } // namespace cashew @@ -161,13 +158,16 @@ struct IString { namespace std { -template<> struct hash<cashew::IString> : public unary_function<cashew::IString, size_t> { +template<> +struct hash<cashew::IString> : public unary_function<cashew::IString, size_t> { size_t operator()(const cashew::IString& str) const { return std::hash<size_t>{}(size_t(str.str)); } }; -template<> struct equal_to<cashew::IString> : public binary_function<cashew::IString, cashew::IString, bool> { +template<> +struct equal_to<cashew::IString> + : public binary_function<cashew::IString, cashew::IString, bool> { bool operator()(const cashew::IString& x, const cashew::IString& y) const { return x == y; } @@ -181,32 +181,31 @@ namespace cashew { class IStringSet : public std::unordered_set<IString> { std::vector<char> data; + public: IStringSet() = default; - IStringSet(const char *init) { // comma-delimited list + IStringSet(const char* init) { // comma-delimited list int size = strlen(init) + 1; data.resize(size); - char *curr = &data[0]; + char* curr = &data[0]; strncpy(curr, init, size); while (1) { - char *end = strchr(curr, ' '); - if (end) *end = 0; + char* end = strchr(curr, ' '); + if (end) + *end = 0; insert(curr); - if (!end) break; + if (!end) + break; curr = end + 1; } } - bool has(const IString& str) { - return count(str) > 0; - } + bool has(const IString& str) { return count(str) > 0; } }; class IOrderedStringSet : public std::set<IString> { public: - bool has(const IString& str) { - return count(str) > 0; - } + bool has(const IString& str) { return count(str) > 0; } }; } // namespace cashew diff --git a/src/emscripten-optimizer/optimizer-shared.cpp b/src/emscripten-optimizer/optimizer-shared.cpp index 81f7949ba..3ac3ca7ea 100644 --- a/src/emscripten-optimizer/optimizer-shared.cpp +++ b/src/emscripten-optimizer/optimizer-shared.cpp @@ -23,14 +23,14 @@ using namespace cashew; IString ASM_FLOAT_ZERO; -IString SIMD_INT8X16_CHECK("SIMD_Int8x16_check"), - SIMD_INT16X8_CHECK("SIMD_Int16x8_check"), - SIMD_INT32X4_CHECK("SIMD_Int32x4_check"), - SIMD_FLOAT32X4_CHECK("SIMD_Float32x4_check"), - SIMD_FLOAT64X2_CHECK("SIMD_Float64x2_check"), - TEMP_RET0("tempRet0"); +IString SIMD_INT8X16_CHECK("SIMD_Int8x16_check"); +IString SIMD_INT16X8_CHECK("SIMD_Int16x8_check"); +IString SIMD_INT32X4_CHECK("SIMD_Int32x4_check"); +IString SIMD_FLOAT32X4_CHECK("SIMD_Float32x4_check"); +IString SIMD_FLOAT64X2_CHECK("SIMD_Float64x2_check"); +IString TEMP_RET0("tempRet0"); -int parseInt(const char *str) { +int parseInt(const char* str) { int ret = *str - '0'; while (*(++str)) { ret *= 10; @@ -39,7 +39,7 @@ int parseInt(const char *str) { return ret; } -HeapInfo parseHeap(const char *name) { +HeapInfo parseHeap(const char* name) { HeapInfo ret; if (name[0] != 'H' || name[1] != 'E' || name[2] != 'A' || name[3] != 'P') { ret.valid = false; @@ -53,33 +53,49 @@ HeapInfo parseHeap(const char *name) { return ret; } -AsmType detectType(Ref node, AsmData *asmData, bool inVarDef, IString minifiedFround, bool allowI64) { +AsmType detectType(Ref node, + AsmData* asmData, + bool inVarDef, + IString minifiedFround, + bool allowI64) { if (node->isString()) { if (asmData) { AsmType ret = asmData->getType(node->getCString()); - if (ret != ASM_NONE) return ret; + if (ret != ASM_NONE) + return ret; } if (!inVarDef) { - if (node == INF || node == NaN) return ASM_DOUBLE; - if (node == TEMP_RET0) return ASM_INT; + if (node == INF || node == NaN) + return ASM_DOUBLE; + if (node == TEMP_RET0) + return ASM_INT; return ASM_NONE; } - // We are in a variable definition, where Math_fround(0) optimized into a global constant becomes f0 = Math_fround(0) - if (ASM_FLOAT_ZERO.isNull()) ASM_FLOAT_ZERO = node->getIString(); - else assert(node == ASM_FLOAT_ZERO); + // We are in a variable definition, where Math_fround(0) optimized into a + // global constant becomes f0 = Math_fround(0) + if (ASM_FLOAT_ZERO.isNull()) + ASM_FLOAT_ZERO = node->getIString(); + else + assert(node == ASM_FLOAT_ZERO); return ASM_FLOAT; } if (node->isNumber()) { - if (!wasm::isInteger(node->getNumber())) return ASM_DOUBLE; + if (!wasm::isInteger(node->getNumber())) + return ASM_DOUBLE; return ASM_INT; } switch (node[0]->getCString()[0]) { case 'u': { if (node[0] == UNARY_PREFIX) { switch (node[1]->getCString()[0]) { - case '+': return ASM_DOUBLE; - case '-': return detectType(node[2], asmData, inVarDef, minifiedFround, allowI64); - case '!': case '~': return ASM_INT; + case '+': + return ASM_DOUBLE; + case '-': + return detectType( + node[2], asmData, inVarDef, minifiedFround, allowI64); + case '!': + case '~': + return ASM_INT; } break; } @@ -89,13 +105,20 @@ AsmType detectType(Ref node, AsmData *asmData, bool inVarDef, IString minifiedFr if (node[0] == CALL) { if (node[1]->isString()) { IString name = node[1]->getIString(); - if (name == MATH_FROUND || name == minifiedFround) return ASM_FLOAT; - else if (allowI64 && (name == INT64 || name == INT64_CONST)) return ASM_INT64; - else if (name == SIMD_FLOAT32X4 || name == SIMD_FLOAT32X4_CHECK) return ASM_FLOAT32X4; - else if (name == SIMD_FLOAT64X2 || name == SIMD_FLOAT64X2_CHECK) return ASM_FLOAT64X2; - else if (name == SIMD_INT8X16 || name == SIMD_INT8X16_CHECK) return ASM_INT8X16; - else if (name == SIMD_INT16X8 || name == SIMD_INT16X8_CHECK) return ASM_INT16X8; - else if (name == SIMD_INT32X4 || name == SIMD_INT32X4_CHECK) return ASM_INT32X4; + if (name == MATH_FROUND || name == minifiedFround) + return ASM_FLOAT; + else if (allowI64 && (name == INT64 || name == INT64_CONST)) + return ASM_INT64; + else if (name == SIMD_FLOAT32X4 || name == SIMD_FLOAT32X4_CHECK) + return ASM_FLOAT32X4; + else if (name == SIMD_FLOAT64X2 || name == SIMD_FLOAT64X2_CHECK) + return ASM_FLOAT64X2; + else if (name == SIMD_INT8X16 || name == SIMD_INT8X16_CHECK) + return ASM_INT8X16; + else if (name == SIMD_INT16X8 || name == SIMD_INT16X8_CHECK) + return ASM_INT16X8; + else if (name == SIMD_INT32X4 || name == SIMD_INT32X4_CHECK) + return ASM_INT32X4; } return ASM_NONE; } else if (node[0] == CONDITIONAL) { @@ -106,10 +129,20 @@ AsmType detectType(Ref node, AsmData *asmData, bool inVarDef, IString minifiedFr case 'b': { if (node[0] == BINARY) { switch (node[1]->getCString()[0]) { - case '+': case '-': - case '*': case '/': case '%': return detectType(node[2], asmData, inVarDef, minifiedFround, allowI64); - case '|': case '&': case '^': case '<': case '>': // handles <<, >>, >>=, <=, >= - case '=': case '!': { // handles ==, != + case '+': + case '-': + case '*': + case '/': + case '%': + return detectType( + node[2], asmData, inVarDef, minifiedFround, allowI64); + case '|': + case '&': + case '^': + case '<': + case '>': // handles <<, >>, >>=, <=, >= + case '=': + case '!': { // handles ==, != return ASM_INT; } } @@ -122,14 +155,15 @@ AsmType detectType(Ref node, AsmData *asmData, bool inVarDef, IString minifiedFr } else if (node[0] == SUB) { assert(node[1]->isString()); HeapInfo info = parseHeap(node[1][1]->getCString()); - if (info.valid) return ASM_NONE; + if (info.valid) + return ASM_NONE; return info.floaty ? ASM_DOUBLE : ASM_INT; // XXX ASM_FLOAT? } break; } } - //dump("horrible", node); - //assert(0); + // dump("horrible", node); + // assert(0); return ASM_NONE; } @@ -145,9 +179,12 @@ AsmSign detectSign(Ref node, IString minifiedFround) { } if (node->isNumber()) { double value = node->getNumber(); - if (value < 0) return ASM_SIGNED; - if (value > uint32_t(-1) || fmod(value, 1) != 0) return ASM_NONSIGNED; - if (wasm::isSInteger32(value)) return ASM_FLEXIBLE; + if (value < 0) + return ASM_SIGNED; + if (value > uint32_t(-1) || fmod(value, 1) != 0) + return ASM_NONSIGNED; + if (wasm::isSInteger32(value)) + return ASM_FLEXIBLE; return ASM_UNSIGNED; } IString type = node[0]->getIString(); @@ -155,25 +192,44 @@ AsmSign detectSign(Ref node, IString minifiedFround) { IString op = node[1]->getIString(); switch (op.str[0]) { case '>': { - if (op == TRSHIFT) return ASM_UNSIGNED; + if (op == TRSHIFT) + return ASM_UNSIGNED; } // fallthrough - case '|': case '&': case '^': case '<': case '=': case '!': return ASM_SIGNED; - case '+': case '-': return ASM_FLEXIBLE; - case '*': case '/': case '%': return ASM_NONSIGNED; // without a coercion, these are double - default: abort_on(node); + case '|': + case '&': + case '^': + case '<': + case '=': + case '!': + return ASM_SIGNED; + case '+': + case '-': + return ASM_FLEXIBLE; + case '*': + case '/': + case '%': + return ASM_NONSIGNED; // without a coercion, these are double + default: + abort_on(node); } } else if (type == UNARY_PREFIX) { IString op = node[1]->getIString(); switch (op.str[0]) { - case '-': return ASM_FLEXIBLE; - case '+': return ASM_NONSIGNED; // XXX double - case '~': return ASM_SIGNED; - default: abort_on(node); + case '-': + return ASM_FLEXIBLE; + case '+': + return ASM_NONSIGNED; // XXX double + case '~': + return ASM_SIGNED; + default: + abort_on(node); } } else if (type == CONDITIONAL) { return detectSign(node[2], minifiedFround); } else if (type == CALL) { - if (node[1]->isString() && (node[1] == MATH_FROUND || node[1] == minifiedFround)) return ASM_NONSIGNED; + if (node[1]->isString() && + (node[1] == MATH_FROUND || node[1] == minifiedFround)) + return ASM_NONSIGNED; } else if (type == SEQ) { return detectSign(node[2], minifiedFround); } @@ -183,8 +239,12 @@ AsmSign detectSign(Ref node, IString minifiedFround) { Ref makeAsmCoercedZero(AsmType type) { switch (type) { - case ASM_INT: return ValueBuilder::makeNum(0); break; - case ASM_DOUBLE: return ValueBuilder::makeUnary(PLUS, ValueBuilder::makeNum(0)); break; + case ASM_INT: + return ValueBuilder::makeNum(0); + break; + case ASM_DOUBLE: + return ValueBuilder::makeUnary(PLUS, ValueBuilder::makeNum(0)); + break; case ASM_FLOAT: { if (!ASM_FLOAT_ZERO.isNull()) { return ValueBuilder::makeName(ASM_FLOAT_ZERO); @@ -194,46 +254,92 @@ Ref makeAsmCoercedZero(AsmType type) { break; } case ASM_FLOAT32X4: { - return ValueBuilder::makeCall(SIMD_FLOAT32X4, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); + return ValueBuilder::makeCall(SIMD_FLOAT32X4, + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0)); break; } case ASM_FLOAT64X2: { - return ValueBuilder::makeCall(SIMD_FLOAT64X2, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); + return ValueBuilder::makeCall( + SIMD_FLOAT64X2, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); break; } case ASM_INT8X16: { - return ValueBuilder::makeCall(SIMD_INT8X16, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); + return ValueBuilder::makeCall(SIMD_INT8X16, + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0)); break; } case ASM_INT16X8: { - return ValueBuilder::makeCall(SIMD_INT16X8, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); + return ValueBuilder::makeCall(SIMD_INT16X8, + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0)); break; } case ASM_INT32X4: { - return ValueBuilder::makeCall(SIMD_INT32X4, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); + return ValueBuilder::makeCall(SIMD_INT32X4, + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0), + ValueBuilder::makeNum(0)); break; } - default: assert(0); + default: + assert(0); } abort(); } Ref makeAsmCoercion(Ref node, AsmType type) { switch (type) { - case ASM_INT: return ValueBuilder::makeBinary(node, OR, ValueBuilder::makeNum(0)); - case ASM_DOUBLE: return ValueBuilder::makeUnary(PLUS, node); - case ASM_FLOAT: return ValueBuilder::makeCall(MATH_FROUND, node); - case ASM_FLOAT32X4: return ValueBuilder::makeCall(SIMD_FLOAT32X4_CHECK, node); - case ASM_FLOAT64X2: return ValueBuilder::makeCall(SIMD_FLOAT64X2_CHECK, node); - case ASM_INT8X16: return ValueBuilder::makeCall(SIMD_INT8X16_CHECK, node); - case ASM_INT16X8: return ValueBuilder::makeCall(SIMD_INT16X8_CHECK, node); - case ASM_INT32X4: return ValueBuilder::makeCall(SIMD_INT32X4_CHECK, node); + case ASM_INT: + return ValueBuilder::makeBinary(node, OR, ValueBuilder::makeNum(0)); + case ASM_DOUBLE: + return ValueBuilder::makeUnary(PLUS, node); + case ASM_FLOAT: + return ValueBuilder::makeCall(MATH_FROUND, node); + case ASM_FLOAT32X4: + return ValueBuilder::makeCall(SIMD_FLOAT32X4_CHECK, node); + case ASM_FLOAT64X2: + return ValueBuilder::makeCall(SIMD_FLOAT64X2_CHECK, node); + case ASM_INT8X16: + return ValueBuilder::makeCall(SIMD_INT8X16_CHECK, node); + case ASM_INT16X8: + return ValueBuilder::makeCall(SIMD_INT16X8_CHECK, node); + case ASM_INT32X4: + return ValueBuilder::makeCall(SIMD_INT32X4_CHECK, node); case ASM_NONE: - default: return node; // non-validating code, emit nothing XXX this is dangerous, we should only allow this when we know we are not validating + default: + // non-validating code, emit nothing XXX this is dangerous, we should only + // allow this when we know we are not validating + return node; } } Ref makeSigning(Ref node, AsmSign sign) { assert(sign == ASM_SIGNED || sign == ASM_UNSIGNED); - return ValueBuilder::makeBinary(node, sign == ASM_SIGNED ? OR : TRSHIFT, ValueBuilder::makeNum(0)); + return ValueBuilder::makeBinary( + node, sign == ASM_SIGNED ? OR : TRSHIFT, ValueBuilder::makeNum(0)); } diff --git a/src/emscripten-optimizer/optimizer.h b/src/emscripten-optimizer/optimizer.h index f6b3aa536..36a9322fd 100644 --- a/src/emscripten-optimizer/optimizer.h +++ b/src/emscripten-optimizer/optimizer.h @@ -19,11 +19,7 @@ #include "simple_ast.h" -extern bool preciseF32, - receiveJSON, - emitJSON, - minifyWhitespace, - last; +extern bool preciseF32, receiveJSON, emitJSON, minifyWhitespace, last; extern cashew::Ref extraInfo; @@ -39,12 +35,16 @@ enum AsmType { ASM_INT16X8, ASM_INT32X4, ASM_INT64, // non-asm.js - ASM_NONE // number of types + ASM_NONE // number of types }; struct AsmData; -AsmType detectType(cashew::Ref node, AsmData *asmData=nullptr, bool inVarDef=false, cashew::IString minifiedFround=cashew::IString(), bool allowI64=false); +AsmType detectType(cashew::Ref node, + AsmData* asmData = nullptr, + bool inVarDef = false, + cashew::IString minifiedFround = cashew::IString(), + bool allowI64 = false); struct AsmData { struct Local { @@ -57,23 +57,22 @@ struct AsmData { Locals locals; std::vector<cashew::IString> params; // in order - std::vector<cashew::IString> vars; // in order + std::vector<cashew::IString> vars; // in order AsmType ret; cashew::Ref func; AsmType getType(const cashew::IString& name) { auto ret = locals.find(name); - if (ret != locals.end()) return ret->second.type; + if (ret != locals.end()) + return ret->second.type; return ASM_NONE; } void setType(const cashew::IString& name, AsmType type) { locals[name].type = type; } - bool isLocal(const cashew::IString& name) { - return locals.count(name) > 0; - } + bool isLocal(const cashew::IString& name) { return locals.count(name) > 0; } bool isParam(const cashew::IString& name) { return isLocal(name) && locals[name].param; } @@ -81,8 +80,11 @@ struct AsmData { return isLocal(name) && !locals[name].param; } - AsmData() = default; // if you want to fill in the data yourself - AsmData(cashew::Ref f); // if you want to read data from f, and modify it as you go (parallel to denormalize) + // if you want to fill in the data yourself + AsmData() = default; + // if you want to read data from f, and modify it as you go (parallel to + // denormalize) + AsmData(cashew::Ref f); void denormalize(); @@ -108,13 +110,13 @@ struct AsmData { extern cashew::IString ASM_FLOAT_ZERO; -extern cashew::IString SIMD_INT8X16_CHECK, - SIMD_INT16X8_CHECK, - SIMD_INT32X4_CHECK, - SIMD_FLOAT32X4_CHECK, - SIMD_FLOAT64X2_CHECK; +extern cashew::IString SIMD_INT8X16_CHECK; +extern cashew::IString SIMD_INT16X8_CHECK; +extern cashew::IString SIMD_INT32X4_CHECK; +extern cashew::IString SIMD_FLOAT32X4_CHECK; +extern cashew::IString SIMD_FLOAT64X2_CHECK; -int parseInt(const char *str); +int parseInt(const char* str); struct HeapInfo { bool valid, unsign, floaty; @@ -122,10 +124,11 @@ struct HeapInfo { AsmType type; }; -HeapInfo parseHeap(const char *name); +HeapInfo parseHeap(const char* name); enum AsmSign { - ASM_FLEXIBLE = 0, // small constants can be signed or unsigned, variables are also flexible + // small constants can be signed or unsigned, variables are also flexible + ASM_FLEXIBLE = 0, ASM_SIGNED = 1, ASM_UNSIGNED = 2, ASM_NONSIGNED = 3, diff --git a/src/emscripten-optimizer/parser.cpp b/src/emscripten-optimizer/parser.cpp index 035817090..72740908e 100644 --- a/src/emscripten-optimizer/parser.cpp +++ b/src/emscripten-optimizer/parser.cpp @@ -20,117 +20,119 @@ namespace cashew { // common strings -IString TOPLEVEL("toplevel"), - DEFUN("defun"), - BLOCK("block"), - VAR("var"), - CONST("const"), - CONDITIONAL("conditional"), - BINARY("binary"), - RETURN("return"), - IF("if"), - ELSE("else"), - WHILE("while"), - DO("do"), - FOR("for"), - SEQ("seq"), - SUB("sub"), - CALL("call"), - LABEL("label"), - BREAK("break"), - CONTINUE("continue"), - SWITCH("switch"), - STRING("string"), - TRY("try"), - INF("inf"), - NaN("nan"), - LLVM_CTTZ_I32("_llvm_cttz_i32"), - UDIVMODDI4("___udivmoddi4"), - UNARY_PREFIX("unary-prefix"), - UNARY_POSTFIX("unary-postfix"), - MATH_FROUND("Math_fround"), - MATH_CLZ32("Math_clz32"), - INT64("i64"), - INT64_CONST("i64_const"), - SIMD_FLOAT32X4("SIMD_Float32x4"), - SIMD_FLOAT64X2("SIMD_Float64x2"), - SIMD_INT8X16("SIMD_Int8x16"), - SIMD_INT16X8("SIMD_Int16x8"), - SIMD_INT32X4("SIMD_Int32x4"), - PLUS("+"), - MINUS("-"), - OR("|"), - AND("&"), - XOR("^"), - L_NOT("!"), - B_NOT("~"), - LT("<"), - GE(">="), - LE("<="), - GT(">"), - EQ("=="), - NE("!="), - DIV("/"), - MOD("%"), - MUL("*"), - RSHIFT(">>"), - LSHIFT("<<"), - TRSHIFT(">>>"), - HEAP8("HEAP8"), - HEAP16("HEAP16"), - HEAP32("HEAP32"), - HEAPF32("HEAPF32"), - HEAPU8("HEAPU8"), - HEAPU16("HEAPU16"), - HEAPU32("HEAPU32"), - HEAPF64("HEAPF64"), - F0("f0"), - EMPTY(""), - FUNCTION("function"), - OPEN_PAREN("("), - OPEN_BRACE("["), - OPEN_CURLY("{"), - CLOSE_CURLY("}"), - COMMA(","), - QUESTION("?"), - COLON(":"), - CASE("case"), - DEFAULT("default"), - DOT("dot"), - PERIOD("."), - NEW("new"), - ARRAY("array"), - OBJECT("object"), - THROW("throw"), - SET("="); - -IStringSet keywords("var const function if else do while for break continue return switch case default throw try catch finally true false null new"); - -const char *OPERATOR_INITS = "+-*/%<>&^|~=!,?:.", - *SEPARATORS = "([;{}"; +IString TOPLEVEL("toplevel"); +IString DEFUN("defun"); +IString BLOCK("block"); +IString VAR("var"); +IString CONST("const"); +IString CONDITIONAL("conditional"); +IString BINARY("binary"); +IString RETURN("return"); +IString IF("if"); +IString ELSE("else"); +IString WHILE("while"); +IString DO("do"); +IString FOR("for"); +IString SEQ("seq"); +IString SUB("sub"); +IString CALL("call"); +IString LABEL("label"); +IString BREAK("break"); +IString CONTINUE("continue"); +IString SWITCH("switch"); +IString STRING("string"); +IString TRY("try"); +IString INF("inf"); +IString NaN("nan"); +IString LLVM_CTTZ_I32("_llvm_cttz_i32"); +IString UDIVMODDI4("___udivmoddi4"); +IString UNARY_PREFIX("unary-prefix"); +IString UNARY_POSTFIX("unary-postfix"); +IString MATH_FROUND("Math_fround"); +IString MATH_CLZ32("Math_clz32"); +IString INT64("i64"); +IString INT64_CONST("i64_const"); +IString SIMD_FLOAT32X4("SIMD_Float32x4"); +IString SIMD_FLOAT64X2("SIMD_Float64x2"); +IString SIMD_INT8X16("SIMD_Int8x16"); +IString SIMD_INT16X8("SIMD_Int16x8"); +IString SIMD_INT32X4("SIMD_Int32x4"); +IString PLUS("+"); +IString MINUS("-"); +IString OR("|"); +IString AND("&"); +IString XOR("^"); +IString L_NOT("!"); +IString B_NOT("~"); +IString LT("<"); +IString GE(">="); +IString LE("<="); +IString GT(">"); +IString EQ("=="); +IString NE("!="); +IString DIV("/"); +IString MOD("%"); +IString MUL("*"); +IString RSHIFT(">>"); +IString LSHIFT("<<"); +IString TRSHIFT(">>>"); +IString HEAP8("HEAP8"); +IString HEAP16("HEAP16"); +IString HEAP32("HEAP32"); +IString HEAPF32("HEAPF32"); +IString HEAPU8("HEAPU8"); +IString HEAPU16("HEAPU16"); +IString HEAPU32("HEAPU32"); +IString HEAPF64("HEAPF64"); +IString F0("f0"); +IString EMPTY(""); +IString FUNCTION("function"); +IString OPEN_PAREN("("); +IString OPEN_BRACE("["); +IString OPEN_CURLY("{"); +IString CLOSE_CURLY("}"); +IString COMMA(","); +IString QUESTION("?"); +IString COLON(":"); +IString CASE("case"); +IString DEFAULT("default"); +IString DOT("dot"); +IString PERIOD("."); +IString NEW("new"); +IString ARRAY("array"); +IString OBJECT("object"); +IString THROW("throw"); +IString SET("="); + +IStringSet + keywords("var const function if else do while for break continue return " + "switch case default throw try catch finally true false null new"); + +const char *OPERATOR_INITS = "+-*/%<>&^|~=!,?:.", *SEPARATORS = "([;{}"; int MAX_OPERATOR_SIZE = 3; std::vector<OperatorClass> operatorClasses; -static std::vector<std::unordered_map<IString, int>> precedences; // op, type => prec +static std::vector<std::unordered_map<IString, int>> + precedences; // op, type => prec struct Init { Init() { // operators, rtl, type - operatorClasses.emplace_back(".", false, OperatorClass::Binary); - operatorClasses.emplace_back("! ~ + -", true, OperatorClass::Prefix); - operatorClasses.emplace_back("* / %", false, OperatorClass::Binary); - operatorClasses.emplace_back("+ -", false, OperatorClass::Binary); + operatorClasses.emplace_back(".", false, OperatorClass::Binary); + operatorClasses.emplace_back("! ~ + -", true, OperatorClass::Prefix); + operatorClasses.emplace_back("* / %", false, OperatorClass::Binary); + operatorClasses.emplace_back("+ -", false, OperatorClass::Binary); operatorClasses.emplace_back("<< >> >>>", false, OperatorClass::Binary); operatorClasses.emplace_back("< <= > >=", false, OperatorClass::Binary); - operatorClasses.emplace_back("== !=", false, OperatorClass::Binary); - operatorClasses.emplace_back("&", false, OperatorClass::Binary); - operatorClasses.emplace_back("^", false, OperatorClass::Binary); - operatorClasses.emplace_back("|", false, OperatorClass::Binary); - operatorClasses.emplace_back("? :", true, OperatorClass::Tertiary); - operatorClasses.emplace_back("=", true, OperatorClass::Binary); - operatorClasses.emplace_back(",", true, OperatorClass::Binary); + operatorClasses.emplace_back("== !=", false, OperatorClass::Binary); + operatorClasses.emplace_back("&", false, OperatorClass::Binary); + operatorClasses.emplace_back("^", false, OperatorClass::Binary); + operatorClasses.emplace_back("|", false, OperatorClass::Binary); + operatorClasses.emplace_back("? :", true, OperatorClass::Tertiary); + operatorClasses.emplace_back("=", true, OperatorClass::Binary); + operatorClasses.emplace_back(",", true, OperatorClass::Binary); precedences.resize(OperatorClass::Tertiary + 1); @@ -148,11 +150,12 @@ int OperatorClass::getPrecedence(Type type, IString op) { return precedences[type][op]; } -bool OperatorClass::getRtl(int prec) { - return operatorClasses[prec].rtl; -} +bool OperatorClass::getRtl(int prec) { return operatorClasses[prec].rtl; } -bool isIdentInit(char x) { return (x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z') || x == '_' || x == '$'; } +bool isIdentInit(char x) { + return (x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z') || x == '_' || + x == '$'; +} bool isIdentPart(char x) { return isIdentInit(x) || (x >= '0' && x <= '9'); } } // namespace cashew diff --git a/src/emscripten-optimizer/parser.h b/src/emscripten-optimizer/parser.h index 78ed27933..d180ea12c 100644 --- a/src/emscripten-optimizer/parser.h +++ b/src/emscripten-optimizer/parser.h @@ -14,10 +14,12 @@ * limitations under the License. */ -// Pure parsing. Calls methods on a Builder (template argument) to actually construct the AST +// Pure parsing. Calls methods on a Builder (template argument) to actually +// construct the AST // -// XXX All parsing methods assume they take ownership of the input string. This lets them reuse -// parts of it. You will segfault if the input string cannot be reused and written to. +// XXX All parsing methods assume they take ownership of the input string. This +// lets them reuse parts of it. You will segfault if the input string cannot +// be reused and written to. #ifndef wasm_parser_h #define wasm_parser_h @@ -35,89 +37,89 @@ namespace cashew { // common strings -extern IString TOPLEVEL, - DEFUN, - BLOCK, - VAR, - CONST, - CONDITIONAL, - BINARY, - RETURN, - IF, - ELSE, - WHILE, - DO, - FOR, - SEQ, - SUB, - CALL, - LABEL, - BREAK, - CONTINUE, - SWITCH, - STRING, - TRY, - INF, - NaN, - LLVM_CTTZ_I32, - UDIVMODDI4, - UNARY_PREFIX, - UNARY_POSTFIX, - MATH_FROUND, - MATH_CLZ32, - INT64, - INT64_CONST, - SIMD_FLOAT32X4, - SIMD_FLOAT64X2, - SIMD_INT8X16, - SIMD_INT16X8, - SIMD_INT32X4, - PLUS, - MINUS, - OR, - AND, - XOR, - L_NOT, - B_NOT, - LT, - GE, - LE, - GT, - EQ, - NE, - DIV, - MOD, - MUL, - RSHIFT, - LSHIFT, - TRSHIFT, - HEAP8, - HEAP16, - HEAP32, - HEAPF32, - HEAPU8, - HEAPU16, - HEAPU32, - HEAPF64, - F0, - EMPTY, - FUNCTION, - OPEN_PAREN, - OPEN_BRACE, - OPEN_CURLY, - CLOSE_CURLY, - COMMA, - QUESTION, - COLON, - CASE, - DEFAULT, - DOT, - PERIOD, - NEW, - ARRAY, - OBJECT, - THROW, - SET; +extern IString TOPLEVEL; +extern IString DEFUN; +extern IString BLOCK; +extern IString VAR; +extern IString CONST; +extern IString CONDITIONAL; +extern IString BINARY; +extern IString RETURN; +extern IString IF; +extern IString ELSE; +extern IString WHILE; +extern IString DO; +extern IString FOR; +extern IString SEQ; +extern IString SUB; +extern IString CALL; +extern IString LABEL; +extern IString BREAK; +extern IString CONTINUE; +extern IString SWITCH; +extern IString STRING; +extern IString TRY; +extern IString INF; +extern IString NaN; +extern IString LLVM_CTTZ_I32; +extern IString UDIVMODDI4; +extern IString UNARY_PREFIX; +extern IString UNARY_POSTFIX; +extern IString MATH_FROUND; +extern IString MATH_CLZ32; +extern IString INT64; +extern IString INT64_CONST; +extern IString SIMD_FLOAT32X4; +extern IString SIMD_FLOAT64X2; +extern IString SIMD_INT8X16; +extern IString SIMD_INT16X8; +extern IString SIMD_INT32X4; +extern IString PLUS; +extern IString MINUS; +extern IString OR; +extern IString AND; +extern IString XOR; +extern IString L_NOT; +extern IString B_NOT; +extern IString LT; +extern IString GE; +extern IString LE; +extern IString GT; +extern IString EQ; +extern IString NE; +extern IString DIV; +extern IString MOD; +extern IString MUL; +extern IString RSHIFT; +extern IString LSHIFT; +extern IString TRSHIFT; +extern IString HEAP8; +extern IString HEAP16; +extern IString HEAP32; +extern IString HEAPF32; +extern IString HEAPU8; +extern IString HEAPU16; +extern IString HEAPU32; +extern IString HEAPF64; +extern IString F0; +extern IString EMPTY; +extern IString FUNCTION; +extern IString OPEN_PAREN; +extern IString OPEN_BRACE; +extern IString OPEN_CURLY; +extern IString CLOSE_CURLY; +extern IString COMMA; +extern IString QUESTION; +extern IString COLON; +extern IString CASE; +extern IString DEFAULT; +extern IString DOT; +extern IString PERIOD; +extern IString NEW; +extern IString ARRAY; +extern IString OBJECT; +extern IString THROW; +extern IString SET; extern IStringSet keywords; @@ -126,12 +128,7 @@ extern const char *OPERATOR_INITS, *SEPARATORS; extern int MAX_OPERATOR_SIZE, LOWEST_PREC; struct OperatorClass { - enum Type { - Binary = 0, - Prefix = 1, - Postfix = 2, - Tertiary = 3 - }; + enum Type { Binary = 0, Prefix = 1, Postfix = 2, Tertiary = 3 }; IStringSet ops; bool rtl; @@ -150,10 +147,11 @@ extern bool isIdentPart(char x); // parser -template<class NodeRef, class Builder> -class Parser { +template<class NodeRef, class Builder> class Parser { - static bool isSpace(char x) { return x == 32 || x == 9 || x == 10 || x == 13; } /* space, tab, linefeed/newline, or return */ + static bool isSpace(char x) { + return x == 32 || x == 9 || x == 10 || x == 13; + } /* space, tab, linefeed/newline, or return */ static void skipSpace(char*& curr) { while (*curr) { if (isSpace(*curr)) { @@ -162,13 +160,16 @@ class Parser { } if (curr[0] == '/' && curr[1] == '/') { curr += 2; - while (*curr && *curr != '\n') curr++; - if (*curr) curr++; + while (*curr && *curr != '\n') + curr++; + if (*curr) + curr++; continue; } if (curr[0] == '/' && curr[1] == '*') { curr += 2; - while (*curr && (curr[0] != '*' || curr[1] != '/')) curr++; + while (*curr && (curr[0] != '*' || curr[1] != '/')) + curr++; curr += 2; continue; } @@ -178,7 +179,12 @@ class Parser { static bool isDigit(char x) { return x >= '0' && x <= '9'; } - static bool hasChar(const char* list, char x) { while (*list) if (*list++ == x) return true; return false; } + static bool hasChar(const char* list, char x) { + while (*list) + if (*list++ == x) + return true; + return false; + } // An atomic fragment of something. Stops at a natural boundary. enum FragType { @@ -192,7 +198,9 @@ class Parser { }; struct Frag { -#ifndef _MSC_VER // MSVC does not allow unrestricted unions: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf + // MSVC does not allow unrestricted unions: + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf +#ifndef _MSC_VER union { #endif IString str; @@ -203,12 +211,10 @@ class Parser { int size; FragType type; - bool isNumber() const { - return type == INT || type == DOUBLE; - } + bool isNumber() const { return type == INT || type == DOUBLE; } explicit Frag(char* src) { - char *start = src; + char* start = src; if (isIdentInit(*src)) { // read an identifier or a keyword src++; @@ -227,15 +233,22 @@ class Parser { } else if (isDigit(*src) || (src[0] == '.' && isDigit(src[1]))) { if (src[0] == '0' && (src[1] == 'x' || src[1] == 'X')) { // Explicitly parse hex numbers of form "0x...", because strtod - // supports hex number strings only in C++11, and Visual Studio 2013 does - // not yet support that functionality. + // supports hex number strings only in C++11, and Visual Studio 2013 + // does not yet support that functionality. src += 2; num = 0; while (1) { - if (*src >= '0' && *src <= '9') { num *= 16; num += *src - '0'; } - else if (*src >= 'a' && *src <= 'f') { num *= 16; num += *src - 'a' + 10; } - else if (*src >= 'A' && *src <= 'F') { num *= 16; num += *src - 'A' + 10; } - else break; + if (*src >= '0' && *src <= '9') { + num *= 16; + num += *src - '0'; + } else if (*src >= 'a' && *src <= 'f') { + num *= 16; + num += *src - 'a' + 10; + } else if (*src >= 'A' && *src <= 'F') { + num *= 16; + num += *src - 'A' + 10; + } else + break; src++; } } else { @@ -244,33 +257,69 @@ class Parser { // asm.js must have a '.' for double values. however, we also tolerate // uglify's tendency to emit without a '.' (and fix it later with a +). // for valid asm.js input, the '.' should be enough, and for uglify - // in the emscripten optimizer pipeline, we use simple_ast where INT/DOUBLE - // is quite the same at this point anyhow + // in the emscripten optimizer pipeline, we use simple_ast where + // INT/DOUBLE is quite the same at this point anyhow type = (std::find(start, src, '.') == src && (wasm::isSInteger32(num) || wasm::isUInteger32(num))) - ? INT - : DOUBLE; + ? INT + : DOUBLE; assert(src > start); } else if (hasChar(OPERATOR_INITS, *src)) { switch (*src) { - case '!': str = src[1] == '=' ? NE : L_NOT; break; - case '%': str = MOD; break; - case '&': str = AND; break; - case '*': str = MUL; break; - case '+': str = PLUS; break; - case ',': str = COMMA; break; - case '-': str = MINUS; break; - case '.': str = PERIOD; break; - case '/': str = DIV; break; - case ':': str = COLON; break; - case '<': str = src[1] == '<' ? LSHIFT : (src[1] == '=' ? LE : LT); break; - case '=': str = src[1] == '=' ? EQ : SET; break; - case '>': str = src[1] == '>' ? (src[2] == '>' ? TRSHIFT : RSHIFT) : (src[1] == '=' ? GE : GT); break; - case '?': str = QUESTION; break; - case '^': str = XOR; break; - case '|': str = OR; break; - case '~': str = B_NOT; break; - default: abort(); + case '!': + str = src[1] == '=' ? NE : L_NOT; + break; + case '%': + str = MOD; + break; + case '&': + str = AND; + break; + case '*': + str = MUL; + break; + case '+': + str = PLUS; + break; + case ',': + str = COMMA; + break; + case '-': + str = MINUS; + break; + case '.': + str = PERIOD; + break; + case '/': + str = DIV; + break; + case ':': + str = COLON; + break; + case '<': + str = src[1] == '<' ? LSHIFT : (src[1] == '=' ? LE : LT); + break; + case '=': + str = src[1] == '=' ? EQ : SET; + break; + case '>': + str = src[1] == '>' ? (src[2] == '>' ? TRSHIFT : RSHIFT) + : (src[1] == '=' ? GE : GT); + break; + case '?': + str = QUESTION; + break; + case '^': + str = XOR; + break; + case '|': + str = OR; + break; + case '~': + str = B_NOT; + break; + default: + abort(); } size = strlen(str.str); #ifndef NDEBUG @@ -289,10 +338,10 @@ class Parser { src[1] = temp; src++; } else if (*src == '"' || *src == '\'') { - char *end = strchr(src+1, *src); + char* end = strchr(src + 1, *src); *end = 0; - str.set(src+1); - src = end+1; + str.set(src + 1); + src = end + 1; type = STRING; } else { dump("frag parsing", src); @@ -304,7 +353,9 @@ class Parser { struct ExpressionElement { bool isNode; -#ifndef _MSC_VER // MSVC does not allow unrestricted unions: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf + // MSVC does not allow unrestricted unions: + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf +#ifndef _MSC_VER union { #endif NodeRef node; @@ -326,13 +377,15 @@ class Parser { }; // This is a list of the current stack of node-operator-node-operator-etc. - // this works by each parseExpression call appending to the vector; then recursing out, and the toplevel sorts it all + // this works by each parseExpression call appending to the vector; then + // recursing out, and the toplevel sorts it all typedef std::vector<ExpressionElement> ExpressionParts; std::vector<ExpressionParts> expressionPartsStack; - // Parses an element in a list of such elements, e.g. list of statements in a block, or list of parameters in a call - NodeRef parseElement(char*& src, const char* seps=";") { - //dump("parseElement", src); + // Parses an element in a list of such elements, e.g. list of statements in a + // block, or list of parameters in a call + NodeRef parseElement(char*& src, const char* seps = ";") { + // dump("parseElement", src); skipSpace(src); Frag frag(src); src += frag.size; @@ -349,44 +402,67 @@ class Parser { return parseExpression(parseFrag(frag), src, seps); } case SEPARATOR: { - if (frag.str == OPEN_PAREN) return parseExpression(parseAfterParen(src), src, seps); - if (frag.str == OPEN_BRACE) return parseExpression(parseAfterBrace(src), src, seps); - if (frag.str == OPEN_CURLY) return parseExpression(parseAfterCurly(src), src, seps); + if (frag.str == OPEN_PAREN) + return parseExpression(parseAfterParen(src), src, seps); + if (frag.str == OPEN_BRACE) + return parseExpression(parseAfterBrace(src), src, seps); + if (frag.str == OPEN_CURLY) + return parseExpression(parseAfterCurly(src), src, seps); abort(); } case OPERATOR: { return parseExpression(frag.str, src, seps); } - default: /* dump("parseElement", src); printf("bad frag type: %d\n", frag.type); */ abort(); + default: + /* dump("parseElement", src); printf("bad frag type: %d\n",frag.type); + */ + abort(); } return nullptr; } NodeRef parseFrag(Frag& frag) { switch (frag.type) { - case IDENT: return Builder::makeName(frag.str); - case STRING: return Builder::makeString(frag.str); - case INT: return Builder::makeInt(uint32_t(frag.num)); - case DOUBLE: return Builder::makeDouble(frag.num); - default: abort(); + case IDENT: + return Builder::makeName(frag.str); + case STRING: + return Builder::makeString(frag.str); + case INT: + return Builder::makeInt(uint32_t(frag.num)); + case DOUBLE: + return Builder::makeDouble(frag.num); + default: + abort(); } return nullptr; } NodeRef parseAfterKeyword(Frag& frag, char*& src, const char* seps) { skipSpace(src); - if (frag.str == FUNCTION) return parseFunction(src, seps); - else if (frag.str == VAR) return parseVar(src, seps, false); - else if (frag.str == CONST) return parseVar(src, seps, true); - else if (frag.str == RETURN) return parseReturn(src, seps); - else if (frag.str == IF) return parseIf(src, seps); - else if (frag.str == DO) return parseDo(src, seps); - else if (frag.str == WHILE) return parseWhile(src, seps); - else if (frag.str == BREAK) return parseBreak(src, seps); - else if (frag.str == CONTINUE) return parseContinue(src, seps); - else if (frag.str == SWITCH) return parseSwitch(src, seps); - else if (frag.str == NEW) return parseNew(src, seps); - else if (frag.str == FOR) return parseFor(src, seps); + if (frag.str == FUNCTION) + return parseFunction(src, seps); + else if (frag.str == VAR) + return parseVar(src, seps, false); + else if (frag.str == CONST) + return parseVar(src, seps, true); + else if (frag.str == RETURN) + return parseReturn(src, seps); + else if (frag.str == IF) + return parseIf(src, seps); + else if (frag.str == DO) + return parseDo(src, seps); + else if (frag.str == WHILE) + return parseWhile(src, seps); + else if (frag.str == BREAK) + return parseBreak(src, seps); + else if (frag.str == CONTINUE) + return parseContinue(src, seps); + else if (frag.str == SWITCH) + return parseSwitch(src, seps); + else if (frag.str == NEW) + return parseNew(src, seps); + else if (frag.str == FOR) + return parseFor(src, seps); dump(frag.str.str, src); abort(); return nullptr; @@ -406,13 +482,15 @@ class Parser { src++; while (1) { skipSpace(src); - if (*src == ')') break; + if (*src == ')') + break; Frag arg(src); assert(arg.type == IDENT); src += arg.size; Builder::appendArgumentToFunction(ret, arg.str); skipSpace(src); - if (*src == ')') break; + if (*src == ')') + break; if (*src == ',') { src++; continue; @@ -429,7 +507,8 @@ class Parser { NodeRef ret = Builder::makeVar(is_const); while (1) { skipSpace(src); - if (*src == ';') break; + if (*src == ';') + break; Frag name(src); assert(name.type == IDENT); NodeRef value; @@ -442,7 +521,8 @@ class Parser { } Builder::appendToVar(ret, name.str, value); skipSpace(src); - if (*src == ';') break; + if (*src == ';') + break; if (*src == ',') { src++; continue; @@ -458,7 +538,8 @@ class Parser { NodeRef value = !hasChar(seps, *src) ? parseElement(src, seps) : nullptr; skipSpace(src); assert(hasChar(seps, *src)); - if (*src == ';') src++; + if (*src == ';') + src++; return Builder::makeReturn(value); } @@ -516,14 +597,16 @@ class Parser { NodeRef parseBreak(char*& src, const char* seps) { skipSpace(src); Frag next(src); - if (next.type == IDENT) src += next.size; + if (next.type == IDENT) + src += next.size; return Builder::makeBreak(next.type == IDENT ? next.str : IString()); } NodeRef parseContinue(char*& src, const char* seps) { skipSpace(src); Frag next(src); - if (next.type == IDENT) src += next.size; + if (next.type == IDENT) + src += next.size; return Builder::makeContinue(next.type == IDENT ? next.str : IString()); } @@ -535,7 +618,8 @@ class Parser { while (1) { // find all cases and possibly a default skipSpace(src); - if (*src == '}') break; + if (*src == '}') + break; Frag next(src); if (next.type == KEYWORD) { if (next.str == CASE) { @@ -575,12 +659,14 @@ class Parser { src++; continue; } - // otherwise, may be some keyword that happens to start a block (e.g. case 1: _return_ 5) + // otherwise, may be some keyword that happens to start a block (e.g. + // case 1: _return_ 5) } // not case X: or default: or }, so must be some code skipSpace(src); bool explicitBlock = *src == '{'; - NodeRef subBlock = explicitBlock ? parseBracketedBlock(src) : parseBlock(src, ";}", CASE, DEFAULT); + NodeRef subBlock = explicitBlock ? parseBracketedBlock(src) + : parseBlock(src, ";}", CASE, DEFAULT); Builder::appendCodeToSwitch(ret, subBlock, explicitBlock); } skipSpace(src); @@ -595,34 +681,40 @@ class Parser { NodeRef parseAfterIdent(Frag& frag, char*& src, const char* seps) { skipSpace(src); - if (*src == '(') return parseExpression(parseCall(parseFrag(frag), src), src, seps); - if (*src == '[') return parseExpression(parseIndexing(parseFrag(frag), src), src, seps); + if (*src == '(') + return parseExpression(parseCall(parseFrag(frag), src), src, seps); + if (*src == '[') + return parseExpression(parseIndexing(parseFrag(frag), src), src, seps); if (*src == ':' && expressionPartsStack.back().size() == 0) { src++; skipSpace(src); NodeRef inner; - if (*src == '{') { // context lets us know this is not an object, but a block + if (*src == '{') { + // context lets us know this is not an object, but a block inner = parseBracketedBlock(src); } else { inner = parseElement(src, seps); } return Builder::makeLabel(frag.str, inner); } - if (*src == '.') return parseExpression(parseDotting(parseFrag(frag), src), src, seps); + if (*src == '.') + return parseExpression(parseDotting(parseFrag(frag), src), src, seps); return parseExpression(parseFrag(frag), src, seps); } NodeRef parseCall(NodeRef target, char*& src) { - expressionPartsStack.resize(expressionPartsStack.size()+1); + expressionPartsStack.resize(expressionPartsStack.size() + 1); assert(*src == '('); src++; NodeRef ret = Builder::makeCall(target); while (1) { skipSpace(src); - if (*src == ')') break; + if (*src == ')') + break; Builder::appendToCall(ret, parseElement(src, ",)")); skipSpace(src); - if (*src == ')') break; + if (*src == ')') + break; if (*src == ',') { src++; continue; @@ -636,7 +728,7 @@ class Parser { } NodeRef parseIndexing(NodeRef target, char*& src) { - expressionPartsStack.resize(expressionPartsStack.size()+1); + expressionPartsStack.resize(expressionPartsStack.size() + 1); assert(*src == '['); src++; NodeRef ret = Builder::makeIndexing(target, parseElement(src, "]")); @@ -658,7 +750,7 @@ class Parser { } NodeRef parseAfterParen(char*& src) { - expressionPartsStack.resize(expressionPartsStack.size()+1); + expressionPartsStack.resize(expressionPartsStack.size() + 1); skipSpace(src); NodeRef ret = parseElement(src, ")"); skipSpace(src); @@ -670,16 +762,18 @@ class Parser { } NodeRef parseAfterBrace(char*& src) { - expressionPartsStack.resize(expressionPartsStack.size()+1); + expressionPartsStack.resize(expressionPartsStack.size() + 1); NodeRef ret = Builder::makeArray(); while (1) { skipSpace(src); assert(*src); - if (*src == ']') break; + if (*src == ']') + break; NodeRef element = parseElement(src, ",]"); Builder::appendToArray(ret, element); skipSpace(src); - if (*src == ']') break; + if (*src == ']') + break; if (*src == ',') { src++; continue; @@ -691,12 +785,13 @@ class Parser { } NodeRef parseAfterCurly(char*& src) { - expressionPartsStack.resize(expressionPartsStack.size()+1); + expressionPartsStack.resize(expressionPartsStack.size() + 1); NodeRef ret = Builder::makeObject(); while (1) { skipSpace(src); assert(*src); - if (*src == '}') break; + if (*src == '}') + break; Frag key(src); assert(key.type == IDENT || key.type == STRING); src += key.size; @@ -706,7 +801,8 @@ class Parser { NodeRef value = parseElement(src, ",}"); Builder::appendToObject(ret, key.str, value); skipSpace(src); - if (*src == '}') break; + if (*src == '}') + break; if (*src == ',') { src++; continue; @@ -735,12 +831,13 @@ class Parser { if (op == PERIOD) { return Builder::makeDot(left, right); } else { - return Builder::makeBinary(left, op ,right); + return Builder::makeBinary(left, op, right); } } - NodeRef parseExpression(ExpressionElement initial, char*&src, const char* seps) { - //dump("parseExpression", src); + NodeRef + parseExpression(ExpressionElement initial, char*& src, const char* seps) { + // dump("parseExpression", src); ExpressionParts& parts = expressionPartsStack.back(); skipSpace(src); if (*src == 0 || hasChar(seps, *src)) { @@ -771,55 +868,77 @@ class Parser { parts.push_back(initial); } NodeRef last = parseElement(src, seps); - if (!top) return last; + if (!top) + return last; { - ExpressionParts& parts = expressionPartsStack.back(); // |parts| may have been invalidated by that call + // |parts| may have been invalidated by that call + ExpressionParts& parts = expressionPartsStack.back(); // we are the toplevel. sort it all out // collapse right to left, highest priority first - //dumpParts(parts, 0); + // dumpParts(parts, 0); for (auto& ops : operatorClasses) { if (ops.rtl) { // right to left - for (int i = parts.size()-1; i >= 0; i--) { - if (parts[i].isNode) continue; + for (int i = parts.size() - 1; i >= 0; i--) { + if (parts[i].isNode) + continue; IString op = parts[i].getOp(); - if (!ops.ops.has(op)) continue; - if (ops.type == OperatorClass::Binary && i > 0 && i < (int)parts.size()-1) { - parts[i] = makeBinary(parts[i-1].getNode(), op, parts[i+1].getNode()); + if (!ops.ops.has(op)) + continue; + if (ops.type == OperatorClass::Binary && i > 0 && + i < (int)parts.size() - 1) { + parts[i] = + makeBinary(parts[i - 1].getNode(), op, parts[i + 1].getNode()); parts.erase(parts.begin() + i + 1); parts.erase(parts.begin() + i - 1); - } else if (ops.type == OperatorClass::Prefix && i < (int)parts.size()-1) { - if (i > 0 && parts[i-1].isNode) continue; // cannot apply prefix operator if it would join two nodes - parts[i] = Builder::makePrefix(op, parts[i+1].getNode()); + } else if (ops.type == OperatorClass::Prefix && + i < (int)parts.size() - 1) { + if (i > 0 && parts[i - 1].isNode) + // cannot apply prefix operator if it would join two nodes + continue; + parts[i] = Builder::makePrefix(op, parts[i + 1].getNode()); parts.erase(parts.begin() + i + 1); } else if (ops.type == OperatorClass::Tertiary) { // we must be at X ? Y : Z // ^ - //dumpParts(parts, i); - if (op != COLON) continue; - assert(i < (int)parts.size()-1 && i >= 3); - if (parts[i-2].getOp() != QUESTION) continue; // e.g. x ? y ? 1 : 0 : 2 - parts[i-3] = Builder::makeConditional(parts[i-3].getNode(), parts[i-1].getNode(), parts[i+1].getNode()); + // dumpParts(parts, i); + if (op != COLON) + continue; + assert(i < (int)parts.size() - 1 && i >= 3); + if (parts[i - 2].getOp() != QUESTION) + continue; // e.g. x ? y ? 1 : 0 : 2 + parts[i - 3] = Builder::makeConditional(parts[i - 3].getNode(), + parts[i - 1].getNode(), + parts[i + 1].getNode()); parts.erase(parts.begin() + i - 2, parts.begin() + i + 2); - i = parts.size(); // basically a reset, due to things like x ? y ? 1 : 0 : 2 + // basically a reset, due to things like x ? y ? 1 : 0 : 2 + i = parts.size(); } // TODO: postfix } } else { // left to right for (int i = 0; i < (int)parts.size(); i++) { - if (parts[i].isNode) continue; + if (parts[i].isNode) + continue; IString op = parts[i].getOp(); - if (!ops.ops.has(op)) continue; - if (ops.type == OperatorClass::Binary && i > 0 && i < (int)parts.size()-1) { - parts[i] = makeBinary(parts[i-1].getNode(), op, parts[i+1].getNode()); + if (!ops.ops.has(op)) + continue; + if (ops.type == OperatorClass::Binary && i > 0 && + i < (int)parts.size() - 1) { + parts[i] = + makeBinary(parts[i - 1].getNode(), op, parts[i + 1].getNode()); parts.erase(parts.begin() + i + 1); parts.erase(parts.begin() + i - 1); i--; - } else if (ops.type == OperatorClass::Prefix && i < (int)parts.size()-1) { - if (i > 0 && parts[i-1].isNode) continue; // cannot apply prefix operator if it would join two nodes - parts[i] = Builder::makePrefix(op, parts[i+1].getNode()); + } else if (ops.type == OperatorClass::Prefix && + i < (int)parts.size() - 1) { + if (i > 0 && parts[i - 1].isNode) + // cannot apply prefix operator if it would join two nodes + continue; + parts[i] = Builder::makePrefix(op, parts[i + 1].getNode()); parts.erase(parts.begin() + i + 1); - i = std::max(i-2, 0); // allow a previous prefix operator to cascade + // allow a previous prefix operator to cascade + i = std::max(i - 2, 0); } // TODO: tertiary, postfix } } @@ -831,25 +950,33 @@ class Parser { } } - // Parses a block of code (e.g. a bunch of statements inside {,}, or the top level of o file) - NodeRef parseBlock(char*& src, const char* seps=";", IString keywordSep1=IString(), IString keywordSep2=IString()) { + // Parses a block of code (e.g. a bunch of statements inside {,}, or the top + // level of o file) + NodeRef parseBlock(char*& src, + const char* seps = ";", + IString keywordSep1 = IString(), + IString keywordSep2 = IString()) { NodeRef block = Builder::makeBlock(); - //dump("parseBlock", src); + // dump("parseBlock", src); while (1) { skipSpace(src); - if (*src == 0) break; + if (*src == 0) + break; if (*src == ';') { src++; // skip a statement in this block continue; } - if (hasChar(seps, *src)) break; + if (hasChar(seps, *src)) + break; if (!!keywordSep1) { Frag next(src); - if (next.type == KEYWORD && next.str == keywordSep1) break; + if (next.type == KEYWORD && next.str == keywordSep1) + break; } if (!!keywordSep2) { Frag next(src); - if (next.type == KEYWORD && next.str == keywordSep2) break; + if (next.type == KEYWORD && next.str == keywordSep2) + break; } NodeRef element = parseElementOrStatement(src, seps); Builder::appendToBlock(block, element); @@ -861,25 +988,29 @@ class Parser { skipSpace(src); assert(*src == '{'); src++; - NodeRef block = parseBlock(src, ";}"); // the two are not symmetrical, ; is just internally separating, } is the final one - parseBlock knows all this + // the two are not symmetrical, ; is just internally separating, } is the + // final one - parseBlock knows all this + NodeRef block = parseBlock(src, ";}"); assert(*src == '}'); src++; return block; } - NodeRef parseElementOrStatement(char*& src, const char *seps) { + NodeRef parseElementOrStatement(char*& src, const char* seps) { skipSpace(src); if (*src == ';') { src++; - return Builder::makeBlock(); // we don't need the brackets here, but oh well + // we don't need the brackets here, but oh well + return Builder::makeBlock(); } if (*src == '{') { // detect a trivial {} in a statement context - char *before = src; + char* before = src; src++; skipSpace(src); if (*src == '}') { src++; - return Builder::makeBlock(); // we don't need the brackets here, but oh well + // we don't need the brackets here, but oh well + return Builder::makeBlock(); } src = before; } @@ -892,9 +1023,10 @@ class Parser { return ret; } - NodeRef parseMaybeBracketed(char*& src, const char *seps) { + NodeRef parseMaybeBracketed(char*& src, const char* seps) { skipSpace(src); - return *src == '{' ? parseBracketedBlock(src) : parseElementOrStatement(src, seps); + return *src == '{' ? parseBracketedBlock(src) + : parseElementOrStatement(src, seps); } NodeRef parseParenned(char*& src) { @@ -910,13 +1042,15 @@ class Parser { // Debugging - char *allSource = nullptr; + char* allSource = nullptr; int allSize = 0; - static void dump(const char *where, char* curr) { + static void dump(const char* where, char* curr) { /* printf("%s:\n=============\n", where); - for (int i = 0; i < allSize; i++) printf("%c", allSource[i] ? allSource[i] : '?'); + for (int i = 0; i < allSize; i++) + printf("%c", allSource[i] ? allSource[i] : + '?'); printf("\n"); for (int i = 0; i < (curr - allSource); i++) printf(" "); printf("^\n=============\n"); @@ -927,20 +1061,19 @@ class Parser { while (*curr) { if (*curr == '\n') { newlinesLeft--; - if (newlinesLeft == 0) break; + if (newlinesLeft == 0) + break; } charsLeft--; - if (charsLeft == 0) break; + if (charsLeft == 0) + break; fprintf(stderr, "%c", *curr++); } fprintf(stderr, "\n\n"); } public: - - Parser() { - expressionPartsStack.resize(1); - } + Parser() { expressionPartsStack.resize(1); } // Highest-level parsing, as of a JavaScript script file. NodeRef parseToplevel(char* src) { diff --git a/src/emscripten-optimizer/simple_ast.cpp b/src/emscripten-optimizer/simple_ast.cpp index 7575cc6f7..5853b3289 100644 --- a/src/emscripten-optimizer/simple_ast.cpp +++ b/src/emscripten-optimizer/simple_ast.cpp @@ -20,37 +20,29 @@ namespace cashew { // Ref methods -Ref& Ref::operator[](unsigned x) { - return (*get())[x]; -} +Ref& Ref::operator[](unsigned x) { return (*get())[x]; } -Ref& Ref::operator[](IString x) { - return (*get())[x]; -} +Ref& Ref::operator[](IString x) { return (*get())[x]; } -bool Ref::operator==(const char *str) { +bool Ref::operator==(const char* str) { return get()->isString() && !strcmp(get()->str.str, str); } -bool Ref::operator!=(const char *str) { +bool Ref::operator!=(const char* str) { return get()->isString() ? !!strcmp(get()->str.str, str) : true; } -bool Ref::operator==(const IString &str) { +bool Ref::operator==(const IString& str) { return get()->isString() && get()->str == str; } -bool Ref::operator!=(const IString &str) { +bool Ref::operator!=(const IString& str) { return get()->isString() && get()->str != str; } -bool Ref::operator==(Ref other) { - return **this == *other; -} +bool Ref::operator==(Ref other) { return **this == *other; } -bool Ref::operator!() { - return !get() || get()->isNull(); -} +bool Ref::operator!() { return !get() || get()->isNull(); } // Arena @@ -80,9 +72,13 @@ AssignName* Value::asAssignName() { return static_cast<AssignName*>(this); } -void Value::stringify(std::ostream &os, bool pretty) { +void Value::stringify(std::ostream& os, bool pretty) { static int indent = 0; - #define indentify() { for (int i_ = 0; i_ < indent; i_++) os << " "; } +#define indentify() \ + { \ + for (int i_ = 0; i_ < indent; i_++) \ + os << " "; \ + } switch (type) { case String: { if (str.str) { @@ -93,7 +89,8 @@ void Value::stringify(std::ostream &os, bool pretty) { break; } case Number: { - os << std::setprecision(17) << num; // doubles can have 17 digits of precision + // doubles can have 17 digits of precision + os << std::setprecision(17) << num; break; } case Array: { @@ -108,8 +105,10 @@ void Value::stringify(std::ostream &os, bool pretty) { } for (size_t i = 0; i < arr->size(); i++) { if (i > 0) { - if (pretty) os << "," << std::endl; - else os << ", "; + if (pretty) + os << "," << std::endl; + else + os << ", "; } indentify(); (*arr)[i]->stringify(os, pretty); @@ -142,7 +141,8 @@ void Value::stringify(std::ostream &os, bool pretty) { first = false; } else { os << ", "; - if (pretty) os << std::endl; + if (pretty) + os << std::endl; } indentify(); os << '"' << i.first.c_str() << "\": "; @@ -176,10 +176,12 @@ void Value::stringify(std::ostream &os, bool pretty) { // dump -void dump(const char *str, Ref node, bool pretty) { +void dump(const char* str, Ref node, bool pretty) { std::cerr << str << ": "; - if (!!node) node->stringify(std::cerr, pretty); - else std::cerr << "(nullptr)"; + if (!!node) + node->stringify(std::cerr, pretty); + else + std::cerr << "(nullptr)"; std::cerr << std::endl; } diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h index 81d20e612..e4f2c1f86 100644 --- a/src/emscripten-optimizer/simple_ast.h +++ b/src/emscripten-optimizer/simple_ast.h @@ -33,10 +33,10 @@ #include <unordered_set> #include <vector> +#include "mixed_arena.h" #include "parser.h" #include "snprintf.h" #include "support/safe_integer.h" -#include "mixed_arena.h" #define err(str) fprintf(stderr, str "\n"); #define errv(str, ...) fprintf(stderr, str "\n", __VA_ARGS__); @@ -47,13 +47,13 @@ namespace cashew { struct Value; struct Ref; -void dump(const char *str, Ref node, bool pretty=false); +void dump(const char* str, Ref node, bool pretty = false); // Reference to a value, plus some operators for convenience struct Ref { Value* inst; - Ref(Value *v=nullptr) : inst(v) {} + Ref(Value* v = nullptr) : inst(v) {} Value* get() { return inst; } @@ -63,11 +63,16 @@ struct Ref { Ref& operator[](IString x); // special conveniences - bool operator==(const char *str); // comparison to string, which is by value - bool operator!=(const char *str); - bool operator==(const IString &str); - bool operator!=(const IString &str); - bool operator==(double d) { abort(); return false; } // prevent Ref == number, which is potentially ambiguous; use ->getNumber() == number + bool operator==(const char* str); // comparison to string, which is by value + bool operator!=(const char* str); + bool operator==(const IString& str); + bool operator!=(const IString& str); + // prevent Ref == number, which is potentially ambiguous; use ->getNumber() == + // number + bool operator==(double d) { + abort(); + return false; + } bool operator==(Ref other); bool operator!(); // check if null, in effect }; @@ -78,8 +83,7 @@ struct Ref { // receive an allocator, they all use the global one anyhow class GlobalMixedArena : public MixedArena { public: - template<class T> - T* alloc() { + template<class T> T* alloc() { auto* ret = static_cast<T*>(allocSpace(sizeof(T), alignof(T))); new (ret) T(); return ret; @@ -92,7 +96,8 @@ class ArrayStorage : public ArenaVectorBase<ArrayStorage, Ref> { public: void allocate(size_t size) { allocatedElements = size; - data = static_cast<Ref*>(arena.allocSpace(sizeof(Ref) * allocatedElements, alignof(Ref))); + data = static_cast<Ref*>( + arena.allocSpace(sizeof(Ref) * allocatedElements, alignof(Ref))); } }; @@ -116,7 +121,9 @@ struct Value { typedef std::unordered_map<IString, Ref> ObjectStorage; -#ifdef _MSC_VER // MSVC does not allow unrestricted unions: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf + // MSVC does not allow unrestricted unions: + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf +#ifdef _MSC_VER IString str; #endif union { // TODO: optimize @@ -124,44 +131,41 @@ struct Value { IString str; #endif double num; - ArrayStorage *arr; + ArrayStorage* arr; bool boo; - ObjectStorage *obj; + ObjectStorage* obj; Ref ref; }; // constructors all copy their input Value() {} - explicit Value(const char *s) { - setString(s); - } - explicit Value(double n) { - setNumber(n); - } - explicit Value(ArrayStorage &a) { + explicit Value(const char* s) { setString(s); } + explicit Value(double n) { setNumber(n); } + explicit Value(ArrayStorage& a) { setArray(); *arr = a; } - // no bool constructor - would endanger the double one (int might convert the wrong way) + // no bool constructor - would endanger the double one (int might convert the + // wrong way) - ~Value() { - free(); - } + ~Value() { free(); } void free() { - if (type == Array) { arr->clear(); } - else if (type == Object) delete obj; + if (type == Array) { + arr->clear(); + } else if (type == Object) + delete obj; type = Null; num = 0; } - Value& setString(const char *s) { + Value& setString(const char* s) { free(); type = String; str.set(s); return *this; } - Value& setString(const IString &s) { + Value& setString(const IString& s) { free(); type = String; str.set(s); @@ -173,14 +177,14 @@ struct Value { num = n; return *this; } - Value& setArray(ArrayStorage &a) { + Value& setArray(ArrayStorage& a) { free(); type = Array; arr = arena.alloc<ArrayStorage>(); *arr = a; return *this; } - Value& setArray(size_t size_hint=0) { + Value& setArray(size_t size_hint = 0) { free(); type = Array; arr = arena.alloc<ArrayStorage>(); @@ -192,7 +196,8 @@ struct Value { type = Null; return *this; } - Value& setBool(bool b) { // Bool in the name, as otherwise might overload over int + // Bool in the name, as otherwise might overload over int + Value& setBool(bool b) { free(); type = Bool; boo = b; @@ -209,22 +214,21 @@ struct Value { bool isString() { return type == String; } bool isNumber() { return type == Number; } - bool isArray() { return type == Array; } - bool isNull() { return type == Null; } - bool isBool() { return type == Bool; } + bool isArray() { return type == Array; } + bool isNull() { return type == Null; } + bool isBool() { return type == Bool; } bool isObject() { return type == Object; } bool isAssign() { return type == Assign_; } bool isAssignName() { return type == AssignName_; } - bool isBool(bool b) { return type == Bool && b == boo; } // avoid overloading == as it might overload over int + // avoid overloading == as it might overload over int + bool isBool(bool b) { return type == Bool && b == boo; } // convenience function to check if something is an array and // also has a certain string as the first element. This is a // very common operation as the first element defines the node // type for most ast nodes - bool isArray(IString name) { - return isArray() && (*this)[0] == name; - } + bool isArray(IString name) { return isArray() && (*this)[0] == name; } const char* getCString() { assert(isString()); @@ -282,7 +286,8 @@ struct Value { } bool operator==(const Value& other) { - if (type != other.type) return false; + if (type != other.type) + return false; switch (other.type) { case String: return str == other.str; @@ -303,17 +308,22 @@ struct Value { } char* parse(char* curr) { - #define is_json_space(x) (x == 32 || x == 9 || x == 10 || x == 13) /* space, tab, linefeed/newline, or return */ - #define skip() { while (*curr && is_json_space(*curr)) curr++; } + /* space, tab, linefeed/newline, or return */ +#define is_json_space(x) (x == 32 || x == 9 || x == 10 || x == 13) +#define skip() \ + { \ + while (*curr && is_json_space(*curr)) \ + curr++; \ + } skip(); if (*curr == '"') { // String curr++; - char *close = strchr(curr, '"'); + char* close = strchr(curr, '"'); assert(close); *close = 0; // end this string, and reuse it straight from the input setString(curr); - curr = close+1; + curr = close + 1; } else if (*curr == '[') { // Array curr++; @@ -324,7 +334,8 @@ struct Value { arr->push_back(temp); curr = temp->parse(curr); skip(); - if (*curr == ']') break; + if (*curr == ']') + break; assert(*curr == ','); curr++; skip(); @@ -353,11 +364,11 @@ struct Value { while (*curr != '}') { assert(*curr == '"'); curr++; - char *close = strchr(curr, '"'); + char* close = strchr(curr, '"'); assert(close); *close = 0; // end this string, and reuse it straight from the input IString key(curr); - curr = close+1; + curr = close + 1; skip(); assert(*curr == ':'); curr++; @@ -366,7 +377,8 @@ struct Value { curr = value->parse(curr); (*obj)[key] = value; skip(); - if (*curr == '}') break; + if (*curr == '}') + break; assert(*curr == ','); curr++; skip(); @@ -374,14 +386,14 @@ struct Value { curr++; } else { // Number - char *after; + char* after; setNumber(strtod(curr, &after)); curr = after; } return curr; } - void stringify(std::ostream &os, bool pretty=false); + void stringify(std::ostream& os, bool pretty = false); // String operations @@ -394,14 +406,13 @@ struct Value { return arr->size(); } - bool empty() { - return size() == 0; - } + bool empty() { return size() == 0; } void setSize(size_t size) { assert(isArray()); auto old = arr->size(); - if (old != size) arr->resize(size); + if (old != size) + arr->resize(size); if (old < size) { for (auto i = old; i < size; i++) { (*arr)[i] = arena.alloc<Value>(); @@ -428,7 +439,8 @@ struct Value { Ref back() { assert(isArray()); - if (arr->size() == 0) return nullptr; + if (arr->size() == 0) + return nullptr; return arr->back(); } @@ -440,12 +452,13 @@ struct Value { int indexOf(Ref other) { assert(isArray()); for (size_t i = 0; i < arr->size(); i++) { - if (other == (*arr)[i]) return i; + if (other == (*arr)[i]) + return i; } return -1; } - Ref map(std::function<Ref (Ref node)> func) { + Ref map(std::function<Ref(Ref node)> func) { assert(isArray()); Ref ret = arena.alloc<Value>(); ret->setArray(); @@ -455,13 +468,14 @@ struct Value { return ret; } - Ref filter(std::function<bool (Ref node)> func) { + Ref filter(std::function<bool(Ref node)> func) { assert(isArray()); Ref ret = arena.alloc<Value>(); ret->setArray(); for (size_t i = 0; i < arr->size(); i++) { Ref curr = (*arr)[i]; - if (func(curr)) ret->push_back(curr); + if (func(curr)) + ret->push_back(curr); } return ret; } @@ -502,12 +516,8 @@ struct Assign : public Value { Assign() : Assign(nullptr, nullptr) {} - Ref& target() { - return ref; - } - Ref& value() { - return value_; - } + Ref& target() { return ref; } + Ref& value() { return value_; } }; struct AssignName : public Value { @@ -521,12 +531,8 @@ struct AssignName : public Value { AssignName() : AssignName(IString(), nullptr) {} - IString& target() { - return target_; - } - Ref& value() { - return ref; - } + IString& target() { return target_; } + Ref& value() { return ref; } }; // JS printing support @@ -534,7 +540,7 @@ struct AssignName : public Value { struct JSPrinter { bool pretty, finalize; - char *buffer = nullptr; + char* buffer = nullptr; size_t size = 0; size_t used = 0; @@ -543,11 +549,10 @@ struct JSPrinter { Ref ast; - JSPrinter(bool pretty_, bool finalize_, Ref ast_) : pretty(pretty_), finalize(finalize_), ast(ast_) {} + JSPrinter(bool pretty_, bool finalize_, Ref ast_) + : pretty(pretty_), finalize(finalize_), ast(ast_) {} - ~JSPrinter() { - free(buffer); - } + ~JSPrinter() { free(buffer); } void printAst() { print(ast); @@ -557,7 +562,7 @@ struct JSPrinter { // Utils - void ensure(int safety=100) { + void ensure(int safety = 100) { if (size >= used + safety) { return; } @@ -569,7 +574,7 @@ struct JSPrinter { abort(); } } else { - char *buf = (char*)realloc(buffer, size); + char* buf = (char*)realloc(buffer, size); if (!buf) { free(buffer); errv("Out of memory allocating %zd bytes for output buffer!", size); @@ -581,38 +586,45 @@ struct JSPrinter { void emit(char c) { maybeSpace(c); - if (!pretty && c == '}' && buffer[used-1] == ';') used--; // optimize ;} into }, the ; is not separating anything + if (!pretty && c == '}' && buffer[used - 1] == ';') + used--; // optimize ;} into }, the ; is not separating anything ensure(1); buffer[used++] = c; } - void emit(const char *s) { + void emit(const char* s) { maybeSpace(*s); int len = strlen(s); - ensure(len+1); - strncpy(buffer + used, s, len+1); + ensure(len + 1); + strncpy(buffer + used, s, len + 1); used += len; } void newline() { - if (!pretty) return; + if (!pretty) + return; emit('\n'); - for (int i = 0; i < indent; i++) emit(' '); + for (int i = 0; i < indent; i++) + emit(' '); } void space() { - if (pretty) emit(' '); + if (pretty) + emit(' '); } void safeSpace() { - if (pretty) emit(' '); - else possibleSpace = true; + if (pretty) + emit(' '); + else + possibleSpace = true; } void maybeSpace(char s) { if (possibleSpace) { possibleSpace = false; - if (isIdentPart(s)) emit(' '); + if (isIdentPart(s)) + emit(' '); } } @@ -620,22 +632,22 @@ struct JSPrinter { return node->isArray() && node[0] == TOPLEVEL && node[1]->size() == 0; } - bool isDefun(Ref node) { - return node->isArray() && node[0] == DEFUN; - } + bool isDefun(Ref node) { return node->isArray() && node[0] == DEFUN; } bool endsInBlock(Ref node) { - if (node->isArray() && node[0] == BLOCK) return true; + if (node->isArray() && node[0] == BLOCK) + return true; // Check for a label on a block - if (node->isArray() && node[0] == LABEL && endsInBlock(node[2])) return true; + if (node->isArray() && node[0] == LABEL && endsInBlock(node[2])) + return true; // Check for an if - if (node->isArray() && node[0] == IF && endsInBlock(ifHasElse(node) ? node[3] : node[2])) return true; + if (node->isArray() && node[0] == IF && + endsInBlock(ifHasElse(node) ? node[3] : node[2])) + return true; return false; } - bool isIf(Ref node) { - return node->isArray() && node[0] == IF; - } + bool isIf(Ref node) { return node->isArray() && node[0] == IF; } void print(Ref node) { ensure(); @@ -658,82 +670,119 @@ struct JSPrinter { IString type = node[0]->getIString(); switch (type.str[0]) { case 'a': { - if (type == ARRAY) printArray(node); - else abort(); + if (type == ARRAY) + printArray(node); + else + abort(); break; } case 'b': { - if (type == BINARY) printBinary(node); - else if (type == BLOCK) printBlock(node); - else if (type == BREAK) printBreak(node); - else abort(); + if (type == BINARY) + printBinary(node); + else if (type == BLOCK) + printBlock(node); + else if (type == BREAK) + printBreak(node); + else + abort(); break; } case 'c': { - if (type == CALL) printCall(node); - else if (type == CONDITIONAL) printConditional(node); - else if (type == CONTINUE) printContinue(node); - else abort(); + if (type == CALL) + printCall(node); + else if (type == CONDITIONAL) + printConditional(node); + else if (type == CONTINUE) + printContinue(node); + else + abort(); break; } case 'd': { - if (type == DEFUN) printDefun(node); - else if (type == DO) printDo(node); - else if (type == DOT) printDot(node); - else abort(); + if (type == DEFUN) + printDefun(node); + else if (type == DO) + printDo(node); + else if (type == DOT) + printDot(node); + else + abort(); break; } case 'i': { - if (type == IF) printIf(node); - else abort(); + if (type == IF) + printIf(node); + else + abort(); break; } case 'l': { - if (type == LABEL) printLabel(node); - else abort(); + if (type == LABEL) + printLabel(node); + else + abort(); break; } case 'n': { - if (type == NEW) printNew(node); - else abort(); + if (type == NEW) + printNew(node); + else + abort(); break; } case 'o': { - if (type == OBJECT) printObject(node); + if (type == OBJECT) + printObject(node); break; } case 'r': { - if (type == RETURN) printReturn(node); - else abort(); + if (type == RETURN) + printReturn(node); + else + abort(); break; } case 's': { - if (type == SUB) printSub(node); - else if (type == SEQ) printSeq(node); - else if (type == SWITCH) printSwitch(node); - else if (type == STRING) printString(node); - else abort(); + if (type == SUB) + printSub(node); + else if (type == SEQ) + printSeq(node); + else if (type == SWITCH) + printSwitch(node); + else if (type == STRING) + printString(node); + else + abort(); break; } case 't': { - if (type == TOPLEVEL) printToplevel(node); - else if (type == TRY) printTry(node); - else abort(); + if (type == TOPLEVEL) + printToplevel(node); + else if (type == TRY) + printTry(node); + else + abort(); break; } case 'u': { - if (type == UNARY_PREFIX) printUnaryPrefix(node); - else abort(); + if (type == UNARY_PREFIX) + printUnaryPrefix(node); + else + abort(); break; } case 'v': { - if (type == VAR) printVar(node); - else abort(); + if (type == VAR) + printVar(node); + else + abort(); break; } case 'w': { - if (type == WHILE) printWhile(node); - else abort(); + if (type == WHILE) + printWhile(node); + else + abort(); break; } default: { @@ -744,10 +793,11 @@ struct JSPrinter { } // print a node, and if nothing is emitted, emit something instead - void print(Ref node, const char *otherwise) { + void print(Ref node, const char* otherwise) { auto last = used; print(node); - if (used == last) emit(otherwise); + if (used == last) + emit(otherwise); } void printStats(Ref stats) { @@ -755,8 +805,10 @@ struct JSPrinter { for (size_t i = 0; i < stats->size(); i++) { Ref curr = stats[i]; if (!isNothing(curr)) { - if (first) first = false; - else newline(); + if (first) + first = false; + else + newline(); print(curr); if (!isDefun(curr) && !endsInBlock(curr) && !isIf(curr)) { emit(';'); @@ -791,7 +843,8 @@ struct JSPrinter { emit('('); Ref args = node[2]; for (size_t i = 0; i < args->size(); i++) { - if (i > 0) (pretty ? emit(", ") : emit(',')); + if (i > 0) + (pretty ? emit(", ") : emit(',')); emit(args[i]->getCString()); } emit(')'); @@ -820,7 +873,7 @@ struct JSPrinter { } void printAssignName(Ref node) { - auto *assign = node->asAssignName(); + auto* assign = node->asAssignName(); emit(assign->target().c_str()); space(); emit('='); @@ -828,11 +881,9 @@ struct JSPrinter { printChild(assign->value(), node, 1); } - void printName(Ref node) { - emit(node->getCString()); - } + void printName(Ref node) { emit(node->getCString()); } - static char* numToString(double d, bool finalize=true) { + static char* numToString(double d, bool finalize = true) { // If this number is NaN or infinite then things are a bit tricky. In JS we // want to eventually use `NaN` and/or `Infinity`, but neither of those // identifiers are valid in asm.js. Instead we have to explicitly import @@ -843,28 +894,32 @@ struct JSPrinter { // asm.js code isn't generated any more if (std::isnan(d)) { if (std::signbit(d)) { - return (char*) "-nan"; + return (char*)"-nan"; } else { - return (char*) "nan"; + return (char*)"nan"; } } else if (!std::isfinite(d)) { if (std::signbit(d)) { - return (char*) "-infinity"; + return (char*)"-infinity"; } else { - return (char*) "infinity"; + return (char*)"infinity"; } } bool neg = d < 0; - if (neg) d = -d; + if (neg) + d = -d; // try to emit the fewest necessary characters bool integer = fmod(d, 1) == 0; - #define BUFFERSIZE 1000 - static char full_storage_f[BUFFERSIZE], full_storage_e[BUFFERSIZE]; // f is normal, e is scientific for float, x for integer - static char *storage_f = full_storage_f + 1, *storage_e = full_storage_e + 1; // full has one more char, for a possible '-' +#define BUFFERSIZE 1000 + // f is normal, e is scientific for float, x for integer + static char full_storage_f[BUFFERSIZE], full_storage_e[BUFFERSIZE]; + // full has one more char, for a possible '-' + static char *storage_f = full_storage_f + 1, + *storage_e = full_storage_e + 1; auto err_f = std::numeric_limits<double>::quiet_NaN(); auto err_e = std::numeric_limits<double>::quiet_NaN(); for (int e = 0; e <= 1; e++) { - char *buffer = e ? storage_e : storage_f; + char* buffer = e ? storage_e : storage_f; double temp; if (!integer) { static char format[6]; @@ -881,10 +936,12 @@ struct JSPrinter { format[4] = e ? 'e' : 'f'; format[5] = 0; } - snprintf(buffer, BUFFERSIZE-1, format, d); + snprintf(buffer, BUFFERSIZE - 1, format, d); sscanf(buffer, "%lf", &temp); - //errv("%.18f, %.18e => %s => %.18f, %.18e (%d), ", d, d, buffer, temp, temp, temp == d); - if (temp == d) break; + // errv("%.18f, %.18e => %s => %.18f, %.18e (%d), ", d, d, + // buffer, temp, temp, temp == d); + if (temp == d) + break; } } else { // integer @@ -892,7 +949,7 @@ struct JSPrinter { if (wasm::isUInteger64(d)) { unsigned long long uu = wasm::toUInteger64(d); bool asHex = e && !finalize; - snprintf(buffer, BUFFERSIZE-1, asHex ? "0x%llx" : "%llu", uu); + snprintf(buffer, BUFFERSIZE - 1, asHex ? "0x%llx" : "%llu", uu); if (asHex) { unsigned long long tempULL; sscanf(buffer, "%llx", &tempULL); @@ -902,43 +959,48 @@ struct JSPrinter { } } else { // too large for a machine integer, just use floats - snprintf(buffer, BUFFERSIZE-1, e ? "%e" : "%.0f", d); // even on integers, e with a dot is useful, e.g. 1.2e+200 + // even on integers, e with a dot is useful, e.g. 1.2e+200 + snprintf(buffer, BUFFERSIZE - 1, e ? "%e" : "%.0f", d); sscanf(buffer, "%lf", &temp); } - //errv("%.18f, %.18e => %s => %.18f, %.18e, %llu (%d)\n", d, d, buffer, temp, temp, uu, temp == d); + // errv("%.18f, %.18e => %s => %.18f, %.18e, %llu (%d)\n", d, + // d, buffer, temp, temp, uu, temp == d); } (e ? err_e : err_f) = fabs(temp - d); - //errv("current attempt: %.18f => %s", d, buffer); - //assert(temp == d); - char *dot = strchr(buffer, '.'); + // errv("current attempt: %.18f => %s", d, buffer); + // assert(temp == d); + char* dot = strchr(buffer, '.'); if (dot) { // remove trailing zeros - char *end = dot+1; - while (*end >= '0' && *end <= '9') end++; + char* end = dot + 1; + while (*end >= '0' && *end <= '9') + end++; end--; while (*end == '0') { - char *copy = end; + char* copy = end; do { copy[0] = copy[1]; } while (*copy++ != 0); end--; } - //errv("%.18f => %s", d, buffer); + // errv("%.18f => %s", d, buffer); // remove preceding zeros while (*buffer == '0') { - char *copy = buffer; + char* copy = buffer; do { copy[0] = copy[1]; } while (*copy++ != 0); } - //errv("%.18f ===> %s", d, buffer); + // errv("%.18f ===> %s", d, buffer); } else if (!integer || !e) { // no dot. try to change 12345000 => 12345e3 - char *end = strchr(buffer, 0); + char* end = strchr(buffer, 0); end--; - char *test = end; - // remove zeros, and also doubles can use at most 24 digits, we can truncate any extras even if not zero - while ((*test == '0' || test - buffer > 24) && test > buffer) test--; + char* test = end; + // remove zeros, and also doubles can use at most 24 digits, we can + // truncate any extras even if not zero + while ((*test == '0' || test - buffer > 24) && test > buffer) + test--; int num = end - test; if (num >= 3) { test++; @@ -959,10 +1021,11 @@ struct JSPrinter { } } } - //errv("..current attempt: %.18f => %s", d, buffer); + // errv("..current attempt: %.18f => %s", d, buffer); } - //fprintf(stderr, "options:\n%s\n%s\n (first? %d)\n", storage_e, storage_f, strlen(storage_e) < strlen(storage_f)); - char *ret; + // fprintf(stderr, "options:\n%s\n%s\n (first? %d)\n", storage_e, storage_f, + // strlen(storage_e) < strlen(storage_f)); + char* ret; if (err_e == err_f) { ret = strlen(storage_e) < strlen(storage_f) ? storage_e : storage_f; } else { @@ -976,7 +1039,7 @@ struct JSPrinter { } void printNum(Ref node) { - if (node->getNumber() < 0 && buffer[used-1] == '-') { + if (node->getNumber() < 0 && buffer[used - 1] == '-') { emit(' '); // cannot join - and - to --, looks like the -- operator } emit(numToString(node->getNumber(), finalize)); @@ -1005,28 +1068,37 @@ struct JSPrinter { } Ref type = node[0]; if (type == BINARY || type == UNARY_PREFIX) { - return OperatorClass::getPrecedence(type == BINARY ? OperatorClass::Binary : OperatorClass::Prefix, node[1]->getIString()); + return OperatorClass::getPrecedence( + type == BINARY ? OperatorClass::Binary : OperatorClass::Prefix, + node[1]->getIString()); } else if (type == SEQ) { return OperatorClass::getPrecedence(OperatorClass::Binary, COMMA); } else if (type == CALL) { - return parent ? OperatorClass::getPrecedence(OperatorClass::Binary, COMMA) : -1; // call arguments are split by commas, but call itself is safe + // call arguments are split by commas, but call itself is safe + return parent ? OperatorClass::getPrecedence(OperatorClass::Binary, COMMA) + : -1; } else if (type == CONDITIONAL) { return OperatorClass::getPrecedence(OperatorClass::Tertiary, QUESTION); } - // otherwise, this is something that fixes precedence explicitly, and we can ignore + // otherwise, this is something that fixes precedence explicitly, and we can + // ignore return -1; // XXX } // check whether we need parens for the child, when rendered in the parent - // @param childPosition -1 means it is printed to the left of parent, 0 means "anywhere", 1 means right + // @param childPosition -1 means it is printed to the left of parent, 0 means + // "anywhere", 1 means right bool needParens(Ref parent, Ref child, int childPosition) { int parentPrecedence = getPrecedence(parent, true); int childPrecedence = getPrecedence(child, false); - if (childPrecedence > parentPrecedence) return true; // child is definitely a danger - if (childPrecedence < parentPrecedence) return false; // definitely cool + if (childPrecedence > parentPrecedence) + return true; // child is definitely a danger + if (childPrecedence < parentPrecedence) + return false; // definitely cool // equal precedence, so associativity (rtl/ltr) is what matters - // (except for some exceptions, where multiple operators can combine into confusion) + // (except for some exceptions, where multiple operators can combine into + // confusion) if (parent->isArray() && parent[0] == UNARY_PREFIX) { assert(child[0] == UNARY_PREFIX); if ((parent[1] == PLUS || parent[1] == MINUS) && child[1] == parent[1]) { @@ -1034,18 +1106,24 @@ struct JSPrinter { return true; } } - if (childPosition == 0) return true; // child could be anywhere, so always paren - if (childPrecedence < 0) return false; // both precedences are safe + if (childPosition == 0) + return true; // child could be anywhere, so always paren + if (childPrecedence < 0) + return false; // both precedences are safe // check if child is on the dangerous side - if (OperatorClass::getRtl(parentPrecedence)) return childPosition < 0; - else return childPosition > 0; + if (OperatorClass::getRtl(parentPrecedence)) + return childPosition < 0; + else + return childPosition > 0; } - void printChild(Ref child, Ref parent, int childPosition=0) { + void printChild(Ref child, Ref parent, int childPosition = 0) { bool parens = needParens(parent, child, childPosition); - if (parens) emit('('); + if (parens) + emit('('); print(child); - if (parens) emit(')'); + if (parens) + emit(')'); } void printBinary(Ref node) { @@ -1064,20 +1142,23 @@ struct JSPrinter { // emit a finalized number int last = used; print(node[2]); - ensure(1); // we temporarily append a 0 - char *curr = buffer + last; // ensure might invalidate + ensure(1); // we temporarily append a 0 + char* curr = buffer + last; // ensure might invalidate buffer[used] = 0; - if (strstr(curr, "infinity")) return; - if (strstr(curr, "nan")) return; - if (strchr(curr, '.')) return; // already a decimal point, all good - char *e = strchr(curr, 'e'); + if (strstr(curr, "infinity")) + return; + if (strstr(curr, "nan")) + return; + if (strchr(curr, '.')) + return; // already a decimal point, all good + char* e = strchr(curr, 'e'); if (!e) { emit(".0"); return; } ensure(3); curr = buffer + last; // ensure might invalidate - char *end = strchr(curr, 0); + char* end = strchr(curr, 0); while (end >= e) { end[2] = end[0]; end--; @@ -1087,8 +1168,8 @@ struct JSPrinter { used += 2; return; } - if ((buffer[used-1] == '-' && node[1] == MINUS) || - (buffer[used-1] == '+' && node[1] == PLUS)) { + if ((buffer[used - 1] == '-' && node[1] == MINUS) || + (buffer[used - 1] == '+' && node[1] == PLUS)) { emit(' '); // cannot join - and - to --, looks like the -- operator } emit(node[1]->getCString()); @@ -1112,7 +1193,8 @@ struct JSPrinter { emit('('); Ref args = node[2]; for (size_t i = 0; i < args->size(); i++) { - if (i > 0) (pretty ? emit(", ") : emit(',')); + if (i > 0) + (pretty ? emit(", ") : emit(',')); printChild(args[i], node, 0); } emit(')'); @@ -1156,8 +1238,10 @@ struct JSPrinter { auto curr = used; printStats(c[1]); indent--; - if (curr != used) newline(); - else used--; // avoid the extra indentation we added tentatively + if (curr != used) + newline(); + else + used--; // avoid the extra indentation we added tentatively } else { newline(); } @@ -1185,7 +1269,8 @@ struct JSPrinter { emit("var "); Ref args = node[1]; for (size_t i = 0; i < args->size(); i++) { - if (i > 0) (pretty ? emit(", ") : emit(',')); + if (i > 0) + (pretty ? emit(", ") : emit(',')); emit(args[i][0]->getCString()); if (args[i]->size() > 1) { space(); @@ -1292,7 +1377,8 @@ struct JSPrinter { emit('['); Ref args = node[1]; for (size_t i = 0; i < args->size(); i++) { - if (i > 0) (pretty ? emit(", ") : emit(',')); + if (i > 0) + (pretty ? emit(", ") : emit(',')); print(args[i]); } emit(']'); @@ -1309,7 +1395,7 @@ struct JSPrinter { newline(); } bool needQuote = false; - const char *str; + const char* str; if (args[i][0]->isArray()) { assert(args[i][0][0] == STRING); // A quoted string. @@ -1319,7 +1405,7 @@ struct JSPrinter { // Just a raw string, no quotes. str = args[i][0]->getCString(); } - const char *check = str; + const char* check = str; while (*check) { if (!isalnum(*check) && *check != '_' && *check != '$') { needQuote = true; @@ -1327,9 +1413,11 @@ struct JSPrinter { } check++; } - if (needQuote) emit('"'); + if (needQuote) + emit('"'); emit(str); - if (needQuote) emit('"'); + if (needQuote) + emit('"'); emit(":"); space(); print(args[i][1]); @@ -1340,7 +1428,6 @@ struct JSPrinter { } }; - // cashew builder class ValueBuilder { @@ -1348,40 +1435,40 @@ class ValueBuilder { return &arena.alloc<Value>()->setString(s); } - static Ref makeNull() { - return &arena.alloc<Value>()->setNull(); - } + static Ref makeNull() { return &arena.alloc<Value>()->setNull(); } public: - static Ref makeRawArray(int size_hint=0) { + static Ref makeRawArray(int size_hint = 0) { return &arena.alloc<Value>()->setArray(size_hint); } static Ref makeToplevel() { - return &makeRawArray(2)->push_back(makeRawString(TOPLEVEL)) - .push_back(makeRawArray()); + return &makeRawArray(2) + ->push_back(makeRawString(TOPLEVEL)) + .push_back(makeRawArray()); } static Ref makeString(IString str) { - return &makeRawArray(2)->push_back(makeRawString(STRING)) - .push_back(makeRawString(str)); + return &makeRawArray(2) + ->push_back(makeRawString(STRING)) + .push_back(makeRawString(str)); } static Ref makeBlock() { - return &makeRawArray(2)->push_back(makeRawString(BLOCK)) - .push_back(makeRawArray()); + return &makeRawArray(2) + ->push_back(makeRawString(BLOCK)) + .push_back(makeRawArray()); } - static Ref makeName(IString name) { - return makeRawString(name); - } + static Ref makeName(IString name) { return makeRawString(name); } static void setBlockContent(Ref target, Ref block) { if (target[0] == TOPLEVEL) { target[1]->setArray(block[1]->getArray()); } else if (target[0] == DEFUN) { target[3]->setArray(block[1]->getArray()); - } else abort(); + } else + abort(); } static void appendToBlock(Ref block, Ref element) { @@ -1390,35 +1477,38 @@ public: } static Ref makeCall(Ref target) { - return &makeRawArray(3)->push_back(makeRawString(CALL)) - .push_back(target) - .push_back(makeRawArray()); + return &makeRawArray(3) + ->push_back(makeRawString(CALL)) + .push_back(target) + .push_back(makeRawArray()); } static Ref makeCall(Ref target, Ref arg) { - Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) - .push_back(target) - .push_back(makeRawArray()); + Ref ret = &makeRawArray(3) + ->push_back(makeRawString(CALL)) + .push_back(target) + .push_back(makeRawArray()); ret[2]->push_back(arg); return ret; } static Ref makeCall(IString target) { - Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) - .push_back(makeName(target)) - .push_back(makeRawArray()); + Ref ret = &makeRawArray(3) + ->push_back(makeRawString(CALL)) + .push_back(makeName(target)) + .push_back(makeRawArray()); return ret; } - template<typename ...Ts> - static Ref makeCall(IString target, Ts... args) { + template<typename... Ts> static Ref makeCall(IString target, Ts... args) { size_t nArgs = sizeof...(Ts); Ref callArgs = makeRawArray(nArgs); Ref argArray[] = {args...}; for (size_t i = 0; i < nArgs; ++i) { callArgs->push_back(argArray[i]); } - return &makeRawArray(3)->push_back(makeRawString(CALL)) - .push_back(makeName(target)) - .push_back(callArgs); + return &makeRawArray(3) + ->push_back(makeRawString(CALL)) + .push_back(makeName(target)) + .push_back(callArgs); } static void appendToCall(Ref call, Ref element) { @@ -1426,59 +1516,57 @@ public: call[2]->push_back(element); } - static Ref makeStatement(Ref contents) { - return contents; - } + static Ref makeStatement(Ref contents) { return contents; } static Ref makeDouble(double num) { return &arena.alloc<Value>()->setNumber(num); } - static Ref makeInt(uint32_t num) { - return makeDouble(double(num)); - } - static Ref makeInt(int32_t num) { - return makeDouble(double(num)); - } - static Ref makeNum(double num) { - return makeDouble(num); - } + static Ref makeInt(uint32_t num) { return makeDouble(double(num)); } + static Ref makeInt(int32_t num) { return makeDouble(double(num)); } + static Ref makeNum(double num) { return makeDouble(num); } static Ref makeUnary(IString op, Ref value) { - return &makeRawArray(3)->push_back(makeRawString(UNARY_PREFIX)) - .push_back(makeRawString(op)) - .push_back(value); + return &makeRawArray(3) + ->push_back(makeRawString(UNARY_PREFIX)) + .push_back(makeRawString(op)) + .push_back(value); } static Ref makeBinary(Ref left, IString op, Ref right) { if (op == SET) { if (left->isString()) { - return &arena.alloc<AssignName>()->setAssignName(left->getIString(), right); + return &arena.alloc<AssignName>()->setAssignName(left->getIString(), + right); } else { return &arena.alloc<Assign>()->setAssign(left, right); } } else if (op == COMMA) { - return &makeRawArray(3)->push_back(makeRawString(SEQ)) - .push_back(left) - .push_back(right); + return &makeRawArray(3) + ->push_back(makeRawString(SEQ)) + .push_back(left) + .push_back(right); } else { - return &makeRawArray(4)->push_back(makeRawString(BINARY)) - .push_back(makeRawString(op)) - .push_back(left) - .push_back(right); + return &makeRawArray(4) + ->push_back(makeRawString(BINARY)) + .push_back(makeRawString(op)) + .push_back(left) + .push_back(right); } } static Ref makePrefix(IString op, Ref right) { - return &makeRawArray(3)->push_back(makeRawString(UNARY_PREFIX)) - .push_back(makeRawString(op)) - .push_back(right); + return &makeRawArray(3) + ->push_back(makeRawString(UNARY_PREFIX)) + .push_back(makeRawString(op)) + .push_back(right); } static Ref makeFunction(IString name) { - return &makeRawArray(4)->push_back(makeRawString(DEFUN)) - .push_back(makeRawString(name)) - .push_back(makeRawArray()) - .push_back(makeRawArray()); + return &makeRawArray(4) + ->push_back(makeRawString(DEFUN)) + .push_back(makeRawString(name)) + .push_back(makeRawArray()) + .push_back(makeRawArray()); } static void appendArgumentToFunction(Ref func, IString arg) { @@ -1486,101 +1574,115 @@ public: func[2]->push_back(makeRawString(arg)); } - static Ref makeVar(bool is_const=false) { - return &makeRawArray(2)->push_back(makeRawString(VAR)) - .push_back(makeRawArray()); + static Ref makeVar(bool is_const = false) { + return &makeRawArray(2) + ->push_back(makeRawString(VAR)) + .push_back(makeRawArray()); } static void appendToVar(Ref var, IString name, Ref value) { assert(var[0] == VAR); Ref array = &makeRawArray(1)->push_back(makeRawString(name)); - if (!!value) array->push_back(value); + if (!!value) + array->push_back(value); var[1]->push_back(array); } static Ref makeReturn(Ref value) { - return &makeRawArray(2)->push_back(makeRawString(RETURN)) - .push_back(!!value ? value : makeNull()); + return &makeRawArray(2) + ->push_back(makeRawString(RETURN)) + .push_back(!!value ? value : makeNull()); } static Ref makeIndexing(Ref target, Ref index) { - return &makeRawArray(3)->push_back(makeRawString(SUB)) - .push_back(target) - .push_back(index); + return &makeRawArray(3) + ->push_back(makeRawString(SUB)) + .push_back(target) + .push_back(index); } static Ref makeIf(Ref condition, Ref ifTrue, Ref ifFalse) { - return &makeRawArray(4)->push_back(makeRawString(IF)) - .push_back(condition) - .push_back(ifTrue) - .push_back(!!ifFalse ? ifFalse : makeNull()); + return &makeRawArray(4) + ->push_back(makeRawString(IF)) + .push_back(condition) + .push_back(ifTrue) + .push_back(!!ifFalse ? ifFalse : makeNull()); } static Ref makeConditional(Ref condition, Ref ifTrue, Ref ifFalse) { - return &makeRawArray(4)->push_back(makeRawString(CONDITIONAL)) - .push_back(condition) - .push_back(ifTrue) - .push_back(ifFalse); + return &makeRawArray(4) + ->push_back(makeRawString(CONDITIONAL)) + .push_back(condition) + .push_back(ifTrue) + .push_back(ifFalse); } static Ref makeSeq(Ref left, Ref right) { - return &makeRawArray(3)->push_back(makeRawString(SEQ)) - .push_back(left) - .push_back(right); + return &makeRawArray(3) + ->push_back(makeRawString(SEQ)) + .push_back(left) + .push_back(right); } static Ref makeDo(Ref body, Ref condition) { - return &makeRawArray(3)->push_back(makeRawString(DO)) - .push_back(condition) - .push_back(body); + return &makeRawArray(3) + ->push_back(makeRawString(DO)) + .push_back(condition) + .push_back(body); } static Ref makeWhile(Ref condition, Ref body) { - return &makeRawArray(3)->push_back(makeRawString(WHILE)) - .push_back(condition) - .push_back(body); + return &makeRawArray(3) + ->push_back(makeRawString(WHILE)) + .push_back(condition) + .push_back(body); } static Ref makeFor(Ref init, Ref condition, Ref inc, Ref body) { - return &makeRawArray(5)->push_back(makeRawString(FOR)) - .push_back(init) - .push_back(condition) - .push_back(inc) - .push_back(body); + return &makeRawArray(5) + ->push_back(makeRawString(FOR)) + .push_back(init) + .push_back(condition) + .push_back(inc) + .push_back(body); } static Ref makeBreak(IString label) { - return &makeRawArray(2)->push_back(makeRawString(BREAK)) - .push_back(!!label ? makeRawString(label) : makeNull()); + return &makeRawArray(2) + ->push_back(makeRawString(BREAK)) + .push_back(!!label ? makeRawString(label) : makeNull()); } static Ref makeContinue(IString label) { - return &makeRawArray(2)->push_back(makeRawString(CONTINUE)) - .push_back(!!label ? makeRawString(label) : makeNull()); + return &makeRawArray(2) + ->push_back(makeRawString(CONTINUE)) + .push_back(!!label ? makeRawString(label) : makeNull()); } static Ref makeLabel(IString name, Ref body) { - return &makeRawArray(3)->push_back(makeRawString(LABEL)) - .push_back(makeRawString(name)) - .push_back(body); + return &makeRawArray(3) + ->push_back(makeRawString(LABEL)) + .push_back(makeRawString(name)) + .push_back(body); } static Ref makeSwitch(Ref input) { - return &makeRawArray(3)->push_back(makeRawString(SWITCH)) - .push_back(input) - .push_back(makeRawArray()); + return &makeRawArray(3) + ->push_back(makeRawString(SWITCH)) + .push_back(input) + .push_back(makeRawArray()); } static void appendCaseToSwitch(Ref switch_, Ref arg) { assert(switch_[0] == SWITCH); - switch_[2]->push_back(&makeRawArray(2)->push_back(arg) - .push_back(makeRawArray())); + switch_[2]->push_back( + &makeRawArray(2)->push_back(arg).push_back(makeRawArray())); } static void appendDefaultToSwitch(Ref switch_) { assert(switch_[0] == SWITCH); - switch_[2]->push_back(&makeRawArray(2)->push_back(makeNull()) - .push_back(makeRawArray())); + switch_[2]->push_back( + &makeRawArray(2)->push_back(makeNull()).push_back(makeRawArray())); } static void appendCodeToSwitch(Ref switch_, Ref code, bool explicitBlock) { @@ -1598,20 +1700,21 @@ public: static Ref makeTry(Ref try_, Ref arg, Ref catch_) { assert(try_[0] == BLOCK); assert(catch_[0] == BLOCK); - return &makeRawArray(3)->push_back(makeRawString(TRY)) - .push_back(try_) - .push_back(arg) - .push_back(catch_); + return &makeRawArray(3) + ->push_back(makeRawString(TRY)) + .push_back(try_) + .push_back(arg) + .push_back(catch_); } static Ref makeDot(Ref obj, IString key) { - return &makeRawArray(3)->push_back(makeRawString(DOT)) - .push_back(obj) - .push_back(makeRawString(key)); + return &makeRawArray(3) + ->push_back(makeRawString(DOT)) + .push_back(obj) + .push_back(makeRawString(key)); } - template<typename ...Ts> - static Ref makeDot(Ref obj, Ref key, Ts... args) { + template<typename... Ts> static Ref makeDot(Ref obj, Ref key, Ts... args) { return makeDot(makeDot(obj, key), args...); } @@ -1621,13 +1724,13 @@ public: } static Ref makeNew(Ref call) { - return &makeRawArray(2)->push_back(makeRawString(NEW)) - .push_back(call); + return &makeRawArray(2)->push_back(makeRawString(NEW)).push_back(call); } static Ref makeArray() { - return &makeRawArray(2)->push_back(makeRawString(ARRAY)) - .push_back(makeRawArray()); + return &makeRawArray(2) + ->push_back(makeRawString(ARRAY)) + .push_back(makeRawArray()); } static void appendToArray(Ref array, Ref element) { @@ -1636,26 +1739,28 @@ public: } static Ref makeObject() { - return &makeRawArray(2)->push_back(makeRawString(OBJECT)) - .push_back(makeRawArray()); + return &makeRawArray(2) + ->push_back(makeRawString(OBJECT)) + .push_back(makeRawArray()); } static void appendToObject(Ref array, IString key, Ref value) { assert(array[0] == OBJECT); - array[1]->push_back(&makeRawArray(2)->push_back(makeRawString(key)) - .push_back(value)); + array[1]->push_back( + &makeRawArray(2)->push_back(makeRawString(key)).push_back(value)); } static void appendToObjectWithQuotes(Ref array, IString key, Ref value) { assert(array[0] == OBJECT); - array[1]->push_back(&makeRawArray(2)->push_back(makeString(key)) - .push_back(value)); + array[1]->push_back( + &makeRawArray(2)->push_back(makeString(key)).push_back(value)); } static Ref makeSub(Ref obj, Ref index) { - return &makeRawArray(2)->push_back(makeRawString(SUB)) - .push_back(obj) - .push_back(index); + return &makeRawArray(2) + ->push_back(makeRawString(SUB)) + .push_back(obj) + .push_back(index); } static Ref makePtrShift(Ref ptr, int shifts) { diff --git a/src/emscripten-optimizer/snprintf.h b/src/emscripten-optimizer/snprintf.h index 86335661d..22c8d8202 100644 --- a/src/emscripten-optimizer/snprintf.h +++ b/src/emscripten-optimizer/snprintf.h @@ -19,34 +19,34 @@ #include <stdarg.h> -// Visual Studio does not support C99, so emulate snprintf support for it manually. +// Visual Studio does not support C99, so emulate snprintf support for it +// manually. #ifdef _MSC_VER #define snprintf c99_snprintf -inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) -{ - int count = -1; +inline int +c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) { + int count = -1; - if (size != 0) - count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); - if (count == -1) - count = _vscprintf(format, ap); + if (size != 0) + count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); - return count; + return count; } -inline int c99_snprintf(char* str, size_t size, const char* format, ...) -{ - int count; - va_list ap; +inline int c99_snprintf(char* str, size_t size, const char* format, ...) { + int count; + va_list ap; - va_start(ap, format); - count = c99_vsnprintf(str, size, format, ap); - va_end(ap); + va_start(ap, format); + count = c99_vsnprintf(str, size, format, ap); + va_end(ap); - return count; + return count; } #endif diff --git a/src/ir/ExpressionAnalyzer.cpp b/src/ir/ExpressionAnalyzer.cpp index fcbd29665..aaab9050c 100644 --- a/src/ir/ExpressionAnalyzer.cpp +++ b/src/ir/ExpressionAnalyzer.cpp @@ -14,19 +14,19 @@ * limitations under the License. */ -#include "support/hash.h" -#include "support/small_vector.h" -#include "wasm.h" -#include "wasm-traversal.h" #include "ir/iteration.h" #include "ir/load-utils.h" #include "ir/utils.h" +#include "support/hash.h" +#include "support/small_vector.h" +#include "wasm-traversal.h" +#include "wasm.h" namespace wasm { // Given a stack of expressions, checks if the topmost is used as a result. -// For example, if the parent is a block and the node is before the last position, -// it is not used. +// For example, if the parent is a block and the node is before the last +// position, it is not used. bool ExpressionAnalyzer::isResultUsed(ExpressionStack& stack, Function* func) { for (int i = int(stack.size()) - 2; i >= 0; i--) { auto* curr = stack[i]; @@ -35,18 +35,22 @@ bool ExpressionAnalyzer::isResultUsed(ExpressionStack& stack, Function* func) { if (curr->is<Block>()) { auto* block = curr->cast<Block>(); for (size_t j = 0; j < block->list.size() - 1; j++) { - if (block->list[j] == above) return false; + if (block->list[j] == above) + return false; } assert(block->list.back() == above); // continue down } else if (curr->is<If>()) { auto* iff = curr->cast<If>(); - if (above == iff->condition) return true; - if (!iff->ifFalse) return false; + if (above == iff->condition) + return true; + if (!iff->ifFalse) + return false; assert(above == iff->ifTrue || above == iff->ifFalse); // continue down } else { - if (curr->is<Drop>()) return false; + if (curr->is<Drop>()) + return false; return true; // all other node types use the result } } @@ -62,19 +66,23 @@ bool ExpressionAnalyzer::isResultDropped(ExpressionStack& stack) { if (curr->is<Block>()) { auto* block = curr->cast<Block>(); for (size_t j = 0; j < block->list.size() - 1; j++) { - if (block->list[j] == above) return false; + if (block->list[j] == above) + return false; } assert(block->list.back() == above); // continue down } else if (curr->is<If>()) { auto* iff = curr->cast<If>(); - if (above == iff->condition) return false; - if (!iff->ifFalse) return false; + if (above == iff->condition) + return false; + if (!iff->ifFalse) + return false; assert(above == iff->ifTrue || above == iff->ifFalse); // continue down } else { - if (curr->is<Drop>()) return true; // dropped - return false; // all other node types use the result + if (curr->is<Drop>()) + return true; // dropped + return false; // all other node types use the result } } return false; @@ -98,8 +106,7 @@ bool ExpressionAnalyzer::isResultDropped(ExpressionStack& stack) { namespace { -template<typename T> -void visitImmediates(Expression* curr, T& visitor) { +template<typename T> void visitImmediates(Expression* curr, T& visitor) { struct ImmediateVisitor : public OverriddenVisitor<ImmediateVisitor> { T& visitor; @@ -107,35 +114,22 @@ void visitImmediates(Expression* curr, T& visitor) { this->visit(curr); } - void visitBlock(Block* curr) { - visitor.visitScopeName(curr->name); - } - void visitIf(If* curr) { - } - void visitLoop(Loop* curr) { - visitor.visitScopeName(curr->name); - } - void visitBreak(Break* curr) { - visitor.visitScopeName(curr->name); - } + void visitBlock(Block* curr) { visitor.visitScopeName(curr->name); } + void visitIf(If* curr) {} + void visitLoop(Loop* curr) { visitor.visitScopeName(curr->name); } + void visitBreak(Break* curr) { visitor.visitScopeName(curr->name); } void visitSwitch(Switch* curr) { for (auto target : curr->targets) { visitor.visitScopeName(target); } visitor.visitScopeName(curr->default_); } - void visitCall(Call* curr) { - visitor.visitNonScopeName(curr->target); - } + void visitCall(Call* curr) { visitor.visitNonScopeName(curr->target); } void visitCallIndirect(CallIndirect* curr) { visitor.visitNonScopeName(curr->fullType); } - void visitGetLocal(GetLocal* curr) { - visitor.visitIndex(curr->index); - } - void visitSetLocal(SetLocal* curr) { - visitor.visitIndex(curr->index); - } + void visitGetLocal(GetLocal* curr) { visitor.visitIndex(curr->index); } + void visitSetLocal(SetLocal* curr) { visitor.visitIndex(curr->index); } void visitGetGlobal(GetGlobal* curr) { visitor.visitNonScopeName(curr->name); } @@ -187,52 +181,37 @@ void visitImmediates(Expression* curr, T& visitor) { visitor.visitInt(x); } } - void visitSIMDBitselect(SIMDBitselect* curr) { - } - void visitSIMDShift(SIMDShift* curr) { - visitor.visitInt(curr->op); - } + void visitSIMDBitselect(SIMDBitselect* curr) {} + void visitSIMDShift(SIMDShift* curr) { visitor.visitInt(curr->op); } void visitMemoryInit(MemoryInit* curr) { visitor.visitIndex(curr->segment); } - void visitDataDrop(DataDrop* curr) { - visitor.visitIndex(curr->segment); - } - void visitMemoryCopy(MemoryCopy* curr) { - } - void visitMemoryFill(MemoryFill* curr) { - } - void visitConst(Const* curr) { - visitor.visitLiteral(curr->value); - } - void visitUnary(Unary* curr) { - visitor.visitInt(curr->op); - } - void visitBinary(Binary* curr) { - visitor.visitInt(curr->op); - } - void visitSelect(Select* curr) { - } - void visitDrop(Drop* curr) { - } - void visitReturn(Return* curr) { - } + void visitDataDrop(DataDrop* curr) { visitor.visitIndex(curr->segment); } + void visitMemoryCopy(MemoryCopy* curr) {} + void visitMemoryFill(MemoryFill* curr) {} + void visitConst(Const* curr) { visitor.visitLiteral(curr->value); } + void visitUnary(Unary* curr) { visitor.visitInt(curr->op); } + void visitBinary(Binary* curr) { visitor.visitInt(curr->op); } + void visitSelect(Select* curr) {} + void visitDrop(Drop* curr) {} + void visitReturn(Return* curr) {} void visitHost(Host* curr) { visitor.visitInt(curr->op); visitor.visitNonScopeName(curr->nameOperand); } - void visitNop(Nop* curr) { - } - void visitUnreachable(Unreachable* curr) { - } + void visitNop(Nop* curr) {} + void visitUnreachable(Unreachable* curr) {} } singleton(curr, visitor); } } // namespace -bool ExpressionAnalyzer::flexibleEqual(Expression* left, Expression* right, ExprComparer comparer) { +bool ExpressionAnalyzer::flexibleEqual(Expression* left, + Expression* right, + ExprComparer comparer) { struct Comparer { - std::map<Name, Name> rightNames; // for each name on the left, the corresponding name on the right + // for each name on the left, the corresponding name on the right + std::map<Name, Name> rightNames; std::vector<Expression*> leftStack; std::vector<Expression*> rightStack; @@ -259,13 +238,15 @@ bool ExpressionAnalyzer::flexibleEqual(Expression* left, Expression* right, Expr // Comparison is by value, except for names, which must match. bool operator==(const Immediates& other) { - if (scopeNames.size() != other.scopeNames.size()) return false; + if (scopeNames.size() != other.scopeNames.size()) + return false; for (Index i = 0; i < scopeNames.size(); i++) { auto leftName = scopeNames[i]; auto rightName = other.scopeNames[i]; auto iter = parent.rightNames.find(leftName); - // If it's not found, that means it was defined out of the expression being compared, - // in which case we can just treat it literally - it must be exactly identical. + // If it's not found, that means it was defined out of the expression + // being compared, in which case we can just treat it literally - it + // must be exactly identical. if (iter != parent.rightNames.end()) { leftName = iter->second; } @@ -273,18 +254,22 @@ bool ExpressionAnalyzer::flexibleEqual(Expression* left, Expression* right, Expr return false; } } - if (nonScopeNames != other.nonScopeNames) return false; - if (ints != other.ints) return false; - if (literals != other.literals) return false; - if (types != other.types) return false; - if (indexes != other.indexes) return false; - if (addresses != other.addresses) return false; + if (nonScopeNames != other.nonScopeNames) + return false; + if (ints != other.ints) + return false; + if (literals != other.literals) + return false; + if (types != other.types) + return false; + if (indexes != other.indexes) + return false; + if (addresses != other.addresses) + return false; return true; } - bool operator!=(const Immediates& other) { - return !(*this == other); - } + bool operator!=(const Immediates& other) { return !(*this == other); } void clear() { scopeNames.clear(); @@ -298,7 +283,8 @@ bool ExpressionAnalyzer::flexibleEqual(Expression* left, Expression* right, Expr }; bool noteNames(Name left, Name right) { - if (left.is() != right.is()) return false; + if (left.is() != right.is()) + return false; if (left.is()) { assert(rightNames.find(left) == rightNames.end()); rightNames[left] = right; @@ -307,8 +293,7 @@ bool ExpressionAnalyzer::flexibleEqual(Expression* left, Expression* right, Expr } bool compare(Expression* left, Expression* right, ExprComparer comparer) { - Immediates leftImmediates(*this), - rightImmediates(*this); + Immediates leftImmediates(*this), rightImmediates(*this); // The empty name is the same on both sides. rightNames[Name()] = Name(); @@ -321,21 +306,28 @@ bool ExpressionAnalyzer::flexibleEqual(Expression* left, Expression* right, Expr leftStack.pop_back(); right = rightStack.back(); rightStack.pop_back(); - if (!left != !right) return false; - if (!left) continue; - if (comparer(left, right)) continue; // comparison hook, before all the rest + if (!left != !right) + return false; + if (!left) + continue; + if (comparer(left, right)) + continue; // comparison hook, before all the rest // continue with normal structural comparison - if (left->_id != right->_id) return false; + if (left->_id != right->_id) + return false; // Blocks and loops introduce scoping. if (auto* block = left->dynCast<Block>()) { - if (!noteNames(block->name, right->cast<Block>()->name)) return false; + if (!noteNames(block->name, right->cast<Block>()->name)) + return false; } else if (auto* loop = left->dynCast<Loop>()) { - if (!noteNames(loop->name, right->cast<Loop>()->name)) return false; + if (!noteNames(loop->name, right->cast<Loop>()->name)) + return false; } else { // For all other nodes, compare their immediate values visitImmediates(left, leftImmediates); visitImmediates(right, rightImmediates); - if (leftImmediates != rightImmediates) return false; + if (leftImmediates != rightImmediates) + return false; leftImmediates.clear(); rightImmediates.clear(); } @@ -349,10 +341,13 @@ bool ExpressionAnalyzer::flexibleEqual(Expression* left, Expression* right, Expr rightStack.push_back(child); counter--; } - // The number of child nodes must match (e.g. return has an optional one). - if (counter != 0) return false; + // The number of child nodes must match (e.g. return has an optional + // one). + if (counter != 0) + return false; } - if (leftStack.size() > 0 || rightStack.size() > 0) return false; + if (leftStack.size() > 0 || rightStack.size() > 0) + return false; return true; } }; @@ -366,7 +361,8 @@ HashType ExpressionAnalyzer::hash(Expression* curr) { HashType digest = 0; Index internalCounter = 0; - std::map<Name, Index> internalNames; // for each internal name, its unique id + // for each internal name, its unique id + std::map<Name, Index> internalNames; ExpressionStack stack; void noteScopeName(Name curr) { @@ -381,7 +377,8 @@ HashType ExpressionAnalyzer::hash(Expression* curr) { while (stack.size() > 0) { curr = stack.back(); stack.pop_back(); - if (!curr) continue; + if (!curr) + continue; hash(curr->_id); // we often don't need to hash the type, as it is tied to other values // we are hashing anyhow, but there are exceptions: for example, a @@ -413,9 +410,7 @@ HashType ExpressionAnalyzer::hash(Expression* curr) { } } - void hash(HashType hash) { - digest = rehash(digest, hash); - } + void hash(HashType hash) { digest = rehash(digest, hash); } void hash64(uint64_t hash) { digest = rehash(rehash(digest, HashType(hash >> 32)), HashType(hash)); } @@ -424,28 +419,23 @@ HashType ExpressionAnalyzer::hash(Expression* curr) { // Names are relative, we give the same hash for // (block $x (br $x)) // (block $y (br $y)) - static_assert(sizeof(Index) == sizeof(int32_t), "wasm64 will need changes here"); + static_assert(sizeof(Index) == sizeof(int32_t), + "wasm64 will need changes here"); assert(internalNames.find(curr) != internalNames.end()); return hash(internalNames[curr]); } - void visitNonScopeName(Name curr) { - return hash64(uint64_t(curr.str)); - } - void visitInt(int32_t curr) { - hash(curr); - } - void visitLiteral(Literal curr) { - hash(std::hash<Literal>()(curr)); - } - void visitType(Type curr) { - hash(int32_t(curr)); - } + void visitNonScopeName(Name curr) { return hash64(uint64_t(curr.str)); } + void visitInt(int32_t curr) { hash(curr); } + void visitLiteral(Literal curr) { hash(std::hash<Literal>()(curr)); } + void visitType(Type curr) { hash(int32_t(curr)); } void visitIndex(Index curr) { - static_assert(sizeof(Index) == sizeof(int32_t), "wasm64 will need changes here"); + static_assert(sizeof(Index) == sizeof(int32_t), + "wasm64 will need changes here"); hash(int32_t(curr)); } void visitAddress(Address curr) { - static_assert(sizeof(Address) == sizeof(int32_t), "wasm64 will need changes here"); + static_assert(sizeof(Address) == sizeof(int32_t), + "wasm64 will need changes here"); hash(int32_t(curr)); } }; diff --git a/src/ir/ExpressionManipulator.cpp b/src/ir/ExpressionManipulator.cpp index 2b648e077..6d35ef97f 100644 --- a/src/ir/ExpressionManipulator.cpp +++ b/src/ir/ExpressionManipulator.cpp @@ -22,156 +22,202 @@ namespace wasm { namespace ExpressionManipulator { -Expression* flexibleCopy(Expression* original, Module& wasm, CustomCopier custom) { +Expression* +flexibleCopy(Expression* original, Module& wasm, CustomCopier custom) { struct Copier : public Visitor<Copier, Expression*> { Module& wasm; CustomCopier custom; Builder builder; - Copier(Module& wasm, CustomCopier custom) : wasm(wasm), custom(custom), builder(wasm) {} + Copier(Module& wasm, CustomCopier custom) + : wasm(wasm), custom(custom), builder(wasm) {} Expression* copy(Expression* curr) { - if (!curr) return nullptr; + if (!curr) + return nullptr; auto* ret = custom(curr); - if (ret) return ret; + if (ret) + return ret; return Visitor<Copier, Expression*>::visit(curr); } - Expression* visitBlock(Block *curr) { + Expression* visitBlock(Block* curr) { ExpressionList list(wasm.allocator); for (Index i = 0; i < curr->list.size(); i++) { list.push_back(copy(curr->list[i])); } return builder.makeBlock(curr->name, list, curr->type); } - Expression* visitIf(If *curr) { - return builder.makeIf(copy(curr->condition), copy(curr->ifTrue), copy(curr->ifFalse), curr->type); + Expression* visitIf(If* curr) { + return builder.makeIf(copy(curr->condition), + copy(curr->ifTrue), + copy(curr->ifFalse), + curr->type); } - Expression* visitLoop(Loop *curr) { + Expression* visitLoop(Loop* curr) { return builder.makeLoop(curr->name, copy(curr->body)); } - Expression* visitBreak(Break *curr) { - return builder.makeBreak(curr->name, copy(curr->value), copy(curr->condition)); + Expression* visitBreak(Break* curr) { + return builder.makeBreak( + curr->name, copy(curr->value), copy(curr->condition)); } - Expression* visitSwitch(Switch *curr) { - return builder.makeSwitch(curr->targets, curr->default_, copy(curr->condition), copy(curr->value)); + Expression* visitSwitch(Switch* curr) { + return builder.makeSwitch(curr->targets, + curr->default_, + copy(curr->condition), + copy(curr->value)); } - Expression* visitCall(Call *curr) { + Expression* visitCall(Call* curr) { auto* ret = builder.makeCall(curr->target, {}, curr->type); for (Index i = 0; i < curr->operands.size(); i++) { ret->operands.push_back(copy(curr->operands[i])); } return ret; } - Expression* visitCallIndirect(CallIndirect *curr) { - auto* ret = builder.makeCallIndirect(curr->fullType, copy(curr->target), {}, curr->type); + Expression* visitCallIndirect(CallIndirect* curr) { + auto* ret = builder.makeCallIndirect( + curr->fullType, copy(curr->target), {}, curr->type); for (Index i = 0; i < curr->operands.size(); i++) { ret->operands.push_back(copy(curr->operands[i])); } return ret; } - Expression* visitGetLocal(GetLocal *curr) { + Expression* visitGetLocal(GetLocal* curr) { return builder.makeGetLocal(curr->index, curr->type); } - Expression* visitSetLocal(SetLocal *curr) { + Expression* visitSetLocal(SetLocal* curr) { if (curr->isTee()) { return builder.makeTeeLocal(curr->index, copy(curr->value)); } else { return builder.makeSetLocal(curr->index, copy(curr->value)); } } - Expression* visitGetGlobal(GetGlobal *curr) { + Expression* visitGetGlobal(GetGlobal* curr) { return builder.makeGetGlobal(curr->name, curr->type); } - Expression* visitSetGlobal(SetGlobal *curr) { + Expression* visitSetGlobal(SetGlobal* curr) { return builder.makeSetGlobal(curr->name, copy(curr->value)); } - Expression* visitLoad(Load *curr) { + Expression* visitLoad(Load* curr) { if (curr->isAtomic) { - return builder.makeAtomicLoad(curr->bytes, curr->offset, - copy(curr->ptr), curr->type); + return builder.makeAtomicLoad( + curr->bytes, curr->offset, copy(curr->ptr), curr->type); } - return builder.makeLoad(curr->bytes, LoadUtils::isSignRelevant(curr) ? curr->signed_ : false, curr->offset, curr->align, copy(curr->ptr), curr->type); - } - Expression* visitStore(Store *curr) { + return builder.makeLoad(curr->bytes, + LoadUtils::isSignRelevant(curr) ? curr->signed_ + : false, + curr->offset, + curr->align, + copy(curr->ptr), + curr->type); + } + Expression* visitStore(Store* curr) { if (curr->isAtomic) { - return builder.makeAtomicStore(curr->bytes, curr->offset, copy(curr->ptr), copy(curr->value), curr->valueType); + return builder.makeAtomicStore(curr->bytes, + curr->offset, + copy(curr->ptr), + copy(curr->value), + curr->valueType); } - return builder.makeStore(curr->bytes, curr->offset, curr->align, copy(curr->ptr), copy(curr->value), curr->valueType); + return builder.makeStore(curr->bytes, + curr->offset, + curr->align, + copy(curr->ptr), + copy(curr->value), + curr->valueType); } Expression* visitAtomicRMW(AtomicRMW* curr) { - return builder.makeAtomicRMW(curr->op, curr->bytes, curr->offset, - copy(curr->ptr), copy(curr->value), curr->type); + return builder.makeAtomicRMW(curr->op, + curr->bytes, + curr->offset, + copy(curr->ptr), + copy(curr->value), + curr->type); } Expression* visitAtomicCmpxchg(AtomicCmpxchg* curr) { - return builder.makeAtomicCmpxchg(curr->bytes, curr->offset, - copy(curr->ptr), copy(curr->expected), copy(curr->replacement), + return builder.makeAtomicCmpxchg(curr->bytes, + curr->offset, + copy(curr->ptr), + copy(curr->expected), + copy(curr->replacement), curr->type); } Expression* visitAtomicWait(AtomicWait* curr) { - return builder.makeAtomicWait(copy(curr->ptr), copy(curr->expected), copy(curr->timeout), curr->expectedType, curr->offset); + return builder.makeAtomicWait(copy(curr->ptr), + copy(curr->expected), + copy(curr->timeout), + curr->expectedType, + curr->offset); } Expression* visitAtomicNotify(AtomicNotify* curr) { - return builder.makeAtomicNotify(copy(curr->ptr), copy(curr->notifyCount), curr->offset); + return builder.makeAtomicNotify( + copy(curr->ptr), copy(curr->notifyCount), curr->offset); } Expression* visitSIMDExtract(SIMDExtract* curr) { return builder.makeSIMDExtract(curr->op, copy(curr->vec), curr->index); } Expression* visitSIMDReplace(SIMDReplace* curr) { - return builder.makeSIMDReplace(curr->op, copy(curr->vec), curr->index, copy(curr->value)); + return builder.makeSIMDReplace( + curr->op, copy(curr->vec), curr->index, copy(curr->value)); } Expression* visitSIMDShuffle(SIMDShuffle* curr) { - return builder.makeSIMDShuffle(copy(curr->left), copy(curr->right), curr->mask); + return builder.makeSIMDShuffle( + copy(curr->left), copy(curr->right), curr->mask); } Expression* visitSIMDBitselect(SIMDBitselect* curr) { - return builder.makeSIMDBitselect(copy(curr->left), copy(curr->right), copy(curr->cond)); + return builder.makeSIMDBitselect( + copy(curr->left), copy(curr->right), copy(curr->cond)); } Expression* visitSIMDShift(SIMDShift* curr) { - return builder.makeSIMDShift(curr->op, copy(curr->vec), copy(curr->shift)); + return builder.makeSIMDShift( + curr->op, copy(curr->vec), copy(curr->shift)); } Expression* visitConst(Const* curr) { return builder.makeConst(curr->value); } Expression* visitMemoryInit(MemoryInit* curr) { - return builder.makeMemoryInit(curr->segment, copy(curr->dest), copy(curr->offset), copy(curr->size)); + return builder.makeMemoryInit( + curr->segment, copy(curr->dest), copy(curr->offset), copy(curr->size)); } Expression* visitDataDrop(DataDrop* curr) { return builder.makeDataDrop(curr->segment); } Expression* visitMemoryCopy(MemoryCopy* curr) { - return builder.makeMemoryCopy(copy(curr->dest), copy(curr->source), copy(curr->size)); + return builder.makeMemoryCopy( + copy(curr->dest), copy(curr->source), copy(curr->size)); } Expression* visitMemoryFill(MemoryFill* curr) { - return builder.makeMemoryFill(copy(curr->dest), copy(curr->value), copy(curr->size)); + return builder.makeMemoryFill( + copy(curr->dest), copy(curr->value), copy(curr->size)); } - Expression* visitUnary(Unary *curr) { + Expression* visitUnary(Unary* curr) { return builder.makeUnary(curr->op, copy(curr->value)); } - Expression* visitBinary(Binary *curr) { + Expression* visitBinary(Binary* curr) { return builder.makeBinary(curr->op, copy(curr->left), copy(curr->right)); } - Expression* visitSelect(Select *curr) { - return builder.makeSelect(copy(curr->condition), copy(curr->ifTrue), copy(curr->ifFalse)); + Expression* visitSelect(Select* curr) { + return builder.makeSelect( + copy(curr->condition), copy(curr->ifTrue), copy(curr->ifFalse)); } - Expression* visitDrop(Drop *curr) { + Expression* visitDrop(Drop* curr) { return builder.makeDrop(copy(curr->value)); } - Expression* visitReturn(Return *curr) { + Expression* visitReturn(Return* curr) { return builder.makeReturn(copy(curr->value)); } - Expression* visitHost(Host *curr) { + Expression* visitHost(Host* curr) { std::vector<Expression*> operands; for (Index i = 0; i < curr->operands.size(); i++) { operands.push_back(copy(curr->operands[i])); } - auto* ret = builder.makeHost(curr->op, curr->nameOperand, std::move(operands)); + auto* ret = + builder.makeHost(curr->op, curr->nameOperand, std::move(operands)); return ret; } - Expression* visitNop(Nop *curr) { - return builder.makeNop(); - } - Expression* visitUnreachable(Unreachable *curr) { + Expression* visitNop(Nop* curr) { return builder.makeNop(); } + Expression* visitUnreachable(Unreachable* curr) { return builder.makeUnreachable(); } }; @@ -180,7 +226,6 @@ Expression* flexibleCopy(Expression* original, Module& wasm, CustomCopier custom return copier.copy(original); } - // Splice an item into the middle of a block's list void spliceIntoBlock(Block* block, Index index, Expression* add) { auto& list = block->list; diff --git a/src/ir/LocalGraph.cpp b/src/ir/LocalGraph.cpp index 1cf883c19..1a2ccc0a2 100644 --- a/src/ir/LocalGraph.cpp +++ b/src/ir/LocalGraph.cpp @@ -16,11 +16,11 @@ #include <iterator> -#include <wasm-builder.h> -#include <wasm-printing.h> +#include <cfg/cfg-traversal.h> #include <ir/find_all.h> #include <ir/local-graph.h> -#include <cfg/cfg-traversal.h> +#include <wasm-builder.h> +#include <wasm-printing.h> namespace wasm { @@ -28,8 +28,10 @@ namespace LocalGraphInternal { // Information about a basic block. struct Info { - std::vector<Expression*> actions; // actions occurring in this block: local.gets and local.sets - std::unordered_map<Index, SetLocal*> lastSets; // for each index, the last local.set for it + // actions occurring in this block: local.gets and local.sets + std::vector<Expression*> actions; + // for each index, the last local.set for it + std::unordered_map<Index, SetLocal*> lastSets; }; // flow helper class. flows the gets to their sets @@ -38,7 +40,10 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { LocalGraph::GetSetses& getSetses; LocalGraph::Locations& locations; - Flower(LocalGraph::GetSetses& getSetses, LocalGraph::Locations& locations, Function* func) : getSetses(getSetses), locations(locations) { + Flower(LocalGraph::GetSetses& getSetses, + LocalGraph::Locations& locations, + Function* func) + : getSetses(getSetses), locations(locations) { setFunction(func); // create the CFG by walking the IR CFGWalker<Flower, Visitor<Flower>, Info>::doWalkFunction(func); @@ -46,16 +51,15 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { flow(func); } - BasicBlock* makeBasicBlock() { - return new BasicBlock(); - } + BasicBlock* makeBasicBlock() { return new BasicBlock(); } // cfg traversal work static void doVisitGetLocal(Flower* self, Expression** currp) { auto* curr = (*currp)->cast<GetLocal>(); - // if in unreachable code, skip - if (!self->currBasicBlock) return; + // if in unreachable code, skip + if (!self->currBasicBlock) + return; self->currBasicBlock->contents.actions.emplace_back(curr); self->locations[curr] = currp; } @@ -63,27 +67,32 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { static void doVisitSetLocal(Flower* self, Expression** currp) { auto* curr = (*currp)->cast<SetLocal>(); // if in unreachable code, skip - if (!self->currBasicBlock) return; + if (!self->currBasicBlock) + return; self->currBasicBlock->contents.actions.emplace_back(curr); self->currBasicBlock->contents.lastSets[curr->index] = curr; self->locations[curr] = currp; } void flow(Function* func) { - // This block struct is optimized for this flow process (Minimal information, iteration index). + // This block struct is optimized for this flow process (Minimal + // information, iteration index). struct FlowBlock { - // Last Traversed Iteration: This value helps us to find if this block has been seen while traversing blocks. - // We compare this value to the current iteration index in order to determine if we already process this block in the current iteration. - // This speeds up the processing compared to unordered_set or other struct usage. (No need to reset internal values, lookup into container, ...) + // Last Traversed Iteration: This value helps us to find if this block has + // been seen while traversing blocks. We compare this value to the current + // iteration index in order to determine if we already process this block + // in the current iteration. This speeds up the processing compared to + // unordered_set or other struct usage. (No need to reset internal values, + // lookup into container, ...) size_t lastTraversedIteration; std::vector<Expression*> actions; std::vector<FlowBlock*> in; // Sor each index, the last local.set for it // The unordered_map from BasicBlock.Info is converted into a vector - // This speeds up search as there are usually few sets in a block, so just scanning - // them linearly is efficient, avoiding hash computations (while in Info, - // it's convenient to have a map so we can assign them easily, where - // the last one seen overwrites the previous; and, we do that O(1)). + // This speeds up search as there are usually few sets in a block, so just + // scanning them linearly is efficient, avoiding hash computations (while + // in Info, it's convenient to have a map so we can assign them easily, + // where the last one seen overwrites the previous; and, we do that O(1)). std::vector<std::pair<Index, SetLocal*>> lastSets; }; @@ -92,7 +101,8 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { allGets.resize(numLocals); std::vector<FlowBlock*> work; - // Convert input blocks (basicBlocks) into more efficient flow blocks to improve memory access. + // Convert input blocks (basicBlocks) into more efficient flow blocks to + // improve memory access. std::vector<FlowBlock> flowBlocks; flowBlocks.resize(basicBlocks.size()); @@ -109,15 +119,17 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { auto& block = basicBlocks[i]; auto& flowBlock = flowBlocks[i]; // Get the equivalent block to entry in the flow list - if (block.get() == entry) entryFlowBlock = &flowBlock; + if (block.get() == entry) + entryFlowBlock = &flowBlock; flowBlock.lastTraversedIteration = NULL_ITERATION; flowBlock.actions.swap(block->contents.actions); // Map in block to flow blocks auto& in = block->in; flowBlock.in.resize(in.size()); - std::transform(in.begin(), in.end(), flowBlock.in.begin(), [&](BasicBlock* block) { - return basicToFlowMap[block]; - }); + std::transform(in.begin(), + in.end(), + flowBlock.in.begin(), + [&](BasicBlock* block) { return basicToFlowMap[block]; }); // Convert unordered_map to vector. flowBlock.lastSets.reserve(block->contents.lastSets.size()); for (auto set : block->contents.lastSets) { @@ -159,7 +171,8 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { // can do that for all gets as a whole, they will get the same results. for (Index index = 0; index < numLocals; index++) { auto& gets = allGets[index]; - if (gets.empty()) continue; + if (gets.empty()) + continue; work.push_back(&block); // Note that we may need to revisit the later parts of this initial // block, if we are in a loop, so don't mark it as seen. @@ -182,9 +195,12 @@ struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { continue; } pred->lastTraversedIteration = currentIteration; - auto lastSet = std::find_if(pred->lastSets.begin(), pred->lastSets.end(), [&](std::pair<Index, SetLocal*>& value) { - return value.first == index; - }); + auto lastSet = + std::find_if(pred->lastSets.begin(), + pred->lastSets.end(), + [&](std::pair<Index, SetLocal*>& value) { + return value.first == index; + }); if (lastSet != pred->lastSets.end()) { // There is a set here, apply it, and stop the flow. for (auto* get : gets) { @@ -271,9 +287,6 @@ void LocalGraph::computeSSAIndexes() { } } -bool LocalGraph::isSSA(Index x) { - return SSAIndexes.count(x); -} +bool LocalGraph::isSSA(Index x) { return SSAIndexes.count(x); } } // namespace wasm - diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index e4d5180d1..2bf68970d 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -51,10 +51,10 @@ void ReFinalize::visitBlock(Block* curr) { // if concrete, it doesn't matter if we have an unreachable child, and we // don't need to look at breaks if (isConcreteType(curr->type)) { - // make sure our branches make sense for us - we may have just made ourselves - // concrete for a value flowing out, while branches did not send a value. such - // branches could not have been actually taken before, that is, there were in - // unreachable code, but we do still need to fix them up here. + // make sure our branches make sense for us - we may have just made + // ourselves concrete for a value flowing out, while branches did not send a + // value. such branches could not have been actually taken before, that is, + // there were in unreachable code, but we do still need to fix them up here. if (!isConcreteType(old)) { auto iter = breakValues.find(curr->name); if (iter != breakValues.end()) { @@ -91,7 +91,8 @@ void ReFinalize::visitBlock(Block* curr) { return; } } - if (curr->type == unreachable) return; + if (curr->type == unreachable) + return; // type is none, but we might be unreachable if (curr->type == none) { for (auto* child : curr->list) { diff --git a/src/ir/abstract.h b/src/ir/abstract.h index 1f15bafa1..33c276409 100644 --- a/src/ir/abstract.h +++ b/src/ir/abstract.h @@ -29,17 +29,28 @@ enum Op { // Unary Neg, // Binary - Add, Sub, Mul, DivU, DivS, Rem, RemU, RemS, - Shl, ShrU, ShrS, - And, Or, Xor, + Add, + Sub, + Mul, + DivU, + DivS, + Rem, + RemU, + RemS, + Shl, + ShrU, + ShrS, + And, + Or, + Xor, // Relational - Eq, Ne, + Eq, + Ne, }; -// Provide a wasm type and an abstract op and get the concrete one. For example, you can -// provide i32 and Add and receive the specific opcode for a 32-bit addition, -// AddInt32. -// If the op does not exist, it returns Invalid. +// Provide a wasm type and an abstract op and get the concrete one. For example, +// you can provide i32 and Add and receive the specific opcode for a 32-bit +// addition, AddInt32. If the op does not exist, it returns Invalid. inline UnaryOp getUnary(Type type, Op op) { switch (type) { case i32: { @@ -50,15 +61,19 @@ inline UnaryOp getUnary(Type type, Op op) { } case f32: { switch (op) { - case Neg: return NegFloat32; - default: return InvalidUnary; + case Neg: + return NegFloat32; + default: + return InvalidUnary; } break; } case f64: { switch (op) { - case Neg: return NegFloat64; - default: return InvalidUnary; + case Neg: + return NegFloat64; + default: + return InvalidUnary; } break; } @@ -78,69 +93,117 @@ inline BinaryOp getBinary(Type type, Op op) { switch (type) { case i32: { switch (op) { - case Add: return AddInt32; - case Sub: return SubInt32; - case Mul: return MulInt32; - case DivU: return DivUInt32; - case DivS: return DivSInt32; - case RemU: return RemUInt32; - case RemS: return RemSInt32; - case Shl: return ShlInt32; - case ShrU: return ShrUInt32; - case ShrS: return ShrSInt32; - case And: return AndInt32; - case Or: return OrInt32; - case Xor: return XorInt32; - case Eq: return EqInt32; - case Ne: return NeInt32; - default: return InvalidBinary; + case Add: + return AddInt32; + case Sub: + return SubInt32; + case Mul: + return MulInt32; + case DivU: + return DivUInt32; + case DivS: + return DivSInt32; + case RemU: + return RemUInt32; + case RemS: + return RemSInt32; + case Shl: + return ShlInt32; + case ShrU: + return ShrUInt32; + case ShrS: + return ShrSInt32; + case And: + return AndInt32; + case Or: + return OrInt32; + case Xor: + return XorInt32; + case Eq: + return EqInt32; + case Ne: + return NeInt32; + default: + return InvalidBinary; } break; } case i64: { switch (op) { - case Add: return AddInt64; - case Sub: return SubInt64; - case Mul: return MulInt64; - case DivU: return DivUInt64; - case DivS: return DivSInt64; - case RemU: return RemUInt64; - case RemS: return RemSInt64; - case Shl: return ShlInt64; - case ShrU: return ShrUInt64; - case ShrS: return ShrSInt64; - case And: return AndInt64; - case Or: return OrInt64; - case Xor: return XorInt64; - case Eq: return EqInt64; - case Ne: return NeInt64; - default: return InvalidBinary; + case Add: + return AddInt64; + case Sub: + return SubInt64; + case Mul: + return MulInt64; + case DivU: + return DivUInt64; + case DivS: + return DivSInt64; + case RemU: + return RemUInt64; + case RemS: + return RemSInt64; + case Shl: + return ShlInt64; + case ShrU: + return ShrUInt64; + case ShrS: + return ShrSInt64; + case And: + return AndInt64; + case Or: + return OrInt64; + case Xor: + return XorInt64; + case Eq: + return EqInt64; + case Ne: + return NeInt64; + default: + return InvalidBinary; } break; } case f32: { switch (op) { - case Add: return AddFloat32; - case Sub: return SubFloat32; - case Mul: return MulFloat32; - case DivU: return DivFloat32; - case DivS: return DivFloat32; - case Eq: return EqFloat32; - case Ne: return NeFloat32; - default: return InvalidBinary; + case Add: + return AddFloat32; + case Sub: + return SubFloat32; + case Mul: + return MulFloat32; + case DivU: + return DivFloat32; + case DivS: + return DivFloat32; + case Eq: + return EqFloat32; + case Ne: + return NeFloat32; + default: + return InvalidBinary; } break; } case f64: { switch (op) { - case Add: return AddFloat64; - case Sub: return SubFloat64; - case Mul: return MulFloat64; - case DivU: return DivFloat64; - case DivS: return DivFloat64; - case Eq: return EqFloat64; - case Ne: return NeFloat64; - default: return InvalidBinary; + case Add: + return AddFloat64; + case Sub: + return SubFloat64; + case Mul: + return MulFloat64; + case DivU: + return DivFloat64; + case DivS: + return DivFloat64; + case Eq: + return EqFloat64; + case Ne: + return NeFloat64; + default: + return InvalidBinary; } break; } diff --git a/src/ir/bits.h b/src/ir/bits.h index ec33c4b11..a2b8fcde4 100644 --- a/src/ir/bits.h +++ b/src/ir/bits.h @@ -17,9 +17,9 @@ #ifndef wasm_ir_bits_h #define wasm_ir_bits_h +#include "ir/literal-utils.h" #include "support/bits.h" #include "wasm-builder.h" -#include "ir/literal-utils.h" namespace wasm { @@ -27,17 +27,23 @@ struct Bits { // get a mask to keep only the low # of bits static int32_t lowBitMask(int32_t bits) { uint32_t ret = -1; - if (bits >= 32) return ret; + if (bits >= 32) + return ret; return ret >> (32 - bits); } - // checks if the input is a mask of lower bits, i.e., all 1s up to some high bit, and all zeros - // from there. returns the number of masked bits, or 0 if this is not such a mask + // checks if the input is a mask of lower bits, i.e., all 1s up to some high + // bit, and all zeros from there. returns the number of masked bits, or 0 if + // this is not such a mask static uint32_t getMaskedBits(uint32_t mask) { - if (mask == uint32_t(-1)) return 32; // all the bits - if (mask == 0) return 0; // trivially not a mask - // otherwise, see if adding one turns this into a 1-bit thing, 00011111 + 1 => 00100000 - if (PopCount(mask + 1) != 1) return 0; + if (mask == uint32_t(-1)) + return 32; // all the bits + if (mask == 0) + return 0; // trivially not a mask + // otherwise, see if adding one turns this into a 1-bit thing, 00011111 + 1 + // => 00100000 + if (PopCount(mask + 1) != 1) + return 0; // this is indeed a mask return 32 - CountLeadingZeroes(mask); } @@ -71,12 +77,8 @@ struct Bits { return builder.makeBinary( ShrSInt32, builder.makeBinary( - ShlInt32, - value, - LiteralUtils::makeFromInt32(shifts, i32, wasm) - ), - LiteralUtils::makeFromInt32(shifts, i32, wasm) - ); + ShlInt32, value, LiteralUtils::makeFromInt32(shifts, i32, wasm)), + LiteralUtils::makeFromInt32(shifts, i32, wasm)); } assert(bytes == 4); return value; // nothing to do @@ -88,12 +90,8 @@ struct Bits { return builder.makeBinary( ShrSInt64, builder.makeBinary( - ShlInt64, - value, - LiteralUtils::makeFromInt32(shifts, i64, wasm) - ), - LiteralUtils::makeFromInt32(shifts, i64, wasm) - ); + ShlInt64, value, LiteralUtils::makeFromInt32(shifts, i64, wasm)), + LiteralUtils::makeFromInt32(shifts, i64, wasm)); } assert(bytes == 8); return value; // nothing to do @@ -104,4 +102,3 @@ struct Bits { } // namespace wasm #endif // wasm_ir_bits_h - diff --git a/src/ir/block-utils.h b/src/ir/block-utils.h index 968332212..120178a47 100644 --- a/src/ir/block-utils.h +++ b/src/ir/block-utils.h @@ -17,51 +17,57 @@ #ifndef wasm_ir_block_h #define wasm_ir_block_h -#include "literal.h" -#include "wasm.h" #include "ir/branch-utils.h" #include "ir/effects.h" +#include "ir/manipulation.h" +#include "literal.h" +#include "wasm-builder.h" +#include "wasm.h" namespace wasm { namespace BlockUtils { - // if a block has just one element, it can often be replaced - // with that content - template<typename T> - inline Expression* simplifyToContents(Block* block, T* parent, bool allowTypeChange = false) { - auto& list = block->list; - if (list.size() == 1 && !BranchUtils::BranchSeeker::hasNamed(list[0], block->name)) { - // just one element. try to replace the block - auto* singleton = list[0]; - auto sideEffects = EffectAnalyzer(parent->getPassOptions(), singleton).hasSideEffects(); - if (!sideEffects && !isConcreteType(singleton->type)) { - // no side effects, and singleton is not returning a value, so we can throw away - // the block and its contents, basically - return Builder(*parent->getModule()).replaceWithIdenticalType(block); - } else if (block->type == singleton->type || allowTypeChange) { - return singleton; - } else { - // (side effects +) type change, must be block with declared value but inside is unreachable - // (if both concrete, must match, and since no name on block, we can't be - // branched to, so if singleton is unreachable, so is the block) - assert(isConcreteType(block->type) && singleton->type == unreachable); - // we could replace with unreachable, but would need to update all - // the parent's types - } - } else if (list.size() == 0) { - ExpressionManipulator::nop(block); +// if a block has just one element, it can often be replaced +// with that content +template<typename T> +inline Expression* +simplifyToContents(Block* block, T* parent, bool allowTypeChange = false) { + auto& list = block->list; + if (list.size() == 1 && + !BranchUtils::BranchSeeker::hasNamed(list[0], block->name)) { + // just one element. try to replace the block + auto* singleton = list[0]; + auto sideEffects = + EffectAnalyzer(parent->getPassOptions(), singleton).hasSideEffects(); + if (!sideEffects && !isConcreteType(singleton->type)) { + // no side effects, and singleton is not returning a value, so we can + // throw away the block and its contents, basically + return Builder(*parent->getModule()).replaceWithIdenticalType(block); + } else if (block->type == singleton->type || allowTypeChange) { + return singleton; + } else { + // (side effects +) type change, must be block with declared value but + // inside is unreachable (if both concrete, must match, and since no name + // on block, we can't be branched to, so if singleton is unreachable, so + // is the block) + assert(isConcreteType(block->type) && singleton->type == unreachable); + // we could replace with unreachable, but would need to update all + // the parent's types } - return block; + } else if (list.size() == 0) { + ExpressionManipulator::nop(block); } + return block; +} - // similar, but when we allow the type to change while doing so - template<typename T> - inline Expression* simplifyToContentsWithPossibleTypeChange(Block* block, T* parent) { - return simplifyToContents(block, parent, true); - } +// similar, but when we allow the type to change while doing so +template<typename T> +inline Expression* simplifyToContentsWithPossibleTypeChange(Block* block, + T* parent) { + return simplifyToContents(block, parent, true); } +} // namespace BlockUtils } // namespace wasm #endif // wasm_ir_block_h - diff --git a/src/ir/branch-utils.h b/src/ir/branch-utils.h index b9448e590..f62941e15 100644 --- a/src/ir/branch-utils.h +++ b/src/ir/branch-utils.h @@ -17,23 +17,24 @@ #ifndef wasm_ir_branch_h #define wasm_ir_branch_h -#include "wasm.h" #include "wasm-traversal.h" +#include "wasm.h" namespace wasm { namespace BranchUtils { -// Some branches are obviously not actually reachable (e.g. (br $out (unreachable))) +// Some branches are obviously not actually reachable (e.g. (br $out +// (unreachable))) inline bool isBranchReachable(Break* br) { - return !(br->value && br->value->type == unreachable) && + return !(br->value && br->value->type == unreachable) && !(br->condition && br->condition->type == unreachable); } inline bool isBranchReachable(Switch* sw) { - return !(sw->value && sw->value->type == unreachable) && - sw->condition->type != unreachable; + return !(sw->value && sw->value->type == unreachable) && + sw->condition->type != unreachable; } inline bool isBranchReachable(Expression* expr) { @@ -91,9 +92,7 @@ inline std::set<Name> getExitingBranches(Expression* ast) { struct Scanner : public PostWalker<Scanner> { std::set<Name> targets; - void visitBreak(Break* curr) { - targets.insert(curr->name); - } + void visitBreak(Break* curr) { targets.insert(curr->name); } void visitSwitch(Switch* curr) { for (auto target : curr->targets) { targets.insert(target); @@ -156,36 +155,47 @@ struct BranchSeeker : public PostWalker<BranchSeeker> { void noteFound(Expression* value) { found++; - if (found == 1) valueType = unreachable; - if (!value) valueType = none; - else if (value->type != unreachable) valueType = value->type; + if (found == 1) + valueType = unreachable; + if (!value) + valueType = none; + else if (value->type != unreachable) + valueType = value->type; } - void visitBreak(Break *curr) { + void visitBreak(Break* curr) { if (!named) { // ignore an unreachable break - if (curr->condition && curr->condition->type == unreachable) return; - if (curr->value && curr->value->type == unreachable) return; + if (curr->condition && curr->condition->type == unreachable) + return; + if (curr->value && curr->value->type == unreachable) + return; } // check the break - if (curr->name == target) noteFound(curr->value); + if (curr->name == target) + noteFound(curr->value); } - void visitSwitch(Switch *curr) { + void visitSwitch(Switch* curr) { if (!named) { // ignore an unreachable switch - if (curr->condition->type == unreachable) return; - if (curr->value && curr->value->type == unreachable) return; + if (curr->condition->type == unreachable) + return; + if (curr->value && curr->value->type == unreachable) + return; } // check the switch for (auto name : curr->targets) { - if (name == target) noteFound(curr->value); + if (name == target) + noteFound(curr->value); } - if (curr->default_ == target) noteFound(curr->value); + if (curr->default_ == target) + noteFound(curr->value); } static bool hasReachable(Expression* tree, Name target) { - if (!target.is()) return false; + if (!target.is()) + return false; BranchSeeker seeker(target); seeker.named = false; seeker.walk(tree); @@ -193,7 +203,8 @@ struct BranchSeeker : public PostWalker<BranchSeeker> { } static Index countReachable(Expression* tree, Name target) { - if (!target.is()) return 0; + if (!target.is()) + return 0; BranchSeeker seeker(target); seeker.named = false; seeker.walk(tree); @@ -201,14 +212,16 @@ struct BranchSeeker : public PostWalker<BranchSeeker> { } static bool hasNamed(Expression* tree, Name target) { - if (!target.is()) return false; + if (!target.is()) + return false; BranchSeeker seeker(target); seeker.walk(tree); return seeker.found > 0; } static Index countNamed(Expression* tree, Name target) { - if (!target.is()) return 0; + if (!target.is()) + return 0; BranchSeeker seeker(target); seeker.walk(tree); return seeker.found; @@ -220,4 +233,3 @@ struct BranchSeeker : public PostWalker<BranchSeeker> { } // namespace wasm #endif // wasm_ir_branch_h - diff --git a/src/ir/cost.h b/src/ir/cost.h index 5179f80b1..95c2178d9 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -17,35 +17,31 @@ #ifndef wasm_ir_cost_h #define wasm_ir_cost_h -#include <wasm.h> #include <wasm-traversal.h> +#include <wasm.h> namespace wasm { // Measure the execution cost of an AST. Very handwave-ey struct CostAnalyzer : public Visitor<CostAnalyzer, Index> { - CostAnalyzer(Expression* ast) { - cost = visit(ast); - } + CostAnalyzer(Expression* ast) { cost = visit(ast); } Index cost; - Index maybeVisit(Expression* curr) { - return curr ? visit(curr) : 0; - } + Index maybeVisit(Expression* curr) { return curr ? visit(curr) : 0; } Index visitBlock(Block* curr) { Index ret = 0; - for (auto* child : curr->list) ret += visit(child); + for (auto* child : curr->list) + ret += visit(child); return ret; } Index visitIf(If* curr) { - return 1 + visit(curr->condition) + std::max(visit(curr->ifTrue), maybeVisit(curr->ifFalse)); - } - Index visitLoop(Loop* curr) { - return 5 * visit(curr->body); + return 1 + visit(curr->condition) + + std::max(visit(curr->ifTrue), maybeVisit(curr->ifFalse)); } + Index visitLoop(Loop* curr) { return 5 * visit(curr->body); } Index visitBreak(Break* curr) { return 1 + maybeVisit(curr->value) + maybeVisit(curr->condition); } @@ -56,41 +52,29 @@ struct CostAnalyzer : public Visitor<CostAnalyzer, Index> { // XXX this does not take into account if the call is to an import, which // may be costlier in general Index ret = 4; - for (auto* child : curr->operands) ret += visit(child); + for (auto* child : curr->operands) + ret += visit(child); return ret; } Index visitCallIndirect(CallIndirect* curr) { Index ret = 6 + visit(curr->target); - for (auto* child : curr->operands) ret += visit(child); + for (auto* child : curr->operands) + ret += visit(child); return ret; } - Index visitGetLocal(GetLocal* curr) { - return 0; - } - Index visitSetLocal(SetLocal* curr) { - return 1; - } - Index visitGetGlobal(GetGlobal* curr) { - return 1; - } - Index visitSetGlobal(SetGlobal* curr) { - return 2; - } + Index visitGetLocal(GetLocal* curr) { return 0; } + Index visitSetLocal(SetLocal* curr) { return 1; } + Index visitGetGlobal(GetGlobal* curr) { return 1; } + Index visitSetGlobal(SetGlobal* curr) { return 2; } Index visitLoad(Load* curr) { return 1 + visit(curr->ptr) + 10 * curr->isAtomic; } Index visitStore(Store* curr) { return 2 + visit(curr->ptr) + visit(curr->value) + 10 * curr->isAtomic; } - Index visitAtomicRMW(AtomicRMW* curr) { - return 100; - } - Index visitAtomicCmpxchg(AtomicCmpxchg* curr) { - return 100; - } - Index visitConst(Const* curr) { - return 1; - } + Index visitAtomicRMW(AtomicRMW* curr) { return 100; } + Index visitAtomicCmpxchg(AtomicCmpxchg* curr) { return 100; } + Index visitConst(Const* curr) { return 1; } Index visitUnary(Unary* curr) { Index ret = 0; switch (curr->op) { @@ -151,9 +135,13 @@ struct CostAnalyzer : public Visitor<CostAnalyzer, Index> { case TruncSatSFloat32ToInt64: case TruncSatUFloat32ToInt64: case TruncSatSFloat64ToInt64: - case TruncSatUFloat64ToInt64: ret = 1; break; + case TruncSatUFloat64ToInt64: + ret = 1; + break; case SqrtFloat32: - case SqrtFloat64: ret = 2; break; + case SqrtFloat64: + ret = 2; + break; case SplatVecI8x16: case SplatVecI16x8: case SplatVecI32x4: @@ -186,188 +174,486 @@ struct CostAnalyzer : public Visitor<CostAnalyzer, Index> { case ConvertSVecI32x4ToVecF32x4: case ConvertUVecI32x4ToVecF32x4: case ConvertSVecI64x2ToVecF64x2: - case ConvertUVecI64x2ToVecF64x2: return 1; - case InvalidUnary: WASM_UNREACHABLE(); + case ConvertUVecI64x2ToVecF64x2: + return 1; + case InvalidUnary: + WASM_UNREACHABLE(); } return ret + visit(curr->value); } Index visitBinary(Binary* curr) { Index ret = 0; switch (curr->op) { - case AddInt32: ret = 1; break; - case SubInt32: ret = 1; break; - case MulInt32: ret = 2; break; - case DivSInt32: ret = 3; break; - case DivUInt32: ret = 3; break; - case RemSInt32: ret = 3; break; - case RemUInt32: ret = 3; break; - case AndInt32: ret = 1; break; - case OrInt32: ret = 1; break; - case XorInt32: ret = 1; break; - case ShlInt32: ret = 1; break; - case ShrUInt32: ret = 1; break; - case ShrSInt32: ret = 1; break; - case RotLInt32: ret = 1; break; - case RotRInt32: ret = 1; break; - case AddInt64: ret = 1; break; - case SubInt64: ret = 1; break; - case MulInt64: ret = 2; break; - case DivSInt64: ret = 3; break; - case DivUInt64: ret = 3; break; - case RemSInt64: ret = 3; break; - case RemUInt64: ret = 3; break; - case AndInt64: ret = 1; break; - case OrInt64: ret = 1; break; - case XorInt64: ret = 1; break; - case ShlInt64: ret = 1; break; - case ShrUInt64: ret = 1; break; - case ShrSInt64: ret = 1; break; - case RotLInt64: ret = 1; break; - case RotRInt64: ret = 1; break; - case AddFloat32: ret = 1; break; - case SubFloat32: ret = 1; break; - case MulFloat32: ret = 2; break; - case DivFloat32: ret = 3; break; - case CopySignFloat32: ret = 1; break; - case MinFloat32: ret = 1; break; - case MaxFloat32: ret = 1; break; - case AddFloat64: ret = 1; break; - case SubFloat64: ret = 1; break; - case MulFloat64: ret = 2; break; - case DivFloat64: ret = 3; break; - case CopySignFloat64: ret = 1; break; - case MinFloat64: ret = 1; break; - case MaxFloat64: ret = 1; break; - case LtUInt32: ret = 1; break; - case LtSInt32: ret = 1; break; - case LeUInt32: ret = 1; break; - case LeSInt32: ret = 1; break; - case GtUInt32: ret = 1; break; - case GtSInt32: ret = 1; break; - case GeUInt32: ret = 1; break; - case GeSInt32: ret = 1; break; - case LtUInt64: ret = 1; break; - case LtSInt64: ret = 1; break; - case LeUInt64: ret = 1; break; - case LeSInt64: ret = 1; break; - case GtUInt64: ret = 1; break; - case GtSInt64: ret = 1; break; - case GeUInt64: ret = 1; break; - case GeSInt64: ret = 1; break; - case LtFloat32: ret = 1; break; - case GtFloat32: ret = 1; break; - case LeFloat32: ret = 1; break; - case GeFloat32: ret = 1; break; - case LtFloat64: ret = 1; break; - case GtFloat64: ret = 1; break; - case LeFloat64: ret = 1; break; - case GeFloat64: ret = 1; break; - case EqInt32: ret = 1; break; - case NeInt32: ret = 1; break; - case EqInt64: ret = 1; break; - case NeInt64: ret = 1; break; - case EqFloat32: ret = 1; break; - case NeFloat32: ret = 1; break; - case EqFloat64: ret = 1; break; - case NeFloat64: ret = 1; break; - case EqVecI8x16: ret = 1; break; - case NeVecI8x16: ret = 1; break; - case LtSVecI8x16: ret = 1; break; - case LtUVecI8x16: ret = 1; break; - case LeSVecI8x16: ret = 1; break; - case LeUVecI8x16: ret = 1; break; - case GtSVecI8x16: ret = 1; break; - case GtUVecI8x16: ret = 1; break; - case GeSVecI8x16: ret = 1; break; - case GeUVecI8x16: ret = 1; break; - case EqVecI16x8: ret = 1; break; - case NeVecI16x8: ret = 1; break; - case LtSVecI16x8: ret = 1; break; - case LtUVecI16x8: ret = 1; break; - case LeSVecI16x8: ret = 1; break; - case LeUVecI16x8: ret = 1; break; - case GtSVecI16x8: ret = 1; break; - case GtUVecI16x8: ret = 1; break; - case GeSVecI16x8: ret = 1; break; - case GeUVecI16x8: ret = 1; break; - case EqVecI32x4: ret = 1; break; - case NeVecI32x4: ret = 1; break; - case LtSVecI32x4: ret = 1; break; - case LtUVecI32x4: ret = 1; break; - case LeSVecI32x4: ret = 1; break; - case LeUVecI32x4: ret = 1; break; - case GtSVecI32x4: ret = 1; break; - case GtUVecI32x4: ret = 1; break; - case GeSVecI32x4: ret = 1; break; - case GeUVecI32x4: ret = 1; break; - case EqVecF32x4: ret = 1; break; - case NeVecF32x4: ret = 1; break; - case LtVecF32x4: ret = 1; break; - case LeVecF32x4: ret = 1; break; - case GtVecF32x4: ret = 1; break; - case GeVecF32x4: ret = 1; break; - case EqVecF64x2: ret = 1; break; - case NeVecF64x2: ret = 1; break; - case LtVecF64x2: ret = 1; break; - case LeVecF64x2: ret = 1; break; - case GtVecF64x2: ret = 1; break; - case GeVecF64x2: ret = 1; break; - case AndVec128: ret = 1; break; - case OrVec128: ret = 1; break; - case XorVec128: ret = 1; break; - case AddVecI8x16: ret = 1; break; - case AddSatSVecI8x16: ret = 1; break; - case AddSatUVecI8x16: ret = 1; break; - case SubVecI8x16: ret = 1; break; - case SubSatSVecI8x16: ret = 1; break; - case SubSatUVecI8x16: ret = 1; break; - case MulVecI8x16: ret = 2; break; - case AddVecI16x8: ret = 1; break; - case AddSatSVecI16x8: ret = 1; break; - case AddSatUVecI16x8: ret = 1; break; - case SubVecI16x8: ret = 1; break; - case SubSatSVecI16x8: ret = 1; break; - case SubSatUVecI16x8: ret = 1; break; - case MulVecI16x8: ret = 2; break; - case AddVecI32x4: ret = 1; break; - case SubVecI32x4: ret = 1; break; - case MulVecI32x4: ret = 2; break; - case AddVecI64x2: ret = 1; break; - case SubVecI64x2: ret = 1; break; - case AddVecF32x4: ret = 1; break; - case SubVecF32x4: ret = 1; break; - case MulVecF32x4: ret = 2; break; - case DivVecF32x4: ret = 3; break; - case MinVecF32x4: ret = 1; break; - case MaxVecF32x4: ret = 1; break; - case AddVecF64x2: ret = 1; break; - case SubVecF64x2: ret = 1; break; - case MulVecF64x2: ret = 2; break; - case DivVecF64x2: ret = 3; break; - case MinVecF64x2: ret = 1; break; - case MaxVecF64x2: ret = 1; break; - case InvalidBinary: WASM_UNREACHABLE(); + case AddInt32: + ret = 1; + break; + case SubInt32: + ret = 1; + break; + case MulInt32: + ret = 2; + break; + case DivSInt32: + ret = 3; + break; + case DivUInt32: + ret = 3; + break; + case RemSInt32: + ret = 3; + break; + case RemUInt32: + ret = 3; + break; + case AndInt32: + ret = 1; + break; + case OrInt32: + ret = 1; + break; + case XorInt32: + ret = 1; + break; + case ShlInt32: + ret = 1; + break; + case ShrUInt32: + ret = 1; + break; + case ShrSInt32: + ret = 1; + break; + case RotLInt32: + ret = 1; + break; + case RotRInt32: + ret = 1; + break; + case AddInt64: + ret = 1; + break; + case SubInt64: + ret = 1; + break; + case MulInt64: + ret = 2; + break; + case DivSInt64: + ret = 3; + break; + case DivUInt64: + ret = 3; + break; + case RemSInt64: + ret = 3; + break; + case RemUInt64: + ret = 3; + break; + case AndInt64: + ret = 1; + break; + case OrInt64: + ret = 1; + break; + case XorInt64: + ret = 1; + break; + case ShlInt64: + ret = 1; + break; + case ShrUInt64: + ret = 1; + break; + case ShrSInt64: + ret = 1; + break; + case RotLInt64: + ret = 1; + break; + case RotRInt64: + ret = 1; + break; + case AddFloat32: + ret = 1; + break; + case SubFloat32: + ret = 1; + break; + case MulFloat32: + ret = 2; + break; + case DivFloat32: + ret = 3; + break; + case CopySignFloat32: + ret = 1; + break; + case MinFloat32: + ret = 1; + break; + case MaxFloat32: + ret = 1; + break; + case AddFloat64: + ret = 1; + break; + case SubFloat64: + ret = 1; + break; + case MulFloat64: + ret = 2; + break; + case DivFloat64: + ret = 3; + break; + case CopySignFloat64: + ret = 1; + break; + case MinFloat64: + ret = 1; + break; + case MaxFloat64: + ret = 1; + break; + case LtUInt32: + ret = 1; + break; + case LtSInt32: + ret = 1; + break; + case LeUInt32: + ret = 1; + break; + case LeSInt32: + ret = 1; + break; + case GtUInt32: + ret = 1; + break; + case GtSInt32: + ret = 1; + break; + case GeUInt32: + ret = 1; + break; + case GeSInt32: + ret = 1; + break; + case LtUInt64: + ret = 1; + break; + case LtSInt64: + ret = 1; + break; + case LeUInt64: + ret = 1; + break; + case LeSInt64: + ret = 1; + break; + case GtUInt64: + ret = 1; + break; + case GtSInt64: + ret = 1; + break; + case GeUInt64: + ret = 1; + break; + case GeSInt64: + ret = 1; + break; + case LtFloat32: + ret = 1; + break; + case GtFloat32: + ret = 1; + break; + case LeFloat32: + ret = 1; + break; + case GeFloat32: + ret = 1; + break; + case LtFloat64: + ret = 1; + break; + case GtFloat64: + ret = 1; + break; + case LeFloat64: + ret = 1; + break; + case GeFloat64: + ret = 1; + break; + case EqInt32: + ret = 1; + break; + case NeInt32: + ret = 1; + break; + case EqInt64: + ret = 1; + break; + case NeInt64: + ret = 1; + break; + case EqFloat32: + ret = 1; + break; + case NeFloat32: + ret = 1; + break; + case EqFloat64: + ret = 1; + break; + case NeFloat64: + ret = 1; + break; + case EqVecI8x16: + ret = 1; + break; + case NeVecI8x16: + ret = 1; + break; + case LtSVecI8x16: + ret = 1; + break; + case LtUVecI8x16: + ret = 1; + break; + case LeSVecI8x16: + ret = 1; + break; + case LeUVecI8x16: + ret = 1; + break; + case GtSVecI8x16: + ret = 1; + break; + case GtUVecI8x16: + ret = 1; + break; + case GeSVecI8x16: + ret = 1; + break; + case GeUVecI8x16: + ret = 1; + break; + case EqVecI16x8: + ret = 1; + break; + case NeVecI16x8: + ret = 1; + break; + case LtSVecI16x8: + ret = 1; + break; + case LtUVecI16x8: + ret = 1; + break; + case LeSVecI16x8: + ret = 1; + break; + case LeUVecI16x8: + ret = 1; + break; + case GtSVecI16x8: + ret = 1; + break; + case GtUVecI16x8: + ret = 1; + break; + case GeSVecI16x8: + ret = 1; + break; + case GeUVecI16x8: + ret = 1; + break; + case EqVecI32x4: + ret = 1; + break; + case NeVecI32x4: + ret = 1; + break; + case LtSVecI32x4: + ret = 1; + break; + case LtUVecI32x4: + ret = 1; + break; + case LeSVecI32x4: + ret = 1; + break; + case LeUVecI32x4: + ret = 1; + break; + case GtSVecI32x4: + ret = 1; + break; + case GtUVecI32x4: + ret = 1; + break; + case GeSVecI32x4: + ret = 1; + break; + case GeUVecI32x4: + ret = 1; + break; + case EqVecF32x4: + ret = 1; + break; + case NeVecF32x4: + ret = 1; + break; + case LtVecF32x4: + ret = 1; + break; + case LeVecF32x4: + ret = 1; + break; + case GtVecF32x4: + ret = 1; + break; + case GeVecF32x4: + ret = 1; + break; + case EqVecF64x2: + ret = 1; + break; + case NeVecF64x2: + ret = 1; + break; + case LtVecF64x2: + ret = 1; + break; + case LeVecF64x2: + ret = 1; + break; + case GtVecF64x2: + ret = 1; + break; + case GeVecF64x2: + ret = 1; + break; + case AndVec128: + ret = 1; + break; + case OrVec128: + ret = 1; + break; + case XorVec128: + ret = 1; + break; + case AddVecI8x16: + ret = 1; + break; + case AddSatSVecI8x16: + ret = 1; + break; + case AddSatUVecI8x16: + ret = 1; + break; + case SubVecI8x16: + ret = 1; + break; + case SubSatSVecI8x16: + ret = 1; + break; + case SubSatUVecI8x16: + ret = 1; + break; + case MulVecI8x16: + ret = 2; + break; + case AddVecI16x8: + ret = 1; + break; + case AddSatSVecI16x8: + ret = 1; + break; + case AddSatUVecI16x8: + ret = 1; + break; + case SubVecI16x8: + ret = 1; + break; + case SubSatSVecI16x8: + ret = 1; + break; + case SubSatUVecI16x8: + ret = 1; + break; + case MulVecI16x8: + ret = 2; + break; + case AddVecI32x4: + ret = 1; + break; + case SubVecI32x4: + ret = 1; + break; + case MulVecI32x4: + ret = 2; + break; + case AddVecI64x2: + ret = 1; + break; + case SubVecI64x2: + ret = 1; + break; + case AddVecF32x4: + ret = 1; + break; + case SubVecF32x4: + ret = 1; + break; + case MulVecF32x4: + ret = 2; + break; + case DivVecF32x4: + ret = 3; + break; + case MinVecF32x4: + ret = 1; + break; + case MaxVecF32x4: + ret = 1; + break; + case AddVecF64x2: + ret = 1; + break; + case SubVecF64x2: + ret = 1; + break; + case MulVecF64x2: + ret = 2; + break; + case DivVecF64x2: + ret = 3; + break; + case MinVecF64x2: + ret = 1; + break; + case MaxVecF64x2: + ret = 1; + break; + case InvalidBinary: + WASM_UNREACHABLE(); } return ret + visit(curr->left) + visit(curr->right); } Index visitSelect(Select* curr) { - return 2 + visit(curr->condition) + visit(curr->ifTrue) + visit(curr->ifFalse); - } - Index visitDrop(Drop* curr) { - return visit(curr->value); - } - Index visitReturn(Return* curr) { - return maybeVisit(curr->value); - } - Index visitHost(Host* curr) { - return 100; - } - Index visitNop(Nop* curr) { - return 0; - } - Index visitUnreachable(Unreachable* curr) { - return 0; + return 2 + visit(curr->condition) + visit(curr->ifTrue) + + visit(curr->ifFalse); } + Index visitDrop(Drop* curr) { return visit(curr->value); } + Index visitReturn(Return* curr) { return maybeVisit(curr->value); } + Index visitHost(Host* curr) { return 100; } + Index visitNop(Nop* curr) { return 0; } + Index visitUnreachable(Unreachable* curr) { return 0; } }; } // namespace wasm diff --git a/src/ir/effects.h b/src/ir/effects.h index e1fa77af0..9a3f29d8a 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -17,31 +17,38 @@ #ifndef wasm_ir_effects_h #define wasm_ir_effects_h +#include "pass.h" +#include "wasm-traversal.h" + namespace wasm { // Look for side effects, including control flow // TODO: optimize -struct EffectAnalyzer : public PostWalker<EffectAnalyzer, OverriddenVisitor<EffectAnalyzer>> { - EffectAnalyzer(PassOptions& passOptions, Expression *ast = nullptr) { +struct EffectAnalyzer + : public PostWalker<EffectAnalyzer, OverriddenVisitor<EffectAnalyzer>> { + EffectAnalyzer(PassOptions& passOptions, Expression* ast = nullptr) { ignoreImplicitTraps = passOptions.ignoreImplicitTraps; debugInfo = passOptions.debugInfo; - if (ast) analyze(ast); + if (ast) + analyze(ast); } bool ignoreImplicitTraps; bool debugInfo; - void analyze(Expression *ast) { + void analyze(Expression* ast) { breakNames.clear(); walk(ast); // if we are left with breaks, they are external - if (breakNames.size() > 0) branches = true; + if (breakNames.size() > 0) + branches = true; } // Core effect tracking - bool branches = false; // branches out of this expression, returns, infinite loops, etc + // branches out of this expression, returns, infinite loops, etc + bool branches = false; bool calls = false; std::set<Index> localsRead; std::set<Index> localsWritten; @@ -49,30 +56,46 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer, OverriddenVisitor<Effe std::set<Name> globalsWritten; bool readsMemory = false; bool writesMemory = false; - bool implicitTrap = false; // a load or div/rem, which may trap. we ignore trap - // differences, so it is ok to reorder these, but we can't - // remove them, as they count as side effects, and we - // can't move them in a way that would cause other noticeable - // (global) side effects - bool isAtomic = false; // An atomic load/store/RMW/Cmpxchg or an operator that - // has a defined ordering wrt atomics (e.g. grow_memory) + // a load or div/rem, which may trap. we ignore trap differences, so it is ok + // to reorder these, but we can't remove them, as they count as side effects, + // and we can't move them in a way that would cause other noticeable (global) + // side effects + bool implicitTrap = false; + // An atomic load/store/RMW/Cmpxchg or an operator that has a defined ordering + // wrt atomics (e.g. grow_memory) + bool isAtomic = false; // Helper functions to check for various effect types - bool accessesLocal() const { return localsRead.size() + localsWritten.size() > 0; } - bool accessesGlobal() const { return globalsRead.size() + globalsWritten.size() > 0; } + bool accessesLocal() const { + return localsRead.size() + localsWritten.size() > 0; + } + bool accessesGlobal() const { + return globalsRead.size() + globalsWritten.size() > 0; + } bool accessesMemory() const { return calls || readsMemory || writesMemory; } - bool hasGlobalSideEffects() const { return calls || globalsWritten.size() > 0 || writesMemory || isAtomic; } - bool hasSideEffects() const { return hasGlobalSideEffects() || localsWritten.size() > 0 || branches || implicitTrap; } - bool hasAnything() const { return branches || calls || accessesLocal() || readsMemory || writesMemory || accessesGlobal() || implicitTrap || isAtomic; } + bool hasGlobalSideEffects() const { + return calls || globalsWritten.size() > 0 || writesMemory || isAtomic; + } + bool hasSideEffects() const { + return hasGlobalSideEffects() || localsWritten.size() > 0 || branches || + implicitTrap; + } + bool hasAnything() const { + return branches || calls || accessesLocal() || readsMemory || + writesMemory || accessesGlobal() || implicitTrap || isAtomic; + } - bool noticesGlobalSideEffects() { return calls || readsMemory || isAtomic || globalsRead.size(); } + bool noticesGlobalSideEffects() { + return calls || readsMemory || isAtomic || globalsRead.size(); + } // check if we break to anything external from ourselves bool hasExternalBreakTargets() { return !breakNames.empty(); } - // checks if these effects would invalidate another set (e.g., if we write, we invalidate someone that reads, they can't be moved past us) + // checks if these effects would invalidate another set (e.g., if we write, we + // invalidate someone that reads, they can't be moved past us) bool invalidates(const EffectAnalyzer& other) { if ((branches && other.hasSideEffects()) || (other.branches && hasSideEffects()) || @@ -92,25 +115,30 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer, OverriddenVisitor<Effe } } for (auto local : localsRead) { - if (other.localsWritten.count(local)) return true; + if (other.localsWritten.count(local)) + return true; } - if ((accessesGlobal() && other.calls) || (other.accessesGlobal() && calls)) { + if ((accessesGlobal() && other.calls) || + (other.accessesGlobal() && calls)) { return true; } for (auto global : globalsWritten) { - if (other.globalsWritten.count(global) || other.globalsRead.count(global)) { + if (other.globalsWritten.count(global) || + other.globalsRead.count(global)) { return true; } } for (auto global : globalsRead) { - if (other.globalsWritten.count(global)) return true; + if (other.globalsWritten.count(global)) + return true; } // we are ok to reorder implicit traps, but not conditionalize them if ((implicitTrap && other.branches) || (other.implicitTrap && branches)) { return true; } // we can't reorder an implicit trap in a way that alters global state - if ((implicitTrap && other.hasGlobalSideEffects()) || (other.implicitTrap && hasGlobalSideEffects())) { + if ((implicitTrap && other.hasGlobalSideEffects()) || + (other.implicitTrap && hasGlobalSideEffects())) { return true; } return false; @@ -123,14 +151,19 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer, OverriddenVisitor<Effe writesMemory = writesMemory || other.writesMemory; implicitTrap = implicitTrap || other.implicitTrap; isAtomic = isAtomic || other.isAtomic; - for (auto i : other.localsRead) localsRead.insert(i); - for (auto i : other.localsWritten) localsWritten.insert(i); - for (auto i : other.globalsRead) globalsRead.insert(i); - for (auto i : other.globalsWritten) globalsWritten.insert(i); + for (auto i : other.localsRead) + localsRead.insert(i); + for (auto i : other.localsWritten) + localsWritten.insert(i); + for (auto i : other.globalsRead) + globalsRead.insert(i); + for (auto i : other.globalsWritten) + globalsWritten.insert(i); } - // the checks above happen after the node's children were processed, in the order of execution - // we must also check for control flow that happens before the children, i.e., loops + // the checks above happen after the node's children were processed, in the + // order of execution we must also check for control flow that happens before + // the children, i.e., loops bool checkPre(Expression* curr) { if (curr->is<Loop>()) { branches = true; @@ -150,35 +183,35 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer, OverriddenVisitor<Effe std::set<Name> breakNames; void visitBlock(Block* curr) { - if (curr->name.is()) breakNames.erase(curr->name); // these were internal breaks + if (curr->name.is()) + breakNames.erase(curr->name); // these were internal breaks } void visitIf(If* curr) {} void visitLoop(Loop* curr) { - if (curr->name.is()) breakNames.erase(curr->name); // these were internal breaks + if (curr->name.is()) + breakNames.erase(curr->name); // these were internal breaks // if the loop is unreachable, then there is branching control flow: - // (1) if the body is unreachable because of a (return), uncaught (br) etc., then we - // already noted branching, so it is ok to mark it again (if we have *caught* - // (br)s, then they did not lead to the loop body being unreachable). - // (same logic applies to blocks) - // (2) if the loop is unreachable because it only has branches up to the loop - // top, but no way to get out, then it is an infinite loop, and we consider - // that a branching side effect (note how the same logic does not apply to - // blocks). + // (1) if the body is unreachable because of a (return), uncaught (br) + // etc., then we already noted branching, so it is ok to mark it again + // (if we have *caught* (br)s, then they did not lead to the loop body + // being unreachable). (same logic applies to blocks) + // (2) if the loop is unreachable because it only has branches up to the + // loop top, but no way to get out, then it is an infinite loop, and we + // consider that a branching side effect (note how the same logic does + // not apply to blocks). if (curr->type == unreachable) { branches = true; } } - void visitBreak(Break *curr) { - breakNames.insert(curr->name); - } - void visitSwitch(Switch *curr) { + void visitBreak(Break* curr) { breakNames.insert(curr->name); } + void visitSwitch(Switch* curr) { for (auto name : curr->targets) { breakNames.insert(name); } breakNames.insert(curr->default_); } - void visitCall(Call *curr) { + void visitCall(Call* curr) { calls = true; if (debugInfo) { // debugInfo call imports must be preserved very strongly, do not @@ -187,40 +220,36 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer, OverriddenVisitor<Effe branches = true; } } - void visitCallIndirect(CallIndirect *curr) { calls = true; } - void visitGetLocal(GetLocal *curr) { - localsRead.insert(curr->index); - } - void visitSetLocal(SetLocal *curr) { - localsWritten.insert(curr->index); - } - void visitGetGlobal(GetGlobal *curr) { - globalsRead.insert(curr->name); - } - void visitSetGlobal(SetGlobal *curr) { - globalsWritten.insert(curr->name); - } - void visitLoad(Load *curr) { + void visitCallIndirect(CallIndirect* curr) { calls = true; } + void visitGetLocal(GetLocal* curr) { localsRead.insert(curr->index); } + void visitSetLocal(SetLocal* curr) { localsWritten.insert(curr->index); } + void visitGetGlobal(GetGlobal* curr) { globalsRead.insert(curr->name); } + void visitSetGlobal(SetGlobal* curr) { globalsWritten.insert(curr->name); } + void visitLoad(Load* curr) { readsMemory = true; isAtomic |= curr->isAtomic; - if (!ignoreImplicitTraps) implicitTrap = true; + if (!ignoreImplicitTraps) + implicitTrap = true; } - void visitStore(Store *curr) { + void visitStore(Store* curr) { writesMemory = true; isAtomic |= curr->isAtomic; - if (!ignoreImplicitTraps) implicitTrap = true; + if (!ignoreImplicitTraps) + implicitTrap = true; } void visitAtomicRMW(AtomicRMW* curr) { readsMemory = true; writesMemory = true; isAtomic = true; - if (!ignoreImplicitTraps) implicitTrap = true; + if (!ignoreImplicitTraps) + implicitTrap = true; } void visitAtomicCmpxchg(AtomicCmpxchg* curr) { readsMemory = true; writesMemory = true; isAtomic = true; - if (!ignoreImplicitTraps) implicitTrap = true; + if (!ignoreImplicitTraps) + implicitTrap = true; } void visitAtomicWait(AtomicWait* curr) { readsMemory = true; @@ -229,16 +258,18 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer, OverriddenVisitor<Effe // write. writesMemory = true; isAtomic = true; - if (!ignoreImplicitTraps) implicitTrap = true; + if (!ignoreImplicitTraps) + implicitTrap = true; } void visitAtomicNotify(AtomicNotify* curr) { - // AtomicNotify doesn't strictly write memory, but it does modify the waiters - // list associated with the specified address, which we can think of as a - // write. + // AtomicNotify doesn't strictly write memory, but it does modify the + // waiters list associated with the specified address, which we can think of + // as a write. readsMemory = true; writesMemory = true; isAtomic = true; - if (!ignoreImplicitTraps) implicitTrap = true; + if (!ignoreImplicitTraps) + implicitTrap = true; }; void visitSIMDExtract(SIMDExtract* curr) {} void visitSIMDReplace(SIMDReplace* curr) {} @@ -247,21 +278,25 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer, OverriddenVisitor<Effe void visitSIMDShift(SIMDShift* curr) {} void visitMemoryInit(MemoryInit* curr) { writesMemory = true; - if (!ignoreImplicitTraps) implicitTrap = true; + if (!ignoreImplicitTraps) + implicitTrap = true; } void visitDataDrop(DataDrop* curr) { // prevent reordering with memory.init readsMemory = true; - if (!ignoreImplicitTraps) implicitTrap = true; + if (!ignoreImplicitTraps) + implicitTrap = true; } void visitMemoryCopy(MemoryCopy* curr) { readsMemory = true; writesMemory = true; - if (!ignoreImplicitTraps) implicitTrap = true; + if (!ignoreImplicitTraps) + implicitTrap = true; } void visitMemoryFill(MemoryFill* curr) { writesMemory = true; - if (!ignoreImplicitTraps) implicitTrap = true; + if (!ignoreImplicitTraps) + implicitTrap = true; } void visitConst(Const* curr) {} void visitUnary(Unary* curr) { @@ -302,20 +337,22 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer, OverriddenVisitor<Effe } void visitSelect(Select* curr) {} void visitDrop(Drop* curr) {} - void visitReturn(Return *curr) { branches = true; } - void visitHost(Host *curr) { + void visitReturn(Return* curr) { branches = true; } + void visitHost(Host* curr) { calls = true; - // grow_memory modifies the set of valid addresses, and thus can be modeled as modifying memory + // grow_memory modifies the set of valid addresses, and thus can be modeled + // as modifying memory writesMemory = true; // Atomics are also sequentially consistent with grow_memory. isAtomic = true; } void visitNop(Nop* curr) {} - void visitUnreachable(Unreachable *curr) { branches = true; } + void visitUnreachable(Unreachable* curr) { branches = true; } // Helpers - static bool canReorder(PassOptions& passOptions, Expression* a, Expression* b) { + static bool + canReorder(PassOptions& passOptions, Expression* a, Expression* b) { EffectAnalyzer aEffects(passOptions, a); EffectAnalyzer bEffects(passOptions, b); return !aEffects.invalidates(bEffects); diff --git a/src/ir/equivalent_sets.h b/src/ir/equivalent_sets.h index dc3e348d2..fc4b1db3d 100644 --- a/src/ir/equivalent_sets.h +++ b/src/ir/equivalent_sets.h @@ -31,9 +31,7 @@ struct EquivalentSets { std::unordered_map<Index, std::shared_ptr<Set>> indexSets; // Clears the state completely, removing all equivalences. - void clear() { - indexSets.clear(); - } + void clear() { indexSets.clear(); } // Resets an index, removing any equivalences between it and others. void reset(Index index) { @@ -69,7 +67,8 @@ struct EquivalentSets { // Checks whether two indexes contain the same data. bool check(Index a, Index b) { - if (a == b) return true; + if (a == b) + return true; if (auto* set = getEquivalents(a)) { if (set->find(b) != set->end()) { return true; @@ -91,4 +90,3 @@ struct EquivalentSets { } // namespace wasm #endif // wasm_ir_equivalent_sets_h - diff --git a/src/ir/features.h b/src/ir/features.h index 505e239a5..6dff96398 100644 --- a/src/ir/features.h +++ b/src/ir/features.h @@ -17,10 +17,10 @@ #ifndef wasm_ir_features_h #define wasm_ir_features_h -#include <wasm.h> +#include <ir/iteration.h> #include <wasm-binary.h> #include <wasm-traversal.h> -#include <ir/iteration.h> +#include <wasm.h> namespace wasm { diff --git a/src/ir/find_all.h b/src/ir/find_all.h index 44dc7a675..1abaab772 100644 --- a/src/ir/find_all.h +++ b/src/ir/find_all.h @@ -23,12 +23,12 @@ namespace wasm { // Find all instances of a certain node type -template<typename T> -struct FindAll { +template<typename T> struct FindAll { std::vector<T*> list; FindAll(Expression* ast) { - struct Finder : public PostWalker<Finder, UnifiedExpressionVisitor<Finder>> { + struct Finder + : public PostWalker<Finder, UnifiedExpressionVisitor<Finder>> { std::vector<T*>* list; void visitExpression(Expression* curr) { if (curr->is<T>()) { @@ -44,7 +44,8 @@ struct FindAll { // Find all pointers to instances of a certain node type -struct PointerFinder : public PostWalker<PointerFinder, UnifiedExpressionVisitor<PointerFinder>> { +struct PointerFinder + : public PostWalker<PointerFinder, UnifiedExpressionVisitor<PointerFinder>> { Expression::Id id; std::vector<Expression**>* list; void visitExpression(Expression* curr) { @@ -54,8 +55,7 @@ struct PointerFinder : public PostWalker<PointerFinder, UnifiedExpressionVisitor } }; -template<typename T> -struct FindAllPointers { +template<typename T> struct FindAllPointers { std::vector<Expression**> list; FindAllPointers(Expression* ast) { @@ -69,4 +69,3 @@ struct FindAllPointers { } // namespace wasm #endif // wasm_ir_find_all_h - diff --git a/src/ir/flat.h b/src/ir/flat.h index ed6178ef5..199d5e35e 100644 --- a/src/ir/flat.h +++ b/src/ir/flat.h @@ -55,8 +55,9 @@ #ifndef wasm_ir_flat_h #define wasm_ir_flat_h -#include "wasm-traversal.h" #include "ir/iteration.h" +#include "pass.h" +#include "wasm-traversal.h" namespace wasm { @@ -67,23 +68,29 @@ inline bool isControlFlowStructure(Expression* curr) { } inline void verifyFlatness(Function* func) { - struct VerifyFlatness : public PostWalker<VerifyFlatness, UnifiedExpressionVisitor<VerifyFlatness>> { + struct VerifyFlatness + : public PostWalker<VerifyFlatness, + UnifiedExpressionVisitor<VerifyFlatness>> { void visitExpression(Expression* curr) { if (isControlFlowStructure(curr)) { - verify(!isConcreteType(curr->type), "control flow structures must not flow values"); + verify(!isConcreteType(curr->type), + "control flow structures must not flow values"); } else if (curr->is<SetLocal>()) { verify(!isConcreteType(curr->type), "tees are not allowed, only sets"); } else { for (auto* child : ChildIterator(curr)) { - verify(child->is<Const>() || child->is<GetLocal>() || child->is<Unreachable>(), - "instructions must only have const, local.get, or unreachable as children"); + verify(child->is<Const>() || child->is<GetLocal>() || + child->is<Unreachable>(), + "instructions must only have const, local.get, or unreachable " + "as children"); } } } void verify(bool condition, const char* message) { if (!condition) { - Fatal() << "IR must be flat: run --flatten beforehand (" << message << ", in " << getFunction()->name << ')'; + Fatal() << "IR must be flat: run --flatten beforehand (" << message + << ", in " << getFunction()->name << ')'; } } }; @@ -91,20 +98,19 @@ inline void verifyFlatness(Function* func) { VerifyFlatness verifier; verifier.walkFunction(func); verifier.setFunction(func); - verifier.verify(!isConcreteType(func->body->type), "function bodies must not flow values"); + verifier.verify(!isConcreteType(func->body->type), + "function bodies must not flow values"); } inline void verifyFlatness(Module* module) { - struct VerifyFlatness : public WalkerPass<PostWalker<VerifyFlatness, UnifiedExpressionVisitor<VerifyFlatness>>> { + struct VerifyFlatness + : public WalkerPass< + PostWalker<VerifyFlatness, UnifiedExpressionVisitor<VerifyFlatness>>> { bool isFunctionParallel() override { return true; } - VerifyFlatness* create() override { - return new VerifyFlatness(); - } + VerifyFlatness* create() override { return new VerifyFlatness(); } - void doVisitFunction(Function* func) { - verifyFlatness(func); - } + void doVisitFunction(Function* func) { verifyFlatness(func); } }; PassRunner runner(module); @@ -113,7 +119,7 @@ inline void verifyFlatness(Module* module) { runner.run(); } -} // namespace Fkat +} // namespace Flat } // namespace wasm diff --git a/src/ir/function-type-utils.h b/src/ir/function-type-utils.h index 3c98cb16b..ee1e95f70 100644 --- a/src/ir/function-type-utils.h +++ b/src/ir/function-type-utils.h @@ -17,7 +17,6 @@ #ifndef wasm_ir_function_type_utils_h #define wasm_ir_function_type_utils_h - namespace wasm { namespace FunctionTypeUtils { diff --git a/src/ir/function-utils.h b/src/ir/function-utils.h index 3895b33bf..446bcc8bb 100644 --- a/src/ir/function-utils.h +++ b/src/ir/function-utils.h @@ -17,8 +17,8 @@ #ifndef wasm_ir_function_h #define wasm_ir_function_h -#include "wasm.h" #include "ir/utils.h" +#include "wasm.h" namespace wasm { @@ -28,13 +28,18 @@ namespace FunctionUtils { // everything but their name (which can't be the same, in the same // module!) - same params, vars, body, result, etc. inline bool equal(Function* left, Function* right) { - if (left->getNumParams() != right->getNumParams()) return false; - if (left->getNumVars() != right->getNumVars()) return false; + if (left->getNumParams() != right->getNumParams()) + return false; + if (left->getNumVars() != right->getNumVars()) + return false; for (Index i = 0; i < left->getNumLocals(); i++) { - if (left->getLocalType(i) != right->getLocalType(i)) return false; + if (left->getLocalType(i) != right->getLocalType(i)) + return false; } - if (left->result != right->result) return false; - if (left->type != right->type) return false; + if (left->result != right->result) + return false; + if (left->type != right->type) + return false; if (!left->imported() && !right->imported()) { return ExpressionAnalyzer::equal(left->body, right->body); } @@ -46,4 +51,3 @@ inline bool equal(Function* left, Function* right) { } // namespace wasm #endif // wasm_ir_function_h - diff --git a/src/ir/global-utils.h b/src/ir/global-utils.h index fa4cdc44a..b32605805 100644 --- a/src/ir/global-utils.h +++ b/src/ir/global-utils.h @@ -20,37 +20,39 @@ #include <algorithm> #include <vector> +#include "ir/module-utils.h" #include "literal.h" #include "wasm.h" -#include "ir/module-utils.h" namespace wasm { namespace GlobalUtils { - // find a global initialized to the value of an import, or null if no such global - inline Global* getGlobalInitializedToImport(Module& wasm, Name module, Name base) { - // find the import - Name imported; - ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) { - if (import->module == module && import->base == base) { - imported = import->name; +// find a global initialized to the value of an import, or null if no such +// global +inline Global* +getGlobalInitializedToImport(Module& wasm, Name module, Name base) { + // find the import + Name imported; + ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) { + if (import->module == module && import->base == base) { + imported = import->name; + } + }); + if (imported.isNull()) + return nullptr; + // find a global inited to it + Global* ret = nullptr; + ModuleUtils::iterDefinedGlobals(wasm, [&](Global* defined) { + if (auto* init = defined->init->dynCast<GetGlobal>()) { + if (init->name == imported) { + ret = defined; } - }); - if (imported.isNull()) return nullptr; - // find a global inited to it - Global* ret = nullptr; - ModuleUtils::iterDefinedGlobals(wasm, [&](Global* defined) { - if (auto* init = defined->init->dynCast<GetGlobal>()) { - if (init->name == imported) { - ret = defined; - } - } - }); - return ret; - } + } + }); + return ret; } +} // namespace GlobalUtils } // namespace wasm #endif // wasm_ir_global_h - diff --git a/src/ir/hashed.h b/src/ir/hashed.h index 61bf19478..676b82cb4 100644 --- a/src/ir/hashed.h +++ b/src/ir/hashed.h @@ -17,9 +17,9 @@ #ifndef _wasm_ir_hashed_h #define _wasm_ir_hashed_h +#include "ir/utils.h" #include "support/hash.h" #include "wasm.h" -#include "ir/utils.h" namespace wasm { @@ -34,24 +34,26 @@ struct HashedExpression { } } - HashedExpression(const HashedExpression& other) : expr(other.expr), hash(other.hash) {} + HashedExpression(const HashedExpression& other) + : expr(other.expr), hash(other.hash) {} }; struct ExpressionHasher { - HashType operator()(const HashedExpression value) const { - return value.hash; - } + HashType operator()(const HashedExpression value) const { return value.hash; } }; struct ExpressionComparer { bool operator()(const HashedExpression a, const HashedExpression b) const { - if (a.hash != b.hash) return false; + if (a.hash != b.hash) + return false; return ExpressionAnalyzer::equal(a.expr, b.expr); } }; template<typename T> -class HashedExpressionMap : public std::unordered_map<HashedExpression, T, ExpressionHasher, ExpressionComparer> { +class HashedExpressionMap + : public std:: + unordered_map<HashedExpression, T, ExpressionHasher, ExpressionComparer> { }; // A pass that hashes all functions @@ -63,21 +65,19 @@ struct FunctionHasher : public WalkerPass<PostWalker<FunctionHasher>> { FunctionHasher(Map* output) : output(output) {} - FunctionHasher* create() override { - return new FunctionHasher(output); - } + FunctionHasher* create() override { return new FunctionHasher(output); } static Map createMap(Module* module) { Map hashes; for (auto& func : module->functions) { - hashes[func.get()] = 0; // ensure an entry for each function - we must not modify the map shape in parallel, just the values + // ensure an entry for each function - we must not modify the map shape in + // parallel, just the values + hashes[func.get()] = 0; } return hashes; } - void doWalkFunction(Function* func) { - output->at(func) = hashFunction(func); - } + void doWalkFunction(Function* func) { output->at(func) = hashFunction(func); } static HashType hashFunction(Function* func) { HashType ret = 0; @@ -90,7 +90,9 @@ struct FunctionHasher : public WalkerPass<PostWalker<FunctionHasher>> { ret = rehash(ret, (HashType)type); } ret = rehash(ret, (HashType)func->result); - ret = rehash(ret, HashType(func->type.is() ? std::hash<wasm::Name>{}(func->type) : HashType(0))); + ret = rehash(ret, + HashType(func->type.is() ? std::hash<wasm::Name>{}(func->type) + : HashType(0))); ret = rehash(ret, (HashType)ExpressionAnalyzer::hash(func->body)); return ret; } @@ -102,4 +104,3 @@ private: } // namespace wasm #endif // _wasm_ir_hashed_h - diff --git a/src/ir/import-utils.h b/src/ir/import-utils.h index 5f7bbc9ed..950b9bfcb 100644 --- a/src/ir/import-utils.h +++ b/src/ir/import-utils.h @@ -61,18 +61,13 @@ struct ImportInfo { return nullptr; } - Index getNumImportedGlobals() { - return importedGlobals.size(); - } + Index getNumImportedGlobals() { return importedGlobals.size(); } - Index getNumImportedFunctions() { - return importedFunctions.size(); - } + Index getNumImportedFunctions() { return importedFunctions.size(); } Index getNumImports() { return getNumImportedGlobals() + getNumImportedFunctions() + - (wasm.memory.imported() ? 1 : 0) + - (wasm.table.imported() ? 1 : 0); + (wasm.memory.imported() ? 1 : 0) + (wasm.table.imported() ? 1 : 0); } Index getNumDefinedGlobals() { @@ -87,4 +82,3 @@ struct ImportInfo { } // namespace wasm #endif // wasm_ir_import_h - diff --git a/src/ir/iteration.h b/src/ir/iteration.h index c6e97d392..289e5fa01 100644 --- a/src/ir/iteration.h +++ b/src/ir/iteration.h @@ -17,8 +17,8 @@ #ifndef wasm_ir_iteration_h #define wasm_ir_iteration_h -#include "wasm.h" #include "wasm-traversal.h" +#include "wasm.h" namespace wasm { @@ -39,19 +39,16 @@ class ChildIterator { const ChildIterator& parent; Index index; - Iterator(const ChildIterator& parent, Index index) : parent(parent), index(index) {} + Iterator(const ChildIterator& parent, Index index) + : parent(parent), index(index) {} bool operator!=(const Iterator& other) const { return index != other.index || &parent != &(other.parent); } - void operator++() { - index++; - } + void operator++() { index++; } - Expression* operator*() { - return parent.children[index]; - } + Expression* operator*() { return parent.children[index]; } }; public: @@ -68,7 +65,8 @@ public: static void scan(Traverser* self, Expression** currp) { if (!self->scanned) { self->scanned = true; - PostWalker<Traverser, UnifiedExpressionVisitor<Traverser>>::scan(self, currp); + PostWalker<Traverser, UnifiedExpressionVisitor<Traverser>>::scan( + self, currp); } else { // This is one of the children. Do not scan further, just note it. self->children->push_back(*currp); @@ -80,15 +78,10 @@ public: traverser.walk(parent); } - Iterator begin() const { - return Iterator(*this, 0); - } - Iterator end() const { - return Iterator(*this, children.size()); - } + Iterator begin() const { return Iterator(*this, 0); } + Iterator end() const { return Iterator(*this, children.size()); } }; -} // wasm +} // namespace wasm #endif // wasm_ir_iteration_h - diff --git a/src/ir/label-utils.h b/src/ir/label-utils.h index f4fb77697..75c3b3a41 100644 --- a/src/ir/label-utils.h +++ b/src/ir/label-utils.h @@ -17,8 +17,8 @@ #ifndef wasm_ir_label_h #define wasm_ir_label_h -#include "wasm.h" #include "wasm-traversal.h" +#include "wasm.h" namespace wasm { @@ -28,9 +28,7 @@ namespace LabelUtils { // ones without duplicates class LabelManager : public PostWalker<LabelManager> { public: - LabelManager(Function* func) { - walkFunction(func); - } + LabelManager(Function* func) { walkFunction(func); } Name getUnique(std::string prefix) { while (1) { @@ -42,12 +40,8 @@ public: } } - void visitBlock(Block* curr) { - labels.insert(curr->name); - } - void visitLoop(Loop* curr) { - labels.insert(curr->name); - } + void visitBlock(Block* curr) { labels.insert(curr->name); } + void visitLoop(Loop* curr) { labels.insert(curr->name); } private: std::set<Name> labels; @@ -59,4 +53,3 @@ private: } // namespace wasm #endif // wasm_ir_label_h - diff --git a/src/ir/load-utils.h b/src/ir/load-utils.h index 8df4bcde8..36e94d0af 100644 --- a/src/ir/load-utils.h +++ b/src/ir/load-utils.h @@ -28,18 +28,16 @@ namespace LoadUtils { // fill in bits either signed or unsigned wise) inline bool isSignRelevant(Load* load) { auto type = load->type; - if (load->type == unreachable) return false; + if (load->type == unreachable) + return false; return !isFloatType(type) && load->bytes < getTypeSize(type); } // check if a load can be signed (which some opts want to do) -inline bool canBeSigned(Load* load) { - return !load->isAtomic; -} +inline bool canBeSigned(Load* load) { return !load->isAtomic; } } // namespace LoadUtils } // namespace wasm #endif // wasm_ir_load_h - diff --git a/src/ir/local-graph.h b/src/ir/local-graph.h index fd6a496c0..ae389c899 100644 --- a/src/ir/local-graph.h +++ b/src/ir/local-graph.h @@ -17,6 +17,8 @@ #ifndef wasm_ir_local_graph_h #define wasm_ir_local_graph_h +#include "wasm.h" + namespace wasm { // @@ -41,8 +43,9 @@ struct LocalGraph { typedef std::map<Expression*, Expression**> Locations; // externally useful information - GetSetses getSetses; // the sets affecting each get. a nullptr set means the initial - // value (0 for a var, the received value for a param) + GetSetses getSetses; // the sets affecting each get. a nullptr set means the + // initial value (0 for a var, the received value for a + // param) Locations locations; // where each get and set is (for easy replacing) // Optional: compute the influence graphs between sets and gets @@ -50,15 +53,18 @@ struct LocalGraph { void computeInfluences(); - std::unordered_map<GetLocal*, std::unordered_set<SetLocal*>> getInfluences; // for each get, the sets whose values are influenced by that get - std::unordered_map<SetLocal*, std::unordered_set<GetLocal*>> setInfluences; // for each set, the gets whose values are influenced by that set + // for each get, the sets whose values are influenced by that get + std::unordered_map<GetLocal*, std::unordered_set<SetLocal*>> getInfluences; + // for each set, the gets whose values are influenced by that set + std::unordered_map<SetLocal*, std::unordered_set<GetLocal*>> setInfluences; // Optional: Compute the local indexes that are SSA, in the sense of // * a single set for all the gets for that local index - // * the set dominates all the gets (logically implied by the former property) + // * the set dominates all the gets (logically implied by the former + // property) // * no other set (aside from the zero-init) - // The third property is not exactly standard SSA, but is useful since we are not in - // SSA form in our IR. To see why it matters, consider these: + // The third property is not exactly standard SSA, but is useful since we are + // not in SSA form in our IR. To see why it matters, consider these: // // x = 0 // zero init // [..] @@ -67,11 +73,11 @@ struct LocalGraph { // x = 30 // !!! // f(y) // - // The !!! line violates that property - it is another set for x, and it may interfere - // say with replacing f(y) with f(x + 20). Instead, if we know the only other possible set for x - // is the zero init, then things like the !!! line cannot exist, and it is valid to replace - // f(y) with f(x + 20). - // (This could be simpler, but in wasm the zero init always exists.) + // The !!! line violates that property - it is another set for x, and it may + // interfere say with replacing f(y) with f(x + 20). Instead, if we know the + // only other possible set for x is the zero init, then things like the !!! + // line cannot exist, and it is valid to replace f(y) with f(x + 20). (This + // could be simpler, but in wasm the zero init always exists.) void computeSSAIndexes(); @@ -84,4 +90,3 @@ private: } // namespace wasm #endif // wasm_ir_local_graph_h - diff --git a/src/ir/local-utils.h b/src/ir/local-utils.h index beb90d7b3..5d6b660cd 100644 --- a/src/ir/local-utils.h +++ b/src/ir/local-utils.h @@ -18,6 +18,7 @@ #define wasm_ir_local_utils_h #include <ir/effects.h> +#include <ir/manipulation.h> namespace wasm { @@ -25,25 +26,17 @@ struct GetLocalCounter : public PostWalker<GetLocalCounter> { std::vector<Index> num; GetLocalCounter() = default; - GetLocalCounter(Function* func) { - analyze(func, func->body); - } - GetLocalCounter(Function* func, Expression* ast) { - analyze(func, ast); - } + GetLocalCounter(Function* func) { analyze(func, func->body); } + GetLocalCounter(Function* func, Expression* ast) { analyze(func, ast); } - void analyze(Function* func) { - analyze(func, func->body); - } + void analyze(Function* func) { analyze(func, func->body); } void analyze(Function* func, Expression* ast) { num.resize(func->getNumLocals()); std::fill(num.begin(), num.end(), 0); walk(ast); } - void visitGetLocal(GetLocal *curr) { - num[curr->index]++; - } + void visitGetLocal(GetLocal* curr) { num[curr->index]++; } }; // Removes trivially unneeded sets: sets for whom there is no possible get, and @@ -53,19 +46,23 @@ struct UnneededSetRemover : public PostWalker<UnneededSetRemover> { GetLocalCounter* getLocalCounter = nullptr; - UnneededSetRemover(Function* func, PassOptions& passOptions) : passOptions(passOptions) { + UnneededSetRemover(Function* func, PassOptions& passOptions) + : passOptions(passOptions) { GetLocalCounter counter(func); UnneededSetRemover inner(counter, func, passOptions); removed = inner.removed; } - UnneededSetRemover(GetLocalCounter& getLocalCounter, Function* func, PassOptions& passOptions) : passOptions(passOptions), getLocalCounter(&getLocalCounter) { + UnneededSetRemover(GetLocalCounter& getLocalCounter, + Function* func, + PassOptions& passOptions) + : passOptions(passOptions), getLocalCounter(&getLocalCounter) { walk(func->body); } bool removed = false; - void visitSetLocal(SetLocal *curr) { + void visitSetLocal(SetLocal* curr) { // If no possible uses, remove. if (getLocalCounter->num[curr->index] == 0) { remove(curr); @@ -108,4 +105,3 @@ struct UnneededSetRemover : public PostWalker<UnneededSetRemover> { } // namespace wasm #endif // wasm_ir_local_utils_h - diff --git a/src/ir/localize.h b/src/ir/localize.h index c910d9f9b..05eadc593 100644 --- a/src/ir/localize.h +++ b/src/ir/localize.h @@ -44,4 +44,3 @@ struct Localizer { } // namespace wasm #endif // wasm_ir_localizer_h - diff --git a/src/ir/manipulation.h b/src/ir/manipulation.h index 57188ad68..d363fc547 100644 --- a/src/ir/manipulation.h +++ b/src/ir/manipulation.h @@ -22,48 +22,45 @@ namespace wasm { namespace ExpressionManipulator { - // Re-use a node's memory. This helps avoid allocation when optimizing. - template<typename InputType, typename OutputType> - inline OutputType* convert(InputType *input) { - static_assert(sizeof(OutputType) <= sizeof(InputType), - "Can only convert to a smaller size Expression node"); - input->~InputType(); // arena-allocaed, so no destructor, but avoid UB. - OutputType* output = (OutputType*)(input); - new (output) OutputType; - return output; - } - - // Convenience method for nop, which is a common conversion - template<typename InputType> - inline Nop* nop(InputType* target) { - return convert<InputType, Nop>(target); - } +// Re-use a node's memory. This helps avoid allocation when optimizing. +template<typename InputType, typename OutputType> +inline OutputType* convert(InputType* input) { + static_assert(sizeof(OutputType) <= sizeof(InputType), + "Can only convert to a smaller size Expression node"); + input->~InputType(); // arena-allocaed, so no destructor, but avoid UB. + OutputType* output = (OutputType*)(input); + new (output) OutputType; + return output; +} - // Convert a node that allocates - template<typename InputType, typename OutputType> - inline OutputType* convert(InputType *input, MixedArena& allocator) { - assert(sizeof(OutputType) <= sizeof(InputType)); - input->~InputType(); // arena-allocaed, so no destructor, but avoid UB. - OutputType* output = (OutputType*)(input); - new (output) OutputType(allocator); - return output; - } +// Convenience method for nop, which is a common conversion +template<typename InputType> inline Nop* nop(InputType* target) { + return convert<InputType, Nop>(target); +} - using CustomCopier = std::function<Expression*(Expression*)>; - Expression* flexibleCopy(Expression* original, Module& wasm, CustomCopier custom); +// Convert a node that allocates +template<typename InputType, typename OutputType> +inline OutputType* convert(InputType* input, MixedArena& allocator) { + assert(sizeof(OutputType) <= sizeof(InputType)); + input->~InputType(); // arena-allocaed, so no destructor, but avoid UB. + OutputType* output = (OutputType*)(input); + new (output) OutputType(allocator); + return output; +} - inline Expression* copy(Expression* original, Module& wasm) { - auto copy = [](Expression* curr) { - return nullptr; - }; - return flexibleCopy(original, wasm, copy); - } +using CustomCopier = std::function<Expression*(Expression*)>; +Expression* +flexibleCopy(Expression* original, Module& wasm, CustomCopier custom); - // Splice an item into the middle of a block's list - void spliceIntoBlock(Block* block, Index index, Expression* add); +inline Expression* copy(Expression* original, Module& wasm) { + auto copy = [](Expression* curr) { return nullptr; }; + return flexibleCopy(original, wasm, copy); } -} // wasm +// Splice an item into the middle of a block's list +void spliceIntoBlock(Block* block, Index index, Expression* add); +} // namespace ExpressionManipulator -#endif // wams_ir_manipulation_h +} // namespace wasm +#endif // wams_ir_manipulation_h diff --git a/src/ir/memory-utils.h b/src/ir/memory-utils.h index fbf1f646c..c1edc8ce9 100644 --- a/src/ir/memory-utils.h +++ b/src/ir/memory-utils.h @@ -21,153 +21,162 @@ #include <vector> #include "literal.h" -#include "wasm.h" #include "wasm-binary.h" +#include "wasm.h" namespace wasm { namespace MemoryUtils { - // flattens memory into a single data segment. returns true if successful - inline bool flatten(Memory& memory) { - if (memory.segments.size() == 0) return true; - std::vector<char> data; - for (auto& segment : memory.segments) { - if (segment.isPassive) { - return false; - } - auto* offset = segment.offset->dynCast<Const>(); - if (!offset) return false; - } - for (auto& segment : memory.segments) { - auto* offset = segment.offset->dynCast<Const>(); - Index start = offset->value.getInteger(); - Index end = start + segment.data.size(); - if (end > data.size()) { - data.resize(end); - } - std::copy(segment.data.begin(), segment.data.end(), data.begin() + start); - } - memory.segments.resize(1); - memory.segments[0].offset->cast<Const>()->value = Literal(int32_t(0)); - memory.segments[0].data.swap(data); +// flattens memory into a single data segment. returns true if successful +inline bool flatten(Memory& memory) { + if (memory.segments.size() == 0) return true; + std::vector<char> data; + for (auto& segment : memory.segments) { + if (segment.isPassive) { + return false; + } + auto* offset = segment.offset->dynCast<Const>(); + if (!offset) + return false; } - - // Ensures that the memory exists (of minimal size). - inline void ensureExists(Memory& memory) { - if (!memory.exists) { - memory.exists = true; - memory.initial = memory.max = 1; + for (auto& segment : memory.segments) { + auto* offset = segment.offset->dynCast<Const>(); + Index start = offset->value.getInteger(); + Index end = start + segment.data.size(); + if (end > data.size()) { + data.resize(end); } + std::copy(segment.data.begin(), segment.data.end(), data.begin() + start); + } + memory.segments.resize(1); + memory.segments[0].offset->cast<Const>()->value = Literal(int32_t(0)); + memory.segments[0].data.swap(data); + return true; +} + +// Ensures that the memory exists (of minimal size). +inline void ensureExists(Memory& memory) { + if (!memory.exists) { + memory.exists = true; + memory.initial = memory.max = 1; } +} - // Try to merge segments until they fit into web limitations. - // Return true if successful. - inline bool ensureLimitedSegments(Module& module) { - Memory& memory = module.memory; - if (memory.segments.size() <= WebLimitations::MaxDataSegments) { - return true; - } +// Try to merge segments until they fit into web limitations. +// Return true if successful. +inline bool ensureLimitedSegments(Module& module) { + Memory& memory = module.memory; + if (memory.segments.size() <= WebLimitations::MaxDataSegments) { + return true; + } - // Conservatively refuse to change segments if there might be memory.init - // and data.drop instructions. - if (module.features.hasBulkMemory()) { - return false; - } + // Conservatively refuse to change segments if there might be memory.init + // and data.drop instructions. + if (module.features.hasBulkMemory()) { + return false; + } - auto isEmpty = [](Memory::Segment& segment) { - return segment.data.size() == 0; - }; - - auto isConstantOffset = [](Memory::Segment& segment) { - return segment.offset && segment.offset->is<Const>(); - }; - - Index numConstant = 0, - numDynamic = 0; - bool hasPassiveSegments = false; - for (auto& segment : memory.segments) { - if (!isEmpty(segment)) { - if (isConstantOffset(segment)) { - numConstant++; - } else { - numDynamic++; - } + auto isEmpty = [](Memory::Segment& segment) { + return segment.data.size() == 0; + }; + + auto isConstantOffset = [](Memory::Segment& segment) { + return segment.offset && segment.offset->is<Const>(); + }; + + Index numConstant = 0, numDynamic = 0; + bool hasPassiveSegments = false; + for (auto& segment : memory.segments) { + if (!isEmpty(segment)) { + if (isConstantOffset(segment)) { + numConstant++; + } else { + numDynamic++; } - hasPassiveSegments |= segment.isPassive; } + hasPassiveSegments |= segment.isPassive; + } - if (hasPassiveSegments) { - return false; - } + if (hasPassiveSegments) { + return false; + } - // check if we have too many dynamic data segments, which we can do nothing about - auto num = numConstant + numDynamic; - if (numDynamic + 1 >= WebLimitations::MaxDataSegments) { - return false; - } + // check if we have too many dynamic data segments, which we can do nothing + // about + auto num = numConstant + numDynamic; + if (numDynamic + 1 >= WebLimitations::MaxDataSegments) { + return false; + } - // we'll merge constant segments if we must - if (numConstant + numDynamic >= WebLimitations::MaxDataSegments) { - numConstant = WebLimitations::MaxDataSegments - numDynamic - 1; - num = numConstant + numDynamic; - assert(num == WebLimitations::MaxDataSegments - 1); - } + // we'll merge constant segments if we must + if (numConstant + numDynamic >= WebLimitations::MaxDataSegments) { + numConstant = WebLimitations::MaxDataSegments - numDynamic - 1; + num = numConstant + numDynamic; + assert(num == WebLimitations::MaxDataSegments - 1); + } + + std::vector<Memory::Segment> mergedSegments; + mergedSegments.reserve(WebLimitations::MaxDataSegments); - std::vector<Memory::Segment> mergedSegments; - mergedSegments.reserve(WebLimitations::MaxDataSegments); + // drop empty segments and pass through dynamic-offset segments + for (auto& segment : memory.segments) { + if (isEmpty(segment)) + continue; + if (isConstantOffset(segment)) + continue; + mergedSegments.push_back(segment); + } - // drop empty segments and pass through dynamic-offset segments - for (auto& segment : memory.segments) { - if (isEmpty(segment)) continue; - if (isConstantOffset(segment)) continue; + // from here on, we concern ourselves with non-empty constant-offset + // segments, the ones which we may need to merge + auto isRelevant = [&](Memory::Segment& segment) { + return !isEmpty(segment) && isConstantOffset(segment); + }; + for (Index i = 0; i < memory.segments.size(); i++) { + auto& segment = memory.segments[i]; + if (!isRelevant(segment)) + continue; + if (mergedSegments.size() + 2 < WebLimitations::MaxDataSegments) { mergedSegments.push_back(segment); + continue; } - - // from here on, we concern ourselves with non-empty constant-offset - // segments, the ones which we may need to merge - auto isRelevant = [&](Memory::Segment& segment) { - return !isEmpty(segment) && isConstantOffset(segment); - }; - for (Index i = 0; i < memory.segments.size(); i++) { - auto& segment = memory.segments[i]; - if (!isRelevant(segment)) continue; - if (mergedSegments.size() + 2 < WebLimitations::MaxDataSegments) { - mergedSegments.push_back(segment); + // we can emit only one more segment! merge everything into one + // start the combined segment at the bottom of them all + auto start = segment.offset->cast<Const>()->value.getInteger(); + for (Index j = i + 1; j < memory.segments.size(); j++) { + auto& segment = memory.segments[j]; + if (!isRelevant(segment)) continue; + auto offset = segment.offset->cast<Const>()->value.getInteger(); + start = std::min(start, offset); + } + // create the segment and add in all the data + auto* c = module.allocator.alloc<Const>(); + c->value = Literal(int32_t(start)); + c->type = i32; + + Memory::Segment combined(c); + for (Index j = i; j < memory.segments.size(); j++) { + auto& segment = memory.segments[j]; + if (!isRelevant(segment)) + continue; + auto offset = segment.offset->cast<Const>()->value.getInteger(); + auto needed = offset + segment.data.size() - start; + if (combined.data.size() < needed) { + combined.data.resize(needed); } - // we can emit only one more segment! merge everything into one - // start the combined segment at the bottom of them all - auto start = segment.offset->cast<Const>()->value.getInteger(); - for (Index j = i + 1; j < memory.segments.size(); j++) { - auto& segment = memory.segments[j]; - if (!isRelevant(segment)) continue; - auto offset = segment.offset->cast<Const>()->value.getInteger(); - start = std::min(start, offset); - } - // create the segment and add in all the data - auto* c = module.allocator.alloc<Const>(); - c->value = Literal(int32_t(start)); - c->type = i32; - - Memory::Segment combined(c); - for (Index j = i; j < memory.segments.size(); j++) { - auto& segment = memory.segments[j]; - if (!isRelevant(segment)) continue; - auto offset = segment.offset->cast<Const>()->value.getInteger(); - auto needed = offset + segment.data.size() - start; - if (combined.data.size() < needed) { - combined.data.resize(needed); - } - std::copy(segment.data.begin(), segment.data.end(), combined.data.begin() + (offset - start)); - } - mergedSegments.push_back(combined); - break; + std::copy(segment.data.begin(), + segment.data.end(), + combined.data.begin() + (offset - start)); } - - memory.segments.swap(mergedSegments); - return true; + mergedSegments.push_back(combined); + break; } + + memory.segments.swap(mergedSegments); + return true; +} } // namespace MemoryUtils } // namespace wasm diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h index fe99a458f..5569843fd 100644 --- a/src/ir/module-utils.h +++ b/src/ir/module-utils.h @@ -17,10 +17,10 @@ #ifndef wasm_ir_module_h #define wasm_ir_module_h -#include "wasm-binary.h" -#include "wasm.h" #include "ir/find_all.h" #include "ir/manipulation.h" +#include "wasm-binary.h" +#include "wasm.h" namespace wasm { @@ -75,7 +75,8 @@ inline Function* copyFunction(Function* func, Module& out) { ret->result = func->result; ret->params = func->params; ret->vars = func->vars; - ret->type = Name(); // start with no named type; the names in the other module may differ + // start with no named type; the names in the other module may differ + ret->type = Name(); ret->localNames = func->localNames; ret->localIndices = func->localIndices; ret->debugLocations = func->debugLocations; @@ -138,8 +139,7 @@ inline void copyModule(Module& in, Module& out) { // Note that for this to work the functions themselves don't necessarily need // to exist. For example, it is possible to remove a given function and then // call this redirect all of its uses. -template<typename T> -inline void renameFunctions(Module& wasm, T& map) { +template<typename T> inline void renameFunctions(Module& wasm, T& map) { // Update the function itself. for (auto& pair : map) { if (Function* F = wasm.getFunctionOrNull(pair.first)) { @@ -186,36 +186,31 @@ inline void renameFunction(Module& wasm, Name oldName, Name newName) { // Convenient iteration over imported/non-imported module elements -template<typename T> -inline void iterImportedMemories(Module& wasm, T visitor) { +template<typename T> inline void iterImportedMemories(Module& wasm, T visitor) { if (wasm.memory.exists && wasm.memory.imported()) { visitor(&wasm.memory); } } -template<typename T> -inline void iterDefinedMemories(Module& wasm, T visitor) { +template<typename T> inline void iterDefinedMemories(Module& wasm, T visitor) { if (wasm.memory.exists && !wasm.memory.imported()) { visitor(&wasm.memory); } } -template<typename T> -inline void iterImportedTables(Module& wasm, T visitor) { +template<typename T> inline void iterImportedTables(Module& wasm, T visitor) { if (wasm.table.exists && wasm.table.imported()) { visitor(&wasm.table); } } -template<typename T> -inline void iterDefinedTables(Module& wasm, T visitor) { +template<typename T> inline void iterDefinedTables(Module& wasm, T visitor) { if (wasm.table.exists && !wasm.table.imported()) { visitor(&wasm.table); } } -template<typename T> -inline void iterImportedGlobals(Module& wasm, T visitor) { +template<typename T> inline void iterImportedGlobals(Module& wasm, T visitor) { for (auto& import : wasm.globals) { if (import->imported()) { visitor(import.get()); @@ -223,8 +218,7 @@ inline void iterImportedGlobals(Module& wasm, T visitor) { } } -template<typename T> -inline void iterDefinedGlobals(Module& wasm, T visitor) { +template<typename T> inline void iterDefinedGlobals(Module& wasm, T visitor) { for (auto& import : wasm.globals) { if (!import->imported()) { visitor(import.get()); @@ -241,8 +235,7 @@ inline void iterImportedFunctions(Module& wasm, T visitor) { } } -template<typename T> -inline void iterDefinedFunctions(Module& wasm, T visitor) { +template<typename T> inline void iterDefinedFunctions(Module& wasm, T visitor) { for (auto& import : wasm.functions) { if (!import->imported()) { visitor(import.get()); diff --git a/src/ir/parents.h b/src/ir/parents.h index 71f2ae1d4..3e1f09559 100644 --- a/src/ir/parents.h +++ b/src/ir/parents.h @@ -20,25 +20,19 @@ namespace wasm { struct Parents { - Parents(Expression* expr) { - inner.walk(expr); - } + Parents(Expression* expr) { inner.walk(expr); } - Expression* getParent(Expression* curr) { - return inner.parentMap[curr]; - } + Expression* getParent(Expression* curr) { return inner.parentMap[curr]; } private: - struct Inner : public ExpressionStackWalker<Inner, UnifiedExpressionVisitor<Inner>> { - void visitExpression(Expression* curr) { - parentMap[curr] = getParent(); - } + struct Inner + : public ExpressionStackWalker<Inner, UnifiedExpressionVisitor<Inner>> { + void visitExpression(Expression* curr) { parentMap[curr] = getParent(); } - std::map<Expression*, Expression *> parentMap; + std::map<Expression*, Expression*> parentMap; } inner; }; } // namespace wasm #endif // wasm_ir_parents_h - diff --git a/src/ir/properties.h b/src/ir/properties.h index 4afe3e909..b664ab008 100644 --- a/src/ir/properties.h +++ b/src/ir/properties.h @@ -17,8 +17,8 @@ #ifndef wasm_ir_properties_h #define wasm_ir_properties_h -#include "wasm.h" #include "ir/bits.h" +#include "wasm.h" namespace wasm { @@ -36,7 +36,7 @@ inline bool emitsBoolean(Expression* curr) { inline bool isSymmetric(Binary* binary) { switch (binary->op) { case AddInt32: - case MulInt32: + case MulInt32: case AndInt32: case OrInt32: case XorInt32: @@ -44,14 +44,16 @@ inline bool isSymmetric(Binary* binary) { case NeInt32: case AddInt64: - case MulInt64: + case MulInt64: case AndInt64: case OrInt64: case XorInt64: case EqInt64: - case NeInt64: return true; + case NeInt64: + return true; - default: return false; + default: + return false; } } @@ -105,7 +107,8 @@ inline Expression* getAlmostSignExt(Expression* curr) { if (auto* inner = outer->left->dynCast<Binary>()) { if (inner->op == ShlInt32) { if (auto* innerConst = inner->right->dynCast<Const>()) { - if (Bits::getEffectiveShifts(outerConst) <= Bits::getEffectiveShifts(innerConst)) { + if (Bits::getEffectiveShifts(outerConst) <= + Bits::getEffectiveShifts(innerConst)) { return inner->left; } } @@ -121,7 +124,8 @@ inline Expression* getAlmostSignExt(Expression* curr) { // gets the size of the almost sign-extended value, as well as the // extra shifts, if any inline Index getAlmostSignExtBits(Expression* curr, Index& extraShifts) { - extraShifts = Bits::getEffectiveShifts(curr->cast<Binary>()->left->cast<Binary>()->right) - + extraShifts = Bits::getEffectiveShifts( + curr->cast<Binary>()->left->cast<Binary>()->right) - Bits::getEffectiveShifts(curr->cast<Binary>()->right); return getSignExtBits(curr); } @@ -143,7 +147,8 @@ inline Expression* getZeroExtValue(Expression* curr) { // gets the size of the sign-extended value inline Index getZeroExtBits(Expression* curr) { - return Bits::getMaskedBits(curr->cast<Binary>()->right->cast<Const>()->value.geti32()); + return Bits::getMaskedBits( + curr->cast<Binary>()->right->cast<Const>()->value.geti32()); } // Returns a falling-through value, that is, it looks through a local.tee @@ -182,9 +187,8 @@ inline Expression* getFallthrough(Expression* curr) { return curr; } -} // Properties +} // namespace Properties -} // wasm +} // namespace wasm #endif // wasm_ir_properties_h - diff --git a/src/ir/table-utils.h b/src/ir/table-utils.h index 4b3b7005c..d2dc3a7d5 100644 --- a/src/ir/table-utils.h +++ b/src/ir/table-utils.h @@ -17,8 +17,8 @@ #ifndef wasm_ir_table_h #define wasm_ir_table_h -#include "wasm.h" #include "wasm-traversal.h" +#include "wasm.h" namespace wasm { diff --git a/src/ir/trapping.h b/src/ir/trapping.h index 48f485faa..e6eca9e53 100644 --- a/src/ir/trapping.h +++ b/src/ir/trapping.h @@ -23,11 +23,7 @@ namespace wasm { -enum class TrapMode { - Allow, - Clamp, - JS -}; +enum class TrapMode { Allow, Clamp, JS }; inline void addTrapModePass(PassRunner& runner, TrapMode trapMode) { if (trapMode == TrapMode::Clamp) { @@ -39,17 +35,13 @@ inline void addTrapModePass(PassRunner& runner, TrapMode trapMode) { class TrappingFunctionContainer { public: - TrappingFunctionContainer(TrapMode mode, Module &wasm, bool immediate = false) - : mode(mode), - wasm(wasm), - immediate(immediate) { } + TrappingFunctionContainer(TrapMode mode, Module& wasm, bool immediate = false) + : mode(mode), wasm(wasm), immediate(immediate) {} bool hasFunction(Name name) { return functions.find(name) != functions.end(); } - bool hasImport(Name name) { - return imports.find(name) != imports.end(); - } + bool hasImport(Name name) { return imports.find(name) != imports.end(); } void addFunction(Function* function) { functions[function->name] = function; @@ -66,10 +58,10 @@ public: void addToModule() { if (!immediate) { - for (auto &pair : functions) { + for (auto& pair : functions) { wasm.addFunction(pair.second); } - for (auto &pair : imports) { + for (auto& pair : imports) { wasm.addFunction(pair.second); } } @@ -77,17 +69,11 @@ public: imports.clear(); } - TrapMode getMode() { - return mode; - } + TrapMode getMode() { return mode; } - Module& getModule() { - return wasm; - } + Module& getModule() { return wasm; } - std::map<Name, Function*>& getFunctions() { - return functions; - } + std::map<Name, Function*>& getFunctions() { return functions; } private: std::map<Name, Function*> functions; @@ -98,8 +84,10 @@ private: bool immediate; }; -Expression* makeTrappingBinary(Binary* curr, TrappingFunctionContainer &trappingFunctions); -Expression* makeTrappingUnary(Unary* curr, TrappingFunctionContainer &trappingFunctions); +Expression* makeTrappingBinary(Binary* curr, + TrappingFunctionContainer& trappingFunctions); +Expression* makeTrappingUnary(Unary* curr, + TrappingFunctionContainer& trappingFunctions); inline TrapMode trapModeFromString(std::string const& str) { if (str == "allow") { @@ -110,11 +98,12 @@ inline TrapMode trapModeFromString(std::string const& str) { return TrapMode::JS; } else { throw std::invalid_argument( - "Unsupported trap mode \"" + str + "\". " + "Unsupported trap mode \"" + str + + "\". " "Valid modes are \"allow\", \"js\", and \"clamp\""); } } -} // wasm +} // namespace wasm #endif // wasm_ir_trapping_h diff --git a/src/ir/type-updating.h b/src/ir/type-updating.h index ba06bccf9..e387483ee 100644 --- a/src/ir/type-updating.h +++ b/src/ir/type-updating.h @@ -28,7 +28,9 @@ namespace wasm { // reaches it // * altering the type of a child to unreachable can make the parent // unreachable -struct TypeUpdater : public ExpressionStackWalker<TypeUpdater, UnifiedExpressionVisitor<TypeUpdater>> { +struct TypeUpdater + : public ExpressionStackWalker<TypeUpdater, + UnifiedExpressionVisitor<TypeUpdater>> { // Part 1: Scanning // track names to their blocks, so that when we remove a break to @@ -75,10 +77,13 @@ struct TypeUpdater : public ExpressionStackWalker<TypeUpdater, UnifiedExpression // note the replacement of one node with another. this should be called // after performing the replacement. - // this does *not* look into the node by default. see noteReplacementWithRecursiveRemoval - // (we don't support recursive addition because in practice we do not create - // new trees in the passes that use this, they just move around children) - void noteReplacement(Expression* from, Expression* to, bool recursivelyRemove=false) { + // this does *not* look into the node by default. see + // noteReplacementWithRecursiveRemoval (we don't support recursive addition + // because in practice we do not create new trees in the passes that use this, + // they just move around children) + void noteReplacement(Expression* from, + Expression* to, + bool recursivelyRemove = false) { auto parent = parents[from]; if (recursivelyRemove) { noteRecursiveRemoval(from); @@ -109,22 +114,23 @@ struct TypeUpdater : public ExpressionStackWalker<TypeUpdater, UnifiedExpression // note the removal of a node and all its children void noteRecursiveRemoval(Expression* curr) { - struct Recurser : public PostWalker<Recurser, UnifiedExpressionVisitor<Recurser>> { + struct Recurser + : public PostWalker<Recurser, UnifiedExpressionVisitor<Recurser>> { TypeUpdater& parent; Recurser(TypeUpdater& parent, Expression* root) : parent(parent) { walk(root); } - void visitExpression(Expression* curr) { - parent.noteRemoval(curr); - } + void visitExpression(Expression* curr) { parent.noteRemoval(curr); } }; Recurser(*this, curr); } - void noteAddition(Expression* curr, Expression* parent, Expression* previous = nullptr) { + void noteAddition(Expression* curr, + Expression* parent, + Expression* previous = nullptr) { assert(parents.find(curr) == parents.end()); // must not already exist noteRemovalOrAddition(curr, parent); // if we didn't replace with the exact same type, propagate types up @@ -188,7 +194,8 @@ struct TypeUpdater : public ExpressionStackWalker<TypeUpdater, UnifiedExpression // alters the type of a node to a new type. // this propagates the type change through all the parents. void changeTypeTo(Expression* curr, Type newType) { - if (curr->type == newType) return; // nothing to do + if (curr->type == newType) + return; // nothing to do curr->type = newType; propagateTypesUp(curr); } @@ -201,11 +208,13 @@ struct TypeUpdater : public ExpressionStackWalker<TypeUpdater, UnifiedExpression // the one thing we need to do here is propagate unreachability, // no other change is possible void propagateTypesUp(Expression* curr) { - if (curr->type != unreachable) return; + if (curr->type != unreachable) + return; while (1) { auto* child = curr; curr = parents[child]; - if (!curr) return; + if (!curr) + return; // get ready to apply unreachability to this node if (curr->type == unreachable) { return; // already unreachable, stop here @@ -254,9 +263,9 @@ struct TypeUpdater : public ExpressionStackWalker<TypeUpdater, UnifiedExpression if (curr->type == unreachable) { return; // no change possible } - if (!curr->list.empty() && - isConcreteType(curr->list.back()->type)) { - return; // should keep type due to fallthrough, even if has an unreachable child + if (!curr->list.empty() && isConcreteType(curr->list.back()->type)) { + // should keep type due to fallthrough, even if has an unreachable child + return; } for (auto* child : curr->list) { if (child->type == unreachable) { diff --git a/src/ir/utils.h b/src/ir/utils.h index c91698124..9be8947a1 100644 --- a/src/ir/utils.h +++ b/src/ir/utils.h @@ -17,22 +17,21 @@ #ifndef wasm_ir_utils_h #define wasm_ir_utils_h -#include "wasm.h" -#include "wasm-traversal.h" -#include "wasm-builder.h" -#include "pass.h" #include "ir/branch-utils.h" +#include "pass.h" +#include "wasm-builder.h" +#include "wasm-traversal.h" +#include "wasm.h" namespace wasm { // Measure the size of an AST -struct Measurer : public PostWalker<Measurer, UnifiedExpressionVisitor<Measurer>> { +struct Measurer + : public PostWalker<Measurer, UnifiedExpressionVisitor<Measurer>> { Index size = 0; - void visitExpression(Expression* curr) { - size++; - } + void visitExpression(Expression* curr) { size++; } static Index measure(Expression* tree) { Measurer measurer; @@ -43,26 +42,24 @@ struct Measurer : public PostWalker<Measurer, UnifiedExpressionVisitor<Measurer> struct ExpressionAnalyzer { // Given a stack of expressions, checks if the topmost is used as a result. - // For example, if the parent is a block and the node is before the last position, - // it is not used. + // For example, if the parent is a block and the node is before the last + // position, it is not used. static bool isResultUsed(ExpressionStack& stack, Function* func); // Checks if a value is dropped. static bool isResultDropped(ExpressionStack& stack); - // Checks if a break is a simple - no condition, no value, just a plain branching - static bool isSimple(Break* curr) { - return !curr->condition && !curr->value; - } + // Checks if a break is a simple - no condition, no value, just a plain + // branching + static bool isSimple(Break* curr) { return !curr->condition && !curr->value; } using ExprComparer = std::function<bool(Expression*, Expression*)>; - static bool flexibleEqual(Expression* left, Expression* right, ExprComparer comparer); + static bool + flexibleEqual(Expression* left, Expression* right, ExprComparer comparer); // Compares two expressions for equivalence. static bool equal(Expression* left, Expression* right) { - auto comparer = [](Expression* left, Expression* right) { - return false; - }; + auto comparer = [](Expression* left, Expression* right) { return false; }; return flexibleEqual(left, right, comparer); } @@ -79,7 +76,8 @@ struct ExpressionAnalyzer { return flexibleEqual(left, right, comparer); } - // hash an expression, ignoring superficial details like specific internal names + // hash an expression, ignoring superficial details like specific internal + // names static HashType hash(Expression* curr); }; @@ -100,15 +98,16 @@ struct ExpressionAnalyzer { // exist, but the breaks don't declare the type, rather everything // depends on the block. To avoid looking at the parent or something // else, just remove such un-taken branches. -struct ReFinalize : public WalkerPass<PostWalker<ReFinalize, OverriddenVisitor<ReFinalize>>> { +struct ReFinalize + : public WalkerPass<PostWalker<ReFinalize, OverriddenVisitor<ReFinalize>>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new ReFinalize; } ReFinalize() { name = "refinalize"; } - // block finalization is O(bad) if we do each block by itself, so do it in bulk, - // tracking break value types so we just do a linear pass + // block finalization is O(bad) if we do each block by itself, so do it in + // bulk, tracking break value types so we just do a linear pass std::map<Name, Type> breakValues; @@ -220,10 +219,10 @@ struct ReFinalizeNode : public OverriddenVisitor<ReFinalizeNode> { } }; -// Adds drop() operations where necessary. This lets you not worry about adding drop when -// generating code. -// This also refinalizes before and after, as dropping can change types, and depends -// on types being cleaned up - no unnecessary block/if/loop types (see refinalize) +// Adds drop() operations where necessary. This lets you not worry about adding +// drop when generating code. This also refinalizes before and after, as +// dropping can change types, and depends on types being cleaned up - no +// unnecessary block/if/loop types (see refinalize) // TODO: optimize that, interleave them struct AutoDrop : public WalkerPass<ExpressionStackWalker<AutoDrop>> { bool isFunctionParallel() override { return true; } @@ -236,7 +235,8 @@ struct AutoDrop : public WalkerPass<ExpressionStackWalker<AutoDrop>> { bool acted = false; if (isConcreteType(child->type)) { expressionStack.push_back(child); - if (!ExpressionAnalyzer::isResultUsed(expressionStack, getFunction()) && !ExpressionAnalyzer::isResultDropped(expressionStack)) { + if (!ExpressionAnalyzer::isResultUsed(expressionStack, getFunction()) && + !ExpressionAnalyzer::isResultDropped(expressionStack)) { child = Builder(*getModule()).makeDrop(child); acted = true; } @@ -245,12 +245,11 @@ struct AutoDrop : public WalkerPass<ExpressionStackWalker<AutoDrop>> { return acted; } - void reFinalize() { - ReFinalizeNode::updateStack(expressionStack); - } + void reFinalize() { ReFinalizeNode::updateStack(expressionStack); } void visitBlock(Block* curr) { - if (curr->list.size() == 0) return; + if (curr->list.size() == 0) + return; for (Index i = 0; i < curr->list.size() - 1; i++) { auto* child = curr->list[i]; if (isConcreteType(child->type)) { @@ -265,9 +264,11 @@ struct AutoDrop : public WalkerPass<ExpressionStackWalker<AutoDrop>> { void visitIf(If* curr) { bool acted = false; - if (maybeDrop(curr->ifTrue)) acted = true; + if (maybeDrop(curr->ifTrue)) + acted = true; if (curr->ifFalse) { - if (maybeDrop(curr->ifFalse)) acted = true; + if (maybeDrop(curr->ifFalse)) + acted = true; } if (acted) { reFinalize(); @@ -286,50 +287,31 @@ struct AutoDrop : public WalkerPass<ExpressionStackWalker<AutoDrop>> { }; struct I64Utilities { - static Expression* recreateI64(Builder& builder, Expression* low, Expression* high) { - return - builder.makeBinary( - OrInt64, - builder.makeUnary( - ExtendUInt32, - low - ), - builder.makeBinary( - ShlInt64, - builder.makeUnary( - ExtendUInt32, - high - ), - builder.makeConst(Literal(int64_t(32))) - ) - ) - ; + static Expression* + recreateI64(Builder& builder, Expression* low, Expression* high) { + return builder.makeBinary( + OrInt64, + builder.makeUnary(ExtendUInt32, low), + builder.makeBinary(ShlInt64, + builder.makeUnary(ExtendUInt32, high), + builder.makeConst(Literal(int64_t(32))))); }; static Expression* recreateI64(Builder& builder, Index low, Index high) { - return recreateI64(builder, builder.makeGetLocal(low, i32), builder.makeGetLocal(high, i32)); + return recreateI64( + builder, builder.makeGetLocal(low, i32), builder.makeGetLocal(high, i32)); }; static Expression* getI64High(Builder& builder, Index index) { - return - builder.makeUnary( - WrapInt64, - builder.makeBinary( - ShrUInt64, - builder.makeGetLocal(index, i64), - builder.makeConst(Literal(int64_t(32))) - ) - ) - ; + return builder.makeUnary( + WrapInt64, + builder.makeBinary(ShrUInt64, + builder.makeGetLocal(index, i64), + builder.makeConst(Literal(int64_t(32))))); } static Expression* getI64Low(Builder& builder, Index index) { - return - builder.makeUnary( - WrapInt64, - builder.makeGetLocal(index, i64) - ) - ; + return builder.makeUnary(WrapInt64, builder.makeGetLocal(index, i64)); } }; diff --git a/src/literal.h b/src/literal.h index 7646b5d29..ff50b2e61 100644 --- a/src/literal.h +++ b/src/literal.h @@ -17,12 +17,12 @@ #ifndef wasm_literal_h #define wasm_literal_h -#include <iostream> #include <array> +#include <iostream> +#include "compiler-support.h" #include "support/hash.h" #include "support/utilities.h" -#include "compiler-support.h" #include "wasm-type.h" namespace wasm { @@ -40,14 +40,16 @@ public: Type type; public: - Literal() : v128(), type(Type::none) {} - explicit Literal(Type type) : v128(), type(type) {} - explicit Literal(int32_t init) : i32(init), type(Type::i32) {} - explicit Literal(uint32_t init) : i32(init), type(Type::i32) {} - explicit Literal(int64_t init) : i64(init), type(Type::i64) {} - explicit Literal(uint64_t init) : i64(init), type(Type::i64) {} - explicit Literal(float init) : i32(bit_cast<int32_t>(init)), type(Type::f32) {} - explicit Literal(double init) : i64(bit_cast<int64_t>(init)), type(Type::f64) {} + Literal() : v128(), type(Type::none) {} + explicit Literal(Type type) : v128(), type(type) {} + explicit Literal(int32_t init) : i32(init), type(Type::i32) {} + explicit Literal(uint32_t init) : i32(init), type(Type::i32) {} + explicit Literal(int64_t init) : i64(init), type(Type::i64) {} + explicit Literal(uint64_t init) : i64(init), type(Type::i64) {} + explicit Literal(float init) + : i32(bit_cast<int32_t>(init)), type(Type::f32) {} + explicit Literal(double init) + : i64(bit_cast<int64_t>(init)), type(Type::f64) {} // v128 literal from bytes explicit Literal(const uint8_t init[16]); // v128 literal from lane value literals @@ -61,45 +63,85 @@ public: inline static Literal makeFromInt32(int32_t x, Type type) { switch (type) { - case Type::i32: return Literal(int32_t(x)); break; - case Type::i64: return Literal(int64_t(x)); break; - case Type::f32: return Literal(float(x)); break; - case Type::f64: return Literal(double(x)); break; - case Type::v128: return Literal( - std::array<Literal, 4>{{ - Literal(x), Literal(int32_t(0)), Literal(int32_t(0)), Literal(int32_t(0)) - }} - ); + case Type::i32: + return Literal(int32_t(x)); + break; + case Type::i64: + return Literal(int64_t(x)); + break; + case Type::f32: + return Literal(float(x)); + break; + case Type::f64: + return Literal(double(x)); + break; + case Type::v128: + return Literal(std::array<Literal, 4>{{Literal(x), + Literal(int32_t(0)), + Literal(int32_t(0)), + Literal(int32_t(0))}}); case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } - inline static Literal makeZero(Type type) { - return makeFromInt32(0, type); - } + inline static Literal makeZero(Type type) { return makeFromInt32(0, type); } Literal castToF32(); Literal castToF64(); Literal castToI32(); Literal castToI64(); - int32_t geti32() const { assert(type == Type::i32); return i32; } - int64_t geti64() const { assert(type == Type::i64); return i64; } - float getf32() const { assert(type == Type::f32); return bit_cast<float>(i32); } - double getf64() const { assert(type == Type::f64); return bit_cast<double>(i64); } + int32_t geti32() const { + assert(type == Type::i32); + return i32; + } + int64_t geti64() const { + assert(type == Type::i64); + return i64; + } + float getf32() const { + assert(type == Type::f32); + return bit_cast<float>(i32); + } + double getf64() const { + assert(type == Type::f64); + return bit_cast<double>(i64); + } std::array<uint8_t, 16> getv128() const; // careful! - int32_t* geti32Ptr() { assert(type == Type::i32); return &i32; } - uint8_t* getv128Ptr() { assert(type == Type::v128); return v128; } - const uint8_t* getv128Ptr() const { assert(type == Type::v128); return v128; } + int32_t* geti32Ptr() { + assert(type == Type::i32); + return &i32; + } + uint8_t* getv128Ptr() { + assert(type == Type::v128); + return v128; + } + const uint8_t* getv128Ptr() const { + assert(type == Type::v128); + return v128; + } - int32_t reinterpreti32() const { assert(type == Type::f32); return i32; } - int64_t reinterpreti64() const { assert(type == Type::f64); return i64; } - float reinterpretf32() const { assert(type == Type::i32); return bit_cast<float>(i32); } - double reinterpretf64() const { assert(type == Type::i64); return bit_cast<double>(i64); } + int32_t reinterpreti32() const { + assert(type == Type::f32); + return i32; + } + int64_t reinterpreti64() const { + assert(type == Type::f64); + return i64; + } + float reinterpretf32() const { + assert(type == Type::i32); + return bit_cast<float>(i32); + } + double reinterpretf64() const { + assert(type == Type::i64); + return bit_cast<double>(i64); + } int64_t getInteger() const; double getFloat() const; @@ -117,7 +159,7 @@ public: static float setQuietNaN(float f); static double setQuietNaN(double f); - static void printFloat(std::ostream &o, float f); + static void printFloat(std::ostream& o, float f); static void printDouble(std::ostream& o, double d); static void printVec128(std::ostream& o, const std::array<uint8_t, 16>& v); @@ -204,7 +246,8 @@ public: std::array<Literal, 4> getLanesF32x4() const; std::array<Literal, 2> getLanesF64x2() const; - Literal shuffleV8x16(const Literal& other, const std::array<uint8_t, 16>& mask) const; + Literal shuffleV8x16(const Literal& other, + const std::array<uint8_t, 16>& mask) const; Literal splatI8x16() const; Literal extractLaneSI8x16(uint8_t index) const; Literal extractLaneUI8x16(uint8_t index) const; @@ -342,7 +385,7 @@ public: Literal convertSToF64x2() const; Literal convertUToF64x2() const; - private: +private: Literal addSatSI8(const Literal& other) const; Literal addSatUI8(const Literal& other) const; Literal addSatSI16(const Literal& other) const; @@ -362,31 +405,35 @@ template<> struct hash<wasm::Literal> { a.getBits(bytes); int64_t chunks[2]; memcpy(chunks, bytes, sizeof(chunks)); - return wasm::rehash( - wasm::rehash( - uint64_t(hash<size_t>()(size_t(a.type))), - uint64_t(hash<int64_t>()(chunks[0])) - ), - uint64_t(hash<int64_t>()(chunks[1])) - ); + return wasm::rehash(wasm::rehash(uint64_t(hash<size_t>()(size_t(a.type))), + uint64_t(hash<int64_t>()(chunks[0]))), + uint64_t(hash<int64_t>()(chunks[1]))); } }; template<> struct less<wasm::Literal> { bool operator()(const wasm::Literal& a, const wasm::Literal& b) const { - if (a.type < b.type) return true; - if (a.type > b.type) return false; + if (a.type < b.type) + return true; + if (a.type > b.type) + return false; switch (a.type) { - case wasm::Type::i32: return a.geti32() < b.geti32(); - case wasm::Type::f32: return a.reinterpreti32() < b.reinterpreti32(); - case wasm::Type::i64: return a.geti64() < b.geti64(); - case wasm::Type::f64: return a.reinterpreti64() < b.reinterpreti64(); - case wasm::Type::v128: return memcmp(a.getv128Ptr(), b.getv128Ptr(), 16) < 0; + case wasm::Type::i32: + return a.geti32() < b.geti32(); + case wasm::Type::f32: + return a.reinterpreti32() < b.reinterpreti32(); + case wasm::Type::i64: + return a.geti64() < b.geti64(); + case wasm::Type::f64: + return a.reinterpreti64() < b.reinterpreti64(); + case wasm::Type::v128: + return memcmp(a.getv128Ptr(), b.getv128Ptr(), 16) < 0; case wasm::Type::none: - case wasm::Type::unreachable: return false; + case wasm::Type::unreachable: + return false; } WASM_UNREACHABLE(); } }; -} +} // namespace std #endif // wasm_literal_h diff --git a/src/mixed_arena.h b/src/mixed_arena.h index 5f48f5220..36bf22a5e 100644 --- a/src/mixed_arena.h +++ b/src/mixed_arena.h @@ -84,7 +84,8 @@ struct MixedArena { // Allocate an amount of space with a guaranteed alignment void* allocSpace(size_t size, size_t align) { - // the bump allocator data should not be modified by multiple threads at once. + // the bump allocator data should not be modified by multiple threads at + // once. auto myId = std::this_thread::get_id(); if (myId != threadId) { MixedArena* curr = this; @@ -112,7 +113,8 @@ struct MixedArena { // otherwise, the cmpxchg updated seen, and we continue to loop curr = seen; } - if (allocated) delete allocated; + if (allocated) + delete allocated; return curr->allocSpace(size, align); } // First, move the current index in the last chunk to an aligned position. @@ -121,22 +123,26 @@ struct MixedArena { // Allocate a new chunk. auto numChunks = (size + CHUNK_SIZE - 1) / CHUNK_SIZE; assert(size <= numChunks * CHUNK_SIZE); - auto* allocation = wasm::aligned_malloc(MAX_ALIGN, numChunks * CHUNK_SIZE); - if (!allocation) abort(); + auto* allocation = + wasm::aligned_malloc(MAX_ALIGN, numChunks * CHUNK_SIZE); + if (!allocation) + abort(); chunks.push_back(allocation); index = 0; } uint8_t* ret = static_cast<uint8_t*>(chunks.back()); ret += index; - index += size; // TODO: if we allocated more than 1 chunk, reuse the remainder, right now we allocate another next time + index += size; // TODO: if we allocated more than 1 chunk, reuse the + // remainder, right now we allocate another next time return static_cast<void*>(ret); } - template<class T> - T* alloc() { - static_assert(alignof(T) <= MAX_ALIGN, "maximum alignment not large enough"); + template<class T> T* alloc() { + static_assert(alignof(T) <= MAX_ALIGN, + "maximum alignment not large enough"); auto* ret = static_cast<T*>(allocSpace(sizeof(T), alignof(T))); - new (ret) T(*this); // allocated objects receive the allocator, so they can allocate more later if necessary + new (ret) T(*this); // allocated objects receive the allocator, so they can + // allocate more later if necessary return ret; } @@ -149,22 +155,20 @@ struct MixedArena { ~MixedArena() { clear(); - if (next.load()) delete next.load(); + if (next.load()) + delete next.load(); } }; - // // A vector that allocates in an arena. // // TODO: specialize on the initial size of the array -template<typename SubType, typename T> -class ArenaVectorBase { +template<typename SubType, typename T> class ArenaVectorBase { protected: T* data = nullptr; - size_t usedElements = 0, - allocatedElements = 0; + size_t usedElements = 0, allocatedElements = 0; void reallocate(size_t size) { T* old = data; @@ -182,13 +186,9 @@ public: return data[index]; } - size_t size() const { - return usedElements; - } + size_t size() const { return usedElements; } - bool empty() const { - return size() == 0; - } + bool empty() const { return size() == 0; } void resize(size_t size) { if (size > allocatedElements) { @@ -235,13 +235,9 @@ public: usedElements -= size; } - void erase(Iterator it) { - erase(it, it + 1); - } + void erase(Iterator it) { erase(it, it + 1); } - void clear() { - usedElements = 0; - } + void clear() { usedElements = 0; } void reserve(size_t size) { if (size > allocatedElements) { @@ -249,8 +245,7 @@ public: } } - template<typename ListType> - void set(const ListType& list) { + template<typename ListType> void set(const ListType& list) { size_t size = list.size(); if (allocatedElements < size) { static_cast<SubType*>(this)->allocate(size); @@ -261,9 +256,7 @@ public: usedElements = size; } - void operator=(SubType& other) { - set(other); - } + void operator=(SubType& other) { set(other); } void swap(SubType& other) { data = other.data; @@ -287,32 +280,25 @@ public: size_t index; Iterator() : parent(nullptr), index(0) {} - Iterator(const SubType* parent, size_t index) : parent(parent), index(index) {} + Iterator(const SubType* parent, size_t index) + : parent(parent), index(index) {} bool operator==(const Iterator& other) const { return index == other.index && parent == other.parent; } - bool operator!=(const Iterator& other) const { - return !(*this == other); - } + bool operator!=(const Iterator& other) const { return !(*this == other); } bool operator<(const Iterator& other) const { assert(parent == other.parent); return index < other.index; } - bool operator>(const Iterator& other) const { - return other < *this; - } + bool operator>(const Iterator& other) const { return other < *this; } - bool operator<=(const Iterator& other) const { - return !(other < *this); - } + bool operator<=(const Iterator& other) const { return !(other < *this); } - bool operator>=(const Iterator& other) const { - return !(*this < other); - } + bool operator>=(const Iterator& other) const { return !(*this < other); } Iterator& operator++() { index++; @@ -341,17 +327,13 @@ public: return *this; } - Iterator& operator-=(std::ptrdiff_t off) { - return *this += -off; - } + Iterator& operator-=(std::ptrdiff_t off) { return *this += -off; } Iterator operator+(std::ptrdiff_t off) const { return Iterator(*this) += off; } - Iterator operator-(std::ptrdiff_t off) const { - return *this + -off; - } + Iterator operator-(std::ptrdiff_t off) const { return *this + -off; } std::ptrdiff_t operator-(const Iterator& other) const { assert(parent == other.parent); @@ -362,17 +344,11 @@ public: return it + off; } - T& operator*() const { - return (*parent)[index]; - } + T& operator*() const { return (*parent)[index]; } - T& operator[](std::ptrdiff_t off) const { - return (*parent)[index + off]; - } + T& operator[](std::ptrdiff_t off) const { return (*parent)[index + off]; } - T* operator->() const { - return &(*parent)[index]; - } + T* operator->() const { return &(*parent)[index]; } }; Iterator begin() const { @@ -407,7 +383,8 @@ public: void allocate(size_t size) { this->allocatedElements = size; - this->data = static_cast<T*>(allocator.allocSpace(sizeof(T) * this->allocatedElements, alignof(T))); + this->data = static_cast<T*>( + allocator.allocSpace(sizeof(T) * this->allocatedElements, alignof(T))); } }; diff --git a/src/parsing.h b/src/parsing.h index 97fc88432..8432b41f2 100644 --- a/src/parsing.h +++ b/src/parsing.h @@ -22,13 +22,13 @@ #include <sstream> #include <string> -#include "shared-constants.h" #include "asmjs/shared-constants.h" #include "mixed_arena.h" +#include "shared-constants.h" #include "support/colors.h" #include "support/utilities.h" -#include "wasm.h" #include "wasm-printing.h" +#include "wasm.h" namespace wasm { @@ -38,7 +38,8 @@ struct ParseException { ParseException() : text("unknown parse error"), line(-1), col(-1) {} ParseException(std::string text) : text(text), line(-1), col(-1) {} - ParseException(std::string text, size_t line, size_t col) : text(text), line(line), col(col) {} + ParseException(std::string text, size_t line, size_t col) + : text(text), line(line), col(col) {} void dump(std::ostream& o) const { Colors::magenta(o); @@ -76,47 +77,63 @@ struct MapParseException { } }; -inline Expression* parseConst(cashew::IString s, Type type, MixedArena& allocator) { - const char *str = s.str; +inline Expression* +parseConst(cashew::IString s, Type type, MixedArena& allocator) { + const char* str = s.str; auto ret = allocator.alloc<Const>(); ret->type = type; if (isFloatType(type)) { if (s == _INFINITY) { switch (type) { - case f32: ret->value = Literal(std::numeric_limits<float>::infinity()); break; - case f64: ret->value = Literal(std::numeric_limits<double>::infinity()); break; - default: return nullptr; + case f32: + ret->value = Literal(std::numeric_limits<float>::infinity()); + break; + case f64: + ret->value = Literal(std::numeric_limits<double>::infinity()); + break; + default: + return nullptr; } - //std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; + // std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; return ret; } if (s == NEG_INFINITY) { switch (type) { - case f32: ret->value = Literal(-std::numeric_limits<float>::infinity()); break; - case f64: ret->value = Literal(-std::numeric_limits<double>::infinity()); break; - default: return nullptr; + case f32: + ret->value = Literal(-std::numeric_limits<float>::infinity()); + break; + case f64: + ret->value = Literal(-std::numeric_limits<double>::infinity()); + break; + default: + return nullptr; } - //std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; + // std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; return ret; } if (s == _NAN) { switch (type) { - case f32: ret->value = Literal(float(std::nan(""))); break; - case f64: ret->value = Literal(double(std::nan(""))); break; - default: return nullptr; + case f32: + ret->value = Literal(float(std::nan(""))); + break; + case f64: + ret->value = Literal(double(std::nan(""))); + break; + default: + return nullptr; } - //std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; + // std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; return ret; } bool negative = str[0] == '-'; - const char *positive = negative ? str + 1 : str; + const char* positive = negative ? str + 1 : str; if (!negative) { if (positive[0] == '+') { positive++; } } if (positive[0] == 'n' && positive[1] == 'a' && positive[2] == 'n') { - const char * modifier = positive[3] == ':' ? positive + 4 : nullptr; + const char* modifier = positive[3] == ':' ? positive + 4 : nullptr; if (!(modifier ? positive[4] == '0' && positive[5] == 'x' : 1)) { throw ParseException("bad nan input"); } @@ -126,13 +143,16 @@ inline Expression* parseConst(cashew::IString s, Type type, MixedArena& allocato if (modifier) { std::istringstream istr(modifier); istr >> std::hex >> pattern; - if (istr.fail()) throw ParseException("invalid f32 format"); + if (istr.fail()) + throw ParseException("invalid f32 format"); pattern |= 0x7f800000U; } else { pattern = 0x7fc00000U; } - if (negative) pattern |= 0x80000000U; - if (!std::isnan(bit_cast<float>(pattern))) pattern |= 1U; + if (negative) + pattern |= 0x80000000U; + if (!std::isnan(bit_cast<float>(pattern))) + pattern |= 1U; ret->value = Literal(pattern).castToF32(); break; } @@ -141,79 +161,97 @@ inline Expression* parseConst(cashew::IString s, Type type, MixedArena& allocato if (modifier) { std::istringstream istr(modifier); istr >> std::hex >> pattern; - if (istr.fail()) throw ParseException("invalid f64 format"); + if (istr.fail()) + throw ParseException("invalid f64 format"); pattern |= 0x7ff0000000000000ULL; } else { pattern = 0x7ff8000000000000UL; } - if (negative) pattern |= 0x8000000000000000ULL; - if (!std::isnan(bit_cast<double>(pattern))) pattern |= 1ULL; + if (negative) + pattern |= 0x8000000000000000ULL; + if (!std::isnan(bit_cast<double>(pattern))) + pattern |= 1ULL; ret->value = Literal(pattern).castToF64(); break; } - default: return nullptr; + default: + return nullptr; } - //std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; + // std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; return ret; } if (s == NEG_NAN) { switch (type) { - case f32: ret->value = Literal(float(-std::nan(""))); break; - case f64: ret->value = Literal(double(-std::nan(""))); break; - default: return nullptr; + case f32: + ret->value = Literal(float(-std::nan(""))); + break; + case f64: + ret->value = Literal(double(-std::nan(""))); + break; + default: + return nullptr; } - //std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; + // std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; return ret; } } switch (type) { case i32: { - if ((str[0] == '0' && str[1] == 'x') || (str[0] == '-' && str[1] == '0' && str[2] == 'x')) { + if ((str[0] == '0' && str[1] == 'x') || + (str[0] == '-' && str[1] == '0' && str[2] == 'x')) { bool negative = str[0] == '-'; - if (negative) str++; + if (negative) + str++; std::istringstream istr(str); uint32_t temp; istr >> std::hex >> temp; - if (istr.fail()) throw ParseException("invalid i32 format"); + if (istr.fail()) + throw ParseException("invalid i32 format"); ret->value = Literal(negative ? -temp : temp); } else { std::istringstream istr(str[0] == '-' ? str + 1 : str); uint32_t temp; istr >> temp; - if (istr.fail()) throw ParseException("invalid i32 format"); + if (istr.fail()) + throw ParseException("invalid i32 format"); ret->value = Literal(str[0] == '-' ? -temp : temp); } break; } case i64: { - if ((str[0] == '0' && str[1] == 'x') || (str[0] == '-' && str[1] == '0' && str[2] == 'x')) { + if ((str[0] == '0' && str[1] == 'x') || + (str[0] == '-' && str[1] == '0' && str[2] == 'x')) { bool negative = str[0] == '-'; - if (negative) str++; + if (negative) + str++; std::istringstream istr(str); uint64_t temp; istr >> std::hex >> temp; - if (istr.fail()) throw ParseException("invalid i64 format"); + if (istr.fail()) + throw ParseException("invalid i64 format"); ret->value = Literal(negative ? -temp : temp); } else { std::istringstream istr(str[0] == '-' ? str + 1 : str); uint64_t temp; istr >> temp; - if (istr.fail()) throw ParseException("invalid i64 format"); + if (istr.fail()) + throw ParseException("invalid i64 format"); ret->value = Literal(str[0] == '-' ? -temp : temp); } break; } case f32: { - char *end; + char* end; ret->value = Literal(strtof(str, &end)); break; } case f64: { - char *end; + char* end; ret->value = Literal(strtod(str, &end)); break; } - case v128: WASM_UNREACHABLE(); + case v128: + WASM_UNREACHABLE(); case none: case unreachable: { return nullptr; @@ -222,7 +260,7 @@ inline Expression* parseConst(cashew::IString s, Type type, MixedArena& allocato if (ret->value.type != type) { throw ParseException("parsed type does not match expected type"); } - //std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; + // std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; return ret; } @@ -230,17 +268,20 @@ inline Expression* parseConst(cashew::IString s, Type type, MixedArena& allocato // the names into unique ones, as required by Binaryen IR. struct UniqueNameMapper { std::vector<Name> labelStack; - std::map<Name, std::vector<Name>> labelMappings; // name in source => stack of uniquified names + // name in source => stack of uniquified names + std::map<Name, std::vector<Name>> labelMappings; std::map<Name, Name> reverseLabelMapping; // uniquified name => name in source Index otherIndex = 0; Name getPrefixedName(Name prefix) { - if (reverseLabelMapping.find(prefix) == reverseLabelMapping.end()) return prefix; + if (reverseLabelMapping.find(prefix) == reverseLabelMapping.end()) + return prefix; // make sure to return a unique name not already on the stack while (1) { Name ret = Name(prefix.str + std::to_string(otherIndex++)); - if (reverseLabelMapping.find(ret) == reverseLabelMapping.end()) return ret; + if (reverseLabelMapping.find(ret) == reverseLabelMapping.end()) + return ret; } } @@ -290,24 +331,28 @@ struct UniqueNameMapper { static void doPreVisitControlFlow(Walker* self, Expression** currp) { auto* curr = *currp; if (auto* block = curr->dynCast<Block>()) { - if (block->name.is()) block->name = self->mapper.pushLabelName(block->name); + if (block->name.is()) + block->name = self->mapper.pushLabelName(block->name); } else if (auto* loop = curr->dynCast<Loop>()) { - if (loop->name.is()) loop->name = self->mapper.pushLabelName(loop->name); + if (loop->name.is()) + loop->name = self->mapper.pushLabelName(loop->name); } } static void doPostVisitControlFlow(Walker* self, Expression** currp) { auto* curr = *currp; if (auto* block = curr->dynCast<Block>()) { - if (block->name.is()) self->mapper.popLabelName(block->name); + if (block->name.is()) + self->mapper.popLabelName(block->name); } else if (auto* loop = curr->dynCast<Loop>()) { - if (loop->name.is()) self->mapper.popLabelName(loop->name); + if (loop->name.is()) + self->mapper.popLabelName(loop->name); } } - void visitBreak(Break *curr) { + void visitBreak(Break* curr) { curr->name = mapper.sourceToUnique(curr->name); } - void visitSwitch(Switch *curr) { + void visitSwitch(Switch* curr) { for (auto& target : curr->targets) { target = mapper.sourceToUnique(target); } diff --git a/src/pass.h b/src/pass.h index 65bf7bf52..720e5c992 100644 --- a/src/pass.h +++ b/src/pass.h @@ -19,10 +19,10 @@ #include <functional> -#include "wasm.h" -#include "wasm-traversal.h" #include "mixed_arena.h" #include "support/utilities.h" +#include "wasm-traversal.h" +#include "wasm.h" namespace wasm { @@ -36,9 +36,9 @@ struct PassRegistry { static PassRegistry* get(); - typedef std::function<Pass* ()> Creator; + typedef std::function<Pass*()> Creator; - void registerPass(const char* name, const char *description, Creator create); + void registerPass(const char* name, const char* description, Creator create); Pass* createPass(std::string name); std::vector<std::string> getRegisteredNames(); std::string getPassDescription(std::string name); @@ -50,7 +50,8 @@ private: std::string description; Creator create; PassInfo() = default; - PassInfo(std::string description, Creator create) : description(description), create(create) {} + PassInfo(std::string description, Creator create) + : description(description), create(create) {} }; std::map<std::string, PassInfo> passInfos; }; @@ -68,13 +69,15 @@ struct PassOptions { int shrinkLevel = 0; // Optimize assuming things like div by 0, bad load/store, will not trap. bool ignoreImplicitTraps = false; - // Optimize assuming that the low 1K of memory is not valid memory for the application - // to use. In that case, we can optimize load/store offsets in many cases. + // Optimize assuming that the low 1K of memory is not valid memory for the + // application to use. In that case, we can optimize load/store offsets in + // many cases. bool lowMemoryUnused = false; enum { LowMemoryBound = 1024 }; // Whether to try to preserve debug info through, which are special calls. bool debugInfo = false; - // Arbitrary string arguments from the commandline, which we forward to passes. + // Arbitrary string arguments from the commandline, which we forward to + // passes. std::map<std::string, std::string> arguments; void setDefaultOptimizationOptions() { @@ -111,7 +114,8 @@ struct PassRunner { PassOptions options; PassRunner(Module* wasm) : wasm(wasm), allocator(&wasm->allocator) {} - PassRunner(Module* wasm, PassOptions options) : wasm(wasm), allocator(&wasm->allocator), options(options) {} + PassRunner(Module* wasm, PassOptions options) + : wasm(wasm), allocator(&wasm->allocator), options(options) {} // no copying, we control |passes| PassRunner(const PassRunner&) = delete; @@ -119,30 +123,24 @@ struct PassRunner { void setDebug(bool debug) { options.debug = debug; - options.validateGlobally = debug; // validate everything by default if debugging - } - void setDebugInfo(bool debugInfo) { - options.debugInfo = debugInfo; + // validate everything by default if debugging + options.validateGlobally = debug; } + void setDebugInfo(bool debugInfo) { options.debugInfo = debugInfo; } void setValidateGlobally(bool validate) { options.validateGlobally = validate; } void add(std::string passName) { auto pass = PassRegistry::get()->createPass(passName); - if (!pass) Fatal() << "Could not find pass: " << passName << "\n"; + if (!pass) + Fatal() << "Could not find pass: " << passName << "\n"; doAdd(pass); } - template<class P> - void add() { - doAdd(new P()); - } + template<class P> void add() { doAdd(new P()); } - template<class P, class Arg> - void add(Arg arg){ - doAdd(new P(arg)); - } + template<class P, class Arg> void add(Arg arg) { doAdd(new P(arg)); } // Adds the default set of optimization passes; this is // what -O does. @@ -172,26 +170,24 @@ struct PassRunner { void runOnFunction(Function* func); // Get the last pass that was already executed of a certain type. - template<class P> - P* getLast(); + template<class P> P* getLast(); ~PassRunner(); // When running a pass runner within another pass runner, this // flag should be set. This influences how pass debugging works, // and may influence other things in the future too. - void setIsNested(bool nested) { - isNested = nested; - } + void setIsNested(bool nested) { isNested = nested; } - // BINARYEN_PASS_DEBUG is a convenient commandline way to log out the toplevel passes, their times, - // and validate between each pass. - // (we don't recurse pass debug into sub-passes, as it doesn't help anyhow and - // also is bad for e.g. printing which is a pass) + // BINARYEN_PASS_DEBUG is a convenient commandline way to log out the toplevel + // passes, their times, and validate between each pass. + // (we don't recurse pass debug into sub-passes, as it + // doesn't help anyhow and also is bad for e.g. printing + // which is a pass) // this method returns whether we are in passDebug mode, and which value: // 1: run pass by pass, validating in between - // 2: also save the last pass, so it breakage happens we can print the last one - // 3: also dump out byn-* files for each pass + // 2: also save the last pass, so it breakage happens we can print the last + // one 3: also dump out byn-* files for each pass static int getPassDebug(); protected: @@ -209,7 +205,7 @@ private: // invalidates. // If a function is passed, we operate just on that function; // otherwise, the whole module. - void handleAfterEffects(Pass* pass, Function* func=nullptr); + void handleAfterEffects(Pass* pass, Function* func = nullptr); }; // @@ -224,13 +220,12 @@ public: virtual void prepareToRun(PassRunner* runner, Module* module) {} // Implement this with code to run the pass on the whole module - virtual void run(PassRunner* runner, Module* module) { - WASM_UNREACHABLE(); - } + virtual void run(PassRunner* runner, Module* module) { WASM_UNREACHABLE(); } // Implement this with code to run the pass on a single function, for // a function-parallel pass - virtual void runOnFunction(PassRunner* runner, Module* module, Function* function) { + virtual void + runOnFunction(PassRunner* runner, Module* module, Function* function) { WASM_UNREACHABLE(); } @@ -242,10 +237,10 @@ public: // if you do not ad global state that could be raced on, your pass could be // function-parallel. // - // Function-parallel passes create an instance of the Walker class per function. - // That means that you can't rely on Walker object properties to persist across - // your functions, and you can't expect a new object to be created for each - // function either (which could be very inefficient). + // Function-parallel passes create an instance of the Walker class per + // function. That means that you can't rely on Walker object properties to + // persist across your functions, and you can't expect a new object to be + // created for each function either (which could be very inefficient). // // It is valid for function-parallel passes to read (but not modify) global // module state, like globals or imports. However, reading other functions' @@ -253,9 +248,9 @@ public: // adding functions to the module. virtual bool isFunctionParallel() { return false; } - // This method is used to create instances per function for a function-parallel - // pass. You may need to override this if you subclass a Walker, as otherwise - // this will create the parent class. + // This method is used to create instances per function for a + // function-parallel pass. You may need to override this if you subclass a + // Walker, as otherwise this will create the parent class. virtual Pass* create() { WASM_UNREACHABLE(); } // Whether this pass modifies the Binaryen IR in the module. This is true for @@ -270,8 +265,8 @@ public: protected: Pass() = default; - Pass(Pass &) = default; - Pass &operator=(const Pass&) = delete; + Pass(Pass&) = default; + Pass& operator=(const Pass&) = delete; }; // @@ -280,7 +275,7 @@ protected: // template<typename WalkerType> class WalkerPass : public Pass, public WalkerType { - PassRunner *runner; + PassRunner* runner; protected: typedef WalkerPass<WalkerType> super; @@ -292,23 +287,18 @@ public: WalkerType::walkModule(module); } - void runOnFunction(PassRunner* runner, Module* module, Function* func) override { + void + runOnFunction(PassRunner* runner, Module* module, Function* func) override { setPassRunner(runner); WalkerType::setModule(module); WalkerType::walkFunction(func); } - PassRunner* getPassRunner() { - return runner; - } + PassRunner* getPassRunner() { return runner; } - PassOptions& getPassOptions() { - return runner->options; - } + PassOptions& getPassOptions() { return runner->options; } - void setPassRunner(PassRunner* runner_) { - runner = runner_; - } + void setPassRunner(PassRunner* runner_) { runner = runner_; } }; } // namespace wasm diff --git a/src/passes/CoalesceLocals.cpp b/src/passes/CoalesceLocals.cpp index 621383ca4..a085b61fb 100644 --- a/src/passes/CoalesceLocals.cpp +++ b/src/passes/CoalesceLocals.cpp @@ -14,32 +14,31 @@ * limitations under the License. */ - // // Coalesce locals, in order to reduce the total number of locals. This // is similar to register allocation, however, there is never any // spilling, and there isn't a fixed number of locals. // - #include <algorithm> #include <memory> #include <unordered_set> -#include "wasm.h" -#include "pass.h" -#include "ir/utils.h" #include "cfg/liveness-traversal.h" -#include "wasm-builder.h" +#include "ir/utils.h" +#include "pass.h" #include "support/learning.h" #include "support/permutations.h" +#include "wasm-builder.h" +#include "wasm.h" #ifdef CFG_PROFILE #include "support/timing.h" #endif namespace wasm { -struct CoalesceLocals : public WalkerPass<LivenessWalker<CoalesceLocals, Visitor<CoalesceLocals>>> { +struct CoalesceLocals + : public WalkerPass<LivenessWalker<CoalesceLocals, Visitor<CoalesceLocals>>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new CoalesceLocals; } @@ -54,23 +53,30 @@ struct CoalesceLocals : public WalkerPass<LivenessWalker<CoalesceLocals, Visitor void calculateInterferences(const LocalSet& locals); - void pickIndicesFromOrder(std::vector<Index>& order, std::vector<Index>& indices); - void pickIndicesFromOrder(std::vector<Index>& order, std::vector<Index>& indices, Index& removedCopies); + void pickIndicesFromOrder(std::vector<Index>& order, + std::vector<Index>& indices); + void pickIndicesFromOrder(std::vector<Index>& order, + std::vector<Index>& indices, + Index& removedCopies); - virtual void pickIndices(std::vector<Index>& indices); // returns a vector of oldIndex => newIndex + // returns a vector of oldIndex => newIndex + virtual void pickIndices(std::vector<Index>& indices); void applyIndices(std::vector<Index>& indices, Expression* root); // interference state - std::vector<bool> interferences; // canonicalized - accesses should check (low, high) + // canonicalized - accesses should check (low, high) + std::vector<bool> interferences; void interfere(Index i, Index j) { - if (i == j) return; + if (i == j) + return; interferences[std::min(i, j) * numLocals + std::max(i, j)] = 1; } - void interfereLowHigh(Index low, Index high) { // optimized version where you know that low < high + // optimized version where you know that low < high + void interfereLowHigh(Index low, Index high) { assert(low < high); interferences[low * numLocals + high] = 1; } @@ -97,20 +103,25 @@ void CoalesceLocals::doWalkFunction(Function* func) { applyIndices(indices, func->body); } -// A copy on a backedge can be especially costly, forcing us to branch just to do that copy. -// Add weight to such copies, so we prioritize getting rid of them. +// A copy on a backedge can be especially costly, forcing us to branch just to +// do that copy. Add weight to such copies, so we prioritize getting rid of +// them. void CoalesceLocals::increaseBackEdgePriorities() { for (auto* loopTop : loopTops) { // ignore the first edge, it is the initial entry, we just want backedges auto& in = loopTop->in; for (Index i = 1; i < in.size(); i++) { auto* arrivingBlock = in[i]; - if (arrivingBlock->out.size() > 1) continue; // we just want unconditional branches to the loop top, true phi fragments + if (arrivingBlock->out.size() > 1) + // we just want unconditional branches to the loop top, true phi + // fragments + continue; for (auto& action : arrivingBlock->contents.actions) { if (action.isSet()) { auto* set = (*action.origin)->cast<SetLocal>(); if (auto* get = getCopy(set)) { - // this is indeed a copy, add to the cost (default cost is 2, so this adds 50%, and can mostly break ties) + // this is indeed a copy, add to the cost (default cost is 2, so + // this adds 50%, and can mostly break ties) addCopy(set->index, get->index); } } @@ -123,8 +134,10 @@ void CoalesceLocals::calculateInterferences() { interferences.resize(numLocals * numLocals); std::fill(interferences.begin(), interferences.end(), false); for (auto& curr : basicBlocks) { - if (liveBlocks.count(curr.get()) == 0) continue; // ignore dead blocks - // everything coming in might interfere, as it might come from a different block + if (liveBlocks.count(curr.get()) == 0) + continue; // ignore dead blocks + // everything coming in might interfere, as it might come from a different + // block auto live = curr->contents.end; calculateInterferences(live); // scan through the block itself @@ -166,18 +179,22 @@ void CoalesceLocals::calculateInterferences(const LocalSet& locals) { // Indices decision making -void CoalesceLocals::pickIndicesFromOrder(std::vector<Index>& order, std::vector<Index>& indices) { +void CoalesceLocals::pickIndicesFromOrder(std::vector<Index>& order, + std::vector<Index>& indices) { Index removedCopies; pickIndicesFromOrder(order, indices, removedCopies); } -void CoalesceLocals::pickIndicesFromOrder(std::vector<Index>& order, std::vector<Index>& indices, Index& removedCopies) { - // mostly-simple greedy coloring +void CoalesceLocals::pickIndicesFromOrder(std::vector<Index>& order, + std::vector<Index>& indices, + Index& removedCopies) { +// mostly-simple greedy coloring #if CFG_DEBUG std::cerr << "\npickIndicesFromOrder on " << getFunction()->name << '\n'; std::cerr << getFunction()->body << '\n'; std::cerr << "order:\n"; - for (auto i : order) std::cerr << i << ' '; + for (auto i : order) + std::cerr << i << ' '; std::cerr << '\n'; std::cerr << "interferences:\n"; for (Index i = 0; i < numLocals; i++) { @@ -204,16 +221,20 @@ void CoalesceLocals::pickIndicesFromOrder(std::vector<Index>& order, std::vector std::cerr << " $" << i << ": " << totalCopies[i] << '\n'; } #endif - // TODO: take into account distribution (99-1 is better than 50-50 with two registers, for gzip) + // TODO: take into account distribution (99-1 is better than 50-50 with two + // registers, for gzip) std::vector<Type> types; - std::vector<bool> newInterferences; // new index * numLocals => list of all interferences of locals merged to it - std::vector<uint8_t> newCopies; // new index * numLocals => list of all copies of locals merged to it + // new index * numLocals => list of all interferences of locals merged to it + std::vector<bool> newInterferences; + // new index * numLocals => list of all copies of locals merged to it + std::vector<uint8_t> newCopies; indices.resize(numLocals); types.resize(numLocals); newInterferences.resize(numLocals * numLocals); std::fill(newInterferences.begin(), newInterferences.end(), false); auto numParams = getFunction()->getNumParams(); - newCopies.resize(numParams * numLocals); // start with enough room for the params + // start with enough room for the params + newCopies.resize(numParams * numLocals); std::fill(newCopies.begin(), newCopies.end(), 0); Index nextFree = 0; removedCopies = 0; @@ -234,9 +255,12 @@ void CoalesceLocals::pickIndicesFromOrder(std::vector<Index>& order, std::vector Index found = -1; uint8_t foundCopies = -1; for (Index j = 0; j < nextFree; j++) { - if (!newInterferences[j * numLocals + actual] && getFunction()->getLocalType(actual) == types[j]) { - // this does not interfere, so it might be what we want. but pick the one eliminating the most copies - // (we could stop looking forward when there are no more items that have copies anyhow, but it doesn't seem to help) + if (!newInterferences[j * numLocals + actual] && + getFunction()->getLocalType(actual) == types[j]) { + // this does not interfere, so it might be what we want. but pick the + // one eliminating the most copies (we could stop looking forward when + // there are no more items that have copies anyhow, but it doesn't seem + // to help) auto currCopies = newCopies[j * numLocals + actual]; if (found == Index(-1) || currCopies > foundCopies) { indices[actual] = found = j; @@ -258,46 +282,53 @@ void CoalesceLocals::pickIndicesFromOrder(std::vector<Index>& order, std::vector #endif // merge new interferences and copies for the new index for (Index k = i + 1; k < numLocals; k++) { - auto j = order[k]; // go in the order, we only need to update for those we will see later - newInterferences[found * numLocals + j] = newInterferences[found * numLocals + j] | interferes(actual, j); + // go in the order, we only need to update for those we will see later + auto j = order[k]; + newInterferences[found * numLocals + j] = + newInterferences[found * numLocals + j] | interferes(actual, j); newCopies[found * numLocals + j] += getCopies(actual, j); } } } -// given a baseline order, adjust it based on an important order of priorities (higher values -// are higher priority). The priorities take precedence, unless they are equal and then -// the original order should be kept. -std::vector<Index> adjustOrderByPriorities(std::vector<Index>& baseline, std::vector<Index>& priorities) { +// given a baseline order, adjust it based on an important order of priorities +// (higher values are higher priority). The priorities take precedence, unless +// they are equal and then the original order should be kept. +std::vector<Index> adjustOrderByPriorities(std::vector<Index>& baseline, + std::vector<Index>& priorities) { std::vector<Index> ret = baseline; std::vector<Index> reversed = makeReversed(baseline); std::sort(ret.begin(), ret.end(), [&priorities, &reversed](Index x, Index y) { - return priorities[x] > priorities[y] || (priorities[x] == priorities[y] && reversed[x] < reversed[y]); + return priorities[x] > priorities[y] || + (priorities[x] == priorities[y] && reversed[x] < reversed[y]); }); return ret; } void CoalesceLocals::pickIndices(std::vector<Index>& indices) { - if (numLocals == 0) return; + if (numLocals == 0) + return; if (numLocals == 1) { indices.push_back(0); return; } - // take into account total copies. but we must keep params in place, so give them max priority + // take into account total copies. but we must keep params in place, so give + // them max priority auto adjustedTotalCopies = totalCopies; auto numParams = getFunction()->getNumParams(); for (Index i = 0; i < numParams; i++) { adjustedTotalCopies[i] = std::numeric_limits<Index>::max(); } - // first try the natural order. this is less arbitrary than it seems, as the program - // may have a natural order of locals inherent in it. + // first try the natural order. this is less arbitrary than it seems, as the + // program may have a natural order of locals inherent in it. auto order = makeIdentity(numLocals); order = adjustOrderByPriorities(order, adjustedTotalCopies); Index removedCopies; pickIndicesFromOrder(order, indices, removedCopies); auto maxIndex = *std::max_element(indices.begin(), indices.end()); - // next try the reverse order. this both gives us another chance at something good, - // and also the very naturalness of the simple order may be quite suboptimal + // next try the reverse order. this both gives us another chance at something + // good, and also the very naturalness of the simple order may be quite + // suboptimal setIdentity(order); for (Index i = numParams; i < numLocals; i++) { order[i] = numParams + numLocals - 1 - i; @@ -306,15 +337,18 @@ void CoalesceLocals::pickIndices(std::vector<Index>& indices) { std::vector<Index> reverseIndices; Index reverseRemovedCopies; pickIndicesFromOrder(order, reverseIndices, reverseRemovedCopies); - auto reverseMaxIndex = *std::max_element(reverseIndices.begin(), reverseIndices.end()); - // prefer to remove copies foremost, as it matters more for code size (minus gzip), and - // improves throughput. - if (reverseRemovedCopies > removedCopies || (reverseRemovedCopies == removedCopies && reverseMaxIndex < maxIndex)) { + auto reverseMaxIndex = + *std::max_element(reverseIndices.begin(), reverseIndices.end()); + // prefer to remove copies foremost, as it matters more for code size (minus + // gzip), and improves throughput. + if (reverseRemovedCopies > removedCopies || + (reverseRemovedCopies == removedCopies && reverseMaxIndex < maxIndex)) { indices.swap(reverseIndices); } } -void CoalesceLocals::applyIndices(std::vector<Index>& indices, Expression* root) { +void CoalesceLocals::applyIndices(std::vector<Index>& indices, + Expression* root) { assert(indices.size() == numLocals); for (auto& curr : basicBlocks) { auto& actions = curr->contents.actions; @@ -325,15 +359,19 @@ void CoalesceLocals::applyIndices(std::vector<Index>& indices, Expression* root) } else if (action.isSet()) { auto* set = (*action.origin)->cast<SetLocal>(); set->index = indices[set->index]; - // in addition, we can optimize out redundant copies and ineffective sets + // in addition, we can optimize out redundant copies and ineffective + // sets GetLocal* get; - if ((get = set->value->dynCast<GetLocal>()) && get->index == set->index) { + if ((get = set->value->dynCast<GetLocal>()) && + get->index == set->index) { action.removeCopy(); continue; } // remove ineffective actions if (!action.effective) { - *action.origin = set->value; // value may have no side effects, further optimizations can eliminate it + // value may have no side effects, further optimizations can eliminate + // it + *action.origin = set->value; if (!set->isTee()) { // we need to drop it Drop* drop = ExpressionManipulator::convert<SetLocal, Drop>(set); @@ -382,10 +420,12 @@ void CoalesceLocalsWithLearning::pickIndices(std::vector<Index>& indices) { double getFitness() { return fitness; } void dump(std::string text) { std::cout << text + ": ( "; - for (Index i = 0; i < size(); i++) std::cout << (*this)[i] << " "; + for (Index i = 0; i < size(); i++) + std::cout << (*this)[i] << " "; std::cout << ")\n"; std::cout << "of quality: " << getFitness() << "\n"; } + private: double fitness; }; @@ -405,9 +445,11 @@ void CoalesceLocalsWithLearning::pickIndices(std::vector<Index>& indices) { // secondarily, it is nice to not reorder locals unnecessarily double fragment = 1.0 / (2.0 * parent->numLocals); for (Index i = 0; i < parent->numLocals; i++) { - if ((*order)[i] == i) fitness += fragment; // boost for each that wasn't moved + if ((*order)[i] == i) + fitness += fragment; // boost for each that wasn't moved } - fitness = (100 * fitness) + removedCopies; // removing copies is a secondary concern + // removing copies is a secondary concern + fitness = (100 * fitness) + removedCopies; order->setFitness(fitness); } @@ -418,16 +460,19 @@ void CoalesceLocalsWithLearning::pickIndices(std::vector<Index>& indices) { (*ret)[i] = i; } if (first) { - // as the first guess, use the natural order. this is not arbitrary for two reasons. - // first, there may be an inherent order in the input (frequent indices are lower, - // etc.). second, by ensuring we start with the natural order, we ensure we are at - // least as good as the non-learning variant. - // TODO: use ::pickIndices from the parent, so we literally get the simpler approach - // as our first option + // as the first guess, use the natural order. this is not arbitrary for + // two reasons. first, there may be an inherent order in the input + // (frequent indices are lower, etc.). second, by ensuring we start with + // the natural order, we ensure we are at least as good as the + // non-learning variant. + // TODO: use ::pickIndices from the parent, so we literally get the + // simpler approach as our first option first = false; } else { // leave params alone, shuffle the rest - std::shuffle(ret->begin() + parent->getFunction()->getNumParams(), ret->end(), noise); + std::shuffle(ret->begin() + parent->getFunction()->getNumParams(), + ret->end(), + noise); } calculateFitness(ret); #ifdef CFG_LEARN_DEBUG @@ -455,7 +500,9 @@ void CoalesceLocalsWithLearning::pickIndices(std::vector<Index>& indices) { // if (i, i + 1) is in reverse order in right, flip them if (reverseRight[(*ret)[i]] > reverseRight[(*ret)[i + 1]]) { std::swap((*ret)[i], (*ret)[i + 1]); - i++; // if we don't skip, we might end up pushing an element all the way to the end, which is not very perturbation-y + // if we don't skip, we might end up pushing an element all the way to + // the end, which is not very perturbation-y + i++; } } calculateFitness(ret); @@ -475,7 +522,8 @@ void CoalesceLocalsWithLearning::pickIndices(std::vector<Index>& indices) { std::cout << "[learning for " << getFunction()->name << "]\n"; #endif auto numVars = this->getFunction()->getNumVars(); - const int GENERATION_SIZE = std::min(Index(numVars * (numVars - 1)), Index(20)); + const int GENERATION_SIZE = + std::min(Index(numVars * (numVars - 1)), Index(20)); Generator generator(this); GeneticLearner<Order, double, Generator> learner(generator, GENERATION_SIZE); #ifdef CFG_LEARN_DEBUG @@ -486,7 +534,8 @@ void CoalesceLocalsWithLearning::pickIndices(std::vector<Index>& indices) { while (1) { learner.runGeneration(); auto newBest = learner.getBest()->getFitness(); - if (newBest == oldBest) break; // unlikely we can improve + if (newBest == oldBest) + break; // unlikely we can improve oldBest = newBest; #ifdef CFG_LEARN_DEBUG learner.getBest()->dump("current best"); @@ -495,16 +544,15 @@ void CoalesceLocalsWithLearning::pickIndices(std::vector<Index>& indices) { #ifdef CFG_LEARN_DEBUG learner.getBest()->dump("the best"); #endif - this->pickIndicesFromOrder(*learner.getBest(), indices); // TODO: cache indices in Orders, at the cost of more memory? + // TODO: cache indices in Orders, at the cost of more memory? + this->pickIndicesFromOrder(*learner.getBest(), indices); } // declare passes -Pass *createCoalesceLocalsPass() { - return new CoalesceLocals(); -} +Pass* createCoalesceLocalsPass() { return new CoalesceLocals(); } -Pass *createCoalesceLocalsWithLearningPass() { +Pass* createCoalesceLocalsWithLearningPass() { return new CoalesceLocalsWithLearning(); } diff --git a/src/passes/CodeFolding.cpp b/src/passes/CodeFolding.cpp index a79980cfe..0479472d8 100644 --- a/src/passes/CodeFolding.cpp +++ b/src/passes/CodeFolding.cpp @@ -57,28 +57,29 @@ #include <iterator> -#include "wasm.h" -#include "pass.h" -#include "wasm-builder.h" -#include "ir/utils.h" #include "ir/branch-utils.h" #include "ir/effects.h" #include "ir/label-utils.h" +#include "ir/utils.h" +#include "pass.h" +#include "wasm-builder.h" +#include "wasm.h" namespace wasm { static const Index WORTH_ADDING_BLOCK_TO_REMOVE_THIS_MUCH = 3; -struct ExpressionMarker : public PostWalker<ExpressionMarker, UnifiedExpressionVisitor<ExpressionMarker>> { +struct ExpressionMarker + : public PostWalker<ExpressionMarker, + UnifiedExpressionVisitor<ExpressionMarker>> { std::set<Expression*>& marked; - ExpressionMarker(std::set<Expression*>& marked, Expression* expr) : marked(marked) { + ExpressionMarker(std::set<Expression*>& marked, Expression* expr) + : marked(marked) { walk(expr); } - void visitExpression(Expression* expr) { - marked.insert(expr); - } + void visitExpression(Expression* expr) { marked.insert(expr); } }; struct CodeFolding : public WalkerPass<ControlFlowWalker<CodeFolding>> { @@ -91,15 +92,18 @@ struct CodeFolding : public WalkerPass<ControlFlowWalker<CodeFolding>> { struct Tail { Expression* expr; // nullptr if this is a fallthrough Block* block; // the enclosing block of code we hope to merge at its tail - Expression** pointer; // for an expr with no parent block, the location it is at, so we can replace it + Expression** pointer; // for an expr with no parent block, the location it + // is at, so we can replace it // For a fallthrough Tail(Block* block) : expr(nullptr), block(block), pointer(nullptr) {} // For a break - Tail(Expression* expr, Block* block) : expr(expr), block(block), pointer(nullptr) { + Tail(Expression* expr, Block* block) + : expr(expr), block(block), pointer(nullptr) { validate(); } - Tail(Expression* expr, Expression** pointer) : expr(expr), block(nullptr), pointer(pointer) {} + Tail(Expression* expr, Expression** pointer) + : expr(expr), block(nullptr), pointer(pointer) {} bool isFallthrough() const { return expr == nullptr; } @@ -116,11 +120,13 @@ struct CodeFolding : public WalkerPass<ControlFlowWalker<CodeFolding>> { // pass state - std::map<Name, std::vector<Tail>> breakTails; // break target name => tails that reach it + std::map<Name, std::vector<Tail>> breakTails; // break target name => tails + // that reach it std::vector<Tail> unreachableTails; // tails leading to (unreachable) - std::vector<Tail> returnTails; // tails leading to (return) - std::set<Name> unoptimizables; // break target names that we can't handle - std::set<Expression*> modifieds; // modified code should not be processed again, wait for next pass + std::vector<Tail> returnTails; // tails leading to (return) + std::set<Name> unoptimizables; // break target names that we can't handle + std::set<Expression*> modifieds; // modified code should not be processed + // again, wait for next pass // walking @@ -167,20 +173,25 @@ struct CodeFolding : public WalkerPass<ControlFlowWalker<CodeFolding>> { return; } } - // otherwise, if we have a large value, it might be worth optimizing us as well + // otherwise, if we have a large value, it might be worth optimizing us as + // well returnTails.push_back(Tail(curr, getCurrentPointer())); } void visitBlock(Block* curr) { - if (curr->list.empty()) return; - if (!curr->name.is()) return; - if (unoptimizables.count(curr->name) > 0) return; + if (curr->list.empty()) + return; + if (!curr->name.is()) + return; + if (unoptimizables.count(curr->name) > 0) + return; // we can't optimize a fallthrough value if (isConcreteType(curr->list.back()->type)) { return; } auto iter = breakTails.find(curr->name); - if (iter == breakTails.end()) return; + if (iter == breakTails.end()) + return; // looks promising auto& tails = iter->second; // see if there is a fallthrough @@ -191,23 +202,22 @@ struct CodeFolding : public WalkerPass<ControlFlowWalker<CodeFolding>> { } } if (hasFallthrough) { - tails.push_back({ Tail(curr) }); + tails.push_back({Tail(curr)}); } optimizeExpressionTails(tails, curr); } void visitIf(If* curr) { - if (!curr->ifFalse) return; + if (!curr->ifFalse) + return; // if both sides are identical, this is easy to fold if (ExpressionAnalyzer::equal(curr->ifTrue, curr->ifFalse)) { Builder builder(*getModule()); // remove if (4 bytes), remove one arm, add drop (1), add block (3), // so this must be a net savings markAsModified(curr); - auto* ret = builder.makeSequence( - builder.makeDrop(curr->condition), - curr->ifTrue - ); + auto* ret = + builder.makeSequence(builder.makeDrop(curr->condition), curr->ifTrue); // we must ensure we present the same type as the if had ret->finalize(curr->type); replaceCurrent(ret); @@ -237,9 +247,8 @@ struct CodeFolding : public WalkerPass<ControlFlowWalker<CodeFolding>> { } // we need nameless blocks, as if there is a name, someone might branch // to the end, skipping the code we want to merge - if (left && right && - !left->name.is() && !right->name.is()) { - std::vector<Tail> tails = { Tail(left), Tail(right) }; + if (left && right && !left->name.is() && !right->name.is()) { + std::vector<Tail> tails = {Tail(left), Tail(right)}; optimizeExpressionTails(tails, curr); } } @@ -251,7 +260,8 @@ struct CodeFolding : public WalkerPass<ControlFlowWalker<CodeFolding>> { anotherPass = false; super::doWalkFunction(func); optimizeTerminatingTails(unreachableTails); - // optimize returns at the end, so we can benefit from a fallthrough if there is a value TODO: separate passes for them? + // optimize returns at the end, so we can benefit from a fallthrough if + // there is a value TODO: separate passes for them? optimizeTerminatingTails(returnTails); // TODO add fallthrough for returns // TODO optimize returns not in blocks, a big return value can be worth it @@ -277,7 +287,10 @@ private: for (auto* item : items) { auto exiting = BranchUtils::getExitingBranches(item); std::vector<Name> intersection; - std::set_intersection(allTargets.begin(), allTargets.end(), exiting.begin(), exiting.end(), + std::set_intersection(allTargets.begin(), + allTargets.end(), + exiting.begin(), + exiting.end(), std::back_inserter(intersection)); if (intersection.size() > 0) { // anything exiting that is in all targets is something bad @@ -287,15 +300,18 @@ private: return true; } - // optimize tails that reach the outside of an expression. code that is identical in all - // paths leading to the block exit can be merged. + // optimize tails that reach the outside of an expression. code that is + // identical in all paths leading to the block exit can be merged. template<typename T> void optimizeExpressionTails(std::vector<Tail>& tails, T* curr) { - if (tails.size() < 2) return; + if (tails.size() < 2) + return; // see if anything is untoward, and we should not do this for (auto& tail : tails) { - if (tail.expr && modifieds.count(tail.expr) > 0) return; - if (modifieds.count(tail.block) > 0) return; + if (tail.expr && modifieds.count(tail.expr) > 0) + return; + if (modifieds.count(tail.block) > 0) + return; // if we were not modified, then we should be valid for processing tail.validate(); } @@ -316,7 +332,7 @@ private: // elements to be worth that extra block (although, there is // some chance the block would get merged higher up, see later) std::vector<Expression*> mergeable; // the elements we can merge - Index num = 0; // how many elements back from the tail to look at + Index num = 0; // how many elements back from the tail to look at Index saved = 0; // how much we can save while (1) { // check if this num is still relevant @@ -329,7 +345,8 @@ private: break; } } - if (stop) break; + if (stop) + break; auto* item = getMergeable(tails[0], num); for (auto& tail : tails) { if (!ExpressionAnalyzer::equal(item, getMergeable(tail, num))) { @@ -338,15 +355,18 @@ private: break; } } - if (stop) break; + if (stop) + break; // we may have found another one we can merge - can we move it? - if (!canMove({ item }, curr)) break; + if (!canMove({item}, curr)) + break; // we found another one we can merge mergeable.push_back(item); num++; saved += Measurer::measure(item); } - if (saved == 0) return; + if (saved == 0) + return; // we may be able to save enough. if (saved < WORTH_ADDING_BLOCK_TO_REMOVE_THIS_MUCH) { // it's not obvious we can save enough. see if we get rid @@ -363,13 +383,16 @@ private: if (!willEmptyBlock) { // last chance, if our parent is a block, then it should be // fine to create a new block here, it will be merged up - assert(curr == controlFlowStack.back()); // we are an if or a block, at the top + // we are an if or a block, at the top + assert(curr == controlFlowStack.back()); if (controlFlowStack.size() <= 1) { return; // no parent at all - // TODO: if we are the toplevel in the function, then in the binary format - // we might avoid emitting a block, so the same logic applies here? + // TODO: if we are the toplevel in the function, then in the binary + // format we might avoid emitting a block, so the same logic + // applies here? } - auto* parent = controlFlowStack[controlFlowStack.size() - 2]->dynCast<Block>(); + auto* parent = + controlFlowStack[controlFlowStack.size() - 2]->dynCast<Block>(); if (!parent) { return; // parent is not a block } @@ -440,15 +463,23 @@ private: // deeper merges first. // returns whether we optimized something. bool optimizeTerminatingTails(std::vector<Tail>& tails, Index num = 0) { - if (tails.size() < 2) return false; - // remove things that are untoward and cannot be optimized - tails.erase(std::remove_if(tails.begin(), tails.end(), [&](Tail& tail) { - if (tail.expr && modifieds.count(tail.expr) > 0) return true; - if (tail.block && modifieds.count(tail.block) > 0) return true; - // if we were not modified, then we should be valid for processing - tail.validate(); + if (tails.size() < 2) return false; - }), tails.end()); + // remove things that are untoward and cannot be optimized + tails.erase( + std::remove_if(tails.begin(), + tails.end(), + [&](Tail& tail) { + if (tail.expr && modifieds.count(tail.expr) > 0) + return true; + if (tail.block && modifieds.count(tail.block) > 0) + return true; + // if we were not modified, then we should be valid for + // processing + tail.validate(); + return false; + }), + tails.end()); // now let's try to find subsets that are mergeable. we don't look hard // for the most optimal; further passes may find more // effectiveSize: TODO: special-case fallthrough, matters for returns @@ -481,7 +512,7 @@ private: // estimate if a merging is worth the cost auto worthIt = [&](Index num, std::vector<Tail>& tails) { auto items = getTailItems(num, tails); // the elements we can merge - Index saved = 0; // how much we can save + Index saved = 0; // how much we can save for (auto* item : items) { saved += Measurer::measure(item) * (tails.size() - 1); } @@ -496,7 +527,8 @@ private: cost += WORTH_ADDING_BLOCK_TO_REMOVE_THIS_MUCH; // if we cannot merge to the end, then we definitely need 2 blocks, // and a branch - if (!canMove(items, getFunction()->body)) { // TODO: efficiency, entire body + // TODO: efficiency, entire body + if (!canMove(items, getFunction()->body)) { cost += 1 + WORTH_ADDING_BLOCK_TO_REMOVE_THIS_MUCH; // TODO: to do this, we need to maintain a map of element=>parent, // so that we can insert the new blocks in the right place @@ -509,64 +541,86 @@ private: // let's see if we can merge deeper than num, to num + 1 auto next = tails; // remove tails that are too short, or that we hit an item we can't handle - next.erase(std::remove_if(next.begin(), next.end(), [&](Tail& tail) { - if (effectiveSize(tail) < num + 1) return true; - auto* newItem = getItem(tail, num); - // ignore tails that break to outside blocks. we want to move code to - // the very outermost position, so such code cannot be moved - // TODO: this should not be a problem in *non*-terminating tails, - // but double-verify that - if (EffectAnalyzer(getPassOptions(), newItem).hasExternalBreakTargets()) { - return true; - } - return false; - }), next.end()); + next.erase(std::remove_if(next.begin(), + next.end(), + [&](Tail& tail) { + if (effectiveSize(tail) < num + 1) + return true; + auto* newItem = getItem(tail, num); + // ignore tails that break to outside blocks. we + // want to move code to the very outermost + // position, so such code cannot be moved + // TODO: this should not be a problem in + // *non*-terminating tails, but + // double-verify that + if (EffectAnalyzer(getPassOptions(), newItem) + .hasExternalBreakTargets()) { + return true; + } + return false; + }), + next.end()); // if we have enough to investigate, do so if (next.size() >= 2) { - // now we want to find a mergeable item - any item that is equal among a subset + // now we want to find a mergeable item - any item that is equal among a + // subset std::map<Expression*, HashType> hashes; // expression => hash value - std::map<HashType, std::vector<Expression*>> hashed; // hash value => expressions with that hash + // hash value => expressions with that hash + std::map<HashType, std::vector<Expression*>> hashed; for (auto& tail : next) { auto* item = getItem(tail, num); auto hash = hashes[item] = ExpressionAnalyzer::hash(item); hashed[hash].push_back(item); } - // look at each hash value exactly once. we do this in a deterministic order. + // look at each hash value exactly once. we do this in a deterministic + // order. std::set<HashType> seen; for (auto& tail : next) { auto* item = getItem(tail, num); auto hash = hashes[item]; - if (seen.count(hash)) continue; + if (seen.count(hash)) + continue; seen.insert(hash); auto& items = hashed[hash]; - if (items.size() == 1) continue; + if (items.size() == 1) + continue; assert(items.size() > 0); // look for an item that has another match. while (items.size() >= 2) { auto first = items[0]; std::vector<Expression*> others; - items.erase(std::remove_if(items.begin(), items.end(), [&](Expression* item) { - if (item == first || // don't bother comparing the first - ExpressionAnalyzer::equal(item, first)) { - // equal, keep it - return false; - } else { - // unequal, look at it later - others.push_back(item); - return true; - } - }), items.end()); + items.erase( + std::remove_if(items.begin(), + items.end(), + [&](Expression* item) { + if (item == + first || // don't bother comparing the first + ExpressionAnalyzer::equal(item, first)) { + // equal, keep it + return false; + } else { + // unequal, look at it later + others.push_back(item); + return true; + } + }), + items.end()); if (items.size() >= 2) { // possible merge here, investigate it auto* correct = items[0]; auto explore = next; - explore.erase(std::remove_if(explore.begin(), explore.end(), [&](Tail& tail) { - auto* item = getItem(tail, num); - return !ExpressionAnalyzer::equal(item, correct); - }), explore.end()); - // try to optimize this deeper tail. if we succeed, then stop here, as the - // changes may influence us. we leave further opts to further passes (as this - // is rare in practice, it's generally not a perf issue, but TODO optimize) + explore.erase(std::remove_if(explore.begin(), + explore.end(), + [&](Tail& tail) { + auto* item = getItem(tail, num); + return !ExpressionAnalyzer::equal( + item, correct); + }), + explore.end()); + // try to optimize this deeper tail. if we succeed, then stop here, + // as the changes may influence us. we leave further opts to further + // passes (as this is rare in practice, it's generally not a perf + // issue, but TODO optimize) if (optimizeTerminatingTails(explore, num + 1)) { return true; } @@ -578,15 +632,18 @@ private: // we explored deeper (higher num) options, but perhaps there // was nothing there while there is something we can do at this level // but if we are at num == 0, then we found nothing at all - if (num == 0) return false; + if (num == 0) + return false; // if not worth it, stop - if (!worthIt(num, tails)) return false; + if (!worthIt(num, tails)) + return false; // this is worth doing, do it! auto mergeable = getTailItems(num, tails); // the elements we can merge // since we managed a merge, then it might open up more opportunities later anotherPass = true; Builder builder(*getModule()); - LabelUtils::LabelManager labels(getFunction()); // TODO: don't create one per merge, linear in function size + // TODO: don't create one per merge, linear in function size + LabelUtils::LabelManager labels(getFunction()); Name innerName = labels.getUnique("folding-inner"); for (auto& tail : tails) { // remove the items we are merging / moving, and add a break @@ -623,7 +680,8 @@ private: // rules, and now it won't be toplevel in the function, it can // change) auto* toplevel = old->dynCast<Block>(); - if (toplevel) toplevel->finalize(); + if (toplevel) + toplevel->finalize(); if (old->type != unreachable) { inner->list.push_back(builder.makeReturn(old)); } else { @@ -649,9 +707,6 @@ private: } }; -Pass *createCodeFoldingPass() { - return new CodeFolding(); -} +Pass* createCodeFoldingPass() { return new CodeFolding(); } } // namespace wasm - diff --git a/src/passes/CodePushing.cpp b/src/passes/CodePushing.cpp index 52aab08ad..342cb5182 100644 --- a/src/passes/CodePushing.cpp +++ b/src/passes/CodePushing.cpp @@ -19,10 +19,10 @@ // a location behind a condition, where it might not always execute. // -#include <wasm.h> +#include <ir/effects.h> #include <pass.h> #include <wasm-builder.h> -#include <ir/effects.h> +#include <wasm.h> namespace wasm { @@ -50,26 +50,23 @@ struct LocalAnalyzer : public PostWalker<LocalAnalyzer> { std::fill(sfa.begin() + func->getNumParams(), sfa.end(), true); walk(func->body); for (Index i = 0; i < num; i++) { - if (numSets[i] == 0) sfa[i] = false; + if (numSets[i] == 0) + sfa[i] = false; } } - bool isSFA(Index i) { - return sfa[i]; - } + bool isSFA(Index i) { return sfa[i]; } - Index getNumGets(Index i) { - return numGets[i]; - } + Index getNumGets(Index i) { return numGets[i]; } - void visitGetLocal(GetLocal *curr) { + void visitGetLocal(GetLocal* curr) { if (numSets[curr->index] == 0) { sfa[curr->index] = false; } numGets[curr->index]++; } - void visitSetLocal(SetLocal *curr) { + void visitSetLocal(SetLocal* curr) { numSets[curr->index]++; if (numSets[curr->index] > 1) { sfa[curr->index] = false; @@ -86,12 +83,18 @@ class Pusher { PassOptions& passOptions; public: - Pusher(Block* block, LocalAnalyzer& analyzer, std::vector<Index>& numGetsSoFar, PassOptions& passOptions) : list(block->list), analyzer(analyzer), numGetsSoFar(numGetsSoFar), passOptions(passOptions) { + Pusher(Block* block, + LocalAnalyzer& analyzer, + std::vector<Index>& numGetsSoFar, + PassOptions& passOptions) + : list(block->list), analyzer(analyzer), numGetsSoFar(numGetsSoFar), + passOptions(passOptions) { // Find an optimization segment: from the first pushable thing, to the first // point past which we want to push. We then push in that range before // continuing forward. - Index relevant = list.size() - 1; // we never need to push past a final element, as - // we couldn't be used after it. + // we never need to push past a final element, as we couldn't be used after + // it. + Index relevant = list.size() - 1; const Index nothing = -1; Index i = 0; Index firstPushable = nothing; @@ -114,7 +117,8 @@ public: private: SetLocal* isPushable(Expression* curr) { auto* set = curr->dynCast<SetLocal>(); - if (!set) return nullptr; + if (!set) + return nullptr; auto index = set->index; // to be pushable, this must be SFA and the right # of gets, // but also have no side effects, as it may not execute if pushed. @@ -133,7 +137,8 @@ private: if (auto* drop = curr->dynCast<Drop>()) { curr = drop->value; } - if (curr->is<If>()) return true; + if (curr->is<If>()) + return true; if (auto* br = curr->dynCast<Break>()) { return !!br->condition; } @@ -146,12 +151,14 @@ private: // forward, that way we can push later things out of the way // of earlier ones. Once we know all we can push, we push it all // in one pass, keeping the order of the pushables intact. - assert(firstPushable != Index(-1) && pushPoint != Index(-1) && firstPushable < pushPoint); - EffectAnalyzer cumulativeEffects(passOptions); // everything that matters if you want - // to be pushed past the pushPoint + assert(firstPushable != Index(-1) && pushPoint != Index(-1) && + firstPushable < pushPoint); + // everything that matters if you want to be pushed past the pushPoint + EffectAnalyzer cumulativeEffects(passOptions); cumulativeEffects.analyze(list[pushPoint]); - cumulativeEffects.branches = false; // it is ok to ignore the branching here, - // that is the crucial point of this opt + // it is ok to ignore the branching here, that is the crucial point of this + // opt + cumulativeEffects.branches = false; std::vector<SetLocal*> toPush; Index i = pushPoint - 1; while (1) { @@ -159,11 +166,11 @@ private: if (pushable) { auto iter = pushableEffects.find(pushable); if (iter == pushableEffects.end()) { - iter = pushableEffects.emplace( - std::piecewise_construct, - std::forward_as_tuple(pushable), - std::forward_as_tuple(passOptions, pushable) - ).first; + iter = pushableEffects + .emplace(std::piecewise_construct, + std::forward_as_tuple(pushable), + std::forward_as_tuple(passOptions, pushable)) + .first; } auto& effects = iter->second; if (cumulativeEffects.invalidates(effects)) { @@ -236,30 +243,26 @@ struct CodePushing : public WalkerPass<PostWalker<CodePushing>> { walk(func->body); } - void visitGetLocal(GetLocal *curr) { - numGetsSoFar[curr->index]++; - } + void visitGetLocal(GetLocal* curr) { numGetsSoFar[curr->index]++; } void visitBlock(Block* curr) { // Pushing code only makes sense if we are size 3 or above: we need // one element to push, an element to push it past, and an element to use // what we pushed. - if (curr->list.size() < 3) return; - // At this point in the postorder traversal we have gone through all our children. - // Therefore any variable whose gets seen so far is equal to the total gets must - // have no further users after this block. And therefore when we see an SFA - // variable defined here, we know it isn't used before it either, and has just this - // one assign. So we can push it forward while we don't hit a non-control-flow - // ordering invalidation issue, since if this isn't a loop, it's fine (we're not - // used outside), and if it is, we hit the assign before any use (as we can't - // push it past a use). + if (curr->list.size() < 3) + return; + // At this point in the postorder traversal we have gone through all our + // children. Therefore any variable whose gets seen so far is equal to the + // total gets must have no further users after this block. And therefore + // when we see an SFA variable defined here, we know it isn't used before it + // either, and has just this one assign. So we can push it forward while we + // don't hit a non-control-flow ordering invalidation issue, since if this + // isn't a loop, it's fine (we're not used outside), and if it is, we hit + // the assign before any use (as we can't push it past a use). Pusher pusher(curr, analyzer, numGetsSoFar, getPassOptions()); } }; -Pass *createCodePushingPass() { - return new CodePushing(); -} +Pass* createCodePushingPass() { return new CodePushing(); } } // namespace wasm - diff --git a/src/passes/ConstHoisting.cpp b/src/passes/ConstHoisting.cpp index 11188a9ba..f67a48645 100644 --- a/src/passes/ConstHoisting.cpp +++ b/src/passes/ConstHoisting.cpp @@ -32,10 +32,10 @@ #include <map> -#include <wasm.h> #include <pass.h> #include <wasm-binary.h> #include <wasm-builder.h> +#include <wasm.h> namespace wasm { @@ -66,16 +66,14 @@ struct ConstHoisting : public WalkerPass<PostWalker<ConstHoisting>> { if (!prelude.empty()) { Builder builder(*getModule()); // merge-blocks can optimize this into a single block later in most cases - curr->body = builder.makeSequence( - builder.makeBlock(prelude), - curr->body - ); + curr->body = builder.makeSequence(builder.makeBlock(prelude), curr->body); } } private: bool worthHoisting(Literal value, Index num) { - if (num < MIN_USES) return false; + if (num < MIN_USES) + return false; // measure the size of the constant Index size = 0; switch (value.type) { @@ -112,8 +110,7 @@ private: return after < before; } - template<typename T> - Index getWrittenSize(const T& thing) { + template<typename T> Index getWrittenSize(const T& thing) { BufferWithRandomAccess buffer; buffer << thing; return buffer.size(); @@ -125,10 +122,7 @@ private: auto type = (*(vec[0]))->type; Builder builder(*getModule()); auto temp = builder.addVar(getFunction(), type); - auto* ret = builder.makeSetLocal( - temp, - *(vec[0]) - ); + auto* ret = builder.makeSetLocal(temp, *(vec[0])); for (auto item : vec) { *item = builder.makeGetLocal(temp, type); } @@ -136,8 +130,6 @@ private: } }; -Pass *createConstHoistingPass() { - return new ConstHoisting(); -} +Pass* createConstHoistingPass() { return new ConstHoisting(); } } // namespace wasm diff --git a/src/passes/DataFlowOpts.cpp b/src/passes/DataFlowOpts.cpp index 42f01673f..3391359ef 100644 --- a/src/passes/DataFlowOpts.cpp +++ b/src/passes/DataFlowOpts.cpp @@ -24,15 +24,15 @@ // --flatten --dfo -Os // -#include "wasm.h" -#include "pass.h" -#include "wasm-builder.h" -#include "ir/flat.h" -#include "ir/utils.h" -#include "dataflow/node.h" #include "dataflow/graph.h" +#include "dataflow/node.h" #include "dataflow/users.h" #include "dataflow/utils.h" +#include "ir/flat.h" +#include "ir/utils.h" +#include "pass.h" +#include "wasm-builder.h" +#include "wasm.h" namespace wasm { @@ -59,8 +59,8 @@ struct DataFlowOpts : public WalkerPass<PostWalker<DataFlowOpts>> { workLeft.insert(node.get()); // we should try to optimize each node } while (!workLeft.empty()) { - //std::cout << "\n\ndump before work iter\n"; - //dump(graph, std::cout); + // std::cout << "\n\ndump before work iter\n"; + // dump(graph, std::cout); auto iter = workLeft.begin(); auto* node = *iter; workLeft.erase(iter); @@ -81,9 +81,11 @@ struct DataFlowOpts : public WalkerPass<PostWalker<DataFlowOpts>> { } void workOn(DataFlow::Node* node) { - if (node->isConst()) return; + if (node->isConst()) + return; // If there are no uses, there is no point to work. - if (nodeUsers.getNumUses(node) == 0) return; + if (nodeUsers.getNumUses(node) == 0) + return; // Optimize: Look for nodes that we can easily convert into // something simpler. // TODO: we can expressionify and run full normal opts on that, @@ -110,8 +112,9 @@ struct DataFlowOpts : public WalkerPass<PostWalker<DataFlowOpts>> { void optimizeExprToConstant(DataFlow::Node* node) { assert(node->isExpr()); assert(!node->isConst()); - //std::cout << "will optimize an Expr of all constant inputs. before" << '\n'; - //dump(node, std::cout); + // std::cout << "will optimize an Expr of all constant inputs. before" << + // '\n'; + // dump(node, std::cout); auto* expr = node->expr; // First, note that some of the expression's children may be // local.gets that we inferred during SSA analysis as constant. @@ -132,7 +135,8 @@ struct DataFlowOpts : public WalkerPass<PostWalker<DataFlowOpts>> { Module temp; // XXX we should copy expr here, in principle, and definitely will need to // when we do arbitrarily regenerated expressions - auto* func = Builder(temp).makeFunction("temp", std::vector<Type>{}, none, std::vector<Type>{}, expr); + auto* func = Builder(temp).makeFunction( + "temp", std::vector<Type>{}, none, std::vector<Type>{}, expr); PassRunner runner(&temp); runner.setIsNested(true); runner.add("precompute"); @@ -140,7 +144,8 @@ struct DataFlowOpts : public WalkerPass<PostWalker<DataFlowOpts>> { // Get the optimized thing auto* result = func->body; // It may not be a constant, e.g. 0 / 0 does not optimize to 0 - if (!result->is<Const>()) return; + if (!result->is<Const>()) + return; // All good, copy it. node->expr = Builder(*getModule()).makeConst(result->cast<Const>()->value); assert(node->isConst()); @@ -206,7 +211,8 @@ struct DataFlowOpts : public WalkerPass<PostWalker<DataFlowOpts>> { // should look into TODO break; } - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } } // No one is a user of this node after we replaced all the uses. @@ -244,9 +250,6 @@ struct DataFlowOpts : public WalkerPass<PostWalker<DataFlowOpts>> { } }; -Pass *createDataFlowOptsPass() { - return new DataFlowOpts(); -} +Pass* createDataFlowOptsPass() { return new DataFlowOpts(); } } // namespace wasm - diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index e4b8eef56..0c6561ef6 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -37,14 +37,14 @@ #include <unordered_map> #include <unordered_set> -#include "wasm.h" -#include "pass.h" -#include "wasm-builder.h" #include "cfg/cfg-traversal.h" #include "ir/effects.h" #include "ir/module-utils.h" +#include "pass.h" #include "passes/opt-utils.h" #include "support/sorted_vector.h" +#include "wasm-builder.h" +#include "wasm.h" namespace wasm { @@ -73,14 +73,13 @@ struct DAEBlockInfo { // If it is both read and written, we just care about the first // action (if it is read first, that's all the info we are // looking for; if it is written first, it can't be read later). - enum LocalUse { - Read, - Written - }; + enum LocalUse { Read, Written }; std::unordered_map<Index, LocalUse> localUses; }; -struct DAEScanner : public WalkerPass<CFGWalker<DAEScanner, Visitor<DAEScanner>, DAEBlockInfo>> { +struct DAEScanner + : public WalkerPass< + CFGWalker<DAEScanner, Visitor<DAEScanner>, DAEBlockInfo>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new DAEScanner(infoMap); } @@ -131,7 +130,8 @@ struct DAEScanner : public WalkerPass<CFGWalker<DAEScanner, Visitor<DAEScanner>, void doWalkFunction(Function* func) { numParams = func->getNumParams(); info = &((*infoMap)[func->name]); - CFGWalker<DAEScanner, Visitor<DAEScanner>, DAEBlockInfo>::doWalkFunction(func); + CFGWalker<DAEScanner, Visitor<DAEScanner>, DAEBlockInfo>::doWalkFunction( + func); // If there are relevant params, check if they are used. (If // we can't optimize the function anyhow, there's no point.) if (numParams > 0 && !info->hasUnseenCalls) { @@ -182,7 +182,8 @@ struct DAEScanner : public WalkerPass<CFGWalker<DAEScanner, Visitor<DAEScanner>, if (use == DAEBlockInfo::Read) { usedParams.insert(i); } - // Whether it was a read or a write, we can stop looking at that local here. + // Whether it was a read or a write, we can stop looking at that local + // here. } else { remainingIndexes.insert(i); } @@ -217,10 +218,10 @@ struct DAE : public Pass { bool iteration(PassRunner* runner, Module* module) { DAEFunctionInfoMap infoMap; - // Ensure they all exist so the parallel threads don't modify the data structure. - ModuleUtils::iterDefinedFunctions(*module, [&](Function* func) { - infoMap[func->name]; - }); + // Ensure they all exist so the parallel threads don't modify the data + // structure. + ModuleUtils::iterDefinedFunctions( + *module, [&](Function* func) { infoMap[func->name]; }); // Check the influence of the table and exports. for (auto& curr : module->exports) { if (curr->kind == ExternalKind::Function) { @@ -287,13 +288,11 @@ struct DAE : public Pass { } } if (value.type != none) { - // Success! We can just apply the constant in the function, which makes - // the parameter value unused, which lets us remove it later. + // Success! We can just apply the constant in the function, which + // makes the parameter value unused, which lets us remove it later. Builder builder(*module); func->body = builder.makeSequence( - builder.makeSetLocal(i, builder.makeConst(value)), - func->body - ); + builder.makeSetLocal(i, builder.makeConst(value)), func->body); // Mark it as unused, which we know it now is (no point to // re-scan just for that). infoMap[name].unusedParams.insert(i); @@ -308,14 +307,15 @@ struct DAE : public Pass { auto& calls = pair.second; auto* func = module->getFunction(name); auto numParams = func->getNumParams(); - if (numParams == 0) continue; + if (numParams == 0) + continue; // Iterate downwards, as we may remove more than one. Index i = numParams - 1; while (1) { if (infoMap[name].unusedParams.has(i)) { - // Great, it's not used. Check if none of the calls has a param with side - // effects, as that would prevent us removing them (flattening should - // have been done earlier). + // Great, it's not used. Check if none of the calls has a param with + // side effects, as that would prevent us removing them (flattening + // should have been done earlier). bool canRemove = true; for (auto* call : calls) { auto* operand = call->operands[i]; @@ -331,13 +331,15 @@ struct DAE : public Pass { changed.insert(func); } } - if (i == 0) break; + if (i == 0) + break; i--; } } - // We can also tell which calls have all their return values dropped. Note that we can't do this - // if we changed anything so far, as we may have modified allCalls (we can't modify a call site - // twice in one iteration, once to remove a param, once to drop the return value). + // We can also tell which calls have all their return values dropped. Note + // that we can't do this if we changed anything so far, as we may have + // modified allCalls (we can't modify a call site twice in one iteration, + // once to remove a param, once to drop the return value). if (changed.empty()) { for (auto& func : module->functions) { if (func->result == none) { @@ -363,7 +365,8 @@ struct DAE : public Pass { continue; } removeReturnValue(func.get(), calls, module); - // TODO Removing a drop may also open optimization opportunities in the callers. + // TODO Removing a drop may also open optimization opportunities in the + // callers. changed.insert(func.get()); } } @@ -391,15 +394,12 @@ private: struct LocalUpdater : public PostWalker<LocalUpdater> { Index removedIndex; Index newIndex; - LocalUpdater(Function* func, Index removedIndex, Index newIndex) : removedIndex(removedIndex), newIndex(newIndex) { + LocalUpdater(Function* func, Index removedIndex, Index newIndex) + : removedIndex(removedIndex), newIndex(newIndex) { walk(func->body); } - void visitGetLocal(GetLocal* curr) { - updateIndex(curr->index); - } - void visitSetLocal(SetLocal* curr) { - updateIndex(curr->index); - } + void visitGetLocal(GetLocal* curr) { updateIndex(curr->index); } + void visitSetLocal(SetLocal* curr) { updateIndex(curr->index); } void updateIndex(Index& index) { if (index == removedIndex) { index = newIndex; @@ -414,7 +414,8 @@ private: } } - void removeReturnValue(Function* func, std::vector<Call*>& calls, Module* module) { + void + removeReturnValue(Function* func, std::vector<Call*>& calls, Module* module) { // Clear the type, which is no longer accurate. func->type = Name(); func->result = none; @@ -430,10 +431,7 @@ private: assert(value); curr->value = nullptr; Builder builder(*module); - replaceCurrent(builder.makeSequence( - builder.makeDrop(value), - curr - )); + replaceCurrent(builder.makeSequence(builder.makeDrop(value), curr)); } } returnUpdater(func, module); // Remove any value flowing out. @@ -454,15 +452,12 @@ private: } }; -Pass *createDAEPass() { - return new DAE(); -} +Pass* createDAEPass() { return new DAE(); } -Pass *createDAEOptimizingPass() { +Pass* createDAEOptimizingPass() { auto* ret = new DAE(); ret->optimize = true; return ret; } } // namespace wasm - diff --git a/src/passes/DeadCodeElimination.cpp b/src/passes/DeadCodeElimination.cpp index a56c88929..d23713060 100644 --- a/src/passes/DeadCodeElimination.cpp +++ b/src/passes/DeadCodeElimination.cpp @@ -28,17 +28,18 @@ // have no side effects. // -#include <vector> -#include <wasm.h> -#include <pass.h> -#include <wasm-builder.h> #include <ir/block-utils.h> #include <ir/branch-utils.h> #include <ir/type-updating.h> +#include <pass.h> +#include <vector> +#include <wasm-builder.h> +#include <wasm.h> namespace wasm { -struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>> { +struct DeadCodeElimination + : public WalkerPass<PostWalker<DeadCodeElimination>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new DeadCodeElimination; } @@ -48,7 +49,8 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>> Expression* replaceCurrent(Expression* expression) { auto* old = getCurrent(); - if (old == expression) return expression; + if (old == expression) + return expression; super::replaceCurrent(expression); // also update the type updater typeUpdater.noteReplacement(old, expression); @@ -79,20 +81,17 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>> } // if a child exists and is unreachable, we can replace ourselves with it - bool isDead(Expression* child) { - return child && child->type == unreachable; - } + bool isDead(Expression* child) { return child && child->type == unreachable; } // a similar check, assumes the child exists - bool isUnreachable(Expression* child) { - return child->type == unreachable; - } + bool isUnreachable(Expression* child) { return child->type == unreachable; } // things that stop control flow void visitBreak(Break* curr) { if (isDead(curr->value)) { - // the condition is evaluated last, so if the value was unreachable, the whole thing is + // the condition is evaluated last, so if the value was unreachable, the + // whole thing is replaceCurrent(curr->value); return; } @@ -152,9 +151,7 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>> reachable = false; } - void visitUnreachable(Unreachable* curr) { - reachable = false; - } + void visitUnreachable(Unreachable* curr) { reachable = false; } void visitBlock(Block* curr) { auto& list = curr->list; @@ -175,9 +172,11 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>> reachableBreaks.erase(curr->name); } if (list.size() == 1 && isUnreachable(list[0])) { - replaceCurrent(BlockUtils::simplifyToContentsWithPossibleTypeChange(curr, this)); + replaceCurrent( + BlockUtils::simplifyToContentsWithPossibleTypeChange(curr, this)); } else { - // the block may have had a type, but can now be unreachable, which allows more reduction outside + // the block may have had a type, but can now be unreachable, which allows + // more reduction outside typeUpdater.maybeUpdateTypeToUnreachable(curr); } } @@ -186,7 +185,8 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>> if (curr->name.is()) { reachableBreaks.erase(curr->name); } - if (isUnreachable(curr->body) && !BranchUtils::BranchSeeker::hasNamed(curr->body, curr->name)) { + if (isUnreachable(curr->body) && + !BranchUtils::BranchSeeker::hasNamed(curr->body, curr->name)) { replaceCurrent(curr->body); return; } @@ -194,9 +194,11 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>> // ifs need special handling - std::vector<bool> ifStack; // stack of reachable state, for forking and joining + // stack of reachable state, for forking and joining + std::vector<bool> ifStack; - static void doAfterIfCondition(DeadCodeElimination* self, Expression** currp) { + static void doAfterIfCondition(DeadCodeElimination* self, + Expression** currp) { self->ifStack.push_back(self->reachable); } @@ -209,67 +211,108 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>> } void visitIf(If* curr) { - // the ifStack has the branch that joins us, either from before if just an if, or the ifTrue if an if-else + // the ifStack has the branch that joins us, either from before if just an + // if, or the ifTrue if an if-else reachable = reachable || ifStack.back(); ifStack.pop_back(); if (isUnreachable(curr->condition)) { replaceCurrent(curr->condition); } - // the if may have had a type, but can now be unreachable, which allows more reduction outside + // the if may have had a type, but can now be unreachable, which allows more + // reduction outside typeUpdater.maybeUpdateTypeToUnreachable(curr); } static void scan(DeadCodeElimination* self, Expression** currp) { auto* curr = *currp; if (!self->reachable) { - // convert to an unreachable safely - #define DELEGATE(CLASS_TO_VISIT) { \ - auto* parent = self->typeUpdater.parents[curr]; \ - self->typeUpdater.noteRecursiveRemoval(curr); \ - ExpressionManipulator::convert<CLASS_TO_VISIT, Unreachable>(static_cast<CLASS_TO_VISIT*>(curr)); \ - self->typeUpdater.noteAddition(curr, parent); \ - break; \ - } +// convert to an unreachable safely +#define DELEGATE(CLASS_TO_VISIT) \ + { \ + auto* parent = self->typeUpdater.parents[curr]; \ + self->typeUpdater.noteRecursiveRemoval(curr); \ + ExpressionManipulator::convert<CLASS_TO_VISIT, Unreachable>( \ + static_cast<CLASS_TO_VISIT*>(curr)); \ + self->typeUpdater.noteAddition(curr, parent); \ + break; \ + } switch (curr->_id) { - case Expression::Id::BlockId: DELEGATE(Block); - case Expression::Id::IfId: DELEGATE(If); - case Expression::Id::LoopId: DELEGATE(Loop); - case Expression::Id::BreakId: DELEGATE(Break); - case Expression::Id::SwitchId: DELEGATE(Switch); - case Expression::Id::CallId: DELEGATE(Call); - case Expression::Id::CallIndirectId: DELEGATE(CallIndirect); - case Expression::Id::GetLocalId: DELEGATE(GetLocal); - case Expression::Id::SetLocalId: DELEGATE(SetLocal); - case Expression::Id::GetGlobalId: DELEGATE(GetGlobal); - case Expression::Id::SetGlobalId: DELEGATE(SetGlobal); - case Expression::Id::LoadId: DELEGATE(Load); - case Expression::Id::StoreId: DELEGATE(Store); - case Expression::Id::ConstId: DELEGATE(Const); - case Expression::Id::UnaryId: DELEGATE(Unary); - case Expression::Id::BinaryId: DELEGATE(Binary); - case Expression::Id::SelectId: DELEGATE(Select); - case Expression::Id::DropId: DELEGATE(Drop); - case Expression::Id::ReturnId: DELEGATE(Return); - case Expression::Id::HostId: DELEGATE(Host); - case Expression::Id::NopId: DELEGATE(Nop); - case Expression::Id::UnreachableId: break; - case Expression::Id::AtomicCmpxchgId: DELEGATE(AtomicCmpxchg); - case Expression::Id::AtomicRMWId: DELEGATE(AtomicRMW); - case Expression::Id::AtomicWaitId: DELEGATE(AtomicWait); - case Expression::Id::AtomicNotifyId: DELEGATE(AtomicNotify); - case Expression::Id::SIMDExtractId: DELEGATE(SIMDExtract); - case Expression::Id::SIMDReplaceId: DELEGATE(SIMDReplace); - case Expression::Id::SIMDShuffleId: DELEGATE(SIMDShuffle); - case Expression::Id::SIMDBitselectId: DELEGATE(SIMDBitselect); - case Expression::Id::SIMDShiftId: DELEGATE(SIMDShift); - case Expression::Id::MemoryInitId: DELEGATE(MemoryInit); - case Expression::Id::DataDropId: DELEGATE(DataDrop); - case Expression::Id::MemoryCopyId: DELEGATE(MemoryCopy); - case Expression::Id::MemoryFillId: DELEGATE(MemoryFill); - case Expression::Id::InvalidId: WASM_UNREACHABLE(); - case Expression::Id::NumExpressionIds: WASM_UNREACHABLE(); + case Expression::Id::BlockId: + DELEGATE(Block); + case Expression::Id::IfId: + DELEGATE(If); + case Expression::Id::LoopId: + DELEGATE(Loop); + case Expression::Id::BreakId: + DELEGATE(Break); + case Expression::Id::SwitchId: + DELEGATE(Switch); + case Expression::Id::CallId: + DELEGATE(Call); + case Expression::Id::CallIndirectId: + DELEGATE(CallIndirect); + case Expression::Id::GetLocalId: + DELEGATE(GetLocal); + case Expression::Id::SetLocalId: + DELEGATE(SetLocal); + case Expression::Id::GetGlobalId: + DELEGATE(GetGlobal); + case Expression::Id::SetGlobalId: + DELEGATE(SetGlobal); + case Expression::Id::LoadId: + DELEGATE(Load); + case Expression::Id::StoreId: + DELEGATE(Store); + case Expression::Id::ConstId: + DELEGATE(Const); + case Expression::Id::UnaryId: + DELEGATE(Unary); + case Expression::Id::BinaryId: + DELEGATE(Binary); + case Expression::Id::SelectId: + DELEGATE(Select); + case Expression::Id::DropId: + DELEGATE(Drop); + case Expression::Id::ReturnId: + DELEGATE(Return); + case Expression::Id::HostId: + DELEGATE(Host); + case Expression::Id::NopId: + DELEGATE(Nop); + case Expression::Id::UnreachableId: + break; + case Expression::Id::AtomicCmpxchgId: + DELEGATE(AtomicCmpxchg); + case Expression::Id::AtomicRMWId: + DELEGATE(AtomicRMW); + case Expression::Id::AtomicWaitId: + DELEGATE(AtomicWait); + case Expression::Id::AtomicNotifyId: + DELEGATE(AtomicNotify); + case Expression::Id::SIMDExtractId: + DELEGATE(SIMDExtract); + case Expression::Id::SIMDReplaceId: + DELEGATE(SIMDReplace); + case Expression::Id::SIMDShuffleId: + DELEGATE(SIMDShuffle); + case Expression::Id::SIMDBitselectId: + DELEGATE(SIMDBitselect); + case Expression::Id::SIMDShiftId: + DELEGATE(SIMDShift); + case Expression::Id::MemoryInitId: + DELEGATE(MemoryInit); + case Expression::Id::DataDropId: + DELEGATE(DataDrop); + case Expression::Id::MemoryCopyId: + DELEGATE(MemoryCopy); + case Expression::Id::MemoryFillId: + DELEGATE(MemoryFill); + case Expression::Id::InvalidId: + WASM_UNREACHABLE(); + case Expression::Id::NumExpressionIds: + WASM_UNREACHABLE(); } - #undef DELEGATE +#undef DELEGATE return; } if (curr->is<If>()) { @@ -290,12 +333,12 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>> // we don't need to drop unreachable nodes Expression* drop(Expression* toDrop) { - if (toDrop->type == unreachable) return toDrop; + if (toDrop->type == unreachable) + return toDrop; return Builder(*getModule()).makeDrop(toDrop); } - template<typename T> - Expression* handleCall(T* curr) { + template<typename T> Expression* handleCall(T* curr) { for (Index i = 0; i < curr->operands.size(); i++) { if (isUnreachable(curr->operands[i])) { if (i > 0) { @@ -316,12 +359,11 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>> return curr; } - void visitCall(Call* curr) { - handleCall(curr); - } + void visitCall(Call* curr) { handleCall(curr); } void visitCallIndirect(CallIndirect* curr) { - if (handleCall(curr) != curr) return; + if (handleCall(curr) != curr) + return; if (isUnreachable(curr->target)) { auto* block = getModule()->allocator.alloc<Block>(); for (auto* operand : curr->operands) { @@ -356,56 +398,52 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>> } void visitSetLocal(SetLocal* curr) { - blockifyReachableOperands({ curr->value }, curr->type); + blockifyReachableOperands({curr->value}, curr->type); } void visitSetGlobal(SetGlobal* curr) { - blockifyReachableOperands({ curr->value }, curr->type); + blockifyReachableOperands({curr->value}, curr->type); } void visitLoad(Load* curr) { - blockifyReachableOperands({ curr->ptr }, curr->type); + blockifyReachableOperands({curr->ptr}, curr->type); } void visitStore(Store* curr) { - blockifyReachableOperands({ curr->ptr, curr->value }, curr->type); + blockifyReachableOperands({curr->ptr, curr->value}, curr->type); } void visitAtomicRMW(AtomicRMW* curr) { - blockifyReachableOperands({ curr->ptr, curr->value }, curr->type); + blockifyReachableOperands({curr->ptr, curr->value}, curr->type); } void visitAtomicCmpxchg(AtomicCmpxchg* curr) { - blockifyReachableOperands({ curr->ptr, curr->expected, curr->replacement }, curr->type); + blockifyReachableOperands({curr->ptr, curr->expected, curr->replacement}, + curr->type); } void visitUnary(Unary* curr) { - blockifyReachableOperands({ curr->value }, curr->type); + blockifyReachableOperands({curr->value}, curr->type); } void visitBinary(Binary* curr) { - blockifyReachableOperands({ curr->left, curr->right }, curr->type); + blockifyReachableOperands({curr->left, curr->right}, curr->type); } void visitSelect(Select* curr) { - blockifyReachableOperands({ curr->ifTrue, curr->ifFalse, curr->condition }, curr->type); + blockifyReachableOperands({curr->ifTrue, curr->ifFalse, curr->condition}, + curr->type); } void visitDrop(Drop* curr) { - blockifyReachableOperands({ curr->value }, curr->type); + blockifyReachableOperands({curr->value}, curr->type); } - void visitHost(Host* curr) { - handleCall(curr); - } + void visitHost(Host* curr) { handleCall(curr); } - void visitFunction(Function* curr) { - assert(reachableBreaks.size() == 0); - } + void visitFunction(Function* curr) { assert(reachableBreaks.size() == 0); } }; -Pass *createDeadCodeEliminationPass() { - return new DeadCodeElimination(); -} +Pass* createDeadCodeEliminationPass() { return new DeadCodeElimination(); } } // namespace wasm diff --git a/src/passes/Directize.cpp b/src/passes/Directize.cpp index 3d8fcdfdd..8f75c8f57 100644 --- a/src/passes/Directize.cpp +++ b/src/passes/Directize.cpp @@ -22,13 +22,13 @@ #include <unordered_map> -#include "wasm.h" -#include "pass.h" -#include "wasm-builder.h" -#include "wasm-traversal.h" #include "asm_v_wasm.h" #include "ir/table-utils.h" #include "ir/utils.h" +#include "pass.h" +#include "wasm-builder.h" +#include "wasm-traversal.h" +#include "wasm.h" namespace wasm { @@ -64,11 +64,8 @@ struct FunctionDirectizer : public WalkerPass<PostWalker<FunctionDirectizer>> { return; } // Everything looks good! - replaceCurrent(Builder(*getModule()).makeCall( - name, - curr->operands, - curr->type - )); + replaceCurrent( + Builder(*getModule()).makeCall(name, curr->operands, curr->type)); } } @@ -88,25 +85,25 @@ private: for (auto*& operand : call->operands) { operand = builder.makeDrop(operand); } - replaceCurrent( - builder.makeSequence( - builder.makeBlock(call->operands), - builder.makeUnreachable() - ) - ); + replaceCurrent(builder.makeSequence(builder.makeBlock(call->operands), + builder.makeUnreachable())); changedTypes = true; } }; struct Directize : public Pass { void run(PassRunner* runner, Module* module) override { - if (!module->table.exists) return; - if (module->table.imported()) return; + if (!module->table.exists) + return; + if (module->table.imported()) + return; for (auto& ex : module->exports) { - if (ex->kind == ExternalKind::Table) return; + if (ex->kind == ExternalKind::Table) + return; } FlatTable flatTable(module->table); - if (!flatTable.valid) return; + if (!flatTable.valid) + return; // The table exists and is constant, so this is possible. { PassRunner runner(module); @@ -119,9 +116,6 @@ struct Directize : public Pass { } // anonymous namespace -Pass *createDirectizePass() { - return new Directize(); -} +Pass* createDirectizePass() { return new Directize(); } } // namespace wasm - diff --git a/src/passes/DuplicateFunctionElimination.cpp b/src/passes/DuplicateFunctionElimination.cpp index b7fcb556c..3caa43e1d 100644 --- a/src/passes/DuplicateFunctionElimination.cpp +++ b/src/passes/DuplicateFunctionElimination.cpp @@ -20,19 +20,20 @@ // identical when finally lowered into concrete wasm code. // -#include "wasm.h" -#include "pass.h" -#include "ir/utils.h" #include "ir/function-utils.h" #include "ir/hashed.h" #include "ir/module-utils.h" +#include "ir/utils.h" +#include "pass.h" +#include "wasm.h" namespace wasm { struct FunctionReplacer : public WalkerPass<PostWalker<FunctionReplacer>> { bool isFunctionParallel() override { return true; } - FunctionReplacer(std::map<Name, Name>* replacements) : replacements(replacements) {} + FunctionReplacer(std::map<Name, Name>* replacements) + : replacements(replacements) {} FunctionReplacer* create() override { return new FunctionReplacer(replacements); @@ -51,15 +52,17 @@ private: struct DuplicateFunctionElimination : public Pass { void run(PassRunner* runner, Module* module) override { - // Multiple iterations may be necessary: A and B may be identical only after we - // see the functions C1 and C2 that they call are in fact identical. Rarely, such - // "chains" can be very long, so we limit how many we do. + // Multiple iterations may be necessary: A and B may be identical only after + // we see the functions C1 and C2 that they call are in fact identical. + // Rarely, such "chains" can be very long, so we limit how many we do. auto& options = runner->options; Index limit; if (options.optimizeLevel >= 3 || options.shrinkLevel >= 1) { limit = module->functions.size(); // no limit } else if (options.optimizeLevel >= 2) { - limit = 10; // 10 passes usually does most of the work, as this is typically logarithmic + // 10 passes usually does most of the work, as this is typically + // logarithmic + limit = 10; } else { limit = 1; } @@ -82,16 +85,19 @@ struct DuplicateFunctionElimination : public Pass { for (auto& pair : hashGroups) { auto& group = pair.second; Index size = group.size(); - if (size == 1) continue; - // The groups should be fairly small, and even if a group is large we should - // have almost all of them identical, so we should not hit actual O(N^2) - // here unless the hash is quite poor. + if (size == 1) + continue; + // The groups should be fairly small, and even if a group is large we + // should have almost all of them identical, so we should not hit actual + // O(N^2) here unless the hash is quite poor. for (Index i = 0; i < size - 1; i++) { auto* first = group[i]; - if (duplicates.count(first->name)) continue; + if (duplicates.count(first->name)) + continue; for (Index j = i + 1; j < size; j++) { auto* second = group[j]; - if (duplicates.count(second->name)) continue; + if (duplicates.count(second->name)) + continue; if (FunctionUtils::equal(first, second)) { // great, we can replace the second with the first! replacements[second->name] = first->name; @@ -104,9 +110,12 @@ struct DuplicateFunctionElimination : public Pass { if (replacements.size() > 0) { // remove the duplicates auto& v = module->functions; - v.erase(std::remove_if(v.begin(), v.end(), [&](const std::unique_ptr<Function>& curr) { - return duplicates.count(curr->name) > 0; - }), v.end()); + v.erase(std::remove_if(v.begin(), + v.end(), + [&](const std::unique_ptr<Function>& curr) { + return duplicates.count(curr->name) > 0; + }), + v.end()); module->updateMaps(); // replace direct calls PassRunner replacerRunner(module); @@ -143,7 +152,7 @@ struct DuplicateFunctionElimination : public Pass { } }; -Pass *createDuplicateFunctionEliminationPass() { +Pass* createDuplicateFunctionEliminationPass() { return new DuplicateFunctionElimination(); } diff --git a/src/passes/ExtractFunction.cpp b/src/passes/ExtractFunction.cpp index 8a97ced8e..8942771fe 100644 --- a/src/passes/ExtractFunction.cpp +++ b/src/passes/ExtractFunction.cpp @@ -18,14 +18,16 @@ // with (mostly) just the code you want to debug (function-parallel, // non-lto) passes on. -#include "wasm.h" #include "pass.h" +#include "wasm.h" namespace wasm { struct ExtractFunction : public Pass { void run(PassRunner* runner, Module* module) override { - Name name = runner->options.getArgument("extract", "ExtractFunction usage: wasm-opt --pass-arg=extract:FUNCTION_NAME"); + Name name = runner->options.getArgument( + "extract", + "ExtractFunction usage: wasm-opt --pass-arg=extract:FUNCTION_NAME"); std::cerr << "extracting " << name << "\n"; bool found = false; for (auto& func : module->functions) { @@ -58,9 +60,6 @@ struct ExtractFunction : public Pass { // declare pass -Pass *createExtractFunctionPass() { - return new ExtractFunction(); -} +Pass* createExtractFunctionPass() { return new ExtractFunction(); } } // namespace wasm - diff --git a/src/passes/Flatten.cpp b/src/passes/Flatten.cpp index df6be947d..a68fc9abe 100644 --- a/src/passes/Flatten.cpp +++ b/src/passes/Flatten.cpp @@ -18,13 +18,13 @@ // Flattens code into "Flat IR" form. See ir/flat.h. // -#include <wasm.h> -#include <pass.h> -#include <wasm-builder.h> #include <ir/branch-utils.h> #include <ir/effects.h> #include <ir/flat.h> #include <ir/utils.h> +#include <pass.h> +#include <wasm-builder.h> +#include <wasm.h> namespace wasm { @@ -43,12 +43,15 @@ namespace wasm { // Once exception is that we allow an (unreachable) node, which is used // when we move something unreachable to another place, and need a // placeholder. We will never reach that (unreachable) anyhow -struct Flatten : public WalkerPass<ExpressionStackWalker<Flatten, UnifiedExpressionVisitor<Flatten>>> { +struct Flatten + : public WalkerPass< + ExpressionStackWalker<Flatten, UnifiedExpressionVisitor<Flatten>>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new Flatten; } - // For each expression, a bunch of expressions that should execute right before it + // For each expression, a bunch of expressions that should execute right + // before it std::unordered_map<Expression*, std::vector<Expression*>> preludes; // Break values are sent through a temp local @@ -61,7 +64,9 @@ struct Flatten : public WalkerPass<ExpressionStackWalker<Flatten, UnifiedExpress if (Flat::isControlFlowStructure(curr)) { // handle control flow explicitly. our children do not have control flow, // but they do have preludes which we need to set up in the right place - assert(preludes.find(curr) == preludes.end()); // no one should have given us preludes, they are on the children + + // no one should have given us preludes, they are on the children + assert(preludes.find(curr) == preludes.end()); if (auto* block = curr->dynCast<Block>()) { // make a new list, where each item's preludes are added before it ExpressionList newList(getModule()->allocator); @@ -123,7 +128,9 @@ struct Flatten : public WalkerPass<ExpressionStackWalker<Flatten, UnifiedExpress rep = builder.makeGetLocal(temp, type); } iff->ifTrue = getPreludesWithExpression(originalIfTrue, iff->ifTrue); - if (iff->ifFalse) iff->ifFalse = getPreludesWithExpression(originalIfFalse, iff->ifFalse); + if (iff->ifFalse) + iff->ifFalse = + getPreludesWithExpression(originalIfFalse, iff->ifFalse); iff->finalize(); if (prelude) { ReFinalizeNode().visit(prelude); @@ -204,10 +211,9 @@ struct Flatten : public WalkerPass<ExpressionStackWalker<Flatten, UnifiedExpress // we don't know which break target will be hit - assign to them all auto names = BranchUtils::getUniqueTargets(sw); for (auto name : names) { - ourPreludes.push_back(builder.makeSetLocal( - getTempForBreakTarget(name, type), - builder.makeGetLocal(temp, type) - )); + ourPreludes.push_back( + builder.makeSetLocal(getTempForBreakTarget(name, type), + builder.makeGetLocal(temp, type))); } sw->value = nullptr; sw->finalize(); @@ -275,9 +281,11 @@ private: // gets an expression, either by itself, or in a block with some // preludes (which we use up) for another expression before it - Expression* getPreludesWithExpression(Expression* preluder, Expression* after) { + Expression* getPreludesWithExpression(Expression* preluder, + Expression* after) { auto iter = preludes.find(preluder); - if (iter == preludes.end()) return after; + if (iter == preludes.end()) + return after; // we have preludes auto& thePreludes = iter->second; auto* ret = Builder(*getModule()).makeBlock(thePreludes); @@ -294,14 +302,12 @@ private: if (iter != breakTemps.end()) { return iter->second; } else { - return breakTemps[name] = Builder(*getModule()).addVar(getFunction(), type); + return breakTemps[name] = + Builder(*getModule()).addVar(getFunction(), type); } } }; -Pass *createFlattenPass() { - return new Flatten(); -} +Pass* createFlattenPass() { return new Flatten(); } } // namespace wasm - diff --git a/src/passes/FuncCastEmulation.cpp b/src/passes/FuncCastEmulation.cpp index 36a2819b2..904b8a202 100644 --- a/src/passes/FuncCastEmulation.cpp +++ b/src/passes/FuncCastEmulation.cpp @@ -27,12 +27,12 @@ // This should work even with dynamic linking, however, the number of // params must be identical, i.e., the "ABI" must match. -#include <wasm.h> -#include <wasm-builder.h> #include <asm_v_wasm.h> +#include <ir/literal-utils.h> #include <pass.h> +#include <wasm-builder.h> #include <wasm-emscripten.h> -#include <ir/literal-utils.h> +#include <wasm.h> namespace wasm { @@ -54,10 +54,8 @@ static Expression* toABI(Expression* value, Module* module) { break; } case f32: { - value = builder.makeUnary( - ExtendUInt32, - builder.makeUnary(ReinterpretFloat32, value) - ); + value = builder.makeUnary(ExtendUInt32, + builder.makeUnary(ReinterpretFloat32, value)); break; } case f64: { @@ -70,10 +68,7 @@ static Expression* toABI(Expression* value, Module* module) { } case none: { // the value is none, but we need a value here - value = builder.makeSequence( - value, - LiteralUtils::makeZero(i64, *module) - ); + value = builder.makeSequence(value, LiteralUtils::makeZero(i64, *module)); break; } case unreachable: { @@ -97,10 +92,8 @@ static Expression* fromABI(Expression* value, Type type, Module* module) { break; } case f32: { - value = builder.makeUnary( - ReinterpretInt32, - builder.makeUnary(WrapInt64, value) - ); + value = builder.makeUnary(ReinterpretInt32, + builder.makeUnary(WrapInt64, value)); break; } case f64: { @@ -122,7 +115,8 @@ static Expression* fromABI(Expression* value, Type type, Module* module) { return value; } -struct ParallelFuncCastEmulation : public WalkerPass<PostWalker<ParallelFuncCastEmulation>> { +struct ParallelFuncCastEmulation + : public WalkerPass<PostWalker<ParallelFuncCastEmulation>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new ParallelFuncCastEmulation(ABIType); } @@ -131,8 +125,8 @@ struct ParallelFuncCastEmulation : public WalkerPass<PostWalker<ParallelFuncCast void visitCallIndirect(CallIndirect* curr) { if (curr->operands.size() > NUM_PARAMS) { - Fatal() << "FuncCastEmulation::NUM_PARAMS needs to be at least " << - curr->operands.size(); + Fatal() << "FuncCastEmulation::NUM_PARAMS needs to be at least " + << curr->operands.size(); } for (Expression*& operand : curr->operands) { operand = toABI(operand, getModule()); @@ -197,7 +191,8 @@ private: Name makeThunk(Name name, Module* module) { Name thunk = std::string("byn$fpcast-emu$") + name.str; if (module->getFunctionOrNull(thunk)) { - Fatal() << "FuncCastEmulation::makeThunk seems a thunk name already in use. Was the pass already run on this code?"; + Fatal() << "FuncCastEmulation::makeThunk seems a thunk name already in " + "use. Was the pass already run on this code?"; } // The item in the table may be a function or a function import. auto* func = module->getFunction(name); @@ -206,28 +201,25 @@ private: Builder builder(*module); std::vector<Expression*> callOperands; for (Index i = 0; i < params.size(); i++) { - callOperands.push_back(fromABI(builder.makeGetLocal(i, i64), params[i], module)); + callOperands.push_back( + fromABI(builder.makeGetLocal(i, i64), params[i], module)); } auto* call = builder.makeCall(name, callOperands, type); std::vector<Type> thunkParams; for (Index i = 0; i < NUM_PARAMS; i++) { thunkParams.push_back(i64); } - auto* thunkFunc = builder.makeFunction( - thunk, - std::move(thunkParams), - i64, - {}, // no vars - toABI(call, module) - ); + auto* thunkFunc = builder.makeFunction(thunk, + std::move(thunkParams), + i64, + {}, // no vars + toABI(call, module)); thunkFunc->type = ABIType; module->addFunction(thunkFunc); return thunk; } }; -Pass* createFuncCastEmulationPass() { - return new FuncCastEmulation(); -} +Pass* createFuncCastEmulationPass() { return new FuncCastEmulation(); } } // namespace wasm diff --git a/src/passes/I64ToI32Lowering.cpp b/src/passes/I64ToI32Lowering.cpp index 731b42d3a..e2d3cc414 100644 --- a/src/passes/I64ToI32Lowering.cpp +++ b/src/passes/I64ToI32Lowering.cpp @@ -21,32 +21,31 @@ // global. // -#include <algorithm> -#include "wasm.h" -#include "pass.h" -#include "emscripten-optimizer/istring.h" -#include "support/name.h" -#include "wasm-builder.h" #include "abi/js.h" +#include "asmjs/shared-constants.h" +#include "emscripten-optimizer/istring.h" #include "ir/flat.h" #include "ir/iteration.h" #include "ir/memory-utils.h" #include "ir/module-utils.h" #include "ir/names.h" -#include "asmjs/shared-constants.h" +#include "pass.h" +#include "support/name.h" +#include "wasm-builder.h" +#include "wasm.h" +#include <algorithm> namespace wasm { -static Name makeHighName(Name n) { - return std::string(n.c_str()) + "$hi"; -} +static Name makeHighName(Name n) { return std::string(n.c_str()) + "$hi"; } struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { struct TempVar { - TempVar(Index idx, Type ty, I64ToI32Lowering& pass) : - idx(idx), pass(pass), moved(false), ty(ty) {} + TempVar(Index idx, Type ty, I64ToI32Lowering& pass) + : idx(idx), pass(pass), moved(false), ty(ty) {} - TempVar(TempVar&& other) : idx(other), pass(other.pass), moved(false), ty(other.ty) { + TempVar(TempVar&& other) + : idx(other), pass(other.pass), moved(false), ty(other.ty) { assert(!other.moved); other.moved = true; } @@ -54,7 +53,8 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { TempVar& operator=(TempVar&& rhs) { assert(!rhs.moved); // free overwritten idx - if (!moved) freeIdx(); + if (!moved) + freeIdx(); idx = rhs.idx; rhs.moved = true; moved = false; @@ -62,7 +62,8 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { } ~TempVar() { - if (!moved) freeIdx(); + if (!moved) + freeIdx(); } bool operator==(const TempVar& rhs) { @@ -81,8 +82,9 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { private: void freeIdx() { - auto &freeList = pass.freeTemps[(int) ty]; - assert(std::find(freeList.begin(), freeList.end(), idx) == freeList.end()); + auto& freeList = pass.freeTemps[(int)ty]; + assert(std::find(freeList.begin(), freeList.end(), idx) == + freeList.end()); freeList.push_back(idx); } @@ -96,19 +98,22 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { // TODO: allow module-level transformations in parallel passes bool isFunctionParallel() override { return false; } - Pass* create() override { - return new I64ToI32Lowering; - } + Pass* create() override { return new I64ToI32Lowering; } void doWalkModule(Module* module) { - if (!builder) builder = make_unique<Builder>(*module); + if (!builder) + builder = make_unique<Builder>(*module); // add new globals for high bits for (size_t i = 0, globals = module->globals.size(); i < globals; ++i) { auto* curr = module->globals[i].get(); - if (curr->type != i64) continue; + if (curr->type != i64) + continue; originallyI64Globals.insert(curr->name); curr->type = i32; - auto* high = builder->makeGlobal(makeHighName(curr->name), i32, builder->makeConst(Literal(int32_t(0))), Builder::Mutable); + auto* high = builder->makeGlobal(makeHighName(curr->name), + i32, + builder->makeConst(Literal(int32_t(0))), + Builder::Mutable); module->addGlobal(high); if (curr->imported()) { Fatal() << "TODO: imported i64 globals"; @@ -157,7 +162,8 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { void doWalkFunction(Function* func) { Flat::verifyFlatness(func); // create builder here if this is first entry to module for this object - if (!builder) builder = make_unique<Builder>(*getModule()); + if (!builder) + builder = make_unique<Builder>(*getModule()); indexMap.clear(); highBitVars.clear(); freeTemps.clear(); @@ -174,9 +180,10 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { Name lowName = oldFunc->getLocalName(i); Name highName = makeHighName(lowName); Type paramType = oldFunc->getLocalType(i); - auto builderFunc = (i < oldFunc->getVarIndexBase()) ? - Builder::addParam : - static_cast<Index (*)(Function*, Name, Type)>(Builder::addVar); + auto builderFunc = + (i < oldFunc->getVarIndexBase()) + ? Builder::addParam + : static_cast<Index (*)(Function*, Name, Type)>(Builder::addVar); if (paramType == i64) { builderFunc(func, lowName, i32); builderFunc(func, highName, i32); @@ -201,14 +208,9 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { if (hasOutParam(func->body)) { TempVar highBits = fetchOutParam(func->body); TempVar lowBits = getTemp(); - SetLocal* setLow = builder->makeSetLocal( - lowBits, - func->body - ); + SetLocal* setLow = builder->makeSetLocal(lowBits, func->body); SetGlobal* setHigh = builder->makeSetGlobal( - INT64_TO_32_HIGH_BITS, - builder->makeGetLocal(highBits, i32) - ); + INT64_TO_32_HIGH_BITS, builder->makeGetLocal(highBits, i32)); GetLocal* getLow = builder->makeGetLocal(lowBits, i32); func->body = builder->blockify(setLow, setHigh, getLow); } @@ -223,7 +225,8 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { template<typename T> using BuilderFunc = std::function<T*(std::vector<Expression*>&, Type)>; - // Fixes up a call. If we performed fixups, returns the call; otherwise returns nullptr; + // Fixes up a call. If we performed fixups, returns the call; otherwise + // returns nullptr; template<typename T> T* visitGenericCall(T* curr, BuilderFunc<T> callBuilder) { bool fixed = false; @@ -233,7 +236,7 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { if (hasOutParam(e)) { TempVar argHighBits = fetchOutParam(e); args.push_back(builder->makeGetLocal(argHighBits, i32)); - fixed = true; + fixed = true; } } if (curr->type != i64) { @@ -244,14 +247,9 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { TempVar lowBits = getTemp(); TempVar highBits = getTemp(); auto* call = callBuilder(args, i32); - SetLocal* doCall = builder->makeSetLocal( - lowBits, - call - ); + SetLocal* doCall = builder->makeSetLocal(lowBits, call); SetLocal* setHigh = builder->makeSetLocal( - highBits, - builder->makeGetGlobal(INT64_TO_32_HIGH_BITS, i32) - ); + highBits, builder->makeGetGlobal(INT64_TO_32_HIGH_BITS, i32)); GetLocal* getLow = builder->makeGetLocal(lowBits, i32); Block* result = builder->blockify(doCall, setHigh, getLow); setOutParam(result, std::move(highBits)); @@ -260,11 +258,9 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { } void visitCall(Call* curr) { auto* fixedCall = visitGenericCall<Call>( - curr, - [&](std::vector<Expression*>& args, Type ty) { + curr, [&](std::vector<Expression*>& args, Type ty) { return builder->makeCall(curr->target, args, ty); - } - ); + }); // If this was to an import, we need to call the legal version. This assumes // that legalize-js-interface has been run before. if (fixedCall && getModule()->getFunction(fixedCall->target)->imported()) { @@ -275,16 +271,10 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { void visitCallIndirect(CallIndirect* curr) { visitGenericCall<CallIndirect>( - curr, - [&](std::vector<Expression*>& args, Type ty) { + curr, [&](std::vector<Expression*>& args, Type ty) { return builder->makeCallIndirect( - curr->fullType, - curr->target, - args, - ty - ); - } - ); + curr->fullType, curr->target, args, ty); + }); } void visitGetLocal(GetLocal* curr) { @@ -297,13 +287,8 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { } curr->type = i32; TempVar highBits = getTemp(); - SetLocal *setHighBits = builder->makeSetLocal( - highBits, - builder->makeGetLocal( - mappedIndex + 1, - i32 - ) - ); + SetLocal* setHighBits = builder->makeSetLocal( + highBits, builder->makeGetLocal(mappedIndex + 1, i32)); Block* result = builder->blockify(setHighBits, curr); replaceCurrent(result); setOutParam(result, std::move(highBits)); @@ -315,9 +300,7 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { curr->type = i32; SetLocal* setLow = builder->makeSetLocal(tmp, curr); SetLocal* setHigh = builder->makeSetLocal( - curr->index + 1, - builder->makeGetLocal(highBits, i32) - ); + curr->index + 1, builder->makeGetLocal(highBits, i32)); GetLocal* getLow = builder->makeGetLocal(tmp, i32); Block* result = builder->blockify(setLow, setHigh, getLow); replaceCurrent(result); @@ -337,44 +320,40 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { return; } TempVar highBits = fetchOutParam(curr->value); - auto* setHigh = builder->makeSetLocal( - mappedIndex + 1, - builder->makeGetLocal(highBits, i32) - ); + auto* setHigh = builder->makeSetLocal(mappedIndex + 1, + builder->makeGetLocal(highBits, i32)); Block* result = builder->blockify(curr, setHigh); replaceCurrent(result); } void visitGetGlobal(GetGlobal* curr) { - if (!getFunction()) return; // if in a global init, skip - we already handled that. - if (!originallyI64Globals.count(curr->name)) return; + if (!getFunction()) + return; // if in a global init, skip - we already handled that. + if (!originallyI64Globals.count(curr->name)) + return; curr->type = i32; TempVar highBits = getTemp(); - SetLocal *setHighBits = builder->makeSetLocal( - highBits, - builder->makeGetGlobal( - makeHighName(curr->name), - i32 - ) - ); + SetLocal* setHighBits = builder->makeSetLocal( + highBits, builder->makeGetGlobal(makeHighName(curr->name), i32)); Block* result = builder->blockify(setHighBits, curr); replaceCurrent(result); setOutParam(result, std::move(highBits)); } void visitSetGlobal(SetGlobal* curr) { - if (!originallyI64Globals.count(curr->name)) return; - if (handleUnreachable(curr)) return; + if (!originallyI64Globals.count(curr->name)) + return; + if (handleUnreachable(curr)) + return; TempVar highBits = fetchOutParam(curr->value); auto* setHigh = builder->makeSetGlobal( - makeHighName(curr->name), - builder->makeGetLocal(highBits, i32) - ); + makeHighName(curr->name), builder->makeGetLocal(highBits, i32)); replaceCurrent(builder->makeSequence(curr, setHigh)); } void visitLoad(Load* curr) { - if (curr->type != i64) return; + if (curr->type != i64) + return; assert(!curr->isAtomic && "atomic load not implemented"); TempVar lowBits = getTemp(); TempVar highBits = getTemp(); @@ -384,46 +363,37 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { if (curr->bytes == 8) { loadHigh = builder->makeSetLocal( highBits, - builder->makeLoad( - 4, - curr->signed_, - curr->offset + 4, - 1, - builder->makeGetLocal(ptrTemp, i32), - i32 - ) - ); + builder->makeLoad(4, + curr->signed_, + curr->offset + 4, + 1, + builder->makeGetLocal(ptrTemp, i32), + i32)); } else if (curr->signed_) { loadHigh = builder->makeSetLocal( highBits, - builder->makeBinary( - ShrSInt32, - builder->makeGetLocal(lowBits, i32), - builder->makeConst(Literal(int32_t(31))) - ) - ); + builder->makeBinary(ShrSInt32, + builder->makeGetLocal(lowBits, i32), + builder->makeConst(Literal(int32_t(31))))); } else { - loadHigh = builder->makeSetLocal( - highBits, - builder->makeConst(Literal(int32_t(0))) - ); + loadHigh = builder->makeSetLocal(highBits, + builder->makeConst(Literal(int32_t(0)))); } curr->type = i32; curr->bytes = std::min(curr->bytes, uint8_t(4)); curr->align = std::min(uint32_t(curr->align), uint32_t(4)); curr->ptr = builder->makeGetLocal(ptrTemp, i32); - Block* result = builder->blockify( - setPtr, - builder->makeSetLocal(lowBits, curr), - loadHigh, - builder->makeGetLocal(lowBits, i32) - ); + Block* result = builder->blockify(setPtr, + builder->makeSetLocal(lowBits, curr), + loadHigh, + builder->makeGetLocal(lowBits, i32)); replaceCurrent(result); setOutParam(result, std::move(highBits)); } void visitStore(Store* curr) { - if (!hasOutParam(curr->value)) return; + if (!hasOutParam(curr->value)) + return; assert(curr->offset + 4 > curr->offset); assert(!curr->isAtomic && "atomic store not implemented"); TempVar highBits = fetchOutParam(curr->value); @@ -436,14 +406,13 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { SetLocal* setPtr = builder->makeSetLocal(ptrTemp, curr->ptr); curr->ptr = builder->makeGetLocal(ptrTemp, i32); curr->finalize(); - Store* storeHigh = builder->makeStore( - 4, - curr->offset + 4, - 1, - builder->makeGetLocal(ptrTemp, i32), - builder->makeGetLocal(highBits, i32), - i32 - ); + Store* storeHigh = + builder->makeStore(4, + curr->offset + 4, + 1, + builder->makeGetLocal(ptrTemp, i32), + builder->makeGetLocal(highBits, i32), + i32); replaceCurrent(builder->blockify(setPtr, curr, storeHigh)); } } @@ -457,18 +426,17 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { } void visitConst(Const* curr) { - if (!getFunction()) return; // if in a global init, skip - we already handled that. - if (curr->type != i64) return; + if (!getFunction()) + return; // if in a global init, skip - we already handled that. + if (curr->type != i64) + return; TempVar highBits = getTemp(); - Const* lowVal = builder->makeConst( - Literal(int32_t(curr->value.geti64() & 0xffffffff)) - ); - SetLocal* setHigh = builder->makeSetLocal( - highBits, - builder->makeConst( - Literal(int32_t(uint64_t(curr->value.geti64()) >> 32)) - ) - ); + Const* lowVal = + builder->makeConst(Literal(int32_t(curr->value.geti64() & 0xffffffff))); + SetLocal* setHigh = + builder->makeSetLocal(highBits, + builder->makeConst(Literal( + int32_t(uint64_t(curr->value.geti64()) >> 32)))); Block* result = builder->blockify(setHigh, lowVal); setOutParam(result, std::move(highBits)); replaceCurrent(result); @@ -480,11 +448,7 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { auto* result = builder->makeUnary( EqZInt32, builder->makeBinary( - OrInt32, - curr->value, - builder->makeGetLocal(highBits, i32) - ) - ); + OrInt32, curr->value, builder->makeGetLocal(highBits, i32))); replaceCurrent(result); } @@ -493,8 +457,7 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { TempVar highBits = getTemp(); Block* result = builder->blockify( builder->makeSetLocal(highBits, builder->makeConst(Literal(int32_t(0)))), - curr->value - ); + curr->value); setOutParam(result, std::move(highBits)); replaceCurrent(result); } @@ -506,18 +469,12 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { SetLocal* setLow = builder->makeSetLocal(lowBits, curr->value); SetLocal* setHigh = builder->makeSetLocal( highBits, - builder->makeBinary( - ShrSInt32, - builder->makeGetLocal(lowBits, i32), - builder->makeConst(Literal(int32_t(31))) - ) - ); + builder->makeBinary(ShrSInt32, + builder->makeGetLocal(lowBits, i32), + builder->makeConst(Literal(int32_t(31))))); - Block* result = builder->blockify( - setLow, - setHigh, - builder->makeGetLocal(lowBits, i32) - ); + Block* result = + builder->blockify(setLow, setHigh, builder->makeGetLocal(lowBits, i32)); setOutParam(result, std::move(highBits)); replaceCurrent(result); @@ -533,14 +490,16 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { // Assume that the wasm file assumes the address 0 is invalid and roundtrip // our f64 through memory at address 0 TempVar highBits = getTemp(); - Block *result = builder->blockify( - builder->makeCall(ABI::wasm2js::SCRATCH_STORE_F64, { curr->value }, none), + Block* result = builder->blockify( + builder->makeCall(ABI::wasm2js::SCRATCH_STORE_F64, {curr->value}, none), builder->makeSetLocal( highBits, - builder->makeCall(ABI::wasm2js::SCRATCH_LOAD_I32, { builder->makeConst(Literal(int32_t(1))) }, i32) - ), - builder->makeCall(ABI::wasm2js::SCRATCH_LOAD_I32, { builder->makeConst(Literal(int32_t(0))) }, i32) - ); + builder->makeCall(ABI::wasm2js::SCRATCH_LOAD_I32, + {builder->makeConst(Literal(int32_t(1)))}, + i32)), + builder->makeCall(ABI::wasm2js::SCRATCH_LOAD_I32, + {builder->makeConst(Literal(int32_t(0)))}, + i32)); setOutParam(result, std::move(highBits)); replaceCurrent(result); MemoryUtils::ensureExists(getModule()->memory); @@ -551,17 +510,21 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { // Assume that the wasm file assumes the address 0 is invalid and roundtrip // our i64 through memory at address 0 TempVar highBits = fetchOutParam(curr->value); - Block *result = builder->blockify( - builder->makeCall(ABI::wasm2js::SCRATCH_STORE_I32, { builder->makeConst(Literal(int32_t(0))), curr->value }, none), - builder->makeCall(ABI::wasm2js::SCRATCH_STORE_I32, { builder->makeConst(Literal(int32_t(1))), builder->makeGetLocal(highBits, i32) }, none), - builder->makeCall(ABI::wasm2js::SCRATCH_LOAD_F64, {}, f64) - ); + Block* result = builder->blockify( + builder->makeCall(ABI::wasm2js::SCRATCH_STORE_I32, + {builder->makeConst(Literal(int32_t(0))), curr->value}, + none), + builder->makeCall(ABI::wasm2js::SCRATCH_STORE_I32, + {builder->makeConst(Literal(int32_t(1))), + builder->makeGetLocal(highBits, i32)}, + none), + builder->makeCall(ABI::wasm2js::SCRATCH_LOAD_F64, {}, f64)); replaceCurrent(result); MemoryUtils::ensureExists(getModule()->memory); ABI::wasm2js::ensureScratchMemoryHelpers(getModule()); } - void lowerTruncFloatToInt(Unary *curr) { + void lowerTruncFloatToInt(Unary* curr) { // hiBits = if abs(f) >= 1.0 { // if f > 0.0 { // (unsigned) min( @@ -584,9 +547,9 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { switch (curr->op) { case TruncSFloat32ToInt64: case TruncUFloat32ToInt64: { - litZero = Literal((float) 0); - litOne = Literal((float) 1); - u32Max = Literal(((float) UINT_MAX) + 1); + litZero = Literal((float)0); + litOne = Literal((float)1); + u32Max = Literal(((float)UINT_MAX) + 1); trunc = TruncUFloat32ToInt32; convert = ConvertUInt32ToFloat32; localType = f32; @@ -602,9 +565,9 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { } case TruncSFloat64ToInt64: case TruncUFloat64ToInt64: { - litZero = Literal((double) 0); - litOne = Literal((double) 1); - u32Max = Literal(((double) UINT_MAX) + 1); + litZero = Literal((double)0); + litOne = Literal((double)1); + u32Max = Literal(((double)UINT_MAX) + 1); trunc = TruncUFloat64ToInt32; convert = ConvertUInt32ToFloat64; localType = f64; @@ -618,74 +581,63 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { sub = SubFloat64; break; } - default: abort(); + default: + abort(); } TempVar f = getTemp(localType); TempVar highBits = getTemp(); - Expression *gtZeroBranch = builder->makeBinary( - min, - builder->makeUnary( - floor, - builder->makeBinary( - div, - builder->makeGetLocal(f, localType), - builder->makeConst(u32Max) - ) - ), - builder->makeBinary(sub, builder->makeConst(u32Max), builder->makeConst(litOne)) - ); - Expression *ltZeroBranch = builder->makeUnary( - ceil, + Expression* gtZeroBranch = builder->makeBinary( + min, + builder->makeUnary( + floor, + builder->makeBinary(div, + builder->makeGetLocal(f, localType), + builder->makeConst(u32Max))), + builder->makeBinary( + sub, builder->makeConst(u32Max), builder->makeConst(litOne))); + Expression* ltZeroBranch = builder->makeUnary( + ceil, + builder->makeBinary( + div, builder->makeBinary( - div, - builder->makeBinary( - sub, - builder->makeGetLocal(f, localType), - builder->makeUnary(convert, - builder->makeUnary(trunc, builder->makeGetLocal(f, localType)) - ) - ), - builder->makeConst(u32Max) - ) - ); - - If *highBitsCalc = builder->makeIf( + sub, + builder->makeGetLocal(f, localType), + builder->makeUnary( + convert, + builder->makeUnary(trunc, builder->makeGetLocal(f, localType)))), + builder->makeConst(u32Max))); + + If* highBitsCalc = builder->makeIf( builder->makeBinary( - gt, - builder-> makeGetLocal(f, localType), - builder->makeConst(litZero) - ), + gt, builder->makeGetLocal(f, localType), builder->makeConst(litZero)), builder->makeUnary(trunc, gtZeroBranch), - builder->makeUnary(trunc, ltZeroBranch) - ); - If *highBitsVal = builder->makeIf( + builder->makeUnary(trunc, ltZeroBranch)); + If* highBitsVal = builder->makeIf( builder->makeBinary( ge, builder->makeUnary(abs, builder->makeGetLocal(f, localType)), - builder->makeConst(litOne) - ), + builder->makeConst(litOne)), highBitsCalc, - builder->makeConst(Literal(int32_t(0))) - ); - Block *result = builder->blockify( + builder->makeConst(Literal(int32_t(0)))); + Block* result = builder->blockify( builder->makeSetLocal(f, curr->value), builder->makeSetLocal(highBits, highBitsVal), - builder->makeUnary(trunc, builder->makeGetLocal(f, localType)) - ); + builder->makeUnary(trunc, builder->makeGetLocal(f, localType))); setOutParam(result, std::move(highBits)); replaceCurrent(result); } - void lowerConvertIntToFloat(Unary *curr) { + void lowerConvertIntToFloat(Unary* curr) { // Here the same strategy as `emcc` is taken which takes the two halves of // the 64-bit integer and creates a mathematical expression using float // arithmetic to reassemble the final floating point value. // // For example for i64 -> f32 we generate: // - // ((double) (unsigned) lowBits) + ((double) U32_MAX) * ((double) (int) highBits) + // ((double) (unsigned) lowBits) + + // ((double) U32_MAX) * ((double) (int) highBits) // // Mostly just shuffling things around here with coercions and whatnot! // Note though that all arithmetic is done with f64 to have as much @@ -704,31 +656,23 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { case ConvertUInt64ToFloat64: convertHigh = ConvertUInt32ToFloat64; break; - default: abort(); + default: + abort(); } - Expression *result = builder->blockify( + Expression* result = builder->blockify( builder->makeSetLocal(lowBits, curr->value), - builder->makeSetLocal( - highResult, - builder->makeConst(Literal(int32_t(0))) - ), + builder->makeSetLocal(highResult, + builder->makeConst(Literal(int32_t(0)))), builder->makeBinary( AddFloat64, - builder->makeUnary( - ConvertUInt32ToFloat64, - builder->makeGetLocal(lowBits, i32) - ), + builder->makeUnary(ConvertUInt32ToFloat64, + builder->makeGetLocal(lowBits, i32)), builder->makeBinary( MulFloat64, builder->makeConst(Literal((double)UINT_MAX + 1)), - builder->makeUnary( - convertHigh, - builder->makeGetLocal(highBits, i32) - ) - ) - ) - ); + builder->makeUnary(convertHigh, + builder->makeGetLocal(highBits, i32))))); switch (curr->op) { case ConvertSInt64ToFloat32: @@ -736,52 +680,43 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { result = builder->makeUnary(DemoteFloat64, result); break; } - default: break; + default: + break; } replaceCurrent(result); } void lowerCountZeros(Unary* curr) { - auto lower = [&](Block* result, UnaryOp op32, TempVar&& first, TempVar&& second) { + auto lower = [&](Block* result, + UnaryOp op32, + TempVar&& first, + TempVar&& second) { TempVar highResult = getTemp(); TempVar firstResult = getTemp(); SetLocal* setFirst = builder->makeSetLocal( firstResult, - builder->makeUnary(op32, builder->makeGetLocal(first, i32)) - ); + builder->makeUnary(op32, builder->makeGetLocal(first, i32))); - Binary* check = builder->makeBinary( - EqInt32, - builder->makeGetLocal(firstResult, i32), - builder->makeConst(Literal(int32_t(32))) - ); + Binary* check = + builder->makeBinary(EqInt32, + builder->makeGetLocal(firstResult, i32), + builder->makeConst(Literal(int32_t(32)))); If* conditional = builder->makeIf( check, builder->makeBinary( AddInt32, builder->makeUnary(op32, builder->makeGetLocal(second, i32)), - builder->makeConst(Literal(int32_t(32))) - ), - builder->makeGetLocal(firstResult, i32) - ); + builder->makeConst(Literal(int32_t(32)))), + builder->makeGetLocal(firstResult, i32)); SetLocal* setHigh = builder->makeSetLocal( - highResult, - builder->makeConst(Literal(int32_t(0))) - ); + highResult, builder->makeConst(Literal(int32_t(0)))); setOutParam(result, std::move(highResult)); - replaceCurrent( - builder->blockify( - result, - setFirst, - setHigh, - conditional - ) - ); + replaceCurrent(builder->blockify(result, setFirst, setHigh, conditional)); }; TempVar highBits = fetchOutParam(curr->value); @@ -820,32 +755,54 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { case ConvertSInt64ToFloat64: case ConvertUInt64ToFloat32: case ConvertUInt64ToFloat64: - case ReinterpretInt64: return true; - default: return false; + case ReinterpretInt64: + return true; + default: + return false; } } void visitUnary(Unary* curr) { - if (!unaryNeedsLowering(curr->op)) return; - if (handleUnreachable(curr)) return; + if (!unaryNeedsLowering(curr->op)) + return; + if (handleUnreachable(curr)) + return; assert(hasOutParam(curr->value) || curr->type == i64 || curr->type == f64); switch (curr->op) { case ClzInt64: - case CtzInt64: lowerCountZeros(curr); break; - case EqZInt64: lowerEqZInt64(curr); break; - case ExtendSInt32: lowerExtendSInt32(curr); break; - case ExtendUInt32: lowerExtendUInt32(curr); break; - case WrapInt64: lowerWrapInt64(curr); break; - case ReinterpretFloat64: lowerReinterpretFloat64(curr); break; - case ReinterpretInt64: lowerReinterpretInt64(curr); break; + case CtzInt64: + lowerCountZeros(curr); + break; + case EqZInt64: + lowerEqZInt64(curr); + break; + case ExtendSInt32: + lowerExtendSInt32(curr); + break; + case ExtendUInt32: + lowerExtendUInt32(curr); + break; + case WrapInt64: + lowerWrapInt64(curr); + break; + case ReinterpretFloat64: + lowerReinterpretFloat64(curr); + break; + case ReinterpretInt64: + lowerReinterpretInt64(curr); + break; case TruncSFloat32ToInt64: case TruncUFloat32ToInt64: case TruncSFloat64ToInt64: - case TruncUFloat64ToInt64: lowerTruncFloatToInt(curr); break; + case TruncUFloat64ToInt64: + lowerTruncFloatToInt(curr); + break; case ConvertSInt64ToFloat32: case ConvertSInt64ToFloat64: case ConvertUInt64ToFloat32: - case ConvertUInt64ToFloat64: lowerConvertIntToFloat(curr); break; + case ConvertUInt64ToFloat64: + lowerConvertIntToFloat(curr); + break; case PopcntInt64: std::cerr << "i64.popcnt should already be removed" << std::endl; WASM_UNREACHABLE(); @@ -855,117 +812,104 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { } } - Block* lowerAdd(Block* result, TempVar&& leftLow, TempVar&& leftHigh, - TempVar&& rightLow, TempVar&& rightHigh) { + Block* lowerAdd(Block* result, + TempVar&& leftLow, + TempVar&& leftHigh, + TempVar&& rightLow, + TempVar&& rightHigh) { TempVar lowResult = getTemp(); TempVar highResult = getTemp(); SetLocal* addLow = builder->makeSetLocal( lowResult, - builder->makeBinary( - AddInt32, - builder->makeGetLocal(leftLow, i32), - builder->makeGetLocal(rightLow, i32) - ) - ); + builder->makeBinary(AddInt32, + builder->makeGetLocal(leftLow, i32), + builder->makeGetLocal(rightLow, i32))); SetLocal* addHigh = builder->makeSetLocal( highResult, - builder->makeBinary( - AddInt32, - builder->makeGetLocal(leftHigh, i32), - builder->makeGetLocal(rightHigh, i32) - ) - ); + builder->makeBinary(AddInt32, + builder->makeGetLocal(leftHigh, i32), + builder->makeGetLocal(rightHigh, i32))); SetLocal* carryBit = builder->makeSetLocal( highResult, - builder->makeBinary( - AddInt32, - builder->makeGetLocal(highResult, i32), - builder->makeConst(Literal(int32_t(1))) - ) - ); - If* checkOverflow = builder->makeIf( - builder->makeBinary( - LtUInt32, - builder->makeGetLocal(lowResult, i32), - builder->makeGetLocal(rightLow, i32) - ), - carryBit - ); + builder->makeBinary(AddInt32, + builder->makeGetLocal(highResult, i32), + builder->makeConst(Literal(int32_t(1))))); + If* checkOverflow = + builder->makeIf(builder->makeBinary(LtUInt32, + builder->makeGetLocal(lowResult, i32), + builder->makeGetLocal(rightLow, i32)), + carryBit); GetLocal* getLow = builder->makeGetLocal(lowResult, i32); result = builder->blockify(result, addLow, addHigh, checkOverflow, getLow); setOutParam(result, std::move(highResult)); return result; } - Block* lowerSub(Block* result, TempVar&& leftLow, TempVar&& leftHigh, - TempVar&& rightLow, TempVar&& rightHigh) { + Block* lowerSub(Block* result, + TempVar&& leftLow, + TempVar&& leftHigh, + TempVar&& rightLow, + TempVar&& rightHigh) { TempVar lowResult = getTemp(); TempVar highResult = getTemp(); TempVar borrow = getTemp(); SetLocal* subLow = builder->makeSetLocal( lowResult, - builder->makeBinary( - SubInt32, - builder->makeGetLocal(leftLow, i32), - builder->makeGetLocal(rightLow, i32) - ) - ); + builder->makeBinary(SubInt32, + builder->makeGetLocal(leftLow, i32), + builder->makeGetLocal(rightLow, i32))); SetLocal* borrowBit = builder->makeSetLocal( borrow, - builder->makeBinary( - LtUInt32, - builder->makeGetLocal(leftLow, i32), - builder->makeGetLocal(rightLow, i32) - ) - ); + builder->makeBinary(LtUInt32, + builder->makeGetLocal(leftLow, i32), + builder->makeGetLocal(rightLow, i32))); SetLocal* subHigh1 = builder->makeSetLocal( highResult, - builder->makeBinary( - AddInt32, - builder->makeGetLocal(borrow, i32), - builder->makeGetLocal(rightHigh, i32) - ) - ); + builder->makeBinary(AddInt32, + builder->makeGetLocal(borrow, i32), + builder->makeGetLocal(rightHigh, i32))); SetLocal* subHigh2 = builder->makeSetLocal( highResult, - builder->makeBinary( - SubInt32, - builder->makeGetLocal(leftHigh, i32), - builder->makeGetLocal(highResult, i32) - ) - ); + builder->makeBinary(SubInt32, + builder->makeGetLocal(leftHigh, i32), + builder->makeGetLocal(highResult, i32))); GetLocal* getLow = builder->makeGetLocal(lowResult, i32); - result = builder->blockify(result, subLow, borrowBit, subHigh1, subHigh2, getLow); + result = + builder->blockify(result, subLow, borrowBit, subHigh1, subHigh2, getLow); setOutParam(result, std::move(highResult)); return result; } - Block* lowerBitwise(BinaryOp op, Block* result, TempVar&& leftLow, - TempVar&& leftHigh, TempVar&& rightLow, + Block* lowerBitwise(BinaryOp op, + Block* result, + TempVar&& leftLow, + TempVar&& leftHigh, + TempVar&& rightLow, TempVar&& rightHigh) { BinaryOp op32; switch (op) { - case AndInt64: op32 = AndInt32; break; - case OrInt64: op32 = OrInt32; break; - case XorInt64: op32 = XorInt32; break; - default: abort(); + case AndInt64: + op32 = AndInt32; + break; + case OrInt64: + op32 = OrInt32; + break; + case XorInt64: + op32 = XorInt32; + break; + default: + abort(); } result = builder->blockify( result, builder->makeSetLocal( rightHigh, - builder->makeBinary( - op32, - builder->makeGetLocal(leftHigh, i32), - builder->makeGetLocal(rightHigh, i32) - ) - ), - builder->makeBinary( - op32, - builder->makeGetLocal(leftLow, i32), - builder->makeGetLocal(rightLow, i32) - ) - ); + builder->makeBinary(op32, + builder->makeGetLocal(leftHigh, i32), + builder->makeGetLocal(rightHigh, i32))), + builder->makeBinary(op32, + builder->makeGetLocal(leftLow, i32), + builder->makeGetLocal(rightLow, i32))); setOutParam(result, std::move(rightHigh)); return result; } @@ -974,14 +918,10 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { return builder->blockify( builder->makeSetLocal( highBits, - builder->makeBinary( - ShlInt32, - builder->makeGetLocal(leftLow, i32), - builder->makeGetLocal(shift, i32) - ) - ), - builder->makeConst(Literal(int32_t(0))) - ); + builder->makeBinary(ShlInt32, + builder->makeGetLocal(leftLow, i32), + builder->makeGetLocal(shift, i32))), + builder->makeConst(Literal(int32_t(0)))); } // a >> b where `b` >= 32 @@ -994,58 +934,43 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { return builder->blockify( builder->makeSetLocal( highBits, - builder->makeBinary( - ShrSInt32, - builder->makeGetLocal(leftHigh, i32), - builder->makeConst(Literal(int32_t(31))) - ) - ), - builder->makeBinary( - ShrSInt32, - builder->makeGetLocal(leftHigh, i32), - builder->makeGetLocal(shift, i32) - ) - ); + builder->makeBinary(ShrSInt32, + builder->makeGetLocal(leftHigh, i32), + builder->makeConst(Literal(int32_t(31))))), + builder->makeBinary(ShrSInt32, + builder->makeGetLocal(leftHigh, i32), + builder->makeGetLocal(shift, i32))); } Block* makeLargeShrU(Index highBits, Index leftHigh, Index shift) { return builder->blockify( builder->makeSetLocal(highBits, builder->makeConst(Literal(int32_t(0)))), - builder->makeBinary( - ShrUInt32, - builder->makeGetLocal(leftHigh, i32), - builder->makeGetLocal(shift, i32) - ) - ); + builder->makeBinary(ShrUInt32, + builder->makeGetLocal(leftHigh, i32), + builder->makeGetLocal(shift, i32))); } - Block* makeSmallShl(Index highBits, Index leftLow, Index leftHigh, - Index shift, Binary* shiftMask, Binary* widthLessShift) { + Block* makeSmallShl(Index highBits, + Index leftLow, + Index leftHigh, + Index shift, + Binary* shiftMask, + Binary* widthLessShift) { Binary* shiftedInBits = builder->makeBinary( AndInt32, shiftMask, builder->makeBinary( - ShrUInt32, - builder->makeGetLocal(leftLow, i32), - widthLessShift - ) - ); - Binary* shiftHigh = builder->makeBinary( - ShlInt32, - builder->makeGetLocal(leftHigh, i32), - builder->makeGetLocal(shift, i32) - ); + ShrUInt32, builder->makeGetLocal(leftLow, i32), widthLessShift)); + Binary* shiftHigh = + builder->makeBinary(ShlInt32, + builder->makeGetLocal(leftHigh, i32), + builder->makeGetLocal(shift, i32)); return builder->blockify( builder->makeSetLocal( - highBits, - builder->makeBinary(OrInt32, shiftedInBits, shiftHigh) - ), - builder->makeBinary( - ShlInt32, - builder->makeGetLocal(leftLow, i32), - builder->makeGetLocal(shift, i32) - ) - ); + highBits, builder->makeBinary(OrInt32, shiftedInBits, shiftHigh)), + builder->makeBinary(ShlInt32, + builder->makeGetLocal(leftLow, i32), + builder->makeGetLocal(shift, i32))); } // a >> b where `b` < 32 @@ -1054,66 +979,58 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { // // hi = leftHigh >> b // lo = (leftLow >>> b) | (leftHigh << (32 - b)) - Block* makeSmallShrS(Index highBits, Index leftLow, Index leftHigh, - Index shift, Binary* shiftMask, Binary* widthLessShift) { + Block* makeSmallShrS(Index highBits, + Index leftLow, + Index leftHigh, + Index shift, + Binary* shiftMask, + Binary* widthLessShift) { Binary* shiftedInBits = builder->makeBinary( ShlInt32, builder->makeBinary( - AndInt32, - shiftMask, - builder->makeGetLocal(leftHigh, i32) - ), - widthLessShift - ); - Binary* shiftLow = builder->makeBinary( - ShrUInt32, - builder->makeGetLocal(leftLow, i32), - builder->makeGetLocal(shift, i32) - ); + AndInt32, shiftMask, builder->makeGetLocal(leftHigh, i32)), + widthLessShift); + Binary* shiftLow = builder->makeBinary(ShrUInt32, + builder->makeGetLocal(leftLow, i32), + builder->makeGetLocal(shift, i32)); return builder->blockify( builder->makeSetLocal( highBits, - builder->makeBinary( - ShrSInt32, - builder->makeGetLocal(leftHigh, i32), - builder->makeGetLocal(shift, i32) - ) - ), - builder->makeBinary(OrInt32, shiftedInBits, shiftLow) - ); + builder->makeBinary(ShrSInt32, + builder->makeGetLocal(leftHigh, i32), + builder->makeGetLocal(shift, i32))), + builder->makeBinary(OrInt32, shiftedInBits, shiftLow)); } - Block* makeSmallShrU(Index highBits, Index leftLow, Index leftHigh, - Index shift, Binary* shiftMask, Binary* widthLessShift) { + Block* makeSmallShrU(Index highBits, + Index leftLow, + Index leftHigh, + Index shift, + Binary* shiftMask, + Binary* widthLessShift) { Binary* shiftedInBits = builder->makeBinary( ShlInt32, builder->makeBinary( - AndInt32, - shiftMask, - builder->makeGetLocal(leftHigh, i32) - ), - widthLessShift - ); - Binary* shiftLow = builder->makeBinary( - ShrUInt32, - builder->makeGetLocal(leftLow, i32), - builder->makeGetLocal(shift, i32) - ); + AndInt32, shiftMask, builder->makeGetLocal(leftHigh, i32)), + widthLessShift); + Binary* shiftLow = builder->makeBinary(ShrUInt32, + builder->makeGetLocal(leftLow, i32), + builder->makeGetLocal(shift, i32)); return builder->blockify( builder->makeSetLocal( highBits, - builder->makeBinary( - ShrUInt32, - builder->makeGetLocal(leftHigh, i32), - builder->makeGetLocal(shift, i32) - ) - ), - builder->makeBinary(OrInt32, shiftedInBits, shiftLow) - ); + builder->makeBinary(ShrUInt32, + builder->makeGetLocal(leftHigh, i32), + builder->makeGetLocal(shift, i32))), + builder->makeBinary(OrInt32, shiftedInBits, shiftLow)); } - Block* lowerShift(BinaryOp op, Block* result, TempVar&& leftLow, - TempVar&& leftHigh, TempVar&& rightLow, TempVar&& rightHigh) { + Block* lowerShift(BinaryOp op, + Block* result, + TempVar&& leftLow, + TempVar&& leftHigh, + TempVar&& rightLow, + TempVar&& rightHigh) { assert(op == ShlInt64 || op == ShrUInt64 || op == ShrSInt64); // shift left lowered as: // if 32 <= rightLow % 64: @@ -1125,191 +1042,192 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { TempVar shift = getTemp(); SetLocal* setShift = builder->makeSetLocal( shift, - builder->makeBinary( - AndInt32, - builder->makeGetLocal(rightLow, i32), - builder->makeConst(Literal(int32_t(32 - 1))) - ) - ); + builder->makeBinary(AndInt32, + builder->makeGetLocal(rightLow, i32), + builder->makeConst(Literal(int32_t(32 - 1))))); Binary* isLargeShift = builder->makeBinary( LeUInt32, builder->makeConst(Literal(int32_t(32))), - builder->makeBinary( - AndInt32, - builder->makeGetLocal(rightLow, i32), - builder->makeConst(Literal(int32_t(64 - 1))) - ) - ); + builder->makeBinary(AndInt32, + builder->makeGetLocal(rightLow, i32), + builder->makeConst(Literal(int32_t(64 - 1))))); Block* largeShiftBlock; switch (op) { case ShlInt64: - largeShiftBlock = makeLargeShl(rightHigh, leftLow, shift); break; + largeShiftBlock = makeLargeShl(rightHigh, leftLow, shift); + break; case ShrSInt64: - largeShiftBlock = makeLargeShrS(rightHigh, leftHigh, shift); break; + largeShiftBlock = makeLargeShrS(rightHigh, leftHigh, shift); + break; case ShrUInt64: - largeShiftBlock = makeLargeShrU(rightHigh, leftHigh, shift); break; - default: abort(); + largeShiftBlock = makeLargeShrU(rightHigh, leftHigh, shift); + break; + default: + abort(); } Binary* shiftMask = builder->makeBinary( SubInt32, - builder->makeBinary( - ShlInt32, - builder->makeConst(Literal(int32_t(1))), - builder->makeGetLocal(shift, i32) - ), - builder->makeConst(Literal(int32_t(1))) - ); - Binary* widthLessShift = builder->makeBinary( - SubInt32, - builder->makeConst(Literal(int32_t(32))), - builder->makeGetLocal(shift, i32) - ); + builder->makeBinary(ShlInt32, + builder->makeConst(Literal(int32_t(1))), + builder->makeGetLocal(shift, i32)), + builder->makeConst(Literal(int32_t(1)))); + Binary* widthLessShift = + builder->makeBinary(SubInt32, + builder->makeConst(Literal(int32_t(32))), + builder->makeGetLocal(shift, i32)); Block* smallShiftBlock; - switch(op) { + switch (op) { case ShlInt64: { - smallShiftBlock = makeSmallShl(rightHigh, leftLow, leftHigh, - shift, shiftMask, widthLessShift); + smallShiftBlock = makeSmallShl( + rightHigh, leftLow, leftHigh, shift, shiftMask, widthLessShift); break; } case ShrSInt64: { - smallShiftBlock = makeSmallShrS(rightHigh, leftLow, leftHigh, - shift, shiftMask, widthLessShift); + smallShiftBlock = makeSmallShrS( + rightHigh, leftLow, leftHigh, shift, shiftMask, widthLessShift); break; } case ShrUInt64: { - smallShiftBlock = makeSmallShrU(rightHigh, leftLow, leftHigh, - shift, shiftMask, widthLessShift); + smallShiftBlock = makeSmallShrU( + rightHigh, leftLow, leftHigh, shift, shiftMask, widthLessShift); break; } - default: abort(); + default: + abort(); } - If* ifLargeShift = builder->makeIf( - isLargeShift, - largeShiftBlock, - smallShiftBlock - ); + If* ifLargeShift = + builder->makeIf(isLargeShift, largeShiftBlock, smallShiftBlock); result = builder->blockify(result, setShift, ifLargeShift); setOutParam(result, std::move(rightHigh)); return result; } - Block* lowerEq(Block* result, TempVar&& leftLow, TempVar&& leftHigh, - TempVar&& rightLow, TempVar&& rightHigh) { + Block* lowerEq(Block* result, + TempVar&& leftLow, + TempVar&& leftHigh, + TempVar&& rightLow, + TempVar&& rightHigh) { return builder->blockify( result, builder->makeBinary( AndInt32, - builder->makeBinary( - EqInt32, - builder->makeGetLocal(leftLow, i32), - builder->makeGetLocal(rightLow, i32) - ), - builder->makeBinary( - EqInt32, - builder->makeGetLocal(leftHigh, i32), - builder->makeGetLocal(rightHigh, i32) - ) - ) - ); + builder->makeBinary(EqInt32, + builder->makeGetLocal(leftLow, i32), + builder->makeGetLocal(rightLow, i32)), + builder->makeBinary(EqInt32, + builder->makeGetLocal(leftHigh, i32), + builder->makeGetLocal(rightHigh, i32)))); } - Block* lowerNe(Block* result, TempVar&& leftLow, TempVar&& leftHigh, - TempVar&& rightLow, TempVar&& rightHigh) { + Block* lowerNe(Block* result, + TempVar&& leftLow, + TempVar&& leftHigh, + TempVar&& rightLow, + TempVar&& rightHigh) { return builder->blockify( result, builder->makeBinary( OrInt32, - builder->makeBinary( - NeInt32, - builder->makeGetLocal(leftLow, i32), - builder->makeGetLocal(rightLow, i32) - ), - builder->makeBinary( - NeInt32, - builder->makeGetLocal(leftHigh, i32), - builder->makeGetLocal(rightHigh, i32) - ) - ) - ); + builder->makeBinary(NeInt32, + builder->makeGetLocal(leftLow, i32), + builder->makeGetLocal(rightLow, i32)), + builder->makeBinary(NeInt32, + builder->makeGetLocal(leftHigh, i32), + builder->makeGetLocal(rightHigh, i32)))); } - Block* lowerUComp(BinaryOp op, Block* result, TempVar&& leftLow, - TempVar&& leftHigh, TempVar&& rightLow, + Block* lowerUComp(BinaryOp op, + Block* result, + TempVar&& leftLow, + TempVar&& leftHigh, + TempVar&& rightLow, TempVar&& rightHigh) { BinaryOp highOp, lowOp; switch (op) { - case LtUInt64: highOp = LtUInt32; lowOp = LtUInt32; break; - case LeUInt64: highOp = LtUInt32; lowOp = LeUInt32; break; - case GtUInt64: highOp = GtUInt32; lowOp = GtUInt32; break; - case GeUInt64: highOp = GtUInt32; lowOp = GeUInt32; break; - default: abort(); + case LtUInt64: + highOp = LtUInt32; + lowOp = LtUInt32; + break; + case LeUInt64: + highOp = LtUInt32; + lowOp = LeUInt32; + break; + case GtUInt64: + highOp = GtUInt32; + lowOp = GtUInt32; + break; + case GeUInt64: + highOp = GtUInt32; + lowOp = GeUInt32; + break; + default: + abort(); } - Binary* compHigh = builder->makeBinary( - highOp, - builder->makeGetLocal(leftHigh, i32), - builder->makeGetLocal(rightHigh, i32) - ); - Binary* eqHigh = builder->makeBinary( - EqInt32, - builder->makeGetLocal(leftHigh, i32), - builder->makeGetLocal(rightHigh, i32) - ); - Binary* compLow = builder->makeBinary( - lowOp, - builder->makeGetLocal(leftLow, i32), - builder->makeGetLocal(rightLow, i32) - ); + Binary* compHigh = + builder->makeBinary(highOp, + builder->makeGetLocal(leftHigh, i32), + builder->makeGetLocal(rightHigh, i32)); + Binary* eqHigh = builder->makeBinary(EqInt32, + builder->makeGetLocal(leftHigh, i32), + builder->makeGetLocal(rightHigh, i32)); + Binary* compLow = builder->makeBinary(lowOp, + builder->makeGetLocal(leftLow, i32), + builder->makeGetLocal(rightLow, i32)); return builder->blockify( result, builder->makeBinary( - OrInt32, - compHigh, - builder->makeBinary(AndInt32, eqHigh, compLow) - ) - ); + OrInt32, compHigh, builder->makeBinary(AndInt32, eqHigh, compLow))); } - Block* lowerSComp(BinaryOp op, Block* result, TempVar&& leftLow, - TempVar&& leftHigh, TempVar&& rightLow, - TempVar&& rightHigh) { + Block* lowerSComp(BinaryOp op, + Block* result, + TempVar&& leftLow, + TempVar&& leftHigh, + TempVar&& rightLow, + TempVar&& rightHigh) { BinaryOp highOp1, highOp2, lowOp; switch (op) { - case LtSInt64: highOp1 = LtSInt32; highOp2 = LeSInt32; lowOp = GeUInt32; break; - case LeSInt64: highOp1 = LtSInt32; highOp2 = LeSInt32; lowOp = GtUInt32; break; - case GtSInt64: highOp1 = GtSInt32; highOp2 = GeSInt32; lowOp = LeUInt32; break; - case GeSInt64: highOp1 = GtSInt32; highOp2 = GeSInt32; lowOp = LtUInt32; break; - default: abort(); + case LtSInt64: + highOp1 = LtSInt32; + highOp2 = LeSInt32; + lowOp = GeUInt32; + break; + case LeSInt64: + highOp1 = LtSInt32; + highOp2 = LeSInt32; + lowOp = GtUInt32; + break; + case GtSInt64: + highOp1 = GtSInt32; + highOp2 = GeSInt32; + lowOp = LeUInt32; + break; + case GeSInt64: + highOp1 = GtSInt32; + highOp2 = GeSInt32; + lowOp = LtUInt32; + break; + default: + abort(); } - Binary* compHigh1 = builder->makeBinary( - highOp1, - builder->makeGetLocal(leftHigh, i32), - builder->makeGetLocal(rightHigh, i32) - ); - Binary* compHigh2 = builder->makeBinary( - highOp2, - builder->makeGetLocal(leftHigh, i32), - builder->makeGetLocal(rightHigh, i32) - ); - Binary* compLow = builder->makeBinary( - lowOp, - builder->makeGetLocal(leftLow, i32), - builder->makeGetLocal(rightLow, i32) - ); - If* lowIf = builder->makeIf( - compLow, - builder->makeConst(Literal(int32_t(0))), - builder->makeConst(Literal(int32_t(1))) - ); + Binary* compHigh1 = + builder->makeBinary(highOp1, + builder->makeGetLocal(leftHigh, i32), + builder->makeGetLocal(rightHigh, i32)); + Binary* compHigh2 = + builder->makeBinary(highOp2, + builder->makeGetLocal(leftHigh, i32), + builder->makeGetLocal(rightHigh, i32)); + Binary* compLow = builder->makeBinary(lowOp, + builder->makeGetLocal(leftLow, i32), + builder->makeGetLocal(rightLow, i32)); + If* lowIf = builder->makeIf(compLow, + builder->makeConst(Literal(int32_t(0))), + builder->makeConst(Literal(int32_t(1)))); If* highIf2 = builder->makeIf( - compHigh2, - lowIf, - builder->makeConst(Literal(int32_t(0))) - ); + compHigh2, lowIf, builder->makeConst(Literal(int32_t(0)))); If* highIf1 = builder->makeIf( - compHigh1, - builder->makeConst(Literal(int32_t(1))), - highIf2 - ); + compHigh1, builder->makeConst(Literal(int32_t(1))), highIf2); return builder->blockify(result, highIf1); } @@ -1339,14 +1257,18 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { case GtSInt64: case GtUInt64: case GeSInt64: - case GeUInt64: return true; - default: return false; + case GeUInt64: + return true; + default: + return false; } } void visitBinary(Binary* curr) { - if (handleUnreachable(curr)) return; - if (!binaryNeedsLowering(curr->op)) return; + if (handleUnreachable(curr)) + return; + if (!binaryNeedsLowering(curr->op)) + return; // left and right reachable, lower normally TempVar leftLow = getTemp(); TempVar leftHigh = fetchOutParam(curr->left); @@ -1357,15 +1279,19 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { Block* result = builder->blockify(setLeft, setRight); switch (curr->op) { case AddInt64: { - replaceCurrent( - lowerAdd(result, std::move(leftLow), std::move(leftHigh), - std::move(rightLow), std::move(rightHigh))); + replaceCurrent(lowerAdd(result, + std::move(leftLow), + std::move(leftHigh), + std::move(rightLow), + std::move(rightHigh))); break; } case SubInt64: { - replaceCurrent( - lowerSub(result, std::move(leftLow), std::move(leftHigh), - std::move(rightLow), std::move(rightHigh))); + replaceCurrent(lowerSub(result, + std::move(leftLow), + std::move(leftHigh), + std::move(rightLow), + std::move(rightHigh))); break; } case MulInt64: @@ -1375,59 +1301,69 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { case RemUInt64: case RotLInt64: case RotRInt64: - std::cerr << "should have been removed by now " << curr->op << std::endl; + std::cerr << "should have been removed by now " << curr->op + << std::endl; WASM_UNREACHABLE(); case AndInt64: case OrInt64: case XorInt64: { - replaceCurrent( - lowerBitwise(curr->op, result, std::move(leftLow), - std::move(leftHigh), std::move(rightLow), - std::move(rightHigh)) - ); + replaceCurrent(lowerBitwise(curr->op, + result, + std::move(leftLow), + std::move(leftHigh), + std::move(rightLow), + std::move(rightHigh))); break; } case ShlInt64: case ShrSInt64: case ShrUInt64: { - replaceCurrent( - lowerShift(curr->op, result, std::move(leftLow), std::move(leftHigh), - std::move(rightLow), std::move(rightHigh)) - ); + replaceCurrent(lowerShift(curr->op, + result, + std::move(leftLow), + std::move(leftHigh), + std::move(rightLow), + std::move(rightHigh))); break; } case EqInt64: { - replaceCurrent( - lowerEq(result, std::move(leftLow), std::move(leftHigh), - std::move(rightLow), std::move(rightHigh)) - ); + replaceCurrent(lowerEq(result, + std::move(leftLow), + std::move(leftHigh), + std::move(rightLow), + std::move(rightHigh))); break; } case NeInt64: { - replaceCurrent( - lowerNe(result, std::move(leftLow), std::move(leftHigh), - std::move(rightLow), std::move(rightHigh)) - ); + replaceCurrent(lowerNe(result, + std::move(leftLow), + std::move(leftHigh), + std::move(rightLow), + std::move(rightHigh))); break; } case LtSInt64: case LeSInt64: case GtSInt64: case GeSInt64: - replaceCurrent( - lowerSComp(curr->op, result, std::move(leftLow), std::move(leftHigh), - std::move(rightLow), std::move(rightHigh)) - ); - break; + replaceCurrent(lowerSComp(curr->op, + result, + std::move(leftLow), + std::move(leftHigh), + std::move(rightLow), + std::move(rightHigh))); + break; case LtUInt64: case LeUInt64: case GtUInt64: case GeUInt64: { - replaceCurrent( - lowerUComp(curr->op, result, std::move(leftLow), std::move(leftHigh), - std::move(rightLow), std::move(rightHigh)) - ); + replaceCurrent(lowerUComp(curr->op, + result, + std::move(leftLow), + std::move(leftHigh), + std::move(rightLow), + std::move(rightHigh))); break; } default: { @@ -1438,7 +1374,8 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { } void visitSelect(Select* curr) { - if (handleUnreachable(curr)) return; + if (handleUnreachable(curr)) + return; if (!hasOutParam(curr->ifTrue)) { assert(!hasOutParam(curr->ifFalse)); return; @@ -1452,40 +1389,33 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> { builder->makeSetLocal( lowBits, builder->makeSelect( - builder->makeGetLocal(cond, i32), - curr->ifTrue, - curr->ifFalse - ) - ), + builder->makeGetLocal(cond, i32), curr->ifTrue, curr->ifFalse)), builder->makeSetLocal( highBits, builder->makeSelect( builder->makeGetLocal(cond, i32), builder->makeGetLocal(fetchOutParam(curr->ifTrue), i32), - builder->makeGetLocal(fetchOutParam(curr->ifFalse), i32) - ) - ), - builder->makeGetLocal(lowBits, i32) - ); + builder->makeGetLocal(fetchOutParam(curr->ifFalse), i32))), + builder->makeGetLocal(lowBits, i32)); setOutParam(result, std::move(highBits)); replaceCurrent(result); } void visitDrop(Drop* curr) { - if (!hasOutParam(curr->value)) return; + if (!hasOutParam(curr->value)) + return; // free temp var fetchOutParam(curr->value); } void visitReturn(Return* curr) { - if (!hasOutParam(curr->value)) return; + if (!hasOutParam(curr->value)) + return; TempVar lowBits = getTemp(); TempVar highBits = fetchOutParam(curr->value); SetLocal* setLow = builder->makeSetLocal(lowBits, curr->value); SetGlobal* setHigh = builder->makeSetGlobal( - INT64_TO_32_HIGH_BITS, - builder->makeGetLocal(highBits, i32) - ); + INT64_TO_32_HIGH_BITS, builder->makeGetLocal(highBits, i32)); curr->value = builder->makeGetLocal(lowBits, i32); Block* result = builder->blockify(setLow, setHigh, curr); replaceCurrent(result); @@ -1502,7 +1432,7 @@ private: TempVar getTemp(Type ty = i32) { Index ret; - auto &freeList = freeTemps[(int) ty]; + auto& freeList = freeTemps[(int)ty]; if (freeList.size() > 0) { ret = freeList.back(); freeList.pop_back(); @@ -1538,7 +1468,8 @@ private: // unconditionally before themselves, so it is not valid for an if, // in particular. bool handleUnreachable(Expression* curr) { - if (curr->type != unreachable) return false; + if (curr->type != unreachable) + return false; std::vector<Expression*> children; bool hasUnreachable = false; for (auto* child : ChildIterator(curr)) { @@ -1549,7 +1480,8 @@ private: } children.push_back(child); } - if (!hasUnreachable) return false; + if (!hasUnreachable) + return false; // This has an unreachable child, so we can replace it with // the children. auto* block = builder->makeBlock(children); @@ -1559,8 +1491,6 @@ private: } }; -Pass *createI64ToI32LoweringPass() { - return new I64ToI32Lowering(); -} +Pass* createI64ToI32LoweringPass() { return new I64ToI32Lowering(); } -} +} // namespace wasm diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp index f801662e0..681109af8 100644 --- a/src/passes/Inlining.cpp +++ b/src/passes/Inlining.cpp @@ -32,14 +32,14 @@ #include <atomic> -#include "wasm.h" -#include "pass.h" -#include "wasm-builder.h" #include "ir/literal-utils.h" #include "ir/module-utils.h" #include "ir/utils.h" #include "parsing.h" +#include "pass.h" #include "passes/opt-utils.h" +#include "wasm-builder.h" +#include "wasm.h" namespace wasm { @@ -79,25 +79,30 @@ struct FunctionInfo { bool worthInlining(PassOptions& options) { // if it's big, it's just not worth doing (TODO: investigate more) - if (size > FLEXIBLE_SIZE_LIMIT) return false; + if (size > FLEXIBLE_SIZE_LIMIT) + return false; // if it's so small we have a guarantee that after we optimize the // size will not increase, inline it - if (size <= INLINING_OPTIMIZING_WILL_DECREASE_SIZE_LIMIT) return true; + if (size <= INLINING_OPTIMIZING_WILL_DECREASE_SIZE_LIMIT) + return true; // if it has one use, then inlining it would likely reduce code size // since we are just moving code around, + optimizing, so worth it // if small enough that we are pretty sure its ok - if (calls == 1 && !usedGlobally && size <= CAREFUL_SIZE_LIMIT) return true; + if (calls == 1 && !usedGlobally && size <= CAREFUL_SIZE_LIMIT) + return true; // more than one use, so we can't eliminate it after inlining, // so only worth it if we really care about speed and don't care // about size, and if it's lightweight so a good candidate for // speeding us up. - return options.optimizeLevel >= 3 && options.shrinkLevel == 0 && lightweight; + return options.optimizeLevel >= 3 && options.shrinkLevel == 0 && + lightweight; } }; typedef std::unordered_map<Name, FunctionInfo> NameInfoMap; -struct FunctionInfoScanner : public WalkerPass<PostWalker<FunctionInfoScanner>> { +struct FunctionInfoScanner + : public WalkerPass<PostWalker<FunctionInfoScanner>> { bool isFunctionParallel() override { return true; } FunctionInfoScanner(NameInfoMap* infos) : infos(infos) {} @@ -112,7 +117,8 @@ struct FunctionInfoScanner : public WalkerPass<PostWalker<FunctionInfoScanner>> } void visitCall(Call* curr) { - assert(infos->count(curr->target) > 0); // can't add a new element in parallel + // can't add a new element in parallel + assert(infos->count(curr->target) > 0); (*infos)[curr->target].calls++; // having a call is not lightweight (*infos)[getFunction()->name].lightweight = false; @@ -130,12 +136,14 @@ struct InliningAction { Expression** callSite; Function* contents; - InliningAction(Expression** callSite, Function* contents) : callSite(callSite), contents(contents) {} + InliningAction(Expression** callSite, Function* contents) + : callSite(callSite), contents(contents) {} }; struct InliningState { std::unordered_set<Name> worthInlining; - std::unordered_map<Name, std::vector<InliningAction>> actionsForFunction; // function name => actions that can be performed in it + // function name => actions that can be performed in it + std::unordered_map<Name, std::vector<InliningAction>> actionsForFunction; }; struct Planner : public WalkerPass<PostWalker<Planner>> { @@ -143,30 +151,27 @@ struct Planner : public WalkerPass<PostWalker<Planner>> { Planner(InliningState* state) : state(state) {} - Planner* create() override { - return new Planner(state); - } + Planner* create() override { return new Planner(state); } void visitCall(Call* curr) { // plan to inline if we know this is valid to inline, and if the call is // actually performed - if it is dead code, it's pointless to inline. // we also cannot inline ourselves. - if (state->worthInlining.count(curr->target) && - curr->type != unreachable && + if (state->worthInlining.count(curr->target) && curr->type != unreachable && curr->target != getFunction()->name) { - // nest the call in a block. that way the location of the pointer to the call will not - // change even if we inline multiple times into the same function, otherwise - // call1(call2()) might be a problem + // nest the call in a block. that way the location of the pointer to the + // call will not change even if we inline multiple times into the same + // function, otherwise call1(call2()) might be a problem auto* block = Builder(*getModule()).makeBlock(curr); replaceCurrent(block); - assert(state->actionsForFunction.count(getFunction()->name) > 0); // can't add a new element in parallel - state->actionsForFunction[getFunction()->name].emplace_back(&block->list[0], getModule()->getFunction(curr->target)); + // can't add a new element in parallel + assert(state->actionsForFunction.count(getFunction()->name) > 0); + state->actionsForFunction[getFunction()->name].emplace_back( + &block->list[0], getModule()->getFunction(curr->target)); } } - void doWalkFunction(Function* func) { - walk(func->body); - } + void doWalkFunction(Function* func) { walk(func->body); } private: InliningState* state; @@ -174,7 +179,8 @@ private: // Core inlining logic. Modifies the outside function (adding locals as // needed), and returns the inlined code. -static Expression* doInlining(Module* module, Function* into, InliningAction& action) { +static Expression* +doInlining(Module* module, Function* into, InliningAction& action) { Function* from = action.contents; auto* call = (*action.callSite)->cast<Call>(); Builder builder(*module); @@ -204,11 +210,15 @@ static Expression* doInlining(Module* module, Function* into, InliningAction& ac } // assign the operands into the params for (Index i = 0; i < from->params.size(); i++) { - block->list.push_back(builder.makeSetLocal(updater.localMapping[i], call->operands[i])); + block->list.push_back( + builder.makeSetLocal(updater.localMapping[i], call->operands[i])); } - // zero out the vars (as we may be in a loop, and may depend on their zero-init value + // zero out the vars (as we may be in a loop, and may depend on their + // zero-init value for (Index i = 0; i < from->vars.size(); i++) { - block->list.push_back(builder.makeSetLocal(updater.localMapping[from->getVarIndexBase() + i], LiteralUtils::makeZero(from->vars[i], *module))); + block->list.push_back( + builder.makeSetLocal(updater.localMapping[from->getVarIndexBase() + i], + LiteralUtils::makeZero(from->vars[i], *module))); } // generate and update the inlined contents auto* contents = ExpressionManipulator::copy(from->body, *module); @@ -246,7 +256,8 @@ struct Inlining : public Pass { // can look like it is worth inlining) while (iterationNumber <= numFunctions) { #ifdef INLINING_DEBUG - std::cout << "inlining loop iter " << iterationNumber << " (numFunctions: " << numFunctions << ")\n"; + std::cout << "inlining loop iter " << iterationNumber + << " (numFunctions: " << numFunctions << ")\n"; #endif calculateInfos(module); if (!iteration(runner, module)) { @@ -258,7 +269,8 @@ struct Inlining : public Pass { void calculateInfos(Module* module) { infos.clear(); - // fill in info, as we operate on it in parallel (each function to its own entry) + // fill in info, as we operate on it in parallel (each function to its own + // entry) for (auto& func : module->functions) { infos[func->name]; } @@ -288,8 +300,10 @@ struct Inlining : public Pass { state.worthInlining.insert(func->name); } }); - if (state.worthInlining.size() == 0) return false; - // fill in actionsForFunction, as we operate on it in parallel (each function to its own entry) + if (state.worthInlining.size() == 0) + return false; + // fill in actionsForFunction, as we operate on it in parallel (each + // function to its own entry) for (auto& func : module->functions) { state.actionsForFunction[func->name]; } @@ -302,20 +316,23 @@ struct Inlining : public Pass { } // perform inlinings TODO: parallelize std::unordered_map<Name, Index> inlinedUses; // how many uses we inlined - std::unordered_set<Function*> inlinedInto; // which functions were inlined into + // which functions were inlined into + std::unordered_set<Function*> inlinedInto; for (auto& func : module->functions) { // if we've inlined a function, don't inline into it in this iteration, // avoid risk of races // note that we do not risk stalling progress, as each iteration() will // inline at least one call before hitting this - if (inlinedUses.count(func->name)) continue; + if (inlinedUses.count(func->name)) + continue; for (auto& action : state.actionsForFunction[func->name]) { auto* inlinedFunction = action.contents; // if we've inlined into a function, don't inline it in this iteration, // avoid risk of races // note that we do not risk stalling progress, as each iteration() will // inline at least one call before hitting this - if (inlinedInto.count(inlinedFunction)) continue; + if (inlinedInto.count(inlinedFunction)) + continue; Name inlinedName = inlinedFunction->name; #ifdef INLINING_DEBUG std::cout << "inline " << inlinedName << " into " << func->name << '\n'; @@ -335,23 +352,28 @@ struct Inlining : public Pass { } // remove functions that we no longer need after inlining auto& funcs = module->functions; - funcs.erase(std::remove_if(funcs.begin(), funcs.end(), [&](const std::unique_ptr<Function>& curr) { - auto name = curr->name; - auto& info = infos[name]; - bool canRemove = inlinedUses.count(name) && inlinedUses[name] == info.calls && !info.usedGlobally; + funcs.erase(std::remove_if(funcs.begin(), + funcs.end(), + [&](const std::unique_ptr<Function>& curr) { + auto name = curr->name; + auto& info = infos[name]; + bool canRemove = + inlinedUses.count(name) && + inlinedUses[name] == info.calls && + !info.usedGlobally; #ifdef INLINING_DEBUG - if (canRemove) std::cout << "removing " << name << '\n'; + if (canRemove) + std::cout << "removing " << name << '\n'; #endif - return canRemove; - }), funcs.end()); + return canRemove; + }), + funcs.end()); // return whether we did any work return inlinedUses.size() > 0; } }; -Pass* createInliningPass() { - return new Inlining(); -} +Pass* createInliningPass() { return new Inlining(); } Pass* createInliningOptimizingPass() { auto* ret = new Inlining(); @@ -360,4 +382,3 @@ Pass* createInliningOptimizingPass() { } } // namespace wasm - diff --git a/src/passes/InstrumentLocals.cpp b/src/passes/InstrumentLocals.cpp index 6b44af0ad..3845b0fee 100644 --- a/src/passes/InstrumentLocals.cpp +++ b/src/passes/InstrumentLocals.cpp @@ -43,13 +43,13 @@ // ) // ) -#include <wasm.h> -#include <wasm-builder.h> -#include <pass.h> -#include "shared-constants.h" -#include "asmjs/shared-constants.h" #include "asm_v_wasm.h" +#include "asmjs/shared-constants.h" #include "ir/function-type-utils.h" +#include "shared-constants.h" +#include <pass.h> +#include <wasm-builder.h> +#include <wasm.h> namespace wasm { @@ -68,59 +68,71 @@ struct InstrumentLocals : public WalkerPass<PostWalker<InstrumentLocals>> { Builder builder(*getModule()); Name import; switch (curr->type) { - case i32: import = get_i32; break; - case i64: return; // TODO - case f32: import = get_f32; break; - case f64: import = get_f64; break; - case v128: assert(false && "v128 not implemented yet"); - case none: WASM_UNREACHABLE(); - case unreachable: WASM_UNREACHABLE(); + case i32: + import = get_i32; + break; + case i64: + return; // TODO + case f32: + import = get_f32; + break; + case f64: + import = get_f64; + break; + case v128: + assert(false && "v128 not implemented yet"); + case none: + WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } replaceCurrent( - builder.makeCall( - import, - { - builder.makeConst(Literal(int32_t(id++))), - builder.makeConst(Literal(int32_t(curr->index))), - curr - }, - curr->type - ) - ); + builder.makeCall(import, + {builder.makeConst(Literal(int32_t(id++))), + builder.makeConst(Literal(int32_t(curr->index))), + curr}, + curr->type)); } void visitSetLocal(SetLocal* curr) { Builder builder(*getModule()); Name import; switch (curr->value->type) { - case i32: import = set_i32; break; - case i64: return; // TODO - case f32: import = set_f32; break; - case f64: import = set_f64; break; - case v128: assert(false && "v128 not implemented yet"); - case unreachable: return; // nothing to do here - case none: WASM_UNREACHABLE(); + case i32: + import = set_i32; + break; + case i64: + return; // TODO + case f32: + import = set_f32; + break; + case f64: + import = set_f64; + break; + case v128: + assert(false && "v128 not implemented yet"); + case unreachable: + return; // nothing to do here + case none: + WASM_UNREACHABLE(); } - curr->value = builder.makeCall( - import, - { - builder.makeConst(Literal(int32_t(id++))), - builder.makeConst(Literal(int32_t(curr->index))), - curr->value - }, - curr->value->type - ); + curr->value = + builder.makeCall(import, + {builder.makeConst(Literal(int32_t(id++))), + builder.makeConst(Literal(int32_t(curr->index))), + curr->value}, + curr->value->type); } void visitModule(Module* curr) { - addImport(curr, get_i32, "iiii"); - addImport(curr, get_i64, "jiij"); - addImport(curr, get_f32, "fiif"); - addImport(curr, get_f64, "diid"); - addImport(curr, set_i32, "iiii"); - addImport(curr, set_i64, "jiij"); - addImport(curr, set_f32, "fiif"); - addImport(curr, set_f64, "diid"); + addImport(curr, get_i32, "iiii"); + addImport(curr, get_i64, "jiij"); + addImport(curr, get_f32, "fiif"); + addImport(curr, get_f64, "diid"); + addImport(curr, set_i32, "iiii"); + addImport(curr, set_i64, "jiij"); + addImport(curr, set_f32, "fiif"); + addImport(curr, set_f64, "diid"); } private: @@ -138,8 +150,6 @@ private: } }; -Pass* createInstrumentLocalsPass() { - return new InstrumentLocals(); -} +Pass* createInstrumentLocalsPass() { return new InstrumentLocals(); } } // namespace wasm diff --git a/src/passes/InstrumentMemory.cpp b/src/passes/InstrumentMemory.cpp index a929478df..4a479db34 100644 --- a/src/passes/InstrumentMemory.cpp +++ b/src/passes/InstrumentMemory.cpp @@ -52,26 +52,26 @@ // ) // ) -#include <wasm.h> -#include <wasm-builder.h> -#include <pass.h> -#include "shared-constants.h" -#include "asmjs/shared-constants.h" #include "asm_v_wasm.h" +#include "asmjs/shared-constants.h" #include "ir/function-type-utils.h" +#include "shared-constants.h" +#include <pass.h> +#include <wasm-builder.h> +#include <wasm.h> namespace wasm { -static Name load_ptr("load_ptr"), - load_val_i32("load_val_i32"), - load_val_i64("load_val_i64"), - load_val_f32("load_val_f32"), - load_val_f64("load_val_f64"), - store_ptr("store_ptr"), - store_val_i32("store_val_i32"), - store_val_i64("store_val_i64"), - store_val_f32("store_val_f32"), - store_val_f64("store_val_f64"); +static Name load_ptr("load_ptr"); +static Name load_val_i32("load_val_i32"); +static Name load_val_i64("load_val_i64"); +static Name load_val_f32("load_val_f32"); +static Name load_val_f64("load_val_f64"); +static Name store_ptr("store_ptr"); +static Name store_val_i32("store_val_i32"); +static Name store_val_i64("store_val_i64"); +static Name store_val_f32("store_val_f32"); +static Name store_val_f64("store_val_f64"); // TODO: Add support for atomicRMW/cmpxchg @@ -79,66 +79,74 @@ struct InstrumentMemory : public WalkerPass<PostWalker<InstrumentMemory>> { void visitLoad(Load* curr) { id++; Builder builder(*getModule()); - curr->ptr = builder.makeCall(load_ptr, - { - builder.makeConst(Literal(int32_t(id))), - builder.makeConst(Literal(int32_t(curr->bytes))), - builder.makeConst(Literal(int32_t(curr->offset.addr))), - curr->ptr - }, - i32 - ); + curr->ptr = + builder.makeCall(load_ptr, + {builder.makeConst(Literal(int32_t(id))), + builder.makeConst(Literal(int32_t(curr->bytes))), + builder.makeConst(Literal(int32_t(curr->offset.addr))), + curr->ptr}, + i32); Name target; switch (curr->type) { - case i32: target = load_val_i32; break; - case i64: target = load_val_i64; break; - case f32: target = load_val_f32; break; - case f64: target = load_val_f64; break; - default: return; // TODO: other types, unreachable, etc. + case i32: + target = load_val_i32; + break; + case i64: + target = load_val_i64; + break; + case f32: + target = load_val_f32; + break; + case f64: + target = load_val_f64; + break; + default: + return; // TODO: other types, unreachable, etc. } - replaceCurrent(builder.makeCall(target, - { - builder.makeConst(Literal(int32_t(id))), - curr - }, - curr->type - )); + replaceCurrent(builder.makeCall( + target, {builder.makeConst(Literal(int32_t(id))), curr}, curr->type)); } void visitStore(Store* curr) { id++; Builder builder(*getModule()); - curr->ptr = builder.makeCall(store_ptr, - { builder.makeConst(Literal(int32_t(id))), - builder.makeConst(Literal(int32_t(curr->bytes))), - builder.makeConst(Literal(int32_t(curr->offset.addr))), - curr->ptr }, - i32 - ); + curr->ptr = + builder.makeCall(store_ptr, + {builder.makeConst(Literal(int32_t(id))), + builder.makeConst(Literal(int32_t(curr->bytes))), + builder.makeConst(Literal(int32_t(curr->offset.addr))), + curr->ptr}, + i32); Name target; switch (curr->value->type) { - case i32: target = store_val_i32; break; - case i64: target = store_val_i64; break; - case f32: target = store_val_f32; break; - case f64: target = store_val_f64; break; - default: return; // TODO: other types, unreachable, etc. + case i32: + target = store_val_i32; + break; + case i64: + target = store_val_i64; + break; + case f32: + target = store_val_f32; + break; + case f64: + target = store_val_f64; + break; + default: + return; // TODO: other types, unreachable, etc. } - curr->value = builder.makeCall(target, - { - builder.makeConst(Literal(int32_t(id))), - curr->value - }, - curr->value->type - ); + curr->value = + builder.makeCall(target, + {builder.makeConst(Literal(int32_t(id))), curr->value}, + curr->value->type); } - void visitModule(Module *curr) { - addImport(curr, load_ptr, "iiiii"); - addImport(curr, load_val_i32, "iii"); - addImport(curr, load_val_i64, "jij"); - addImport(curr, load_val_f32, "fif"); - addImport(curr, load_val_f64, "did"); - addImport(curr, store_ptr, "iiiii"); + void visitModule(Module* curr) { + addImport(curr, load_ptr, "iiiii"); + addImport(curr, load_val_i32, "iii"); + addImport(curr, load_val_i64, "jij"); + addImport(curr, load_val_f32, "fif"); + addImport(curr, load_val_f64, "did"); + addImport(curr, store_ptr, "iiiii"); addImport(curr, store_val_i32, "iii"); addImport(curr, store_val_i64, "jij"); addImport(curr, store_val_f32, "fif"); @@ -148,7 +156,7 @@ struct InstrumentMemory : public WalkerPass<PostWalker<InstrumentMemory>> { private: Index id; - void addImport(Module *curr, Name name, std::string sig) { + void addImport(Module* curr, Name name, std::string sig) { auto import = new Function; import->name = name; import->module = ENV; @@ -160,8 +168,6 @@ private: } }; -Pass *createInstrumentMemoryPass() { - return new InstrumentMemory(); -} +Pass* createInstrumentMemoryPass() { return new InstrumentMemory(); } } // namespace wasm diff --git a/src/passes/LegalizeJSInterface.cpp b/src/passes/LegalizeJSInterface.cpp index cde4373f8..324590427 100644 --- a/src/passes/LegalizeJSInterface.cpp +++ b/src/passes/LegalizeJSInterface.cpp @@ -30,17 +30,17 @@ // table even to a signature that is not legal. // -#include <utility> -#include "wasm.h" -#include "pass.h" #include "asm_v_wasm.h" -#include "shared-constants.h" #include "asmjs/shared-constants.h" -#include "wasm-builder.h" #include "ir/function-type-utils.h" #include "ir/import-utils.h" #include "ir/literal-utils.h" #include "ir/utils.h" +#include "pass.h" +#include "shared-constants.h" +#include "wasm-builder.h" +#include "wasm.h" +#include <utility> namespace wasm { @@ -71,8 +71,9 @@ struct LegalizeJSInterface : public Pass { if (im->imported() && isIllegal(im) && shouldBeLegalized(im)) { auto funcName = makeLegalStubForCalledImport(im, module); illegalImportsToLegal[im->name] = funcName; - // we need to use the legalized version in the table, as the import from JS - // is legal for JS. Our stub makes it look like a native wasm function. + // we need to use the legalized version in the table, as the import from + // JS is legal for JS. Our stub makes it look like a native wasm + // function. for (auto& segment : module->table.segments) { for (auto& name : segment.data) { if (name == im->name) { @@ -87,23 +88,32 @@ struct LegalizeJSInterface : public Pass { module->removeFunction(pair.first); } - // fix up imports: call_import of an illegal must be turned to a call of a legal + // fix up imports: call_import of an illegal must be turned to a call of a + // legal struct FixImports : public WalkerPass<PostWalker<FixImports>> { bool isFunctionParallel() override { return true; } - Pass* create() override { return new FixImports(illegalImportsToLegal); } + Pass* create() override { + return new FixImports(illegalImportsToLegal); + } std::map<Name, Name>* illegalImportsToLegal; - FixImports(std::map<Name, Name>* illegalImportsToLegal) : illegalImportsToLegal(illegalImportsToLegal) {} + FixImports(std::map<Name, Name>* illegalImportsToLegal) + : illegalImportsToLegal(illegalImportsToLegal) {} void visitCall(Call* curr) { auto iter = illegalImportsToLegal->find(curr->target); - if (iter == illegalImportsToLegal->end()) return; - - if (iter->second == getFunction()->name) return; // inside the stub function itself, is the one safe place to do the call - replaceCurrent(Builder(*getModule()).makeCall(iter->second, curr->operands, curr->type)); + if (iter == illegalImportsToLegal->end()) + return; + + if (iter->second == getFunction()->name) + // inside the stub function itself, is the one safe place to do the + // call + return; + replaceCurrent(Builder(*getModule()) + .makeCall(iter->second, curr->operands, curr->type)); } }; @@ -118,29 +128,32 @@ private: // map of illegal to legal names for imports std::map<Name, Name> illegalImportsToLegal; - template<typename T> - bool isIllegal(T* t) { + template<typename T> bool isIllegal(T* t) { for (auto param : t->params) { - if (param == i64) return true; + if (param == i64) + return true; } return t->result == i64; } // Check if an export should be legalized. bool shouldBeLegalized(Export* ex, Function* func) { - if (full) return true; + if (full) + return true; // We are doing minimal legalization - just what JS needs. return ex->name.startsWith("dynCall_"); } // Check if an import should be legalized. bool shouldBeLegalized(Function* im) { - if (full) return true; + if (full) + return true; // We are doing minimal legalization - just what JS needs. return im->module == ENV && im->base.startsWith("invoke_"); } - // JS calls the export, so it must call a legal stub that calls the actual wasm function + // JS calls the export, so it must call a legal stub that calls the actual + // wasm function Name makeLegalStub(Function* func, Module* module) { Builder builder(*module); auto* legal = new Function(); @@ -152,11 +165,13 @@ private: for (auto param : func->params) { if (param == i64) { - call->operands.push_back(I64Utilities::recreateI64(builder, legal->params.size(), legal->params.size() + 1)); + call->operands.push_back(I64Utilities::recreateI64( + builder, legal->params.size(), legal->params.size() + 1)); legal->params.push_back(i32); legal->params.push_back(i32); } else { - call->operands.push_back(builder.makeGetLocal(legal->params.size(), param)); + call->operands.push_back( + builder.makeGetLocal(legal->params.size(), param)); legal->params.push_back(param); } } @@ -167,7 +182,8 @@ private: auto index = Builder::addVar(legal, Name(), i64); auto* block = builder.makeBlock(); block->list.push_back(builder.makeSetLocal(index, call)); - block->list.push_back(builder.makeCall(f->name, {I64Utilities::getI64High(builder, index)}, none)); + block->list.push_back(builder.makeCall( + f->name, {I64Utilities::getI64High(builder, index)}, none)); block->list.push_back(I64Utilities::getI64Low(builder, index)); block->finalize(); legal->body = block; @@ -183,11 +199,12 @@ private: return legal->name; } - // wasm calls the import, so it must call a stub that calls the actual legal JS import + // wasm calls the import, so it must call a stub that calls the actual legal + // JS import Name makeLegalStubForCalledImport(Function* im, Module* module) { Builder builder(*module); auto type = make_unique<FunctionType>(); - type->name = Name(std::string("legaltype$") + im->name.str); + type->name = Name(std::string("legaltype$") + im->name.str); auto legal = make_unique<Function>(); legal->name = Name(std::string("legalimport$") + im->name.str); legal->module = im->module; @@ -203,12 +220,15 @@ private: for (auto param : imFunctionType->params) { if (param == i64) { - call->operands.push_back(I64Utilities::getI64Low(builder, func->params.size())); - call->operands.push_back(I64Utilities::getI64High(builder, func->params.size())); + call->operands.push_back( + I64Utilities::getI64Low(builder, func->params.size())); + call->operands.push_back( + I64Utilities::getI64High(builder, func->params.size())); type->params.push_back(i32); type->params.push_back(i32); } else { - call->operands.push_back(builder.makeGetLocal(func->params.size(), param)); + call->operands.push_back( + builder.makeGetLocal(func->params.size(), param)); type->params.push_back(param); } func->params.push_back(param); @@ -241,7 +261,8 @@ private: return funcName; } - static Function* getFunctionOrImport(Module* module, Name name, std::string sig) { + static Function* + getFunctionOrImport(Module* module, Name name, std::string sig) { // First look for the function by name if (Function* f = module->getFunctionOrNull(name)) { return f; @@ -264,13 +285,10 @@ private: } }; -Pass *createLegalizeJSInterfacePass() { - return new LegalizeJSInterface(true); -} +Pass* createLegalizeJSInterfacePass() { return new LegalizeJSInterface(true); } -Pass *createLegalizeJSInterfaceMinimallyPass() { +Pass* createLegalizeJSInterfaceMinimallyPass() { return new LegalizeJSInterface(false); } } // namespace wasm - diff --git a/src/passes/LimitSegments.cpp b/src/passes/LimitSegments.cpp index 521969a28..0ea70f53d 100644 --- a/src/passes/LimitSegments.cpp +++ b/src/passes/LimitSegments.cpp @@ -26,14 +26,11 @@ struct LimitSegments : public Pass { void run(PassRunner* runner, Module* module) override { if (!MemoryUtils::ensureLimitedSegments(*module)) { std::cerr << "Unable to merge segments. " - << "wasm VMs may not accept this binary" - << std::endl; + << "wasm VMs may not accept this binary" << std::endl; } } }; -Pass *createLimitSegmentsPass() { - return new LimitSegments(); -} +Pass* createLimitSegmentsPass() { return new LimitSegments(); } } // namespace wasm diff --git a/src/passes/LocalCSE.cpp b/src/passes/LocalCSE.cpp index 1338c6571..d582c8275 100644 --- a/src/passes/LocalCSE.cpp +++ b/src/passes/LocalCSE.cpp @@ -37,15 +37,15 @@ #include <algorithm> #include <memory> -#include <wasm.h> -#include <wasm-builder.h> -#include <wasm-traversal.h> -#include <pass.h> +#include "ir/flat.h" #include <ir/cost.h> #include <ir/effects.h> #include <ir/equivalent_sets.h> -#include "ir/flat.h" #include <ir/hashed.h> +#include <pass.h> +#include <wasm-builder.h> +#include <wasm-traversal.h> +#include <wasm.h> namespace wasm { @@ -60,7 +60,8 @@ struct LocalCSE : public WalkerPass<LinearExecutionWalker<LocalCSE>> { Index index; // the local we are assigned to, local.get that to reuse us EffectAnalyzer effects; - UsableInfo(Expression* value, Index index, PassOptions& passOptions) : value(value), index(index), effects(passOptions, value) {} + UsableInfo(Expression* value, Index index, PassOptions& passOptions) + : value(value), index(index), effects(passOptions, value) {} }; // a list of usables in a linear execution trace @@ -183,11 +184,13 @@ struct LocalCSE : public WalkerPass<LinearExecutionWalker<LocalCSE>> { if (iter != usables.end()) { // already exists in the table, this is good to reuse auto& info = iter->second; - set->value = Builder(*getModule()).makeGetLocal(info.index, value->type); + set->value = + Builder(*getModule()).makeGetLocal(info.index, value->type); anotherPass = true; } else { // not in table, add this, maybe we can help others later - usables.emplace(std::make_pair(hashed, UsableInfo(value, set->index, getPassOptions()))); + usables.emplace(std::make_pair( + hashed, UsableInfo(value, set->index, getPassOptions()))); } } } else if (auto* get = curr->dynCast<GetLocal>()) { @@ -227,8 +230,6 @@ struct LocalCSE : public WalkerPass<LinearExecutionWalker<LocalCSE>> { } }; -Pass *createLocalCSEPass() { - return new LocalCSE(); -} +Pass* createLocalCSEPass() { return new LocalCSE(); } } // namespace wasm diff --git a/src/passes/LogExecution.cpp b/src/passes/LogExecution.cpp index abdaa8d23..7bfee7c24 100644 --- a/src/passes/LogExecution.cpp +++ b/src/passes/LogExecution.cpp @@ -28,26 +28,22 @@ // value. // -#include <wasm.h> -#include <wasm-builder.h> -#include <pass.h> -#include "shared-constants.h" -#include "asmjs/shared-constants.h" #include "asm_v_wasm.h" +#include "asmjs/shared-constants.h" #include "ir/function-type-utils.h" +#include "shared-constants.h" +#include <pass.h> +#include <wasm-builder.h> +#include <wasm.h> namespace wasm { Name LOGGER("log_execution"); struct LogExecution : public WalkerPass<PostWalker<LogExecution>> { - void visitLoop(Loop* curr) { - curr->body = makeLogCall(curr->body); - } + void visitLoop(Loop* curr) { curr->body = makeLogCall(curr->body); } - void visitReturn(Return* curr) { - replaceCurrent(makeLogCall(curr)); - } + void visitReturn(Return* curr) { replaceCurrent(makeLogCall(curr)); } void visitFunction(Function* curr) { if (curr->imported()) { @@ -61,7 +57,7 @@ struct LogExecution : public WalkerPass<PostWalker<LogExecution>> { curr->body = makeLogCall(curr->body); } - void visitModule(Module *curr) { + void visitModule(Module* curr) { // Add the import auto import = new Function; import->name = LOGGER; @@ -79,17 +75,11 @@ private: Builder builder(*getModule()); return builder.makeSequence( builder.makeCall( - LOGGER, - { builder.makeConst(Literal(int32_t(id++))) }, - none - ), - curr - ); + LOGGER, {builder.makeConst(Literal(int32_t(id++)))}, none), + curr); } }; -Pass *createLogExecutionPass() { - return new LogExecution(); -} +Pass* createLogExecutionPass() { return new LogExecution(); } } // namespace wasm diff --git a/src/passes/LoopInvariantCodeMotion.cpp b/src/passes/LoopInvariantCodeMotion.cpp index aec2f7ce5..e9f376bd3 100644 --- a/src/passes/LoopInvariantCodeMotion.cpp +++ b/src/passes/LoopInvariantCodeMotion.cpp @@ -24,16 +24,17 @@ #include <unordered_map> -#include "wasm.h" -#include "pass.h" -#include "wasm-builder.h" -#include "ir/local-graph.h" #include "ir/effects.h" #include "ir/find_all.h" +#include "ir/local-graph.h" +#include "pass.h" +#include "wasm-builder.h" +#include "wasm.h" namespace wasm { -struct LoopInvariantCodeMotion : public WalkerPass<ExpressionStackWalker<LoopInvariantCodeMotion>> { +struct LoopInvariantCodeMotion + : public WalkerPass<ExpressionStackWalker<LoopInvariantCodeMotion>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new LoopInvariantCodeMotion; } @@ -128,11 +129,12 @@ struct LoopInvariantCodeMotion : public WalkerPass<ExpressionStackWalker<LoopInv // outside of the loop, in which case everything is good - // either they are before the loop and constant for us, or // they are after and don't matter. - if (effects.localsRead.empty() || !hasGetDependingOnLoopSet(curr, loopSets)) { - // We have checked if our gets are influenced by sets in the loop, and - // must also check if our sets interfere with them. To do so, assume - // temporarily that we are moving curr out; see if any sets remain for - // its indexes. + if (effects.localsRead.empty() || + !hasGetDependingOnLoopSet(curr, loopSets)) { + // We have checked if our gets are influenced by sets in the loop, + // and must also check if our sets interfere with them. To do so, + // assume temporarily that we are moving curr out; see if any sets + // remain for its indexes. FindAll<SetLocal> currSets(curr); for (auto* set : currSets.list) { assert(numSetsForIndex[set->index] > 0); @@ -187,8 +189,8 @@ struct LoopInvariantCodeMotion : public WalkerPass<ExpressionStackWalker<LoopInv bool interestingToMove(Expression* curr) { // In theory we could consider blocks, but then heavy nesting of // switch patterns would be heavy, and almost always pointless. - if (curr->type != none || curr->is<Nop>() || curr->is<Block>() - || curr->is<Loop>()) { + if (curr->type != none || curr->is<Nop>() || curr->is<Block>() || + curr->is<Loop>()) { return false; } // Don't move copies (set of a get, or set of a tee of a get, etc.), @@ -206,7 +208,8 @@ struct LoopInvariantCodeMotion : public WalkerPass<ExpressionStackWalker<LoopInv if (auto* set = curr->dynCast<SetLocal>()) { while (1) { auto* next = set->value->dynCast<SetLocal>(); - if (!next) break; + if (!next) + break; set = next; } if (set->value->is<GetLocal>() || set->value->is<Const>()) { @@ -223,7 +226,8 @@ struct LoopInvariantCodeMotion : public WalkerPass<ExpressionStackWalker<LoopInv for (auto* set : sets) { // nullptr means a parameter or zero-init value; // no danger to us. - if (!set) continue; + if (!set) + continue; // Check if the set is in the loop. If not, it's either before, // which is fine, or after, which is also fine - moving curr // to just outside the loop will preserve those relationships. @@ -238,9 +242,8 @@ struct LoopInvariantCodeMotion : public WalkerPass<ExpressionStackWalker<LoopInv } }; -Pass *createLoopInvariantCodeMotionPass() { +Pass* createLoopInvariantCodeMotionPass() { return new LoopInvariantCodeMotion(); } } // namespace wasm - diff --git a/src/passes/MemoryPacking.cpp b/src/passes/MemoryPacking.cpp index 704f57d89..11dbc1743 100644 --- a/src/passes/MemoryPacking.cpp +++ b/src/passes/MemoryPacking.cpp @@ -15,9 +15,9 @@ */ #include "pass.h" -#include "wasm.h" #include "wasm-binary.h" #include "wasm-builder.h" +#include "wasm.h" namespace wasm { @@ -57,7 +57,8 @@ struct MemoryPacking : public Pass { }; for (auto& segment : module->memory.segments) { - if (!isSplittable(segment)) continue; + if (!isSplittable(segment)) + continue; // skip final zeros while (segment.data.size() > 0 && segment.data.back() == 0) { @@ -81,7 +82,7 @@ struct MemoryPacking : public Pass { start++; } Index end = start; // end of data-containing part - Index next = end; // after zeros we can skip. preserves next >= end + Index next = end; // after zeros we can skip. preserves next >= end if (!shouldSplit()) { next = end = data.size(); } @@ -99,7 +100,10 @@ struct MemoryPacking : public Pass { } } if (end != start) { - packed.emplace_back(Builder(*module).makeConst(Literal(int32_t(base + start))), &data[start], end - start); + packed.emplace_back( + Builder(*module).makeConst(Literal(int32_t(base + start))), + &data[start], + end - start); } start = next; } @@ -109,8 +113,6 @@ struct MemoryPacking : public Pass { } }; -Pass *createMemoryPackingPass() { - return new MemoryPacking(); -} +Pass* createMemoryPackingPass() { return new MemoryPacking(); } } // namespace wasm diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp index 38e9fc6a2..8df35abd2 100644 --- a/src/passes/MergeBlocks.cpp +++ b/src/passes/MergeBlocks.cpp @@ -72,21 +72,23 @@ // single outside block. // -#include <wasm.h> -#include <pass.h> -#include <wasm-builder.h> #include <ir/branch-utils.h> #include <ir/effects.h> #include <ir/utils.h> +#include <pass.h> +#include <wasm-builder.h> +#include <wasm.h> namespace wasm { // Looks for reasons we can't remove the values from breaks to an origin -// For example, if there is a switch targeting us, we can't do it - we can't remove the value from other targets +// For example, if there is a switch targeting us, we can't do it - we can't +// remove the value from other targets struct ProblemFinder : public ControlFlowWalker<ProblemFinder> { Name origin; bool foundProblem = false; - // count br_ifs, and dropped br_ifs. if they don't match, then a br_if flow value is used, and we can't drop it + // count br_ifs, and dropped br_ifs. if they don't match, then a br_if flow + // value is used, and we can't drop it Index brIfs = 0; Index droppedBrIfs = 0; PassOptions& passOptions; @@ -158,8 +160,10 @@ struct BreakValueDropper : public ControlFlowWalker<BreakValueDropper> { } void visitDrop(Drop* curr) { - // if we dropped a br_if whose value we removed, then we are now dropping a (block (drop value) (br_if)) with type none, which does not need a drop - // likewise, unreachable does not need to be dropped, so we just leave drops of concrete values + // if we dropped a br_if whose value we removed, then we are now dropping a + // (block (drop value) (br_if)) with type none, which does not need a drop + // likewise, unreachable does not need to be dropped, so we just leave drops + // of concrete values if (!isConcreteType(curr->value->type)) { replaceCurrent(curr->value); } @@ -188,7 +192,8 @@ static bool hasDeadCode(Block* block) { } // core block optimizer routine -static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) { +static void +optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) { auto& list = curr->list; // Main merging loop. bool more = true; @@ -205,7 +210,8 @@ static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) Loop* loop = nullptr; // To to handle a non-block child. if (!childBlock) { - // if we have a child that is (drop (block ..)) then we can move the drop into the block, and remove br values. this allows more merging, + // if we have a child that is (drop (block ..)) then we can move the + // drop into the block, and remove br values. this allows more merging, if (auto* drop = list[i]->dynCast<Drop>()) { childBlock = drop->value->dynCast<Block>(); if (childBlock) { @@ -253,13 +259,16 @@ static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) } } // If no block, we can't do anything. - if (!childBlock) continue; + if (!childBlock) + continue; auto& childList = childBlock->list; auto childSize = childList.size(); - if (childSize == 0) continue; - // If the child has items after an unreachable, ignore it - dce should have - // been run, and we prefer to not handle the complexity here. - if (hasDeadCode(childBlock)) continue; + if (childSize == 0) + continue; + // If the child has items after an unreachable, ignore it - dce should + // have been run, and we prefer to not handle the complexity here. + if (hasDeadCode(childBlock)) + continue; // In some cases we can remove only the head or the tail of the block, // and must keep some things in the child block. Index keepStart = childSize; @@ -295,8 +304,9 @@ static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) break; } } - // If we can only do part of the block, and if the block has a flowing value, we - // would need special handling for that - not worth it, probably TODO + // If we can only do part of the block, and if the block has a flowing + // value, we would need special handling for that - not worth it, + // probably TODO // FIXME is this not handled by the drop later down? if (keepEnd < childSize && isConcreteType(childList.back()->type)) { continue; @@ -304,7 +314,8 @@ static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions) } // Maybe there's nothing to do, if we must keep it all in the // child anyhow. - if (keepStart == 0 && keepEnd == childSize) continue; + if (keepStart == 0 && keepEnd == childSize) + continue; // There is something to do! bool keepingPart = keepStart < keepEnd; // Create a new merged list, and fill in the code before the @@ -393,31 +404,45 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { // (..other..children..) // ) // ) - // at which point the block is on the outside and potentially mergeable with an outer block - Block* optimize(Expression* curr, Expression*& child, Block* outer = nullptr, Expression** dependency1 = nullptr, Expression** dependency2 = nullptr) { - if (!child) return outer; + // at which point the block is on the outside and potentially mergeable with + // an outer block + Block* optimize(Expression* curr, + Expression*& child, + Block* outer = nullptr, + Expression** dependency1 = nullptr, + Expression** dependency2 = nullptr) { + if (!child) + return outer; if ((dependency1 && *dependency1) || (dependency2 && *dependency2)) { - // there are dependencies, things we must be reordered through. make sure no problems there + // there are dependencies, things we must be reordered through. make sure + // no problems there EffectAnalyzer childEffects(getPassOptions(), child); - if (dependency1 && *dependency1 && EffectAnalyzer(getPassOptions(), *dependency1).invalidates(childEffects)) return outer; - if (dependency2 && *dependency2 && EffectAnalyzer(getPassOptions(), *dependency2).invalidates(childEffects)) return outer; + if (dependency1 && *dependency1 && + EffectAnalyzer(getPassOptions(), *dependency1) + .invalidates(childEffects)) + return outer; + if (dependency2 && *dependency2 && + EffectAnalyzer(getPassOptions(), *dependency2) + .invalidates(childEffects)) + return outer; } if (auto* block = child->dynCast<Block>()) { if (!block->name.is() && block->list.size() >= 2) { - // if we move around unreachable code, type changes could occur. avoid that, as - // anyhow it means we should have run dce before getting here + // if we move around unreachable code, type changes could occur. avoid + // that, as anyhow it means we should have run dce before getting here if (curr->type == none && hasUnreachableChild(block)) { - // moving the block to the outside would replace a none with an unreachable + // moving the block to the outside would replace a none with an + // unreachable return outer; } auto* back = block->list.back(); if (back->type == unreachable) { - // curr is not reachable, dce could remove it; don't try anything fancy - // here + // curr is not reachable, dce could remove it; don't try anything + // fancy here return outer; } - // we are going to replace the block with the final element, so they should - // be identically typed + // we are going to replace the block with the final element, so they + // should be identically typed if (block->type != back->type) { return outer; } @@ -443,18 +468,10 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { return outer; } - void visitUnary(Unary* curr) { - optimize(curr, curr->value); - } - void visitSetLocal(SetLocal* curr) { - optimize(curr, curr->value); - } - void visitLoad(Load* curr) { - optimize(curr, curr->ptr); - } - void visitReturn(Return* curr) { - optimize(curr, curr->value); - } + void visitUnary(Unary* curr) { optimize(curr, curr->value); } + void visitSetLocal(SetLocal* curr) { optimize(curr, curr->value); } + void visitLoad(Load* curr) { optimize(curr, curr->ptr); } + void visitReturn(Return* curr) { optimize(curr, curr->value); } void visitBinary(Binary* curr) { optimize(curr, curr->right, optimize(curr, curr->left), &curr->left); @@ -466,15 +483,20 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { optimize(curr, curr->value, optimize(curr, curr->ptr), &curr->ptr); } void optimizeTernary(Expression* curr, - Expression*& first, Expression*& second, Expression*& third) { + Expression*& first, + Expression*& second, + Expression*& third) { // TODO: for now, just stop when we see any side effect. instead, we could // check effects carefully for reordering Block* outer = nullptr; - if (EffectAnalyzer(getPassOptions(), first).hasSideEffects()) return; + if (EffectAnalyzer(getPassOptions(), first).hasSideEffects()) + return; outer = optimize(curr, first, outer); - if (EffectAnalyzer(getPassOptions(), second).hasSideEffects()) return; + if (EffectAnalyzer(getPassOptions(), second).hasSideEffects()) + return; outer = optimize(curr, second, outer); - if (EffectAnalyzer(getPassOptions(), third).hasSideEffects()) return; + if (EffectAnalyzer(getPassOptions(), third).hasSideEffects()) + return; optimize(curr, third, outer); } void visitAtomicCmpxchg(AtomicCmpxchg* curr) { @@ -485,9 +507,7 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { optimizeTernary(curr, curr->ifTrue, curr->ifFalse, curr->condition); } - void visitDrop(Drop* curr) { - optimize(curr, curr->value); - } + void visitDrop(Drop* curr) { optimize(curr, curr->value); } void visitBreak(Break* curr) { optimize(curr, curr->condition, optimize(curr, curr->value), &curr->value); @@ -496,33 +516,31 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { optimize(curr, curr->condition, optimize(curr, curr->value), &curr->value); } - template<typename T> - void handleCall(T* curr) { + template<typename T> void handleCall(T* curr) { Block* outer = nullptr; for (Index i = 0; i < curr->operands.size(); i++) { - if (EffectAnalyzer(getPassOptions(), curr->operands[i]).hasSideEffects()) return; + if (EffectAnalyzer(getPassOptions(), curr->operands[i]).hasSideEffects()) + return; outer = optimize(curr, curr->operands[i], outer); } return; } - void visitCall(Call* curr) { - handleCall(curr); - } + void visitCall(Call* curr) { handleCall(curr); } void visitCallIndirect(CallIndirect* curr) { Block* outer = nullptr; for (Index i = 0; i < curr->operands.size(); i++) { - if (EffectAnalyzer(getPassOptions(), curr->operands[i]).hasSideEffects()) return; + if (EffectAnalyzer(getPassOptions(), curr->operands[i]).hasSideEffects()) + return; outer = optimize(curr, curr->operands[i], outer); } - if (EffectAnalyzer(getPassOptions(), curr->target).hasSideEffects()) return; + if (EffectAnalyzer(getPassOptions(), curr->target).hasSideEffects()) + return; optimize(curr, curr->target, outer); } }; -Pass *createMergeBlocksPass() { - return new MergeBlocks(); -} +Pass* createMergeBlocksPass() { return new MergeBlocks(); } } // namespace wasm diff --git a/src/passes/MergeLocals.cpp b/src/passes/MergeLocals.cpp index 4092e1ea8..fe9f4bb86 100644 --- a/src/passes/MergeLocals.cpp +++ b/src/passes/MergeLocals.cpp @@ -28,7 +28,7 @@ // (i32.const 100) // (local.get $x) // ) -// +// // If that assignment of $y is never used again, everything is fine. But if // if is, then the live range of $y does not end in that get, and will // necessarily overlap with that of $x - making them appear to interfere @@ -46,14 +46,16 @@ // TODO: investigate more // -#include <wasm.h> +#include <ir/local-graph.h> #include <pass.h> #include <wasm-builder.h> -#include <ir/local-graph.h> +#include <wasm.h> namespace wasm { -struct MergeLocals : public WalkerPass<PostWalker<MergeLocals, UnifiedExpressionVisitor<MergeLocals>>> { +struct MergeLocals + : public WalkerPass< + PostWalker<MergeLocals, UnifiedExpressionVisitor<MergeLocals>>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new MergeLocals(); } @@ -94,12 +96,14 @@ struct MergeLocals : public WalkerPass<PostWalker<MergeLocals, UnifiedExpression } void optimizeCopies() { - if (copies.empty()) return; + if (copies.empty()) + return; // compute all dependencies LocalGraph preGraph(getFunction()); preGraph.computeInfluences(); // optimize each copy - std::unordered_map<SetLocal*, SetLocal*> optimizedToCopy, optimizedToTrivial; + std::unordered_map<SetLocal*, SetLocal*> optimizedToCopy, + optimizedToTrivial; for (auto* copy : copies) { auto* trivial = copy->value->cast<SetLocal>(); bool canOptimizeToCopy = false; @@ -108,8 +112,8 @@ struct MergeLocals : public WalkerPass<PostWalker<MergeLocals, UnifiedExpression canOptimizeToCopy = true; for (auto* influencedGet : trivialInfluences) { // this get uses the trivial write, so it uses the value in the copy. - // however, it may depend on other writes too, if there is a merge/phi, - // and in that case we can't do anything + // however, it may depend on other writes too, if there is a + // merge/phi, and in that case we can't do anything assert(influencedGet->index == trivial->index); if (preGraph.getSetses[influencedGet].size() == 1) { // this is ok @@ -127,14 +131,17 @@ struct MergeLocals : public WalkerPass<PostWalker<MergeLocals, UnifiedExpression } optimizedToCopy[copy] = trivial; } else { - // alternatively, we can try to remove the conflict in the opposite way: given + // alternatively, we can try to remove the conflict in the opposite way: + // given // (local.set $x // (local.get $y) // ) - // we can look for uses of $x that could instead be uses of $y. this extends - // $y's live range, but if it removes the conflict between $x and $y, it may be - // worth it - if (!trivialInfluences.empty()) { // if the trivial set we added has influences, it means $y lives on + // we can look for uses of $x that could instead be uses of $y. this + // extends $y's live range, but if it removes the conflict between $x + // and $y, it may be worth it + + // if the trivial set we added has influences, it means $y lives on + if (!trivialInfluences.empty()) { auto& copyInfluences = preGraph.setInfluences[copy]; if (!copyInfluences.empty()) { bool canOptimizeToTrivial = true; @@ -212,9 +219,6 @@ struct MergeLocals : public WalkerPass<PostWalker<MergeLocals, UnifiedExpression } }; -Pass *createMergeLocalsPass() { - return new MergeLocals(); -} +Pass* createMergeLocalsPass() { return new MergeLocals(); } } // namespace wasm - diff --git a/src/passes/Metrics.cpp b/src/passes/Metrics.cpp index 8717a86b7..0baca3e8b 100644 --- a/src/passes/Metrics.cpp +++ b/src/passes/Metrics.cpp @@ -16,22 +16,23 @@ #include <algorithm> #include <iomanip> +#include <ir/module-utils.h> #include <pass.h> #include <support/colors.h> -#include <wasm.h> #include <wasm-binary.h> -#include <ir/module-utils.h> +#include <wasm.h> using namespace std; namespace wasm { -typedef map<const char *, int> Counts; +typedef map<const char*, int> Counts; static Counts lastCounts; // Prints metrics between optimization passes. -struct Metrics : public WalkerPass<PostWalker<Metrics, UnifiedExpressionVisitor<Metrics>>> { +struct Metrics + : public WalkerPass<PostWalker<Metrics, UnifiedExpressionVisitor<Metrics>>> { bool modifiesBinaryenIR() override { return false; } bool byFunction; @@ -56,9 +57,8 @@ struct Metrics : public WalkerPass<PostWalker<Metrics, UnifiedExpressionVisitor< for (auto& curr : module->exports) { visitExport(curr.get()); } - ModuleUtils::iterDefinedGlobals(*module, [&](Global* curr) { - walkGlobal(curr); - }); + ModuleUtils::iterDefinedGlobals(*module, + [&](Global* curr) { walkGlobal(curr); }); walkTable(&module->table); walkMemory(&module->memory); @@ -70,14 +70,14 @@ struct Metrics : public WalkerPass<PostWalker<Metrics, UnifiedExpressionVisitor< // add memory and table if (module->memory.exists) { Index size = 0; - for (auto& segment: module->memory.segments) { + for (auto& segment : module->memory.segments) { size += segment.data.size(); } counts["[memory-data]"] = size; } if (module->table.exists) { Index size = 0; - for (auto& segment: module->table.segments) { + for (auto& segment : module->table.segments) { size += segment.data.size(); } counts["[table-data]"] = size; @@ -96,13 +96,15 @@ struct Metrics : public WalkerPass<PostWalker<Metrics, UnifiedExpressionVisitor< counts.clear(); walkFunction(func); counts["[vars]"] = func->getNumVars(); - counts["[binary-bytes]"] = writer.tableOfContents.functionBodies[binaryIndex++].size; + counts["[binary-bytes]"] = + writer.tableOfContents.functionBodies[binaryIndex++].size; printCounts(std::string("func: ") + func->name.str); }); // print for each export how much code size is due to it, i.e., // how much the module could shrink without it. auto sizeAfterGlobalCleanup = [](Module* module) { - PassRunner runner(module, PassOptions::getWithDefaultOptimizationOptions()); + PassRunner runner(module, + PassOptions::getWithDefaultOptimizationOptions()); runner.setIsNested(true); runner.addDefaultGlobalOptimizationPostPasses(); // remove stuff runner.run(); @@ -118,13 +120,16 @@ struct Metrics : public WalkerPass<PostWalker<Metrics, UnifiedExpressionVisitor< baseline = sizeAfterGlobalCleanup(&test); } for (auto& exp : module->exports) { - // create a test module where we remove the export and then see how much can be removed thanks to that + // create a test module where we remove the export and then see how much + // can be removed thanks to that Module test; ModuleUtils::copyModule(*module, test); test.removeExport(exp->name); counts.clear(); - counts["[removable-bytes-without-it]"] = baseline - sizeAfterGlobalCleanup(&test); - printCounts(std::string("export: ") + exp->name.str + " (" + exp->value.str + ')'); + counts["[removable-bytes-without-it]"] = + baseline - sizeAfterGlobalCleanup(&test); + printCounts(std::string("export: ") + exp->name.str + " (" + + exp->value.str + ')'); } // check how much size depends on the start method if (!module->start.isNull()) { @@ -132,7 +137,8 @@ struct Metrics : public WalkerPass<PostWalker<Metrics, UnifiedExpressionVisitor< ModuleUtils::copyModule(*module, test); test.start = Name(); counts.clear(); - counts["[removable-bytes-without-it]"] = baseline - sizeAfterGlobalCleanup(&test); + counts["[removable-bytes-without-it]"] = + baseline - sizeAfterGlobalCleanup(&test); printCounts(std::string("start: ") + module->start.str); } // can't compare detailed info between passes yet @@ -153,7 +159,7 @@ struct Metrics : public WalkerPass<PostWalker<Metrics, UnifiedExpressionVisitor< } void printCounts(std::string title) { - ostream &o = cout; + ostream& o = cout; vector<const char*> keys; // add total int total = 0; @@ -173,9 +179,9 @@ struct Metrics : public WalkerPass<PostWalker<Metrics, UnifiedExpressionVisitor< o << title << "\n"; for (auto* key : keys) { auto value = counts[key]; - if (value == 0 && key[0] != '[') continue; - o << " " << left << setw(15) << key << ": " << setw(8) - << value; + if (value == 0 && key[0] != '[') + continue; + o << " " << left << setw(15) << key << ": " << setw(8) << value; if (lastCounts.count(key)) { int before = lastCounts[key]; int after = value; @@ -195,12 +201,8 @@ struct Metrics : public WalkerPass<PostWalker<Metrics, UnifiedExpressionVisitor< } }; -Pass* createMetricsPass() { - return new Metrics(false); -} +Pass* createMetricsPass() { return new Metrics(false); } -Pass* createFunctionMetricsPass() { - return new Metrics(true); -} +Pass* createFunctionMetricsPass() { return new Metrics(true); } } // namespace wasm diff --git a/src/passes/MinifyImportsAndExports.cpp b/src/passes/MinifyImportsAndExports.cpp index 007f4b629..23dd2a21a 100644 --- a/src/passes/MinifyImportsAndExports.cpp +++ b/src/passes/MinifyImportsAndExports.cpp @@ -32,12 +32,12 @@ #include <string> #include <unordered_set> -#include <wasm.h> -#include <pass.h> -#include <shared-constants.h> #include <asmjs/shared-constants.h> #include <ir/import-utils.h> #include <ir/module-utils.h> +#include <pass.h> +#include <shared-constants.h> +#include <wasm.h> namespace wasm { @@ -45,7 +45,8 @@ struct MinifyImportsAndExports : public Pass { bool minifyExports; public: - explicit MinifyImportsAndExports(bool minifyExports):minifyExports(minifyExports) {} + explicit MinifyImportsAndExports(bool minifyExports) + : minifyExports(minifyExports) {} private: // Generates minified names that are valid in JS. @@ -53,8 +54,8 @@ private: class MinifiedNames { public: MinifiedNames() { - // Reserved words in JS up to size 4 - size 5 and above would mean we use an astronomical - // number of symbols, which is not realistic anyhow. + // Reserved words in JS up to size 4 - size 5 and above would mean we use + // an astronomical number of symbols, which is not realistic anyhow. reserved.insert("do"); reserved.insert("if"); reserved.insert("in"); @@ -71,7 +72,8 @@ private: reserved.insert("this"); reserved.insert("with"); - validInitialChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$"; + validInitialChars = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$"; validLaterChars = validInitialChars + "0123456789"; minifiedState.push_back(0); @@ -120,14 +122,16 @@ private: size_t i = 0; while (1) { minifiedState[i]++; - if (minifiedState[i] < (i == 0 ? validInitialChars : validLaterChars).size()) { + if (minifiedState[i] < + (i == 0 ? validInitialChars : validLaterChars).size()) { break; } // Overflow. minifiedState[i] = 0; i++; if (i == minifiedState.size()) { - minifiedState.push_back(-1); // will become 0 after increment in next loop head + // will become 0 after increment in next loop head + minifiedState.push_back(-1); } } } @@ -170,11 +174,9 @@ private: } }; -Pass *createMinifyImportsPass() { - return new MinifyImportsAndExports(false); -} +Pass* createMinifyImportsPass() { return new MinifyImportsAndExports(false); } -Pass *createMinifyImportsAndExportsPass() { +Pass* createMinifyImportsAndExportsPass() { return new MinifyImportsAndExports(true); } diff --git a/src/passes/NameList.cpp b/src/passes/NameList.cpp index 6b1d528e4..1051a31d1 100644 --- a/src/passes/NameList.cpp +++ b/src/passes/NameList.cpp @@ -18,24 +18,22 @@ // Write out the name list of the module, similar to `nm`. // -#include "wasm.h" -#include "pass.h" #include "ir/module-utils.h" #include "ir/utils.h" +#include "pass.h" +#include "wasm.h" namespace wasm { struct NameList : public Pass { void run(PassRunner* runner, Module* module) override { ModuleUtils::iterDefinedFunctions(*module, [&](Function* func) { - std::cout << " " << func->name << " : " << Measurer::measure(func->body) << '\n'; + std::cout << " " << func->name << " : " + << Measurer::measure(func->body) << '\n'; }); } }; -Pass *createNameListPass() { - return new NameList(); -} +Pass* createNameListPass() { return new NameList(); } } // namespace wasm - diff --git a/src/passes/NoExitRuntime.cpp b/src/passes/NoExitRuntime.cpp index 05dd639c9..680b91b23 100644 --- a/src/passes/NoExitRuntime.cpp +++ b/src/passes/NoExitRuntime.cpp @@ -20,10 +20,10 @@ // run. // +#include <asmjs/shared-constants.h> #include <pass.h> -#include <wasm.h> #include <wasm-builder.h> -#include <asmjs/shared-constants.h> +#include <wasm.h> using namespace std; @@ -34,27 +34,23 @@ struct NoExitRuntime : public WalkerPass<PostWalker<NoExitRuntime>> { Pass* create() override { return new NoExitRuntime; } - // Remove all possible manifestations of atexit, across asm2wasm and llvm wasm backend. - std::array<Name, 4> ATEXIT_NAMES = {{ "___cxa_atexit", - "__cxa_atexit", - "_atexit", - "atexit" }}; + // Remove all possible manifestations of atexit, across asm2wasm and llvm wasm + // backend. + std::array<Name, 4> ATEXIT_NAMES = { + {"___cxa_atexit", "__cxa_atexit", "_atexit", "atexit"}}; void visitCall(Call* curr) { auto* import = getModule()->getFunctionOrNull(curr->target); - if (!import || !import->imported() || import->module != ENV) return; + if (!import || !import->imported() || import->module != ENV) + return; for (auto name : ATEXIT_NAMES) { if (name == import->base) { - replaceCurrent( - Builder(*getModule()).replaceWithIdenticalType(curr) - ); + replaceCurrent(Builder(*getModule()).replaceWithIdenticalType(curr)); } } } }; -Pass* createNoExitRuntimePass() { - return new NoExitRuntime(); -} +Pass* createNoExitRuntimePass() { return new NoExitRuntime(); } } // namespace wasm diff --git a/src/passes/OptimizeAddedConstants.cpp b/src/passes/OptimizeAddedConstants.cpp index e2cfb1418..b8d011cfb 100644 --- a/src/passes/OptimizeAddedConstants.cpp +++ b/src/passes/OptimizeAddedConstants.cpp @@ -15,9 +15,9 @@ */ // -// Optimize added constants into load/store offsets. This requires the assumption -// that low memory is unused, so that we can replace an add (which might wrap) -// with a load/store offset (which does not). +// Optimize added constants into load/store offsets. This requires the +// assumption that low memory is unused, so that we can replace an add (which +// might wrap) with a load/store offset (which does not). // // The propagate option also propagates offsets across set/get local pairs. // @@ -30,20 +30,22 @@ // speed, and may lead to code size reductions elsewhere by using fewer locals. // -#include <wasm.h> -#include <pass.h> -#include <wasm-builder.h> #include <ir/local-graph.h> #include <ir/local-utils.h> #include <ir/parents.h> +#include <pass.h> +#include <wasm-builder.h> +#include <wasm.h> namespace wasm { -template<typename P, typename T> -class MemoryAccessOptimizer { +template<typename P, typename T> class MemoryAccessOptimizer { public: - MemoryAccessOptimizer(P* parent, T* curr, Module* module, LocalGraph* localGraph) : - parent(parent), curr(curr), module(module), localGraph(localGraph) {} + MemoryAccessOptimizer(P* parent, + T* curr, + Module* module, + LocalGraph* localGraph) + : parent(parent), curr(curr), module(module), localGraph(localGraph) {} // Tries to optimize, and returns whether we propagated a change. bool optimize() { @@ -78,18 +80,20 @@ public: auto& sets = localGraph->getSetses[get]; if (sets.size() == 1) { auto* set = *sets.begin(); - // May be a zero-init (in which case, we can ignore it). Must also be valid - // to propagate, as checked earlier in the parent. + // May be a zero-init (in which case, we can ignore it). Must also be + // valid to propagate, as checked earlier in the parent. if (set && parent->isPropagatable(set)) { auto* value = set->value; if (auto* add = value->template dynCast<Binary>()) { if (add->op == AddInt32) { // We can optimize on either side, but only if both we find // a constant *and* the other side cannot change in the middle. - // TODO If it could change, we may add a new local to capture the - // old value. - if (tryToOptimizePropagatedAdd(add->right, add->left, get, set) || - tryToOptimizePropagatedAdd(add->left, add->right, get, set)) { + // TODO If it could change, we may add a new local to capture + // the old value. + if (tryToOptimizePropagatedAdd( + add->right, add->left, get, set) || + tryToOptimizePropagatedAdd( + add->left, add->right, get, set)) { return true; } } @@ -153,7 +157,10 @@ private: return false; } - bool tryToOptimizePropagatedAdd(Expression* oneSide, Expression* otherSide, GetLocal* ptr, SetLocal* set) { + bool tryToOptimizePropagatedAdd(Expression* oneSide, + Expression* otherSide, + GetLocal* ptr, + SetLocal* set) { if (auto* c = oneSide->template dynCast<Const>()) { if (otherSide->template is<Const>()) { // Both sides are constant - this is not optimized code, ignore. @@ -171,16 +178,17 @@ private: // // load(x, offset=10) // - // If the other side is a get, we may be able to prove that we can just use that same - // local, if both it and the pointer are in SSA form. In that case, + // If the other side is a get, we may be able to prove that we can just + // use that same local, if both it and the pointer are in SSA form. In + // that case, // // y = .. // single assignment that dominates all uses // x = y + 10 // single assignment that dominates all uses // [..] // load(x) => load(y, offset=10) // - // This is valid since dominance is transitive, so y's definition dominates the load, - // and it is ok to replace x with y + 10 there. + // This is valid since dominance is transitive, so y's definition + // dominates the load, and it is ok to replace x with y + 10 there. Index index = -1; bool canReuseIndex = false; if (auto* get = otherSide->template dynCast<GetLocal>()) { @@ -228,7 +236,10 @@ private: } }; -struct OptimizeAddedConstants : public WalkerPass<PostWalker<OptimizeAddedConstants, UnifiedExpressionVisitor<OptimizeAddedConstants>>> { +struct OptimizeAddedConstants + : public WalkerPass< + PostWalker<OptimizeAddedConstants, + UnifiedExpressionVisitor<OptimizeAddedConstants>>> { bool isFunctionParallel() override { return true; } bool propagate; @@ -238,14 +249,16 @@ struct OptimizeAddedConstants : public WalkerPass<PostWalker<OptimizeAddedConsta Pass* create() override { return new OptimizeAddedConstants(propagate); } void visitLoad(Load* curr) { - MemoryAccessOptimizer<OptimizeAddedConstants, Load> optimizer(this, curr, getModule(), localGraph.get()); + MemoryAccessOptimizer<OptimizeAddedConstants, Load> optimizer( + this, curr, getModule(), localGraph.get()); if (optimizer.optimize()) { propagated = true; } } void visitStore(Store* curr) { - MemoryAccessOptimizer<OptimizeAddedConstants, Store> optimizer(this, curr, getModule(), localGraph.get()); + MemoryAccessOptimizer<OptimizeAddedConstants, Store> optimizer( + this, curr, getModule(), localGraph.get()); if (optimizer.optimize()) { propagated = true; } @@ -254,9 +267,10 @@ struct OptimizeAddedConstants : public WalkerPass<PostWalker<OptimizeAddedConsta void doWalkFunction(Function* func) { // This pass is only valid under the assumption of unused low memory. assert(getPassOptions().lowMemoryUnused); - // Multiple passes may be needed if we have x + 4 + 8 etc. (nested structs in C - // can cause this, but it's rare). Note that we only need that for the propagation - // case (as 4 + 8 would be optimized directly if it were adjacent). + // Multiple passes may be needed if we have x + 4 + 8 etc. (nested structs + // in C can cause this, but it's rare). Note that we only need that for the + // propagation case (as 4 + 8 would be optimized directly if it were + // adjacent). while (1) { propagated = false; helperIndexes.clear(); @@ -279,22 +293,21 @@ struct OptimizeAddedConstants : public WalkerPass<PostWalker<OptimizeAddedConsta } } - // For a given expression, store it to a local and return us the local index we can use, - // in order to get that value someplace else. We are provided not the expression, - // but the set in which it is in, as the arm of an add that is the set's value (the other - // arm is a constant, and we are not a constant). + // For a given expression, store it to a local and return us the local index + // we can use, in order to get that value someplace else. We are provided not + // the expression, but the set in which it is in, as the arm of an add that is + // the set's value (the other arm is a constant, and we are not a constant). // We cache these, that is, use a single one for all requests. Index getHelperIndex(SetLocal* set) { auto iter = helperIndexes.find(set); if (iter != helperIndexes.end()) { return iter->second; } - return helperIndexes[set] = Builder(*getModule()).addVar(getFunction(), i32); + return helperIndexes[set] = + Builder(*getModule()).addVar(getFunction(), i32); } - bool isPropagatable(SetLocal* set) { - return propagatable.count(set); - } + bool isPropagatable(SetLocal* set) { return propagatable.count(set); } private: bool propagated; @@ -305,15 +318,16 @@ private: std::set<SetLocal*> propagatable; void findPropagatable() { - // Conservatively, only propagate if all uses can be removed of the original. That is, + // Conservatively, only propagate if all uses can be removed of the + // original. That is, // x = a + 10 // f(x) // g(x) // should be optimized to // f(a, offset=10) // g(a, offset=10) - // but if x has other uses, then avoid doing so - we'll be doing that add anyhow, so - // the load/store offset trick won't actually help. + // but if x has other uses, then avoid doing so - we'll be doing that add + // anyhow, so the load/store offset trick won't actually help. Parents parents(getFunction()->body); for (auto& pair : localGraph->locations) { auto* location = pair.first; @@ -323,9 +337,11 @@ private: if (add->left->is<Const>() || add->right->is<Const>()) { // Looks like this might be relevant, check all uses. bool canPropagate = true; - for (auto* get :localGraph->setInfluences[set]) { + for (auto* get : localGraph->setInfluences[set]) { auto* parent = parents.getParent(get); - assert(parent); // if this is at the top level, it's the whole body - no set can exist! + // if this is at the top level, it's the whole body - no set can + // exist! + assert(parent); if (!(parent->is<Load>() || parent->is<Store>())) { canPropagate = false; break; @@ -342,8 +358,8 @@ private: } void cleanUpAfterPropagation() { - // Remove sets that no longer have uses. This allows further propagation by letting - // us see the accurate amount of uses of each set. + // Remove sets that no longer have uses. This allows further propagation by + // letting us see the accurate amount of uses of each set. UnneededSetRemover remover(getFunction(), getPassOptions()); } @@ -354,7 +370,8 @@ private: std::map<SetLocal*, Index>& helperIndexes; Module* module; - Creator(std::map<SetLocal*, Index>& helperIndexes) : helperIndexes(helperIndexes) {} + Creator(std::map<SetLocal*, Index>& helperIndexes) + : helperIndexes(helperIndexes) {} void visitSetLocal(SetLocal* curr) { auto iter = helperIndexes.find(curr); @@ -372,11 +389,7 @@ private: Builder builder(*module); *target = builder.makeGetLocal(index, i32); replaceCurrent( - builder.makeSequence( - builder.makeSetLocal(index, value), - curr - ) - ); + builder.makeSequence(builder.makeSetLocal(index, value), curr)); } } } creator(helperIndexes); @@ -385,13 +398,12 @@ private: } }; -Pass *createOptimizeAddedConstantsPass() { +Pass* createOptimizeAddedConstantsPass() { return new OptimizeAddedConstants(false); } -Pass *createOptimizeAddedConstantsPropagatePass() { +Pass* createOptimizeAddedConstantsPropagatePass() { return new OptimizeAddedConstants(true); } } // namespace wasm - diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index c098d0ed7..8a9309554 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -20,28 +20,29 @@ #include <algorithm> -#include <wasm.h> -#include <pass.h> -#include <wasm-s-parser.h> -#include <support/threads.h> #include <ir/abstract.h> -#include <ir/utils.h> #include <ir/cost.h> #include <ir/effects.h> -#include <ir/manipulation.h> -#include <ir/properties.h> #include <ir/literal-utils.h> #include <ir/load-utils.h> +#include <ir/manipulation.h> +#include <ir/properties.h> +#include <ir/utils.h> +#include <pass.h> +#include <support/threads.h> +#include <wasm-s-parser.h> +#include <wasm.h> -// TODO: Use the new sign-extension opcodes where appropriate. This needs to be conditionalized on the availability of atomics. +// TODO: Use the new sign-extension opcodes where appropriate. This needs to be +// conditionalized on the availability of atomics. namespace wasm { -Name I32_EXPR = "i32.expr", - I64_EXPR = "i64.expr", - F32_EXPR = "f32.expr", - F64_EXPR = "f64.expr", - ANY_EXPR = "any.expr"; +Name I32_EXPR = "i32.expr"; +Name I64_EXPR = "i64.expr"; +Name F32_EXPR = "f32.expr"; +Name F64_EXPR = "f64.expr"; +Name ANY_EXPR = "any.expr"; // Utilities @@ -53,28 +54,47 @@ template<typename LocalInfoProvider> Index getMaxBits(Expression* curr, LocalInfoProvider* localInfoProvider) { if (auto* const_ = curr->dynCast<Const>()) { switch (curr->type) { - case i32: return 32 - const_->value.countLeadingZeroes().geti32(); - case i64: return 64 - const_->value.countLeadingZeroes().geti64(); - default: WASM_UNREACHABLE(); + case i32: + return 32 - const_->value.countLeadingZeroes().geti32(); + case i64: + return 64 - const_->value.countLeadingZeroes().geti64(); + default: + WASM_UNREACHABLE(); } } else if (auto* binary = curr->dynCast<Binary>()) { switch (binary->op) { // 32-bit - case AddInt32: case SubInt32: case MulInt32: - case DivSInt32: case DivUInt32: case RemSInt32: - case RemUInt32: case RotLInt32: case RotRInt32: return 32; - case AndInt32: return std::min(getMaxBits(binary->left, localInfoProvider), getMaxBits(binary->right, localInfoProvider)); - case OrInt32: case XorInt32: return std::max(getMaxBits(binary->left, localInfoProvider), getMaxBits(binary->right, localInfoProvider)); + case AddInt32: + case SubInt32: + case MulInt32: + case DivSInt32: + case DivUInt32: + case RemSInt32: + case RemUInt32: + case RotLInt32: + case RotRInt32: + return 32; + case AndInt32: + return std::min(getMaxBits(binary->left, localInfoProvider), + getMaxBits(binary->right, localInfoProvider)); + case OrInt32: + case XorInt32: + return std::max(getMaxBits(binary->left, localInfoProvider), + getMaxBits(binary->right, localInfoProvider)); case ShlInt32: { if (auto* shifts = binary->right->dynCast<Const>()) { - return std::min(Index(32), getMaxBits(binary->left, localInfoProvider) + Bits::getEffectiveShifts(shifts)); + return std::min(Index(32), + getMaxBits(binary->left, localInfoProvider) + + Bits::getEffectiveShifts(shifts)); } return 32; } case ShrUInt32: { if (auto* shift = binary->right->dynCast<Const>()) { auto maxBits = getMaxBits(binary->left, localInfoProvider); - auto shifts = std::min(Index(Bits::getEffectiveShifts(shift)), maxBits); // can ignore more shifts than zero us out + auto shifts = + std::min(Index(Bits::getEffectiveShifts(shift)), + maxBits); // can ignore more shifts than zero us out return std::max(Index(0), maxBits - shifts); } return 32; @@ -82,34 +102,67 @@ Index getMaxBits(Expression* curr, LocalInfoProvider* localInfoProvider) { case ShrSInt32: { if (auto* shift = binary->right->dynCast<Const>()) { auto maxBits = getMaxBits(binary->left, localInfoProvider); - if (maxBits == 32) return 32; - auto shifts = std::min(Index(Bits::getEffectiveShifts(shift)), maxBits); // can ignore more shifts than zero us out + if (maxBits == 32) + return 32; + auto shifts = + std::min(Index(Bits::getEffectiveShifts(shift)), + maxBits); // can ignore more shifts than zero us out return std::max(Index(0), maxBits - shifts); } return 32; } // 64-bit TODO // comparisons - case EqInt32: case NeInt32: case LtSInt32: - case LtUInt32: case LeSInt32: case LeUInt32: - case GtSInt32: case GtUInt32: case GeSInt32: + case EqInt32: + case NeInt32: + case LtSInt32: + case LtUInt32: + case LeSInt32: + case LeUInt32: + case GtSInt32: + case GtUInt32: + case GeSInt32: case GeUInt32: - case EqInt64: case NeInt64: case LtSInt64: - case LtUInt64: case LeSInt64: case LeUInt64: - case GtSInt64: case GtUInt64: case GeSInt64: + case EqInt64: + case NeInt64: + case LtSInt64: + case LtUInt64: + case LeSInt64: + case LeUInt64: + case GtSInt64: + case GtUInt64: + case GeSInt64: case GeUInt64: - case EqFloat32: case NeFloat32: - case LtFloat32: case LeFloat32: case GtFloat32: case GeFloat32: - case EqFloat64: case NeFloat64: - case LtFloat64: case LeFloat64: case GtFloat64: case GeFloat64: return 1; + case EqFloat32: + case NeFloat32: + case LtFloat32: + case LeFloat32: + case GtFloat32: + case GeFloat32: + case EqFloat64: + case NeFloat64: + case LtFloat64: + case LeFloat64: + case GtFloat64: + case GeFloat64: + return 1; default: {} } } else if (auto* unary = curr->dynCast<Unary>()) { switch (unary->op) { - case ClzInt32: case CtzInt32: case PopcntInt32: return 6; - case ClzInt64: case CtzInt64: case PopcntInt64: return 7; - case EqZInt32: case EqZInt64: return 1; - case WrapInt64: return std::min(Index(32), getMaxBits(unary->value, localInfoProvider)); + case ClzInt32: + case CtzInt32: + case PopcntInt32: + return 6; + case ClzInt64: + case CtzInt64: + case PopcntInt64: + return 7; + case EqZInt32: + case EqZInt64: + return 1; + case WrapInt64: + return std::min(Index(32), getMaxBits(unary->value, localInfoProvider)); default: {} } } else if (auto* set = curr->dynCast<SetLocal>()) { @@ -125,10 +178,14 @@ Index getMaxBits(Expression* curr, LocalInfoProvider* localInfoProvider) { } } switch (curr->type) { - case i32: return 32; - case i64: return 64; - case unreachable: return 64; // not interesting, but don't crash - default: WASM_UNREACHABLE(); + case i32: + return 32; + case i64: + return 64; + case unreachable: + return 64; // not interesting, but don't crash + default: + WASM_UNREACHABLE(); } } @@ -170,9 +227,11 @@ struct LocalScanner : PostWalker<LocalScanner> { void visitSetLocal(SetLocal* curr) { auto* func = getFunction(); - if (func->isParam(curr->index)) return; + if (func->isParam(curr->index)) + return; auto type = getFunction()->getLocalType(curr->index); - if (type != i32 && type != i64) return; + if (type != i32 && type != i64) + return; // an integer var, worth processing auto* value = Properties::getFallthrough(curr->value); auto& info = localInfo[curr->index]; @@ -188,26 +247,32 @@ struct LocalScanner : PostWalker<LocalScanner> { if (info.signExtedBits == 0) { info.signExtedBits = signExtBits; // first info we see } else if (info.signExtedBits != signExtBits) { - info.signExtedBits = LocalInfo::kUnknown; // contradictory information, give up + // contradictory information, give up + info.signExtedBits = LocalInfo::kUnknown; } } - // define this for the templated getMaxBits method. we know nothing here yet about locals, so return the maxes - Index getMaxBitsForLocal(GetLocal* get) { - return getBitsForType(get->type); - } + // define this for the templated getMaxBits method. we know nothing here yet + // about locals, so return the maxes + Index getMaxBitsForLocal(GetLocal* get) { return getBitsForType(get->type); } Index getBitsForType(Type type) { switch (type) { - case i32: return 32; - case i64: return 64; - default: return -1; + case i32: + return 32; + case i64: + return 64; + default: + return -1; } } }; // Main pass class -struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, UnifiedExpressionVisitor<OptimizeInstructions>>> { +struct OptimizeInstructions + : public WalkerPass< + PostWalker<OptimizeInstructions, + UnifiedExpressionVisitor<OptimizeInstructions>>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new OptimizeInstructions; } @@ -229,7 +294,8 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, } void visitExpression(Expression* curr) { - // we may be able to apply multiple patterns, one may open opportunities that look deeper NB: patterns must not have cycles + // we may be able to apply multiple patterns, one may open opportunities + // that look deeper NB: patterns must not have cycles while (1) { auto* handOptimized = handOptimize(curr); if (handOptimized) { @@ -258,14 +324,15 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, } } - // Optimizations that don't yet fit in the pattern DSL, but could be eventually maybe + // Optimizations that don't yet fit in the pattern DSL, but could be + // eventually maybe Expression* handOptimize(Expression* curr) { // if this contains dead code, don't bother trying to optimize it, the type - // might change (if might not be unreachable if just one arm is, for example). - // this optimization pass focuses on actually executing code. the only - // exceptions are control flow changes - if (curr->type == unreachable && - !curr->is<Break>() && !curr->is<Switch>() && !curr->is<If>()) { + // might change (if might not be unreachable if just one arm is, for + // example). this optimization pass focuses on actually executing code. the + // only exceptions are control flow changes + if (curr->type == unreachable && !curr->is<Break>() && + !curr->is<Switch>() && !curr->is<If>()) { return nullptr; } if (auto* binary = curr->dynCast<Binary>()) { @@ -277,10 +344,13 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, auto bits = Properties::getAlmostSignExtBits(binary, extraShifts); if (extraShifts == 0) { if (auto* load = Properties::getFallthrough(ext)->dynCast<Load>()) { - // pattern match a load of 8 bits and a sign extend using a shl of 24 then shr_s of 24 as well, etc. + // pattern match a load of 8 bits and a sign extend using a shl of + // 24 then shr_s of 24 as well, etc. if (LoadUtils::canBeSigned(load) && - ((load->bytes == 1 && bits == 8) || (load->bytes == 2 && bits == 16))) { - // if the value falls through, we can't alter the load, as it might be captured in a tee + ((load->bytes == 1 && bits == 8) || + (load->bytes == 2 && bits == 16))) { + // if the value falls through, we can't alter the load, as it + // might be captured in a tee if (load->signed_ == true || load == ext) { load->signed_ = true; return ext; @@ -289,8 +359,10 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, } } // if the sign-extend input cannot have a sign bit, we don't need it - // we also don't need it if it already has an identical-sized sign extend - if (getMaxBits(ext, this) + extraShifts < bits || isSignExted(ext, bits)) { + // we also don't need it if it already has an identical-sized sign + // extend + if (getMaxBits(ext, this) + extraShifts < bits || + isSignExted(ext, bits)) { return removeAlmostSignExt(binary); } } else if (binary->op == EqInt32 || binary->op == NeInt32) { @@ -300,34 +372,44 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, return Builder(*getModule()).makeUnary(EqZInt32, binary->left); } if (auto* ext = Properties::getSignExtValue(binary->left)) { - // we are comparing a sign extend to a constant, which means we can use a cheaper zext + // we are comparing a sign extend to a constant, which means we can + // use a cheaper zext auto bits = Properties::getSignExtBits(binary->left); binary->left = makeZeroExt(ext, bits); - // when we replace the sign-ext of the non-constant with a zero-ext, we are forcing - // the high bits to be all zero, instead of all zero or all one depending on the - // sign bit. so we may be changing the high bits from all one to all zero: - // * if the constant value's higher bits are mixed, then it can't be equal anyhow - // * if they are all zero, we may get a false true if the non-constant's upper bits - // were one. this can only happen if the non-constant's sign bit is set, so this - // false true is a risk only if the constant's sign bit is set (otherwise, false). - // But a constant with a sign bit but with upper bits zero is impossible to be - // equal to a sign-extended value anyhow, so the entire thing is false. - // * if they were all one, we may get a false false, if the only difference is in - // those upper bits. that means we are equal on the other bits, including the sign - // bit. so we can just mask off the upper bits in the constant value, in this - // case, forcing them to zero like we do in the zero-extend. + // when we replace the sign-ext of the non-constant with a zero-ext, + // we are forcing the high bits to be all zero, instead of all zero + // or all one depending on the sign bit. so we may be changing the + // high bits from all one to all zero: + // * if the constant value's higher bits are mixed, then it can't + // be equal anyhow + // * if they are all zero, we may get a false true if the + // non-constant's upper bits were one. this can only happen if + // the non-constant's sign bit is set, so this false true is a + // risk only if the constant's sign bit is set (otherwise, + // false). But a constant with a sign bit but with upper bits + // zero is impossible to be equal to a sign-extended value + // anyhow, so the entire thing is false. + // * if they were all one, we may get a false false, if the only + // difference is in those upper bits. that means we are equal on + // the other bits, including the sign bit. so we can just mask + // off the upper bits in the constant value, in this case, + // forcing them to zero like we do in the zero-extend. int32_t constValue = c->value.geti32(); auto upperConstValue = constValue & ~Bits::lowBitMask(bits); uint32_t count = PopCount(upperConstValue); auto constSignBit = constValue & (1 << (bits - 1)); - if ((count > 0 && count < 32 - bits) || (constSignBit && count == 0)) { - // mixed or [zero upper const bits with sign bit set]; the compared values can never be identical, so - // force something definitely impossible even after zext + if ((count > 0 && count < 32 - bits) || + (constSignBit && count == 0)) { + // mixed or [zero upper const bits with sign bit set]; the + // compared values can never be identical, so force something + // definitely impossible even after zext assert(bits < 32); c->value = Literal(int32_t(0x80000000)); - // TODO: if no side effects, we can just replace it all with 1 or 0 + // TODO: if no side effects, we can just replace it all with 1 or + // 0 } else { - // otherwise, they are all ones, so we can mask them off as mentioned before + // otherwise, they are all ones, so we can mask them off as + // mentioned before c->value = c->value.and_(Literal(Bits::lowBitMask(bits))); } return binary; @@ -336,13 +418,15 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, if (auto* right = Properties::getSignExtValue(binary->right)) { auto bits = Properties::getSignExtBits(binary->left); if (Properties::getSignExtBits(binary->right) == bits) { - // we are comparing two sign-exts with the same bits, so we may as well replace both with cheaper zexts + // we are comparing two sign-exts with the same bits, so we may as + // well replace both with cheaper zexts binary->left = makeZeroExt(left, bits); binary->right = makeZeroExt(right, bits); return binary; } } else if (auto* load = binary->right->dynCast<Load>()) { - // we are comparing a load to a sign-ext, we may be able to switch to zext + // we are comparing a load to a sign-ext, we may be able to switch + // to zext auto leftBits = Properties::getSignExtBits(binary->left); if (load->signed_ && leftBits == load->bytes * 8) { load->signed_ = false; @@ -352,7 +436,8 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, } } else if (auto* load = binary->left->dynCast<Load>()) { if (auto* right = Properties::getSignExtValue(binary->right)) { - // we are comparing a load to a sign-ext, we may be able to switch to zext + // we are comparing a load to a sign-ext, we may be able to switch + // to zext auto rightBits = Properties::getSignExtBits(binary->right); if (load->signed_ && rightBits == load->bytes * 8) { load->signed_ = false; @@ -361,7 +446,8 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, } } } - // note that both left and right may be consts, but then we let precompute compute the constant result + // note that both left and right may be consts, but then we let + // precompute compute the constant result } else if (binary->op == AddInt32) { // try to get rid of (0 - ..), that is, a zero only used to negate an // int. an add of a subtract can be flipped in order to remove it: @@ -382,7 +468,8 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, if (sub->op == SubInt32) { if (auto* subZero = sub->left->dynCast<Const>()) { if (subZero->value.geti32() == 0) { - if (EffectAnalyzer::canReorder(getPassOptions(), sub->right, binary->right)) { + if (EffectAnalyzer::canReorder( + getPassOptions(), sub->right, binary->right)) { sub->left = binary->right; return sub; } @@ -414,10 +501,12 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, } } auto* ret = optimizeAddedConstants(binary); - if (ret) return ret; + if (ret) + return ret; } else if (binary->op == SubInt32) { auto* ret = optimizeAddedConstants(binary); - if (ret) return ret; + if (ret) + return ret; } // a bunch of operations on a constant right side can be simplified if (auto* right = binary->right->dynCast<Const>()) { @@ -443,7 +532,8 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, } // some math operations have trivial results Expression* ret = optimizeWithConstantOnRight(binary); - if (ret) return ret; + if (ret) + return ret; // the square of some operations can be merged if (auto* left = binary->left->dynCast<Binary>()) { if (left->op == binary->op) { @@ -454,11 +544,13 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, } else if (left->op == OrInt32) { leftRight->value = leftRight->value.or_(right->value); return left; - } else if (left->op == ShlInt32 || left->op == ShrUInt32 || left->op == ShrSInt32 || - left->op == ShlInt64 || left->op == ShrUInt64 || left->op == ShrSInt64) { - // shifts only use an effective amount from the constant, so adding must - // be done carefully - auto total = Bits::getEffectiveShifts(leftRight) + Bits::getEffectiveShifts(right); + } else if (left->op == ShlInt32 || left->op == ShrUInt32 || + left->op == ShrSInt32 || left->op == ShlInt64 || + left->op == ShrUInt64 || left->op == ShrSInt64) { + // shifts only use an effective amount from the constant, so + // adding must be done carefully + auto total = Bits::getEffectiveShifts(leftRight) + + Bits::getEffectiveShifts(right); if (total == Bits::getEffectiveShifts(total, right->type)) { // no overflow, we can do this leftRight->value = Literal::makeFromInt32(total, right->type); @@ -483,7 +575,8 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, // a bunch of operations on a constant left side can be simplified if (binary->left->is<Const>()) { Expression* ret = optimizeWithConstantOnLeft(binary); - if (ret) return ret; + if (ret) + return ret; } // bitwise operations if (binary->op == AndInt32) { @@ -540,40 +633,89 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, if (unary->op == EqZInt32) { if (auto* inner = unary->value->dynCast<Binary>()) { switch (inner->op) { - case EqInt32: inner->op = NeInt32; return inner; - case NeInt32: inner->op = EqInt32; return inner; - case LtSInt32: inner->op = GeSInt32; return inner; - case LtUInt32: inner->op = GeUInt32; return inner; - case LeSInt32: inner->op = GtSInt32; return inner; - case LeUInt32: inner->op = GtUInt32; return inner; - case GtSInt32: inner->op = LeSInt32; return inner; - case GtUInt32: inner->op = LeUInt32; return inner; - case GeSInt32: inner->op = LtSInt32; return inner; - case GeUInt32: inner->op = LtUInt32; return inner; + case EqInt32: + inner->op = NeInt32; + return inner; + case NeInt32: + inner->op = EqInt32; + return inner; + case LtSInt32: + inner->op = GeSInt32; + return inner; + case LtUInt32: + inner->op = GeUInt32; + return inner; + case LeSInt32: + inner->op = GtSInt32; + return inner; + case LeUInt32: + inner->op = GtUInt32; + return inner; + case GtSInt32: + inner->op = LeSInt32; + return inner; + case GtUInt32: + inner->op = LeUInt32; + return inner; + case GeSInt32: + inner->op = LtSInt32; + return inner; + case GeUInt32: + inner->op = LtUInt32; + return inner; - case EqInt64: inner->op = NeInt64; return inner; - case NeInt64: inner->op = EqInt64; return inner; - case LtSInt64: inner->op = GeSInt64; return inner; - case LtUInt64: inner->op = GeUInt64; return inner; - case LeSInt64: inner->op = GtSInt64; return inner; - case LeUInt64: inner->op = GtUInt64; return inner; - case GtSInt64: inner->op = LeSInt64; return inner; - case GtUInt64: inner->op = LeUInt64; return inner; - case GeSInt64: inner->op = LtSInt64; return inner; - case GeUInt64: inner->op = LtUInt64; return inner; + case EqInt64: + inner->op = NeInt64; + return inner; + case NeInt64: + inner->op = EqInt64; + return inner; + case LtSInt64: + inner->op = GeSInt64; + return inner; + case LtUInt64: + inner->op = GeUInt64; + return inner; + case LeSInt64: + inner->op = GtSInt64; + return inner; + case LeUInt64: + inner->op = GtUInt64; + return inner; + case GtSInt64: + inner->op = LeSInt64; + return inner; + case GtUInt64: + inner->op = LeUInt64; + return inner; + case GeSInt64: + inner->op = LtSInt64; + return inner; + case GeUInt64: + inner->op = LtUInt64; + return inner; - case EqFloat32: inner->op = NeFloat32; return inner; - case NeFloat32: inner->op = EqFloat32; return inner; + case EqFloat32: + inner->op = NeFloat32; + return inner; + case NeFloat32: + inner->op = EqFloat32; + return inner; - case EqFloat64: inner->op = NeFloat64; return inner; - case NeFloat64: inner->op = EqFloat64; return inner; + case EqFloat64: + inner->op = NeFloat64; + return inner; + case NeFloat64: + inner->op = EqFloat64; + return inner; default: {} } } // eqz of a sign extension can be of zero-extension if (auto* ext = Properties::getSignExtValue(unary->value)) { - // we are comparing a sign extend to a constant, which means we can use a cheaper zext + // we are comparing a sign extend to a constant, which means we can + // use a cheaper zext auto bits = Properties::getSignExtBits(unary->value); unary->value = makeZeroExt(ext, bits); return unary; @@ -595,24 +737,26 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, std::swap(iff->ifTrue, iff->ifFalse); } } - if (iff->condition->type != unreachable && ExpressionAnalyzer::equal(iff->ifTrue, iff->ifFalse)) { + if (iff->condition->type != unreachable && + ExpressionAnalyzer::equal(iff->ifTrue, iff->ifFalse)) { // sides are identical, fold - // if we can replace the if with one arm, and no side effects in the condition, do that - auto needCondition = EffectAnalyzer(getPassOptions(), iff->condition).hasSideEffects(); + // if we can replace the if with one arm, and no side effects in the + // condition, do that + auto needCondition = + EffectAnalyzer(getPassOptions(), iff->condition).hasSideEffects(); auto typeIsIdentical = iff->ifTrue->type == iff->type; if (typeIsIdentical && !needCondition) { return iff->ifTrue; } else { Builder builder(*getModule()); if (typeIsIdentical) { - return builder.makeSequence( - builder.makeDrop(iff->condition), - iff->ifTrue - ); + return builder.makeSequence(builder.makeDrop(iff->condition), + iff->ifTrue); } else { - // the types diff. as the condition is reachable, that means the if must be - // concrete while the arm is not - assert(isConcreteType(iff->type) && iff->ifTrue->type == unreachable); + // the types diff. as the condition is reachable, that means the + // if must be concrete while the arm is not + assert(isConcreteType(iff->type) && + iff->ifTrue->type == unreachable); // emit a block with a forced type auto* ret = builder.makeBlock(); if (needCondition) { @@ -638,22 +782,24 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, } } if (auto* c = select->condition->dynCast<Const>()) { - // constant condition, we can just pick the right side (barring side effects) + // constant condition, we can just pick the right side (barring side + // effects) if (c->value.getInteger()) { - if (!EffectAnalyzer(getPassOptions(), select->ifFalse).hasSideEffects()) { + if (!EffectAnalyzer(getPassOptions(), select->ifFalse) + .hasSideEffects()) { return select->ifTrue; } else { - // don't bother - we would need to reverse the order using a temp local, which is bad + // don't bother - we would need to reverse the order using a temp + // local, which is bad } } else { - if (!EffectAnalyzer(getPassOptions(), select->ifTrue).hasSideEffects()) { + if (!EffectAnalyzer(getPassOptions(), select->ifTrue) + .hasSideEffects()) { return select->ifFalse; } else { Builder builder(*getModule()); - return builder.makeSequence( - builder.makeDrop(select->ifTrue), - select->ifFalse - ); + return builder.makeSequence(builder.makeDrop(select->ifTrue), + select->ifFalse); } } } @@ -676,10 +822,8 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, // can reorder if (!condition.invalidates(value)) { Builder builder(*getModule()); - return builder.makeSequence( - builder.makeDrop(select->condition), - select->ifTrue - ); + return builder.makeSequence(builder.makeDrop(select->condition), + select->ifTrue); } } } @@ -705,8 +849,9 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, } } } else if (auto* ext = Properties::getSignExtValue(binary)) { - // if sign extending the exact bit size we store, we can skip the extension - // if extending something bigger, then we just alter bits we don't save anyhow + // if sign extending the exact bit size we store, we can skip the + // extension if extending something bigger, then we just alter bits we + // don't save anyhow if (Properties::getSignExtBits(binary) >= Index(store->bytes) * 8) { store->value = ext; } @@ -736,11 +881,13 @@ private: void canonicalize(Binary* binary) { assert(Properties::isSymmetric(binary)); auto swap = [&]() { - assert(EffectAnalyzer::canReorder(getPassOptions(), binary->left, binary->right)); + assert(EffectAnalyzer::canReorder( + getPassOptions(), binary->left, binary->right)); std::swap(binary->left, binary->right); }; auto maybeSwap = [&]() { - if (EffectAnalyzer::canReorder(getPassOptions(), binary->left, binary->right)) { + if (EffectAnalyzer::canReorder( + getPassOptions(), binary->left, binary->right)) { swap(); } }; @@ -748,7 +895,8 @@ private: if (binary->left->is<Const>() && !binary->right->is<Const>()) { return swap(); } - if (binary->right->is<Const>()) return; + if (binary->right->is<Const>()) + return; // Prefer a get on the right. if (binary->left->is<GetLocal>() && !binary->right->is<GetLocal>()) { return maybeSwap(); @@ -793,7 +941,8 @@ private: } } else if (auto* binary = boolean->dynCast<Binary>()) { if (binary->op == OrInt32) { - // an or flowing into a boolean context can consider each input as boolean + // an or flowing into a boolean context can consider each input as + // boolean binary->left = optimizeBoolean(binary->left); binary->right = optimizeBoolean(binary->right); } else if (binary->op == NeInt32) { @@ -805,7 +954,8 @@ private: } } if (auto* ext = Properties::getSignExtValue(binary)) { - // use a cheaper zero-extent, we just care about the boolean value anyhow + // use a cheaper zero-extent, we just care about the boolean value + // anyhow return makeZeroExt(ext, Properties::getSignExtBits(binary)); } } else if (auto* block = boolean->dynCast<Block>()) { @@ -822,12 +972,14 @@ private: return boolean; } - // find added constants in an expression tree, including multiplied/shifted, and combine them - // note that we ignore division/shift-right, as rounding makes this nonlinear, so not a valid opt + // find added constants in an expression tree, including multiplied/shifted, + // and combine them note that we ignore division/shift-right, as rounding + // makes this nonlinear, so not a valid opt Expression* optimizeAddedConstants(Binary* binary) { uint32_t constant = 0; std::vector<Const*> constants; - std::function<void (Expression*, int)> seek = [&](Expression* curr, int mul) { + std::function<void(Expression*, int)> seek = [&](Expression* curr, + int mul) { if (auto* c = curr->dynCast<Const>()) { uint32_t value = c->value.geti32(); if (value != 0) { @@ -867,7 +1019,8 @@ private: // find all factors seek(binary, 1); if (constants.size() <= 1) { - // nothing much to do, except for the trivial case of adding/subbing a zero + // nothing much to do, except for the trivial case of adding/subbing a + // zero if (auto* c = binary->right->dynCast<Const>()) { if (c->value.geti32() == 0) { return binary->left; @@ -906,19 +1059,24 @@ private: return; } } else if (curr->op == ShlInt32) { - // shifting a 0 is a 0, or anything by 0 has no effect, all unless the shift has side effects - if (((left && left->value.geti32() == 0) || (right && Bits::getEffectiveShifts(right) == 0)) && + // shifting a 0 is a 0, or anything by 0 has no effect, all unless the + // shift has side effects + if (((left && left->value.geti32() == 0) || + (right && Bits::getEffectiveShifts(right) == 0)) && !EffectAnalyzer(passOptions, curr->right).hasSideEffects()) { replaceCurrent(curr->left); return; } } else if (curr->op == MulInt32) { - // multiplying by zero is a zero, unless the other side has side effects - if (left && left->value.geti32() == 0 && !EffectAnalyzer(passOptions, curr->right).hasSideEffects()) { + // multiplying by zero is a zero, unless the other side has side + // effects + if (left && left->value.geti32() == 0 && + !EffectAnalyzer(passOptions, curr->right).hasSideEffects()) { replaceCurrent(left); return; } - if (right && right->value.geti32() == 0 && !EffectAnalyzer(passOptions, curr->left).hasSideEffects()) { + if (right && right->value.geti32() == 0 && + !EffectAnalyzer(passOptions, curr->left).hasSideEffects()) { replaceCurrent(right); return; } @@ -927,50 +1085,58 @@ private: }; Expression* walked = binary; ZeroRemover(getPassOptions()).walk(walked); - if (constant == 0) return walked; // nothing more to do + if (constant == 0) + return walked; // nothing more to do if (auto* c = walked->dynCast<Const>()) { assert(c->value.geti32() == 0); c->value = Literal(constant); return c; } Builder builder(*getModule()); - return builder.makeBinary(AddInt32, - walked, - builder.makeConst(Literal(constant)) - ); + return builder.makeBinary( + AddInt32, walked, builder.makeConst(Literal(constant))); } - // expensive1 | expensive2 can be turned into expensive1 ? 1 : expensive2, and - // expensive | cheap can be turned into cheap ? 1 : expensive, + // expensive1 | expensive2 can be turned into expensive1 ? 1 : expensive2, + // and expensive | cheap can be turned into cheap ? 1 : expensive, // so that we can avoid one expensive computation, if it has no side effects. Expression* conditionalizeExpensiveOnBitwise(Binary* binary) { // this operation can increase code size, so don't always do it auto& options = getPassRunner()->options; - if (options.optimizeLevel < 2 || options.shrinkLevel > 0) return nullptr; + if (options.optimizeLevel < 2 || options.shrinkLevel > 0) + return nullptr; const auto MIN_COST = 7; assert(binary->op == AndInt32 || binary->op == OrInt32); - if (binary->right->is<Const>()) return nullptr; // trivial - // bitwise logical operator on two non-numerical values, check if they are boolean + if (binary->right->is<Const>()) + return nullptr; // trivial + // bitwise logical operator on two non-numerical values, check if they are + // boolean auto* left = binary->left; auto* right = binary->right; - if (!Properties::emitsBoolean(left) || !Properties::emitsBoolean(right)) return nullptr; + if (!Properties::emitsBoolean(left) || !Properties::emitsBoolean(right)) + return nullptr; auto leftEffects = EffectAnalyzer(getPassOptions(), left); auto rightEffects = EffectAnalyzer(getPassOptions(), right); auto leftHasSideEffects = leftEffects.hasSideEffects(); auto rightHasSideEffects = rightEffects.hasSideEffects(); - if (leftHasSideEffects && rightHasSideEffects) return nullptr; // both must execute + if (leftHasSideEffects && rightHasSideEffects) + return nullptr; // both must execute // canonicalize with side effects, if any, happening on the left if (rightHasSideEffects) { - if (CostAnalyzer(left).cost < MIN_COST) return nullptr; // avoidable code is too cheap - if (leftEffects.invalidates(rightEffects)) return nullptr; // cannot reorder + if (CostAnalyzer(left).cost < MIN_COST) + return nullptr; // avoidable code is too cheap + if (leftEffects.invalidates(rightEffects)) + return nullptr; // cannot reorder std::swap(left, right); } else if (leftHasSideEffects) { - if (CostAnalyzer(right).cost < MIN_COST) return nullptr; // avoidable code is too cheap + if (CostAnalyzer(right).cost < MIN_COST) + return nullptr; // avoidable code is too cheap } else { // no side effects, reorder based on cost estimation auto leftCost = CostAnalyzer(left).cost; auto rightCost = CostAnalyzer(right).cost; - if (std::max(leftCost, rightCost) < MIN_COST) return nullptr; // avoidable code is too cheap + if (std::max(leftCost, rightCost) < MIN_COST) + return nullptr; // avoidable code is too cheap // canonicalize with expensive code on the right if (leftCost > rightCost) { std::swap(left, right); @@ -979,9 +1145,11 @@ private: // worth it! perform conditionalization Builder builder(*getModule()); if (binary->op == OrInt32) { - return builder.makeIf(left, builder.makeConst(Literal(int32_t(1))), right); + return builder.makeIf( + left, builder.makeConst(Literal(int32_t(1))), right); } else { // & - return builder.makeIf(left, right, builder.makeConst(Literal(int32_t(0)))); + return builder.makeIf( + left, right, builder.makeConst(Literal(int32_t(0)))); } } @@ -1015,8 +1183,9 @@ private: // fold constant factors into the offset void optimizeMemoryAccess(Expression*& ptr, Address& offset) { - // ptr may be a const, but it isn't worth folding that in (we still have a const); in fact, - // it's better to do the opposite for gzip purposes as well as for readability. + // ptr may be a const, but it isn't worth folding that in (we still have a + // const); in fact, it's better to do the opposite for gzip purposes as well + // as for readability. auto* last = ptr->dynCast<Const>(); if (last) { // don't do this if it would wrap the pointer @@ -1058,7 +1227,8 @@ private: Expression* makeZeroExt(Expression* curr, int32_t bits) { Builder builder(*getModule()); - return builder.makeBinary(AndInt32, curr, builder.makeConst(Literal(Bits::lowBitMask(bits)))); + return builder.makeBinary( + AndInt32, curr, builder.makeConst(Literal(Bits::lowBitMask(bits)))); } // given an "almost" sign extend - either a proper one, or it @@ -1070,7 +1240,8 @@ private: auto* outerConst = outer->right->cast<Const>(); auto* innerConst = inner->right->cast<Const>(); auto* value = inner->left; - if (outerConst->value == innerConst->value) return value; + if (outerConst->value == innerConst->value) + return value; // add a shift, by reusing the existing node innerConst->value = innerConst->value.sub(outerConst->value); return inner; @@ -1105,7 +1276,8 @@ private: return binary->left; } else if ((binary->op == Abstract::getBinary(type, Abstract::Mul) || binary->op == Abstract::getBinary(type, Abstract::And)) && - !EffectAnalyzer(getPassOptions(), binary->left).hasSideEffects()) { + !EffectAnalyzer(getPassOptions(), binary->left) + .hasSideEffects()) { return binary->right; } } @@ -1116,7 +1288,8 @@ private: if (binary->op == Abstract::getBinary(type, Abstract::And)) { return binary->left; } else if (binary->op == Abstract::getBinary(type, Abstract::Or) && - !EffectAnalyzer(getPassOptions(), binary->left).hasSideEffects()) { + !EffectAnalyzer(getPassOptions(), binary->left) + .hasSideEffects()) { return binary->right; } } @@ -1129,15 +1302,10 @@ private: if (binary->op == Abstract::getBinary(type, Abstract::Add) || binary->op == Abstract::getBinary(type, Abstract::Sub)) { auto value = right->value.getInteger(); - if (value == 0x40 || - value == 0x2000 || - value == 0x100000 || - value == 0x8000000 || - value == 0x400000000LL || - value == 0x20000000000LL || - value == 0x1000000000000LL || - value == 0x80000000000000LL || - value == 0x4000000000000000LL) { + if (value == 0x40 || value == 0x2000 || value == 0x100000 || + value == 0x8000000 || value == 0x400000000LL || + value == 0x20000000000LL || value == 0x1000000000000LL || + value == 0x80000000000000LL || value == 0x4000000000000000LL) { right->value = right->value.neg(); if (binary->op == Abstract::getBinary(type, Abstract::Add)) { binary->op = Abstract::getBinary(type, Abstract::Sub); @@ -1202,12 +1370,16 @@ private: left->op == Abstract::getBinary(type, Abstract::Sub)) { if (auto* leftConst = left->right->dynCast<Const>()) { if (auto* rightConst = binary->right->dynCast<Const>()) { - return combineRelationalConstants(binary, left, leftConst, nullptr, rightConst); + return combineRelationalConstants( + binary, left, leftConst, nullptr, rightConst); } else if (auto* rightBinary = binary->right->dynCast<Binary>()) { - if (rightBinary->op == Abstract::getBinary(type, Abstract::Add) || - rightBinary->op == Abstract::getBinary(type, Abstract::Sub)) { + if (rightBinary->op == + Abstract::getBinary(type, Abstract::Add) || + rightBinary->op == + Abstract::getBinary(type, Abstract::Sub)) { if (auto* rightConst = rightBinary->right->dynCast<Const>()) { - return combineRelationalConstants(binary, left, leftConst, rightBinary, rightConst); + return combineRelationalConstants( + binary, left, leftConst, rightBinary, rightConst); } } } @@ -1220,9 +1392,13 @@ private: } // given a relational binary with a const on both sides, combine the constants - // left is also a binary, and has a constant; right may be just a constant, in which - // case right is nullptr - Expression* combineRelationalConstants(Binary* binary, Binary* left, Const* leftConst, Binary* right, Const* rightConst) { + // left is also a binary, and has a constant; right may be just a constant, in + // which case right is nullptr + Expression* combineRelationalConstants(Binary* binary, + Binary* left, + Const* leftConst, + Binary* right, + Const* rightConst) { auto type = binary->right->type; // we fold constants to the right Literal extra = leftConst->value; @@ -1237,8 +1413,8 @@ private: return binary; } - // given a binary expression with equal children and no side effects in either, - // we can fold various things + // given a binary expression with equal children and no side effects in + // either, we can fold various things // TODO: trinaries, things like (x & (y & x)) ? Expression* optimizeBinaryWithEqualEffectlessChildren(Binary* binary) { // TODO add: perhaps worth doing 2*x if x is quite large? @@ -1246,7 +1422,8 @@ private: case SubInt32: case XorInt32: case SubInt64: - case XorInt64: return LiteralUtils::makeZero(binary->left->type, *getModule()); + case XorInt64: + return LiteralUtils::makeZero(binary->left->type, *getModule()); case NeInt64: case LtSInt64: case LtUInt64: @@ -1256,11 +1433,13 @@ private: case LtSInt32: case LtUInt32: case GtSInt32: - case GtUInt32: return LiteralUtils::makeZero(i32, *getModule()); + case GtUInt32: + return LiteralUtils::makeZero(i32, *getModule()); case AndInt32: case OrInt32: case AndInt64: - case OrInt64: return binary->left; + case OrInt64: + return binary->left; case EqInt32: case LeSInt32: case LeUInt32: @@ -1270,14 +1449,14 @@ private: case LeSInt64: case LeUInt64: case GeSInt64: - case GeUInt64: return LiteralUtils::makeFromInt32(1, i32, *getModule()); - default: return nullptr; + case GeUInt64: + return LiteralUtils::makeFromInt32(1, i32, *getModule()); + default: + return nullptr; } } }; -Pass *createOptimizeInstructionsPass() { - return new OptimizeInstructions(); -} +Pass* createOptimizeInstructionsPass() { return new OptimizeInstructions(); } } // namespace wasm diff --git a/src/passes/PickLoadSigns.cpp b/src/passes/PickLoadSigns.cpp index fce50b4bb..f494159a1 100644 --- a/src/passes/PickLoadSigns.cpp +++ b/src/passes/PickLoadSigns.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#include <wasm.h> -#include <pass.h> #include <ir/properties.h> +#include <pass.h> +#include <wasm.h> namespace wasm { @@ -39,7 +39,8 @@ struct PickLoadSigns : public WalkerPass<ExpressionStackWalker<PickLoadSigns>> { }; std::vector<Usage> usages; // local index => usage - std::unordered_map<Load*, Index> loads; // loads that write to a local => the local + // loads that write to a local => the local + std::unordered_map<Load*, Index> loads; void doWalkFunction(Function* func) { // prepare @@ -51,7 +52,8 @@ struct PickLoadSigns : public WalkerPass<ExpressionStackWalker<PickLoadSigns>> { } void visitGetLocal(GetLocal* curr) { - // this is a use. check from the context what it is, signed or unsigned, etc. + // this is a use. check from the context what it is, signed or unsigned, + // etc. auto& usage = usages[curr->index]; usage.totalUsages++; if (expressionStack.size() >= 2) { @@ -97,9 +99,14 @@ struct PickLoadSigns : public WalkerPass<ExpressionStackWalker<PickLoadSigns>> { auto& usage = usages[index]; // if we can't optimize, give up if (usage.totalUsages == 0 || // no usages, so no idea - usage.signedUsages + usage.unsignedUsages != usage.totalUsages || // non-sign/unsigned usages, so cannot change - (usage.signedUsages != 0 && usage.signedBits != load->bytes * 8) || // sign usages exist but the wrong size - (usage.unsignedUsages != 0 && usage.unsignedBits != load->bytes * 8)) { // unsigned usages exist but the wrong size + usage.signedUsages + usage.unsignedUsages != + usage.totalUsages || // non-sign/unsigned usages, so cannot change + (usage.signedUsages != 0 && + usage.signedBits != + load->bytes * 8) || // sign usages exist but the wrong size + (usage.unsignedUsages != 0 && + usage.unsignedBits != + load->bytes * 8)) { // unsigned usages exist but the wrong size continue; } // we can pick the optimal one. our hope is to remove 2 items per @@ -107,11 +114,8 @@ struct PickLoadSigns : public WalkerPass<ExpressionStackWalker<PickLoadSigns>> { load->signed_ = usage.signedUsages * 2 >= usage.unsignedUsages; } } - }; -Pass *createPickLoadSignsPass() { - return new PickLoadSigns(); -} +Pass* createPickLoadSignsPass() { return new PickLoadSigns(); } } // namespace wasm diff --git a/src/passes/PostEmscripten.cpp b/src/passes/PostEmscripten.cpp index 7e2bacf25..6c9b84d7c 100644 --- a/src/passes/PostEmscripten.cpp +++ b/src/passes/PostEmscripten.cpp @@ -19,11 +19,11 @@ // emscripten output. // -#include <wasm.h> +#include <asmjs/shared-constants.h> +#include <ir/localize.h> #include <pass.h> #include <wasm-builder.h> -#include <ir/localize.h> -#include <asmjs/shared-constants.h> +#include <wasm.h> namespace wasm { @@ -35,7 +35,8 @@ struct PostEmscripten : public WalkerPass<PostWalker<PostEmscripten>> { void visitCall(Call* curr) { // special asm.js imports can be optimized auto* func = getModule()->getFunction(curr->target); - if (!func->imported()) return; + if (!func->imported()) + return; if (func->module == GLOBAL_MATH) { if (func->base == POW) { if (auto* exponent = curr->operands[1]->dynCast<Const>()) { @@ -43,10 +44,14 @@ struct PostEmscripten : public WalkerPass<PostWalker<PostEmscripten>> { // This is just a square operation, do a multiply Localizer localizer(curr->operands[0], getFunction(), getModule()); Builder builder(*getModule()); - replaceCurrent(builder.makeBinary(MulFloat64, localizer.expr, builder.makeGetLocal(localizer.index, localizer.expr->type))); + replaceCurrent(builder.makeBinary( + MulFloat64, + localizer.expr, + builder.makeGetLocal(localizer.index, localizer.expr->type))); } else if (exponent->value == Literal(double(0.5))) { // This is just a square root operation - replaceCurrent(Builder(*getModule()).makeUnary(SqrtFloat64, curr->operands[0])); + replaceCurrent( + Builder(*getModule()).makeUnary(SqrtFloat64, curr->operands[0])); } } } @@ -54,8 +59,6 @@ struct PostEmscripten : public WalkerPass<PostWalker<PostEmscripten>> { } }; -Pass *createPostEmscriptenPass() { - return new PostEmscripten(); -} +Pass* createPostEmscriptenPass() { return new PostEmscripten(); } } // namespace wasm diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp index 565809ddb..074dd832c 100644 --- a/src/passes/Precompute.cpp +++ b/src/passes/Precompute.cpp @@ -27,14 +27,14 @@ // looked at. // -#include <wasm.h> -#include <pass.h> -#include <wasm-builder.h> -#include <wasm-interpreter.h> -#include <ir/utils.h> #include <ir/literal-utils.h> #include <ir/local-graph.h> #include <ir/manipulation.h> +#include <ir/utils.h> +#include <pass.h> +#include <wasm-builder.h> +#include <wasm-interpreter.h> +#include <wasm.h> namespace wasm { @@ -42,38 +42,44 @@ static const Name NOTPRECOMPUTABLE_FLOW("Binaryen|notprecomputable"); typedef std::unordered_map<GetLocal*, Literal> GetValues; -// Precomputes an expression. Errors if we hit anything that can't be precomputed. -class PrecomputingExpressionRunner : public ExpressionRunner<PrecomputingExpressionRunner> { +// Precomputes an expression. Errors if we hit anything that can't be +// precomputed. +class PrecomputingExpressionRunner + : public ExpressionRunner<PrecomputingExpressionRunner> { Module* module; // map gets to constant values, if they are known to be constant GetValues& getValues; - // Whether we are trying to precompute down to an expression (which we can do on - // say 5 + 6) or to a value (which we can't do on a local.tee that flows a 7 - // through it). When we want to replace the expression, we can only do so - // when it has no side effects. When we don't care about replacing the expression, - // we just want to know if it will contain a known constant. + // Whether we are trying to precompute down to an expression (which we can do + // on say 5 + 6) or to a value (which we can't do on a local.tee that flows a + // 7 through it). When we want to replace the expression, we can only do so + // when it has no side effects. When we don't care about replacing the + // expression, we just want to know if it will contain a known constant. bool replaceExpression; public: - PrecomputingExpressionRunner(Module* module, GetValues& getValues, bool replaceExpression) : module(module), getValues(getValues), replaceExpression(replaceExpression) {} + PrecomputingExpressionRunner(Module* module, + GetValues& getValues, + bool replaceExpression) + : module(module), getValues(getValues), + replaceExpression(replaceExpression) {} - struct NonstandaloneException {}; // TODO: use a flow with a special name, as this is likely very slow + struct NonstandaloneException { + }; // TODO: use a flow with a special name, as this is likely very slow Flow visitLoop(Loop* curr) { // loops might be infinite, so must be careful - // but we can't tell if non-infinite, since we don't have state, so loops are just impossible to optimize for now + // but we can't tell if non-infinite, since we don't have state, so loops + // are just impossible to optimize for now return Flow(NOTPRECOMPUTABLE_FLOW); } - Flow visitCall(Call* curr) { - return Flow(NOTPRECOMPUTABLE_FLOW); - } + Flow visitCall(Call* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); } Flow visitCallIndirect(CallIndirect* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); } - Flow visitGetLocal(GetLocal *curr) { + Flow visitGetLocal(GetLocal* curr) { auto iter = getValues.find(curr); if (iter != getValues.end()) { auto value = iter->second; @@ -83,7 +89,7 @@ public: } return Flow(NOTPRECOMPUTABLE_FLOW); } - Flow visitSetLocal(SetLocal *curr) { + Flow visitSetLocal(SetLocal* curr) { // If we don't need to replace the whole expression, see if there // is a value flowing through a tee. if (!replaceExpression) { @@ -94,56 +100,36 @@ public: } return Flow(NOTPRECOMPUTABLE_FLOW); } - Flow visitGetGlobal(GetGlobal *curr) { + Flow visitGetGlobal(GetGlobal* curr) { auto* global = module->getGlobal(curr->name); if (!global->imported() && !global->mutable_) { return visit(global->init); } return Flow(NOTPRECOMPUTABLE_FLOW); } - Flow visitSetGlobal(SetGlobal *curr) { + Flow visitSetGlobal(SetGlobal* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); } + Flow visitLoad(Load* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); } + Flow visitStore(Store* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); } + Flow visitAtomicRMW(AtomicRMW* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); } + Flow visitAtomicCmpxchg(AtomicCmpxchg* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); } - Flow visitLoad(Load *curr) { - return Flow(NOTPRECOMPUTABLE_FLOW); - } - Flow visitStore(Store *curr) { - return Flow(NOTPRECOMPUTABLE_FLOW); - } - Flow visitAtomicRMW(AtomicRMW *curr) { - return Flow(NOTPRECOMPUTABLE_FLOW); - } - Flow visitAtomicCmpxchg(AtomicCmpxchg *curr) { - return Flow(NOTPRECOMPUTABLE_FLOW); - } - Flow visitAtomicWait(AtomicWait *curr) { - return Flow(NOTPRECOMPUTABLE_FLOW); - } - Flow visitAtomicNotify(AtomicNotify *curr) { - return Flow(NOTPRECOMPUTABLE_FLOW); - } - Flow visitMemoryInit(MemoryInit *curr) { - return Flow(NOTPRECOMPUTABLE_FLOW); - } - Flow visitDataDrop(DataDrop *curr) { - return Flow(NOTPRECOMPUTABLE_FLOW); - } - Flow visitMemoryCopy(MemoryCopy *curr) { - return Flow(NOTPRECOMPUTABLE_FLOW); - } - Flow visitMemoryFill(MemoryFill *curr) { - return Flow(NOTPRECOMPUTABLE_FLOW); - } - Flow visitHost(Host *curr) { + Flow visitAtomicWait(AtomicWait* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); } + Flow visitAtomicNotify(AtomicNotify* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); } + Flow visitMemoryInit(MemoryInit* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); } + Flow visitDataDrop(DataDrop* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); } + Flow visitMemoryCopy(MemoryCopy* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); } + Flow visitMemoryFill(MemoryFill* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); } + Flow visitHost(Host* curr) { return Flow(NOTPRECOMPUTABLE_FLOW); } - void trap(const char* why) override { - throw NonstandaloneException(); - } + void trap(const char* why) override { throw NonstandaloneException(); } }; -struct Precompute : public WalkerPass<PostWalker<Precompute, UnifiedExpressionVisitor<Precompute>>> { +struct Precompute + : public WalkerPass< + PostWalker<Precompute, UnifiedExpressionVisitor<Precompute>>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new Precompute(propagate); } @@ -175,19 +161,25 @@ struct Precompute : public WalkerPass<PostWalker<Precompute, UnifiedExpressionVi } void visitExpression(Expression* curr) { - // TODO: if local.get, only replace with a constant if we don't care about size...? - if (curr->is<Const>() || curr->is<Nop>()) return; + // TODO: if local.get, only replace with a constant if we don't care about + // size...? + if (curr->is<Const>() || curr->is<Nop>()) + return; // Until engines implement v128.const and we have SIMD-aware optimizations // that can break large v128.const instructions into smaller consts and // splats, do not try to precompute v128 expressions. - if (isVectorType(curr->type)) return; + if (isVectorType(curr->type)) + return; // try to evaluate this into a const Flow flow = precomputeExpression(curr); - if (isVectorType(flow.value.type)) return; + if (isVectorType(flow.value.type)) + return; if (flow.breaking()) { - if (flow.breakTo == NOTPRECOMPUTABLE_FLOW) return; + if (flow.breakTo == NOTPRECOMPUTABLE_FLOW) + return; if (flow.breakTo == RETURN_FLOW) { - // this expression causes a return. if it's already a return, reuse the node + // this expression causes a return. if it's already a return, reuse the + // node if (auto* ret = curr->dynCast<Return>()) { if (flow.value.type != none) { // reuse a const value if there is one @@ -204,11 +196,13 @@ struct Precompute : public WalkerPass<PostWalker<Precompute, UnifiedExpressionVi } } else { Builder builder(*getModule()); - replaceCurrent(builder.makeReturn(flow.value.type != none ? builder.makeConst(flow.value) : nullptr)); + replaceCurrent(builder.makeReturn( + flow.value.type != none ? builder.makeConst(flow.value) : nullptr)); } return; } - // this expression causes a break, emit it directly. if it's already a br, reuse the node. + // this expression causes a break, emit it directly. if it's already a br, + // reuse the node. if (auto* br = curr->dynCast<Break>()) { br->name = flow.breakTo; br->condition = nullptr; @@ -229,7 +223,9 @@ struct Precompute : public WalkerPass<PostWalker<Precompute, UnifiedExpressionVi br->finalize(); } else { Builder builder(*getModule()); - replaceCurrent(builder.makeBreak(flow.breakTo, flow.value.type != none ? builder.makeConst(flow.value) : nullptr)); + replaceCurrent(builder.makeBreak( + flow.breakTo, + flow.value.type != none ? builder.makeConst(flow.value) : nullptr)); } return; } @@ -252,7 +248,9 @@ private: // (that we can replace the expression with if replaceExpression is set). Flow precomputeExpression(Expression* curr, bool replaceExpression = true) { try { - return PrecomputingExpressionRunner(getModule(), getValues, replaceExpression).visit(curr); + return PrecomputingExpressionRunner( + getModule(), getValues, replaceExpression) + .visit(curr); } catch (PrecomputingExpressionRunner::NonstandaloneException&) { return Flow(NOTPRECOMPUTABLE_FLOW); } @@ -292,7 +290,8 @@ private: auto* curr = pair.first; work.insert(curr); } - std::unordered_map<SetLocal*, Literal> setValues; // the constant value, or none if not a constant + // the constant value, or none if not a constant + std::unordered_map<SetLocal*, Literal> setValues; // propagate constant values while (!work.empty()) { auto iter = work.begin(); @@ -302,7 +301,8 @@ private: // mark it as such and add everything it influences to the work list, // as they may be constant too. if (auto* set = curr->dynCast<SetLocal>()) { - if (setValues[set].isConcrete()) continue; // already known constant + if (setValues[set].isConcrete()) + continue; // already known constant auto value = setValues[set] = precomputeValue(set->value); if (value.isConcrete()) { for (auto* get : localGraph.setInfluences[set]) { @@ -311,7 +311,8 @@ private: } } else { auto* get = curr->cast<GetLocal>(); - if (getValues[get].isConcrete()) continue; // already known constant + if (getValues[get].isConcrete()) + continue; // already known constant // for this get to have constant value, all sets must agree Literal value; bool first = true; @@ -358,12 +359,8 @@ private: } }; -Pass *createPrecomputePass() { - return new Precompute(false); -} +Pass* createPrecomputePass() { return new Precompute(false); } -Pass *createPrecomputePropagatePass() { - return new Precompute(true); -} +Pass* createPrecomputePropagatePass() { return new Precompute(true); } } // namespace wasm diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 29d867d76..405097455 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -18,12 +18,12 @@ // Print out text in s-expression format // -#include <wasm.h> -#include <wasm-printing.h> -#include <wasm-stack.h> +#include <ir/module-utils.h> #include <pass.h> #include <pretty_printing.h> -#include <ir/module-utils.h> +#include <wasm-printing.h> +#include <wasm-stack.h> +#include <wasm.h> namespace wasm { @@ -55,15 +55,14 @@ static Name printableLocal(Index index, Function* func) { return name; } - // Prints the internal contents of an expression: everything but // the children. struct PrintExpressionContents : public Visitor<PrintExpressionContents> { Function* currFunction = nullptr; std::ostream& o; - PrintExpressionContents(Function* currFunction, std::ostream& o) : - currFunction(currFunction), o(o) {} + PrintExpressionContents(Function* currFunction, std::ostream& o) + : currFunction(currFunction), o(o) {} void visitBlock(Block* curr) { printMedium(o, "block"); @@ -133,7 +132,8 @@ struct PrintExpressionContents : public Visitor<PrintExpressionContents> { } void visitLoad(Load* curr) { prepareColor(o) << printType(curr->type); - if (curr->isAtomic) o << ".atomic"; + if (curr->isAtomic) + o << ".atomic"; o << ".load"; if (curr->type != unreachable && curr->bytes < getTypeSize(curr->type)) { if (curr->bytes == 1) { @@ -157,7 +157,8 @@ struct PrintExpressionContents : public Visitor<PrintExpressionContents> { } void visitStore(Store* curr) { prepareColor(o) << printType(curr->valueType); - if (curr->isAtomic) o << ".atomic"; + if (curr->isAtomic) + o << ".atomic"; o << ".store"; if (curr->bytes < 4 || (curr->valueType == i64 && curr->bytes < 8)) { if (curr->bytes == 1) { @@ -199,12 +200,24 @@ struct PrintExpressionContents : public Visitor<PrintExpressionContents> { prepareColor(o); printRMWSize(o, curr->type, curr->bytes); switch (curr->op) { - case Add: o << "add"; break; - case Sub: o << "sub"; break; - case And: o << "and"; break; - case Or: o << "or"; break; - case Xor: o << "xor"; break; - case Xchg: o << "xchg"; break; + case Add: + o << "add"; + break; + case Sub: + o << "sub"; + break; + case And: + o << "and"; + break; + case Or: + o << "or"; + break; + case Xor: + o << "xor"; + break; + case Xchg: + o << "xchg"; + break; } if (curr->type != unreachable && curr->bytes != getTypeSize(curr->type)) { o << "_u"; @@ -217,7 +230,7 @@ struct PrintExpressionContents : public Visitor<PrintExpressionContents> { void visitAtomicCmpxchg(AtomicCmpxchg* curr) { prepareColor(o); printRMWSize(o, curr->type, curr->bytes); - o << "cmpxchg"; + o << "cmpxchg"; if (curr->type != unreachable && curr->bytes != getTypeSize(curr->type)) { o << "_u"; } @@ -242,26 +255,54 @@ struct PrintExpressionContents : public Visitor<PrintExpressionContents> { void visitSIMDExtract(SIMDExtract* curr) { prepareColor(o); switch (curr->op) { - case ExtractLaneSVecI8x16: o << "i8x16.extract_lane_s"; break; - case ExtractLaneUVecI8x16: o << "i8x16.extract_lane_u"; break; - case ExtractLaneSVecI16x8: o << "i16x8.extract_lane_s"; break; - case ExtractLaneUVecI16x8: o << "i16x8.extract_lane_u"; break; - case ExtractLaneVecI32x4: o << "i32x4.extract_lane"; break; - case ExtractLaneVecI64x2: o << "i64x2.extract_lane"; break; - case ExtractLaneVecF32x4: o << "f32x4.extract_lane"; break; - case ExtractLaneVecF64x2: o << "f64x2.extract_lane"; break; + case ExtractLaneSVecI8x16: + o << "i8x16.extract_lane_s"; + break; + case ExtractLaneUVecI8x16: + o << "i8x16.extract_lane_u"; + break; + case ExtractLaneSVecI16x8: + o << "i16x8.extract_lane_s"; + break; + case ExtractLaneUVecI16x8: + o << "i16x8.extract_lane_u"; + break; + case ExtractLaneVecI32x4: + o << "i32x4.extract_lane"; + break; + case ExtractLaneVecI64x2: + o << "i64x2.extract_lane"; + break; + case ExtractLaneVecF32x4: + o << "f32x4.extract_lane"; + break; + case ExtractLaneVecF64x2: + o << "f64x2.extract_lane"; + break; } o << " " << int(curr->index); } void visitSIMDReplace(SIMDReplace* curr) { prepareColor(o); switch (curr->op) { - case ReplaceLaneVecI8x16: o << "i8x16.replace_lane"; break; - case ReplaceLaneVecI16x8: o << "i16x8.replace_lane"; break; - case ReplaceLaneVecI32x4: o << "i32x4.replace_lane"; break; - case ReplaceLaneVecI64x2: o << "i64x2.replace_lane"; break; - case ReplaceLaneVecF32x4: o << "f32x4.replace_lane"; break; - case ReplaceLaneVecF64x2: o << "f64x2.replace_lane"; break; + case ReplaceLaneVecI8x16: + o << "i8x16.replace_lane"; + break; + case ReplaceLaneVecI16x8: + o << "i16x8.replace_lane"; + break; + case ReplaceLaneVecI32x4: + o << "i32x4.replace_lane"; + break; + case ReplaceLaneVecI64x2: + o << "i64x2.replace_lane"; + break; + case ReplaceLaneVecF32x4: + o << "f32x4.replace_lane"; + break; + case ReplaceLaneVecF64x2: + o << "f64x2.replace_lane"; + break; } o << " " << int(curr->index); } @@ -279,18 +320,42 @@ struct PrintExpressionContents : public Visitor<PrintExpressionContents> { void visitSIMDShift(SIMDShift* curr) { prepareColor(o); switch (curr->op) { - case ShlVecI8x16: o << "i8x16.shl"; break; - case ShrSVecI8x16: o << "i8x16.shr_s"; break; - case ShrUVecI8x16: o << "i8x16.shr_u"; break; - case ShlVecI16x8: o << "i16x8.shl"; break; - case ShrSVecI16x8: o << "i16x8.shr_s"; break; - case ShrUVecI16x8: o << "i16x8.shr_u"; break; - case ShlVecI32x4: o << "i32x4.shl"; break; - case ShrSVecI32x4: o << "i32x4.shr_s"; break; - case ShrUVecI32x4: o << "i32x4.shr_u"; break; - case ShlVecI64x2: o << "i64x2.shl"; break; - case ShrSVecI64x2: o << "i64x2.shr_s"; break; - case ShrUVecI64x2: o << "i64x2.shr_u"; break; + case ShlVecI8x16: + o << "i8x16.shl"; + break; + case ShrSVecI8x16: + o << "i8x16.shr_s"; + break; + case ShrUVecI8x16: + o << "i8x16.shr_u"; + break; + case ShlVecI16x8: + o << "i16x8.shl"; + break; + case ShrSVecI16x8: + o << "i16x8.shr_s"; + break; + case ShrUVecI16x8: + o << "i16x8.shr_u"; + break; + case ShlVecI32x4: + o << "i32x4.shl"; + break; + case ShrSVecI32x4: + o << "i32x4.shr_s"; + break; + case ShrUVecI32x4: + o << "i32x4.shr_u"; + break; + case ShlVecI64x2: + o << "i64x2.shl"; + break; + case ShrSVecI64x2: + o << "i64x2.shr_s"; + break; + case ShrUVecI64x2: + o << "i64x2.shr_u"; + break; } } void visitMemoryInit(MemoryInit* curr) { @@ -309,296 +374,780 @@ struct PrintExpressionContents : public Visitor<PrintExpressionContents> { prepareColor(o); o << "memory.fill"; } - void visitConst(Const* curr) { - o << curr->value; - } + void visitConst(Const* curr) { o << curr->value; } void visitUnary(Unary* curr) { prepareColor(o); switch (curr->op) { - case ClzInt32: o << "i32.clz"; break; - case CtzInt32: o << "i32.ctz"; break; - case PopcntInt32: o << "i32.popcnt"; break; - case EqZInt32: o << "i32.eqz"; break; - case ClzInt64: o << "i64.clz"; break; - case CtzInt64: o << "i64.ctz"; break; - case PopcntInt64: o << "i64.popcnt"; break; - case EqZInt64: o << "i64.eqz"; break; - case NegFloat32: o << "f32.neg"; break; - case AbsFloat32: o << "f32.abs"; break; - case CeilFloat32: o << "f32.ceil"; break; - case FloorFloat32: o << "f32.floor"; break; - case TruncFloat32: o << "f32.trunc"; break; - case NearestFloat32: o << "f32.nearest"; break; - case SqrtFloat32: o << "f32.sqrt"; break; - case NegFloat64: o << "f64.neg"; break; - case AbsFloat64: o << "f64.abs"; break; - case CeilFloat64: o << "f64.ceil"; break; - case FloorFloat64: o << "f64.floor"; break; - case TruncFloat64: o << "f64.trunc"; break; - case NearestFloat64: o << "f64.nearest"; break; - case SqrtFloat64: o << "f64.sqrt"; break; - case ExtendSInt32: o << "i64.extend_i32_s"; break; - case ExtendUInt32: o << "i64.extend_i32_u"; break; - case WrapInt64: o << "i32.wrap_i64"; break; - case TruncSFloat32ToInt32: o << "i32.trunc_f32_s"; break; - case TruncSFloat32ToInt64: o << "i64.trunc_f32_s"; break; - case TruncUFloat32ToInt32: o << "i32.trunc_f32_u"; break; - case TruncUFloat32ToInt64: o << "i64.trunc_f32_u"; break; - case TruncSFloat64ToInt32: o << "i32.trunc_f64_s"; break; - case TruncSFloat64ToInt64: o << "i64.trunc_f64_s"; break; - case TruncUFloat64ToInt32: o << "i32.trunc_f64_u"; break; - case TruncUFloat64ToInt64: o << "i64.trunc_f64_u"; break; - case ReinterpretFloat32: o << "i32.reinterpret_f32"; break; - case ReinterpretFloat64: o << "i64.reinterpret_f64"; break; - case ConvertUInt32ToFloat32: o << "f32.convert_i32_u"; break; - case ConvertUInt32ToFloat64: o << "f64.convert_i32_u"; break; - case ConvertSInt32ToFloat32: o << "f32.convert_i32_s"; break; - case ConvertSInt32ToFloat64: o << "f64.convert_i32_s"; break; - case ConvertUInt64ToFloat32: o << "f32.convert_i64_u"; break; - case ConvertUInt64ToFloat64: o << "f64.convert_i64_u"; break; - case ConvertSInt64ToFloat32: o << "f32.convert_i64_s"; break; - case ConvertSInt64ToFloat64: o << "f64.convert_i64_s"; break; - case PromoteFloat32: o << "f64.promote_f32"; break; - case DemoteFloat64: o << "f32.demote_f64"; break; - case ReinterpretInt32: o << "f32.reinterpret_i32"; break; - case ReinterpretInt64: o << "f64.reinterpret_i64"; break; - case ExtendS8Int32: o << "i32.extend8_s"; break; - case ExtendS16Int32: o << "i32.extend16_s"; break; - case ExtendS8Int64: o << "i64.extend8_s"; break; - case ExtendS16Int64: o << "i64.extend16_s"; break; - case ExtendS32Int64: o << "i64.extend32_s"; break; - case TruncSatSFloat32ToInt32: o << "i32.trunc_sat_f32_s"; break; - case TruncSatUFloat32ToInt32: o << "i32.trunc_sat_f32_u"; break; - case TruncSatSFloat64ToInt32: o << "i32.trunc_sat_f64_s"; break; - case TruncSatUFloat64ToInt32: o << "i32.trunc_sat_f64_u"; break; - case TruncSatSFloat32ToInt64: o << "i64.trunc_sat_f32_s"; break; - case TruncSatUFloat32ToInt64: o << "i64.trunc_sat_f32_u"; break; - case TruncSatSFloat64ToInt64: o << "i64.trunc_sat_f64_s"; break; - case TruncSatUFloat64ToInt64: o << "i64.trunc_sat_f64_u"; break; - case SplatVecI8x16: o << "i8x16.splat"; break; - case SplatVecI16x8: o << "i16x8.splat"; break; - case SplatVecI32x4: o << "i32x4.splat"; break; - case SplatVecI64x2: o << "i64x2.splat"; break; - case SplatVecF32x4: o << "f32x4.splat"; break; - case SplatVecF64x2: o << "f64x2.splat"; break; - case NotVec128: o << "v128.not"; break; - case NegVecI8x16: o << "i8x16.neg"; break; - case AnyTrueVecI8x16: o << "i8x16.any_true"; break; - case AllTrueVecI8x16: o << "i8x16.all_true"; break; - case NegVecI16x8: o << "i16x8.neg"; break; - case AnyTrueVecI16x8: o << "i16x8.any_true"; break; - case AllTrueVecI16x8: o << "i16x8.all_true"; break; - case NegVecI32x4: o << "i32x4.neg"; break; - case AnyTrueVecI32x4: o << "i32x4.any_true"; break; - case AllTrueVecI32x4: o << "i32x4.all_true"; break; - case NegVecI64x2: o << "i64x2.neg"; break; - case AnyTrueVecI64x2: o << "i64x2.any_true"; break; - case AllTrueVecI64x2: o << "i64x2.all_true"; break; - case AbsVecF32x4: o << "f32x4.abs"; break; - case NegVecF32x4: o << "f32x4.neg"; break; - case SqrtVecF32x4: o << "f32x4.sqrt"; break; - case AbsVecF64x2: o << "f64x2.abs"; break; - case NegVecF64x2: o << "f64x2.neg"; break; - case SqrtVecF64x2: o << "f64x2.sqrt"; break; - case TruncSatSVecF32x4ToVecI32x4: o << "i32x4.trunc_sat_f32x4_s"; break; - case TruncSatUVecF32x4ToVecI32x4: o << "i32x4.trunc_sat_f32x4_u"; break; - case TruncSatSVecF64x2ToVecI64x2: o << "i64x2.trunc_sat_f64x2_s"; break; - case TruncSatUVecF64x2ToVecI64x2: o << "i64x2.trunc_sat_f64x2_u"; break; - case ConvertSVecI32x4ToVecF32x4: o << "f32x4.convert_i32x4_s"; break; - case ConvertUVecI32x4ToVecF32x4: o << "f32x4.convert_i32x4_u"; break; - case ConvertSVecI64x2ToVecF64x2: o << "f64x2.convert_i64x2_s"; break; - case ConvertUVecI64x2ToVecF64x2: o << "f64x2.convert_i64x2_u"; break; - case InvalidUnary: WASM_UNREACHABLE(); + case ClzInt32: + o << "i32.clz"; + break; + case CtzInt32: + o << "i32.ctz"; + break; + case PopcntInt32: + o << "i32.popcnt"; + break; + case EqZInt32: + o << "i32.eqz"; + break; + case ClzInt64: + o << "i64.clz"; + break; + case CtzInt64: + o << "i64.ctz"; + break; + case PopcntInt64: + o << "i64.popcnt"; + break; + case EqZInt64: + o << "i64.eqz"; + break; + case NegFloat32: + o << "f32.neg"; + break; + case AbsFloat32: + o << "f32.abs"; + break; + case CeilFloat32: + o << "f32.ceil"; + break; + case FloorFloat32: + o << "f32.floor"; + break; + case TruncFloat32: + o << "f32.trunc"; + break; + case NearestFloat32: + o << "f32.nearest"; + break; + case SqrtFloat32: + o << "f32.sqrt"; + break; + case NegFloat64: + o << "f64.neg"; + break; + case AbsFloat64: + o << "f64.abs"; + break; + case CeilFloat64: + o << "f64.ceil"; + break; + case FloorFloat64: + o << "f64.floor"; + break; + case TruncFloat64: + o << "f64.trunc"; + break; + case NearestFloat64: + o << "f64.nearest"; + break; + case SqrtFloat64: + o << "f64.sqrt"; + break; + case ExtendSInt32: + o << "i64.extend_i32_s"; + break; + case ExtendUInt32: + o << "i64.extend_i32_u"; + break; + case WrapInt64: + o << "i32.wrap_i64"; + break; + case TruncSFloat32ToInt32: + o << "i32.trunc_f32_s"; + break; + case TruncSFloat32ToInt64: + o << "i64.trunc_f32_s"; + break; + case TruncUFloat32ToInt32: + o << "i32.trunc_f32_u"; + break; + case TruncUFloat32ToInt64: + o << "i64.trunc_f32_u"; + break; + case TruncSFloat64ToInt32: + o << "i32.trunc_f64_s"; + break; + case TruncSFloat64ToInt64: + o << "i64.trunc_f64_s"; + break; + case TruncUFloat64ToInt32: + o << "i32.trunc_f64_u"; + break; + case TruncUFloat64ToInt64: + o << "i64.trunc_f64_u"; + break; + case ReinterpretFloat32: + o << "i32.reinterpret_f32"; + break; + case ReinterpretFloat64: + o << "i64.reinterpret_f64"; + break; + case ConvertUInt32ToFloat32: + o << "f32.convert_i32_u"; + break; + case ConvertUInt32ToFloat64: + o << "f64.convert_i32_u"; + break; + case ConvertSInt32ToFloat32: + o << "f32.convert_i32_s"; + break; + case ConvertSInt32ToFloat64: + o << "f64.convert_i32_s"; + break; + case ConvertUInt64ToFloat32: + o << "f32.convert_i64_u"; + break; + case ConvertUInt64ToFloat64: + o << "f64.convert_i64_u"; + break; + case ConvertSInt64ToFloat32: + o << "f32.convert_i64_s"; + break; + case ConvertSInt64ToFloat64: + o << "f64.convert_i64_s"; + break; + case PromoteFloat32: + o << "f64.promote_f32"; + break; + case DemoteFloat64: + o << "f32.demote_f64"; + break; + case ReinterpretInt32: + o << "f32.reinterpret_i32"; + break; + case ReinterpretInt64: + o << "f64.reinterpret_i64"; + break; + case ExtendS8Int32: + o << "i32.extend8_s"; + break; + case ExtendS16Int32: + o << "i32.extend16_s"; + break; + case ExtendS8Int64: + o << "i64.extend8_s"; + break; + case ExtendS16Int64: + o << "i64.extend16_s"; + break; + case ExtendS32Int64: + o << "i64.extend32_s"; + break; + case TruncSatSFloat32ToInt32: + o << "i32.trunc_sat_f32_s"; + break; + case TruncSatUFloat32ToInt32: + o << "i32.trunc_sat_f32_u"; + break; + case TruncSatSFloat64ToInt32: + o << "i32.trunc_sat_f64_s"; + break; + case TruncSatUFloat64ToInt32: + o << "i32.trunc_sat_f64_u"; + break; + case TruncSatSFloat32ToInt64: + o << "i64.trunc_sat_f32_s"; + break; + case TruncSatUFloat32ToInt64: + o << "i64.trunc_sat_f32_u"; + break; + case TruncSatSFloat64ToInt64: + o << "i64.trunc_sat_f64_s"; + break; + case TruncSatUFloat64ToInt64: + o << "i64.trunc_sat_f64_u"; + break; + case SplatVecI8x16: + o << "i8x16.splat"; + break; + case SplatVecI16x8: + o << "i16x8.splat"; + break; + case SplatVecI32x4: + o << "i32x4.splat"; + break; + case SplatVecI64x2: + o << "i64x2.splat"; + break; + case SplatVecF32x4: + o << "f32x4.splat"; + break; + case SplatVecF64x2: + o << "f64x2.splat"; + break; + case NotVec128: + o << "v128.not"; + break; + case NegVecI8x16: + o << "i8x16.neg"; + break; + case AnyTrueVecI8x16: + o << "i8x16.any_true"; + break; + case AllTrueVecI8x16: + o << "i8x16.all_true"; + break; + case NegVecI16x8: + o << "i16x8.neg"; + break; + case AnyTrueVecI16x8: + o << "i16x8.any_true"; + break; + case AllTrueVecI16x8: + o << "i16x8.all_true"; + break; + case NegVecI32x4: + o << "i32x4.neg"; + break; + case AnyTrueVecI32x4: + o << "i32x4.any_true"; + break; + case AllTrueVecI32x4: + o << "i32x4.all_true"; + break; + case NegVecI64x2: + o << "i64x2.neg"; + break; + case AnyTrueVecI64x2: + o << "i64x2.any_true"; + break; + case AllTrueVecI64x2: + o << "i64x2.all_true"; + break; + case AbsVecF32x4: + o << "f32x4.abs"; + break; + case NegVecF32x4: + o << "f32x4.neg"; + break; + case SqrtVecF32x4: + o << "f32x4.sqrt"; + break; + case AbsVecF64x2: + o << "f64x2.abs"; + break; + case NegVecF64x2: + o << "f64x2.neg"; + break; + case SqrtVecF64x2: + o << "f64x2.sqrt"; + break; + case TruncSatSVecF32x4ToVecI32x4: + o << "i32x4.trunc_sat_f32x4_s"; + break; + case TruncSatUVecF32x4ToVecI32x4: + o << "i32x4.trunc_sat_f32x4_u"; + break; + case TruncSatSVecF64x2ToVecI64x2: + o << "i64x2.trunc_sat_f64x2_s"; + break; + case TruncSatUVecF64x2ToVecI64x2: + o << "i64x2.trunc_sat_f64x2_u"; + break; + case ConvertSVecI32x4ToVecF32x4: + o << "f32x4.convert_i32x4_s"; + break; + case ConvertUVecI32x4ToVecF32x4: + o << "f32x4.convert_i32x4_u"; + break; + case ConvertSVecI64x2ToVecF64x2: + o << "f64x2.convert_i64x2_s"; + break; + case ConvertUVecI64x2ToVecF64x2: + o << "f64x2.convert_i64x2_u"; + break; + case InvalidUnary: + WASM_UNREACHABLE(); } } void visitBinary(Binary* curr) { prepareColor(o); switch (curr->op) { - case AddInt32: o << "i32.add"; break; - case SubInt32: o << "i32.sub"; break; - case MulInt32: o << "i32.mul"; break; - case DivSInt32: o << "i32.div_s"; break; - case DivUInt32: o << "i32.div_u"; break; - case RemSInt32: o << "i32.rem_s"; break; - case RemUInt32: o << "i32.rem_u"; break; - case AndInt32: o << "i32.and"; break; - case OrInt32: o << "i32.or"; break; - case XorInt32: o << "i32.xor"; break; - case ShlInt32: o << "i32.shl"; break; - case ShrUInt32: o << "i32.shr_u"; break; - case ShrSInt32: o << "i32.shr_s"; break; - case RotLInt32: o << "i32.rotl"; break; - case RotRInt32: o << "i32.rotr"; break; - case EqInt32: o << "i32.eq"; break; - case NeInt32: o << "i32.ne"; break; - case LtSInt32: o << "i32.lt_s"; break; - case LtUInt32: o << "i32.lt_u"; break; - case LeSInt32: o << "i32.le_s"; break; - case LeUInt32: o << "i32.le_u"; break; - case GtSInt32: o << "i32.gt_s"; break; - case GtUInt32: o << "i32.gt_u"; break; - case GeSInt32: o << "i32.ge_s"; break; - case GeUInt32: o << "i32.ge_u"; break; + case AddInt32: + o << "i32.add"; + break; + case SubInt32: + o << "i32.sub"; + break; + case MulInt32: + o << "i32.mul"; + break; + case DivSInt32: + o << "i32.div_s"; + break; + case DivUInt32: + o << "i32.div_u"; + break; + case RemSInt32: + o << "i32.rem_s"; + break; + case RemUInt32: + o << "i32.rem_u"; + break; + case AndInt32: + o << "i32.and"; + break; + case OrInt32: + o << "i32.or"; + break; + case XorInt32: + o << "i32.xor"; + break; + case ShlInt32: + o << "i32.shl"; + break; + case ShrUInt32: + o << "i32.shr_u"; + break; + case ShrSInt32: + o << "i32.shr_s"; + break; + case RotLInt32: + o << "i32.rotl"; + break; + case RotRInt32: + o << "i32.rotr"; + break; + case EqInt32: + o << "i32.eq"; + break; + case NeInt32: + o << "i32.ne"; + break; + case LtSInt32: + o << "i32.lt_s"; + break; + case LtUInt32: + o << "i32.lt_u"; + break; + case LeSInt32: + o << "i32.le_s"; + break; + case LeUInt32: + o << "i32.le_u"; + break; + case GtSInt32: + o << "i32.gt_s"; + break; + case GtUInt32: + o << "i32.gt_u"; + break; + case GeSInt32: + o << "i32.ge_s"; + break; + case GeUInt32: + o << "i32.ge_u"; + break; - case AddInt64: o << "i64.add"; break; - case SubInt64: o << "i64.sub"; break; - case MulInt64: o << "i64.mul"; break; - case DivSInt64: o << "i64.div_s"; break; - case DivUInt64: o << "i64.div_u"; break; - case RemSInt64: o << "i64.rem_s"; break; - case RemUInt64: o << "i64.rem_u"; break; - case AndInt64: o << "i64.and"; break; - case OrInt64: o << "i64.or"; break; - case XorInt64: o << "i64.xor"; break; - case ShlInt64: o << "i64.shl"; break; - case ShrUInt64: o << "i64.shr_u"; break; - case ShrSInt64: o << "i64.shr_s"; break; - case RotLInt64: o << "i64.rotl"; break; - case RotRInt64: o << "i64.rotr"; break; - case EqInt64: o << "i64.eq"; break; - case NeInt64: o << "i64.ne"; break; - case LtSInt64: o << "i64.lt_s"; break; - case LtUInt64: o << "i64.lt_u"; break; - case LeSInt64: o << "i64.le_s"; break; - case LeUInt64: o << "i64.le_u"; break; - case GtSInt64: o << "i64.gt_s"; break; - case GtUInt64: o << "i64.gt_u"; break; - case GeSInt64: o << "i64.ge_s"; break; - case GeUInt64: o << "i64.ge_u"; break; + case AddInt64: + o << "i64.add"; + break; + case SubInt64: + o << "i64.sub"; + break; + case MulInt64: + o << "i64.mul"; + break; + case DivSInt64: + o << "i64.div_s"; + break; + case DivUInt64: + o << "i64.div_u"; + break; + case RemSInt64: + o << "i64.rem_s"; + break; + case RemUInt64: + o << "i64.rem_u"; + break; + case AndInt64: + o << "i64.and"; + break; + case OrInt64: + o << "i64.or"; + break; + case XorInt64: + o << "i64.xor"; + break; + case ShlInt64: + o << "i64.shl"; + break; + case ShrUInt64: + o << "i64.shr_u"; + break; + case ShrSInt64: + o << "i64.shr_s"; + break; + case RotLInt64: + o << "i64.rotl"; + break; + case RotRInt64: + o << "i64.rotr"; + break; + case EqInt64: + o << "i64.eq"; + break; + case NeInt64: + o << "i64.ne"; + break; + case LtSInt64: + o << "i64.lt_s"; + break; + case LtUInt64: + o << "i64.lt_u"; + break; + case LeSInt64: + o << "i64.le_s"; + break; + case LeUInt64: + o << "i64.le_u"; + break; + case GtSInt64: + o << "i64.gt_s"; + break; + case GtUInt64: + o << "i64.gt_u"; + break; + case GeSInt64: + o << "i64.ge_s"; + break; + case GeUInt64: + o << "i64.ge_u"; + break; - case AddFloat32: o << "f32.add"; break; - case SubFloat32: o << "f32.sub"; break; - case MulFloat32: o << "f32.mul"; break; - case DivFloat32: o << "f32.div"; break; - case CopySignFloat32: o << "f32.copysign"; break; - case MinFloat32: o << "f32.min"; break; - case MaxFloat32: o << "f32.max"; break; - case EqFloat32: o << "f32.eq"; break; - case NeFloat32: o << "f32.ne"; break; - case LtFloat32: o << "f32.lt"; break; - case LeFloat32: o << "f32.le"; break; - case GtFloat32: o << "f32.gt"; break; - case GeFloat32: o << "f32.ge"; break; + case AddFloat32: + o << "f32.add"; + break; + case SubFloat32: + o << "f32.sub"; + break; + case MulFloat32: + o << "f32.mul"; + break; + case DivFloat32: + o << "f32.div"; + break; + case CopySignFloat32: + o << "f32.copysign"; + break; + case MinFloat32: + o << "f32.min"; + break; + case MaxFloat32: + o << "f32.max"; + break; + case EqFloat32: + o << "f32.eq"; + break; + case NeFloat32: + o << "f32.ne"; + break; + case LtFloat32: + o << "f32.lt"; + break; + case LeFloat32: + o << "f32.le"; + break; + case GtFloat32: + o << "f32.gt"; + break; + case GeFloat32: + o << "f32.ge"; + break; - case AddFloat64: o << "f64.add"; break; - case SubFloat64: o << "f64.sub"; break; - case MulFloat64: o << "f64.mul"; break; - case DivFloat64: o << "f64.div"; break; - case CopySignFloat64: o << "f64.copysign"; break; - case MinFloat64: o << "f64.min"; break; - case MaxFloat64: o << "f64.max"; break; - case EqFloat64: o << "f64.eq"; break; - case NeFloat64: o << "f64.ne"; break; - case LtFloat64: o << "f64.lt"; break; - case LeFloat64: o << "f64.le"; break; - case GtFloat64: o << "f64.gt"; break; - case GeFloat64: o << "f64.ge"; break; + case AddFloat64: + o << "f64.add"; + break; + case SubFloat64: + o << "f64.sub"; + break; + case MulFloat64: + o << "f64.mul"; + break; + case DivFloat64: + o << "f64.div"; + break; + case CopySignFloat64: + o << "f64.copysign"; + break; + case MinFloat64: + o << "f64.min"; + break; + case MaxFloat64: + o << "f64.max"; + break; + case EqFloat64: + o << "f64.eq"; + break; + case NeFloat64: + o << "f64.ne"; + break; + case LtFloat64: + o << "f64.lt"; + break; + case LeFloat64: + o << "f64.le"; + break; + case GtFloat64: + o << "f64.gt"; + break; + case GeFloat64: + o << "f64.ge"; + break; - case EqVecI8x16: o << "i8x16.eq"; break; - case NeVecI8x16: o << "i8x16.ne"; break; - case LtSVecI8x16: o << "i8x16.lt_s"; break; - case LtUVecI8x16: o << "i8x16.lt_u"; break; - case GtSVecI8x16: o << "i8x16.gt_s"; break; - case GtUVecI8x16: o << "i8x16.gt_u"; break; - case LeSVecI8x16: o << "i8x16.le_s"; break; - case LeUVecI8x16: o << "i8x16.le_u"; break; - case GeSVecI8x16: o << "i8x16.ge_s"; break; - case GeUVecI8x16: o << "i8x16.ge_u"; break; - case EqVecI16x8: o << "i16x8.eq"; break; - case NeVecI16x8: o << "i16x8.ne"; break; - case LtSVecI16x8: o << "i16x8.lt_s"; break; - case LtUVecI16x8: o << "i16x8.lt_u"; break; - case GtSVecI16x8: o << "i16x8.gt_s"; break; - case GtUVecI16x8: o << "i16x8.gt_u"; break; - case LeSVecI16x8: o << "i16x8.le_s"; break; - case LeUVecI16x8: o << "i16x8.le_u"; break; - case GeSVecI16x8: o << "i16x8.ge_s"; break; - case GeUVecI16x8: o << "i16x8.ge_u"; break; - case EqVecI32x4: o << "i32x4.eq"; break; - case NeVecI32x4: o << "i32x4.ne"; break; - case LtSVecI32x4: o << "i32x4.lt_s"; break; - case LtUVecI32x4: o << "i32x4.lt_u"; break; - case GtSVecI32x4: o << "i32x4.gt_s"; break; - case GtUVecI32x4: o << "i32x4.gt_u"; break; - case LeSVecI32x4: o << "i32x4.le_s"; break; - case LeUVecI32x4: o << "i32x4.le_u"; break; - case GeSVecI32x4: o << "i32x4.ge_s"; break; - case GeUVecI32x4: o << "i32x4.ge_u"; break; - case EqVecF32x4: o << "f32x4.eq"; break; - case NeVecF32x4: o << "f32x4.ne"; break; - case LtVecF32x4: o << "f32x4.lt"; break; - case GtVecF32x4: o << "f32x4.gt"; break; - case LeVecF32x4: o << "f32x4.le"; break; - case GeVecF32x4: o << "f32x4.ge"; break; - case EqVecF64x2: o << "f64x2.eq"; break; - case NeVecF64x2: o << "f64x2.ne"; break; - case LtVecF64x2: o << "f64x2.lt"; break; - case GtVecF64x2: o << "f64x2.gt"; break; - case LeVecF64x2: o << "f64x2.le"; break; - case GeVecF64x2: o << "f64x2.ge"; break; + case EqVecI8x16: + o << "i8x16.eq"; + break; + case NeVecI8x16: + o << "i8x16.ne"; + break; + case LtSVecI8x16: + o << "i8x16.lt_s"; + break; + case LtUVecI8x16: + o << "i8x16.lt_u"; + break; + case GtSVecI8x16: + o << "i8x16.gt_s"; + break; + case GtUVecI8x16: + o << "i8x16.gt_u"; + break; + case LeSVecI8x16: + o << "i8x16.le_s"; + break; + case LeUVecI8x16: + o << "i8x16.le_u"; + break; + case GeSVecI8x16: + o << "i8x16.ge_s"; + break; + case GeUVecI8x16: + o << "i8x16.ge_u"; + break; + case EqVecI16x8: + o << "i16x8.eq"; + break; + case NeVecI16x8: + o << "i16x8.ne"; + break; + case LtSVecI16x8: + o << "i16x8.lt_s"; + break; + case LtUVecI16x8: + o << "i16x8.lt_u"; + break; + case GtSVecI16x8: + o << "i16x8.gt_s"; + break; + case GtUVecI16x8: + o << "i16x8.gt_u"; + break; + case LeSVecI16x8: + o << "i16x8.le_s"; + break; + case LeUVecI16x8: + o << "i16x8.le_u"; + break; + case GeSVecI16x8: + o << "i16x8.ge_s"; + break; + case GeUVecI16x8: + o << "i16x8.ge_u"; + break; + case EqVecI32x4: + o << "i32x4.eq"; + break; + case NeVecI32x4: + o << "i32x4.ne"; + break; + case LtSVecI32x4: + o << "i32x4.lt_s"; + break; + case LtUVecI32x4: + o << "i32x4.lt_u"; + break; + case GtSVecI32x4: + o << "i32x4.gt_s"; + break; + case GtUVecI32x4: + o << "i32x4.gt_u"; + break; + case LeSVecI32x4: + o << "i32x4.le_s"; + break; + case LeUVecI32x4: + o << "i32x4.le_u"; + break; + case GeSVecI32x4: + o << "i32x4.ge_s"; + break; + case GeUVecI32x4: + o << "i32x4.ge_u"; + break; + case EqVecF32x4: + o << "f32x4.eq"; + break; + case NeVecF32x4: + o << "f32x4.ne"; + break; + case LtVecF32x4: + o << "f32x4.lt"; + break; + case GtVecF32x4: + o << "f32x4.gt"; + break; + case LeVecF32x4: + o << "f32x4.le"; + break; + case GeVecF32x4: + o << "f32x4.ge"; + break; + case EqVecF64x2: + o << "f64x2.eq"; + break; + case NeVecF64x2: + o << "f64x2.ne"; + break; + case LtVecF64x2: + o << "f64x2.lt"; + break; + case GtVecF64x2: + o << "f64x2.gt"; + break; + case LeVecF64x2: + o << "f64x2.le"; + break; + case GeVecF64x2: + o << "f64x2.ge"; + break; - case AndVec128: o << "v128.and"; break; - case OrVec128: o << "v128.or"; break; - case XorVec128: o << "v128.xor"; break; + case AndVec128: + o << "v128.and"; + break; + case OrVec128: + o << "v128.or"; + break; + case XorVec128: + o << "v128.xor"; + break; - case AddVecI8x16: o << "i8x16.add"; break; - case AddSatSVecI8x16: o << "i8x16.add_saturate_s"; break; - case AddSatUVecI8x16: o << "i8x16.add_saturate_u"; break; - case SubVecI8x16: o << "i8x16.sub"; break; - case SubSatSVecI8x16: o << "i8x16.sub_saturate_s"; break; - case SubSatUVecI8x16: o << "i8x16.sub_saturate_u"; break; - case MulVecI8x16: o << "i8x16.mul"; break; - case AddVecI16x8: o << "i16x8.add"; break; - case AddSatSVecI16x8: o << "i16x8.add_saturate_s"; break; - case AddSatUVecI16x8: o << "i16x8.add_saturate_u"; break; - case SubVecI16x8: o << "i16x8.sub"; break; - case SubSatSVecI16x8: o << "i16x8.sub_saturate_s"; break; - case SubSatUVecI16x8: o << "i16x8.sub_saturate_u"; break; - case MulVecI16x8: o << "i16x8.mul"; break; - case AddVecI32x4: o << "i32x4.add"; break; - case SubVecI32x4: o << "i32x4.sub"; break; - case MulVecI32x4: o << "i32x4.mul"; break; - case AddVecI64x2: o << "i64x2.add"; break; - case SubVecI64x2: o << "i64x2.sub"; break; + case AddVecI8x16: + o << "i8x16.add"; + break; + case AddSatSVecI8x16: + o << "i8x16.add_saturate_s"; + break; + case AddSatUVecI8x16: + o << "i8x16.add_saturate_u"; + break; + case SubVecI8x16: + o << "i8x16.sub"; + break; + case SubSatSVecI8x16: + o << "i8x16.sub_saturate_s"; + break; + case SubSatUVecI8x16: + o << "i8x16.sub_saturate_u"; + break; + case MulVecI8x16: + o << "i8x16.mul"; + break; + case AddVecI16x8: + o << "i16x8.add"; + break; + case AddSatSVecI16x8: + o << "i16x8.add_saturate_s"; + break; + case AddSatUVecI16x8: + o << "i16x8.add_saturate_u"; + break; + case SubVecI16x8: + o << "i16x8.sub"; + break; + case SubSatSVecI16x8: + o << "i16x8.sub_saturate_s"; + break; + case SubSatUVecI16x8: + o << "i16x8.sub_saturate_u"; + break; + case MulVecI16x8: + o << "i16x8.mul"; + break; + case AddVecI32x4: + o << "i32x4.add"; + break; + case SubVecI32x4: + o << "i32x4.sub"; + break; + case MulVecI32x4: + o << "i32x4.mul"; + break; + case AddVecI64x2: + o << "i64x2.add"; + break; + case SubVecI64x2: + o << "i64x2.sub"; + break; - case AddVecF32x4: o << "f32x4.add"; break; - case SubVecF32x4: o << "f32x4.sub"; break; - case MulVecF32x4: o << "f32x4.mul"; break; - case DivVecF32x4: o << "f32x4.div"; break; - case MinVecF32x4: o << "f32x4.min"; break; - case MaxVecF32x4: o << "f32x4.max"; break; - case AddVecF64x2: o << "f64x2.add"; break; - case SubVecF64x2: o << "f64x2.sub"; break; - case MulVecF64x2: o << "f64x2.mul"; break; - case DivVecF64x2: o << "f64x2.div"; break; - case MinVecF64x2: o << "f64x2.min"; break; - case MaxVecF64x2: o << "f64x2.max"; break; + case AddVecF32x4: + o << "f32x4.add"; + break; + case SubVecF32x4: + o << "f32x4.sub"; + break; + case MulVecF32x4: + o << "f32x4.mul"; + break; + case DivVecF32x4: + o << "f32x4.div"; + break; + case MinVecF32x4: + o << "f32x4.min"; + break; + case MaxVecF32x4: + o << "f32x4.max"; + break; + case AddVecF64x2: + o << "f64x2.add"; + break; + case SubVecF64x2: + o << "f64x2.sub"; + break; + case MulVecF64x2: + o << "f64x2.mul"; + break; + case DivVecF64x2: + o << "f64x2.div"; + break; + case MinVecF64x2: + o << "f64x2.min"; + break; + case MaxVecF64x2: + o << "f64x2.max"; + break; - case InvalidBinary: WASM_UNREACHABLE(); + case InvalidBinary: + WASM_UNREACHABLE(); } restoreNormalColor(o); } - void visitSelect(Select* curr) { - prepareColor(o) << "select"; - } - void visitDrop(Drop* curr) { - printMedium(o, "drop"); - } - void visitReturn(Return* curr) { - printMedium(o, "return"); - } + void visitSelect(Select* curr) { prepareColor(o) << "select"; } + void visitDrop(Drop* curr) { printMedium(o, "drop"); } + void visitReturn(Return* curr) { printMedium(o, "return"); } void visitHost(Host* curr) { switch (curr->op) { - case CurrentMemory: printMedium(o, "current_memory"); break; - case GrowMemory: printMedium(o, "grow_memory"); break; + case CurrentMemory: + printMedium(o, "current_memory"); + break; + case GrowMemory: + printMedium(o, "grow_memory"); + break; } } - void visitNop(Nop* curr) { - printMinor(o, "nop"); - } - void visitUnreachable(Unreachable* curr) { - printMinor(o, "unreachable"); - } + void visitNop(Nop* curr) { printMinor(o, "nop"); } + void visitUnreachable(Unreachable* curr) { printMinor(o, "unreachable"); } }; // Prints an expression in s-expr format, including both the @@ -608,8 +1157,8 @@ struct PrintSExpression : public Visitor<PrintSExpression> { unsigned indent = 0; bool minify; - const char *maybeSpace; - const char *maybeNewLine; + const char* maybeSpace; + const char* maybeNewLine; bool full = false; // whether to not elide nodes in output when possible // (like implicit blocks) and to emit types @@ -625,16 +1174,18 @@ struct PrintSExpression : public Visitor<PrintSExpression> { PrintSExpression(std::ostream& o) : o(o) { setMinify(false); - if (!full) full = isFullForced(); + if (!full) + full = isFullForced(); } - void printDebugLocation(const Function::DebugLocation &location) { + void printDebugLocation(const Function::DebugLocation& location) { if (lastPrintedLocation == location) { return; } lastPrintedLocation = location; auto fileName = currModule->debugInfoFileNames[location.fileIndex]; - o << ";;@ " << fileName << ":" << location.lineNumber << ":" << location.columnNumber << '\n'; + o << ";;@ " << fileName << ":" << location.lineNumber << ":" + << location.columnNumber << '\n'; doIndent(o, indent); } @@ -663,7 +1214,8 @@ struct PrintSExpression : public Visitor<PrintSExpression> { void setFull(bool full_) { full = full_; } void incIndent() { - if (minify) return; + if (minify) + return; o << '\n'; indent++; } @@ -675,7 +1227,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> { } o << ')'; } - void printFullLine(Expression *expression) { + void printFullLine(Expression* expression) { !minify && doIndent(o, indent); if (full) { o << "[" << printType(expression->type) << "] "; @@ -685,7 +1237,8 @@ struct PrintSExpression : public Visitor<PrintSExpression> { } void visitBlock(Block* curr) { - // special-case Block, because Block nesting (in their first element) can be incredibly deep + // special-case Block, because Block nesting (in their first element) can be + // incredibly deep std::vector<Block*> stack; while (1) { if (stack.size() > 0) { @@ -743,13 +1296,17 @@ struct PrintSExpression : public Visitor<PrintSExpression> { incIndent(); printFullLine(curr->condition); // ifTrue and False have implict blocks, avoid printing them if possible - if (!full && curr->ifTrue->is<Block>() && curr->ifTrue->dynCast<Block>()->name.isNull() && curr->ifTrue->dynCast<Block>()->list.size() == 1) { + if (!full && curr->ifTrue->is<Block>() && + curr->ifTrue->dynCast<Block>()->name.isNull() && + curr->ifTrue->dynCast<Block>()->list.size() == 1) { printFullLine(curr->ifTrue->dynCast<Block>()->list.back()); } else { printFullLine(curr->ifTrue); } if (curr->ifFalse) { - if (!full && curr->ifFalse->is<Block>() && curr->ifFalse->dynCast<Block>()->name.isNull() && curr->ifFalse->dynCast<Block>()->list.size() == 1) { + if (!full && curr->ifFalse->is<Block>() && + curr->ifFalse->dynCast<Block>()->name.isNull() && + curr->ifFalse->dynCast<Block>()->list.size() == 1) { printFullLine(curr->ifFalse->dynCast<Block>()->list.back()); } else { printFullLine(curr->ifFalse); @@ -795,7 +1352,8 @@ struct PrintSExpression : public Visitor<PrintSExpression> { } incIndent(); } - if (curr->value && !curr->value->is<Nop>()) printFullLine(curr->value); + if (curr->value && !curr->value->is<Nop>()) + printFullLine(curr->value); if (curr->condition) { printFullLine(curr->condition); } @@ -805,13 +1363,13 @@ struct PrintSExpression : public Visitor<PrintSExpression> { o << '('; PrintExpressionContents(currFunction, o).visit(curr); incIndent(); - if (curr->value && !curr->value->is<Nop>()) printFullLine(curr->value); + if (curr->value && !curr->value->is<Nop>()) + printFullLine(curr->value); printFullLine(curr->condition); decIndent(); } - template<typename CallBase> - void printCallOperands(CallBase* curr) { + template<typename CallBase> void printCallOperands(CallBase* curr) { if (curr->operands.size() > 0) { incIndent(); for (auto operand : curr->operands) { @@ -895,7 +1453,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> { decIndent(); } void visitAtomicWait(AtomicWait* curr) { - o << '(' ; + o << '('; PrintExpressionContents(currFunction, o).visit(curr); restoreNormalColor(o); incIndent(); @@ -1060,7 +1618,8 @@ struct PrintSExpression : public Visitor<PrintSExpression> { // Module-level visitors void visitFunctionType(FunctionType* curr, Name* internalName = nullptr) { o << "(func"; - if (internalName) o << ' ' << *internalName; + if (internalName) + o << ' ' << *internalName; if (curr->params.size() > 0) { o << maybeSpace; o << '('; @@ -1082,11 +1641,20 @@ struct PrintSExpression : public Visitor<PrintSExpression> { printMedium(o, "export "); printText(o, curr->name.str) << " ("; switch (curr->kind) { - case ExternalKind::Function: o << "func"; break; - case ExternalKind::Table: o << "table"; break; - case ExternalKind::Memory: o << "memory"; break; - case ExternalKind::Global: o << "global"; break; - case ExternalKind::Invalid: WASM_UNREACHABLE(); + case ExternalKind::Function: + o << "func"; + break; + case ExternalKind::Table: + o << "table"; + break; + case ExternalKind::Memory: + o << "memory"; + break; + case ExternalKind::Global: + o << "global"; + break; + case ExternalKind::Invalid: + WASM_UNREACHABLE(); } o << ' '; printName(curr->value, o) << "))"; @@ -1140,7 +1708,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> { void visitImportedFunction(Function* curr) { doIndent(o, indent); currFunction = curr; - lastPrintedLocation = { 0, 0, 0 }; + lastPrintedLocation = {0, 0, 0}; o << '('; emitImportHeader(curr); if (curr->type.is()) { @@ -1155,7 +1723,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> { void visitDefinedFunction(Function* curr) { doIndent(o, indent); currFunction = curr; - lastPrintedLocation = { 0, 0, 0 }; + lastPrintedLocation = {0, 0, 0}; if (currFunction->prologLocation.size()) { printDebugLocation(*currFunction->prologLocation.begin()); } @@ -1180,7 +1748,8 @@ struct PrintSExpression : public Visitor<PrintSExpression> { for (size_t i = 0; i < curr->params.size(); i++) { o << maybeSpace; o << '('; - printMinor(o, "param ") << printableLocal(i, currFunction) << ' ' << printType(curr->getLocalType(i)) << ')'; + printMinor(o, "param ") << printableLocal(i, currFunction) << ' ' + << printType(curr->getLocalType(i)) << ')'; } } if (curr->result != none) { @@ -1192,14 +1761,17 @@ struct PrintSExpression : public Visitor<PrintSExpression> { for (size_t i = curr->getVarIndexBase(); i < curr->getNumLocals(); i++) { doIndent(o, indent); o << '('; - printMinor(o, "local ") << printableLocal(i, currFunction) << ' ' << printType(curr->getLocalType(i)) << ')'; + printMinor(o, "local ") << printableLocal(i, currFunction) << ' ' + << printType(curr->getLocalType(i)) << ')'; o << maybeNewLine; } // Print the body. if (!printStackIR || !curr->stackIR) { - // It is ok to emit a block here, as a function can directly contain a list, even if our - // ast avoids that for simplicity. We can just do that optimization here.. - if (!full && curr->body->is<Block>() && curr->body->cast<Block>()->name.isNull()) { + // It is ok to emit a block here, as a function can directly contain a + // list, even if our ast avoids that for simplicity. We can just do that + // optimization here.. + if (!full && curr->body->is<Block>() && + curr->body->cast<Block>()->name.isNull()) { Block* block = curr->body->cast<Block>(); for (auto item : block->list) { printFullLine(item); @@ -1211,8 +1783,10 @@ struct PrintSExpression : public Visitor<PrintSExpression> { // Print the stack IR. WasmPrinter::printStackIR(curr->stackIR.get(), o, curr); } - if (currFunction->epilogLocation.size() && lastPrintedLocation != *currFunction->epilogLocation.begin()) { - // Print last debug location: mix of decIndent and printDebugLocation logic. + if (currFunction->epilogLocation.size() && + lastPrintedLocation != *currFunction->epilogLocation.begin()) { + // Print last debug location: mix of decIndent and printDebugLocation + // logic. doIndent(o, indent); if (!minify) { indent--; @@ -1229,11 +1803,13 @@ struct PrintSExpression : public Visitor<PrintSExpression> { printMedium(o, "table") << ' '; printName(curr->name, o) << ' '; o << curr->initial; - if (curr->hasMax()) o << ' ' << curr->max; + if (curr->hasMax()) + o << ' ' << curr->max; o << " funcref)"; } void visitTable(Table* curr) { - if (!curr->exists) return; + if (!curr->exists) + return; if (curr->imported()) { doIndent(o, indent); o << '('; @@ -1247,7 +1823,8 @@ struct PrintSExpression : public Visitor<PrintSExpression> { } for (auto& segment : curr->segments) { // Don't print empty segments - if (segment.data.empty()) continue; + if (segment.data.empty()) + continue; doIndent(o, indent); o << '('; printMajor(o, "elem "); @@ -1268,12 +1845,15 @@ struct PrintSExpression : public Visitor<PrintSExpression> { printMedium(o, "shared "); } o << curr->initial; - if (curr->hasMax()) o << ' ' << curr->max; - if (curr->shared) o << ")"; + if (curr->hasMax()) + o << ' ' << curr->max; + if (curr->shared) + o << ")"; o << ")"; } void visitMemory(Memory* curr) { - if (!curr->exists) return; + if (!curr->exists) + return; if (curr->imported()) { doIndent(o, indent); o << '('; @@ -1298,19 +1878,35 @@ struct PrintSExpression : public Visitor<PrintSExpression> { for (size_t i = 0; i < segment.data.size(); i++) { unsigned char c = segment.data[i]; switch (c) { - case '\n': o << "\\n"; break; - case '\r': o << "\\0d"; break; - case '\t': o << "\\t"; break; - case '\f': o << "\\0c"; break; - case '\b': o << "\\08"; break; - case '\\': o << "\\\\"; break; - case '"' : o << "\\\""; break; - case '\'' : o << "\\'"; break; + case '\n': + o << "\\n"; + break; + case '\r': + o << "\\0d"; + break; + case '\t': + o << "\\t"; + break; + case '\f': + o << "\\0c"; + break; + case '\b': + o << "\\08"; + break; + case '\\': + o << "\\\\"; + break; + case '"': + o << "\\\""; + break; + case '\'': + o << "\\'"; + break; default: { if (c >= 32 && c < 127) { o << c; } else { - o << std::hex << '\\' << (c/16) << (c%16) << std::dec; + o << std::hex << '\\' << (c / 16) << (c % 16) << std::dec; } } } @@ -1331,27 +1927,20 @@ struct PrintSExpression : public Visitor<PrintSExpression> { visitFunctionType(child.get()); o << ")" << maybeNewLine; } - ModuleUtils::iterImportedMemories(*curr, [&](Memory* memory) { - visitMemory(memory); - }); - ModuleUtils::iterImportedTables(*curr, [&](Table* table) { - visitTable(table); - }); - ModuleUtils::iterImportedGlobals(*curr, [&](Global* global) { - visitGlobal(global); - }); - ModuleUtils::iterImportedFunctions(*curr, [&](Function* func) { - visitFunction(func); - }); - ModuleUtils::iterDefinedMemories(*curr, [&](Memory* memory) { - visitMemory(memory); - }); - ModuleUtils::iterDefinedTables(*curr, [&](Table* table) { - visitTable(table); - }); - ModuleUtils::iterDefinedGlobals(*curr, [&](Global* global) { - visitGlobal(global); - }); + ModuleUtils::iterImportedMemories( + *curr, [&](Memory* memory) { visitMemory(memory); }); + ModuleUtils::iterImportedTables(*curr, + [&](Table* table) { visitTable(table); }); + ModuleUtils::iterImportedGlobals( + *curr, [&](Global* global) { visitGlobal(global); }); + ModuleUtils::iterImportedFunctions( + *curr, [&](Function* func) { visitFunction(func); }); + ModuleUtils::iterDefinedMemories( + *curr, [&](Memory* memory) { visitMemory(memory); }); + ModuleUtils::iterDefinedTables(*curr, + [&](Table* table) { visitTable(table); }); + ModuleUtils::iterDefinedGlobals( + *curr, [&](Global* global) { visitGlobal(global); }); for (auto& child : curr->exports) { doIndent(o, indent); visitExport(child.get()); @@ -1363,12 +1952,12 @@ struct PrintSExpression : public Visitor<PrintSExpression> { printMedium(o, "start") << ' ' << curr->start << ')'; o << maybeNewLine; } - ModuleUtils::iterDefinedFunctions(*curr, [&](Function* func) { - visitFunction(func); - }); + ModuleUtils::iterDefinedFunctions( + *curr, [&](Function* func) { visitFunction(func); }); for (auto& section : curr->userSections) { doIndent(o, indent); - o << ";; custom section \"" << section.name << "\", size " << section.data.size(); + o << ";; custom section \"" << section.name << "\", size " + << section.data.size(); o << maybeNewLine; } decIndent(); @@ -1394,9 +1983,7 @@ public: } }; -Pass *createPrinterPass() { - return new Printer(); -} +Pass* createPrinterPass() { return new Printer(); } // Prints out a minified module @@ -1412,9 +1999,7 @@ public: } }; -Pass *createMinifiedPrinterPass() { - return new MinifiedPrinter(); -} +Pass* createMinifiedPrinterPass() { return new MinifiedPrinter(); } // Prints out a module withough elision, i.e., the full ast @@ -1430,9 +2015,7 @@ public: } }; -Pass *createFullPrinterPass() { - return new FullPrinter(); -} +Pass* createFullPrinterPass() { return new FullPrinter(); } // Print Stack IR (if present) @@ -1448,9 +2031,7 @@ public: } }; -Pass* createPrintStackIRPass() { - return new PrintStackIR(); -} +Pass* createPrintStackIRPass() { return new PrintStackIR(); } // Print individual expressions @@ -1466,7 +2047,10 @@ std::ostream& WasmPrinter::printModule(Module* module) { return printModule(module, std::cout); } -std::ostream& WasmPrinter::printExpression(Expression* expression, std::ostream& o, bool minify, bool full) { +std::ostream& WasmPrinter::printExpression(Expression* expression, + std::ostream& o, + bool minify, + bool full) { if (!expression) { o << "(null expression)"; return o; @@ -1481,7 +2065,8 @@ std::ostream& WasmPrinter::printExpression(Expression* expression, std::ostream& return o; } -std::ostream& WasmPrinter::printStackInst(StackInst* inst, std::ostream& o, Function* func) { +std::ostream& +WasmPrinter::printStackInst(StackInst* inst, std::ostream& o, Function* func) { switch (inst->op) { case StackInst::Basic: { PrintExpressionContents(func, o).visit(inst->origin); @@ -1503,12 +2088,14 @@ std::ostream& WasmPrinter::printStackInst(StackInst* inst, std::ostream& o, Func o << "else"; break; } - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } return o; } -std::ostream& WasmPrinter::printStackIR(StackIR* ir, std::ostream& o, Function* func) { +std::ostream& +WasmPrinter::printStackIR(StackIR* ir, std::ostream& o, Function* func) { size_t indent = func ? 2 : 0; auto doIndent = [&indent, &o]() { for (size_t j = 0; j < indent; j++) { @@ -1517,7 +2104,8 @@ std::ostream& WasmPrinter::printStackIR(StackIR* ir, std::ostream& o, Function* }; for (Index i = 0; i < (*ir).size(); i++) { auto* inst = (*ir)[i]; - if (!inst) continue; + if (!inst) + continue; switch (inst->op) { case StackInst::Basic: { doIndent(); @@ -1548,7 +2136,8 @@ std::ostream& WasmPrinter::printStackIR(StackIR* ir, std::ostream& o, Function* doIndent(); break; } - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } std::cout << '\n'; } diff --git a/src/passes/PrintCallGraph.cpp b/src/passes/PrintCallGraph.cpp index 2a82b7aa1..7df5a8875 100644 --- a/src/passes/PrintCallGraph.cpp +++ b/src/passes/PrintCallGraph.cpp @@ -15,17 +15,17 @@ */ // -// Prints the call graph in .dot format. You can use http://www.graphviz.org/ to view .dot files. +// Prints the call graph in .dot format. You can use http://www.graphviz.org/ to +// view .dot files. // - -#include <memory> #include <iomanip> +#include <memory> -#include "wasm.h" -#include "pass.h" #include "ir/module-utils.h" #include "ir/utils.h" +#include "pass.h" +#include "wasm.h" namespace wasm { @@ -33,7 +33,7 @@ struct PrintCallGraph : public Pass { bool modifiesBinaryenIR() override { return false; } void run(PassRunner* runner, Module* module) override { - std::ostream &o = std::cout; + std::ostream& o = std::cout; o << "digraph call {\n" " rankdir = LR;\n" " subgraph cluster_key {\n" @@ -42,35 +42,40 @@ struct PrintCallGraph : public Pass { " label = \"Key\";\n" " \"Import\" [style=\"filled\", fillcolor=\"turquoise\"];\n" " \"Export\" [style=\"filled\", fillcolor=\"gray\"];\n" - " \"Indirect Target\" [style=\"filled, rounded\", fillcolor=\"white\"];\n" - " \"A\" -> \"B\" [style=\"filled, rounded\", label = \"Direct Call\"];\n" + " \"Indirect Target\" [style=\"filled, rounded\", " + "fillcolor=\"white\"];\n" + " \"A\" -> \"B\" [style=\"filled, rounded\", label = \"Direct " + "Call\"];\n" " }\n\n" " node [shape=box, fontname=courier, fontsize=10];\n"; // Defined functions ModuleUtils::iterDefinedFunctions(*module, [&](Function* curr) { - std::cout << " \"" << curr->name << "\" [style=\"filled\", fillcolor=\"white\"];\n"; + std::cout << " \"" << curr->name + << "\" [style=\"filled\", fillcolor=\"white\"];\n"; }); // Imported functions ModuleUtils::iterImportedFunctions(*module, [&](Function* curr) { - o << " \"" << curr->name << "\" [style=\"filled\", fillcolor=\"turquoise\"];\n"; + o << " \"" << curr->name + << "\" [style=\"filled\", fillcolor=\"turquoise\"];\n"; }); // Exports for (auto& curr : module->exports) { if (curr->kind == ExternalKind::Function) { Function* func = module->getFunction(curr->value); - o << " \"" << func->name << "\" [style=\"filled\", fillcolor=\"gray\"];\n"; + o << " \"" << func->name + << "\" [style=\"filled\", fillcolor=\"gray\"];\n"; } } struct CallPrinter : public PostWalker<CallPrinter> { - Module *module; - Function *currFunction; + Module* module; + Function* currFunction; std::set<Name> visitedTargets; // Used to avoid printing duplicate edges. std::vector<Function*> allIndirectTargets; - CallPrinter(Module *module) : module(module) { + CallPrinter(Module* module) : module(module) { // Walk function bodies. ModuleUtils::iterDefinedFunctions(*module, [&](Function* curr) { currFunction = curr; @@ -78,11 +83,13 @@ struct PrintCallGraph : public Pass { walk(curr->body); }); } - void visitCall(Call *curr) { + void visitCall(Call* curr) { auto* target = module->getFunction(curr->target); - if (visitedTargets.count(target->name) > 0) return; + if (visitedTargets.count(target->name) > 0) + return; visitedTargets.insert(target->name); - std::cout << " \"" << currFunction->name << "\" -> \"" << target->name << "\"; // call\n"; + std::cout << " \"" << currFunction->name << "\" -> \"" << target->name + << "\"; // call\n"; } }; CallPrinter printer(module); @@ -99,8 +106,6 @@ struct PrintCallGraph : public Pass { } }; -Pass *createPrintCallGraphPass() { - return new PrintCallGraph(); -} +Pass* createPrintCallGraphPass() { return new PrintCallGraph(); } } // namespace wasm diff --git a/src/passes/PrintFeatures.cpp b/src/passes/PrintFeatures.cpp index 9c7172eee..53ef6676f 100644 --- a/src/passes/PrintFeatures.cpp +++ b/src/passes/PrintFeatures.cpp @@ -18,9 +18,9 @@ // Print out the feature options corresponding to enabled features // -#include "wasm.h" -#include "wasm-features.h" #include "pass.h" +#include "wasm-features.h" +#include "wasm.h" namespace wasm { @@ -32,8 +32,6 @@ struct PrintFeatures : public Pass { } }; -Pass* createPrintFeaturesPass() { - return new PrintFeatures(); -} +Pass* createPrintFeaturesPass() { return new PrintFeatures(); } } // namespace wasm diff --git a/src/passes/ReReloop.cpp b/src/passes/ReReloop.cpp index 760bab2b5..3a7c2ad87 100644 --- a/src/passes/ReReloop.cpp +++ b/src/passes/ReReloop.cpp @@ -23,13 +23,13 @@ #include <memory> -#include "wasm.h" -#include "wasm-builder.h" -#include "wasm-traversal.h" -#include "pass.h" #include "cfg/Relooper.h" #include "ir/flat.h" #include "ir/utils.h" +#include "pass.h" +#include "wasm-builder.h" +#include "wasm-traversal.h" +#include "wasm.h" #ifdef RERELOOP_DEBUG #include <wasm-printing.h> @@ -62,21 +62,13 @@ struct ReReloop final : public Pass { return currCFGBlock = curr; } - CFG::Block* startCFGBlock() { - return setCurrCFGBlock(makeCFGBlock()); - } + CFG::Block* startCFGBlock() { return setCurrCFGBlock(makeCFGBlock()); } - CFG::Block* getCurrCFGBlock() { - return currCFGBlock; - } + CFG::Block* getCurrCFGBlock() { return currCFGBlock; } - Block* getCurrBlock() { - return currCFGBlock->Code->cast<Block>(); - } + Block* getCurrBlock() { return currCFGBlock->Code->cast<Block>(); } - void finishBlock() { - getCurrBlock()->finalize(); - } + void finishBlock() { getCurrBlock()->finalize(); } // break handling @@ -86,19 +78,21 @@ struct ReReloop final : public Pass { breakTargets[name] = target; } - CFG::Block* getBreakTarget(Name name) { - return breakTargets[name]; - } + CFG::Block* getBreakTarget(Name name) { return breakTargets[name]; } // branch handling - void addBranch(CFG::Block* from, CFG::Block* to, Expression* condition = nullptr) { + void + addBranch(CFG::Block* from, CFG::Block* to, Expression* condition = nullptr) { from->AddBranchTo(to, condition); } - void addSwitchBranch(CFG::Block* from, CFG::Block* to, const std::set<Index>& values) { + void addSwitchBranch(CFG::Block* from, + CFG::Block* to, + const std::set<Index>& values) { std::vector<Index> list; - for (auto i : values) list.push_back(i); + for (auto i : values) + list.push_back(i); from->AddSwitchBranchTo(to, std::move(list)); } @@ -107,9 +101,7 @@ struct ReReloop final : public Pass { struct Task { ReReloop& parent; Task(ReReloop& parent) : parent(parent) {} - virtual void run() { - WASM_UNREACHABLE(); - } + virtual void run() { WASM_UNREACHABLE(); } }; typedef std::shared_ptr<Task> TaskPtr; @@ -120,9 +112,7 @@ struct ReReloop final : public Pass { TriageTask(ReReloop& parent, Expression* curr) : Task(parent), curr(curr) {} - void run() override { - parent.triage(curr); - } + void run() override { parent.triage(curr); } }; struct BlockTask final : public Task { @@ -183,10 +173,12 @@ struct ReReloop final : public Pass { parent.addBranch(task->condition, ifTrueBegin, curr->condition); if (curr->ifFalse) { parent.stack.push_back(task); - parent.stack.push_back(std::make_shared<TriageTask>(parent, curr->ifFalse)); + parent.stack.push_back( + std::make_shared<TriageTask>(parent, curr->ifFalse)); } parent.stack.push_back(task); - parent.stack.push_back(std::make_shared<TriageTask>(parent, curr->ifTrue)); + parent.stack.push_back( + std::make_shared<TriageTask>(parent, curr->ifTrue)); } void run() override { @@ -194,7 +186,8 @@ struct ReReloop final : public Pass { // end of ifTrue ifTrueEnd = parent.getCurrCFGBlock(); auto* after = parent.startCFGBlock(); - parent.addBranch(condition, after); // if condition was false, go after the ifTrue, to ifFalse or outside + // if condition was false, go after the ifTrue, to ifFalse or outside + parent.addBranch(condition, after); if (!curr->ifFalse) { parent.addBranch(ifTrueEnd, after); } @@ -213,9 +206,11 @@ struct ReReloop final : public Pass { struct BreakTask : public Task { static void handle(ReReloop& parent, Break* curr) { - // add the branch. note how if the condition is false, it is the right value there as well + // add the branch. note how if the condition is false, it is the right + // value there as well auto* before = parent.getCurrCFGBlock(); - parent.addBranch(before, parent.getBreakTarget(curr->name), curr->condition); + parent.addBranch( + before, parent.getBreakTarget(curr->name), curr->condition); if (curr->condition) { auto* after = parent.startCFGBlock(); parent.addBranch(before, after); @@ -238,12 +233,14 @@ struct ReReloop final : public Pass { targetValues[targets[i]].insert(i); } for (auto& iter : targetValues) { - parent.addSwitchBranch(before, parent.getBreakTarget(iter.first), iter.second); + parent.addSwitchBranch( + before, parent.getBreakTarget(iter.first), iter.second); } - // the default may be among the targets, in which case, we can't add it simply as - // it would be a duplicate, so create a temp block + // the default may be among the targets, in which case, we can't add it + // simply as it would be a duplicate, so create a temp block if (targetValues.count(curr->default_) == 0) { - parent.addSwitchBranch(before, parent.getBreakTarget(curr->default_), std::set<Index>()); + parent.addSwitchBranch( + before, parent.getBreakTarget(curr->default_), std::set<Index>()); } else { auto* temp = parent.startCFGBlock(); parent.addSwitchBranch(before, temp, std::set<Index>()); @@ -297,7 +294,9 @@ struct ReReloop final : public Pass { // TODO: optimize with this? } - void runOnFunction(PassRunner* runner, Module* module, Function* function) override { + void runOnFunction(PassRunner* runner, + Module* module, + Function* function) override { Flat::verifyFlatness(function); // since control flow is flattened, this is pretty simple @@ -316,15 +315,14 @@ struct ReReloop final : public Pass { // finish the current block finishBlock(); // blocks that do not have any exits are dead ends in the relooper. we need - // to make sure that are in fact dead ends, and do not flow control anywhere. - // add a return as needed + // to make sure that are in fact dead ends, and do not flow control + // anywhere. add a return as needed for (auto* cfgBlock : relooper->Blocks) { auto* block = cfgBlock->Code->cast<Block>(); if (cfgBlock->BranchesOut.empty() && block->type != unreachable) { - block->list.push_back( - function->result == none ? (Expression*)builder->makeReturn() - : (Expression*)builder->makeUnreachable() - ); + block->list.push_back(function->result == none + ? (Expression*)builder->makeReturn() + : (Expression*)builder->makeUnreachable()); block->finalize(); } } @@ -356,10 +354,8 @@ struct ReReloop final : public Pass { // code, for example, which could be optimized out later // but isn't yet), then make sure it has a proper type if (function->result != none && function->body->type == none) { - function->body = builder.makeSequence( - function->body, - builder.makeUnreachable() - ); + function->body = + builder.makeSequence(function->body, builder.makeUnreachable()); } } // TODO: should this be in the relooper itself? @@ -367,8 +363,6 @@ struct ReReloop final : public Pass { } }; -Pass *createReReloopPass() { - return new ReReloop(); -} +Pass* createReReloopPass() { return new ReReloop(); } } // namespace wasm diff --git a/src/passes/RedundantSetElimination.cpp b/src/passes/RedundantSetElimination.cpp index 6f39fce9f..e03020da0 100644 --- a/src/passes/RedundantSetElimination.cpp +++ b/src/passes/RedundantSetElimination.cpp @@ -33,13 +33,13 @@ // here). // -#include <wasm.h> -#include <pass.h> -#include <wasm-builder.h> #include <cfg/cfg-traversal.h> #include <ir/literal-utils.h> #include <ir/utils.h> +#include <pass.h> #include <support/unique_deferring_queue.h> +#include <wasm-builder.h> +#include <wasm.h> namespace wasm { @@ -57,7 +57,10 @@ struct Info { std::vector<Expression**> setps; }; -struct RedundantSetElimination : public WalkerPass<CFGWalker<RedundantSetElimination, Visitor<RedundantSetElimination>, Info>> { +struct RedundantSetElimination + : public WalkerPass<CFGWalker<RedundantSetElimination, + Visitor<RedundantSetElimination>, + Info>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new RedundantSetElimination(); } @@ -66,7 +69,8 @@ struct RedundantSetElimination : public WalkerPass<CFGWalker<RedundantSetElimina // cfg traversal work - static void doVisitSetLocal(RedundantSetElimination* self, Expression** currp) { + static void doVisitSetLocal(RedundantSetElimination* self, + Expression** currp) { if (self->currBasicBlock) { self->currBasicBlock->contents.setps.push_back(currp); } @@ -77,7 +81,8 @@ struct RedundantSetElimination : public WalkerPass<CFGWalker<RedundantSetElimina void doWalkFunction(Function* func) { numLocals = func->getNumLocals(); // create the CFG by walking the IR - CFGWalker<RedundantSetElimination, Visitor<RedundantSetElimination>, Info>::doWalkFunction(func); + CFGWalker<RedundantSetElimination, Visitor<RedundantSetElimination>, Info>:: + doWalkFunction(func); // flow values across blocks flowValues(func); // remove redundant sets @@ -88,8 +93,10 @@ struct RedundantSetElimination : public WalkerPass<CFGWalker<RedundantSetElimina Index nextValue = 1; // 0 is reserved for the "unseen value" std::unordered_map<Literal, Index> literalValues; // each constant has a value - std::unordered_map<Expression*, Index> expressionValues; // each value can have a value - std::unordered_map<BasicBlock*, std::unordered_map<Index, Index>> blockMergeValues; // each block has values for each merge + std::unordered_map<Expression*, Index> + expressionValues; // each value can have a value + std::unordered_map<BasicBlock*, std::unordered_map<Index, Index>> + blockMergeValues; // each block has values for each merge Index getUnseenValue() { // we haven't seen this location yet return 0; @@ -130,17 +137,20 @@ struct RedundantSetElimination : public WalkerPass<CFGWalker<RedundantSetElimina return iter->second; } #ifdef RSE_DEBUG - std::cout << "new block-merge value for " << block << " : " << index << '\n'; + std::cout << "new block-merge value for " << block << " : " << index + << '\n'; #endif return mergeValues[index] = getUniqueValue(); } bool isBlockMergeValue(BasicBlock* block, Index index, Index value) { auto iter = blockMergeValues.find(block); - if (iter == blockMergeValues.end()) return false; + if (iter == blockMergeValues.end()) + return false; auto& mergeValues = iter->second; auto iter2 = mergeValues.find(index); - if (iter2 == mergeValues.end()) return false; + if (iter2 == mergeValues.end()) + return false; return value == iter2->second; } @@ -172,7 +182,8 @@ struct RedundantSetElimination : public WalkerPass<CFGWalker<RedundantSetElimina #endif start[i] = getUniqueValue(); } else { - start[i] = getLiteralValue(Literal::makeZero(func->getLocalType(i))); + start[i] = + getLiteralValue(Literal::makeZero(func->getLocalType(i))); } } } else { @@ -274,7 +285,8 @@ struct RedundantSetElimination : public WalkerPass<CFGWalker<RedundantSetElimina #ifdef RSE_DEBUG dump("start", curr->contents.start); #endif - // flow values through it, then add those we can reach if they need an update. + // flow values through it, then add those we can reach if they need an + // update. auto currValues = curr->contents.start; // we'll modify this as we go auto& setps = curr->contents.setps; for (auto** setp : setps) { @@ -351,7 +363,8 @@ struct RedundantSetElimination : public WalkerPass<CFGWalker<RedundantSetElimina } } for (Index i = 0; i < block->contents.start.size(); i++) { - std::cout << " start[" << i << "] = " << block->contents.start[i] << '\n'; + std::cout << " start[" << i << "] = " << block->contents.start[i] + << '\n'; } for (auto** setp : block->contents.setps) { std::cout << " " << *setp << '\n'; @@ -370,7 +383,7 @@ struct RedundantSetElimination : public WalkerPass<CFGWalker<RedundantSetElimina } // namespace -Pass *createRedundantSetEliminationPass() { +Pass* createRedundantSetEliminationPass() { return new RedundantSetElimination(); } diff --git a/src/passes/RelooperJumpThreading.cpp b/src/passes/RelooperJumpThreading.cpp index db865d1bb..9fcf7a4a8 100644 --- a/src/passes/RelooperJumpThreading.cpp +++ b/src/passes/RelooperJumpThreading.cpp @@ -19,14 +19,13 @@ // This assumes the very specific output the fastcomp relooper emits, // including the name of the 'label' variable. -#include "wasm.h" -#include "pass.h" -#include "ir/utils.h" #include "ir/manipulation.h" +#include "ir/utils.h" +#include "pass.h" +#include "wasm.h" namespace wasm { - static Name LABEL("label"); static Name getInnerName(int i) { @@ -38,13 +37,17 @@ static Name getOuterName(int i) { } static If* isLabelCheckingIf(Expression* curr, Index labelIndex) { - if (!curr) return nullptr; + if (!curr) + return nullptr; auto* iff = curr->dynCast<If>(); - if (!iff) return nullptr; + if (!iff) + return nullptr; auto* condition = iff->condition->dynCast<Binary>(); - if (!(condition && condition->op == EqInt32)) return nullptr; + if (!(condition && condition->op == EqInt32)) + return nullptr; auto* left = condition->left->dynCast<GetLocal>(); - if (!(left && left->index == labelIndex)) return nullptr; + if (!(left && left->index == labelIndex)) + return nullptr; return iff; } @@ -53,10 +56,13 @@ static Index getCheckedLabelValue(If* iff) { } static SetLocal* isLabelSettingSetLocal(Expression* curr, Index labelIndex) { - if (!curr) return nullptr; + if (!curr) + return nullptr; auto* set = curr->dynCast<SetLocal>(); - if (!set) return nullptr; - if (set->index != labelIndex) return nullptr; + if (!set) + return nullptr; + if (set->index != labelIndex) + return nullptr; return set; } @@ -69,7 +75,10 @@ struct LabelUseFinder : public PostWalker<LabelUseFinder> { std::map<Index, Index>& checks; // label value => number of checks on it std::map<Index, Index>& sets; // label value => number of sets to it - LabelUseFinder(Index labelIndex, std::map<Index, Index>& checks, std::map<Index, Index>& sets) : labelIndex(labelIndex), checks(checks), sets(sets) {} + LabelUseFinder(Index labelIndex, + std::map<Index, Index>& checks, + std::map<Index, Index>& sets) + : labelIndex(labelIndex), checks(checks), sets(sets) {} void visitIf(If* curr) { if (isLabelCheckingIf(curr, labelIndex)) { @@ -84,7 +93,8 @@ struct LabelUseFinder : public PostWalker<LabelUseFinder> { } }; -struct RelooperJumpThreading : public WalkerPass<ExpressionStackWalker<RelooperJumpThreading>> { +struct RelooperJumpThreading + : public WalkerPass<ExpressionStackWalker<RelooperJumpThreading>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new RelooperJumpThreading; } @@ -98,9 +108,11 @@ struct RelooperJumpThreading : public WalkerPass<ExpressionStackWalker<RelooperJ void visitBlock(Block* curr) { // look for the if label == X pattern auto& list = curr->list; - if (list.size() == 0) return; + if (list.size() == 0) + return; for (Index i = 0; i < list.size() - 1; i++) { - // once we see something that might be irreducible, we must skip that if and the rest of the dependents + // once we see something that might be irreducible, we must skip that if + // and the rest of the dependents bool irreducible = false; Index origin = i; for (Index j = i + 1; j < list.size(); j++) { @@ -113,15 +125,20 @@ struct RelooperJumpThreading : public WalkerPass<ExpressionStackWalker<RelooperJ i++; continue; } - // if the next element is a block, it may be the holding block of label-checking ifs + // if the next element is a block, it may be the holding block of + // label-checking ifs if (auto* holder = list[j]->dynCast<Block>()) { if (holder->list.size() > 0) { if (If* iff = isLabelCheckingIf(holder->list[0], labelIndex)) { irreducible |= hasIrreducibleControlFlow(iff, list[origin]); if (!irreducible) { - // this is indeed a holder. we can process the ifs, and must also move - // the block to enclose the origin, so it is properly reachable - assert(holder->list.size() == 1); // must be size 1, a relooper multiple will have its own label, and is an if-else sequence and nothing more + // this is indeed a holder. we can process the ifs, and must + // also move the block to enclose the origin, so it is properly + // reachable + + // must be size 1, a relooper multiple will have its own label, + // and is an if-else sequence and nothing more + assert(holder->list.size() == 1); optimizeJumpsToLabelCheck(list[origin], iff); holder->list[0] = list[origin]; list[origin] = holder; @@ -155,13 +172,13 @@ struct RelooperJumpThreading : public WalkerPass<ExpressionStackWalker<RelooperJ } private: - bool hasIrreducibleControlFlow(If* iff, Expression* origin) { - // Gather the checks in this if chain. If all the label values checked are only set in origin, - // then since origin is right before us, this is not irreducible - we can replace all sets - // in origin with jumps forward to us, and since there is nothing else, this is safe and complete. - // We must also have the property that there is just one check for the label value, as otherwise - // node splitting has complicated things. + // Gather the checks in this if chain. If all the label values checked are + // only set in origin, then since origin is right before us, this is not + // irreducible - we can replace all sets in origin with jumps forward to us, + // and since there is nothing else, this is safe and complete. We must also + // have the property that there is just one check for the label value, as + // otherwise node splitting has complicated things. std::map<Index, Index> labelChecksInOrigin; std::map<Index, Index> labelSetsInOrigin; LabelUseFinder finder(labelIndex, labelChecksInOrigin, labelSetsInOrigin); @@ -169,23 +186,27 @@ private: while (iff) { auto num = getCheckedLabelValue(iff); assert(labelChecks[num] > 0); - if (labelChecks[num] > 1) return true; // checked more than once, somewhere in function + if (labelChecks[num] > 1) + return true; // checked more than once, somewhere in function assert(labelChecksInOrigin[num] == 0); if (labelSetsInOrigin[num] != labelSets[num]) { assert(labelSetsInOrigin[num] < labelSets[num]); // the label is set outside of the origin - // if the only other location is inside the if body, then it is ok - it must be in a loop - // and returning to the top of the loop body, so we don't need to do anything for that - // label setting anyhow + // if the only other location is inside the if body, then it is ok - it + // must be in a loop and returning to the top of the loop body, so we + // don't need to do anything for that label setting anyhow std::map<Index, Index> labelChecksInIfTrue; std::map<Index, Index> labelSetsInIfTrue; - LabelUseFinder finder(labelIndex, labelChecksInIfTrue, labelSetsInIfTrue); + LabelUseFinder finder( + labelIndex, labelChecksInIfTrue, labelSetsInIfTrue); finder.walk(iff->ifTrue); if (labelSetsInOrigin[num] + labelSetsInIfTrue[num] < labelSets[num]) { - // label set somewhere we can't see now, could be irreducible control flow - // TODO: one case where this happens is instead of an if-chain, we have - // ifs and a switch on label|0, in separate elements. perhaps not - // emitting switches on label|0 in the relooper would avoid that. + // label set somewhere we can't see now, could be irreducible control + // flow + // TODO: one case where this happens is instead of an if-chain, we + // have ifs and a switch on label|0, in separate elements. + // perhaps not emitting switches on label|0 in the relooper + // would avoid that. return true; } } @@ -195,19 +216,23 @@ private: } // optimizes jumps to a label check - // * origin is where the jumps originate, and also where we should write our output + // * origin is where the jumps originate, and also where we should write our + // output // * iff is the if void optimizeJumpsToLabelCheck(Expression*& origin, If* iff) { Index nameCounter = newNameCounter++; Index num = getCheckedLabelValue(iff); // create a new block for this jump target Builder builder(*getModule()); - // origin is where all jumps to this target must come from - the element right before this if - // we break out of inner to reach the target. instead of flowing out of normally, we break out of the outer, so we skip the target. + // origin is where all jumps to this target must come from - the element + // right before this if we break out of inner to reach the target. instead + // of flowing out of normally, we break out of the outer, so we skip the + // target. auto innerName = getInnerName(nameCounter); auto outerName = getOuterName(nameCounter); auto* ifFalse = iff->ifFalse; - // all assignments of label to the target can be replaced with breaks to the target, via innerName + // all assignments of label to the target can be replaced with breaks to the + // target, via innerName struct JumpUpdater : public PostWalker<JumpUpdater> { Index labelIndex; Index targetNum; @@ -228,7 +253,8 @@ private: updater.setModule(getModule()); updater.walk(origin); // restructure code - auto* inner = builder.blockifyWithName(origin, innerName, builder.makeBreak(outerName)); + auto* inner = + builder.blockifyWithName(origin, innerName, builder.makeBreak(outerName)); auto* outer = builder.makeSequence(inner, iff->ifTrue); outer->name = outerName; origin = outer; @@ -241,9 +267,6 @@ private: // declare pass -Pass *createRelooperJumpThreadingPass() { - return new RelooperJumpThreading(); -} +Pass* createRelooperJumpThreadingPass() { return new RelooperJumpThreading(); } } // namespace wasm - diff --git a/src/passes/RemoveImports.cpp b/src/passes/RemoveImports.cpp index e70cbb3ac..a3ca45b16 100644 --- a/src/passes/RemoveImports.cpp +++ b/src/passes/RemoveImports.cpp @@ -22,14 +22,14 @@ // look at all the rest of the code). // -#include "wasm.h" -#include "pass.h" #include "ir/module-utils.h" +#include "pass.h" +#include "wasm.h" namespace wasm { struct RemoveImports : public WalkerPass<PostWalker<RemoveImports>> { - void visitCall(Call *curr) { + void visitCall(Call* curr) { auto* func = getModule()->getFunction(curr->target); if (!func->imported()) { return; @@ -44,19 +44,16 @@ struct RemoveImports : public WalkerPass<PostWalker<RemoveImports>> { } } - void visitModule(Module *curr) { + void visitModule(Module* curr) { std::vector<Name> names; - ModuleUtils::iterImportedFunctions(*curr, [&](Function* func) { - names.push_back(func->name); - }); + ModuleUtils::iterImportedFunctions( + *curr, [&](Function* func) { names.push_back(func->name); }); for (auto& name : names) { curr->removeFunction(name); } } }; -Pass *createRemoveImportsPass() { - return new RemoveImports(); -} +Pass* createRemoveImportsPass() { return new RemoveImports(); } } // namespace wasm diff --git a/src/passes/RemoveMemory.cpp b/src/passes/RemoveMemory.cpp index 33d9e6da5..399a32933 100644 --- a/src/passes/RemoveMemory.cpp +++ b/src/passes/RemoveMemory.cpp @@ -18,8 +18,8 @@ // Removeds memory segments, leaving only code in the module. // -#include <wasm.h> #include <pass.h> +#include <wasm.h> namespace wasm { @@ -29,8 +29,6 @@ struct RemoveMemory : public Pass { } }; -Pass *createRemoveMemoryPass() { - return new RemoveMemory(); -} +Pass* createRemoveMemoryPass() { return new RemoveMemory(); } } // namespace wasm diff --git a/src/passes/RemoveNonJSOps.cpp b/src/passes/RemoveNonJSOps.cpp index fc3e42185..26ede65c5 100644 --- a/src/passes/RemoveNonJSOps.cpp +++ b/src/passes/RemoveNonJSOps.cpp @@ -27,17 +27,17 @@ // after walking the current module. // -#include <wasm.h> #include <pass.h> +#include <wasm.h> -#include "asmjs/shared-constants.h" -#include "wasm-builder.h" -#include "wasm-s-parser.h" #include "abi/js.h" +#include "asmjs/shared-constants.h" +#include "ir/find_all.h" #include "ir/memory-utils.h" #include "ir/module-utils.h" -#include "ir/find_all.h" #include "passes/intrinsics-module.h" +#include "wasm-builder.h" +#include "wasm-s-parser.h" namespace wasm { @@ -56,7 +56,8 @@ struct RemoveNonJSOpsPass : public WalkerPass<PostWalker<RemoveNonJSOpsPass>> { // Discover all of the intrinsics that we need to inject, lowering all // operations to intrinsic calls while we're at it. - if (!builder) builder = make_unique<Builder>(*module); + if (!builder) + builder = make_unique<Builder>(*module); PostWalker<RemoveNonJSOpsPass>::doWalkModule(module); if (neededIntrinsics.size() == 0) { @@ -86,7 +87,7 @@ struct RemoveNonJSOpsPass : public WalkerPass<PostWalker<RemoveNonJSOpsPass>> { // Recursively probe all needed intrinsics for transitively used // functions. This is building up a set of functions we'll link into our // module. - for (auto &name : neededIntrinsics) { + for (auto& name : neededIntrinsics) { addNeededFunctions(intrinsicsModule, name, neededFunctions); } neededIntrinsics.clear(); @@ -94,10 +95,11 @@ struct RemoveNonJSOpsPass : public WalkerPass<PostWalker<RemoveNonJSOpsPass>> { // Link in everything that wasn't already linked in. After we've done the // copy we then walk the function to rewrite any non-js operations it has // as well. - for (auto &name : neededFunctions) { + for (auto& name : neededFunctions) { auto* func = module->getFunctionOrNull(name); if (!func) { - func = ModuleUtils::copyFunction(intrinsicsModule.getFunction(name), *module); + func = ModuleUtils::copyFunction(intrinsicsModule.getFunction(name), + *module); } doWalkFunction(func); } @@ -123,7 +125,7 @@ struct RemoveNonJSOpsPass : public WalkerPass<PostWalker<RemoveNonJSOpsPass>> { } } - void addNeededFunctions(Module &m, Name name, std::set<Name> &needed) { + void addNeededFunctions(Module& m, Name name, std::set<Name>& needed) { if (needed.count(name)) { return; } @@ -140,7 +142,8 @@ struct RemoveNonJSOpsPass : public WalkerPass<PostWalker<RemoveNonJSOpsPass>> { } void doWalkFunction(Function* func) { - if (!builder) builder = make_unique<Builder>(*getModule()); + if (!builder) + builder = make_unique<Builder>(*getModule()); PostWalker<RemoveNonJSOpsPass>::doWalkFunction(func); } @@ -224,10 +227,12 @@ struct RemoveNonJSOpsPass : public WalkerPass<PostWalker<RemoveNonJSOpsPass>> { name = WASM_I64_UREM; break; - default: return; + default: + return; } neededIntrinsics.insert(name); - replaceCurrent(builder->makeCall(name, {curr->left, curr->right}, curr->type)); + replaceCurrent( + builder->makeCall(name, {curr->left, curr->right}, curr->type)); } void rewriteCopysign(Binary* curr) { @@ -253,33 +258,20 @@ struct RemoveNonJSOpsPass : public WalkerPass<PostWalker<RemoveNonJSOpsPass>> { otherBits = Literal((uint64_t(1) << 63) - 1); break; - default: return; + default: + return; } - replaceCurrent( - builder->makeUnary( - int2float, - builder->makeBinary( - bitOr, - builder->makeBinary( - bitAnd, - builder->makeUnary( - float2int, - curr->left - ), - builder->makeConst(otherBits) - ), - builder->makeBinary( - bitAnd, - builder->makeUnary( - float2int, - curr->right - ), - builder->makeConst(signBit) - ) - ) - ) - ); + replaceCurrent(builder->makeUnary( + int2float, + builder->makeBinary( + bitOr, + builder->makeBinary(bitAnd, + builder->makeUnary(float2int, curr->left), + builder->makeConst(otherBits)), + builder->makeBinary(bitAnd, + builder->makeUnary(float2int, curr->right), + builder->makeConst(signBit))))); } void visitUnary(Unary* curr) { @@ -313,7 +305,8 @@ struct RemoveNonJSOpsPass : public WalkerPass<PostWalker<RemoveNonJSOpsPass>> { functionCall = WASM_CTZ32; break; - default: return; + default: + return; } neededIntrinsics.insert(functionCall); replaceCurrent(builder->makeCall(functionCall, {curr->value}, curr->type)); @@ -324,9 +317,6 @@ struct RemoveNonJSOpsPass : public WalkerPass<PostWalker<RemoveNonJSOpsPass>> { } }; -Pass* createRemoveNonJSOpsPass() { - return new RemoveNonJSOpsPass(); -} +Pass* createRemoveNonJSOpsPass() { return new RemoveNonJSOpsPass(); } } // namespace wasm - diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index 614503581..4b5c9613e 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -18,26 +18,31 @@ // Removes branches for which we go to where they go anyhow // -#include <wasm.h> -#include <pass.h> -#include <parsing.h> #include <ir/branch-utils.h> #include <ir/cost.h> #include <ir/effects.h> #include <ir/utils.h> +#include <parsing.h> +#include <pass.h> #include <wasm-builder.h> +#include <wasm.h> namespace wasm { // to turn an if into a br-if, we must be able to reorder the // condition and possible value, and the possible value must // not have side effects (as they would run unconditionally) -static bool canTurnIfIntoBrIf(Expression* ifCondition, Expression* brValue, PassOptions& options) { +static bool canTurnIfIntoBrIf(Expression* ifCondition, + Expression* brValue, + PassOptions& options) { // if the if isn't even reached, this is all dead code anyhow - if (ifCondition->type == unreachable) return false; - if (!brValue) return true; + if (ifCondition->type == unreachable) + return false; + if (!brValue) + return true; EffectAnalyzer value(options, brValue); - if (value.hasSideEffects()) return false; + if (value.hasSideEffects()) + return false; return !EffectAnalyzer(options, ifCondition).invalidates(value); } @@ -50,8 +55,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { typedef std::vector<Expression**> Flows; - // list of breaks that are currently flowing. if they reach their target without - // interference, they can be removed (or their value forwarded TODO) + // list of breaks that are currently flowing. if they reach their target + // without interference, they can be removed (or their value forwarded TODO) Flows flows; // a stack for if-else contents, we merge their outputs @@ -160,23 +165,22 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } } - void stopFlow() { - flows.clear(); - } + void stopFlow() { flows.clear(); } void removeValueFlow(Flows& currFlows) { - currFlows.erase(std::remove_if(currFlows.begin(), currFlows.end(), [&](Expression** currp) { - auto* curr = *currp; - if (auto* ret = curr->dynCast<Return>()) { - return ret->value; - } - return curr->cast<Break>()->value; - }), currFlows.end()); + currFlows.erase(std::remove_if(currFlows.begin(), + currFlows.end(), + [&](Expression** currp) { + auto* curr = *currp; + if (auto* ret = curr->dynCast<Return>()) { + return ret->value; + } + return curr->cast<Break>()->value; + }), + currFlows.end()); } - void stopValueFlow() { - removeValueFlow(flows); - } + void stopValueFlow() { removeValueFlow(flows); } static void clear(RemoveUnusedBrs* self, Expression** currp) { self->flows.clear(); @@ -186,20 +190,20 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { self->ifStack.push_back(std::move(self->flows)); } - void visitLoop(Loop* curr) { - loops.push_back(curr); - } + void visitLoop(Loop* curr) { loops.push_back(curr); } void optimizeSwitch(Switch* curr) { // if the final element is the default, we don't need it while (!curr->targets.empty() && curr->targets.back() == curr->default_) { curr->targets.pop_back(); } - // if the first element is the default, we can remove it by shifting everything (which - // does add a subtraction of a constant, but often that is worth it as the constant can - // be folded away and/or we remove multiple elements here) + // if the first element is the default, we can remove it by shifting + // everything (which does add a subtraction of a constant, but often that is + // worth it as the constant can be folded away and/or we remove multiple + // elements here) Index removable = 0; - while (removable < curr->targets.size() && curr->targets[removable] == curr->default_) { + while (removable < curr->targets.size() && + curr->targets[removable] == curr->default_) { removable++; } if (removable > 0) { @@ -208,50 +212,47 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } curr->targets.resize(curr->targets.size() - removable); Builder builder(*getModule()); - curr->condition = builder.makeBinary(SubInt32, - curr->condition, - builder.makeConst(Literal(int32_t(removable))) - ); + curr->condition = + builder.makeBinary(SubInt32, + curr->condition, + builder.makeConst(Literal(int32_t(removable)))); } - // when there isn't a value, we can do some trivial optimizations without worrying about - // the value being executed before the condition - if (curr->value) return; + // when there isn't a value, we can do some trivial optimizations without + // worrying about the value being executed before the condition + if (curr->value) + return; if (curr->targets.size() == 0) { // a switch with just a default always goes there Builder builder(*getModule()); - replaceCurrent(builder.makeSequence( - builder.makeDrop(curr->condition), - builder.makeBreak(curr->default_) - )); + replaceCurrent(builder.makeSequence(builder.makeDrop(curr->condition), + builder.makeBreak(curr->default_))); } else if (curr->targets.size() == 1) { // a switch with two options is basically an if Builder builder(*getModule()); - replaceCurrent(builder.makeIf( - curr->condition, - builder.makeBreak(curr->default_), - builder.makeBreak(curr->targets.front()) - )); + replaceCurrent(builder.makeIf(curr->condition, + builder.makeBreak(curr->default_), + builder.makeBreak(curr->targets.front()))); } else { - // there are also some other cases where we want to convert a switch into ifs, - // especially if the switch is large and we are focusing on size. - // an especially egregious case is a switch like this: - // [a b b [..] b b c] with default b - // (which may be arrived at after we trim away excess default values on both - // sides). in this case, we really have 3 values in a simple form, so it is the - // next logical case after handling 1 and 2 values right above here. - // to optimize this, we must add a local + a bunch of nodes (if*2, tee, eq, - // get, const, break*3), so the table must be big enough for it to make sense + // there are also some other cases where we want to convert a switch into + // ifs, especially if the switch is large and we are focusing on size. an + // especially egregious case is a switch like this: [a b b [..] b b c] + // with default b (which may be arrived at after we trim away excess + // default values on both sides). in this case, we really have 3 values in + // a simple form, so it is the next logical case after handling 1 and 2 + // values right above here. to optimize this, we must add a local + a + // bunch of nodes (if*2, tee, eq, get, const, break*3), so the table must + // be big enough for it to make sense - // How many targets we need when shrinking. This is literally the size at which - // the transformation begins to be smaller. + // How many targets we need when shrinking. This is literally the size at + // which the transformation begins to be smaller. const uint32_t MIN_SHRINK = 13; - // How many targets we need when not shrinking, in which case, 2 ifs may be slower, - // so we do this when the table is ridiculously large for one with just 3 values - // in it. + // How many targets we need when not shrinking, in which case, 2 ifs may + // be slower, so we do this when the table is ridiculously large for one + // with just 3 values in it. const uint32_t MIN_GENERAL = 128; auto shrink = getPassRunner()->options.shrinkLevel > 0; - if ((curr->targets.size() >= MIN_SHRINK && shrink) || + if ((curr->targets.size() >= MIN_SHRINK && shrink) || (curr->targets.size() >= MIN_GENERAL && !shrink)) { for (Index i = 1; i < curr->targets.size() - 1; i++) { if (curr->targets[i] != curr->default_) { @@ -262,25 +263,24 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { Builder builder(*getModule()); auto temp = builder.addVar(getFunction(), i32); Expression* z; - replaceCurrent(z = builder.makeIf( - builder.makeTeeLocal(temp, curr->condition), - builder.makeIf( - builder.makeBinary(EqInt32, - builder.makeGetLocal(temp, i32), - builder.makeConst(Literal(int32_t(curr->targets.size() - 1))) - ), - builder.makeBreak(curr->targets.back()), - builder.makeBreak(curr->default_) - ), - builder.makeBreak(curr->targets.front()) - )); + replaceCurrent( + z = builder.makeIf( + builder.makeTeeLocal(temp, curr->condition), + builder.makeIf(builder.makeBinary(EqInt32, + builder.makeGetLocal(temp, i32), + builder.makeConst(Literal(int32_t( + curr->targets.size() - 1)))), + builder.makeBreak(curr->targets.back()), + builder.makeBreak(curr->default_)), + builder.makeBreak(curr->targets.front()))); } } } void visitIf(If* curr) { if (!curr->ifFalse) { - // if without an else. try to reduce if (condition) br => br_if (condition) + // if without an else. try to reduce if (condition) br => br_if + // (condition) Break* br = curr->ifTrue->dynCast<Break>(); if (br && !br->condition) { // TODO: if there is a condition, join them if (canTurnIfIntoBrIf(curr->condition, br->value, getPassOptions())) { @@ -291,9 +291,9 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } } } - // TODO: if-else can be turned into a br_if as well, if one of the sides is a dead end - // we handle the case of a returned value to a local.set later down, see - // visitSetLocal. + // TODO: if-else can be turned into a br_if as well, if one of the sides is + // a dead end we handle the case of a returned value to a local.set + // later down, see visitSetLocal. } // override scan to add a pre and a post check task to all nodes @@ -309,9 +309,11 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } self->pushTask(doVisitIf, currp); if (iff->ifFalse) { - // we need to join up if-else control flow, and clear after the condition + // we need to join up if-else control flow, and clear after the + // condition self->pushTask(scan, &iff->ifFalse); - self->pushTask(saveIfTrue, currp); // safe the ifTrue flow, we'll join it later + // safe the ifTrue flow, we'll join it later + self->pushTask(saveIfTrue, currp); } self->pushTask(scan, &iff->ifTrue); self->pushTask(clear, currp); // clear all flow after the condition @@ -342,24 +344,32 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { // helpful, as it shortens the logical loop. it is also good to generate // an if-else instead of an if, as it might allow an eqz to be removed // by flipping arms) - if (!loop->name.is()) return false; + if (!loop->name.is()) + return false; auto* block = loop->body->dynCast<Block>(); - if (!block) return false; + if (!block) + return false; // does the last element break to the top of the loop? auto& list = block->list; - if (list.size() <= 1) return false; + if (list.size() <= 1) + return false; auto* last = list.back()->dynCast<Break>(); - if (!last || !ExpressionAnalyzer::isSimple(last) || last->name != loop->name) return false; - // last is a simple break to the top of the loop. if we can conditionalize it, - // it won't block things from flowing out and not needing breaks to do so. + if (!last || !ExpressionAnalyzer::isSimple(last) || + last->name != loop->name) + return false; + // last is a simple break to the top of the loop. if we can conditionalize + // it, it won't block things from flowing out and not needing breaks to do + // so. Index i = list.size() - 2; Builder builder(*getModule()); while (1) { auto* curr = list[i]; if (auto* iff = curr->dynCast<If>()) { - // let's try to move the code going to the top of the loop into the if-else + // let's try to move the code going to the top of the loop into the + // if-else if (!iff->ifFalse) { - // we need the ifTrue to break, so it cannot reach the code we want to move + // we need the ifTrue to break, so it cannot reach the code we want to + // move if (iff->ifTrue->type == unreachable) { iff->ifFalse = builder.stealSlice(block, i + 1, list.size()); iff->finalize(); @@ -367,20 +377,25 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { return true; } } else { - // this is already an if-else. if one side is a dead end, we can append to the other, if - // there is no returned value to concern us - assert(!isConcreteType(iff->type)); // can't be, since in the middle of a block + // this is already an if-else. if one side is a dead end, we can + // append to the other, if there is no returned value to concern us - // ensures the first node is a block, if it isn't already, and merges in the second, - // either as a single element or, if a block, by appending to the first block. this - // keeps the order of operations in place, that is, the appended element will be - // executed after the first node's elements - auto blockifyMerge = [&](Expression* any, Expression* append) -> Block* { + // can't be, since in the middle of a block + assert(!isConcreteType(iff->type)); + + // ensures the first node is a block, if it isn't already, and merges + // in the second, either as a single element or, if a block, by + // appending to the first block. this keeps the order of operations in + // place, that is, the appended element will be executed after the + // first node's elements + auto blockifyMerge = [&](Expression* any, + Expression* append) -> Block* { Block* block = nullptr; - if (any) block = any->dynCast<Block>(); - // if the first isn't a block, or it's a block with a name (so we might - // branch to the end, and so can't append to it, we might skip that code!) - // then make a new block + if (any) + block = any->dynCast<Block>(); + // if the first isn't a block, or it's a block with a name (so we + // might branch to the end, and so can't append to it, we might skip + // that code!) then make a new block if (!block || block->name.is()) { block = builder.makeBlock(any); } else { @@ -399,12 +414,14 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { }; if (iff->ifTrue->type == unreachable) { - iff->ifFalse = blockifyMerge(iff->ifFalse, builder.stealSlice(block, i + 1, list.size())); + iff->ifFalse = blockifyMerge( + iff->ifFalse, builder.stealSlice(block, i + 1, list.size())); iff->finalize(); block->finalize(); return true; } else if (iff->ifFalse->type == unreachable) { - iff->ifTrue = blockifyMerge(iff->ifTrue, builder.stealSlice(block, i + 1, list.size())); + iff->ifTrue = blockifyMerge( + iff->ifTrue, builder.stealSlice(block, i + 1, list.size())); iff->finalize(); block->finalize(); return true; @@ -415,7 +432,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { // br_if is similar to if. if (brIf->condition && !brIf->value && brIf->name != loop->name) { if (i == list.size() - 2) { - // there is the br_if, and then the br to the top, so just flip them and the condition + // there is the br_if, and then the br to the top, so just flip them + // and the condition brIf->condition = builder.makeUnary(EqZInt32, brIf->condition); last->name = brIf->name; brIf->name = loop->name; @@ -428,11 +446,18 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { // we can convert the br_if to an if. this has a cost, though, // so only do it if it looks useful, which it definitely is if // (a) $somewhere is straight out (so the br out vanishes), and - // (b) this br_if is the only branch to that block (so the block will vanish) - if (brIf->name == block->name && BranchUtils::BranchSeeker::countNamed(block, block->name) == 1) { - // note that we could drop the last element here, it is a br we know for sure is removable, - // but telling stealSlice to steal all to the end is more efficient, it can just truncate. - list[i] = builder.makeIf(brIf->condition, builder.makeBreak(brIf->name), builder.stealSlice(block, i + 1, list.size())); + // (b) this br_if is the only branch to that block (so the block + // will vanish) + if (brIf->name == block->name && + BranchUtils::BranchSeeker::countNamed(block, block->name) == + 1) { + // note that we could drop the last element here, it is a br we + // know for sure is removable, but telling stealSlice to steal all + // to the end is more efficient, it can just truncate. + list[i] = + builder.makeIf(brIf->condition, + builder.makeBreak(brIf->name), + builder.stealSlice(block, i + 1, list.size())); return true; } } @@ -443,7 +468,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { if (EffectAnalyzer(getPassOptions(), curr).branches) { return false; } - if (i == 0) return false; + if (i == 0) + return false; i--; } } @@ -453,11 +479,11 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { bool worked = false; void visitBlock(Block* curr) { - // If the block has a single child which is a loop, and the block is named, - // then it is the exit for the loop. It's better to move it into the loop, - // where it can be better optimized by other passes. - // Similar logic for ifs: if the block is an exit for the if, we can - // move the block in, consider for example: + // If the block has a single child which is a loop, and the block is + // named, then it is the exit for the loop. It's better to move it into + // the loop, where it can be better optimized by other passes. Similar + // logic for ifs: if the block is an exit for the if, we can move the + // block in, consider for example: // (block $label // (if (..condition1..) // (block @@ -484,27 +510,31 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { worked = true; } else if (auto* iff = curr->list[0]->dynCast<If>()) { // The label can't be used in the condition. - if (BranchUtils::BranchSeeker::countNamed(iff->condition, curr->name) == 0) { - // We can move the block into either arm, if there are no uses in the other. + if (BranchUtils::BranchSeeker::countNamed(iff->condition, + curr->name) == 0) { + // We can move the block into either arm, if there are no uses in + // the other. Expression** target = nullptr; - if (!iff->ifFalse || - BranchUtils::BranchSeeker::countNamed(iff->ifFalse, curr->name) == 0) { + if (!iff->ifFalse || BranchUtils::BranchSeeker::countNamed( + iff->ifFalse, curr->name) == 0) { target = &iff->ifTrue; - } else if (BranchUtils::BranchSeeker::countNamed(iff->ifTrue, curr->name) == 0) { + } else if (BranchUtils::BranchSeeker::countNamed( + iff->ifTrue, curr->name) == 0) { target = &iff->ifFalse; } if (target) { curr->list[0] = *target; *target = curr; - // The block used to contain the if, and may have changed type from unreachable - // to none, for example, if the if has an unreachable condition but the arm - // is not unreachable. + // The block used to contain the if, and may have changed type + // from unreachable to none, for example, if the if has an + // unreachable condition but the arm is not unreachable. curr->finalize(); iff->finalize(); replaceCurrent(iff); worked = true; - // Note that the type might change, e.g. if the if condition is unreachable - // but the block that was on the outside had a break. + // Note that the type might change, e.g. if the if condition is + // unreachable but the block that was on the outside had a + // break. } } } @@ -526,10 +556,12 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { anotherCycle = false; super::doWalkFunction(func); assert(ifStack.empty()); - // flows may contain returns, which are flowing out and so can be optimized + // flows may contain returns, which are flowing out and so can be + // optimized for (Index i = 0; i < flows.size(); i++) { auto* flow = (*flows[i])->dynCast<Return>(); - if (!flow) continue; + if (!flow) + continue; if (!flow->value) { // return => nop ExpressionManipulator::nop(flow); @@ -541,7 +573,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } } flows.clear(); - // optimize loops (we don't do it while tracking flows, as they can interfere) + // optimize loops (we don't do it while tracking flows, as they can + // interfere) for (auto* loop : loops) { anotherCycle |= optimizeLoop(loop); } @@ -557,7 +590,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { // thread trivial jumps struct JumpThreader : public ControlFlowWalker<JumpThreader> { - // map of all value-less breaks and switches going to a block (and not a loop) + // map of all value-less breaks and switches going to a block (and not a + // loop) std::map<Block*, std::vector<Expression*>> branchesToBlock; bool worked = false; @@ -582,19 +616,25 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { void visitBlock(Block* curr) { auto& list = curr->list; if (list.size() == 1 && curr->name.is()) { - // if this block has just one child, a sub-block, then jumps to the former are jumps to us, really + // if this block has just one child, a sub-block, then jumps to the + // former are jumps to us, really if (auto* child = list[0]->dynCast<Block>()) { - // the two blocks must have the same type for us to update the branch, as otherwise - // one block may be unreachable and the other concrete, so one might lack a value - if (child->name.is() && child->name != curr->name && child->type == curr->type) { + // the two blocks must have the same type for us to update the + // branch, as otherwise one block may be unreachable and the other + // concrete, so one might lack a value + if (child->name.is() && child->name != curr->name && + child->type == curr->type) { redirectBranches(child, curr->name); } } } else if (list.size() == 2) { - // if this block has two children, a child-block and a simple jump, then jumps to child-block can be replaced with jumps to the new target + // if this block has two children, a child-block and a simple jump, + // then jumps to child-block can be replaced with jumps to the new + // target auto* child = list[0]->dynCast<Block>(); auto* jump = list[1]->dynCast<Break>(); - if (child && child->name.is() && jump && ExpressionAnalyzer::isSimple(jump)) { + if (child && child->name.is() && jump && + ExpressionAnalyzer::isSimple(jump)) { redirectBranches(child, jump->name); } } @@ -607,7 +647,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { worked = true; } } - // if the jump is to another block then we can update the list, and maybe push it even more later + // if the jump is to another block then we can update the list, and + // maybe push it even more later if (auto* newTarget = findBreakTarget(to)->dynCast<Block>()) { for (auto* branch : branches) { branchesToBlock[newTarget].push_back(branch); @@ -637,18 +678,27 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { FinalOptimizer(PassOptions& passOptions) : passOptions(passOptions) {} void visitBlock(Block* curr) { - // if a block has an if br else br, we can un-conditionalize the latter, allowing - // the if to become a br_if. - // * note that if not in a block already, then we need to create a block for this, so not useful otherwise - // * note that this only happens at the end of a block, as code after the if is dead - // * note that we do this at the end, because un-conditionalizing can interfere with optimizeLoop()ing. + // if a block has an if br else br, we can un-conditionalize the latter, + // allowing the if to become a br_if. + // * note that if not in a block already, then we need to create a block + // for this, so not useful otherwise + // * note that this only happens at the end of a block, as code after + // the if is dead + // * note that we do this at the end, because un-conditionalizing can + // interfere with optimizeLoop()ing. auto& list = curr->list; for (Index i = 0; i < list.size(); i++) { auto* iff = list[i]->dynCast<If>(); - if (!iff || !iff->ifFalse) continue; // if it lacked an if-false, it would already be a br_if, as that's the easy case + if (!iff || !iff->ifFalse) + // if it lacked an if-false, it would already be a br_if, as that's + // the easy case + continue; auto* ifTrueBreak = iff->ifTrue->dynCast<Break>(); - if (ifTrueBreak && !ifTrueBreak->condition && canTurnIfIntoBrIf(iff->condition, ifTrueBreak->value, passOptions)) { - // we are an if-else where the ifTrue is a break without a condition, so we can do this + if (ifTrueBreak && !ifTrueBreak->condition && + canTurnIfIntoBrIf( + iff->condition, ifTrueBreak->value, passOptions)) { + // we are an if-else where the ifTrue is a break without a + // condition, so we can do this ifTrueBreak->condition = iff->condition; ifTrueBreak->finalize(); list[i] = Builder(*getModule()).dropIfConcretelyTyped(ifTrueBreak); @@ -657,8 +707,11 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } // otherwise, perhaps we can flip the if auto* ifFalseBreak = iff->ifFalse->dynCast<Break>(); - if (ifFalseBreak && !ifFalseBreak->condition && canTurnIfIntoBrIf(iff->condition, ifFalseBreak->value, passOptions)) { - ifFalseBreak->condition = Builder(*getModule()).makeUnary(EqZInt32, iff->condition); + if (ifFalseBreak && !ifFalseBreak->condition && + canTurnIfIntoBrIf( + iff->condition, ifFalseBreak->value, passOptions)) { + ifFalseBreak->condition = + Builder(*getModule()).makeUnary(EqZInt32, iff->condition); ifFalseBreak->finalize(); list[i] = Builder(*getModule()).dropIfConcretelyTyped(ifFalseBreak); ExpressionManipulator::spliceIntoBlock(curr, i + 1, iff->ifTrue); @@ -669,22 +722,26 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { // combine/optimize adjacent br_ifs + a br (maybe _if) right after it for (Index i = 0; i < list.size() - 1; i++) { auto* br1 = list[i]->dynCast<Break>(); - // avoid unreachable brs, as they are dead code anyhow, and after merging - // them the outer scope could need type changes - if (!br1 || !br1->condition || br1->type == unreachable) continue; + // avoid unreachable brs, as they are dead code anyhow, and after + // merging them the outer scope could need type changes + if (!br1 || !br1->condition || br1->type == unreachable) + continue; assert(!br1->value); auto* br2 = list[i + 1]->dynCast<Break>(); - if (!br2 || br1->name != br2->name) continue; + if (!br2 || br1->name != br2->name) + continue; assert(!br2->value); // same target as previous, which has no value // a br_if and then a br[_if] with the same target right after it if (br2->condition) { if (shrink && br2->type != unreachable) { - // Join adjacent br_ifs to the same target, making one br_if with - // a "selectified" condition that executes both. - if (!EffectAnalyzer(passOptions, br2->condition).hasSideEffects()) { + // Join adjacent br_ifs to the same target, making one br_if + // with a "selectified" condition that executes both. + if (!EffectAnalyzer(passOptions, br2->condition) + .hasSideEffects()) { // it's ok to execute them both, do it Builder builder(*getModule()); - br1->condition = builder.makeBinary(OrInt32, br1->condition, br2->condition); + br1->condition = + builder.makeBinary(OrInt32, br1->condition, br2->condition); ExpressionManipulator::nop(br2); } } @@ -706,12 +763,9 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { if (BranchUtils::getUniqueTargets(curr).size() == 1) { // This switch has just one target no matter what; replace with a br. Builder builder(*getModule()); - replaceCurrent( - builder.makeSequence( - builder.makeDrop(curr->condition), // might have side effects - builder.makeBreak(curr->default_, curr->value) - ) - ); + replaceCurrent(builder.makeSequence( + builder.makeDrop(curr->condition), // might have side effects + builder.makeBreak(curr->default_, curr->value))); } } @@ -754,36 +808,32 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } else { br = list[0]->dynCast<Break>(); } - // Check if the br is conditional and goes to the block. It may or may not have - // a value, depending on if it was dropped or not. - // If the type is unreachable that means it is not actually reached, - // which we can ignore. - if (br && br->condition && br->name == curr->name && br->type != unreachable) { + // Check if the br is conditional and goes to the block. It may or may + // not have a value, depending on if it was dropped or not. If the + // type is unreachable that means it is not actually reached, which we + // can ignore. + if (br && br->condition && br->name == curr->name && + br->type != unreachable) { if (BranchUtils::BranchSeeker::countNamed(curr, curr->name) == 1) { // no other breaks to that name, so we can do this if (!drop) { assert(!br->value); Builder builder(*getModule()); replaceCurrent(builder.makeIf( - builder.makeUnary(EqZInt32, br->condition), - curr - )); + builder.makeUnary(EqZInt32, br->condition), curr)); ExpressionManipulator::nop(br); curr->finalize(curr->type); } else { - // If the items we move around have side effects, we can't do this. + // If the items we move around have side effects, we can't do + // this. // TODO: we could use a select, in some cases..? if (!EffectAnalyzer(passOptions, br->value).hasSideEffects() && - !EffectAnalyzer(passOptions, br->condition).hasSideEffects()) { + !EffectAnalyzer(passOptions, br->condition) + .hasSideEffects()) { ExpressionManipulator::nop(list[0]); Builder builder(*getModule()); replaceCurrent( - builder.makeIf( - br->condition, - br->value, - curr - ) - ); + builder.makeIf(br->condition, br->value, curr)); } } } @@ -800,20 +850,20 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { // Convert an if into a select, if possible and beneficial to do so. Select* selectify(If* iff) { - if (!iff->ifFalse || - !isConcreteType(iff->ifTrue->type) || + if (!iff->ifFalse || !isConcreteType(iff->ifTrue->type) || !isConcreteType(iff->ifFalse->type)) { return nullptr; } - // This is always helpful for code size, but can be a tradeoff with performance - // as we run both code paths. So when shrinking we always try to do this, but - // otherwise must consider more carefully. + // This is always helpful for code size, but can be a tradeoff with + // performance as we run both code paths. So when shrinking we always + // try to do this, but otherwise must consider more carefully. if (!passOptions.shrinkLevel) { // Consider the cost of executing all the code unconditionally const auto MAX_COST = 7; - auto total = CostAnalyzer(iff->ifTrue).cost + - CostAnalyzer(iff->ifFalse).cost; - if (total >= MAX_COST) return nullptr; + auto total = + CostAnalyzer(iff->ifTrue).cost + CostAnalyzer(iff->ifFalse).cost; + if (total >= MAX_COST) + return nullptr; } // Check if side effects allow this. EffectAnalyzer condition(passOptions, iff->condition); @@ -822,11 +872,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { if (!ifTrue.hasSideEffects()) { EffectAnalyzer ifFalse(passOptions, iff->ifFalse); if (!ifFalse.hasSideEffects()) { - return Builder(*getModule()).makeSelect( - iff->condition, - iff->ifTrue, - iff->ifFalse - ); + return Builder(*getModule()) + .makeSelect(iff->condition, iff->ifTrue, iff->ifFalse); } } } @@ -842,8 +889,10 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } void optimizeSetIf(Expression** currp) { - if (optimizeSetIfWithBrArm(currp)) return; - if (optimizeSetIfWithCopyArm(currp)) return; + if (optimizeSetIfWithBrArm(currp)) + return; + if (optimizeSetIfWithCopyArm(currp)) + return; } // If one arm is a br, we prefer a br_if and the set later: @@ -865,33 +914,33 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { bool optimizeSetIfWithBrArm(Expression** currp) { auto* set = (*currp)->cast<SetLocal>(); auto* iff = set->value->dynCast<If>(); - if (!iff || - !isConcreteType(iff->type) || + if (!iff || !isConcreteType(iff->type) || !isConcreteType(iff->condition->type)) { return false; } - auto tryToOptimize = [&](Expression* one, Expression* two, bool flipCondition) { - if (one->type == unreachable && two->type != unreachable) { - if (auto* br = one->dynCast<Break>()) { - if (ExpressionAnalyzer::isSimple(br)) { - // Wonderful, do it! - Builder builder(*getModule()); - if (flipCondition) { - builder.flip(iff); + auto tryToOptimize = + [&](Expression* one, Expression* two, bool flipCondition) { + if (one->type == unreachable && two->type != unreachable) { + if (auto* br = one->dynCast<Break>()) { + if (ExpressionAnalyzer::isSimple(br)) { + // Wonderful, do it! + Builder builder(*getModule()); + if (flipCondition) { + builder.flip(iff); + } + br->condition = iff->condition; + br->finalize(); + set->value = two; + auto* block = builder.makeSequence(br, set); + *currp = block; + // Recurse on the set, which now has a new value. + optimizeSetIf(&block->list[1]); + return true; } - br->condition = iff->condition; - br->finalize(); - set->value = two; - auto* block = builder.makeSequence(br, set); - *currp = block; - // Recurse on the set, which now has a new value. - optimizeSetIf(&block->list[1]); - return true; } } - } - return false; - }; + return false; + }; return tryToOptimize(iff->ifTrue, iff->ifFalse, false) || tryToOptimize(iff->ifFalse, iff->ifTrue, true); } @@ -940,8 +989,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { bool optimizeSetIfWithCopyArm(Expression** currp) { auto* set = (*currp)->cast<SetLocal>(); auto* iff = set->value->dynCast<If>(); - if (!iff || - !isConcreteType(iff->type) || + if (!iff || !isConcreteType(iff->type) || !isConcreteType(iff->condition->type)) { return false; } @@ -955,7 +1003,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { get = nullptr; } } - if (!get) return false; + if (!get) + return false; // We can do it! bool tee = set->isTee(); assert(set->index == get->index); @@ -969,9 +1018,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { if (tee) { set->setTee(false); // We need a block too. - replacement = builder.makeSequence( - iff, - get // reuse the get + replacement = builder.makeSequence(iff, + get // reuse the get ); } *currp = replacement; @@ -996,10 +1044,12 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { // ) // TODO: consider also looking at <= etc. and not just eq void tablify(Block* block) { - auto &list = block->list; - if (list.size() <= 1) return; + auto& list = block->list; + if (list.size() <= 1) + return; - // Heuristics. These are slightly inspired by the constants from the asm.js backend. + // Heuristics. These are slightly inspired by the constants from the + // asm.js backend. // How many br_ifs we need to see to consider doing this const uint32_t MIN_NUM = 3; @@ -1009,35 +1059,51 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { // this is high, we allow larger ranges. const uint32_t NUM_TO_RANGE_FACTOR = 3; - // check if the input is a proper br_if on an i32.eq of a condition value to a const, - // and the const is in the proper range, [0-int32_max), to avoid overflow concerns. - // returns the br_if if so, or nullptr otherwise - auto getProperBrIf = [](Expression* curr) -> Break*{ + // check if the input is a proper br_if on an i32.eq of a condition + // value to a const, and the const is in the proper range, + // [0-int32_max), to avoid overflow concerns. returns the br_if if so, + // or nullptr otherwise + auto getProperBrIf = [](Expression* curr) -> Break* { auto* br = curr->dynCast<Break>(); - if (!br) return nullptr; - if (!br->condition || br->value) return nullptr; - if (br->type != none) return nullptr; // no value, so can be unreachable or none. ignore unreachable ones, dce will clean it up + if (!br) + return nullptr; + if (!br->condition || br->value) + return nullptr; + if (br->type != none) + // no value, so can be unreachable or none. ignore unreachable ones, + // dce will clean it up + return nullptr; auto* binary = br->condition->dynCast<Binary>(); - if (!binary) return nullptr; - if (binary->op != EqInt32) return nullptr; + if (!binary) + return nullptr; + if (binary->op != EqInt32) + return nullptr; auto* c = binary->right->dynCast<Const>(); - if (!c) return nullptr; + if (!c) + return nullptr; uint32_t value = c->value.geti32(); - if (value >= uint32_t(std::numeric_limits<int32_t>::max())) return nullptr; + if (value >= uint32_t(std::numeric_limits<int32_t>::max())) + return nullptr; return br; }; // check if the input is a proper br_if // and returns the condition if so, or nullptr otherwise - auto getProperBrIfConditionValue = [&getProperBrIf](Expression* curr) -> Expression* { + auto getProperBrIfConditionValue = + [&getProperBrIf](Expression* curr) -> Expression* { auto* br = getProperBrIf(curr); - if (!br) return nullptr; + if (!br) + return nullptr; return br->condition->cast<Binary>()->left; }; // returns the constant value, as a uint32_t - auto getProperBrIfConstant = [&getProperBrIf](Expression* curr) -> uint32_t { - return getProperBrIf(curr)->condition->cast<Binary>()->right->cast<Const>()->value.geti32(); + auto getProperBrIfConstant = + [&getProperBrIf](Expression* curr) -> uint32_t { + return getProperBrIf(curr) + ->condition->cast<Binary>() + ->right->cast<Const>() + ->value.geti32(); }; Index start = 0; while (start < list.size() - 1) { @@ -1046,23 +1112,25 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { start++; continue; } - // if the condition has side effects, we can't replace many appearances of it - // with a single one + // if the condition has side effects, we can't replace many + // appearances of it with a single one if (EffectAnalyzer(passOptions, conditionValue).hasSideEffects()) { start++; continue; } - // look for a "run" of br_ifs with all the same conditionValue, and having - // unique constants (an overlapping constant could be handled, just the first - // branch is taken, but we can't remove the other br_if (it may be the only - // branch keeping a block reachable), which may make this bad for code size. + // look for a "run" of br_ifs with all the same conditionValue, and + // having unique constants (an overlapping constant could be handled, + // just the first branch is taken, but we can't remove the other br_if + // (it may be the only branch keeping a block reachable), which may + // make this bad for code size. Index end = start + 1; std::unordered_set<uint32_t> usedConstants; usedConstants.insert(getProperBrIfConstant(list[start])); while (end < list.size() && - ExpressionAnalyzer::equal(getProperBrIfConditionValue(list[end]), - conditionValue)) { - if (!usedConstants.insert(getProperBrIfConstant(list[end])).second) { + ExpressionAnalyzer::equal( + getProperBrIfConditionValue(list[end]), conditionValue)) { + if (!usedConstants.insert(getProperBrIfConstant(list[end])) + .second) { // this constant already appeared break; } @@ -1081,8 +1149,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } uint32_t range = max - min; // decision time - if (range <= MAX_RANGE && - range <= num * NUM_TO_RANGE_FACTOR) { + if (range <= MAX_RANGE && range <= num * NUM_TO_RANGE_FACTOR) { // great! let's do this std::unordered_set<Name> usedNames; for (Index i = start; i < end; i++) { @@ -1093,7 +1160,8 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { Index i = 0; while (1) { defaultName = "tablify|" + std::to_string(i++); - if (usedNames.count(defaultName) == 0) break; + if (usedNames.count(defaultName) == 0) + break; } std::vector<Name> table; for (Index i = start; i < end; i++) { @@ -1103,26 +1171,21 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { while (table.size() <= index) { table.push_back(defaultName); } - assert(table[index] == defaultName); // we should have made sure there are no overlaps + // we should have made sure there are no overlaps + assert(table[index] == defaultName); table[index] = name; } Builder builder(*getModule()); // the table and condition are offset by the min if (min != 0) { - conditionValue = builder.makeBinary( - SubInt32, - conditionValue, - builder.makeConst(Literal(int32_t(min))) - ); + conditionValue = + builder.makeBinary(SubInt32, + conditionValue, + builder.makeConst(Literal(int32_t(min)))); } list[end - 1] = builder.makeBlock( defaultName, - builder.makeSwitch( - table, - defaultName, - conditionValue - ) - ); + builder.makeSwitch(table, defaultName, conditionValue)); for (Index i = start; i < end - 1; i++) { ExpressionManipulator::nop(list[i]); } @@ -1145,8 +1208,6 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { } }; -Pass *createRemoveUnusedBrsPass() { - return new RemoveUnusedBrs(); -} +Pass* createRemoveUnusedBrsPass() { return new RemoveUnusedBrs(); } } // namespace wasm diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index e91fafcba..3338bb756 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -20,21 +20,17 @@ // and remove if unneeded) // - #include <memory> -#include "wasm.h" -#include "pass.h" +#include "asm_v_wasm.h" #include "ir/module-utils.h" #include "ir/utils.h" -#include "asm_v_wasm.h" +#include "pass.h" +#include "wasm.h" namespace wasm { -enum class ModuleElementKind { - Function, - Global -}; +enum class ModuleElementKind { Function, Global }; typedef std::pair<ModuleElementKind, Name> ModuleElement; @@ -48,7 +44,8 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> { bool usesMemory = false; bool usesTable = false; - ReachabilityAnalyzer(Module* module, const std::vector<ModuleElement>& roots) : module(module) { + ReachabilityAnalyzer(Module* module, const std::vector<ModuleElement>& roots) + : module(module) { queue = roots; // Globals used in memory/table init expressions are also roots for (auto& segment : module->memory.segments) { @@ -83,55 +80,36 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> { } void visitCall(Call* curr) { - if (reachable.count(ModuleElement(ModuleElementKind::Function, curr->target)) == 0) { + if (reachable.count( + ModuleElement(ModuleElementKind::Function, curr->target)) == 0) { queue.emplace_back(ModuleElementKind::Function, curr->target); } } - void visitCallIndirect(CallIndirect* curr) { - usesTable = true; - } + void visitCallIndirect(CallIndirect* curr) { usesTable = true; } void visitGetGlobal(GetGlobal* curr) { - if (reachable.count(ModuleElement(ModuleElementKind::Global, curr->name)) == 0) { + if (reachable.count(ModuleElement(ModuleElementKind::Global, curr->name)) == + 0) { queue.emplace_back(ModuleElementKind::Global, curr->name); } } void visitSetGlobal(SetGlobal* curr) { - if (reachable.count(ModuleElement(ModuleElementKind::Global, curr->name)) == 0) { + if (reachable.count(ModuleElement(ModuleElementKind::Global, curr->name)) == + 0) { queue.emplace_back(ModuleElementKind::Global, curr->name); } } - void visitLoad(Load* curr) { - usesMemory = true; - } - void visitStore(Store* curr) { - usesMemory = true; - } - void visitAtomicCmpxchg(AtomicCmpxchg* curr) { - usesMemory = true; - } - void visitAtomicRMW(AtomicRMW* curr) { - usesMemory = true; - } - void visitAtomicWait(AtomicWait* curr) { - usesMemory = true; - } - void visitAtomicNotify(AtomicNotify* curr) { - usesMemory = true; - } - void visitMemoryInit(MemoryInit* curr) { - usesMemory = true; - } - void visitDataDrop(DataDrop* curr) { - usesMemory = true; - } - void visitMemoryCopy(MemoryCopy* curr) { - usesMemory = true; - } - void visitMemoryFill(MemoryFill* curr) { - usesMemory = true; - } + void visitLoad(Load* curr) { usesMemory = true; } + void visitStore(Store* curr) { usesMemory = true; } + void visitAtomicCmpxchg(AtomicCmpxchg* curr) { usesMemory = true; } + void visitAtomicRMW(AtomicRMW* curr) { usesMemory = true; } + void visitAtomicWait(AtomicWait* curr) { usesMemory = true; } + void visitAtomicNotify(AtomicNotify* curr) { usesMemory = true; } + void visitMemoryInit(MemoryInit* curr) { usesMemory = true; } + void visitDataDrop(DataDrop* curr) { usesMemory = true; } + void visitMemoryCopy(MemoryCopy* curr) { usesMemory = true; } + void visitMemoryFill(MemoryFill* curr) { usesMemory = true; } void visitHost(Host* curr) { if (curr->op == CurrentMemory || curr->op == GrowMemory) { usesMemory = true; @@ -156,15 +134,14 @@ struct FunctionTypeAnalyzer : public PostWalker<FunctionTypeAnalyzer> { } } - void visitCallIndirect(CallIndirect* curr) { - indirectCalls.push_back(curr); - } + void visitCallIndirect(CallIndirect* curr) { indirectCalls.push_back(curr); } }; struct RemoveUnusedModuleElements : public Pass { bool rootAllFunctions; - RemoveUnusedModuleElements(bool rootAllFunctions) : rootAllFunctions(rootAllFunctions) {} + RemoveUnusedModuleElements(bool rootAllFunctions) + : rootAllFunctions(rootAllFunctions) {} void run(PassRunner* runner, Module* module) override { optimizeGlobalsAndFunctions(module); @@ -223,21 +200,32 @@ struct RemoveUnusedModuleElements : public Pass { // Remove unreachable elements. { auto& v = module->functions; - v.erase(std::remove_if(v.begin(), v.end(), [&](const std::unique_ptr<Function>& curr) { - return analyzer.reachable.count(ModuleElement(ModuleElementKind::Function, curr->name)) == 0; - }), v.end()); + v.erase(std::remove_if(v.begin(), + v.end(), + [&](const std::unique_ptr<Function>& curr) { + return analyzer.reachable.count(ModuleElement( + ModuleElementKind::Function, + curr->name)) == 0; + }), + v.end()); } { auto& v = module->globals; - v.erase(std::remove_if(v.begin(), v.end(), [&](const std::unique_ptr<Global>& curr) { - return analyzer.reachable.count(ModuleElement(ModuleElementKind::Global, curr->name)) == 0; - }), v.end()); + v.erase(std::remove_if(v.begin(), + v.end(), + [&](const std::unique_ptr<Global>& curr) { + return analyzer.reachable.count( + ModuleElement(ModuleElementKind::Global, + curr->name)) == 0; + }), + v.end()); } module->updateMaps(); // Handle the memory and table if (!exportsMemory && !analyzer.usesMemory) { if (!importsMemory) { - // The memory is unobservable to the outside, we can remove the contents. + // The memory is unobservable to the outside, we can remove the + // contents. module->memory.segments.clear(); } if (module->memory.segments.empty()) { @@ -268,7 +256,8 @@ struct RemoveUnusedModuleElements : public Pass { std::unordered_map<std::string, FunctionType*> canonicals; std::unordered_set<FunctionType*> needed; auto canonicalize = [&](Name name) { - if (!name.is()) return name; + if (!name.is()) + return name; FunctionType* type = module->getFunctionType(name); auto sig = getSig(type); auto iter = canonicals.find(sig); @@ -291,9 +280,13 @@ struct RemoveUnusedModuleElements : public Pass { call->fullType = canonicalize(call->fullType); } // remove no-longer used types - module->functionTypes.erase(std::remove_if(module->functionTypes.begin(), module->functionTypes.end(), [&needed](std::unique_ptr<FunctionType>& type) { - return needed.count(type.get()) == 0; - }), module->functionTypes.end()); + module->functionTypes.erase( + std::remove_if(module->functionTypes.begin(), + module->functionTypes.end(), + [&needed](std::unique_ptr<FunctionType>& type) { + return needed.count(type.get()) == 0; + }), + module->functionTypes.end()); module->updateMaps(); } }; diff --git a/src/passes/RemoveUnusedNames.cpp b/src/passes/RemoveUnusedNames.cpp index e5aaf5509..86db53b0c 100644 --- a/src/passes/RemoveUnusedNames.cpp +++ b/src/passes/RemoveUnusedNames.cpp @@ -19,8 +19,8 @@ // merge names when possible (by merging their blocks) // -#include <wasm.h> #include <pass.h> +#include <wasm.h> namespace wasm { @@ -33,11 +33,9 @@ struct RemoveUnusedNames : public WalkerPass<PostWalker<RemoveUnusedNames>> { // a parent block, we know if it was branched to std::map<Name, std::set<Expression*>> branchesSeen; - void visitBreak(Break *curr) { - branchesSeen[curr->name].insert(curr); - } + void visitBreak(Break* curr) { branchesSeen[curr->name].insert(curr); } - void visitSwitch(Switch *curr) { + void visitSwitch(Switch* curr) { for (auto name : curr->targets) { branchesSeen[name].insert(curr); } @@ -54,20 +52,24 @@ struct RemoveUnusedNames : public WalkerPass<PostWalker<RemoveUnusedNames>> { } } - void visitBlock(Block *curr) { + void visitBlock(Block* curr) { if (curr->name.is() && curr->list.size() == 1) { auto* child = curr->list[0]->dynCast<Block>(); if (child && child->name.is() && child->type == curr->type) { - // we have just one child, this block, so breaking out of it goes to the same place as breaking out of us, we just need one name (and block) + // we have just one child, this block, so breaking out of it goes to the + // same place as breaking out of us, we just need one name (and block) auto& branches = branchesSeen[curr->name]; for (auto* branch : branches) { if (Break* br = branch->dynCast<Break>()) { - if (br->name == curr->name) br->name = child->name; + if (br->name == curr->name) + br->name = child->name; } else if (Switch* sw = branch->dynCast<Switch>()) { for (auto& target : sw->targets) { - if (target == curr->name) target = child->name; + if (target == curr->name) + target = child->name; } - if (sw->default_ == curr->name) sw->default_ = child->name; + if (sw->default_ == curr->name) + sw->default_ = child->name; } else { WASM_UNREACHABLE(); } @@ -79,20 +81,16 @@ struct RemoveUnusedNames : public WalkerPass<PostWalker<RemoveUnusedNames>> { handleBreakTarget(curr->name); } - void visitLoop(Loop *curr) { + void visitLoop(Loop* curr) { handleBreakTarget(curr->name); if (!curr->name.is()) { replaceCurrent(curr->body); } } - void visitFunction(Function *curr) { - assert(branchesSeen.empty()); - } + void visitFunction(Function* curr) { assert(branchesSeen.empty()); } }; -Pass *createRemoveUnusedNamesPass() { - return new RemoveUnusedNames(); -} +Pass* createRemoveUnusedNamesPass() { return new RemoveUnusedNames(); } } // namespace wasm diff --git a/src/passes/ReorderFunctions.cpp b/src/passes/ReorderFunctions.cpp index 5312ee90a..5cd70c20f 100644 --- a/src/passes/ReorderFunctions.cpp +++ b/src/passes/ReorderFunctions.cpp @@ -22,16 +22,15 @@ // This may incur a tradeoff, though, as while it reduces binary size, it may // increase gzip size. This might be because the new order has the functions in // a less beneficial position for compression, that is, mutually-compressible -// functions are no longer together (when they were before, in the original order, -// the has some natural tendency one way or the other). TODO: investigate +// functions are no longer together (when they were before, in the original +// order, the has some natural tendency one way or the other). TODO: investigate // similarity ordering here. // - #include <memory> -#include <wasm.h> #include <pass.h> +#include <wasm.h> namespace wasm { @@ -42,12 +41,11 @@ struct CallCountScanner : public WalkerPass<PostWalker<CallCountScanner>> { CallCountScanner(NameCountMap* counts) : counts(counts) {} - CallCountScanner* create() override { - return new CallCountScanner(counts); - } + CallCountScanner* create() override { return new CallCountScanner(counts); } void visitCall(Call* curr) { - assert(counts->count(curr->target) > 0); // can't add a new element in parallel + // can't add a new element in parallel + assert(counts->count(curr->target) > 0); (*counts)[curr->target]++; } @@ -58,7 +56,8 @@ private: struct ReorderFunctions : public Pass { void run(PassRunner* runner, Module* module) override { NameCountMap counts; - // fill in info, as we operate on it in parallel (each function to its own entry) + // fill in info, as we operate on it in parallel (each function to its own + // entry) for (auto& func : module->functions) { counts[func->name]; } @@ -82,19 +81,18 @@ struct ReorderFunctions : public Pass { } } // sort - std::sort(module->functions.begin(), module->functions.end(), [&counts]( - const std::unique_ptr<Function>& a, - const std::unique_ptr<Function>& b) -> bool { - if (counts[a->name] == counts[b->name]) { - return strcmp(a->name.str, b->name.str) > 0; - } - return counts[a->name] > counts[b->name]; - }); + std::sort(module->functions.begin(), + module->functions.end(), + [&counts](const std::unique_ptr<Function>& a, + const std::unique_ptr<Function>& b) -> bool { + if (counts[a->name] == counts[b->name]) { + return strcmp(a->name.str, b->name.str) > 0; + } + return counts[a->name] > counts[b->name]; + }); } }; -Pass *createReorderFunctionsPass() { - return new ReorderFunctions(); -} +Pass* createReorderFunctionsPass() { return new ReorderFunctions(); } } // namespace wasm diff --git a/src/passes/ReorderLocals.cpp b/src/passes/ReorderLocals.cpp index fe4f775e9..45796c0aa 100644 --- a/src/passes/ReorderLocals.cpp +++ b/src/passes/ReorderLocals.cpp @@ -24,8 +24,8 @@ #include <memory> -#include <wasm.h> #include <pass.h> +#include <wasm.h> namespace wasm { @@ -35,27 +35,32 @@ struct ReorderLocals : public WalkerPass<PostWalker<ReorderLocals>> { Pass* create() override { return new ReorderLocals; } std::map<Index, Index> counts; // local => times it is used - std::map<Index, Index> firstUses; // local => index in the list of which local is first seen + // local => index in the list of which local is first seen + std::map<Index, Index> firstUses; - void visitFunction(Function *curr) { + void visitFunction(Function* curr) { Index num = curr->getNumLocals(); std::vector<Index> newToOld; for (size_t i = 0; i < num; i++) { newToOld.push_back(i); } // sort, keeping params in front (where they will not be moved) - sort(newToOld.begin(), newToOld.end(), [this, curr](Index a, Index b) -> bool { - if (curr->isParam(a) && !curr->isParam(b)) return true; - if (curr->isParam(b) && !curr->isParam(a)) return false; - if (curr->isParam(b) && curr->isParam(a)) { - return a < b; - } - if (counts[a] == counts[b]) { - if (counts[a] == 0) return a < b; - return firstUses[a] < firstUses[b]; - } - return counts[a] > counts[b]; - }); + sort( + newToOld.begin(), newToOld.end(), [this, curr](Index a, Index b) -> bool { + if (curr->isParam(a) && !curr->isParam(b)) + return true; + if (curr->isParam(b) && !curr->isParam(a)) + return false; + if (curr->isParam(b) && curr->isParam(a)) { + return a < b; + } + if (counts[a] == counts[b]) { + if (counts[a] == 0) + return a < b; + return firstUses[a] < firstUses[b]; + } + return counts[a] > counts[b]; + }); // sorting left params in front, perhaps slightly reordered. verify and fix. for (size_t i = 0; i < curr->params.size(); i++) { assert(newToOld[i] < curr->params.size()); @@ -90,15 +95,16 @@ struct ReorderLocals : public WalkerPass<PostWalker<ReorderLocals>> { Function* func; std::vector<Index>& oldToNew; - ReIndexer(Function* func, std::vector<Index>& oldToNew) : func(func), oldToNew(oldToNew) {} + ReIndexer(Function* func, std::vector<Index>& oldToNew) + : func(func), oldToNew(oldToNew) {} - void visitGetLocal(GetLocal *curr) { + void visitGetLocal(GetLocal* curr) { if (func->isVar(curr->index)) { curr->index = oldToNew[curr->index]; } } - void visitSetLocal(SetLocal *curr) { + void visitSetLocal(SetLocal* curr) { if (func->isVar(curr->index)) { curr->index = oldToNew[curr->index]; } @@ -120,14 +126,14 @@ struct ReorderLocals : public WalkerPass<PostWalker<ReorderLocals>> { } } - void visitGetLocal(GetLocal *curr) { + void visitGetLocal(GetLocal* curr) { counts[curr->index]++; if (firstUses.count(curr->index) == 0) { firstUses[curr->index] = firstUses.size(); } } - void visitSetLocal(SetLocal *curr) { + void visitSetLocal(SetLocal* curr) { counts[curr->index]++; if (firstUses.count(curr->index) == 0) { firstUses[curr->index] = firstUses.size(); @@ -135,8 +141,6 @@ struct ReorderLocals : public WalkerPass<PostWalker<ReorderLocals>> { } }; -Pass *createReorderLocalsPass() { - return new ReorderLocals(); -} +Pass* createReorderLocalsPass() { return new ReorderLocals(); } } // namespace wasm diff --git a/src/passes/SSAify.cpp b/src/passes/SSAify.cpp index 1ed3b976f..2f0f9439c 100644 --- a/src/passes/SSAify.cpp +++ b/src/passes/SSAify.cpp @@ -50,13 +50,13 @@ #include <iterator> -#include "wasm.h" -#include "pass.h" -#include "wasm-builder.h" -#include "support/permutations.h" #include "ir/find_all.h" #include "ir/literal-utils.h" #include "ir/local-graph.h" +#include "pass.h" +#include "support/permutations.h" +#include "wasm-builder.h" +#include "wasm.h" namespace wasm { @@ -77,9 +77,11 @@ struct SSAify : public Pass { Module* module; Function* func; - std::vector<Expression*> functionPrepends; // things we add to the function prologue + // things we add to the function prologue + std::vector<Expression*> functionPrepends; - void runOnFunction(PassRunner* runner, Module* module_, Function* func_) override { + void + runOnFunction(PassRunner* runner, Module* module_, Function* func_) override { module = module_; func = func_; LocalGraph graph(func); @@ -87,7 +89,8 @@ struct SSAify : public Pass { graph.computeSSAIndexes(); // create new local indexes, one for each set createNewIndexes(graph); - // we now know the sets for each get, and can compute get indexes and handle phis + // we now know the sets for each get, and can compute get indexes and handle + // phis computeGetsAndPhis(graph); // add prepends to function addPrepends(); @@ -96,9 +99,9 @@ struct SSAify : public Pass { void createNewIndexes(LocalGraph& graph) { FindAll<SetLocal> sets(func->body); for (auto* set : sets.list) { - // Indexes already in SSA form do not need to be modified - there is already - // just one set for that index. Otherwise, use a new index, unless merges - // are disallowed. + // Indexes already in SSA form do not need to be modified - there is + // already just one set for that index. Otherwise, use a new index, unless + // merges are disallowed. if (!graph.isSSA(set->index) && (allowMerges || !hasMerges(set, graph))) { set->index = addLocal(func->getLocalType(set->index)); } @@ -132,12 +135,14 @@ struct SSAify : public Pass { // leave it, it's fine } else { // zero it out - (*graph.locations[get]) = LiteralUtils::makeZero(get->type, *module); + (*graph.locations[get]) = + LiteralUtils::makeZero(get->type, *module); } } continue; } - if (!allowMerges) continue; + if (!allowMerges) + continue; // more than 1 set, need a phi: a new local written to at each of the sets auto new_ = addLocal(get->type); auto old = get->index; @@ -148,10 +153,7 @@ struct SSAify : public Pass { if (set) { // a set exists, just add a tee of its value auto* value = set->value; - auto* tee = builder.makeTeeLocal( - new_, - value - ); + auto* tee = builder.makeTeeLocal(new_, value); set->value = tee; // the value may have been something we tracked the location // of. if so, update that, since we moved it into the tee @@ -165,9 +167,7 @@ struct SSAify : public Pass { // we add a set with the proper // param value at the beginning of the function auto* set = builder.makeSetLocal( - new_, - builder.makeGetLocal(old, func->getLocalType(old)) - ); + new_, builder.makeGetLocal(old, func->getLocalType(old))); functionPrepends.push_back(set); } else { // this is a zero init, so we don't need to do anything actually @@ -177,9 +177,7 @@ struct SSAify : public Pass { } } - Index addLocal(Type type) { - return Builder::addVar(func, type); - } + Index addLocal(Type type) { return Builder::addVar(func, type); } void addPrepends() { if (functionPrepends.size() > 0) { @@ -195,13 +193,8 @@ struct SSAify : public Pass { } }; -Pass* createSSAifyPass() { - return new SSAify(true); -} +Pass* createSSAifyPass() { return new SSAify(true); } -Pass* createSSAifyNoMergePass() { - return new SSAify(false); -} +Pass* createSSAifyNoMergePass() { return new SSAify(false); } } // namespace wasm - diff --git a/src/passes/SafeHeap.cpp b/src/passes/SafeHeap.cpp index 738a201ee..a293ee5e9 100644 --- a/src/passes/SafeHeap.cpp +++ b/src/passes/SafeHeap.cpp @@ -20,21 +20,21 @@ // top of sbrk()-addressible memory, and incorrect alignment notation. // -#include "wasm.h" -#include "pass.h" #include "asm_v_wasm.h" #include "asmjs/shared-constants.h" -#include "wasm-builder.h" #include "ir/bits.h" #include "ir/function-type-utils.h" #include "ir/import-utils.h" #include "ir/load-utils.h" +#include "pass.h" +#include "wasm-builder.h" +#include "wasm.h" namespace wasm { -const Name DYNAMICTOP_PTR_IMPORT("DYNAMICTOP_PTR"), - SEGFAULT_IMPORT("segfault"), - ALIGNFAULT_IMPORT("alignfault"); +const Name DYNAMICTOP_PTR_IMPORT("DYNAMICTOP_PTR"); +const Name SEGFAULT_IMPORT("segfault"); +const Name ALIGNFAULT_IMPORT("alignfault"); static Name getLoadName(Load* curr) { std::string ret = "SAFE_HEAP_LOAD_"; @@ -69,34 +69,30 @@ struct AccessInstrumenter : public WalkerPass<PostWalker<AccessInstrumenter>> { AccessInstrumenter* create() override { return new AccessInstrumenter; } void visitLoad(Load* curr) { - if (curr->type == unreachable) return; + if (curr->type == unreachable) + return; Builder builder(*getModule()); replaceCurrent( - builder.makeCall( - getLoadName(curr), - { - curr->ptr, - builder.makeConst(Literal(int32_t(curr->offset))), - }, - curr->type - ) - ); + builder.makeCall(getLoadName(curr), + { + curr->ptr, + builder.makeConst(Literal(int32_t(curr->offset))), + }, + curr->type)); } void visitStore(Store* curr) { - if (curr->type == unreachable) return; + if (curr->type == unreachable) + return; Builder builder(*getModule()); replaceCurrent( - builder.makeCall( - getStoreName(curr), - { - curr->ptr, - builder.makeConst(Literal(int32_t(curr->offset))), - curr->value, - }, - none - ) - ); + builder.makeCall(getStoreName(curr), + { + curr->ptr, + builder.makeConst(Literal(int32_t(curr->offset))), + curr->value, + }, + none)); } }; @@ -156,32 +152,35 @@ struct SafeHeap : public Pass { } } - bool isPossibleAtomicOperation(Index align, Index bytes, bool shared, Type type) { + bool + isPossibleAtomicOperation(Index align, Index bytes, bool shared, Type type) { return align == bytes && shared && isIntegerType(type); } void addGlobals(Module* module, FeatureSet features) { // load funcs Load load; - for (auto type : { i32, i64, f32, f64, v128 }) { - if (type == v128 && !features.hasSIMD()) continue; + for (auto type : {i32, i64, f32, f64, v128}) { + if (type == v128 && !features.hasSIMD()) + continue; load.type = type; - for (Index bytes : { 1, 2, 4, 8, 16 }) { + for (Index bytes : {1, 2, 4, 8, 16}) { load.bytes = bytes; - if (bytes > getTypeSize(type) || - (type == f32 && bytes != 4) || - (type == f64 && bytes != 8) || - (type == v128 && bytes != 16)) continue; - for (auto signed_ : { true, false }) { + if (bytes > getTypeSize(type) || (type == f32 && bytes != 4) || + (type == f64 && bytes != 8) || (type == v128 && bytes != 16)) + continue; + for (auto signed_ : {true, false}) { load.signed_ = signed_; - if (isFloatType(type) && signed_) continue; - for (Index align : { 1, 2, 4, 8, 16 }) { + if (isFloatType(type) && signed_) + continue; + for (Index align : {1, 2, 4, 8, 16}) { load.align = align; - if (align > bytes) continue; - for (auto isAtomic : { true, false }) { + if (align > bytes) + continue; + for (auto isAtomic : {true, false}) { load.isAtomic = isAtomic; - if (isAtomic && - !isPossibleAtomicOperation(align, bytes, module->memory.shared, type)) { + if (isAtomic && !isPossibleAtomicOperation( + align, bytes, module->memory.shared, type)) { continue; } addLoadFunc(load, module); @@ -192,23 +191,26 @@ struct SafeHeap : public Pass { } // store funcs Store store; - for (auto valueType : { i32, i64, f32, f64, v128 }) { - if (valueType == v128 && !features.hasSIMD()) continue; + for (auto valueType : {i32, i64, f32, f64, v128}) { + if (valueType == v128 && !features.hasSIMD()) + continue; store.valueType = valueType; store.type = none; - for (Index bytes : { 1, 2, 4, 8, 16 }) { + for (Index bytes : {1, 2, 4, 8, 16}) { store.bytes = bytes; if (bytes > getTypeSize(valueType) || (valueType == f32 && bytes != 4) || (valueType == f64 && bytes != 8) || - (valueType == v128 && bytes != 16)) continue; - for (Index align : { 1, 2, 4, 8, 16 }) { + (valueType == v128 && bytes != 16)) + continue; + for (Index align : {1, 2, 4, 8, 16}) { store.align = align; - if (align > bytes) continue; - for (auto isAtomic : { true, false }) { + if (align > bytes) + continue; + for (auto isAtomic : {true, false}) { store.isAtomic = isAtomic; - if (isAtomic && - !isPossibleAtomicOperation(align, bytes, module->memory.shared, valueType)) { + if (isAtomic && !isPossibleAtomicOperation( + align, bytes, module->memory.shared, valueType)) { continue; } addStoreFunc(store, module); @@ -221,34 +223,25 @@ struct SafeHeap : public Pass { // creates a function for a particular style of load void addLoadFunc(Load style, Module* module) { auto name = getLoadName(&style); - if (module->getFunctionOrNull(name)) return; + if (module->getFunctionOrNull(name)) + return; auto* func = new Function; func->name = name; func->params.push_back(i32); // pointer func->params.push_back(i32); // offset - func->vars.push_back(i32); // pointer + offset + func->vars.push_back(i32); // pointer + offset func->result = style.type; Builder builder(*module); auto* block = builder.makeBlock(); - block->list.push_back( - builder.makeSetLocal( - 2, - builder.makeBinary( - AddInt32, - builder.makeGetLocal(0, i32), - builder.makeGetLocal(1, i32) - ) - ) - ); + block->list.push_back(builder.makeSetLocal( + 2, + builder.makeBinary( + AddInt32, builder.makeGetLocal(0, i32), builder.makeGetLocal(1, i32)))); // check for reading past valid memory: if pointer + offset + bytes - block->list.push_back( - makeBoundsCheck(style.type, builder, 2, style.bytes) - ); + block->list.push_back(makeBoundsCheck(style.type, builder, 2, style.bytes)); // check proper alignment if (style.align > 1) { - block->list.push_back( - makeAlignCheck(style.align, builder, 2) - ); + block->list.push_back(makeAlignCheck(style.align, builder, 2)); } // do the load auto* load = module->allocator.alloc<Load>(); @@ -269,35 +262,27 @@ struct SafeHeap : public Pass { // creates a function for a particular type of store void addStoreFunc(Store style, Module* module) { auto name = getStoreName(&style); - if (module->getFunctionOrNull(name)) return; + if (module->getFunctionOrNull(name)) + return; auto* func = new Function; func->name = name; - func->params.push_back(i32); // pointer - func->params.push_back(i32); // offset + func->params.push_back(i32); // pointer + func->params.push_back(i32); // offset func->params.push_back(style.valueType); // value - func->vars.push_back(i32); // pointer + offset + func->vars.push_back(i32); // pointer + offset func->result = none; Builder builder(*module); auto* block = builder.makeBlock(); - block->list.push_back( - builder.makeSetLocal( - 3, - builder.makeBinary( - AddInt32, - builder.makeGetLocal(0, i32), - builder.makeGetLocal(1, i32) - ) - ) - ); + block->list.push_back(builder.makeSetLocal( + 3, + builder.makeBinary( + AddInt32, builder.makeGetLocal(0, i32), builder.makeGetLocal(1, i32)))); // check for reading past valid memory: if pointer + offset + bytes block->list.push_back( - makeBoundsCheck(style.valueType, builder, 3, style.bytes) - ); + makeBoundsCheck(style.valueType, builder, 3, style.bytes)); // check proper alignment if (style.align > 1) { - block->list.push_back( - makeAlignCheck(style.align, builder, 3) - ); + block->list.push_back(makeAlignCheck(style.align, builder, 3)); } // do the store auto* store = module->allocator.alloc<Store>(); @@ -312,45 +297,33 @@ struct SafeHeap : public Pass { Expression* makeAlignCheck(Address align, Builder& builder, Index local) { return builder.makeIf( - builder.makeBinary( - AndInt32, - builder.makeGetLocal(local, i32), - builder.makeConst(Literal(int32_t(align - 1))) - ), - builder.makeCall(alignfault, {}, none) - ); + builder.makeBinary(AndInt32, + builder.makeGetLocal(local, i32), + builder.makeConst(Literal(int32_t(align - 1)))), + builder.makeCall(alignfault, {}, none)); } - Expression* makeBoundsCheck(Type type, Builder& builder, Index local, Index bytes) { + Expression* + makeBoundsCheck(Type type, Builder& builder, Index local, Index bytes) { auto upperOp = options.lowMemoryUnused ? LtUInt32 : EqInt32; auto upperBound = options.lowMemoryUnused ? PassOptions::LowMemoryBound : 0; return builder.makeIf( builder.makeBinary( OrInt32, - builder.makeBinary( - upperOp, - builder.makeGetLocal(local, i32), - builder.makeConst(Literal(int32_t(upperBound))) - ), + builder.makeBinary(upperOp, + builder.makeGetLocal(local, i32), + builder.makeConst(Literal(int32_t(upperBound)))), builder.makeBinary( GtUInt32, - builder.makeBinary( - AddInt32, - builder.makeGetLocal(local, i32), - builder.makeConst(Literal(int32_t(bytes))) - ), - builder.makeLoad(4, false, 0, 4, - builder.makeGetGlobal(dynamicTopPtr, i32), i32 - ) - ) - ), - builder.makeCall(segfault, {}, none) - ); + builder.makeBinary(AddInt32, + builder.makeGetLocal(local, i32), + builder.makeConst(Literal(int32_t(bytes)))), + builder.makeLoad( + 4, false, 0, 4, builder.makeGetGlobal(dynamicTopPtr, i32), i32))), + builder.makeCall(segfault, {}, none)); } }; -Pass *createSafeHeapPass() { - return new SafeHeap(); -} +Pass* createSafeHeapPass() { return new SafeHeap(); } } // namespace wasm diff --git a/src/passes/SimplifyLocals.cpp b/src/passes/SimplifyLocals.cpp index 8007d92ff..f366c7085 100644 --- a/src/passes/SimplifyLocals.cpp +++ b/src/passes/SimplifyLocals.cpp @@ -46,33 +46,40 @@ // removing redundant locals. // -#include <wasm.h> -#include <wasm-builder.h> -#include <wasm-traversal.h> -#include <pass.h> +#include "ir/equivalent_sets.h" #include <ir/branch-utils.h> -#include <ir/local-utils.h> #include <ir/effects.h> -#include "ir/equivalent_sets.h" #include <ir/find_all.h> +#include <ir/local-utils.h> #include <ir/manipulation.h> +#include <pass.h> +#include <wasm-builder.h> +#include <wasm-traversal.h> +#include <wasm.h> namespace wasm { // Main class -template<bool allowTee = true, bool allowStructure = true, bool allowNesting = true> -struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<allowTee, allowStructure, allowNesting>>> { +template<bool allowTee = true, + bool allowStructure = true, + bool allowNesting = true> +struct SimplifyLocals + : public WalkerPass<LinearExecutionWalker< + SimplifyLocals<allowTee, allowStructure, allowNesting>>> { bool isFunctionParallel() override { return true; } - Pass* create() override { return new SimplifyLocals<allowTee, allowStructure, allowNesting>(); } + Pass* create() override { + return new SimplifyLocals<allowTee, allowStructure, allowNesting>(); + } // information for a local.set we can sink struct SinkableInfo { Expression** item; EffectAnalyzer effects; - SinkableInfo(Expression** item, PassOptions& passOptions) : item(item), effects(passOptions, *item) {} + SinkableInfo(Expression** item, PassOptions& passOptions) + : item(item), effects(passOptions, *item) {} }; // a list of sinkables in a linear execution trace @@ -112,7 +119,9 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a // local => # of local.gets for it GetLocalCounter getCounter; - static void doNoteNonLinear(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, Expression** currp) { + static void + doNoteNonLinear(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, + Expression** currp) { // Main processing. auto* curr = *currp; if (curr->is<Break>()) { @@ -121,12 +130,14 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a // value means the block already has a return value self->unoptimizableBlocks.insert(br->name); } else { - self->blockBreaks[br->name].push_back({ currp, std::move(self->sinkables) }); + self->blockBreaks[br->name].push_back( + {currp, std::move(self->sinkables)}); } } else if (curr->is<Block>()) { return; // handled in visitBlock } else if (curr->is<If>()) { - assert(!curr->cast<If>()->ifFalse); // if-elses are handled by doNoteIf* methods + assert(!curr->cast<If>() + ->ifFalse); // if-elses are handled by doNoteIf* methods } else if (curr->is<Switch>()) { auto* sw = curr->cast<Switch>(); auto targets = BranchUtils::getUniqueTargets(sw); @@ -138,13 +149,17 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a self->sinkables.clear(); } - static void doNoteIfCondition(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, Expression** currp) { + static void doNoteIfCondition( + SimplifyLocals<allowTee, allowStructure, allowNesting>* self, + Expression** currp) { // we processed the condition of this if-else, and now control flow branches // into either the true or the false sides self->sinkables.clear(); } - static void doNoteIfTrue(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, Expression** currp) { + static void + doNoteIfTrue(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, + Expression** currp) { auto* iff = (*currp)->dynCast<If>(); if (iff->ifFalse) { // We processed the ifTrue side of this if-else, save it on the stack. @@ -159,7 +174,9 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a } } - static void doNoteIfFalse(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, Expression** currp) { + static void + doNoteIfFalse(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, + Expression** currp) { // we processed the ifFalse side of this if-else, we can now try to // mere with the ifTrue side and optimize a return value, if possible auto* iff = (*currp)->cast<If>(); @@ -199,13 +216,16 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a } } - void visitGetLocal(GetLocal *curr) { + void visitGetLocal(GetLocal* curr) { auto found = sinkables.find(curr->index); if (found != sinkables.end()) { - auto* set = (*found->second.item)->template cast<SetLocal>(); // the set we may be sinking + auto* set = (*found->second.item) + ->template cast<SetLocal>(); // the set we may be sinking bool oneUse = firstCycle || getCounter.num[curr->index] == 1; - auto* get = set->value->template dynCast<GetLocal>(); // the set's value may be a get (i.e., the set is a copy) - // if nesting is not allowed, and this might cause nesting, check if the sink would cause such a thing + // the set's value may be a get (i.e., the set is a copy) + auto* get = set->value->template dynCast<GetLocal>(); + // if nesting is not allowed, and this might cause nesting, check if the + // sink would cause such a thing if (!allowNesting) { // a get is always ok to sink if (!get) { @@ -213,8 +233,8 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a assert(expressionStack[expressionStack.size() - 1] == curr); auto* parent = expressionStack[expressionStack.size() - 2]; bool parentIsSet = parent->template is<SetLocal>(); - // if the parent of this get is a set, we can sink into the set's value, - // it would not be nested. + // if the parent of this get is a set, we can sink into the set's + // value, it would not be nested. if (!parentIsSet) { return; } @@ -273,7 +293,9 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a // a sink would cause nesting ExpressionStack expressionStack; - static void visitPre(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, Expression** currp) { + static void + visitPre(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, + Expression** currp) { Expression* curr = *currp; EffectAnalyzer effects(self->getPassOptions()); @@ -286,14 +308,16 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a } } - static void visitPost(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, Expression** currp) { + static void + visitPost(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, + Expression** currp) { // perform main SetLocal processing here, since we may be the result of // replaceCurrent, i.e., the visitor was not called. auto* set = (*currp)->dynCast<SetLocal>(); if (set) { - // if we see a set that was already potentially-sinkable, then the previous - // store is dead, leave just the value + // if we see a set that was already potentially-sinkable, then the + // previous store is dead, leave just the value auto found = self->sinkables.find(set->index); if (found != self->sinkables.end()) { auto* previous = (*found->second.item)->template cast<SetLocal>(); @@ -315,7 +339,8 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a if (set && self->canSink(set)) { Index index = set->index; assert(self->sinkables.count(index) == 0); - self->sinkables.emplace(std::make_pair(index, SinkableInfo(currp, self->getPassOptions()))); + self->sinkables.emplace( + std::make_pair(index, SinkableInfo(currp, self->getPassOptions()))); } if (!allowNesting) { @@ -325,9 +350,12 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a bool canSink(SetLocal* set) { // we can never move a tee - if (set->isTee()) return false; - // if in the first cycle, or not allowing tees, then we cannot sink if >1 use as that would make a tee - if ((firstCycle || !allowTee) && getCounter.num[set->index] > 1) return false; + if (set->isTee()) + return false; + // if in the first cycle, or not allowing tees, then we cannot sink if >1 + // use as that would make a tee + if ((firstCycle || !allowTee) && getCounter.num[set->index] > 1) + return false; return true; } @@ -338,13 +366,16 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a void optimizeLoopReturn(Loop* loop) { // If there is a sinkable thing in an eligible loop, we can optimize // it in a trivial way to the outside of the loop. - if (loop->type != none) return; - if (sinkables.empty()) return; + if (loop->type != none) + return; + if (sinkables.empty()) + return; Index goodIndex = sinkables.begin()->first; // Ensure we have a place to write the return values for, if not, we // need another cycle. auto* block = loop->body->dynCast<Block>(); - if (!block || block->name.is() || block->list.size() == 0 || !block->list.back()->is<Nop>()) { + if (!block || block->name.is() || block->list.size() == 0 || + !block->list.back()->is<Nop>()) { loopsToEnlarge.push_back(loop); return; } @@ -371,8 +402,12 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a } auto breaks = std::move(blockBreaks[block->name]); blockBreaks.erase(block->name); - if (breaks.size() == 0) return; // block has no branches TODO we might optimize trivial stuff here too - assert(!(*breaks[0].brp)->template cast<Break>()->value); // block does not already have a return value (if one break has one, they all do) + if (breaks.size() == 0) + // block has no branches TODO we might optimize trivial stuff here too + return; + // block does not already have a return value (if one break has one, they + // all do) + assert(!(*breaks[0].brp)->template cast<Break>()->value); // look for a local.set that is present in them all bool found = false; Index sharedIndex = -1; @@ -391,7 +426,8 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a break; } } - if (!found) return; + if (!found) + return; // If one of our brs is a br_if, then we will give it a value. since // the value executes before the condition, it is dangerous if we are // moving code out of the condition, @@ -446,7 +482,8 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a blocksToEnlarge.push_back(block); return; } - // move block local.set's value to the end, in return position, and nop the set + // move block local.set's value to the end, in return position, and nop the + // set auto* blockSetLocalPointer = sinkables.at(sharedIndex).item; auto* value = (*blockSetLocalPointer)->template cast<SetLocal>()->value; block->list[block->list.size() - 1] = value; @@ -458,13 +495,16 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a auto* brp = breaks[j].brp; auto* br = (*brp)->template cast<Break>(); assert(!br->value); - // if the break is conditional, then we must set the value here - if the break is not reached, we must still have the new value in the local + // if the break is conditional, then we must set the value here - if the + // break is not reached, we must still have the new value in the local auto* set = (*breakSetLocalPointer)->template cast<SetLocal>(); if (br->condition) { br->value = set; set->setTee(true); - *breakSetLocalPointer = this->getModule()->allocator.template alloc<Nop>(); - // in addition, as this is a conditional br that now has a value, it now returns a value, so it must be dropped + *breakSetLocalPointer = + this->getModule()->allocator.template alloc<Nop>(); + // in addition, as this is a conditional br that now has a value, it now + // returns a value, so it must be dropped br->finalize(); *brp = Builder(*this->getModule()).makeDrop(br); } else { @@ -473,7 +513,8 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a } } // finally, create a local.set on the block itself - auto* newSetLocal = Builder(*this->getModule()).makeSetLocal(sharedIndex, block); + auto* newSetLocal = + Builder(*this->getModule()).makeSetLocal(sharedIndex, block); this->replaceCurrent(newSetLocal); sinkables.clear(); anotherCycle = true; @@ -484,7 +525,8 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a assert(iff->ifFalse); // if this if already has a result, or is unreachable code, we have // nothing to do - if (iff->type != none) return; + if (iff->type != none) + return; // We now have the sinkables from both sides of the if, and can look // for something to sink. That is either a shared index on both sides, // *or* if one side is unreachable, we can sink anything from the other, @@ -527,35 +569,42 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a } } } - if (!found) return; + if (!found) + return; // great, we can optimize! // ensure we have a place to write the return values for, if not, we // need another cycle - auto* ifTrueBlock = iff->ifTrue->dynCast<Block>(); + auto* ifTrueBlock = iff->ifTrue->dynCast<Block>(); if (iff->ifTrue->type != unreachable) { - if (!ifTrueBlock || ifTrueBlock->name.is() || ifTrueBlock->list.size() == 0 || !ifTrueBlock->list.back()->is<Nop>()) { + if (!ifTrueBlock || ifTrueBlock->name.is() || + ifTrueBlock->list.size() == 0 || + !ifTrueBlock->list.back()->is<Nop>()) { ifsToEnlarge.push_back(iff); return; } } auto* ifFalseBlock = iff->ifFalse->dynCast<Block>(); if (iff->ifFalse->type != unreachable) { - if (!ifFalseBlock || ifFalseBlock->name.is() || ifFalseBlock->list.size() == 0 || !ifFalseBlock->list.back()->is<Nop>()) { + if (!ifFalseBlock || ifFalseBlock->name.is() || + ifFalseBlock->list.size() == 0 || + !ifFalseBlock->list.back()->is<Nop>()) { ifsToEnlarge.push_back(iff); return; } } // all set, go if (iff->ifTrue->type != unreachable) { - auto *ifTrueItem = ifTrue.at(goodIndex).item; - ifTrueBlock->list[ifTrueBlock->list.size() - 1] = (*ifTrueItem)->template cast<SetLocal>()->value; + auto* ifTrueItem = ifTrue.at(goodIndex).item; + ifTrueBlock->list[ifTrueBlock->list.size() - 1] = + (*ifTrueItem)->template cast<SetLocal>()->value; ExpressionManipulator::nop(*ifTrueItem); ifTrueBlock->finalize(); assert(ifTrueBlock->type != none); } if (iff->ifFalse->type != unreachable) { - auto *ifFalseItem = ifFalse.at(goodIndex).item; - ifFalseBlock->list[ifFalseBlock->list.size() - 1] = (*ifFalseItem)->template cast<SetLocal>()->value; + auto* ifFalseItem = ifFalse.at(goodIndex).item; + ifFalseBlock->list[ifFalseBlock->list.size() - 1] = + (*ifFalseItem)->template cast<SetLocal>()->value; ExpressionManipulator::nop(*ifFalseItem); ifFalseBlock->finalize(); assert(ifFalseBlock->type != none); @@ -563,7 +612,8 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a iff->finalize(); // update type assert(iff->type != none); // finally, create a local.set on the iff itself - auto* newSetLocal = Builder(*this->getModule()).makeSetLocal(goodIndex, iff); + auto* newSetLocal = + Builder(*this->getModule()).makeSetLocal(goodIndex, iff); *currp = newSetLocal; anotherCycle = true; } @@ -592,14 +642,17 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a // arm into a one-sided if. void optimizeIfReturn(If* iff, Expression** currp) { // If this if is unreachable code, we have nothing to do. - if (iff->type != none || iff->ifTrue->type != none) return; + if (iff->type != none || iff->ifTrue->type != none) + return; // Anything sinkable is good for us. - if (sinkables.empty()) return; + if (sinkables.empty()) + return; Index goodIndex = sinkables.begin()->first; // Ensure we have a place to write the return values for, if not, we // need another cycle. auto* ifTrueBlock = iff->ifTrue->dynCast<Block>(); - if (!ifTrueBlock || ifTrueBlock->name.is() || ifTrueBlock->list.size() == 0 || !ifTrueBlock->list.back()->is<Nop>()) { + if (!ifTrueBlock || ifTrueBlock->name.is() || + ifTrueBlock->list.size() == 0 || !ifTrueBlock->list.back()->is<Nop>()) { ifsToEnlarge.push_back(iff); return; } @@ -625,7 +678,8 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a } // override scan to add a pre and a post check task to all nodes - static void scan(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, Expression** currp) { + static void scan(SimplifyLocals<allowTee, allowStructure, allowNesting>* self, + Expression** currp) { self->pushTask(visitPost, currp); auto* curr = *currp; @@ -633,15 +687,29 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a if (auto* iff = curr->dynCast<If>()) { // handle if in a special manner, using the ifStack for if-elses etc. if (iff->ifFalse) { - self->pushTask(SimplifyLocals<allowTee, allowStructure, allowNesting>::doNoteIfFalse, currp); - self->pushTask(SimplifyLocals<allowTee, allowStructure, allowNesting>::scan, &iff->ifFalse); + self->pushTask( + SimplifyLocals<allowTee, allowStructure, allowNesting>::doNoteIfFalse, + currp); + self->pushTask( + SimplifyLocals<allowTee, allowStructure, allowNesting>::scan, + &iff->ifFalse); } - self->pushTask(SimplifyLocals<allowTee, allowStructure, allowNesting>::doNoteIfTrue, currp); - self->pushTask(SimplifyLocals<allowTee, allowStructure, allowNesting>::scan, &iff->ifTrue); - self->pushTask(SimplifyLocals<allowTee, allowStructure, allowNesting>::doNoteIfCondition, currp); - self->pushTask(SimplifyLocals<allowTee, allowStructure, allowNesting>::scan, &iff->condition); + self->pushTask( + SimplifyLocals<allowTee, allowStructure, allowNesting>::doNoteIfTrue, + currp); + self->pushTask( + SimplifyLocals<allowTee, allowStructure, allowNesting>::scan, + &iff->ifTrue); + self->pushTask(SimplifyLocals<allowTee, allowStructure, allowNesting>:: + doNoteIfCondition, + currp); + self->pushTask( + SimplifyLocals<allowTee, allowStructure, allowNesting>::scan, + &iff->condition); } else { - WalkerPass<LinearExecutionWalker<SimplifyLocals<allowTee, allowStructure, allowNesting>>>::scan(self, currp); + WalkerPass<LinearExecutionWalker< + SimplifyLocals<allowTee, allowStructure, allowNesting>>>::scan(self, + currp); } self->pushTask(visitPre, currp); @@ -685,11 +753,14 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a bool runMainOptimizations(Function* func) { anotherCycle = false; - WalkerPass<LinearExecutionWalker<SimplifyLocals<allowTee, allowStructure, allowNesting>>>::doWalkFunction(func); + WalkerPass<LinearExecutionWalker< + SimplifyLocals<allowTee, allowStructure, allowNesting>>>:: + doWalkFunction(func); // enlarge blocks that were marked, for the next round if (blocksToEnlarge.size() > 0) { for (auto* block : blocksToEnlarge) { - block->list.push_back(this->getModule()->allocator.template alloc<Nop>()); + block->list.push_back( + this->getModule()->allocator.template alloc<Nop>()); } blocksToEnlarge.clear(); anotherCycle = true; @@ -697,16 +768,22 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a // enlarge ifs that were marked, for the next round if (ifsToEnlarge.size() > 0) { for (auto* iff : ifsToEnlarge) { - auto ifTrue = Builder(*this->getModule()).blockifyWithName(iff->ifTrue, Name()); + auto ifTrue = + Builder(*this->getModule()).blockifyWithName(iff->ifTrue, Name()); iff->ifTrue = ifTrue; - if (ifTrue->list.size() == 0 || !ifTrue->list.back()->template is<Nop>()) { - ifTrue->list.push_back(this->getModule()->allocator.template alloc<Nop>()); + if (ifTrue->list.size() == 0 || + !ifTrue->list.back()->template is<Nop>()) { + ifTrue->list.push_back( + this->getModule()->allocator.template alloc<Nop>()); } if (iff->ifFalse) { - auto ifFalse = Builder(*this->getModule()).blockifyWithName(iff->ifFalse, Name()); + auto ifFalse = + Builder(*this->getModule()).blockifyWithName(iff->ifFalse, Name()); iff->ifFalse = ifFalse; - if (ifFalse->list.size() == 0 || !ifFalse->list.back()->template is<Nop>()) { - ifFalse->list.push_back(this->getModule()->allocator.template alloc<Nop>()); + if (ifFalse->list.size() == 0 || + !ifFalse->list.back()->template is<Nop>()) { + ifFalse->list.push_back( + this->getModule()->allocator.template alloc<Nop>()); } } } @@ -716,10 +793,13 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a // enlarge loops that were marked, for the next round if (loopsToEnlarge.size() > 0) { for (auto* loop : loopsToEnlarge) { - auto block = Builder(*this->getModule()).blockifyWithName(loop->body, Name()); + auto block = + Builder(*this->getModule()).blockifyWithName(loop->body, Name()); loop->body = block; - if (block->list.size() == 0 || !block->list.back()->template is<Nop>()) { - block->list.push_back(this->getModule()->allocator.template alloc<Nop>()); + if (block->list.size() == 0 || + !block->list.back()->template is<Nop>()) { + block->list.push_back( + this->getModule()->allocator.template alloc<Nop>()); } } loopsToEnlarge.clear(); @@ -750,7 +830,8 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a // ) // ) // will inhibit us creating an if return value. - struct EquivalentOptimizer : public LinearExecutionWalker<EquivalentOptimizer> { + struct EquivalentOptimizer + : public LinearExecutionWalker<EquivalentOptimizer> { std::vector<Index>* numGetLocals; bool removeEquivalentSets; Module* module; @@ -760,13 +841,14 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a // We track locals containing the same value. EquivalentSets equivalences; - static void doNoteNonLinear(EquivalentOptimizer* self, Expression** currp) { - // TODO do this across non-linear paths too, in coalesce-locals perhaps? (would inhibit structure - // opts here, though. + static void doNoteNonLinear(EquivalentOptimizer* self, + Expression** currp) { + // TODO do this across non-linear paths too, in coalesce-locals perhaps? + // (would inhibit structure opts here, though. self->equivalences.clear(); } - void visitSetLocal(SetLocal *curr) { + void visitSetLocal(SetLocal* curr) { // Remove trivial copies, even through a tee auto* value = curr->value; while (auto* subSet = value->dynCast<SetLocal>()) { @@ -794,14 +876,14 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a } } - void visitGetLocal(GetLocal *curr) { + void visitGetLocal(GetLocal* curr) { // Canonicalize gets: if some are equivalent, then we can pick more // then one, and other passes may benefit from having more uniformity. if (auto* set = equivalences.getEquivalents(curr->index)) { // Pick the index with the most uses - maximizing the chance to // lower one's uses to zero. - // Helper method that returns the # of gets *ignoring the current get*, - // as we want to see what is best overall, treating this one as + // Helper method that returns the # of gets *ignoring the current + // get*, as we want to see what is best overall, treating this one as // to be decided upon. auto getNumGetsIgnoringCurr = [&](Index index) { auto ret = (*numGetLocals)[index]; @@ -821,8 +903,8 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a assert(best != Index(-1)); // Due to ordering, the best index may be different from us but have // the same # of locals - make sure we actually improve. - if (best != curr->index && - getNumGetsIgnoringCurr(best) > getNumGetsIgnoringCurr(curr->index)) { + if (best != curr->index && getNumGetsIgnoringCurr(best) > + getNumGetsIgnoringCurr(curr->index)) { // Update the get counts. (*numGetLocals)[best]++; assert((*numGetLocals)[curr->index] >= 1); @@ -850,23 +932,21 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals<a } }; -Pass *createSimplifyLocalsPass() { - return new SimplifyLocals<true, true>(); -} +Pass* createSimplifyLocalsPass() { return new SimplifyLocals<true, true>(); } -Pass *createSimplifyLocalsNoTeePass() { +Pass* createSimplifyLocalsNoTeePass() { return new SimplifyLocals<false, true>(); } -Pass *createSimplifyLocalsNoStructurePass() { +Pass* createSimplifyLocalsNoStructurePass() { return new SimplifyLocals<true, false>(); } -Pass *createSimplifyLocalsNoTeeNoStructurePass() { +Pass* createSimplifyLocalsNoTeeNoStructurePass() { return new SimplifyLocals<false, false>(); } -Pass *createSimplifyLocalsNoNestingPass() { +Pass* createSimplifyLocalsNoNestingPass() { return new SimplifyLocals<false, false, false>(); } diff --git a/src/passes/Souperify.cpp b/src/passes/Souperify.cpp index f6700a698..1cc3037fe 100644 --- a/src/passes/Souperify.cpp +++ b/src/passes/Souperify.cpp @@ -35,15 +35,15 @@ // directly, without the need for *-propagate techniques. // -#include "wasm.h" -#include "pass.h" -#include "wasm-builder.h" +#include "dataflow/graph.h" +#include "dataflow/node.h" +#include "dataflow/utils.h" #include "ir/flat.h" #include "ir/local-graph.h" #include "ir/utils.h" -#include "dataflow/node.h" -#include "dataflow/graph.h" -#include "dataflow/utils.h" +#include "pass.h" +#include "wasm-builder.h" +#include "wasm.h" namespace wasm { @@ -62,7 +62,8 @@ struct UseFinder { // (or rather, their values) that contain a get that uses that value. // There may also be non-set uses of the value, for example in a drop // or a return. We represent those with a nullptr, meaning "other". - std::vector<Expression*> getUses(Expression* origin, Graph& graph, LocalGraph& localGraph) { + std::vector<Expression*> + getUses(Expression* origin, Graph& graph, LocalGraph& localGraph) { if (debug() >= 2) { std::cout << "getUses\n" << origin << '\n'; } @@ -80,9 +81,13 @@ struct UseFinder { // There may be loops of sets with copies between them. std::unordered_set<SetLocal*> seenSets; - void addSetUses(SetLocal* set, Graph& graph, LocalGraph& localGraph, std::vector<Expression*>& ret) { + void addSetUses(SetLocal* set, + Graph& graph, + LocalGraph& localGraph, + std::vector<Expression*>& ret) { // If already handled, nothing to do here. - if (seenSets.count(set)) return; + if (seenSets.count(set)) + return; seenSets.insert(set); // Find all the uses of that set. auto& gets = localGraph.setInfluences[set]; @@ -165,7 +170,12 @@ struct Trace { // The local information graph. Used to check if a node has external uses. LocalGraph& localGraph; - Trace(Graph& graph, Node* toInfer, std::unordered_set<Node*>& excludeAsChildren, LocalGraph& localGraph) : graph(graph), toInfer(toInfer), excludeAsChildren(excludeAsChildren), localGraph(localGraph) { + Trace(Graph& graph, + Node* toInfer, + std::unordered_set<Node*>& excludeAsChildren, + LocalGraph& localGraph) + : graph(graph), toInfer(toInfer), excludeAsChildren(excludeAsChildren), + localGraph(localGraph) { if (debug() >= 2) { std::cout << "\nstart a trace (in " << graph.func->name << ")\n"; } @@ -180,7 +190,8 @@ struct Trace { } // Pull in all the dependencies, starting from the value itself. add(toInfer, 0); - if (bad) return; + if (bad) + return; // If we are trivial before adding pcs, we are still trivial, and // can ignore this. auto sizeBeforePathConditions = nodes.size(); @@ -238,7 +249,8 @@ struct Trace { // If we've gone too deep, emit a var instead. // Do the same if this is a node we should exclude from traces. if (depth >= depthLimit || nodes.size() >= totalLimit || - (node != toInfer && excludeAsChildren.find(node) != excludeAsChildren.end())) { + (node != toInfer && + excludeAsChildren.find(node) != excludeAsChildren.end())) { auto type = node->getWasmType(); assert(isConcreteType(type)); auto* var = Node::makeVar(type); @@ -293,7 +305,8 @@ struct Trace { bad = true; return nullptr; } - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } // Assert on no cycles assert(addedNodes.find(node) == addedNodes.end()); @@ -319,7 +332,9 @@ struct Trace { // curr is a child of parent, and parent has a Block which we are // give as 'node'. Add a path condition for reaching the child. - void addPathTo(Expression* parent, Expression* curr, std::vector<Node*> conditions) { + void addPathTo(Expression* parent, + Expression* curr, + std::vector<Node*> conditions) { if (auto* iff = parent->dynCast<If>()) { Index index; if (curr == iff->ifTrue) { @@ -340,9 +355,7 @@ struct Trace { } } - bool isBad() { - return bad; - } + bool isBad() { return bad; } static bool isTraceable(Node* node) { if (!node->origin) { @@ -372,7 +385,8 @@ struct Trace { } } for (auto& node : nodes) { - if (node == toInfer) continue; + if (node == toInfer) + continue; if (auto* origin = node->origin) { auto uses = UseFinder().getUses(origin, graph, localGraph); for (auto* use : uses) { @@ -407,7 +421,8 @@ struct Printer { std::cout << "\n; start LHS (in " << graph.func->name << ")\n"; // Index the nodes. for (auto* node : trace.nodes) { - if (!node->isCond()) { // pcs and blockpcs are not instructions and do not need to be indexed + // pcs and blockpcs are not instructions and do not need to be indexed + if (!node->isCond()) { auto index = indexing.size(); indexing[node] = index; } @@ -440,7 +455,8 @@ struct Printer { assert(node); switch (node->type) { case Node::Type::Var: { - std::cout << "%" << indexing[node] << ":" << printType(node->wasmType) << " = var"; + std::cout << "%" << indexing[node] << ":" << printType(node->wasmType) + << " = var"; break; // nothing more to add } case Node::Type::Expr: { @@ -464,18 +480,21 @@ struct Printer { break; } case Node::Type::Cond: { - std::cout << "blockpc %" << indexing[node->getValue(0)] << ' ' << node->index << ' '; + std::cout << "blockpc %" << indexing[node->getValue(0)] << ' ' + << node->index << ' '; printInternal(node->getValue(1)); std::cout << " 1:i1"; break; } case Node::Type::Block: { - std::cout << "%" << indexing[node] << " = block " << node->values.size(); + std::cout << "%" << indexing[node] << " = block " + << node->values.size(); break; } case Node::Type::Zext: { auto* child = node->getValue(0); - std::cout << "%" << indexing[node] << ':' << printType(child->getWasmType()); + std::cout << "%" << indexing[node] << ':' + << printType(child->getWasmType()); std::cout << " = zext "; printInternal(child); break; @@ -484,10 +503,12 @@ struct Printer { std::cout << "!!!BAD!!!"; WASM_UNREACHABLE(); } - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } if (node->isExpr() || node->isPhi()) { - if (node->origin != trace.toInfer->origin && trace.hasExternalUses.count(node) > 0) { + if (node->origin != trace.toInfer->origin && + trace.hasExternalUses.count(node) > 0) { std::cout << " (hasExternalUses)"; printedHasExternalUses = true; } @@ -523,12 +544,19 @@ struct Printer { } else if (auto* unary = curr->dynCast<Unary>()) { switch (unary->op) { case ClzInt32: - case ClzInt64: std::cout << "ctlz"; break; + case ClzInt64: + std::cout << "ctlz"; + break; case CtzInt32: - case CtzInt64: std::cout << "cttz"; break; + case CtzInt64: + std::cout << "cttz"; + break; case PopcntInt32: - case PopcntInt64: std::cout << "ctpop"; break; - default: WASM_UNREACHABLE(); + case PopcntInt64: + std::cout << "ctpop"; + break; + default: + WASM_UNREACHABLE(); } std::cout << ' '; auto* value = node->getValue(0); @@ -536,48 +564,91 @@ struct Printer { } else if (auto* binary = curr->dynCast<Binary>()) { switch (binary->op) { case AddInt32: - case AddInt64: std::cout << "add"; break; + case AddInt64: + std::cout << "add"; + break; case SubInt32: - case SubInt64: std::cout << "sub"; break; + case SubInt64: + std::cout << "sub"; + break; case MulInt32: - case MulInt64: std::cout << "mul"; break; + case MulInt64: + std::cout << "mul"; + break; case DivSInt32: - case DivSInt64: std::cout << "sdiv"; break; + case DivSInt64: + std::cout << "sdiv"; + break; case DivUInt32: - case DivUInt64: std::cout << "udiv"; break; + case DivUInt64: + std::cout << "udiv"; + break; case RemSInt32: - case RemSInt64: std::cout << "srem"; break; + case RemSInt64: + std::cout << "srem"; + break; case RemUInt32: - case RemUInt64: std::cout << "urem"; break; + case RemUInt64: + std::cout << "urem"; + break; case AndInt32: - case AndInt64: std::cout << "and"; break; + case AndInt64: + std::cout << "and"; + break; case OrInt32: - case OrInt64: std::cout << "or"; break; + case OrInt64: + std::cout << "or"; + break; case XorInt32: - case XorInt64: std::cout << "xor"; break; + case XorInt64: + std::cout << "xor"; + break; case ShlInt32: - case ShlInt64: std::cout << "shl"; break; + case ShlInt64: + std::cout << "shl"; + break; case ShrUInt32: - case ShrUInt64: std::cout << "lshr"; break; + case ShrUInt64: + std::cout << "lshr"; + break; case ShrSInt32: - case ShrSInt64: std::cout << "ashr"; break; + case ShrSInt64: + std::cout << "ashr"; + break; case RotLInt32: - case RotLInt64: std::cout << "rotl"; break; + case RotLInt64: + std::cout << "rotl"; + break; case RotRInt32: - case RotRInt64: std::cout << "rotr"; break; + case RotRInt64: + std::cout << "rotr"; + break; case EqInt32: - case EqInt64: std::cout << "eq"; break; + case EqInt64: + std::cout << "eq"; + break; case NeInt32: - case NeInt64: std::cout << "ne"; break; + case NeInt64: + std::cout << "ne"; + break; case LtSInt32: - case LtSInt64: std::cout << "slt"; break; + case LtSInt64: + std::cout << "slt"; + break; case LtUInt32: - case LtUInt64: std::cout << "ult"; break; + case LtUInt64: + std::cout << "ult"; + break; case LeSInt32: - case LeSInt64: std::cout << "sle"; break; + case LeSInt64: + std::cout << "sle"; + break; case LeUInt32: - case LeUInt64: std::cout << "ule"; break; - default: WASM_UNREACHABLE(); + case LeUInt64: + std::cout << "ule"; + break; + default: + WASM_UNREACHABLE(); } std::cout << ' '; auto* left = node->getValue(0); @@ -616,11 +687,13 @@ struct Printer { } } if (allInputsIdentical(node)) { - std::cout << "^^ suspicious identical inputs! missing optimization in " << graph.func->name << "? ^^\n"; + std::cout << "^^ suspicious identical inputs! missing optimization in " + << graph.func->name << "? ^^\n"; return; } if (!node->isPhi() && allInputsConstant(node)) { - std::cout << "^^ suspicious constant inputs! missing optimization in " << graph.func->name << "? ^^\n"; + std::cout << "^^ suspicious constant inputs! missing optimization in " + << graph.func->name << "? ^^\n"; return; } } @@ -642,7 +715,8 @@ struct Souperify : public WalkerPass<PostWalker<Souperify>> { // Build the data-flow IR. DataFlow::Graph graph; graph.build(func, getModule()); - if (debug() >= 2) dump(graph, std::cout); + if (debug() >= 2) + dump(graph, std::cout); // Build the local graph data structure. LocalGraph localGraph(func); localGraph.computeInfluences(); @@ -653,7 +727,8 @@ struct Souperify : public WalkerPass<PostWalker<Souperify>> { auto* node = nodePtr.get(); if (node->origin) { // TODO: work for identical origins could be saved - auto uses = DataFlow::UseFinder().getUses(node->origin, graph, localGraph); + auto uses = + DataFlow::UseFinder().getUses(node->origin, graph, localGraph); if (debug() >= 2) { std::cout << "following node has " << uses.size() << " uses\n"; dump(node, std::cout); @@ -681,12 +756,8 @@ struct Souperify : public WalkerPass<PostWalker<Souperify>> { } }; -Pass *createSouperifyPass() { - return new Souperify(false); -} +Pass* createSouperifyPass() { return new Souperify(false); } -Pass *createSouperifySingleUsePass() { - return new Souperify(true); -} +Pass* createSouperifySingleUsePass() { return new Souperify(true); } } // namespace wasm diff --git a/src/passes/SpillPointers.cpp b/src/passes/SpillPointers.cpp index 36c2ae948..75fa72652 100644 --- a/src/passes/SpillPointers.cpp +++ b/src/passes/SpillPointers.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ - // // Spills values that might be pointers to the C stack. This allows // Boehm-style GC to see them properly. @@ -26,16 +25,17 @@ // * There is currently no check that there is enough stack space. // -#include "wasm.h" -#include "pass.h" -#include "cfg/liveness-traversal.h" -#include "wasm-builder.h" #include "abi/abi.h" #include "abi/stack.h" +#include "cfg/liveness-traversal.h" +#include "pass.h" +#include "wasm-builder.h" +#include "wasm.h" namespace wasm { -struct SpillPointers : public WalkerPass<LivenessWalker<SpillPointers, Visitor<SpillPointers>>> { +struct SpillPointers + : public WalkerPass<LivenessWalker<SpillPointers, Visitor<SpillPointers>>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new SpillPointers; } @@ -48,21 +48,18 @@ struct SpillPointers : public WalkerPass<LivenessWalker<SpillPointers, Visitor<S std::unordered_map<Expression**, Expression**> actualPointers; // note calls in basic blocks - template<typename T> - void visitSpillable(T* curr) { - // if in unreachable code, ignore - if (!currBasicBlock) return; + template<typename T> void visitSpillable(T* curr) { + // if in unreachable code, ignore + if (!currBasicBlock) + return; auto* pointer = getCurrentPointer(); currBasicBlock->contents.actions.emplace_back(pointer); - actualPointers[pointer] = pointer; // starts out as correct, may change later + // starts out as correct, may change later + actualPointers[pointer] = pointer; } - void visitCall(Call* curr) { - visitSpillable(curr); - } - void visitCallIndirect(CallIndirect* curr) { - visitSpillable(curr); - } + void visitCall(Call* curr) { visitSpillable(curr); } + void visitCallIndirect(CallIndirect* curr) { visitSpillable(curr); } // main entry point @@ -73,7 +70,7 @@ struct SpillPointers : public WalkerPass<LivenessWalker<SpillPointers, Visitor<S // map pointers to their offset in the spill area typedef std::unordered_map<Index, Index> PointerMap; - + void spillPointers() { // we only care about possible pointers auto* func = getFunction(); @@ -88,7 +85,8 @@ struct SpillPointers : public WalkerPass<LivenessWalker<SpillPointers, Visitor<S bool spilled = false; Index spillLocal = -1; for (auto& curr : basicBlocks) { - if (liveBlocks.count(curr.get()) == 0) continue; // ignore dead blocks + if (liveBlocks.count(curr.get()) == 0) + continue; // ignore dead blocks auto& liveness = curr->contents; auto& actions = liveness.actions; Index lastCall = -1; @@ -98,7 +96,8 @@ struct SpillPointers : public WalkerPass<LivenessWalker<SpillPointers, Visitor<S lastCall = i; } } - if (lastCall == Index(-1)) continue; // nothing to see here + if (lastCall == Index(-1)) + continue; // nothing to see here // scan through the block, spilling around the calls // TODO: we can filter on pointerMap everywhere LocalSet live = liveness.end; @@ -119,12 +118,15 @@ struct SpillPointers : public WalkerPass<LivenessWalker<SpillPointers, Visitor<S // we now have a call + the information about which locals // should be spilled if (!spilled) { - // prepare stack support: get a pointer to stack space big enough for all our data + // prepare stack support: get a pointer to stack space big enough + // for all our data spillLocal = Builder::addVar(func, ABI::PointerType); spilled = true; } - auto* pointer = actualPointers[action.origin]; // the origin was seen at walk, but the thing may have moved - spillPointersAroundCall(pointer, toSpill, spillLocal, pointerMap, func, getModule()); + // the origin was seen at walk, but the thing may have moved + auto* pointer = actualPointers[action.origin]; + spillPointersAroundCall( + pointer, toSpill, spillLocal, pointerMap, func, getModule()); } } else { WASM_UNREACHABLE(); @@ -133,13 +135,22 @@ struct SpillPointers : public WalkerPass<LivenessWalker<SpillPointers, Visitor<S } if (spilled) { // get the stack space, and set the local to it - ABI::getStackSpace(spillLocal, func, getTypeSize(ABI::PointerType) * pointerMap.size(), *getModule()); + ABI::getStackSpace(spillLocal, + func, + getTypeSize(ABI::PointerType) * pointerMap.size(), + *getModule()); } } - void spillPointersAroundCall(Expression** origin, std::vector<Index>& toSpill, Index spillLocal, PointerMap& pointerMap, Function* func, Module* module) { + void spillPointersAroundCall(Expression** origin, + std::vector<Index>& toSpill, + Index spillLocal, + PointerMap& pointerMap, + Function* func, + Module* module) { auto* call = *origin; - if (call->type == unreachable) return; // the call is never reached anyhow, ignore + if (call->type == unreachable) + return; // the call is never reached anyhow, ignore Builder builder(*module); auto* block = builder.makeBlock(); // move the operands into locals, as we must spill after they are executed @@ -168,14 +179,13 @@ struct SpillPointers : public WalkerPass<LivenessWalker<SpillPointers, Visitor<S } // add the spills for (auto index : toSpill) { - block->list.push_back(builder.makeStore( - getTypeSize(ABI::PointerType), - pointerMap[index], - getTypeSize(ABI::PointerType), - builder.makeGetLocal(spillLocal, ABI::PointerType), - builder.makeGetLocal(index, ABI::PointerType), - ABI::PointerType - )); + block->list.push_back( + builder.makeStore(getTypeSize(ABI::PointerType), + pointerMap[index], + getTypeSize(ABI::PointerType), + builder.makeGetLocal(spillLocal, ABI::PointerType), + builder.makeGetLocal(index, ABI::PointerType), + ABI::PointerType)); } // add the (modified) call block->list.push_back(call); @@ -184,8 +194,6 @@ struct SpillPointers : public WalkerPass<LivenessWalker<SpillPointers, Visitor<S } }; -Pass *createSpillPointersPass() { - return new SpillPointers(); -} +Pass* createSpillPointersPass() { return new SpillPointers(); } } // namespace wasm diff --git a/src/passes/StackIR.cpp b/src/passes/StackIR.cpp index a8d66ae42..2506eca27 100644 --- a/src/passes/StackIR.cpp +++ b/src/passes/StackIR.cpp @@ -18,11 +18,11 @@ // Operations on Stack IR. // -#include "wasm.h" -#include "pass.h" -#include "wasm-stack.h" #include "ir/iteration.h" #include "ir/local-graph.h" +#include "pass.h" +#include "wasm-stack.h" +#include "wasm.h" namespace wasm { @@ -43,23 +43,16 @@ struct GenerateStackIR : public WalkerPass<PostWalker<GenerateStackIR>> { Module* module; Parent(Module* module) : module(module) {} - Module* getModule() { - return module; - } + Module* getModule() { return module; } void writeDebugLocation(Expression* curr, Function* func) { WASM_UNREACHABLE(); } - Index getFunctionIndex(Name name) { - WASM_UNREACHABLE(); - } - Index getFunctionTypeIndex(Name name) { - WASM_UNREACHABLE(); - } - Index getGlobalIndex(Name name) { - WASM_UNREACHABLE(); - } + Index getFunctionIndex(Name name) { WASM_UNREACHABLE(); } + Index getFunctionTypeIndex(Name name) { WASM_UNREACHABLE(); } + Index getGlobalIndex(Name name) { WASM_UNREACHABLE(); } } parent(getModule()); - StackWriter<StackWriterMode::Binaryen2Stack, Parent> stackWriter(parent, buffer, false); + StackWriter<StackWriterMode::Binaryen2Stack, Parent> stackWriter( + parent, buffer, false); stackWriter.setFunction(func); stackWriter.visitPossibleBlockContents(func->body); func->stackIR = make_unique<StackIR>(); @@ -67,9 +60,7 @@ struct GenerateStackIR : public WalkerPass<PostWalker<GenerateStackIR>> { } }; -Pass* createGenerateStackIRPass() { - return new GenerateStackIR(); -} +Pass* createGenerateStackIRPass() { return new GenerateStackIR(); } // Optimize @@ -79,8 +70,8 @@ class StackIROptimizer { StackIR& insts; public: - StackIROptimizer(Function* func, PassOptions& passOptions) : - func(func), passOptions(passOptions), insts(*func->stackIR.get()) { + StackIROptimizer(Function* func, PassOptions& passOptions) + : func(func), passOptions(passOptions), insts(*func->stackIR.get()) { assert(func->stackIR); } @@ -103,7 +94,8 @@ private: bool inUnreachableCode = false; for (Index i = 0; i < insts.size(); i++) { auto* inst = insts[i]; - if (!inst) continue; + if (!inst) + continue; if (inUnreachableCode) { // Does the unreachable code end here? if (isControlFlowBarrier(inst)) { @@ -151,12 +143,15 @@ private: #endif for (Index i = 0; i < insts.size(); i++) { auto* inst = insts[i]; - if (!inst) continue; + if (!inst) + continue; // First, consume values from the stack as required. auto consumed = getNumConsumedValues(inst); #ifdef STACK_OPT_DEBUG - std::cout << " " << i << " : " << *inst << ", " << values.size() << " on stack, will consume " << consumed << "\n "; - for (auto s : values) std::cout << s << ' '; + std::cout << " " << i << " : " << *inst << ", " << values.size() + << " on stack, will consume " << consumed << "\n "; + for (auto s : values) + std::cout << s << ' '; std::cout << '\n'; #endif // TODO: currently we run dce before this, but if we didn't, we'd need @@ -199,7 +194,8 @@ private: while (1) { // If there's an actual value in the way, we've failed. auto index = values[j]; - if (index == null) break; + if (index == null) + break; auto* set = insts[index]->origin->cast<SetLocal>(); if (set->index == get->index) { // This might be a proper set-get pair, where the set is @@ -228,7 +224,8 @@ private: } } // We failed here. Can we look some more? - if (j == 0) break; + if (j == 0) + break; j--; } } @@ -250,7 +247,8 @@ private: // a branch to that if body void removeUnneededBlocks() { for (auto*& inst : insts) { - if (!inst) continue; + if (!inst) + continue; if (auto* block = inst->origin->dynCast<Block>()) { if (!BranchUtils::BranchSeeker::hasNamed(block, block->name)) { // TODO optimize, maybe run remove-unused-names @@ -272,9 +270,7 @@ private: case StackInst::LoopEnd: { return true; } - default: { - return false; - } + default: { return false; } } } @@ -286,9 +282,7 @@ private: case StackInst::LoopBegin: { return true; } - default: { - return false; - } + default: { return false; } } } @@ -300,15 +294,11 @@ private: case StackInst::LoopEnd: { return true; } - default: { - return false; - } + default: { return false; } } } - bool isControlFlow(StackInst* inst) { - return inst->op != StackInst::Basic; - } + bool isControlFlow(StackInst* inst) { return inst->op != StackInst::Basic; } // Remove the instruction at index i. If the instruction // is control flow, and so has been expanded to multiple @@ -359,9 +349,6 @@ struct OptimizeStackIR : public WalkerPass<PostWalker<OptimizeStackIR>> { } }; -Pass* createOptimizeStackIRPass() { - return new OptimizeStackIR(); -} +Pass* createOptimizeStackIRPass() { return new OptimizeStackIR(); } } // namespace wasm - diff --git a/src/passes/Strip.cpp b/src/passes/Strip.cpp index edc171ab3..e85379003 100644 --- a/src/passes/Strip.cpp +++ b/src/passes/Strip.cpp @@ -21,9 +21,9 @@ #include <functional> -#include "wasm.h" -#include "wasm-binary.h" #include "pass.h" +#include "wasm-binary.h" +#include "wasm.h" using namespace std; @@ -31,7 +31,7 @@ namespace wasm { struct Strip : public Pass { // A function that returns true if the method should be removed. - typedef std::function<bool (UserSection&)> Decider; + typedef std::function<bool(UserSection&)> Decider; Decider decider; Strip(Decider decider) : decider(decider) {} @@ -39,14 +39,8 @@ struct Strip : public Pass { void run(PassRunner* runner, Module* module) override { // Remove name and debug sections. auto& sections = module->userSections; - sections.erase( - std::remove_if( - sections.begin(), - sections.end(), - decider - ), - sections.end() - ); + sections.erase(std::remove_if(sections.begin(), sections.end(), decider), + sections.end()); // If we're cleaning up debug info, clear on the function and module too. UserSection temp; temp.name = BinaryConsts::UserSections::Name; @@ -60,16 +54,15 @@ struct Strip : public Pass { } }; -Pass *createStripDebugPass() { +Pass* createStripDebugPass() { return new Strip([&](const UserSection& curr) { return curr.name == BinaryConsts::UserSections::Name || curr.name == BinaryConsts::UserSections::SourceMapUrl || - curr.name.find(".debug") == 0 || - curr.name.find("reloc..debug") == 0; + curr.name.find(".debug") == 0 || curr.name.find("reloc..debug") == 0; }); } -Pass *createStripProducersPass() { +Pass* createStripProducersPass() { return new Strip([&](const UserSection& curr) { return curr.name == BinaryConsts::UserSections::Producers; }); diff --git a/src/passes/StripTargetFeatures.cpp b/src/passes/StripTargetFeatures.cpp index 8eb7b0b75..542b4a6c1 100644 --- a/src/passes/StripTargetFeatures.cpp +++ b/src/passes/StripTargetFeatures.cpp @@ -24,8 +24,6 @@ struct StripTargetFeatures : public Pass { } }; -Pass *createStripTargetFeaturesPass() { - return new StripTargetFeatures(); -} +Pass* createStripTargetFeaturesPass() { return new StripTargetFeatures(); } } // namespace wasm diff --git a/src/passes/TrapMode.cpp b/src/passes/TrapMode.cpp index e6327479c..b36427138 100644 --- a/src/passes/TrapMode.cpp +++ b/src/passes/TrapMode.cpp @@ -26,66 +26,84 @@ #include "ir/trapping.h" #include "mixed_arena.h" #include "pass.h" -#include "wasm.h" +#include "support/name.h" #include "wasm-builder.h" #include "wasm-printing.h" #include "wasm-type.h" -#include "support/name.h" +#include "wasm.h" namespace wasm { -Name I64S_REM("i64s-rem"), - I64U_REM("i64u-rem"), - I64S_DIV("i64s-div"), - I64U_DIV("i64u-div"); +Name I64S_REM("i64s-rem"); +Name I64U_REM("i64u-rem"); +Name I64S_DIV("i64s-div"); +Name I64U_DIV("i64u-div"); Name getBinaryFuncName(Binary* curr) { switch (curr->op) { - case RemSInt32: return I32S_REM; - case RemUInt32: return I32U_REM; - case DivSInt32: return I32S_DIV; - case DivUInt32: return I32U_DIV; - case RemSInt64: return I64S_REM; - case RemUInt64: return I64U_REM; - case DivSInt64: return I64S_DIV; - case DivUInt64: return I64U_DIV; - default: return Name(); + case RemSInt32: + return I32S_REM; + case RemUInt32: + return I32U_REM; + case DivSInt32: + return I32S_DIV; + case DivUInt32: + return I32U_DIV; + case RemSInt64: + return I64S_REM; + case RemUInt64: + return I64U_REM; + case DivSInt64: + return I64S_DIV; + case DivUInt64: + return I64U_DIV; + default: + return Name(); } } Name getUnaryFuncName(Unary* curr) { switch (curr->op) { - case TruncSFloat32ToInt32: return F32_TO_INT; - case TruncUFloat32ToInt32: return F32_TO_UINT; - case TruncSFloat32ToInt64: return F32_TO_INT64; - case TruncUFloat32ToInt64: return F32_TO_UINT64; - case TruncSFloat64ToInt32: return F64_TO_INT; - case TruncUFloat64ToInt32: return F64_TO_UINT; - case TruncSFloat64ToInt64: return F64_TO_INT64; - case TruncUFloat64ToInt64: return F64_TO_UINT64; - default: return Name(); + case TruncSFloat32ToInt32: + return F32_TO_INT; + case TruncUFloat32ToInt32: + return F32_TO_UINT; + case TruncSFloat32ToInt64: + return F32_TO_INT64; + case TruncUFloat32ToInt64: + return F32_TO_UINT64; + case TruncSFloat64ToInt32: + return F64_TO_INT; + case TruncUFloat64ToInt32: + return F64_TO_UINT; + case TruncSFloat64ToInt64: + return F64_TO_INT64; + case TruncUFloat64ToInt64: + return F64_TO_UINT64; + default: + return Name(); } } bool isTruncOpSigned(UnaryOp op) { switch (op) { - case TruncUFloat32ToInt32: - case TruncUFloat32ToInt64: - case TruncUFloat64ToInt32: - case TruncUFloat64ToInt64: return false; - default: return true; + case TruncUFloat32ToInt32: + case TruncUFloat32ToInt64: + case TruncUFloat64ToInt32: + case TruncUFloat64ToInt64: + return false; + default: + return true; } } -Function* generateBinaryFunc(Module& wasm, Binary *curr) { +Function* generateBinaryFunc(Module& wasm, Binary* curr) { BinaryOp op = curr->op; Type type = curr->type; bool isI64 = type == i64; Builder builder(wasm); - Expression* result = builder.makeBinary(op, - builder.makeGetLocal(0, type), - builder.makeGetLocal(1, type) - ); + Expression* result = builder.makeBinary( + op, builder.makeGetLocal(0, type), builder.makeGetLocal(1, type)); BinaryOp divSIntOp = isI64 ? DivSInt64 : DivSInt32; UnaryOp eqZOp = isI64 ? EqZInt64 : EqZInt32; Literal minLit = isI64 ? Literal(std::numeric_limits<int64_t>::min()) @@ -96,32 +114,24 @@ Function* generateBinaryFunc(Module& wasm, Binary *curr) { BinaryOp eqOp = isI64 ? EqInt64 : EqInt32; Literal negLit = isI64 ? Literal(int64_t(-1)) : Literal(int32_t(-1)); result = builder.makeIf( - builder.makeBinary(AndInt32, - builder.makeBinary(eqOp, - builder.makeGetLocal(0, type), - builder.makeConst(minLit) - ), - builder.makeBinary(eqOp, - builder.makeGetLocal(1, type), - builder.makeConst(negLit) - ) - ), + builder.makeBinary( + AndInt32, + builder.makeBinary( + eqOp, builder.makeGetLocal(0, type), builder.makeConst(minLit)), + builder.makeBinary( + eqOp, builder.makeGetLocal(1, type), builder.makeConst(negLit))), builder.makeConst(zeroLit), - result - ); + result); } auto func = new Function; func->name = getBinaryFuncName(curr); func->params.push_back(type); func->params.push_back(type); func->result = type; - func->body = builder.makeIf( - builder.makeUnary(eqZOp, - builder.makeGetLocal(1, type) - ), - builder.makeConst(zeroLit), - result - ); + func->body = + builder.makeIf(builder.makeUnary(eqZOp, builder.makeGetLocal(1, type)), + builder.makeConst(zeroLit), + result); return func; } @@ -134,7 +144,7 @@ void makeClampLimitLiterals(Literal& iMin, Literal& fMin, Literal& fMax) { fMax = Literal(FloatType(maxVal) + 1); } -Function* generateUnaryFunc(Module& wasm, Unary *curr) { +Function* generateUnaryFunc(Module& wasm, Unary* curr) { Type type = curr->value->type; Type retType = curr->type; UnaryOp truncOp = curr->op; @@ -148,59 +158,66 @@ Function* generateUnaryFunc(Module& wasm, Unary *curr) { Literal iMin, fMin, fMax; switch (truncOp) { - case TruncSFloat32ToInt32: makeClampLimitLiterals< int32_t, float>(iMin, fMin, fMax); break; - case TruncUFloat32ToInt32: makeClampLimitLiterals<uint32_t, float>(iMin, fMin, fMax); break; - case TruncSFloat32ToInt64: makeClampLimitLiterals< int64_t, float>(iMin, fMin, fMax); break; - case TruncUFloat32ToInt64: makeClampLimitLiterals<uint64_t, float>(iMin, fMin, fMax); break; - case TruncSFloat64ToInt32: makeClampLimitLiterals< int32_t, double>(iMin, fMin, fMax); break; - case TruncUFloat64ToInt32: makeClampLimitLiterals<uint32_t, double>(iMin, fMin, fMax); break; - case TruncSFloat64ToInt64: makeClampLimitLiterals< int64_t, double>(iMin, fMin, fMax); break; - case TruncUFloat64ToInt64: makeClampLimitLiterals<uint64_t, double>(iMin, fMin, fMax); break; - default: WASM_UNREACHABLE(); + case TruncSFloat32ToInt32: + makeClampLimitLiterals<int32_t, float>(iMin, fMin, fMax); + break; + case TruncUFloat32ToInt32: + makeClampLimitLiterals<uint32_t, float>(iMin, fMin, fMax); + break; + case TruncSFloat32ToInt64: + makeClampLimitLiterals<int64_t, float>(iMin, fMin, fMax); + break; + case TruncUFloat32ToInt64: + makeClampLimitLiterals<uint64_t, float>(iMin, fMin, fMax); + break; + case TruncSFloat64ToInt32: + makeClampLimitLiterals<int32_t, double>(iMin, fMin, fMax); + break; + case TruncUFloat64ToInt32: + makeClampLimitLiterals<uint32_t, double>(iMin, fMin, fMax); + break; + case TruncSFloat64ToInt64: + makeClampLimitLiterals<int64_t, double>(iMin, fMin, fMax); + break; + case TruncUFloat64ToInt64: + makeClampLimitLiterals<uint64_t, double>(iMin, fMin, fMax); + break; + default: + WASM_UNREACHABLE(); } auto func = new Function; func->name = getUnaryFuncName(curr); func->params.push_back(type); func->result = retType; - func->body = builder.makeUnary(truncOp, - builder.makeGetLocal(0, type) - ); + func->body = builder.makeUnary(truncOp, builder.makeGetLocal(0, type)); // too small XXX this is different than asm.js, which does frem. here we // clamp, which is much simpler/faster, and similar to native builds - func->body = builder.makeIf( - builder.makeBinary(leOp, - builder.makeGetLocal(0, type), - builder.makeConst(fMin) - ), - builder.makeConst(iMin), - func->body - ); + func->body = builder.makeIf(builder.makeBinary(leOp, + builder.makeGetLocal(0, type), + builder.makeConst(fMin)), + builder.makeConst(iMin), + func->body); // too big XXX see above func->body = builder.makeIf( - builder.makeBinary(geOp, - builder.makeGetLocal(0, type), - builder.makeConst(fMax) - ), + builder.makeBinary( + geOp, builder.makeGetLocal(0, type), builder.makeConst(fMax)), // NB: min here as well. anything out of range => to the min builder.makeConst(iMin), - func->body - ); + func->body); // nan func->body = builder.makeIf( - builder.makeBinary(neOp, - builder.makeGetLocal(0, type), - builder.makeGetLocal(0, type) - ), + builder.makeBinary( + neOp, builder.makeGetLocal(0, type), builder.makeGetLocal(0, type)), // NB: min here as well. anything invalid => to the min builder.makeConst(iMin), - func->body - ); + func->body); return func; } -void ensureBinaryFunc(Binary* curr, Module& wasm, - TrappingFunctionContainer &trappingFunctions) { +void ensureBinaryFunc(Binary* curr, + Module& wasm, + TrappingFunctionContainer& trappingFunctions) { Name name = getBinaryFuncName(curr); if (trappingFunctions.hasFunction(name)) { return; @@ -208,8 +225,9 @@ void ensureBinaryFunc(Binary* curr, Module& wasm, trappingFunctions.addFunction(generateBinaryFunc(wasm, curr)); } -void ensureUnaryFunc(Unary *curr, Module& wasm, - TrappingFunctionContainer &trappingFunctions) { +void ensureUnaryFunc(Unary* curr, + Module& wasm, + TrappingFunctionContainer& trappingFunctions) { Name name = getUnaryFuncName(curr); if (trappingFunctions.hasFunction(name)) { return; @@ -217,7 +235,7 @@ void ensureUnaryFunc(Unary *curr, Module& wasm, trappingFunctions.addFunction(generateUnaryFunc(wasm, curr)); } -void ensureF64ToI64JSImport(TrappingFunctionContainer &trappingFunctions) { +void ensureF64ToI64JSImport(TrappingFunctionContainer& trappingFunctions) { if (trappingFunctions.hasImport(F64_TO_INT)) { return; } @@ -233,7 +251,8 @@ void ensureF64ToI64JSImport(TrappingFunctionContainer &trappingFunctions) { trappingFunctions.addImport(import); } -Expression* makeTrappingBinary(Binary* curr, TrappingFunctionContainer &trappingFunctions) { +Expression* makeTrappingBinary(Binary* curr, + TrappingFunctionContainer& trappingFunctions) { Name name = getBinaryFuncName(curr); if (!name.is() || trappingFunctions.getMode() == TrapMode::Allow) { return curr; @@ -247,7 +266,8 @@ Expression* makeTrappingBinary(Binary* curr, TrappingFunctionContainer &trapping return builder.makeCall(name, {curr->left, curr->right}, type); } -Expression* makeTrappingUnary(Unary* curr, TrappingFunctionContainer &trappingFunctions) { +Expression* makeTrappingUnary(Unary* curr, + TrappingFunctionContainer& trappingFunctions) { Name name = getUnaryFuncName(curr); TrapMode mode = trappingFunctions.getMode(); if (!name.is() || mode == TrapMode::Allow) { @@ -256,13 +276,15 @@ Expression* makeTrappingUnary(Unary* curr, TrappingFunctionContainer &trappingFu Module& wasm = trappingFunctions.getModule(); Builder builder(wasm); - // WebAssembly traps on float-to-int overflows, but asm.js wouldn't, so we must do something - // We can handle this in one of two ways: clamping, which is fast, or JS, which - // is precisely like JS but in order to do that we do a slow ffi - // If i64, there is no "JS" way to handle this, as no i64s in JS, so always clamp if we don't allow traps - // asm.js doesn't have unsigned f64-to-int, so just use the signed one. + // WebAssembly traps on float-to-int overflows, but asm.js wouldn't, so we + // must do something We can handle this in one of two ways: clamping, which is + // fast, or JS, which is precisely like JS but in order to do that we do a + // slow ffi If i64, there is no "JS" way to handle this, as no i64s in JS, so + // always clamp if we don't allow traps asm.js doesn't have unsigned + // f64-to-int, so just use the signed one. if (curr->type != i64 && mode == TrapMode::JS) { - // WebAssembly traps on float-to-int overflows, but asm.js wouldn't, so we must emulate that + // WebAssembly traps on float-to-int overflows, but asm.js wouldn't, so we + // must emulate that ensureF64ToI64JSImport(trappingFunctions); Expression* f64Value = ensureDouble(curr->value, wasm.allocator); return builder.makeCall(F64_TO_INT, {f64Value}, i32); @@ -274,14 +296,11 @@ Expression* makeTrappingUnary(Unary* curr, TrappingFunctionContainer &trappingFu struct TrapModePass : public WalkerPass<PostWalker<TrapModePass>> { public: - // Needs to be non-parallel so that visitModule gets called after visiting // each node in the module, so we can add the functions that we created. bool isFunctionParallel() override { return false; } - TrapModePass(TrapMode mode) : mode(mode) { - assert(mode != TrapMode::Allow); - } + TrapModePass(TrapMode mode) : mode(mode) { assert(mode != TrapMode::Allow); } Pass* create() override { return new TrapModePass(mode); } @@ -293,9 +312,7 @@ public: replaceCurrent(makeTrappingBinary(curr, *trappingFunctions)); } - void visitModule(Module* curr) { - trappingFunctions->addToModule(); - } + void visitModule(Module* curr) { trappingFunctions->addToModule(); } void doWalkModule(Module* module) { trappingFunctions = make_unique<TrappingFunctionContainer>(mode, *module); @@ -309,12 +326,8 @@ private: std::unique_ptr<TrappingFunctionContainer> trappingFunctions; }; -Pass *createTrapModeClamp() { - return new TrapModePass(TrapMode::Clamp); -} +Pass* createTrapModeClamp() { return new TrapModePass(TrapMode::Clamp); } -Pass *createTrapModeJS() { - return new TrapModePass(TrapMode::JS); -} +Pass* createTrapModeJS() { return new TrapModePass(TrapMode::JS); } } // namespace wasm diff --git a/src/passes/Untee.cpp b/src/passes/Untee.cpp index 00f2ffe5d..713962aeb 100644 --- a/src/passes/Untee.cpp +++ b/src/passes/Untee.cpp @@ -22,9 +22,9 @@ // more effective. // -#include <wasm.h> #include <pass.h> #include <wasm-builder.h> +#include <wasm.h> namespace wasm { @@ -33,7 +33,7 @@ struct Untee : public WalkerPass<PostWalker<Untee>> { Pass* create() override { return new Untee; } - void visitSetLocal(SetLocal *curr) { + void visitSetLocal(SetLocal* curr) { if (curr->isTee()) { if (curr->value->type == unreachable) { // we don't reach the tee, just remove it @@ -41,21 +41,14 @@ struct Untee : public WalkerPass<PostWalker<Untee>> { } else { // a normal tee. replace with set and get Builder builder(*getModule()); - replaceCurrent( - builder.makeSequence( - curr, - builder.makeGetLocal(curr->index, curr->value->type) - ) - ); + replaceCurrent(builder.makeSequence( + curr, builder.makeGetLocal(curr->index, curr->value->type))); curr->setTee(false); } } } }; -Pass *createUnteePass() { - return new Untee(); -} +Pass* createUnteePass() { return new Untee(); } } // namespace wasm - diff --git a/src/passes/Vacuum.cpp b/src/passes/Vacuum.cpp index 08581a3eb..8874ffed2 100644 --- a/src/passes/Vacuum.cpp +++ b/src/passes/Vacuum.cpp @@ -18,14 +18,14 @@ // Removes obviously unneeded code // -#include <wasm.h> -#include <pass.h> -#include <wasm-builder.h> #include <ir/block-utils.h> #include <ir/effects.h> #include <ir/literal-utils.h> #include <ir/type-updating.h> #include <ir/utils.h> +#include <pass.h> +#include <wasm-builder.h> +#include <wasm.h> namespace wasm { @@ -49,15 +49,17 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { walk(func->body); } - // Returns nullptr if curr is dead, curr if it must stay as is, or another node if it can be replaced. - // Takes into account: + // Returns nullptr if curr is dead, curr if it must stay as is, or another + // node if it can be replaced. Takes into account: // * The result may be used or unused. // * The type may or may not matter (a drop can drop anything, for example). Expression* optimize(Expression* curr, bool resultUsed, bool typeMatters) { auto type = curr->type; // An unreachable node must not be changed. - if (type == unreachable) return curr; - // We iterate on possible replacements. If a replacement changes the type, stop and go back. + if (type == unreachable) + return curr; + // We iterate on possible replacements. If a replacement changes the type, + // stop and go back. auto* prev = curr; while (1) { if (typeMatters && curr->type != type) { @@ -65,12 +67,17 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { } prev = curr; switch (curr->_id) { - case Expression::Id::NopId: return nullptr; // never needed + case Expression::Id::NopId: + return nullptr; // never needed - case Expression::Id::BlockId: return curr; // not always needed, but handled in visitBlock() - case Expression::Id::IfId: return curr; // not always needed, but handled in visitIf() - case Expression::Id::LoopId: return curr; // not always needed, but handled in visitLoop() - case Expression::Id::DropId: return curr; // not always needed, but handled in visitDrop() + case Expression::Id::BlockId: + return curr; // not always needed, but handled in visitBlock() + case Expression::Id::IfId: + return curr; // not always needed, but handled in visitIf() + case Expression::Id::LoopId: + return curr; // not always needed, but handled in visitLoop() + case Expression::Id::DropId: + return curr; // not always needed, but handled in visitDrop() case Expression::Id::BreakId: case Expression::Id::SwitchId: @@ -81,12 +88,15 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { case Expression::Id::ReturnId: case Expression::Id::SetGlobalId: case Expression::Id::HostId: - case Expression::Id::UnreachableId: return curr; // always needed + case Expression::Id::UnreachableId: + return curr; // always needed case Expression::Id::LoadId: { // it is ok to remove a load if the result is not used, and it has no - // side effects (the load itself may trap, if we are not ignoring such things) - if (!resultUsed && !EffectAnalyzer(getPassOptions(), curr).hasSideEffects()) { + // side effects (the load itself may trap, if we are not ignoring such + // things) + if (!resultUsed && + !EffectAnalyzer(getPassOptions(), curr).hasSideEffects()) { return curr->cast<Load>()->ptr; } return curr; @@ -94,7 +104,8 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { case Expression::Id::ConstId: case Expression::Id::GetLocalId: case Expression::Id::GetGlobalId: { - if (!resultUsed) return nullptr; + if (!resultUsed) + return nullptr; return curr; } @@ -104,15 +115,17 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { if (resultUsed) { return curr; // used, keep it } - // for unary, binary, and select, we need to check their arguments for side effects, - // as well as the node itself, as some unaries and binaries have implicit traps + // for unary, binary, and select, we need to check their arguments for + // side effects, as well as the node itself, as some unaries and + // binaries have implicit traps if (auto* unary = curr->dynCast<Unary>()) { EffectAnalyzer tester(getPassOptions()); tester.visitUnary(unary); if (tester.hasSideEffects()) { return curr; } - if (EffectAnalyzer(getPassOptions(), unary->value).hasSideEffects()) { + if (EffectAnalyzer(getPassOptions(), unary->value) + .hasSideEffects()) { curr = unary->value; continue; } else { @@ -124,15 +137,18 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { if (tester.hasSideEffects()) { return curr; } - if (EffectAnalyzer(getPassOptions(), binary->left).hasSideEffects()) { - if (EffectAnalyzer(getPassOptions(), binary->right).hasSideEffects()) { + if (EffectAnalyzer(getPassOptions(), binary->left) + .hasSideEffects()) { + if (EffectAnalyzer(getPassOptions(), binary->right) + .hasSideEffects()) { return curr; // leave them } else { curr = binary->left; continue; } } else { - if (EffectAnalyzer(getPassOptions(), binary->right).hasSideEffects()) { + if (EffectAnalyzer(getPassOptions(), binary->right) + .hasSideEffects()) { curr = binary->right; continue; } else { @@ -140,13 +156,17 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { } } } else { - // TODO: if two have side effects, we could replace the select with say an add? + // TODO: if two have side effects, we could replace the select with + // say an add? auto* select = curr->cast<Select>(); - if (EffectAnalyzer(getPassOptions(), select->ifTrue).hasSideEffects()) { - if (EffectAnalyzer(getPassOptions(), select->ifFalse).hasSideEffects()) { + if (EffectAnalyzer(getPassOptions(), select->ifTrue) + .hasSideEffects()) { + if (EffectAnalyzer(getPassOptions(), select->ifFalse) + .hasSideEffects()) { return curr; // leave them } else { - if (EffectAnalyzer(getPassOptions(), select->condition).hasSideEffects()) { + if (EffectAnalyzer(getPassOptions(), select->condition) + .hasSideEffects()) { return curr; // leave them } else { curr = select->ifTrue; @@ -154,15 +174,18 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { } } } else { - if (EffectAnalyzer(getPassOptions(), select->ifFalse).hasSideEffects()) { - if (EffectAnalyzer(getPassOptions(), select->condition).hasSideEffects()) { + if (EffectAnalyzer(getPassOptions(), select->ifFalse) + .hasSideEffects()) { + if (EffectAnalyzer(getPassOptions(), select->condition) + .hasSideEffects()) { return curr; // leave them } else { curr = select->ifFalse; continue; } } else { - if (EffectAnalyzer(getPassOptions(), select->condition).hasSideEffects()) { + if (EffectAnalyzer(getPassOptions(), select->condition) + .hasSideEffects()) { curr = select->condition; continue; } else { @@ -173,12 +196,13 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { } } - default: return curr; // assume needed + default: + return curr; // assume needed } } } - void visitBlock(Block *curr) { + void visitBlock(Block* curr) { // compress out nops and other dead code int skip = 0; auto& list = curr->list; @@ -186,18 +210,20 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { for (size_t z = 0; z < size; z++) { auto* child = list[z]; // The last element may be used. - bool used = z == size - 1 && - isConcreteType(curr->type) && - ExpressionAnalyzer::isResultUsed(expressionStack, getFunction()); + bool used = + z == size - 1 && isConcreteType(curr->type) && + ExpressionAnalyzer::isResultUsed(expressionStack, getFunction()); auto* optimized = optimize(child, used, true); if (!optimized) { if (isConcreteType(child->type)) { - // We can't just skip a final concrete element, even if it isn't used. Instead, - // replace it with something that's easy to optimize out (for example, code-folding - // can merge out identical zeros at the end of if arms). + // We can't just skip a final concrete element, even if it isn't used. + // Instead, replace it with something that's easy to optimize out (for + // example, code-folding can merge out identical zeros at the end of + // if arms). optimized = LiteralUtils::makeZero(child->type, *getModule()); } else if (child->type == unreachable) { - // Don't try to optimize out an unreachable child (dce can do that properly). + // Don't try to optimize out an unreachable child (dce can do that + // properly). optimized = child; } } @@ -232,7 +258,8 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { list.resize(size - skip); typeUpdater.maybeUpdateTypeToUnreachable(curr); } - // the block may now be a trivial one that we can get rid of and just leave its contents + // the block may now be a trivial one that we can get rid of and just leave + // its contents replaceCurrent(BlockUtils::simplifyToContents(curr, this)); } @@ -275,9 +302,11 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { } else if (curr->ifTrue->is<Nop>()) { curr->ifTrue = curr->ifFalse; curr->ifFalse = nullptr; - curr->condition = Builder(*getModule()).makeUnary(EqZInt32, curr->condition); + curr->condition = + Builder(*getModule()).makeUnary(EqZInt32, curr->condition); } else if (curr->ifTrue->is<Drop>() && curr->ifFalse->is<Drop>()) { - // instead of dropping both sides, drop the if, if they are the same type + // instead of dropping both sides, drop the if, if they are the same + // type auto* left = curr->ifTrue->cast<Drop>()->value; auto* right = curr->ifFalse->cast<Drop>()->value; if (left->type == right->type) { @@ -297,7 +326,8 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { } void visitLoop(Loop* curr) { - if (curr->body->is<Nop>()) ExpressionManipulator::nop(curr); + if (curr->body->is<Nop>()) + ExpressionManipulator::nop(curr); } void visitDrop(Drop* curr) { @@ -314,12 +344,13 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { replaceCurrent(set); return; } - // if we are dropping a block's return value, we might be able to remove it entirely + // if we are dropping a block's return value, we might be able to remove it + // entirely if (auto* block = curr->value->dynCast<Block>()) { auto* last = block->list.back(); // note that the last element may be concrete but not the block, if the - // block has an unreachable element in the middle, making the block unreachable - // despite later elements and in particular the last + // block has an unreachable element in the middle, making the block + // unreachable despite later elements and in particular the last if (isConcreteType(last->type) && block->type == last->type) { last = optimize(last, false, false); if (!last) { @@ -338,7 +369,8 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { block->list.back() = last; block->list.pop_back(); block->type = none; - // we don't need the drop anymore, let's see what we have left in the block + // we don't need the drop anymore, let's see what we have left in + // the block if (block->list.size() > 1) { replaceCurrent(block); } else if (block->list.size() == 1) { @@ -351,16 +383,20 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { } } } - // sink a drop into an arm of an if-else if the other arm ends in an unreachable, as it if is a branch, this can make that branch optimizable and more vaccuming possible + // sink a drop into an arm of an if-else if the other arm ends in an + // unreachable, as it if is a branch, this can make that branch optimizable + // and more vaccuming possible auto* iff = curr->value->dynCast<If>(); if (iff && iff->ifFalse && isConcreteType(iff->type)) { // reuse the drop in both cases - if (iff->ifTrue->type == unreachable && isConcreteType(iff->ifFalse->type)) { + if (iff->ifTrue->type == unreachable && + isConcreteType(iff->ifFalse->type)) { curr->value = iff->ifFalse; iff->ifFalse = curr; iff->type = none; replaceCurrent(iff); - } else if (iff->ifFalse->type == unreachable && isConcreteType(iff->ifTrue->type)) { + } else if (iff->ifFalse->type == unreachable && + isConcreteType(iff->ifTrue->type)) { curr->value = iff->ifTrue; iff->ifTrue = curr; iff->type = none; @@ -376,15 +412,13 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> { } else { ExpressionManipulator::nop(curr->body); } - if (curr->result == none && !EffectAnalyzer(getPassOptions(), curr->body).hasSideEffects()) { + if (curr->result == none && + !EffectAnalyzer(getPassOptions(), curr->body).hasSideEffects()) { ExpressionManipulator::nop(curr->body); } } }; -Pass *createVacuumPass() { - return new Vacuum(); -} +Pass* createVacuumPass() { return new Vacuum(); } } // namespace wasm - diff --git a/src/passes/intrinsics-module.h b/src/passes/intrinsics-module.h index c9a757dc0..e7f7a3a6e 100644 --- a/src/passes/intrinsics-module.h +++ b/src/passes/intrinsics-module.h @@ -24,4 +24,3 @@ extern const char* IntrinsicsModuleWast; } // namespace wasm #endif // passes_intrinsics_module_h - diff --git a/src/passes/opt-utils.h b/src/passes/opt-utils.h index a880e2623..9ac6da4a2 100644 --- a/src/passes/opt-utils.h +++ b/src/passes/opt-utils.h @@ -19,8 +19,8 @@ #include <unordered_set> -#include <wasm.h> #include <pass.h> +#include <wasm.h> namespace wasm { @@ -28,7 +28,9 @@ namespace OptUtils { // Run useful optimizations after inlining new code into a set // of functions. -inline void optimizeAfterInlining(std::unordered_set<Function*>& funcs, Module* module, PassRunner* parentRunner) { +inline void optimizeAfterInlining(std::unordered_set<Function*>& funcs, + Module* module, + PassRunner* parentRunner) { // save the full list of functions on the side std::vector<std::unique_ptr<Function>> all; all.swap(module->functions); @@ -39,7 +41,8 @@ inline void optimizeAfterInlining(std::unordered_set<Function*>& funcs, Module* PassRunner runner(module, parentRunner->options); runner.setIsNested(true); runner.setValidateGlobally(false); // not a full valid module - runner.add("precompute-propagate"); // this is especially useful after inlining + // this is especially useful after inlining + runner.add("precompute-propagate"); runner.addDefaultFunctionOptimizationPasses(); // do all the usual stuff runner.run(); // restore all the funcs diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 41d2026bc..ae940a56d 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -17,35 +17,34 @@ #include <chrono> #include <sstream> -#include "support/colors.h" -#include "passes/passes.h" -#include "pass.h" -#include "wasm-validator.h" -#include "wasm-io.h" #include "ir/hashed.h" #include "ir/module-utils.h" +#include "pass.h" +#include "passes/passes.h" +#include "support/colors.h" +#include "wasm-io.h" +#include "wasm-validator.h" namespace wasm { // PassRegistry -PassRegistry::PassRegistry() { - registerPasses(); -} +PassRegistry::PassRegistry() { registerPasses(); } static PassRegistry singleton; -PassRegistry* PassRegistry::get() { - return &singleton; -} +PassRegistry* PassRegistry::get() { return &singleton; } -void PassRegistry::registerPass(const char* name, const char *description, Creator create) { +void PassRegistry::registerPass(const char* name, + const char* description, + Creator create) { assert(passInfos.find(name) == passInfos.end()); passInfos[name] = PassInfo(description, create); } Pass* PassRegistry::createPass(std::string name) { - if (passInfos.find(name) == passInfos.end()) return nullptr; + if (passInfos.find(name) == passInfos.end()) + return nullptr; auto ret = passInfos[name].create(); ret->name = name; return ret; @@ -67,87 +66,234 @@ std::string PassRegistry::getPassDescription(std::string name) { // PassRunner void PassRegistry::registerPasses() { - registerPass("dae", "removes arguments to calls in an lto-like manner", createDAEPass); - registerPass("dae-optimizing", "removes arguments to calls in an lto-like manner, and optimizes where we removed", createDAEOptimizingPass); - registerPass("coalesce-locals", "reduce # of locals by coalescing", createCoalesceLocalsPass); - registerPass("coalesce-locals-learning", "reduce # of locals by coalescing and learning", createCoalesceLocalsWithLearningPass); - registerPass("code-pushing", "push code forward, potentially making it not always execute", createCodePushingPass); - registerPass("code-folding", "fold code, merging duplicates", createCodeFoldingPass); - registerPass("const-hoisting", "hoist repeated constants to a local", createConstHoistingPass); - registerPass("dce", "removes unreachable code", createDeadCodeEliminationPass); - registerPass("directize", "turns indirect calls into direct ones", createDirectizePass); - registerPass("dfo", "optimizes using the DataFlow SSA IR", createDataFlowOptsPass); - registerPass("duplicate-function-elimination", "removes duplicate functions", createDuplicateFunctionEliminationPass); - registerPass("extract-function", "leaves just one function (useful for debugging)", createExtractFunctionPass); - registerPass("flatten", "flattens out code, removing nesting", createFlattenPass); - registerPass("fpcast-emu", "emulates function pointer casts, allowing incorrect indirect calls to (sometimes) work", createFuncCastEmulationPass); - registerPass("func-metrics", "reports function metrics", createFunctionMetricsPass); - registerPass("generate-stack-ir", "generate Stack IR", createGenerateStackIRPass); - registerPass("inlining", "inline functions (you probably want inlining-optimizing)", createInliningPass); - registerPass("inlining-optimizing", "inline functions and optimizes where we inlined", createInliningOptimizingPass); - registerPass("legalize-js-interface", "legalizes i64 types on the import/export boundary", createLegalizeJSInterfacePass); - registerPass("legalize-js-interface-minimally", "legalizes i64 types on the import/export boundary in a minimal manner, only on things only JS will call", createLegalizeJSInterfaceMinimallyPass); - registerPass("local-cse", "common subexpression elimination inside basic blocks", createLocalCSEPass); - registerPass("log-execution", "instrument the build with logging of where execution goes", createLogExecutionPass); - registerPass("i64-to-i32-lowering", "lower all uses of i64s to use i32s instead", createI64ToI32LoweringPass); - registerPass("instrument-locals", "instrument the build with code to intercept all loads and stores", createInstrumentLocalsPass); - registerPass("instrument-memory", "instrument the build with code to intercept all loads and stores", createInstrumentMemoryPass); - registerPass("licm", "loop invariant code motion", createLoopInvariantCodeMotionPass); - registerPass("limit-segments", "attempt to merge segments to fit within web limits", createLimitSegmentsPass); - registerPass("memory-packing", "packs memory into separate segments, skipping zeros", createMemoryPackingPass); - registerPass("merge-blocks", "merges blocks to their parents", createMergeBlocksPass); - registerPass("merge-locals", "merges locals when beneficial", createMergeLocalsPass); + registerPass( + "dae", "removes arguments to calls in an lto-like manner", createDAEPass); + registerPass("dae-optimizing", + "removes arguments to calls in an lto-like manner, and " + "optimizes where we removed", + createDAEOptimizingPass); + registerPass("coalesce-locals", + "reduce # of locals by coalescing", + createCoalesceLocalsPass); + registerPass("coalesce-locals-learning", + "reduce # of locals by coalescing and learning", + createCoalesceLocalsWithLearningPass); + registerPass("code-pushing", + "push code forward, potentially making it not always execute", + createCodePushingPass); + registerPass( + "code-folding", "fold code, merging duplicates", createCodeFoldingPass); + registerPass("const-hoisting", + "hoist repeated constants to a local", + createConstHoistingPass); + registerPass( + "dce", "removes unreachable code", createDeadCodeEliminationPass); + registerPass( + "directize", "turns indirect calls into direct ones", createDirectizePass); + registerPass( + "dfo", "optimizes using the DataFlow SSA IR", createDataFlowOptsPass); + registerPass("duplicate-function-elimination", + "removes duplicate functions", + createDuplicateFunctionEliminationPass); + registerPass("extract-function", + "leaves just one function (useful for debugging)", + createExtractFunctionPass); + registerPass( + "flatten", "flattens out code, removing nesting", createFlattenPass); + registerPass("fpcast-emu", + "emulates function pointer casts, allowing incorrect indirect " + "calls to (sometimes) work", + createFuncCastEmulationPass); + registerPass( + "func-metrics", "reports function metrics", createFunctionMetricsPass); + registerPass( + "generate-stack-ir", "generate Stack IR", createGenerateStackIRPass); + registerPass("inlining", + "inline functions (you probably want inlining-optimizing)", + createInliningPass); + registerPass("inlining-optimizing", + "inline functions and optimizes where we inlined", + createInliningOptimizingPass); + registerPass("legalize-js-interface", + "legalizes i64 types on the import/export boundary", + createLegalizeJSInterfacePass); + registerPass("legalize-js-interface-minimally", + "legalizes i64 types on the import/export boundary in a minimal " + "manner, only on things only JS will call", + createLegalizeJSInterfaceMinimallyPass); + registerPass("local-cse", + "common subexpression elimination inside basic blocks", + createLocalCSEPass); + registerPass("log-execution", + "instrument the build with logging of where execution goes", + createLogExecutionPass); + registerPass("i64-to-i32-lowering", + "lower all uses of i64s to use i32s instead", + createI64ToI32LoweringPass); + registerPass( + "instrument-locals", + "instrument the build with code to intercept all loads and stores", + createInstrumentLocalsPass); + registerPass( + "instrument-memory", + "instrument the build with code to intercept all loads and stores", + createInstrumentMemoryPass); + registerPass( + "licm", "loop invariant code motion", createLoopInvariantCodeMotionPass); + registerPass("limit-segments", + "attempt to merge segments to fit within web limits", + createLimitSegmentsPass); + registerPass("memory-packing", + "packs memory into separate segments, skipping zeros", + createMemoryPackingPass); + registerPass( + "merge-blocks", "merges blocks to their parents", createMergeBlocksPass); + registerPass( + "merge-locals", "merges locals when beneficial", createMergeLocalsPass); registerPass("metrics", "reports metrics", createMetricsPass); - registerPass("minify-imports", "minifies import names (only those, and not export names), and emits a mapping to the minified ones", createMinifyImportsPass); - registerPass("minify-imports-and-exports", "minifies both import and export names, and emits a mapping to the minified ones", createMinifyImportsAndExportsPass); + registerPass("minify-imports", + "minifies import names (only those, and not export names), and " + "emits a mapping to the minified ones", + createMinifyImportsPass); + registerPass("minify-imports-and-exports", + "minifies both import and export names, and emits a mapping to " + "the minified ones", + createMinifyImportsAndExportsPass); registerPass("nm", "name list", createNameListPass); - registerPass("no-exit-runtime", "removes calls to atexit(), which is valid if the C runtime will never be exited", createNoExitRuntimePass); - registerPass("optimize-added-constants", "optimizes added constants into load/store offsets", createOptimizeAddedConstantsPass); - registerPass("optimize-added-constants-propagate", "optimizes added constants into load/store offsets, propagating them across locals too", createOptimizeAddedConstantsPropagatePass); - registerPass("optimize-instructions", "optimizes instruction combinations", createOptimizeInstructionsPass); - registerPass("optimize-stack-ir", "optimize Stack IR", createOptimizeStackIRPass); - registerPass("pick-load-signs", "pick load signs based on their uses", createPickLoadSignsPass); - registerPass("post-emscripten", "miscellaneous optimizations for Emscripten-generated code", createPostEmscriptenPass); - registerPass("precompute", "computes compile-time evaluatable expressions", createPrecomputePass); - registerPass("precompute-propagate", "computes compile-time evaluatable expressions and propagates them through locals", createPrecomputePropagatePass); + registerPass("no-exit-runtime", + "removes calls to atexit(), which is valid if the C runtime " + "will never be exited", + createNoExitRuntimePass); + registerPass("optimize-added-constants", + "optimizes added constants into load/store offsets", + createOptimizeAddedConstantsPass); + registerPass("optimize-added-constants-propagate", + "optimizes added constants into load/store offsets, propagating " + "them across locals too", + createOptimizeAddedConstantsPropagatePass); + registerPass("optimize-instructions", + "optimizes instruction combinations", + createOptimizeInstructionsPass); + registerPass( + "optimize-stack-ir", "optimize Stack IR", createOptimizeStackIRPass); + registerPass("pick-load-signs", + "pick load signs based on their uses", + createPickLoadSignsPass); + registerPass("post-emscripten", + "miscellaneous optimizations for Emscripten-generated code", + createPostEmscriptenPass); + registerPass("precompute", + "computes compile-time evaluatable expressions", + createPrecomputePass); + registerPass("precompute-propagate", + "computes compile-time evaluatable expressions and propagates " + "them through locals", + createPrecomputePropagatePass); registerPass("print", "print in s-expression format", createPrinterPass); - registerPass("print-minified", "print in minified s-expression format", createMinifiedPrinterPass); - registerPass("print-features", "print options for enabled features", createPrintFeaturesPass); - registerPass("print-full", "print in full s-expression format", createFullPrinterPass); - registerPass("print-call-graph", "print call graph", createPrintCallGraphPass); - registerPass("print-stack-ir", "print out Stack IR (useful for internal debugging)", createPrintStackIRPass); - registerPass("relooper-jump-threading", "thread relooper jumps (fastcomp output only)", createRelooperJumpThreadingPass); - registerPass("remove-non-js-ops", "removes operations incompatible with js", createRemoveNonJSOpsPass); - registerPass("remove-imports", "removes imports and replaces them with nops", createRemoveImportsPass); - registerPass("remove-memory", "removes memory segments", createRemoveMemoryPass); - registerPass("remove-unused-brs", "removes breaks from locations that are not needed", createRemoveUnusedBrsPass); - registerPass("remove-unused-module-elements", "removes unused module elements", createRemoveUnusedModuleElementsPass); - registerPass("remove-unused-nonfunction-module-elements", "removes unused module elements that are not functions", createRemoveUnusedNonFunctionModuleElementsPass); - registerPass("remove-unused-names", "removes names from locations that are never branched to", createRemoveUnusedNamesPass); - registerPass("reorder-functions", "sorts functions by access frequency", createReorderFunctionsPass); - registerPass("reorder-locals", "sorts locals by access frequency", createReorderLocalsPass); - registerPass("rereloop", "re-optimize control flow using the relooper algorithm", createReReloopPass); - registerPass("rse", "remove redundant local.sets", createRedundantSetEliminationPass); - registerPass("safe-heap", "instrument loads and stores to check for invalid behavior", createSafeHeapPass); - registerPass("simplify-locals", "miscellaneous locals-related optimizations", createSimplifyLocalsPass); - registerPass("simplify-locals-nonesting", "miscellaneous locals-related optimizations (no nesting at all; preserves flatness)", createSimplifyLocalsNoNestingPass); - registerPass("simplify-locals-notee", "miscellaneous locals-related optimizations (no tees)", createSimplifyLocalsNoTeePass); - registerPass("simplify-locals-nostructure", "miscellaneous locals-related optimizations (no structure)", createSimplifyLocalsNoStructurePass); - registerPass("simplify-locals-notee-nostructure", "miscellaneous locals-related optimizations (no tees or structure)", createSimplifyLocalsNoTeeNoStructurePass); + registerPass("print-minified", + "print in minified s-expression format", + createMinifiedPrinterPass); + registerPass("print-features", + "print options for enabled features", + createPrintFeaturesPass); + registerPass( + "print-full", "print in full s-expression format", createFullPrinterPass); + registerPass( + "print-call-graph", "print call graph", createPrintCallGraphPass); + registerPass("print-stack-ir", + "print out Stack IR (useful for internal debugging)", + createPrintStackIRPass); + registerPass("relooper-jump-threading", + "thread relooper jumps (fastcomp output only)", + createRelooperJumpThreadingPass); + registerPass("remove-non-js-ops", + "removes operations incompatible with js", + createRemoveNonJSOpsPass); + registerPass("remove-imports", + "removes imports and replaces them with nops", + createRemoveImportsPass); + registerPass( + "remove-memory", "removes memory segments", createRemoveMemoryPass); + registerPass("remove-unused-brs", + "removes breaks from locations that are not needed", + createRemoveUnusedBrsPass); + registerPass("remove-unused-module-elements", + "removes unused module elements", + createRemoveUnusedModuleElementsPass); + registerPass("remove-unused-nonfunction-module-elements", + "removes unused module elements that are not functions", + createRemoveUnusedNonFunctionModuleElementsPass); + registerPass("remove-unused-names", + "removes names from locations that are never branched to", + createRemoveUnusedNamesPass); + registerPass("reorder-functions", + "sorts functions by access frequency", + createReorderFunctionsPass); + registerPass("reorder-locals", + "sorts locals by access frequency", + createReorderLocalsPass); + registerPass("rereloop", + "re-optimize control flow using the relooper algorithm", + createReReloopPass); + registerPass( + "rse", "remove redundant local.sets", createRedundantSetEliminationPass); + registerPass("safe-heap", + "instrument loads and stores to check for invalid behavior", + createSafeHeapPass); + registerPass("simplify-locals", + "miscellaneous locals-related optimizations", + createSimplifyLocalsPass); + registerPass("simplify-locals-nonesting", + "miscellaneous locals-related optimizations (no nesting at all; " + "preserves flatness)", + createSimplifyLocalsNoNestingPass); + registerPass("simplify-locals-notee", + "miscellaneous locals-related optimizations (no tees)", + createSimplifyLocalsNoTeePass); + registerPass("simplify-locals-nostructure", + "miscellaneous locals-related optimizations (no structure)", + createSimplifyLocalsNoStructurePass); + registerPass( + "simplify-locals-notee-nostructure", + "miscellaneous locals-related optimizations (no tees or structure)", + createSimplifyLocalsNoTeeNoStructurePass); registerPass("souperify", "emit Souper IR in text form", createSouperifyPass); - registerPass("souperify-single-use", "emit Souper IR in text form (single-use nodes only)", createSouperifySingleUsePass); - registerPass("spill-pointers", "spill pointers to the C stack (useful for Boehm-style GC)", createSpillPointersPass); - registerPass("ssa", "ssa-ify variables so that they have a single assignment", createSSAifyPass); - registerPass("ssa-nomerge", "ssa-ify variables so that they have a single assignment, ignoring merges", createSSAifyNoMergePass); - registerPass("strip", "deprecated; same as strip-debug", createStripDebugPass); - registerPass("strip-debug", "strip debug info (including the names section)", createStripDebugPass); - registerPass("strip-producers", "strip the wasm producers section", createStripProducersPass); - registerPass("strip-target-features", "strip the wasm target features section", createStripTargetFeaturesPass); - registerPass("trap-mode-clamp", "replace trapping operations with clamping semantics", createTrapModeClamp); - registerPass("trap-mode-js", "replace trapping operations with js semantics", createTrapModeJS); - registerPass("untee", "removes local.tees, replacing them with sets and gets", createUnteePass); + registerPass("souperify-single-use", + "emit Souper IR in text form (single-use nodes only)", + createSouperifySingleUsePass); + registerPass("spill-pointers", + "spill pointers to the C stack (useful for Boehm-style GC)", + createSpillPointersPass); + registerPass("ssa", + "ssa-ify variables so that they have a single assignment", + createSSAifyPass); + registerPass( + "ssa-nomerge", + "ssa-ify variables so that they have a single assignment, ignoring merges", + createSSAifyNoMergePass); + registerPass( + "strip", "deprecated; same as strip-debug", createStripDebugPass); + registerPass("strip-debug", + "strip debug info (including the names section)", + createStripDebugPass); + registerPass("strip-producers", + "strip the wasm producers section", + createStripProducersPass); + registerPass("strip-target-features", + "strip the wasm target features section", + createStripTargetFeaturesPass); + registerPass("trap-mode-clamp", + "replace trapping operations with clamping semantics", + createTrapModeClamp); + registerPass("trap-mode-js", + "replace trapping operations with js semantics", + createTrapModeJS); + registerPass("untee", + "removes local.tees, replacing them with sets and gets", + createUnteePass); registerPass("vacuum", "removes obviously unneeded code", createVacuumPass); -// registerPass("lower-i64", "lowers i64 into pairs of i32s", createLowerInt64Pass); + // registerPass( + // "lower-i64", "lowers i64 into pairs of i32s", createLowerInt64Pass); } void PassRunner::addDefaultOptimizationPasses() { @@ -191,10 +337,13 @@ void PassRunner::addDefaultFunctionOptimizationPasses() { if (options.optimizeLevel >= 2 || options.shrinkLevel >= 2) { add("code-pushing"); } - add("simplify-locals-nostructure"); // don't create if/block return values yet, as coalesce can remove copies that that could inhibit + // don't create if/block return values yet, as coalesce can remove copies that + // that could inhibit + add("simplify-locals-nostructure"); add("vacuum"); // previous pass creates garbage add("reorder-locals"); - add("remove-unused-brs"); // simplify-locals opens opportunities for optimizations + // simplify-locals opens opportunities for optimizations + add("remove-unused-brs"); // if we are willing to work hard, also optimize copies before coalescing if (options.optimizeLevel >= 3 || options.shrinkLevel >= 2) { add("merge-locals"); // very slow on e.g. sqlite @@ -209,10 +358,10 @@ void PassRunner::addDefaultFunctionOptimizationPasses() { if (options.optimizeLevel >= 3 || options.shrinkLevel >= 1) { add("code-folding"); } - add("merge-blocks"); // makes remove-unused-brs more effective - add("remove-unused-brs"); // coalesce-locals opens opportunities + add("merge-blocks"); // makes remove-unused-brs more effective + add("remove-unused-brs"); // coalesce-locals opens opportunities add("remove-unused-names"); // remove-unused-brs opens opportunities - add("merge-blocks"); // clean up remove-unused-brs new blocks + add("merge-blocks"); // clean up remove-unused-brs new blocks // late propagation if (options.optimizeLevel >= 3 || options.shrinkLevel >= 2) { add("precompute-propagate"); @@ -237,10 +386,12 @@ void PassRunner::addDefaultGlobalOptimizationPostPasses() { if (options.optimizeLevel >= 2 || options.shrinkLevel >= 2) { add("inlining-optimizing"); } - add("duplicate-function-elimination"); // optimizations show more functions as duplicate + // optimizations show more functions as duplicate + add("duplicate-function-elimination"); add("remove-unused-module-elements"); add("memory-packing"); - add("directize"); // may allow more inlining/dae/etc., need --converge for that + // may allow more inlining/dae/etc., need --converge for that + add("directize"); // perform Stack IR optimizations here, at the very end of the // optimization pipeline if (options.optimizeLevel >= 2 || options.shrinkLevel >= 1) { @@ -266,7 +417,8 @@ static void dumpWast(Name name, Module* wasm) { void PassRunner::run() { static const int passDebug = getPassDebug(); if (!isNested && (options.debug || passDebug)) { - // for debug logging purposes, run each pass in full before running the other + // for debug logging purposes, run each pass in full before running the + // other auto totalTime = std::chrono::duration<double>(0); size_t padding = 0; WasmValidator::Flags validationFlags = WasmValidator::Minimal; @@ -281,7 +433,8 @@ void PassRunner::run() { dumpWast("before", wasm); } for (auto* pass : passes) { - // ignoring the time, save a printout of the module before, in case this pass breaks it, so we can print the before and after + // ignoring the time, save a printout of the module before, in case this + // pass breaks it, so we can print the before and after std::stringstream moduleBefore; if (passDebug == 2) { WasmPrinter::printModule(wasm, moduleBefore); @@ -294,9 +447,8 @@ void PassRunner::run() { auto before = std::chrono::steady_clock::now(); if (pass->isFunctionParallel()) { // function-parallel passes should get a new instance per function - ModuleUtils::iterDefinedFunctions(*wasm, [&](Function* func) { - runPassOnFunction(pass, func); - }); + ModuleUtils::iterDefinedFunctions( + *wasm, [&](Function* func) { runPassOnFunction(pass, func); }); } else { runPass(pass); } @@ -310,9 +462,14 @@ void PassRunner::run() { if (!WasmValidator().validate(*wasm, validationFlags)) { WasmPrinter::printModule(wasm); if (passDebug >= 2) { - std::cerr << "Last pass (" << pass->name << ") broke validation. Here is the module before: \n" << moduleBefore.str() << "\n"; + std::cerr << "Last pass (" << pass->name + << ") broke validation. Here is the module before: \n" + << moduleBefore.str() << "\n"; } else { - std::cerr << "Last pass (" << pass->name << ") broke validation. Run with BINARYEN_PASS_DEBUG=2 in the env to see the earlier state, or 3 to dump byn-* files for each pass\n"; + std::cerr << "Last pass (" << pass->name + << ") broke validation. Run with BINARYEN_PASS_DEBUG=2 " + "in the env to see the earlier state, or 3 to dump " + "byn-* files for each pass\n"; } abort(); } @@ -321,7 +478,8 @@ void PassRunner::run() { dumpWast(pass->name, wasm); } } - std::cerr << "[PassRunner] passes took " << totalTime.count() << " seconds." << std::endl; + std::cerr << "[PassRunner] passes took " << totalTime.count() << " seconds." + << std::endl; if (options.validate) { std::cerr << "[PassRunner] (final validation)\n"; if (!WasmValidator().validate(*wasm, validationFlags)) { @@ -331,14 +489,15 @@ void PassRunner::run() { } } } else { - // non-debug normal mode, run them in an optimal manner - for locality it is better - // to run as many passes as possible on a single function before moving to the next + // non-debug normal mode, run them in an optimal manner - for locality it is + // better to run as many passes as possible on a single function before + // moving to the next std::vector<Pass*> stack; auto flush = [&]() { if (stack.size() > 0) { // run the stack of passes on all the functions, in parallel size_t num = ThreadPool::get()->size(); - std::vector<std::function<ThreadWorkState ()>> doWorkers; + std::vector<std::function<ThreadWorkState()>> doWorkers; std::atomic<size_t> nextFunction; nextFunction.store(0); size_t numFunctions = wasm->functions.size(); @@ -380,7 +539,8 @@ void PassRunner::run() { void PassRunner::runOnFunction(Function* func) { if (options.debug) { - std::cerr << "[PassRunner] running passes on function " << func->name << std::endl; + std::cerr << "[PassRunner] running passes on function " << func->name + << std::endl; } for (auto* pass : passes) { runPassOnFunction(pass, func); @@ -425,14 +585,18 @@ struct AfterEffectFunctionChecker { if (beganWithStackIR && func->stackIR) { auto after = FunctionHasher::hashFunction(func); if (after != originalFunctionHash) { - Fatal() << "[PassRunner] PASS_DEBUG check failed: had Stack IR before and after the pass ran, and the pass modified the main IR, which invalidates Stack IR - pass should have been marked 'modifiesBinaryenIR'"; + Fatal() << "[PassRunner] PASS_DEBUG check failed: had Stack IR before " + "and after the pass ran, and the pass modified the main IR, " + "which invalidates Stack IR - pass should have been marked " + "'modifiesBinaryenIR'"; } } } }; // Runs checks on the entire module, in a non-function-parallel pass. -// In particular, in such a pass functions may be removed or renamed, track that. +// In particular, in such a pass functions may be removed or renamed, track +// that. struct AfterEffectModuleChecker { Module* module; @@ -473,7 +637,9 @@ struct AfterEffectModuleChecker { } void error() { - Fatal() << "[PassRunner] PASS_DEBUG check failed: had Stack IR before and after the pass ran, and the pass modified global function state - pass should have been marked 'modifiesBinaryenIR'"; + Fatal() << "[PassRunner] PASS_DEBUG check failed: had Stack IR before and " + "after the pass ran, and the pass modified global function " + "state - pass should have been marked 'modifiesBinaryenIR'"; } bool hasAnyStackIR() { @@ -530,7 +696,8 @@ void PassRunner::handleAfterEffects(Pass* pass, Function* func) { } int PassRunner::getPassDebug() { - static const int passDebug = getenv("BINARYEN_PASS_DEBUG") ? atoi(getenv("BINARYEN_PASS_DEBUG")) : 0; + static const int passDebug = + getenv("BINARYEN_PASS_DEBUG") ? atoi(getenv("BINARYEN_PASS_DEBUG")) : 0; return passDebug; } diff --git a/src/passes/passes.h b/src/passes/passes.h index af9141ac9..fc01c1cd5 100644 --- a/src/passes/passes.h +++ b/src/passes/passes.h @@ -102,6 +102,6 @@ Pass* createTrapModeJS(); Pass* createUnteePass(); Pass* createVacuumPass(); -} +} // namespace wasm #endif diff --git a/src/pretty_printing.h b/src/pretty_printing.h index c5d755dcc..5e41a4d2d 100644 --- a/src/pretty_printing.h +++ b/src/pretty_printing.h @@ -25,36 +25,36 @@ #include "support/colors.h" -inline std::ostream &doIndent(std::ostream &o, unsigned indent) { +inline std::ostream& doIndent(std::ostream& o, unsigned indent) { for (unsigned i = 0; i < indent; i++) { o << " "; } return o; } -inline std::ostream &prepareMajorColor(std::ostream &o) { +inline std::ostream& prepareMajorColor(std::ostream& o) { Colors::red(o); Colors::bold(o); return o; } -inline std::ostream &prepareColor(std::ostream &o) { +inline std::ostream& prepareColor(std::ostream& o) { Colors::magenta(o); Colors::bold(o); return o; } -inline std::ostream &prepareMinorColor(std::ostream &o) { +inline std::ostream& prepareMinorColor(std::ostream& o) { Colors::orange(o); return o; } -inline std::ostream &restoreNormalColor(std::ostream &o) { +inline std::ostream& restoreNormalColor(std::ostream& o) { Colors::normal(o); return o; } -inline std::ostream& printText(std::ostream &o, const char *str) { +inline std::ostream& printText(std::ostream& o, const char* str) { o << '"'; Colors::green(o); o << str; @@ -62,21 +62,23 @@ inline std::ostream& printText(std::ostream &o, const char *str) { return o << '"'; } -inline std::ostream& printMajor(std::ostream &o, const char *str, bool major=false) { +inline std::ostream& +printMajor(std::ostream& o, const char* str, bool major = false) { prepareMajorColor(o); o << str; restoreNormalColor(o); return o; } -inline std::ostream& printMedium(std::ostream &o, const char *str, bool major=false) { +inline std::ostream& +printMedium(std::ostream& o, const char* str, bool major = false) { prepareColor(o); o << str; restoreNormalColor(o); return o; } -inline std::ostream& printMinor(std::ostream &o, const char *str) { +inline std::ostream& printMinor(std::ostream& o, const char* str) { prepareMinorColor(o); o << str; restoreNormalColor(o); diff --git a/src/shared-constants.h b/src/shared-constants.h index dbbc25e02..2ce2ab81c 100644 --- a/src/shared-constants.h +++ b/src/shared-constants.h @@ -21,46 +21,46 @@ namespace wasm { -extern Name GROW_WASM_MEMORY, - WASM_CALL_CTORS, - MEMORY_BASE, - TABLE_BASE, - GET_TEMP_RET0, - SET_TEMP_RET0, - NEW_SIZE, - MODULE, - START, - FUNC, - PARAM, - RESULT, - MEMORY, - DATA, - PASSIVE, - EXPORT, - IMPORT, - TABLE, - ELEM, - LOCAL, - TYPE, - CALL, - CALL_IMPORT, - CALL_INDIRECT, - BLOCK, - BR_IF, - THEN, - ELSE, - _NAN, - _INFINITY, - NEG_INFINITY, - NEG_NAN, - CASE, - BR, - FUNCREF, - FAKE_RETURN, - MUT, - SPECTEST, - PRINT, - EXIT; +extern Name GROW_WASM_MEMORY; +extern Name WASM_CALL_CTORS; +extern Name MEMORY_BASE; +extern Name TABLE_BASE; +extern Name GET_TEMP_RET0; +extern Name SET_TEMP_RET0; +extern Name NEW_SIZE; +extern Name MODULE; +extern Name START; +extern Name FUNC; +extern Name PARAM; +extern Name RESULT; +extern Name MEMORY; +extern Name DATA; +extern Name PASSIVE; +extern Name EXPORT; +extern Name IMPORT; +extern Name TABLE; +extern Name ELEM; +extern Name LOCAL; +extern Name TYPE; +extern Name CALL; +extern Name CALL_IMPORT; +extern Name CALL_INDIRECT; +extern Name BLOCK; +extern Name BR_IF; +extern Name THEN; +extern Name ELSE; +extern Name _NAN; +extern Name _INFINITY; +extern Name NEG_INFINITY; +extern Name NEG_NAN; +extern Name CASE; +extern Name BR; +extern Name FUNCREF; +extern Name FAKE_RETURN; +extern Name MUT; +extern Name SPECTEST; +extern Name PRINT; +extern Name EXIT; } // namespace wasm diff --git a/src/shell-interface.h b/src/shell-interface.h index 4cb5f5349..1bbe97dd9 100644 --- a/src/shell-interface.h +++ b/src/shell-interface.h @@ -21,12 +21,12 @@ #ifndef wasm_shell_interface_h #define wasm_shell_interface_h -#include "shared-constants.h" #include "asmjs/shared-constants.h" +#include "ir/module-utils.h" +#include "shared-constants.h" #include "support/name.h" -#include "wasm.h" #include "wasm-interpreter.h" -#include "ir/module-utils.h" +#include "wasm.h" namespace wasm { @@ -44,15 +44,14 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { class Memory { // Use char because it doesn't run afoul of aliasing rules. std::vector<char> memory; - template<typename T> - static bool aligned(const char* address) { + template<typename T> static bool aligned(const char* address) { static_assert(!(sizeof(T) & (sizeof(T) - 1)), "must be a power of 2"); return 0 == (reinterpret_cast<uintptr_t>(address) & (sizeof(T) - 1)); } Memory(Memory&) = delete; Memory& operator=(const Memory&) = delete; - public: + public: Memory() = default; void resize(size_t newSize) { // Ensure the smallest allocation is large enough that most allocators @@ -68,16 +67,14 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { std::memset(&memory[newSize], 0, minSize - newSize); } } - template<typename T> - void set(size_t address, T value) { + template<typename T> void set(size_t address, T value) { if (aligned<T>(&memory[address])) { *reinterpret_cast<T*>(&memory[address]) = value; } else { std::memcpy(&memory[address], &value, sizeof(T)); } } - template<typename T> - T get(size_t address) { + template<typename T> T get(size_t address) { if (aligned<T>(&memory[address])) { return *reinterpret_cast<T*>(&memory[address]); } else { @@ -103,17 +100,28 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) { if (import->module == SPECTEST && import->base == GLOBAL) { switch (import->type) { - case i32: globals[import->name] = Literal(int32_t(666)); break; - case i64: globals[import->name] = Literal(int64_t(666)); break; - case f32: globals[import->name] = Literal(float(666.6)); break; - case f64: globals[import->name] = Literal(double(666.6)); break; - case v128: assert(false && "v128 not implemented yet"); + case i32: + globals[import->name] = Literal(int32_t(666)); + break; + case i64: + globals[import->name] = Literal(int64_t(666)); + break; + case f32: + globals[import->name] = Literal(float(666.6)); + break; + case f64: + globals[import->name] = Literal(double(666.6)); + break; + case v128: + assert(false && "v128 not implemented yet"); case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } } }); - if (wasm.memory.imported() && wasm.memory.module == SPECTEST && wasm.memory.base == MEMORY) { + if (wasm.memory.imported() && wasm.memory.module == SPECTEST && + wasm.memory.base == MEMORY) { // imported memory has initial 1 and max 2 wasm.memory.initial = 1; wasm.memory.max = 2; @@ -135,11 +143,17 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { << import->name.str; } - Literal callTable(Index index, LiteralList& arguments, Type result, ModuleInstance& instance) override { - if (index >= table.size()) trap("callTable overflow"); + Literal callTable(Index index, + LiteralList& arguments, + Type result, + ModuleInstance& instance) override { + if (index >= table.size()) + trap("callTable overflow"); auto* func = instance.wasm.getFunctionOrNull(table[index]); - if (!func) trap("uninitialized table element"); - if (func->params.size() != arguments.size()) trap("callIndirect: bad # of arguments"); + if (!func) + trap("uninitialized table element"); + if (func->params.size() != arguments.size()) + trap("callIndirect: bad # of arguments"); for (size_t i = 0; i < func->params.size(); i++) { if (func->params[i] != arguments[i].type) { trap("callIndirect: bad argument type"); @@ -167,17 +181,23 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { return memory.get<std::array<uint8_t, 16>>(addr); } - void store8(Address addr, int8_t value) override { memory.set<int8_t>(addr, value); } - void store16(Address addr, int16_t value) override { memory.set<int16_t>(addr, value); } - void store32(Address addr, int32_t value) override { memory.set<int32_t>(addr, value); } - void store64(Address addr, int64_t value) override { memory.set<int64_t>(addr, value); } + void store8(Address addr, int8_t value) override { + memory.set<int8_t>(addr, value); + } + void store16(Address addr, int16_t value) override { + memory.set<int16_t>(addr, value); + } + void store32(Address addr, int32_t value) override { + memory.set<int32_t>(addr, value); + } + void store64(Address addr, int64_t value) override { + memory.set<int64_t>(addr, value); + } void store128(Address addr, const std::array<uint8_t, 16>& value) override { memory.set<std::array<uint8_t, 16>>(addr, value); } - void tableStore(Address addr, Name entry) override { - table[addr] = entry; - } + void tableStore(Address addr, Name entry) override { table[addr] = entry; } void growMemory(Address /*oldSize*/, Address newSize) override { memory.resize(newSize); @@ -189,6 +209,6 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { } }; -} +} // namespace wasm #endif // wasm_shell_interface_h diff --git a/src/support/alloc.h b/src/support/alloc.h index 075896694..d85cbb0f7 100644 --- a/src/support/alloc.h +++ b/src/support/alloc.h @@ -35,10 +35,11 @@ inline void* aligned_malloc(size_t align, size_t size) { #if defined(WIN32) || defined(_WIN32) _set_errno(0); void* ret = _aligned_malloc(size, align); - if (errno == ENOMEM) ret = nullptr; + if (errno == ENOMEM) + ret = nullptr; return ret; #elif defined(__APPLE__) - void *ptr; + void* ptr; int result = posix_memalign(&ptr, align, size); return result == 0 ? ptr : nullptr; #else @@ -56,4 +57,4 @@ inline void aligned_free(void* ptr) { } // namespace wasm -#endif // wasm_support_alloc_h +#endif // wasm_support_alloc_h diff --git a/src/support/archive.cpp b/src/support/archive.cpp index b9ca827e4..66f9c192a 100644 --- a/src/support/archive.cpp +++ b/src/support/archive.cpp @@ -16,19 +16,19 @@ #include "support/archive.h" -#include <cstring> #include "support/utilities.h" +#include <cstring> static const char* const magic = "!<arch>\n"; class ArchiveMemberHeader { - public: +public: uint8_t fileName[16]; uint8_t timestamp[12]; uint8_t UID[6]; uint8_t GID[6]; uint8_t accessMode[8]; - uint8_t size[10]; // Size of data only, not including padding or header + uint8_t size[10]; // Size of data only, not including padding or header uint8_t magic[2]; std::string getName() const; @@ -42,11 +42,12 @@ std::string ArchiveMemberHeader::getName() const { // Special name (string table or reference, or symbol table) endChar = ' '; } else { - endChar = '/'; // regular name + endChar = '/'; // regular name } auto* end = - static_cast<const uint8_t*>(memchr(fileName, endChar, sizeof(fileName))); - if (!end) end = fileName + sizeof(fileName); + static_cast<const uint8_t*>(memchr(fileName, endChar, sizeof(fileName))); + if (!end) + end = fileName + sizeof(fileName); return std::string((char*)(fileName), end - fileName); } @@ -60,7 +61,9 @@ uint32_t ArchiveMemberHeader::getSize() const { return static_cast<uint32_t>(sizeInt); } -Archive::Archive(Buffer& b, bool& error) : data(b), symbolTable({nullptr, 0}), stringTable({nullptr, 0}), firstRegularData(nullptr) { +Archive::Archive(Buffer& b, bool& error) + : data(b), symbolTable({nullptr, 0}), stringTable({nullptr, 0}), + firstRegularData(nullptr) { error = false; if (data.size() < strlen(magic) || memcmp(data.data(), magic, strlen(magic))) { @@ -78,14 +81,16 @@ Archive::Archive(Buffer& b, bool& error) : data(b), symbolTable({nullptr, 0}), s return; } child_iterator end = child_end(); - if (it == end) return; // Empty archive. + if (it == end) + return; // Empty archive. const Child* c = &*it; auto increment = [&]() { ++it; error = it.hasError(); - if (error) return true; + if (error) + return true; c = &*it; return false; }; @@ -93,13 +98,15 @@ Archive::Archive(Buffer& b, bool& error) : data(b), symbolTable({nullptr, 0}), s std::string name = c->getRawName(); if (name == "/") { symbolTable = c->getBuffer(); - if (increment() || it == end) return; + if (increment() || it == end) + return; name = c->getRawName(); } if (name == "//") { stringTable = c->getBuffer(); - if (increment() || it == end) return; + if (increment() || it == end) + return; setFirstRegular(*c); return; } @@ -112,8 +119,9 @@ Archive::Archive(Buffer& b, bool& error) : data(b), symbolTable({nullptr, 0}), s } Archive::Child::Child(const Archive* parent, const uint8_t* data, bool* error) - : parent(parent), data(data) { - if (!data) return; + : parent(parent), data(data) { + if (!data) + return; len = sizeof(ArchiveMemberHeader) + getHeader()->getSize(); startOfFile = sizeof(ArchiveMemberHeader); } @@ -129,8 +137,10 @@ std::string Archive::Child::getRawName() const { } Archive::Child Archive::Child::getNext(bool& error) const { - uint32_t nextOffset = len + (len & 1); // Members are aligned to even byte boundaries. - if ((size_t)(data - (const uint8_t*)parent->data.data() + nextOffset) >= parent->data.size()) { // End of the archive. + // Members are aligned to even byte boundaries. + uint32_t nextOffset = len + (len & 1); + if ((size_t)(data - (const uint8_t*)parent->data.data() + nextOffset) >= + parent->data.size()) { // End of the archive. return Child(); } return Child(parent, data + nextOffset, &error); @@ -140,10 +150,10 @@ std::string Archive::Child::getName() const { std::string name = getRawName(); // Check if it's a special name. if (name[0] == '/') { - if (name.size() == 1) { // Linker member. + if (name.size() == 1) { // Linker member. return name; } - if (name.size() == 2 && name[1] == '/') { // String table. + if (name.size() == 2 && name[1] == '/') { // String table. return name; } // It's a long name. @@ -170,7 +180,8 @@ std::string Archive::Child::getName() const { } Archive::child_iterator Archive::child_begin(bool SkipInternal) const { - if (data.size() == 0) return child_end(); + if (data.size() == 0) + return child_end(); if (SkipInternal) { child_iterator it; @@ -197,7 +208,7 @@ struct Symbol { ++symbolIndex; } }; -} +} // namespace static uint32_t read32be(const uint8_t* buf) { return static_cast<uint32_t>(buf[0]) << 24 | @@ -206,15 +217,20 @@ static uint32_t read32be(const uint8_t* buf) { } void Archive::dump() const { - printf("Archive data %p len %zu, firstRegularData %p\n", data.data(), - data.size(), firstRegularData); + printf("Archive data %p len %zu, firstRegularData %p\n", + data.data(), + data.size(), + firstRegularData); printf("Symbol table %p, len %u\n", symbolTable.data, symbolTable.len); printf("string table %p, len %u\n", stringTable.data, stringTable.len); const uint8_t* buf = symbolTable.data; if (!buf) { for (auto c = child_begin(), e = child_end(); c != e; ++c) { - printf("Child %p, len %u, name %s, size %u\n", c->data, c->len, - c->getName().c_str(), c->getSize()); + printf("Child %p, len %u, name %s, size %u\n", + c->data, + c->len, + c->getName().c_str(), + c->getSize()); } return; } diff --git a/src/support/archive.h b/src/support/archive.h index 0ca066ff1..cba7c4fc1 100644 --- a/src/support/archive.h +++ b/src/support/archive.h @@ -38,7 +38,7 @@ class Archive { // because most things in these buffers are not nul-terminated using Buffer = std::vector<char>; - public: +public: struct SubBuffer { const uint8_t* data; uint32_t len; @@ -56,7 +56,7 @@ class Archive { } Child getNext(bool& error) const; - public: + public: Child(){}; Child(const Archive* parent, const uint8_t* data, bool* error); // Size of actual member data (no header/padding) @@ -69,8 +69,8 @@ class Archive { class child_iterator { friend class Archive; Child child; - bool error = false; // TODO: use std::error_code instead? - public: + bool error = false; // TODO: use std::error_code instead? + public: child_iterator() = default; explicit child_iterator(bool error) : error(error) {} child_iterator(const Child& c) : child(c) {} @@ -94,7 +94,7 @@ class Archive { child_iterator child_end() const; void dump() const; - private: +private: void setFirstRegular(const Child& c) { firstRegularData = c.data; } Buffer& data; SubBuffer symbolTable; @@ -102,4 +102,4 @@ class Archive { const uint8_t* firstRegularData; }; -#endif // wasm_support_archive_h +#endif // wasm_support_archive_h diff --git a/src/support/base64.h b/src/support/base64.h index 0c87c37c1..8634246a6 100644 --- a/src/support/base64.h +++ b/src/support/base64.h @@ -21,20 +21,18 @@ #include <string> #include <vector> -inline std::string base64Encode(std::vector<char> &data) { +inline std::string base64Encode(std::vector<char>& data) { std::string ret; size_t i = 0; - const char* alphabet = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; + const char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; while (i + 3 <= data.size()) { - uint32_t bits = - (((uint32_t)(uint8_t) data[i + 0]) << 16) | - (((uint32_t)(uint8_t) data[i + 1]) << 8) | - (((uint32_t)(uint8_t) data[i + 2]) << 0); + uint32_t bits = (((uint32_t)(uint8_t)data[i + 0]) << 16) | + (((uint32_t)(uint8_t)data[i + 1]) << 8) | + (((uint32_t)(uint8_t)data[i + 2]) << 0); ret += alphabet[(bits >> 18) & 0x3f]; ret += alphabet[(bits >> 12) & 0x3f]; ret += alphabet[(bits >> 6) & 0x3f]; @@ -43,15 +41,14 @@ inline std::string base64Encode(std::vector<char> &data) { } if (i + 2 == data.size()) { - uint32_t bits = - (((uint32_t)(uint8_t) data[i + 0]) << 8) | - (((uint32_t)(uint8_t) data[i + 1]) << 0); + uint32_t bits = (((uint32_t)(uint8_t)data[i + 0]) << 8) | + (((uint32_t)(uint8_t)data[i + 1]) << 0); ret += alphabet[(bits >> 10) & 0x3f]; ret += alphabet[(bits >> 4) & 0x3f]; ret += alphabet[(bits << 2) & 0x3f]; ret += '='; } else if (i + 1 == data.size()) { - uint32_t bits = (uint32_t)(uint8_t) data[i + 0]; + uint32_t bits = (uint32_t)(uint8_t)data[i + 0]; ret += alphabet[(bits >> 2) & 0x3f]; ret += alphabet[(bits << 4) & 0x3f]; ret += '='; diff --git a/src/support/bits.cpp b/src/support/bits.cpp index 8f28a7fdb..c237807ee 100644 --- a/src/support/bits.cpp +++ b/src/support/bits.cpp @@ -15,28 +15,24 @@ */ #define wasm_support_bits_definitions -#include "../compiler-support.h" #include "support/bits.h" +#include "../compiler-support.h" namespace wasm { -template<> -int PopCount<uint8_t>(uint8_t v) { +template<> int PopCount<uint8_t>(uint8_t v) { // Small table lookup. - static const uint8_t tbl[32] = { - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5 - }; + static const uint8_t tbl[32] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, + 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, + 3, 4, 2, 3, 3, 4, 3, 4, 4, 5}; return tbl[v & 0xf] + tbl[v >> 4]; } -template<> -int PopCount<uint16_t>(uint16_t v) { +template<> int PopCount<uint16_t>(uint16_t v) { return PopCount((uint8_t)(v & 0xff)) + PopCount((uint8_t)(v >> 8)); } -template<> -int PopCount<uint32_t>(uint32_t v) { +template<> int PopCount<uint32_t>(uint32_t v) { // See Stanford bithacks, counting bits set in parallel, "best method": // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel v = v - ((v >> 1) & 0x55555555); @@ -44,13 +40,11 @@ int PopCount<uint32_t>(uint32_t v) { return (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; } -template<> -int PopCount<uint64_t>(uint64_t v) { +template<> int PopCount<uint64_t>(uint64_t v) { return PopCount((uint32_t)v) + PopCount((uint32_t)(v >> 32)); } -template<> -uint32_t BitReverse<uint32_t>(uint32_t v) { +template<> uint32_t BitReverse<uint32_t>(uint32_t v) { // See Hacker's Delight, first edition, figure 7-1. v = ((v & 0x55555555) << 1) | ((v >> 1) & 0x55555555); v = ((v & 0x33333333) << 2) | ((v >> 2) & 0x33333333); @@ -59,33 +53,28 @@ uint32_t BitReverse<uint32_t>(uint32_t v) { return v; } -template<> -int CountTrailingZeroes<uint32_t>(uint32_t v) { +template<> int CountTrailingZeroes<uint32_t>(uint32_t v) { // See Stanford bithacks, count the consecutive zero bits (trailing) on the // right with multiply and lookup: // http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup - static const uint8_t tbl[32] = { - 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, - 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 - }; + static const uint8_t tbl[32] = {0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, + 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, + 16, 7, 26, 12, 18, 6, 11, 5, 10, 9}; return v ? (int)tbl[((uint32_t)((v & -v) * 0x077CB531U)) >> 27] : 32; } -template<> -int CountTrailingZeroes<uint64_t>(uint64_t v) { +template<> int CountTrailingZeroes<uint64_t>(uint64_t v) { return (uint32_t)v ? CountTrailingZeroes((uint32_t)v) : 32 + CountTrailingZeroes((uint32_t)(v >> 32)); } -template<> -int CountLeadingZeroes<uint32_t>(uint32_t v) { +template<> int CountLeadingZeroes<uint32_t>(uint32_t v) { // See Stanford bithacks, find the log base 2 of an N-bit integer in // O(lg(N)) operations with multiply and lookup: // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn - static const uint8_t tbl[32] = { - 31, 22, 30, 21, 18, 10, 29, 2, 20, 17, 15, 13, 9, 6, 28, 1, - 23, 19, 11, 3, 16, 14, 7, 24, 12, 4, 8, 25, 5, 26, 27, 0 - }; + static const uint8_t tbl[32] = {31, 22, 30, 21, 18, 10, 29, 2, 20, 17, 15, + 13, 9, 6, 28, 1, 23, 19, 11, 3, 16, 14, + 7, 24, 12, 4, 8, 25, 5, 26, 27, 0}; v = v | (v >> 1); v = v | (v >> 2); v = v | (v >> 4); @@ -94,33 +83,46 @@ int CountLeadingZeroes<uint32_t>(uint32_t v) { return v ? (int)tbl[((uint32_t)(v * 0x07C4ACDDU)) >> 27] : 32; } -template<> -int CountLeadingZeroes<uint64_t>(uint64_t v) { +template<> int CountLeadingZeroes<uint64_t>(uint64_t v) { return v >> 32 ? CountLeadingZeroes((uint32_t)(v >> 32)) : 32 + CountLeadingZeroes((uint32_t)v); } uint32_t Log2(uint32_t v) { switch (v) { - default: WASM_UNREACHABLE(); - case 1: return 0; - case 2: return 1; - case 4: return 2; - case 8: return 3; - case 16: return 4; - case 32: return 5; + default: + WASM_UNREACHABLE(); + case 1: + return 0; + case 2: + return 1; + case 4: + return 2; + case 8: + return 3; + case 16: + return 4; + case 32: + return 5; } } uint32_t Pow2(uint32_t v) { switch (v) { - case 0: return 1; - case 1: return 2; - case 2: return 4; - case 3: return 8; - case 4: return 16; - case 5: return 32; - default: return 1 << v; + case 0: + return 1; + case 1: + return 2; + case 2: + return 4; + case 3: + return 8; + case 4: + return 16; + case 5: + return 32; + default: + return 1 << v; } } diff --git a/src/support/bits.h b/src/support/bits.h index f1dc4364f..f2241bea8 100644 --- a/src/support/bits.h +++ b/src/support/bits.h @@ -56,31 +56,23 @@ extern template int CountLeadingZeroes(uint64_t); // Convenience signed -> unsigned. It usually doesn't make much sense to use bit // functions on signed types. -template<typename T> -int PopCount(T v) { +template<typename T> int PopCount(T v) { return PopCount(typename std::make_unsigned<T>::type(v)); } -template<typename T> -int CountTrailingZeroes(T v) { +template<typename T> int CountTrailingZeroes(T v) { return CountTrailingZeroes(typename std::make_unsigned<T>::type(v)); } -template<typename T> -int CountLeadingZeroes(T v) { +template<typename T> int CountLeadingZeroes(T v) { return CountLeadingZeroes(typename std::make_unsigned<T>::type(v)); } -template<typename T> -bool IsPowerOf2(T v) { - return v != 0 && PopCount(v) == 1; -} +template<typename T> bool IsPowerOf2(T v) { return v != 0 && PopCount(v) == 1; } -template<typename T, typename U> -inline static T RotateLeft(T val, U count) { +template<typename T, typename U> inline static T RotateLeft(T val, U count) { T mask = sizeof(T) * CHAR_BIT - 1; count &= mask; return (val << count) | (val >> (-count & mask)); } -template<typename T, typename U> -inline static T RotateRight(T val, U count) { +template<typename T, typename U> inline static T RotateRight(T val, U count) { T mask = sizeof(T) * CHAR_BIT - 1; count &= mask; return (val >> count) | (val << (-count & mask)); @@ -89,6 +81,6 @@ inline static T RotateRight(T val, U count) { extern uint32_t Log2(uint32_t v); extern uint32_t Pow2(uint32_t v); -} // namespace wasm +} // namespace wasm -#endif // wasm_support_bits_h +#endif // wasm_support_bits_h diff --git a/src/support/colors.cpp b/src/support/colors.cpp index 7d78306e0..d0dc5ceb3 100644 --- a/src/support/colors.cpp +++ b/src/support/colors.cpp @@ -21,7 +21,7 @@ namespace { bool colors_disabled = false; -} // anonymous namespace +} // anonymous namespace void Colors::disable() { colors_disabled = true; } @@ -30,25 +30,27 @@ void Colors::disable() { colors_disabled = true; } void Colors::outputColorCode(std::ostream& stream, const char* colorCode) { const static bool has_color = []() { - return (getenv("COLORS") && getenv("COLORS")[0] == '1') || // forced + return (getenv("COLORS") && getenv("COLORS")[0] == '1') || // forced (isatty(STDOUT_FILENO) && - (!getenv("COLORS") || getenv("COLORS")[0] != '0')); // implicit + (!getenv("COLORS") || getenv("COLORS")[0] != '0')); // implicit }(); - if (has_color && !colors_disabled) stream << colorCode; + if (has_color && !colors_disabled) + stream << colorCode; } #elif defined(_WIN32) -#include <windows.h> #include <io.h> #include <iostream> +#include <windows.h> -void Colors::outputColorCode(std::ostream&stream, const WORD &colorCode) { +void Colors::outputColorCode(std::ostream& stream, const WORD& colorCode) { const static bool has_color = []() { return _isatty(_fileno(stdout)) && - (!getenv("COLORS") || getenv("COLORS")[0] != '0'); // implicit + (!getenv("COLORS") || getenv("COLORS")[0] != '0'); // implicit }(); static HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); static HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE); if (has_color && !colors_disabled) - SetConsoleTextAttribute(&stream == &std::cout ? hStdout : hStderr, colorCode); + SetConsoleTextAttribute(&stream == &std::cout ? hStdout : hStderr, + colorCode); } #endif diff --git a/src/support/colors.h b/src/support/colors.h index 6761639d9..68a251969 100644 --- a/src/support/colors.h +++ b/src/support/colors.h @@ -23,17 +23,21 @@ namespace Colors { void disable(); #if defined(__linux__) || defined(__APPLE__) -void outputColorCode(std::ostream& stream, const char *colorCode); -inline void normal(std::ostream& stream) { outputColorCode(stream,"\033[0m"); } -inline void red(std::ostream& stream) { outputColorCode(stream,"\033[31m"); } -inline void magenta(std::ostream& stream) { outputColorCode(stream,"\033[35m"); } -inline void orange(std::ostream& stream) { outputColorCode(stream,"\033[33m"); } -inline void grey(std::ostream& stream) { outputColorCode(stream,"\033[37m"); } -inline void green(std::ostream& stream) { outputColorCode(stream,"\033[32m"); } -inline void blue(std::ostream& stream) { outputColorCode(stream,"\033[34m"); } -inline void bold(std::ostream& stream) { outputColorCode(stream,"\033[1m"); } +void outputColorCode(std::ostream& stream, const char* colorCode); +inline void normal(std::ostream& stream) { outputColorCode(stream, "\033[0m"); } +inline void red(std::ostream& stream) { outputColorCode(stream, "\033[31m"); } +inline void magenta(std::ostream& stream) { + outputColorCode(stream, "\033[35m"); +} +inline void orange(std::ostream& stream) { + outputColorCode(stream, "\033[33m"); +} +inline void grey(std::ostream& stream) { outputColorCode(stream, "\033[37m"); } +inline void green(std::ostream& stream) { outputColorCode(stream, "\033[32m"); } +inline void blue(std::ostream& stream) { outputColorCode(stream, "\033[34m"); } +inline void bold(std::ostream& stream) { outputColorCode(stream, "\033[1m"); } #elif defined(_WIN32) -void outputColorCode(std::ostream& stream, const unsigned short &colorCode); +void outputColorCode(std::ostream& stream, const unsigned short& colorCode); inline void normal(std::ostream& stream) { outputColorCode(stream, 0x07); } inline void red(std::ostream& stream) { outputColorCode(stream, 0x0c); } inline void magenta(std::ostream& stream) { outputColorCode(stream, 0x05); } @@ -41,7 +45,8 @@ inline void orange(std::ostream& stream) { outputColorCode(stream, 0x06); } inline void grey(std::ostream& stream) { outputColorCode(stream, 0x08); } inline void green(std::ostream& stream) { outputColorCode(stream, 0x02); } inline void blue(std::ostream& stream) { outputColorCode(stream, 0x09); } -inline void bold(std::ostream& stream) { /* Do nothing */ } +inline void bold(std::ostream& stream) { /* Do nothing */ +} #else inline void normal(std::ostream& stream) {} inline void red(std::ostream& stream) {} @@ -52,6 +57,6 @@ inline void green(std::ostream& stream) {} inline void blue(std::ostream& stream) {} inline void bold(std::ostream& stream) {} #endif -} +} // namespace Colors #endif // wasm_support_color_h diff --git a/src/support/command-line.cpp b/src/support/command-line.cpp index 6882dda7b..f3b9ffe25 100644 --- a/src/support/command-line.cpp +++ b/src/support/command-line.cpp @@ -37,7 +37,8 @@ void printWrap(std::ostream& os, int leftPad, const std::string& content) { } os << nextWord; space -= nextWord.size() + 1; - if (space > 0) os << ' '; + if (space > 0) + os << ' '; nextWord.clear(); if (content[i] == '\n') { os << '\n'; @@ -48,18 +49,22 @@ void printWrap(std::ostream& os, int leftPad, const std::string& content) { } Options::Options(const std::string& command, const std::string& description) - : debug(false), positional(Arguments::Zero) { - add("--help", "-h", "Show this help message and exit", Arguments::Zero, + : debug(false), positional(Arguments::Zero) { + add("--help", + "-h", + "Show this help message and exit", + Arguments::Zero, [this, command, description](Options* o, const std::string&) { std::cout << command; - if (positional != Arguments::Zero) std::cout << ' ' << positionalName; + if (positional != Arguments::Zero) + std::cout << ' ' << positionalName; std::cout << "\n\n"; printWrap(std::cout, 0, description); std::cout << "\n\nOptions:\n"; size_t optionWidth = 0; for (const auto& o : options) { optionWidth = - std::max(optionWidth, o.longName.size() + o.shortName.size()); + std::max(optionWidth, o.longName.size() + o.shortName.size()); } for (const auto& o : options) { bool long_n_short = o.longName.size() != 0 && o.shortName.size() != 0; @@ -72,20 +77,26 @@ Options::Options(const std::string& command, const std::string& description) std::cout << '\n'; exit(EXIT_SUCCESS); }); - add("--debug", "-d", "Print debug information to stderr", Arguments::Zero, + add("--debug", + "-d", + "Print debug information to stderr", + Arguments::Zero, [&](Options* o, const std::string& arguments) { debug = true; }); } Options::~Options() {} -Options& Options::add(const std::string& longName, const std::string& shortName, - const std::string& description, Arguments arguments, +Options& Options::add(const std::string& longName, + const std::string& shortName, + const std::string& description, + Arguments arguments, const Action& action) { options.push_back({longName, shortName, description, arguments, action, 0}); return *this; } -Options& Options::add_positional(const std::string& name, Arguments arguments, +Options& Options::add_positional(const std::string& name, + Arguments arguments, const Action& action) { positional = arguments; positionalName = name; @@ -98,7 +109,8 @@ void Options::parse(int argc, const char* argv[]) { size_t positionalsSeen = 0; auto dashes = [](const std::string& s) { for (size_t i = 0;; ++i) { - if (s[i] != '-') return i; + if (s[i] != '-') + return i; } }; for (size_t i = 1, e = argc; i != e; ++i) { @@ -169,7 +181,8 @@ void Options::parse(int argc, const char* argv[]) { break; case Arguments::Optional: if (!argument.size()) { - if (i + 1 != e) argument = argv[++i]; + if (i + 1 != e) + argument = argv[++i]; } break; } diff --git a/src/support/command-line.h b/src/support/command-line.h index de773bfa8..82193244e 100644 --- a/src/support/command-line.h +++ b/src/support/command-line.h @@ -29,12 +29,11 @@ #include "wasm.h" - namespace wasm { class Options { - public: - using Action = std::function<void(Options *, const std::string& )>; +public: + using Action = std::function<void(Options*, const std::string&)>; enum class Arguments { Zero, One, N, Optional }; bool debug; @@ -42,17 +41,20 @@ class Options { Options(const std::string& command, const std::string& description); ~Options(); - Options &add(const std::string& longName, const std::string& shortName, - const std::string& description, Arguments arguments, + Options& add(const std::string& longName, + const std::string& shortName, + const std::string& description, + Arguments arguments, const Action& action); - Options &add_positional(const std::string& name, Arguments arguments, - const Action &action); - void parse(int argc, const char *argv[]); + Options& add_positional(const std::string& name, + Arguments arguments, + const Action& action); + void parse(int argc, const char* argv[]); - private: +private: Options() = delete; - Options(const Options &) = delete; - Options &operator=(const Options &) = delete; + Options(const Options&) = delete; + Options& operator=(const Options&) = delete; struct Option { std::string longName; @@ -68,6 +70,6 @@ class Options { Action positionalAction; }; -} // namespace wasm +} // namespace wasm #endif // wasm_support_command_line_h diff --git a/src/support/file.cpp b/src/support/file.cpp index c10646720..3af7af44b 100644 --- a/src/support/file.cpp +++ b/src/support/file.cpp @@ -16,13 +16,14 @@ #include "support/file.h" -#include <iostream> -#include <cstdlib> #include <cstdint> +#include <cstdlib> +#include <iostream> #include <limits> std::vector<char> wasm::read_stdin(Flags::DebugOption debug) { - if (debug == Flags::Debug) std::cerr << "Loading stdin..." << std::endl; + if (debug == Flags::Debug) + std::cerr << "Loading stdin..." << std::endl; std::vector<char> input; char c; while (std::cin.get(c) && !std::cin.eof()) { @@ -31,13 +32,16 @@ std::vector<char> wasm::read_stdin(Flags::DebugOption debug) { return input; } - template<typename T> -T wasm::read_file(const std::string& filename, Flags::BinaryOption binary, Flags::DebugOption debug) { - if (debug == Flags::Debug) std::cerr << "Loading '" << filename << "'..." << std::endl; +T wasm::read_file(const std::string& filename, + Flags::BinaryOption binary, + Flags::DebugOption debug) { + if (debug == Flags::Debug) + std::cerr << "Loading '" << filename << "'..." << std::endl; std::ifstream infile; std::ios_base::openmode flags = std::ifstream::in; - if (binary == Flags::Binary) flags |= std::ifstream::binary; + if (binary == Flags::Binary) + flags |= std::ifstream::binary; infile.open(filename, flags); if (!infile.is_open()) { std::cerr << "Failed opening '" << filename << "'" << std::endl; @@ -46,47 +50,60 @@ T wasm::read_file(const std::string& filename, Flags::BinaryOption binary, Flags infile.seekg(0, std::ios::end); std::streampos insize = infile.tellg(); if (uint64_t(insize) >= std::numeric_limits<size_t>::max()) { - // Building a 32-bit executable where size_t == 32 bits, we are not able to create strings larger than 2^32 bytes in length, so must abort here. - std::cerr << "Failed opening '" << filename << "': Input file too large: " << insize << " bytes. Try rebuilding in 64-bit mode." << std::endl; + // Building a 32-bit executable where size_t == 32 bits, we are not able to + // create strings larger than 2^32 bytes in length, so must abort here. + std::cerr << "Failed opening '" << filename + << "': Input file too large: " << insize + << " bytes. Try rebuilding in 64-bit mode." << std::endl; exit(EXIT_FAILURE); } T input(size_t(insize) + (binary == Flags::Binary ? 0 : 1), '\0'); - if (size_t(insize) == 0) return input; + if (size_t(insize) == 0) + return input; infile.seekg(0); infile.read(&input[0], insize); if (binary == Flags::Text) { size_t chars = size_t(infile.gcount()); - input.resize(chars+1); // Truncate size to the number of ASCII characters actually read in text mode (which is generally less than the number of bytes on Windows, if \r\n line endings are present) + // Truncate size to the number of ASCII characters actually read in text + // mode (which is generally less than the number of bytes on Windows, if + // \r\n line endings are present) + input.resize(chars + 1); input[chars] = '\0'; } return input; } // Explicit instantiations for the explicit specializations. -template std::string wasm::read_file<>(const std::string& , Flags::BinaryOption, Flags::DebugOption); -template std::vector<char> wasm::read_file<>(const std::string& , Flags::BinaryOption, Flags::DebugOption); +template std::string +wasm::read_file<>(const std::string&, Flags::BinaryOption, Flags::DebugOption); +template std::vector<char> +wasm::read_file<>(const std::string&, Flags::BinaryOption, Flags::DebugOption); -wasm::Output::Output(const std::string& filename, Flags::BinaryOption binary, Flags::DebugOption debug) - : outfile(), out([this, filename, binary, debug]() { - if (filename == "-") { - return std::cout.rdbuf(); - } - std::streambuf *buffer; - if (filename.size()) { - if (debug == Flags::Debug) std::cerr << "Opening '" << filename << "'" << std::endl; - auto flags = std::ofstream::out | std::ofstream::trunc; - if (binary == Flags::Binary) flags |= std::ofstream::binary; - outfile.open(filename, flags); - if (!outfile.is_open()) { - std::cerr << "Failed opening '" << filename << "'" << std::endl; - exit(EXIT_FAILURE); - } - buffer = outfile.rdbuf(); - } else { - buffer = std::cout.rdbuf(); +wasm::Output::Output(const std::string& filename, + Flags::BinaryOption binary, + Flags::DebugOption debug) + : outfile(), out([this, filename, binary, debug]() { + if (filename == "-") { + return std::cout.rdbuf(); + } + std::streambuf* buffer; + if (filename.size()) { + if (debug == Flags::Debug) + std::cerr << "Opening '" << filename << "'" << std::endl; + auto flags = std::ofstream::out | std::ofstream::trunc; + if (binary == Flags::Binary) + flags |= std::ofstream::binary; + outfile.open(filename, flags); + if (!outfile.is_open()) { + std::cerr << "Failed opening '" << filename << "'" << std::endl; + exit(EXIT_FAILURE); } - return buffer; - }()) {} + buffer = outfile.rdbuf(); + } else { + buffer = std::cout.rdbuf(); + } + return buffer; + }()) {} void wasm::copy_file(std::string input, std::string output) { std::ifstream src(input, std::ios::binary); diff --git a/src/support/file.h b/src/support/file.h index cb9c82ca9..67d63315b 100644 --- a/src/support/file.h +++ b/src/support/file.h @@ -29,46 +29,41 @@ namespace wasm { namespace Flags { - enum BinaryOption { - Binary, - Text - }; - enum DebugOption { - Debug, - Release - }; -} +enum BinaryOption { Binary, Text }; +enum DebugOption { Debug, Release }; +} // namespace Flags std::vector<char> read_stdin(Flags::DebugOption); template<typename T> -T read_file(const std::string& filename, Flags::BinaryOption binary, Flags::DebugOption debug); +T read_file(const std::string& filename, + Flags::BinaryOption binary, + Flags::DebugOption debug); // Declare the valid explicit specializations. -extern template std::string read_file<>(const std::string& , Flags::BinaryOption, Flags::DebugOption); -extern template std::vector<char> read_file<>(const std::string& , Flags::BinaryOption, Flags::DebugOption); +extern template std::string +read_file<>(const std::string&, Flags::BinaryOption, Flags::DebugOption); +extern template std::vector<char> +read_file<>(const std::string&, Flags::BinaryOption, Flags::DebugOption); class Output { - public: +public: // An empty filename will open stdout instead. - Output(const std::string& filename, Flags::BinaryOption binary, Flags::DebugOption debug); + Output(const std::string& filename, + Flags::BinaryOption binary, + Flags::DebugOption debug); ~Output() = default; - template<typename T> - std::ostream &operator<<(const T &v) { - return out << v; - } + template<typename T> std::ostream& operator<<(const T& v) { return out << v; } - std::ostream& getStream() { - return out; - } + std::ostream& getStream() { return out; } std::ostream& write(const char* s, std::streamsize c) { return out.write(s, c); } - private: +private: Output() = delete; - Output(const Output &) = delete; - Output &operator=(const Output &) = delete; + Output(const Output&) = delete; + Output& operator=(const Output&) = delete; std::ofstream outfile; std::ostream out; }; @@ -81,4 +76,4 @@ size_t file_size(std::string filename); } // namespace wasm -#endif // wasm_support_file_h +#endif // wasm_support_file_h diff --git a/src/support/hash.h b/src/support/hash.h index 98d7ceead..647369ac8 100644 --- a/src/support/hash.h +++ b/src/support/hash.h @@ -25,7 +25,8 @@ namespace wasm { typedef uint32_t HashType; inline HashType rehash(HashType x, HashType y) { - // see http://www.cse.yorku.ca/~oz/hash.html and https://stackoverflow.com/a/2595226/1176841 + // see http://www.cse.yorku.ca/~oz/hash.html and + // https://stackoverflow.com/a/2595226/1176841 HashType hash = 5381; while (x) { hash = ((hash << 5) + hash) ^ (x & 0xff); diff --git a/src/support/json.h b/src/support/json.h index 7ffa1211f..8f2edc04d 100644 --- a/src/support/json.h +++ b/src/support/json.h @@ -50,12 +50,8 @@ struct Value { Ref() = default; Ref(Value* value) : std::shared_ptr<Value>(value) {} - Ref& operator[](size_t x) { - return (*this->get())[x]; - } - Ref& operator[](IString x) { - return (*this->get())[x]; - } + Ref& operator[](size_t x) { return (*this->get())[x]; } + Ref& operator[](IString x) { return (*this->get())[x]; } }; enum Type { @@ -72,7 +68,9 @@ struct Value { typedef std::vector<Ref> ArrayStorage; typedef std::unordered_map<IString, Ref> ObjectStorage; -#ifdef _MSC_VER // MSVC does not allow unrestricted unions: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf + // MSVC does not allow unrestricted unions: + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf +#ifdef _MSC_VER IString str; #endif union { // TODO: optimize @@ -80,29 +78,24 @@ struct Value { IString str; #endif double num = 0; - ArrayStorage *arr; // manually allocated/freed + ArrayStorage* arr; // manually allocated/freed bool boo; - ObjectStorage *obj; // manually allocated/freed + ObjectStorage* obj; // manually allocated/freed Ref ref; }; // constructors all copy their input Value() {} - explicit Value(const char *s) : type(Null) { - setString(s); - } - explicit Value(double n) : type(Null) { - setNumber(n); - } - explicit Value(ArrayStorage &a) : type(Null) { + explicit Value(const char* s) : type(Null) { setString(s); } + explicit Value(double n) : type(Null) { setNumber(n); } + explicit Value(ArrayStorage& a) : type(Null) { setArray(); *arr = a; } - // no bool constructor - would endanger the double one (int might convert the wrong way) + // no bool constructor - would endanger the double one (int might convert the + // wrong way) - ~Value() { - free(); - } + ~Value() { free(); } void free() { if (type == Array) { @@ -116,13 +109,13 @@ struct Value { num = 0; } - Value& setString(const char *s) { + Value& setString(const char* s) { free(); type = String; str.set(s); return *this; } - Value& setString(const IString &s) { + Value& setString(const IString& s) { free(); type = String; str.set(s); @@ -134,14 +127,14 @@ struct Value { num = n; return *this; } - Value& setArray(ArrayStorage &a) { + Value& setArray(ArrayStorage& a) { free(); type = Array; arr = new ArrayStorage; *arr = a; return *this; } - Value& setArray(size_t size_hint=0) { + Value& setArray(size_t size_hint = 0) { free(); type = Array; arr = new ArrayStorage; @@ -153,7 +146,8 @@ struct Value { type = Null; return *this; } - Value& setBool(bool b) { // Bool in the name, as otherwise might overload over int + Value& + setBool(bool b) { // Bool in the name, as otherwise might overload over int free(); type = Bool; boo = b; @@ -168,12 +162,14 @@ struct Value { bool isString() { return type == String; } bool isNumber() { return type == Number; } - bool isArray() { return type == Array; } - bool isNull() { return type == Null; } - bool isBool() { return type == Bool; } + bool isArray() { return type == Array; } + bool isNull() { return type == Null; } + bool isBool() { return type == Bool; } bool isObject() { return type == Object; } - bool isBool(bool b) { return type == Bool && b == boo; } // avoid overloading == as it might overload over int + bool isBool(bool b) { + return type == Bool && b == boo; + } // avoid overloading == as it might overload over int const char* getCString() { assert(isString()); @@ -228,7 +224,8 @@ struct Value { } bool operator==(const Value& other) { - if (type != other.type) return false; + if (type != other.type) + return false; switch (other.type) { case String: return str == other.str; @@ -249,17 +246,23 @@ struct Value { } char* parse(char* curr) { - #define is_json_space(x) (x == 32 || x == 9 || x == 10 || x == 13) /* space, tab, linefeed/newline, or return */ - #define skip() { while (*curr && is_json_space(*curr)) curr++; } +#define is_json_space(x) \ + (x == 32 || x == 9 || x == 10 || \ + x == 13) /* space, tab, linefeed/newline, or return */ +#define skip() \ + { \ + while (*curr && is_json_space(*curr)) \ + curr++; \ + } skip(); if (*curr == '"') { // String curr++; - char *close = strchr(curr, '"'); + char* close = strchr(curr, '"'); assert(close); *close = 0; // end this string, and reuse it straight from the input setString(curr); - curr = close+1; + curr = close + 1; } else if (*curr == '[') { // Array curr++; @@ -270,7 +273,8 @@ struct Value { arr->push_back(temp); curr = temp->parse(curr); skip(); - if (*curr == ']') break; + if (*curr == ']') + break; assert(*curr == ','); curr++; skip(); @@ -299,11 +303,11 @@ struct Value { while (*curr != '}') { assert(*curr == '"'); curr++; - char *close = strchr(curr, '"'); + char* close = strchr(curr, '"'); assert(close); *close = 0; // end this string, and reuse it straight from the input IString key(curr); - curr = close+1; + curr = close + 1; skip(); assert(*curr == ':'); curr++; @@ -312,7 +316,8 @@ struct Value { curr = value->parse(curr); (*obj)[key] = value; skip(); - if (*curr == '}') break; + if (*curr == '}') + break; assert(*curr == ','); curr++; skip(); @@ -320,14 +325,14 @@ struct Value { curr++; } else { // Number - char *after; + char* after; setNumber(strtod(curr, &after)); curr = after; } return curr; } - void stringify(std::ostream &os, bool pretty=false); + void stringify(std::ostream& os, bool pretty = false); // String operations @@ -343,7 +348,8 @@ struct Value { void setSize(size_t size) { assert(isArray()); auto old = arr->size(); - if (old != size) arr->resize(size); + if (old != size) + arr->resize(size); if (old < size) { for (auto i = old; i < size; i++) { (*arr)[i] = Ref(new Value()); @@ -370,7 +376,8 @@ struct Value { Ref back() { assert(isArray()); - if (arr->size() == 0) return nullptr; + if (arr->size() == 0) + return nullptr; return arr->back(); } diff --git a/src/support/learning.h b/src/support/learning.h index 8427a2362..0cdd4e929 100644 --- a/src/support/learning.h +++ b/src/support/learning.h @@ -33,7 +33,8 @@ namespace wasm { // The Generator must implement the following: // // * Genome* makeRandom(); - make a random element -// * Genome* makeMixture(Genome* one, Genome* two); - make a new element by mixing two +// * Genome* makeMixture(Genome* one, Genome* two); - make a new element by +// mixing two // // Fitness is the type of the fitness values, e.g. uint32_t. More is better. // @@ -51,19 +52,20 @@ class GeneticLearner { std::vector<unique_ptr> population; void sort() { - std::sort(population.begin(), population.end(), [](const unique_ptr& left, const unique_ptr& right) { - return left->getFitness() > right->getFitness(); - }); + std::sort(population.begin(), + population.end(), + [](const unique_ptr& left, const unique_ptr& right) { + return left->getFitness() > right->getFitness(); + }); } std::mt19937 noise; - size_t randomIndex() { - return noise() % population.size(); - } + size_t randomIndex() { return noise() % population.size(); } public: - GeneticLearner(Generator& generator, size_t size) : generator(generator), noise(1337) { + GeneticLearner(Generator& generator, size_t size) + : generator(generator), noise(1337) { population.resize(size); for (size_t i = 0; i < size; i++) { population[i] = unique_ptr(generator.makeRandom()); @@ -71,27 +73,26 @@ public: sort(); } - Genome* getBest() { - return population[0].get(); - } + Genome* getBest() { return population[0].get(); } - unique_ptr acquireBest() { - return population[0]; - } + unique_ptr acquireBest() { return population[0]; } void runGeneration() { size_t size = population.size(); - // we have a mix of promoted from the last generation, mixed from the last generation, and random + // we have a mix of promoted from the last generation, mixed from the last + // generation, and random const size_t promoted = (25 * size) / 100; const size_t mixed = (50 * size) / 100; // promoted just stay in place - // mixtures are computed, then added back in (as we still need them as we work) + // mixtures are computed, then added back in (as we still need them as we + // work) std::vector<unique_ptr> mixtures; mixtures.resize(mixed); for (size_t i = 0; i < mixed; i++) { - mixtures[i] = unique_ptr(generator.makeMixture(population[randomIndex()].get(), population[randomIndex()].get())); + mixtures[i] = unique_ptr(generator.makeMixture( + population[randomIndex()].get(), population[randomIndex()].get())); } for (size_t i = 0; i < mixed; i++) { population[promoted + i].swap(mixtures[i]); @@ -106,6 +107,6 @@ public: } }; -} // namespace wasm +} // namespace wasm -#endif // wasm_learning_h +#endif // wasm_learning_h diff --git a/src/support/name.h b/src/support/name.h index 0a745b2f7..6f17c3ebf 100644 --- a/src/support/name.h +++ b/src/support/name.h @@ -41,7 +41,8 @@ struct Name : public cashew::IString { friend std::ostream& operator<<(std::ostream& o, Name name) { if (name.str) { - return o << '$' << name.str; // reference interpreter requires we prefix all names + // reference interpreter requires we prefix all names + return o << '$' << name.str; } else { return o << "(null Name)"; } @@ -64,5 +65,4 @@ template<> struct hash<wasm::Name> : hash<cashew::IString> {}; } // namespace std - #endif // wasm_support_string_h diff --git a/src/support/path.cpp b/src/support/path.cpp index 322099a95..7ae68d719 100644 --- a/src/support/path.cpp +++ b/src/support/path.cpp @@ -27,7 +27,7 @@ namespace Path { std::string getPathSeparator() { // TODO: use c++17's path separator // http://en.cppreference.com/w/cpp/experimental/fs/path -#if defined(WIN32) || defined(_WIN32) +#if defined(WIN32) || defined(_WIN32) return "\\"; #else return "/"; @@ -36,7 +36,8 @@ std::string getPathSeparator() { std::string getBinaryenRoot() { auto* envVar = getenv("BINARYEN_ROOT"); - if (envVar) return envVar; + if (envVar) + return envVar; return "."; } @@ -50,9 +51,7 @@ std::string getBinaryenBinDir() { } } -void setBinaryenBinDir(std::string dir) { - binDir = dir; -} +void setBinaryenBinDir(std::string dir) { binDir = dir; } // Gets the path to a binaryen binary tool, like wasm-opt std::string getBinaryenBinaryTool(std::string name) { @@ -62,4 +61,3 @@ std::string getBinaryenBinaryTool(std::string name) { } // namespace Path } // namespace wasm - diff --git a/src/support/safe_integer.cpp b/src/support/safe_integer.cpp index ca5052cd2..23ef84b22 100644 --- a/src/support/safe_integer.cpp +++ b/src/support/safe_integer.cpp @@ -35,17 +35,18 @@ bool wasm::isSInteger32(double x) { } uint32_t wasm::toUInteger32(double x) { - return std::signbit(x) ? 0 : (x < std::numeric_limits<uint32_t>::max() - ? (uint32_t)x - : std::numeric_limits<uint32_t>::max()); + return std::signbit(x) ? 0 + : (x < std::numeric_limits<uint32_t>::max() + ? (uint32_t)x + : std::numeric_limits<uint32_t>::max()); } int32_t wasm::toSInteger32(double x) { return (x > std::numeric_limits<int32_t>::min() && x < std::numeric_limits<int32_t>::max()) - ? (int32_t)x - : (std::signbit(x) ? std::numeric_limits<int32_t>::min() - : std::numeric_limits<int32_t>::max()); + ? (int32_t)x + : (std::signbit(x) ? std::numeric_limits<int32_t>::min() + : std::numeric_limits<int32_t>::max()); } bool wasm::isUInteger64(double x) { @@ -59,17 +60,18 @@ bool wasm::isSInteger64(double x) { } uint64_t wasm::toUInteger64(double x) { - return std::signbit(x) ? 0 : (x < (double)std::numeric_limits<uint64_t>::max() - ? (uint64_t)x - : std::numeric_limits<uint64_t>::max()); + return std::signbit(x) ? 0 + : (x < (double)std::numeric_limits<uint64_t>::max() + ? (uint64_t)x + : std::numeric_limits<uint64_t>::max()); } int64_t wasm::toSInteger64(double x) { return (x > (double)std::numeric_limits<int64_t>::min() && x < (double)std::numeric_limits<int64_t>::max()) - ? (int64_t)x - : (std::signbit(x) ? std::numeric_limits<int64_t>::min() - : std::numeric_limits<int64_t>::max()); + ? (int64_t)x + : (std::signbit(x) ? std::numeric_limits<int64_t>::min() + : std::numeric_limits<int64_t>::max()); } /* 3 32222222 222...00 @@ -116,6 +118,7 @@ bool wasm::isInRangeI64TruncU(int32_t i) { return (u < 0x5f800000U) || (u >= 0x80000000U && u < 0xbf800000U); } +/* clang-format off */ /* * 6 66655555555 5544...222221...000 * 3 21098765432 1098...432109...210 @@ -139,6 +142,7 @@ bool wasm::isInRangeI64TruncU(int32_t i) { * 1 11111111111 0000...000000...001 0xfff0000000000001 => -nan(0x1) * 1 11111111111 1111...111111...111 0xffffffffffffffff => -nan(0xfff...) */ +/* clang-format on */ bool wasm::isInRangeI32TruncS(int64_t i) { uint64_t u = i; diff --git a/src/support/safe_integer.h b/src/support/safe_integer.h index 5bd807a18..031c6c323 100644 --- a/src/support/safe_integer.h +++ b/src/support/safe_integer.h @@ -41,6 +41,6 @@ bool isInRangeI32TruncU(int64_t i); bool isInRangeI64TruncS(int64_t i); bool isInRangeI64TruncU(int64_t i); -} // namespace wasm +} // namespace wasm -#endif // wasm_safe_integer_h +#endif // wasm_safe_integer_h diff --git a/src/support/small_vector.h b/src/support/small_vector.h index e746700bf..dd6afb526 100644 --- a/src/support/small_vector.h +++ b/src/support/small_vector.h @@ -29,8 +29,7 @@ namespace wasm { -template<typename T, size_t N> -class SmallVector { +template<typename T, size_t N> class SmallVector { // fixed-space storage size_t usedFixed = 0; std::array<T, N> fixed; @@ -65,10 +64,9 @@ public: } } - template <typename... ArgTypes> - void emplace_back(ArgTypes &&... Args) { + template<typename... ArgTypes> void emplace_back(ArgTypes&&... Args) { if (usedFixed < N) { - new(&fixed[usedFixed++]) T(std::forward<ArgTypes>(Args)...); + new (&fixed[usedFixed++]) T(std::forward<ArgTypes>(Args)...); } else { flexible.emplace_back(std::forward<ArgTypes>(Args)...); } @@ -101,13 +99,9 @@ public: } } - size_t size() const { - return usedFixed + flexible.size(); - } + size_t size() const { return usedFixed + flexible.size(); } - bool empty() const { - return size() == 0; - } + bool empty() const { return size() == 0; } void clear() { usedFixed = 0; @@ -115,9 +109,11 @@ public: } bool operator==(const SmallVector<T, N>& other) const { - if (usedFixed != other.usedFixed) return false; + if (usedFixed != other.usedFixed) + return false; for (size_t i = 0; i < usedFixed; i++) { - if (fixed[i] != other.fixed[i]) return false; + if (fixed[i] != other.fixed[i]) + return false; } return flexible == other.flexible; } @@ -136,15 +132,14 @@ public: const SmallVector<T, N>* parent; size_t index; - Iterator(const SmallVector<T, N>* parent, size_t index) : parent(parent), index(index) {} + Iterator(const SmallVector<T, N>* parent, size_t index) + : parent(parent), index(index) {} bool operator!=(const Iterator& other) const { return index != other.index || parent != other.parent; } - void operator++() { - index++; - } + void operator++() { index++; } Iterator& operator+=(difference_type off) { index += off; @@ -155,9 +150,7 @@ public: return Iterator(*this) += off; } - const value_type operator*() const { - return (*parent)[index]; - } + const value_type operator*() const { return (*parent)[index]; } }; Iterator begin() const { diff --git a/src/support/sorted_vector.h b/src/support/sorted_vector.h index da991ce78..e26d5e3af 100644 --- a/src/support/sorted_vector.h +++ b/src/support/sorted_vector.h @@ -61,7 +61,8 @@ struct SortedVector : public std::vector<Index> { void insert(Index x) { auto it = std::lower_bound(begin(), end(), x); - if (it == end()) push_back(x); + if (it == end()) + push_back(x); else if (*it > x) { Index i = it - begin(); resize(size() + 1); @@ -85,8 +86,7 @@ struct SortedVector : public std::vector<Index> { return it != end() && *it == x; } - template<typename T> - SortedVector& filter(T keep) { + template<typename T> SortedVector& filter(T keep) { size_t skip = 0; for (size_t i = 0; i < size(); i++) { if (keep((*this)[i])) { @@ -107,7 +107,8 @@ struct SortedVector : public std::vector<Index> { void dump(const char* str = nullptr) const { std::cout << "SortedVector " << (str ? str : "") << ": "; - for (auto x : *this) std::cout << x << " "; + for (auto x : *this) + std::cout << x << " "; std::cout << '\n'; } }; diff --git a/src/support/threads.cpp b/src/support/threads.cpp index 7ae304c4f..785994b4e 100644 --- a/src/support/threads.cpp +++ b/src/support/threads.cpp @@ -20,23 +20,29 @@ #include <iostream> #include <string> -#include "threads.h" #include "compiler-support.h" +#include "threads.h" #include "utilities.h" - // debugging tools #ifdef BINARYEN_THREAD_DEBUG static std::mutex debug; -#define DEBUG_THREAD(x) { std::lock_guard<std::mutex> lock(debug); std::cerr << "[THREAD " << std::this_thread::get_id() << "] " << x; } -#define DEBUG_POOL(x) { std::lock_guard<std::mutex> lock(debug); std::cerr << "[POOL " << std::this_thread::get_id() << "] " << x; } +#define DEBUG_THREAD(x) \ + { \ + std::lock_guard<std::mutex> lock(debug); \ + std::cerr << "[THREAD " << std::this_thread::get_id() << "] " << x; \ + } +#define DEBUG_POOL(x) \ + { \ + std::lock_guard<std::mutex> lock(debug); \ + std::cerr << "[POOL " << std::this_thread::get_id() << "] " << x; \ + } #else #define DEBUG_THREAD(x) #define DEBUG_POOL(x) #endif - namespace wasm { // Thread @@ -56,7 +62,7 @@ Thread::~Thread() { thread->join(); } -void Thread::work(std::function<ThreadWorkState ()> doWork_) { +void Thread::work(std::function<ThreadWorkState()> doWork_) { // TODO: fancy work stealing DEBUG_THREAD("send work to thread\n"); { @@ -68,7 +74,7 @@ void Thread::work(std::function<ThreadWorkState ()> doWork_) { } } -void Thread::mainLoop(void *self_) { +void Thread::mainLoop(void* self_) { auto* self = static_cast<Thread*>(self_); while (1) { DEBUG_THREAD("checking for work\n"); @@ -77,7 +83,8 @@ void Thread::mainLoop(void *self_) { if (self->doWork) { DEBUG_THREAD("doing work\n"); // run tasks until they are all done - while (self->doWork() == ThreadWorkState::More) {} + while (self->doWork() == ThreadWorkState::More) { + } self->doWork = nullptr; } else if (self->done) { DEBUG_THREAD("done\n"); @@ -107,16 +114,19 @@ std::mutex ThreadPool::workMutex; std::mutex ThreadPool::threadMutex; void ThreadPool::initialize(size_t num) { - if (num == 1) return; // no multiple cores, don't create threads + if (num == 1) + return; // no multiple cores, don't create threads DEBUG_POOL("initialize()\n"); std::unique_lock<std::mutex> lock(threadMutex); - ready.store(threads.size()); // initial state before first resetThreadsAreReady() + // initial state before first resetThreadsAreReady() + ready.store(threads.size()); resetThreadsAreReady(); for (size_t i = 0; i < num; i++) { try { threads.emplace_back(make_unique<Thread>(this)); } catch (std::system_error&) { - // failed to create a thread - don't use multithreading, as if num cores == 1 + // failed to create a thread - don't use multithreading, as if num cores + // == 1 DEBUG_POOL("could not create thread\n"); threads.clear(); return; @@ -154,14 +164,16 @@ ThreadPool* ThreadPool::get() { return pool.get(); } -void ThreadPool::work(std::vector<std::function<ThreadWorkState ()>>& doWorkers) { +void ThreadPool::work( + std::vector<std::function<ThreadWorkState()>>& doWorkers) { size_t num = threads.size(); // If no multiple cores, or on a side thread, do not use worker threads if (num == 0) { // just run sequentially DEBUG_POOL("work() sequentially\n"); assert(doWorkers.size() > 0); - while (doWorkers[0]() == ThreadWorkState::More) {} + while (doWorkers[0]() == ThreadWorkState::More) { + } return; } // run in parallel on threads @@ -187,9 +199,7 @@ void ThreadPool::work(std::vector<std::function<ThreadWorkState ()>>& doWorkers) DEBUG_POOL("work() is done\n"); } -size_t ThreadPool::size() { - return std::max(size_t(1), threads.size()); -} +size_t ThreadPool::size() { return std::max(size_t(1), threads.size()); } bool ThreadPool::isRunning() { DEBUG_POOL("check if running\n"); @@ -216,4 +226,3 @@ bool ThreadPool::areThreadsReady() { } } // namespace wasm - diff --git a/src/support/threads.h b/src/support/threads.h index e76783ebb..8cfa79c87 100644 --- a/src/support/threads.h +++ b/src/support/threads.h @@ -33,10 +33,7 @@ namespace wasm { // The work state of a helper thread - is there more to do, // or are we finished for now. -enum class ThreadWorkState { - More, - Finished -}; +enum class ThreadWorkState { More, Finished }; class ThreadPool; @@ -52,7 +49,7 @@ class Thread { std::mutex mutex; std::condition_variable condition; bool done = false; - std::function<ThreadWorkState ()> doWork = nullptr; + std::function<ThreadWorkState()> doWork = nullptr; public: Thread(ThreadPool* parent); @@ -60,10 +57,10 @@ public: // Start to do work, calling doWork() until // it returns false. - void work(std::function<ThreadWorkState ()> doWork); + void work(std::function<ThreadWorkState()> doWork); private: - static void mainLoop(void *self); + static void mainLoop(void* self); }; // @@ -100,7 +97,7 @@ public: // getTask() (in a thread-safe manner) to get tasks, and // sends them to workers to be executed. This method // blocks until all tasks are complete. - void work(std::vector<std::function<ThreadWorkState ()>>& doWorkers); + void work(std::vector<std::function<ThreadWorkState()>>& doWorkers); size_t size(); @@ -123,9 +120,7 @@ class OnlyOnce { std::atomic<int> created; public: - OnlyOnce() { - created.store(0); - } + OnlyOnce() { created.store(0); } void verify() { auto before = created.fetch_add(1); @@ -135,4 +130,4 @@ public: } // namespace wasm -#endif // wasm_support_threads_h +#endif // wasm_support_threads_h diff --git a/src/support/timing.h b/src/support/timing.h index a8de8de04..0b0430e24 100644 --- a/src/support/timing.h +++ b/src/support/timing.h @@ -33,23 +33,19 @@ class Timer { public: Timer(std::string name = "") : name(name) {} - void start() { - startTime = std::chrono::steady_clock::now(); - } + void start() { startTime = std::chrono::steady_clock::now(); } void stop() { - total += std::chrono::duration<double>(std::chrono::steady_clock::now() - startTime).count(); + total += std::chrono::duration<double>(std::chrono::steady_clock::now() - + startTime) + .count(); } - double getTotal() { - return total; - } + double getTotal() { return total; } - void dump() { - std::cerr << "<Timer " << name << ": " << getTotal() << ">\n"; - } + void dump() { std::cerr << "<Timer " << name << ": " << getTotal() << ">\n"; } }; } // namespace wasm -#endif // wasm_support_timing_h +#endif // wasm_support_timing_h diff --git a/src/support/unique_deferring_queue.h b/src/support/unique_deferring_queue.h index eb024e5b8..ad8f3967d 100644 --- a/src/support/unique_deferring_queue.h +++ b/src/support/unique_deferring_queue.h @@ -28,8 +28,7 @@ namespace wasm { -template<typename T> -struct UniqueDeferredQueue { +template<typename T> struct UniqueDeferredQueue { // implemented as an internal queue, plus a map // that says how many times an element appears. we // can then skip non-final appearances. this lets us diff --git a/src/support/utilities.h b/src/support/utilities.h index 07a163ef9..71d8ce2e0 100644 --- a/src/support/utilities.h +++ b/src/support/utilities.h @@ -22,8 +22,8 @@ #include <cassert> #include <cstdint> #include <cstring> -#include <memory> #include <iostream> +#include <memory> #include <type_traits> #include "support/bits.h" @@ -53,19 +53,15 @@ inline size_t alignAddr(size_t address, size_t alignment) { } template<typename T, typename... Args> -std::unique_ptr<T> make_unique(Args&&... args) -{ - return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); +std::unique_ptr<T> make_unique(Args&&... args) { + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); } // For fatal errors which could arise from input (i.e. not assertion failures) class Fatal { - public: - Fatal() { - std::cerr << "Fatal: "; - } - template<typename T> - Fatal &operator<<(T arg) { +public: + Fatal() { std::cerr << "Fatal: "; } + template<typename T> Fatal& operator<<(T arg) { std::cerr << arg; return *this; } @@ -78,7 +74,6 @@ class Fatal { } }; +} // namespace wasm -} // namespace wasm - -#endif // wasm_support_utilities_h +#endif // wasm_support_utilities_h diff --git a/src/tools/asm2wasm.cpp b/src/tools/asm2wasm.cpp index 547bf4ab1..843106ffd 100644 --- a/src/tools/asm2wasm.cpp +++ b/src/tools/asm2wasm.cpp @@ -21,21 +21,21 @@ #include <exception> #include "ir/trapping.h" +#include "optimization-options.h" #include "support/colors.h" #include "support/command-line.h" #include "support/file.h" #include "wasm-builder.h" -#include "wasm-printing.h" #include "wasm-io.h" +#include "wasm-printing.h" #include "wasm-validator.h" -#include "optimization-options.h" #include "asm2wasm.h" using namespace cashew; using namespace wasm; -int main(int argc, const char *argv[]) { +int main(int argc, const char* argv[]) { bool legalizeJavaScriptFFI = true; TrapMode trapMode = TrapMode::JS; bool wasmOnly = false; @@ -44,81 +44,138 @@ int main(int argc, const char *argv[]) { std::string symbolMap; bool emitBinary = true; - OptimizationOptions options("asm2wasm", "Translate asm.js files to .wast files"); + OptimizationOptions options("asm2wasm", + "Translate asm.js files to .wast files"); options - .add("--output", "-o", "Output file (stdout if not specified)", - Options::Arguments::One, - [](Options *o, const std::string& argument) { - o->extra["output"] = argument; - Colors::disable(); - }) - .add("--mapped-globals", "-n", "Mapped globals", Options::Arguments::One, - [](Options *o, const std::string& argument) { - std::cerr << "warning: the --mapped-globals/-m option is deprecated (a mapped globals file is no longer needed as we use wasm globals)" << std::endl; - }) - .add("--mem-init", "-t", "Import a memory initialization file into the output module", Options::Arguments::One, - [](Options *o, const std::string& argument) { - o->extra["mem init"] = argument; - }) - .add("--mem-base", "-mb", "Set the location to write the memory initialization (--mem-init) file (GLOBAL_BASE in emscripten). If not provided, the __memory_base global import is used.", Options::Arguments::One, - [](Options *o, const std::string& argument) { - o->extra["mem base"] = argument; - }) - .add("--mem-max", "-mm", "Set the maximum size of memory in the wasm module (in bytes). -1 means no limit. Without this, TOTAL_MEMORY is used (as it is used for the initial value), or if memory growth is enabled, no limit is set. This overrides both of those.", Options::Arguments::One, - [](Options *o, const std::string& argument) { - o->extra["mem max"] = argument; - }) - .add("--total-memory", "-m", "Total memory size", Options::Arguments::One, - [](Options *o, const std::string& argument) { - o->extra["total memory"] = argument; - }) - .add("--table-max", "-tM", "Set the maximum size of the table. Without this, it is set depending on how many functions are in the module. -1 means no limit", Options::Arguments::One, - [](Options *o, const std::string& argument) { - o->extra["table max"] = argument; - }) - .add("--no-opts", "-n", "Disable optimization passes (deprecated)", Options::Arguments::Zero, - [](Options *o, const std::string& ) { - std::cerr << "--no-opts is deprecated (use -O0, etc.)\n"; - }) - .add("--trap-mode", "", - "Strategy for handling potentially trapping instructions. Valid " - "values are \"allow\", \"js\", and \"clamp\"", - Options::Arguments::One, - [&trapMode](Options *o, const std::string& argument) { - try { - trapMode = trapModeFromString(argument); - } catch (std::invalid_argument& e) { - std::cerr << "Error: " << e.what() << "\n"; - exit(EXIT_FAILURE); - } - }) - .add("--wasm-only", "-w", "Input is in WebAssembly-only format, and not actually valid asm.js", Options::Arguments::Zero, - [&wasmOnly](Options *o, const std::string& ) { - wasmOnly = true; - }) - .add("--no-legalize-javascript-ffi", "-nj", "Do not fully legalize (i64->i32, f32->f64) the imports and exports for interfacing with JS", Options::Arguments::Zero, - [&legalizeJavaScriptFFI](Options *o, const std::string& ) { - legalizeJavaScriptFFI = false; - }) - .add("--debuginfo", "-g", "Emit names section in wasm binary (or full debuginfo in wast)", - Options::Arguments::Zero, - [&](Options *o, const std::string& arguments) { options.passOptions.debugInfo = true; }) - .add("--source-map", "-sm", "Emit source map (if using binary output) to the specified file", - Options::Arguments::One, - [&sourceMapFilename](Options *o, const std::string& argument) { sourceMapFilename = argument; }) - .add("--source-map-url", "-su", "Use specified string as source map URL", - Options::Arguments::One, - [&sourceMapUrl](Options *o, const std::string& argument) { sourceMapUrl = argument; }) - .add("--symbolmap", "-s", "Emit a symbol map (indexes => names)", - Options::Arguments::One, - [&](Options *o, const std::string& argument) { symbolMap = argument; }) - .add("--emit-text", "-S", "Emit text instead of binary for the output file", - Options::Arguments::Zero, - [&](Options *o, const std::string& argument) { emitBinary = false; }) - .add_positional("INFILE", Options::Arguments::One, - [](Options *o, const std::string& argument) { - o->extra["infile"] = argument; - }); + .add("--output", + "-o", + "Output file (stdout if not specified)", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["output"] = argument; + Colors::disable(); + }) + .add( + "--mapped-globals", + "-n", + "Mapped globals", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + std::cerr + << "warning: the --mapped-globals/-m option is deprecated (a mapped " + "globals file is no longer needed as we use wasm globals)" + << std::endl; + }) + .add("--mem-init", + "-t", + "Import a memory initialization file into the output module", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["mem init"] = argument; + }) + .add("--mem-base", + "-mb", + "Set the location to write the memory initialization (--mem-init) " + "file (GLOBAL_BASE in emscripten). If not provided, the __memory_base " + "global import is used.", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["mem base"] = argument; + }) + .add("--mem-max", + "-mm", + "Set the maximum size of memory in the wasm module (in bytes). -1 " + "means no limit. Without this, TOTAL_MEMORY is used (as it is used " + "for the initial value), or if memory growth is enabled, no limit is " + "set. This overrides both of those.", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["mem max"] = argument; + }) + .add("--total-memory", + "-m", + "Total memory size", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["total memory"] = argument; + }) + .add("--table-max", + "-tM", + "Set the maximum size of the table. Without this, it is set depending " + "on how many functions are in the module. -1 means no limit", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["table max"] = argument; + }) + .add("--no-opts", + "-n", + "Disable optimization passes (deprecated)", + Options::Arguments::Zero, + [](Options* o, const std::string&) { + std::cerr << "--no-opts is deprecated (use -O0, etc.)\n"; + }) + .add("--trap-mode", + "", + "Strategy for handling potentially trapping instructions. Valid " + "values are \"allow\", \"js\", and \"clamp\"", + Options::Arguments::One, + [&trapMode](Options* o, const std::string& argument) { + try { + trapMode = trapModeFromString(argument); + } catch (std::invalid_argument& e) { + std::cerr << "Error: " << e.what() << "\n"; + exit(EXIT_FAILURE); + } + }) + .add("--wasm-only", + "-w", + "Input is in WebAssembly-only format, and not actually valid asm.js", + Options::Arguments::Zero, + [&wasmOnly](Options* o, const std::string&) { wasmOnly = true; }) + .add("--no-legalize-javascript-ffi", + "-nj", + "Do not fully legalize (i64->i32, f32->f64) the imports and exports " + "for interfacing with JS", + Options::Arguments::Zero, + [&legalizeJavaScriptFFI](Options* o, const std::string&) { + legalizeJavaScriptFFI = false; + }) + .add("--debuginfo", + "-g", + "Emit names section in wasm binary (or full debuginfo in wast)", + Options::Arguments::Zero, + [&](Options* o, const std::string& arguments) { + options.passOptions.debugInfo = true; + }) + .add("--source-map", + "-sm", + "Emit source map (if using binary output) to the specified file", + Options::Arguments::One, + [&sourceMapFilename](Options* o, const std::string& argument) { + sourceMapFilename = argument; + }) + .add("--source-map-url", + "-su", + "Use specified string as source map URL", + Options::Arguments::One, + [&sourceMapUrl](Options* o, const std::string& argument) { + sourceMapUrl = argument; + }) + .add("--symbolmap", + "-s", + "Emit a symbol map (indexes => names)", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { symbolMap = argument; }) + .add("--emit-text", + "-S", + "Emit text instead of binary for the output file", + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { emitBinary = false; }) + .add_positional("INFILE", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["infile"] = argument; + }); options.parse(argc, argv); // finalize arguments @@ -129,32 +186,39 @@ int main(int argc, const char *argv[]) { if (options.runningDefaultOptimizationPasses()) { if (options.passes.size() > 1) { - Fatal() << "asm2wasm can only run default optimization passes (-O, -Ox, etc.), and not specific additional passes"; + Fatal() << "asm2wasm can only run default optimization passes (-O, -Ox, " + "etc.), and not specific additional passes"; } } - const auto &tm_it = options.extra.find("total memory"); - size_t totalMemory = - tm_it == options.extra.end() ? 16 * 1024 * 1024 : atoll(tm_it->second.c_str()); + const auto& tm_it = options.extra.find("total memory"); + size_t totalMemory = tm_it == options.extra.end() + ? 16 * 1024 * 1024 + : atoll(tm_it->second.c_str()); if (totalMemory & ~Memory::kPageMask) { - std::cerr << "Error: total memory size " << totalMemory << - " is not a multiple of the 64k wasm page size\n"; + std::cerr << "Error: total memory size " << totalMemory + << " is not a multiple of the 64k wasm page size\n"; exit(EXIT_FAILURE); } Asm2WasmPreProcessor pre; // wasm binaries can contain a names section, but not full debug info -- // debug info is disabled if a map file is not specified with wasm binary - pre.debugInfo = options.passOptions.debugInfo && (!emitBinary || sourceMapFilename.size()); - auto input( - read_file<std::vector<char>>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release)); - char *start = pre.process(input.data()); + pre.debugInfo = + options.passOptions.debugInfo && (!emitBinary || sourceMapFilename.size()); + auto input(read_file<std::vector<char>>(options.extra["infile"], + Flags::Text, + options.debug ? Flags::Debug + : Flags::Release)); + char* start = pre.process(input.data()); - if (options.debug) std::cerr << "parsing..." << std::endl; + if (options.debug) + std::cerr << "parsing..." << std::endl; cashew::Parser<Ref, DotZeroValueBuilder> builder; Ref asmjs = builder.parseToplevel(start); - if (options.debug) std::cerr << "wasming..." << std::endl; + if (options.debug) + std::cerr << "wasming..." << std::endl; Module wasm; // set up memory @@ -162,17 +226,19 @@ int main(int argc, const char *argv[]) { // import mem init file, if provided (do this before compiling the module, // since the optimizer should see the memory segments) - const auto &memInit = options.extra.find("mem init"); + const auto& memInit = options.extra.find("mem init"); if (memInit != options.extra.end()) { auto filename = memInit->second.c_str(); - auto data(read_file<std::vector<char>>(filename, Flags::Binary, options.debug ? Flags::Debug : Flags::Release)); + auto data(read_file<std::vector<char>>( + filename, Flags::Binary, options.debug ? Flags::Debug : Flags::Release)); // create the memory segment Expression* init; - const auto &memBase = options.extra.find("mem base"); + const auto& memBase = options.extra.find("mem base"); if (memBase == options.extra.end()) { init = Builder(wasm).makeGetGlobal(MEMORY_BASE, i32); } else { - init = Builder(wasm).makeConst(Literal(int32_t(atoi(memBase->second.c_str())))); + init = Builder(wasm).makeConst( + Literal(int32_t(atoi(memBase->second.c_str())))); } wasm.memory.segments.emplace_back(init, data); } @@ -181,7 +247,14 @@ int main(int argc, const char *argv[]) { options.applyFeatures(wasm); // compile the code - Asm2WasmBuilder asm2wasm(wasm, pre, options.debug, trapMode, options.passOptions, legalizeJavaScriptFFI, options.runningDefaultOptimizationPasses(), wasmOnly); + Asm2WasmBuilder asm2wasm(wasm, + pre, + options.debug, + trapMode, + options.passOptions, + legalizeJavaScriptFFI, + options.runningDefaultOptimizationPasses(), + wasmOnly); asm2wasm.processAsm(asmjs); // finalize the imported mem init @@ -194,7 +267,7 @@ int main(int argc, const char *argv[]) { } // Set the max memory size, if requested - const auto &memMax = options.extra.find("mem max"); + const auto& memMax = options.extra.find("mem max"); if (memMax != options.extra.end()) { uint64_t max = strtoull(memMax->second.c_str(), nullptr, 10); if (max != uint64_t(-1)) { @@ -204,7 +277,7 @@ int main(int argc, const char *argv[]) { } } // Set the table sizes, if requested - const auto &tableMax = options.extra.find("table max"); + const auto& tableMax = options.extra.find("table max"); if (tableMax != options.extra.end()) { int max = atoi(tableMax->second.c_str()); if (max >= 0) { @@ -221,7 +294,8 @@ int main(int argc, const char *argv[]) { } } - if (options.debug) std::cerr << "emitting..." << std::endl; + if (options.debug) + std::cerr << "emitting..." << std::endl; ModuleWriter writer; writer.setDebug(options.debug); writer.setDebugInfo(options.passOptions.debugInfo); @@ -233,5 +307,6 @@ int main(int argc, const char *argv[]) { } writer.write(wasm, options.extra["output"]); - if (options.debug) std::cerr << "done." << std::endl; + if (options.debug) + std::cerr << "done." << std::endl; } diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h index 554b4eb6c..2a42b941b 100644 --- a/src/tools/execution-results.h +++ b/src/tools/execution-results.h @@ -18,9 +18,9 @@ // Shared execution result checking code // -#include "wasm.h" -#include "shell-interface.h" #include "ir/import-utils.h" +#include "shell-interface.h" +#include "wasm.h" namespace wasm { @@ -59,9 +59,11 @@ struct ExecutionResults { LoggingExternalInterface interface(loggings); try { ModuleInstance instance(wasm, &interface); - // execute all exported methods (that are therefore preserved through opts) + // execute all exported methods (that are therefore preserved through + // opts) for (auto& exp : wasm.exports) { - if (exp->kind != ExternalKind::Function) continue; + if (exp->kind != ExternalKind::Function) + continue; std::cout << "[fuzz-exec] calling " << exp->name << "\n"; auto* func = wasm.getFunction(exp->value); if (func->result != none) { @@ -69,7 +71,8 @@ struct ExecutionResults { results[exp->name] = run(func, wasm, instance); // ignore the result if we hit an unreachable and returned no value if (isConcreteType(results[exp->name].type)) { - std::cout << "[fuzz-exec] note result: " << exp->name << " => " << results[exp->name] << '\n'; + std::cout << "[fuzz-exec] note result: " << exp->name << " => " + << results[exp->name] << '\n'; } } else { // no result, run it anyhow (it might modify memory etc.) @@ -111,9 +114,7 @@ struct ExecutionResults { return true; } - bool operator!=(ExecutionResults& other) { - return !((*this) == other); - } + bool operator!=(ExecutionResults& other) { return !((*this) == other); } Literal run(Function* func, Module& wasm) { LoggingExternalInterface interface(loggings); 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 diff --git a/src/tools/js-wrapper.h b/src/tools/js-wrapper.h index 433d6fc5a..e39a015d6 100644 --- a/src/tools/js-wrapper.h +++ b/src/tools/js-wrapper.h @@ -28,7 +28,8 @@ static std::string generateJSWrapper(Module& wasm) { "}\n" "var tempRet0;\n" "var binary;\n" - "if (typeof process === 'object' && typeof require === 'function' /* node.js detection */) {\n" + "if (typeof process === 'object' && typeof require === 'function' /* " + "node.js detection */) {\n" " var args = process.argv.slice(2);\n" " binary = require('fs').readFileSync(args[0]);\n" " if (!binary.buffer) binary = new Uint8Array(binary);\n" @@ -59,12 +60,21 @@ static std::string generateJSWrapper(Module& wasm) { " }\n" " return ret;\n" "}\n" - "var instance = new WebAssembly.Instance(new WebAssembly.Module(binary), {\n" + "var instance = new WebAssembly.Instance(new " + "WebAssembly.Module(binary), {\n" " 'fuzzing-support': {\n" - " 'log-i32': function(x) { console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') + ']') },\n" - " 'log-i64': function(x, y) { console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') + ' ' + literal(y, 'i32') + ']') },\n" // legalization: two i32s - " 'log-f32': function(x) { console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') + ']') },\n" // legalization: an f64 - " 'log-f64': function(x) { console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') + ']') },\n" + " 'log-i32': function(x) { " + "console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') " + "+ ']') },\n" + " 'log-i64': function(x, y) { " + "console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') " + "+ ' ' + literal(y, 'i32') + ']') },\n" // legalization: two i32s + " 'log-f32': function(x) { " + "console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') " + "+ ']') },\n" // legalization: an f64 + " 'log-f64': function(x) { " + "console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') " + "+ ']') },\n" " },\n" " 'env': {\n" " 'setTempRet0': function(x) { tempRet0 = x },\n" @@ -73,12 +83,16 @@ static std::string generateJSWrapper(Module& wasm) { "});\n"; for (auto& exp : wasm.exports) { auto* func = wasm.getFunctionOrNull(exp->value); - if (!func) continue; // something exported other than a function - ret += "if (instance.exports.hangLimitInitializer) instance.exports.hangLimitInitializer();\n"; + if (!func) + continue; // something exported other than a function + ret += "if (instance.exports.hangLimitInitializer) " + "instance.exports.hangLimitInitializer();\n"; ret += "try {\n"; - ret += std::string(" console.log('[fuzz-exec] calling $") + exp->name.str + "');\n"; + ret += std::string(" console.log('[fuzz-exec] calling $") + exp->name.str + + "');\n"; if (func->result != none) { - ret += std::string(" console.log('[fuzz-exec] note result: $") + exp->name.str + " => ' + literal("; + ret += std::string(" console.log('[fuzz-exec] note result: $") + + exp->name.str + " => ' + literal("; } else { ret += " "; } @@ -110,4 +124,3 @@ static std::string generateJSWrapper(Module& wasm) { } } // namespace wasm - diff --git a/src/tools/optimization-options.h b/src/tools/optimization-options.h index cf7c612ea..6fa9aac36 100644 --- a/src/tools/optimization-options.h +++ b/src/tools/optimization-options.h @@ -14,6 +14,9 @@ * limitations under the License. */ +#ifndef wasm_tools_optimization_options_h +#define wasm_tools_optimization_options_h + #include "tool-options.h" // @@ -27,101 +30,137 @@ struct OptimizationOptions : public ToolOptions { std::vector<std::string> passes; - OptimizationOptions(const std::string& command, const std::string& description) : ToolOptions(command, description) { - (*this).add("", "-O", "execute default optimization passes", - Options::Arguments::Zero, - [this](Options*, const std::string&) { - passOptions.setDefaultOptimizationOptions(); - passes.push_back(DEFAULT_OPT_PASSES); - }) - .add("", "-O0", "execute no optimization passes", - Options::Arguments::Zero, - [this](Options*, const std::string&) { - passOptions.optimizeLevel = 0; - passOptions.shrinkLevel = 0; - }) - .add("", "-O1", "execute -O1 optimization passes (quick&useful opts, useful for iteration builds)", - Options::Arguments::Zero, - [this](Options*, const std::string&) { - passOptions.optimizeLevel = 1; - passOptions.shrinkLevel = 0; - passes.push_back(DEFAULT_OPT_PASSES); - }) - .add("", "-O2", "execute -O2 optimization passes (most opts, generally gets most perf)", - Options::Arguments::Zero, - [this](Options*, const std::string&) { - passOptions.optimizeLevel = 2; - passOptions.shrinkLevel = 0; - passes.push_back(DEFAULT_OPT_PASSES); - }) - .add("", "-O3", "execute -O3 optimization passes (spends potentially a lot of time optimizing)", - Options::Arguments::Zero, - [this](Options*, const std::string&) { - passOptions.optimizeLevel = 3; - passOptions.shrinkLevel = 0; - passes.push_back(DEFAULT_OPT_PASSES); - }) - .add("", "-O4", "execute -O4 optimization passes (also flatten the IR, which can take a lot more time and memory, but is useful on more nested / complex / less-optimized input)", - Options::Arguments::Zero, - [this](Options*, const std::string&) { - passOptions.optimizeLevel = 4; - passOptions.shrinkLevel = 0; - passes.push_back(DEFAULT_OPT_PASSES); - }) - .add("", "-Os", "execute default optimization passes, focusing on code size", - Options::Arguments::Zero, - [this](Options*, const std::string&) { - passOptions.optimizeLevel = 2; - passOptions.shrinkLevel = 1; - passes.push_back(DEFAULT_OPT_PASSES); - }) - .add("", "-Oz", "execute default optimization passes, super-focusing on code size", - Options::Arguments::Zero, - [this](Options*, const std::string&) { - passOptions.optimizeLevel = 2; - passOptions.shrinkLevel = 2; - passes.push_back(DEFAULT_OPT_PASSES); - }) - .add("--optimize-level", "-ol", "How much to focus on optimizing code", - Options::Arguments::One, - [this](Options* o, const std::string& argument) { - passOptions.optimizeLevel = atoi(argument.c_str()); - }) - .add("--shrink-level", "-s", "How much to focus on shrinking code size", - Options::Arguments::One, - [this](Options* o, const std::string& argument) { - passOptions.shrinkLevel = atoi(argument.c_str()); - }) - .add("--ignore-implicit-traps", "-iit", "Optimize under the helpful assumption that no surprising traps occur (from load, div/mod, etc.)", - Options::Arguments::Zero, - [this](Options*, const std::string&) { - passOptions.ignoreImplicitTraps = true; - }) - .add("--low-memory-unused", "-lmu", "Optimize under the helpful assumption that the low 1K of memory is not used by the application", - Options::Arguments::Zero, - [this](Options*, const std::string&) { - passOptions.lowMemoryUnused = true; - }) - .add("--pass-arg", "-pa", "An argument passed along to optimization passes being run. Must be in the form KEY:VALUE", - Options::Arguments::N, - [this](Options*, const std::string& argument) { - auto colon = argument.find(':'); - if (colon == std::string::npos) { - Fatal() << "--pass-arg value must be in the form of KEY:VALUE"; - } - auto key = argument.substr(0, colon); - auto value = argument.substr(colon + 1); - passOptions.arguments[key] = value; - }); + OptimizationOptions(const std::string& command, + const std::string& description) + : ToolOptions(command, description) { + (*this) + .add("", + "-O", + "execute default optimization passes", + Options::Arguments::Zero, + [this](Options*, const std::string&) { + passOptions.setDefaultOptimizationOptions(); + passes.push_back(DEFAULT_OPT_PASSES); + }) + .add("", + "-O0", + "execute no optimization passes", + Options::Arguments::Zero, + [this](Options*, const std::string&) { + passOptions.optimizeLevel = 0; + passOptions.shrinkLevel = 0; + }) + .add("", + "-O1", + "execute -O1 optimization passes (quick&useful opts, useful for " + "iteration builds)", + Options::Arguments::Zero, + [this](Options*, const std::string&) { + passOptions.optimizeLevel = 1; + passOptions.shrinkLevel = 0; + passes.push_back(DEFAULT_OPT_PASSES); + }) + .add( + "", + "-O2", + "execute -O2 optimization passes (most opts, generally gets most perf)", + Options::Arguments::Zero, + [this](Options*, const std::string&) { + passOptions.optimizeLevel = 2; + passOptions.shrinkLevel = 0; + passes.push_back(DEFAULT_OPT_PASSES); + }) + .add("", + "-O3", + "execute -O3 optimization passes (spends potentially a lot of time " + "optimizing)", + Options::Arguments::Zero, + [this](Options*, const std::string&) { + passOptions.optimizeLevel = 3; + passOptions.shrinkLevel = 0; + passes.push_back(DEFAULT_OPT_PASSES); + }) + .add("", + "-O4", + "execute -O4 optimization passes (also flatten the IR, which can " + "take a lot more time and memory, but is useful on more nested / " + "complex / less-optimized input)", + Options::Arguments::Zero, + [this](Options*, const std::string&) { + passOptions.optimizeLevel = 4; + passOptions.shrinkLevel = 0; + passes.push_back(DEFAULT_OPT_PASSES); + }) + .add("", + "-Os", + "execute default optimization passes, focusing on code size", + Options::Arguments::Zero, + [this](Options*, const std::string&) { + passOptions.optimizeLevel = 2; + passOptions.shrinkLevel = 1; + passes.push_back(DEFAULT_OPT_PASSES); + }) + .add("", + "-Oz", + "execute default optimization passes, super-focusing on code size", + Options::Arguments::Zero, + [this](Options*, const std::string&) { + passOptions.optimizeLevel = 2; + passOptions.shrinkLevel = 2; + passes.push_back(DEFAULT_OPT_PASSES); + }) + .add("--optimize-level", + "-ol", + "How much to focus on optimizing code", + Options::Arguments::One, + [this](Options* o, const std::string& argument) { + passOptions.optimizeLevel = atoi(argument.c_str()); + }) + .add("--shrink-level", + "-s", + "How much to focus on shrinking code size", + Options::Arguments::One, + [this](Options* o, const std::string& argument) { + passOptions.shrinkLevel = atoi(argument.c_str()); + }) + .add("--ignore-implicit-traps", + "-iit", + "Optimize under the helpful assumption that no surprising traps " + "occur (from load, div/mod, etc.)", + Options::Arguments::Zero, + [this](Options*, const std::string&) { + passOptions.ignoreImplicitTraps = true; + }) + .add("--low-memory-unused", + "-lmu", + "Optimize under the helpful assumption that the low 1K of memory is " + "not used by the application", + Options::Arguments::Zero, + [this](Options*, const std::string&) { + passOptions.lowMemoryUnused = true; + }) + .add("--pass-arg", + "-pa", + "An argument passed along to optimization passes being run. Must be " + "in the form KEY:VALUE", + Options::Arguments::N, + [this](Options*, const std::string& argument) { + auto colon = argument.find(':'); + if (colon == std::string::npos) { + Fatal() << "--pass-arg value must be in the form of KEY:VALUE"; + } + auto key = argument.substr(0, colon); + auto value = argument.substr(colon + 1); + passOptions.arguments[key] = value; + }); // add passes in registry for (const auto& p : PassRegistry::get()->getRegisteredNames()) { (*this).add( - std::string("--") + p, "", PassRegistry::get()->getPassDescription(p), + std::string("--") + p, + "", + PassRegistry::get()->getPassDescription(p), Options::Arguments::Zero, - [this, p](Options*, const std::string&) { - passes.push_back(p); - } - ); + [this, p](Options*, const std::string&) { passes.push_back(p); }); } } @@ -134,13 +173,12 @@ struct OptimizationOptions : public ToolOptions { return false; } - bool runningPasses() { - return passes.size() > 0; - } + bool runningPasses() { return passes.size() > 0; } void runPasses(Module& wasm) { PassRunner passRunner(&wasm, passOptions); - if (debug) passRunner.setDebug(true); + if (debug) + passRunner.setDebug(true); for (auto& pass : passes) { if (pass == DEFAULT_OPT_PASSES) { passRunner.addDefaultOptimizationPasses(); @@ -153,3 +191,5 @@ struct OptimizationOptions : public ToolOptions { }; } // namespace wasm + +#endif diff --git a/src/tools/spec-wrapper.h b/src/tools/spec-wrapper.h index 77db8a0f4..516ce17a9 100644 --- a/src/tools/spec-wrapper.h +++ b/src/tools/spec-wrapper.h @@ -25,18 +25,31 @@ static std::string generateSpecWrapper(Module& wasm) { std::string ret; for (auto& exp : wasm.exports) { auto* func = wasm.getFunctionOrNull(exp->value); - if (!func) continue; // something exported other than a function - ret += std::string("(invoke \"hangLimitInitializer\") (invoke \"") + exp->name.str + "\" "; + if (!func) + continue; // something exported other than a function + ret += std::string("(invoke \"hangLimitInitializer\") (invoke \"") + + exp->name.str + "\" "; for (Type param : func->params) { // zeros in arguments TODO more? switch (param) { - case i32: ret += "(i32.const 0)"; break; - case i64: ret += "(i64.const 0)"; break; - case f32: ret += "(f32.const 0)"; break; - case f64: ret += "(f64.const 0)"; break; - case v128: ret += "(v128.const i32x4 0 0 0 0)"; break; + case i32: + ret += "(i32.const 0)"; + break; + case i64: + ret += "(i64.const 0)"; + break; + case f32: + ret += "(f32.const 0)"; + break; + case f64: + ret += "(f64.const 0)"; + break; + case v128: + ret += "(v128.const i32x4 0 0 0 0)"; + break; case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } ret += " "; } diff --git a/src/tools/tool-options.h b/src/tools/tool-options.h index 02572d72e..1f13063c5 100644 --- a/src/tools/tool-options.h +++ b/src/tools/tool-options.h @@ -14,9 +14,12 @@ * limitations under the License. */ +#ifndef wasm_tools_tool_options_h +#define wasm_tools_tool_options_h + #include "ir/module-utils.h" -#include "support/command-line.h" #include "pass.h" +#include "support/command-line.h" // // Shared optimization options for commandline tools @@ -28,64 +31,74 @@ struct ToolOptions : public Options { PassOptions passOptions; ToolOptions(const std::string& command, const std::string& description) - : Options(command, description) { + : Options(command, description) { (*this) - .add("--mvp-features", "-mvp", "Disable all non-MVP features", - Arguments::Zero, - [this](Options*, const std::string&) { - hasFeatureOptions = true; - enabledFeatures.makeMVP(); - disabledFeatures.setAll(); - }) - .add("--all-features", "-all", "Enable all features", - Arguments::Zero, - [this](Options*, const std::string&) { - hasFeatureOptions = true; - enabledFeatures.setAll(); - disabledFeatures.makeMVP(); - }) - .add("--detect-features", "", - "Use features from the target features section, or MVP (default)", - Arguments::Zero, - [this](Options*, const std::string&) { - hasFeatureOptions = true; - detectFeatures = true; - enabledFeatures.makeMVP(); - disabledFeatures.makeMVP(); - }); + .add("--mvp-features", + "-mvp", + "Disable all non-MVP features", + Arguments::Zero, + [this](Options*, const std::string&) { + hasFeatureOptions = true; + enabledFeatures.makeMVP(); + disabledFeatures.setAll(); + }) + .add("--all-features", + "-all", + "Enable all features", + Arguments::Zero, + [this](Options*, const std::string&) { + hasFeatureOptions = true; + enabledFeatures.setAll(); + disabledFeatures.makeMVP(); + }) + .add("--detect-features", + "", + "Use features from the target features section, or MVP (default)", + Arguments::Zero, + [this](Options*, const std::string&) { + hasFeatureOptions = true; + detectFeatures = true; + enabledFeatures.makeMVP(); + disabledFeatures.makeMVP(); + }); (*this) - .addFeature(FeatureSet::SignExt, "sign extension operations") - .addFeature(FeatureSet::Atomics, "atomic operations") - .addFeature(FeatureSet::MutableGlobals, "mutable globals") - .addFeature(FeatureSet::TruncSat, "nontrapping float-to-int operations") - .addFeature(FeatureSet::SIMD, "SIMD operations and types") - .addFeature(FeatureSet::BulkMemory, "bulk memory operations") - .add("--no-validation", "-n", - "Disables validation, assumes inputs are correct", - Options::Arguments::Zero, - [this](Options* o, const std::string& argument) { - passOptions.validate = false; - }); + .addFeature(FeatureSet::SignExt, "sign extension operations") + .addFeature(FeatureSet::Atomics, "atomic operations") + .addFeature(FeatureSet::MutableGlobals, "mutable globals") + .addFeature(FeatureSet::TruncSat, "nontrapping float-to-int operations") + .addFeature(FeatureSet::SIMD, "SIMD operations and types") + .addFeature(FeatureSet::BulkMemory, "bulk memory operations") + .add("--no-validation", + "-n", + "Disables validation, assumes inputs are correct", + Options::Arguments::Zero, + [this](Options* o, const std::string& argument) { + passOptions.validate = false; + }); } ToolOptions& addFeature(FeatureSet::Feature feature, const std::string& description) { (*this) - .add(std::string("--enable-") + FeatureSet::toString(feature), "", - std::string("Enable ") + description, Arguments::Zero, - [=](Options*, const std::string&) { - hasFeatureOptions = true; - enabledFeatures.set(feature, true); - disabledFeatures.set(feature, false); - }) + .add(std::string("--enable-") + FeatureSet::toString(feature), + "", + std::string("Enable ") + description, + Arguments::Zero, + [=](Options*, const std::string&) { + hasFeatureOptions = true; + enabledFeatures.set(feature, true); + disabledFeatures.set(feature, false); + }) - .add(std::string("--disable-") + FeatureSet::toString(feature), "", - std::string("Disable ") + description, Arguments::Zero, - [=](Options*, const std::string&) { - hasFeatureOptions = true; - enabledFeatures.set(feature, false); - disabledFeatures.set(feature, true); - }); + .add(std::string("--disable-") + FeatureSet::toString(feature), + "", + std::string("Disable ") + description, + Arguments::Zero, + [=](Options*, const std::string&) { + hasFeatureOptions = true; + enabledFeatures.set(feature, false); + disabledFeatures.set(feature, true); + }); return *this; } @@ -113,3 +126,5 @@ private: }; } // namespace wasm + +#endif diff --git a/src/tools/tool-utils.h b/src/tools/tool-utils.h index a897e01f0..9e010ae3a 100644 --- a/src/tools/tool-utils.h +++ b/src/tools/tool-utils.h @@ -34,4 +34,3 @@ inline std::string removeSpecificSuffix(std::string str, std::string suffix) { } } // namespace wasm - diff --git a/src/tools/wasm-as.cpp b/src/tools/wasm-as.cpp index 20e51e31f..253a93b3b 100644 --- a/src/tools/wasm-as.cpp +++ b/src/tools/wasm-as.cpp @@ -30,61 +30,87 @@ using namespace cashew; using namespace wasm; -int main(int argc, const char *argv[]) { +int main(int argc, const char* argv[]) { bool debugInfo = false; std::string symbolMap; std::string sourceMapFilename; std::string sourceMapUrl; - ToolOptions options("wasm-as", "Assemble a .wast (WebAssembly text format) into a .wasm (WebAssembly binary format)"); + ToolOptions options("wasm-as", + "Assemble a .wast (WebAssembly text format) into a .wasm " + "(WebAssembly binary format)"); options.extra["validate"] = "wasm"; options - .add("--output", "-o", "Output file (stdout if not specified)", - Options::Arguments::One, - [](Options *o, const std::string& argument) { - o->extra["output"] = argument; - Colors::disable(); - }) - .add("--validate", "-v", "Control validation of the output module", - Options::Arguments::One, - [](Options *o, const std::string& argument) { - if (argument != "web" && argument != "none" && argument != "wasm") { - std::cerr << "Valid arguments for --validate flag are 'wasm', 'web', and 'none'.\n"; - exit(1); - } - o->extra["validate"] = argument; - }) - .add("--debuginfo", "-g", "Emit names section and debug info", - Options::Arguments::Zero, - [&](Options *o, const std::string& arguments) { debugInfo = true; }) - .add("--source-map", "-sm", "Emit source map to the specified file", - Options::Arguments::One, - [&sourceMapFilename](Options *o, const std::string& argument) { sourceMapFilename = argument; }) - .add("--source-map-url", "-su", "Use specified string as source map URL", - Options::Arguments::One, - [&sourceMapUrl](Options *o, const std::string& argument) { sourceMapUrl = argument; }) - .add("--symbolmap", "-s", "Emit a symbol map (indexes => names)", - Options::Arguments::One, - [&](Options *o, const std::string& argument) { symbolMap = argument; }) - .add_positional("INFILE", Options::Arguments::One, - [](Options *o, const std::string& argument) { - o->extra["infile"] = argument; - }); + .add("--output", + "-o", + "Output file (stdout if not specified)", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["output"] = argument; + Colors::disable(); + }) + .add("--validate", + "-v", + "Control validation of the output module", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + if (argument != "web" && argument != "none" && argument != "wasm") { + std::cerr << "Valid arguments for --validate flag are 'wasm', " + "'web', and 'none'.\n"; + exit(1); + } + o->extra["validate"] = argument; + }) + .add("--debuginfo", + "-g", + "Emit names section and debug info", + Options::Arguments::Zero, + [&](Options* o, const std::string& arguments) { debugInfo = true; }) + .add("--source-map", + "-sm", + "Emit source map to the specified file", + Options::Arguments::One, + [&sourceMapFilename](Options* o, const std::string& argument) { + sourceMapFilename = argument; + }) + .add("--source-map-url", + "-su", + "Use specified string as source map URL", + Options::Arguments::One, + [&sourceMapUrl](Options* o, const std::string& argument) { + sourceMapUrl = argument; + }) + .add("--symbolmap", + "-s", + "Emit a symbol map (indexes => names)", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { symbolMap = argument; }) + .add_positional("INFILE", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["infile"] = argument; + }); options.parse(argc, argv); // default output is infile with changed suffix if (options.extra.find("output") == options.extra.end()) { - options.extra["output"] = removeSpecificSuffix(options.extra["infile"], ".wast") + ".wasm"; + options.extra["output"] = + removeSpecificSuffix(options.extra["infile"], ".wast") + ".wasm"; } - auto input(read_file<std::string>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release)); + auto input( + read_file<std::string>(options.extra["infile"], + Flags::Text, + options.debug ? Flags::Debug : Flags::Release)); Module wasm; try { - if (options.debug) std::cerr << "s-parsing..." << std::endl; + if (options.debug) + std::cerr << "s-parsing..." << std::endl; SExpressionParser parser(const_cast<char*>(input.c_str())); Element& root = *parser.root; - if (options.debug) std::cerr << "w-parsing..." << std::endl; + if (options.debug) + std::cerr << "w-parsing..." << std::endl; SExpressionWasmBuilder builder(wasm, *root[0]); } catch (ParseException& p) { p.dump(std::cerr); @@ -94,15 +120,19 @@ int main(int argc, const char *argv[]) { options.applyFeatures(wasm); if (options.extra["validate"] != "none") { - if (options.debug) std::cerr << "Validating..." << std::endl; - if (!wasm::WasmValidator().validate(wasm, - WasmValidator::Globally | (options.extra["validate"] == "web" ? WasmValidator::Web : 0))) { + if (options.debug) + std::cerr << "Validating..." << std::endl; + if (!wasm::WasmValidator().validate( + wasm, + WasmValidator::Globally | + (options.extra["validate"] == "web" ? WasmValidator::Web : 0))) { WasmPrinter::printModule(&wasm); Fatal() << "Error: input module is not valid.\n"; } } - if (options.debug) std::cerr << "writing..." << std::endl; + if (options.debug) + std::cerr << "writing..." << std::endl; ModuleWriter writer; writer.setBinary(true); writer.setDebugInfo(debugInfo); @@ -115,5 +145,6 @@ int main(int argc, const char *argv[]) { } writer.write(wasm, options.extra["output"]); - if (options.debug) std::cerr << "Done." << std::endl; + if (options.debug) + std::cerr << "Done." << std::endl; } diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 420ec06ad..2050b3ebc 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -24,19 +24,19 @@ #include <memory> +#include "ir/global-utils.h" +#include "ir/import-utils.h" +#include "ir/literal-utils.h" +#include "ir/memory-utils.h" +#include "ir/module-utils.h" #include "pass.h" -#include "support/file.h" #include "support/colors.h" +#include "support/file.h" #include "tool-options.h" -#include "wasm-io.h" -#include "wasm-interpreter.h" #include "wasm-builder.h" +#include "wasm-interpreter.h" +#include "wasm-io.h" #include "wasm-validator.h" -#include "ir/memory-utils.h" -#include "ir/global-utils.h" -#include "ir/import-utils.h" -#include "ir/literal-utils.h" -#include "ir/module-utils.h" using namespace wasm; @@ -57,13 +57,9 @@ class EvallingGlobalManager { bool sealed = false; public: - void addDangerous(Name name) { - dangerousGlobals.insert(name); - } + void addDangerous(Name name) { dangerousGlobals.insert(name); } - void seal() { - sealed = true; - } + void seal() { sealed = true; } // for equality purposes, we just care about the globals // and whether they have changed @@ -78,9 +74,13 @@ public: if (dangerousGlobals.count(name) > 0) { std::string extra; if (name == "___dso_handle") { - extra = "\nrecommendation: build with -s NO_EXIT_RUNTIME=1 so that calls to atexit that use ___dso_handle are not emitted"; + extra = "\nrecommendation: build with -s NO_EXIT_RUNTIME=1 so that " + "calls to atexit that use ___dso_handle are not emitted"; } - throw FailToEvalException(std::string("tried to access a dangerous (import-initialized) global: ") + name.str + extra); + throw FailToEvalException( + std::string( + "tried to access a dangerous (import-initialized) global: ") + + name.str + extra); } return globals[name]; } @@ -91,14 +91,14 @@ public: bool found; Iterator() : found(false) {} - Iterator(Name name, Literal value) : first(name), second(value), found(true) {} + Iterator(Name name, Literal value) + : first(name), second(value), found(true) {} bool operator==(const Iterator& other) { - return first == other.first && second == other.second && found == other.found; - } - bool operator!=(const Iterator& other) { - return !(*this == other); + return first == other.first && second == other.second && + found == other.found; } + bool operator!=(const Iterator& other) { return !(*this == other); } }; Iterator find(Name name) { @@ -108,9 +108,7 @@ public: return Iterator(name, globals[name]); } - Iterator end() { - return Iterator(); - } + Iterator end() { return Iterator(); } }; // Use a ridiculously large stack size. @@ -125,25 +123,28 @@ static Index STACK_START = 1024 * 1024 * 1024 + STACK_SIZE; static Index STACK_LOWER_LIMIT = STACK_START - STACK_SIZE; static Index STACK_UPPER_LIMIT = STACK_START + STACK_SIZE; -class EvallingModuleInstance : public ModuleInstanceBase<EvallingGlobalManager, EvallingModuleInstance> { +class EvallingModuleInstance + : public ModuleInstanceBase<EvallingGlobalManager, EvallingModuleInstance> { public: - EvallingModuleInstance(Module& wasm, ExternalInterface* externalInterface) : ModuleInstanceBase(wasm, externalInterface) { - // if any global in the module has a non-const constructor, it is using a global import, - // which we don't have, and is illegal to use + EvallingModuleInstance(Module& wasm, ExternalInterface* externalInterface) + : ModuleInstanceBase(wasm, externalInterface) { + // if any global in the module has a non-const constructor, it is using a + // global import, which we don't have, and is illegal to use ModuleUtils::iterDefinedGlobals(wasm, [&](Global* global) { if (!global->init->is<Const>()) { // some constants are ok to use if (auto* get = global->init->dynCast<GetGlobal>()) { auto name = get->name; auto* import = wasm.getGlobal(name); - if (import->module == Name(ENV) && ( - import->base == STACKTOP || // stack constants are special, we handle them - import->base == STACK_MAX - )) { + if (import->module == Name(ENV) && + (import->base == + STACKTOP || // stack constants are special, we handle them + import->base == STACK_MAX)) { return; // this is fine } } - // this global is dangerously initialized by an import, so if it is used, we must fail + // this global is dangerously initialized by an import, so if it is + // used, we must fail globals.addDangerous(global->name); } }); @@ -177,13 +178,15 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { ImportInfo imports(wasm_); if (auto* stackTop = imports.getImportedGlobal(ENV, STACKTOP)) { globals[stackTop->name] = Literal(int32_t(STACK_START)); - if (auto* stackTop = GlobalUtils::getGlobalInitializedToImport(wasm_, ENV, STACKTOP)) { + if (auto* stackTop = + GlobalUtils::getGlobalInitializedToImport(wasm_, ENV, STACKTOP)) { globals[stackTop->name] = Literal(int32_t(STACK_START)); } } if (auto* stackMax = imports.getImportedGlobal(ENV, STACK_MAX)) { globals[stackMax->name] = Literal(int32_t(STACK_START)); - if (auto* stackMax = GlobalUtils::getGlobalInitializedToImport(wasm_, ENV, STACK_MAX)) { + if (auto* stackMax = + GlobalUtils::getGlobalInitializedToImport(wasm_, ENV, STACK_MAX)) { globals[stackMax->name] = Literal(int32_t(STACK_START)); } } @@ -203,19 +206,26 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { Literal callImport(Function* import, LiteralList& arguments) override { std::string extra; if (import->module == ENV && import->base == "___cxa_atexit") { - extra = "\nrecommendation: build with -s NO_EXIT_RUNTIME=1 so that calls to atexit are not emitted"; + extra = "\nrecommendation: build with -s NO_EXIT_RUNTIME=1 so that calls " + "to atexit are not emitted"; } - throw FailToEvalException(std::string("call import: ") + import->module.str + "." + import->base.str + extra); + throw FailToEvalException(std::string("call import: ") + + import->module.str + "." + import->base.str + + extra); } - Literal callTable(Index index, LiteralList& arguments, Type result, EvallingModuleInstance& instance) override { + Literal callTable(Index index, + LiteralList& arguments, + Type result, + EvallingModuleInstance& instance) override { // we assume the table is not modified (hmm) // look through the segments, try to find the function for (auto& segment : wasm->table.segments) { Index start; - // look for the index in this segment. if it has a constant offset, we look in - // the proper range. if it instead gets a global, we rely on the fact that when - // not dynamically linking then the table is loaded at offset 0. + // look for the index in this segment. if it has a constant offset, we + // look in the proper range. if it instead gets a global, we rely on the + // fact that when not dynamically linking then the table is loaded at + // offset 0. if (auto* c = segment.offset->dynCast<Const>()) { start = c->value.getInteger(); } else if (segment.offset->is<GetGlobal>()) { @@ -226,16 +236,20 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { auto end = start + segment.data.size(); if (start <= index && index < end) { auto name = segment.data[index - start]; - // if this is one of our functions, we can call it; if it was imported, fail + // if this is one of our functions, we can call it; if it was imported, + // fail auto* func = wasm->getFunction(name); if (!func->imported()) { return instance.callFunctionInternal(name, arguments); } else { - throw FailToEvalException(std::string("callTable on imported function: ") + name.str); + throw FailToEvalException( + std::string("callTable on imported function: ") + name.str); } } } - throw FailToEvalException(std::string("callTable on index not found in static segments: ") + std::to_string(index)); + throw FailToEvalException( + std::string("callTable on index not found in static segments: ") + + std::to_string(index)); } int8_t load8s(Address addr) override { return doLoad<int8_t>(addr); } @@ -247,13 +261,21 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { int64_t load64s(Address addr) override { return doLoad<int64_t>(addr); } uint64_t load64u(Address addr) override { return doLoad<uint64_t>(addr); } - void store8(Address addr, int8_t value) override { doStore<int8_t>(addr, value); } - void store16(Address addr, int16_t value) override { doStore<int16_t>(addr, value); } - void store32(Address addr, int32_t value) override { doStore<int32_t>(addr, value); } - void store64(Address addr, int64_t value) override { doStore<int64_t>(addr, value); } + void store8(Address addr, int8_t value) override { + doStore<int8_t>(addr, value); + } + void store16(Address addr, int16_t value) override { + doStore<int16_t>(addr, value); + } + void store32(Address addr, int32_t value) override { + doStore<int32_t>(addr, value); + } + void store64(Address addr, int64_t value) override { + doStore<int64_t>(addr, value); + } // called during initialization, but we don't keep track of a table - void tableStore(Address addr, Name value) override { } + void tableStore(Address addr, Name value) override {} void growMemory(Address /*oldSize*/, Address newSize) override { throw FailToEvalException("grow memory"); @@ -266,8 +288,7 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { private: // TODO: handle unaligned too, see shell-interface - template<typename T> - T* getMemory(Address address) { + template<typename T> T* getMemory(Address address) { // if memory is on the stack, use the stack if (address >= STACK_LOWER_LIMIT) { if (address >= STACK_UPPER_LIMIT) { @@ -283,14 +304,11 @@ private: std::vector<char> temp; Builder builder(*wasm); wasm->memory.segments.push_back( - Memory::Segment( - builder.makeConst(Literal(int32_t(0))), - temp - ) - ); + Memory::Segment(builder.makeConst(Literal(int32_t(0))), temp)); } // memory should already have been flattened - assert(wasm->memory.segments[0].offset->cast<Const>()->value.getInteger() == 0); + assert(wasm->memory.segments[0].offset->cast<Const>()->value.getInteger() == + 0); auto max = address + sizeof(T); auto& data = wasm->memory.segments[0].data; if (max > data.size()) { @@ -299,14 +317,12 @@ private: return (T*)(&data[address]); } - template<typename T> - void doStore(Address address, T value) { + template<typename T> void doStore(Address address, T value) { // do a memcpy to avoid undefined behavior if unaligned memcpy(getMemory<T>(address), &value, sizeof(T)); } - template<typename T> - T doLoad(Address address) { + template<typename T> T doLoad(Address address) { // do a memcpy to avoid undefined behavior if unaligned T ret; memcpy(&ret, getMemory<T>(address), sizeof(T)); @@ -337,7 +353,7 @@ void evalCtors(Module& wasm, std::vector<std::string> ctors) { // snapshot globals (note that STACKTOP might be modified, but should // be returned, so that works out) auto globalsBefore = instance.globals; - Export *ex = wasm.getExportOrNull(ctor); + Export* ex = wasm.getExportOrNull(ctor); if (!ex) { Fatal() << "export not found: " << ctor; } @@ -366,7 +382,8 @@ void evalCtors(Module& wasm, std::vector<std::string> ctors) { } } catch (FailToEvalException& fail) { // that's it, we failed to even create the instance - std::cerr << " ...stopping since could not create module instance: " << fail.why << "\n"; + std::cerr << " ...stopping since could not create module instance: " + << fail.why << "\n"; return; } } @@ -382,37 +399,50 @@ int main(int argc, const char* argv[]) { bool debugInfo = false; std::string ctorsString; - ToolOptions options("wasm-ctor-eval", "Execute C++ global constructors ahead of time"); + ToolOptions options("wasm-ctor-eval", + "Execute C++ global constructors ahead of time"); options - .add("--output", "-o", "Output file (stdout if not specified)", - Options::Arguments::One, - [](Options* o, const std::string& argument) { - o->extra["output"] = argument; - Colors::disable(); - }) - .add("--emit-text", "-S", "Emit text instead of binary for the output file", - Options::Arguments::Zero, - [&](Options *o, const std::string& argument) { emitBinary = false; }) - .add("--debuginfo", "-g", "Emit names section and debug info", - Options::Arguments::Zero, - [&](Options *o, const std::string& arguments) { debugInfo = true; }) - .add("--ctors", "-c", "Comma-separated list of global constructor functions to evaluate", - Options::Arguments::One, - [&](Options* o, const std::string& argument) { - ctorsString = argument; - }) - .add_positional("INFILE", Options::Arguments::One, - [](Options* o, const std::string& argument) { - o->extra["infile"] = argument; - }); + .add("--output", + "-o", + "Output file (stdout if not specified)", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["output"] = argument; + Colors::disable(); + }) + .add("--emit-text", + "-S", + "Emit text instead of binary for the output file", + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { emitBinary = false; }) + .add("--debuginfo", + "-g", + "Emit names section and debug info", + Options::Arguments::Zero, + [&](Options* o, const std::string& arguments) { debugInfo = true; }) + .add( + "--ctors", + "-c", + "Comma-separated list of global constructor functions to evaluate", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { ctorsString = argument; }) + .add_positional("INFILE", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["infile"] = argument; + }); options.parse(argc, argv); - auto input(read_file<std::string>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release)); + auto input( + read_file<std::string>(options.extra["infile"], + Flags::Text, + options.debug ? Flags::Debug : Flags::Release)); Module wasm; { - if (options.debug) std::cerr << "reading...\n"; + if (options.debug) + std::cerr << "reading...\n"; ModuleReader reader; reader.setDebug(options.debug); @@ -453,7 +483,8 @@ int main(int argc, const char* argv[]) { } if (options.extra.count("output") > 0) { - if (options.debug) std::cerr << "writing..." << std::endl; + if (options.debug) + std::cerr << "writing..." << std::endl; ModuleWriter writer; writer.setDebug(options.debug); writer.setBinary(emitBinary); diff --git a/src/tools/wasm-dis.cpp b/src/tools/wasm-dis.cpp index 3ff6819a5..5b9cb9804 100644 --- a/src/tools/wasm-dis.cpp +++ b/src/tools/wasm-dis.cpp @@ -27,25 +27,37 @@ using namespace cashew; using namespace wasm; -int main(int argc, const char *argv[]) { +int main(int argc, const char* argv[]) { std::string sourceMapFilename; - Options options("wasm-dis", "Un-assemble a .wasm (WebAssembly binary format) into a .wast (WebAssembly text format)"); - options.add("--output", "-o", "Output file (stdout if not specified)", - Options::Arguments::One, - [](Options *o, const std::string& argument) { - o->extra["output"] = argument; - Colors::disable(); - }) - .add("--source-map", "-sm", "Consume source map from the specified file to add location information", - Options::Arguments::One, - [&sourceMapFilename](Options *o, const std::string& argument) { sourceMapFilename = argument; }) - .add_positional("INFILE", Options::Arguments::One, - [](Options *o, const std::string& argument) { - o->extra["infile"] = argument; - }); + Options options("wasm-dis", + "Un-assemble a .wasm (WebAssembly binary format) into a " + ".wast (WebAssembly text format)"); + options + .add("--output", + "-o", + "Output file (stdout if not specified)", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["output"] = argument; + Colors::disable(); + }) + .add( + "--source-map", + "-sm", + "Consume source map from the specified file to add location information", + Options::Arguments::One, + [&sourceMapFilename](Options* o, const std::string& argument) { + sourceMapFilename = argument; + }) + .add_positional("INFILE", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["infile"] = argument; + }); options.parse(argc, argv); - if (options.debug) std::cerr << "parsing binary..." << std::endl; + if (options.debug) + std::cerr << "parsing binary..." << std::endl; Module wasm; try { ModuleReader().readBinary(options.extra["infile"], wasm, sourceMapFilename); @@ -59,10 +71,14 @@ int main(int argc, const char *argv[]) { Fatal() << "error in parsing wasm source mapping"; } - if (options.debug) std::cerr << "Printing..." << std::endl; - Output output(options.extra["output"], Flags::Text, options.debug ? Flags::Debug : Flags::Release); + if (options.debug) + std::cerr << "Printing..." << std::endl; + Output output(options.extra["output"], + Flags::Text, + options.debug ? Flags::Debug : Flags::Release); WasmPrinter::printModule(&wasm, output.getStream()); output << '\n'; - if (options.debug) std::cerr << "Done." << std::endl; + if (options.debug) + std::cerr << "Done." << std::endl; } diff --git a/src/tools/wasm-emscripten-finalize.cpp b/src/tools/wasm-emscripten-finalize.cpp index 46fb0533b..a23ba1c5a 100644 --- a/src/tools/wasm-emscripten-finalize.cpp +++ b/src/tools/wasm-emscripten-finalize.cpp @@ -21,6 +21,7 @@ #include <exception> +#include "abi/js.h" #include "ir/trapping.h" #include "support/colors.h" #include "support/file.h" @@ -30,12 +31,11 @@ #include "wasm-io.h" #include "wasm-printing.h" #include "wasm-validator.h" -#include "abi/js.h" using namespace cashew; using namespace wasm; -int main(int argc, const char *argv[]) { +int main(int argc, const char* argv[]) { const uint64_t INVALID_BASE = -1; std::string infile; @@ -53,60 +53,86 @@ int main(int argc, const char *argv[]) { ToolOptions options("wasm-emscripten-finalize", "Performs Emscripten-specific transforms on .wasm files"); options - .add("--output", "-o", "Output file", - Options::Arguments::One, - [&outfile](Options*, const std::string& argument) { - outfile = argument; - Colors::disable(); - }) - .add("--debuginfo", "-g", - "Emit names section in wasm binary (or full debuginfo in wast)", - Options::Arguments::Zero, - [&debugInfo](Options *, const std::string &) { - debugInfo = true; - }) - .add("--emit-text", "-S", "Emit text instead of binary for the output file", - Options::Arguments::Zero, - [&emitBinary](Options*, const std::string& ) { - emitBinary = false; - }) - .add("--global-base", "", "The address at which static globals were placed", - Options::Arguments::One, - [&globalBase](Options*, const std::string&argument ) { - globalBase = std::stoull(argument); - }) - .add("--initial-stack-pointer", "", "The initial location of the stack pointer", - Options::Arguments::One, - [&initialStackPointer](Options*, const std::string&argument ) { - initialStackPointer = std::stoull(argument); - }) - .add("--side-module", "", "Input is an emscripten side module", - Options::Arguments::Zero, - [&isSideModule](Options *o, const std::string& argument) { - isSideModule = true; - }) - .add("--input-source-map", "-ism", "Consume source map from the specified file", - Options::Arguments::One, - [&inputSourceMapFilename](Options *o, const std::string& argument) { inputSourceMapFilename = argument; }) - .add("--no-legalize-javascript-ffi", "-nj", "Do not fully legalize (i64->i32, " - "f32->f64) the imports and exports for interfacing with JS", - Options::Arguments::Zero, - [&legalizeJavaScriptFFI](Options *o, const std::string& ) { - legalizeJavaScriptFFI = false; - }) - .add("--output-source-map", "-osm", "Emit source map to the specified file", - Options::Arguments::One, - [&outputSourceMapFilename](Options *o, const std::string& argument) { outputSourceMapFilename = argument; }) - .add("--output-source-map-url", "-osu", "Emit specified string as source map URL", - Options::Arguments::One, - [&outputSourceMapUrl](Options *o, const std::string& argument) { outputSourceMapUrl = argument; }) - .add("--separate-data-segments", "", "Separate data segments to a file", - Options::Arguments::One, - [&dataSegmentFile](Options *o, const std::string& argument) { dataSegmentFile = argument;}) - .add_positional("INFILE", Options::Arguments::One, - [&infile](Options *o, const std::string& argument) { - infile = argument; - }); + .add("--output", + "-o", + "Output file", + Options::Arguments::One, + [&outfile](Options*, const std::string& argument) { + outfile = argument; + Colors::disable(); + }) + .add("--debuginfo", + "-g", + "Emit names section in wasm binary (or full debuginfo in wast)", + Options::Arguments::Zero, + [&debugInfo](Options*, const std::string&) { debugInfo = true; }) + .add("--emit-text", + "-S", + "Emit text instead of binary for the output file", + Options::Arguments::Zero, + [&emitBinary](Options*, const std::string&) { emitBinary = false; }) + .add("--global-base", + "", + "The address at which static globals were placed", + Options::Arguments::One, + [&globalBase](Options*, const std::string& argument) { + globalBase = std::stoull(argument); + }) + .add("--initial-stack-pointer", + "", + "The initial location of the stack pointer", + Options::Arguments::One, + [&initialStackPointer](Options*, const std::string& argument) { + initialStackPointer = std::stoull(argument); + }) + .add("--side-module", + "", + "Input is an emscripten side module", + Options::Arguments::Zero, + [&isSideModule](Options* o, const std::string& argument) { + isSideModule = true; + }) + .add("--input-source-map", + "-ism", + "Consume source map from the specified file", + Options::Arguments::One, + [&inputSourceMapFilename](Options* o, const std::string& argument) { + inputSourceMapFilename = argument; + }) + .add("--no-legalize-javascript-ffi", + "-nj", + "Do not fully legalize (i64->i32, " + "f32->f64) the imports and exports for interfacing with JS", + Options::Arguments::Zero, + [&legalizeJavaScriptFFI](Options* o, const std::string&) { + legalizeJavaScriptFFI = false; + }) + .add("--output-source-map", + "-osm", + "Emit source map to the specified file", + Options::Arguments::One, + [&outputSourceMapFilename](Options* o, const std::string& argument) { + outputSourceMapFilename = argument; + }) + .add("--output-source-map-url", + "-osu", + "Emit specified string as source map URL", + Options::Arguments::One, + [&outputSourceMapUrl](Options* o, const std::string& argument) { + outputSourceMapUrl = argument; + }) + .add("--separate-data-segments", + "", + "Separate data segments to a file", + Options::Arguments::One, + [&dataSegmentFile](Options* o, const std::string& argument) { + dataSegmentFile = argument; + }) + .add_positional("INFILE", + Options::Arguments::One, + [&infile](Options* o, const std::string& argument) { + infile = argument; + }); options.parse(argc, argv); if (infile == "") { @@ -167,10 +193,12 @@ int main(int argc, const char *argv[]) { std::vector<Name> initializerFunctions; if (wasm.table.imported()) { - if (wasm.table.base != "table") wasm.table.base = Name("table"); + if (wasm.table.base != "table") + wasm.table.base = Name("table"); } if (wasm.memory.imported()) { - if (wasm.table.base != "memory") wasm.memory.base = Name("memory"); + if (wasm.table.base != "memory") + wasm.memory.base = Name("memory"); } wasm.updateMaps(); @@ -204,13 +232,13 @@ int main(int argc, const char *argv[]) { passRunner.setDebugInfo(debugInfo); passRunner.add(ABI::getLegalizationPass( legalizeJavaScriptFFI ? ABI::LegalizationLevel::Full - : ABI::LegalizationLevel::Minimal - )); + : ABI::LegalizationLevel::Minimal)); passRunner.run(); } // Substantial changes to the wasm are done, enough to create the metadata. - std::string metadata = generator.generateEmscriptenMetadata(dataSize, initializerFunctions); + std::string metadata = + generator.generateEmscriptenMetadata(dataSize, initializerFunctions); // Finally, separate out data segments if relevant (they may have been needed // for metadata). diff --git a/src/tools/wasm-metadce.cpp b/src/tools/wasm-metadce.cpp index adb623ea0..a6f5bb012 100644 --- a/src/tools/wasm-metadce.cpp +++ b/src/tools/wasm-metadce.cpp @@ -26,14 +26,14 @@ #include <memory> +#include "ir/module-utils.h" #include "pass.h" +#include "support/colors.h" #include "support/command-line.h" #include "support/file.h" #include "support/json.h" -#include "support/colors.h" -#include "wasm-io.h" #include "wasm-builder.h" -#include "ir/module-utils.h" +#include "wasm-io.h" using namespace wasm; @@ -51,9 +51,10 @@ struct MetaDCEGraph { std::unordered_map<Name, DCENode> nodes; std::unordered_set<Name> roots; - std::unordered_map<Name, Name> exportToDCENode; // export exported name => DCE name + // export exported name => DCE name + std::unordered_map<Name, Name> exportToDCENode; std::unordered_map<Name, Name> functionToDCENode; // function name => DCE name - std::unordered_map<Name, Name> globalToDCENode; // global name => DCE name + std::unordered_map<Name, Name> globalToDCENode; // global name => DCE name std::unordered_map<Name, Name> DCENodeToExport; // reverse maps std::unordered_map<Name, Name> DCENodeToFunction; @@ -79,18 +80,20 @@ struct MetaDCEGraph { return getImportId(imp->module, imp->base); } - std::unordered_map<Name, Name> importIdToDCENode; // import module.base => DCE name + // import module.base => DCE name + std::unordered_map<Name, Name> importIdToDCENode; Module& wasm; MetaDCEGraph(Module& wasm) : wasm(wasm) {} - // populate the graph with info from the wasm, integrating with potentially-existing - // nodes for imports and exports that the graph may already contain. + // populate the graph with info from the wasm, integrating with + // potentially-existing nodes for imports and exports that the graph may + // already contain. void scanWebAssembly() { // Add an entry for everything we might need ahead of time, so parallel work - // does not alter parent state, just adds to things pointed by it, independently - // (each thread will add for one function, etc.) + // does not alter parent state, just adds to things pointed by it, + // independently (each thread will add for one function, etc.) ModuleUtils::iterDefinedFunctions(wasm, [&](Function* func) { auto dceName = getName("func", func->name.str); DCENodeToFunction[dceName] = func->name; @@ -103,7 +106,8 @@ struct MetaDCEGraph { globalToDCENode[global->name] = dceName; nodes[dceName] = DCENode(dceName); }); - // only process function and global imports - the table and memory are always there + // only process function and global imports - the table and memory are + // always there ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) { auto id = getImportId(import->module, import->base); if (importIdToDCENode.find(id) == importIdToDCENode.end()) { @@ -131,13 +135,15 @@ struct MetaDCEGraph { if (!wasm.getFunction(exp->value)->imported()) { node.reaches.push_back(functionToDCENode[exp->value]); } else { - node.reaches.push_back(importIdToDCENode[getFunctionImportId(exp->value)]); + node.reaches.push_back( + importIdToDCENode[getFunctionImportId(exp->value)]); } } else if (exp->kind == ExternalKind::Global) { if (!wasm.getGlobal(exp->value)->imported()) { node.reaches.push_back(globalToDCENode[exp->value]); } else { - node.reaches.push_back(importIdToDCENode[getGlobalImportId(exp->value)]); + node.reaches.push_back( + importIdToDCENode[getGlobalImportId(exp->value)]); } } } @@ -145,14 +151,11 @@ struct MetaDCEGraph { // if we provide a parent DCE name, that is who can reach what we see // if none is provided, then it is something we must root struct InitScanner : public PostWalker<InitScanner> { - InitScanner(MetaDCEGraph* parent, Name parentDceName) : parent(parent), parentDceName(parentDceName) {} + InitScanner(MetaDCEGraph* parent, Name parentDceName) + : parent(parent), parentDceName(parentDceName) {} - void visitGetGlobal(GetGlobal* curr) { - handleGlobal(curr->name); - } - void visitSetGlobal(SetGlobal* curr) { - handleGlobal(curr->name); - } + void visitGetGlobal(GetGlobal* curr) { handleGlobal(curr->name); } + void visitSetGlobal(SetGlobal* curr) { handleGlobal(curr->name); } private: MetaDCEGraph* parent; @@ -206,34 +209,29 @@ struct MetaDCEGraph { Scanner(MetaDCEGraph* parent) : parent(parent) {} - Scanner* create() override { - return new Scanner(parent); - } + Scanner* create() override { return new Scanner(parent); } void visitCall(Call* curr) { if (!getModule()->getFunction(curr->target)->imported()) { - parent->nodes[parent->functionToDCENode[getFunction()->name]].reaches.push_back( - parent->functionToDCENode[curr->target] - ); + parent->nodes[parent->functionToDCENode[getFunction()->name]] + .reaches.push_back(parent->functionToDCENode[curr->target]); } else { assert(parent->functionToDCENode.count(getFunction()->name) > 0); - parent->nodes[parent->functionToDCENode[getFunction()->name]].reaches.push_back( - parent->importIdToDCENode[parent->getFunctionImportId(curr->target)] - ); + parent->nodes[parent->functionToDCENode[getFunction()->name]] + .reaches.push_back( + parent + ->importIdToDCENode[parent->getFunctionImportId(curr->target)]); } } - void visitGetGlobal(GetGlobal* curr) { - handleGlobal(curr->name); - } - void visitSetGlobal(SetGlobal* curr) { - handleGlobal(curr->name); - } + void visitGetGlobal(GetGlobal* curr) { handleGlobal(curr->name); } + void visitSetGlobal(SetGlobal* curr) { handleGlobal(curr->name); } private: MetaDCEGraph* parent; void handleGlobal(Name name) { - if (!getFunction()) return; // non-function stuff (initializers) are handled separately + if (!getFunction()) + return; // non-function stuff (initializers) are handled separately Name dceName; if (!getModule()->getGlobal(name)->imported()) { // its a global @@ -242,7 +240,8 @@ struct MetaDCEGraph { // it's an import. dceName = parent->importIdToDCENode[parent->getGlobalImportId(name)]; } - parent->nodes[parent->functionToDCENode[getFunction()->name]].reaches.push_back(dceName); + parent->nodes[parent->functionToDCENode[getFunction()->name]] + .reaches.push_back(dceName); } }; @@ -256,7 +255,8 @@ private: // gets a unique name for the graph Name getName(std::string prefix1, std::string prefix2) { while (1) { - auto curr = Name(prefix1 + '$' + prefix2 + '$' + std::to_string(nameIndex++)); + auto curr = + Name(prefix1 + '$' + prefix2 + '$' + std::to_string(nameIndex++)); if (nodes.find(curr) == nodes.end()) { return curr; } @@ -305,7 +305,8 @@ public: // Now they are gone, standard optimization passes can do the rest! PassRunner passRunner(&wasm); passRunner.add("remove-unused-module-elements"); - passRunner.add("reorder-functions"); // removing functions may alter the optimum order, as # of calls can change + // removing functions may alter the optimum order, as # of calls can change + passRunner.add("reorder-functions"); passRunner.run(); } @@ -344,7 +345,8 @@ public: std::cout << " is import " << importMap[name] << '\n'; } if (DCENodeToExport.find(name) != DCENodeToExport.end()) { - std::cout << " is export " << DCENodeToExport[name].str << ", " << wasm.getExport(DCENodeToExport[name])->value << '\n'; + std::cout << " is export " << DCENodeToExport[name].str << ", " + << wasm.getExport(DCENodeToExport[name])->value << '\n'; } if (DCENodeToFunction.find(name) != DCENodeToFunction.end()) { std::cout << " is function " << DCENodeToFunction[name] << '\n'; @@ -372,86 +374,99 @@ int main(int argc, const char* argv[]) { std::string graphFile; bool dump = false; - Options options("wasm-metadce", "This tool performs dead code elimination (DCE) on a larger space " - "that the wasm module is just a part of. For example, if you have " - "JS and wasm that are connected, this can DCE the combined graph. " - "By doing so, it is able to eliminate wasm module exports, which " - "otherwise regular optimizations cannot.\n\n" - "This tool receives a representation of the reachability graph " - "that the wasm module resides in, which contains abstract nodes " - "and connections showing what they reach. Some of those nodes " - "can represent the wasm module's imports and exports. The tool " - "then completes the graph by adding the internal parts of the " - "module, and does DCE on the entire thing.\n\n" - "This tool will output a wasm module with dead code eliminated, " - "and metadata describing the things in the rest of the graph " - "that can be eliminated as well.\n\n" - "The graph description file should represent the graph in the following " - "JSON-like notation (note, this is not true JSON, things like " - "comments, escaping, single-quotes, etc. are not supported):\n\n" - " [\n" - " {\n" - " \"name\": \"entity1\",\n" - " \"reaches\": [\"entity2, \"entity3\"],\n" - " \"root\": true\n" - " },\n" - " {\n" - " \"name\": \"entity2\",\n" - " \"reaches\": [\"entity1, \"entity4\"]\n" - " },\n" - " {\n" - " \"name\": \"entity3\",\n" - " \"reaches\": [\"entity1\"],\n" - " \"export\": \"export1\"\n" - " },\n" - " {\n" - " \"name\": \"entity4\",\n" - " \"import\": [\"module\", \"import1\"]\n" - " },\n" - " ]\n\n" - "Each entity has a name and an optional list of the other " - "entities it reaches. It can also be marked as a root, " - "export (with the export string), or import (with the " - "module and import strings). DCE then computes what is " - "reachable from the roots."); + Options options( + "wasm-metadce", + "This tool performs dead code elimination (DCE) on a larger space " + "that the wasm module is just a part of. For example, if you have " + "JS and wasm that are connected, this can DCE the combined graph. " + "By doing so, it is able to eliminate wasm module exports, which " + "otherwise regular optimizations cannot.\n\n" + "This tool receives a representation of the reachability graph " + "that the wasm module resides in, which contains abstract nodes " + "and connections showing what they reach. Some of those nodes " + "can represent the wasm module's imports and exports. The tool " + "then completes the graph by adding the internal parts of the " + "module, and does DCE on the entire thing.\n\n" + "This tool will output a wasm module with dead code eliminated, " + "and metadata describing the things in the rest of the graph " + "that can be eliminated as well.\n\n" + "The graph description file should represent the graph in the following " + "JSON-like notation (note, this is not true JSON, things like " + "comments, escaping, single-quotes, etc. are not supported):\n\n" + " [\n" + " {\n" + " \"name\": \"entity1\",\n" + " \"reaches\": [\"entity2, \"entity3\"],\n" + " \"root\": true\n" + " },\n" + " {\n" + " \"name\": \"entity2\",\n" + " \"reaches\": [\"entity1, \"entity4\"]\n" + " },\n" + " {\n" + " \"name\": \"entity3\",\n" + " \"reaches\": [\"entity1\"],\n" + " \"export\": \"export1\"\n" + " },\n" + " {\n" + " \"name\": \"entity4\",\n" + " \"import\": [\"module\", \"import1\"]\n" + " },\n" + " ]\n\n" + "Each entity has a name and an optional list of the other " + "entities it reaches. It can also be marked as a root, " + "export (with the export string), or import (with the " + "module and import strings). DCE then computes what is " + "reachable from the roots."); options - .add("--output", "-o", "Output file (stdout if not specified)", - Options::Arguments::One, - [](Options* o, const std::string& argument) { - o->extra["output"] = argument; - Colors::disable(); - }) - .add("--emit-text", "-S", "Emit text instead of binary for the output file", - Options::Arguments::Zero, - [&](Options *o, const std::string& argument) { emitBinary = false; }) - .add("--debuginfo", "-g", "Emit names section and debug info", - Options::Arguments::Zero, - [&](Options *o, const std::string& arguments) { debugInfo = true; }) - .add("--graph-file", "-f", "Filename of the graph description file", - Options::Arguments::One, - [&](Options* o, const std::string& argument) { - graphFile = argument; - }) - .add("--dump", "-d", "Dump the combined graph file (useful for debugging)", - Options::Arguments::Zero, - [&](Options *o, const std::string& arguments) { dump = true; }) - .add_positional("INFILE", Options::Arguments::One, - [](Options* o, const std::string& argument) { - o->extra["infile"] = argument; - }); + .add("--output", + "-o", + "Output file (stdout if not specified)", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["output"] = argument; + Colors::disable(); + }) + .add("--emit-text", + "-S", + "Emit text instead of binary for the output file", + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { emitBinary = false; }) + .add("--debuginfo", + "-g", + "Emit names section and debug info", + Options::Arguments::Zero, + [&](Options* o, const std::string& arguments) { debugInfo = true; }) + .add("--graph-file", + "-f", + "Filename of the graph description file", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { graphFile = argument; }) + .add("--dump", + "-d", + "Dump the combined graph file (useful for debugging)", + Options::Arguments::Zero, + [&](Options* o, const std::string& arguments) { dump = true; }) + .add_positional("INFILE", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["infile"] = argument; + }); options.parse(argc, argv); if (graphFile.size() == 0) { Fatal() << "no graph file provided."; } - auto input(read_file<std::string>(options.extra["infile"], Flags::Text, Flags::Release)); + auto input(read_file<std::string>( + options.extra["infile"], Flags::Text, Flags::Release)); Module wasm; { - if (options.debug) std::cerr << "reading...\n"; + if (options.debug) + std::cerr << "reading...\n"; ModuleReader reader; reader.setDebug(options.debug); @@ -463,32 +478,36 @@ int main(int argc, const char* argv[]) { } } - auto graphInput(read_file<std::string>(graphFile, Flags::Text, Flags::Release)); + auto graphInput( + read_file<std::string>(graphFile, Flags::Text, Flags::Release)); auto* copy = strdup(graphInput.c_str()); json::Value outside; outside.parse(copy); // parse the JSON into our graph, doing all the JSON parsing here, leaving // the abstract computation for the class itself - const json::IString NAME("name"), - REACHES("reaches"), - ROOT("root"), - EXPORT("export"), - IMPORT("import"); + const json::IString NAME("name"); + const json::IString REACHES("reaches"); + const json::IString ROOT("root"); + const json::IString EXPORT("export"); + const json::IString IMPORT("import"); MetaDCEGraph graph(wasm); if (!outside.isArray()) { - Fatal() << "input graph must be a JSON array of nodes. see --help for the form"; + Fatal() + << "input graph must be a JSON array of nodes. see --help for the form"; } auto size = outside.size(); for (size_t i = 0; i < size; i++) { json::Ref ref = outside[i]; if (!ref->isObject()) { - Fatal() << "nodes in input graph must be JSON objects. see --help for the form"; + Fatal() + << "nodes in input graph must be JSON objects. see --help for the form"; } if (!ref->has(NAME)) { - Fatal() << "nodes in input graph must have a name. see --help for the form"; + Fatal() + << "nodes in input graph must have a name. see --help for the form"; } DCENode node(ref[NAME]->getIString()); if (ref->has(REACHES)) { @@ -500,7 +519,8 @@ int main(int argc, const char* argv[]) { for (size_t j = 0; j < size; j++) { json::Ref name = reaches[j]; if (!name->isString()) { - Fatal() << "node.reaches items must be strings. see --help for the form"; + Fatal() + << "node.reaches items must be strings. see --help for the form"; } node.reaches.push_back(name->getIString()); } @@ -508,22 +528,26 @@ int main(int argc, const char* argv[]) { if (ref->has(ROOT)) { json::Ref root = ref[ROOT]; if (!root->isBool() || !root->getBool()) { - Fatal() << "node.root, if it exists, must be true. see --help for the form"; + Fatal() + << "node.root, if it exists, must be true. see --help for the form"; } graph.roots.insert(node.name); } if (ref->has(EXPORT)) { json::Ref exp = ref[EXPORT]; if (!exp->isString()) { - Fatal() << "node.export, if it exists, must be a string. see --help for the form"; + Fatal() << "node.export, if it exists, must be a string. see --help " + "for the form"; } graph.exportToDCENode[exp->getIString()] = node.name; graph.DCENodeToExport[node.name] = exp->getIString(); } if (ref->has(IMPORT)) { json::Ref imp = ref[IMPORT]; - if (!imp->isArray() || imp->size() != 2 || !imp[0]->isString() || !imp[1]->isString()) { - Fatal() << "node.import, if it exists, must be an array of two strings. see --help for the form"; + if (!imp->isArray() || imp->size() != 2 || !imp[0]->isString() || + !imp[1]->isString()) { + Fatal() << "node.import, if it exists, must be an array of two " + "strings. see --help for the form"; } auto id = graph.getImportId(imp[0]->getIString(), imp[1]->getIString()); graph.importIdToDCENode[id] = node.name; diff --git a/src/tools/wasm-opt.cpp b/src/tools/wasm-opt.cpp index 519713298..e9f369d22 100644 --- a/src/tools/wasm-opt.cpp +++ b/src/tools/wasm-opt.cpp @@ -21,21 +21,21 @@ #include <memory> +#include "execution-results.h" +#include "fuzzing.h" +#include "js-wrapper.h" +#include "optimization-options.h" #include "pass.h" +#include "shell-interface.h" +#include "spec-wrapper.h" #include "support/command-line.h" #include "support/file.h" +#include "wasm-binary.h" +#include "wasm-interpreter.h" +#include "wasm-io.h" #include "wasm-printing.h" #include "wasm-s-parser.h" #include "wasm-validator.h" -#include "wasm-io.h" -#include "wasm-interpreter.h" -#include "wasm-binary.h" -#include "shell-interface.h" -#include "optimization-options.h" -#include "execution-results.h" -#include "fuzzing.h" -#include "js-wrapper.h" -#include "spec-wrapper.h" using namespace wasm; @@ -45,7 +45,7 @@ std::string runCommand(std::string command) { std::string output; const int MAX_BUFFER = 1024; char buffer[MAX_BUFFER]; - FILE *stream = popen(command.c_str(), "r"); + FILE* stream = popen(command.c_str(), "r"); while (fgets(buffer, MAX_BUFFER, stream) != NULL) { output.append(buffer); } @@ -81,69 +81,130 @@ int main(int argc, const char* argv[]) { OptimizationOptions options("wasm-opt", "Read, write, and optimize files"); options - .add("--output", "-o", "Output file (stdout if not specified)", - Options::Arguments::One, - [](Options* o, const std::string& argument) { - o->extra["output"] = argument; - Colors::disable(); - }) - .add("--emit-text", "-S", "Emit text instead of binary for the output file", - Options::Arguments::Zero, - [&](Options *o, const std::string& argument) { emitBinary = false; }) - .add("--debuginfo", "-g", "Emit names section and debug info", - Options::Arguments::Zero, - [&](Options *o, const std::string& arguments) { debugInfo = true; }) - .add("--converge", "-c", "Run passes to convergence, continuing while binary size decreases", - Options::Arguments::Zero, - [&](Options *o, const std::string& arguments) { converge = true; }) - .add("--fuzz-exec-before", "-feh", "Execute functions before optimization, helping fuzzing find bugs", - Options::Arguments::Zero, - [&](Options *o, const std::string& arguments) { fuzzExecBefore = true; }) - .add("--fuzz-exec", "-fe", "Execute functions before and after optimization, helping fuzzing find bugs", - Options::Arguments::Zero, - [&](Options *o, const std::string& arguments) { fuzzExecBefore = fuzzExecAfter = true; }) - .add("--fuzz-binary", "-fb", "Convert to binary and back after optimizations and before fuzz-exec, helping fuzzing find binary format bugs", - Options::Arguments::Zero, - [&](Options *o, const std::string& arguments) { fuzzBinary = true; }) - .add("--extra-fuzz-command", "-efc", "An extra command to run on the output before and after optimizing. The output is compared between the two, and an error occurs if they are not equal", - Options::Arguments::One, - [&](Options *o, const std::string& arguments) { extraFuzzCommand = arguments; }) - .add("--translate-to-fuzz", "-ttf", "Translate the input into a valid wasm module *somehow*, useful for fuzzing", - Options::Arguments::Zero, - [&](Options *o, const std::string& arguments) { translateToFuzz = true; }) - .add("--fuzz-passes", "-fp", "Pick a random set of passes to run, useful for fuzzing. this depends on translate-to-fuzz (it picks the passes from the input)", - Options::Arguments::Zero, - [&](Options *o, const std::string& arguments) { fuzzPasses = true; }) - .add("--no-fuzz-nans", "", "don't emit NaNs when fuzzing, and remove them at runtime as well (helps avoid nondeterminism between VMs)", - Options::Arguments::Zero, - [&](Options *o, const std::string& arguments) { fuzzNaNs = false; }) - .add("--no-fuzz-memory", "", "don't emit memory ops when fuzzing", - Options::Arguments::Zero, - [&](Options *o, const std::string& arguments) { fuzzMemory = false; }) - .add("--emit-js-wrapper", "-ejw", "Emit a JavaScript wrapper file that can run the wasm with some test values, useful for fuzzing", - Options::Arguments::One, - [&](Options *o, const std::string& arguments) { emitJSWrapper = arguments; }) - .add("--emit-spec-wrapper", "-esw", "Emit a wasm spec interpreter wrapper file that can run the wasm with some test values, useful for fuzzing", - Options::Arguments::One, - [&](Options *o, const std::string& arguments) { emitSpecWrapper = arguments; }) - .add("--input-source-map", "-ism", "Consume source map from the specified file", - Options::Arguments::One, - [&inputSourceMapFilename](Options *o, const std::string& argument) { inputSourceMapFilename = argument; }) - .add("--output-source-map", "-osm", "Emit source map to the specified file", - Options::Arguments::One, - [&outputSourceMapFilename](Options *o, const std::string& argument) { outputSourceMapFilename = argument; }) - .add("--output-source-map-url", "-osu", "Emit specified string as source map URL", - Options::Arguments::One, - [&outputSourceMapUrl](Options *o, const std::string& argument) { outputSourceMapUrl = argument; }) - .add_positional("INFILE", Options::Arguments::One, - [](Options* o, const std::string& argument) { - o->extra["infile"] = argument; - }); + .add("--output", + "-o", + "Output file (stdout if not specified)", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["output"] = argument; + Colors::disable(); + }) + .add("--emit-text", + "-S", + "Emit text instead of binary for the output file", + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { emitBinary = false; }) + .add("--debuginfo", + "-g", + "Emit names section and debug info", + Options::Arguments::Zero, + [&](Options* o, const std::string& arguments) { debugInfo = true; }) + .add("--converge", + "-c", + "Run passes to convergence, continuing while binary size decreases", + Options::Arguments::Zero, + [&](Options* o, const std::string& arguments) { converge = true; }) + .add( + "--fuzz-exec-before", + "-feh", + "Execute functions before optimization, helping fuzzing find bugs", + Options::Arguments::Zero, + [&](Options* o, const std::string& arguments) { fuzzExecBefore = true; }) + .add("--fuzz-exec", + "-fe", + "Execute functions before and after optimization, helping fuzzing " + "find bugs", + Options::Arguments::Zero, + [&](Options* o, const std::string& arguments) { + fuzzExecBefore = fuzzExecAfter = true; + }) + .add("--fuzz-binary", + "-fb", + "Convert to binary and back after optimizations and before fuzz-exec, " + "helping fuzzing find binary format bugs", + Options::Arguments::Zero, + [&](Options* o, const std::string& arguments) { fuzzBinary = true; }) + .add("--extra-fuzz-command", + "-efc", + "An extra command to run on the output before and after optimizing. " + "The output is compared between the two, and an error occurs if they " + "are not equal", + Options::Arguments::One, + [&](Options* o, const std::string& arguments) { + extraFuzzCommand = arguments; + }) + .add( + "--translate-to-fuzz", + "-ttf", + "Translate the input into a valid wasm module *somehow*, useful for " + "fuzzing", + Options::Arguments::Zero, + [&](Options* o, const std::string& arguments) { translateToFuzz = true; }) + .add("--fuzz-passes", + "-fp", + "Pick a random set of passes to run, useful for fuzzing. this depends " + "on translate-to-fuzz (it picks the passes from the input)", + Options::Arguments::Zero, + [&](Options* o, const std::string& arguments) { fuzzPasses = true; }) + .add("--no-fuzz-nans", + "", + "don't emit NaNs when fuzzing, and remove them at runtime as well " + "(helps avoid nondeterminism between VMs)", + Options::Arguments::Zero, + [&](Options* o, const std::string& arguments) { fuzzNaNs = false; }) + .add("--no-fuzz-memory", + "", + "don't emit memory ops when fuzzing", + Options::Arguments::Zero, + [&](Options* o, const std::string& arguments) { fuzzMemory = false; }) + .add("--emit-js-wrapper", + "-ejw", + "Emit a JavaScript wrapper file that can run the wasm with some test " + "values, useful for fuzzing", + Options::Arguments::One, + [&](Options* o, const std::string& arguments) { + emitJSWrapper = arguments; + }) + .add("--emit-spec-wrapper", + "-esw", + "Emit a wasm spec interpreter wrapper file that can run the wasm with " + "some test values, useful for fuzzing", + Options::Arguments::One, + [&](Options* o, const std::string& arguments) { + emitSpecWrapper = arguments; + }) + .add("--input-source-map", + "-ism", + "Consume source map from the specified file", + Options::Arguments::One, + [&inputSourceMapFilename](Options* o, const std::string& argument) { + inputSourceMapFilename = argument; + }) + .add("--output-source-map", + "-osm", + "Emit source map to the specified file", + Options::Arguments::One, + [&outputSourceMapFilename](Options* o, const std::string& argument) { + outputSourceMapFilename = argument; + }) + .add("--output-source-map-url", + "-osu", + "Emit specified string as source map URL", + Options::Arguments::One, + [&outputSourceMapUrl](Options* o, const std::string& argument) { + outputSourceMapUrl = argument; + }) + .add_positional("INFILE", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["infile"] = argument; + }); options.parse(argc, argv); Module wasm; - if (options.debug) std::cerr << "reading...\n"; + if (options.debug) + std::cerr << "reading...\n"; if (!translateToFuzz) { ModuleReader reader; @@ -159,7 +220,8 @@ int main(int argc, const char* argv[]) { std::cerr << '\n'; Fatal() << "error in parsing wasm source map"; } catch (std::bad_alloc&) { - Fatal() << "error in building module, std::bad_alloc (possibly invalid request for silly amounts of memory)"; + Fatal() << "error in building module, std::bad_alloc (possibly invalid " + "request for silly amounts of memory)"; } options.applyFeatures(wasm); @@ -218,7 +280,9 @@ int main(int argc, const char* argv[]) { std::string firstOutput; if (extraFuzzCommand.size() > 0 && options.extra.count("output") > 0) { - if (options.debug) std::cerr << "writing binary before opts, for extra fuzz command..." << std::endl; + if (options.debug) + std::cerr << "writing binary before opts, for extra fuzz command..." + << std::endl; ModuleWriter writer; writer.setDebug(options.debug); writer.setBinary(emitBinary); @@ -252,12 +316,12 @@ int main(int argc, const char* argv[]) { } if (options.runningPasses()) { - if (options.debug) std::cerr << "running passes...\n"; + if (options.debug) + std::cerr << "running passes...\n"; auto runPasses = [&]() { options.runPasses(*curr); if (options.passOptions.validate) { - bool valid = - WasmValidator().validate(*curr); + bool valid = WasmValidator().validate(*curr); if (!valid) { WasmPrinter::printModule(&*curr); } @@ -276,10 +340,13 @@ int main(int argc, const char* argv[]) { }; auto lastSize = getSize(); while (1) { - if (options.debug) std::cerr << "running iteration for convergence (" << lastSize << ")...\n"; + if (options.debug) + std::cerr << "running iteration for convergence (" << lastSize + << ")...\n"; runPasses(); auto currSize = getSize(); - if (currSize >= lastSize) break; + if (currSize >= lastSize) + break; lastSize = currSize; } } @@ -292,7 +359,8 @@ int main(int argc, const char* argv[]) { if (options.extra.count("output") == 0) { std::cerr << "(no output file specified, not emitting output)\n"; } else { - if (options.debug) std::cerr << "writing..." << std::endl; + if (options.debug) + std::cerr << "writing..." << std::endl; ModuleWriter writer; writer.setDebug(options.debug); writer.setBinary(emitBinary); @@ -305,7 +373,8 @@ int main(int argc, const char* argv[]) { if (extraFuzzCommand.size() > 0) { auto secondOutput = runCommand(extraFuzzCommand); - std::cout << "[extra-fuzz-command second output:]\n" << firstOutput << '\n'; + std::cout << "[extra-fuzz-command second output:]\n" + << firstOutput << '\n'; if (firstOutput != secondOutput) { std::cerr << "extra fuzz command output differs\n"; abort(); diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp index e064abbcb..87f64c1ae 100644 --- a/src/tools/wasm-reduce.cpp +++ b/src/tools/wasm-reduce.cpp @@ -23,22 +23,22 @@ // much more debuggable manner). // -#include <memory> #include <cstdio> #include <cstdlib> +#include <memory> +#include "ir/branch-utils.h" +#include "ir/iteration.h" +#include "ir/literal-utils.h" +#include "ir/properties.h" #include "pass.h" -#include "support/command-line.h" #include "support/colors.h" +#include "support/command-line.h" #include "support/file.h" #include "support/path.h" #include "support/timing.h" -#include "wasm-io.h" #include "wasm-builder.h" -#include "ir/branch-utils.h" -#include "ir/iteration.h" -#include "ir/literal-utils.h" -#include "ir/properties.h" +#include "wasm-io.h" #include "wasm-validator.h" #ifdef _WIN32 #ifndef NOMINMAX @@ -50,18 +50,18 @@ std::string GetLastErrorStdStr() { DWORD error = GetLastError(); if (error) { LPVOID lpMsgBuf; - DWORD bufLen = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &lpMsgBuf, - 0, NULL ); + DWORD bufLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lpMsgBuf, + 0, + NULL); if (bufLen) { LPCSTR lpMsgStr = (LPCSTR)lpMsgBuf; - std::string result(lpMsgStr, lpMsgStr+bufLen); + std::string result(lpMsgStr, lpMsgStr + bufLen); LocalFree(lpMsgBuf); return result; } @@ -80,9 +80,7 @@ struct ProgramResult { double time; ProgramResult() = default; - ProgramResult(std::string command) { - getFromExecution(command); - } + ProgramResult(std::string command) { getFromExecution(command); } #ifdef _WIN32 void getFromExecution(std::string command) { @@ -100,9 +98,9 @@ struct ProgramResult { // Create a pipe for the child process's STDOUT. !CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &saAttr, 0) || // Ensure the read handle to the pipe for STDOUT is not inherited. - !SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) - ) { - Fatal() << "CreatePipe \"" << command << "\" failed: " << GetLastErrorStdStr() << ".\n"; + !SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) { + Fatal() << "CreatePipe \"" << command + << "\" failed: " << GetLastErrorStdStr() << ".\n"; } STARTUPINFO si; @@ -116,18 +114,19 @@ struct ProgramResult { ZeroMemory(&pi, sizeof(pi)); // Start the child process. - if (!CreateProcess(NULL, // No module name (use command line) - (LPSTR)command.c_str(),// Command line - NULL, // Process handle not inheritable - NULL, // Thread handle not inheritable - TRUE, // Set handle inheritance to TRUE - 0, // No creation flags - NULL, // Use parent's environment block - NULL, // Use parent's starting directory - &si, // Pointer to STARTUPINFO structure - &pi ) // Pointer to PROCESS_INFORMATION structure + if (!CreateProcess(NULL, // No module name (use command line) + (LPSTR)command.c_str(), // Command line + NULL, // Process handle not inheritable + NULL, // Thread handle not inheritable + TRUE, // Set handle inheritance to TRUE + 0, // No creation flags + NULL, // Use parent's environment block + NULL, // Use parent's starting directory + &si, // Pointer to STARTUPINFO structure + &pi) // Pointer to PROCESS_INFORMATION structure ) { - Fatal() << "CreateProcess \"" << command << "\" failed: " << GetLastErrorStdStr() << ".\n"; + Fatal() << "CreateProcess \"" << command + << "\" failed: " << GetLastErrorStdStr() << ".\n"; } // Wait until child process exits. @@ -156,8 +155,10 @@ struct ProgramResult { PeekNamedPipe(hChildStd_OUT_Rd, NULL, 0, NULL, &dwTotal, NULL); while (dwTotalRead < dwTotal) { - bSuccess = ReadFile(hChildStd_OUT_Rd, chBuf, BUFSIZE - 1, &dwRead, NULL); - if (!bSuccess || dwRead == 0) break; + bSuccess = + ReadFile(hChildStd_OUT_Rd, chBuf, BUFSIZE - 1, &dwRead, NULL); + if (!bSuccess || dwRead == 0) + break; chBuf[dwRead] = 0; dwTotalRead += dwRead; output.append(chBuf); @@ -166,7 +167,7 @@ struct ProgramResult { timer.stop(); time = timer.getTotal(); } -#else // POSIX +#else // POSIX // runs the command and notes the output // TODO: also stderr, not just stdout? void getFromExecution(std::string command) { @@ -174,10 +175,15 @@ struct ProgramResult { timer.start(); // do this using just core stdio.h and stdlib.h, for portability // sadly this requires two invokes - code = system(("timeout " + std::to_string(timeout) + "s " + command + " > /dev/null 2> /dev/null").c_str()); + code = system(("timeout " + std::to_string(timeout) + "s " + command + + " > /dev/null 2> /dev/null") + .c_str()); const int MAX_BUFFER = 1024; char buffer[MAX_BUFFER]; - FILE *stream = popen(("timeout " + std::to_string(timeout) + "s " + command + " 2> /dev/null").c_str(), "r"); + FILE* stream = popen( + ("timeout " + std::to_string(timeout) + "s " + command + " 2> /dev/null") + .c_str(), + "r"); while (fgets(buffer, MAX_BUFFER, stream) != NULL) { output.append(buffer); } @@ -190,16 +196,13 @@ struct ProgramResult { bool operator==(ProgramResult& other) { return code == other.code && output == other.output; } - bool operator!=(ProgramResult& other) { - return !(*this == other); - } + bool operator!=(ProgramResult& other) { return !(*this == other); } - bool failed() { - return code != 0; - } + bool failed() { return code != 0; } void dump(std::ostream& o) { - o << "[ProgramResult] code: " << code << " stdout: \n" << output << "[====]\nin " << time << " seconds\n[/ProgramResult]\n"; + o << "[ProgramResult] code: " << code << " stdout: \n" + << output << "[====]\nin " << time << " seconds\n[/ProgramResult]\n"; } }; @@ -210,7 +213,7 @@ inline std::ostream& operator<<(std::ostream& o, ProgramResult& result) { return o; } -} +} // namespace std ProgramResult expected; @@ -219,14 +222,22 @@ ProgramResult expected; // case we may try again but much later. static std::unordered_set<Name> functionsWeTriedToRemove; -struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<Reducer>>> { +struct Reducer + : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<Reducer>>> { std::string command, test, working; bool binary, deNan, verbose, debugInfo; // test is the file we write to that the command will operate on // working is the current temporary state, the reduction so far - Reducer(std::string command, std::string test, std::string working, bool binary, bool deNan, bool verbose, bool debugInfo) : - command(command), test(test), working(working), binary(binary), deNan(deNan), verbose(verbose), debugInfo(debugInfo) {} + Reducer(std::string command, + std::string test, + std::string working, + bool binary, + bool deNan, + bool verbose, + bool debugInfo) + : command(command), test(test), working(working), binary(binary), + deNan(deNan), verbose(verbose), debugInfo(debugInfo) {} // runs passes in order to reduce, until we can't reduce any more // the criterion here is wasm binary size @@ -261,28 +272,32 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< "--reorder-locals", "--simplify-locals --vacuum", "--strip", - "--vacuum" - }; + "--vacuum"}; auto oldSize = file_size(working); bool more = true; while (more) { - //std::cerr << "| starting passes loop iteration\n"; + // std::cerr << "| starting passes loop iteration\n"; more = false; - // try both combining with a generic shrink (so minor pass overhead is compensated for), and without + // try both combining with a generic shrink (so minor pass overhead is + // compensated for), and without for (auto pass : passes) { std::string currCommand = Path::getBinaryenBinaryTool("wasm-opt") + " "; // TODO(tlively): -all should be replaced with an option to use the // existing feature set, once implemented. currCommand += working + " -all -o " + test + " " + pass; - if (debugInfo) currCommand += " -g "; - if (verbose) std::cerr << "| trying pass command: " << currCommand << "\n"; + if (debugInfo) + currCommand += " -g "; + if (verbose) + std::cerr << "| trying pass command: " << currCommand << "\n"; if (!ProgramResult(currCommand).failed()) { auto newSize = file_size(test); if (newSize < oldSize) { // the pass didn't fail, and the size looks smaller, so promising // see if it is still has the property we are preserving if (ProgramResult(command) == expected) { - std::cerr << "| command \"" << currCommand << "\" succeeded, reduced size to " << newSize << ", and preserved the property\n"; + std::cerr << "| command \"" << currCommand + << "\" succeeded, reduced size to " << newSize + << ", and preserved the property\n"; copy_file(test, working); more = true; oldSize = newSize; @@ -291,7 +306,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< } } } - if (verbose) std::cerr << "| done with passes for now\n"; + if (verbose) + std::cerr << "| done with passes for now\n"; } // does one pass of slow and destructive reduction. returns whether it @@ -311,7 +327,9 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< // size should be as expected, and output should be as expected ProgramResult result; if (!writeAndTestReduction(result)) { - std::cerr << "\n|! WARNING: writing before destructive reduction fails, very unlikely reduction can work\n" << result << '\n'; + std::cerr << "\n|! WARNING: writing before destructive reduction fails, " + "very unlikely reduction can work\n" + << result << '\n'; } // destroy! walkModule(getModule()); @@ -381,15 +399,18 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< return false; } auto* curr = getCurrent(); - //std::cerr << "try " << curr << " => " << with << '\n'; - if (curr->type != with->type) return false; - if (!shouldTryToReduce()) return false; + // std::cerr << "try " << curr << " => " << with << '\n'; + if (curr->type != with->type) + return false; + if (!shouldTryToReduce()) + return false; replaceCurrent(with); if (!writeAndTestReduction()) { replaceCurrent(curr); return false; } - std::cerr << "| tryToReplaceCurrent succeeded (in " << getLocation() << ")\n"; + std::cerr << "| tryToReplaceCurrent succeeded (in " << getLocation() + << ")\n"; noteReduction(); return true; } @@ -404,38 +425,45 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< if (!isOkReplacement(with)) { return false; } - if (child->type != with->type) return false; - if (!shouldTryToReduce()) return false; + if (child->type != with->type) + return false; + if (!shouldTryToReduce()) + return false; auto* before = child; child = with; if (!writeAndTestReduction()) { child = before; return false; } - std::cerr << "| tryToReplaceChild succeeded (in " << getLocation() << ")\n"; - //std::cerr << "| " << before << " => " << with << '\n'; + std::cerr << "| tryToReplaceChild succeeded (in " << getLocation() + << ")\n"; + // std::cerr << "| " << before << " => " << with << '\n'; noteReduction(); return true; } std::string getLocation() { - if (getFunction()) return getFunction()->name.str; + if (getFunction()) + return getFunction()->name.str; return "(non-function context)"; } - // visitors. in each we try to remove code in a destructive and nontrivial way. - // "nontrivial" means something that optimization passes can't achieve, since we - // don't need to duplicate work that they do + // visitors. in each we try to remove code in a destructive and nontrivial + // way. "nontrivial" means something that optimization passes can't achieve, + // since we don't need to duplicate work that they do void visitExpression(Expression* curr) { // type-based reductions if (curr->type == none) { - if (tryToReduceCurrentToNop()) return; + if (tryToReduceCurrentToNop()) + return; } else if (isConcreteType(curr->type)) { - if (tryToReduceCurrentToConst()) return; + if (tryToReduceCurrentToConst()) + return; } else { assert(curr->type == unreachable); - if (tryToReduceCurrentToUnreachable()) return; + if (tryToReduceCurrentToUnreachable()) + return; } // specific reductions if (auto* iff = curr->dynCast<If>()) { @@ -472,11 +500,14 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< } } } else if (auto* block = curr->dynCast<Block>()) { - if (!shouldTryToReduce()) return; + if (!shouldTryToReduce()) + return; // replace a singleton auto& list = block->list; - if (list.size() == 1 && !BranchUtils::BranchSeeker::hasNamed(block, block->name)) { - if (tryToReplaceCurrent(block->list[0])) return; + if (list.size() == 1 && + !BranchUtils::BranchSeeker::hasNamed(block, block->name)) { + if (tryToReplaceCurrent(block->list[0])) + return; } // try to get rid of nops Index i = 0; @@ -504,7 +535,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< } return; // nothing more to do } else if (auto* loop = curr->dynCast<Loop>()) { - if (shouldTryToReduce() && !BranchUtils::BranchSeeker::hasNamed(loop, loop->name)) { + if (shouldTryToReduce() && + !BranchUtils::BranchSeeker::hasNamed(loop, loop->name)) { tryToReplaceCurrent(loop->body); } return; // nothing more to do @@ -512,73 +544,116 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< // Finally, try to replace with a child. for (auto* child : ChildIterator(curr)) { if (isConcreteType(child->type) && curr->type == none) { - if (tryToReplaceCurrent(builder->makeDrop(child))) return; + if (tryToReplaceCurrent(builder->makeDrop(child))) + return; } else { - if (tryToReplaceCurrent(child)) return; + if (tryToReplaceCurrent(child)) + return; } } // If that didn't work, try to replace with a child + a unary conversion if (isConcreteType(curr->type) && !curr->is<Unary>()) { // but not if it's already unary for (auto* child : ChildIterator(curr)) { - if (child->type == curr->type) continue; // already tried - if (!isConcreteType(child->type)) continue; // no conversion + if (child->type == curr->type) + continue; // already tried + if (!isConcreteType(child->type)) + continue; // no conversion Expression* fixed = nullptr; switch (curr->type) { case i32: { switch (child->type) { - case i32: WASM_UNREACHABLE(); - case i64: fixed = builder->makeUnary(WrapInt64, child); break; - case f32: fixed = builder->makeUnary(TruncSFloat32ToInt32, child); break; - case f64: fixed = builder->makeUnary(TruncSFloat64ToInt32, child); break; - case v128: continue; // v128 not implemented yet + case i32: + WASM_UNREACHABLE(); + case i64: + fixed = builder->makeUnary(WrapInt64, child); + break; + case f32: + fixed = builder->makeUnary(TruncSFloat32ToInt32, child); + break; + case f64: + fixed = builder->makeUnary(TruncSFloat64ToInt32, child); + break; + case v128: + continue; // v128 not implemented yet case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } break; } case i64: { switch (child->type) { - case i32: fixed = builder->makeUnary(ExtendSInt32, child); break; - case i64: WASM_UNREACHABLE(); - case f32: fixed = builder->makeUnary(TruncSFloat32ToInt64, child); break; - case f64: fixed = builder->makeUnary(TruncSFloat64ToInt64, child); break; - case v128: continue; // v128 not implemented yet + case i32: + fixed = builder->makeUnary(ExtendSInt32, child); + break; + case i64: + WASM_UNREACHABLE(); + case f32: + fixed = builder->makeUnary(TruncSFloat32ToInt64, child); + break; + case f64: + fixed = builder->makeUnary(TruncSFloat64ToInt64, child); + break; + case v128: + continue; // v128 not implemented yet case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } break; } case f32: { switch (child->type) { - case i32: fixed = builder->makeUnary(ConvertSInt32ToFloat32, child); break; - case i64: fixed = builder->makeUnary(ConvertSInt64ToFloat32, child); break; - case f32: WASM_UNREACHABLE(); - case f64: fixed = builder->makeUnary(DemoteFloat64, child); break; - case v128: continue; // v128 not implemented yet + case i32: + fixed = builder->makeUnary(ConvertSInt32ToFloat32, child); + break; + case i64: + fixed = builder->makeUnary(ConvertSInt64ToFloat32, child); + break; + case f32: + WASM_UNREACHABLE(); + case f64: + fixed = builder->makeUnary(DemoteFloat64, child); + break; + case v128: + continue; // v128 not implemented yet case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } break; } case f64: { switch (child->type) { - case i32: fixed = builder->makeUnary(ConvertSInt32ToFloat64, child); break; - case i64: fixed = builder->makeUnary(ConvertSInt64ToFloat64, child); break; - case f32: fixed = builder->makeUnary(PromoteFloat32, child); break; - case f64: WASM_UNREACHABLE(); - case v128: continue; // v128 not implemented yet + case i32: + fixed = builder->makeUnary(ConvertSInt32ToFloat64, child); + break; + case i64: + fixed = builder->makeUnary(ConvertSInt64ToFloat64, child); + break; + case f32: + fixed = builder->makeUnary(PromoteFloat32, child); + break; + case f64: + WASM_UNREACHABLE(); + case v128: + continue; // v128 not implemented yet case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } break; } - case v128: continue; // v128 not implemented yet + case v128: + continue; // v128 not implemented yet case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } assert(fixed->type == curr->type); - if (tryToReplaceCurrent(fixed)) return; + if (tryToReplaceCurrent(fixed)) + return; } } } @@ -609,7 +684,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< first = item; break; } - if (!first.isNull()) break; + if (!first.isNull()) + break; } visitSegmented(curr, first, 100); } @@ -627,12 +703,15 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< bool shrank = false; for (auto& segment : curr->segments) { auto& data = segment.data; - size_t skip = 1; // when we succeed, try to shrink by more and more, similar to bisection + // when we succeed, try to shrink by more and more, similar to bisection + size_t skip = 1; for (size_t i = 0; i < data.size() && !data.empty(); i++) { - if (!justShrank && !shouldTryToReduce(bonus)) continue; + if (!justShrank && !shouldTryToReduce(bonus)) + continue; auto save = data; for (size_t j = 0; j < skip; j++) { - if (!data.empty()) data.pop_back(); + if (!data.empty()) + data.pop_back(); } auto justShrank = writeAndTestReduction(); if (justShrank) { @@ -648,10 +727,13 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< } // the "opposite" of shrinking: copy a 'zero' element for (auto& segment : curr->segments) { - if (segment.data.empty()) continue; + if (segment.data.empty()) + continue; for (auto& item : segment.data) { - if (!shouldTryToReduce(bonus)) continue; - if (item == zero) continue; + if (!shouldTryToReduce(bonus)) + continue; + if (item == zero) + continue; auto save = item; item = zero; if (writeAndTestReduction()) { @@ -678,23 +760,27 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< functionNames.push_back(func->name); } size_t skip = 1; - // If we just removed some functions in the previous iteration, keep trying to remove more - // as this is one of the most efficient ways to reduce. + // If we just removed some functions in the previous iteration, keep trying + // to remove more as this is one of the most efficient ways to reduce. bool justRemoved = false; for (size_t i = 0; i < functionNames.size(); i++) { if (!justRemoved && functionsWeTriedToRemove.count(functionNames[i]) == 1 && - !shouldTryToReduce(std::max((factor / 100) + 1, 1000))) continue; + !shouldTryToReduce(std::max((factor / 100) + 1, 1000))) + continue; std::vector<Name> names; - for (size_t j = 0; names.size() < skip && i + j < functionNames.size(); j++) { + for (size_t j = 0; names.size() < skip && i + j < functionNames.size(); + j++) { auto name = functionNames[i + j]; if (module->getFunctionOrNull(name)) { names.push_back(name); functionsWeTriedToRemove.insert(name); } } - if (names.size() == 0) continue; - std::cout << "| try to remove " << names.size() << " functions (skip: " << skip << ")\n"; + if (names.size() == 0) + continue; + std::cout << "| try to remove " << names.size() + << " functions (skip: " << skip << ")\n"; justRemoved = tryToRemoveFunctions(names); if (justRemoved) { noteReduction(names.size()); @@ -712,9 +798,11 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< } skip = 1; for (size_t i = 0; i < exports.size(); i++) { - if (!shouldTryToReduce(std::max((factor / 100) + 1, 1000))) continue; + if (!shouldTryToReduce(std::max((factor / 100) + 1, 1000))) + continue; std::vector<Export> currExports; - for (size_t j = 0; currExports.size() < skip && i + j < exports.size(); j++) { + for (size_t j = 0; currExports.size() < skip && i + j < exports.size(); + j++) { auto exp = exports[i + j]; if (module->getExportOrNull(exp.name)) { currExports.push_back(exp); @@ -736,7 +824,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< } // If we are left with a single function that is not exported or used in // a table, that is useful as then we can change the return type. - if (module->functions.size() == 1 && module->exports.empty() && module->table.segments.empty()) { + if (module->functions.size() == 1 && module->exports.empty() && + module->table.segments.empty()) { auto* func = module->functions[0].get(); // We can't remove something that might have breaks to it. if (!func->imported() && !Properties::isNamedControlFlow(func->body)) { @@ -773,7 +862,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< } // remove all references to them - struct FunctionReferenceRemover : public PostWalker<FunctionReferenceRemover> { + struct FunctionReferenceRemover + : public PostWalker<FunctionReferenceRemover> { std::unordered_set<Name> names; std::vector<Name> exportsToRemove; @@ -801,9 +891,11 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< break; } } - if (!other.isNull()) break; + if (!other.isNull()) + break; } - if (other.isNull()) return; // we failed to find a replacement + if (other.isNull()) + return; // we failed to find a replacement for (auto& segment : curr->segments) { for (auto& name : segment.data) { if (names.count(name)) { @@ -822,7 +914,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< FunctionReferenceRemover referenceRemover(names); referenceRemover.walkModule(module.get()); - if (WasmValidator().validate(*module, WasmValidator::Globally | WasmValidator::Quiet) && + if (WasmValidator().validate( + *module, WasmValidator::Globally | WasmValidator::Quiet) && writeAndTestReduction()) { std::cerr << "| removed " << names.size() << " functions\n"; return true; @@ -836,8 +929,10 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< // try to replace condition with always true and always false void handleCondition(Expression*& condition) { - if (!condition) return; - if (condition->is<Const>()) return; + if (!condition) + return; + if (condition->is<Const>()) + return; auto* c = builder->makeConst(Literal(int32_t(0))); if (!tryToReplaceChild(condition, c)) { c->value = Literal(int32_t(1)); @@ -847,7 +942,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< bool tryToReduceCurrentToNop() { auto* curr = getCurrent(); - if (curr->is<Nop>()) return false; + if (curr->is<Nop>()) + return false; // try to replace with a trivial value Nop nop; if (tryToReplaceCurrent(&nop)) { @@ -860,10 +956,12 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< // try to replace a concrete value with a trivial constant bool tryToReduceCurrentToConst() { auto* curr = getCurrent(); - if (curr->is<Const>()) return false; + if (curr->is<Const>()) + return false; // try to replace with a trivial value Const* c = builder->makeConst(Literal(int32_t(0))); - if (tryToReplaceCurrent(c)) return true; + if (tryToReplaceCurrent(c)) + return true; c->value = Literal::makeFromInt32(1, curr->type); c->type = curr->type; return tryToReplaceCurrent(c); @@ -871,7 +969,8 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< bool tryToReduceCurrentToUnreachable() { auto* curr = getCurrent(); - if (curr->is<Unreachable>()) return false; + if (curr->is<Unreachable>()) + return false; // try to replace with a trivial value Unreachable un; if (tryToReplaceCurrent(&un)) { @@ -889,76 +988,87 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< int main(int argc, const char* argv[]) { std::string input, test, working, command; - bool binary = true, - deNan = false, - verbose = false, - debugInfo = false, + bool binary = true, deNan = false, verbose = false, debugInfo = false, force = false; - Options options("wasm-reduce", "Reduce a wasm file to a smaller one that has the same behavior on a given command"); + Options options("wasm-reduce", + "Reduce a wasm file to a smaller one that has the same " + "behavior on a given command"); options - .add("--command", "-cmd", "The command to run on the test, that we want to reduce while keeping the command's output identical. " - "We look at the command's return code and stdout here (TODO: stderr), " - "and we reduce while keeping those unchanged.", - Options::Arguments::One, - [&](Options* o, const std::string& argument) { - command = argument; - }) - .add("--test", "-t", "Test file (this will be written to to test, the given command should read it when we call it)", - Options::Arguments::One, - [&](Options* o, const std::string& argument) { - test = argument; - }) - .add("--working", "-w", "Working file (this will contain the current good state while doing temporary computations, " - "and will contain the final best result at the end)", - Options::Arguments::One, - [&](Options* o, const std::string& argument) { - working = argument; - }) - .add("--binaries", "-b", "binaryen binaries location (bin/ directory)", - Options::Arguments::One, - [&](Options* o, const std::string& argument) { - // Add separator just in case - Path::setBinaryenBinDir(argument + Path::getPathSeparator()); - }) - .add("--text", "-S", "Emit intermediate files as text, instead of binary (also make sure the test and working files have a .wat or .wast suffix)", - Options::Arguments::Zero, - [&](Options* o, const std::string& argument) { - binary = false; - }) - .add("--denan", "", "Avoid nans when reducing", - Options::Arguments::Zero, - [&](Options* o, const std::string& argument) { - deNan = true; - }) - .add("--verbose", "-v", "Verbose output mode", - Options::Arguments::Zero, - [&](Options* o, const std::string& argument) { - verbose = true; - }) - .add("--debugInfo", "-g", "Keep debug info in binaries", - Options::Arguments::Zero, - [&](Options* o, const std::string& argument) { - debugInfo = true; - }) - .add("--force", "-f", "Force the reduction attempt, ignoring problems that imply it is unlikely to succeed", - Options::Arguments::Zero, - [&](Options* o, const std::string& argument) { - force = true; - }) - .add("--timeout", "-to", "A timeout to apply to each execution of the command, in seconds (default: 2)", - Options::Arguments::One, - [&](Options* o, const std::string& argument) { - timeout = atoi(argument.c_str()); - std::cout << "|applying timeout: " << timeout << "\n"; - }) - .add_positional("INFILE", Options::Arguments::One, - [&](Options* o, const std::string& argument) { - input = argument; - }); + .add("--command", + "-cmd", + "The command to run on the test, that we want to reduce while keeping " + "the command's output identical. " + "We look at the command's return code and stdout here (TODO: stderr), " + "and we reduce while keeping those unchanged.", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { command = argument; }) + .add("--test", + "-t", + "Test file (this will be written to to test, the given command should " + "read it when we call it)", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { test = argument; }) + .add("--working", + "-w", + "Working file (this will contain the current good state while doing " + "temporary computations, " + "and will contain the final best result at the end)", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { working = argument; }) + .add("--binaries", + "-b", + "binaryen binaries location (bin/ directory)", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { + // Add separator just in case + Path::setBinaryenBinDir(argument + Path::getPathSeparator()); + }) + .add("--text", + "-S", + "Emit intermediate files as text, instead of binary (also make sure " + "the test and working files have a .wat or .wast suffix)", + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { binary = false; }) + .add("--denan", + "", + "Avoid nans when reducing", + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { deNan = true; }) + .add("--verbose", + "-v", + "Verbose output mode", + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { verbose = true; }) + .add("--debugInfo", + "-g", + "Keep debug info in binaries", + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { debugInfo = true; }) + .add("--force", + "-f", + "Force the reduction attempt, ignoring problems that imply it is " + "unlikely to succeed", + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { force = true; }) + .add("--timeout", + "-to", + "A timeout to apply to each execution of the command, in seconds " + "(default: 2)", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { + timeout = atoi(argument.c_str()); + std::cout << "|applying timeout: " << timeout << "\n"; + }) + .add_positional( + "INFILE", + Options::Arguments::One, + [&](Options* o, const std::string& argument) { input = argument; }); options.parse(argc, argv); - if (test.size() == 0) Fatal() << "test file not provided\n"; - if (working.size() == 0) Fatal() << "working file not provided\n"; + if (test.size() == 0) + Fatal() << "test file not provided\n"; + if (working.size() == 0) + Fatal() << "working file not provided\n"; if (!binary) { Colors::disable(); @@ -979,15 +1089,20 @@ int main(int argc, const char* argv[]) { auto stopIfNotForced = [&](std::string message, ProgramResult& result) { std::cerr << "|! " << message << '\n' << result << '\n'; if (!force) { - Fatal() << "|! stopping, as it is very unlikely reduction can succeed (use -f to ignore this check)"; + Fatal() << "|! stopping, as it is very unlikely reduction can succeed " + "(use -f to ignore this check)"; } }; if (expected.time + 1 >= timeout) { - stopIfNotForced("execution time is dangerously close to the timeout - you should probably increase the timeout", expected); + stopIfNotForced("execution time is dangerously close to the timeout - you " + "should probably increase the timeout", + expected); } - std::cerr << "|checking that command has different behavior on invalid binary (this verifies that the test file is used by the command)\n"; + std::cerr + << "|checking that command has different behavior on invalid binary (this " + "verifies that the test file is used by the command)\n"; { { std::ofstream dst(test, std::ios::binary); @@ -995,24 +1110,31 @@ int main(int argc, const char* argv[]) { } ProgramResult result(command); if (result == expected) { - stopIfNotForced("running command on an invalid module should give different results", result); + stopIfNotForced( + "running command on an invalid module should give different results", + result); } } - std::cerr << "|checking that command has expected behavior on canonicalized (read-written) binary\n"; + std::cerr << "|checking that command has expected behavior on canonicalized " + "(read-written) binary\n"; { // read and write it // TODO(tlively): -all should be replaced with an option to use the existing // feature set, once implemented. - auto cmd = Path::getBinaryenBinaryTool("wasm-opt") + " " + input + " -all -o " + test; - if (!binary) cmd += " -S"; + auto cmd = Path::getBinaryenBinaryTool("wasm-opt") + " " + input + + " -all -o " + test; + if (!binary) + cmd += " -S"; ProgramResult readWrite(cmd); if (readWrite.failed()) { stopIfNotForced("failed to read and write the binary", readWrite); } else { ProgramResult result(command); if (result != expected) { - stopIfNotForced("running command on the canonicalized module should give the same results", result); + stopIfNotForced("running command on the canonicalized module should " + "give the same results", + result); } } } @@ -1044,13 +1166,15 @@ int main(int argc, const char* argv[]) { std::cerr << "| after pass reduction: " << newSize << "\n"; // always stop after a pass reduction attempt, for final cleanup - if (stopping) break; + if (stopping) + break; // check if the full cycle (destructive/passes) has helped or not if (lastPostPassesSize && newSize >= lastPostPassesSize) { std::cerr << "| progress has stopped, skipping to the end\n"; if (factor == 1) { - // this is after doing work with factor 1, so after the remaining work, stop + // this is after doing work with factor 1, so after the remaining work, + // stop stopping = true; } else { // just try to remove all we can and finish up @@ -1059,9 +1183,10 @@ int main(int argc, const char* argv[]) { } lastPostPassesSize = newSize; - // if destructive reductions lead to useful proportionate pass reductions, keep - // going at the same factor, as pass reductions are far faster - std::cerr << "| pass progress: " << passProgress << ", last destructive: " << lastDestructiveReductions << '\n'; + // if destructive reductions lead to useful proportionate pass reductions, + // keep going at the same factor, as pass reductions are far faster + std::cerr << "| pass progress: " << passProgress + << ", last destructive: " << lastDestructiveReductions << '\n'; if (passProgress >= 4 * lastDestructiveReductions) { // don't change std::cerr << "| progress is good, do not quickly decrease factor\n"; @@ -1083,16 +1208,19 @@ int main(int argc, const char* argv[]) { while (1) { std::cerr << "| reduce destructively... (factor: " << factor << ")\n"; lastDestructiveReductions = reducer.reduceDestructively(factor); - if (lastDestructiveReductions > 0) break; + if (lastDestructiveReductions > 0) + break; // we failed to reduce destructively if (factor == 1) { stopping = true; break; } - factor = std::max(1, factor / 4); // quickly now, try to find *something* we can reduce + factor = std::max( + 1, factor / 4); // quickly now, try to find *something* we can reduce } - std::cerr << "| destructive reduction led to size: " << file_size(working) << '\n'; + std::cerr << "| destructive reduction led to size: " << file_size(working) + << '\n'; } std::cerr << "|finished, final size: " << file_size(working) << "\n"; copy_file(working, test); // just to avoid confusion diff --git a/src/tools/wasm-shell.cpp b/src/tools/wasm-shell.cpp index 0c95b39ea..6141b1a37 100644 --- a/src/tools/wasm-shell.cpp +++ b/src/tools/wasm-shell.cpp @@ -35,13 +35,13 @@ using namespace cashew; using namespace wasm; -Name ASSERT_RETURN("assert_return"), - ASSERT_TRAP("assert_trap"), - ASSERT_INVALID("assert_invalid"), - ASSERT_MALFORMED("assert_malformed"), - ASSERT_UNLINKABLE("assert_unlinkable"), - INVOKE("invoke"), - GET("get"); +Name ASSERT_RETURN("assert_return"); +Name ASSERT_TRAP("assert_trap"); +Name ASSERT_INVALID("assert_invalid"); +Name ASSERT_MALFORMED("assert_malformed"); +Name ASSERT_UNLINKABLE("assert_unlinkable"); +Name INVOKE("invoke"); +Name GET("get"); // Modules named in the file @@ -60,7 +60,10 @@ struct Operation { Name name; LiteralList arguments; - Operation(Element& element, ModuleInstance* instanceInit, SExpressionWasmBuilder& builder) : instance(instanceInit) { + Operation(Element& element, + ModuleInstance* instanceInit, + SExpressionWasmBuilder& builder) + : instance(instanceInit) { operation = element[0]->str(); Index i = 1; if (element.size() >= 3 && element[2]->isStr()) { @@ -87,14 +90,19 @@ struct Operation { } }; -static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm, +static void run_asserts(Name moduleName, + size_t* i, + bool* checked, + Module* wasm, Element* root, SExpressionWasmBuilder* builder, Name entry) { ModuleInstance* instance = nullptr; if (wasm) { - auto tempInterface = wasm::make_unique<ShellExternalInterface>(); // prefix make_unique to work around visual studio bugs - auto tempInstance = wasm::make_unique<ModuleInstance>(*wasm, tempInterface.get()); + // prefix make_unique to work around visual studio bugs + auto tempInterface = wasm::make_unique<ShellExternalInterface>(); + auto tempInstance = + wasm::make_unique<ModuleInstance>(*wasm, tempInterface.get()); interfaces[moduleName].swap(tempInterface); instances[moduleName].swap(tempInstance); instance = instances[moduleName].get(); @@ -117,7 +125,8 @@ static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm, while (*i < root->size()) { Element& curr = *(*root)[*i]; IString id = curr[0]->str(); - if (id == MODULE) break; + if (id == MODULE) + break; *checked = true; Colors::red(std::cerr); std::cerr << *i << '/' << (root->size() - 1); @@ -128,15 +137,15 @@ static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm, Colors::green(std::cerr); std::cerr << " [line: " << curr.line << "]\n"; Colors::normal(std::cerr); - if (id == ASSERT_INVALID || id == ASSERT_MALFORMED || id == ASSERT_UNLINKABLE) { + if (id == ASSERT_INVALID || id == ASSERT_MALFORMED || + id == ASSERT_UNLINKABLE) { // a module invalidity test Module wasm; bool invalid = false; std::unique_ptr<SExpressionWasmBuilder> builder; try { builder = std::unique_ptr<SExpressionWasmBuilder>( - new SExpressionWasmBuilder(wasm, *curr[1]) - ); + new SExpressionWasmBuilder(wasm, *curr[1])); } catch (const ParseException&) { invalid = true; } @@ -147,7 +156,8 @@ static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm, if (!invalid && id == ASSERT_UNLINKABLE) { // validate "instantiating" the mdoule auto reportUnknownImport = [&](Importable* import) { - std::cerr << "unknown import: " << import->module << '.' << import->base << '\n'; + std::cerr << "unknown import: " << import->module << '.' + << import->base << '\n'; invalid = true; }; ModuleUtils::iterImportedGlobals(wasm, reportUnknownImport); @@ -168,7 +178,8 @@ static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm, for (auto name : segment.data) { // spec tests consider it illegal to use spectest.print in a table if (auto* import = wasm.getFunction(name)) { - if (import->imported() && import->module == SPECTEST && import->base == PRINT) { + if (import->imported() && import->module == SPECTEST && + import->base == PRINT) { std::cerr << "cannot put spectest.print in table\n"; invalid = true; } @@ -201,10 +212,8 @@ static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm, if (id == ASSERT_RETURN) { assert(!trapped); if (curr.size() >= 3) { - Literal expected = builder - ->parseExpression(*curr[2]) - ->dynCast<Const>() - ->value; + Literal expected = + builder->parseExpression(*curr[2])->dynCast<Const>()->value; std::cerr << "seen " << result << ", expected " << expected << '\n'; if (expected != result) { std::cout << "unexpected, should be identical\n"; @@ -219,7 +228,8 @@ static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm, } } } - if (id == ASSERT_TRAP) assert(trapped); + if (id == ASSERT_TRAP) + assert(trapped); } *i += 1; } @@ -235,37 +245,44 @@ int main(int argc, const char* argv[]) { Options options("wasm-shell", "Execute .wast files"); options - .add( - "--entry", "-e", "Call the entry point after parsing the module", - Options::Arguments::One, - [&entry](Options*, const std::string& argument) { entry = argument; }) - .add( - "--skip", "-s", "Skip input on certain lines (comma-separated-list)", - Options::Arguments::One, - [&skipped](Options*, const std::string& argument) { - size_t i = 0; - while (i < argument.size()) { - auto ending = argument.find(',', i); - if (ending == std::string::npos) { - ending = argument.size(); - } - auto sub = argument.substr(i, ending - i); - skipped.insert(atoi(sub.c_str())); - i = ending + 1; - } - }) - .add_positional("INFILE", Options::Arguments::One, - [](Options* o, const std::string& argument) { - o->extra["infile"] = argument; - }); + .add("--entry", + "-e", + "Call the entry point after parsing the module", + Options::Arguments::One, + [&entry](Options*, const std::string& argument) { entry = argument; }) + .add("--skip", + "-s", + "Skip input on certain lines (comma-separated-list)", + Options::Arguments::One, + [&skipped](Options*, const std::string& argument) { + size_t i = 0; + while (i < argument.size()) { + auto ending = argument.find(',', i); + if (ending == std::string::npos) { + ending = argument.size(); + } + auto sub = argument.substr(i, ending - i); + skipped.insert(atoi(sub.c_str())); + i = ending + 1; + } + }) + .add_positional("INFILE", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["infile"] = argument; + }); options.parse(argc, argv); - auto input(read_file<std::vector<char>>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release)); + auto input(read_file<std::vector<char>>(options.extra["infile"], + Flags::Text, + options.debug ? Flags::Debug + : Flags::Release)); bool checked = false; try { - if (options.debug) std::cerr << "parsing text to s-expressions...\n"; + if (options.debug) + std::cerr << "parsing text to s-expressions...\n"; SExpressionParser parser(input.data()); Element& root = *parser.root; @@ -282,13 +299,15 @@ int main(int argc, const char* argv[]) { } IString id = curr[0]->str(); if (id == MODULE) { - if (options.debug) std::cerr << "parsing s-expressions to wasm...\n"; + if (options.debug) + std::cerr << "parsing s-expressions to wasm...\n"; Colors::green(std::cerr); std::cerr << "BUILDING MODULE [line: " << curr.line << "]\n"; Colors::normal(std::cerr); auto module = wasm::make_unique<Module>(); Name moduleName; - auto builder = wasm::make_unique<SExpressionWasmBuilder>(*module, *root[i], &moduleName); + auto builder = wasm::make_unique<SExpressionWasmBuilder>( + *module, *root[i], &moduleName); builders[moduleName].swap(builder); modules[moduleName].swap(module); i++; @@ -298,7 +317,13 @@ int main(int argc, const char* argv[]) { WasmPrinter::printModule(modules[moduleName].get()); } assert(valid); - run_asserts(moduleName, &i, &checked, modules[moduleName].get(), &root, builders[moduleName].get(), entry); + run_asserts(moduleName, + &i, + &checked, + modules[moduleName].get(), + &root, + builders[moduleName].get(), + entry); } else { run_asserts(Name(), &i, &checked, nullptr, &root, nullptr, entry); } diff --git a/src/tools/wasm2js.cpp b/src/tools/wasm2js.cpp index 84aa9f024..3d5f484da 100644 --- a/src/tools/wasm2js.cpp +++ b/src/tools/wasm2js.cpp @@ -18,13 +18,14 @@ // wasm2js console tool // +#include "wasm2js.h" +#include "optimization-options.h" +#include "pass.h" #include "support/colors.h" #include "support/command-line.h" #include "support/file.h" #include "wasm-s-parser.h" #include "wasm2js.h" -#include "optimization-options.h" -#include "pass.h" using namespace cashew; using namespace wasm; @@ -34,8 +35,8 @@ using namespace wasm; namespace { static void optimizeWasm(Module& wasm, PassOptions options) { - // Perform various optimizations that will be good for JS, but would not be great - // for wasm in general + // Perform various optimizations that will be good for JS, but would not be + // great for wasm in general struct OptimizeForJS : public WalkerPass<PostWalker<OptimizeForJS>> { bool isFunctionParallel() override { return true; } @@ -60,14 +61,12 @@ static void optimizeWasm(Module& wasm, PassOptions options) { runner.run(); } -template<typename T> -static void printJS(Ref ast, T& output) { +template<typename T> static void printJS(Ref ast, T& output) { JSPrinter jser(true, true, ast); jser.printAst(); output << jser.buffer << std::endl; } - // Traversals struct TraverseInfo { @@ -102,7 +101,9 @@ private: }; // Traverse, calling visit after the children -static void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::function<void (Ref)> visitPost) { +static void traversePrePost(Ref node, + std::function<void(Ref)> visitPre, + std::function<void(Ref)> visitPost) { std::vector<TraverseInfo> stack; stack.push_back(TraverseInfo(node)); while (!stack.empty()) { @@ -123,7 +124,7 @@ static void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::f } } -static void traversePost(Ref node, std::function<void (Ref)> visit) { +static void traversePost(Ref node, std::function<void(Ref)> visit) { traversePrePost(node, [](Ref node) {}, visit); } @@ -131,24 +132,28 @@ static void optimizeJS(Ref ast) { // helpers auto isOrZero = [](Ref node) { - return node->isArray() && !node->empty() && node[0] == BINARY && node[1] == OR && node[3]->isNumber() && node[3]->getNumber() == 0; + return node->isArray() && !node->empty() && node[0] == BINARY && + node[1] == OR && node[3]->isNumber() && node[3]->getNumber() == 0; }; auto isPlus = [](Ref node) { - return node->isArray() && !node->empty() && node[0] == UNARY_PREFIX && node[1] == PLUS; + return node->isArray() && !node->empty() && node[0] == UNARY_PREFIX && + node[1] == PLUS; }; auto isBitwise = [](Ref node) { if (node->isArray() && !node->empty() && node[0] == BINARY) { auto op = node[1]; - return op == OR || op == AND || op == XOR || op == RSHIFT || op == TRSHIFT || op == LSHIFT; + return op == OR || op == AND || op == XOR || op == RSHIFT || + op == TRSHIFT || op == LSHIFT; } return false; }; // x >> 0 => x | 0 traversePost(ast, [](Ref node) { - if (node->isArray() && !node->empty() && node[0] == BINARY && node[1] == RSHIFT && node[3]->isNumber()) { + if (node->isArray() && !node->empty() && node[0] == BINARY && + node[1] == RSHIFT && node[3]->isNumber()) { if (node[3]->getNumber() == 0) { node[1]->setString(OR); } @@ -190,60 +195,69 @@ static void optimizeJS(Ref ast) { // XXX IString invalid("__wasm2js$INVALID_LABEL__"); std::vector<Ref> breakCapturers; std::vector<Ref> continueCapturers; - std::unordered_map<IString, Ref> labelToValue; // maps the label to the loop/etc. + std::unordered_map<IString, Ref> + labelToValue; // maps the label to the loop/etc. std::unordered_set<Value*> labelled; // all things with a label on them. Value INVALID; - traversePrePost(ast, [&](Ref node) { - if (node->isArray() && !node->empty()) { - if (node[0] == LABEL) { - auto label = node[1]->getIString(); - labelToValue[label] = node[2]; - labelled.insert(node[2].get()); - } else if (node[0] == WHILE || node[0] == DO || node[0] == FOR) { - breakCapturers.push_back(node); - continueCapturers.push_back(node); - } else if (node[0] == cashew::BLOCK) { - if (labelled.count(node.get())) { - // Cannot break to a block without the label. - breakCapturers.push_back(Ref(&INVALID)); + traversePrePost( + ast, + [&](Ref node) { + if (node->isArray() && !node->empty()) { + if (node[0] == LABEL) { + auto label = node[1]->getIString(); + labelToValue[label] = node[2]; + labelled.insert(node[2].get()); + } else if (node[0] == WHILE || node[0] == DO || node[0] == FOR) { + breakCapturers.push_back(node); + continueCapturers.push_back(node); + } else if (node[0] == cashew::BLOCK) { + if (labelled.count(node.get())) { + // Cannot break to a block without the label. + breakCapturers.push_back(Ref(&INVALID)); + } + } else if (node[0] == SWITCH) { + breakCapturers.push_back(node); } - } else if (node[0] == SWITCH) { - breakCapturers.push_back(node); } - } - }, [&](Ref node) { - if (node->isArray() && !node->empty()) { - if (node[0] == LABEL) { - auto label = node[1]->getIString(); - labelToValue.erase(label); - labelled.erase(node[2].get()); - } else if (node[0] == WHILE || node[0] == DO || node[0] == FOR) { - breakCapturers.pop_back(); - continueCapturers.pop_back(); - } else if (node[0] == cashew::BLOCK) { - if (labelled.count(node.get())) { - breakCapturers.pop_back(); - } - } else if (node[0] == SWITCH) { - breakCapturers.pop_back(); - } else if (node[0] == BREAK || node[0] == CONTINUE) { - if (!node[1]->isNull()) { + }, + [&](Ref node) { + if (node->isArray() && !node->empty()) { + if (node[0] == LABEL) { auto label = node[1]->getIString(); - assert(labelToValue.count(label)); - auto& capturers = node[0] == BREAK ? breakCapturers : continueCapturers; - assert(!capturers.empty()); - if (capturers.back() == labelToValue[label]) { - // Success, the break/continue goes exactly where we would if we - // didn't have the label! - node[1]->setNull(); + labelToValue.erase(label); + labelled.erase(node[2].get()); + } else if (node[0] == WHILE || node[0] == DO || node[0] == FOR) { + breakCapturers.pop_back(); + continueCapturers.pop_back(); + } else if (node[0] == cashew::BLOCK) { + if (labelled.count(node.get())) { + breakCapturers.pop_back(); + } + } else if (node[0] == SWITCH) { + breakCapturers.pop_back(); + } else if (node[0] == BREAK || node[0] == CONTINUE) { + if (!node[1]->isNull()) { + auto label = node[1]->getIString(); + assert(labelToValue.count(label)); + auto& capturers = + node[0] == BREAK ? breakCapturers : continueCapturers; + assert(!capturers.empty()); + if (capturers.back() == labelToValue[label]) { + // Success, the break/continue goes exactly where we would if we + // didn't have the label! + node[1]->setNull(); + } } } } - } - }); + }); } -static void emitWasm(Module& wasm, Output& output, Wasm2JSBuilder::Flags flags, PassOptions options, Name name) { +static void emitWasm(Module& wasm, + Output& output, + Wasm2JSBuilder::Flags flags, + PassOptions options, + Name name) { if (options.optimizeLevel > 0) { optimizeWasm(wasm, options); } @@ -264,7 +278,9 @@ public: SExpressionWasmBuilder& sexpBuilder, Output& out, Wasm2JSBuilder::Flags flags, - PassOptions options) : root(root), sexpBuilder(sexpBuilder), out(out), flags(flags), options(options) {} + PassOptions options) + : root(root), sexpBuilder(sexpBuilder), out(out), flags(flags), + options(options) {} void emit(); @@ -310,11 +326,9 @@ Ref AssertionEmitter::emitAssertReturnFunc(Builder& wasmBuilder, Expression* actual = sexpBuilder.parseExpression(e[1]); Expression* body = nullptr; if (e.size() == 2) { - if (actual->type == none) { - body = wasmBuilder.blockify( - actual, - wasmBuilder.makeConst(Literal(uint32_t(1))) - ); + if (actual->type == none) { + body = wasmBuilder.blockify(actual, + wasmBuilder.makeConst(Literal(uint32_t(1)))); } else { body = actual; } @@ -330,9 +344,10 @@ Ref AssertionEmitter::emitAssertReturnFunc(Builder& wasmBuilder, case i64: body = wasmBuilder.makeCall( "i64Equal", - {actual, wasmBuilder.makeCall(WASM_FETCH_HIGH_BITS, {}, i32), expected}, - i32 - ); + {actual, + wasmBuilder.makeCall(WASM_FETCH_HIGH_BITS, {}, i32), + expected}, + i32); break; case f32: { @@ -353,14 +368,11 @@ Ref AssertionEmitter::emitAssertReturnFunc(Builder& wasmBuilder, assert(false && "Unexpected number of parameters in assert_return"); } std::unique_ptr<Function> testFunc( - wasmBuilder.makeFunction( - testFuncName, - std::vector<NameType>{}, - body->type, - std::vector<NameType>{}, - body - ) - ); + wasmBuilder.makeFunction(testFuncName, + std::vector<NameType>{}, + body->type, + std::vector<NameType>{}, + body)); Ref jsFunc = processFunction(testFunc.get()); fixCalls(jsFunc, asmModule); emitFunction(jsFunc); @@ -374,14 +386,11 @@ Ref AssertionEmitter::emitAssertReturnNanFunc(Builder& wasmBuilder, Expression* actual = sexpBuilder.parseExpression(e[1]); Expression* body = wasmBuilder.makeCall("isNaN", {actual}, i32); std::unique_ptr<Function> testFunc( - wasmBuilder.makeFunction( - testFuncName, - std::vector<NameType>{}, - body->type, - std::vector<NameType>{}, - body - ) - ); + wasmBuilder.makeFunction(testFuncName, + std::vector<NameType>{}, + body->type, + std::vector<NameType>{}, + body)); Ref jsFunc = processFunction(testFunc.get()); fixCalls(jsFunc, asmModule); emitFunction(jsFunc); @@ -399,8 +408,7 @@ Ref AssertionEmitter::emitAssertTrapFunc(Builder& wasmBuilder, std::vector<NameType>{}, expr->type, std::vector<NameType>{}, - expr) - ); + expr)); IString expectedErr = e[2]->str(); Ref innerFunc = processFunction(exprFunc.get()); fixCalls(innerFunc, asmModule); @@ -411,33 +419,25 @@ Ref AssertionEmitter::emitAssertTrapFunc(Builder& wasmBuilder, Ref catchBlock = ValueBuilder::makeBlock(); ValueBuilder::appendToBlock( catchBlock, - ValueBuilder::makeReturn( - ValueBuilder::makeCall( - ValueBuilder::makeDot( - ValueBuilder::makeName(IString("e")), - ValueBuilder::makeName(IString("message")), - ValueBuilder::makeName(IString("includes")) - ), - ValueBuilder::makeString(expectedErr) - ) - ) - ); + ValueBuilder::makeReturn(ValueBuilder::makeCall( + ValueBuilder::makeDot(ValueBuilder::makeName(IString("e")), + ValueBuilder::makeName(IString("message")), + ValueBuilder::makeName(IString("includes"))), + ValueBuilder::makeString(expectedErr)))); outerFunc[3]->push_back(ValueBuilder::makeTry( - tryBlock, - ValueBuilder::makeName((IString("e"))), - catchBlock)); + tryBlock, ValueBuilder::makeName((IString("e"))), catchBlock)); outerFunc[3]->push_back(ValueBuilder::makeReturn(ValueBuilder::makeInt(0))); emitFunction(outerFunc); return outerFunc; } bool AssertionEmitter::isAssertHandled(Element& e) { - return e.isList() && e.size() >= 2 && e[0]->isStr() - && (e[0]->str() == Name("assert_return") || + return e.isList() && e.size() >= 2 && e[0]->isStr() && + (e[0]->str() == Name("assert_return") || e[0]->str() == Name("assert_return_nan") || - (flags.pedantic && e[0]->str() == Name("assert_trap"))) - && e[1]->isList() && e[1]->size() >= 2 && (*e[1])[0]->isStr() - && (*e[1])[0]->str() == Name("invoke"); + (flags.pedantic && e[0]->str() == Name("assert_trap"))) && + e[1]->isList() && e[1]->size() >= 2 && (*e[1])[0]->isStr() && + (*e[1])[0]->str() == Name("invoke"); } void AssertionEmitter::fixCalls(Ref asmjs, Name asmModule) { @@ -446,7 +446,8 @@ void AssertionEmitter::fixCalls(Ref asmjs, Name asmModule) { for (Ref& r : arr) { fixCalls(r, asmModule); } - if (arr.size() > 0 && arr[0]->isString() && arr[0]->getIString() == cashew::CALL) { + if (arr.size() > 0 && arr[0]->isString() && + arr[0]->getIString() == cashew::CALL) { assert(arr.size() >= 2); if (arr[1]->getIString() == "f32Equal" || arr[1]->getIString() == "f64Equal" || @@ -457,7 +458,7 @@ void AssertionEmitter::fixCalls(Ref asmjs, Name asmModule) { arr[1]->setString("Math.fround"); } else { Ref fixed = ValueBuilder::makeDot(ValueBuilder::makeName(asmModule), - arr[1]->getIString()); + arr[1]->getIString()); arr[1]->setArray(fixed->getArray()); } } @@ -522,7 +523,8 @@ void AssertionEmitter::emit() { Name asmModule = std::string("ret") + ASM_FUNC.str; for (size_t i = 0; i < root.size(); ++i) { Element& e = *root[i]; - if (e.isList() && e.size() >= 1 && e[0]->isStr() && e[0]->str() == Name("module")) { + if (e.isList() && e.size() >= 1 && e[0]->isStr() && + e[0]->str() == Name("module")) { std::stringstream funcNameS; funcNameS << ASM_FUNC.c_str() << i; std::stringstream moduleNameS; @@ -555,10 +557,7 @@ void AssertionEmitter::emit() { emitAssertTrapFunc(wasmBuilder, e, testFuncName, asmModule); } - out << "if (!" - << testFuncName.str - << "()) throw 'assertion failed: " - << e + out << "if (!" << testFuncName.str << "()) throw 'assertion failed: " << e << "';\n"; } } @@ -567,38 +566,48 @@ void AssertionEmitter::emit() { // Main -int main(int argc, const char *argv[]) { +int main(int argc, const char* argv[]) { Wasm2JSBuilder::Flags flags; - OptimizationOptions options("wasm2js", "Transform .wasm/.wast files to asm.js"); + OptimizationOptions options("wasm2js", + "Transform .wasm/.wast files to asm.js"); options - .add("--output", "-o", "Output file (stdout if not specified)", - Options::Arguments::One, - [](Options* o, const std::string& argument) { - o->extra["output"] = argument; - Colors::disable(); - }) - .add("--allow-asserts", "", "Allow compilation of .wast testing asserts", - Options::Arguments::Zero, - [&](Options* o, const std::string& argument) { - flags.allowAsserts = true; - o->extra["asserts"] = "1"; - }) - .add("--pedantic", "", "Emulate WebAssembly trapping behavior", - Options::Arguments::Zero, - [&](Options* o, const std::string& argument) { - flags.pedantic = true; - }) - .add("--emscripten", "", "Emulate the glue in emscripten-compatible form (and not ES6 module form)", - Options::Arguments::Zero, - [&](Options* o, const std::string& argument) { - flags.emscripten = true; - }) - .add_positional("INFILE", Options::Arguments::One, - [](Options *o, const std::string& argument) { - o->extra["infile"] = argument; - }); + .add("--output", + "-o", + "Output file (stdout if not specified)", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["output"] = argument; + Colors::disable(); + }) + .add("--allow-asserts", + "", + "Allow compilation of .wast testing asserts", + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { + flags.allowAsserts = true; + o->extra["asserts"] = "1"; + }) + .add( + "--pedantic", + "", + "Emulate WebAssembly trapping behavior", + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { flags.pedantic = true; }) + .add( + "--emscripten", + "", + "Emulate the glue in emscripten-compatible form (and not ES6 module " + "form)", + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { flags.emscripten = true; }) + .add_positional("INFILE", + Options::Arguments::One, + [](Options* o, const std::string& argument) { + o->extra["infile"] = argument; + }); options.parse(argc, argv); - if (options.debug) flags.debug = true; + if (options.debug) + flags.debug = true; Element* root = nullptr; Module wasm; @@ -606,11 +615,11 @@ int main(int argc, const char *argv[]) { std::unique_ptr<SExpressionParser> sexprParser; std::unique_ptr<SExpressionWasmBuilder> sexprBuilder; - auto &input = options.extra["infile"]; + auto& input = options.extra["infile"]; std::string suffix(".wasm"); bool binaryInput = - input.size() >= suffix.size() && - input.compare(input.size() - suffix.size(), suffix.size(), suffix) == 0; + input.size() >= suffix.size() && + input.compare(input.size() - suffix.size(), suffix.size(), suffix) == 0; try { // If the input filename ends in `.wasm`, then parse it in binary form, @@ -627,20 +636,25 @@ int main(int argc, const char *argv[]) { reader.read(input, wasm, ""); options.applyFeatures(wasm); } else { - auto input( - read_file<std::vector<char>>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release)); - if (options.debug) std::cerr << "s-parsing..." << std::endl; + auto input(read_file<std::vector<char>>(options.extra["infile"], + Flags::Text, + options.debug ? Flags::Debug + : Flags::Release)); + if (options.debug) + std::cerr << "s-parsing..." << std::endl; sexprParser = make_unique<SExpressionParser>(input.data()); root = sexprParser->root; - if (options.debug) std::cerr << "w-parsing..." << std::endl; + if (options.debug) + std::cerr << "w-parsing..." << std::endl; sexprBuilder = make_unique<SExpressionWasmBuilder>(wasm, *(*root)[0]); } } catch (ParseException& p) { p.dump(std::cerr); Fatal() << "error in parsing input"; } catch (std::bad_alloc&) { - Fatal() << "error in building module, std::bad_alloc (possibly invalid request for silly amounts of memory)"; + Fatal() << "error in building module, std::bad_alloc (possibly invalid " + "request for silly amounts of memory)"; } if (options.passOptions.validate) { @@ -650,13 +664,18 @@ int main(int argc, const char *argv[]) { } } - if (options.debug) std::cerr << "j-printing..." << std::endl; - Output output(options.extra["output"], Flags::Text, options.debug ? Flags::Debug : Flags::Release); + if (options.debug) + std::cerr << "j-printing..." << std::endl; + Output output(options.extra["output"], + Flags::Text, + options.debug ? Flags::Debug : Flags::Release); if (!binaryInput && options.extra["asserts"] == "1") { - AssertionEmitter(*root, *sexprBuilder, output, flags, options.passOptions).emit(); + AssertionEmitter(*root, *sexprBuilder, output, flags, options.passOptions) + .emit(); } else { emitWasm(wasm, output, flags, options.passOptions, "asmFunc"); } - if (options.debug) std::cerr << "done." << std::endl; + if (options.debug) + std::cerr << "done." << std::endl; } diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 1f6222cba..aa13685e1 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -25,14 +25,14 @@ #include <ostream> #include <type_traits> -#include "wasm.h" -#include "wasm-traversal.h" -#include "asmjs/shared-constants.h" #include "asm_v_wasm.h" +#include "asmjs/shared-constants.h" +#include "ir/import-utils.h" #include "parsing.h" #include "wasm-builder.h" +#include "wasm-traversal.h" #include "wasm-validator.h" -#include "ir/import-utils.h" +#include "wasm.h" namespace wasm { @@ -49,8 +49,7 @@ enum WebLimitations { MaxFunctionLocals = 50 * 1000 }; -template<typename T, typename MiniT> -struct LEB { +template<typename T, typename MiniT> struct LEB { static_assert(sizeof(MiniT) == 1, "MiniT must be a byte"); T value; @@ -59,8 +58,12 @@ struct LEB { LEB(T value) : value(value) {} bool hasMore(T temp, MiniT byte) { - // for signed, we must ensure the last bit has the right sign, as it will zero extend - return std::is_signed<T>::value ? (temp != 0 && temp != T(-1)) || (value >= 0 && (byte & 64)) || (value < 0 && !(byte & 64)) : (temp != 0); + // for signed, we must ensure the last bit has the right sign, as it will + // zero extend + return std::is_signed<T>::value + ? (temp != 0 && temp != T(-1)) || (value >= 0 && (byte & 64)) || + (value < 0 && !(byte & 64)) + : (temp != 0); } void write(std::vector<uint8_t>* out) { @@ -106,8 +109,8 @@ struct LEB { T payload = byte & 127; typedef typename std::make_unsigned<T>::type mask_type; auto shift_mask = 0 == shift - ? ~mask_type(0) - : ((mask_type(1) << (sizeof(T) * 8 - shift)) - 1u); + ? ~mask_type(0) + : ((mask_type(1) << (sizeof(T) * 8 - shift)) - 1u); T significant_payload = payload & shift_mask; if (significant_payload != payload) { if (!(std::is_signed<T>::value && last)) { @@ -115,7 +118,8 @@ struct LEB { } } value |= significant_payload << shift; - if (last) break; + if (last) + break; shift += 7; if (size_t(shift) >= sizeof(T) * 8) { throw ParseException("LEB overflow"); @@ -130,7 +134,8 @@ struct LEB { value <<= sext_bits; value >>= sext_bits; if (value >= 0) { - throw ParseException(" LEBsign-extend should produce a negative value"); + throw ParseException( + " LEBsign-extend should produce a negative value"); } } } @@ -155,33 +160,48 @@ public: BufferWithRandomAccess(bool debug = false) : debug(debug) {} BufferWithRandomAccess& operator<<(int8_t x) { - if (debug) std::cerr << "writeInt8: " << (int)(uint8_t)x << " (at " << size() << ")" << std::endl; + if (debug) + std::cerr << "writeInt8: " << (int)(uint8_t)x << " (at " << size() << ")" + << std::endl; push_back(x); return *this; } BufferWithRandomAccess& operator<<(int16_t x) { - if (debug) std::cerr << "writeInt16: " << x << " (at " << size() << ")" << std::endl; + if (debug) + std::cerr << "writeInt16: " << x << " (at " << size() << ")" << std::endl; push_back(x & 0xff); push_back(x >> 8); return *this; } BufferWithRandomAccess& operator<<(int32_t x) { - if (debug) std::cerr << "writeInt32: " << x << " (at " << size() << ")" << std::endl; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; + if (debug) + std::cerr << "writeInt32: " << x << " (at " << size() << ")" << std::endl; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; push_back(x & 0xff); return *this; } BufferWithRandomAccess& operator<<(int64_t x) { - if (debug) std::cerr << "writeInt64: " << x << " (at " << size() << ")" << std::endl; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; + if (debug) + std::cerr << "writeInt64: " << x << " (at " << size() << ")" << std::endl; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; push_back(x & 0xff); return *this; } @@ -189,7 +209,8 @@ public: size_t before = -1; if (debug) { before = size(); - std::cerr << "writeU32LEB: " << x.value << " (at " << before << ")" << std::endl; + std::cerr << "writeU32LEB: " << x.value << " (at " << before << ")" + << std::endl; } x.write(this); if (debug) { @@ -203,7 +224,8 @@ public: size_t before = -1; if (debug) { before = size(); - std::cerr << "writeU64LEB: " << x.value << " (at " << before << ")" << std::endl; + std::cerr << "writeU64LEB: " << x.value << " (at " << before << ")" + << std::endl; } x.write(this); if (debug) { @@ -217,7 +239,8 @@ public: size_t before = -1; if (debug) { before = size(); - std::cerr << "writeS32LEB: " << x.value << " (at " << before << ")" << std::endl; + std::cerr << "writeS32LEB: " << x.value << " (at " << before << ")" + << std::endl; } x.write(this); if (debug) { @@ -231,7 +254,8 @@ public: size_t before = -1; if (debug) { before = size(); - std::cerr << "writeS64LEB: " << x.value << " (at " << before << ")" << std::endl; + std::cerr << "writeS64LEB: " << x.value << " (at " << before << ")" + << std::endl; } x.write(this); if (debug) { @@ -242,57 +266,63 @@ public: return *this; } - BufferWithRandomAccess& operator<<(uint8_t x) { - return *this << (int8_t)x; - } - BufferWithRandomAccess& operator<<(uint16_t x) { - return *this << (int16_t)x; - } - BufferWithRandomAccess& operator<<(uint32_t x) { - return *this << (int32_t)x; - } - BufferWithRandomAccess& operator<<(uint64_t x) { - return *this << (int64_t)x; - } + BufferWithRandomAccess& operator<<(uint8_t x) { return *this << (int8_t)x; } + BufferWithRandomAccess& operator<<(uint16_t x) { return *this << (int16_t)x; } + BufferWithRandomAccess& operator<<(uint32_t x) { return *this << (int32_t)x; } + BufferWithRandomAccess& operator<<(uint64_t x) { return *this << (int64_t)x; } BufferWithRandomAccess& operator<<(float x) { - if (debug) std::cerr << "writeFloat32: " << x << " (at " << size() << ")" << std::endl; + if (debug) + std::cerr << "writeFloat32: " << x << " (at " << size() << ")" + << std::endl; return *this << Literal(x).reinterpreti32(); } BufferWithRandomAccess& operator<<(double x) { - if (debug) std::cerr << "writeFloat64: " << x << " (at " << size() << ")" << std::endl; + if (debug) + std::cerr << "writeFloat64: " << x << " (at " << size() << ")" + << std::endl; return *this << Literal(x).reinterpreti64(); } void writeAt(size_t i, uint16_t x) { - if (debug) std::cerr << "backpatchInt16: " << x << " (at " << i << ")" << std::endl; + if (debug) + std::cerr << "backpatchInt16: " << x << " (at " << i << ")" << std::endl; (*this)[i] = x & 0xff; - (*this)[i+1] = x >> 8; + (*this)[i + 1] = x >> 8; } void writeAt(size_t i, uint32_t x) { - if (debug) std::cerr << "backpatchInt32: " << x << " (at " << i << ")" << std::endl; - (*this)[i] = x & 0xff; x >>= 8; - (*this)[i+1] = x & 0xff; x >>= 8; - (*this)[i+2] = x & 0xff; x >>= 8; - (*this)[i+3] = x & 0xff; + if (debug) + std::cerr << "backpatchInt32: " << x << " (at " << i << ")" << std::endl; + (*this)[i] = x & 0xff; + x >>= 8; + (*this)[i + 1] = x & 0xff; + x >>= 8; + (*this)[i + 2] = x & 0xff; + x >>= 8; + (*this)[i + 3] = x & 0xff; } // writes out an LEB to an arbitrary location. this writes the LEB as a full // 5 bytes, the fixed amount that can easily be set aside ahead of time void writeAtFullFixedSize(size_t i, U32LEB x) { - if (debug) std::cerr << "backpatchU32LEB: " << x.value << " (at " << i << ")" << std::endl; - x.writeAt(this, i, MaxLEB32Bytes); // fill all 5 bytes, we have to do this when backpatching + if (debug) + std::cerr << "backpatchU32LEB: " << x.value << " (at " << i << ")" + << std::endl; + // fill all 5 bytes, we have to do this when backpatching + x.writeAt(this, i, MaxLEB32Bytes); } // writes out an LEB of normal size // returns how many bytes were written size_t writeAt(size_t i, U32LEB x) { - if (debug) std::cerr << "writeAtU32LEB: " << x.value << " (at " << i << ")" << std::endl; + if (debug) + std::cerr << "writeAtU32LEB: " << x.value << " (at " << i << ")" + << std::endl; return x.writeAt(this, i); } - template<typename T> - void writeTo(T& o) { - for (auto c : *this) o << c; + template<typename T> void writeTo(T& o) { + for (auto c : *this) + o << c; } std::vector<char> getAsChars() { @@ -305,10 +335,7 @@ public: namespace BinaryConsts { -enum Meta { - Magic = 0x6d736100, - Version = 0x01 -}; +enum Meta { Magic = 0x6d736100, Version = 0x01 }; enum Section { User = 0, @@ -333,10 +360,10 @@ enum SegmentFlag { enum EncodedType { // value_type - i32 = -0x1, // 0x7f - i64 = -0x2, // 0x7e - f32 = -0x3, // 0x7d - f64 = -0x4, // 0x7c + i32 = -0x1, // 0x7f + i64 = -0x2, // 0x7e + f32 = -0x3, // 0x7d + f64 = -0x4, // 0x7c v128 = -0x5, // 0x7b // elem_type AnyFunc = -0x10, // 0x70 @@ -394,7 +421,6 @@ enum ASTNodes { GetGlobal = 0x23, SetGlobal = 0x24, - I32LoadMem = 0x28, I64LoadMem = 0x29, F32LoadMem = 0x2a, @@ -809,15 +835,12 @@ enum BulkMemoryOpcodes { }; enum MemoryAccess { - Offset = 0x10, // bit 4 - Alignment = 0x80, // bit 7 + Offset = 0x10, // bit 4 + Alignment = 0x80, // bit 7 NaturalAlignment = 0 }; -enum MemoryFlags { - HasMaximum = 1 << 0, - IsShared = 1 << 1 -}; +enum MemoryFlags { HasMaximum = 1 << 0, IsShared = 1 << 1 }; enum FeaturePrefix { FeatureUsed = '+', @@ -827,18 +850,30 @@ enum FeaturePrefix { } // namespace BinaryConsts - inline S32LEB binaryType(Type type) { int ret = 0; switch (type) { // None only used for block signatures. TODO: Separate out? - case none: ret = BinaryConsts::EncodedType::Empty; break; - case i32: ret = BinaryConsts::EncodedType::i32; break; - case i64: ret = BinaryConsts::EncodedType::i64; break; - case f32: ret = BinaryConsts::EncodedType::f32; break; - case f64: ret = BinaryConsts::EncodedType::f64; break; - case v128: ret = BinaryConsts::EncodedType::v128; break; - case unreachable: WASM_UNREACHABLE(); + case none: + ret = BinaryConsts::EncodedType::Empty; + break; + case i32: + ret = BinaryConsts::EncodedType::i32; + break; + case i64: + ret = BinaryConsts::EncodedType::i64; + break; + case f32: + ret = BinaryConsts::EncodedType::f32; + break; + case f64: + ret = BinaryConsts::EncodedType::f64; + break; + case v128: + ret = BinaryConsts::EncodedType::v128; + break; + case unreachable: + WASM_UNREACHABLE(); } return S32LEB(ret); } @@ -847,10 +882,8 @@ inline S32LEB binaryType(Type type) { class WasmBinaryWriter { public: - WasmBinaryWriter(Module* input, - BufferWithRandomAccess& o, - bool debug = false) : - wasm(input), o(o), debug(debug) { + WasmBinaryWriter(Module* input, BufferWithRandomAccess& o, bool debug = false) + : wasm(input), o(o), debug(debug) { prepare(); } @@ -859,8 +892,9 @@ public: struct Entry { Name name; size_t offset; // where the entry starts - size_t size; // the size of the entry - Entry(Name name, size_t offset, size_t size) : name(name), offset(offset), size(size) {} + size_t size; // the size of the entry + Entry(Name name, size_t offset, size_t size) + : name(name), offset(offset), size(size) {} }; std::vector<Entry> functionBodies; } tableOfContents; @@ -875,9 +909,11 @@ public: void write(); void writeHeader(); int32_t writeU32LEBPlaceholder(); - void writeResizableLimits(Address initial, Address maximum, bool hasMaximum, bool shared); - template<typename T> - int32_t startSection(T code); + void writeResizableLimits(Address initial, + Address maximum, + bool hasMaximum, + bool shared); + template<typename T> int32_t startSection(T code); void finishSection(int32_t start); int32_t startSubsection(BinaryConsts::UserSections::Subsection code); void finishSubsection(int32_t start); @@ -895,8 +931,10 @@ public: void writeDataCount(); void writeDataSegments(); - std::unordered_map<Name, Index> mappedFunctions; // name of the Function => index. first imports, then internals - std::unordered_map<Name, uint32_t> mappedGlobals; // name of the Global => index. first imported globals, then internal globals + // name of the Function => index. first imports, then internals + std::unordered_map<Name, Index> mappedFunctions; + // name of the Global => index. first imported globals, then internal globals + std::unordered_map<Name, uint32_t> mappedGlobals; uint32_t getFunctionIndex(Name name); uint32_t getGlobalIndex(Name name); @@ -925,13 +963,14 @@ public: const char* data; size_t size; size_t pointerLocation; - Buffer(const char* data, size_t size, size_t pointerLocation) : data(data), size(size), pointerLocation(pointerLocation) {} + Buffer(const char* data, size_t size, size_t pointerLocation) + : data(data), size(size), pointerLocation(pointerLocation) {} }; std::vector<Buffer> buffersToWrite; void emitBuffer(const char* data, size_t size); - void emitString(const char *str); + void emitString(const char* str); void finishUp(); Module* getModule() { return wasm; } @@ -948,9 +987,10 @@ private: MixedArena allocator; - // storage of source map locations until the section is placed at its final location - // (shrinking LEBs may cause changes there) - std::vector<std::pair<size_t, const Function::DebugLocation*>> sourceMapLocations; + // storage of source map locations until the section is placed at its final + // location (shrinking LEBs may cause changes there) + std::vector<std::pair<size_t, const Function::DebugLocation*>> + sourceMapLocations; size_t sourceMapLocationsSizeAtSectionStart; Function::DebugLocation lastDebugLocation; @@ -975,13 +1015,8 @@ class WasmBinaryBuilder { public: WasmBinaryBuilder(Module& wasm, const std::vector<char>& input, bool debug) - : wasm(wasm), - allocator(wasm.allocator), - input(input), - debug(debug), - sourceMap(nullptr), - nextDebugLocation(0, { 0, 0, 0 }), - debugLocation() {} + : wasm(wasm), allocator(wasm.allocator), input(input), debug(debug), + sourceMap(nullptr), nextDebugLocation(0, {0, 0, 0}), debugLocation() {} void read(); void readUserSection(size_t payloadLen); @@ -993,7 +1028,8 @@ public: uint32_t getInt32(); uint64_t getInt64(); uint8_t getLaneIndex(size_t lanes); - // it is unsafe to return a float directly, due to ABI issues with the signalling bit + // it is unsafe to return a float directly, due to ABI issues with the + // signalling bit Literal getFloat32Literal(); Literal getFloat64Literal(); Literal getVec128Literal(); @@ -1016,7 +1052,10 @@ public: // gets a name in the combined function import+defined function space Name getFunctionIndexName(Index i); - void getResizableLimits(Address& initial, Address& max, bool& shared, Address defaultIfNoMax); + void getResizableLimits(Address& initial, + Address& max, + bool& shared, + Address defaultIfNoMax); void readImports(); std::vector<FunctionType*> functionTypes; // types of defined functions @@ -1026,12 +1065,20 @@ public: Name getNextLabel(); - // We read functions before we know their names, so we need to backpatch the names later - std::vector<Function*> functions; // we store functions here before wasm.addFunction after we know their names - std::vector<Function*> functionImports; // we store function imports here before wasm.addFunctionImport after we know their names - std::map<Index, std::vector<Call*>> functionCalls; // at index i we have all calls to the function i + // We read functions before we know their names, so we need to backpatch the + // names later + + // we store functions here before wasm.addFunction after we know their names + std::vector<Function*> functions; + // we store function imports here before wasm.addFunctionImport after we know + // their names + std::vector<Function*> functionImports; + // at index i we have all calls to the function i + std::map<Index, std::vector<Call*>> functionCalls; Function* currFunction = nullptr; - Index endOfFunction = -1; // before we see a function (like global init expressions), there is no end of function to check + // before we see a function (like global init expressions), there is no end of + // function to check + Index endOfFunction = -1; // Throws a parsing error if we are not in a function context void requireFunctionContext(const char* error); @@ -1051,22 +1098,24 @@ public: BreakTarget(Name name, int arity) : name(name), arity(arity) {} }; std::vector<BreakTarget> breakStack; - // the names that breaks target. this lets us know if a block has breaks to it or not. + // the names that breaks target. this lets us know if a block has breaks to it + // or not. std::unordered_set<Name> breakTargetNames; std::vector<Expression*> expressionStack; - // set when we know code is unreachable in the sense of the wasm spec: we are in a block - // and after an unreachable element. - // this helps parse stacky wasm code, which can be unsuitable for our IR when unreachable. + // set when we know code is unreachable in the sense of the wasm spec: we are + // in a block and after an unreachable element. this helps parse stacky wasm + // code, which can be unsuitable for our IR when unreachable. bool unreachableInTheWasmSense; - // set when the current code being processed will not be emitted in the output, which is the - // case when it is literally unreachable, for example, + // set when the current code being processed will not be emitted in the + // output, which is the case when it is literally unreachable, for example, // (block $a // (unreachable) // (block $b - // ;; code here is reachable in the wasm sense, even though $b as a whole is not + // ;; code here is reachable in the wasm sense, even though $b as a whole + // ;; is not // (unreachable) // ;; code here is unreachable in the wasm sense // ) @@ -1075,14 +1124,16 @@ public: BinaryConsts::ASTNodes lastSeparator = BinaryConsts::End; - // process a block-type scope, until an end or else marker, or the end of the function + // process a block-type scope, until an end or else marker, or the end of the + // function void processExpressions(); void skipUnreachableCode(); Expression* popExpression(); Expression* popNonVoidExpression(); - std::map<Index, Name> mappedGlobals; // index of the Global => name. first imported globals, then internal globals + // index of the Global => name. first imported globals, then internal globals + std::map<Index, Name> mappedGlobals; Name getGlobalName(Index index); void validateBinary(); // validations that cannot be performed on the Module @@ -1102,9 +1153,7 @@ public: void readFeatures(size_t); // Debug information reading helpers - void setDebugLocations(std::istream* sourceMap_) { - sourceMap = sourceMap_; - } + void setDebugLocations(std::istream* sourceMap_) { sourceMap = sourceMap_; } std::unordered_map<std::string, Index> debugInfoFileIndices; void readNextDebugLocation(); void readSourceMapHeader(); @@ -1122,13 +1171,13 @@ public: void visitIf(If* curr); void visitLoop(Loop* curr); BreakTarget getBreakTarget(int32_t offset); - void visitBreak(Break *curr, uint8_t code); + void visitBreak(Break* curr, uint8_t code); void visitSwitch(Switch* curr); void visitCall(Call* curr); void visitCallIndirect(CallIndirect* curr); void visitGetLocal(GetLocal* curr); - void visitSetLocal(SetLocal *curr, uint8_t code); + void visitSetLocal(SetLocal* curr, uint8_t code); void visitGetGlobal(GetGlobal* curr); void visitSetGlobal(SetGlobal* curr); void readMemoryAccess(Address& alignment, Address& offset); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 0bbc8eebc..8e7cc9908 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -17,8 +17,8 @@ #ifndef wasm_wasm_builder_h #define wasm_wasm_builder_h -#include "wasm.h" #include "ir/manipulation.h" +#include "wasm.h" namespace wasm { @@ -90,9 +90,7 @@ public: // IR nodes - Nop* makeNop() { - return allocator.alloc<Nop>(); - } + Nop* makeNop() { return allocator.alloc<Nop>(); } Block* makeBlock(Expression* first = nullptr) { auto* ret = allocator.alloc<Block>(); if (first) { @@ -139,53 +137,75 @@ public: ret->finalize(type); return ret; } - If* makeIf(Expression* condition, Expression* ifTrue, Expression* ifFalse = nullptr) { + If* makeIf(Expression* condition, + Expression* ifTrue, + Expression* ifFalse = nullptr) { auto* ret = allocator.alloc<If>(); - ret->condition = condition; ret->ifTrue = ifTrue; ret->ifFalse = ifFalse; + ret->condition = condition; + ret->ifTrue = ifTrue; + ret->ifFalse = ifFalse; ret->finalize(); return ret; } - If* makeIf(Expression* condition, Expression* ifTrue, Expression* ifFalse, Type type) { + If* makeIf(Expression* condition, + Expression* ifTrue, + Expression* ifFalse, + Type type) { auto* ret = allocator.alloc<If>(); - ret->condition = condition; ret->ifTrue = ifTrue; ret->ifFalse = ifFalse; + ret->condition = condition; + ret->ifTrue = ifTrue; + ret->ifFalse = ifFalse; ret->finalize(type); return ret; } Loop* makeLoop(Name name, Expression* body) { auto* ret = allocator.alloc<Loop>(); - ret->name = name; ret->body = body; + ret->name = name; + ret->body = body; ret->finalize(); return ret; } - Break* makeBreak(Name name, Expression* value = nullptr, Expression* condition = nullptr) { + Break* makeBreak(Name name, + Expression* value = nullptr, + Expression* condition = nullptr) { auto* ret = allocator.alloc<Break>(); - ret->name = name; ret->value = value; ret->condition = condition; + ret->name = name; + ret->value = value; + ret->condition = condition; ret->finalize(); return ret; } template<typename T> - Switch* makeSwitch(T& list, Name default_, Expression* condition, Expression* value = nullptr) { + Switch* makeSwitch(T& list, + Name default_, + Expression* condition, + Expression* value = nullptr) { auto* ret = allocator.alloc<Switch>(); ret->targets.set(list); - ret->default_ = default_; ret->value = value; ret->condition = condition; + ret->default_ = default_; + ret->value = value; + ret->condition = condition; return ret; } Call* makeCall(Name target, const std::vector<Expression*>& args, Type type) { auto* call = allocator.alloc<Call>(); - call->type = type; // not all functions may exist yet, so type must be provided + // not all functions may exist yet, so type must be provided + call->type = type; call->target = target; call->operands.set(args); return call; } - template<typename T> - Call* makeCall(Name target, const T& args, Type type) { + template<typename T> Call* makeCall(Name target, const T& args, Type type) { auto* call = allocator.alloc<Call>(); - call->type = type; // not all functions may exist yet, so type must be provided + // not all functions may exist yet, so type must be provided + call->type = type; call->target = target; call->operands.set(args); return call; } - CallIndirect* makeCallIndirect(FunctionType* type, Expression* target, const std::vector<Expression*>& args) { + CallIndirect* makeCallIndirect(FunctionType* type, + Expression* target, + const std::vector<Expression*>& args) { auto* call = allocator.alloc<CallIndirect>(); call->fullType = type->name; call->type = type->result; @@ -193,7 +213,10 @@ public: call->operands.set(args); return call; } - CallIndirect* makeCallIndirect(Name fullType, Expression* target, const std::vector<Expression*>& args, Type type) { + CallIndirect* makeCallIndirect(Name fullType, + Expression* target, + const std::vector<Expression*>& args, + Type type) { auto* call = allocator.alloc<CallIndirect>(); call->fullType = fullType; call->type = type; @@ -235,19 +258,33 @@ public: ret->finalize(); return ret; } - Load* makeLoad(unsigned bytes, bool signed_, uint32_t offset, unsigned align, Expression *ptr, Type type) { + Load* makeLoad(unsigned bytes, + bool signed_, + uint32_t offset, + unsigned align, + Expression* ptr, + Type type) { auto* ret = allocator.alloc<Load>(); ret->isAtomic = false; - ret->bytes = bytes; ret->signed_ = signed_; ret->offset = offset; ret->align = align; ret->ptr = ptr; + ret->bytes = bytes; + ret->signed_ = signed_; + ret->offset = offset; + ret->align = align; + ret->ptr = ptr; ret->type = type; return ret; } - Load* makeAtomicLoad(unsigned bytes, uint32_t offset, Expression* ptr, Type type) { + Load* + makeAtomicLoad(unsigned bytes, uint32_t offset, Expression* ptr, Type type) { Load* load = makeLoad(bytes, false, offset, bytes, ptr, type); load->isAtomic = true; return load; } - AtomicWait* makeAtomicWait(Expression* ptr, Expression* expected, Expression* timeout, Type expectedType, Address offset) { + AtomicWait* makeAtomicWait(Expression* ptr, + Expression* expected, + Expression* timeout, + Type expectedType, + Address offset) { auto* wait = allocator.alloc<AtomicWait>(); wait->offset = offset; wait->ptr = ptr; @@ -257,7 +294,8 @@ public: wait->finalize(); return wait; } - AtomicNotify* makeAtomicNotify(Expression* ptr, Expression* notifyCount, Address offset) { + AtomicNotify* + makeAtomicNotify(Expression* ptr, Expression* notifyCount, Address offset) { auto* notify = allocator.alloc<AtomicNotify>(); notify->offset = offset; notify->ptr = ptr; @@ -265,21 +303,39 @@ public: notify->finalize(); return notify; } - Store* makeStore(unsigned bytes, uint32_t offset, unsigned align, Expression *ptr, Expression *value, Type type) { + Store* makeStore(unsigned bytes, + uint32_t offset, + unsigned align, + Expression* ptr, + Expression* value, + Type type) { auto* ret = allocator.alloc<Store>(); ret->isAtomic = false; - ret->bytes = bytes; ret->offset = offset; ret->align = align; ret->ptr = ptr; ret->value = value; ret->valueType = type; + ret->bytes = bytes; + ret->offset = offset; + ret->align = align; + ret->ptr = ptr; + ret->value = value; + ret->valueType = type; ret->finalize(); assert(isConcreteType(ret->value->type) ? ret->value->type == type : true); return ret; } - Store* makeAtomicStore(unsigned bytes, uint32_t offset, Expression* ptr, Expression* value, Type type) { + Store* makeAtomicStore(unsigned bytes, + uint32_t offset, + Expression* ptr, + Expression* value, + Type type) { Store* store = makeStore(bytes, offset, bytes, ptr, value, type); store->isAtomic = true; return store; } - AtomicRMW* makeAtomicRMW(AtomicRMWOp op, unsigned bytes, uint32_t offset, - Expression* ptr, Expression* value, Type type) { + AtomicRMW* makeAtomicRMW(AtomicRMWOp op, + unsigned bytes, + uint32_t offset, + Expression* ptr, + Expression* value, + Type type) { auto* ret = allocator.alloc<AtomicRMW>(); ret->op = op; ret->bytes = bytes; @@ -290,9 +346,12 @@ public: ret->finalize(); return ret; } - AtomicCmpxchg* makeAtomicCmpxchg(unsigned bytes, uint32_t offset, - Expression* ptr, Expression* expected, - Expression* replacement, Type type) { + AtomicCmpxchg* makeAtomicCmpxchg(unsigned bytes, + uint32_t offset, + Expression* ptr, + Expression* expected, + Expression* replacement, + Type type) { auto* ret = allocator.alloc<AtomicCmpxchg>(); ret->bytes = bytes; ret->offset = offset; @@ -303,7 +362,8 @@ public: ret->finalize(); return ret; } - SIMDExtract* makeSIMDExtract(SIMDExtractOp op, Expression* vec, uint8_t index) { + SIMDExtract* + makeSIMDExtract(SIMDExtractOp op, Expression* vec, uint8_t index) { auto* ret = allocator.alloc<SIMDExtract>(); ret->op = op; ret->vec = vec; @@ -311,7 +371,10 @@ public: ret->finalize(); return ret; } - SIMDReplace* makeSIMDReplace(SIMDReplaceOp op, Expression* vec, uint8_t index, Expression* value) { + SIMDReplace* makeSIMDReplace(SIMDReplaceOp op, + Expression* vec, + uint8_t index, + Expression* value) { auto* ret = allocator.alloc<SIMDReplace>(); ret->op = op; ret->vec = vec; @@ -320,7 +383,9 @@ public: ret->finalize(); return ret; } - SIMDShuffle* makeSIMDShuffle(Expression* left, Expression* right, const std::array<uint8_t, 16>& mask) { + SIMDShuffle* makeSIMDShuffle(Expression* left, + Expression* right, + const std::array<uint8_t, 16>& mask) { auto* ret = allocator.alloc<SIMDShuffle>(); ret->left = left; ret->right = right; @@ -328,7 +393,8 @@ public: ret->finalize(); return ret; } - SIMDBitselect* makeSIMDBitselect(Expression* left, Expression* right, Expression* cond) { + SIMDBitselect* + makeSIMDBitselect(Expression* left, Expression* right, Expression* cond) { auto* ret = allocator.alloc<SIMDBitselect>(); ret->left = left; ret->right = right; @@ -344,7 +410,10 @@ public: ret->finalize(); return ret; } - MemoryInit* makeMemoryInit(uint32_t segment, Expression* dest, Expression* offset, Expression* size) { + MemoryInit* makeMemoryInit(uint32_t segment, + Expression* dest, + Expression* offset, + Expression* size) { auto* ret = allocator.alloc<MemoryInit>(); ret->segment = segment; ret->dest = dest; @@ -359,7 +428,8 @@ public: ret->finalize(); return ret; } - MemoryCopy* makeMemoryCopy(Expression* dest, Expression* source, Expression* size) { + MemoryCopy* + makeMemoryCopy(Expression* dest, Expression* source, Expression* size) { auto* ret = allocator.alloc<MemoryCopy>(); ret->dest = dest; ret->source = source; @@ -367,7 +437,8 @@ public: ret->finalize(); return ret; } - MemoryFill* makeMemoryFill(Expression* dest, Expression* value, Expression* size) { + MemoryFill* + makeMemoryFill(Expression* dest, Expression* value, Expression* size) { auto* ret = allocator.alloc<MemoryFill>(); ret->dest = dest; ret->value = value; @@ -382,30 +453,37 @@ public: ret->type = value.type; return ret; } - Unary* makeUnary(UnaryOp op, Expression *value) { + Unary* makeUnary(UnaryOp op, Expression* value) { auto* ret = allocator.alloc<Unary>(); - ret->op = op; ret->value = value; + ret->op = op; + ret->value = value; ret->finalize(); return ret; } - Binary* makeBinary(BinaryOp op, Expression *left, Expression *right) { + Binary* makeBinary(BinaryOp op, Expression* left, Expression* right) { auto* ret = allocator.alloc<Binary>(); - ret->op = op; ret->left = left; ret->right = right; + ret->op = op; + ret->left = left; + ret->right = right; ret->finalize(); return ret; } - Select* makeSelect(Expression* condition, Expression *ifTrue, Expression *ifFalse) { + Select* + makeSelect(Expression* condition, Expression* ifTrue, Expression* ifFalse) { auto* ret = allocator.alloc<Select>(); - ret->condition = condition; ret->ifTrue = ifTrue; ret->ifFalse = ifFalse; + ret->condition = condition; + ret->ifTrue = ifTrue; + ret->ifFalse = ifFalse; ret->finalize(); return ret; } - Return* makeReturn(Expression *value = nullptr) { + Return* makeReturn(Expression* value = nullptr) { auto* ret = allocator.alloc<Return>(); ret->value = value; return ret; } - Host* makeHost(HostOp op, Name nameOperand, std::vector<Expression*>&& operands) { + Host* + makeHost(HostOp op, Name nameOperand, std::vector<Expression*>&& operands) { auto* ret = allocator.alloc<Host>(); ret->op = op; ret->nameOperand = nameOperand; @@ -413,13 +491,11 @@ public: ret->finalize(); return ret; } - Unreachable* makeUnreachable() { - return allocator.alloc<Unreachable>(); - } + Unreachable* makeUnreachable() { return allocator.alloc<Unreachable>(); } // Additional helpers - Drop* makeDrop(Expression *value) { + Drop* makeDrop(Expression* value) { auto* ret = allocator.alloc<Drop>(); ret->value = value; ret->finalize(); @@ -467,11 +543,14 @@ public: clearLocalNames(func); } - // ensure a node is a block, if it isn't already, and optionally append to the block + // ensure a node is a block, if it isn't already, and optionally append to the + // block Block* blockify(Expression* any, Expression* append = nullptr) { Block* block = nullptr; - if (any) block = any->dynCast<Block>(); - if (!block) block = makeBlock(any); + if (any) + block = any->dynCast<Block>(); + if (!block) + block = makeBlock(any); if (append) { block->list.push_back(append); block->finalize(); @@ -479,17 +558,21 @@ public: return block; } - template<typename ...Ts> + template<typename... Ts> Block* blockify(Expression* any, Expression* append, Ts... args) { return blockify(blockify(any, append), args...); } - // ensure a node is a block, if it isn't already, and optionally append to the block - // this variant sets a name for the block, so it will not reuse a block already named - Block* blockifyWithName(Expression* any, Name name, Expression* append = nullptr) { + // ensure a node is a block, if it isn't already, and optionally append to the + // block this variant sets a name for the block, so it will not reuse a block + // already named + Block* + blockifyWithName(Expression* any, Name name, Expression* append = nullptr) { Block* block = nullptr; - if (any) block = any->dynCast<Block>(); - if (!block || block->name.is()) block = makeBlock(any); + if (any) + block = any->dynCast<Block>(); + if (!block || block->name.is()) + block = makeBlock(any); block->name = name; if (append) { block->list.push_back(append); @@ -498,8 +581,8 @@ public: return block; } - // a helper for the common pattern of a sequence of two expressions. Similar to - // blockify, but does *not* reuse a block if the first is one. + // a helper for the common pattern of a sequence of two expressions. Similar + // to blockify, but does *not* reuse a block if the first is one. Block* makeSequence(Expression* left, Expression* right) { auto* block = makeBlock(left); block->list.push_back(right); @@ -508,7 +591,8 @@ public: } // Grab a slice out of a block, replacing it with nops, and returning - // either another block with the contents (if more than 1) or a single expression + // either another block with the contents (if more than 1) or a single + // expression Expression* stealSlice(Block* input, Index from, Index to) { Expression* ret; if (to == from + 1) { @@ -535,7 +619,8 @@ public: // Drop an expression if it has a concrete type Expression* dropIfConcretelyTyped(Expression* curr) { - if (!isConcreteType(curr->type)) return curr; + if (!isConcreteType(curr->type)) + return curr; return makeDrop(curr); } @@ -547,35 +632,42 @@ public: // returns a replacement with the precise same type, and with // minimal contents. as a replacement, this may reuse the // input node - template<typename T> - Expression* replaceWithIdenticalType(T* curr) { + template<typename T> Expression* replaceWithIdenticalType(T* curr) { Literal value; // TODO: reuse node conditionally when possible for literals switch (curr->type) { - case i32: value = Literal(int32_t(0)); break; - case i64: value = Literal(int64_t(0)); break; - case f32: value = Literal(float(0)); break; - case f64: value = Literal(double(0)); break; + case i32: + value = Literal(int32_t(0)); + break; + case i64: + value = Literal(int64_t(0)); + break; + case f32: + value = Literal(float(0)); + break; + case f64: + value = Literal(double(0)); + break; case v128: { std::array<uint8_t, 16> bytes; bytes.fill(0); value = Literal(bytes.data()); break; } - case none: return ExpressionManipulator::nop(curr); - case unreachable: return ExpressionManipulator::convert<T, Unreachable>(curr); + case none: + return ExpressionManipulator::nop(curr); + case unreachable: + return ExpressionManipulator::convert<T, Unreachable>(curr); } return makeConst(value); } // Module-level helpers - enum Mutability { - Mutable, - Immutable - }; + enum Mutability { Mutable, Immutable }; - static Global* makeGlobal(Name name, Type type, Expression* init, Mutability mutable_) { + static Global* + makeGlobal(Name name, Type type, Expression* init, Mutability mutable_) { auto* glob = new Global; glob->name = name; glob->type = type; diff --git a/src/wasm-emscripten.h b/src/wasm-emscripten.h index 7d86031c7..686269a1c 100644 --- a/src/wasm-emscripten.h +++ b/src/wasm-emscripten.h @@ -17,10 +17,9 @@ #ifndef wasm_wasm_emscripten_h #define wasm_wasm_emscripten_h -#include "wasm.h" -#include "wasm-builder.h" #include "support/file.h" - +#include "wasm-builder.h" +#include "wasm.h" namespace wasm { @@ -29,10 +28,8 @@ namespace wasm { class EmscriptenGlueGenerator { public: EmscriptenGlueGenerator(Module& wasm, Address stackPointerOffset = Address(0)) - : wasm(wasm), - builder(wasm), - stackPointerOffset(stackPointerOffset), - useStackPointerGlobal(stackPointerOffset == 0) { } + : wasm(wasm), builder(wasm), stackPointerOffset(stackPointerOffset), + useStackPointerGlobal(stackPointerOffset == 0) {} void generateRuntimeFunctions(); Function* generateMemoryGrowthFunction(); @@ -48,15 +45,16 @@ public: // and restore functions. void replaceStackPointerGlobal(); - std::string generateEmscriptenMetadata( - Address staticBump, std::vector<Name> const& initializerFunctions); - + std::string + generateEmscriptenMetadata(Address staticBump, + std::vector<Name> const& initializerFunctions); void fixInvokeFunctionNames(); // Emits the data segments to a file. The file contains data from address base - // onwards (we must pass in base, as we can't tell it from the wasm - the first - // segment may start after a run of zeros, but we need those zeros in the file). + // onwards (we must pass in base, as we can't tell it from the wasm - the + // first segment may start after a run of zeros, but we need those zeros in + // the file). void separateDataSegments(Output* outfile, Address base); private: diff --git a/src/wasm-features.h b/src/wasm-features.h index 6ef704f82..d31715f18 100644 --- a/src/wasm-features.h +++ b/src/wasm-features.h @@ -36,13 +36,20 @@ struct FeatureSet { static std::string toString(Feature f) { switch (f) { - case Atomics: return "threads"; - case MutableGlobals: return "mutable-globals"; - case TruncSat: return "nontrapping-float-to-int"; - case SIMD: return "simd"; - case BulkMemory: return "bulk-memory"; - case SignExt: return "sign-ext"; - default: WASM_UNREACHABLE(); + case Atomics: + return "threads"; + case MutableGlobals: + return "mutable-globals"; + case TruncSat: + return "nontrapping-float-to-int"; + case SIMD: + return "simd"; + case BulkMemory: + return "bulk-memory"; + case SignExt: + return "sign-ext"; + default: + WASM_UNREACHABLE(); } } @@ -60,7 +67,9 @@ struct FeatureSet { bool hasAll() const { return features & All; } void makeMVP() { features = MVP; } - void set(Feature f, bool v = true) { features = v ? (features | f) : (features & ~f); } + void set(Feature f, bool v = true) { + features = v ? (features | f) : (features & ~f); + } void setAtomics(bool v = true) { set(Atomics, v); } void setMutableGlobals(bool v = true) { set(MutableGlobals, v); } void setTruncSat(bool v = true) { set(TruncSat, v); } @@ -74,14 +83,19 @@ struct FeatureSet { features = features & ~other.features & All; } - template<typename F> - void iterFeatures(F f) { - if (hasAtomics()) f(Atomics); - if (hasBulkMemory()) f(BulkMemory); - if (hasMutableGlobals()) f(MutableGlobals); - if (hasTruncSat()) f(TruncSat); - if (hasSignExt()) f(SignExt); - if (hasSIMD()) f(SIMD); + template<typename F> void iterFeatures(F f) { + if (hasAtomics()) + f(Atomics); + if (hasBulkMemory()) + f(BulkMemory); + if (hasMutableGlobals()) + f(MutableGlobals); + if (hasTruncSat()) + f(TruncSat); + if (hasSignExt()) + f(SignExt); + if (hasSIMD()) + f(SIMD); } bool operator<=(const FeatureSet& other) const { @@ -92,9 +106,7 @@ struct FeatureSet { return *this <= other && other <= *this; } - bool operator!=(const FeatureSet& other) const { - return !(*this == other); - } + bool operator!=(const FeatureSet& other) const { return !(*this == other); } FeatureSet& operator|=(const FeatureSet& other) { features |= other.features; diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 23121a849..dd6e2b073 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -27,17 +27,16 @@ #include <limits.h> #include <sstream> +#include "ir/module-utils.h" #include "support/bits.h" #include "support/safe_integer.h" -#include "wasm.h" #include "wasm-traversal.h" -#include "ir/module-utils.h" +#include "wasm.h" #ifdef WASM_INTERPRETER_DEBUG #include "wasm-printing.h" #endif - namespace wasm { using namespace cashew; @@ -46,11 +45,10 @@ using namespace cashew; extern Name WASM, RETURN_FLOW; -enum { - maxCallDepth = 250 -}; +enum { maxCallDepth = 250 }; -// Stuff that flows around during executing expressions: a literal, or a change in control flow. +// Stuff that flows around during executing expressions: a literal, or a change +// in control flow. class Flow { public: Flow() = default; @@ -69,7 +67,8 @@ public: } friend std::ostream& operator<<(std::ostream& o, Flow& flow) { - o << "(flow " << (flow.breakTo.is() ? flow.breakTo.str : "-") << " : " << flow.value << ')'; + o << "(flow " << (flow.breakTo.is() ? flow.breakTo.str : "-") << " : " + << flow.value << ')'; return o; } }; @@ -91,21 +90,33 @@ public: static void print(); }; -#define NOTE_ENTER(x) Indenter _int_blah(x); { \ - Indenter::print(); \ - std::cout << "visit " << x << " : " << curr << "\n"; } -#define NOTE_ENTER_(x) Indenter _int_blah(x); { \ - Indenter::print(); \ - std::cout << "visit " << x << "\n"; } -#define NOTE_NAME(p0) { \ - Indenter::print(); \ - std::cout << "name " << '(' << Name(p0) << ")\n"; } -#define NOTE_EVAL1(p0) { \ - Indenter::print(); \ - std::cout << "eval " #p0 " (" << p0 << ")\n"; } -#define NOTE_EVAL2(p0, p1) { \ - Indenter::print(); \ - std::cout << "eval " #p0 " (" << p0 << "), " #p1 " (" << p1 << ")\n"; } +#define NOTE_ENTER(x) \ + Indenter _int_blah(x); \ + { \ + Indenter::print(); \ + std::cout << "visit " << x << " : " << curr << "\n"; \ + } +#define NOTE_ENTER_(x) \ + Indenter _int_blah(x); \ + { \ + Indenter::print(); \ + std::cout << "visit " << x << "\n"; \ + } +#define NOTE_NAME(p0) \ + { \ + Indenter::print(); \ + std::cout << "name " << '(' << Name(p0) << ")\n"; \ + } +#define NOTE_EVAL1(p0) \ + { \ + Indenter::print(); \ + std::cout << "eval " #p0 " (" << p0 << ")\n"; \ + } +#define NOTE_EVAL2(p0, p1) \ + { \ + Indenter::print(); \ + std::cout << "eval " #p0 " (" << p0 << "), " #p1 " (" << p1 << ")\n"; \ + } #else // WASM_INTERPRETER_DEBUG #define NOTE_ENTER(x) #define NOTE_ENTER_(x) @@ -118,12 +129,15 @@ public: template<typename SubType> class ExpressionRunner : public OverriddenVisitor<SubType, Flow> { public: - Flow visit(Expression *curr) { + Flow visit(Expression* curr) { auto ret = OverriddenVisitor<SubType, Flow>::visit(curr); - if (!ret.breaking() && (isConcreteType(curr->type) || isConcreteType(ret.value.type))) { + if (!ret.breaking() && + (isConcreteType(curr->type) || isConcreteType(ret.value.type))) { #if 1 // def WASM_INTERPRETER_DEBUG if (ret.value.type != curr->type) { - std::cerr << "expected " << printType(curr->type) << ", seeing " << printType(ret.value.type) << " from\n" << curr << '\n'; + std::cerr << "expected " << printType(curr->type) << ", seeing " + << printType(ret.value.type) << " from\n" + << curr << '\n'; } #endif assert(ret.value.type == curr->type); @@ -131,9 +145,10 @@ public: return ret; } - Flow visitBlock(Block *curr) { + Flow visitBlock(Block* curr) { NOTE_ENTER("Block"); - // special-case Block, because Block nesting (in their first element) can be incredibly deep + // special-case Block, because Block nesting (in their first element) can be + // incredibly deep std::vector<Block*> stack; stack.push_back(curr); while (curr->list.size() > 0 && curr->list[0]->is<Block>()) { @@ -164,58 +179,68 @@ public: } return flow; } - Flow visitIf(If *curr) { + Flow visitIf(If* curr) { NOTE_ENTER("If"); Flow flow = visit(curr->condition); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; NOTE_EVAL1(flow.value); if (flow.value.geti32()) { Flow flow = visit(curr->ifTrue); - if (!flow.breaking() && !curr->ifFalse) flow.value = Literal(); // if_else returns a value, but if does not + if (!flow.breaking() && !curr->ifFalse) + flow.value = Literal(); // if_else returns a value, but if does not return flow; } - if (curr->ifFalse) return visit(curr->ifFalse); + if (curr->ifFalse) + return visit(curr->ifFalse); return Flow(); } - Flow visitLoop(Loop *curr) { + Flow visitLoop(Loop* curr) { NOTE_ENTER("Loop"); while (1) { Flow flow = visit(curr->body); if (flow.breaking()) { - if (flow.breakTo == curr->name) continue; // lol + if (flow.breakTo == curr->name) + continue; // lol } - return flow; // loop does not loop automatically, only continue achieves that + // loop does not loop automatically, only continue achieves that + return flow; } } - Flow visitBreak(Break *curr) { + Flow visitBreak(Break* curr) { NOTE_ENTER("Break"); bool condition = true; Flow flow; if (curr->value) { flow = visit(curr->value); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; } if (curr->condition) { Flow conditionFlow = visit(curr->condition); - if (conditionFlow.breaking()) return conditionFlow; + if (conditionFlow.breaking()) + return conditionFlow; condition = conditionFlow.value.getInteger() != 0; - if (!condition) return flow; + if (!condition) + return flow; } flow.breakTo = curr->name; return flow; } - Flow visitSwitch(Switch *curr) { + Flow visitSwitch(Switch* curr) { NOTE_ENTER("Switch"); Flow flow; Literal value; if (curr->value) { flow = visit(curr->value); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; value = flow.value; NOTE_EVAL1(value); } flow = visit(curr->condition); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; int64_t index = flow.value.getInteger(); Name target = curr->default_; if (index >= 0 && (size_t)index < curr->targets.size()) { @@ -226,7 +251,7 @@ public: return flow; } - Flow visitConst(Const *curr) { + Flow visitConst(Const* curr) { NOTE_ENTER("Const"); NOTE_EVAL1(curr->value); return Flow(curr->value); // heh @@ -235,429 +260,662 @@ public: // Unary and Binary nodes, the core math computations. We mostly just // delegate to the Literal::* methods, except we handle traps here. - Flow visitUnary(Unary *curr) { + Flow visitUnary(Unary* curr) { NOTE_ENTER("Unary"); Flow flow = visit(curr->value); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; Literal value = flow.value; NOTE_EVAL1(value); switch (curr->op) { case ClzInt32: - case ClzInt64: return value.countLeadingZeroes(); + case ClzInt64: + return value.countLeadingZeroes(); case CtzInt32: - case CtzInt64: return value.countTrailingZeroes(); + case CtzInt64: + return value.countTrailingZeroes(); case PopcntInt32: - case PopcntInt64: return value.popCount(); + case PopcntInt64: + return value.popCount(); case EqZInt32: - case EqZInt64: return value.eqz(); - case ReinterpretInt32: return value.castToF32(); - case ReinterpretInt64: return value.castToF64(); - case ExtendSInt32: return value.extendToSI64(); - case ExtendUInt32: return value.extendToUI64(); - case WrapInt64: return value.wrapToI32(); + case EqZInt64: + return value.eqz(); + case ReinterpretInt32: + return value.castToF32(); + case ReinterpretInt64: + return value.castToF64(); + case ExtendSInt32: + return value.extendToSI64(); + case ExtendUInt32: + return value.extendToUI64(); + case WrapInt64: + return value.wrapToI32(); case ConvertUInt32ToFloat32: - case ConvertUInt64ToFloat32: return value.convertUIToF32(); + case ConvertUInt64ToFloat32: + return value.convertUIToF32(); case ConvertUInt32ToFloat64: - case ConvertUInt64ToFloat64: return value.convertUIToF64(); + case ConvertUInt64ToFloat64: + return value.convertUIToF64(); case ConvertSInt32ToFloat32: - case ConvertSInt64ToFloat32: return value.convertSIToF32(); + case ConvertSInt64ToFloat32: + return value.convertSIToF32(); case ConvertSInt32ToFloat64: - case ConvertSInt64ToFloat64: return value.convertSIToF64(); + case ConvertSInt64ToFloat64: + return value.convertSIToF64(); case ExtendS8Int32: - case ExtendS8Int64: return value.extendS8(); + case ExtendS8Int64: + return value.extendS8(); case ExtendS16Int32: - case ExtendS16Int64: return value.extendS16(); - case ExtendS32Int64: return value.extendS32(); + case ExtendS16Int64: + return value.extendS16(); + case ExtendS32Int64: + return value.extendS32(); case NegFloat32: - case NegFloat64: return value.neg(); + case NegFloat64: + return value.neg(); case AbsFloat32: - case AbsFloat64: return value.abs(); + case AbsFloat64: + return value.abs(); case CeilFloat32: - case CeilFloat64: return value.ceil(); + case CeilFloat64: + return value.ceil(); case FloorFloat32: - case FloorFloat64: return value.floor(); + case FloorFloat64: + return value.floor(); case TruncFloat32: - case TruncFloat64: return value.trunc(); + case TruncFloat64: + return value.trunc(); case NearestFloat32: - case NearestFloat64: return value.nearbyint(); + case NearestFloat64: + return value.nearbyint(); case SqrtFloat32: - case SqrtFloat64: return value.sqrt(); + case SqrtFloat64: + return value.sqrt(); case TruncSFloat32ToInt32: case TruncSFloat64ToInt32: case TruncSFloat32ToInt64: - case TruncSFloat64ToInt64: return truncSFloat(curr, value); + case TruncSFloat64ToInt64: + return truncSFloat(curr, value); case TruncUFloat32ToInt32: case TruncUFloat64ToInt32: case TruncUFloat32ToInt64: - case TruncUFloat64ToInt64: return truncUFloat(curr, value); + case TruncUFloat64ToInt64: + return truncUFloat(curr, value); case TruncSatSFloat32ToInt32: - case TruncSatSFloat64ToInt32: return value.truncSatToSI32(); + case TruncSatSFloat64ToInt32: + return value.truncSatToSI32(); case TruncSatSFloat32ToInt64: - case TruncSatSFloat64ToInt64: return value.truncSatToSI64(); + case TruncSatSFloat64ToInt64: + return value.truncSatToSI64(); case TruncSatUFloat32ToInt32: - case TruncSatUFloat64ToInt32: return value.truncSatToUI32(); + case TruncSatUFloat64ToInt32: + return value.truncSatToUI32(); case TruncSatUFloat32ToInt64: - case TruncSatUFloat64ToInt64: return value.truncSatToUI64(); - case ReinterpretFloat32: return value.castToI32(); - case PromoteFloat32: return value.extendToF64(); - case ReinterpretFloat64: return value.castToI64(); - case DemoteFloat64: return value.demote(); - case SplatVecI8x16: return value.splatI8x16(); - case SplatVecI16x8: return value.splatI16x8(); - case SplatVecI32x4: return value.splatI32x4(); - case SplatVecI64x2: return value.splatI64x2(); - case SplatVecF32x4: return value.splatF32x4(); - case SplatVecF64x2: return value.splatF64x2(); - case NotVec128: return value.notV128(); - case NegVecI8x16: return value.negI8x16(); - case AnyTrueVecI8x16: return value.anyTrueI8x16(); - case AllTrueVecI8x16: return value.allTrueI8x16(); - case NegVecI16x8: return value.negI16x8(); - case AnyTrueVecI16x8: return value.anyTrueI16x8(); - case AllTrueVecI16x8: return value.allTrueI16x8(); - case NegVecI32x4: return value.negI32x4(); - case AnyTrueVecI32x4: return value.anyTrueI32x4(); - case AllTrueVecI32x4: return value.allTrueI32x4(); - case NegVecI64x2: return value.negI64x2(); - case AnyTrueVecI64x2: return value.anyTrueI64x2(); - case AllTrueVecI64x2: return value.allTrueI64x2(); - case AbsVecF32x4: return value.absF32x4(); - case NegVecF32x4: return value.negF32x4(); - case SqrtVecF32x4: return value.sqrtF32x4(); - case AbsVecF64x2: return value.absF64x2(); - case NegVecF64x2: return value.negF64x2(); - case SqrtVecF64x2: return value.sqrtF64x2(); - case TruncSatSVecF32x4ToVecI32x4: return value.truncSatToSI32x4(); - case TruncSatUVecF32x4ToVecI32x4: return value.truncSatToUI32x4(); - case TruncSatSVecF64x2ToVecI64x2: return value.truncSatToSI64x2(); - case TruncSatUVecF64x2ToVecI64x2: return value.truncSatToUI64x2(); - case ConvertSVecI32x4ToVecF32x4: return value.convertSToF32x4(); - case ConvertUVecI32x4ToVecF32x4: return value.convertUToF32x4(); - case ConvertSVecI64x2ToVecF64x2: return value.convertSToF64x2(); - case ConvertUVecI64x2ToVecF64x2: return value.convertUToF64x2(); - case InvalidUnary: WASM_UNREACHABLE(); + case TruncSatUFloat64ToInt64: + return value.truncSatToUI64(); + case ReinterpretFloat32: + return value.castToI32(); + case PromoteFloat32: + return value.extendToF64(); + case ReinterpretFloat64: + return value.castToI64(); + case DemoteFloat64: + return value.demote(); + case SplatVecI8x16: + return value.splatI8x16(); + case SplatVecI16x8: + return value.splatI16x8(); + case SplatVecI32x4: + return value.splatI32x4(); + case SplatVecI64x2: + return value.splatI64x2(); + case SplatVecF32x4: + return value.splatF32x4(); + case SplatVecF64x2: + return value.splatF64x2(); + case NotVec128: + return value.notV128(); + case NegVecI8x16: + return value.negI8x16(); + case AnyTrueVecI8x16: + return value.anyTrueI8x16(); + case AllTrueVecI8x16: + return value.allTrueI8x16(); + case NegVecI16x8: + return value.negI16x8(); + case AnyTrueVecI16x8: + return value.anyTrueI16x8(); + case AllTrueVecI16x8: + return value.allTrueI16x8(); + case NegVecI32x4: + return value.negI32x4(); + case AnyTrueVecI32x4: + return value.anyTrueI32x4(); + case AllTrueVecI32x4: + return value.allTrueI32x4(); + case NegVecI64x2: + return value.negI64x2(); + case AnyTrueVecI64x2: + return value.anyTrueI64x2(); + case AllTrueVecI64x2: + return value.allTrueI64x2(); + case AbsVecF32x4: + return value.absF32x4(); + case NegVecF32x4: + return value.negF32x4(); + case SqrtVecF32x4: + return value.sqrtF32x4(); + case AbsVecF64x2: + return value.absF64x2(); + case NegVecF64x2: + return value.negF64x2(); + case SqrtVecF64x2: + return value.sqrtF64x2(); + case TruncSatSVecF32x4ToVecI32x4: + return value.truncSatToSI32x4(); + case TruncSatUVecF32x4ToVecI32x4: + return value.truncSatToUI32x4(); + case TruncSatSVecF64x2ToVecI64x2: + return value.truncSatToSI64x2(); + case TruncSatUVecF64x2ToVecI64x2: + return value.truncSatToUI64x2(); + case ConvertSVecI32x4ToVecF32x4: + return value.convertSToF32x4(); + case ConvertUVecI32x4ToVecF32x4: + return value.convertUToF32x4(); + case ConvertSVecI64x2ToVecF64x2: + return value.convertSToF64x2(); + case ConvertUVecI64x2ToVecF64x2: + return value.convertUToF64x2(); + case InvalidUnary: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } - Flow visitBinary(Binary *curr) { + Flow visitBinary(Binary* curr) { NOTE_ENTER("Binary"); Flow flow = visit(curr->left); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; Literal left = flow.value; flow = visit(curr->right); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; Literal right = flow.value; NOTE_EVAL2(left, right); - assert(isConcreteType(curr->left->type) ? left.type == curr->left->type : true); - assert(isConcreteType(curr->right->type) ? right.type == curr->right->type : true); + assert(isConcreteType(curr->left->type) ? left.type == curr->left->type + : true); + assert(isConcreteType(curr->right->type) ? right.type == curr->right->type + : true); switch (curr->op) { case AddInt32: case AddInt64: case AddFloat32: - case AddFloat64: return left.add(right); + case AddFloat64: + return left.add(right); case SubInt32: case SubInt64: case SubFloat32: - case SubFloat64: return left.sub(right); + case SubFloat64: + return left.sub(right); case MulInt32: case MulInt64: case MulFloat32: - case MulFloat64: return left.mul(right); + case MulFloat64: + return left.mul(right); case DivSInt32: { - if (right.getInteger() == 0) trap("i32.div_s by 0"); - if (left.getInteger() == std::numeric_limits<int32_t>::min() && right.getInteger() == -1) trap("i32.div_s overflow"); // signed division overflow + if (right.getInteger() == 0) + trap("i32.div_s by 0"); + if (left.getInteger() == std::numeric_limits<int32_t>::min() && + right.getInteger() == -1) + trap("i32.div_s overflow"); // signed division overflow return left.divS(right); } case DivUInt32: { - if (right.getInteger() == 0) trap("i32.div_u by 0"); + if (right.getInteger() == 0) + trap("i32.div_u by 0"); return left.divU(right); } case RemSInt32: { - if (right.getInteger() == 0) trap("i32.rem_s by 0"); - if (left.getInteger() == std::numeric_limits<int32_t>::min() && right.getInteger() == -1) return Literal(int32_t(0)); + if (right.getInteger() == 0) + trap("i32.rem_s by 0"); + if (left.getInteger() == std::numeric_limits<int32_t>::min() && + right.getInteger() == -1) + return Literal(int32_t(0)); return left.remS(right); } case RemUInt32: { - if (right.getInteger() == 0) trap("i32.rem_u by 0"); + if (right.getInteger() == 0) + trap("i32.rem_u by 0"); return left.remU(right); } case DivSInt64: { - if (right.getInteger() == 0) trap("i64.div_s by 0"); - if (left.getInteger() == LLONG_MIN && right.getInteger() == -1LL) trap("i64.div_s overflow"); // signed division overflow + if (right.getInteger() == 0) + trap("i64.div_s by 0"); + if (left.getInteger() == LLONG_MIN && right.getInteger() == -1LL) + trap("i64.div_s overflow"); // signed division overflow return left.divS(right); } case DivUInt64: { - if (right.getInteger() == 0) trap("i64.div_u by 0"); + if (right.getInteger() == 0) + trap("i64.div_u by 0"); return left.divU(right); } case RemSInt64: { - if (right.getInteger() == 0) trap("i64.rem_s by 0"); - if (left.getInteger() == LLONG_MIN && right.getInteger() == -1LL) return Literal(int64_t(0)); + if (right.getInteger() == 0) + trap("i64.rem_s by 0"); + if (left.getInteger() == LLONG_MIN && right.getInteger() == -1LL) + return Literal(int64_t(0)); return left.remS(right); } case RemUInt64: { - if (right.getInteger() == 0) trap("i64.rem_u by 0"); + if (right.getInteger() == 0) + trap("i64.rem_u by 0"); return left.remU(right); } case DivFloat32: - case DivFloat64: return left.div(right); + case DivFloat64: + return left.div(right); case AndInt32: - case AndInt64: return left.and_(right); + case AndInt64: + return left.and_(right); case OrInt32: - case OrInt64: return left.or_(right); + case OrInt64: + return left.or_(right); case XorInt32: - case XorInt64: return left.xor_(right); + case XorInt64: + return left.xor_(right); case ShlInt32: - case ShlInt64: return left.shl(right); + case ShlInt64: + return left.shl(right); case ShrUInt32: - case ShrUInt64: return left.shrU(right); + case ShrUInt64: + return left.shrU(right); case ShrSInt32: - case ShrSInt64: return left.shrS(right); + case ShrSInt64: + return left.shrS(right); case RotLInt32: - case RotLInt64: return left.rotL(right); + case RotLInt64: + return left.rotL(right); case RotRInt32: - case RotRInt64: return left.rotR(right); + case RotRInt64: + return left.rotR(right); case EqInt32: case EqInt64: case EqFloat32: - case EqFloat64: return left.eq(right); + case EqFloat64: + return left.eq(right); case NeInt32: case NeInt64: case NeFloat32: - case NeFloat64: return left.ne(right); + case NeFloat64: + return left.ne(right); case LtSInt32: - case LtSInt64: return left.ltS(right); + case LtSInt64: + return left.ltS(right); case LtUInt32: - case LtUInt64: return left.ltU(right); + case LtUInt64: + return left.ltU(right); case LeSInt32: - case LeSInt64: return left.leS(right); + case LeSInt64: + return left.leS(right); case LeUInt32: - case LeUInt64: return left.leU(right); + case LeUInt64: + return left.leU(right); case GtSInt32: - case GtSInt64: return left.gtS(right); + case GtSInt64: + return left.gtS(right); case GtUInt32: - case GtUInt64: return left.gtU(right); + case GtUInt64: + return left.gtU(right); case GeSInt32: - case GeSInt64: return left.geS(right); + case GeSInt64: + return left.geS(right); case GeUInt32: - case GeUInt64: return left.geU(right); + case GeUInt64: + return left.geU(right); case LtFloat32: - case LtFloat64: return left.lt(right); + case LtFloat64: + return left.lt(right); case LeFloat32: - case LeFloat64: return left.le(right); + case LeFloat64: + return left.le(right); case GtFloat32: - case GtFloat64: return left.gt(right); + case GtFloat64: + return left.gt(right); case GeFloat32: - case GeFloat64: return left.ge(right); + case GeFloat64: + return left.ge(right); case CopySignFloat32: - case CopySignFloat64: return left.copysign(right); + case CopySignFloat64: + return left.copysign(right); case MinFloat32: - case MinFloat64: return left.min(right); + case MinFloat64: + return left.min(right); case MaxFloat32: - case MaxFloat64: return left.max(right); - - case EqVecI8x16: return left.eqI8x16(right); - case NeVecI8x16: return left.neI8x16(right); - case LtSVecI8x16: return left.ltSI8x16(right); - case LtUVecI8x16: return left.ltUI8x16(right); - case GtSVecI8x16: return left.gtSI8x16(right); - case GtUVecI8x16: return left.gtUI8x16(right); - case LeSVecI8x16: return left.leSI8x16(right); - case LeUVecI8x16: return left.leUI8x16(right); - case GeSVecI8x16: return left.geSI8x16(right); - case GeUVecI8x16: return left.geUI8x16(right); - case EqVecI16x8: return left.eqI16x8(right); - case NeVecI16x8: return left.neI16x8(right); - case LtSVecI16x8: return left.ltSI16x8(right); - case LtUVecI16x8: return left.ltUI16x8(right); - case GtSVecI16x8: return left.gtSI16x8(right); - case GtUVecI16x8: return left.gtUI16x8(right); - case LeSVecI16x8: return left.leSI16x8(right); - case LeUVecI16x8: return left.leUI16x8(right); - case GeSVecI16x8: return left.geSI16x8(right); - case GeUVecI16x8: return left.geUI16x8(right); - case EqVecI32x4: return left.eqI32x4(right); - case NeVecI32x4: return left.neI32x4(right); - case LtSVecI32x4: return left.ltSI32x4(right); - case LtUVecI32x4: return left.ltUI32x4(right); - case GtSVecI32x4: return left.gtSI32x4(right); - case GtUVecI32x4: return left.gtUI32x4(right); - case LeSVecI32x4: return left.leSI32x4(right); - case LeUVecI32x4: return left.leUI32x4(right); - case GeSVecI32x4: return left.geSI32x4(right); - case GeUVecI32x4: return left.geUI32x4(right); - case EqVecF32x4: return left.eqF32x4(right); - case NeVecF32x4: return left.neF32x4(right); - case LtVecF32x4: return left.ltF32x4(right); - case GtVecF32x4: return left.gtF32x4(right); - case LeVecF32x4: return left.leF32x4(right); - case GeVecF32x4: return left.geF32x4(right); - case EqVecF64x2: return left.eqF64x2(right); - case NeVecF64x2: return left.neF64x2(right); - case LtVecF64x2: return left.ltF64x2(right); - case GtVecF64x2: return left.gtF64x2(right); - case LeVecF64x2: return left.leF64x2(right); - case GeVecF64x2: return left.geF64x2(right); - - case AndVec128: return left.andV128(right); - case OrVec128: return left.orV128(right); - case XorVec128: return left.xorV128(right); - - case AddVecI8x16: return left.addI8x16(right); - case AddSatSVecI8x16: return left.addSaturateSI8x16(right); - case AddSatUVecI8x16: return left.addSaturateUI8x16(right); - case SubVecI8x16: return left.subI8x16(right); - case SubSatSVecI8x16: return left.subSaturateSI8x16(right); - case SubSatUVecI8x16: return left.subSaturateUI8x16(right); - case MulVecI8x16: return left.mulI8x16(right); - case AddVecI16x8: return left.addI16x8(right); - case AddSatSVecI16x8: return left.addSaturateSI16x8(right); - case AddSatUVecI16x8: return left.addSaturateUI16x8(right); - case SubVecI16x8: return left.subI16x8(right); - case SubSatSVecI16x8: return left.subSaturateSI16x8(right); - case SubSatUVecI16x8: return left.subSaturateUI16x8(right); - case MulVecI16x8: return left.mulI16x8(right); - case AddVecI32x4: return left.addI32x4(right); - case SubVecI32x4: return left.subI32x4(right); - case MulVecI32x4: return left.mulI32x4(right); - case AddVecI64x2: return left.addI64x2(right); - case SubVecI64x2: return left.subI64x2(right); - - case AddVecF32x4: return left.addF32x4(right); - case SubVecF32x4: return left.subF32x4(right); - case MulVecF32x4: return left.mulF32x4(right); - case DivVecF32x4: return left.divF32x4(right); - case MinVecF32x4: return left.minF32x4(right); - case MaxVecF32x4: return left.maxF32x4(right); - case AddVecF64x2: return left.addF64x2(right); - case SubVecF64x2: return left.subF64x2(right); - case MulVecF64x2: return left.mulF64x2(right); - case DivVecF64x2: return left.divF64x2(right); - case MinVecF64x2: return left.minF64x2(right); - case MaxVecF64x2: return left.maxF64x2(right); - - case InvalidBinary: WASM_UNREACHABLE(); + case MaxFloat64: + return left.max(right); + + case EqVecI8x16: + return left.eqI8x16(right); + case NeVecI8x16: + return left.neI8x16(right); + case LtSVecI8x16: + return left.ltSI8x16(right); + case LtUVecI8x16: + return left.ltUI8x16(right); + case GtSVecI8x16: + return left.gtSI8x16(right); + case GtUVecI8x16: + return left.gtUI8x16(right); + case LeSVecI8x16: + return left.leSI8x16(right); + case LeUVecI8x16: + return left.leUI8x16(right); + case GeSVecI8x16: + return left.geSI8x16(right); + case GeUVecI8x16: + return left.geUI8x16(right); + case EqVecI16x8: + return left.eqI16x8(right); + case NeVecI16x8: + return left.neI16x8(right); + case LtSVecI16x8: + return left.ltSI16x8(right); + case LtUVecI16x8: + return left.ltUI16x8(right); + case GtSVecI16x8: + return left.gtSI16x8(right); + case GtUVecI16x8: + return left.gtUI16x8(right); + case LeSVecI16x8: + return left.leSI16x8(right); + case LeUVecI16x8: + return left.leUI16x8(right); + case GeSVecI16x8: + return left.geSI16x8(right); + case GeUVecI16x8: + return left.geUI16x8(right); + case EqVecI32x4: + return left.eqI32x4(right); + case NeVecI32x4: + return left.neI32x4(right); + case LtSVecI32x4: + return left.ltSI32x4(right); + case LtUVecI32x4: + return left.ltUI32x4(right); + case GtSVecI32x4: + return left.gtSI32x4(right); + case GtUVecI32x4: + return left.gtUI32x4(right); + case LeSVecI32x4: + return left.leSI32x4(right); + case LeUVecI32x4: + return left.leUI32x4(right); + case GeSVecI32x4: + return left.geSI32x4(right); + case GeUVecI32x4: + return left.geUI32x4(right); + case EqVecF32x4: + return left.eqF32x4(right); + case NeVecF32x4: + return left.neF32x4(right); + case LtVecF32x4: + return left.ltF32x4(right); + case GtVecF32x4: + return left.gtF32x4(right); + case LeVecF32x4: + return left.leF32x4(right); + case GeVecF32x4: + return left.geF32x4(right); + case EqVecF64x2: + return left.eqF64x2(right); + case NeVecF64x2: + return left.neF64x2(right); + case LtVecF64x2: + return left.ltF64x2(right); + case GtVecF64x2: + return left.gtF64x2(right); + case LeVecF64x2: + return left.leF64x2(right); + case GeVecF64x2: + return left.geF64x2(right); + + case AndVec128: + return left.andV128(right); + case OrVec128: + return left.orV128(right); + case XorVec128: + return left.xorV128(right); + + case AddVecI8x16: + return left.addI8x16(right); + case AddSatSVecI8x16: + return left.addSaturateSI8x16(right); + case AddSatUVecI8x16: + return left.addSaturateUI8x16(right); + case SubVecI8x16: + return left.subI8x16(right); + case SubSatSVecI8x16: + return left.subSaturateSI8x16(right); + case SubSatUVecI8x16: + return left.subSaturateUI8x16(right); + case MulVecI8x16: + return left.mulI8x16(right); + case AddVecI16x8: + return left.addI16x8(right); + case AddSatSVecI16x8: + return left.addSaturateSI16x8(right); + case AddSatUVecI16x8: + return left.addSaturateUI16x8(right); + case SubVecI16x8: + return left.subI16x8(right); + case SubSatSVecI16x8: + return left.subSaturateSI16x8(right); + case SubSatUVecI16x8: + return left.subSaturateUI16x8(right); + case MulVecI16x8: + return left.mulI16x8(right); + case AddVecI32x4: + return left.addI32x4(right); + case SubVecI32x4: + return left.subI32x4(right); + case MulVecI32x4: + return left.mulI32x4(right); + case AddVecI64x2: + return left.addI64x2(right); + case SubVecI64x2: + return left.subI64x2(right); + + case AddVecF32x4: + return left.addF32x4(right); + case SubVecF32x4: + return left.subF32x4(right); + case MulVecF32x4: + return left.mulF32x4(right); + case DivVecF32x4: + return left.divF32x4(right); + case MinVecF32x4: + return left.minF32x4(right); + case MaxVecF32x4: + return left.maxF32x4(right); + case AddVecF64x2: + return left.addF64x2(right); + case SubVecF64x2: + return left.subF64x2(right); + case MulVecF64x2: + return left.mulF64x2(right); + case DivVecF64x2: + return left.divF64x2(right); + case MinVecF64x2: + return left.minF64x2(right); + case MaxVecF64x2: + return left.maxF64x2(right); + + case InvalidBinary: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } - Flow visitSIMDExtract(SIMDExtract *curr) { + Flow visitSIMDExtract(SIMDExtract* curr) { NOTE_ENTER("SIMDExtract"); Flow flow = this->visit(curr->vec); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; Literal vec = flow.value; switch (curr->op) { - case ExtractLaneSVecI8x16: return vec.extractLaneSI8x16(curr->index); - case ExtractLaneUVecI8x16: return vec.extractLaneUI8x16(curr->index); - case ExtractLaneSVecI16x8: return vec.extractLaneSI16x8(curr->index); - case ExtractLaneUVecI16x8: return vec.extractLaneUI16x8(curr->index); - case ExtractLaneVecI32x4: return vec.extractLaneI32x4(curr->index); - case ExtractLaneVecI64x2: return vec.extractLaneI64x2(curr->index); - case ExtractLaneVecF32x4: return vec.extractLaneF32x4(curr->index); - case ExtractLaneVecF64x2: return vec.extractLaneF64x2(curr->index); + case ExtractLaneSVecI8x16: + return vec.extractLaneSI8x16(curr->index); + case ExtractLaneUVecI8x16: + return vec.extractLaneUI8x16(curr->index); + case ExtractLaneSVecI16x8: + return vec.extractLaneSI16x8(curr->index); + case ExtractLaneUVecI16x8: + return vec.extractLaneUI16x8(curr->index); + case ExtractLaneVecI32x4: + return vec.extractLaneI32x4(curr->index); + case ExtractLaneVecI64x2: + return vec.extractLaneI64x2(curr->index); + case ExtractLaneVecF32x4: + return vec.extractLaneF32x4(curr->index); + case ExtractLaneVecF64x2: + return vec.extractLaneF64x2(curr->index); } WASM_UNREACHABLE(); } - Flow visitSIMDReplace(SIMDReplace *curr) { + Flow visitSIMDReplace(SIMDReplace* curr) { NOTE_ENTER("SIMDReplace"); Flow flow = this->visit(curr->vec); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; Literal vec = flow.value; flow = this->visit(curr->value); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; Literal value = flow.value; switch (curr->op) { - case ReplaceLaneVecI8x16: return vec.replaceLaneI8x16(value, curr->index); - case ReplaceLaneVecI16x8: return vec.replaceLaneI16x8(value, curr->index); - case ReplaceLaneVecI32x4: return vec.replaceLaneI32x4(value, curr->index); - case ReplaceLaneVecI64x2: return vec.replaceLaneI64x2(value, curr->index); - case ReplaceLaneVecF32x4: return vec.replaceLaneF32x4(value, curr->index); - case ReplaceLaneVecF64x2: return vec.replaceLaneF64x2(value, curr->index); + case ReplaceLaneVecI8x16: + return vec.replaceLaneI8x16(value, curr->index); + case ReplaceLaneVecI16x8: + return vec.replaceLaneI16x8(value, curr->index); + case ReplaceLaneVecI32x4: + return vec.replaceLaneI32x4(value, curr->index); + case ReplaceLaneVecI64x2: + return vec.replaceLaneI64x2(value, curr->index); + case ReplaceLaneVecF32x4: + return vec.replaceLaneF32x4(value, curr->index); + case ReplaceLaneVecF64x2: + return vec.replaceLaneF64x2(value, curr->index); } WASM_UNREACHABLE(); } - Flow visitSIMDShuffle(SIMDShuffle *curr) { + Flow visitSIMDShuffle(SIMDShuffle* curr) { NOTE_ENTER("SIMDShuffle"); Flow flow = this->visit(curr->left); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; Literal left = flow.value; flow = this->visit(curr->right); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; Literal right = flow.value; return left.shuffleV8x16(right, curr->mask); } - Flow visitSIMDBitselect(SIMDBitselect *curr) { + Flow visitSIMDBitselect(SIMDBitselect* curr) { NOTE_ENTER("SIMDBitselect"); Flow flow = this->visit(curr->left); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; Literal left = flow.value; flow = this->visit(curr->right); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; Literal right = flow.value; flow = this->visit(curr->cond); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; Literal cond = flow.value; return cond.bitselectV128(left, right); } - Flow visitSIMDShift(SIMDShift *curr) { + Flow visitSIMDShift(SIMDShift* curr) { NOTE_ENTER("SIMDShift"); Flow flow = this->visit(curr->vec); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; Literal vec = flow.value; flow = this->visit(curr->shift); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; Literal shift = flow.value; switch (curr->op) { - case ShlVecI8x16: return vec.shlI8x16(shift); - case ShrSVecI8x16: return vec.shrSI8x16(shift); - case ShrUVecI8x16: return vec.shrUI8x16(shift); - case ShlVecI16x8: return vec.shlI16x8(shift); - case ShrSVecI16x8: return vec.shrSI16x8(shift); - case ShrUVecI16x8: return vec.shrUI16x8(shift); - case ShlVecI32x4: return vec.shlI32x4(shift); - case ShrSVecI32x4: return vec.shrSI32x4(shift); - case ShrUVecI32x4: return vec.shrUI32x4(shift); - case ShlVecI64x2: return vec.shlI64x2(shift); - case ShrSVecI64x2: return vec.shrSI64x2(shift); - case ShrUVecI64x2: return vec.shrUI64x2(shift); + case ShlVecI8x16: + return vec.shlI8x16(shift); + case ShrSVecI8x16: + return vec.shrSI8x16(shift); + case ShrUVecI8x16: + return vec.shrUI8x16(shift); + case ShlVecI16x8: + return vec.shlI16x8(shift); + case ShrSVecI16x8: + return vec.shrSI16x8(shift); + case ShrUVecI16x8: + return vec.shrUI16x8(shift); + case ShlVecI32x4: + return vec.shlI32x4(shift); + case ShrSVecI32x4: + return vec.shrSI32x4(shift); + case ShrUVecI32x4: + return vec.shrUI32x4(shift); + case ShlVecI64x2: + return vec.shlI64x2(shift); + case ShrSVecI64x2: + return vec.shrSI64x2(shift); + case ShrUVecI64x2: + return vec.shrUI64x2(shift); } WASM_UNREACHABLE(); } - Flow visitSelect(Select *curr) { + Flow visitSelect(Select* curr) { NOTE_ENTER("Select"); Flow ifTrue = visit(curr->ifTrue); - if (ifTrue.breaking()) return ifTrue; + if (ifTrue.breaking()) + return ifTrue; Flow ifFalse = visit(curr->ifFalse); - if (ifFalse.breaking()) return ifFalse; + if (ifFalse.breaking()) + return ifFalse; Flow condition = visit(curr->condition); - if (condition.breaking()) return condition; + if (condition.breaking()) + return condition; NOTE_EVAL1(condition.value); return condition.value.geti32() ? ifTrue : ifFalse; // ;-) } - Flow visitDrop(Drop *curr) { + Flow visitDrop(Drop* curr) { NOTE_ENTER("Drop"); Flow value = visit(curr->value); - if (value.breaking()) return value; + if (value.breaking()) + return value; return Flow(); } - Flow visitReturn(Return *curr) { + Flow visitReturn(Return* curr) { NOTE_ENTER("Return"); Flow flow; if (curr->value) { flow = visit(curr->value); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; NOTE_EVAL1(flow.value); } flow.breakTo = RETURN_FLOW; return flow; } - Flow visitNop(Nop *curr) { + Flow visitNop(Nop* curr) { NOTE_ENTER("Nop"); return Flow(); } - Flow visitUnreachable(Unreachable *curr) { + Flow visitUnreachable(Unreachable* curr) { NOTE_ENTER("Unreachable"); trap("unreachable"); WASM_UNREACHABLE(); @@ -665,19 +923,24 @@ public: Literal truncSFloat(Unary* curr, Literal value) { double val = value.getFloat(); - if (std::isnan(val)) trap("truncSFloat of nan"); + if (std::isnan(val)) + trap("truncSFloat of nan"); if (curr->type == i32) { if (value.type == f32) { - if (!isInRangeI32TruncS(value.reinterpreti32())) trap("i32.truncSFloat overflow"); + if (!isInRangeI32TruncS(value.reinterpreti32())) + trap("i32.truncSFloat overflow"); } else { - if (!isInRangeI32TruncS(value.reinterpreti64())) trap("i32.truncSFloat overflow"); + if (!isInRangeI32TruncS(value.reinterpreti64())) + trap("i32.truncSFloat overflow"); } return Literal(int32_t(val)); } else { if (value.type == f32) { - if (!isInRangeI64TruncS(value.reinterpreti32())) trap("i64.truncSFloat overflow"); + if (!isInRangeI64TruncS(value.reinterpreti32())) + trap("i64.truncSFloat overflow"); } else { - if (!isInRangeI64TruncS(value.reinterpreti64())) trap("i64.truncSFloat overflow"); + if (!isInRangeI64TruncS(value.reinterpreti64())) + trap("i64.truncSFloat overflow"); } return Literal(int64_t(val)); } @@ -685,19 +948,24 @@ public: Literal truncUFloat(Unary* curr, Literal value) { double val = value.getFloat(); - if (std::isnan(val)) trap("truncUFloat of nan"); + if (std::isnan(val)) + trap("truncUFloat of nan"); if (curr->type == i32) { if (value.type == f32) { - if (!isInRangeI32TruncU(value.reinterpreti32())) trap("i32.truncUFloat overflow"); + if (!isInRangeI32TruncU(value.reinterpreti32())) + trap("i32.truncUFloat overflow"); } else { - if (!isInRangeI32TruncU(value.reinterpreti64())) trap("i32.truncUFloat overflow"); + if (!isInRangeI32TruncU(value.reinterpreti64())) + trap("i32.truncUFloat overflow"); } return Literal(uint32_t(val)); } else { if (value.type == f32) { - if (!isInRangeI64TruncU(value.reinterpreti32())) trap("i64.truncUFloat overflow"); + if (!isInRangeI64TruncU(value.reinterpreti32())) + trap("i64.truncUFloat overflow"); } else { - if (!isInRangeI64TruncU(value.reinterpreti64())) trap("i64.truncUFloat overflow"); + if (!isInRangeI64TruncU(value.reinterpreti64())) + trap("i64.truncUFloat overflow"); } return Literal(uint64_t(val)); } @@ -708,37 +976,36 @@ public: Flow visitGetLocal(GetLocal*) { WASM_UNREACHABLE(); } Flow visitSetLocal(SetLocal*) { WASM_UNREACHABLE(); } Flow visitSetGlobal(SetGlobal*) { WASM_UNREACHABLE(); } - Flow visitLoad(Load *curr) { WASM_UNREACHABLE(); } - Flow visitStore(Store *curr) { WASM_UNREACHABLE(); } - Flow visitHost(Host *curr) { WASM_UNREACHABLE(); } - Flow visitMemoryInit(MemoryInit *curr) { WASM_UNREACHABLE(); } - Flow visitDataDrop(DataDrop *curr) { WASM_UNREACHABLE(); } - Flow visitMemoryCopy(MemoryCopy *curr) { WASM_UNREACHABLE(); } - Flow visitMemoryFill(MemoryFill *curr) { WASM_UNREACHABLE(); } + Flow visitLoad(Load* curr) { WASM_UNREACHABLE(); } + Flow visitStore(Store* curr) { WASM_UNREACHABLE(); } + Flow visitHost(Host* curr) { WASM_UNREACHABLE(); } + Flow visitMemoryInit(MemoryInit* curr) { WASM_UNREACHABLE(); } + Flow visitDataDrop(DataDrop* curr) { WASM_UNREACHABLE(); } + Flow visitMemoryCopy(MemoryCopy* curr) { WASM_UNREACHABLE(); } + Flow visitMemoryFill(MemoryFill* curr) { WASM_UNREACHABLE(); } Flow visitAtomicRMW(AtomicRMW*) { WASM_UNREACHABLE(); } Flow visitAtomicCmpxchg(AtomicCmpxchg*) { WASM_UNREACHABLE(); } Flow visitAtomicWait(AtomicWait*) { WASM_UNREACHABLE(); } Flow visitAtomicNotify(AtomicNotify*) { WASM_UNREACHABLE(); } - virtual void trap(const char* why) { - WASM_UNREACHABLE(); - } + virtual void trap(const char* why) { WASM_UNREACHABLE(); } }; // Execute an constant expression in a global init or memory offset. template<typename GlobalManager> -class ConstantExpressionRunner : public ExpressionRunner<ConstantExpressionRunner<GlobalManager>> { +class ConstantExpressionRunner + : public ExpressionRunner<ConstantExpressionRunner<GlobalManager>> { GlobalManager& globals; + public: ConstantExpressionRunner(GlobalManager& globals) : globals(globals) {} - Flow visitGetGlobal(GetGlobal *curr) { - return Flow(globals[curr->name]); - } + Flow visitGetGlobal(GetGlobal* curr) { return Flow(globals[curr->name]); } }; // -// An instance of a WebAssembly module, which can execute it via AST interpretation. +// An instance of a WebAssembly module, which can execute it via AST +// interpretation. // // To embed this interpreter, you need to provide an ExternalInterface instance // (see below) which provides the embedding-specific details, that is, how to @@ -747,8 +1014,7 @@ public: // To call into the interpreter, use callExport. // -template<typename GlobalManager, typename SubType> -class ModuleInstanceBase { +template<typename GlobalManager, typename SubType> class ModuleInstanceBase { public: // // You need to implement one of these to create a concrete interpreter. The @@ -759,7 +1025,10 @@ public: virtual void init(Module& wasm, SubType& instance) {} virtual void importGlobals(GlobalManager& globals, Module& wasm) = 0; virtual Literal callImport(Function* import, LiteralList& arguments) = 0; - virtual Literal callTable(Index index, LiteralList& arguments, Type result, SubType& instance) = 0; + virtual Literal callTable(Index index, + LiteralList& arguments, + Type result, + SubType& instance) = 0; virtual void growMemory(Address oldSize, Address newSize) = 0; virtual void trap(const char* why) = 0; @@ -769,28 +1038,46 @@ public: switch (load->type) { case i32: { switch (load->bytes) { - case 1: return load->signed_ ? Literal((int32_t)load8s(addr)) : Literal((int32_t)load8u(addr)); - case 2: return load->signed_ ? Literal((int32_t)load16s(addr)) : Literal((int32_t)load16u(addr)); - case 4: return Literal((int32_t)load32s(addr)); - default: WASM_UNREACHABLE(); + case 1: + return load->signed_ ? Literal((int32_t)load8s(addr)) + : Literal((int32_t)load8u(addr)); + case 2: + return load->signed_ ? Literal((int32_t)load16s(addr)) + : Literal((int32_t)load16u(addr)); + case 4: + return Literal((int32_t)load32s(addr)); + default: + WASM_UNREACHABLE(); } break; } case i64: { switch (load->bytes) { - case 1: return load->signed_ ? Literal((int64_t)load8s(addr)) : Literal((int64_t)load8u(addr)); - case 2: return load->signed_ ? Literal((int64_t)load16s(addr)) : Literal((int64_t)load16u(addr)); - case 4: return load->signed_ ? Literal((int64_t)load32s(addr)) : Literal((int64_t)load32u(addr)); - case 8: return Literal((int64_t)load64s(addr)); - default: WASM_UNREACHABLE(); + case 1: + return load->signed_ ? Literal((int64_t)load8s(addr)) + : Literal((int64_t)load8u(addr)); + case 2: + return load->signed_ ? Literal((int64_t)load16s(addr)) + : Literal((int64_t)load16u(addr)); + case 4: + return load->signed_ ? Literal((int64_t)load32s(addr)) + : Literal((int64_t)load32u(addr)); + case 8: + return Literal((int64_t)load64s(addr)); + default: + WASM_UNREACHABLE(); } break; } - case f32: return Literal(load32u(addr)).castToF32(); - case f64: return Literal(load64u(addr)).castToF64(); - case v128: return Literal(load128(addr).data()); + case f32: + return Literal(load32u(addr)).castToF32(); + case f64: + return Literal(load64u(addr)).castToF64(); + case v128: + return Literal(load128(addr).data()); case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } @@ -798,29 +1085,52 @@ public: switch (store->valueType) { case i32: { switch (store->bytes) { - case 1: store8(addr, value.geti32()); break; - case 2: store16(addr, value.geti32()); break; - case 4: store32(addr, value.geti32()); break; - default: WASM_UNREACHABLE(); + case 1: + store8(addr, value.geti32()); + break; + case 2: + store16(addr, value.geti32()); + break; + case 4: + store32(addr, value.geti32()); + break; + default: + WASM_UNREACHABLE(); } break; } case i64: { switch (store->bytes) { - case 1: store8(addr, value.geti64()); break; - case 2: store16(addr, value.geti64()); break; - case 4: store32(addr, value.geti64()); break; - case 8: store64(addr, value.geti64()); break; - default: WASM_UNREACHABLE(); + case 1: + store8(addr, value.geti64()); + break; + case 2: + store16(addr, value.geti64()); + break; + case 4: + store32(addr, value.geti64()); + break; + case 8: + store64(addr, value.geti64()); + break; + default: + WASM_UNREACHABLE(); } break; } // write floats carefully, ensuring all bits reach memory - case f32: store32(addr, value.reinterpreti32()); break; - case f64: store64(addr, value.reinterpreti64()); break; - case v128: store128(addr, value.getv128()); break; + case f32: + store32(addr, value.reinterpreti32()); + break; + case f64: + store64(addr, value.reinterpreti64()); + break; + case v128: + store128(addr, value.getv128()); + break; case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } } @@ -832,34 +1142,39 @@ public: virtual uint32_t load32u(Address addr) { WASM_UNREACHABLE(); } virtual int64_t load64s(Address addr) { WASM_UNREACHABLE(); } virtual uint64_t load64u(Address addr) { WASM_UNREACHABLE(); } - virtual std::array<uint8_t, 16> load128(Address addr) { WASM_UNREACHABLE(); } + virtual std::array<uint8_t, 16> load128(Address addr) { + WASM_UNREACHABLE(); + } virtual void store8(Address addr, int8_t value) { WASM_UNREACHABLE(); } virtual void store16(Address addr, int16_t value) { WASM_UNREACHABLE(); } virtual void store32(Address addr, int32_t value) { WASM_UNREACHABLE(); } virtual void store64(Address addr, int64_t value) { WASM_UNREACHABLE(); } - virtual void store128(Address addr, const std::array<uint8_t, 16>&) { WASM_UNREACHABLE(); } + virtual void store128(Address addr, const std::array<uint8_t, 16>&) { + WASM_UNREACHABLE(); + } virtual void tableStore(Address addr, Name entry) { WASM_UNREACHABLE(); } }; - SubType* self() { - return static_cast<SubType*>(this); - } + SubType* self() { return static_cast<SubType*>(this); } Module& wasm; // Values of globals GlobalManager globals; - ModuleInstanceBase(Module& wasm, ExternalInterface* externalInterface) : wasm(wasm), externalInterface(externalInterface) { + ModuleInstanceBase(Module& wasm, ExternalInterface* externalInterface) + : wasm(wasm), externalInterface(externalInterface) { // import globals from the outside externalInterface->importGlobals(globals, wasm); // prepare memory memorySize = wasm.memory.initial; // generate internal (non-imported) globals ModuleUtils::iterDefinedGlobals(wasm, [&](Global* global) { - globals[global->name] = ConstantExpressionRunner<GlobalManager>(globals).visit(global->init).value; + globals[global->name] = ConstantExpressionRunner<GlobalManager>(globals) + .visit(global->init) + .value; }); // initialize the rest of the external interface @@ -877,22 +1192,23 @@ public: // call an exported function Literal callExport(Name name, const LiteralList& arguments) { - Export *export_ = wasm.getExportOrNull(name); - if (!export_) externalInterface->trap("callExport not found"); + Export* export_ = wasm.getExportOrNull(name); + if (!export_) + externalInterface->trap("callExport not found"); return callFunction(export_->value, arguments); } - Literal callExport(Name name) { - return callExport(name, LiteralList()); - } + Literal callExport(Name name) { return callExport(name, LiteralList()); } // get an exported global Literal getExport(Name name) { - Export *export_ = wasm.getExportOrNull(name); - if (!export_) externalInterface->trap("getExport external not found"); + Export* export_ = wasm.getExportOrNull(name); + if (!export_) + externalInterface->trap("getExport external not found"); Name internalName = export_->value; auto iter = globals.find(internalName); - if (iter == globals.end()) externalInterface->trap("getExport internal not found"); + if (iter == globals.end()) + externalInterface->trap("getExport internal not found"); return iter->second; } @@ -917,7 +1233,10 @@ private: void initializeTableContents() { for (auto& segment : wasm.table.segments) { - Address offset = (uint32_t)ConstantExpressionRunner<GlobalManager>(globals).visit(segment.offset).value.geti32(); + Address offset = + (uint32_t)ConstantExpressionRunner<GlobalManager>(globals) + .visit(segment.offset) + .value.geti32(); if (offset + segment.data.size() > wasm.table.initial) { externalInterface->trap("invalid offset when initializing table"); } @@ -965,12 +1284,12 @@ private: } class FunctionScope { - public: + public: std::vector<Literal> locals; Function* function; FunctionScope(Function* function, const LiteralList& arguments) - : function(function) { + : function(function) { if (function->params.size() != arguments.size()) { std::cerr << "Function `" << function->name << "` expects " << function->params.size() << " parameters, got " @@ -983,9 +1302,9 @@ private: assert(function->isParam(i)); if (function->params[i] != arguments[i].type) { std::cerr << "Function `" << function->name << "` expects type " - << printType(function->params[i]) - << " for parameter " << i << ", got " - << printType(arguments[i].type) << "." << std::endl; + << printType(function->params[i]) << " for parameter " + << i << ", got " << printType(arguments[i].type) << "." + << std::endl; WASM_UNREACHABLE(); } locals[i] = arguments[i]; @@ -997,32 +1316,38 @@ private: } }; - // Executes expressions with concrete runtime info, the function and module at runtime - class RuntimeExpressionRunner : public ExpressionRunner<RuntimeExpressionRunner> { + // Executes expressions with concrete runtime info, the function and module at + // runtime + class RuntimeExpressionRunner + : public ExpressionRunner<RuntimeExpressionRunner> { ModuleInstanceBase& instance; FunctionScope& scope; - public: - RuntimeExpressionRunner(ModuleInstanceBase& instance, FunctionScope& scope) : instance(instance), scope(scope) {} + public: + RuntimeExpressionRunner(ModuleInstanceBase& instance, FunctionScope& scope) + : instance(instance), scope(scope) {} - Flow generateArguments(const ExpressionList& operands, LiteralList& arguments) { + Flow generateArguments(const ExpressionList& operands, + LiteralList& arguments) { NOTE_ENTER_("generateArguments"); arguments.reserve(operands.size()); for (auto expression : operands) { Flow flow = this->visit(expression); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; NOTE_EVAL1(flow.value); arguments.push_back(flow.value); } return Flow(); } - Flow visitCall(Call *curr) { + Flow visitCall(Call* curr) { NOTE_ENTER("Call"); NOTE_NAME(curr->target); LiteralList arguments; Flow flow = generateArguments(curr->operands, arguments); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; auto* func = instance.wasm.getFunction(curr->target); Flow ret; if (func->imported()) { @@ -1035,29 +1360,33 @@ private: #endif return ret; } - Flow visitCallIndirect(CallIndirect *curr) { + Flow visitCallIndirect(CallIndirect* curr) { NOTE_ENTER("CallIndirect"); LiteralList arguments; Flow flow = generateArguments(curr->operands, arguments); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; Flow target = this->visit(curr->target); - if (target.breaking()) return target; + if (target.breaking()) + return target; Index index = target.value.geti32(); - return instance.externalInterface->callTable(index, arguments, curr->type, *instance.self()); + return instance.externalInterface->callTable( + index, arguments, curr->type, *instance.self()); } - Flow visitGetLocal(GetLocal *curr) { + Flow visitGetLocal(GetLocal* curr) { NOTE_ENTER("GetLocal"); auto index = curr->index; NOTE_EVAL1(index); NOTE_EVAL1(scope.locals[index]); return scope.locals[index]; } - Flow visitSetLocal(SetLocal *curr) { + Flow visitSetLocal(SetLocal* curr) { NOTE_ENTER("SetLocal"); auto index = curr->index; Flow flow = this->visit(curr->value); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; NOTE_EVAL1(index); NOTE_EVAL1(flow.value); assert(curr->isTee() ? flow.value.type == curr->type : true); @@ -1065,7 +1394,7 @@ private: return curr->isTee() ? flow : Flow(); } - Flow visitGetGlobal(GetGlobal *curr) { + Flow visitGetGlobal(GetGlobal* curr) { NOTE_ENTER("GetGlobal"); auto name = curr->name; NOTE_EVAL1(name); @@ -1073,21 +1402,23 @@ private: NOTE_EVAL1(instance.globals[name]); return instance.globals[name]; } - Flow visitSetGlobal(SetGlobal *curr) { + Flow visitSetGlobal(SetGlobal* curr) { NOTE_ENTER("SetGlobal"); auto name = curr->name; Flow flow = this->visit(curr->value); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; NOTE_EVAL1(name); NOTE_EVAL1(flow.value); instance.globals[name] = flow.value; return Flow(); } - Flow visitLoad(Load *curr) { + Flow visitLoad(Load* curr) { NOTE_ENTER("Load"); Flow flow = this->visit(curr->ptr); - if (flow.breaking()) return flow; + if (flow.breaking()) + return flow; NOTE_EVAL1(flow); auto addr = instance.getFinalAddress(curr, flow.value); auto ret = instance.externalInterface->load(curr, addr); @@ -1095,12 +1426,14 @@ private: NOTE_EVAL1(ret); return ret; } - Flow visitStore(Store *curr) { + Flow visitStore(Store* curr) { NOTE_ENTER("Store"); Flow ptr = this->visit(curr->ptr); - if (ptr.breaking()) return ptr; + if (ptr.breaking()) + return ptr; Flow value = this->visit(curr->value); - if (value.breaking()) return value; + if (value.breaking()) + return value; auto addr = instance.getFinalAddress(curr, ptr.value); NOTE_EVAL1(addr); NOTE_EVAL1(value); @@ -1108,12 +1441,14 @@ private: return Flow(); } - Flow visitAtomicRMW(AtomicRMW *curr) { + Flow visitAtomicRMW(AtomicRMW* curr) { NOTE_ENTER("AtomicRMW"); Flow ptr = this->visit(curr->ptr); - if (ptr.breaking()) return ptr; + if (ptr.breaking()) + return ptr; auto value = this->visit(curr->value); - if (value.breaking()) return value; + if (value.breaking()) + return value; NOTE_EVAL1(ptr); auto addr = instance.getFinalAddress(curr, ptr.value); NOTE_EVAL1(addr); @@ -1122,25 +1457,40 @@ private: NOTE_EVAL1(loaded); auto computed = value.value; switch (curr->op) { - case Add: computed = computed.add(value.value); break; - case Sub: computed = computed.sub(value.value); break; - case And: computed = computed.and_(value.value); break; - case Or: computed = computed.or_(value.value); break; - case Xor: computed = computed.xor_(value.value); break; - case Xchg: computed = value.value; break; + case Add: + computed = computed.add(value.value); + break; + case Sub: + computed = computed.sub(value.value); + break; + case And: + computed = computed.and_(value.value); + break; + case Or: + computed = computed.or_(value.value); + break; + case Xor: + computed = computed.xor_(value.value); + break; + case Xchg: + computed = value.value; + break; } instance.doAtomicStore(addr, curr->bytes, computed); return loaded; } - Flow visitAtomicCmpxchg(AtomicCmpxchg *curr) { + Flow visitAtomicCmpxchg(AtomicCmpxchg* curr) { NOTE_ENTER("AtomicCmpxchg"); Flow ptr = this->visit(curr->ptr); - if (ptr.breaking()) return ptr; + if (ptr.breaking()) + return ptr; NOTE_EVAL1(ptr); auto expected = this->visit(curr->expected); - if (expected.breaking()) return expected; + if (expected.breaking()) + return expected; auto replacement = this->visit(curr->replacement); - if (replacement.breaking()) return replacement; + if (replacement.breaking()) + return replacement; auto addr = instance.getFinalAddress(curr, ptr.value); NOTE_EVAL1(addr); NOTE_EVAL1(expected); @@ -1152,17 +1502,20 @@ private: } return loaded; } - Flow visitAtomicWait(AtomicWait *curr) { + Flow visitAtomicWait(AtomicWait* curr) { NOTE_ENTER("AtomicWait"); Flow ptr = this->visit(curr->ptr); - if (ptr.breaking()) return ptr; + if (ptr.breaking()) + return ptr; NOTE_EVAL1(ptr); auto expected = this->visit(curr->expected); NOTE_EVAL1(expected); - if (expected.breaking()) return expected; + if (expected.breaking()) + return expected; auto timeout = this->visit(curr->timeout); NOTE_EVAL1(timeout); - if (timeout.breaking()) return timeout; + if (timeout.breaking()) + return timeout; auto bytes = getTypeSize(curr->expectedType); auto addr = instance.getFinalAddress(ptr.value, bytes); auto loaded = instance.doAtomicLoad(addr, bytes, curr->expectedType); @@ -1174,46 +1527,58 @@ private: // for now, just assume we are woken up return Literal(int32_t(0)); // woken up } - Flow visitAtomicNotify(AtomicNotify *curr) { + Flow visitAtomicNotify(AtomicNotify* curr) { NOTE_ENTER("AtomicNotify"); Flow ptr = this->visit(curr->ptr); - if (ptr.breaking()) return ptr; + if (ptr.breaking()) + return ptr; NOTE_EVAL1(ptr); auto count = this->visit(curr->notifyCount); NOTE_EVAL1(count); - if (count.breaking()) return count; + if (count.breaking()) + return count; // TODO: add threads support! return Literal(int32_t(0)); // none woken up } - Flow visitHost(Host *curr) { + Flow visitHost(Host* curr) { NOTE_ENTER("Host"); switch (curr->op) { - case CurrentMemory: return Literal(int32_t(instance.memorySize)); - case GrowMemory: { - auto fail = Literal(int32_t(-1)); - Flow flow = this->visit(curr->operands[0]); - if (flow.breaking()) return flow; - int32_t ret = instance.memorySize; - uint32_t delta = flow.value.geti32(); - if (delta > uint32_t(-1) /Memory::kPageSize) return fail; - if (instance.memorySize >= uint32_t(-1) - delta) return fail; - uint32_t newSize = instance.memorySize + delta; - if (newSize > instance.wasm.memory.max) return fail; - instance.externalInterface->growMemory(instance.memorySize * Memory::kPageSize, newSize * Memory::kPageSize); - instance.memorySize = newSize; - return Literal(int32_t(ret)); - } + case CurrentMemory: + return Literal(int32_t(instance.memorySize)); + case GrowMemory: { + auto fail = Literal(int32_t(-1)); + Flow flow = this->visit(curr->operands[0]); + if (flow.breaking()) + return flow; + int32_t ret = instance.memorySize; + uint32_t delta = flow.value.geti32(); + if (delta > uint32_t(-1) / Memory::kPageSize) + return fail; + if (instance.memorySize >= uint32_t(-1) - delta) + return fail; + uint32_t newSize = instance.memorySize + delta; + if (newSize > instance.wasm.memory.max) + return fail; + instance.externalInterface->growMemory(instance.memorySize * + Memory::kPageSize, + newSize * Memory::kPageSize); + instance.memorySize = newSize; + return Literal(int32_t(ret)); + } } WASM_UNREACHABLE(); } - Flow visitMemoryInit(MemoryInit *curr) { + Flow visitMemoryInit(MemoryInit* curr) { NOTE_ENTER("MemoryInit"); Flow dest = this->visit(curr->dest); - if (dest.breaking()) return dest; + if (dest.breaking()) + return dest; Flow offset = this->visit(curr->offset); - if (offset.breaking()) return offset; + if (offset.breaking()) + return offset; Flow size = this->visit(curr->size); - if (size.breaking()) return size; + if (size.breaking()) + return size; NOTE_EVAL1(dest); NOTE_EVAL1(offset); NOTE_EVAL1(size); @@ -1239,14 +1604,12 @@ private: trap("out of bounds segment access in memory.init"); } Literal addr(uint32_t(destVal + i)); - instance.externalInterface->store8( - instance.getFinalAddress(addr, 1), - segment.data[offsetVal + i] - ); + instance.externalInterface->store8(instance.getFinalAddress(addr, 1), + segment.data[offsetVal + i]); } return {}; } - Flow visitDataDrop(DataDrop *curr) { + Flow visitDataDrop(DataDrop* curr) { NOTE_ENTER("DataDrop"); if (instance.droppedSegments.count(curr->segment)) { trap("data.drop of dropped segment"); @@ -1254,14 +1617,17 @@ private: instance.droppedSegments.insert(curr->segment); return {}; } - Flow visitMemoryCopy(MemoryCopy *curr) { + Flow visitMemoryCopy(MemoryCopy* curr) { NOTE_ENTER("MemoryCopy"); Flow dest = this->visit(curr->dest); - if (dest.breaking()) return dest; + if (dest.breaking()) + return dest; Flow source = this->visit(curr->source); - if (source.breaking()) return source; + if (source.breaking()) + return source; Flow size = this->visit(curr->size); - if (size.breaking()) return size; + if (size.breaking()) + return size; NOTE_EVAL1(dest); NOTE_EVAL1(source); NOTE_EVAL1(size); @@ -1293,14 +1659,17 @@ private: } return {}; } - Flow visitMemoryFill(MemoryFill *curr) { + Flow visitMemoryFill(MemoryFill* curr) { NOTE_ENTER("MemoryFill"); Flow dest = this->visit(curr->dest); - if (dest.breaking()) return dest; + if (dest.breaking()) + return dest; Flow value = this->visit(curr->value); - if (value.breaking()) return value; + if (value.breaking()) + return value; Flow size = this->visit(curr->size); - if (size.breaking()) return size; + if (size.breaking()) + return size; NOTE_EVAL1(dest); NOTE_EVAL1(value); NOTE_EVAL1(size); @@ -1325,40 +1694,45 @@ private: public: // Call a function, starting an invocation. Literal callFunction(Name name, const LiteralList& arguments) { - // if the last call ended in a jump up the stack, it might have left stuff for us to clean up here + // if the last call ended in a jump up the stack, it might have left stuff + // for us to clean up here callDepth = 0; functionStack.clear(); return callFunctionInternal(name, arguments); } - // Internal function call. Must be public so that callTable implementations can use it (refactor?) + // Internal function call. Must be public so that callTable implementations + // can use it (refactor?) Literal callFunctionInternal(Name name, const LiteralList& arguments) { - if (callDepth > maxCallDepth) externalInterface->trap("stack limit"); + if (callDepth > maxCallDepth) + externalInterface->trap("stack limit"); auto previousCallDepth = callDepth; callDepth++; auto previousFunctionStackSize = functionStack.size(); functionStack.push_back(name); - Function *function = wasm.getFunction(name); + Function* function = wasm.getFunction(name); assert(function); FunctionScope scope(function, arguments); #ifdef WASM_INTERPRETER_DEBUG - std::cout << "entering " << function->name - << "\n with arguments:\n"; + std::cout << "entering " << function->name << "\n with arguments:\n"; for (unsigned i = 0; i < arguments.size(); ++i) { std::cout << " $" << i << ": " << arguments[i] << '\n'; } #endif Flow flow = RuntimeExpressionRunner(*this, scope).visit(function->body); - assert(!flow.breaking() || flow.breakTo == RETURN_FLOW); // cannot still be breaking, it means we missed our stop + // cannot still be breaking, it means we missed our stop + assert(!flow.breaking() || flow.breakTo == RETURN_FLOW); Literal ret = flow.value; if (function->result != ret.type) { - std::cerr << "calling " << function->name << " resulted in " << ret << " but the function type is " << function->result << '\n'; + std::cerr << "calling " << function->name << " resulted in " << ret + << " but the function type is " << function->result << '\n'; WASM_UNREACHABLE(); } - callDepth = previousCallDepth; // may decrease more than one, if we jumped up the stack + // may decrease more than one, if we jumped up the stack + callDepth = previousCallDepth; // if we jumped up the stack, we also need to pop higher frames while (functionStack.size() > previousFunctionStackSize) { functionStack.pop_back(); @@ -1370,7 +1744,6 @@ public: } protected: - Address memorySize; // in pages void trapIfGt(uint64_t lhs, uint64_t rhs, const char* msg) { @@ -1381,8 +1754,7 @@ protected: } } - template<class LS> - Address getFinalAddress(LS* curr, Literal ptr) { + template<class LS> Address getFinalAddress(LS* curr, Literal ptr) { Address memorySizeBytes = memorySize * Memory::kPageSize; uint64_t addr = ptr.type == i32 ? ptr.geti32() : ptr.geti64(); trapIfGt(curr->offset, memorySizeBytes, "offset > memory"); @@ -1442,9 +1814,11 @@ protected: // The default ModuleInstance uses a trivial global manager using TrivialGlobalManager = std::map<Name, Literal>; -class ModuleInstance : public ModuleInstanceBase<TrivialGlobalManager, ModuleInstance> { +class ModuleInstance + : public ModuleInstanceBase<TrivialGlobalManager, ModuleInstance> { public: - ModuleInstance(Module& wasm, ExternalInterface* externalInterface) : ModuleInstanceBase(wasm, externalInterface) {} + ModuleInstance(Module& wasm, ExternalInterface* externalInterface) + : ModuleInstanceBase(wasm, externalInterface) {} }; } // namespace wasm diff --git a/src/wasm-io.h b/src/wasm-io.h index 6d8dcc6e5..9c772f115 100644 --- a/src/wasm-io.h +++ b/src/wasm-io.h @@ -21,9 +21,9 @@ #ifndef wasm_wasm_io_h #define wasm_wasm_io_h -#include "wasm.h" #include "parsing.h" #include "support/file.h" +#include "wasm.h" namespace wasm { @@ -40,12 +40,13 @@ public: // read text void readText(std::string filename, Module& wasm); // read binary - void readBinary(std::string filename, Module& wasm, - std::string sourceMapFilename=""); + void readBinary(std::string filename, + Module& wasm, + std::string sourceMapFilename = ""); // read text or binary, checking the contents for what it is. If `filename` is // empty, read from stdin. - void read(std::string filename, Module& wasm, - std::string sourceMapFilename=""); + void + read(std::string filename, Module& wasm, std::string sourceMapFilename = ""); // check whether a file is a wasm binary bool isBinaryFile(std::string filename); @@ -64,8 +65,12 @@ public: void setBinary(bool binary_) { binary = binary_; } void setDebugInfo(bool debugInfo_) { debugInfo = debugInfo_; } void setSymbolMap(std::string symbolMap_) { symbolMap = symbolMap_; } - void setSourceMapFilename(std::string sourceMapFilename_) { sourceMapFilename = sourceMapFilename_; } - void setSourceMapUrl(std::string sourceMapUrl_) { sourceMapUrl = sourceMapUrl_; } + void setSourceMapFilename(std::string sourceMapFilename_) { + sourceMapFilename = sourceMapFilename_; + } + void setSourceMapUrl(std::string sourceMapUrl_) { + sourceMapUrl = sourceMapUrl_; + } // write text void writeText(Module& wasm, Output& output); @@ -80,6 +85,6 @@ public: void write(Module& wasm, std::string filename); }; -} +} // namespace wasm #endif // wasm_wasm_io_h diff --git a/src/wasm-module-building.h b/src/wasm-module-building.h index d1f8f5504..6f9a58eeb 100644 --- a/src/wasm-module-building.h +++ b/src/wasm-module-building.h @@ -17,14 +17,20 @@ #ifndef wasm_wasm_module_building_h #define wasm_wasm_module_building_h -#include <wasm.h> #include <support/threads.h> +#include <wasm.h> namespace wasm { #ifdef BINARYEN_THREAD_DEBUG static std::mutex debug; -#define DEBUG_THREAD(x) { std::lock_guard<std::mutex> lock(debug); std::cerr << "[OptimizingIncrementalModuleBuilder Threading (thread: " << std::this_thread::get_id() << ")] " << x; std::cerr << '\n'; } +#define DEBUG_THREAD(x) \ + { \ + std::lock_guard<std::mutex> lock(debug); \ + std::cerr << "[OptimizingIncrementalModuleBuilder Threading (thread: " \ + << std::this_thread::get_id() << ")] " << x; \ + std::cerr << '\n'; \ + } #else #define DEBUG_THREAD(x) #endif @@ -81,13 +87,14 @@ class OptimizingIncrementalModuleBuilder { Module* wasm; uint32_t numFunctions; PassOptions passOptions; - std::function<void (PassRunner&)> addPrePasses; + std::function<void(PassRunner&)> addPrePasses; Function* endMarker; std::atomic<Function*>* list; uint32_t nextFunction; // only used on main thread uint32_t numWorkers; std::vector<std::unique_ptr<std::thread>> threads; - std::atomic<uint32_t> liveWorkers, activeWorkers, availableFuncs, finishedFuncs; + std::atomic<uint32_t> liveWorkers, activeWorkers, availableFuncs, + finishedFuncs; std::mutex mutex; std::condition_variable condition; bool finishing; @@ -95,16 +102,20 @@ class OptimizingIncrementalModuleBuilder { bool validateGlobally; public: - // numFunctions must be equal to the number of functions allocated, or higher. Knowing - // this bounds helps avoid locking. - OptimizingIncrementalModuleBuilder(Module* wasm, Index numFunctions, PassOptions passOptions, - std::function<void (PassRunner&)> addPrePasses, - bool debug, bool validateGlobally) - : wasm(wasm), numFunctions(numFunctions), passOptions(passOptions), - addPrePasses(addPrePasses), - endMarker(nullptr), list(nullptr), nextFunction(0), - numWorkers(0), liveWorkers(0), activeWorkers(0), availableFuncs(0), finishedFuncs(0), - finishing(false), debug(debug), validateGlobally(validateGlobally) { + // numFunctions must be equal to the number of functions allocated, or higher. + // Knowing this bounds helps avoid locking. + OptimizingIncrementalModuleBuilder( + Module* wasm, + Index numFunctions, + PassOptions passOptions, + std::function<void(PassRunner&)> addPrePasses, + bool debug, + bool validateGlobally) + : wasm(wasm), numFunctions(numFunctions), passOptions(passOptions), + addPrePasses(addPrePasses), endMarker(nullptr), list(nullptr), + nextFunction(0), numWorkers(0), liveWorkers(0), activeWorkers(0), + availableFuncs(0), finishedFuncs(0), finishing(false), debug(debug), + validateGlobally(validateGlobally) { if (!useWorkers()) { // if we shouldn't use threads, don't @@ -112,7 +123,8 @@ public: } // Before parallelism, create all passes on the main thread here, to ensure - // prepareToRun() is called for each pass before we start to optimize functions. + // prepareToRun() is called for each pass before we start to optimize + // functions. { PassRunner passRunner(wasm, passOptions); addPrePasses(passRunner); @@ -132,7 +144,9 @@ public: // worth it to use threads liveWorkers.store(0); activeWorkers.store(0); - for (uint32_t i = 0; i < numWorkers; i++) { // TODO: one less, and add it at the very end, to not compete with main thread? + // TODO: one less, and add it at the very end, to not compete with main + // thread? + for (uint32_t i = 0; i < numWorkers; i++) { createWorker(); } waitUntilAllReady(); @@ -148,13 +162,15 @@ public: } bool useWorkers() { - return numFunctions > 0 && !debug && ThreadPool::getNumCores() > 1 && !PassRunner::getPassDebug(); + return numFunctions > 0 && !debug && ThreadPool::getNumCores() > 1 && + !PassRunner::getPassDebug(); } // Add a function to the module, and to be optimized void addFunction(Function* func) { wasm->addFunction(func); - if (!useWorkers()) return; // we optimize at the end in that case + if (!useWorkers()) + return; // we optimize at the end in that case queueFunction(func); // notify workers if needed auto notify = availableFuncs.load(); @@ -183,7 +199,8 @@ public: notifyAllWorkers(); waitUntilAllFinished(); } - // TODO: clear side thread allocators from module allocator, as these threads were transient + // TODO: clear side thread allocators from module allocator, as these + // threads were transient } private: @@ -208,7 +225,8 @@ private: DEBUG_THREAD("wait until all workers are ready"); std::unique_lock<std::mutex> lock(mutex); if (liveWorkers.load() < numWorkers) { - condition.wait(lock, [this]() { return liveWorkers.load() == numWorkers; }); + condition.wait(lock, + [this]() { return liveWorkers.load() == numWorkers; }); } } @@ -222,13 +240,15 @@ private: } } DEBUG_THREAD("joining"); - for (auto& thread : threads) thread->join(); + for (auto& thread : threads) + thread->join(); DEBUG_THREAD("joined"); } void queueFunction(Function* func) { DEBUG_THREAD("queue function"); - assert(nextFunction < numFunctions); // TODO: if we are given more than we expected, use a slower work queue? + // TODO: if we are given more than we expected, use a slower work queue? + assert(nextFunction < numFunctions); list[nextFunction++].store(func); availableFuncs++; } @@ -264,7 +284,8 @@ private: self->activeWorkers--; { std::unique_lock<std::mutex> lock(self->mutex); - if (!self->finishing) { // while waiting for the lock, things may have ended + // while waiting for the lock, things may have ended + if (!self->finishing) { self->condition.wait(lock); } } diff --git a/src/wasm-printing.h b/src/wasm-printing.h index d3327aa4c..712dd3c1f 100644 --- a/src/wasm-printing.h +++ b/src/wasm-printing.h @@ -19,8 +19,8 @@ #include <ostream> -#include "wasm.h" #include "pass.h" +#include "wasm.h" namespace wasm { @@ -29,11 +29,16 @@ struct WasmPrinter { static std::ostream& printModule(Module* module); - static std::ostream& printExpression(Expression* expression, std::ostream& o, bool minify = false, bool full = false); + static std::ostream& printExpression(Expression* expression, + std::ostream& o, + bool minify = false, + bool full = false); - static std::ostream& printStackInst(StackInst* inst, std::ostream& o, Function* func=nullptr); + static std::ostream& + printStackInst(StackInst* inst, std::ostream& o, Function* func = nullptr); - static std::ostream& printStackIR(StackIR* ir, std::ostream& o, Function* func=nullptr); + static std::ostream& + printStackIR(StackIR* ir, std::ostream& o, Function* func = nullptr); }; } // namespace wasm diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index 295e6b6c9..004fd9840 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -22,20 +22,21 @@ #ifndef wasm_wasm_s_parser_h #define wasm_wasm_s_parser_h -#include "wasm.h" #include "mixed_arena.h" #include "parsing.h" // for UniqueNameMapper. TODO: move dependency to cpp file? +#include "wasm.h" namespace wasm { -class SourceLocation -{ +class SourceLocation { public: cashew::IString filename; uint32_t line; uint32_t column; - SourceLocation(cashew::IString filename_, uint32_t line_, uint32_t column_ = 0) - : filename(filename_), line(line_), column(column_) {} + SourceLocation(cashew::IString filename_, + uint32_t line_, + uint32_t column_ = 0) + : filename(filename_), line(line_), column(column_) {} }; // @@ -59,7 +60,7 @@ public: bool quoted() const { return isStr() && quoted_; } size_t line = -1; - size_t col = -1; + size_t col = -1; // original locations at the start/end of the S-Expression list SourceLocation* startLoc = nullptr; SourceLocation* endLoc = nullptr; @@ -67,9 +68,7 @@ public: // list methods List& list(); Element* operator[](unsigned i); - size_t size() { - return list().size(); - } + size_t size() { return list().size(); } // string methods cashew::IString str() const; @@ -116,15 +115,19 @@ class SExpressionWasmBuilder { std::vector<Name> globalNames; int functionCounter; int globalCounter = 0; - std::map<Name, Type> functionTypes; // we need to know function return types before we parse their contents + // we need to know function return types before we parse their contents + std::map<Name, Type> functionTypes; std::unordered_map<cashew::IString, Index> debugInfoFileIndices; public: // Assumes control of and modifies the input. - SExpressionWasmBuilder(Module& wasm, Element& module, Name* moduleName = nullptr); + SExpressionWasmBuilder(Module& wasm, + Element& module, + Name* moduleName = nullptr); private: - // pre-parse types and function definitions, so we know function return types before parsing their contents + // pre-parse types and function definitions, so we know function return types + // before parsing their contents void preParseFunctionType(Element& s); bool isImport(Element& curr); void preParseImports(Element& curr); @@ -142,30 +145,27 @@ private: Name getFunctionName(Element& s); Name getFunctionTypeName(Element& s); Name getGlobalName(Element& s); - void parseStart(Element& s) { wasm.addStart(getFunctionName(*s[1]));} + void parseStart(Element& s) { wasm.addStart(getFunctionName(*s[1])); } // returns the next index in s size_t parseFunctionNames(Element& s, Name& name, Name& exportName); void parseFunction(Element& s, bool preParseImport = false); - Type stringToType(cashew::IString str, bool allowError=false, bool prefix=false) { + Type stringToType(cashew::IString str, + bool allowError = false, + bool prefix = false) { return stringToType(str.str, allowError, prefix); } - Type stringToType(const char* str, bool allowError=false, bool prefix=false); + Type + stringToType(const char* str, bool allowError = false, bool prefix = false); Type stringToLaneType(const char* str); - bool isType(cashew::IString str) { - return stringToType(str, true) != none; - } + bool isType(cashew::IString str) { return stringToType(str, true) != none; } public: - Expression* parseExpression(Element* s) { - return parseExpression(*s); - } + Expression* parseExpression(Element* s) { return parseExpression(*s); } Expression* parseExpression(Element& s); - MixedArena& getAllocator() { - return allocator; - } + MixedArena& getAllocator() { return allocator; } private: Expression* makeExpression(Element& s); @@ -188,8 +188,10 @@ private: Expression* makeLoad(Element& s, Type type, bool isAtomic); Expression* makeStore(Element& s, Type type, bool isAtomic); Expression* makeAtomicRMWOrCmpxchg(Element& s, Type type); - Expression* makeAtomicRMW(Element& s, Type type, uint8_t bytes, const char* extra); - Expression* makeAtomicCmpxchg(Element& s, Type type, uint8_t bytes, const char* extra); + Expression* + makeAtomicRMW(Element& s, Type type, uint8_t bytes, const char* extra); + Expression* + makeAtomicCmpxchg(Element& s, Type type, uint8_t bytes, const char* extra); Expression* makeAtomicWait(Element& s, Type type); Expression* makeAtomicNotify(Element& s); Expression* makeSIMDExtract(Element& s, SIMDExtractOp op, size_t lanes); diff --git a/src/wasm-stack.h b/src/wasm-stack.h index afef2632b..124b70926 100644 --- a/src/wasm-stack.h +++ b/src/wasm-stack.h @@ -17,11 +17,11 @@ #ifndef wasm_stack_h #define wasm_stack_h -#include "wasm.h" -#include "wasm-binary.h" -#include "wasm-traversal.h" #include "ir/branch-utils.h" #include "pass.h" +#include "wasm-binary.h" +#include "wasm-traversal.h" +#include "wasm.h" namespace wasm { @@ -40,8 +40,8 @@ namespace wasm { // IR (so that we have just a single IR). // A StackIR instance (see wasm.h) contains a linear sequence of -// stack instructions. This representation is very simple: just a single vector of -// all instructions, in order. +// stack instructions. This representation is very simple: just a single vector +// of all instructions, in order. // * nullptr is allowed in the vector, representing something to skip. // This is useful as a common thing optimizations do is remove instructions, // so this way we can do so without compacting the vector all the time. @@ -66,12 +66,14 @@ public: Expression* origin; // the expression this originates from - Type type; // the type - usually identical to the origin type, but - // e.g. wasm has no unreachable blocks, they must be none + // the type - usually identical to the origin type, but e.g. wasm has no + // unreachable blocks, they must be none + Type type; }; // -// StackWriter: Writes out binary format stack machine code for a Binaryen IR expression +// StackWriter: Writes out binary format stack machine code for a Binaryen IR +// expression // // A stack writer has one of three modes: // * Binaryen2Binary: directly writes the expression to wasm binary @@ -89,23 +91,27 @@ public: // downside. // -enum class StackWriterMode { - Binaryen2Binary, Binaryen2Stack, Stack2Binary -}; +enum class StackWriterMode { Binaryen2Binary, Binaryen2Stack, Stack2Binary }; template<StackWriterMode Mode, typename Parent> class StackWriter : public Visitor<StackWriter<Mode, Parent>> { public: - StackWriter(Parent& parent, BufferWithRandomAccess& o, bool sourceMap=false, bool debug=false) - : parent(parent), o(o), sourceMap(sourceMap), debug(debug), allocator(parent.getModule()->allocator) {} + StackWriter(Parent& parent, + BufferWithRandomAccess& o, + bool sourceMap = false, + bool debug = false) + : parent(parent), o(o), sourceMap(sourceMap), debug(debug), + allocator(parent.getModule()->allocator) {} StackIR stackIR; // filled in Binaryen2Stack, read in Stack2Binary - std::map<Type, size_t> numLocalsByType; // type => number of locals of that type in the compact form + // type => number of locals of that type in the compact form + std::map<Type, size_t> numLocalsByType; // visits a node, emitting the proper code for it void visit(Expression* curr); - // emits a node, but if it is a block with no name, emit a list of its contents + // emits a node, but if it is a block with no name, emit a list of its + // contents void visitPossibleBlockContents(Expression* curr); // visits a child node. (in some modes we may not want to visit children, // that logic is handled here) @@ -162,9 +168,7 @@ public: // non-control flow expressions. bool justAddToStack(Expression* curr); - void setFunction(Function* funcInit) { - func = funcInit; - } + void setFunction(Function* funcInit) { func = funcInit; } void mapLocalsAndEmitHeader(); @@ -178,7 +182,8 @@ protected: Function* func; - std::map<Index, size_t> mappedLocals; // local index => index in compact form of [all int32s][all int64s]etc + // local index => index in compact form of [all int32s][all int64s]etc + std::map<Index, size_t> mappedLocals; std::vector<Name> breakStack; @@ -195,20 +200,31 @@ protected: // Write out a single expression, such as an offset for a global segment. template<typename Parent> -class ExpressionStackWriter : StackWriter<StackWriterMode::Binaryen2Binary, Parent> { +class ExpressionStackWriter + : StackWriter<StackWriterMode::Binaryen2Binary, Parent> { public: - ExpressionStackWriter(Expression* curr, Parent& parent, BufferWithRandomAccess& o, bool debug=false) : - StackWriter<StackWriterMode::Binaryen2Binary, Parent>(parent, o, /* sourceMap= */ false, debug) { + ExpressionStackWriter(Expression* curr, + Parent& parent, + BufferWithRandomAccess& o, + bool debug = false) + : StackWriter<StackWriterMode::Binaryen2Binary, Parent>( + parent, o, /* sourceMap= */ false, debug) { this->visit(curr); } }; // Write out a function body, including the local header info. template<typename Parent> -class FunctionStackWriter : StackWriter<StackWriterMode::Binaryen2Binary, Parent> { +class FunctionStackWriter + : StackWriter<StackWriterMode::Binaryen2Binary, Parent> { public: - FunctionStackWriter(Function* funcInit, Parent& parent, BufferWithRandomAccess& o, bool sourceMap=false, bool debug=false) : - StackWriter<StackWriterMode::Binaryen2Binary, Parent>(parent, o, sourceMap, debug) { + FunctionStackWriter(Function* funcInit, + Parent& parent, + BufferWithRandomAccess& o, + bool sourceMap = false, + bool debug = false) + : StackWriter<StackWriterMode::Binaryen2Binary, Parent>( + parent, o, sourceMap, debug) { this->setFunction(funcInit); this->mapLocalsAndEmitHeader(); this->visitPossibleBlockContents(this->func->body); @@ -218,14 +234,20 @@ public: // Use Stack IR to write the function body template<typename Parent> -class StackIRFunctionStackWriter : StackWriter<StackWriterMode::Stack2Binary, Parent> { +class StackIRFunctionStackWriter + : StackWriter<StackWriterMode::Stack2Binary, Parent> { public: - StackIRFunctionStackWriter(Function* funcInit, Parent& parent, BufferWithRandomAccess& o, bool debug=false) : - StackWriter<StackWriterMode::Stack2Binary, Parent>(parent, o, false, debug) { + StackIRFunctionStackWriter(Function* funcInit, + Parent& parent, + BufferWithRandomAccess& o, + bool debug = false) + : StackWriter<StackWriterMode::Stack2Binary, Parent>( + parent, o, false, debug) { this->setFunction(funcInit); this->mapLocalsAndEmitHeader(); for (auto* inst : *funcInit->stackIR) { - if (!inst) continue; // a nullptr is just something we can skip + if (!inst) + continue; // a nullptr is just something we can skip switch (inst->op) { case StackInst::Basic: case StackInst::BlockBegin: @@ -250,7 +272,8 @@ public: this->visitLoopEnd(inst->origin->template cast<Loop>()); break; } - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } } this->finishFunctionBody(); @@ -280,7 +303,8 @@ void StackWriter<Mode, Parent>::mapLocalsAndEmitHeader() { for (Index i = func->getVarIndexBase(); i < func->getNumLocals(); i++) { size_t index = func->getVarIndexBase(); Type type = func->getLocalType(i); - currLocalsByType[type]++; // increment now for simplicity, must decrement it in returns + // increment now for simplicity, must decrement it in returns + currLocalsByType[type]++; if (type == i32) { mappedLocals[i] = index + currLocalsByType[i32] - 1; continue; @@ -308,18 +332,19 @@ void StackWriter<Mode, Parent>::mapLocalsAndEmitHeader() { WASM_UNREACHABLE(); } // Emit them. - o << U32LEB( - (numLocalsByType[i32] ? 1 : 0) + - (numLocalsByType[i64] ? 1 : 0) + - (numLocalsByType[f32] ? 1 : 0) + - (numLocalsByType[f64] ? 1 : 0) + - (numLocalsByType[v128] ? 1 : 0) - ); - if (numLocalsByType[i32]) o << U32LEB(numLocalsByType[i32]) << binaryType(i32); - if (numLocalsByType[i64]) o << U32LEB(numLocalsByType[i64]) << binaryType(i64); - if (numLocalsByType[f32]) o << U32LEB(numLocalsByType[f32]) << binaryType(f32); - if (numLocalsByType[f64]) o << U32LEB(numLocalsByType[f64]) << binaryType(f64); - if (numLocalsByType[v128]) o << U32LEB(numLocalsByType[v128]) << binaryType(v128); + o << U32LEB((numLocalsByType[i32] ? 1 : 0) + (numLocalsByType[i64] ? 1 : 0) + + (numLocalsByType[f32] ? 1 : 0) + (numLocalsByType[f64] ? 1 : 0) + + (numLocalsByType[v128] ? 1 : 0)); + if (numLocalsByType[i32]) + o << U32LEB(numLocalsByType[i32]) << binaryType(i32); + if (numLocalsByType[i64]) + o << U32LEB(numLocalsByType[i64]) << binaryType(i64); + if (numLocalsByType[f32]) + o << U32LEB(numLocalsByType[f32]) << binaryType(f32); + if (numLocalsByType[f64]) + o << U32LEB(numLocalsByType[f64]) << binaryType(f64); + if (numLocalsByType[v128]) + o << U32LEB(numLocalsByType[v128]) << binaryType(v128); } template<StackWriterMode Mode, typename Parent> @@ -366,7 +391,8 @@ void StackWriter<Mode, Parent>::visitBlock(Block* curr) { o << int8_t(BinaryConsts::Block); o << binaryType(curr->type != unreachable ? curr->type : none); } - breakStack.push_back(curr->name); // TODO: we don't need to do this in Binaryen2Stack + // TODO: we don't need to do this in Binaryen2Stack + breakStack.push_back(curr->name); }; auto visitChildren = [this](Block* curr, Index from) { auto& list = curr->list; @@ -390,8 +416,7 @@ void StackWriter<Mode, Parent>::visitBlock(Block* curr) { if (!curr->list.empty() && curr->list[0]->is<Block>()) { std::vector<Block*> parents; Block* child; - while (!curr->list.empty() && - (child = curr->list[0]->dynCast<Block>())) { + while (!curr->list.empty() && (child = curr->list[0]->dynCast<Block>())) { parents.push_back(curr); tilChildren(curr); curr = child; @@ -420,10 +445,10 @@ void StackWriter<Mode, Parent>::visitBlock(Block* curr) { template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitBlockEnd(Block* curr) { if (curr->type == unreachable) { - // an unreachable block is one that cannot be exited. We cannot encode this directly - // in wasm, where blocks must be none,i32,i64,f32,f64. Since the block cannot be - // exited, we can emit an unreachable at the end, and that will always be valid, - // and then the block is ok as a none + // an unreachable block is one that cannot be exited. We cannot encode this + // directly in wasm, where blocks must be none,i32,i64,f32,f64. Since the + // block cannot be exited, we can emit an unreachable at the end, and that + // will always be valid, and then the block is ok as a none emitExtraUnreachable(); } if (Mode == StackWriterMode::Binaryen2Stack) { @@ -434,7 +459,8 @@ void StackWriter<Mode, Parent>::visitBlockEnd(Block* curr) { assert(!breakStack.empty()); breakStack.pop_back(); if (curr->type == unreachable) { - // and emit an unreachable *outside* the block too, so later things can pop anything + // and emit an unreachable *outside* the block too, so later things can pop + // anything emitExtraUnreachable(); } } @@ -455,10 +481,12 @@ void StackWriter<Mode, Parent>::visitIf(If* curr) { o << int8_t(BinaryConsts::If); o << binaryType(curr->type != unreachable ? curr->type : none); } - breakStack.push_back(IMPOSSIBLE_CONTINUE); // the binary format requires this; we have a block if we need one - // TODO: optimize this in Stack IR (if child is a block, we - // may break to this instead) - visitPossibleBlockContents(curr->ifTrue); // TODO: emit block contents directly, if possible + // the binary format requires this; we have a block if we need one + // TODO: optimize this in Stack IR (if child is a block, we may break to this + // instead) + breakStack.push_back(IMPOSSIBLE_CONTINUE); + // TODO: emit block contents directly, if possible + visitPossibleBlockContents(curr->ifTrue); if (Mode == StackWriterMode::Stack2Binary) { return; } @@ -491,10 +519,11 @@ void StackWriter<Mode, Parent>::visitIfEnd(If* curr) { o << int8_t(BinaryConsts::End); } if (curr->type == unreachable) { - // we already handled the case of the condition being unreachable. otherwise, - // we may still be unreachable, if we are an if-else with both sides unreachable. - // wasm does not allow this to be emitted directly, so we must do something more. we could do - // better, but for now we emit an extra unreachable instruction after the if, so it is not consumed itself, + // we already handled the case of the condition being unreachable. + // otherwise, we may still be unreachable, if we are an if-else with both + // sides unreachable. wasm does not allow this to be emitted directly, so we + // must do something more. we could do better, but for now we emit an extra + // unreachable instruction after the if, so it is not consumed itself, assert(curr->ifFalse); emitExtraUnreachable(); } @@ -541,7 +570,8 @@ void StackWriter<Mode, Parent>::visitBreak(Break* curr) { if (curr->value) { visitChild(curr->value); } - if (curr->condition) visitChild(curr->condition); + if (curr->condition) + visitChild(curr->condition); if (!justAddToStack(curr)) { o << int8_t(curr->condition ? BinaryConsts::BrIf : BinaryConsts::Br) << U32LEB(getBreakIndex(curr->name)); @@ -571,7 +601,8 @@ void StackWriter<Mode, Parent>::visitSwitch(Switch* curr) { emitExtraUnreachable(); return; } - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::TableSwitch) << U32LEB(curr->targets.size()); for (auto target : curr->targets) { o << U32LEB(getBreakIndex(target)); @@ -585,9 +616,11 @@ void StackWriter<Mode, Parent>::visitCall(Call* curr) { visitChild(operand); } if (!justAddToStack(curr)) { - o << int8_t(BinaryConsts::CallFunction) << U32LEB(parent.getFunctionIndex(curr->target)); + o << int8_t(BinaryConsts::CallFunction) + << U32LEB(parent.getFunctionIndex(curr->target)); } - if (curr->type == unreachable) { // TODO FIXME: this and similar can be removed + // TODO FIXME: this and similar can be removed + if (curr->type == unreachable) { emitExtraUnreachable(); } } @@ -610,7 +643,8 @@ void StackWriter<Mode, Parent>::visitCallIndirect(CallIndirect* curr) { template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitGetLocal(GetLocal* curr) { - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::GetLocal) << U32LEB(mappedLocals[curr->index]); } @@ -618,7 +652,8 @@ template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitSetLocal(SetLocal* curr) { visitChild(curr->value); if (!justAddToStack(curr)) { - o << int8_t(curr->isTee() ? BinaryConsts::TeeLocal : BinaryConsts::SetLocal) << U32LEB(mappedLocals[curr->index]); + o << int8_t(curr->isTee() ? BinaryConsts::TeeLocal : BinaryConsts::SetLocal) + << U32LEB(mappedLocals[curr->index]); } if (curr->type == unreachable) { emitExtraUnreachable(); @@ -627,15 +662,19 @@ void StackWriter<Mode, Parent>::visitSetLocal(SetLocal* curr) { template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitGetGlobal(GetGlobal* curr) { - if (justAddToStack(curr)) return; - o << int8_t(BinaryConsts::GetGlobal) << U32LEB(parent.getGlobalIndex(curr->name)); + if (justAddToStack(curr)) + return; + o << int8_t(BinaryConsts::GetGlobal) + << U32LEB(parent.getGlobalIndex(curr->name)); } template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitSetGlobal(SetGlobal* curr) { visitChild(curr->value); - if (justAddToStack(curr)) return; - o << int8_t(BinaryConsts::SetGlobal) << U32LEB(parent.getGlobalIndex(curr->name)); + if (justAddToStack(curr)) + return; + o << int8_t(BinaryConsts::SetGlobal) + << U32LEB(parent.getGlobalIndex(curr->name)); } template<StackWriterMode Mode, typename Parent> @@ -646,58 +685,108 @@ void StackWriter<Mode, Parent>::visitLoad(Load* curr) { emitExtraUnreachable(); return; } - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; if (!curr->isAtomic) { switch (curr->type) { case i32: { switch (curr->bytes) { - case 1: o << int8_t(curr->signed_ ? BinaryConsts::I32LoadMem8S : BinaryConsts::I32LoadMem8U); break; - case 2: o << int8_t(curr->signed_ ? BinaryConsts::I32LoadMem16S : BinaryConsts::I32LoadMem16U); break; - case 4: o << int8_t(BinaryConsts::I32LoadMem); break; - default: abort(); + case 1: + o << int8_t(curr->signed_ ? BinaryConsts::I32LoadMem8S + : BinaryConsts::I32LoadMem8U); + break; + case 2: + o << int8_t(curr->signed_ ? BinaryConsts::I32LoadMem16S + : BinaryConsts::I32LoadMem16U); + break; + case 4: + o << int8_t(BinaryConsts::I32LoadMem); + break; + default: + abort(); } break; } case i64: { switch (curr->bytes) { - case 1: o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem8S : BinaryConsts::I64LoadMem8U); break; - case 2: o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem16S : BinaryConsts::I64LoadMem16U); break; - case 4: o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem32S : BinaryConsts::I64LoadMem32U); break; - case 8: o << int8_t(BinaryConsts::I64LoadMem); break; - default: abort(); + case 1: + o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem8S + : BinaryConsts::I64LoadMem8U); + break; + case 2: + o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem16S + : BinaryConsts::I64LoadMem16U); + break; + case 4: + o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem32S + : BinaryConsts::I64LoadMem32U); + break; + case 8: + o << int8_t(BinaryConsts::I64LoadMem); + break; + default: + abort(); } break; } - case f32: o << int8_t(BinaryConsts::F32LoadMem); break; - case f64: o << int8_t(BinaryConsts::F64LoadMem); break; - case v128: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::V128Load); break; - case unreachable: return; // the pointer is unreachable, so we are never reached; just don't emit a load - case none: WASM_UNREACHABLE(); + case f32: + o << int8_t(BinaryConsts::F32LoadMem); + break; + case f64: + o << int8_t(BinaryConsts::F64LoadMem); + break; + case v128: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::V128Load); + break; + case unreachable: + // the pointer is unreachable, so we are never reached; just don't emit + // a load + return; + case none: + WASM_UNREACHABLE(); } } else { o << int8_t(BinaryConsts::AtomicPrefix); switch (curr->type) { case i32: { switch (curr->bytes) { - case 1: o << int8_t(BinaryConsts::I32AtomicLoad8U); break; - case 2: o << int8_t(BinaryConsts::I32AtomicLoad16U); break; - case 4: o << int8_t(BinaryConsts::I32AtomicLoad); break; - default: WASM_UNREACHABLE(); + case 1: + o << int8_t(BinaryConsts::I32AtomicLoad8U); + break; + case 2: + o << int8_t(BinaryConsts::I32AtomicLoad16U); + break; + case 4: + o << int8_t(BinaryConsts::I32AtomicLoad); + break; + default: + WASM_UNREACHABLE(); } break; } case i64: { switch (curr->bytes) { - case 1: o << int8_t(BinaryConsts::I64AtomicLoad8U); break; - case 2: o << int8_t(BinaryConsts::I64AtomicLoad16U); break; - case 4: o << int8_t(BinaryConsts::I64AtomicLoad32U); break; - case 8: o << int8_t(BinaryConsts::I64AtomicLoad); break; - default: WASM_UNREACHABLE(); + case 1: + o << int8_t(BinaryConsts::I64AtomicLoad8U); + break; + case 2: + o << int8_t(BinaryConsts::I64AtomicLoad16U); + break; + case 4: + o << int8_t(BinaryConsts::I64AtomicLoad32U); + break; + case 8: + o << int8_t(BinaryConsts::I64AtomicLoad); + break; + default: + WASM_UNREACHABLE(); } break; } - case unreachable: return; - default: WASM_UNREACHABLE(); + case unreachable: + return; + default: + WASM_UNREACHABLE(); } } emitMemoryAccess(curr->align, curr->bytes, curr->offset); @@ -712,57 +801,99 @@ void StackWriter<Mode, Parent>::visitStore(Store* curr) { emitExtraUnreachable(); return; } - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; if (!curr->isAtomic) { switch (curr->valueType) { case i32: { switch (curr->bytes) { - case 1: o << int8_t(BinaryConsts::I32StoreMem8); break; - case 2: o << int8_t(BinaryConsts::I32StoreMem16); break; - case 4: o << int8_t(BinaryConsts::I32StoreMem); break; - default: abort(); + case 1: + o << int8_t(BinaryConsts::I32StoreMem8); + break; + case 2: + o << int8_t(BinaryConsts::I32StoreMem16); + break; + case 4: + o << int8_t(BinaryConsts::I32StoreMem); + break; + default: + abort(); } break; } case i64: { switch (curr->bytes) { - case 1: o << int8_t(BinaryConsts::I64StoreMem8); break; - case 2: o << int8_t(BinaryConsts::I64StoreMem16); break; - case 4: o << int8_t(BinaryConsts::I64StoreMem32); break; - case 8: o << int8_t(BinaryConsts::I64StoreMem); break; - default: abort(); + case 1: + o << int8_t(BinaryConsts::I64StoreMem8); + break; + case 2: + o << int8_t(BinaryConsts::I64StoreMem16); + break; + case 4: + o << int8_t(BinaryConsts::I64StoreMem32); + break; + case 8: + o << int8_t(BinaryConsts::I64StoreMem); + break; + default: + abort(); } break; } - case f32: o << int8_t(BinaryConsts::F32StoreMem); break; - case f64: o << int8_t(BinaryConsts::F64StoreMem); break; - case v128: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::V128Store); break; + case f32: + o << int8_t(BinaryConsts::F32StoreMem); + break; + case f64: + o << int8_t(BinaryConsts::F64StoreMem); + break; + case v128: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::V128Store); + break; case none: - case unreachable: WASM_UNREACHABLE(); + case unreachable: + WASM_UNREACHABLE(); } } else { o << int8_t(BinaryConsts::AtomicPrefix); switch (curr->valueType) { case i32: { switch (curr->bytes) { - case 1: o << int8_t(BinaryConsts::I32AtomicStore8); break; - case 2: o << int8_t(BinaryConsts::I32AtomicStore16); break; - case 4: o << int8_t(BinaryConsts::I32AtomicStore); break; - default: WASM_UNREACHABLE(); + case 1: + o << int8_t(BinaryConsts::I32AtomicStore8); + break; + case 2: + o << int8_t(BinaryConsts::I32AtomicStore16); + break; + case 4: + o << int8_t(BinaryConsts::I32AtomicStore); + break; + default: + WASM_UNREACHABLE(); } break; } case i64: { switch (curr->bytes) { - case 1: o << int8_t(BinaryConsts::I64AtomicStore8); break; - case 2: o << int8_t(BinaryConsts::I64AtomicStore16); break; - case 4: o << int8_t(BinaryConsts::I64AtomicStore32); break; - case 8: o << int8_t(BinaryConsts::I64AtomicStore); break; - default: WASM_UNREACHABLE(); + case 1: + o << int8_t(BinaryConsts::I64AtomicStore8); + break; + case 2: + o << int8_t(BinaryConsts::I64AtomicStore16); + break; + case 4: + o << int8_t(BinaryConsts::I64AtomicStore32); + break; + case 8: + o << int8_t(BinaryConsts::I64AtomicStore); + break; + default: + WASM_UNREACHABLE(); } break; } - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } } emitMemoryAccess(curr->align, curr->bytes, curr->offset); @@ -772,50 +903,71 @@ template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitAtomicRMW(AtomicRMW* curr) { visitChild(curr->ptr); // stop if the rest isn't reachable anyhow - if (curr->ptr->type == unreachable) return; + if (curr->ptr->type == unreachable) + return; visitChild(curr->value); - if (curr->value->type == unreachable) return; + if (curr->value->type == unreachable) + return; if (curr->type == unreachable) { // don't even emit it; we don't know the right type emitExtraUnreachable(); return; } - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::AtomicPrefix); -#define CASE_FOR_OP(Op) \ - case Op: \ - switch (curr->type) { \ - case i32: \ - switch (curr->bytes) { \ - case 1: o << int8_t(BinaryConsts::I32AtomicRMW##Op##8U); break; \ - case 2: o << int8_t(BinaryConsts::I32AtomicRMW##Op##16U); break; \ - case 4: o << int8_t(BinaryConsts::I32AtomicRMW##Op); break; \ - default: WASM_UNREACHABLE(); \ - } \ - break; \ - case i64: \ - switch (curr->bytes) { \ - case 1: o << int8_t(BinaryConsts::I64AtomicRMW##Op##8U); break; \ - case 2: o << int8_t(BinaryConsts::I64AtomicRMW##Op##16U); break; \ - case 4: o << int8_t(BinaryConsts::I64AtomicRMW##Op##32U); break; \ - case 8: o << int8_t(BinaryConsts::I64AtomicRMW##Op); break; \ - default: WASM_UNREACHABLE(); \ - } \ - break; \ - default: WASM_UNREACHABLE(); \ - } \ +#define CASE_FOR_OP(Op) \ + case Op: \ + switch (curr->type) { \ + case i32: \ + switch (curr->bytes) { \ + case 1: \ + o << int8_t(BinaryConsts::I32AtomicRMW##Op##8U); \ + break; \ + case 2: \ + o << int8_t(BinaryConsts::I32AtomicRMW##Op##16U); \ + break; \ + case 4: \ + o << int8_t(BinaryConsts::I32AtomicRMW##Op); \ + break; \ + default: \ + WASM_UNREACHABLE(); \ + } \ + break; \ + case i64: \ + switch (curr->bytes) { \ + case 1: \ + o << int8_t(BinaryConsts::I64AtomicRMW##Op##8U); \ + break; \ + case 2: \ + o << int8_t(BinaryConsts::I64AtomicRMW##Op##16U); \ + break; \ + case 4: \ + o << int8_t(BinaryConsts::I64AtomicRMW##Op##32U); \ + break; \ + case 8: \ + o << int8_t(BinaryConsts::I64AtomicRMW##Op); \ + break; \ + default: \ + WASM_UNREACHABLE(); \ + } \ + break; \ + default: \ + WASM_UNREACHABLE(); \ + } \ break - switch(curr->op) { + switch (curr->op) { CASE_FOR_OP(Add); CASE_FOR_OP(Sub); CASE_FOR_OP(And); CASE_FOR_OP(Or); CASE_FOR_OP(Xor); CASE_FOR_OP(Xchg); - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } #undef CASE_FOR_OP @@ -826,38 +978,59 @@ template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitAtomicCmpxchg(AtomicCmpxchg* curr) { visitChild(curr->ptr); // stop if the rest isn't reachable anyhow - if (curr->ptr->type == unreachable) return; + if (curr->ptr->type == unreachable) + return; visitChild(curr->expected); - if (curr->expected->type == unreachable) return; + if (curr->expected->type == unreachable) + return; visitChild(curr->replacement); - if (curr->replacement->type == unreachable) return; + if (curr->replacement->type == unreachable) + return; if (curr->type == unreachable) { // don't even emit it; we don't know the right type emitExtraUnreachable(); return; } - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::AtomicPrefix); switch (curr->type) { case i32: switch (curr->bytes) { - case 1: o << int8_t(BinaryConsts::I32AtomicCmpxchg8U); break; - case 2: o << int8_t(BinaryConsts::I32AtomicCmpxchg16U); break; - case 4: o << int8_t(BinaryConsts::I32AtomicCmpxchg); break; - default: WASM_UNREACHABLE(); + case 1: + o << int8_t(BinaryConsts::I32AtomicCmpxchg8U); + break; + case 2: + o << int8_t(BinaryConsts::I32AtomicCmpxchg16U); + break; + case 4: + o << int8_t(BinaryConsts::I32AtomicCmpxchg); + break; + default: + WASM_UNREACHABLE(); } break; case i64: switch (curr->bytes) { - case 1: o << int8_t(BinaryConsts::I64AtomicCmpxchg8U); break; - case 2: o << int8_t(BinaryConsts::I64AtomicCmpxchg16U); break; - case 4: o << int8_t(BinaryConsts::I64AtomicCmpxchg32U); break; - case 8: o << int8_t(BinaryConsts::I64AtomicCmpxchg); break; - default: WASM_UNREACHABLE(); + case 1: + o << int8_t(BinaryConsts::I64AtomicCmpxchg8U); + break; + case 2: + o << int8_t(BinaryConsts::I64AtomicCmpxchg16U); + break; + case 4: + o << int8_t(BinaryConsts::I64AtomicCmpxchg32U); + break; + case 8: + o << int8_t(BinaryConsts::I64AtomicCmpxchg); + break; + default: + WASM_UNREACHABLE(); } break; - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } emitMemoryAccess(curr->bytes, curr->bytes, curr->offset); } @@ -866,12 +1039,16 @@ template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitAtomicWait(AtomicWait* curr) { visitChild(curr->ptr); // stop if the rest isn't reachable anyhow - if (curr->ptr->type == unreachable) return; + if (curr->ptr->type == unreachable) + return; visitChild(curr->expected); - if (curr->expected->type == unreachable) return; + if (curr->expected->type == unreachable) + return; visitChild(curr->timeout); - if (curr->timeout->type == unreachable) return; - if (justAddToStack(curr)) return; + if (curr->timeout->type == unreachable) + return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::AtomicPrefix); switch (curr->expectedType) { @@ -885,7 +1062,8 @@ void StackWriter<Mode, Parent>::visitAtomicWait(AtomicWait* curr) { emitMemoryAccess(8, 8, 0); break; } - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } } @@ -893,10 +1071,13 @@ template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitAtomicNotify(AtomicNotify* curr) { visitChild(curr->ptr); // stop if the rest isn't reachable anyhow - if (curr->ptr->type == unreachable) return; + if (curr->ptr->type == unreachable) + return; visitChild(curr->notifyCount); - if (curr->notifyCount->type == unreachable) return; - if (justAddToStack(curr)) return; + if (curr->notifyCount->type == unreachable) + return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::AtomicPrefix) << int8_t(BinaryConsts::AtomicNotify); emitMemoryAccess(4, 4, 0); @@ -905,17 +1086,34 @@ void StackWriter<Mode, Parent>::visitAtomicNotify(AtomicNotify* curr) { template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitSIMDExtract(SIMDExtract* curr) { visitChild(curr->vec); - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::SIMDPrefix); switch (curr->op) { - case ExtractLaneSVecI8x16: o << U32LEB(BinaryConsts::I8x16ExtractLaneS); break; - case ExtractLaneUVecI8x16: o << U32LEB(BinaryConsts::I8x16ExtractLaneU); break; - case ExtractLaneSVecI16x8: o << U32LEB(BinaryConsts::I16x8ExtractLaneS); break; - case ExtractLaneUVecI16x8: o << U32LEB(BinaryConsts::I16x8ExtractLaneU); break; - case ExtractLaneVecI32x4: o << U32LEB(BinaryConsts::I32x4ExtractLane); break; - case ExtractLaneVecI64x2: o << U32LEB(BinaryConsts::I64x2ExtractLane); break; - case ExtractLaneVecF32x4: o << U32LEB(BinaryConsts::F32x4ExtractLane); break; - case ExtractLaneVecF64x2: o << U32LEB(BinaryConsts::F64x2ExtractLane); break; + case ExtractLaneSVecI8x16: + o << U32LEB(BinaryConsts::I8x16ExtractLaneS); + break; + case ExtractLaneUVecI8x16: + o << U32LEB(BinaryConsts::I8x16ExtractLaneU); + break; + case ExtractLaneSVecI16x8: + o << U32LEB(BinaryConsts::I16x8ExtractLaneS); + break; + case ExtractLaneUVecI16x8: + o << U32LEB(BinaryConsts::I16x8ExtractLaneU); + break; + case ExtractLaneVecI32x4: + o << U32LEB(BinaryConsts::I32x4ExtractLane); + break; + case ExtractLaneVecI64x2: + o << U32LEB(BinaryConsts::I64x2ExtractLane); + break; + case ExtractLaneVecF32x4: + o << U32LEB(BinaryConsts::F32x4ExtractLane); + break; + case ExtractLaneVecF64x2: + o << U32LEB(BinaryConsts::F64x2ExtractLane); + break; } o << uint8_t(curr->index); } @@ -924,15 +1122,28 @@ template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitSIMDReplace(SIMDReplace* curr) { visitChild(curr->vec); visitChild(curr->value); - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::SIMDPrefix); switch (curr->op) { - case ReplaceLaneVecI8x16: o << U32LEB(BinaryConsts::I8x16ReplaceLane); break; - case ReplaceLaneVecI16x8: o << U32LEB(BinaryConsts::I16x8ReplaceLane); break; - case ReplaceLaneVecI32x4: o << U32LEB(BinaryConsts::I32x4ReplaceLane); break; - case ReplaceLaneVecI64x2: o << U32LEB(BinaryConsts::I64x2ReplaceLane); break; - case ReplaceLaneVecF32x4: o << U32LEB(BinaryConsts::F32x4ReplaceLane); break; - case ReplaceLaneVecF64x2: o << U32LEB(BinaryConsts::F64x2ReplaceLane); break; + case ReplaceLaneVecI8x16: + o << U32LEB(BinaryConsts::I8x16ReplaceLane); + break; + case ReplaceLaneVecI16x8: + o << U32LEB(BinaryConsts::I16x8ReplaceLane); + break; + case ReplaceLaneVecI32x4: + o << U32LEB(BinaryConsts::I32x4ReplaceLane); + break; + case ReplaceLaneVecI64x2: + o << U32LEB(BinaryConsts::I64x2ReplaceLane); + break; + case ReplaceLaneVecF32x4: + o << U32LEB(BinaryConsts::F32x4ReplaceLane); + break; + case ReplaceLaneVecF64x2: + o << U32LEB(BinaryConsts::F64x2ReplaceLane); + break; } assert(curr->index < 16); o << uint8_t(curr->index); @@ -942,7 +1153,8 @@ template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitSIMDShuffle(SIMDShuffle* curr) { visitChild(curr->left); visitChild(curr->right); - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::V8x16Shuffle); for (uint8_t m : curr->mask) { o << m; @@ -954,7 +1166,8 @@ void StackWriter<Mode, Parent>::visitSIMDBitselect(SIMDBitselect* curr) { visitChild(curr->left); visitChild(curr->right); visitChild(curr->cond); - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::V128Bitselect); } @@ -962,21 +1175,46 @@ template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitSIMDShift(SIMDShift* curr) { visitChild(curr->vec); visitChild(curr->shift); - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::SIMDPrefix); switch (curr->op) { - case ShlVecI8x16: o << U32LEB(BinaryConsts::I8x16Shl); break; - case ShrSVecI8x16: o << U32LEB(BinaryConsts::I8x16ShrS); break; - case ShrUVecI8x16: o << U32LEB(BinaryConsts::I8x16ShrU); break; - case ShlVecI16x8: o << U32LEB(BinaryConsts::I16x8Shl); break; - case ShrSVecI16x8: o << U32LEB(BinaryConsts::I16x8ShrS); break; - case ShrUVecI16x8: o << U32LEB(BinaryConsts::I16x8ShrU); break; - case ShlVecI32x4: o << U32LEB(BinaryConsts::I32x4Shl); break; - case ShrSVecI32x4: o << U32LEB(BinaryConsts::I32x4ShrS); break; - case ShrUVecI32x4: o << U32LEB(BinaryConsts::I32x4ShrU); break; - case ShlVecI64x2: o << U32LEB(BinaryConsts::I64x2Shl); break; - case ShrSVecI64x2: o << U32LEB(BinaryConsts::I64x2ShrS); break; - case ShrUVecI64x2: o << U32LEB(BinaryConsts::I64x2ShrU); break; + case ShlVecI8x16: + o << U32LEB(BinaryConsts::I8x16Shl); + break; + case ShrSVecI8x16: + o << U32LEB(BinaryConsts::I8x16ShrS); + break; + case ShrUVecI8x16: + o << U32LEB(BinaryConsts::I8x16ShrU); + break; + case ShlVecI16x8: + o << U32LEB(BinaryConsts::I16x8Shl); + break; + case ShrSVecI16x8: + o << U32LEB(BinaryConsts::I16x8ShrS); + break; + case ShrUVecI16x8: + o << U32LEB(BinaryConsts::I16x8ShrU); + break; + case ShlVecI32x4: + o << U32LEB(BinaryConsts::I32x4Shl); + break; + case ShrSVecI32x4: + o << U32LEB(BinaryConsts::I32x4ShrS); + break; + case ShrUVecI32x4: + o << U32LEB(BinaryConsts::I32x4ShrU); + break; + case ShlVecI64x2: + o << U32LEB(BinaryConsts::I64x2Shl); + break; + case ShrSVecI64x2: + o << U32LEB(BinaryConsts::I64x2ShrS); + break; + case ShrUVecI64x2: + o << U32LEB(BinaryConsts::I64x2ShrU); + break; } } @@ -985,7 +1223,8 @@ void StackWriter<Mode, Parent>::visitMemoryInit(MemoryInit* curr) { visitChild(curr->dest); visitChild(curr->offset); visitChild(curr->size); - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::MiscPrefix); o << U32LEB(BinaryConsts::MemoryInit); o << U32LEB(curr->segment) << int8_t(0); @@ -993,7 +1232,8 @@ void StackWriter<Mode, Parent>::visitMemoryInit(MemoryInit* curr) { template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitDataDrop(DataDrop* curr) { - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::MiscPrefix); o << U32LEB(BinaryConsts::DataDrop); o << U32LEB(curr->segment); @@ -1004,7 +1244,8 @@ void StackWriter<Mode, Parent>::visitMemoryCopy(MemoryCopy* curr) { visitChild(curr->dest); visitChild(curr->source); visitChild(curr->size); - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::MiscPrefix); o << U32LEB(BinaryConsts::MemoryCopy); o << int8_t(0) << int8_t(0); @@ -1015,7 +1256,8 @@ void StackWriter<Mode, Parent>::visitMemoryFill(MemoryFill* curr) { visitChild(curr->dest); visitChild(curr->value); visitChild(curr->size); - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::MiscPrefix); o << U32LEB(BinaryConsts::MemoryFill); o << int8_t(0); @@ -1023,7 +1265,8 @@ void StackWriter<Mode, Parent>::visitMemoryFill(MemoryFill* curr) { template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitConst(Const* curr) { - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; switch (curr->type) { case i32: { o << int8_t(BinaryConsts::I32Const) << S32LEB(curr->value.geti32()); @@ -1062,102 +1305,314 @@ void StackWriter<Mode, Parent>::visitUnary(Unary* curr) { emitExtraUnreachable(); return; } - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; switch (curr->op) { - case ClzInt32: o << int8_t(BinaryConsts::I32Clz); break; - case CtzInt32: o << int8_t(BinaryConsts::I32Ctz); break; - case PopcntInt32: o << int8_t(BinaryConsts::I32Popcnt); break; - case EqZInt32: o << int8_t(BinaryConsts::I32EqZ); break; - case ClzInt64: o << int8_t(BinaryConsts::I64Clz); break; - case CtzInt64: o << int8_t(BinaryConsts::I64Ctz); break; - case PopcntInt64: o << int8_t(BinaryConsts::I64Popcnt); break; - case EqZInt64: o << int8_t(BinaryConsts::I64EqZ); break; - case NegFloat32: o << int8_t(BinaryConsts::F32Neg); break; - case AbsFloat32: o << int8_t(BinaryConsts::F32Abs); break; - case CeilFloat32: o << int8_t(BinaryConsts::F32Ceil); break; - case FloorFloat32: o << int8_t(BinaryConsts::F32Floor); break; - case TruncFloat32: o << int8_t(BinaryConsts::F32Trunc); break; - case NearestFloat32: o << int8_t(BinaryConsts::F32NearestInt); break; - case SqrtFloat32: o << int8_t(BinaryConsts::F32Sqrt); break; - case NegFloat64: o << int8_t(BinaryConsts::F64Neg); break; - case AbsFloat64: o << int8_t(BinaryConsts::F64Abs); break; - case CeilFloat64: o << int8_t(BinaryConsts::F64Ceil); break; - case FloorFloat64: o << int8_t(BinaryConsts::F64Floor); break; - case TruncFloat64: o << int8_t(BinaryConsts::F64Trunc); break; - case NearestFloat64: o << int8_t(BinaryConsts::F64NearestInt); break; - case SqrtFloat64: o << int8_t(BinaryConsts::F64Sqrt); break; - case ExtendSInt32: o << int8_t(BinaryConsts::I64STruncI32); break; - case ExtendUInt32: o << int8_t(BinaryConsts::I64UTruncI32); break; - case WrapInt64: o << int8_t(BinaryConsts::I32ConvertI64); break; - case TruncUFloat32ToInt32: o << int8_t(BinaryConsts::I32UTruncF32); break; - case TruncUFloat32ToInt64: o << int8_t(BinaryConsts::I64UTruncF32); break; - case TruncSFloat32ToInt32: o << int8_t(BinaryConsts::I32STruncF32); break; - case TruncSFloat32ToInt64: o << int8_t(BinaryConsts::I64STruncF32); break; - case TruncUFloat64ToInt32: o << int8_t(BinaryConsts::I32UTruncF64); break; - case TruncUFloat64ToInt64: o << int8_t(BinaryConsts::I64UTruncF64); break; - case TruncSFloat64ToInt32: o << int8_t(BinaryConsts::I32STruncF64); break; - case TruncSFloat64ToInt64: o << int8_t(BinaryConsts::I64STruncF64); break; - case ConvertUInt32ToFloat32: o << int8_t(BinaryConsts::F32UConvertI32); break; - case ConvertUInt32ToFloat64: o << int8_t(BinaryConsts::F64UConvertI32); break; - case ConvertSInt32ToFloat32: o << int8_t(BinaryConsts::F32SConvertI32); break; - case ConvertSInt32ToFloat64: o << int8_t(BinaryConsts::F64SConvertI32); break; - case ConvertUInt64ToFloat32: o << int8_t(BinaryConsts::F32UConvertI64); break; - case ConvertUInt64ToFloat64: o << int8_t(BinaryConsts::F64UConvertI64); break; - case ConvertSInt64ToFloat32: o << int8_t(BinaryConsts::F32SConvertI64); break; - case ConvertSInt64ToFloat64: o << int8_t(BinaryConsts::F64SConvertI64); break; - case DemoteFloat64: o << int8_t(BinaryConsts::F32ConvertF64); break; - case PromoteFloat32: o << int8_t(BinaryConsts::F64ConvertF32); break; - case ReinterpretFloat32: o << int8_t(BinaryConsts::I32ReinterpretF32); break; - case ReinterpretFloat64: o << int8_t(BinaryConsts::I64ReinterpretF64); break; - case ReinterpretInt32: o << int8_t(BinaryConsts::F32ReinterpretI32); break; - case ReinterpretInt64: o << int8_t(BinaryConsts::F64ReinterpretI64); break; - case ExtendS8Int32: o << int8_t(BinaryConsts::I32ExtendS8); break; - case ExtendS16Int32: o << int8_t(BinaryConsts::I32ExtendS16); break; - case ExtendS8Int64: o << int8_t(BinaryConsts::I64ExtendS8); break; - case ExtendS16Int64: o << int8_t(BinaryConsts::I64ExtendS16); break; - case ExtendS32Int64: o << int8_t(BinaryConsts::I64ExtendS32); break; - case TruncSatSFloat32ToInt32: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I32STruncSatF32); break; - case TruncSatUFloat32ToInt32: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I32UTruncSatF32); break; - case TruncSatSFloat64ToInt32: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I32STruncSatF64); break; - case TruncSatUFloat64ToInt32: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I32UTruncSatF64); break; - case TruncSatSFloat32ToInt64: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I64STruncSatF32); break; - case TruncSatUFloat32ToInt64: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I64UTruncSatF32); break; - case TruncSatSFloat64ToInt64: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I64STruncSatF64); break; - case TruncSatUFloat64ToInt64: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I64UTruncSatF64); break; - case SplatVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16Splat); break; - case SplatVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8Splat); break; - case SplatVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4Splat); break; - case SplatVecI64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I64x2Splat); break; - case SplatVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Splat); break; - case SplatVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Splat); break; - case NotVec128: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::V128Not); break; - case NegVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16Neg); break; - case AnyTrueVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16AnyTrue); break; - case AllTrueVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16AllTrue); break; - case NegVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8Neg); break; - case AnyTrueVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8AnyTrue); break; - case AllTrueVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8AllTrue); break; - case NegVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4Neg); break; - case AnyTrueVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4AnyTrue); break; - case AllTrueVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4AllTrue); break; - case NegVecI64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I64x2Neg); break; - case AnyTrueVecI64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I64x2AnyTrue); break; - case AllTrueVecI64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I64x2AllTrue); break; - case AbsVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Abs); break; - case NegVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Neg); break; - case SqrtVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Sqrt); break; - case AbsVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Abs); break; - case NegVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Neg); break; - case SqrtVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Sqrt); break; - case TruncSatSVecF32x4ToVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4TruncSatSF32x4); break; - case TruncSatUVecF32x4ToVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4TruncSatUF32x4); break; - case TruncSatSVecF64x2ToVecI64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I64x2TruncSatSF64x2); break; - case TruncSatUVecF64x2ToVecI64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I64x2TruncSatUF64x2); break; - case ConvertSVecI32x4ToVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4ConvertSI32x4); break; - case ConvertUVecI32x4ToVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4ConvertUI32x4); break; - case ConvertSVecI64x2ToVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2ConvertSI64x2); break; - case ConvertUVecI64x2ToVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2ConvertUI64x2); break; - case InvalidUnary: WASM_UNREACHABLE(); + case ClzInt32: + o << int8_t(BinaryConsts::I32Clz); + break; + case CtzInt32: + o << int8_t(BinaryConsts::I32Ctz); + break; + case PopcntInt32: + o << int8_t(BinaryConsts::I32Popcnt); + break; + case EqZInt32: + o << int8_t(BinaryConsts::I32EqZ); + break; + case ClzInt64: + o << int8_t(BinaryConsts::I64Clz); + break; + case CtzInt64: + o << int8_t(BinaryConsts::I64Ctz); + break; + case PopcntInt64: + o << int8_t(BinaryConsts::I64Popcnt); + break; + case EqZInt64: + o << int8_t(BinaryConsts::I64EqZ); + break; + case NegFloat32: + o << int8_t(BinaryConsts::F32Neg); + break; + case AbsFloat32: + o << int8_t(BinaryConsts::F32Abs); + break; + case CeilFloat32: + o << int8_t(BinaryConsts::F32Ceil); + break; + case FloorFloat32: + o << int8_t(BinaryConsts::F32Floor); + break; + case TruncFloat32: + o << int8_t(BinaryConsts::F32Trunc); + break; + case NearestFloat32: + o << int8_t(BinaryConsts::F32NearestInt); + break; + case SqrtFloat32: + o << int8_t(BinaryConsts::F32Sqrt); + break; + case NegFloat64: + o << int8_t(BinaryConsts::F64Neg); + break; + case AbsFloat64: + o << int8_t(BinaryConsts::F64Abs); + break; + case CeilFloat64: + o << int8_t(BinaryConsts::F64Ceil); + break; + case FloorFloat64: + o << int8_t(BinaryConsts::F64Floor); + break; + case TruncFloat64: + o << int8_t(BinaryConsts::F64Trunc); + break; + case NearestFloat64: + o << int8_t(BinaryConsts::F64NearestInt); + break; + case SqrtFloat64: + o << int8_t(BinaryConsts::F64Sqrt); + break; + case ExtendSInt32: + o << int8_t(BinaryConsts::I64STruncI32); + break; + case ExtendUInt32: + o << int8_t(BinaryConsts::I64UTruncI32); + break; + case WrapInt64: + o << int8_t(BinaryConsts::I32ConvertI64); + break; + case TruncUFloat32ToInt32: + o << int8_t(BinaryConsts::I32UTruncF32); + break; + case TruncUFloat32ToInt64: + o << int8_t(BinaryConsts::I64UTruncF32); + break; + case TruncSFloat32ToInt32: + o << int8_t(BinaryConsts::I32STruncF32); + break; + case TruncSFloat32ToInt64: + o << int8_t(BinaryConsts::I64STruncF32); + break; + case TruncUFloat64ToInt32: + o << int8_t(BinaryConsts::I32UTruncF64); + break; + case TruncUFloat64ToInt64: + o << int8_t(BinaryConsts::I64UTruncF64); + break; + case TruncSFloat64ToInt32: + o << int8_t(BinaryConsts::I32STruncF64); + break; + case TruncSFloat64ToInt64: + o << int8_t(BinaryConsts::I64STruncF64); + break; + case ConvertUInt32ToFloat32: + o << int8_t(BinaryConsts::F32UConvertI32); + break; + case ConvertUInt32ToFloat64: + o << int8_t(BinaryConsts::F64UConvertI32); + break; + case ConvertSInt32ToFloat32: + o << int8_t(BinaryConsts::F32SConvertI32); + break; + case ConvertSInt32ToFloat64: + o << int8_t(BinaryConsts::F64SConvertI32); + break; + case ConvertUInt64ToFloat32: + o << int8_t(BinaryConsts::F32UConvertI64); + break; + case ConvertUInt64ToFloat64: + o << int8_t(BinaryConsts::F64UConvertI64); + break; + case ConvertSInt64ToFloat32: + o << int8_t(BinaryConsts::F32SConvertI64); + break; + case ConvertSInt64ToFloat64: + o << int8_t(BinaryConsts::F64SConvertI64); + break; + case DemoteFloat64: + o << int8_t(BinaryConsts::F32ConvertF64); + break; + case PromoteFloat32: + o << int8_t(BinaryConsts::F64ConvertF32); + break; + case ReinterpretFloat32: + o << int8_t(BinaryConsts::I32ReinterpretF32); + break; + case ReinterpretFloat64: + o << int8_t(BinaryConsts::I64ReinterpretF64); + break; + case ReinterpretInt32: + o << int8_t(BinaryConsts::F32ReinterpretI32); + break; + case ReinterpretInt64: + o << int8_t(BinaryConsts::F64ReinterpretI64); + break; + case ExtendS8Int32: + o << int8_t(BinaryConsts::I32ExtendS8); + break; + case ExtendS16Int32: + o << int8_t(BinaryConsts::I32ExtendS16); + break; + case ExtendS8Int64: + o << int8_t(BinaryConsts::I64ExtendS8); + break; + case ExtendS16Int64: + o << int8_t(BinaryConsts::I64ExtendS16); + break; + case ExtendS32Int64: + o << int8_t(BinaryConsts::I64ExtendS32); + break; + case TruncSatSFloat32ToInt32: + o << int8_t(BinaryConsts::MiscPrefix) + << U32LEB(BinaryConsts::I32STruncSatF32); + break; + case TruncSatUFloat32ToInt32: + o << int8_t(BinaryConsts::MiscPrefix) + << U32LEB(BinaryConsts::I32UTruncSatF32); + break; + case TruncSatSFloat64ToInt32: + o << int8_t(BinaryConsts::MiscPrefix) + << U32LEB(BinaryConsts::I32STruncSatF64); + break; + case TruncSatUFloat64ToInt32: + o << int8_t(BinaryConsts::MiscPrefix) + << U32LEB(BinaryConsts::I32UTruncSatF64); + break; + case TruncSatSFloat32ToInt64: + o << int8_t(BinaryConsts::MiscPrefix) + << U32LEB(BinaryConsts::I64STruncSatF32); + break; + case TruncSatUFloat32ToInt64: + o << int8_t(BinaryConsts::MiscPrefix) + << U32LEB(BinaryConsts::I64UTruncSatF32); + break; + case TruncSatSFloat64ToInt64: + o << int8_t(BinaryConsts::MiscPrefix) + << U32LEB(BinaryConsts::I64STruncSatF64); + break; + case TruncSatUFloat64ToInt64: + o << int8_t(BinaryConsts::MiscPrefix) + << U32LEB(BinaryConsts::I64UTruncSatF64); + break; + case SplatVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16Splat); + break; + case SplatVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8Splat); + break; + case SplatVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4Splat); + break; + case SplatVecI64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I64x2Splat); + break; + case SplatVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Splat); + break; + case SplatVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Splat); + break; + case NotVec128: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::V128Not); + break; + case NegVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16Neg); + break; + case AnyTrueVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I8x16AnyTrue); + break; + case AllTrueVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I8x16AllTrue); + break; + case NegVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8Neg); + break; + case AnyTrueVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I16x8AnyTrue); + break; + case AllTrueVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I16x8AllTrue); + break; + case NegVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4Neg); + break; + case AnyTrueVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I32x4AnyTrue); + break; + case AllTrueVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I32x4AllTrue); + break; + case NegVecI64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I64x2Neg); + break; + case AnyTrueVecI64x2: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I64x2AnyTrue); + break; + case AllTrueVecI64x2: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I64x2AllTrue); + break; + case AbsVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Abs); + break; + case NegVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Neg); + break; + case SqrtVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Sqrt); + break; + case AbsVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Abs); + break; + case NegVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Neg); + break; + case SqrtVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Sqrt); + break; + case TruncSatSVecF32x4ToVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I32x4TruncSatSF32x4); + break; + case TruncSatUVecF32x4ToVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I32x4TruncSatUF32x4); + break; + case TruncSatSVecF64x2ToVecI64x2: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I64x2TruncSatSF64x2); + break; + case TruncSatUVecF64x2ToVecI64x2: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I64x2TruncSatUF64x2); + break; + case ConvertSVecI32x4ToVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::F32x4ConvertSI32x4); + break; + case ConvertUVecI32x4ToVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::F32x4ConvertUI32x4); + break; + case ConvertSVecI64x2ToVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::F64x2ConvertSI64x2); + break; + case ConvertUVecI64x2ToVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::F64x2ConvertUI64x2); + break; + case InvalidUnary: + WASM_UNREACHABLE(); } } @@ -1169,167 +1624,481 @@ void StackWriter<Mode, Parent>::visitBinary(Binary* curr) { emitExtraUnreachable(); return; } - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; switch (curr->op) { - case AddInt32: o << int8_t(BinaryConsts::I32Add); break; - case SubInt32: o << int8_t(BinaryConsts::I32Sub); break; - case MulInt32: o << int8_t(BinaryConsts::I32Mul); break; - case DivSInt32: o << int8_t(BinaryConsts::I32DivS); break; - case DivUInt32: o << int8_t(BinaryConsts::I32DivU); break; - case RemSInt32: o << int8_t(BinaryConsts::I32RemS); break; - case RemUInt32: o << int8_t(BinaryConsts::I32RemU); break; - case AndInt32: o << int8_t(BinaryConsts::I32And); break; - case OrInt32: o << int8_t(BinaryConsts::I32Or); break; - case XorInt32: o << int8_t(BinaryConsts::I32Xor); break; - case ShlInt32: o << int8_t(BinaryConsts::I32Shl); break; - case ShrUInt32: o << int8_t(BinaryConsts::I32ShrU); break; - case ShrSInt32: o << int8_t(BinaryConsts::I32ShrS); break; - case RotLInt32: o << int8_t(BinaryConsts::I32RotL); break; - case RotRInt32: o << int8_t(BinaryConsts::I32RotR); break; - case EqInt32: o << int8_t(BinaryConsts::I32Eq); break; - case NeInt32: o << int8_t(BinaryConsts::I32Ne); break; - case LtSInt32: o << int8_t(BinaryConsts::I32LtS); break; - case LtUInt32: o << int8_t(BinaryConsts::I32LtU); break; - case LeSInt32: o << int8_t(BinaryConsts::I32LeS); break; - case LeUInt32: o << int8_t(BinaryConsts::I32LeU); break; - case GtSInt32: o << int8_t(BinaryConsts::I32GtS); break; - case GtUInt32: o << int8_t(BinaryConsts::I32GtU); break; - case GeSInt32: o << int8_t(BinaryConsts::I32GeS); break; - case GeUInt32: o << int8_t(BinaryConsts::I32GeU); break; - - case AddInt64: o << int8_t(BinaryConsts::I64Add); break; - case SubInt64: o << int8_t(BinaryConsts::I64Sub); break; - case MulInt64: o << int8_t(BinaryConsts::I64Mul); break; - case DivSInt64: o << int8_t(BinaryConsts::I64DivS); break; - case DivUInt64: o << int8_t(BinaryConsts::I64DivU); break; - case RemSInt64: o << int8_t(BinaryConsts::I64RemS); break; - case RemUInt64: o << int8_t(BinaryConsts::I64RemU); break; - case AndInt64: o << int8_t(BinaryConsts::I64And); break; - case OrInt64: o << int8_t(BinaryConsts::I64Or); break; - case XorInt64: o << int8_t(BinaryConsts::I64Xor); break; - case ShlInt64: o << int8_t(BinaryConsts::I64Shl); break; - case ShrUInt64: o << int8_t(BinaryConsts::I64ShrU); break; - case ShrSInt64: o << int8_t(BinaryConsts::I64ShrS); break; - case RotLInt64: o << int8_t(BinaryConsts::I64RotL); break; - case RotRInt64: o << int8_t(BinaryConsts::I64RotR); break; - case EqInt64: o << int8_t(BinaryConsts::I64Eq); break; - case NeInt64: o << int8_t(BinaryConsts::I64Ne); break; - case LtSInt64: o << int8_t(BinaryConsts::I64LtS); break; - case LtUInt64: o << int8_t(BinaryConsts::I64LtU); break; - case LeSInt64: o << int8_t(BinaryConsts::I64LeS); break; - case LeUInt64: o << int8_t(BinaryConsts::I64LeU); break; - case GtSInt64: o << int8_t(BinaryConsts::I64GtS); break; - case GtUInt64: o << int8_t(BinaryConsts::I64GtU); break; - case GeSInt64: o << int8_t(BinaryConsts::I64GeS); break; - case GeUInt64: o << int8_t(BinaryConsts::I64GeU); break; - - case AddFloat32: o << int8_t(BinaryConsts::F32Add); break; - case SubFloat32: o << int8_t(BinaryConsts::F32Sub); break; - case MulFloat32: o << int8_t(BinaryConsts::F32Mul); break; - case DivFloat32: o << int8_t(BinaryConsts::F32Div); break; - case CopySignFloat32: o << int8_t(BinaryConsts::F32CopySign);break; - case MinFloat32: o << int8_t(BinaryConsts::F32Min); break; - case MaxFloat32: o << int8_t(BinaryConsts::F32Max); break; - case EqFloat32: o << int8_t(BinaryConsts::F32Eq); break; - case NeFloat32: o << int8_t(BinaryConsts::F32Ne); break; - case LtFloat32: o << int8_t(BinaryConsts::F32Lt); break; - case LeFloat32: o << int8_t(BinaryConsts::F32Le); break; - case GtFloat32: o << int8_t(BinaryConsts::F32Gt); break; - case GeFloat32: o << int8_t(BinaryConsts::F32Ge); break; - - case AddFloat64: o << int8_t(BinaryConsts::F64Add); break; - case SubFloat64: o << int8_t(BinaryConsts::F64Sub); break; - case MulFloat64: o << int8_t(BinaryConsts::F64Mul); break; - case DivFloat64: o << int8_t(BinaryConsts::F64Div); break; - case CopySignFloat64: o << int8_t(BinaryConsts::F64CopySign);break; - case MinFloat64: o << int8_t(BinaryConsts::F64Min); break; - case MaxFloat64: o << int8_t(BinaryConsts::F64Max); break; - case EqFloat64: o << int8_t(BinaryConsts::F64Eq); break; - case NeFloat64: o << int8_t(BinaryConsts::F64Ne); break; - case LtFloat64: o << int8_t(BinaryConsts::F64Lt); break; - case LeFloat64: o << int8_t(BinaryConsts::F64Le); break; - case GtFloat64: o << int8_t(BinaryConsts::F64Gt); break; - case GeFloat64: o << int8_t(BinaryConsts::F64Ge); break; - - case EqVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16Eq); break; - case NeVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16Ne); break; - case LtSVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16LtS); break; - case LtUVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16LtU); break; - case GtSVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16GtS); break; - case GtUVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16GtU); break; - case LeSVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16LeS); break; - case LeUVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16LeU); break; - case GeSVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16GeS); break; - case GeUVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16GeU); break; - case EqVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8Eq); break; - case NeVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8Ne); break; - case LtSVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8LtS); break; - case LtUVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8LtU); break; - case GtSVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8GtS); break; - case GtUVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8GtU); break; - case LeSVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8LeS); break; - case LeUVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8LeU); break; - case GeSVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8GeS); break; - case GeUVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8GeU); break; - case EqVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4Eq); break; - case NeVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4Ne); break; - case LtSVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4LtS); break; - case LtUVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4LtU); break; - case GtSVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4GtS); break; - case GtUVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4GtU); break; - case LeSVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4LeS); break; - case LeUVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4LeU); break; - case GeSVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4GeS); break; - case GeUVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4GeU); break; - case EqVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Eq); break; - case NeVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Ne); break; - case LtVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Lt); break; - case GtVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Gt); break; - case LeVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Le); break; - case GeVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Ge); break; - case EqVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Eq); break; - case NeVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Ne); break; - case LtVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Lt); break; - case GtVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Gt); break; - case LeVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Le); break; - case GeVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Ge); break; - case AndVec128: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::V128And); break; - case OrVec128: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::V128Or); break; - case XorVec128: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::V128Xor); break; - - case AddVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16Add); break; - case AddSatSVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16AddSatS); break; - case AddSatUVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16AddSatU); break; - case SubVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16Sub); break; - case SubSatSVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16SubSatS); break; - case SubSatUVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16SubSatU); break; - case MulVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16Mul); break; - case AddVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8Add); break; - case AddSatSVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8AddSatS); break; - case AddSatUVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8AddSatU); break; - case SubVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8Sub); break; - case SubSatSVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8SubSatS); break; - case SubSatUVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8SubSatU); break; - case MulVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8Mul); break; - case AddVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4Add); break; - case SubVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4Sub); break; - case MulVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4Mul); break; - case AddVecI64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I64x2Add); break; - case SubVecI64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I64x2Sub); break; - - case AddVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Add); break; - case SubVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Sub); break; - case MulVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Mul); break; - case DivVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Div); break; - case MinVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Min); break; - case MaxVecF32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Max); break; - case AddVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Add); break; - case SubVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Sub); break; - case MulVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Mul); break; - case DivVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Div); break; - case MinVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Min); break; - case MaxVecF64x2: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Max); break; - case InvalidBinary: WASM_UNREACHABLE(); + case AddInt32: + o << int8_t(BinaryConsts::I32Add); + break; + case SubInt32: + o << int8_t(BinaryConsts::I32Sub); + break; + case MulInt32: + o << int8_t(BinaryConsts::I32Mul); + break; + case DivSInt32: + o << int8_t(BinaryConsts::I32DivS); + break; + case DivUInt32: + o << int8_t(BinaryConsts::I32DivU); + break; + case RemSInt32: + o << int8_t(BinaryConsts::I32RemS); + break; + case RemUInt32: + o << int8_t(BinaryConsts::I32RemU); + break; + case AndInt32: + o << int8_t(BinaryConsts::I32And); + break; + case OrInt32: + o << int8_t(BinaryConsts::I32Or); + break; + case XorInt32: + o << int8_t(BinaryConsts::I32Xor); + break; + case ShlInt32: + o << int8_t(BinaryConsts::I32Shl); + break; + case ShrUInt32: + o << int8_t(BinaryConsts::I32ShrU); + break; + case ShrSInt32: + o << int8_t(BinaryConsts::I32ShrS); + break; + case RotLInt32: + o << int8_t(BinaryConsts::I32RotL); + break; + case RotRInt32: + o << int8_t(BinaryConsts::I32RotR); + break; + case EqInt32: + o << int8_t(BinaryConsts::I32Eq); + break; + case NeInt32: + o << int8_t(BinaryConsts::I32Ne); + break; + case LtSInt32: + o << int8_t(BinaryConsts::I32LtS); + break; + case LtUInt32: + o << int8_t(BinaryConsts::I32LtU); + break; + case LeSInt32: + o << int8_t(BinaryConsts::I32LeS); + break; + case LeUInt32: + o << int8_t(BinaryConsts::I32LeU); + break; + case GtSInt32: + o << int8_t(BinaryConsts::I32GtS); + break; + case GtUInt32: + o << int8_t(BinaryConsts::I32GtU); + break; + case GeSInt32: + o << int8_t(BinaryConsts::I32GeS); + break; + case GeUInt32: + o << int8_t(BinaryConsts::I32GeU); + break; + + case AddInt64: + o << int8_t(BinaryConsts::I64Add); + break; + case SubInt64: + o << int8_t(BinaryConsts::I64Sub); + break; + case MulInt64: + o << int8_t(BinaryConsts::I64Mul); + break; + case DivSInt64: + o << int8_t(BinaryConsts::I64DivS); + break; + case DivUInt64: + o << int8_t(BinaryConsts::I64DivU); + break; + case RemSInt64: + o << int8_t(BinaryConsts::I64RemS); + break; + case RemUInt64: + o << int8_t(BinaryConsts::I64RemU); + break; + case AndInt64: + o << int8_t(BinaryConsts::I64And); + break; + case OrInt64: + o << int8_t(BinaryConsts::I64Or); + break; + case XorInt64: + o << int8_t(BinaryConsts::I64Xor); + break; + case ShlInt64: + o << int8_t(BinaryConsts::I64Shl); + break; + case ShrUInt64: + o << int8_t(BinaryConsts::I64ShrU); + break; + case ShrSInt64: + o << int8_t(BinaryConsts::I64ShrS); + break; + case RotLInt64: + o << int8_t(BinaryConsts::I64RotL); + break; + case RotRInt64: + o << int8_t(BinaryConsts::I64RotR); + break; + case EqInt64: + o << int8_t(BinaryConsts::I64Eq); + break; + case NeInt64: + o << int8_t(BinaryConsts::I64Ne); + break; + case LtSInt64: + o << int8_t(BinaryConsts::I64LtS); + break; + case LtUInt64: + o << int8_t(BinaryConsts::I64LtU); + break; + case LeSInt64: + o << int8_t(BinaryConsts::I64LeS); + break; + case LeUInt64: + o << int8_t(BinaryConsts::I64LeU); + break; + case GtSInt64: + o << int8_t(BinaryConsts::I64GtS); + break; + case GtUInt64: + o << int8_t(BinaryConsts::I64GtU); + break; + case GeSInt64: + o << int8_t(BinaryConsts::I64GeS); + break; + case GeUInt64: + o << int8_t(BinaryConsts::I64GeU); + break; + + case AddFloat32: + o << int8_t(BinaryConsts::F32Add); + break; + case SubFloat32: + o << int8_t(BinaryConsts::F32Sub); + break; + case MulFloat32: + o << int8_t(BinaryConsts::F32Mul); + break; + case DivFloat32: + o << int8_t(BinaryConsts::F32Div); + break; + case CopySignFloat32: + o << int8_t(BinaryConsts::F32CopySign); + break; + case MinFloat32: + o << int8_t(BinaryConsts::F32Min); + break; + case MaxFloat32: + o << int8_t(BinaryConsts::F32Max); + break; + case EqFloat32: + o << int8_t(BinaryConsts::F32Eq); + break; + case NeFloat32: + o << int8_t(BinaryConsts::F32Ne); + break; + case LtFloat32: + o << int8_t(BinaryConsts::F32Lt); + break; + case LeFloat32: + o << int8_t(BinaryConsts::F32Le); + break; + case GtFloat32: + o << int8_t(BinaryConsts::F32Gt); + break; + case GeFloat32: + o << int8_t(BinaryConsts::F32Ge); + break; + + case AddFloat64: + o << int8_t(BinaryConsts::F64Add); + break; + case SubFloat64: + o << int8_t(BinaryConsts::F64Sub); + break; + case MulFloat64: + o << int8_t(BinaryConsts::F64Mul); + break; + case DivFloat64: + o << int8_t(BinaryConsts::F64Div); + break; + case CopySignFloat64: + o << int8_t(BinaryConsts::F64CopySign); + break; + case MinFloat64: + o << int8_t(BinaryConsts::F64Min); + break; + case MaxFloat64: + o << int8_t(BinaryConsts::F64Max); + break; + case EqFloat64: + o << int8_t(BinaryConsts::F64Eq); + break; + case NeFloat64: + o << int8_t(BinaryConsts::F64Ne); + break; + case LtFloat64: + o << int8_t(BinaryConsts::F64Lt); + break; + case LeFloat64: + o << int8_t(BinaryConsts::F64Le); + break; + case GtFloat64: + o << int8_t(BinaryConsts::F64Gt); + break; + case GeFloat64: + o << int8_t(BinaryConsts::F64Ge); + break; + + case EqVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16Eq); + break; + case NeVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16Ne); + break; + case LtSVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16LtS); + break; + case LtUVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16LtU); + break; + case GtSVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16GtS); + break; + case GtUVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16GtU); + break; + case LeSVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16LeS); + break; + case LeUVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16LeU); + break; + case GeSVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16GeS); + break; + case GeUVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16GeU); + break; + case EqVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8Eq); + break; + case NeVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8Ne); + break; + case LtSVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8LtS); + break; + case LtUVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8LtU); + break; + case GtSVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8GtS); + break; + case GtUVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8GtU); + break; + case LeSVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8LeS); + break; + case LeUVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8LeU); + break; + case GeSVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8GeS); + break; + case GeUVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8GeU); + break; + case EqVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4Eq); + break; + case NeVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4Ne); + break; + case LtSVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4LtS); + break; + case LtUVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4LtU); + break; + case GtSVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4GtS); + break; + case GtUVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4GtU); + break; + case LeSVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4LeS); + break; + case LeUVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4LeU); + break; + case GeSVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4GeS); + break; + case GeUVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4GeU); + break; + case EqVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Eq); + break; + case NeVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Ne); + break; + case LtVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Lt); + break; + case GtVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Gt); + break; + case LeVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Le); + break; + case GeVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Ge); + break; + case EqVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Eq); + break; + case NeVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Ne); + break; + case LtVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Lt); + break; + case GtVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Gt); + break; + case LeVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Le); + break; + case GeVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Ge); + break; + case AndVec128: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::V128And); + break; + case OrVec128: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::V128Or); + break; + case XorVec128: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::V128Xor); + break; + + case AddVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16Add); + break; + case AddSatSVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I8x16AddSatS); + break; + case AddSatUVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I8x16AddSatU); + break; + case SubVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16Sub); + break; + case SubSatSVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I8x16SubSatS); + break; + case SubSatUVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I8x16SubSatU); + break; + case MulVecI8x16: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16Mul); + break; + case AddVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8Add); + break; + case AddSatSVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I16x8AddSatS); + break; + case AddSatUVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I16x8AddSatU); + break; + case SubVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8Sub); + break; + case SubSatSVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I16x8SubSatS); + break; + case SubSatUVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I16x8SubSatU); + break; + case MulVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8Mul); + break; + case AddVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4Add); + break; + case SubVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4Sub); + break; + case MulVecI32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4Mul); + break; + case AddVecI64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I64x2Add); + break; + case SubVecI64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I64x2Sub); + break; + + case AddVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Add); + break; + case SubVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Sub); + break; + case MulVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Mul); + break; + case DivVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Div); + break; + case MinVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Min); + break; + case MaxVecF32x4: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F32x4Max); + break; + case AddVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Add); + break; + case SubVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Sub); + break; + case MulVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Mul); + break; + case DivVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Div); + break; + case MinVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Min); + break; + case MaxVecF64x2: + o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::F64x2Max); + break; + case InvalidBinary: + WASM_UNREACHABLE(); } } @@ -1342,7 +2111,8 @@ void StackWriter<Mode, Parent>::visitSelect(Select* curr) { emitExtraUnreachable(); return; } - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::Select); } @@ -1351,7 +2121,8 @@ void StackWriter<Mode, Parent>::visitReturn(Return* curr) { if (curr->value) { visitChild(curr->value); } - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::Return); } @@ -1367,7 +2138,8 @@ void StackWriter<Mode, Parent>::visitHost(Host* curr) { break; } } - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; switch (curr->op) { case CurrentMemory: { o << int8_t(BinaryConsts::CurrentMemory); @@ -1383,20 +2155,23 @@ void StackWriter<Mode, Parent>::visitHost(Host* curr) { template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitNop(Nop* curr) { - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::Nop); } template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitUnreachable(Unreachable* curr) { - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::Unreachable); } template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitDrop(Drop* curr) { visitChild(curr->value); - if (justAddToStack(curr)) return; + if (justAddToStack(curr)) + return; o << int8_t(BinaryConsts::Drop); } @@ -1411,7 +2186,9 @@ int32_t StackWriter<Mode, Parent>::getBreakIndex(Name name) { // -1 if not found } template<StackWriterMode Mode, typename Parent> -void StackWriter<Mode, Parent>::emitMemoryAccess(size_t alignment, size_t bytes, uint32_t offset) { +void StackWriter<Mode, Parent>::emitMemoryAccess(size_t alignment, + size_t bytes, + uint32_t offset) { o << U32LEB(Log2(alignment ? alignment : bytes)); o << U32LEB(offset); } @@ -1443,18 +2220,19 @@ void StackWriter<Mode, Parent>::finishFunctionBody() { } template<StackWriterMode Mode, typename Parent> -StackInst* StackWriter<Mode, Parent>::makeStackInst(StackInst::Op op, Expression* origin) { +StackInst* StackWriter<Mode, Parent>::makeStackInst(StackInst::Op op, + Expression* origin) { auto* ret = allocator.alloc<StackInst>(); ret->op = op; ret->origin = origin; auto stackType = origin->type; if (origin->is<Block>() || origin->is<Loop>() || origin->is<If>()) { if (stackType == unreachable) { - // There are no unreachable blocks, loops, or ifs. we emit extra unreachables - // to fix that up, so that they are valid as having none type. + // There are no unreachable blocks, loops, or ifs. we emit extra + // unreachables to fix that up, so that they are valid as having none + // type. stackType = none; - } else if (op != StackInst::BlockEnd && - op != StackInst::IfEnd && + } else if (op != StackInst::BlockEnd && op != StackInst::IfEnd && op != StackInst::LoopEnd) { // If a concrete type is returned, we mark the end of the construct has // having that type (as it is pushed to the value stack at that point), diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index 38fcdb990..2b495e025 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -27,16 +27,15 @@ #ifndef wasm_wasm_traversal_h #define wasm_wasm_traversal_h -#include "wasm.h" #include "support/small_vector.h" #include "support/threads.h" +#include "wasm.h" namespace wasm { // A generic visitor, defaulting to doing nothing on each visit -template<typename SubType, typename ReturnType = void> -struct Visitor { +template<typename SubType, typename ReturnType = void> struct Visitor { // Expression visitors ReturnType visitBlock(Block* curr) { return ReturnType(); } ReturnType visitIf(If* curr) { return ReturnType(); } @@ -85,51 +84,87 @@ struct Visitor { ReturnType visit(Expression* curr) { assert(curr); - #define DELEGATE(CLASS_TO_VISIT) \ - return static_cast<SubType*>(this)-> \ - visit##CLASS_TO_VISIT(static_cast<CLASS_TO_VISIT*>(curr)) +#define DELEGATE(CLASS_TO_VISIT) \ + return static_cast<SubType*>(this)->visit##CLASS_TO_VISIT( \ + static_cast<CLASS_TO_VISIT*>(curr)) switch (curr->_id) { - case Expression::Id::BlockId: DELEGATE(Block); - case Expression::Id::IfId: DELEGATE(If); - case Expression::Id::LoopId: DELEGATE(Loop); - case Expression::Id::BreakId: DELEGATE(Break); - case Expression::Id::SwitchId: DELEGATE(Switch); - case Expression::Id::CallId: DELEGATE(Call); - case Expression::Id::CallIndirectId: DELEGATE(CallIndirect); - case Expression::Id::GetLocalId: DELEGATE(GetLocal); - case Expression::Id::SetLocalId: DELEGATE(SetLocal); - case Expression::Id::GetGlobalId: DELEGATE(GetGlobal); - case Expression::Id::SetGlobalId: DELEGATE(SetGlobal); - case Expression::Id::LoadId: DELEGATE(Load); - case Expression::Id::StoreId: DELEGATE(Store); - case Expression::Id::AtomicRMWId: DELEGATE(AtomicRMW); - case Expression::Id::AtomicCmpxchgId: DELEGATE(AtomicCmpxchg); - case Expression::Id::AtomicWaitId: DELEGATE(AtomicWait); - case Expression::Id::AtomicNotifyId: DELEGATE(AtomicNotify); - case Expression::Id::SIMDExtractId: DELEGATE(SIMDExtract); - case Expression::Id::SIMDReplaceId: DELEGATE(SIMDReplace); - case Expression::Id::SIMDShuffleId: DELEGATE(SIMDShuffle); - case Expression::Id::SIMDBitselectId: DELEGATE(SIMDBitselect); - case Expression::Id::SIMDShiftId: DELEGATE(SIMDShift); - case Expression::Id::MemoryInitId: DELEGATE(MemoryInit); - case Expression::Id::DataDropId: DELEGATE(DataDrop); - case Expression::Id::MemoryCopyId: DELEGATE(MemoryCopy); - case Expression::Id::MemoryFillId: DELEGATE(MemoryFill); - case Expression::Id::ConstId: DELEGATE(Const); - case Expression::Id::UnaryId: DELEGATE(Unary); - case Expression::Id::BinaryId: DELEGATE(Binary); - case Expression::Id::SelectId: DELEGATE(Select); - case Expression::Id::DropId: DELEGATE(Drop); - case Expression::Id::ReturnId: DELEGATE(Return); - case Expression::Id::HostId: DELEGATE(Host); - case Expression::Id::NopId: DELEGATE(Nop); - case Expression::Id::UnreachableId: DELEGATE(Unreachable); + case Expression::Id::BlockId: + DELEGATE(Block); + case Expression::Id::IfId: + DELEGATE(If); + case Expression::Id::LoopId: + DELEGATE(Loop); + case Expression::Id::BreakId: + DELEGATE(Break); + case Expression::Id::SwitchId: + DELEGATE(Switch); + case Expression::Id::CallId: + DELEGATE(Call); + case Expression::Id::CallIndirectId: + DELEGATE(CallIndirect); + case Expression::Id::GetLocalId: + DELEGATE(GetLocal); + case Expression::Id::SetLocalId: + DELEGATE(SetLocal); + case Expression::Id::GetGlobalId: + DELEGATE(GetGlobal); + case Expression::Id::SetGlobalId: + DELEGATE(SetGlobal); + case Expression::Id::LoadId: + DELEGATE(Load); + case Expression::Id::StoreId: + DELEGATE(Store); + case Expression::Id::AtomicRMWId: + DELEGATE(AtomicRMW); + case Expression::Id::AtomicCmpxchgId: + DELEGATE(AtomicCmpxchg); + case Expression::Id::AtomicWaitId: + DELEGATE(AtomicWait); + case Expression::Id::AtomicNotifyId: + DELEGATE(AtomicNotify); + case Expression::Id::SIMDExtractId: + DELEGATE(SIMDExtract); + case Expression::Id::SIMDReplaceId: + DELEGATE(SIMDReplace); + case Expression::Id::SIMDShuffleId: + DELEGATE(SIMDShuffle); + case Expression::Id::SIMDBitselectId: + DELEGATE(SIMDBitselect); + case Expression::Id::SIMDShiftId: + DELEGATE(SIMDShift); + case Expression::Id::MemoryInitId: + DELEGATE(MemoryInit); + case Expression::Id::DataDropId: + DELEGATE(DataDrop); + case Expression::Id::MemoryCopyId: + DELEGATE(MemoryCopy); + case Expression::Id::MemoryFillId: + DELEGATE(MemoryFill); + case Expression::Id::ConstId: + DELEGATE(Const); + case Expression::Id::UnaryId: + DELEGATE(Unary); + case Expression::Id::BinaryId: + DELEGATE(Binary); + case Expression::Id::SelectId: + DELEGATE(Select); + case Expression::Id::DropId: + DELEGATE(Drop); + case Expression::Id::ReturnId: + DELEGATE(Return); + case Expression::Id::HostId: + DELEGATE(Host); + case Expression::Id::NopId: + DELEGATE(Nop); + case Expression::Id::UnreachableId: + DELEGATE(Unreachable); case Expression::Id::InvalidId: - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } - #undef DELEGATE +#undef DELEGATE } }; @@ -137,12 +172,15 @@ struct Visitor { template<typename SubType, typename ReturnType = void> struct OverriddenVisitor { - // Expression visitors, which must be overridden - #define UNIMPLEMENTED(CLASS_TO_VISIT) \ - ReturnType visit##CLASS_TO_VISIT(CLASS_TO_VISIT* curr) { \ - static_assert(&SubType::visit##CLASS_TO_VISIT != &OverriddenVisitor<SubType, ReturnType>::visit##CLASS_TO_VISIT, "Derived class must implement visit" #CLASS_TO_VISIT); \ - WASM_UNREACHABLE(); \ - } +// Expression visitors, which must be overridden +#define UNIMPLEMENTED(CLASS_TO_VISIT) \ + ReturnType visit##CLASS_TO_VISIT(CLASS_TO_VISIT* curr) { \ + static_assert( \ + &SubType::visit##CLASS_TO_VISIT != \ + &OverriddenVisitor<SubType, ReturnType>::visit##CLASS_TO_VISIT, \ + "Derived class must implement visit" #CLASS_TO_VISIT); \ + WASM_UNREACHABLE(); \ + } UNIMPLEMENTED(Block); UNIMPLEMENTED(If); @@ -187,56 +225,92 @@ struct OverriddenVisitor { UNIMPLEMENTED(Memory); UNIMPLEMENTED(Module); - #undef UNIMPLEMENTED +#undef UNIMPLEMENTED ReturnType visit(Expression* curr) { assert(curr); - #define DELEGATE(CLASS_TO_VISIT) \ - return static_cast<SubType*>(this)-> \ - visit##CLASS_TO_VISIT(static_cast<CLASS_TO_VISIT*>(curr)) +#define DELEGATE(CLASS_TO_VISIT) \ + return static_cast<SubType*>(this)->visit##CLASS_TO_VISIT( \ + static_cast<CLASS_TO_VISIT*>(curr)) switch (curr->_id) { - case Expression::Id::BlockId: DELEGATE(Block); - case Expression::Id::IfId: DELEGATE(If); - case Expression::Id::LoopId: DELEGATE(Loop); - case Expression::Id::BreakId: DELEGATE(Break); - case Expression::Id::SwitchId: DELEGATE(Switch); - case Expression::Id::CallId: DELEGATE(Call); - case Expression::Id::CallIndirectId: DELEGATE(CallIndirect); - case Expression::Id::GetLocalId: DELEGATE(GetLocal); - case Expression::Id::SetLocalId: DELEGATE(SetLocal); - case Expression::Id::GetGlobalId: DELEGATE(GetGlobal); - case Expression::Id::SetGlobalId: DELEGATE(SetGlobal); - case Expression::Id::LoadId: DELEGATE(Load); - case Expression::Id::StoreId: DELEGATE(Store); - case Expression::Id::AtomicRMWId: DELEGATE(AtomicRMW); - case Expression::Id::AtomicCmpxchgId: DELEGATE(AtomicCmpxchg); - case Expression::Id::AtomicWaitId: DELEGATE(AtomicWait); - case Expression::Id::AtomicNotifyId: DELEGATE(AtomicNotify); - case Expression::Id::SIMDExtractId: DELEGATE(SIMDExtract); - case Expression::Id::SIMDReplaceId: DELEGATE(SIMDReplace); - case Expression::Id::SIMDShuffleId: DELEGATE(SIMDShuffle); - case Expression::Id::SIMDBitselectId: DELEGATE(SIMDBitselect); - case Expression::Id::SIMDShiftId: DELEGATE(SIMDShift); - case Expression::Id::MemoryInitId: DELEGATE(MemoryInit); - case Expression::Id::DataDropId: DELEGATE(DataDrop); - case Expression::Id::MemoryCopyId: DELEGATE(MemoryCopy); - case Expression::Id::MemoryFillId: DELEGATE(MemoryFill); - case Expression::Id::ConstId: DELEGATE(Const); - case Expression::Id::UnaryId: DELEGATE(Unary); - case Expression::Id::BinaryId: DELEGATE(Binary); - case Expression::Id::SelectId: DELEGATE(Select); - case Expression::Id::DropId: DELEGATE(Drop); - case Expression::Id::ReturnId: DELEGATE(Return); - case Expression::Id::HostId: DELEGATE(Host); - case Expression::Id::NopId: DELEGATE(Nop); - case Expression::Id::UnreachableId: DELEGATE(Unreachable); + case Expression::Id::BlockId: + DELEGATE(Block); + case Expression::Id::IfId: + DELEGATE(If); + case Expression::Id::LoopId: + DELEGATE(Loop); + case Expression::Id::BreakId: + DELEGATE(Break); + case Expression::Id::SwitchId: + DELEGATE(Switch); + case Expression::Id::CallId: + DELEGATE(Call); + case Expression::Id::CallIndirectId: + DELEGATE(CallIndirect); + case Expression::Id::GetLocalId: + DELEGATE(GetLocal); + case Expression::Id::SetLocalId: + DELEGATE(SetLocal); + case Expression::Id::GetGlobalId: + DELEGATE(GetGlobal); + case Expression::Id::SetGlobalId: + DELEGATE(SetGlobal); + case Expression::Id::LoadId: + DELEGATE(Load); + case Expression::Id::StoreId: + DELEGATE(Store); + case Expression::Id::AtomicRMWId: + DELEGATE(AtomicRMW); + case Expression::Id::AtomicCmpxchgId: + DELEGATE(AtomicCmpxchg); + case Expression::Id::AtomicWaitId: + DELEGATE(AtomicWait); + case Expression::Id::AtomicNotifyId: + DELEGATE(AtomicNotify); + case Expression::Id::SIMDExtractId: + DELEGATE(SIMDExtract); + case Expression::Id::SIMDReplaceId: + DELEGATE(SIMDReplace); + case Expression::Id::SIMDShuffleId: + DELEGATE(SIMDShuffle); + case Expression::Id::SIMDBitselectId: + DELEGATE(SIMDBitselect); + case Expression::Id::SIMDShiftId: + DELEGATE(SIMDShift); + case Expression::Id::MemoryInitId: + DELEGATE(MemoryInit); + case Expression::Id::DataDropId: + DELEGATE(DataDrop); + case Expression::Id::MemoryCopyId: + DELEGATE(MemoryCopy); + case Expression::Id::MemoryFillId: + DELEGATE(MemoryFill); + case Expression::Id::ConstId: + DELEGATE(Const); + case Expression::Id::UnaryId: + DELEGATE(Unary); + case Expression::Id::BinaryId: + DELEGATE(Binary); + case Expression::Id::SelectId: + DELEGATE(Select); + case Expression::Id::DropId: + DELEGATE(Drop); + case Expression::Id::ReturnId: + DELEGATE(Return); + case Expression::Id::HostId: + DELEGATE(Host); + case Expression::Id::NopId: + DELEGATE(Nop); + case Expression::Id::UnreachableId: + DELEGATE(Unreachable); case Expression::Id::InvalidId: - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } - #undef DELEGATE +#undef DELEGATE } }; @@ -249,41 +323,111 @@ struct UnifiedExpressionVisitor : public Visitor<SubType, ReturnType> { ReturnType visitExpression(Expression* curr) { return ReturnType(); } // redirects - ReturnType visitBlock(Block* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitIf(If* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitLoop(Loop* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitBreak(Break* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitSwitch(Switch* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitCall(Call* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitCallIndirect(CallIndirect* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitGetLocal(GetLocal* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitSetLocal(SetLocal* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitGetGlobal(GetGlobal* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitSetGlobal(SetGlobal* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitLoad(Load* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitStore(Store* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitAtomicRMW(AtomicRMW* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitAtomicCmpxchg(AtomicCmpxchg* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitAtomicWait(AtomicWait* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitAtomicNotify(AtomicNotify* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitSIMDExtract(SIMDExtract* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitSIMDReplace(SIMDReplace* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitSIMDShuffle(SIMDShuffle* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitSIMDBitselect(SIMDBitselect* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitSIMDShift(SIMDShift* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitMemoryInit(MemoryInit* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitDataDrop(DataDrop* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitMemoryCopy(MemoryCopy* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitMemoryFill(MemoryFill* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitConst(Const* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitUnary(Unary* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitBinary(Binary* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitSelect(Select* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitDrop(Drop* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitReturn(Return* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitHost(Host* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitNop(Nop* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } - ReturnType visitUnreachable(Unreachable* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } + ReturnType visitBlock(Block* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitIf(If* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitLoop(Loop* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitBreak(Break* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitSwitch(Switch* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitCall(Call* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitCallIndirect(CallIndirect* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitGetLocal(GetLocal* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitSetLocal(SetLocal* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitGetGlobal(GetGlobal* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitSetGlobal(SetGlobal* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitLoad(Load* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitStore(Store* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitAtomicRMW(AtomicRMW* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitAtomicCmpxchg(AtomicCmpxchg* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitAtomicWait(AtomicWait* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitAtomicNotify(AtomicNotify* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitSIMDExtract(SIMDExtract* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitSIMDReplace(SIMDReplace* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitSIMDShuffle(SIMDShuffle* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitSIMDBitselect(SIMDBitselect* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitSIMDShift(SIMDShift* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitMemoryInit(MemoryInit* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitDataDrop(DataDrop* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitMemoryCopy(MemoryCopy* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitMemoryFill(MemoryFill* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitConst(Const* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitUnary(Unary* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitBinary(Binary* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitSelect(Select* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitDrop(Drop* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitReturn(Return* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitHost(Host* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitNop(Nop* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } + ReturnType visitUnreachable(Unreachable* curr) { + return static_cast<SubType*>(this)->visitExpression(curr); + } }; // @@ -318,23 +462,15 @@ struct Walker : public VisitorType { return *replacep = expression; } - Expression* getCurrent() { - return *replacep; - } + Expression* getCurrent() { return *replacep; } - Expression** getCurrentPointer() { - return replacep; - } + Expression** getCurrentPointer() { return replacep; } // Get the current module - Module* getModule() { - return currModule; - } + Module* getModule() { return currModule; } // Get the current function - Function* getFunction() { - return currFunction; - } + Function* getFunction() { return currFunction; } // Walk starting @@ -360,9 +496,7 @@ struct Walker : public VisitorType { } // override this to provide custom functionality - void doWalkFunction(Function* func) { - walk(func->body); - } + void doWalkFunction(Function* func) { walk(func->body); } void walkTable(Table* table) { for (auto& segment : table->segments) { @@ -459,55 +593,122 @@ struct Walker : public VisitorType { // task hooks to call visitors - static void doVisitBlock(SubType* self, Expression** currp) { self->visitBlock((*currp)->cast<Block>()); } - static void doVisitIf(SubType* self, Expression** currp) { self->visitIf((*currp)->cast<If>()); } - static void doVisitLoop(SubType* self, Expression** currp) { self->visitLoop((*currp)->cast<Loop>()); } - static void doVisitBreak(SubType* self, Expression** currp) { self->visitBreak((*currp)->cast<Break>()); } - static void doVisitSwitch(SubType* self, Expression** currp) { self->visitSwitch((*currp)->cast<Switch>()); } - static void doVisitCall(SubType* self, Expression** currp) { self->visitCall((*currp)->cast<Call>()); } - static void doVisitCallIndirect(SubType* self, Expression** currp) { self->visitCallIndirect((*currp)->cast<CallIndirect>()); } - static void doVisitGetLocal(SubType* self, Expression** currp) { self->visitGetLocal((*currp)->cast<GetLocal>()); } - static void doVisitSetLocal(SubType* self, Expression** currp) { self->visitSetLocal((*currp)->cast<SetLocal>()); } - static void doVisitGetGlobal(SubType* self, Expression** currp) { self->visitGetGlobal((*currp)->cast<GetGlobal>()); } - static void doVisitSetGlobal(SubType* self, Expression** currp) { self->visitSetGlobal((*currp)->cast<SetGlobal>()); } - static void doVisitLoad(SubType* self, Expression** currp) { self->visitLoad((*currp)->cast<Load>()); } - static void doVisitStore(SubType* self, Expression** currp) { self->visitStore((*currp)->cast<Store>()); } - static void doVisitAtomicRMW(SubType* self, Expression** currp) { self->visitAtomicRMW((*currp)->cast<AtomicRMW>()); } - static void doVisitAtomicCmpxchg(SubType* self, Expression** currp){ self->visitAtomicCmpxchg((*currp)->cast<AtomicCmpxchg>()); } - static void doVisitAtomicWait(SubType* self, Expression** currp) { self->visitAtomicWait((*currp)->cast<AtomicWait>()); } - static void doVisitAtomicNotify(SubType* self, Expression** currp) { self->visitAtomicNotify((*currp)->cast<AtomicNotify>()); } - static void doVisitSIMDExtract(SubType* self, Expression** currp) { self->visitSIMDExtract((*currp)->cast<SIMDExtract>()); } - static void doVisitSIMDReplace(SubType* self, Expression** currp) { self->visitSIMDReplace((*currp)->cast<SIMDReplace>()); } - static void doVisitSIMDShuffle(SubType* self, Expression** currp) { self->visitSIMDShuffle((*currp)->cast<SIMDShuffle>()); } - static void doVisitSIMDBitselect(SubType* self, Expression** currp) { self->visitSIMDBitselect((*currp)->cast<SIMDBitselect>()); } - static void doVisitSIMDShift(SubType* self, Expression** currp) { self->visitSIMDShift((*currp)->cast<SIMDShift>()); } - static void doVisitMemoryInit(SubType* self, Expression** currp) { self->visitMemoryInit((*currp)->cast<MemoryInit>()); } - static void doVisitDataDrop(SubType* self, Expression** currp) { self->visitDataDrop((*currp)->cast<DataDrop>()); } - static void doVisitMemoryCopy(SubType* self, Expression** currp) { self->visitMemoryCopy((*currp)->cast<MemoryCopy>()); } - static void doVisitMemoryFill(SubType* self, Expression** currp) { self->visitMemoryFill((*currp)->cast<MemoryFill>()); } - static void doVisitConst(SubType* self, Expression** currp) { self->visitConst((*currp)->cast<Const>()); } - static void doVisitUnary(SubType* self, Expression** currp) { self->visitUnary((*currp)->cast<Unary>()); } - static void doVisitBinary(SubType* self, Expression** currp) { self->visitBinary((*currp)->cast<Binary>()); } - static void doVisitSelect(SubType* self, Expression** currp) { self->visitSelect((*currp)->cast<Select>()); } - static void doVisitDrop(SubType* self, Expression** currp) { self->visitDrop((*currp)->cast<Drop>()); } - static void doVisitReturn(SubType* self, Expression** currp) { self->visitReturn((*currp)->cast<Return>()); } - static void doVisitHost(SubType* self, Expression** currp) { self->visitHost((*currp)->cast<Host>()); } - static void doVisitNop(SubType* self, Expression** currp) { self->visitNop((*currp)->cast<Nop>()); } - static void doVisitUnreachable(SubType* self, Expression** currp) { self->visitUnreachable((*currp)->cast<Unreachable>()); } - - void setModule(Module* module) { - currModule = module; - } - - void setFunction(Function* func) { - currFunction = func; + static void doVisitBlock(SubType* self, Expression** currp) { + self->visitBlock((*currp)->cast<Block>()); + } + static void doVisitIf(SubType* self, Expression** currp) { + self->visitIf((*currp)->cast<If>()); + } + static void doVisitLoop(SubType* self, Expression** currp) { + self->visitLoop((*currp)->cast<Loop>()); + } + static void doVisitBreak(SubType* self, Expression** currp) { + self->visitBreak((*currp)->cast<Break>()); + } + static void doVisitSwitch(SubType* self, Expression** currp) { + self->visitSwitch((*currp)->cast<Switch>()); + } + static void doVisitCall(SubType* self, Expression** currp) { + self->visitCall((*currp)->cast<Call>()); + } + static void doVisitCallIndirect(SubType* self, Expression** currp) { + self->visitCallIndirect((*currp)->cast<CallIndirect>()); + } + static void doVisitGetLocal(SubType* self, Expression** currp) { + self->visitGetLocal((*currp)->cast<GetLocal>()); + } + static void doVisitSetLocal(SubType* self, Expression** currp) { + self->visitSetLocal((*currp)->cast<SetLocal>()); + } + static void doVisitGetGlobal(SubType* self, Expression** currp) { + self->visitGetGlobal((*currp)->cast<GetGlobal>()); + } + static void doVisitSetGlobal(SubType* self, Expression** currp) { + self->visitSetGlobal((*currp)->cast<SetGlobal>()); + } + static void doVisitLoad(SubType* self, Expression** currp) { + self->visitLoad((*currp)->cast<Load>()); + } + static void doVisitStore(SubType* self, Expression** currp) { + self->visitStore((*currp)->cast<Store>()); + } + static void doVisitAtomicRMW(SubType* self, Expression** currp) { + self->visitAtomicRMW((*currp)->cast<AtomicRMW>()); + } + static void doVisitAtomicCmpxchg(SubType* self, Expression** currp) { + self->visitAtomicCmpxchg((*currp)->cast<AtomicCmpxchg>()); + } + static void doVisitAtomicWait(SubType* self, Expression** currp) { + self->visitAtomicWait((*currp)->cast<AtomicWait>()); + } + static void doVisitAtomicNotify(SubType* self, Expression** currp) { + self->visitAtomicNotify((*currp)->cast<AtomicNotify>()); + } + static void doVisitSIMDExtract(SubType* self, Expression** currp) { + self->visitSIMDExtract((*currp)->cast<SIMDExtract>()); + } + static void doVisitSIMDReplace(SubType* self, Expression** currp) { + self->visitSIMDReplace((*currp)->cast<SIMDReplace>()); + } + static void doVisitSIMDShuffle(SubType* self, Expression** currp) { + self->visitSIMDShuffle((*currp)->cast<SIMDShuffle>()); + } + static void doVisitSIMDBitselect(SubType* self, Expression** currp) { + self->visitSIMDBitselect((*currp)->cast<SIMDBitselect>()); + } + static void doVisitSIMDShift(SubType* self, Expression** currp) { + self->visitSIMDShift((*currp)->cast<SIMDShift>()); + } + static void doVisitMemoryInit(SubType* self, Expression** currp) { + self->visitMemoryInit((*currp)->cast<MemoryInit>()); + } + static void doVisitDataDrop(SubType* self, Expression** currp) { + self->visitDataDrop((*currp)->cast<DataDrop>()); } + static void doVisitMemoryCopy(SubType* self, Expression** currp) { + self->visitMemoryCopy((*currp)->cast<MemoryCopy>()); + } + static void doVisitMemoryFill(SubType* self, Expression** currp) { + self->visitMemoryFill((*currp)->cast<MemoryFill>()); + } + static void doVisitConst(SubType* self, Expression** currp) { + self->visitConst((*currp)->cast<Const>()); + } + static void doVisitUnary(SubType* self, Expression** currp) { + self->visitUnary((*currp)->cast<Unary>()); + } + static void doVisitBinary(SubType* self, Expression** currp) { + self->visitBinary((*currp)->cast<Binary>()); + } + static void doVisitSelect(SubType* self, Expression** currp) { + self->visitSelect((*currp)->cast<Select>()); + } + static void doVisitDrop(SubType* self, Expression** currp) { + self->visitDrop((*currp)->cast<Drop>()); + } + static void doVisitReturn(SubType* self, Expression** currp) { + self->visitReturn((*currp)->cast<Return>()); + } + static void doVisitHost(SubType* self, Expression** currp) { + self->visitHost((*currp)->cast<Host>()); + } + static void doVisitNop(SubType* self, Expression** currp) { + self->visitNop((*currp)->cast<Nop>()); + } + static void doVisitUnreachable(SubType* self, Expression** currp) { + self->visitUnreachable((*currp)->cast<Unreachable>()); + } + + void setModule(Module* module) { currModule = module; } + + void setFunction(Function* func) { currFunction = func; } private: - Expression** replacep = nullptr; // the address of the current node, used to replace it - SmallVector<Task, 10> stack; // stack of tasks + Expression** replacep = + nullptr; // the address of the current node, used to replace it + SmallVector<Task, 10> stack; // stack of tasks Function* currFunction = nullptr; // current function being processed - Module* currModule = nullptr; // current module being processed + Module* currModule = nullptr; // current module being processed }; // Walks in post-order, i.e., children first. When there isn't an obvious @@ -520,7 +721,8 @@ struct PostWalker : public Walker<SubType, VisitorType> { Expression* curr = *currp; switch (curr->_id) { - case Expression::Id::InvalidId: abort(); + case Expression::Id::InvalidId: + abort(); case Expression::Id::BlockId: { self->pushTask(SubType::doVisitBlock, currp); auto& list = curr->cast<Block>()->list; @@ -571,7 +773,8 @@ struct PostWalker : public Walker<SubType, VisitorType> { break; } case Expression::Id::GetLocalId: { - self->pushTask(SubType::doVisitGetLocal, currp); // TODO: optimize leaves with a direct call? + // TODO: optimize leaves with a direct call? + self->pushTask(SubType::doVisitGetLocal, currp); break; } case Expression::Id::SetLocalId: { @@ -607,7 +810,8 @@ struct PostWalker : public Walker<SubType, VisitorType> { } case Expression::Id::AtomicCmpxchgId: { self->pushTask(SubType::doVisitAtomicCmpxchg, currp); - self->pushTask(SubType::scan, &curr->cast<AtomicCmpxchg>()->replacement); + self->pushTask(SubType::scan, + &curr->cast<AtomicCmpxchg>()->replacement); self->pushTask(SubType::scan, &curr->cast<AtomicCmpxchg>()->expected); self->pushTask(SubType::scan, &curr->cast<AtomicCmpxchg>()->ptr); break; @@ -661,25 +865,25 @@ struct PostWalker : public Walker<SubType, VisitorType> { self->pushTask(SubType::scan, &curr->cast<MemoryInit>()->offset); self->pushTask(SubType::scan, &curr->cast<MemoryInit>()->dest); break; - } + } case Expression::Id::DataDropId: { self->pushTask(SubType::doVisitDataDrop, currp); break; - } + } case Expression::Id::MemoryCopyId: { self->pushTask(SubType::doVisitMemoryCopy, currp); self->pushTask(SubType::scan, &curr->cast<MemoryCopy>()->size); self->pushTask(SubType::scan, &curr->cast<MemoryCopy>()->source); self->pushTask(SubType::scan, &curr->cast<MemoryCopy>()->dest); break; - } + } case Expression::Id::MemoryFillId: { self->pushTask(SubType::doVisitMemoryFill, currp); self->pushTask(SubType::scan, &curr->cast<MemoryFill>()->size); self->pushTask(SubType::scan, &curr->cast<MemoryFill>()->value); self->pushTask(SubType::scan, &curr->cast<MemoryFill>()->dest); break; - } + } case Expression::Id::ConstId: { self->pushTask(SubType::doVisitConst, currp); break; @@ -728,7 +932,8 @@ struct PostWalker : public Walker<SubType, VisitorType> { self->pushTask(SubType::doVisitUnreachable, currp); break; } - case Expression::Id::NumExpressionIds: WASM_UNREACHABLE(); + case Expression::Id::NumExpressionIds: + WASM_UNREACHABLE(); } } }; @@ -752,14 +957,17 @@ struct ControlFlowWalker : public PostWalker<SubType, VisitorType> { while (1) { auto* curr = controlFlowStack[i]; if (Block* block = curr->template dynCast<Block>()) { - if (name == block->name) return curr; + if (name == block->name) + return curr; } else if (Loop* loop = curr->template dynCast<Loop>()) { - if (name == loop->name) return curr; + if (name == loop->name) + return curr; } else { // an if, ignorable assert(curr->template is<If>()); } - if (i == 0) return nullptr; + if (i == 0) + return nullptr; i--; } } @@ -769,7 +977,8 @@ struct ControlFlowWalker : public PostWalker<SubType, VisitorType> { } static void doPostVisitControlFlow(SubType* self, Expression** currp) { - // note that we might be popping something else, as we may have been replaced + // note that we might be popping something else, as we may have been + // replaced self->controlFlowStack.pop_back(); } @@ -815,19 +1024,23 @@ struct ExpressionStackWalker : public PostWalker<SubType, VisitorType> { while (1) { auto* curr = expressionStack[i]; if (Block* block = curr->template dynCast<Block>()) { - if (name == block->name) return curr; + if (name == block->name) + return curr; } else if (Loop* loop = curr->template dynCast<Loop>()) { - if (name == loop->name) return curr; + if (name == loop->name) + return curr; } else { WASM_UNREACHABLE(); } - if (i == 0) return nullptr; + if (i == 0) + return nullptr; i--; } } Expression* getParent() { - if (expressionStack.size() == 1) return nullptr; + if (expressionStack.size() == 1) + return nullptr; assert(expressionStack.size() >= 2); return expressionStack[expressionStack.size() - 2]; } @@ -882,7 +1095,8 @@ struct LinearExecutionWalker : public PostWalker<SubType, VisitorType> { Expression* curr = *currp; switch (curr->_id) { - case Expression::Id::InvalidId: abort(); + case Expression::Id::InvalidId: + abort(); case Expression::Id::BlockId: { self->pushTask(SubType::doVisitBlock, currp); if (curr->cast<Block>()->name.is()) { diff --git a/src/wasm-validator.h b/src/wasm-validator.h index 9f9cddb02..496109a4c 100644 --- a/src/wasm-validator.h +++ b/src/wasm-validator.h @@ -43,8 +43,8 @@ #include <sstream> #include <unordered_set> -#include "wasm.h" #include "wasm-printing.h" +#include "wasm.h" namespace wasm { diff --git a/src/wasm.h b/src/wasm.h index 83809243c..a6b6b83d1 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -34,8 +34,8 @@ #include "literal.h" #include "mixed_arena.h" #include "support/name.h" -#include "wasm-type.h" #include "wasm-features.h" +#include "wasm-type.h" namespace wasm { @@ -56,114 +56,375 @@ struct Address { return *this; } operator address_t() const { return addr; } - Address& operator++() { ++addr; return *this; } + Address& operator++() { + ++addr; + return *this; + } }; // Operators enum UnaryOp { - ClzInt32, ClzInt64, CtzInt32, CtzInt64, PopcntInt32, PopcntInt64, // int - NegFloat32, NegFloat64, AbsFloat32, AbsFloat64, CeilFloat32, CeilFloat64, FloorFloat32, FloorFloat64, TruncFloat32, TruncFloat64, NearestFloat32, NearestFloat64, SqrtFloat32, SqrtFloat64, // float + // int + ClzInt32, + ClzInt64, + CtzInt32, + CtzInt64, + PopcntInt32, + PopcntInt64, + + // float + NegFloat32, + NegFloat64, + AbsFloat32, + AbsFloat64, + CeilFloat32, + CeilFloat64, + FloorFloat32, + FloorFloat64, + TruncFloat32, + TruncFloat64, + NearestFloat32, + NearestFloat64, + SqrtFloat32, + SqrtFloat64, + // relational - EqZInt32, EqZInt64, + EqZInt32, + EqZInt64, + // conversions - ExtendSInt32, ExtendUInt32, // extend i32 to i64 - WrapInt64, // i64 to i32 - TruncSFloat32ToInt32, TruncSFloat32ToInt64, TruncUFloat32ToInt32, TruncUFloat32ToInt64, TruncSFloat64ToInt32, TruncSFloat64ToInt64, TruncUFloat64ToInt32, TruncUFloat64ToInt64, // float to int - ReinterpretFloat32, ReinterpretFloat64, // reintepret bits to int - ConvertSInt32ToFloat32, ConvertSInt32ToFloat64, ConvertUInt32ToFloat32, ConvertUInt32ToFloat64, ConvertSInt64ToFloat32, ConvertSInt64ToFloat64, ConvertUInt64ToFloat32, ConvertUInt64ToFloat64, // int to float - PromoteFloat32, // f32 to f64 - DemoteFloat64, // f64 to f32 - ReinterpretInt32, ReinterpretInt64, // reinterpret bits to float + // extend i32 to i64 + ExtendSInt32, + ExtendUInt32, + // i64 to i32 + WrapInt64, + // float to int + TruncSFloat32ToInt32, + TruncSFloat32ToInt64, + TruncUFloat32ToInt32, + TruncUFloat32ToInt64, + TruncSFloat64ToInt32, + TruncSFloat64ToInt64, + TruncUFloat64ToInt32, + TruncUFloat64ToInt64, + // reintepret bits to int + ReinterpretFloat32, + ReinterpretFloat64, + // int to float + ConvertSInt32ToFloat32, + ConvertSInt32ToFloat64, + ConvertUInt32ToFloat32, + ConvertUInt32ToFloat64, + ConvertSInt64ToFloat32, + ConvertSInt64ToFloat64, + ConvertUInt64ToFloat32, + ConvertUInt64ToFloat64, + // f32 to f64 + PromoteFloat32, + // f64 to f32 + DemoteFloat64, + // reinterpret bits to float + ReinterpretInt32, + ReinterpretInt64, + // Extend signed subword-sized integer. This differs from e.g. ExtendSInt32 // because the input integer is in an i64 value insetad of an i32 value. - ExtendS8Int32, ExtendS16Int32, ExtendS8Int64, ExtendS16Int64, ExtendS32Int64, + ExtendS8Int32, + ExtendS16Int32, + ExtendS8Int64, + ExtendS16Int64, + ExtendS32Int64, + // Saturating float-to-int - TruncSatSFloat32ToInt32, TruncSatUFloat32ToInt32, TruncSatSFloat64ToInt32, TruncSatUFloat64ToInt32, - TruncSatSFloat32ToInt64, TruncSatUFloat32ToInt64, TruncSatSFloat64ToInt64, TruncSatUFloat64ToInt64, + TruncSatSFloat32ToInt32, + TruncSatUFloat32ToInt32, + TruncSatSFloat64ToInt32, + TruncSatUFloat64ToInt32, + TruncSatSFloat32ToInt64, + TruncSatUFloat32ToInt64, + TruncSatSFloat64ToInt64, + TruncSatUFloat64ToInt64, + // SIMD splats - SplatVecI8x16, SplatVecI16x8, SplatVecI32x4, SplatVecI64x2, SplatVecF32x4, SplatVecF64x2, + SplatVecI8x16, + SplatVecI16x8, + SplatVecI32x4, + SplatVecI64x2, + SplatVecF32x4, + SplatVecF64x2, + // SIMD arithmetic NotVec128, - NegVecI8x16, AnyTrueVecI8x16, AllTrueVecI8x16, NegVecI16x8, AnyTrueVecI16x8, AllTrueVecI16x8, - NegVecI32x4, AnyTrueVecI32x4, AllTrueVecI32x4, NegVecI64x2, AnyTrueVecI64x2, AllTrueVecI64x2, - AbsVecF32x4, NegVecF32x4, SqrtVecF32x4, AbsVecF64x2, NegVecF64x2, SqrtVecF64x2, - TruncSatSVecF32x4ToVecI32x4, TruncSatUVecF32x4ToVecI32x4, TruncSatSVecF64x2ToVecI64x2, TruncSatUVecF64x2ToVecI64x2, - ConvertSVecI32x4ToVecF32x4, ConvertUVecI32x4ToVecF32x4, ConvertSVecI64x2ToVecF64x2, ConvertUVecI64x2ToVecF64x2, + NegVecI8x16, + AnyTrueVecI8x16, + AllTrueVecI8x16, + NegVecI16x8, + AnyTrueVecI16x8, + AllTrueVecI16x8, + NegVecI32x4, + AnyTrueVecI32x4, + AllTrueVecI32x4, + NegVecI64x2, + AnyTrueVecI64x2, + AllTrueVecI64x2, + AbsVecF32x4, + NegVecF32x4, + SqrtVecF32x4, + AbsVecF64x2, + NegVecF64x2, + SqrtVecF64x2, + TruncSatSVecF32x4ToVecI32x4, + TruncSatUVecF32x4ToVecI32x4, + TruncSatSVecF64x2ToVecI64x2, + TruncSatUVecF64x2ToVecI64x2, + ConvertSVecI32x4ToVecF32x4, + ConvertUVecI32x4ToVecF32x4, + ConvertSVecI64x2ToVecF64x2, + ConvertUVecI64x2ToVecF64x2, InvalidUnary }; enum BinaryOp { - AddInt32, SubInt32, MulInt32, // int or float - DivSInt32, DivUInt32, RemSInt32, RemUInt32, AndInt32, OrInt32, XorInt32, ShlInt32, ShrUInt32, ShrSInt32, RotLInt32, RotRInt32, // int + // int or float + AddInt32, + SubInt32, + MulInt32, + + // int + DivSInt32, + DivUInt32, + RemSInt32, + RemUInt32, + AndInt32, + OrInt32, + XorInt32, + ShlInt32, + ShrUInt32, + ShrSInt32, + RotLInt32, + RotRInt32, + // relational ops - EqInt32, NeInt32, // int or float - LtSInt32, LtUInt32, LeSInt32, LeUInt32, GtSInt32, GtUInt32, GeSInt32, GeUInt32, // int + // int or float + EqInt32, + NeInt32, + // int + LtSInt32, + LtUInt32, + LeSInt32, + LeUInt32, + GtSInt32, + GtUInt32, + GeSInt32, + GeUInt32, + + // int or float + AddInt64, + SubInt64, + MulInt64, + + // int + DivSInt64, + DivUInt64, + RemSInt64, + RemUInt64, + AndInt64, + OrInt64, + XorInt64, + ShlInt64, + ShrUInt64, + ShrSInt64, + RotLInt64, + RotRInt64, - AddInt64, SubInt64, MulInt64, // int or float - DivSInt64, DivUInt64, RemSInt64, RemUInt64, AndInt64, OrInt64, XorInt64, ShlInt64, ShrUInt64, ShrSInt64, RotLInt64, RotRInt64, // int // relational ops - EqInt64, NeInt64, // int or float - LtSInt64, LtUInt64, LeSInt64, LeUInt64, GtSInt64, GtUInt64, GeSInt64, GeUInt64, // int + // int or float + EqInt64, + NeInt64, + // int + LtSInt64, + LtUInt64, + LeSInt64, + LeUInt64, + GtSInt64, + GtUInt64, + GeSInt64, + GeUInt64, + + // int or float + AddFloat32, + SubFloat32, + MulFloat32, + + // float + DivFloat32, + CopySignFloat32, + MinFloat32, + MaxFloat32, - AddFloat32, SubFloat32, MulFloat32, // int or float - DivFloat32, CopySignFloat32, MinFloat32, MaxFloat32, // float // relational ops - EqFloat32, NeFloat32, // int or float - LtFloat32, LeFloat32, GtFloat32, GeFloat32, // float + // int or float + EqFloat32, + NeFloat32, + // float + LtFloat32, + LeFloat32, + GtFloat32, + GeFloat32, + + // int or float + AddFloat64, + SubFloat64, + MulFloat64, + + // float + DivFloat64, + CopySignFloat64, + MinFloat64, + MaxFloat64, - AddFloat64, SubFloat64, MulFloat64, // int or float - DivFloat64, CopySignFloat64, MinFloat64, MaxFloat64, // float // relational ops - EqFloat64, NeFloat64, // int or float - LtFloat64, LeFloat64, GtFloat64, GeFloat64, // float + // int or float + EqFloat64, + NeFloat64, + // float + LtFloat64, + LeFloat64, + GtFloat64, + GeFloat64, + // SIMD relational ops (return vectors) - 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, + 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, + // SIMD arithmetic - 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, + 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, InvalidBinary }; -enum HostOp { - CurrentMemory, GrowMemory -}; +enum HostOp { CurrentMemory, GrowMemory }; -enum AtomicRMWOp { - Add, Sub, And, Or, Xor, Xchg -}; +enum AtomicRMWOp { Add, Sub, And, Or, Xor, Xchg }; enum SIMDExtractOp { - ExtractLaneSVecI8x16, ExtractLaneUVecI8x16, ExtractLaneSVecI16x8, ExtractLaneUVecI16x8, - ExtractLaneVecI32x4, ExtractLaneVecI64x2, ExtractLaneVecF32x4, ExtractLaneVecF64x2 + ExtractLaneSVecI8x16, + ExtractLaneUVecI8x16, + ExtractLaneSVecI16x8, + ExtractLaneUVecI16x8, + ExtractLaneVecI32x4, + ExtractLaneVecI64x2, + ExtractLaneVecF32x4, + ExtractLaneVecF64x2 }; enum SIMDReplaceOp { - ReplaceLaneVecI8x16, ReplaceLaneVecI16x8, ReplaceLaneVecI32x4, ReplaceLaneVecI64x2, ReplaceLaneVecF32x4, ReplaceLaneVecF64x2 + ReplaceLaneVecI8x16, + ReplaceLaneVecI16x8, + ReplaceLaneVecI32x4, + ReplaceLaneVecI64x2, + ReplaceLaneVecF32x4, + ReplaceLaneVecF64x2 }; enum SIMDShiftOp { - ShlVecI8x16, ShrSVecI8x16, ShrUVecI8x16, ShlVecI16x8, ShrSVecI16x8, ShrUVecI16x8, - ShlVecI32x4, ShrSVecI32x4, ShrUVecI32x4, ShlVecI64x2, ShrSVecI64x2, ShrUVecI64x2 + ShlVecI8x16, + ShrSVecI8x16, + ShrUVecI8x16, + ShlVecI16x8, + ShrSVecI16x8, + ShrUVecI16x8, + ShlVecI32x4, + ShrSVecI32x4, + ShrUVecI32x4, + ShlVecI64x2, + ShrSVecI64x2, + ShrUVecI64x2 }; // // Expressions // -// Note that little is provided in terms of constructors for these. The rationale -// is that writing new Something(a, b, c, d, e) is not the clearest, and it would -// be better to write new Something(name=a, leftOperand=b... etc., but C++ -// lacks named operands, so in asm2wasm etc. you will see things like +// Note that little is provided in terms of constructors for these. The +// rationale is that writing new Something(a, b, c, d, e) is not the clearest, +// and it would be better to write new Something(name=a, leftOperand=b... +// etc., but C++ lacks named operands, so in asm2wasm etc. you will see things +// like // auto x = new Something(); // x->name = a; // x->leftOperand = b; @@ -227,29 +488,22 @@ public: void finalize() {} - template<class T> - bool is() const { - return int(_id) == int(T::SpecificId); - } + template<class T> bool is() const { return int(_id) == int(T::SpecificId); } - template<class T> - T* dynCast() { + template<class T> T* dynCast() { return int(_id) == int(T::SpecificId) ? (T*)this : nullptr; } - template <class T> - const T* dynCast() const { + template<class T> const T* dynCast() const { return int(_id) == int(T::SpecificId) ? (const T*)this : nullptr; } - template<class T> - T* cast() { + template<class T> T* cast() { assert(int(_id) == int(T::SpecificId)); return (T*)this; } - template<class T> - const T* cast() const { + template<class T> const T* cast() const { assert(int(_id) == int(T::SpecificId)); return (const T*)this; } @@ -259,8 +513,7 @@ const char* getExpressionName(Expression* curr); typedef ArenaVector<Expression*> ExpressionList; -template<Expression::Id SID> -class SpecificExpression : public Expression { +template<Expression::Id SID> class SpecificExpression : public Expression { public: enum { SpecificId = SID // compile-time access to the type for the class @@ -282,19 +535,20 @@ public: Name name; ExpressionList list; - // set the type purely based on its contents. this scans the block, so it is not fast. + // set the type purely based on its contents. this scans the block, so it is + // not fast. void finalize(); // set the type given you know its type, which is the case when parsing - // s-expression or binary, as explicit types are given. the only additional work - // this does is to set the type to unreachable in the cases that is needed - // (which may require scanning the block) + // s-expression or binary, as explicit types are given. the only additional + // work this does is to set the type to unreachable in the cases that is + // needed (which may require scanning the block) void finalize(Type type_); - // set the type given you know its type, and you know if there is a break to this - // block. this avoids the need to scan the contents of the block in the case that - // it might be unreachable, so it is recommended if you already know the type - // and breakability anyhow. + // set the type given you know its type, and you know if there is a break to + // this block. this avoids the need to scan the contents of the block in the + // case that it might be unreachable, so it is recommended if you already know + // the type and breakability anyhow. void finalize(Type type_, bool hasBreak); }; @@ -308,8 +562,9 @@ public: Expression* ifFalse; // set the type given you know its type, which is the case when parsing - // s-expression or binary, as explicit types are given. the only additional work - // this does is to set the type to unreachable in the cases that is needed. + // s-expression or binary, as explicit types are given. the only additional + // work this does is to set the type to unreachable in the cases that is + // needed. void finalize(Type type_); // set the type purely based on its contents. @@ -325,8 +580,9 @@ public: Expression* body; // set the type given you know its type, which is the case when parsing - // s-expression or binary, as explicit types are given. the only additional work - // this does is to set the type to unreachable in the cases that is needed. + // s-expression or binary, as explicit types are given. the only additional + // work this does is to set the type to unreachable in the cases that is + // needed. void finalize(Type type_); // set the type purely based on its contents. @@ -336,9 +592,7 @@ public: class Break : public SpecificExpression<Expression::BreakId> { public: Break() : value(nullptr), condition(nullptr) {} - Break(MixedArena& allocator) : Break() { - type = unreachable; - } + Break(MixedArena& allocator) : Break() { type = unreachable; } Name name; Expression* value; @@ -349,9 +603,7 @@ public: class Switch : public SpecificExpression<Expression::SwitchId> { public: - Switch(MixedArena& allocator) : targets(allocator) { - type = unreachable; - } + Switch(MixedArena& allocator) : targets(allocator) { type = unreachable; } ArenaVector<Name> targets; Name default_; @@ -471,7 +723,7 @@ public: }; class AtomicRMW : public SpecificExpression<Expression::AtomicRMWId> { - public: +public: AtomicRMW() = default; AtomicRMW(MixedArena& allocator) : AtomicRMW() {} @@ -485,7 +737,7 @@ class AtomicRMW : public SpecificExpression<Expression::AtomicRMWId> { }; class AtomicCmpxchg : public SpecificExpression<Expression::AtomicCmpxchgId> { - public: +public: AtomicCmpxchg() = default; AtomicCmpxchg(MixedArena& allocator) : AtomicCmpxchg() {} @@ -499,7 +751,7 @@ class AtomicCmpxchg : public SpecificExpression<Expression::AtomicCmpxchgId> { }; class AtomicWait : public SpecificExpression<Expression::AtomicWaitId> { - public: +public: AtomicWait() = default; AtomicWait(MixedArena& allocator) : AtomicWait() {} @@ -513,7 +765,7 @@ class AtomicWait : public SpecificExpression<Expression::AtomicWaitId> { }; class AtomicNotify : public SpecificExpression<Expression::AtomicNotifyId> { - public: +public: AtomicNotify() = default; AtomicNotify(MixedArena& allocator) : AtomicNotify() {} @@ -525,7 +777,7 @@ class AtomicNotify : public SpecificExpression<Expression::AtomicNotifyId> { }; class SIMDExtract : public SpecificExpression<Expression::SIMDExtractId> { - public: +public: SIMDExtract() = default; SIMDExtract(MixedArena& allocator) : SIMDExtract() {} @@ -537,7 +789,7 @@ class SIMDExtract : public SpecificExpression<Expression::SIMDExtractId> { }; class SIMDReplace : public SpecificExpression<Expression::SIMDReplaceId> { - public: +public: SIMDReplace() = default; SIMDReplace(MixedArena& allocator) : SIMDReplace() {} @@ -550,7 +802,7 @@ class SIMDReplace : public SpecificExpression<Expression::SIMDReplaceId> { }; class SIMDShuffle : public SpecificExpression<Expression::SIMDShuffleId> { - public: +public: SIMDShuffle() = default; SIMDShuffle(MixedArena& allocator) : SIMDShuffle() {} @@ -562,7 +814,7 @@ class SIMDShuffle : public SpecificExpression<Expression::SIMDShuffleId> { }; class SIMDBitselect : public SpecificExpression<Expression::SIMDBitselectId> { - public: +public: SIMDBitselect() = default; SIMDBitselect(MixedArena& allocator) : SIMDBitselect() {} @@ -574,7 +826,7 @@ class SIMDBitselect : public SpecificExpression<Expression::SIMDBitselectId> { }; class SIMDShift : public SpecificExpression<Expression::SIMDShiftId> { - public: +public: SIMDShift() = default; SIMDShift(MixedArena& allocator) : SIMDShift() {} @@ -586,7 +838,7 @@ class SIMDShift : public SpecificExpression<Expression::SIMDShiftId> { }; class MemoryInit : public SpecificExpression<Expression::MemoryInitId> { - public: +public: MemoryInit() = default; MemoryInit(MixedArena& allocator) : MemoryInit() {} @@ -599,7 +851,7 @@ class MemoryInit : public SpecificExpression<Expression::MemoryInitId> { }; class DataDrop : public SpecificExpression<Expression::DataDropId> { - public: +public: DataDrop() = default; DataDrop(MixedArena& allocator) : DataDrop() {} @@ -609,7 +861,7 @@ class DataDrop : public SpecificExpression<Expression::DataDropId> { }; class MemoryCopy : public SpecificExpression<Expression::MemoryCopyId> { - public: +public: MemoryCopy() = default; MemoryCopy(MixedArena& allocator) : MemoryCopy() {} @@ -621,7 +873,7 @@ class MemoryCopy : public SpecificExpression<Expression::MemoryCopyId> { }; class MemoryFill : public SpecificExpression<Expression::MemoryFillId> { - public: +public: MemoryFill() = default; MemoryFill(MixedArena& allocator) : MemoryFill() {} @@ -698,9 +950,7 @@ public: class Return : public SpecificExpression<Expression::ReturnId> { public: - Return() { - type = unreachable; - } + Return() { type = unreachable; } Return(MixedArena& allocator) : Return() {} Expression* value = nullptr; @@ -719,9 +969,7 @@ public: class Unreachable : public SpecificExpression<Expression::UnreachableId> { public: - Unreachable() { - type = unreachable; - } + Unreachable() { type = unreachable; } Unreachable(MixedArena& allocator) : Unreachable() {} }; @@ -731,9 +979,7 @@ struct Importable { // If these are set, then this is an import, as module.base Name module, base; - bool imported() { - return module.is(); - } + bool imported() { return module.is(); } }; // Forward declarations of Stack IR, as functions can contain it, see @@ -749,7 +995,7 @@ public: Type result = none; std::vector<Type> params; // function locals are std::vector<Type> vars; // params plus vars - Name type; // if null, it is implicit in params and result + Name type; // if null, it is implicit in params and result // The body of the function Expression* body = nullptr; @@ -770,11 +1016,19 @@ public: struct DebugLocation { uint32_t fileIndex, lineNumber, columnNumber; - bool operator==(const DebugLocation& other) const { return fileIndex == other.fileIndex && lineNumber == other.lineNumber && columnNumber == other.columnNumber; } - bool operator!=(const DebugLocation& other) const { return !(*this == other); } + bool operator==(const DebugLocation& other) const { + return fileIndex == other.fileIndex && lineNumber == other.lineNumber && + columnNumber == other.columnNumber; + } + bool operator!=(const DebugLocation& other) const { + return !(*this == other); + } bool operator<(const DebugLocation& other) const { - return fileIndex != other.fileIndex ? fileIndex < other.fileIndex : - lineNumber != other.lineNumber ? lineNumber < other.lineNumber : columnNumber < other.columnNumber; + return fileIndex != other.fileIndex + ? fileIndex < other.fileIndex + : lineNumber != other.lineNumber + ? lineNumber < other.lineNumber + : columnNumber < other.columnNumber; } }; std::unordered_map<Expression*, DebugLocation> debugLocations; @@ -813,7 +1067,9 @@ enum class ExternalKind { class Export { public: - Name name; // exported name - note that this is the key, as the internal name is non-unique (can have multiple exports for an internal, also over kinds) + // exported name - note that this is the key, as the internal name is + // non-unique (can have multiple exports for an internal, also over kinds) + Name name; Name value; // internal name ExternalKind kind; }; @@ -835,17 +1091,16 @@ public: } }; - // Currently the wasm object always 'has' one Table. It 'exists' if it has been defined or imported. - // The table can exist but be empty and have no defined initial or max size. + // Currently the wasm object always 'has' one Table. It 'exists' if it has + // been defined or imported. The table can exist but be empty and have no + // defined initial or max size. bool exists = false; Name name; Address initial = 0; Address max = kMaxSize; std::vector<Segment> segments; - Table() { - name = Name::fromInt(0); - } + Table() { name = Name::fromInt(0); } bool hasMax() { return max != kUnlimitedSize; } }; @@ -854,7 +1109,8 @@ public: static const Address::address_t kPageSize = 64 * 1024; static const Address::address_t kUnlimitedSize = Address::address_t(-1); // In wasm32, the maximum memory size is limited by a 32-bit pointer: 4GB - static const Address::address_t kMaxSize = (uint64_t(4) * 1024 * 1024 * 1024) / kPageSize; + static const Address::address_t kMaxSize = + (uint64_t(4) * 1024 * 1024 * 1024) / kPageSize; static const Address::address_t kPageMask = ~(kPageSize - 1); struct Segment { @@ -863,7 +1119,8 @@ public: std::vector<char> data; // TODO: optimize Segment() = default; Segment(Expression* offset) : offset(offset) {} - Segment(Expression* offset, const char* init, Address size) : offset(offset) { + Segment(Expression* offset, const char* init, Address size) + : offset(offset) { data.resize(size); std::copy_n(init, size, data.begin()); } @@ -871,7 +1128,7 @@ public: data.swap(init); } Segment(bool isPassive, Expression* offset, const char* init, Address size) - : isPassive(isPassive), offset(offset) { + : isPassive(isPassive), offset(offset) { data.resize(size); std::copy_n(init, size, data.begin()); } @@ -886,9 +1143,7 @@ public: // See comment in Table. bool shared = false; - Memory() { - name = Name::fromInt(0); - } + Memory() { name = Name::fromInt(0); } bool hasMax() { return max != kUnlimitedSize; } }; @@ -910,7 +1165,8 @@ public: class Module { public: - // wasm contents (generally you shouldn't access these from outside, except maybe for iterating; use add*() and the get() functions) + // wasm contents (generally you shouldn't access these from outside, except + // maybe for iterating; use add*() and the get() functions) std::vector<std::unique_ptr<FunctionType>> functionTypes; std::vector<std::unique_ptr<Export>> exports; std::vector<std::unique_ptr<Function>> functions; @@ -933,9 +1189,11 @@ public: MixedArena allocator; private: - // TODO: add a build option where Names are just indices, and then these methods are not needed + // TODO: add a build option where Names are just indices, and then these + // methods are not needed std::map<Name, FunctionType*> functionTypesMap; - std::map<Name, Export*> exportsMap; // exports map is by the *exported* name, which is unique + // exports map is by the *exported* name, which is unique + std::map<Name, Export*> exportsMap; std::map<Name, Function*> functionsMap; std::map<Name, Global*> globalsMap; @@ -978,6 +1236,6 @@ template<> struct hash<wasm::Address> { return std::hash<wasm::Address::address_t>()(a.addr); } }; -} +} // namespace std #endif // wasm_wasm_h diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp index b374566d1..be92ae03d 100644 --- a/src/wasm/literal.cpp +++ b/src/wasm/literal.cpp @@ -20,16 +20,14 @@ #include <cmath> #include "emscripten-optimizer/simple_ast.h" +#include "ir/bits.h" #include "pretty_printing.h" #include "support/bits.h" #include "support/utilities.h" -#include "ir/bits.h" - namespace wasm { -template<int N> -using LaneArray = std::array<Literal, N>; +template<int N> using LaneArray = std::array<Literal, N>; Literal::Literal(const uint8_t init[16]) : type(Type::v128) { memcpy(&v128, init, 16); @@ -45,7 +43,8 @@ static void extractBytes(uint8_t (&dest)[16], const LaneArray<Lanes>& lanes) { LaneT lane; memcpy(&lane, bits, sizeof(lane)); for (size_t offset = 0; offset < lane_width; ++offset) { - bytes.at(lane_index * lane_width + offset) = uint8_t(lane >> (8 * offset)); + bytes.at(lane_index * lane_width + offset) = + uint8_t(lane >> (8 * offset)); } } memcpy(&dest, bytes.data(), sizeof(bytes)); @@ -104,17 +103,23 @@ Literal Literal::castToI64() { int64_t Literal::getInteger() const { switch (type) { - case Type::i32: return i32; - case Type::i64: return i64; - default: abort(); + case Type::i32: + return i32; + case Type::i64: + return i64; + default: + abort(); } } double Literal::getFloat() const { switch (type) { - case Type::f32: return getf32(); - case Type::f64: return getf64(); - default: abort(); + case Type::f32: + return getf32(); + case Type::f64: + return getf64(); + default: + abort(); } } @@ -122,18 +127,27 @@ void Literal::getBits(uint8_t (&buf)[16]) const { memset(buf, 0, 16); switch (type) { case Type::i32: - case Type::f32: memcpy(buf, &i32, sizeof(i32)); break; + case Type::f32: + memcpy(buf, &i32, sizeof(i32)); + break; case Type::i64: - case Type::f64: memcpy(buf, &i64, sizeof(i64)); break; - case Type::v128: memcpy(buf, &v128, sizeof(v128)); break; + case Type::f64: + memcpy(buf, &i64, sizeof(i64)); + break; + case Type::v128: + memcpy(buf, &v128, sizeof(v128)); + break; case Type::none: - case Type::unreachable: WASM_UNREACHABLE(); + case Type::unreachable: + WASM_UNREACHABLE(); } } bool Literal::operator==(const Literal& other) const { - if (type != other.type) return false; - if (type == none) return true; + if (type != other.type) + return false; + if (type == none) + return true; uint8_t bits[16], other_bits[16]; getBits(bits); other.getBits(other_bits); @@ -181,7 +195,7 @@ double Literal::setQuietNaN(double f) { return bit_cast<double>(0x0008000000000000ull | bit_cast<uint64_t>(f)); } -void Literal::printFloat(std::ostream &o, float f) { +void Literal::printFloat(std::ostream& o, float f) { if (std::isnan(f)) { const char* sign = std::signbit(f) ? "-" : ""; o << sign << "nan"; @@ -224,13 +238,10 @@ void Literal::printDouble(std::ostream& o, double d) { void Literal::printVec128(std::ostream& o, const std::array<uint8_t, 16>& v) { o << std::hex; for (auto i = 0; i < 16; i += 4) { - if (i) o << " "; - o << "0x" << std::setfill('0') << std::setw(8) << uint32_t( - v[i ] | - (v[i + 1] << 8) | - (v[i + 2] << 16) | - (v[i + 3] << 24) - ); + if (i) + o << " "; + o << "0x" << std::setfill('0') << std::setw(8) + << uint32_t(v[i] | (v[i + 1] << 8) | (v[i + 2] << 16) | (v[i + 3] << 24)); } o << std::dec; } @@ -238,33 +249,53 @@ void Literal::printVec128(std::ostream& o, const std::array<uint8_t, 16>& v) { std::ostream& operator<<(std::ostream& o, Literal literal) { prepareMinorColor(o) << printType(literal.type) << ".const "; switch (literal.type) { - case Type::none: o << "?"; break; - case Type::i32: o << literal.i32; break; - case Type::i64: o << literal.i64; break; - case Type::f32: literal.printFloat(o, literal.getf32()); break; - case Type::f64: literal.printDouble(o, literal.getf64()); break; - case Type::v128: o << "i32x4 "; literal.printVec128(o, literal.getv128()); break; - case Type::unreachable: WASM_UNREACHABLE(); + case Type::none: + o << "?"; + break; + case Type::i32: + o << literal.i32; + break; + case Type::i64: + o << literal.i64; + break; + case Type::f32: + literal.printFloat(o, literal.getf32()); + break; + case Type::f64: + literal.printDouble(o, literal.getf64()); + break; + case Type::v128: + o << "i32x4 "; + literal.printVec128(o, literal.getv128()); + break; + case Type::unreachable: + WASM_UNREACHABLE(); } restoreNormalColor(o); return o; } Literal Literal::countLeadingZeroes() const { - if (type == Type::i32) return Literal((int32_t)CountLeadingZeroes(i32)); - if (type == Type::i64) return Literal((int64_t)CountLeadingZeroes(i64)); + if (type == Type::i32) + return Literal((int32_t)CountLeadingZeroes(i32)); + if (type == Type::i64) + return Literal((int64_t)CountLeadingZeroes(i64)); WASM_UNREACHABLE(); } Literal Literal::countTrailingZeroes() const { - if (type == Type::i32) return Literal((int32_t)CountTrailingZeroes(i32)); - if (type == Type::i64) return Literal((int64_t)CountTrailingZeroes(i64)); + if (type == Type::i32) + return Literal((int32_t)CountTrailingZeroes(i32)); + if (type == Type::i64) + return Literal((int64_t)CountTrailingZeroes(i64)); WASM_UNREACHABLE(); } Literal Literal::popCount() const { - if (type == Type::i32) return Literal((int32_t)PopCount(i32)); - if (type == Type::i64) return Literal((int64_t)PopCount(i64)); + if (type == Type::i32) + return Literal((int32_t)PopCount(i32)); + if (type == Type::i64) + return Literal((int64_t)PopCount(i64)); WASM_UNREACHABLE(); } @@ -284,19 +315,24 @@ Literal Literal::extendToF64() const { } Literal Literal::extendS8() const { - if (type == Type::i32) return Literal(int32_t(int8_t(geti32() & 0xFF))); - if (type == Type::i64) return Literal(int64_t(int8_t(geti64() & 0xFF))); + if (type == Type::i32) + return Literal(int32_t(int8_t(geti32() & 0xFF))); + if (type == Type::i64) + return Literal(int64_t(int8_t(geti64() & 0xFF))); WASM_UNREACHABLE(); } Literal Literal::extendS16() const { - if (type == Type::i32) return Literal(int32_t(int16_t(geti32() & 0xFFFF))); - if (type == Type::i64) return Literal(int64_t(int16_t(geti64() & 0xFFFF))); + if (type == Type::i32) + return Literal(int32_t(int16_t(geti32() & 0xFFFF))); + if (type == Type::i64) + return Literal(int64_t(int16_t(geti64() & 0xFFFF))); WASM_UNREACHABLE(); } Literal Literal::extendS32() const { - if (type == Type::i64) return Literal(int64_t(int32_t(geti64() & 0xFFFFFFFF))); + if (type == Type::i64) + return Literal(int64_t(int32_t(geti64() & 0xFFFFFFFF))); WASM_UNREACHABLE(); } @@ -306,33 +342,38 @@ Literal Literal::wrapToI32() const { } Literal Literal::convertSIToF32() const { - if (type == Type::i32) return Literal(float(i32)); - if (type == Type::i64) return Literal(float(i64)); + if (type == Type::i32) + return Literal(float(i32)); + if (type == Type::i64) + return Literal(float(i64)); WASM_UNREACHABLE(); } Literal Literal::convertUIToF32() const { - if (type == Type::i32) return Literal(float(uint32_t(i32))); - if (type == Type::i64) return Literal(float(uint64_t(i64))); + if (type == Type::i32) + return Literal(float(uint32_t(i32))); + if (type == Type::i64) + return Literal(float(uint64_t(i64))); WASM_UNREACHABLE(); } Literal Literal::convertSIToF64() const { - if (type == Type::i32) return Literal(double(i32)); - if (type == Type::i64) return Literal(double(i64)); + if (type == Type::i32) + return Literal(double(i32)); + if (type == Type::i64) + return Literal(double(i64)); WASM_UNREACHABLE(); } Literal Literal::convertUIToF64() const { - if (type == Type::i32) return Literal(double(uint32_t(i32))); - if (type == Type::i64) return Literal(double(uint64_t(i64))); + if (type == Type::i32) + return Literal(double(uint32_t(i32))); + if (type == Type::i64) + return Literal(double(uint64_t(i64))); WASM_UNREACHABLE(); } -template<typename F> -struct AsInt { - using type = void; -}; +template<typename F> struct AsInt { using type = void; }; template<> struct AsInt<float> { using type = int32_t; }; template<> struct AsInt<double> { using type = int64_t; }; @@ -354,13 +395,11 @@ static Literal saturating_trunc(typename AsInt<F>::type val) { Literal Literal::truncSatToSI32() const { if (type == Type::f32) { return saturating_trunc<float, int32_t, isInRangeI32TruncS>( - Literal(*this).castToI32().geti32() - ); + Literal(*this).castToI32().geti32()); } if (type == Type::f64) { return saturating_trunc<double, int32_t, isInRangeI32TruncS>( - Literal(*this).castToI64().geti64() - ); + Literal(*this).castToI64().geti64()); } WASM_UNREACHABLE(); } @@ -368,13 +407,11 @@ Literal Literal::truncSatToSI32() const { Literal Literal::truncSatToSI64() const { if (type == Type::f32) { return saturating_trunc<float, int64_t, isInRangeI64TruncS>( - Literal(*this).castToI32().geti32() - ); + Literal(*this).castToI32().geti32()); } if (type == Type::f64) { return saturating_trunc<double, int64_t, isInRangeI64TruncS>( - Literal(*this).castToI64().geti64() - ); + Literal(*this).castToI64().geti64()); } WASM_UNREACHABLE(); } @@ -382,13 +419,11 @@ Literal Literal::truncSatToSI64() const { Literal Literal::truncSatToUI32() const { if (type == Type::f32) { return saturating_trunc<float, uint32_t, isInRangeI32TruncU>( - Literal(*this).castToI32().geti32() - ); + Literal(*this).castToI32().geti32()); } if (type == Type::f64) { return saturating_trunc<double, uint32_t, isInRangeI32TruncU>( - Literal(*this).castToI64().geti64() - ); + Literal(*this).castToI64().geti64()); } WASM_UNREACHABLE(); } @@ -396,180 +431,223 @@ Literal Literal::truncSatToUI32() const { Literal Literal::truncSatToUI64() const { if (type == Type::f32) { return saturating_trunc<float, uint64_t, isInRangeI64TruncU>( - Literal(*this).castToI32().geti32() - ); + Literal(*this).castToI32().geti32()); } if (type == Type::f64) { return saturating_trunc<double, uint64_t, isInRangeI64TruncU>( - Literal(*this).castToI64().geti64() - ); + Literal(*this).castToI64().geti64()); } WASM_UNREACHABLE(); } Literal Literal::eqz() const { switch (type) { - case Type::i32: return eq(Literal(int32_t(0))); - case Type::i64: return eq(Literal(int64_t(0))); - case Type::f32: return eq(Literal(float(0))); - case Type::f64: return eq(Literal(double(0))); + case Type::i32: + return eq(Literal(int32_t(0))); + case Type::i64: + return eq(Literal(int64_t(0))); + case Type::f32: + return eq(Literal(float(0))); + case Type::f64: + return eq(Literal(double(0))); case Type::v128: case Type::none: - case Type::unreachable: WASM_UNREACHABLE(); + case Type::unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } Literal Literal::neg() const { switch (type) { - case Type::i32: return Literal(-uint32_t(i32)); - case Type::i64: return Literal(-uint64_t(i64)); - case Type::f32: return Literal(i32 ^ 0x80000000).castToF32(); - case Type::f64: return Literal(int64_t(i64 ^ 0x8000000000000000ULL)).castToF64(); + case Type::i32: + return Literal(-uint32_t(i32)); + case Type::i64: + return Literal(-uint64_t(i64)); + case Type::f32: + return Literal(i32 ^ 0x80000000).castToF32(); + case Type::f64: + return Literal(int64_t(i64 ^ 0x8000000000000000ULL)).castToF64(); case Type::v128: case Type::none: - case Type::unreachable: WASM_UNREACHABLE(); + case Type::unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } Literal Literal::abs() const { switch (type) { - case Type::i32: return Literal(i32 & 0x7fffffff); - case Type::i64: return Literal(int64_t(i64 & 0x7fffffffffffffffULL)); - case Type::f32: return Literal(i32 & 0x7fffffff).castToF32(); - case Type::f64: return Literal(int64_t(i64 & 0x7fffffffffffffffULL)).castToF64(); + case Type::i32: + return Literal(i32 & 0x7fffffff); + case Type::i64: + return Literal(int64_t(i64 & 0x7fffffffffffffffULL)); + case Type::f32: + return Literal(i32 & 0x7fffffff).castToF32(); + case Type::f64: + return Literal(int64_t(i64 & 0x7fffffffffffffffULL)).castToF64(); case Type::v128: case Type::none: - case Type::unreachable: WASM_UNREACHABLE(); + case Type::unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } Literal Literal::ceil() const { switch (type) { - case Type::f32: return Literal(std::ceil(getf32())); - case Type::f64: return Literal(std::ceil(getf64())); - default: WASM_UNREACHABLE(); + case Type::f32: + return Literal(std::ceil(getf32())); + case Type::f64: + return Literal(std::ceil(getf64())); + default: + WASM_UNREACHABLE(); } } Literal Literal::floor() const { switch (type) { - case Type::f32: return Literal(std::floor(getf32())); - case Type::f64: return Literal(std::floor(getf64())); - default: WASM_UNREACHABLE(); + case Type::f32: + return Literal(std::floor(getf32())); + case Type::f64: + return Literal(std::floor(getf64())); + default: + WASM_UNREACHABLE(); } } Literal Literal::trunc() const { switch (type) { - case Type::f32: return Literal(std::trunc(getf32())); - case Type::f64: return Literal(std::trunc(getf64())); - default: WASM_UNREACHABLE(); + case Type::f32: + return Literal(std::trunc(getf32())); + case Type::f64: + return Literal(std::trunc(getf64())); + default: + WASM_UNREACHABLE(); } } Literal Literal::nearbyint() const { switch (type) { - case Type::f32: return Literal(std::nearbyint(getf32())); - case Type::f64: return Literal(std::nearbyint(getf64())); - default: WASM_UNREACHABLE(); + case Type::f32: + return Literal(std::nearbyint(getf32())); + case Type::f64: + return Literal(std::nearbyint(getf64())); + default: + WASM_UNREACHABLE(); } } Literal Literal::sqrt() const { switch (type) { - case Type::f32: return Literal(std::sqrt(getf32())); - case Type::f64: return Literal(std::sqrt(getf64())); - default: WASM_UNREACHABLE(); + case Type::f32: + return Literal(std::sqrt(getf32())); + case Type::f64: + return Literal(std::sqrt(getf64())); + default: + WASM_UNREACHABLE(); } } Literal Literal::demote() const { auto f64 = getf64(); - if (std::isnan(f64)) return Literal(float(f64)); - if (std::isinf(f64)) return Literal(float(f64)); + if (std::isnan(f64)) + return Literal(float(f64)); + if (std::isinf(f64)) + return Literal(float(f64)); // when close to the limit, but still truncatable to a valid value, do that - // see https://github.com/WebAssembly/sexpr-wasm-prototype/blob/2d375e8d502327e814d62a08f22da9d9b6b675dc/src/wasm-interpreter.c#L247 + // see + // https://github.com/WebAssembly/sexpr-wasm-prototype/blob/2d375e8d502327e814d62a08f22da9d9b6b675dc/src/wasm-interpreter.c#L247 uint64_t bits = reinterpreti64(); - if (bits > 0x47efffffe0000000ULL && bits < 0x47effffff0000000ULL) return Literal(std::numeric_limits<float>::max()); - if (bits > 0xc7efffffe0000000ULL && bits < 0xc7effffff0000000ULL) return Literal(-std::numeric_limits<float>::max()); + if (bits > 0x47efffffe0000000ULL && bits < 0x47effffff0000000ULL) + return Literal(std::numeric_limits<float>::max()); + if (bits > 0xc7efffffe0000000ULL && bits < 0xc7effffff0000000ULL) + return Literal(-std::numeric_limits<float>::max()); // when we must convert to infinity, do that - if (f64 < -std::numeric_limits<float>::max()) return Literal(-std::numeric_limits<float>::infinity()); - if (f64 > std::numeric_limits<float>::max()) return Literal(std::numeric_limits<float>::infinity()); + if (f64 < -std::numeric_limits<float>::max()) + return Literal(-std::numeric_limits<float>::infinity()); + if (f64 > std::numeric_limits<float>::max()) + return Literal(std::numeric_limits<float>::infinity()); return Literal(float(getf64())); } Literal Literal::add(const Literal& other) const { switch (type) { - case Type::i32: return Literal(uint32_t(i32) + uint32_t(other.i32)); - case Type::i64: return Literal(uint64_t(i64) + uint64_t(other.i64)); - case Type::f32: return Literal(getf32() + other.getf32()); - case Type::f64: return Literal(getf64() + other.getf64()); + case Type::i32: + return Literal(uint32_t(i32) + uint32_t(other.i32)); + case Type::i64: + return Literal(uint64_t(i64) + uint64_t(other.i64)); + case Type::f32: + return Literal(getf32() + other.getf32()); + case Type::f64: + return Literal(getf64() + other.getf64()); case Type::v128: case Type::none: - case Type::unreachable: WASM_UNREACHABLE(); + case Type::unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } Literal Literal::sub(const Literal& other) const { switch (type) { - case Type::i32: return Literal(uint32_t(i32) - uint32_t(other.i32)); - case Type::i64: return Literal(uint64_t(i64) - uint64_t(other.i64)); - case Type::f32: return Literal(getf32() - other.getf32()); - case Type::f64: return Literal(getf64() - other.getf64()); + case Type::i32: + return Literal(uint32_t(i32) - uint32_t(other.i32)); + case Type::i64: + return Literal(uint64_t(i64) - uint64_t(other.i64)); + case Type::f32: + return Literal(getf32() - other.getf32()); + case Type::f64: + return Literal(getf64() - other.getf64()); case Type::v128: case Type::none: - case Type::unreachable: WASM_UNREACHABLE(); + case Type::unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } -template<typename T> -static T add_sat_s(T a, T b) { - static_assert(std::is_signed<T>::value, "Trying to instantiate add_sat_s with unsigned type"); +template<typename T> static T add_sat_s(T a, T b) { + static_assert(std::is_signed<T>::value, + "Trying to instantiate add_sat_s with unsigned type"); using UT = typename std::make_unsigned<T>::type; UT ua = static_cast<UT>(a); UT ub = static_cast<UT>(b); UT ures = ua + ub; // overflow if sign of result is different from sign of a and b if (static_cast<T>((ures ^ ua) & (ures ^ ub)) < 0) { - return (a < 0) - ? std::numeric_limits<T>::min() - : std::numeric_limits<T>::max(); + return (a < 0) ? std::numeric_limits<T>::min() + : std::numeric_limits<T>::max(); } return static_cast<T>(ures); } -template<typename T> -static T sub_sat_s(T a, T b) { - static_assert(std::is_signed<T>::value, "Trying to instantiate sub_sat_s with unsigned type"); +template<typename T> static T sub_sat_s(T a, T b) { + static_assert(std::is_signed<T>::value, + "Trying to instantiate sub_sat_s with unsigned type"); using UT = typename std::make_unsigned<T>::type; UT ua = static_cast<UT>(a); UT ub = static_cast<UT>(b); UT ures = ua - ub; // overflow if a and b have different signs and result and a differ in sign if (static_cast<T>((ua ^ ub) & (ures ^ ua)) < 0) { - return (a < 0) - ? std::numeric_limits<T>::min() - : std::numeric_limits<T>::max(); + return (a < 0) ? std::numeric_limits<T>::min() + : std::numeric_limits<T>::max(); } return static_cast<T>(ures); } -template<typename T> -static T add_sat_u(T a, T b) { - static_assert(std::is_unsigned<T>::value, "Trying to instantiate add_sat_u with signed type"); +template<typename T> static T add_sat_u(T a, T b) { + static_assert(std::is_unsigned<T>::value, + "Trying to instantiate add_sat_u with signed type"); T res = a + b; // overflow if result is less than arguments return (res < a) ? std::numeric_limits<T>::max() : res; } -template<typename T> -static T sub_sat_u(T a, T b) { - static_assert(std::is_unsigned<T>::value, "Trying to instantiate sub_sat_u with signed type"); +template<typename T> static T sub_sat_u(T a, T b) { + static_assert(std::is_unsigned<T>::value, + "Trying to instantiate sub_sat_u with signed type"); T res = a - b; // overflow if result is greater than a return (res > a) ? 0 : res; @@ -602,13 +680,18 @@ Literal Literal::subSatUI16(const Literal& other) const { Literal Literal::mul(const Literal& other) const { switch (type) { - case Type::i32: return Literal(uint32_t(i32) * uint32_t(other.i32)); - case Type::i64: return Literal(uint64_t(i64) * uint64_t(other.i64)); - case Type::f32: return Literal(getf32() * other.getf32()); - case Type::f64: return Literal(getf64() * other.getf64()); + case Type::i32: + return Literal(uint32_t(i32) * uint32_t(other.i32)); + case Type::i64: + return Literal(uint64_t(i64) * uint64_t(other.i64)); + case Type::f32: + return Literal(getf32() * other.getf32()); + case Type::f64: + return Literal(getf64() * other.getf64()); case Type::v128: case Type::none: - case Type::unreachable: WASM_UNREACHABLE(); + case Type::unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } @@ -620,19 +703,27 @@ Literal Literal::div(const Literal& other) const { float sign = std::signbit(lhs) == std::signbit(rhs) ? 0.f : -0.f; switch (std::fpclassify(rhs)) { case FP_ZERO: - switch (std::fpclassify(lhs)) { - case FP_NAN: return Literal(setQuietNaN(lhs)); - case FP_ZERO: return Literal(std::copysign(std::numeric_limits<float>::quiet_NaN(), sign)); - case FP_NORMAL: // fallthrough - case FP_SUBNORMAL: // fallthrough - case FP_INFINITE: return Literal(std::copysign(std::numeric_limits<float>::infinity(), sign)); - default: WASM_UNREACHABLE(); - } - case FP_NAN: // fallthrough + switch (std::fpclassify(lhs)) { + case FP_NAN: + return Literal(setQuietNaN(lhs)); + case FP_ZERO: + return Literal( + std::copysign(std::numeric_limits<float>::quiet_NaN(), sign)); + case FP_NORMAL: // fallthrough + case FP_SUBNORMAL: // fallthrough + case FP_INFINITE: + return Literal( + std::copysign(std::numeric_limits<float>::infinity(), sign)); + default: + WASM_UNREACHABLE(); + } + case FP_NAN: // fallthrough case FP_INFINITE: // fallthrough - case FP_NORMAL: // fallthrough - case FP_SUBNORMAL: return Literal(lhs / rhs); - default: WASM_UNREACHABLE(); + case FP_NORMAL: // fallthrough + case FP_SUBNORMAL: + return Literal(lhs / rhs); + default: + WASM_UNREACHABLE(); } } case Type::f64: { @@ -640,240 +731,335 @@ Literal Literal::div(const Literal& other) const { double sign = std::signbit(lhs) == std::signbit(rhs) ? 0. : -0.; switch (std::fpclassify(rhs)) { case FP_ZERO: - switch (std::fpclassify(lhs)) { - case FP_NAN: return Literal(setQuietNaN(lhs)); - case FP_ZERO: return Literal(std::copysign(std::numeric_limits<double>::quiet_NaN(), sign)); - case FP_NORMAL: // fallthrough - case FP_SUBNORMAL: // fallthrough - case FP_INFINITE: return Literal(std::copysign(std::numeric_limits<double>::infinity(), sign)); - default: WASM_UNREACHABLE(); - } - case FP_NAN: // fallthrough + switch (std::fpclassify(lhs)) { + case FP_NAN: + return Literal(setQuietNaN(lhs)); + case FP_ZERO: + return Literal( + std::copysign(std::numeric_limits<double>::quiet_NaN(), sign)); + case FP_NORMAL: // fallthrough + case FP_SUBNORMAL: // fallthrough + case FP_INFINITE: + return Literal( + std::copysign(std::numeric_limits<double>::infinity(), sign)); + default: + WASM_UNREACHABLE(); + } + case FP_NAN: // fallthrough case FP_INFINITE: // fallthrough - case FP_NORMAL: // fallthrough - case FP_SUBNORMAL: return Literal(lhs / rhs); - default: WASM_UNREACHABLE(); + case FP_NORMAL: // fallthrough + case FP_SUBNORMAL: + return Literal(lhs / rhs); + default: + WASM_UNREACHABLE(); } } - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } } Literal Literal::divS(const Literal& other) const { switch (type) { - case Type::i32: return Literal(i32 / other.i32); - case Type::i64: return Literal(i64 / other.i64); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(i32 / other.i32); + case Type::i64: + return Literal(i64 / other.i64); + default: + WASM_UNREACHABLE(); } } Literal Literal::divU(const Literal& other) const { switch (type) { - case Type::i32: return Literal(uint32_t(i32) / uint32_t(other.i32)); - case Type::i64: return Literal(uint64_t(i64) / uint64_t(other.i64)); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(uint32_t(i32) / uint32_t(other.i32)); + case Type::i64: + return Literal(uint64_t(i64) / uint64_t(other.i64)); + default: + WASM_UNREACHABLE(); } } Literal Literal::remS(const Literal& other) const { switch (type) { - case Type::i32: return Literal(i32 % other.i32); - case Type::i64: return Literal(i64 % other.i64); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(i32 % other.i32); + case Type::i64: + return Literal(i64 % other.i64); + default: + WASM_UNREACHABLE(); } } Literal Literal::remU(const Literal& other) const { switch (type) { - case Type::i32: return Literal(uint32_t(i32) % uint32_t(other.i32)); - case Type::i64: return Literal(uint64_t(i64) % uint64_t(other.i64)); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(uint32_t(i32) % uint32_t(other.i32)); + case Type::i64: + return Literal(uint64_t(i64) % uint64_t(other.i64)); + default: + WASM_UNREACHABLE(); } } Literal Literal::and_(const Literal& other) const { switch (type) { - case Type::i32: return Literal(i32 & other.i32); - case Type::i64: return Literal(i64 & other.i64); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(i32 & other.i32); + case Type::i64: + return Literal(i64 & other.i64); + default: + WASM_UNREACHABLE(); } } Literal Literal::or_(const Literal& other) const { switch (type) { - case Type::i32: return Literal(i32 | other.i32); - case Type::i64: return Literal(i64 | other.i64); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(i32 | other.i32); + case Type::i64: + return Literal(i64 | other.i64); + default: + WASM_UNREACHABLE(); } } Literal Literal::xor_(const Literal& other) const { switch (type) { - case Type::i32: return Literal(i32 ^ other.i32); - case Type::i64: return Literal(i64 ^ other.i64); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(i32 ^ other.i32); + case Type::i64: + return Literal(i64 ^ other.i64); + default: + WASM_UNREACHABLE(); } } Literal Literal::shl(const Literal& other) const { switch (type) { - case Type::i32: return Literal(uint32_t(i32) << Bits::getEffectiveShifts(other.i32, Type::i32)); - case Type::i64: return Literal(uint64_t(i64) << Bits::getEffectiveShifts(other.i64, Type::i64)); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(uint32_t(i32) + << Bits::getEffectiveShifts(other.i32, Type::i32)); + case Type::i64: + return Literal(uint64_t(i64) + << Bits::getEffectiveShifts(other.i64, Type::i64)); + default: + WASM_UNREACHABLE(); } } Literal Literal::shrS(const Literal& other) const { switch (type) { - case Type::i32: return Literal(i32 >> Bits::getEffectiveShifts(other.i32, Type::i32)); - case Type::i64: return Literal(i64 >> Bits::getEffectiveShifts(other.i64, Type::i64)); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(i32 >> Bits::getEffectiveShifts(other.i32, Type::i32)); + case Type::i64: + return Literal(i64 >> Bits::getEffectiveShifts(other.i64, Type::i64)); + default: + WASM_UNREACHABLE(); } } Literal Literal::shrU(const Literal& other) const { switch (type) { - case Type::i32: return Literal(uint32_t(i32) >> Bits::getEffectiveShifts(other.i32, Type::i32)); - case Type::i64: return Literal(uint64_t(i64) >> Bits::getEffectiveShifts(other.i64, Type::i64)); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(uint32_t(i32) >> + Bits::getEffectiveShifts(other.i32, Type::i32)); + case Type::i64: + return Literal(uint64_t(i64) >> + Bits::getEffectiveShifts(other.i64, Type::i64)); + default: + WASM_UNREACHABLE(); } } Literal Literal::rotL(const Literal& other) const { switch (type) { - case Type::i32: return Literal(RotateLeft(uint32_t(i32), uint32_t(other.i32))); - case Type::i64: return Literal(RotateLeft(uint64_t(i64), uint64_t(other.i64))); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(RotateLeft(uint32_t(i32), uint32_t(other.i32))); + case Type::i64: + return Literal(RotateLeft(uint64_t(i64), uint64_t(other.i64))); + default: + WASM_UNREACHABLE(); } } Literal Literal::rotR(const Literal& other) const { switch (type) { - case Type::i32: return Literal(RotateRight(uint32_t(i32), uint32_t(other.i32))); - case Type::i64: return Literal(RotateRight(uint64_t(i64), uint64_t(other.i64))); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(RotateRight(uint32_t(i32), uint32_t(other.i32))); + case Type::i64: + return Literal(RotateRight(uint64_t(i64), uint64_t(other.i64))); + default: + WASM_UNREACHABLE(); } } Literal Literal::eq(const Literal& other) const { switch (type) { - case Type::i32: return Literal(i32 == other.i32); - case Type::i64: return Literal(i64 == other.i64); - case Type::f32: return Literal(getf32() == other.getf32()); - case Type::f64: return Literal(getf64() == other.getf64()); + case Type::i32: + return Literal(i32 == other.i32); + case Type::i64: + return Literal(i64 == other.i64); + case Type::f32: + return Literal(getf32() == other.getf32()); + case Type::f64: + return Literal(getf64() == other.getf64()); case Type::v128: case Type::none: - case Type::unreachable: WASM_UNREACHABLE(); + case Type::unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } Literal Literal::ne(const Literal& other) const { switch (type) { - case Type::i32: return Literal(i32 != other.i32); - case Type::i64: return Literal(i64 != other.i64); - case Type::f32: return Literal(getf32() != other.getf32()); - case Type::f64: return Literal(getf64() != other.getf64()); + case Type::i32: + return Literal(i32 != other.i32); + case Type::i64: + return Literal(i64 != other.i64); + case Type::f32: + return Literal(getf32() != other.getf32()); + case Type::f64: + return Literal(getf64() != other.getf64()); case Type::v128: case Type::none: - case Type::unreachable: WASM_UNREACHABLE(); + case Type::unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } Literal Literal::ltS(const Literal& other) const { switch (type) { - case Type::i32: return Literal(i32 < other.i32); - case Type::i64: return Literal(i64 < other.i64); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(i32 < other.i32); + case Type::i64: + return Literal(i64 < other.i64); + default: + WASM_UNREACHABLE(); } } Literal Literal::ltU(const Literal& other) const { switch (type) { - case Type::i32: return Literal(uint32_t(i32) < uint32_t(other.i32)); - case Type::i64: return Literal(uint64_t(i64) < uint64_t(other.i64)); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(uint32_t(i32) < uint32_t(other.i32)); + case Type::i64: + return Literal(uint64_t(i64) < uint64_t(other.i64)); + default: + WASM_UNREACHABLE(); } } Literal Literal::lt(const Literal& other) const { switch (type) { - case Type::f32: return Literal(getf32() < other.getf32()); - case Type::f64: return Literal(getf64() < other.getf64()); - default: WASM_UNREACHABLE(); + case Type::f32: + return Literal(getf32() < other.getf32()); + case Type::f64: + return Literal(getf64() < other.getf64()); + default: + WASM_UNREACHABLE(); } } Literal Literal::leS(const Literal& other) const { switch (type) { - case Type::i32: return Literal(i32 <= other.i32); - case Type::i64: return Literal(i64 <= other.i64); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(i32 <= other.i32); + case Type::i64: + return Literal(i64 <= other.i64); + default: + WASM_UNREACHABLE(); } } Literal Literal::leU(const Literal& other) const { switch (type) { - case Type::i32: return Literal(uint32_t(i32) <= uint32_t(other.i32)); - case Type::i64: return Literal(uint64_t(i64) <= uint64_t(other.i64)); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(uint32_t(i32) <= uint32_t(other.i32)); + case Type::i64: + return Literal(uint64_t(i64) <= uint64_t(other.i64)); + default: + WASM_UNREACHABLE(); } } Literal Literal::le(const Literal& other) const { switch (type) { - case Type::f32: return Literal(getf32() <= other.getf32()); - case Type::f64: return Literal(getf64() <= other.getf64()); - default: WASM_UNREACHABLE(); + case Type::f32: + return Literal(getf32() <= other.getf32()); + case Type::f64: + return Literal(getf64() <= other.getf64()); + default: + WASM_UNREACHABLE(); } } Literal Literal::gtS(const Literal& other) const { switch (type) { - case Type::i32: return Literal(i32 > other.i32); - case Type::i64: return Literal(i64 > other.i64); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(i32 > other.i32); + case Type::i64: + return Literal(i64 > other.i64); + default: + WASM_UNREACHABLE(); } } Literal Literal::gtU(const Literal& other) const { switch (type) { - case Type::i32: return Literal(uint32_t(i32) > uint32_t(other.i32)); - case Type::i64: return Literal(uint64_t(i64) > uint64_t(other.i64)); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(uint32_t(i32) > uint32_t(other.i32)); + case Type::i64: + return Literal(uint64_t(i64) > uint64_t(other.i64)); + default: + WASM_UNREACHABLE(); } } Literal Literal::gt(const Literal& other) const { switch (type) { - case Type::f32: return Literal(getf32() > other.getf32()); - case Type::f64: return Literal(getf64() > other.getf64()); - default: WASM_UNREACHABLE(); + case Type::f32: + return Literal(getf32() > other.getf32()); + case Type::f64: + return Literal(getf64() > other.getf64()); + default: + WASM_UNREACHABLE(); } } Literal Literal::geS(const Literal& other) const { switch (type) { - case Type::i32: return Literal(i32 >= other.i32); - case Type::i64: return Literal(i64 >= other.i64); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(i32 >= other.i32); + case Type::i64: + return Literal(i64 >= other.i64); + default: + WASM_UNREACHABLE(); } } Literal Literal::geU(const Literal& other) const { switch (type) { - case Type::i32: return Literal(uint32_t(i32) >= uint32_t(other.i32)); - case Type::i64: return Literal(uint64_t(i64) >= uint64_t(other.i64)); - default: WASM_UNREACHABLE(); + case Type::i32: + return Literal(uint32_t(i32) >= uint32_t(other.i32)); + case Type::i64: + return Literal(uint64_t(i64) >= uint64_t(other.i64)); + default: + WASM_UNREACHABLE(); } } Literal Literal::ge(const Literal& other) const { switch (type) { - case Type::f32: return Literal(getf32() >= other.getf32()); - case Type::f64: return Literal(getf64() >= other.getf64()); - default: WASM_UNREACHABLE(); + case Type::f32: + return Literal(getf32() >= other.getf32()); + case Type::f64: + return Literal(getf64() >= other.getf64()); + default: + WASM_UNREACHABLE(); } } @@ -881,23 +1067,36 @@ Literal Literal::min(const Literal& other) const { switch (type) { case Type::f32: { auto l = getf32(), r = other.getf32(); - if (l == r && l == 0) return Literal(std::signbit(l) ? l : r); + if (l == r && l == 0) + return Literal(std::signbit(l) ? l : r); auto result = std::min(l, r); bool lnan = std::isnan(l), rnan = std::isnan(r); - if (!std::isnan(result) && !lnan && !rnan) return Literal(result); - if (!lnan && !rnan) return Literal((int32_t)0x7fc00000).castToF32(); - return Literal(lnan ? l : r).castToI32().or_(Literal(0xc00000)).castToF32(); + if (!std::isnan(result) && !lnan && !rnan) + return Literal(result); + if (!lnan && !rnan) + return Literal((int32_t)0x7fc00000).castToF32(); + return Literal(lnan ? l : r) + .castToI32() + .or_(Literal(0xc00000)) + .castToF32(); } case Type::f64: { auto l = getf64(), r = other.getf64(); - if (l == r && l == 0) return Literal(std::signbit(l) ? l : r); + if (l == r && l == 0) + return Literal(std::signbit(l) ? l : r); auto result = std::min(l, r); bool lnan = std::isnan(l), rnan = std::isnan(r); - if (!std::isnan(result) && !lnan && !rnan) return Literal(result); - if (!lnan && !rnan) return Literal((int64_t)0x7ff8000000000000LL).castToF64(); - return Literal(lnan ? l : r).castToI64().or_(Literal(int64_t(0x8000000000000LL))).castToF64(); + if (!std::isnan(result) && !lnan && !rnan) + return Literal(result); + if (!lnan && !rnan) + return Literal((int64_t)0x7ff8000000000000LL).castToF64(); + return Literal(lnan ? l : r) + .castToI64() + .or_(Literal(int64_t(0x8000000000000LL))) + .castToF64(); } - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } } @@ -905,32 +1104,52 @@ Literal Literal::max(const Literal& other) const { switch (type) { case Type::f32: { auto l = getf32(), r = other.getf32(); - if (l == r && l == 0) return Literal(std::signbit(l) ? r : l); + if (l == r && l == 0) + return Literal(std::signbit(l) ? r : l); auto result = std::max(l, r); bool lnan = std::isnan(l), rnan = std::isnan(r); - if (!std::isnan(result) && !lnan && !rnan) return Literal(result); - if (!lnan && !rnan) return Literal((int32_t)0x7fc00000).castToF32(); - return Literal(lnan ? l : r).castToI32().or_(Literal(0xc00000)).castToF32(); + if (!std::isnan(result) && !lnan && !rnan) + return Literal(result); + if (!lnan && !rnan) + return Literal((int32_t)0x7fc00000).castToF32(); + return Literal(lnan ? l : r) + .castToI32() + .or_(Literal(0xc00000)) + .castToF32(); } case Type::f64: { auto l = getf64(), r = other.getf64(); - if (l == r && l == 0) return Literal(std::signbit(l) ? r : l); + if (l == r && l == 0) + return Literal(std::signbit(l) ? r : l); auto result = std::max(l, r); bool lnan = std::isnan(l), rnan = std::isnan(r); - if (!std::isnan(result) && !lnan && !rnan) return Literal(result); - if (!lnan && !rnan) return Literal((int64_t)0x7ff8000000000000LL).castToF64(); - return Literal(lnan ? l : r).castToI64().or_(Literal(int64_t(0x8000000000000LL))).castToF64(); + if (!std::isnan(result) && !lnan && !rnan) + return Literal(result); + if (!lnan && !rnan) + return Literal((int64_t)0x7ff8000000000000LL).castToF64(); + return Literal(lnan ? l : r) + .castToI64() + .or_(Literal(int64_t(0x8000000000000LL))) + .castToF64(); } - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } } Literal Literal::copysign(const Literal& other) const { // operate on bits directly, to avoid signalling bit being set on a float switch (type) { - case Type::f32: return Literal((i32 & 0x7fffffff) | (other.i32 & 0x80000000)).castToF32(); break; - case Type::f64: return Literal((i64 & 0x7fffffffffffffffUL) | (other.i64 & 0x8000000000000000UL)).castToF64(); break; - default: WASM_UNREACHABLE(); + case Type::f32: + return Literal((i32 & 0x7fffffff) | (other.i32 & 0x80000000)).castToF32(); + break; + case Type::f64: + return Literal((i64 & 0x7fffffffffffffffUL) | + (other.i64 & 0x8000000000000000UL)) + .castToF64(); + break; + default: + WASM_UNREACHABLE(); } } @@ -943,7 +1162,8 @@ static LaneArray<Lanes> getLanes(const Literal& val) { for (size_t lane_index = 0; lane_index < Lanes; ++lane_index) { LaneT lane(0); for (size_t offset = 0; offset < lane_width; ++offset) { - lane |= LaneT(bytes.at(lane_index * lane_width + offset)) << LaneT(8 * offset); + lane |= LaneT(bytes.at(lane_index * lane_width + offset)) + << LaneT(8 * offset); } lanes.at(lane_index) = Literal(lane); } @@ -983,7 +1203,8 @@ LaneArray<2> Literal::getLanesF64x2() const { return lanes; } -Literal Literal::shuffleV8x16(const Literal& other, const std::array<uint8_t, 16>& mask) const { +Literal Literal::shuffleV8x16(const Literal& other, + const std::array<uint8_t, 16>& mask) const { assert(type == Type::v128); uint8_t bytes[16]; for (size_t i = 0; i < mask.size(); ++i) { @@ -992,8 +1213,7 @@ Literal Literal::shuffleV8x16(const Literal& other, const std::array<uint8_t, 16 return Literal(bytes); } -template<Type Ty, int Lanes> -static Literal splat(const Literal& val) { +template<Type Ty, int Lanes> static Literal splat(const Literal& val) { assert(val.type == Ty); LaneArray<Lanes> lanes; lanes.fill(val); @@ -1007,17 +1227,34 @@ Literal Literal::splatI64x2() const { return splat<Type::i64, 2>(*this); } Literal Literal::splatF32x4() const { return splat<Type::f32, 4>(*this); } Literal Literal::splatF64x2() const { return splat<Type::f64, 2>(*this); } -Literal Literal::extractLaneSI8x16(uint8_t index) const { return getLanesSI8x16().at(index); } -Literal Literal::extractLaneUI8x16(uint8_t index) const { return getLanesUI8x16().at(index); } -Literal Literal::extractLaneSI16x8(uint8_t index) const { return getLanesSI16x8().at(index); } -Literal Literal::extractLaneUI16x8(uint8_t index) const { return getLanesUI16x8().at(index); } -Literal Literal::extractLaneI32x4(uint8_t index) const { return getLanesI32x4().at(index); } -Literal Literal::extractLaneI64x2(uint8_t index) const { return getLanesI64x2().at(index); } -Literal Literal::extractLaneF32x4(uint8_t index) const { return getLanesF32x4().at(index); } -Literal Literal::extractLaneF64x2(uint8_t index) const { return getLanesF64x2().at(index); } +Literal Literal::extractLaneSI8x16(uint8_t index) const { + return getLanesSI8x16().at(index); +} +Literal Literal::extractLaneUI8x16(uint8_t index) const { + return getLanesUI8x16().at(index); +} +Literal Literal::extractLaneSI16x8(uint8_t index) const { + return getLanesSI16x8().at(index); +} +Literal Literal::extractLaneUI16x8(uint8_t index) const { + return getLanesUI16x8().at(index); +} +Literal Literal::extractLaneI32x4(uint8_t index) const { + return getLanesI32x4().at(index); +} +Literal Literal::extractLaneI64x2(uint8_t index) const { + return getLanesI64x2().at(index); +} +Literal Literal::extractLaneF32x4(uint8_t index) const { + return getLanesF32x4().at(index); +} +Literal Literal::extractLaneF64x2(uint8_t index) const { + return getLanesF64x2().at(index); +} template<int Lanes, LaneArray<Lanes> (Literal::*IntoLanes)() const> -static Literal replace(const Literal& val, const Literal& other, uint8_t index) { +static Literal +replace(const Literal& val, const Literal& other, uint8_t index) { LaneArray<Lanes> lanes = (val.*IntoLanes)(); lanes.at(index) = other; auto ret = Literal(lanes); @@ -1043,7 +1280,8 @@ Literal Literal::replaceLaneF64x2(const Literal& other, uint8_t index) const { return replace<2, &Literal::getLanesF64x2>(*this, other, index); } -template<int Lanes, LaneArray<Lanes> (Literal::*IntoLanes)() const, +template<int Lanes, + LaneArray<Lanes> (Literal::*IntoLanes)() const, Literal (Literal::*UnaryOp)(void) const> static Literal unary(const Literal& val) { LaneArray<Lanes> lanes = (val.*IntoLanes)(); @@ -1160,14 +1398,16 @@ Literal Literal::allTrueI64x2() const { return all_true<2, &Literal::getLanesI64x2>(*this); } -template<int Lanes, LaneArray<Lanes> (Literal::*IntoLanes)() const, +template<int Lanes, + LaneArray<Lanes> (Literal::*IntoLanes)() const, Literal (Literal::*ShiftOp)(const Literal&) const> static Literal shift(const Literal& vec, const Literal& shift) { assert(shift.type == Type::i32); size_t lane_bits = 128 / Lanes; LaneArray<Lanes> lanes = (vec.*IntoLanes)(); for (size_t i = 0; i < Lanes; ++i) { - lanes[i] = (lanes[i].*ShiftOp)(Literal(int32_t(shift.geti32() % lane_bits))); + lanes[i] = + (lanes[i].*ShiftOp)(Literal(int32_t(shift.geti32() % lane_bits))); } return Literal(lanes); } @@ -1209,7 +1449,8 @@ Literal Literal::shrUI64x2(const Literal& other) const { return shift<2, &Literal::getLanesI64x2, &Literal::shrU>(*this, other); } -template<int Lanes, LaneArray<Lanes> (Literal::*IntoLanes)() const, +template<int Lanes, + LaneArray<Lanes> (Literal::*IntoLanes)() const, Literal (Literal::*CompareOp)(const Literal&) const, typename LaneT = int32_t> static Literal compare(const Literal& val, const Literal& other) { @@ -1217,8 +1458,8 @@ static Literal compare(const Literal& val, const Literal& other) { LaneArray<Lanes> other_lanes = (other.*IntoLanes)(); for (size_t i = 0; i < Lanes; ++i) { lanes[i] = (lanes[i].*CompareOp)(other_lanes[i]) == Literal(int32_t(1)) - ? Literal(LaneT(-1)) - : Literal(LaneT(0)); + ? Literal(LaneT(-1)) + : Literal(LaneT(0)); } return Literal(lanes); } @@ -1235,7 +1476,7 @@ Literal Literal::ltSI8x16(const Literal& other) const { Literal Literal::ltUI8x16(const Literal& other) const { return compare<16, &Literal::getLanesUI8x16, &Literal::ltU>(*this, other); } -Literal Literal::gtSI8x16(const Literal& other) const { +Literal Literal::gtSI8x16(const Literal& other) const { return compare<16, &Literal::getLanesSI8x16, &Literal::gtS>(*this, other); } Literal Literal::gtUI8x16(const Literal& other) const { @@ -1332,26 +1573,33 @@ Literal Literal::geF32x4(const Literal& other) const { return compare<4, &Literal::getLanesF32x4, &Literal::ge>(*this, other); } Literal Literal::eqF64x2(const Literal& other) const { - return compare<2, &Literal::getLanesF64x2, &Literal::eq, int64_t>(*this, other); + return compare<2, &Literal::getLanesF64x2, &Literal::eq, int64_t>(*this, + other); } Literal Literal::neF64x2(const Literal& other) const { - return compare<2, &Literal::getLanesF64x2, &Literal::ne, int64_t>(*this, other); + return compare<2, &Literal::getLanesF64x2, &Literal::ne, int64_t>(*this, + other); } Literal Literal::ltF64x2(const Literal& other) const { - return compare<2, &Literal::getLanesF64x2, &Literal::lt, int64_t>(*this, other); + return compare<2, &Literal::getLanesF64x2, &Literal::lt, int64_t>(*this, + other); } Literal Literal::gtF64x2(const Literal& other) const { - return compare<2, &Literal::getLanesF64x2, &Literal::gt, int64_t>(*this, other); + return compare<2, &Literal::getLanesF64x2, &Literal::gt, int64_t>(*this, + other); } Literal Literal::leF64x2(const Literal& other) const { - return compare<2, &Literal::getLanesF64x2, &Literal::le, int64_t>(*this, other); + return compare<2, &Literal::getLanesF64x2, &Literal::le, int64_t>(*this, + other); } Literal Literal::geF64x2(const Literal& other) const { - return compare<2, &Literal::getLanesF64x2, &Literal::ge, int64_t>(*this, other); + return compare<2, &Literal::getLanesF64x2, &Literal::ge, int64_t>(*this, + other); } -template<int Lanes, LaneArray<Lanes> (Literal::*IntoLanes)() const, - Literal (Literal::*BinaryOp)(const Literal&) const> +template<int Lanes, + LaneArray<Lanes> (Literal::*IntoLanes)() const, + Literal (Literal::*BinaryOp)(const Literal&) const> static Literal binary(const Literal& val, const Literal& other) { LaneArray<Lanes> lanes = (val.*IntoLanes)(); LaneArray<Lanes> other_lanes = (other.*IntoLanes)(); @@ -1374,19 +1622,23 @@ Literal Literal::addI8x16(const Literal& other) const { return binary<16, &Literal::getLanesUI8x16, &Literal::add>(*this, other); } Literal Literal::addSaturateSI8x16(const Literal& other) const { - return binary<16, &Literal::getLanesUI8x16, &Literal::addSatSI8>(*this, other); + return binary<16, &Literal::getLanesUI8x16, &Literal::addSatSI8>(*this, + other); } Literal Literal::addSaturateUI8x16(const Literal& other) const { - return binary<16, &Literal::getLanesSI8x16, &Literal::addSatUI8>(*this, other); + return binary<16, &Literal::getLanesSI8x16, &Literal::addSatUI8>(*this, + other); } Literal Literal::subI8x16(const Literal& other) const { return binary<16, &Literal::getLanesUI8x16, &Literal::sub>(*this, other); } Literal Literal::subSaturateSI8x16(const Literal& other) const { - return binary<16, &Literal::getLanesUI8x16, &Literal::subSatSI8>(*this, other); + return binary<16, &Literal::getLanesUI8x16, &Literal::subSatSI8>(*this, + other); } Literal Literal::subSaturateUI8x16(const Literal& other) const { - return binary<16, &Literal::getLanesSI8x16, &Literal::subSatUI8>(*this, other); + return binary<16, &Literal::getLanesSI8x16, &Literal::subSatUI8>(*this, + other); } Literal Literal::mulI8x16(const Literal& other) const { return binary<16, &Literal::getLanesUI8x16, &Literal::mul>(*this, other); @@ -1395,19 +1647,23 @@ Literal Literal::addI16x8(const Literal& other) const { return binary<8, &Literal::getLanesUI16x8, &Literal::add>(*this, other); } Literal Literal::addSaturateSI16x8(const Literal& other) const { - return binary<8, &Literal::getLanesUI16x8, &Literal::addSatSI16>(*this, other); + return binary<8, &Literal::getLanesUI16x8, &Literal::addSatSI16>(*this, + other); } Literal Literal::addSaturateUI16x8(const Literal& other) const { - return binary<8, &Literal::getLanesSI16x8, &Literal::addSatUI16>(*this, other); + return binary<8, &Literal::getLanesSI16x8, &Literal::addSatUI16>(*this, + other); } Literal Literal::subI16x8(const Literal& other) const { return binary<8, &Literal::getLanesUI16x8, &Literal::sub>(*this, other); } Literal Literal::subSaturateSI16x8(const Literal& other) const { - return binary<8, &Literal::getLanesUI16x8, &Literal::subSatSI16>(*this, other); + return binary<8, &Literal::getLanesUI16x8, &Literal::subSatSI16>(*this, + other); } Literal Literal::subSaturateUI16x8(const Literal& other) const { - return binary<8, &Literal::getLanesSI16x8, &Literal::subSatUI16>(*this, other); + return binary<8, &Literal::getLanesSI16x8, &Literal::subSatUI16>(*this, + other); } Literal Literal::mulI16x8(const Literal& other) const { return binary<8, &Literal::getLanesUI16x8, &Literal::mul>(*this, other); @@ -1464,7 +1720,8 @@ Literal Literal::maxF64x2(const Literal& other) const { return binary<2, &Literal::getLanesF64x2, &Literal::max>(*this, other); } -Literal Literal::bitselectV128(const Literal& left, const Literal& right) const { +Literal Literal::bitselectV128(const Literal& left, + const Literal& right) const { return andV128(left).orV128(notV128().andV128(right)); } diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index b8c3f1049..6369a79fa 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -17,10 +17,10 @@ #include <algorithm> #include <fstream> -#include "wasm-binary.h" -#include "wasm-stack.h" #include "ir/module-utils.h" #include "support/bits.h" +#include "wasm-binary.h" +#include "wasm-stack.h" namespace wasm { @@ -30,7 +30,9 @@ void WasmBinaryWriter::prepare() { if (func->type.isNull()) { func->type = ensureFunctionType(getSig(func.get()), wasm)->name; } - // TODO: depending on upstream flux https://github.com/WebAssembly/spec/pull/301 might want this: assert(!func->type.isNull()); + // TODO: depending on upstream flux + // https://github.com/WebAssembly/spec/pull/301 might want this: + // assert(!func->type.isNull()); } ModuleUtils::BinaryIndexes indexes(*wasm); mappedFunctions = std::move(indexes.functionIndexes); @@ -61,9 +63,12 @@ void WasmBinaryWriter::write() { writeDataCount(); writeFunctions(); writeDataSegments(); - if (debugInfo) writeNames(); - if (sourceMap && !sourceMapUrl.empty()) writeSourceMapUrl(); - if (symbolMap.size() > 0) writeSymbolMap(); + if (debugInfo) + writeNames(); + if (sourceMap && !sourceMapUrl.empty()) + writeSourceMapUrl(); + if (symbolMap.size() > 0) + writeSymbolMap(); if (sourceMap) { writeSourceMapEpilog(); @@ -76,7 +81,8 @@ void WasmBinaryWriter::write() { } void WasmBinaryWriter::writeHeader() { - if (debug) std::cerr << "== writeHeader" << std::endl; + if (debug) + std::cerr << "== writeHeader" << std::endl; o << int32_t(BinaryConsts::Magic); // magic number \0asm o << int32_t(BinaryConsts::Version); } @@ -88,11 +94,12 @@ int32_t WasmBinaryWriter::writeU32LEBPlaceholder() { return ret; } -void WasmBinaryWriter::writeResizableLimits(Address initial, Address maximum, - bool hasMaximum, bool shared) { - uint32_t flags = - (hasMaximum ? (uint32_t) BinaryConsts::HasMaximum : 0U) | - (shared ? (uint32_t) BinaryConsts::IsShared : 0U); +void WasmBinaryWriter::writeResizableLimits(Address initial, + Address maximum, + bool hasMaximum, + bool shared) { + uint32_t flags = (hasMaximum ? (uint32_t)BinaryConsts::HasMaximum : 0U) | + (shared ? (uint32_t)BinaryConsts::IsShared : 0U); o << U32LEB(flags); o << U32LEB(initial); if (hasMaximum) { @@ -100,63 +107,76 @@ void WasmBinaryWriter::writeResizableLimits(Address initial, Address maximum, } } -template<typename T> -int32_t WasmBinaryWriter::startSection(T code) { +template<typename T> int32_t WasmBinaryWriter::startSection(T code) { o << U32LEB(code); - if (sourceMap) sourceMapLocationsSizeAtSectionStart = sourceMapLocations.size(); + if (sourceMap) + sourceMapLocationsSizeAtSectionStart = sourceMapLocations.size(); return writeU32LEBPlaceholder(); // section size to be filled in later } void WasmBinaryWriter::finishSection(int32_t start) { - int32_t size = o.size() - start - MaxLEB32Bytes; // section size does not include the reserved bytes of the size field itself + // section size does not include the reserved bytes of the size field itself + int32_t size = o.size() - start - MaxLEB32Bytes; auto sizeFieldSize = o.writeAt(start, U32LEB(size)); if (sizeFieldSize != MaxLEB32Bytes) { // we can save some room, nice assert(sizeFieldSize < MaxLEB32Bytes); - std::move(&o[start] + MaxLEB32Bytes, &o[start] + MaxLEB32Bytes + size, &o[start] + sizeFieldSize); + std::move(&o[start] + MaxLEB32Bytes, + &o[start] + MaxLEB32Bytes + size, + &o[start] + sizeFieldSize); auto adjustment = MaxLEB32Bytes - sizeFieldSize; o.resize(o.size() - adjustment); if (sourceMap) { - for (auto i = sourceMapLocationsSizeAtSectionStart; i < sourceMapLocations.size(); ++i) { + for (auto i = sourceMapLocationsSizeAtSectionStart; + i < sourceMapLocations.size(); + ++i) { sourceMapLocations[i].first -= adjustment; } } } } -int32_t WasmBinaryWriter::startSubsection(BinaryConsts::UserSections::Subsection code) { +int32_t +WasmBinaryWriter::startSubsection(BinaryConsts::UserSections::Subsection code) { return startSection(code); } -void WasmBinaryWriter::finishSubsection(int32_t start) { - finishSection(start); -} +void WasmBinaryWriter::finishSubsection(int32_t start) { finishSection(start); } void WasmBinaryWriter::writeStart() { - if (!wasm->start.is()) return; - if (debug) std::cerr << "== writeStart" << std::endl; + if (!wasm->start.is()) + return; + if (debug) + std::cerr << "== writeStart" << std::endl; auto start = startSection(BinaryConsts::Section::Start); o << U32LEB(getFunctionIndex(wasm->start.str)); finishSection(start); } void WasmBinaryWriter::writeMemory() { - if (!wasm->memory.exists || wasm->memory.imported()) return; - if (debug) std::cerr << "== writeMemory" << std::endl; + if (!wasm->memory.exists || wasm->memory.imported()) + return; + if (debug) + std::cerr << "== writeMemory" << std::endl; auto start = startSection(BinaryConsts::Section::Memory); o << U32LEB(1); // Define 1 memory - writeResizableLimits(wasm->memory.initial, wasm->memory.max, - wasm->memory.hasMax(), wasm->memory.shared); + writeResizableLimits(wasm->memory.initial, + wasm->memory.max, + wasm->memory.hasMax(), + wasm->memory.shared); finishSection(start); } void WasmBinaryWriter::writeTypes() { - if (wasm->functionTypes.size() == 0) return; - if (debug) std::cerr << "== writeTypes" << std::endl; + if (wasm->functionTypes.size() == 0) + return; + if (debug) + std::cerr << "== writeTypes" << std::endl; auto start = startSection(BinaryConsts::Section::Type); o << U32LEB(wasm->functionTypes.size()); for (auto& type : wasm->functionTypes) { - if (debug) std::cerr << "write one" << std::endl; + if (debug) + std::cerr << "write one" << std::endl; o << S32LEB(BinaryConsts::EncodedType::Func); o << U32LEB(type->params.size()); for (auto param : type->params) { @@ -175,15 +195,18 @@ void WasmBinaryWriter::writeTypes() { int32_t WasmBinaryWriter::getFunctionTypeIndex(Name type) { // TODO: optimize for (size_t i = 0; i < wasm->functionTypes.size(); i++) { - if (wasm->functionTypes[i]->name == type) return i; + if (wasm->functionTypes[i]->name == type) + return i; } abort(); } void WasmBinaryWriter::writeImports() { auto num = importInfo->getNumImports(); - if (num == 0) return; - if (debug) std::cerr << "== writeImports" << std::endl; + if (num == 0) + return; + if (debug) + std::cerr << "== writeImports" << std::endl; auto start = startSection(BinaryConsts::Section::Import); o << U32LEB(num); auto writeImportHeader = [&](Importable* import) { @@ -191,42 +214,54 @@ void WasmBinaryWriter::writeImports() { writeInlineString(import->base.str); }; ModuleUtils::iterImportedFunctions(*wasm, [&](Function* func) { - if (debug) std::cerr << "write one function" << std::endl; + if (debug) + std::cerr << "write one function" << std::endl; writeImportHeader(func); o << U32LEB(int32_t(ExternalKind::Function)); o << U32LEB(getFunctionTypeIndex(func->type)); }); ModuleUtils::iterImportedGlobals(*wasm, [&](Global* global) { - if (debug) std::cerr << "write one global" << std::endl; + if (debug) + std::cerr << "write one global" << std::endl; writeImportHeader(global); o << U32LEB(int32_t(ExternalKind::Global)); o << binaryType(global->type); o << U32LEB(global->mutable_); }); if (wasm->memory.imported()) { - if (debug) std::cerr << "write one memory" << std::endl; + if (debug) + std::cerr << "write one memory" << std::endl; writeImportHeader(&wasm->memory); o << U32LEB(int32_t(ExternalKind::Memory)); - writeResizableLimits(wasm->memory.initial, wasm->memory.max, - wasm->memory.hasMax(), wasm->memory.shared); + writeResizableLimits(wasm->memory.initial, + wasm->memory.max, + wasm->memory.hasMax(), + wasm->memory.shared); } if (wasm->table.imported()) { - if (debug) std::cerr << "write one table" << std::endl; + if (debug) + std::cerr << "write one table" << std::endl; writeImportHeader(&wasm->table); o << U32LEB(int32_t(ExternalKind::Table)); o << S32LEB(BinaryConsts::EncodedType::AnyFunc); - writeResizableLimits(wasm->table.initial, wasm->table.max, wasm->table.hasMax(), /*shared=*/false); + writeResizableLimits(wasm->table.initial, + wasm->table.max, + wasm->table.hasMax(), + /*shared=*/false); } finishSection(start); } void WasmBinaryWriter::writeFunctionSignatures() { - if (importInfo->getNumDefinedFunctions() == 0) return; - if (debug) std::cerr << "== writeFunctionSignatures" << std::endl; + if (importInfo->getNumDefinedFunctions() == 0) + return; + if (debug) + std::cerr << "== writeFunctionSignatures" << std::endl; auto start = startSection(BinaryConsts::Section::Function); o << U32LEB(importInfo->getNumDefinedFunctions()); ModuleUtils::iterDefinedFunctions(*wasm, [&](Function* func) { - if (debug) std::cerr << "write one" << std::endl; + if (debug) + std::cerr << "write one" << std::endl; o << U32LEB(getFunctionTypeIndex(func->type)); }); finishSection(start); @@ -237,27 +272,35 @@ void WasmBinaryWriter::writeExpression(Expression* curr) { } void WasmBinaryWriter::writeFunctions() { - if (importInfo->getNumDefinedFunctions() == 0) return; - if (debug) std::cerr << "== writeFunctions" << std::endl; + if (importInfo->getNumDefinedFunctions() == 0) + return; + if (debug) + std::cerr << "== writeFunctions" << std::endl; auto start = startSection(BinaryConsts::Section::Code); o << U32LEB(importInfo->getNumDefinedFunctions()); ModuleUtils::iterDefinedFunctions(*wasm, [&](Function* func) { size_t sourceMapLocationsSizeAtFunctionStart = sourceMapLocations.size(); - if (debug) std::cerr << "write one at" << o.size() << std::endl; + if (debug) + std::cerr << "write one at" << o.size() << std::endl; size_t sizePos = writeU32LEBPlaceholder(); size_t start = o.size(); - if (debug) std::cerr << "writing" << func->name << std::endl; + if (debug) + std::cerr << "writing" << func->name << std::endl; // Emit Stack IR if present, and if we can if (func->stackIR && !sourceMap) { - if (debug) std::cerr << "write Stack IR" << std::endl; + if (debug) + std::cerr << "write Stack IR" << std::endl; StackIRFunctionStackWriter<WasmBinaryWriter>(func, *this, o, debug); } else { - if (debug) std::cerr << "write Binaryen IR" << std::endl; + if (debug) + std::cerr << "write Binaryen IR" << std::endl; FunctionStackWriter<WasmBinaryWriter>(func, *this, o, sourceMap, debug); } size_t size = o.size() - start; assert(size <= std::numeric_limits<uint32_t>::max()); - if (debug) std::cerr << "body size: " << size << ", writing at " << sizePos << ", next starts at " << o.size() << std::endl; + if (debug) + std::cerr << "body size: " << size << ", writing at " << sizePos + << ", next starts at " << o.size() << std::endl; auto sizeFieldSize = o.writeAt(sizePos, U32LEB(size)); if (sizeFieldSize != MaxLEB32Bytes) { // we can save some room, nice @@ -266,24 +309,30 @@ void WasmBinaryWriter::writeFunctions() { auto adjustment = MaxLEB32Bytes - sizeFieldSize; o.resize(o.size() - adjustment); if (sourceMap) { - for (auto i = sourceMapLocationsSizeAtFunctionStart; i < sourceMapLocations.size(); ++i) { + for (auto i = sourceMapLocationsSizeAtFunctionStart; + i < sourceMapLocations.size(); + ++i) { sourceMapLocations[i].first -= adjustment; } } } - tableOfContents.functionBodies.emplace_back(func->name, sizePos + sizeFieldSize, size); + tableOfContents.functionBodies.emplace_back( + func->name, sizePos + sizeFieldSize, size); }); finishSection(start); } void WasmBinaryWriter::writeGlobals() { - if (importInfo->getNumDefinedGlobals() == 0) return; - if (debug) std::cerr << "== writeglobals" << std::endl; + if (importInfo->getNumDefinedGlobals() == 0) + return; + if (debug) + std::cerr << "== writeglobals" << std::endl; auto start = startSection(BinaryConsts::Section::Global); auto num = importInfo->getNumDefinedGlobals(); o << U32LEB(num); ModuleUtils::iterDefinedGlobals(*wasm, [&](Global* global) { - if (debug) std::cerr << "write one" << std::endl; + if (debug) + std::cerr << "write one" << std::endl; o << binaryType(global->type); o << U32LEB(global->mutable_); writeExpression(global->init); @@ -293,22 +342,33 @@ void WasmBinaryWriter::writeGlobals() { } void WasmBinaryWriter::writeExports() { - if (wasm->exports.size() == 0) return; - if (debug) std::cerr << "== writeexports" << std::endl; + if (wasm->exports.size() == 0) + return; + if (debug) + std::cerr << "== writeexports" << std::endl; auto start = startSection(BinaryConsts::Section::Export); o << U32LEB(wasm->exports.size()); for (auto& curr : wasm->exports) { - if (debug) std::cerr << "write one" << std::endl; + if (debug) + std::cerr << "write one" << std::endl; writeInlineString(curr->name.str); o << U32LEB(int32_t(curr->kind)); switch (curr->kind) { - case ExternalKind::Function: o << U32LEB(getFunctionIndex(curr->value)); break; - case ExternalKind::Table: o << U32LEB(0); break; - case ExternalKind::Memory: o << U32LEB(0); break; - case ExternalKind::Global: o << U32LEB(getGlobalIndex(curr->value)); break; - default: WASM_UNREACHABLE(); + case ExternalKind::Function: + o << U32LEB(getFunctionIndex(curr->value)); + break; + case ExternalKind::Table: + o << U32LEB(0); + break; + case ExternalKind::Memory: + o << U32LEB(0); + break; + case ExternalKind::Global: + o << U32LEB(getGlobalIndex(curr->value)); + break; + default: + WASM_UNREACHABLE(); } - } finishSection(start); } @@ -323,7 +383,8 @@ void WasmBinaryWriter::writeDataCount() { } void WasmBinaryWriter::writeDataSegments() { - if (wasm->memory.segments.size() == 0) return; + if (wasm->memory.segments.size() == 0) + return; if (wasm->memory.segments.size() > WebLimitations::MaxDataSegments) { std::cerr << "Some VMs may not accept this binary because it has a large " << "number of data segments. Run the limit-segments pass to " @@ -357,12 +418,17 @@ uint32_t WasmBinaryWriter::getGlobalIndex(Name name) { } void WasmBinaryWriter::writeFunctionTableDeclaration() { - if (!wasm->table.exists || wasm->table.imported()) return; - if (debug) std::cerr << "== writeFunctionTableDeclaration" << std::endl; + if (!wasm->table.exists || wasm->table.imported()) + return; + if (debug) + std::cerr << "== writeFunctionTableDeclaration" << std::endl; auto start = startSection(BinaryConsts::Section::Table); o << U32LEB(1); // Declare 1 table. o << S32LEB(BinaryConsts::EncodedType::AnyFunc); - writeResizableLimits(wasm->table.initial, wasm->table.max, wasm->table.hasMax(), /*shared=*/false); + writeResizableLimits(wasm->table.initial, + wasm->table.max, + wasm->table.hasMax(), + /*shared=*/false); finishSection(start); } @@ -370,12 +436,14 @@ void WasmBinaryWriter::writeTableElements() { if (!wasm->table.exists || wasm->table.segments.size() == 0) { return; } - if (debug) std::cerr << "== writeTableElements" << std::endl; + if (debug) + std::cerr << "== writeTableElements" << std::endl; auto start = startSection(BinaryConsts::Section::Element); o << U32LEB(wasm->table.segments.size()); for (auto& segment : wasm->table.segments) { - o << U32LEB(0); // Table index; 0 in the MVP (and binaryen IR only has 1 table) + // Table index; 0 in the MVP (and binaryen IR only has 1 table) + o << U32LEB(0); writeExpression(segment.offset); o << int8_t(BinaryConsts::End); o << U32LEB(segment.data.size()); @@ -392,11 +460,14 @@ void WasmBinaryWriter::writeNames() { hasContents = true; getFunctionIndex(wasm->functions[0]->name); // generate mappedFunctions } - if (!hasContents) return; - if (debug) std::cerr << "== writeNames" << std::endl; + if (!hasContents) + return; + if (debug) + std::cerr << "== writeNames" << std::endl; auto start = startSection(BinaryConsts::Section::User); writeInlineString(BinaryConsts::UserSections::Name); - auto substart = startSubsection(BinaryConsts::UserSections::Subsection::NameFunction); + auto substart = + startSubsection(BinaryConsts::UserSections::Subsection::NameFunction); o << U32LEB(mappedFunctions.size()); Index emitted = 0; auto add = [&](Function* curr) { @@ -413,7 +484,8 @@ void WasmBinaryWriter::writeNames() { } void WasmBinaryWriter::writeSourceMapUrl() { - if (debug) std::cerr << "== writeSourceMapUrl" << std::endl; + if (debug) + std::cerr << "== writeSourceMapUrl" << std::endl; auto start = startSection(BinaryConsts::Section::User); writeInlineString(BinaryConsts::UserSections::SourceMapUrl); writeInlineString(sourceMapUrl.c_str()); @@ -431,13 +503,14 @@ void WasmBinaryWriter::writeSymbolMap() { } void WasmBinaryWriter::initializeDebugInfo() { - lastDebugLocation = { 0, /* lineNumber = */ 1, 0 }; + lastDebugLocation = {0, /* lineNumber = */ 1, 0}; } void WasmBinaryWriter::writeSourceMapProlog() { *sourceMap << "{\"version\":3,\"sources\":["; for (size_t i = 0; i < wasm->debugInfoFileNames.size(); i++) { - if (i > 0) *sourceMap << ","; + if (i > 0) + *sourceMap << ","; // TODO respect JSON string encoding, e.g. quotes and control chars. *sourceMap << "\"" << wasm->debugInfoFileNames[i] << "\""; } @@ -456,15 +529,17 @@ static void writeBase64VLQ(std::ostream& out, int32_t n) { } // more VLG digit will follow -- add continuation bit (0x20), // base64 codes 'g'..'z', '0'..'9', '+', '/' - out << char(digit < 20 ? 'g' + digit : digit < 30 ? '0' + digit - 20 : digit == 30 ? '+' : '/'); + out << char(digit < 20 + ? 'g' + digit + : digit < 30 ? '0' + digit - 20 : digit == 30 ? '+' : '/'); } } void WasmBinaryWriter::writeSourceMapEpilog() { // write source map entries size_t lastOffset = 0; - Function::DebugLocation lastLoc = { 0, /* lineNumber = */ 1, 0 }; - for (const auto &offsetAndlocPair : sourceMapLocations) { + Function::DebugLocation lastLoc = {0, /* lineNumber = */ 1, 0}; + for (const auto& offsetAndlocPair : sourceMapLocations) { if (lastOffset > 0) { *sourceMap << ","; } @@ -473,7 +548,8 @@ void WasmBinaryWriter::writeSourceMapEpilog() { writeBase64VLQ(*sourceMap, int32_t(offset - lastOffset)); writeBase64VLQ(*sourceMap, int32_t(loc.fileIndex - lastLoc.fileIndex)); writeBase64VLQ(*sourceMap, int32_t(loc.lineNumber - lastLoc.lineNumber)); - writeBase64VLQ(*sourceMap, int32_t(loc.columnNumber - lastLoc.columnNumber)); + writeBase64VLQ(*sourceMap, + int32_t(loc.columnNumber - lastLoc.columnNumber)); lastLoc = loc; lastOffset = offset; } @@ -516,20 +592,26 @@ void WasmBinaryWriter::writeFeaturesSection() { // FeatureSet::toString() auto toString = [](FeatureSet::Feature f) { switch (f) { - case FeatureSet::Atomics: return "atomics"; - case FeatureSet::MutableGlobals: return "mutable-globals"; - case FeatureSet::TruncSat: return "nontrapping-fptoint"; - case FeatureSet::SIMD: return "simd128"; - case FeatureSet::BulkMemory: return "bulk-memory"; - case FeatureSet::SignExt: return "sign-ext"; - default: WASM_UNREACHABLE(); + case FeatureSet::Atomics: + return "atomics"; + case FeatureSet::MutableGlobals: + return "mutable-globals"; + case FeatureSet::TruncSat: + return "nontrapping-fptoint"; + case FeatureSet::SIMD: + return "simd128"; + case FeatureSet::BulkMemory: + return "bulk-memory"; + case FeatureSet::SignExt: + return "sign-ext"; + default: + WASM_UNREACHABLE(); } }; std::vector<const char*> features; - wasm->features.iterFeatures([&](FeatureSet::Feature f) { - features.push_back(toString(f)); - }); + wasm->features.iterFeatures( + [&](FeatureSet::Feature f) { features.push_back(toString(f)); }); auto start = startSection(BinaryConsts::User); writeInlineString(BinaryConsts::UserSections::TargetFeatures); @@ -541,7 +623,6 @@ void WasmBinaryWriter::writeFeaturesSection() { finishSection(start); } - void WasmBinaryWriter::writeDebugLocation(const Function::DebugLocation& loc) { if (loc == lastDebugLocation) { return; @@ -568,7 +649,8 @@ void WasmBinaryWriter::writeInlineString(const char* name) { } static bool isHexDigit(char ch) { - return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); + return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || + (ch >= 'A' && ch <= 'F'); } static int decodeHexNibble(char ch) { @@ -586,11 +668,13 @@ void WasmBinaryWriter::writeEscapedName(const char* name) { for (int32_t i = 0; i < size;) { char ch = name[i++]; // support only `\xx` escapes; ignore invalid or unsupported escapes - if (ch != '\\' || i + 1 >= size || !isHexDigit(name[i]) || !isHexDigit(name[i + 1])) { + if (ch != '\\' || i + 1 >= size || !isHexDigit(name[i]) || + !isHexDigit(name[i + 1])) { unescaped.push_back(ch); continue; } - unescaped.push_back(char((decodeHexNibble(name[i]) << 4) | decodeHexNibble(name[i + 1]))); + unescaped.push_back( + char((decodeHexNibble(name[i]) << 4) | decodeHexNibble(name[i + 1]))); i += 2; } writeInlineString(unescaped.c_str()); @@ -606,19 +690,25 @@ void WasmBinaryWriter::writeInlineBuffer(const char* data, size_t size) { void WasmBinaryWriter::emitBuffer(const char* data, size_t size) { assert(size > 0); buffersToWrite.emplace_back(data, size, o.size()); - o << uint32_t(0); // placeholder, we'll fill in the pointer to the buffer later when we have it + // placeholder, we'll fill in the pointer to the buffer later when we have it + o << uint32_t(0); } -void WasmBinaryWriter::emitString(const char *str) { - if (debug) std::cerr << "emitString " << str << std::endl; +void WasmBinaryWriter::emitString(const char* str) { + if (debug) + std::cerr << "emitString " << str << std::endl; emitBuffer(str, strlen(str) + 1); } void WasmBinaryWriter::finishUp() { - if (debug) std::cerr << "finishUp" << std::endl; + if (debug) + std::cerr << "finishUp" << std::endl; // finish buffers for (const auto& buffer : buffersToWrite) { - if (debug) std::cerr << "writing buffer" << (int)buffer.data[0] << "," << (int)buffer.data[1] << " at " << o.size() << " and pointer is at " << buffer.pointerLocation << std::endl; + if (debug) + std::cerr << "writing buffer" << (int)buffer.data[0] << "," + << (int)buffer.data[1] << " at " << o.size() + << " and pointer is at " << buffer.pointerLocation << std::endl; o.writeAt(buffer.pointerLocation, (uint32_t)o.size()); for (size_t i = 0; i < buffer.size; i++) { o << (uint8_t)buffer.data[i]; @@ -637,42 +727,71 @@ void WasmBinaryBuilder::read() { while (more()) { uint32_t sectionCode = getU32LEB(); uint32_t payloadLen = getU32LEB(); - if (pos + payloadLen > input.size()) throwError("Section extends beyond end of input"); + if (pos + payloadLen > input.size()) + throwError("Section extends beyond end of input"); auto oldPos = pos; - // note the section in the list of seen sections, as almost no sections can appear more than once, - // and verify those that shouldn't do not. - if (sectionCode != BinaryConsts::Section::User && sectionCode != BinaryConsts::Section::Code) { + // note the section in the list of seen sections, as almost no sections can + // appear more than once, and verify those that shouldn't do not. + if (sectionCode != BinaryConsts::Section::User && + sectionCode != BinaryConsts::Section::Code) { if (!seenSections.insert(BinaryConsts::Section(sectionCode)).second) { - throwError("section seen more than once: " + std::to_string(sectionCode)); + throwError("section seen more than once: " + + std::to_string(sectionCode)); } } switch (sectionCode) { - case BinaryConsts::Section::Start: readStart(); break; - case BinaryConsts::Section::Memory: readMemory(); break; - case BinaryConsts::Section::Type: readSignatures(); break; - case BinaryConsts::Section::Import: readImports(); break; - case BinaryConsts::Section::Function: readFunctionSignatures(); break; - case BinaryConsts::Section::Code: readFunctions(); break; - case BinaryConsts::Section::Export: readExports(); break; - case BinaryConsts::Section::Element: readTableElements(); break; + case BinaryConsts::Section::Start: + readStart(); + break; + case BinaryConsts::Section::Memory: + readMemory(); + break; + case BinaryConsts::Section::Type: + readSignatures(); + break; + case BinaryConsts::Section::Import: + readImports(); + break; + case BinaryConsts::Section::Function: + readFunctionSignatures(); + break; + case BinaryConsts::Section::Code: + readFunctions(); + break; + case BinaryConsts::Section::Export: + readExports(); + break; + case BinaryConsts::Section::Element: + readTableElements(); + break; case BinaryConsts::Section::Global: { readGlobals(); - // imports can read global imports, so we run getGlobalName and create the mapping - // but after we read globals, we need to add the internal globals too, so do that here + // imports can read global imports, so we run getGlobalName and create + // the mapping but after we read globals, we need to add the internal + // globals too, so do that here mappedGlobals.clear(); // wipe the mapping - getGlobalName(-1); // force rebuild + getGlobalName(-1); // force rebuild break; } - case BinaryConsts::Section::Data: readDataSegments(); break; - case BinaryConsts::Section::DataCount: readDataCount(); break; - case BinaryConsts::Section::Table: readFunctionTableDeclaration(); break; + case BinaryConsts::Section::Data: + readDataSegments(); + break; + case BinaryConsts::Section::DataCount: + readDataCount(); + break; + case BinaryConsts::Section::Table: + readFunctionTableDeclaration(); + break; default: { readUserSection(payloadLen); if (pos > oldPos + payloadLen) { - throwError("bad user section size, started at " + std::to_string(oldPos) + " plus payload " + std::to_string(payloadLen) + " not being equal to new position " + std::to_string(pos)); + throwError("bad user section size, started at " + + std::to_string(oldPos) + " plus payload " + + std::to_string(payloadLen) + + " not being equal to new position " + std::to_string(pos)); } pos = oldPos + payloadLen; } @@ -680,7 +799,9 @@ void WasmBinaryBuilder::read() { // make sure we advanced exactly past this section if (pos != oldPos + payloadLen) { - throwError("bad section size, started at " + std::to_string(oldPos) + " plus payload " + std::to_string(payloadLen) + " not being equal to new position " + std::to_string(pos)); + throwError("bad section size, started at " + std::to_string(oldPos) + + " plus payload " + std::to_string(payloadLen) + + " not being equal to new position " + std::to_string(pos)); } } @@ -703,7 +824,9 @@ void WasmBinaryBuilder::readUserSection(size_t payloadLen) { } else { // an unfamiliar custom section if (sectionName.equals(BinaryConsts::UserSections::Linking)) { - std::cerr << "warning: linking section is present, so this is not a standard wasm file - binaryen cannot handle this properly!\n"; + std::cerr + << "warning: linking section is present, so this is not a standard " + "wasm file - binaryen cannot handle this properly!\n"; } wasm.userSections.resize(wasm.userSections.size() + 1); auto& section = wasm.userSections.back(); @@ -717,107 +840,129 @@ void WasmBinaryBuilder::readUserSection(size_t payloadLen) { } uint8_t WasmBinaryBuilder::getInt8() { - if (!more()) throwError("unexpected end of input"); - if (debug) std::cerr << "getInt8: " << (int)(uint8_t)input[pos] << " (at " << pos << ")" << std::endl; + if (!more()) + throwError("unexpected end of input"); + if (debug) + std::cerr << "getInt8: " << (int)(uint8_t)input[pos] << " (at " << pos + << ")" << std::endl; return input[pos++]; } uint16_t WasmBinaryBuilder::getInt16() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; auto ret = uint16_t(getInt8()); ret |= uint16_t(getInt8()) << 8; - if (debug) std::cerr << "getInt16: " << ret << "/0x" << std::hex << ret << std::dec << " ==>" << std::endl; + if (debug) + std::cerr << "getInt16: " << ret << "/0x" << std::hex << ret << std::dec + << " ==>" << std::endl; return ret; } uint32_t WasmBinaryBuilder::getInt32() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; auto ret = uint32_t(getInt16()); ret |= uint32_t(getInt16()) << 16; - if (debug) std::cerr << "getInt32: " << ret << "/0x" << std::hex << ret << std::dec <<" ==>" << std::endl; + if (debug) + std::cerr << "getInt32: " << ret << "/0x" << std::hex << ret << std::dec + << " ==>" << std::endl; return ret; } uint64_t WasmBinaryBuilder::getInt64() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; auto ret = uint64_t(getInt32()); ret |= uint64_t(getInt32()) << 32; - if (debug) std::cerr << "getInt64: " << ret << "/0x" << std::hex << ret << std::dec << " ==>" << std::endl; + if (debug) + std::cerr << "getInt64: " << ret << "/0x" << std::hex << ret << std::dec + << " ==>" << std::endl; return ret; } uint8_t WasmBinaryBuilder::getLaneIndex(size_t lanes) { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; auto ret = getInt8(); - if (ret >= lanes) throwError("Illegal lane index"); - if (debug) std::cerr << "getLaneIndex(" << lanes << "): " << ret << " ==>" << std::endl; + if (ret >= lanes) + throwError("Illegal lane index"); + if (debug) + std::cerr << "getLaneIndex(" << lanes << "): " << ret << " ==>" + << std::endl; return ret; } Literal WasmBinaryBuilder::getFloat32Literal() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; auto ret = Literal(getInt32()); ret = ret.castToF32(); - if (debug) std::cerr << "getFloat32: " << ret << " ==>" << std::endl; + if (debug) + std::cerr << "getFloat32: " << ret << " ==>" << std::endl; return ret; } Literal WasmBinaryBuilder::getFloat64Literal() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; auto ret = Literal(getInt64()); ret = ret.castToF64(); - if (debug) std::cerr << "getFloat64: " << ret << " ==>" << std::endl; + if (debug) + std::cerr << "getFloat64: " << ret << " ==>" << std::endl; return ret; } Literal WasmBinaryBuilder::getVec128Literal() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; std::array<uint8_t, 16> bytes; for (auto i = 0; i < 16; ++i) { bytes[i] = getInt8(); } auto ret = Literal(bytes.data()); - if (debug) std::cerr << "getVec128: " << ret << " ==>" << std::endl; + if (debug) + std::cerr << "getVec128: " << ret << " ==>" << std::endl; return ret; } uint32_t WasmBinaryBuilder::getU32LEB() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; U32LEB ret; - ret.read([&]() { - return getInt8(); - }); - if (debug) std::cerr << "getU32LEB: " << ret.value << " ==>" << std::endl; + ret.read([&]() { return getInt8(); }); + if (debug) + std::cerr << "getU32LEB: " << ret.value << " ==>" << std::endl; return ret.value; } uint64_t WasmBinaryBuilder::getU64LEB() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; U64LEB ret; - ret.read([&]() { - return getInt8(); - }); - if (debug) std::cerr << "getU64LEB: " << ret.value << " ==>" << std::endl; + ret.read([&]() { return getInt8(); }); + if (debug) + std::cerr << "getU64LEB: " << ret.value << " ==>" << std::endl; return ret.value; } int32_t WasmBinaryBuilder::getS32LEB() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; S32LEB ret; - ret.read([&]() { - return (int8_t)getInt8(); - }); - if (debug) std::cerr << "getS32LEB: " << ret.value << " ==>" << std::endl; + ret.read([&]() { return (int8_t)getInt8(); }); + if (debug) + std::cerr << "getS32LEB: " << ret.value << " ==>" << std::endl; return ret.value; } int64_t WasmBinaryBuilder::getS64LEB() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; S64LEB ret; - ret.read([&]() { - return (int8_t)getInt8(); - }); - if (debug) std::cerr << "getS64LEB: " << ret.value << " ==>" << std::endl; + ret.read([&]() { return (int8_t)getInt8(); }); + if (debug) + std::cerr << "getS64LEB: " << ret.value << " ==>" << std::endl; return ret.value; } @@ -825,15 +970,19 @@ Type WasmBinaryBuilder::getType() { int type = getS32LEB(); switch (type) { // None only used for block signatures. TODO: Separate out? - case BinaryConsts::EncodedType::Empty: return none; - case BinaryConsts::EncodedType::i32: return i32; - case BinaryConsts::EncodedType::i64: return i64; - case BinaryConsts::EncodedType::f32: return f32; - case BinaryConsts::EncodedType::f64: return f64; - case BinaryConsts::EncodedType::v128: return v128; - default: { - throwError("invalid wasm type: " + std::to_string(type)); - } + case BinaryConsts::EncodedType::Empty: + return none; + case BinaryConsts::EncodedType::i32: + return i32; + case BinaryConsts::EncodedType::i64: + return i64; + case BinaryConsts::EncodedType::f32: + return f32; + case BinaryConsts::EncodedType::f64: + return f64; + case BinaryConsts::EncodedType::v128: + return v128; + default: { throwError("invalid wasm type: " + std::to_string(type)); } } WASM_UNREACHABLE(); } @@ -847,61 +996,74 @@ Type WasmBinaryBuilder::getConcreteType() { } Name WasmBinaryBuilder::getInlineString() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; auto len = getU32LEB(); std::string str; for (size_t i = 0; i < len; i++) { auto curr = char(getInt8()); if (curr == 0) { - throwError("inline string contains NULL (0). that is technically valid in wasm, but you shouldn't do it, and it's not supported in binaryen"); + throwError( + "inline string contains NULL (0). that is technically valid in wasm, " + "but you shouldn't do it, and it's not supported in binaryen"); } str = str + curr; } - if (debug) std::cerr << "getInlineString: " << str << " ==>" << std::endl; + if (debug) + std::cerr << "getInlineString: " << str << " ==>" << std::endl; return Name(str); } void WasmBinaryBuilder::verifyInt8(int8_t x) { int8_t y = getInt8(); - if (x != y) throwError("surprising value"); + if (x != y) + throwError("surprising value"); } void WasmBinaryBuilder::verifyInt16(int16_t x) { int16_t y = getInt16(); - if (x != y) throwError("surprising value"); + if (x != y) + throwError("surprising value"); } void WasmBinaryBuilder::verifyInt32(int32_t x) { int32_t y = getInt32(); - if (x != y) throwError("surprising value"); + if (x != y) + throwError("surprising value"); } void WasmBinaryBuilder::verifyInt64(int64_t x) { int64_t y = getInt64(); - if (x != y) throwError("surprising value"); + if (x != y) + throwError("surprising value"); } void WasmBinaryBuilder::ungetInt8() { assert(pos > 0); - if (debug) std::cerr << "ungetInt8 (at " << pos << ")" << std::endl; + if (debug) + std::cerr << "ungetInt8 (at " << pos << ")" << std::endl; pos--; } void WasmBinaryBuilder::readHeader() { - if (debug) std::cerr << "== readHeader" << std::endl; + if (debug) + std::cerr << "== readHeader" << std::endl; verifyInt32(BinaryConsts::Magic); verifyInt32(BinaryConsts::Version); } void WasmBinaryBuilder::readStart() { - if (debug) std::cerr << "== readStart" << std::endl; + if (debug) + std::cerr << "== readStart" << std::endl; startIndex = getU32LEB(); } void WasmBinaryBuilder::readMemory() { - if (debug) std::cerr << "== readMemory" << std::endl; + if (debug) + std::cerr << "== readMemory" << std::endl; auto numMemories = getU32LEB(); - if (!numMemories) return; + if (!numMemories) + return; if (numMemories != 1) { throwError("Must be exactly 1 memory"); } @@ -909,22 +1071,29 @@ void WasmBinaryBuilder::readMemory() { throwError("Memory cannot be both imported and defined"); } wasm.memory.exists = true; - getResizableLimits(wasm.memory.initial, wasm.memory.max, wasm.memory.shared, Memory::kUnlimitedSize); + getResizableLimits(wasm.memory.initial, + wasm.memory.max, + wasm.memory.shared, + Memory::kUnlimitedSize); } void WasmBinaryBuilder::readSignatures() { - if (debug) std::cerr << "== readSignatures" << std::endl; + if (debug) + std::cerr << "== readSignatures" << std::endl; size_t numTypes = getU32LEB(); - if (debug) std::cerr << "num: " << numTypes << std::endl; + if (debug) + std::cerr << "num: " << numTypes << std::endl; for (size_t i = 0; i < numTypes; i++) { - if (debug) std::cerr << "read one" << std::endl; + if (debug) + std::cerr << "read one" << std::endl; auto curr = make_unique<FunctionType>(); auto form = getS32LEB(); if (form != BinaryConsts::EncodedType::Func) { throwError("bad signature form " + std::to_string(form)); } size_t numParams = getU32LEB(); - if (debug) std::cerr << "num params: " << numParams << std::endl; + if (debug) + std::cerr << "num params: " << numParams << std::endl; for (size_t j = 0; j < numParams; j++) { curr->params.push_back(getConcreteType()); } @@ -949,36 +1118,46 @@ Name WasmBinaryBuilder::getFunctionIndexName(Index i) { return wasm.functions[i]->name; } -void WasmBinaryBuilder::getResizableLimits(Address& initial, Address& max, bool &shared, Address defaultIfNoMax) { +void WasmBinaryBuilder::getResizableLimits(Address& initial, + Address& max, + bool& shared, + Address defaultIfNoMax) { auto flags = getU32LEB(); initial = getU32LEB(); bool hasMax = (flags & BinaryConsts::HasMaximum) != 0; bool isShared = (flags & BinaryConsts::IsShared) != 0; - if (isShared && !hasMax) throwError("shared memory must have max size"); + if (isShared && !hasMax) + throwError("shared memory must have max size"); shared = isShared; - if (hasMax) max = getU32LEB(); - else max = defaultIfNoMax; + if (hasMax) + max = getU32LEB(); + else + max = defaultIfNoMax; } void WasmBinaryBuilder::readImports() { - if (debug) std::cerr << "== readImports" << std::endl; + if (debug) + std::cerr << "== readImports" << std::endl; size_t num = getU32LEB(); - if (debug) std::cerr << "num: " << num << std::endl; + if (debug) + std::cerr << "num: " << num << std::endl; Builder builder(wasm); for (size_t i = 0; i < num; i++) { - if (debug) std::cerr << "read one" << std::endl; + if (debug) + std::cerr << "read one" << std::endl; auto module = getInlineString(); auto base = getInlineString(); auto kind = (ExternalKind)getU32LEB(); - // We set a unique prefix for the name based on the kind. This ensures no collisions - // between them, which can't occur here (due to the index i) but could occur later - // due to the names section. + // We set a unique prefix for the name based on the kind. This ensures no + // collisions between them, which can't occur here (due to the index i) but + // could occur later due to the names section. switch (kind) { case ExternalKind::Function: { auto name = Name(std::string("fimport$") + std::to_string(i)); auto index = getU32LEB(); if (index >= wasm.functionTypes.size()) { - throwError("invalid function index " + std::to_string(index) + " / " + std::to_string(wasm.functionTypes.size())); + throwError("invalid function index " + std::to_string(index) + " / " + + std::to_string(wasm.functionTypes.size())); } auto* functionType = wasm.functionTypes[index].get(); auto params = functionType->params; @@ -997,11 +1176,14 @@ void WasmBinaryBuilder::readImports() { wasm.table.name = Name(std::string("timport$") + std::to_string(i)); auto elementType = getS32LEB(); WASM_UNUSED(elementType); - if (elementType != BinaryConsts::EncodedType::AnyFunc) throwError("Imported table type is not AnyFunc"); + if (elementType != BinaryConsts::EncodedType::AnyFunc) + throwError("Imported table type is not AnyFunc"); wasm.table.exists = true; bool is_shared; - getResizableLimits(wasm.table.initial, wasm.table.max, is_shared, Table::kUnlimitedSize); - if (is_shared) throwError("Tables may not be shared"); + getResizableLimits( + wasm.table.initial, wasm.table.max, is_shared, Table::kUnlimitedSize); + if (is_shared) + throwError("Tables may not be shared"); break; } case ExternalKind::Memory: { @@ -1009,22 +1191,27 @@ void WasmBinaryBuilder::readImports() { wasm.memory.base = base; wasm.memory.name = Name(std::to_string(i)); wasm.memory.exists = true; - getResizableLimits(wasm.memory.initial, wasm.memory.max, wasm.memory.shared, Memory::kUnlimitedSize); + getResizableLimits(wasm.memory.initial, + wasm.memory.max, + wasm.memory.shared, + Memory::kUnlimitedSize); break; } case ExternalKind::Global: { auto name = Name(std::string("gimport$") + std::to_string(i)); auto type = getConcreteType(); auto mutable_ = getU32LEB(); - auto* curr = builder.makeGlobal(name, type, nullptr, mutable_ ? Builder::Mutable : Builder::Immutable); + auto* curr = + builder.makeGlobal(name, + type, + nullptr, + mutable_ ? Builder::Mutable : Builder::Immutable); curr->module = module; curr->base = base; wasm.addGlobal(curr); break; } - default: { - throwError("bad import kind"); - } + default: { throwError("bad import kind"); } } } } @@ -1041,11 +1228,14 @@ void WasmBinaryBuilder::requireFunctionContext(const char* error) { } void WasmBinaryBuilder::readFunctionSignatures() { - if (debug) std::cerr << "== readFunctionSignatures" << std::endl; + if (debug) + std::cerr << "== readFunctionSignatures" << std::endl; size_t num = getU32LEB(); - if (debug) std::cerr << "num: " << num << std::endl; + if (debug) + std::cerr << "num: " << num << std::endl; for (size_t i = 0; i < num; i++) { - if (debug) std::cerr << "read one" << std::endl; + if (debug) + std::cerr << "read one" << std::endl; auto index = getU32LEB(); if (index >= wasm.functionTypes.size()) { throwError("invalid function type index for function"); @@ -1055,27 +1245,30 @@ void WasmBinaryBuilder::readFunctionSignatures() { } void WasmBinaryBuilder::readFunctions() { - if (debug) std::cerr << "== readFunctions" << std::endl; + if (debug) + std::cerr << "== readFunctions" << std::endl; size_t total = getU32LEB(); if (total != functionTypes.size()) { throwError("invalid function section size, must equal types"); } for (size_t i = 0; i < total; i++) { - if (debug) std::cerr << "read one at " << pos << std::endl; + if (debug) + std::cerr << "read one at " << pos << std::endl; size_t size = getU32LEB(); if (size == 0) { throwError("empty function size"); } endOfFunction = pos + size; - Function *func = new Function; + Function* func = new Function; func->name = Name::fromInt(i); currFunction = func; readNextDebugLocation(); auto type = functionTypes[i]; - if (debug) std::cerr << "reading " << i << std::endl; + if (debug) + std::cerr << "reading " << i << std::endl; func->type = type->name; func->result = type->result; for (size_t j = 0; j < type->params.size(); j++) { @@ -1093,7 +1286,8 @@ void WasmBinaryBuilder::readFunctions() { std::swap(func->prologLocation, debugLocation); { // process the function body - if (debug) std::cerr << "processing function: " << i << std::endl; + if (debug) + std::cerr << "processing function: " << i << std::endl; nextLabel = 0; debugLocation.clear(); willBeIgnored = false; @@ -1118,16 +1312,20 @@ void WasmBinaryBuilder::readFunctions() { debugLocation.clear(); functions.push_back(func); } - if (debug) std::cerr << " end function bodies" << std::endl; + if (debug) + std::cerr << " end function bodies" << std::endl; } void WasmBinaryBuilder::readExports() { - if (debug) std::cerr << "== readExports" << std::endl; + if (debug) + std::cerr << "== readExports" << std::endl; size_t num = getU32LEB(); - if (debug) std::cerr << "num: " << num << std::endl; + if (debug) + std::cerr << "num: " << num << std::endl; std::set<Name> names; for (size_t i = 0; i < num; i++) { - if (debug) std::cerr << "read one" << std::endl; + if (debug) + std::cerr << "read one" << std::endl; auto curr = new Export; curr->name = getInlineString(); if (names.count(curr->name) > 0) { @@ -1154,11 +1352,12 @@ static int32_t readBase64VLQ(std::istream& in) { value |= digit << shift; break; } - if (!(ch >= 'g' && ch <= 'z') && !(ch >= '0' && ch <= '9') && - ch != '+' && ch != '/') { + if (!(ch >= 'g' && ch <= 'z') && !(ch >= '0' && ch <= '9') && ch != '+' && + ch != '/') { throw MapParseException("invalid VLQ digit"); } - uint32_t digit = ch > '9' ? ch - 'g' : (ch >= '0' ? ch - '0' + 20 : (ch == '+' ? 30 : 31)); + uint32_t digit = + ch > '9' ? ch - 'g' : (ch >= '0' ? ch - '0' + 20 : (ch == '+' ? 30 : 31)); value |= digit << shift; shift += 5; } @@ -1166,7 +1365,8 @@ static int32_t readBase64VLQ(std::istream& in) { } void WasmBinaryBuilder::readSourceMapHeader() { - if (!sourceMap) return; + if (!sourceMap) + return; auto skipWhitespace = [&]() { while (sourceMap->peek() == ' ' || sourceMap->peek() == '\n') @@ -1174,7 +1374,8 @@ void WasmBinaryBuilder::readSourceMapHeader() { }; auto maybeReadChar = [&](char expected) { - if (sourceMap->peek() != expected) return false; + if (sourceMap->peek() != expected) + return false; sourceMap->get(); return true; }; @@ -1193,7 +1394,8 @@ void WasmBinaryBuilder::readSourceMapHeader() { size_t pos; while (1) { int ch = sourceMap->get(); - if (ch == EOF) return false; + if (ch == EOF) + return false; if (ch == '\"') { if (matching) { // we matched a terminating quote. @@ -1226,7 +1428,8 @@ void WasmBinaryBuilder::readSourceMapHeader() { if (ch == EOF) { throw MapParseException("unexpected EOF in the middle of string"); } - if (ch == '\"') break; + if (ch == '\"') + break; vec.push_back(ch); } } @@ -1263,13 +1466,15 @@ void WasmBinaryBuilder::readSourceMapHeader() { // read first debug location uint32_t position = readBase64VLQ(*sourceMap); uint32_t fileIndex = readBase64VLQ(*sourceMap); - uint32_t lineNumber = readBase64VLQ(*sourceMap) + 1; // adjust zero-based line number + uint32_t lineNumber = + readBase64VLQ(*sourceMap) + 1; // adjust zero-based line number uint32_t columnNumber = readBase64VLQ(*sourceMap); - nextDebugLocation = { position, { fileIndex, lineNumber, columnNumber } }; + nextDebugLocation = {position, {fileIndex, lineNumber, columnNumber}}; } void WasmBinaryBuilder::readNextDebugLocation() { - if (!sourceMap) return; + if (!sourceMap) + return; while (nextDebugLocation.first && nextDebugLocation.first <= pos) { if (nextDebugLocation.first < pos) { @@ -1299,9 +1504,10 @@ void WasmBinaryBuilder::readNextDebugLocation() { int32_t lineNumberDelta = readBase64VLQ(*sourceMap); uint32_t lineNumber = nextDebugLocation.second.lineNumber + lineNumberDelta; int32_t columnNumberDelta = readBase64VLQ(*sourceMap); - uint32_t columnNumber = nextDebugLocation.second.columnNumber + columnNumberDelta; + uint32_t columnNumber = + nextDebugLocation.second.columnNumber + columnNumberDelta; - nextDebugLocation = { position, { fileIndex, lineNumber, columnNumber } }; + nextDebugLocation = {position, {fileIndex, lineNumber, columnNumber}}; } } @@ -1317,33 +1523,38 @@ Expression* WasmBinaryBuilder::readExpression() { } void WasmBinaryBuilder::readGlobals() { - if (debug) std::cerr << "== readGlobals" << std::endl; + if (debug) + std::cerr << "== readGlobals" << std::endl; size_t num = getU32LEB(); - if (debug) std::cerr << "num: " << num << std::endl; + if (debug) + std::cerr << "num: " << num << std::endl; for (size_t i = 0; i < num; i++) { - if (debug) std::cerr << "read one" << std::endl; + if (debug) + std::cerr << "read one" << std::endl; auto type = getConcreteType(); auto mutable_ = getU32LEB(); - if (mutable_ & ~1) throwError("Global mutability must be 0 or 1"); + if (mutable_ & ~1) + throwError("Global mutability must be 0 or 1"); auto* init = readExpression(); - wasm.addGlobal(Builder::makeGlobal( - "global$" + std::to_string(i), - type, - init, - mutable_ ? Builder::Mutable : Builder::Immutable - )); + wasm.addGlobal( + Builder::makeGlobal("global$" + std::to_string(i), + type, + init, + mutable_ ? Builder::Mutable : Builder::Immutable)); } } void WasmBinaryBuilder::processExpressions() { - if (debug) std::cerr << "== processExpressions" << std::endl; + if (debug) + std::cerr << "== processExpressions" << std::endl; unreachableInTheWasmSense = false; while (1) { Expression* curr; auto ret = readExpression(curr); if (!curr) { lastSeparator = ret; - if (debug) std::cerr << "== processExpressions finished" << std::endl; + if (debug) + std::cerr << "== processExpressions finished" << std::endl; return; } expressionStack.push_back(curr); @@ -1351,15 +1562,18 @@ void WasmBinaryBuilder::processExpressions() { // once we see something unreachable, we don't want to add anything else // to the stack, as it could be stacky code that is non-representable in // our AST. but we do need to skip it - // if there is nothing else here, just stop. otherwise, go into unreachable - // mode. peek to see what to do + // if there is nothing else here, just stop. otherwise, go into + // unreachable mode. peek to see what to do if (pos == endOfFunction) { throwError("Reached function end without seeing End opcode"); } - if (!more()) throwError("unexpected end of input"); + if (!more()) + throwError("unexpected end of input"); auto peek = input[pos]; if (peek == BinaryConsts::End || peek == BinaryConsts::Else) { - if (debug) std::cerr << "== processExpressions finished with unreachable" << std::endl; + if (debug) + std::cerr << "== processExpressions finished with unreachable" + << std::endl; readNextDebugLocation(); lastSeparator = BinaryConsts::ASTNodes(peek); pos++; @@ -1373,10 +1587,11 @@ void WasmBinaryBuilder::processExpressions() { } void WasmBinaryBuilder::skipUnreachableCode() { - if (debug) std::cerr << "== skipUnreachableCode" << std::endl; - // preserve the stack, and restore it. it contains the instruction that made us - // unreachable, and we can ignore anything after it. things after it may pop, - // we want to undo that + if (debug) + std::cerr << "== skipUnreachableCode" << std::endl; + // preserve the stack, and restore it. it contains the instruction that made + // us unreachable, and we can ignore anything after it. things after it may + // pop, we want to undo that auto savedStack = expressionStack; // note we are entering unreachable code, and note what the state as before so // we can restore it @@ -1387,12 +1602,14 @@ void WasmBinaryBuilder::skipUnreachableCode() { // result in uneachables being returned expressionStack.clear(); while (1) { - // set the unreachableInTheWasmSense flag each time, as sub-blocks may set and unset it + // set the unreachableInTheWasmSense flag each time, as sub-blocks may set + // and unset it unreachableInTheWasmSense = true; Expression* curr; auto ret = readExpression(curr); if (!curr) { - if (debug) std::cerr << "== skipUnreachableCode finished" << std::endl; + if (debug) + std::cerr << "== skipUnreachableCode finished" << std::endl; lastSeparator = ret; unreachableInTheWasmSense = false; willBeIgnored = before; @@ -1404,15 +1621,20 @@ void WasmBinaryBuilder::skipUnreachableCode() { } Expression* WasmBinaryBuilder::popExpression() { - if (debug) std::cerr << "== popExpression" << std::endl; + if (debug) + std::cerr << "== popExpression" << std::endl; if (expressionStack.empty()) { if (unreachableInTheWasmSense) { // in unreachable code, trying to pop past the polymorphic stack // area results in receiving unreachables - if (debug) std::cerr << "== popping unreachable from polymorphic stack" << std::endl; + if (debug) + std::cerr << "== popping unreachable from polymorphic stack" + << std::endl; return allocator.alloc<Unreachable>(); } - throwError("attempted pop from empty stack / beyond block start boundary at " + std::to_string(pos)); + throwError( + "attempted pop from empty stack / beyond block start boundary at " + + std::to_string(pos)); } // the stack is not empty, and we would not be going out of the current block auto ret = expressionStack.back(); @@ -1422,7 +1644,8 @@ Expression* WasmBinaryBuilder::popExpression() { Expression* WasmBinaryBuilder::popNonVoidExpression() { auto* ret = popExpression(); - if (ret->type != none) return ret; + if (ret->type != none) + return ret; // we found a void, so this is stacky code that we must handle carefully Builder builder(wasm); // add elements until we find a non-void @@ -1431,7 +1654,8 @@ Expression* WasmBinaryBuilder::popNonVoidExpression() { while (1) { auto* curr = popExpression(); expressions.push_back(curr); - if (curr->type != none) break; + if (curr->type != none) + break; } auto* block = builder.makeBlock(); while (!expressions.empty()) { @@ -1462,7 +1686,8 @@ Name WasmBinaryBuilder::getGlobalName(Index index) { ModuleUtils::iterImportedGlobals(wasm, add); ModuleUtils::iterDefinedGlobals(wasm, add); } - if (index == Index(-1)) return Name("null"); // just a force-rebuild + if (index == Index(-1)) + return Name("null"); // just a force-rebuild if (mappedGlobals.count(index) == 0) { throwError("bad global index"); } @@ -1493,10 +1718,17 @@ void WasmBinaryBuilder::processFunctions() { curr->value = getFunctionIndexName(index); break; } - case ExternalKind::Table: curr->value = Name::fromInt(0); break; - case ExternalKind::Memory: curr->value = Name::fromInt(0); break; - case ExternalKind::Global: curr->value = getGlobalName(index); break; - default: throwError("bad export kind"); + case ExternalKind::Table: + curr->value = Name::fromInt(0); + break; + case ExternalKind::Memory: + curr->value = Name::fromInt(0); + break; + case ExternalKind::Global: + curr->value = getGlobalName(index); + break; + default: + throwError("bad export kind"); } wasm.addExport(curr); } @@ -1523,13 +1755,15 @@ void WasmBinaryBuilder::processFunctions() { } void WasmBinaryBuilder::readDataCount() { - if (debug) std::cerr << "== readDataCount" << std::endl; + if (debug) + std::cerr << "== readDataCount" << std::endl; hasDataCount = true; dataCount = getU32LEB(); } void WasmBinaryBuilder::readDataSegments() { - if (debug) std::cerr << "== readDataSegments" << std::endl; + if (debug) + std::cerr << "== readDataSegments" << std::endl; auto num = getU32LEB(); for (size_t i = 0; i < num; i++) { Memory::Segment curr; @@ -1558,25 +1792,34 @@ void WasmBinaryBuilder::readDataSegments() { } void WasmBinaryBuilder::readFunctionTableDeclaration() { - if (debug) std::cerr << "== readFunctionTableDeclaration" << std::endl; + if (debug) + std::cerr << "== readFunctionTableDeclaration" << std::endl; auto numTables = getU32LEB(); - if (numTables != 1) throwError("Only 1 table definition allowed in MVP"); - if (wasm.table.exists) throwError("Table cannot be both imported and defined"); + if (numTables != 1) + throwError("Only 1 table definition allowed in MVP"); + if (wasm.table.exists) + throwError("Table cannot be both imported and defined"); wasm.table.exists = true; auto elemType = getS32LEB(); - if (elemType != BinaryConsts::EncodedType::AnyFunc) throwError("ElementType must be AnyFunc in MVP"); + if (elemType != BinaryConsts::EncodedType::AnyFunc) + throwError("ElementType must be AnyFunc in MVP"); bool is_shared; - getResizableLimits(wasm.table.initial, wasm.table.max, is_shared, Table::kUnlimitedSize); - if (is_shared) throwError("Tables may not be shared"); + getResizableLimits( + wasm.table.initial, wasm.table.max, is_shared, Table::kUnlimitedSize); + if (is_shared) + throwError("Tables may not be shared"); } void WasmBinaryBuilder::readTableElements() { - if (debug) std::cerr << "== readTableElements" << std::endl; + if (debug) + std::cerr << "== readTableElements" << std::endl; auto numSegments = getU32LEB(); - if (numSegments >= Table::kMaxSize) throwError("Too many segments"); + if (numSegments >= Table::kMaxSize) + throwError("Too many segments"); for (size_t i = 0; i < numSegments; i++) { auto tableIndex = getU32LEB(); - if (tableIndex != 0) throwError("Table elements must refer to table 0 in MVP"); + if (tableIndex != 0) + throwError("Table elements must refer to table 0 in MVP"); wasm.table.segments.emplace_back(readExpression()); auto& indexSegment = functionTable[i]; @@ -1588,20 +1831,21 @@ void WasmBinaryBuilder::readTableElements() { } static bool isIdChar(char ch) { - return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || - ch == '!' || ch == '#' || ch == '$' || ch == '%' || ch == '&' || ch == '\'' || ch == '*' || - ch == '+' || ch == '-' || ch == '.' || ch == '/' || ch == ':' || ch == '<' || ch == '=' || - ch == '>' || ch == '?' || ch == '@' || ch == '^' || ch == '_' || ch == '`' || ch == '|' || - ch == '~'; + return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || + (ch >= 'a' && ch <= 'z') || ch == '!' || ch == '#' || ch == '$' || + ch == '%' || ch == '&' || ch == '\'' || ch == '*' || ch == '+' || + ch == '-' || ch == '.' || ch == '/' || ch == ':' || ch == '<' || + ch == '=' || ch == '>' || ch == '?' || ch == '@' || ch == '^' || + ch == '_' || ch == '`' || ch == '|' || ch == '~'; } static char formatNibble(int nibble) { return nibble < 10 ? '0' + nibble : 'a' - 10 + nibble; } -static void escapeName(Name &name) { +static void escapeName(Name& name) { bool allIdChars = true; - for (const char *p = name.str; allIdChars && *p; p++) { + for (const char* p = name.str; allIdChars && *p; p++) { allIdChars = isIdChar(*p); } if (allIdChars) { @@ -1609,7 +1853,7 @@ static void escapeName(Name &name) { } // encode name, if at least one non-idchar (per WebAssembly spec) was found std::string escaped; - for (const char *p = name.str; *p; p++) { + for (const char* p = name.str; *p; p++) { char ch = *p; if (isIdChar(ch)) { escaped.push_back(ch); @@ -1624,7 +1868,8 @@ static void escapeName(Name &name) { } void WasmBinaryBuilder::readNames(size_t payloadLen) { - if (debug) std::cerr << "== readNames" << std::endl; + if (debug) + std::cerr << "== readNames" << std::endl; auto sectionPos = pos; while (pos < sectionPos + payloadLen) { auto nameType = getU32LEB(); @@ -1713,79 +1958,146 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { if (pos == endOfFunction) { throwError("Reached function end without seeing End opcode"); } - if (debug) std::cerr << "zz recurse into " << ++depth << " at " << pos << std::endl; + if (debug) + std::cerr << "zz recurse into " << ++depth << " at " << pos << std::endl; readNextDebugLocation(); std::set<Function::DebugLocation> currDebugLocation; if (debugLocation.size()) { currDebugLocation.insert(*debugLocation.begin()); } uint8_t code = getInt8(); - if (debug) std::cerr << "readExpression seeing " << (int)code << std::endl; + if (debug) + std::cerr << "readExpression seeing " << (int)code << std::endl; switch (code) { - case BinaryConsts::Block: visitBlock((curr = allocator.alloc<Block>())->cast<Block>()); break; - case BinaryConsts::If: visitIf((curr = allocator.alloc<If>())->cast<If>()); break; - case BinaryConsts::Loop: visitLoop((curr = allocator.alloc<Loop>())->cast<Loop>()); break; + case BinaryConsts::Block: + visitBlock((curr = allocator.alloc<Block>())->cast<Block>()); + break; + case BinaryConsts::If: + visitIf((curr = allocator.alloc<If>())->cast<If>()); + break; + case BinaryConsts::Loop: + visitLoop((curr = allocator.alloc<Loop>())->cast<Loop>()); + break; case BinaryConsts::Br: - case BinaryConsts::BrIf: visitBreak((curr = allocator.alloc<Break>())->cast<Break>(), code); break; // code distinguishes br from br_if - case BinaryConsts::TableSwitch: visitSwitch((curr = allocator.alloc<Switch>())->cast<Switch>()); break; - case BinaryConsts::CallFunction: visitCall((curr = allocator.alloc<Call>())->cast<Call>()); break; - case BinaryConsts::CallIndirect: visitCallIndirect((curr = allocator.alloc<CallIndirect>())->cast<CallIndirect>()); break; - case BinaryConsts::GetLocal: visitGetLocal((curr = allocator.alloc<GetLocal>())->cast<GetLocal>()); break; + case BinaryConsts::BrIf: + visitBreak((curr = allocator.alloc<Break>())->cast<Break>(), code); + break; // code distinguishes br from br_if + case BinaryConsts::TableSwitch: + visitSwitch((curr = allocator.alloc<Switch>())->cast<Switch>()); + break; + case BinaryConsts::CallFunction: + visitCall((curr = allocator.alloc<Call>())->cast<Call>()); + break; + case BinaryConsts::CallIndirect: + visitCallIndirect( + (curr = allocator.alloc<CallIndirect>())->cast<CallIndirect>()); + break; + case BinaryConsts::GetLocal: + visitGetLocal((curr = allocator.alloc<GetLocal>())->cast<GetLocal>()); + break; case BinaryConsts::TeeLocal: - case BinaryConsts::SetLocal: visitSetLocal((curr = allocator.alloc<SetLocal>())->cast<SetLocal>(), code); break; - case BinaryConsts::GetGlobal: visitGetGlobal((curr = allocator.alloc<GetGlobal>())->cast<GetGlobal>()); break; - case BinaryConsts::SetGlobal: visitSetGlobal((curr = allocator.alloc<SetGlobal>())->cast<SetGlobal>()); break; - case BinaryConsts::Select: visitSelect((curr = allocator.alloc<Select>())->cast<Select>()); break; - case BinaryConsts::Return: visitReturn((curr = allocator.alloc<Return>())->cast<Return>()); break; - case BinaryConsts::Nop: visitNop((curr = allocator.alloc<Nop>())->cast<Nop>()); break; - case BinaryConsts::Unreachable: visitUnreachable((curr = allocator.alloc<Unreachable>())->cast<Unreachable>()); break; - case BinaryConsts::Drop: visitDrop((curr = allocator.alloc<Drop>())->cast<Drop>()); break; + case BinaryConsts::SetLocal: + visitSetLocal((curr = allocator.alloc<SetLocal>())->cast<SetLocal>(), + code); + break; + case BinaryConsts::GetGlobal: + visitGetGlobal((curr = allocator.alloc<GetGlobal>())->cast<GetGlobal>()); + break; + case BinaryConsts::SetGlobal: + visitSetGlobal((curr = allocator.alloc<SetGlobal>())->cast<SetGlobal>()); + break; + case BinaryConsts::Select: + visitSelect((curr = allocator.alloc<Select>())->cast<Select>()); + break; + case BinaryConsts::Return: + visitReturn((curr = allocator.alloc<Return>())->cast<Return>()); + break; + case BinaryConsts::Nop: + visitNop((curr = allocator.alloc<Nop>())->cast<Nop>()); + break; + case BinaryConsts::Unreachable: + visitUnreachable( + (curr = allocator.alloc<Unreachable>())->cast<Unreachable>()); + break; + case BinaryConsts::Drop: + visitDrop((curr = allocator.alloc<Drop>())->cast<Drop>()); + break; case BinaryConsts::End: - case BinaryConsts::Else: curr = nullptr; break; + case BinaryConsts::Else: + curr = nullptr; + break; case BinaryConsts::AtomicPrefix: { code = static_cast<uint8_t>(getU32LEB()); - if (maybeVisitLoad(curr, code, /*isAtomic=*/true)) break; - if (maybeVisitStore(curr, code, /*isAtomic=*/true)) break; - if (maybeVisitAtomicRMW(curr, code)) break; - if (maybeVisitAtomicCmpxchg(curr, code)) break; - if (maybeVisitAtomicWait(curr, code)) break; - if (maybeVisitAtomicNotify(curr, code)) break; + if (maybeVisitLoad(curr, code, /*isAtomic=*/true)) + break; + if (maybeVisitStore(curr, code, /*isAtomic=*/true)) + break; + if (maybeVisitAtomicRMW(curr, code)) + break; + if (maybeVisitAtomicCmpxchg(curr, code)) + break; + if (maybeVisitAtomicWait(curr, code)) + break; + if (maybeVisitAtomicNotify(curr, code)) + break; throwError("invalid code after atomic prefix: " + std::to_string(code)); break; } case BinaryConsts::MiscPrefix: { auto opcode = getU32LEB(); - if (maybeVisitTruncSat(curr, opcode)) break; - if (maybeVisitMemoryInit(curr, opcode)) break; - if (maybeVisitDataDrop(curr, opcode)) break; - if (maybeVisitMemoryCopy(curr, opcode)) break; - if (maybeVisitMemoryFill(curr, opcode)) break; - throwError("invalid code after nontrapping float-to-int prefix: " + std::to_string(opcode)); + if (maybeVisitTruncSat(curr, opcode)) + break; + if (maybeVisitMemoryInit(curr, opcode)) + break; + if (maybeVisitDataDrop(curr, opcode)) + break; + if (maybeVisitMemoryCopy(curr, opcode)) + break; + if (maybeVisitMemoryFill(curr, opcode)) + break; + throwError("invalid code after nontrapping float-to-int prefix: " + + std::to_string(opcode)); break; } case BinaryConsts::SIMDPrefix: { auto opcode = getU32LEB(); - if (maybeVisitSIMDBinary(curr, opcode)) break; - if (maybeVisitSIMDUnary(curr, opcode)) break; - if (maybeVisitSIMDConst(curr, opcode)) break; - if (maybeVisitSIMDLoad(curr, opcode)) break; - if (maybeVisitSIMDStore(curr, opcode)) break; - if (maybeVisitSIMDExtract(curr, opcode)) break; - if (maybeVisitSIMDReplace(curr, opcode)) break; - if (maybeVisitSIMDShuffle(curr, opcode)) break; - if (maybeVisitSIMDBitselect(curr, opcode)) break; - if (maybeVisitSIMDShift(curr, opcode)) break; + if (maybeVisitSIMDBinary(curr, opcode)) + break; + if (maybeVisitSIMDUnary(curr, opcode)) + break; + if (maybeVisitSIMDConst(curr, opcode)) + break; + if (maybeVisitSIMDLoad(curr, opcode)) + break; + if (maybeVisitSIMDStore(curr, opcode)) + break; + if (maybeVisitSIMDExtract(curr, opcode)) + break; + if (maybeVisitSIMDReplace(curr, opcode)) + break; + if (maybeVisitSIMDShuffle(curr, opcode)) + break; + if (maybeVisitSIMDBitselect(curr, opcode)) + break; + if (maybeVisitSIMDShift(curr, opcode)) + break; throwError("invalid code after SIMD prefix: " + std::to_string(opcode)); break; } default: { // otherwise, the code is a subcode TODO: optimize - if (maybeVisitBinary(curr, code)) break; - if (maybeVisitUnary(curr, code)) break; - if (maybeVisitConst(curr, code)) break; - if (maybeVisitLoad(curr, code, /*isAtomic=*/false)) break; - if (maybeVisitStore(curr, code, /*isAtomic=*/false)) break; - if (maybeVisitHost(curr, code)) break; + if (maybeVisitBinary(curr, code)) + break; + if (maybeVisitUnary(curr, code)) + break; + if (maybeVisitConst(curr, code)) + break; + if (maybeVisitLoad(curr, code, /*isAtomic=*/false)) + break; + if (maybeVisitStore(curr, code, /*isAtomic=*/false)) + break; + if (maybeVisitHost(curr, code)) + break; throwError("bad node code " + std::to_string(code)); break; } @@ -1793,26 +2105,31 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { if (curr && currDebugLocation.size()) { currFunction->debugLocations[curr] = *currDebugLocation.begin(); } - if (debug) std::cerr << "zz recurse from " << depth-- << " at " << pos << std::endl; + if (debug) + std::cerr << "zz recurse from " << depth-- << " at " << pos << std::endl; return BinaryConsts::ASTNodes(code); } -void WasmBinaryBuilder::pushBlockElements(Block* curr, size_t start, size_t end) { +void WasmBinaryBuilder::pushBlockElements(Block* curr, + size_t start, + size_t end) { assert(start <= expressionStack.size()); assert(start <= end); assert(end <= expressionStack.size()); - // the first dropped element may be consumed by code later - it was on the stack first, - // and is the only thing left on the stack. there must be just one thing on the stack - // since we are at the end of a block context. note that we may need to drop more than - // one thing, since a bunch of concrete values may be all "consumed" by an unreachable - // (in which case, the first value can't be consumed anyhow, so it doesn't matter) + // the first dropped element may be consumed by code later - it was on the + // stack first, and is the only thing left on the stack. there must be just + // one thing on the stack since we are at the end of a block context. note + // that we may need to drop more than one thing, since a bunch of concrete + // values may be all "consumed" by an unreachable (in which case, the first + // value can't be consumed anyhow, so it doesn't matter) const Index NONE = -1; Index consumable = NONE; for (size_t i = start; i < end; i++) { auto* item = expressionStack[i]; curr->list.push_back(item); if (i < end - 1) { - // stacky&unreachable code may introduce elements that need to be dropped in non-final positions + // stacky&unreachable code may introduce elements that need to be dropped + // in non-final positions if (isConcreteType(item->type)) { curr->list.back() = Builder(wasm).makeDrop(item); if (consumable == NONE) { @@ -1825,7 +2142,8 @@ void WasmBinaryBuilder::pushBlockElements(Block* curr, size_t start, size_t end) expressionStack.resize(start); // if we have a consumable item and need it, use it if (consumable != NONE && curr->list.back()->type == none) { - requireFunctionContext("need an extra var in a non-function context, invalid wasm"); + requireFunctionContext( + "need an extra var in a non-function context, invalid wasm"); Builder builder(wasm); auto* item = curr->list[consumable]->cast<Drop>()->value; auto temp = builder.addVar(currFunction, item->type); @@ -1835,9 +2153,10 @@ void WasmBinaryBuilder::pushBlockElements(Block* curr, size_t start, size_t end) } void WasmBinaryBuilder::visitBlock(Block* curr) { - if (debug) std::cerr << "zz node: Block" << std::endl; - // special-case Block and de-recurse nested blocks in their first position, as that is - // a common pattern that can be very highly nested. + if (debug) + std::cerr << "zz node: Block" << std::endl; + // special-case Block and de-recurse nested blocks in their first position, as + // that is a common pattern that can be very highly nested. std::vector<Block*> stack; while (1) { curr->type = getType(); @@ -1862,7 +2181,8 @@ void WasmBinaryBuilder::visitBlock(Block* curr) { while (stack.size() > 0) { curr = stack.back(); stack.pop_back(); - size_t start = expressionStack.size(); // everything after this, that is left when we see the marker, is ours + // everything after this, that is left when we see the marker, is ours + size_t start = expressionStack.size(); if (last) { // the previous block is our first-position element expressionStack.push_back(last); @@ -1874,7 +2194,9 @@ void WasmBinaryBuilder::visitBlock(Block* curr) { throwError("block cannot pop from outside"); } pushBlockElements(curr, start, end); - curr->finalize(curr->type, breakTargetNames.find(curr->name) != breakTargetNames.end() /* hasBreak */); + curr->finalize(curr->type, + breakTargetNames.find(curr->name) != + breakTargetNames.end() /* hasBreak */); breakStack.pop_back(); breakTargetNames.erase(curr->name); } @@ -1906,7 +2228,8 @@ Expression* WasmBinaryBuilder::getBlockOrSingleton(Type type) { } void WasmBinaryBuilder::visitIf(If* curr) { - if (debug) std::cerr << "zz node: If" << std::endl; + if (debug) + std::cerr << "zz node: If" << std::endl; curr->type = getType(); curr->condition = popNonVoidExpression(); curr->ifTrue = getBlockOrSingleton(curr->type); @@ -1920,7 +2243,8 @@ void WasmBinaryBuilder::visitIf(If* curr) { } void WasmBinaryBuilder::visitLoop(Loop* curr) { - if (debug) std::cerr << "zz node: Loop" << std::endl; + if (debug) + std::cerr << "zz node: Loop" << std::endl; curr->type = getType(); curr->name = getNextLabel(); breakStack.push_back({curr->name, 0}); @@ -1948,8 +2272,10 @@ void WasmBinaryBuilder::visitLoop(Loop* curr) { curr->finalize(curr->type); } -WasmBinaryBuilder::BreakTarget WasmBinaryBuilder::getBreakTarget(int32_t offset) { - if (debug) std::cerr << "getBreakTarget " << offset << std::endl; +WasmBinaryBuilder::BreakTarget +WasmBinaryBuilder::getBreakTarget(int32_t offset) { + if (debug) + std::cerr << "getBreakTarget " << offset << std::endl; if (breakStack.size() < 1 + size_t(offset)) { throwError("bad breakindex (low)"); } @@ -1957,42 +2283,52 @@ WasmBinaryBuilder::BreakTarget WasmBinaryBuilder::getBreakTarget(int32_t offset) if (index >= breakStack.size()) { throwError("bad breakindex (high)"); } - if (debug) std::cerr << "breaktarget "<< breakStack[index].name << " arity " << breakStack[index].arity << std::endl; + if (debug) + std::cerr << "breaktarget " << breakStack[index].name << " arity " + << breakStack[index].arity << std::endl; auto& ret = breakStack[index]; - // if the break is in literally unreachable code, then we will not emit it anyhow, - // so do not note that the target has breaks to it + // if the break is in literally unreachable code, then we will not emit it + // anyhow, so do not note that the target has breaks to it if (!willBeIgnored) { breakTargetNames.insert(ret.name); } return ret; } -void WasmBinaryBuilder::visitBreak(Break *curr, uint8_t code) { - if (debug) std::cerr << "zz node: Break, code "<< int32_t(code) << std::endl; +void WasmBinaryBuilder::visitBreak(Break* curr, uint8_t code) { + if (debug) + std::cerr << "zz node: Break, code " << int32_t(code) << std::endl; BreakTarget target = getBreakTarget(getU32LEB()); curr->name = target.name; - if (code == BinaryConsts::BrIf) curr->condition = popNonVoidExpression(); - if (target.arity) curr->value = popNonVoidExpression(); + if (code == BinaryConsts::BrIf) + curr->condition = popNonVoidExpression(); + if (target.arity) + curr->value = popNonVoidExpression(); curr->finalize(); } void WasmBinaryBuilder::visitSwitch(Switch* curr) { - if (debug) std::cerr << "zz node: Switch" << std::endl; + if (debug) + std::cerr << "zz node: Switch" << std::endl; curr->condition = popNonVoidExpression(); auto numTargets = getU32LEB(); - if (debug) std::cerr << "targets: "<< numTargets<<std::endl; + if (debug) + std::cerr << "targets: " << numTargets << std::endl; for (size_t i = 0; i < numTargets; i++) { curr->targets.push_back(getBreakTarget(getU32LEB()).name); } auto defaultTarget = getBreakTarget(getU32LEB()); curr->default_ = defaultTarget.name; - if (debug) std::cerr << "default: "<< curr->default_<<std::endl; - if (defaultTarget.arity) curr->value = popNonVoidExpression(); + if (debug) + std::cerr << "default: " << curr->default_ << std::endl; + if (defaultTarget.arity) + curr->value = popNonVoidExpression(); curr->finalize(); } void WasmBinaryBuilder::visitCall(Call* curr) { - if (debug) std::cerr << "zz node: Call" << std::endl; + if (debug) + std::cerr << "zz node: Call" << std::endl; auto index = getU32LEB(); FunctionType* type; if (index < functionImports.size()) { @@ -2017,14 +2353,16 @@ void WasmBinaryBuilder::visitCall(Call* curr) { } void WasmBinaryBuilder::visitCallIndirect(CallIndirect* curr) { - if (debug) std::cerr << "zz node: CallIndirect" << std::endl; + if (debug) + std::cerr << "zz node: CallIndirect" << std::endl; auto index = getU32LEB(); if (index >= wasm.functionTypes.size()) { throwError("bad call_indirect function index"); } auto* fullType = wasm.functionTypes[index].get(); auto reserved = getU32LEB(); - if (reserved != 0) throwError("Invalid flags field in call_indirect"); + if (reserved != 0) + throwError("Invalid flags field in call_indirect"); curr->fullType = fullType->name; auto num = fullType->params.size(); curr->operands.resize(num); @@ -2037,7 +2375,8 @@ void WasmBinaryBuilder::visitCallIndirect(CallIndirect* curr) { } void WasmBinaryBuilder::visitGetLocal(GetLocal* curr) { - if (debug) std::cerr << "zz node: GetLocal " << pos << std::endl; + if (debug) + std::cerr << "zz node: GetLocal " << pos << std::endl; requireFunctionContext("local.get"); curr->index = getU32LEB(); if (curr->index >= currFunction->getNumLocals()) { @@ -2047,8 +2386,9 @@ void WasmBinaryBuilder::visitGetLocal(GetLocal* curr) { curr->finalize(); } -void WasmBinaryBuilder::visitSetLocal(SetLocal *curr, uint8_t code) { - if (debug) std::cerr << "zz node: Set|TeeLocal" << std::endl; +void WasmBinaryBuilder::visitSetLocal(SetLocal* curr, uint8_t code) { + if (debug) + std::cerr << "zz node: Set|TeeLocal" << std::endl; requireFunctionContext("local.set outside of function"); curr->index = getU32LEB(); if (curr->index >= currFunction->getNumLocals()) { @@ -2061,14 +2401,16 @@ void WasmBinaryBuilder::visitSetLocal(SetLocal *curr, uint8_t code) { } void WasmBinaryBuilder::visitGetGlobal(GetGlobal* curr) { - if (debug) std::cerr << "zz node: GetGlobal " << pos << std::endl; + if (debug) + std::cerr << "zz node: GetGlobal " << pos << std::endl; auto index = getU32LEB(); curr->name = getGlobalName(index); curr->type = wasm.getGlobal(curr->name)->type; } void WasmBinaryBuilder::visitSetGlobal(SetGlobal* curr) { - if (debug) std::cerr << "zz node: SetGlobal" << std::endl; + if (debug) + std::cerr << "zz node: SetGlobal" << std::endl; auto index = getU32LEB(); curr->name = getGlobalName(index); curr->value = popNonVoidExpression(); @@ -2077,45 +2419,146 @@ void WasmBinaryBuilder::visitSetGlobal(SetGlobal* curr) { void WasmBinaryBuilder::readMemoryAccess(Address& alignment, Address& offset) { auto rawAlignment = getU32LEB(); - if (rawAlignment > 4) throwError("Alignment must be of a reasonable size"); + if (rawAlignment > 4) + throwError("Alignment must be of a reasonable size"); alignment = Pow2(rawAlignment); offset = getU32LEB(); } -bool WasmBinaryBuilder::maybeVisitLoad(Expression*& out, uint8_t code, bool isAtomic) { +bool WasmBinaryBuilder::maybeVisitLoad(Expression*& out, + uint8_t code, + bool isAtomic) { Load* curr; if (!isAtomic) { switch (code) { - case BinaryConsts::I32LoadMem8S: curr = allocator.alloc<Load>(); curr->bytes = 1; curr->type = i32; curr->signed_ = true; break; - case BinaryConsts::I32LoadMem8U: curr = allocator.alloc<Load>(); curr->bytes = 1; curr->type = i32; curr->signed_ = false; break; - case BinaryConsts::I32LoadMem16S: curr = allocator.alloc<Load>(); curr->bytes = 2; curr->type = i32; curr->signed_ = true; break; - case BinaryConsts::I32LoadMem16U: curr = allocator.alloc<Load>(); curr->bytes = 2; curr->type = i32; curr->signed_ = false; break; - case BinaryConsts::I32LoadMem: curr = allocator.alloc<Load>(); curr->bytes = 4; curr->type = i32; break; - case BinaryConsts::I64LoadMem8S: curr = allocator.alloc<Load>(); curr->bytes = 1; curr->type = i64; curr->signed_ = true; break; - case BinaryConsts::I64LoadMem8U: curr = allocator.alloc<Load>(); curr->bytes = 1; curr->type = i64; curr->signed_ = false; break; - case BinaryConsts::I64LoadMem16S: curr = allocator.alloc<Load>(); curr->bytes = 2; curr->type = i64; curr->signed_ = true; break; - case BinaryConsts::I64LoadMem16U: curr = allocator.alloc<Load>(); curr->bytes = 2; curr->type = i64; curr->signed_ = false; break; - case BinaryConsts::I64LoadMem32S: curr = allocator.alloc<Load>(); curr->bytes = 4; curr->type = i64; curr->signed_ = true; break; - case BinaryConsts::I64LoadMem32U: curr = allocator.alloc<Load>(); curr->bytes = 4; curr->type = i64; curr->signed_ = false; break; - case BinaryConsts::I64LoadMem: curr = allocator.alloc<Load>(); curr->bytes = 8; curr->type = i64; break; - case BinaryConsts::F32LoadMem: curr = allocator.alloc<Load>(); curr->bytes = 4; curr->type = f32; break; - case BinaryConsts::F64LoadMem: curr = allocator.alloc<Load>(); curr->bytes = 8; curr->type = f64; break; - default: return false; - } - if (debug) std::cerr << "zz node: Load" << std::endl; + case BinaryConsts::I32LoadMem8S: + curr = allocator.alloc<Load>(); + curr->bytes = 1; + curr->type = i32; + curr->signed_ = true; + break; + case BinaryConsts::I32LoadMem8U: + curr = allocator.alloc<Load>(); + curr->bytes = 1; + curr->type = i32; + curr->signed_ = false; + break; + case BinaryConsts::I32LoadMem16S: + curr = allocator.alloc<Load>(); + curr->bytes = 2; + curr->type = i32; + curr->signed_ = true; + break; + case BinaryConsts::I32LoadMem16U: + curr = allocator.alloc<Load>(); + curr->bytes = 2; + curr->type = i32; + curr->signed_ = false; + break; + case BinaryConsts::I32LoadMem: + curr = allocator.alloc<Load>(); + curr->bytes = 4; + curr->type = i32; + break; + case BinaryConsts::I64LoadMem8S: + curr = allocator.alloc<Load>(); + curr->bytes = 1; + curr->type = i64; + curr->signed_ = true; + break; + case BinaryConsts::I64LoadMem8U: + curr = allocator.alloc<Load>(); + curr->bytes = 1; + curr->type = i64; + curr->signed_ = false; + break; + case BinaryConsts::I64LoadMem16S: + curr = allocator.alloc<Load>(); + curr->bytes = 2; + curr->type = i64; + curr->signed_ = true; + break; + case BinaryConsts::I64LoadMem16U: + curr = allocator.alloc<Load>(); + curr->bytes = 2; + curr->type = i64; + curr->signed_ = false; + break; + case BinaryConsts::I64LoadMem32S: + curr = allocator.alloc<Load>(); + curr->bytes = 4; + curr->type = i64; + curr->signed_ = true; + break; + case BinaryConsts::I64LoadMem32U: + curr = allocator.alloc<Load>(); + curr->bytes = 4; + curr->type = i64; + curr->signed_ = false; + break; + case BinaryConsts::I64LoadMem: + curr = allocator.alloc<Load>(); + curr->bytes = 8; + curr->type = i64; + break; + case BinaryConsts::F32LoadMem: + curr = allocator.alloc<Load>(); + curr->bytes = 4; + curr->type = f32; + break; + case BinaryConsts::F64LoadMem: + curr = allocator.alloc<Load>(); + curr->bytes = 8; + curr->type = f64; + break; + default: + return false; + } + if (debug) + std::cerr << "zz node: Load" << std::endl; } else { switch (code) { - case BinaryConsts::I32AtomicLoad8U: curr = allocator.alloc<Load>(); curr->bytes = 1; curr->type = i32; break; - case BinaryConsts::I32AtomicLoad16U: curr = allocator.alloc<Load>(); curr->bytes = 2; curr->type = i32; break; - case BinaryConsts::I32AtomicLoad: curr = allocator.alloc<Load>(); curr->bytes = 4; curr->type = i32; break; - case BinaryConsts::I64AtomicLoad8U: curr = allocator.alloc<Load>(); curr->bytes = 1; curr->type = i64; break; - case BinaryConsts::I64AtomicLoad16U: curr = allocator.alloc<Load>(); curr->bytes = 2; curr->type = i64; break; - case BinaryConsts::I64AtomicLoad32U: curr = allocator.alloc<Load>(); curr->bytes = 4; curr->type = i64; break; - case BinaryConsts::I64AtomicLoad: curr = allocator.alloc<Load>(); curr->bytes = 8; curr->type = i64; break; - default: return false; + case BinaryConsts::I32AtomicLoad8U: + curr = allocator.alloc<Load>(); + curr->bytes = 1; + curr->type = i32; + break; + case BinaryConsts::I32AtomicLoad16U: + curr = allocator.alloc<Load>(); + curr->bytes = 2; + curr->type = i32; + break; + case BinaryConsts::I32AtomicLoad: + curr = allocator.alloc<Load>(); + curr->bytes = 4; + curr->type = i32; + break; + case BinaryConsts::I64AtomicLoad8U: + curr = allocator.alloc<Load>(); + curr->bytes = 1; + curr->type = i64; + break; + case BinaryConsts::I64AtomicLoad16U: + curr = allocator.alloc<Load>(); + curr->bytes = 2; + curr->type = i64; + break; + case BinaryConsts::I64AtomicLoad32U: + curr = allocator.alloc<Load>(); + curr->bytes = 4; + curr->type = i64; + break; + case BinaryConsts::I64AtomicLoad: + curr = allocator.alloc<Load>(); + curr->bytes = 8; + curr->type = i64; + break; + default: + return false; } curr->signed_ = false; - if (debug) std::cerr << "zz node: AtomicLoad" << std::endl; + if (debug) + std::cerr << "zz node: AtomicLoad" << std::endl; } curr->isAtomic = isAtomic; @@ -2126,36 +2569,105 @@ bool WasmBinaryBuilder::maybeVisitLoad(Expression*& out, uint8_t code, bool isAt return true; } -bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, uint8_t code, bool isAtomic) { +bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, + uint8_t code, + bool isAtomic) { Store* curr; if (!isAtomic) { switch (code) { - case BinaryConsts::I32StoreMem8: curr = allocator.alloc<Store>(); curr->bytes = 1; curr->valueType = i32; break; - case BinaryConsts::I32StoreMem16: curr = allocator.alloc<Store>(); curr->bytes = 2; curr->valueType = i32; break; - case BinaryConsts::I32StoreMem: curr = allocator.alloc<Store>(); curr->bytes = 4; curr->valueType = i32; break; - case BinaryConsts::I64StoreMem8: curr = allocator.alloc<Store>(); curr->bytes = 1; curr->valueType = i64; break; - case BinaryConsts::I64StoreMem16: curr = allocator.alloc<Store>(); curr->bytes = 2; curr->valueType = i64; break; - case BinaryConsts::I64StoreMem32: curr = allocator.alloc<Store>(); curr->bytes = 4; curr->valueType = i64; break; - case BinaryConsts::I64StoreMem: curr = allocator.alloc<Store>(); curr->bytes = 8; curr->valueType = i64; break; - case BinaryConsts::F32StoreMem: curr = allocator.alloc<Store>(); curr->bytes = 4; curr->valueType = f32; break; - case BinaryConsts::F64StoreMem: curr = allocator.alloc<Store>(); curr->bytes = 8; curr->valueType = f64; break; - default: return false; + case BinaryConsts::I32StoreMem8: + curr = allocator.alloc<Store>(); + curr->bytes = 1; + curr->valueType = i32; + break; + case BinaryConsts::I32StoreMem16: + curr = allocator.alloc<Store>(); + curr->bytes = 2; + curr->valueType = i32; + break; + case BinaryConsts::I32StoreMem: + curr = allocator.alloc<Store>(); + curr->bytes = 4; + curr->valueType = i32; + break; + case BinaryConsts::I64StoreMem8: + curr = allocator.alloc<Store>(); + curr->bytes = 1; + curr->valueType = i64; + break; + case BinaryConsts::I64StoreMem16: + curr = allocator.alloc<Store>(); + curr->bytes = 2; + curr->valueType = i64; + break; + case BinaryConsts::I64StoreMem32: + curr = allocator.alloc<Store>(); + curr->bytes = 4; + curr->valueType = i64; + break; + case BinaryConsts::I64StoreMem: + curr = allocator.alloc<Store>(); + curr->bytes = 8; + curr->valueType = i64; + break; + case BinaryConsts::F32StoreMem: + curr = allocator.alloc<Store>(); + curr->bytes = 4; + curr->valueType = f32; + break; + case BinaryConsts::F64StoreMem: + curr = allocator.alloc<Store>(); + curr->bytes = 8; + curr->valueType = f64; + break; + default: + return false; } } else { switch (code) { - case BinaryConsts::I32AtomicStore8: curr = allocator.alloc<Store>(); curr->bytes = 1; curr->valueType = i32; break; - case BinaryConsts::I32AtomicStore16: curr = allocator.alloc<Store>(); curr->bytes = 2; curr->valueType = i32; break; - case BinaryConsts::I32AtomicStore: curr = allocator.alloc<Store>(); curr->bytes = 4; curr->valueType = i32; break; - case BinaryConsts::I64AtomicStore8: curr = allocator.alloc<Store>(); curr->bytes = 1; curr->valueType = i64; break; - case BinaryConsts::I64AtomicStore16: curr = allocator.alloc<Store>(); curr->bytes = 2; curr->valueType = i64; break; - case BinaryConsts::I64AtomicStore32: curr = allocator.alloc<Store>(); curr->bytes = 4; curr->valueType = i64; break; - case BinaryConsts::I64AtomicStore: curr = allocator.alloc<Store>(); curr->bytes = 8; curr->valueType = i64; break; - default: return false; + case BinaryConsts::I32AtomicStore8: + curr = allocator.alloc<Store>(); + curr->bytes = 1; + curr->valueType = i32; + break; + case BinaryConsts::I32AtomicStore16: + curr = allocator.alloc<Store>(); + curr->bytes = 2; + curr->valueType = i32; + break; + case BinaryConsts::I32AtomicStore: + curr = allocator.alloc<Store>(); + curr->bytes = 4; + curr->valueType = i32; + break; + case BinaryConsts::I64AtomicStore8: + curr = allocator.alloc<Store>(); + curr->bytes = 1; + curr->valueType = i64; + break; + case BinaryConsts::I64AtomicStore16: + curr = allocator.alloc<Store>(); + curr->bytes = 2; + curr->valueType = i64; + break; + case BinaryConsts::I64AtomicStore32: + curr = allocator.alloc<Store>(); + curr->bytes = 4; + curr->valueType = i64; + break; + case BinaryConsts::I64AtomicStore: + curr = allocator.alloc<Store>(); + curr->bytes = 8; + curr->valueType = i64; + break; + default: + return false; } } curr->isAtomic = isAtomic; - if (debug) std::cerr << "zz node: Store" << std::endl; + if (debug) + std::cerr << "zz node: Store" << std::endl; readMemoryAccess(curr->align, curr->offset); curr->value = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); @@ -2165,41 +2677,60 @@ bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, uint8_t code, bool isA } bool WasmBinaryBuilder::maybeVisitAtomicRMW(Expression*& out, uint8_t code) { - if (code < BinaryConsts::AtomicRMWOps_Begin || code > BinaryConsts::AtomicRMWOps_End) return false; + if (code < BinaryConsts::AtomicRMWOps_Begin || + code > BinaryConsts::AtomicRMWOps_End) + return false; auto* curr = allocator.alloc<AtomicRMW>(); // Set curr to the given opcode, type and size. -#define SET(opcode, optype, size) \ - curr->op = opcode; \ - curr->type = optype; \ +#define SET(opcode, optype, size) \ + curr->op = opcode; \ + curr->type = optype; \ curr->bytes = size // Handle the cases for all the valid types for a particular opcode -#define SET_FOR_OP(Op) \ - case BinaryConsts::I32AtomicRMW##Op: SET(Op, i32, 4); break; \ - case BinaryConsts::I32AtomicRMW##Op##8U: SET(Op, i32, 1); break; \ - case BinaryConsts::I32AtomicRMW##Op##16U: SET(Op, i32, 2); break; \ - case BinaryConsts::I64AtomicRMW##Op: SET(Op, i64, 8); break; \ - case BinaryConsts::I64AtomicRMW##Op##8U: SET(Op, i64, 1); break; \ - case BinaryConsts::I64AtomicRMW##Op##16U: SET(Op, i64, 2); break; \ - case BinaryConsts::I64AtomicRMW##Op##32U: SET(Op, i64, 4); break; - - switch(code) { +#define SET_FOR_OP(Op) \ + case BinaryConsts::I32AtomicRMW##Op: \ + SET(Op, i32, 4); \ + break; \ + case BinaryConsts::I32AtomicRMW##Op##8U: \ + SET(Op, i32, 1); \ + break; \ + case BinaryConsts::I32AtomicRMW##Op##16U: \ + SET(Op, i32, 2); \ + break; \ + case BinaryConsts::I64AtomicRMW##Op: \ + SET(Op, i64, 8); \ + break; \ + case BinaryConsts::I64AtomicRMW##Op##8U: \ + SET(Op, i64, 1); \ + break; \ + case BinaryConsts::I64AtomicRMW##Op##16U: \ + SET(Op, i64, 2); \ + break; \ + case BinaryConsts::I64AtomicRMW##Op##32U: \ + SET(Op, i64, 4); \ + break; + + switch (code) { SET_FOR_OP(Add); SET_FOR_OP(Sub); SET_FOR_OP(And); SET_FOR_OP(Or); SET_FOR_OP(Xor); SET_FOR_OP(Xchg); - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } #undef SET_FOR_OP #undef SET - if (debug) std::cerr << "zz node: AtomicRMW" << std::endl; + if (debug) + std::cerr << "zz node: AtomicRMW" << std::endl; Address readAlign; readMemoryAccess(readAlign, curr->offset); - if (readAlign != curr->bytes) throwError("Align of AtomicRMW must match size"); + if (readAlign != curr->bytes) + throwError("Align of AtomicRMW must match size"); curr->value = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); curr->finalize(); @@ -2207,30 +2738,50 @@ bool WasmBinaryBuilder::maybeVisitAtomicRMW(Expression*& out, uint8_t code) { return true; } -bool WasmBinaryBuilder::maybeVisitAtomicCmpxchg(Expression*& out, uint8_t code) { - if (code < BinaryConsts::AtomicCmpxchgOps_Begin || code > BinaryConsts::AtomicCmpxchgOps_End) return false; +bool WasmBinaryBuilder::maybeVisitAtomicCmpxchg(Expression*& out, + uint8_t code) { + if (code < BinaryConsts::AtomicCmpxchgOps_Begin || + code > BinaryConsts::AtomicCmpxchgOps_End) + return false; auto* curr = allocator.alloc<AtomicCmpxchg>(); // Set curr to the given type and size. -#define SET(optype, size) \ - curr->type = optype; \ +#define SET(optype, size) \ + curr->type = optype; \ curr->bytes = size switch (code) { - case BinaryConsts::I32AtomicCmpxchg: SET(i32, 4); break; - case BinaryConsts::I64AtomicCmpxchg: SET(i64, 8); break; - case BinaryConsts::I32AtomicCmpxchg8U: SET(i32, 1); break; - case BinaryConsts::I32AtomicCmpxchg16U: SET(i32, 2); break; - case BinaryConsts::I64AtomicCmpxchg8U: SET(i64, 1); break; - case BinaryConsts::I64AtomicCmpxchg16U: SET(i64, 2); break; - case BinaryConsts::I64AtomicCmpxchg32U: SET(i64, 4); break; - default: WASM_UNREACHABLE(); + case BinaryConsts::I32AtomicCmpxchg: + SET(i32, 4); + break; + case BinaryConsts::I64AtomicCmpxchg: + SET(i64, 8); + break; + case BinaryConsts::I32AtomicCmpxchg8U: + SET(i32, 1); + break; + case BinaryConsts::I32AtomicCmpxchg16U: + SET(i32, 2); + break; + case BinaryConsts::I64AtomicCmpxchg8U: + SET(i64, 1); + break; + case BinaryConsts::I64AtomicCmpxchg16U: + SET(i64, 2); + break; + case BinaryConsts::I64AtomicCmpxchg32U: + SET(i64, 4); + break; + default: + WASM_UNREACHABLE(); } - if (debug) std::cerr << "zz node: AtomicCmpxchg" << std::endl; + if (debug) + std::cerr << "zz node: AtomicCmpxchg" << std::endl; Address readAlign; readMemoryAccess(readAlign, curr->offset); - if (readAlign != curr->bytes) throwError("Align of AtomicCpxchg must match size"); + if (readAlign != curr->bytes) + throwError("Align of AtomicCpxchg must match size"); curr->replacement = popNonVoidExpression(); curr->expected = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); @@ -2240,38 +2791,49 @@ bool WasmBinaryBuilder::maybeVisitAtomicCmpxchg(Expression*& out, uint8_t code) } bool WasmBinaryBuilder::maybeVisitAtomicWait(Expression*& out, uint8_t code) { - if (code < BinaryConsts::I32AtomicWait || code > BinaryConsts::I64AtomicWait) return false; + if (code < BinaryConsts::I32AtomicWait || code > BinaryConsts::I64AtomicWait) + return false; auto* curr = allocator.alloc<AtomicWait>(); switch (code) { - case BinaryConsts::I32AtomicWait: curr->expectedType = i32; break; - case BinaryConsts::I64AtomicWait: curr->expectedType = i64; break; - default: WASM_UNREACHABLE(); + case BinaryConsts::I32AtomicWait: + curr->expectedType = i32; + break; + case BinaryConsts::I64AtomicWait: + curr->expectedType = i64; + break; + default: + WASM_UNREACHABLE(); } curr->type = i32; - if (debug) std::cerr << "zz node: AtomicWait" << std::endl; + if (debug) + std::cerr << "zz node: AtomicWait" << std::endl; curr->timeout = popNonVoidExpression(); curr->expected = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); Address readAlign; readMemoryAccess(readAlign, curr->offset); - if (readAlign != getTypeSize(curr->expectedType)) throwError("Align of AtomicWait must match size"); + if (readAlign != getTypeSize(curr->expectedType)) + throwError("Align of AtomicWait must match size"); curr->finalize(); out = curr; return true; } bool WasmBinaryBuilder::maybeVisitAtomicNotify(Expression*& out, uint8_t code) { - if (code != BinaryConsts::AtomicNotify) return false; + if (code != BinaryConsts::AtomicNotify) + return false; auto* curr = allocator.alloc<AtomicNotify>(); - if (debug) std::cerr << "zz node: AtomicNotify" << std::endl; + if (debug) + std::cerr << "zz node: AtomicNotify" << std::endl; curr->type = i32; curr->notifyCount = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); Address readAlign; readMemoryAccess(readAlign, curr->offset); - if (readAlign != getTypeSize(curr->type)) throwError("Align of AtomicNotify must match size"); + if (readAlign != getTypeSize(curr->type)) + throwError("Align of AtomicNotify must match size"); curr->finalize(); out = curr; return true; @@ -2279,13 +2841,27 @@ bool WasmBinaryBuilder::maybeVisitAtomicNotify(Expression*& out, uint8_t code) { bool WasmBinaryBuilder::maybeVisitConst(Expression*& out, uint8_t code) { Const* curr; - if (debug) std::cerr << "zz node: Const, code " << code << std::endl; + if (debug) + std::cerr << "zz node: Const, code " << code << std::endl; switch (code) { - case BinaryConsts::I32Const: curr = allocator.alloc<Const>(); curr->value = Literal(getS32LEB()); break; - case BinaryConsts::I64Const: curr = allocator.alloc<Const>(); curr->value = Literal(getS64LEB()); break; - case BinaryConsts::F32Const: curr = allocator.alloc<Const>(); curr->value = getFloat32Literal(); break; - case BinaryConsts::F64Const: curr = allocator.alloc<Const>(); curr->value = getFloat64Literal(); break; - default: return false; + case BinaryConsts::I32Const: + curr = allocator.alloc<Const>(); + curr->value = Literal(getS32LEB()); + break; + case BinaryConsts::I64Const: + curr = allocator.alloc<Const>(); + curr->value = Literal(getS64LEB()); + break; + case BinaryConsts::F32Const: + curr = allocator.alloc<Const>(); + curr->value = getFloat32Literal(); + break; + case BinaryConsts::F64Const: + curr = allocator.alloc<Const>(); + curr->value = getFloat64Literal(); + break; + default: + return false; } curr->type = curr->value.type; out = curr; @@ -2296,67 +2872,225 @@ bool WasmBinaryBuilder::maybeVisitConst(Expression*& out, uint8_t code) { bool WasmBinaryBuilder::maybeVisitUnary(Expression*& out, uint8_t code) { Unary* curr; switch (code) { - case BinaryConsts::I32Clz: curr = allocator.alloc<Unary>(); curr->op = ClzInt32; break; - case BinaryConsts::I64Clz: curr = allocator.alloc<Unary>(); curr->op = ClzInt64; break; - case BinaryConsts::I32Ctz: curr = allocator.alloc<Unary>(); curr->op = CtzInt32; break; - case BinaryConsts::I64Ctz: curr = allocator.alloc<Unary>(); curr->op = CtzInt64; break; - case BinaryConsts::I32Popcnt: curr = allocator.alloc<Unary>(); curr->op = PopcntInt32; break; - case BinaryConsts::I64Popcnt: curr = allocator.alloc<Unary>(); curr->op = PopcntInt64; break; - case BinaryConsts::I32EqZ: curr = allocator.alloc<Unary>(); curr->op = EqZInt32; break; - case BinaryConsts::I64EqZ: curr = allocator.alloc<Unary>(); curr->op = EqZInt64; break; - case BinaryConsts::F32Neg: curr = allocator.alloc<Unary>(); curr->op = NegFloat32; break; - case BinaryConsts::F64Neg: curr = allocator.alloc<Unary>(); curr->op = NegFloat64; break; - case BinaryConsts::F32Abs: curr = allocator.alloc<Unary>(); curr->op = AbsFloat32; break; - case BinaryConsts::F64Abs: curr = allocator.alloc<Unary>(); curr->op = AbsFloat64; break; - case BinaryConsts::F32Ceil: curr = allocator.alloc<Unary>(); curr->op = CeilFloat32; break; - case BinaryConsts::F64Ceil: curr = allocator.alloc<Unary>(); curr->op = CeilFloat64; break; - case BinaryConsts::F32Floor: curr = allocator.alloc<Unary>(); curr->op = FloorFloat32; break; - case BinaryConsts::F64Floor: curr = allocator.alloc<Unary>(); curr->op = FloorFloat64; break; - case BinaryConsts::F32NearestInt: curr = allocator.alloc<Unary>(); curr->op = NearestFloat32; break; - case BinaryConsts::F64NearestInt: curr = allocator.alloc<Unary>(); curr->op = NearestFloat64; break; - case BinaryConsts::F32Sqrt: curr = allocator.alloc<Unary>(); curr->op = SqrtFloat32; break; - case BinaryConsts::F64Sqrt: curr = allocator.alloc<Unary>(); curr->op = SqrtFloat64; break; - case BinaryConsts::F32UConvertI32: curr = allocator.alloc<Unary>(); curr->op = ConvertUInt32ToFloat32; break; - case BinaryConsts::F64UConvertI32: curr = allocator.alloc<Unary>(); curr->op = ConvertUInt32ToFloat64; break; - case BinaryConsts::F32SConvertI32: curr = allocator.alloc<Unary>(); curr->op = ConvertSInt32ToFloat32; break; - case BinaryConsts::F64SConvertI32: curr = allocator.alloc<Unary>(); curr->op = ConvertSInt32ToFloat64; break; - case BinaryConsts::F32UConvertI64: curr = allocator.alloc<Unary>(); curr->op = ConvertUInt64ToFloat32; break; - case BinaryConsts::F64UConvertI64: curr = allocator.alloc<Unary>(); curr->op = ConvertUInt64ToFloat64; break; - case BinaryConsts::F32SConvertI64: curr = allocator.alloc<Unary>(); curr->op = ConvertSInt64ToFloat32; break; - case BinaryConsts::F64SConvertI64: curr = allocator.alloc<Unary>(); curr->op = ConvertSInt64ToFloat64; break; - - case BinaryConsts::I64STruncI32: curr = allocator.alloc<Unary>(); curr->op = ExtendSInt32; break; - case BinaryConsts::I64UTruncI32: curr = allocator.alloc<Unary>(); curr->op = ExtendUInt32; break; - case BinaryConsts::I32ConvertI64: curr = allocator.alloc<Unary>(); curr->op = WrapInt64; break; - - case BinaryConsts::I32UTruncF32: curr = allocator.alloc<Unary>(); curr->op = TruncUFloat32ToInt32; break; - case BinaryConsts::I32UTruncF64: curr = allocator.alloc<Unary>(); curr->op = TruncUFloat64ToInt32; break; - case BinaryConsts::I32STruncF32: curr = allocator.alloc<Unary>(); curr->op = TruncSFloat32ToInt32; break; - case BinaryConsts::I32STruncF64: curr = allocator.alloc<Unary>(); curr->op = TruncSFloat64ToInt32; break; - case BinaryConsts::I64UTruncF32: curr = allocator.alloc<Unary>(); curr->op = TruncUFloat32ToInt64; break; - case BinaryConsts::I64UTruncF64: curr = allocator.alloc<Unary>(); curr->op = TruncUFloat64ToInt64; break; - case BinaryConsts::I64STruncF32: curr = allocator.alloc<Unary>(); curr->op = TruncSFloat32ToInt64; break; - case BinaryConsts::I64STruncF64: curr = allocator.alloc<Unary>(); curr->op = TruncSFloat64ToInt64; break; - - case BinaryConsts::F32Trunc: curr = allocator.alloc<Unary>(); curr->op = TruncFloat32; break; - case BinaryConsts::F64Trunc: curr = allocator.alloc<Unary>(); curr->op = TruncFloat64; break; - - case BinaryConsts::F32ConvertF64: curr = allocator.alloc<Unary>(); curr->op = DemoteFloat64; break; - case BinaryConsts::F64ConvertF32: curr = allocator.alloc<Unary>(); curr->op = PromoteFloat32; break; - case BinaryConsts::I32ReinterpretF32: curr = allocator.alloc<Unary>(); curr->op = ReinterpretFloat32; break; - case BinaryConsts::I64ReinterpretF64: curr = allocator.alloc<Unary>(); curr->op = ReinterpretFloat64; break; - case BinaryConsts::F32ReinterpretI32: curr = allocator.alloc<Unary>(); curr->op = ReinterpretInt32; break; - case BinaryConsts::F64ReinterpretI64: curr = allocator.alloc<Unary>(); curr->op = ReinterpretInt64; break; - - case BinaryConsts::I32ExtendS8: curr = allocator.alloc<Unary>(); curr->op = ExtendS8Int32; break; - case BinaryConsts::I32ExtendS16: curr = allocator.alloc<Unary>(); curr->op = ExtendS16Int32; break; - case BinaryConsts::I64ExtendS8: curr = allocator.alloc<Unary>(); curr->op = ExtendS8Int64; break; - case BinaryConsts::I64ExtendS16: curr = allocator.alloc<Unary>(); curr->op = ExtendS16Int64; break; - case BinaryConsts::I64ExtendS32: curr = allocator.alloc<Unary>(); curr->op = ExtendS32Int64; break; - - default: return false; - } - if (debug) std::cerr << "zz node: Unary" << std::endl; + case BinaryConsts::I32Clz: + curr = allocator.alloc<Unary>(); + curr->op = ClzInt32; + break; + case BinaryConsts::I64Clz: + curr = allocator.alloc<Unary>(); + curr->op = ClzInt64; + break; + case BinaryConsts::I32Ctz: + curr = allocator.alloc<Unary>(); + curr->op = CtzInt32; + break; + case BinaryConsts::I64Ctz: + curr = allocator.alloc<Unary>(); + curr->op = CtzInt64; + break; + case BinaryConsts::I32Popcnt: + curr = allocator.alloc<Unary>(); + curr->op = PopcntInt32; + break; + case BinaryConsts::I64Popcnt: + curr = allocator.alloc<Unary>(); + curr->op = PopcntInt64; + break; + case BinaryConsts::I32EqZ: + curr = allocator.alloc<Unary>(); + curr->op = EqZInt32; + break; + case BinaryConsts::I64EqZ: + curr = allocator.alloc<Unary>(); + curr->op = EqZInt64; + break; + case BinaryConsts::F32Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegFloat32; + break; + case BinaryConsts::F64Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegFloat64; + break; + case BinaryConsts::F32Abs: + curr = allocator.alloc<Unary>(); + curr->op = AbsFloat32; + break; + case BinaryConsts::F64Abs: + curr = allocator.alloc<Unary>(); + curr->op = AbsFloat64; + break; + case BinaryConsts::F32Ceil: + curr = allocator.alloc<Unary>(); + curr->op = CeilFloat32; + break; + case BinaryConsts::F64Ceil: + curr = allocator.alloc<Unary>(); + curr->op = CeilFloat64; + break; + case BinaryConsts::F32Floor: + curr = allocator.alloc<Unary>(); + curr->op = FloorFloat32; + break; + case BinaryConsts::F64Floor: + curr = allocator.alloc<Unary>(); + curr->op = FloorFloat64; + break; + case BinaryConsts::F32NearestInt: + curr = allocator.alloc<Unary>(); + curr->op = NearestFloat32; + break; + case BinaryConsts::F64NearestInt: + curr = allocator.alloc<Unary>(); + curr->op = NearestFloat64; + break; + case BinaryConsts::F32Sqrt: + curr = allocator.alloc<Unary>(); + curr->op = SqrtFloat32; + break; + case BinaryConsts::F64Sqrt: + curr = allocator.alloc<Unary>(); + curr->op = SqrtFloat64; + break; + case BinaryConsts::F32UConvertI32: + curr = allocator.alloc<Unary>(); + curr->op = ConvertUInt32ToFloat32; + break; + case BinaryConsts::F64UConvertI32: + curr = allocator.alloc<Unary>(); + curr->op = ConvertUInt32ToFloat64; + break; + case BinaryConsts::F32SConvertI32: + curr = allocator.alloc<Unary>(); + curr->op = ConvertSInt32ToFloat32; + break; + case BinaryConsts::F64SConvertI32: + curr = allocator.alloc<Unary>(); + curr->op = ConvertSInt32ToFloat64; + break; + case BinaryConsts::F32UConvertI64: + curr = allocator.alloc<Unary>(); + curr->op = ConvertUInt64ToFloat32; + break; + case BinaryConsts::F64UConvertI64: + curr = allocator.alloc<Unary>(); + curr->op = ConvertUInt64ToFloat64; + break; + case BinaryConsts::F32SConvertI64: + curr = allocator.alloc<Unary>(); + curr->op = ConvertSInt64ToFloat32; + break; + case BinaryConsts::F64SConvertI64: + curr = allocator.alloc<Unary>(); + curr->op = ConvertSInt64ToFloat64; + break; + + case BinaryConsts::I64STruncI32: + curr = allocator.alloc<Unary>(); + curr->op = ExtendSInt32; + break; + case BinaryConsts::I64UTruncI32: + curr = allocator.alloc<Unary>(); + curr->op = ExtendUInt32; + break; + case BinaryConsts::I32ConvertI64: + curr = allocator.alloc<Unary>(); + curr->op = WrapInt64; + break; + + case BinaryConsts::I32UTruncF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncUFloat32ToInt32; + break; + case BinaryConsts::I32UTruncF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncUFloat64ToInt32; + break; + case BinaryConsts::I32STruncF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncSFloat32ToInt32; + break; + case BinaryConsts::I32STruncF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncSFloat64ToInt32; + break; + case BinaryConsts::I64UTruncF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncUFloat32ToInt64; + break; + case BinaryConsts::I64UTruncF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncUFloat64ToInt64; + break; + case BinaryConsts::I64STruncF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncSFloat32ToInt64; + break; + case BinaryConsts::I64STruncF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncSFloat64ToInt64; + break; + + case BinaryConsts::F32Trunc: + curr = allocator.alloc<Unary>(); + curr->op = TruncFloat32; + break; + case BinaryConsts::F64Trunc: + curr = allocator.alloc<Unary>(); + curr->op = TruncFloat64; + break; + + case BinaryConsts::F32ConvertF64: + curr = allocator.alloc<Unary>(); + curr->op = DemoteFloat64; + break; + case BinaryConsts::F64ConvertF32: + curr = allocator.alloc<Unary>(); + curr->op = PromoteFloat32; + break; + case BinaryConsts::I32ReinterpretF32: + curr = allocator.alloc<Unary>(); + curr->op = ReinterpretFloat32; + break; + case BinaryConsts::I64ReinterpretF64: + curr = allocator.alloc<Unary>(); + curr->op = ReinterpretFloat64; + break; + case BinaryConsts::F32ReinterpretI32: + curr = allocator.alloc<Unary>(); + curr->op = ReinterpretInt32; + break; + case BinaryConsts::F64ReinterpretI64: + curr = allocator.alloc<Unary>(); + curr->op = ReinterpretInt64; + break; + + case BinaryConsts::I32ExtendS8: + curr = allocator.alloc<Unary>(); + curr->op = ExtendS8Int32; + break; + case BinaryConsts::I32ExtendS16: + curr = allocator.alloc<Unary>(); + curr->op = ExtendS16Int32; + break; + case BinaryConsts::I64ExtendS8: + curr = allocator.alloc<Unary>(); + curr->op = ExtendS8Int64; + break; + case BinaryConsts::I64ExtendS16: + curr = allocator.alloc<Unary>(); + curr->op = ExtendS16Int64; + break; + case BinaryConsts::I64ExtendS32: + curr = allocator.alloc<Unary>(); + curr->op = ExtendS32Int64; + break; + + default: + return false; + } + if (debug) + std::cerr << "zz node: Unary" << std::endl; curr->value = popNonVoidExpression(); curr->finalize(); out = curr; @@ -2366,17 +3100,43 @@ bool WasmBinaryBuilder::maybeVisitUnary(Expression*& out, uint8_t code) { bool WasmBinaryBuilder::maybeVisitTruncSat(Expression*& out, uint32_t code) { Unary* curr; switch (code) { - case BinaryConsts::I32STruncSatF32: curr = allocator.alloc<Unary>(); curr->op = TruncSatSFloat32ToInt32; break; - case BinaryConsts::I32UTruncSatF32: curr = allocator.alloc<Unary>(); curr->op = TruncSatUFloat32ToInt32; break; - case BinaryConsts::I32STruncSatF64: curr = allocator.alloc<Unary>(); curr->op = TruncSatSFloat64ToInt32; break; - case BinaryConsts::I32UTruncSatF64: curr = allocator.alloc<Unary>(); curr->op = TruncSatUFloat64ToInt32; break; - case BinaryConsts::I64STruncSatF32: curr = allocator.alloc<Unary>(); curr->op = TruncSatSFloat32ToInt64; break; - case BinaryConsts::I64UTruncSatF32: curr = allocator.alloc<Unary>(); curr->op = TruncSatUFloat32ToInt64; break; - case BinaryConsts::I64STruncSatF64: curr = allocator.alloc<Unary>(); curr->op = TruncSatSFloat64ToInt64; break; - case BinaryConsts::I64UTruncSatF64: curr = allocator.alloc<Unary>(); curr->op = TruncSatUFloat64ToInt64; break; - default: return false; - } - if (debug) std::cerr << "zz node: Unary (nontrapping float-to-int)" << std::endl; + case BinaryConsts::I32STruncSatF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatSFloat32ToInt32; + break; + case BinaryConsts::I32UTruncSatF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatUFloat32ToInt32; + break; + case BinaryConsts::I32STruncSatF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatSFloat64ToInt32; + break; + case BinaryConsts::I32UTruncSatF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatUFloat64ToInt32; + break; + case BinaryConsts::I64STruncSatF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatSFloat32ToInt64; + break; + case BinaryConsts::I64UTruncSatF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatUFloat32ToInt64; + break; + case BinaryConsts::I64STruncSatF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatSFloat64ToInt64; + break; + case BinaryConsts::I64UTruncSatF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatUFloat64ToInt64; + break; + default: + return false; + } + if (debug) + std::cerr << "zz node: Unary (nontrapping float-to-int)" << std::endl; curr->value = popNonVoidExpression(); curr->finalize(); out = curr; @@ -2445,18 +3205,33 @@ bool WasmBinaryBuilder::maybeVisitMemoryFill(Expression*& out, uint32_t code) { bool WasmBinaryBuilder::maybeVisitBinary(Expression*& out, uint8_t code) { Binary* curr; -#define INT_TYPED_CODE(code) { \ - case BinaryConsts::I32##code: curr = allocator.alloc<Binary>(); curr->op = code##Int32; break; \ - case BinaryConsts::I64##code: curr = allocator.alloc<Binary>(); curr->op = code##Int64; break; \ - } -#define FLOAT_TYPED_CODE(code) { \ - case BinaryConsts::F32##code: curr = allocator.alloc<Binary>(); curr->op = code##Float32; break; \ - case BinaryConsts::F64##code: curr = allocator.alloc<Binary>(); curr->op = code##Float64; break; \ +#define INT_TYPED_CODE(code) \ + { \ + case BinaryConsts::I32##code: \ + curr = allocator.alloc<Binary>(); \ + curr->op = code##Int32; \ + break; \ + case BinaryConsts::I64##code: \ + curr = allocator.alloc<Binary>(); \ + curr->op = code##Int64; \ + break; \ + } +#define FLOAT_TYPED_CODE(code) \ + { \ + case BinaryConsts::F32##code: \ + curr = allocator.alloc<Binary>(); \ + curr->op = code##Float32; \ + break; \ + case BinaryConsts::F64##code: \ + curr = allocator.alloc<Binary>(); \ + curr->op = code##Float64; \ + break; \ + } +#define TYPED_CODE(code) \ + { \ + INT_TYPED_CODE(code) \ + FLOAT_TYPED_CODE(code) \ } -#define TYPED_CODE(code) { \ - INT_TYPED_CODE(code) \ - FLOAT_TYPED_CODE(code) \ - } switch (code) { TYPED_CODE(Add); @@ -2492,9 +3267,11 @@ bool WasmBinaryBuilder::maybeVisitBinary(Expression*& out, uint8_t code) { FLOAT_TYPED_CODE(Le); FLOAT_TYPED_CODE(Gt); FLOAT_TYPED_CODE(Ge); - default: return false; + default: + return false; } - if (debug) std::cerr << "zz node: Binary" << std::endl; + if (debug) + std::cerr << "zz node: Binary" << std::endl; curr->right = popNonVoidExpression(); curr->left = popNonVoidExpression(); curr->finalize(); @@ -2508,85 +3285,315 @@ bool WasmBinaryBuilder::maybeVisitBinary(Expression*& out, uint8_t code) { bool WasmBinaryBuilder::maybeVisitSIMDBinary(Expression*& out, uint32_t code) { Binary* curr; switch (code) { - case BinaryConsts::I8x16Eq: curr = allocator.alloc<Binary>(); curr->op = EqVecI8x16; break; - case BinaryConsts::I8x16Ne: curr = allocator.alloc<Binary>(); curr->op = NeVecI8x16; break; - case BinaryConsts::I8x16LtS: curr = allocator.alloc<Binary>(); curr->op = LtSVecI8x16; break; - case BinaryConsts::I8x16LtU: curr = allocator.alloc<Binary>(); curr->op = LtUVecI8x16; break; - case BinaryConsts::I8x16GtS: curr = allocator.alloc<Binary>(); curr->op = GtSVecI8x16; break; - case BinaryConsts::I8x16GtU: curr = allocator.alloc<Binary>(); curr->op = GtUVecI8x16; break; - case BinaryConsts::I8x16LeS: curr = allocator.alloc<Binary>(); curr->op = LeSVecI8x16; break; - case BinaryConsts::I8x16LeU: curr = allocator.alloc<Binary>(); curr->op = LeUVecI8x16; break; - case BinaryConsts::I8x16GeS: curr = allocator.alloc<Binary>(); curr->op = GeSVecI8x16; break; - case BinaryConsts::I8x16GeU: curr = allocator.alloc<Binary>(); curr->op = GeUVecI8x16; break; - case BinaryConsts::I16x8Eq: curr = allocator.alloc<Binary>(); curr->op = EqVecI16x8; break; - case BinaryConsts::I16x8Ne: curr = allocator.alloc<Binary>(); curr->op = NeVecI16x8; break; - case BinaryConsts::I16x8LtS: curr = allocator.alloc<Binary>(); curr->op = LtSVecI16x8; break; - case BinaryConsts::I16x8LtU: curr = allocator.alloc<Binary>(); curr->op = LtUVecI16x8; break; - case BinaryConsts::I16x8GtS: curr = allocator.alloc<Binary>(); curr->op = GtSVecI16x8; break; - case BinaryConsts::I16x8GtU: curr = allocator.alloc<Binary>(); curr->op = GtUVecI16x8; break; - case BinaryConsts::I16x8LeS: curr = allocator.alloc<Binary>(); curr->op = LeSVecI16x8; break; - case BinaryConsts::I16x8LeU: curr = allocator.alloc<Binary>(); curr->op = LeUVecI16x8; break; - case BinaryConsts::I16x8GeS: curr = allocator.alloc<Binary>(); curr->op = GeSVecI16x8; break; - case BinaryConsts::I16x8GeU: curr = allocator.alloc<Binary>(); curr->op = GeUVecI16x8; break; - case BinaryConsts::I32x4Eq: curr = allocator.alloc<Binary>(); curr->op = EqVecI32x4; break; - case BinaryConsts::I32x4Ne: curr = allocator.alloc<Binary>(); curr->op = NeVecI32x4; break; - case BinaryConsts::I32x4LtS: curr = allocator.alloc<Binary>(); curr->op = LtSVecI32x4; break; - case BinaryConsts::I32x4LtU: curr = allocator.alloc<Binary>(); curr->op = LtUVecI32x4; break; - case BinaryConsts::I32x4GtS: curr = allocator.alloc<Binary>(); curr->op = GtSVecI32x4; break; - case BinaryConsts::I32x4GtU: curr = allocator.alloc<Binary>(); curr->op = GtUVecI32x4; break; - case BinaryConsts::I32x4LeS: curr = allocator.alloc<Binary>(); curr->op = LeSVecI32x4; break; - case BinaryConsts::I32x4LeU: curr = allocator.alloc<Binary>(); curr->op = LeUVecI32x4; break; - case BinaryConsts::I32x4GeS: curr = allocator.alloc<Binary>(); curr->op = GeSVecI32x4; break; - case BinaryConsts::I32x4GeU: curr = allocator.alloc<Binary>(); curr->op = GeUVecI32x4; break; - case BinaryConsts::F32x4Eq: curr = allocator.alloc<Binary>(); curr->op = EqVecF32x4; break; - case BinaryConsts::F32x4Ne: curr = allocator.alloc<Binary>(); curr->op = NeVecF32x4; break; - case BinaryConsts::F32x4Lt: curr = allocator.alloc<Binary>(); curr->op = LtVecF32x4; break; - case BinaryConsts::F32x4Gt: curr = allocator.alloc<Binary>(); curr->op = GtVecF32x4; break; - case BinaryConsts::F32x4Le: curr = allocator.alloc<Binary>(); curr->op = LeVecF32x4; break; - case BinaryConsts::F32x4Ge: curr = allocator.alloc<Binary>(); curr->op = GeVecF32x4; break; - case BinaryConsts::F64x2Eq: curr = allocator.alloc<Binary>(); curr->op = EqVecF64x2; break; - case BinaryConsts::F64x2Ne: curr = allocator.alloc<Binary>(); curr->op = NeVecF64x2; break; - case BinaryConsts::F64x2Lt: curr = allocator.alloc<Binary>(); curr->op = LtVecF64x2; break; - case BinaryConsts::F64x2Gt: curr = allocator.alloc<Binary>(); curr->op = GtVecF64x2; break; - case BinaryConsts::F64x2Le: curr = allocator.alloc<Binary>(); curr->op = LeVecF64x2; break; - case BinaryConsts::F64x2Ge: curr = allocator.alloc<Binary>(); curr->op = GeVecF64x2; break; - case BinaryConsts::V128And: curr = allocator.alloc<Binary>(); curr->op = AndVec128; break; - case BinaryConsts::V128Or: curr = allocator.alloc<Binary>(); curr->op = OrVec128; break; - case BinaryConsts::V128Xor: curr = allocator.alloc<Binary>(); curr->op = XorVec128; break; - case BinaryConsts::I8x16Add: curr = allocator.alloc<Binary>(); curr->op = AddVecI8x16; break; - case BinaryConsts::I8x16AddSatS: curr = allocator.alloc<Binary>(); curr->op = AddSatSVecI8x16; break; - case BinaryConsts::I8x16AddSatU: curr = allocator.alloc<Binary>(); curr->op = AddSatUVecI8x16; break; - case BinaryConsts::I8x16Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecI8x16; break; - case BinaryConsts::I8x16SubSatS: curr = allocator.alloc<Binary>(); curr->op = SubSatSVecI8x16; break; - case BinaryConsts::I8x16SubSatU: curr = allocator.alloc<Binary>(); curr->op = SubSatUVecI8x16; break; - case BinaryConsts::I8x16Mul: curr = allocator.alloc<Binary>(); curr->op = MulVecI8x16; break; - case BinaryConsts::I16x8Add: curr = allocator.alloc<Binary>(); curr->op = AddVecI16x8; break; - case BinaryConsts::I16x8AddSatS: curr = allocator.alloc<Binary>(); curr->op = AddSatSVecI16x8; break; - case BinaryConsts::I16x8AddSatU: curr = allocator.alloc<Binary>(); curr->op = AddSatUVecI16x8; break; - case BinaryConsts::I16x8Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecI16x8; break; - case BinaryConsts::I16x8SubSatS: curr = allocator.alloc<Binary>(); curr->op = SubSatSVecI16x8; break; - case BinaryConsts::I16x8SubSatU: curr = allocator.alloc<Binary>(); curr->op = SubSatUVecI16x8; break; - case BinaryConsts::I16x8Mul: curr = allocator.alloc<Binary>(); curr->op = MulVecI16x8; break; - case BinaryConsts::I32x4Add: curr = allocator.alloc<Binary>(); curr->op = AddVecI32x4; break; - case BinaryConsts::I32x4Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecI32x4; break; - case BinaryConsts::I32x4Mul: curr = allocator.alloc<Binary>(); curr->op = MulVecI32x4; break; - case BinaryConsts::I64x2Add: curr = allocator.alloc<Binary>(); curr->op = AddVecI64x2; break; - case BinaryConsts::I64x2Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecI64x2; break; - case BinaryConsts::F32x4Add: curr = allocator.alloc<Binary>(); curr->op = AddVecF32x4; break; - case BinaryConsts::F32x4Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecF32x4; break; - case BinaryConsts::F32x4Mul: curr = allocator.alloc<Binary>(); curr->op = MulVecF32x4; break; - case BinaryConsts::F32x4Div: curr = allocator.alloc<Binary>(); curr->op = DivVecF32x4; break; - case BinaryConsts::F32x4Min: curr = allocator.alloc<Binary>(); curr->op = MinVecF32x4; break; - case BinaryConsts::F32x4Max: curr = allocator.alloc<Binary>(); curr->op = MaxVecF32x4; break; - case BinaryConsts::F64x2Add: curr = allocator.alloc<Binary>(); curr->op = AddVecF64x2; break; - case BinaryConsts::F64x2Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecF64x2; break; - case BinaryConsts::F64x2Mul: curr = allocator.alloc<Binary>(); curr->op = MulVecF64x2; break; - case BinaryConsts::F64x2Div: curr = allocator.alloc<Binary>(); curr->op = DivVecF64x2; break; - case BinaryConsts::F64x2Min: curr = allocator.alloc<Binary>(); curr->op = MinVecF64x2; break; - case BinaryConsts::F64x2Max: curr = allocator.alloc<Binary>(); curr->op = MaxVecF64x2; break; - default: return false; - } - if (debug) std::cerr << "zz node: Binary" << std::endl; + case BinaryConsts::I8x16Eq: + curr = allocator.alloc<Binary>(); + curr->op = EqVecI8x16; + break; + case BinaryConsts::I8x16Ne: + curr = allocator.alloc<Binary>(); + curr->op = NeVecI8x16; + break; + case BinaryConsts::I8x16LtS: + curr = allocator.alloc<Binary>(); + curr->op = LtSVecI8x16; + break; + case BinaryConsts::I8x16LtU: + curr = allocator.alloc<Binary>(); + curr->op = LtUVecI8x16; + break; + case BinaryConsts::I8x16GtS: + curr = allocator.alloc<Binary>(); + curr->op = GtSVecI8x16; + break; + case BinaryConsts::I8x16GtU: + curr = allocator.alloc<Binary>(); + curr->op = GtUVecI8x16; + break; + case BinaryConsts::I8x16LeS: + curr = allocator.alloc<Binary>(); + curr->op = LeSVecI8x16; + break; + case BinaryConsts::I8x16LeU: + curr = allocator.alloc<Binary>(); + curr->op = LeUVecI8x16; + break; + case BinaryConsts::I8x16GeS: + curr = allocator.alloc<Binary>(); + curr->op = GeSVecI8x16; + break; + case BinaryConsts::I8x16GeU: + curr = allocator.alloc<Binary>(); + curr->op = GeUVecI8x16; + break; + case BinaryConsts::I16x8Eq: + curr = allocator.alloc<Binary>(); + curr->op = EqVecI16x8; + break; + case BinaryConsts::I16x8Ne: + curr = allocator.alloc<Binary>(); + curr->op = NeVecI16x8; + break; + case BinaryConsts::I16x8LtS: + curr = allocator.alloc<Binary>(); + curr->op = LtSVecI16x8; + break; + case BinaryConsts::I16x8LtU: + curr = allocator.alloc<Binary>(); + curr->op = LtUVecI16x8; + break; + case BinaryConsts::I16x8GtS: + curr = allocator.alloc<Binary>(); + curr->op = GtSVecI16x8; + break; + case BinaryConsts::I16x8GtU: + curr = allocator.alloc<Binary>(); + curr->op = GtUVecI16x8; + break; + case BinaryConsts::I16x8LeS: + curr = allocator.alloc<Binary>(); + curr->op = LeSVecI16x8; + break; + case BinaryConsts::I16x8LeU: + curr = allocator.alloc<Binary>(); + curr->op = LeUVecI16x8; + break; + case BinaryConsts::I16x8GeS: + curr = allocator.alloc<Binary>(); + curr->op = GeSVecI16x8; + break; + case BinaryConsts::I16x8GeU: + curr = allocator.alloc<Binary>(); + curr->op = GeUVecI16x8; + break; + case BinaryConsts::I32x4Eq: + curr = allocator.alloc<Binary>(); + curr->op = EqVecI32x4; + break; + case BinaryConsts::I32x4Ne: + curr = allocator.alloc<Binary>(); + curr->op = NeVecI32x4; + break; + case BinaryConsts::I32x4LtS: + curr = allocator.alloc<Binary>(); + curr->op = LtSVecI32x4; + break; + case BinaryConsts::I32x4LtU: + curr = allocator.alloc<Binary>(); + curr->op = LtUVecI32x4; + break; + case BinaryConsts::I32x4GtS: + curr = allocator.alloc<Binary>(); + curr->op = GtSVecI32x4; + break; + case BinaryConsts::I32x4GtU: + curr = allocator.alloc<Binary>(); + curr->op = GtUVecI32x4; + break; + case BinaryConsts::I32x4LeS: + curr = allocator.alloc<Binary>(); + curr->op = LeSVecI32x4; + break; + case BinaryConsts::I32x4LeU: + curr = allocator.alloc<Binary>(); + curr->op = LeUVecI32x4; + break; + case BinaryConsts::I32x4GeS: + curr = allocator.alloc<Binary>(); + curr->op = GeSVecI32x4; + break; + case BinaryConsts::I32x4GeU: + curr = allocator.alloc<Binary>(); + curr->op = GeUVecI32x4; + break; + case BinaryConsts::F32x4Eq: + curr = allocator.alloc<Binary>(); + curr->op = EqVecF32x4; + break; + case BinaryConsts::F32x4Ne: + curr = allocator.alloc<Binary>(); + curr->op = NeVecF32x4; + break; + case BinaryConsts::F32x4Lt: + curr = allocator.alloc<Binary>(); + curr->op = LtVecF32x4; + break; + case BinaryConsts::F32x4Gt: + curr = allocator.alloc<Binary>(); + curr->op = GtVecF32x4; + break; + case BinaryConsts::F32x4Le: + curr = allocator.alloc<Binary>(); + curr->op = LeVecF32x4; + break; + case BinaryConsts::F32x4Ge: + curr = allocator.alloc<Binary>(); + curr->op = GeVecF32x4; + break; + case BinaryConsts::F64x2Eq: + curr = allocator.alloc<Binary>(); + curr->op = EqVecF64x2; + break; + case BinaryConsts::F64x2Ne: + curr = allocator.alloc<Binary>(); + curr->op = NeVecF64x2; + break; + case BinaryConsts::F64x2Lt: + curr = allocator.alloc<Binary>(); + curr->op = LtVecF64x2; + break; + case BinaryConsts::F64x2Gt: + curr = allocator.alloc<Binary>(); + curr->op = GtVecF64x2; + break; + case BinaryConsts::F64x2Le: + curr = allocator.alloc<Binary>(); + curr->op = LeVecF64x2; + break; + case BinaryConsts::F64x2Ge: + curr = allocator.alloc<Binary>(); + curr->op = GeVecF64x2; + break; + case BinaryConsts::V128And: + curr = allocator.alloc<Binary>(); + curr->op = AndVec128; + break; + case BinaryConsts::V128Or: + curr = allocator.alloc<Binary>(); + curr->op = OrVec128; + break; + case BinaryConsts::V128Xor: + curr = allocator.alloc<Binary>(); + curr->op = XorVec128; + break; + case BinaryConsts::I8x16Add: + curr = allocator.alloc<Binary>(); + curr->op = AddVecI8x16; + break; + case BinaryConsts::I8x16AddSatS: + curr = allocator.alloc<Binary>(); + curr->op = AddSatSVecI8x16; + break; + case BinaryConsts::I8x16AddSatU: + curr = allocator.alloc<Binary>(); + curr->op = AddSatUVecI8x16; + break; + case BinaryConsts::I8x16Sub: + curr = allocator.alloc<Binary>(); + curr->op = SubVecI8x16; + break; + case BinaryConsts::I8x16SubSatS: + curr = allocator.alloc<Binary>(); + curr->op = SubSatSVecI8x16; + break; + case BinaryConsts::I8x16SubSatU: + curr = allocator.alloc<Binary>(); + curr->op = SubSatUVecI8x16; + break; + case BinaryConsts::I8x16Mul: + curr = allocator.alloc<Binary>(); + curr->op = MulVecI8x16; + break; + case BinaryConsts::I16x8Add: + curr = allocator.alloc<Binary>(); + curr->op = AddVecI16x8; + break; + case BinaryConsts::I16x8AddSatS: + curr = allocator.alloc<Binary>(); + curr->op = AddSatSVecI16x8; + break; + case BinaryConsts::I16x8AddSatU: + curr = allocator.alloc<Binary>(); + curr->op = AddSatUVecI16x8; + break; + case BinaryConsts::I16x8Sub: + curr = allocator.alloc<Binary>(); + curr->op = SubVecI16x8; + break; + case BinaryConsts::I16x8SubSatS: + curr = allocator.alloc<Binary>(); + curr->op = SubSatSVecI16x8; + break; + case BinaryConsts::I16x8SubSatU: + curr = allocator.alloc<Binary>(); + curr->op = SubSatUVecI16x8; + break; + case BinaryConsts::I16x8Mul: + curr = allocator.alloc<Binary>(); + curr->op = MulVecI16x8; + break; + case BinaryConsts::I32x4Add: + curr = allocator.alloc<Binary>(); + curr->op = AddVecI32x4; + break; + case BinaryConsts::I32x4Sub: + curr = allocator.alloc<Binary>(); + curr->op = SubVecI32x4; + break; + case BinaryConsts::I32x4Mul: + curr = allocator.alloc<Binary>(); + curr->op = MulVecI32x4; + break; + case BinaryConsts::I64x2Add: + curr = allocator.alloc<Binary>(); + curr->op = AddVecI64x2; + break; + case BinaryConsts::I64x2Sub: + curr = allocator.alloc<Binary>(); + curr->op = SubVecI64x2; + break; + case BinaryConsts::F32x4Add: + curr = allocator.alloc<Binary>(); + curr->op = AddVecF32x4; + break; + case BinaryConsts::F32x4Sub: + curr = allocator.alloc<Binary>(); + curr->op = SubVecF32x4; + break; + case BinaryConsts::F32x4Mul: + curr = allocator.alloc<Binary>(); + curr->op = MulVecF32x4; + break; + case BinaryConsts::F32x4Div: + curr = allocator.alloc<Binary>(); + curr->op = DivVecF32x4; + break; + case BinaryConsts::F32x4Min: + curr = allocator.alloc<Binary>(); + curr->op = MinVecF32x4; + break; + case BinaryConsts::F32x4Max: + curr = allocator.alloc<Binary>(); + curr->op = MaxVecF32x4; + break; + case BinaryConsts::F64x2Add: + curr = allocator.alloc<Binary>(); + curr->op = AddVecF64x2; + break; + case BinaryConsts::F64x2Sub: + curr = allocator.alloc<Binary>(); + curr->op = SubVecF64x2; + break; + case BinaryConsts::F64x2Mul: + curr = allocator.alloc<Binary>(); + curr->op = MulVecF64x2; + break; + case BinaryConsts::F64x2Div: + curr = allocator.alloc<Binary>(); + curr->op = DivVecF64x2; + break; + case BinaryConsts::F64x2Min: + curr = allocator.alloc<Binary>(); + curr->op = MinVecF64x2; + break; + case BinaryConsts::F64x2Max: + curr = allocator.alloc<Binary>(); + curr->op = MaxVecF64x2; + break; + default: + return false; + } + if (debug) + std::cerr << "zz node: Binary" << std::endl; curr->right = popNonVoidExpression(); curr->left = popNonVoidExpression(); curr->finalize(); @@ -2596,40 +3603,140 @@ bool WasmBinaryBuilder::maybeVisitSIMDBinary(Expression*& out, uint32_t code) { bool WasmBinaryBuilder::maybeVisitSIMDUnary(Expression*& out, uint32_t code) { Unary* curr; switch (code) { - case BinaryConsts::I8x16Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecI8x16; break; - case BinaryConsts::I16x8Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecI16x8; break; - case BinaryConsts::I32x4Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecI32x4; break; - case BinaryConsts::I64x2Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecI64x2; break; - case BinaryConsts::F32x4Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecF32x4; break; - case BinaryConsts::F64x2Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecF64x2; break; - case BinaryConsts::V128Not: curr = allocator.alloc<Unary>(); curr->op = NotVec128; break; - case BinaryConsts::I8x16Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecI8x16; break; - case BinaryConsts::I8x16AnyTrue: curr = allocator.alloc<Unary>(); curr->op = AnyTrueVecI8x16; break; - case BinaryConsts::I8x16AllTrue: curr = allocator.alloc<Unary>(); curr->op = AllTrueVecI8x16; break; - case BinaryConsts::I16x8Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecI16x8; break; - case BinaryConsts::I16x8AnyTrue: curr = allocator.alloc<Unary>(); curr->op = AnyTrueVecI16x8; break; - case BinaryConsts::I16x8AllTrue: curr = allocator.alloc<Unary>(); curr->op = AllTrueVecI16x8; break; - case BinaryConsts::I32x4Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecI32x4; break; - case BinaryConsts::I32x4AnyTrue: curr = allocator.alloc<Unary>(); curr->op = AnyTrueVecI32x4; break; - case BinaryConsts::I32x4AllTrue: curr = allocator.alloc<Unary>(); curr->op = AllTrueVecI32x4; break; - case BinaryConsts::I64x2Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecI64x2; break; - case BinaryConsts::I64x2AnyTrue: curr = allocator.alloc<Unary>(); curr->op = AnyTrueVecI64x2; break; - case BinaryConsts::I64x2AllTrue: curr = allocator.alloc<Unary>(); curr->op = AllTrueVecI64x2; break; - case BinaryConsts::F32x4Abs: curr = allocator.alloc<Unary>(); curr->op = AbsVecF32x4; break; - case BinaryConsts::F32x4Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecF32x4; break; - case BinaryConsts::F32x4Sqrt: curr = allocator.alloc<Unary>(); curr->op = SqrtVecF32x4; break; - case BinaryConsts::F64x2Abs: curr = allocator.alloc<Unary>(); curr->op = AbsVecF64x2; break; - case BinaryConsts::F64x2Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecF64x2; break; - case BinaryConsts::F64x2Sqrt: curr = allocator.alloc<Unary>(); curr->op = SqrtVecF64x2; break; - case BinaryConsts::I32x4TruncSatSF32x4: curr = allocator.alloc<Unary>(); curr->op = TruncSatSVecF32x4ToVecI32x4; break; - case BinaryConsts::I32x4TruncSatUF32x4: curr = allocator.alloc<Unary>(); curr->op = TruncSatUVecF32x4ToVecI32x4; break; - case BinaryConsts::I64x2TruncSatSF64x2: curr = allocator.alloc<Unary>(); curr->op = TruncSatSVecF64x2ToVecI64x2; break; - case BinaryConsts::I64x2TruncSatUF64x2: curr = allocator.alloc<Unary>(); curr->op = TruncSatUVecF64x2ToVecI64x2; break; - case BinaryConsts::F32x4ConvertSI32x4: curr = allocator.alloc<Unary>(); curr->op = ConvertSVecI32x4ToVecF32x4; break; - case BinaryConsts::F32x4ConvertUI32x4: curr = allocator.alloc<Unary>(); curr->op = ConvertUVecI32x4ToVecF32x4; break; - case BinaryConsts::F64x2ConvertSI64x2: curr = allocator.alloc<Unary>(); curr->op = ConvertSVecI64x2ToVecF64x2; break; - case BinaryConsts::F64x2ConvertUI64x2: curr = allocator.alloc<Unary>(); curr->op = ConvertUVecI64x2ToVecF64x2; break; - default: return false; + case BinaryConsts::I8x16Splat: + curr = allocator.alloc<Unary>(); + curr->op = SplatVecI8x16; + break; + case BinaryConsts::I16x8Splat: + curr = allocator.alloc<Unary>(); + curr->op = SplatVecI16x8; + break; + case BinaryConsts::I32x4Splat: + curr = allocator.alloc<Unary>(); + curr->op = SplatVecI32x4; + break; + case BinaryConsts::I64x2Splat: + curr = allocator.alloc<Unary>(); + curr->op = SplatVecI64x2; + break; + case BinaryConsts::F32x4Splat: + curr = allocator.alloc<Unary>(); + curr->op = SplatVecF32x4; + break; + case BinaryConsts::F64x2Splat: + curr = allocator.alloc<Unary>(); + curr->op = SplatVecF64x2; + break; + case BinaryConsts::V128Not: + curr = allocator.alloc<Unary>(); + curr->op = NotVec128; + break; + case BinaryConsts::I8x16Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegVecI8x16; + break; + case BinaryConsts::I8x16AnyTrue: + curr = allocator.alloc<Unary>(); + curr->op = AnyTrueVecI8x16; + break; + case BinaryConsts::I8x16AllTrue: + curr = allocator.alloc<Unary>(); + curr->op = AllTrueVecI8x16; + break; + case BinaryConsts::I16x8Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegVecI16x8; + break; + case BinaryConsts::I16x8AnyTrue: + curr = allocator.alloc<Unary>(); + curr->op = AnyTrueVecI16x8; + break; + case BinaryConsts::I16x8AllTrue: + curr = allocator.alloc<Unary>(); + curr->op = AllTrueVecI16x8; + break; + case BinaryConsts::I32x4Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegVecI32x4; + break; + case BinaryConsts::I32x4AnyTrue: + curr = allocator.alloc<Unary>(); + curr->op = AnyTrueVecI32x4; + break; + case BinaryConsts::I32x4AllTrue: + curr = allocator.alloc<Unary>(); + curr->op = AllTrueVecI32x4; + break; + case BinaryConsts::I64x2Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegVecI64x2; + break; + case BinaryConsts::I64x2AnyTrue: + curr = allocator.alloc<Unary>(); + curr->op = AnyTrueVecI64x2; + break; + case BinaryConsts::I64x2AllTrue: + curr = allocator.alloc<Unary>(); + curr->op = AllTrueVecI64x2; + break; + case BinaryConsts::F32x4Abs: + curr = allocator.alloc<Unary>(); + curr->op = AbsVecF32x4; + break; + case BinaryConsts::F32x4Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegVecF32x4; + break; + case BinaryConsts::F32x4Sqrt: + curr = allocator.alloc<Unary>(); + curr->op = SqrtVecF32x4; + break; + case BinaryConsts::F64x2Abs: + curr = allocator.alloc<Unary>(); + curr->op = AbsVecF64x2; + break; + case BinaryConsts::F64x2Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegVecF64x2; + break; + case BinaryConsts::F64x2Sqrt: + curr = allocator.alloc<Unary>(); + curr->op = SqrtVecF64x2; + break; + case BinaryConsts::I32x4TruncSatSF32x4: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatSVecF32x4ToVecI32x4; + break; + case BinaryConsts::I32x4TruncSatUF32x4: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatUVecF32x4ToVecI32x4; + break; + case BinaryConsts::I64x2TruncSatSF64x2: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatSVecF64x2ToVecI64x2; + break; + case BinaryConsts::I64x2TruncSatUF64x2: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatUVecF64x2ToVecI64x2; + break; + case BinaryConsts::F32x4ConvertSI32x4: + curr = allocator.alloc<Unary>(); + curr->op = ConvertSVecI32x4ToVecF32x4; + break; + case BinaryConsts::F32x4ConvertUI32x4: + curr = allocator.alloc<Unary>(); + curr->op = ConvertUVecI32x4ToVecF32x4; + break; + case BinaryConsts::F64x2ConvertSI64x2: + curr = allocator.alloc<Unary>(); + curr->op = ConvertSVecI64x2ToVecF64x2; + break; + case BinaryConsts::F64x2ConvertUI64x2: + curr = allocator.alloc<Unary>(); + curr->op = ConvertUVecI64x2ToVecF64x2; + break; + default: + return false; } curr->value = popNonVoidExpression(); curr->finalize(); @@ -2682,15 +3789,48 @@ bool WasmBinaryBuilder::maybeVisitSIMDStore(Expression*& out, uint32_t code) { bool WasmBinaryBuilder::maybeVisitSIMDExtract(Expression*& out, uint32_t code) { SIMDExtract* curr; switch (code) { - case BinaryConsts::I8x16ExtractLaneS: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneSVecI8x16; curr->index = getLaneIndex(16); break; - case BinaryConsts::I8x16ExtractLaneU: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneUVecI8x16; curr->index = getLaneIndex(16); break; - case BinaryConsts::I16x8ExtractLaneS: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneSVecI16x8; curr->index = getLaneIndex(8); break; - case BinaryConsts::I16x8ExtractLaneU: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneUVecI16x8; curr->index = getLaneIndex(8); break; - case BinaryConsts::I32x4ExtractLane: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneVecI32x4; curr->index = getLaneIndex(4); break; - case BinaryConsts::I64x2ExtractLane: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneVecI64x2; curr->index = getLaneIndex(2); break; - case BinaryConsts::F32x4ExtractLane: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneVecF32x4; curr->index = getLaneIndex(4); break; - case BinaryConsts::F64x2ExtractLane: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneVecF64x2; curr->index = getLaneIndex(2); break; - default: return false; + case BinaryConsts::I8x16ExtractLaneS: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneSVecI8x16; + curr->index = getLaneIndex(16); + break; + case BinaryConsts::I8x16ExtractLaneU: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneUVecI8x16; + curr->index = getLaneIndex(16); + break; + case BinaryConsts::I16x8ExtractLaneS: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneSVecI16x8; + curr->index = getLaneIndex(8); + break; + case BinaryConsts::I16x8ExtractLaneU: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneUVecI16x8; + curr->index = getLaneIndex(8); + break; + case BinaryConsts::I32x4ExtractLane: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneVecI32x4; + curr->index = getLaneIndex(4); + break; + case BinaryConsts::I64x2ExtractLane: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneVecI64x2; + curr->index = getLaneIndex(2); + break; + case BinaryConsts::F32x4ExtractLane: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneVecF32x4; + curr->index = getLaneIndex(4); + break; + case BinaryConsts::F64x2ExtractLane: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneVecF64x2; + curr->index = getLaneIndex(2); + break; + default: + return false; } curr->vec = popNonVoidExpression(); curr->finalize(); @@ -2701,13 +3841,38 @@ bool WasmBinaryBuilder::maybeVisitSIMDExtract(Expression*& out, uint32_t code) { bool WasmBinaryBuilder::maybeVisitSIMDReplace(Expression*& out, uint32_t code) { SIMDReplace* curr; switch (code) { - case BinaryConsts::I8x16ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecI8x16; curr->index = getLaneIndex(16); break; - case BinaryConsts::I16x8ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecI16x8; curr->index = getLaneIndex(8); break; - case BinaryConsts::I32x4ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecI32x4; curr->index = getLaneIndex(4); break; - case BinaryConsts::I64x2ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecI64x2; curr->index = getLaneIndex(2); break; - case BinaryConsts::F32x4ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecF32x4; curr->index = getLaneIndex(4); break; - case BinaryConsts::F64x2ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecF64x2; curr->index = getLaneIndex(2); break; - default: return false; + case BinaryConsts::I8x16ReplaceLane: + curr = allocator.alloc<SIMDReplace>(); + curr->op = ReplaceLaneVecI8x16; + curr->index = getLaneIndex(16); + break; + case BinaryConsts::I16x8ReplaceLane: + curr = allocator.alloc<SIMDReplace>(); + curr->op = ReplaceLaneVecI16x8; + curr->index = getLaneIndex(8); + break; + case BinaryConsts::I32x4ReplaceLane: + curr = allocator.alloc<SIMDReplace>(); + curr->op = ReplaceLaneVecI32x4; + curr->index = getLaneIndex(4); + break; + case BinaryConsts::I64x2ReplaceLane: + curr = allocator.alloc<SIMDReplace>(); + curr->op = ReplaceLaneVecI64x2; + curr->index = getLaneIndex(2); + break; + case BinaryConsts::F32x4ReplaceLane: + curr = allocator.alloc<SIMDReplace>(); + curr->op = ReplaceLaneVecF32x4; + curr->index = getLaneIndex(4); + break; + case BinaryConsts::F64x2ReplaceLane: + curr = allocator.alloc<SIMDReplace>(); + curr->op = ReplaceLaneVecF64x2; + curr->index = getLaneIndex(2); + break; + default: + return false; } curr->value = popNonVoidExpression(); curr->vec = popNonVoidExpression(); @@ -2731,7 +3896,8 @@ bool WasmBinaryBuilder::maybeVisitSIMDShuffle(Expression*& out, uint32_t code) { return true; } -bool WasmBinaryBuilder::maybeVisitSIMDBitselect(Expression*& out, uint32_t code) { +bool WasmBinaryBuilder::maybeVisitSIMDBitselect(Expression*& out, + uint32_t code) { if (code != BinaryConsts::V128Bitselect) { return false; } @@ -2747,19 +3913,56 @@ bool WasmBinaryBuilder::maybeVisitSIMDBitselect(Expression*& out, uint32_t code) bool WasmBinaryBuilder::maybeVisitSIMDShift(Expression*& out, uint32_t code) { SIMDShift* curr; switch (code) { - case BinaryConsts::I8x16Shl: curr = allocator.alloc<SIMDShift>(); curr->op = ShlVecI8x16; break; - case BinaryConsts::I8x16ShrS: curr = allocator.alloc<SIMDShift>(); curr->op = ShrSVecI8x16; break; - case BinaryConsts::I8x16ShrU: curr = allocator.alloc<SIMDShift>(); curr->op = ShrUVecI8x16; break; - case BinaryConsts::I16x8Shl: curr = allocator.alloc<SIMDShift>(); curr->op = ShlVecI16x8; break; - case BinaryConsts::I16x8ShrS: curr = allocator.alloc<SIMDShift>(); curr->op = ShrSVecI16x8; break; - case BinaryConsts::I16x8ShrU: curr = allocator.alloc<SIMDShift>(); curr->op = ShrUVecI16x8; break; - case BinaryConsts::I32x4Shl: curr = allocator.alloc<SIMDShift>(); curr->op = ShlVecI32x4; break; - case BinaryConsts::I32x4ShrS: curr = allocator.alloc<SIMDShift>(); curr->op = ShrSVecI32x4; break; - case BinaryConsts::I32x4ShrU: curr = allocator.alloc<SIMDShift>(); curr->op = ShrUVecI32x4; break; - case BinaryConsts::I64x2Shl: curr = allocator.alloc<SIMDShift>(); curr->op = ShlVecI64x2; break; - case BinaryConsts::I64x2ShrS: curr = allocator.alloc<SIMDShift>(); curr->op = ShrSVecI64x2; break; - case BinaryConsts::I64x2ShrU: curr = allocator.alloc<SIMDShift>(); curr->op = ShrUVecI64x2; break; - default: return false; + case BinaryConsts::I8x16Shl: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShlVecI8x16; + break; + case BinaryConsts::I8x16ShrS: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrSVecI8x16; + break; + case BinaryConsts::I8x16ShrU: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrUVecI8x16; + break; + case BinaryConsts::I16x8Shl: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShlVecI16x8; + break; + case BinaryConsts::I16x8ShrS: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrSVecI16x8; + break; + case BinaryConsts::I16x8ShrU: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrUVecI16x8; + break; + case BinaryConsts::I32x4Shl: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShlVecI32x4; + break; + case BinaryConsts::I32x4ShrS: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrSVecI32x4; + break; + case BinaryConsts::I32x4ShrU: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrUVecI32x4; + break; + case BinaryConsts::I64x2Shl: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShlVecI64x2; + break; + case BinaryConsts::I64x2ShrS: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrSVecI64x2; + break; + case BinaryConsts::I64x2ShrU: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrUVecI64x2; + break; + default: + return false; } curr->shift = popNonVoidExpression(); curr->vec = popNonVoidExpression(); @@ -2769,7 +3972,8 @@ bool WasmBinaryBuilder::maybeVisitSIMDShift(Expression*& out, uint32_t code) { } void WasmBinaryBuilder::visitSelect(Select* curr) { - if (debug) std::cerr << "zz node: Select" << std::endl; + if (debug) + std::cerr << "zz node: Select" << std::endl; curr->condition = popNonVoidExpression(); curr->ifFalse = popNonVoidExpression(); curr->ifTrue = popNonVoidExpression(); @@ -2777,7 +3981,8 @@ void WasmBinaryBuilder::visitSelect(Select* curr) { } void WasmBinaryBuilder::visitReturn(Return* curr) { - if (debug) std::cerr << "zz node: Return" << std::endl; + if (debug) + std::cerr << "zz node: Return" << std::endl; requireFunctionContext("return"); if (currFunction->result != none) { curr->value = popNonVoidExpression(); @@ -2800,26 +4005,32 @@ bool WasmBinaryBuilder::maybeVisitHost(Expression*& out, uint8_t code) { curr->operands[0] = popNonVoidExpression(); break; } - default: return false; + default: + return false; } - if (debug) std::cerr << "zz node: Host" << std::endl; + if (debug) + std::cerr << "zz node: Host" << std::endl; auto reserved = getU32LEB(); - if (reserved != 0) throwError("Invalid reserved field on grow_memory/current_memory"); + if (reserved != 0) + throwError("Invalid reserved field on grow_memory/current_memory"); curr->finalize(); out = curr; return true; } void WasmBinaryBuilder::visitNop(Nop* curr) { - if (debug) std::cerr << "zz node: Nop" << std::endl; + if (debug) + std::cerr << "zz node: Nop" << std::endl; } void WasmBinaryBuilder::visitUnreachable(Unreachable* curr) { - if (debug) std::cerr << "zz node: Unreachable" << std::endl; + if (debug) + std::cerr << "zz node: Unreachable" << std::endl; } void WasmBinaryBuilder::visitDrop(Drop* curr) { - if (debug) std::cerr << "zz node: Drop" << std::endl; + if (debug) + std::cerr << "zz node: Drop" << std::endl; curr->value = popNonVoidExpression(); curr->finalize(); } diff --git a/src/wasm/wasm-emscripten.cpp b/src/wasm/wasm-emscripten.cpp index a66c8d1ac..c1bb36338 100644 --- a/src/wasm/wasm-emscripten.cpp +++ b/src/wasm/wasm-emscripten.cpp @@ -20,25 +20,25 @@ #include "asm_v_wasm.h" #include "asmjs/shared-constants.h" +#include "ir/function-type-utils.h" +#include "ir/import-utils.h" +#include "ir/module-utils.h" #include "shared-constants.h" #include "wasm-builder.h" #include "wasm-traversal.h" #include "wasm.h" -#include "ir/function-type-utils.h" -#include "ir/import-utils.h" -#include "ir/module-utils.h" namespace wasm { cashew::IString EMSCRIPTEN_ASM_CONST("emscripten_asm_const"); cashew::IString EM_JS_PREFIX("__em_js__"); -static Name STACK_SAVE("stackSave"), - STACK_RESTORE("stackRestore"), - STACK_ALLOC("stackAlloc"), - STACK_INIT("stack$init"), - POST_INSTANTIATE("__post_instantiate"), - ASSIGN_GOT_ENTIRES("__assign_got_enties"); +static Name STACK_SAVE("stackSave"); +static Name STACK_RESTORE("stackRestore"); +static Name STACK_ALLOC("stackAlloc"); +static Name STACK_INIT("stack$init"); +static Name POST_INSTANTIATE("__post_instantiate"); +static Name ASSIGN_GOT_ENTIRES("__assign_got_enties"); void addExportedFunction(Module& wasm, Function* function) { wasm.addFunction(function); @@ -78,13 +78,12 @@ Global* EmscriptenGlueGenerator::getStackPointerGlobal() { Expression* EmscriptenGlueGenerator::generateLoadStackPointer() { if (!useStackPointerGlobal) { return builder.makeLoad( - /* bytes =*/ 4, - /* signed =*/ false, - /* offset =*/ stackPointerOffset, - /* align =*/ 4, - /* ptr =*/ builder.makeConst(Literal(0)), - /* type =*/ i32 - ); + /* bytes =*/4, + /* signed =*/false, + /* offset =*/stackPointerOffset, + /* align =*/4, + /* ptr =*/builder.makeConst(Literal(0)), + /* type =*/i32); } Global* stackPointer = getStackPointerGlobal(); if (!stackPointer) @@ -92,16 +91,16 @@ Expression* EmscriptenGlueGenerator::generateLoadStackPointer() { return builder.makeGetGlobal(stackPointer->name, i32); } -Expression* EmscriptenGlueGenerator::generateStoreStackPointer(Expression* value) { +Expression* +EmscriptenGlueGenerator::generateStoreStackPointer(Expression* value) { if (!useStackPointerGlobal) { return builder.makeStore( - /* bytes =*/ 4, - /* offset =*/ stackPointerOffset, - /* align =*/ 4, - /* ptr =*/ builder.makeConst(Literal(0)), - /* value =*/ value, - /* type =*/ i32 - ); + /* bytes =*/4, + /* offset =*/stackPointerOffset, + /* align =*/4, + /* ptr =*/builder.makeConst(Literal(0)), + /* value =*/value, + /* type =*/i32); } Global* stackPointer = getStackPointerGlobal(); if (!stackPointer) @@ -110,10 +109,9 @@ Expression* EmscriptenGlueGenerator::generateStoreStackPointer(Expression* value } void EmscriptenGlueGenerator::generateStackSaveFunction() { - std::vector<NameType> params { }; - Function* function = builder.makeFunction( - STACK_SAVE, std::move(params), i32, {} - ); + std::vector<NameType> params{}; + Function* function = + builder.makeFunction(STACK_SAVE, std::move(params), i32, {}); function->body = generateLoadStackPointer(); @@ -121,10 +119,9 @@ void EmscriptenGlueGenerator::generateStackSaveFunction() { } void EmscriptenGlueGenerator::generateStackAllocFunction() { - std::vector<NameType> params { { "0", i32 } }; - Function* function = builder.makeFunction( - STACK_ALLOC, std::move(params), i32, { { "1", i32 } } - ); + std::vector<NameType> params{{"0", i32}}; + Function* function = + builder.makeFunction(STACK_ALLOC, std::move(params), i32, {{"1", i32}}); Expression* loadStack = generateLoadStackPointer(); GetLocal* getSizeArg = builder.makeGetLocal(0, i32); Binary* sub = builder.makeBinary(SubInt32, loadStack, getSizeArg); @@ -146,10 +143,9 @@ void EmscriptenGlueGenerator::generateStackAllocFunction() { } void EmscriptenGlueGenerator::generateStackRestoreFunction() { - std::vector<NameType> params { { "0", i32 } }; - Function* function = builder.makeFunction( - STACK_RESTORE, std::move(params), none, {} - ); + std::vector<NameType> params{{"0", i32}}; + Function* function = + builder.makeFunction(STACK_RESTORE, std::move(params), none, {}); GetLocal* getArg = builder.makeGetLocal(0, i32); Expression* store = generateStoreStackPointer(getArg); @@ -164,7 +160,8 @@ void EmscriptenGlueGenerator::generateRuntimeFunctions() { generateStackRestoreFunction(); } -static Function* ensureFunctionImport(Module* module, Name name, std::string sig) { +static Function* +ensureFunctionImport(Module* module, Name name, std::string sig) { // Then see if its already imported ImportInfo info(*module); if (Function* f = info.getImportedFunction(ENV, name)) { @@ -246,7 +243,9 @@ Function* EmscriptenGlueGenerator::generateAssignGOTEntriesFunction() { } } - Name getter((std::string("fp$") + g->base.c_str() + std::string("$") + getSig(f)).c_str()); + Name getter( + (std::string("fp$") + g->base.c_str() + std::string("$") + getSig(f)) + .c_str()); ensureFunctionImport(&wasm, getter, "i"); Expression* call = builder.makeCall(getter, {}, i32); SetGlobal* set_global = builder.makeSetGlobal(g->name, call); @@ -299,15 +298,11 @@ void EmscriptenGlueGenerator::generatePostInstantiateFunction() { Function* EmscriptenGlueGenerator::generateMemoryGrowthFunction() { Name name(GROW_WASM_MEMORY); - std::vector<NameType> params { { NEW_SIZE, i32 } }; - Function* growFunction = builder.makeFunction( - name, std::move(params), i32, {} - ); - growFunction->body = builder.makeHost( - GrowMemory, - Name(), - { builder.makeGetLocal(0, i32) } - ); + std::vector<NameType> params{{NEW_SIZE, i32}}; + Function* growFunction = + builder.makeFunction(name, std::move(params), i32, {}); + growFunction->body = + builder.makeHost(GrowMemory, Name(), {builder.makeGetLocal(0, i32)}); addExportedFunction(wasm, growFunction); @@ -327,7 +322,8 @@ inline void exportFunction(Module& wasm, Name name, bool must_export) { assert(!must_export); return; } - if (wasm.getExportOrNull(name)) return; // Already exported + if (wasm.getExportOrNull(name)) + return; // Already exported auto exp = new Export; exp->name = exp->value = name; exp->kind = ExternalKind::Function; @@ -354,8 +350,10 @@ void EmscriptenGlueGenerator::generateDynCallThunks() { std::vector<NameType> params; params.emplace_back("fptr", i32); // function pointer param int p = 0; - for (const auto& ty : funcType->params) params.emplace_back(std::to_string(p++), ty); - Function* f = builder.makeFunction(name, std::move(params), funcType->result, {}); + for (const auto& ty : funcType->params) + params.emplace_back(std::to_string(p++), ty); + Function* f = + builder.makeFunction(name, std::move(params), funcType->result, {}); Expression* fptr = builder.makeGetLocal(0, i32); std::vector<Expression*> args; for (unsigned i = 0; i < funcType->params.size(); ++i) { @@ -375,7 +373,8 @@ struct RemoveStackPointer : public PostWalker<RemoveStackPointer> { void visitGetGlobal(GetGlobal* curr) { if (getModule()->getGlobalOrNull(curr->name) == stackPointer) { needStackSave = true; - if (!builder) builder = make_unique<Builder>(*getModule()); + if (!builder) + builder = make_unique<Builder>(*getModule()); replaceCurrent(builder->makeCall(STACK_SAVE, {}, i32)); } } @@ -383,7 +382,8 @@ struct RemoveStackPointer : public PostWalker<RemoveStackPointer> { void visitSetGlobal(SetGlobal* curr) { if (getModule()->getGlobalOrNull(curr->name) == stackPointer) { needStackRestore = true; - if (!builder) builder = make_unique<Builder>(*getModule()); + if (!builder) + builder = make_unique<Builder>(*getModule()); replaceCurrent(builder->makeCall(STACK_RESTORE, {curr->value}, none)); } } @@ -431,7 +431,7 @@ std::vector<Address> getSegmentOffsets(Module& wasm) { return segmentOffsets; } -std::string escape(const char *input) { +std::string escape(const char* input) { std::string code = input; // replace newlines quotes with escaped newlines size_t curr = 0; @@ -442,11 +442,18 @@ std::string escape(const char *input) { // replace double quotes with escaped single quotes curr = 0; while ((curr = code.find('"', curr)) != std::string::npos) { - if (curr == 0 || code[curr-1] != '\\') { - code = code.replace(curr, 1, "\\" "\""); + if (curr == 0 || code[curr - 1] != '\\') { + code = code.replace(curr, + 1, + "\\" + "\""); curr += 2; // skip this one - } else { // already escaped, escape the slash as well - code = code.replace(curr, 1, "\\" "\\" "\""); + } else { // already escaped, escape the slash as well + code = code.replace(curr, + 1, + "\\" + "\\" + "\""); curr += 3; // skip this one } } @@ -486,11 +493,11 @@ struct AsmConstWalker : public LinearExecutionWalker<AsmConstWalker> { std::map<std::string, std::set<std::string>> sigsForCode; std::map<std::string, Address> ids; std::set<std::string> allSigs; - std::map<Index, SetLocal*> sets; // last sets in the current basic block, per index + // last sets in the current basic block, per index + std::map<Index, SetLocal*> sets; AsmConstWalker(Module& _wasm) - : wasm(_wasm), - segmentOffsets(getSegmentOffsets(wasm)) { } + : wasm(_wasm), segmentOffsets(getSegmentOffsets(wasm)) {} void noteNonLinear(Expression* curr); @@ -516,9 +523,7 @@ void AsmConstWalker::noteNonLinear(Expression* curr) { sets.clear(); } -void AsmConstWalker::visitSetLocal(SetLocal* curr) { - sets[curr->index] = curr; -} +void AsmConstWalker::visitSetLocal(SetLocal* curr) { sets[curr->index] = curr; } void AsmConstWalker::visitCall(Call* curr) { auto* import = wasm.getFunction(curr->target); @@ -547,7 +552,7 @@ void AsmConstWalker::visitCall(Call* curr) { } else { if (!value) { Fatal() << "Unexpected arg0 type (" << getExpressionName(arg) - << ") in call to to: " << import->base; + << ") in call to to: " << import->base; } } } @@ -608,7 +613,8 @@ Literal AsmConstWalker::idLiteralForCode(std::string code) { std::string AsmConstWalker::asmConstSig(std::string baseSig) { std::string sig = ""; for (size_t i = 0; i < baseSig.size(); ++i) { - // Omit the signature of the "code" parameter, taken as a string, as the first argument + // Omit the signature of the "code" parameter, taken as a string, as the + // first argument if (i != 1) { sig += baseSig[i]; } @@ -663,8 +669,7 @@ struct EmJsWalker : public PostWalker<EmJsWalker> { std::map<std::string, std::string> codeByName; EmJsWalker(Module& _wasm) - : wasm(_wasm), - segmentOffsets(getSegmentOffsets(wasm)) { } + : wasm(_wasm), segmentOffsets(getSegmentOffsets(wasm)) {} void visitFunction(Function* curr) { if (curr->imported()) { @@ -750,7 +755,8 @@ EmJsWalker fixEmJsFuncsAndReturnWalker(Module& wasm) { // emscripten_longjmp here. // 2. Converts invoke wrapper names. // Refer to the comments in fixEmExceptionInvoke below. -struct FixInvokeFunctionNamesWalker : public PostWalker<FixInvokeFunctionNamesWalker> { +struct FixInvokeFunctionNamesWalker + : public PostWalker<FixInvokeFunctionNamesWalker> { Module& wasm; std::map<Name, Name> importRenames; std::vector<Name> toRemove; @@ -789,7 +795,7 @@ struct FixInvokeFunctionNamesWalker : public PostWalker<FixInvokeFunctionNamesWa return Name("invoke_" + sigWoOrigFunc); } - static Name fixEmEHSjLjNames(const Name &name, const std::string& sig) { + static Name fixEmEHSjLjNames(const Name& name, const std::string& sig) { if (name == "emscripten_longjmp_jmpbuf") return "emscripten_longjmp"; return fixEmExceptionInvoke(name, sig); @@ -830,20 +836,21 @@ void EmscriptenGlueGenerator::fixInvokeFunctionNames() { walker.walkModule(&wasm); } -template<class C> -void printSet(std::ostream& o, C& c) { +template<class C> void printSet(std::ostream& o, C& c) { o << "["; bool first = true; for (auto& item : c) { - if (first) first = false; - else o << ","; + if (first) + first = false; + else + o << ","; o << '"' << item << '"'; } o << "]"; } std::string EmscriptenGlueGenerator::generateEmscriptenMetadata( - Address staticBump, std::vector<Name> const& initializerFunctions) { + Address staticBump, std::vector<Name> const& initializerFunctions) { bool commaFirst; auto nextElement = [&commaFirst]() { if (commaFirst) { @@ -964,7 +971,8 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata( assert(g->type == i32); Const* init = g->init->cast<Const>(); uint32_t addr = init->value.geti32(); - meta << nextElement() << '"' << ex->name.str << "\" : \"" << addr << '"'; + meta << nextElement() << '"' << ex->name.str << "\" : \"" << addr + << '"'; } } meta << "\n },\n"; @@ -993,7 +1001,8 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata( return meta.str(); } -void EmscriptenGlueGenerator::separateDataSegments(Output* outfile, Address base) { +void EmscriptenGlueGenerator::separateDataSegments(Output* outfile, + Address base) { size_t lastEnd = 0; for (Memory::Segment& seg : wasm.memory.segments) { assert(!seg.isPassive && "separating passive segments not implemented"); diff --git a/src/wasm/wasm-interpreter.cpp b/src/wasm/wasm-interpreter.cpp index e7df785ac..ad4b565fe 100644 --- a/src/wasm/wasm-interpreter.cpp +++ b/src/wasm/wasm-interpreter.cpp @@ -5,9 +5,7 @@ namespace wasm { #ifdef WASM_INTERPRETER_DEBUG int Indenter::indentLevel = 0; -Indenter::Indenter(const char* entry) : entryName(entry) { - ++indentLevel; -} +Indenter::Indenter(const char* entry) : entryName(entry) { ++indentLevel; } Indenter::~Indenter() { print(); std::cout << "exit " << entryName << '\n'; diff --git a/src/wasm/wasm-io.cpp b/src/wasm/wasm-io.cpp index 057798d2e..1bdb76d5f 100644 --- a/src/wasm/wasm-io.cpp +++ b/src/wasm/wasm-io.cpp @@ -25,8 +25,8 @@ // #include "wasm-io.h" -#include "wasm-s-parser.h" #include "wasm-binary.h" +#include "wasm-s-parser.h" namespace wasm { @@ -34,17 +34,20 @@ static void readTextData(std::string& input, Module& wasm) { SExpressionParser parser(const_cast<char*>(input.c_str())); Element& root = *parser.root; SExpressionWasmBuilder builder(wasm, *root[0]); - } void ModuleReader::readText(std::string filename, Module& wasm) { - if (debug) std::cerr << "reading text from " << filename << "\n"; - auto input(read_file<std::string>(filename, Flags::Text, debug ? Flags::Debug : Flags::Release)); + if (debug) + std::cerr << "reading text from " << filename << "\n"; + auto input(read_file<std::string>( + filename, Flags::Text, debug ? Flags::Debug : Flags::Release)); readTextData(input, wasm); } -static void readBinaryData(std::vector<char>& input, Module& wasm, - std::string sourceMapFilename, bool debug) { +static void readBinaryData(std::vector<char>& input, + Module& wasm, + std::string sourceMapFilename, + bool debug) { std::unique_ptr<std::ifstream> sourceMapStream; WasmBinaryBuilder parser(wasm, input, debug); if (sourceMapFilename.size()) { @@ -58,10 +61,13 @@ static void readBinaryData(std::vector<char>& input, Module& wasm, } } -void ModuleReader::readBinary(std::string filename, Module& wasm, +void ModuleReader::readBinary(std::string filename, + Module& wasm, std::string sourceMapFilename) { - if (debug) std::cerr << "reading binary from " << filename << "\n"; - auto input(read_file<std::vector<char>>(filename, Flags::Binary, debug ? Flags::Debug : Flags::Release)); + if (debug) + std::cerr << "reading binary from " << filename << "\n"; + auto input(read_file<std::vector<char>>( + filename, Flags::Binary, debug ? Flags::Debug : Flags::Release)); readBinaryData(input, wasm, sourceMapFilename, debug); } @@ -69,13 +75,15 @@ bool ModuleReader::isBinaryFile(std::string filename) { std::ifstream infile; std::ios_base::openmode flags = std::ifstream::in | std::ifstream::binary; infile.open(filename, flags); - char buffer[4] = { 1, 2, 3, 4 }; + char buffer[4] = {1, 2, 3, 4}; infile.read(buffer, 4); infile.close(); - return buffer[0] == '\0' && buffer[1] == 'a' && buffer[2] == 's' && buffer[3] == 'm'; + return buffer[0] == '\0' && buffer[1] == 'a' && buffer[2] == 's' && + buffer[3] == 'm'; } -void ModuleReader::read(std::string filename, Module& wasm, +void ModuleReader::read(std::string filename, + Module& wasm, std::string sourceMapFilename) { // empty filename means read from stdin if (!filename.size()) { @@ -87,7 +95,8 @@ void ModuleReader::read(std::string filename, Module& wasm, } else { // default to text if (sourceMapFilename.size()) { - std::cerr << "Binaryen ModuleReader::read() - source map filename provided, but file appears to not be binary\n"; + std::cerr << "Binaryen ModuleReader::read() - source map filename " + "provided, but file appears to not be binary\n"; } readText(filename, wasm); } @@ -109,13 +118,13 @@ void ModuleReader::readStdin(Module& wasm, std::string sourceMapFilename) { } } - void ModuleWriter::writeText(Module& wasm, Output& output) { WasmPrinter::printModule(&wasm, output.getStream()); } void ModuleWriter::writeText(Module& wasm, std::string filename) { - if (debug) std::cerr << "writing text to " << filename << "\n"; + if (debug) + std::cerr << "writing text to " << filename << "\n"; Output output(filename, Flags::Text, debug ? Flags::Debug : Flags::Release); writeText(wasm, output); } @@ -131,7 +140,8 @@ void ModuleWriter::writeBinary(Module& wasm, Output& output) { sourceMapStream->open(sourceMapFilename); writer.setSourceMap(sourceMapStream.get(), sourceMapUrl); } - if (symbolMap.size() > 0) writer.setSymbolMap(symbolMap); + if (symbolMap.size() > 0) + writer.setSymbolMap(symbolMap); writer.write(); buffer.writeTo(output); if (sourceMapStream) { @@ -140,7 +150,8 @@ void ModuleWriter::writeBinary(Module& wasm, Output& output) { } void ModuleWriter::writeBinary(Module& wasm, std::string filename) { - if (debug) std::cerr << "writing binary to " << filename << "\n"; + if (debug) + std::cerr << "writing binary to " << filename << "\n"; Output output(filename, Flags::Binary, debug ? Flags::Debug : Flags::Release); writeBinary(wasm, output); } @@ -161,4 +172,4 @@ void ModuleWriter::write(Module& wasm, std::string filename) { } } -} +} // namespace wasm diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 3ba8e0622..bb498329f 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -16,8 +16,8 @@ #include "wasm-s-parser.h" -#include <cmath> #include <cctype> +#include <cmath> #include <limits> #include "asm_v_wasm.h" @@ -28,19 +28,24 @@ #include "wasm-binary.h" #include "wasm-builder.h" -#define abort_on(str) { throw ParseException(std::string("abort_on ") + str); } -#define element_assert(condition) assert((condition) ? true : (std::cerr << "on: " << *this << '\n' && 0)); +#define abort_on(str) \ + { throw ParseException(std::string("abort_on ") + str); } +#define element_assert(condition) \ + assert((condition) ? true : (std::cerr << "on: " << *this << '\n' && 0)); using cashew::IString; namespace { int unhex(char c) { - if (c >= '0' && c <= '9') return c - '0'; - if (c >= 'a' && c <= 'f') return c - 'a' + 10; - if (c >= 'A' && c <= 'F') return c - 'A' + 10; + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; throw wasm::ParseException("invalid hexadecimal"); } -} +} // namespace namespace wasm { @@ -53,23 +58,28 @@ static Address getCheckedAddress(const Element* s, const char* errorText) { } Element::List& Element::list() { - if (!isList()) throw ParseException("expected list", line, col); + if (!isList()) + throw ParseException("expected list", line, col); return list_; } Element* Element::operator[](unsigned i) { - if (!isList()) throw ParseException("expected list", line, col); - if (i >= list().size()) throw ParseException("expected more elements in list", line, col); + if (!isList()) + throw ParseException("expected list", line, col); + if (i >= list().size()) + throw ParseException("expected more elements in list", line, col); return list()[i]; } IString Element::str() const { - if (!isStr()) throw ParseException("expected string", line, col); + if (!isStr()) + throw ParseException("expected string", line, col); return str_; } const char* Element::c_str() const { - if (!isStr()) throw ParseException("expected string", line, col); + if (!isStr()) + throw ParseException("expected string", line, col); return str_.str; } @@ -81,7 +91,8 @@ Element* Element::setString(IString str__, bool dollared__, bool quoted__) { return this; } -Element* Element::setMetadata(size_t line_, size_t col_, SourceLocation* startLoc_) { +Element* +Element::setMetadata(size_t line_, size_t col_, SourceLocation* startLoc_) { line = line_; col = col_; startLoc = startLoc_; @@ -91,7 +102,8 @@ Element* Element::setMetadata(size_t line_, size_t col_, SourceLocation* startLo std::ostream& operator<<(std::ostream& o, Element& e) { if (e.isList_) { o << '('; - for (auto item : e.list_) o << ' ' << *item; + for (auto item : e.list_) + o << ' ' << *item; o << " )"; } else { o << e.str_.str; @@ -103,7 +115,6 @@ void Element::dump() { std::cout << "dumping " << this << " : " << *this << ".\n"; } - SExpressionParser::SExpressionParser(char* input) : input(input) { root = nullptr; line = 1; @@ -114,16 +125,18 @@ SExpressionParser::SExpressionParser(char* input) : input(input) { } Element* SExpressionParser::parse() { - std::vector<Element *> stack; + std::vector<Element*> stack; std::vector<SourceLocation*> stackLocs; - Element *curr = allocator.alloc<Element>(); + Element* curr = allocator.alloc<Element>(); while (1) { skipWhitespace(); - if (input[0] == 0) break; + if (input[0] == 0) + break; if (input[0] == '(') { input++; stack.push_back(curr); - curr = allocator.alloc<Element>()->setMetadata(line, input - lineStart - 1, loc); + curr = allocator.alloc<Element>()->setMetadata( + line, input - lineStart - 1, loc); stackLocs.push_back(loc); assert(stack.size() == stackLocs.size()); } else if (input[0] == ')') { @@ -143,31 +156,38 @@ Element* SExpressionParser::parse() { curr->list().push_back(parseString()); } } - if (stack.size() != 0) throw ParseException("stack is not empty", curr->line, curr->col); + if (stack.size() != 0) + throw ParseException("stack is not empty", curr->line, curr->col); return curr; } void SExpressionParser::parseDebugLocation() { // Extracting debug location (if valid) char* debugLoc = input + 3; // skipping ";;@" - while (debugLoc[0] && debugLoc[0] == ' ') debugLoc++; + while (debugLoc[0] && debugLoc[0] == ' ') + debugLoc++; char* debugLocEnd = debugLoc; - while (debugLocEnd[0] && debugLocEnd[0] != '\n') debugLocEnd++; + while (debugLocEnd[0] && debugLocEnd[0] != '\n') + debugLocEnd++; char* pos = debugLoc; - while (pos < debugLocEnd && pos[0] != ':') pos++; + while (pos < debugLocEnd && pos[0] != ':') + pos++; if (pos >= debugLocEnd) { return; // no line number } std::string name(debugLoc, pos); char* lineStart = ++pos; - while (pos < debugLocEnd && pos[0] != ':') pos++; + while (pos < debugLocEnd && pos[0] != ':') + pos++; std::string lineStr(lineStart, pos); if (pos >= debugLocEnd) { return; // no column number } std::string colStr(++pos, debugLocEnd); - void* buf = allocator.allocSpace(sizeof(SourceLocation), alignof(SourceLocation)); - loc = new (buf) SourceLocation(IString(name.c_str(), false), atoi(lineStr.c_str()), atoi(colStr.c_str())); + void* buf = + allocator.allocSpace(sizeof(SourceLocation), alignof(SourceLocation)); + loc = new (buf) SourceLocation( + IString(name.c_str(), false), atoi(lineStr.c_str()), atoi(colStr.c_str())); } void SExpressionParser::skipWhitespace() { @@ -183,16 +203,19 @@ void SExpressionParser::skipWhitespace() { if (input[2] == '@') { parseDebugLocation(); } - while (input[0] && input[0] != '\n') input++; + while (input[0] && input[0] != '\n') + input++; line++; - if (!input[0]) return; + if (!input[0]) + return; lineStart = ++input; } else if (input[0] == '(' && input[1] == ';') { // Skip nested block comments. input += 2; int depth = 1; while (1) { - if (!input[0]) return; + if (!input[0]) + return; if (input[0] == '(' && input[1] == ';') { input += 2; depth++; @@ -222,17 +245,22 @@ Element* SExpressionParser::parseString() { input++; dollared = true; } - char *start = input; + char* start = input; if (input[0] == '"') { - // parse escaping \", but leave code escaped - we'll handle escaping in memory segments specifically + // parse escaping \", but leave code escaped - we'll handle escaping in + // memory segments specifically input++; std::string str; while (1) { - if (input[0] == 0) throw ParseException("unterminated string", line, start - lineStart); - if (input[0] == '"') break; + if (input[0] == 0) + throw ParseException("unterminated string", line, start - lineStart); + if (input[0] == '"') + break; if (input[0] == '\\') { str += input[0]; - if (input[1] == 0) throw ParseException("unterminated string escape", line, start - lineStart); + if (input[1] == 0) + throw ParseException( + "unterminated string escape", line, start - lineStart); str += input[1]; input += 2; continue; @@ -241,21 +269,34 @@ Element* SExpressionParser::parseString() { input++; } input++; - return allocator.alloc<Element>()->setString(IString(str.c_str(), false), dollared, true)->setMetadata(line, start - lineStart, loc); + return allocator.alloc<Element>() + ->setString(IString(str.c_str(), false), dollared, true) + ->setMetadata(line, start - lineStart, loc); } - while (input[0] && !isspace(input[0]) && input[0] != ')' && input[0] != '(' && input[0] != ';') input++; - if (start == input) throw ParseException("expected string", line, input - lineStart); + while (input[0] && !isspace(input[0]) && input[0] != ')' && input[0] != '(' && + input[0] != ';') + input++; + if (start == input) + throw ParseException("expected string", line, input - lineStart); char temp = input[0]; input[0] = 0; - auto ret = allocator.alloc<Element>()->setString(IString(start, false), dollared, false)->setMetadata(line, start - lineStart, loc); + auto ret = allocator.alloc<Element>() + ->setString(IString(start, false), dollared, false) + ->setMetadata(line, start - lineStart, loc); input[0] = temp; return ret; } -SExpressionWasmBuilder::SExpressionWasmBuilder(Module& wasm, Element& module, Name* moduleName) : wasm(wasm), allocator(wasm.allocator) { - if (module.size() == 0) throw ParseException("empty toplevel, expected module"); - if (module[0]->str() != MODULE) throw ParseException("toplevel does not start with module"); - if (module.size() == 1) return; +SExpressionWasmBuilder::SExpressionWasmBuilder(Module& wasm, + Element& module, + Name* moduleName) + : wasm(wasm), allocator(wasm.allocator) { + if (module.size() == 0) + throw ParseException("empty toplevel, expected module"); + if (module[0]->str() != MODULE) + throw ParseException("toplevel does not start with module"); + if (module.size() == 1) + return; Index i = 1; if (module[i]->dollared()) { if (moduleName) { @@ -286,7 +327,9 @@ SExpressionWasmBuilder::SExpressionWasmBuilder(Module& wasm, Element& module, Na implementedFunctions++; } } - functionCounter -= implementedFunctions; // we go through the functions again, now parsing them, and the counter begins from where imports ended + // we go through the functions again, now parsing them, and the counter begins + // from where imports ended + functionCounter -= implementedFunctions; for (unsigned j = i; j < module.size(); j++) { parseModuleElement(*module[j]); } @@ -295,36 +338,55 @@ SExpressionWasmBuilder::SExpressionWasmBuilder(Module& wasm, Element& module, Na bool SExpressionWasmBuilder::isImport(Element& curr) { for (Index i = 0; i < curr.size(); i++) { auto& x = *curr[i]; - if (x.isList() && x.size() > 0 && x[0]->isStr() && x[0]->str() == IMPORT) return true; + if (x.isList() && x.size() > 0 && x[0]->isStr() && x[0]->str() == IMPORT) + return true; } return false; } void SExpressionWasmBuilder::preParseImports(Element& curr) { IString id = curr[0]->str(); - if (id == IMPORT) parseImport(curr); + if (id == IMPORT) + parseImport(curr); if (isImport(curr)) { - if (id == FUNC) parseFunction(curr, true /* preParseImport */); - else if (id == GLOBAL) parseGlobal(curr, true /* preParseImport */); - else if (id == TABLE) parseTable(curr, true /* preParseImport */); - else if (id == MEMORY) parseMemory(curr, true /* preParseImport */); - else throw ParseException("fancy import we don't support yet", curr.line, curr.col); + if (id == FUNC) + parseFunction(curr, true /* preParseImport */); + else if (id == GLOBAL) + parseGlobal(curr, true /* preParseImport */); + else if (id == TABLE) + parseTable(curr, true /* preParseImport */); + else if (id == MEMORY) + parseMemory(curr, true /* preParseImport */); + else + throw ParseException( + "fancy import we don't support yet", curr.line, curr.col); } } void SExpressionWasmBuilder::parseModuleElement(Element& curr) { - if (isImport(curr)) return; // already done + if (isImport(curr)) + return; // already done IString id = curr[0]->str(); - if (id == START) return parseStart(curr); - if (id == FUNC) return parseFunction(curr); - if (id == MEMORY) return parseMemory(curr); - if (id == DATA) return parseData(curr); - if (id == EXPORT) return parseExport(curr); - if (id == IMPORT) return; // already done - if (id == GLOBAL) return parseGlobal(curr); - if (id == TABLE) return parseTable(curr); - if (id == ELEM) return parseElem(curr); - if (id == TYPE) return; // already done + if (id == START) + return parseStart(curr); + if (id == FUNC) + return parseFunction(curr); + if (id == MEMORY) + return parseMemory(curr); + if (id == DATA) + return parseData(curr); + if (id == EXPORT) + return parseExport(curr); + if (id == IMPORT) + return; // already done + if (id == GLOBAL) + return parseGlobal(curr); + if (id == TABLE) + return parseTable(curr); + if (id == ELEM) + return parseElem(curr); + if (id == TYPE) + return; // already done std::cerr << "bad module element " << id.str << '\n'; throw ParseException("unknown module element", curr.line, curr.col); } @@ -335,7 +397,8 @@ Name SExpressionWasmBuilder::getFunctionName(Element& s) { } else { // index size_t offset = atoi(s.str().c_str()); - if (offset >= functionNames.size()) throw ParseException("unknown function in getFunctionName"); + if (offset >= functionNames.size()) + throw ParseException("unknown function in getFunctionName"); return functionNames[offset]; } } @@ -346,7 +409,8 @@ Name SExpressionWasmBuilder::getFunctionTypeName(Element& s) { } else { // index size_t offset = atoi(s.str().c_str()); - if (offset >= functionTypeNames.size()) throw ParseException("unknown function type in getFunctionTypeName"); + if (offset >= functionTypeNames.size()) + throw ParseException("unknown function type in getFunctionTypeName"); return functionTypeNames[offset]; } } @@ -357,16 +421,18 @@ Name SExpressionWasmBuilder::getGlobalName(Element& s) { } else { // index size_t offset = atoi(s.str().c_str()); - if (offset >= globalNames.size()) throw ParseException("unknown global in getGlobalName"); + if (offset >= globalNames.size()) + throw ParseException("unknown global in getGlobalName"); return globalNames[offset]; } } - void SExpressionWasmBuilder::preParseFunctionType(Element& s) { IString id = s[0]->str(); - if (id == TYPE) return parseType(s); - if (id != FUNC) return; + if (id == TYPE) + return parseType(s); + if (id != FUNC) + return; size_t i = 1; Name name, exportName; i = parseFunctionNames(s, name, exportName); @@ -379,15 +445,17 @@ void SExpressionWasmBuilder::preParseFunctionType(Element& s) { FunctionType* type = nullptr; functionTypes[name] = none; std::vector<Type> params; - for (;i < s.size(); i++) { + for (; i < s.size(); i++) { Element& curr = *s[i]; IString id = curr[0]->str(); if (id == RESULT) { - if (curr.size() > 2) throw ParseException("invalid result arity", curr.line, curr.col); + if (curr.size() > 2) + throw ParseException("invalid result arity", curr.line, curr.col); functionTypes[name] = stringToType(curr[1]->str()); } else if (id == TYPE) { Name typeName = getFunctionTypeName(*curr[1]); - if (!wasm.getFunctionTypeOrNull(typeName)) throw ParseException("unknown function type", curr.line, curr.col); + if (!wasm.getFunctionTypeOrNull(typeName)) + throw ParseException("unknown function type", curr.line, curr.col); type = wasm.getFunctionType(typeName); functionTypes[name] = type->result; } else if (id == PARAM && curr.size() > 1) { @@ -403,8 +471,8 @@ void SExpressionWasmBuilder::preParseFunctionType(Element& s) { } } if (!type) { - // if no function type provided, generate one, but reuse a previous one with the - // right structure if there is one. + // if no function type provided, generate one, but reuse a previous one with + // the right structure if there is one. // see https://github.com/WebAssembly/spec/pull/301 bool need = true; std::unique_ptr<FunctionType> functionType = make_unique<FunctionType>(); @@ -419,13 +487,16 @@ void SExpressionWasmBuilder::preParseFunctionType(Element& s) { if (need) { functionType->name = Name::fromInt(wasm.functionTypes.size()); functionTypeNames.push_back(functionType->name); - if (wasm.getFunctionTypeOrNull(functionType->name)) throw ParseException("duplicate function type", s.line, s.col); + if (wasm.getFunctionTypeOrNull(functionType->name)) + throw ParseException("duplicate function type", s.line, s.col); wasm.addFunctionType(std::move(functionType)); } } } -size_t SExpressionWasmBuilder::parseFunctionNames(Element& s, Name& name, Name& exportName) { +size_t SExpressionWasmBuilder::parseFunctionNames(Element& s, + Name& name, + Name& exportName) { size_t i = 1; while (i < s.size() && i < 3 && s[i]->isStr()) { if (s[i]->quoted()) { @@ -476,27 +547,26 @@ void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) { ex->name = exportName; ex->value = name; ex->kind = ExternalKind::Function; - if (wasm.getExportOrNull(ex->name)) throw ParseException("duplicate export", s.line, s.col); + if (wasm.getExportOrNull(ex->name)) + throw ParseException("duplicate export", s.line, s.col); wasm.addExport(ex.release()); } Expression* body = nullptr; localIndex = 0; otherIndex = 0; brokeToAutoBlock = false; - std::vector<NameType> typeParams; // we may have both params and a type. store the type info here + // we may have both params and a type. store the type info here + std::vector<NameType> typeParams; std::vector<NameType> params; std::vector<NameType> vars; Type result = none; Name type; - Block* autoBlock = nullptr; // we may need to add a block for the very top level + // we may need to add a block for the very top level + Block* autoBlock = nullptr; Name importModule, importBase; auto makeFunction = [&]() { currFunction = std::unique_ptr<Function>(Builder(wasm).makeFunction( - name, - std::move(params), - result, - std::move(vars) - )); + name, std::move(params), result, std::move(vars))); }; auto ensureAutoBlock = [&]() { if (!autoBlock) { @@ -505,7 +575,7 @@ void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) { body = autoBlock; } }; - for (;i < s.size(); i++) { + for (; i < s.size(); i++) { Element& curr = *s[i]; IString id = curr[0]->str(); if (id == PARAM || id == LOCAL) { @@ -521,7 +591,7 @@ void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) { name = Name::fromInt(localIndex); } else { name = curr[j]->str(); - type = stringToType(curr[j+1]->str()); + type = stringToType(curr[j + 1]->str()); j++; } j++; @@ -534,12 +604,14 @@ void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) { currLocalTypes[name] = type; } } else if (id == RESULT) { - if (curr.size() > 2) throw ParseException("invalid result arity", curr.line, curr.col); + if (curr.size() > 2) + throw ParseException("invalid result arity", curr.line, curr.col); result = stringToType(curr[1]->str()); } else if (id == TYPE) { Name name = getFunctionTypeName(*curr[1]); type = name; - if (!wasm.getFunctionTypeOrNull(name)) throw ParseException("unknown function type"); + if (!wasm.getFunctionTypeOrNull(name)) + throw ParseException("unknown function type"); FunctionType* type = wasm.getFunctionType(name); result = type->result; for (size_t j = 0; j < type->params.size(); j++) { @@ -556,7 +628,8 @@ void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) { if (typeParams.size() > 0 && params.size() == 0) { params = typeParams; } - if (!currFunction) makeFunction(); + if (!currFunction) + makeFunction(); Expression* ex = parseExpression(curr); if (!body) { body = ex; @@ -569,19 +642,23 @@ void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) { // see https://github.com/WebAssembly/spec/pull/301 if (type.isNull()) { // if no function type name provided, then we generated one - auto functionType = make_unique<FunctionType>(sigToFunctionType(getSigFromStructs(result, params))); + auto functionType = make_unique<FunctionType>( + sigToFunctionType(getSigFromStructs(result, params))); for (auto& existing : wasm.functionTypes) { if (existing->structuralComparison(*functionType)) { type = existing->name; break; } } - if (!type.is()) throw ParseException("no function type [internal error?]", s.line, s.col); + if (!type.is()) + throw ParseException("no function type [internal error?]", s.line, s.col); } if (importModule.is()) { // this is an import, actually - if (!importBase.size()) throw ParseException("module but no base for import"); - if (!preParseImport) throw ParseException("!preParseImport in func"); + if (!importBase.size()) + throw ParseException("module but no base for import"); + if (!preParseImport) + throw ParseException("!preParseImport in func"); auto im = make_unique<Function>(); im->name = name; im->module = importModule; @@ -589,14 +666,17 @@ void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) { im->type = type; FunctionTypeUtils::fillFunction(im.get(), wasm.getFunctionType(type)); functionTypes[name] = im->result; - if (wasm.getFunctionOrNull(im->name)) throw ParseException("duplicate import", s.line, s.col); + if (wasm.getFunctionOrNull(im->name)) + throw ParseException("duplicate import", s.line, s.col); wasm.addFunction(im.release()); - if (currFunction) throw ParseException("import module inside function dec"); + if (currFunction) + throw ParseException("import module inside function dec"); currLocalTypes.clear(); nameMapper.clear(); return; } - if (preParseImport) throw ParseException("preParseImport in func"); + if (preParseImport) + throw ParseException("preParseImport in func"); if (brokeToAutoBlock) { ensureAutoBlock(); autoBlock->name = FAKE_RETURN; @@ -608,7 +688,8 @@ void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) { makeFunction(); body = allocator.alloc<Nop>(); } - if (currFunction->result != result) throw ParseException("bad func declaration", s.line, s.col); + if (currFunction->result != result) + throw ParseException("bad func declaration", s.line, s.col); currFunction->body = body; currFunction->type = type; if (s.startLoc) { @@ -617,41 +698,57 @@ void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) { if (s.endLoc) { currFunction->epilogLocation.insert(getDebugLocation(*s.endLoc)); } - if (wasm.getFunctionOrNull(currFunction->name)) throw ParseException("duplicate function", s.line, s.col); + if (wasm.getFunctionOrNull(currFunction->name)) + throw ParseException("duplicate function", s.line, s.col); wasm.addFunction(currFunction.release()); currLocalTypes.clear(); nameMapper.clear(); } -Type SExpressionWasmBuilder::stringToType(const char* str, bool allowError, bool prefix) { +Type SExpressionWasmBuilder::stringToType(const char* str, + bool allowError, + bool prefix) { if (str[0] == 'i') { - if (str[1] == '3' && str[2] == '2' && (prefix || str[3] == 0)) return i32; - if (str[1] == '6' && str[2] == '4' && (prefix || str[3] == 0)) return i64; + if (str[1] == '3' && str[2] == '2' && (prefix || str[3] == 0)) + return i32; + if (str[1] == '6' && str[2] == '4' && (prefix || str[3] == 0)) + return i64; } if (str[0] == 'f') { - if (str[1] == '3' && str[2] == '2' && (prefix || str[3] == 0)) return f32; - if (str[1] == '6' && str[2] == '4' && (prefix || str[3] == 0)) return f64; + if (str[1] == '3' && str[2] == '2' && (prefix || str[3] == 0)) + return f32; + if (str[1] == '6' && str[2] == '4' && (prefix || str[3] == 0)) + return f64; } if (str[0] == 'v') { - if (str[1] == '1' && str[2] == '2' && str[3] == '8' && (prefix || str[4] == 0)) { + if (str[1] == '1' && str[2] == '2' && str[3] == '8' && + (prefix || str[4] == 0)) { return v128; } } - if (allowError) return none; + if (allowError) + return none; throw ParseException("invalid wasm type"); } Type SExpressionWasmBuilder::stringToLaneType(const char* str) { - if (strcmp(str, "i8x16") == 0) return i32; - if (strcmp(str, "i16x8") == 0) return i32; - if (strcmp(str, "i32x4") == 0) return i32; - if (strcmp(str, "i64x2") == 0) return i64; - if (strcmp(str, "f32x4") == 0) return f32; - if (strcmp(str, "f64x2") == 0) return f64; + if (strcmp(str, "i8x16") == 0) + return i32; + if (strcmp(str, "i16x8") == 0) + return i32; + if (strcmp(str, "i32x4") == 0) + return i32; + if (strcmp(str, "i64x2") == 0) + return i64; + if (strcmp(str, "f32x4") == 0) + return f32; + if (strcmp(str, "f64x2") == 0) + return f64; return none; } -Function::DebugLocation SExpressionWasmBuilder::getDebugLocation(const SourceLocation& loc) { +Function::DebugLocation +SExpressionWasmBuilder::getDebugLocation(const SourceLocation& loc) { IString file = loc.filename; auto& debugInfoFileNames = wasm.debugInfoFileNames; auto iter = debugInfoFileIndices.find(file); @@ -672,7 +769,7 @@ Expression* SExpressionWasmBuilder::parseExpression(Element& s) { return result; } -Expression* SExpressionWasmBuilder::makeExpression(Element& s) { +Expression* SExpressionWasmBuilder::makeExpression(Element& s){ #define INSTRUCTION_PARSER #include "gen-s-parser.inc" } @@ -681,9 +778,7 @@ Expression* SExpressionWasmBuilder::makeUnreachable() { return allocator.alloc<Unreachable>(); } -Expression* SExpressionWasmBuilder::makeNop() { - return allocator.alloc<Nop>(); -} +Expression* SExpressionWasmBuilder::makeNop() { return allocator.alloc<Nop>(); } Expression* SExpressionWasmBuilder::makeBinary(Element& s, BinaryOp op) { auto ret = allocator.alloc<Binary>(); @@ -694,7 +789,6 @@ Expression* SExpressionWasmBuilder::makeBinary(Element& s, BinaryOp op) { return ret; } - Expression* SExpressionWasmBuilder::makeUnary(Element& s, UnaryOp op) { auto ret = allocator.alloc<Unary>(); ret->op = op; @@ -737,15 +831,18 @@ Expression* SExpressionWasmBuilder::makeHost(Element& s, HostOp op) { } Index SExpressionWasmBuilder::getLocalIndex(Element& s) { - if (!currFunction) throw ParseException("local access in non-function scope", s.line, s.col); + if (!currFunction) + throw ParseException("local access in non-function scope", s.line, s.col); if (s.dollared()) { auto ret = s.str(); - if (currFunction->localIndices.count(ret) == 0) throw ParseException("bad local name", s.line, s.col); + if (currFunction->localIndices.count(ret) == 0) + throw ParseException("bad local name", s.line, s.col); return currFunction->getLocalIndex(ret); } // this is a numeric index Index ret = atoi(s.c_str()); - if (ret >= currFunction->getNumLocals()) throw ParseException("bad local index", s.line, s.col); + if (ret >= currFunction->getNumLocals()) + throw ParseException("bad local index", s.line, s.col); return ret; } @@ -788,16 +885,19 @@ Expression* SExpressionWasmBuilder::makeGetGlobal(Element& s) { Expression* SExpressionWasmBuilder::makeSetGlobal(Element& s) { auto ret = allocator.alloc<SetGlobal>(); ret->name = getGlobalName(*s[1]); - if (wasm.getGlobalOrNull(ret->name) && !wasm.getGlobalOrNull(ret->name)->mutable_) throw ParseException("global.set of immutable", s.line, s.col); + if (wasm.getGlobalOrNull(ret->name) && + !wasm.getGlobalOrNull(ret->name)->mutable_) + throw ParseException("global.set of immutable", s.line, s.col); ret->value = parseExpression(s[2]); ret->finalize(); return ret; } - Expression* SExpressionWasmBuilder::makeBlock(Element& s) { - if (!currFunction) throw ParseException("block is unallowed outside of functions"); - // special-case Block, because Block nesting (in their first element) can be incredibly deep + if (!currFunction) + throw ParseException("block is unallowed outside of functions"); + // special-case Block, because Block nesting (in their first element) can be + // incredibly deep auto curr = allocator.alloc<Block>(); auto* sp = &s; std::vector<std::pair<Element*, Block*>> stack; @@ -808,7 +908,8 @@ Expression* SExpressionWasmBuilder::makeBlock(Element& s) { Name sName; if (i < s.size() && s[i]->isStr()) { // could be a name or a type - if (s[i]->dollared() || stringToType(s[i]->str(), true /* allowError */) == none) { + if (s[i]->dollared() || + stringToType(s[i]->str(), true /* allowError */) == none) { sName = s[i++]->str(); } else { sName = "block"; @@ -819,7 +920,8 @@ Expression* SExpressionWasmBuilder::makeBlock(Element& s) { curr->name = nameMapper.pushLabelName(sName); // block signature curr->type = parseOptionalResultType(s, i); - if (i >= s.size()) break; // empty block + if (i >= s.size()) + break; // empty block auto& first = *s[i]; if (first[0]->str() == BLOCK) { // recurse @@ -860,7 +962,8 @@ Expression* SExpressionWasmBuilder::makeBlock(Element& s) { return stack[0].second; } -// Similar to block, but the label is handled by the enclosing if (since there might not be a then or else, ick) +// Similar to block, but the label is handled by the enclosing if (since there +// might not be a then or else, ick) Expression* SExpressionWasmBuilder::makeThenOrElse(Element& s) { auto ret = allocator.alloc<Block>(); size_t i = 1; @@ -878,7 +981,7 @@ template<int Lanes> static Literal makeLanes(Element& s, MixedArena& allocator, Type lane_t) { std::array<Literal, Lanes> lanes; for (size_t i = 0; i < Lanes; ++i) { - Expression* lane = parseConst(s[i+2]->str(), lane_t, allocator); + Expression* lane = parseConst(s[i + 2]->str(), lane_t, allocator); if (lane) { lanes[i] = lane->cast<Const>()->value; } else { @@ -891,7 +994,8 @@ static Literal makeLanes(Element& s, MixedArena& allocator, Type lane_t) { Expression* SExpressionWasmBuilder::makeConst(Element& s, Type type) { if (type != v128) { auto ret = parseConst(s[1]->str(), type, allocator); - if (!ret) throw ParseException("bad const"); + if (!ret) + throw ParseException("bad const"); return ret; } @@ -927,7 +1031,8 @@ Expression* SExpressionWasmBuilder::makeConst(Element& s, Type type) { ret->value = makeLanes<16>(s, allocator, lane_t); break; } - default: throw ParseException("Unexpected number of lanes in v128 literal"); + default: + throw ParseException("Unexpected number of lanes in v128 literal"); } ret->finalize(); return ret; @@ -939,11 +1044,14 @@ static uint8_t parseMemBytes(const char*& s, uint8_t fallback) { ret = 1; s++; } else if (s[0] == '1') { - if (s[1] != '6') throw ParseException("expected 16 for memop size"); + if (s[1] != '6') + throw ParseException("expected 16 for memop size"); ret = 2; s += 2; } else if (s[0] == '3') { - if (s[1] != '2') throw ParseException("expected 32 for memop size");; + if (s[1] != '2') + throw ParseException("expected 32 for memop size"); + ; ret = 4; s += 2; } else { @@ -952,28 +1060,36 @@ static uint8_t parseMemBytes(const char*& s, uint8_t fallback) { return ret; } -static size_t parseMemAttributes(Element& s, Address* offset, Address* align, Address fallback) { +static size_t parseMemAttributes(Element& s, + Address* offset, + Address* align, + Address fallback) { size_t i = 1; *offset = 0; *align = fallback; while (!s[i]->isList()) { - const char *str = s[i]->c_str(); - const char *eq = strchr(str, '='); - if (!eq) throw ParseException("missing = in memory attribute"); + const char* str = s[i]->c_str(); + const char* eq = strchr(str, '='); + if (!eq) + throw ParseException("missing = in memory attribute"); eq++; - if (*eq == 0) throw ParseException("missing value in memory attribute", s.line, s.col); + if (*eq == 0) + throw ParseException("missing value in memory attribute", s.line, s.col); char* endptr; uint64_t value = strtoll(eq, &endptr, 10); if (*endptr != 0) { throw ParseException("bad memory attribute immediate", s.line, s.col); } if (str[0] == 'a') { - if (value > std::numeric_limits<uint32_t>::max()) throw ParseException("bad align", s.line, s.col); + if (value > std::numeric_limits<uint32_t>::max()) + throw ParseException("bad align", s.line, s.col); *align = value; } else if (str[0] == 'o') { - if (value > std::numeric_limits<uint32_t>::max()) throw ParseException("bad offset", s.line, s.col); + if (value > std::numeric_limits<uint32_t>::max()) + throw ParseException("bad offset", s.line, s.col); *offset = value; - } else throw ParseException("bad memory attribute"); + } else + throw ParseException("bad memory attribute"); i++; } return i; @@ -983,14 +1099,18 @@ static const char* findMemExtra(const Element& s, size_t skip, bool isAtomic) { auto* str = s.c_str(); auto size = strlen(str); auto* ret = strchr(str, '.'); - if (!ret) throw ParseException("missing '.' in memory access", s.line, s.col); + if (!ret) + throw ParseException("missing '.' in memory access", s.line, s.col); ret += skip; - if (isAtomic) ret += 7; // after "type.atomic.load" - if (ret > str + size) throw ParseException("memory access ends abruptly", s.line, s.col); + if (isAtomic) + ret += 7; // after "type.atomic.load" + if (ret > str + size) + throw ParseException("memory access ends abruptly", s.line, s.col); return ret; } -Expression* SExpressionWasmBuilder::makeLoad(Element& s, Type type, bool isAtomic) { +Expression* +SExpressionWasmBuilder::makeLoad(Element& s, Type type, bool isAtomic) { const char* extra = findMemExtra(*s[0], 5 /* after "type.load" */, isAtomic); auto* ret = allocator.alloc<Load>(); ret->isAtomic = isAtomic; @@ -1003,7 +1123,8 @@ Expression* SExpressionWasmBuilder::makeLoad(Element& s, Type type, bool isAtomi return ret; } -Expression* SExpressionWasmBuilder::makeStore(Element& s, Type type, bool isAtomic) { +Expression* +SExpressionWasmBuilder::makeStore(Element& s, Type type, bool isAtomic) { const char* extra = findMemExtra(*s[0], 6 /* after "type.store" */, isAtomic); auto ret = allocator.alloc<Store>(); ret->isAtomic = isAtomic; @@ -1011,51 +1132,70 @@ Expression* SExpressionWasmBuilder::makeStore(Element& s, Type type, bool isAtom ret->bytes = parseMemBytes(extra, getTypeSize(type)); size_t i = parseMemAttributes(s, &ret->offset, &ret->align, ret->bytes); ret->ptr = parseExpression(s[i]); - ret->value = parseExpression(s[i+1]); + ret->value = parseExpression(s[i + 1]); ret->finalize(); return ret; } -Expression* SExpressionWasmBuilder::makeAtomicRMWOrCmpxchg(Element& s, Type type) { - const char* extra = findMemExtra(*s[0], 11 /* after "type.atomic.rmw" */, /* isAtomic = */ false); +Expression* SExpressionWasmBuilder::makeAtomicRMWOrCmpxchg(Element& s, + Type type) { + const char* extra = findMemExtra( + *s[0], 11 /* after "type.atomic.rmw" */, /* isAtomic = */ false); auto bytes = parseMemBytes(extra, getTypeSize(type)); extra = strchr(extra, '.'); // after the optional '_u' and before the opcode - if (!extra) throw ParseException("malformed atomic rmw instruction"); + if (!extra) + throw ParseException("malformed atomic rmw instruction"); extra++; // after the '.' - if (!strncmp(extra, "cmpxchg", 7)) return makeAtomicCmpxchg(s, type, bytes, extra); + if (!strncmp(extra, "cmpxchg", 7)) + return makeAtomicCmpxchg(s, type, bytes, extra); return makeAtomicRMW(s, type, bytes, extra); } -Expression* SExpressionWasmBuilder::makeAtomicRMW(Element& s, Type type, uint8_t bytes, const char* extra) { +Expression* SExpressionWasmBuilder::makeAtomicRMW(Element& s, + Type type, + uint8_t bytes, + const char* extra) { auto ret = allocator.alloc<AtomicRMW>(); ret->type = type; ret->bytes = bytes; - if (!strncmp(extra, "add", 3)) ret->op = Add; - else if (!strncmp(extra, "and", 3)) ret->op = And; - else if (!strncmp(extra, "or", 2)) ret->op = Or; - else if (!strncmp(extra, "sub", 3)) ret->op = Sub; - else if (!strncmp(extra, "xor", 3)) ret->op = Xor; - else if (!strncmp(extra, "xchg", 4)) ret->op = Xchg; - else throw ParseException("bad atomic rmw operator"); + if (!strncmp(extra, "add", 3)) + ret->op = Add; + else if (!strncmp(extra, "and", 3)) + ret->op = And; + else if (!strncmp(extra, "or", 2)) + ret->op = Or; + else if (!strncmp(extra, "sub", 3)) + ret->op = Sub; + else if (!strncmp(extra, "xor", 3)) + ret->op = Xor; + else if (!strncmp(extra, "xchg", 4)) + ret->op = Xchg; + else + throw ParseException("bad atomic rmw operator"); Address align; size_t i = parseMemAttributes(s, &ret->offset, &align, ret->bytes); - if (align != ret->bytes) throw ParseException("Align of Atomic RMW must match size"); + if (align != ret->bytes) + throw ParseException("Align of Atomic RMW must match size"); ret->ptr = parseExpression(s[i]); - ret->value = parseExpression(s[i+1]); + ret->value = parseExpression(s[i + 1]); ret->finalize(); return ret; } -Expression* SExpressionWasmBuilder::makeAtomicCmpxchg(Element& s, Type type, uint8_t bytes, const char* extra) { +Expression* SExpressionWasmBuilder::makeAtomicCmpxchg(Element& s, + Type type, + uint8_t bytes, + const char* extra) { auto ret = allocator.alloc<AtomicCmpxchg>(); ret->type = type; ret->bytes = bytes; Address align; size_t i = parseMemAttributes(s, &ret->offset, &align, ret->bytes); - if (align != ret->bytes) throw ParseException("Align of Atomic Cmpxchg must match size"); + if (align != ret->bytes) + throw ParseException("Align of Atomic Cmpxchg must match size"); ret->ptr = parseExpression(s[i]); - ret->expected = parseExpression(s[i+1]); - ret->replacement = parseExpression(s[i+2]); + ret->expected = parseExpression(s[i + 1]); + ret->replacement = parseExpression(s[i + 2]); ret->finalize(); return ret; } @@ -1081,15 +1221,20 @@ Expression* SExpressionWasmBuilder::makeAtomicNotify(Element& s) { } static uint8_t parseLaneIndex(const Element* s, size_t lanes) { - const char *str = s->c_str(); - char *end; + const char* str = s->c_str(); + char* end; auto n = static_cast<unsigned long long>(strtoll(str, &end, 10)); - if (end == str || *end != '\0') throw ParseException("Expected lane index"); - if (n > lanes) throw ParseException("lane index must be less than " + std::to_string(lanes)); + if (end == str || *end != '\0') + throw ParseException("Expected lane index"); + if (n > lanes) + throw ParseException("lane index must be less than " + + std::to_string(lanes)); return uint8_t(n); } -Expression* SExpressionWasmBuilder::makeSIMDExtract(Element& s, SIMDExtractOp op, size_t lanes) { +Expression* SExpressionWasmBuilder::makeSIMDExtract(Element& s, + SIMDExtractOp op, + size_t lanes) { auto ret = allocator.alloc<SIMDExtract>(); ret->op = op; ret->index = parseLaneIndex(s[1], lanes); @@ -1098,7 +1243,9 @@ Expression* SExpressionWasmBuilder::makeSIMDExtract(Element& s, SIMDExtractOp op return ret; } -Expression* SExpressionWasmBuilder::makeSIMDReplace(Element& s, SIMDReplaceOp op, size_t lanes) { +Expression* SExpressionWasmBuilder::makeSIMDReplace(Element& s, + SIMDReplaceOp op, + size_t lanes) { auto ret = allocator.alloc<SIMDReplace>(); ret->op = op; ret->index = parseLaneIndex(s[1], lanes); @@ -1111,7 +1258,7 @@ Expression* SExpressionWasmBuilder::makeSIMDReplace(Element& s, SIMDReplaceOp op Expression* SExpressionWasmBuilder::makeSIMDShuffle(Element& s) { auto ret = allocator.alloc<SIMDShuffle>(); for (size_t i = 0; i < 16; ++i) { - ret->mask[i] = parseLaneIndex(s[i+1], 32); + ret->mask[i] = parseLaneIndex(s[i + 1], 32); } ret->left = parseExpression(s[17]); ret->right = parseExpression(s[18]); @@ -1203,11 +1350,13 @@ Expression* SExpressionWasmBuilder::makeIf(Element& s) { return ret; } - -Expression* SExpressionWasmBuilder::makeMaybeBlock(Element& s, size_t i, Type type) { +Expression* +SExpressionWasmBuilder::makeMaybeBlock(Element& s, size_t i, Type type) { Index stopAt = -1; - if (s.size() == i) return allocator.alloc<Nop>(); - if (s.size() == i+1) return parseExpression(s[i]); + if (s.size() == i) + return allocator.alloc<Nop>(); + if (s.size() == i + 1) + return parseExpression(s[i]); auto ret = allocator.alloc<Block>(); for (; i < s.size() && i < stopAt; i++) { ret->list.push_back(parseExpression(s[i])); @@ -1265,7 +1414,8 @@ Expression* SExpressionWasmBuilder::makeCall(Element& s) { } Expression* SExpressionWasmBuilder::makeCallIndirect(Element& s) { - if (!wasm.table.exists) throw ParseException("no table"); + if (!wasm.table.exists) + throw ParseException("no table"); auto ret = allocator.alloc<CallIndirect>(); Index i = 1; Element& typeElement = *s[i]; @@ -1273,7 +1423,8 @@ Expression* SExpressionWasmBuilder::makeCallIndirect(Element& s) { // type name given IString type = typeElement[1]->str(); auto* fullType = wasm.getFunctionTypeOrNull(type); - if (!fullType) throw ParseException("invalid call_indirect type", s.line, s.col); + if (!fullType) + throw ParseException("invalid call_indirect type", s.line, s.col); ret->fullType = fullType->name; i++; } else { @@ -1314,9 +1465,11 @@ Name SExpressionWasmBuilder::getLabel(Element& s) { } catch (std::out_of_range&) { throw ParseException("out of range break offset"); } - if (offset > nameMapper.labelStack.size()) throw ParseException("invalid label", s.line, s.col); + if (offset > nameMapper.labelStack.size()) + throw ParseException("invalid label", s.line, s.col); if (offset == nameMapper.labelStack.size()) { - // a break to the function's scope. this means we need an automatic block, with a name + // a break to the function's scope. this means we need an automatic block, + // with a name brokeToAutoBlock = true; return FAKE_RETURN; } @@ -1329,7 +1482,8 @@ Expression* SExpressionWasmBuilder::makeBreak(Element& s) { size_t i = 1; ret->name = getLabel(*s[i]); i++; - if (i == s.size()) return ret; + if (i == s.size()) + return ret; if (s[0]->str() == BR_IF) { if (i + 1 < s.size()) { ret->value = parseExpression(s[i]); @@ -1349,7 +1503,8 @@ Expression* SExpressionWasmBuilder::makeBreakTable(Element& s) { while (!s[i]->isList()) { ret->targets.push_back(getLabel(*s[i++])); } - if (ret->targets.size() == 0) throw ParseException("switch with no targets"); + if (ret->targets.size() == 0) + throw ParseException("switch with no targets"); ret->default_ = ret->targets.back(); ret->targets.pop_back(); ret->condition = parseExpression(s[i++]); @@ -1368,14 +1523,18 @@ Expression* SExpressionWasmBuilder::makeReturn(Element& s) { return ret; } -// converts an s-expression string representing binary data into an output sequence of raw bytes -// this appends to data, which may already contain content. -void SExpressionWasmBuilder::stringToBinary(const char* input, size_t size, std::vector<char>& data) { +// converts an s-expression string representing binary data into an output +// sequence of raw bytes this appends to data, which may already contain +// content. +void SExpressionWasmBuilder::stringToBinary(const char* input, + size_t size, + std::vector<char>& data) { auto originalSize = data.size(); data.resize(originalSize + size); - char *write = data.data() + originalSize; + char* write = data.data() + originalSize; while (1) { - if (input[0] == 0) break; + if (input[0] == 0) + break; if (input[0] == '\\') { if (input[1] == '"') { *write++ = '"'; @@ -1398,7 +1557,7 @@ void SExpressionWasmBuilder::stringToBinary(const char* input, size_t size, std: input += 2; continue; } else { - *write++ = (char)(unhex(input[1])*16 + unhex(input[2])); + *write++ = (char)(unhex(input[1]) * 16 + unhex(input[2])); input += 3; continue; } @@ -1419,13 +1578,15 @@ Index SExpressionWasmBuilder::parseMemoryLimits(Element& s, Index i) { return i; } uint64_t max = atoll(s[i++]->c_str()); - if (max > Memory::kMaxSize) throw ParseException("total memory must be <= 4GB"); + if (max > Memory::kMaxSize) + throw ParseException("total memory must be <= 4GB"); wasm.memory.max = max; return i; } void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { - if (wasm.memory.exists) throw ParseException("too many memories"); + if (wasm.memory.exists) + throw ParseException("too many memories"); wasm.memory.exists = true; wasm.memory.shared = false; Index i = 1; @@ -1440,7 +1601,8 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { ex->name = inner[1]->str(); ex->value = wasm.memory.name; ex->kind = ExternalKind::Memory; - if (wasm.getExportOrNull(ex->name)) throw ParseException("duplicate export", s.line, s.col); + if (wasm.getExportOrNull(ex->name)) + throw ParseException("duplicate export", s.line, s.col); wasm.addExport(ex.release()); i++; } else if (inner[0]->str() == IMPORT) { @@ -1452,7 +1614,8 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { parseMemoryLimits(inner, 1); i++; } else { - if (!(inner.size() > 0 ? inner[0]->str() != IMPORT : true)) throw ParseException("bad import ending"); + if (!(inner.size() > 0 ? inner[0]->str() != IMPORT : true)) + throw ParseException("bad import ending"); // (memory (data ..)) format auto offset = allocator.alloc<Const>()->set(Literal(int32_t(0))); parseInnerData(*s[i], 1, offset, false); @@ -1460,7 +1623,8 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { return; } } - if (!wasm.memory.shared) i = parseMemoryLimits(s, i); + if (!wasm.memory.shared) + i = parseMemoryLimits(s, i); // Parse memory initializers. while (i < s.size()) { @@ -1472,7 +1636,7 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { } else { offsetValue = getCheckedAddress(curr[j++], "excessive memory offset"); } - const char *input = curr[j]->c_str(); + const char* input = curr[j]->c_str(); auto* offset = allocator.alloc<Const>(); offset->type = i32; offset->value = Literal(int32_t(offsetValue)); @@ -1488,7 +1652,8 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) { } void SExpressionWasmBuilder::parseData(Element& s) { - if (!wasm.memory.exists) throw ParseException("data but no memory"); + if (!wasm.memory.exists) + throw ParseException("data but no memory"); bool isPassive = false; Expression* offset = nullptr; Index i = 1; @@ -1508,15 +1673,19 @@ void SExpressionWasmBuilder::parseData(Element& s) { parseInnerData(s, s.size() - 1, offset, isPassive); } -void SExpressionWasmBuilder::parseInnerData(Element& s, Index i, Expression* offset, bool isPassive) { +void SExpressionWasmBuilder::parseInnerData(Element& s, + Index i, + Expression* offset, + bool isPassive) { std::vector<char> data; while (i < s.size()) { - const char *input = s[i++]->c_str(); + const char* input = s[i++]->c_str(); if (auto size = strlen(input)) { stringToBinary(input, size, data); } } - wasm.memory.segments.emplace_back(isPassive, offset, data.data(), data.size()); + wasm.memory.segments.emplace_back( + isPassive, offset, data.data(), data.size()); } void SExpressionWasmBuilder::parseExport(Element& s) { @@ -1533,14 +1702,17 @@ void SExpressionWasmBuilder::parseExport(Element& s) { ex->kind = ExternalKind::Table; } else if (inner[0]->str() == GLOBAL) { ex->kind = ExternalKind::Global; - if (wasm.getGlobalOrNull(ex->value) && wasm.getGlobal(ex->value)->mutable_) throw ParseException("cannot export a mutable global", s.line, s.col); + if (wasm.getGlobalOrNull(ex->value) && + wasm.getGlobal(ex->value)->mutable_) + throw ParseException("cannot export a mutable global", s.line, s.col); } else { throw ParseException("invalid export"); } } else if (!s[2]->dollared() && !std::isdigit(s[2]->str()[0])) { ex->value = s[3]->str(); if (s[2]->str() == MEMORY) { - if (!wasm.memory.exists) throw ParseException("memory exported but no memory"); + if (!wasm.memory.exists) + throw ParseException("memory exported but no memory"); ex->kind = ExternalKind::Memory; } else if (s[2]->str() == TABLE) { ex->kind = ExternalKind::Table; @@ -1554,24 +1726,28 @@ void SExpressionWasmBuilder::parseExport(Element& s) { ex->value = s[2]->str(); ex->kind = ExternalKind::Function; } - if (wasm.getExportOrNull(ex->name)) throw ParseException("duplicate export", s.line, s.col); + if (wasm.getExportOrNull(ex->name)) + throw ParseException("duplicate export", s.line, s.col); wasm.addExport(ex.release()); } void SExpressionWasmBuilder::parseImport(Element& s) { size_t i = 1; - bool newStyle = s.size() == 4 && s[3]->isList(); // (import "env" "STACKTOP" (global $stackTop i32)) + // (import "env" "STACKTOP" (global $stackTop i32)) + bool newStyle = s.size() == 4 && s[3]->isList(); auto kind = ExternalKind::Invalid; if (newStyle) { if ((*s[3])[0]->str() == FUNC) { kind = ExternalKind::Function; } else if ((*s[3])[0]->str() == MEMORY) { kind = ExternalKind::Memory; - if (wasm.memory.exists) throw ParseException("more than one memory"); + if (wasm.memory.exists) + throw ParseException("more than one memory"); wasm.memory.exists = true; } else if ((*s[3])[0]->str() == TABLE) { kind = ExternalKind::Table; - if (wasm.table.exists) throw ParseException("more than one table"); + if (wasm.table.exists) + throw ParseException("more than one table"); wasm.table.exists = true; } else if ((*s[3])[0]->str() == GLOBAL) { kind = ExternalKind::Global; @@ -1583,7 +1759,8 @@ void SExpressionWasmBuilder::parseImport(Element& s) { Name name; if (s.size() > 3 && s[3]->isStr()) { name = s[i++]->str(); - } else if (newStyle && newStyleInner < s[3]->size() && (*s[3])[newStyleInner]->dollared()) { + } else if (newStyle && newStyleInner < s[3]->size() && + (*s[3])[newStyleInner]->dollared()) { name = (*s[3])[newStyleInner++]->str(); } if (!name.is()) { @@ -1616,9 +1793,11 @@ void SExpressionWasmBuilder::parseImport(Element& s) { kind = ExternalKind::Function; } auto module = s[i++]->str(); - if (!s[i]->isStr()) throw ParseException("no name for import"); + if (!s[i]->isStr()) + throw ParseException("no name for import"); auto base = s[i++]->str(); - if (!module.size() || !base.size()) throw ParseException("imports must have module and base"); + if (!module.size() || !base.size()) + throw ParseException("imports must have module and base"); // parse internals Element& inner = newStyle ? *s[3] : s; Index j = newStyle ? newStyleInner : i; @@ -1635,14 +1814,16 @@ void SExpressionWasmBuilder::parseImport(Element& s) { type->result = stringToType(params[1]->str()); } else if (id == TYPE) { IString name = params[1]->str(); - if (!wasm.getFunctionTypeOrNull(name)) throw ParseException("bad function type for import"); + if (!wasm.getFunctionTypeOrNull(name)) + throw ParseException("bad function type for import"); *type = *wasm.getFunctionType(name); } else { throw ParseException("bad import element"); } - if (inner.size() > j+1) { - Element& result = *inner[j+1]; - if (result[0]->str() != RESULT) throw ParseException("expected result"); + if (inner.size() > j + 1) { + Element& result = *inner[j + 1]; + if (result[0]->str() != RESULT) + throw ParseException("expected result"); type->result = stringToType(result[1]->str()); } } @@ -1662,7 +1843,8 @@ void SExpressionWasmBuilder::parseImport(Element& s) { type = stringToType(inner[j]->str()); } else { auto& inner2 = *inner[j]; - if (inner2[0]->str() != MUT) throw ParseException("expected mut"); + if (inner2[0]->str() != MUT) + throw ParseException("expected mut"); type = stringToType(inner2[1]->str()); mutable_ = true; } @@ -1677,10 +1859,12 @@ void SExpressionWasmBuilder::parseImport(Element& s) { wasm.table.module = module; wasm.table.base = base; if (j < inner.size() - 1) { - wasm.table.initial = getCheckedAddress(inner[j++], "excessive table init size"); + wasm.table.initial = + getCheckedAddress(inner[j++], "excessive table init size"); } if (j < inner.size() - 1) { - wasm.table.max = getCheckedAddress(inner[j++], "excessive table max size"); + wasm.table.max = + getCheckedAddress(inner[j++], "excessive table max size"); } else { wasm.table.max = Table::kUnlimitedSize; } @@ -1690,7 +1874,8 @@ void SExpressionWasmBuilder::parseImport(Element& s) { wasm.memory.base = base; if (inner[j]->isList()) { auto& limits = *inner[j]; - if (!(limits[0]->isStr() && limits[0]->str() == "shared")) throw ParseException("bad memory limit declaration"); + if (!(limits[0]->isStr() && limits[0]->str() == "shared")) + throw ParseException("bad memory limit declaration"); wasm.memory.shared = true; parseMemoryLimits(limits, 1); } else { @@ -1720,7 +1905,8 @@ void SExpressionWasmBuilder::parseGlobal(Element& s, bool preParseImport) { ex->name = inner[1]->str(); ex->value = global->name; ex->kind = ExternalKind::Global; - if (wasm.getExportOrNull(ex->name)) throw ParseException("duplicate export", s.line, s.col); + if (wasm.getExportOrNull(ex->name)) + throw ParseException("duplicate export", s.line, s.col); wasm.addExport(ex.release()); exported = true; i++; @@ -1736,25 +1922,30 @@ void SExpressionWasmBuilder::parseGlobal(Element& s, bool preParseImport) { break; } } - if (exported && mutable_) throw ParseException("cannot export a mutable global", s.line, s.col); + if (exported && mutable_) + throw ParseException("cannot export a mutable global", s.line, s.col); if (type == none) { type = stringToType(s[i++]->str()); } if (importModule.is()) { // this is an import, actually - if (!importBase.size()) throw ParseException("module but no base for import"); - if (!preParseImport) throw ParseException("!preParseImport in global"); + if (!importBase.size()) + throw ParseException("module but no base for import"); + if (!preParseImport) + throw ParseException("!preParseImport in global"); auto im = make_unique<Global>(); im->name = global->name; im->module = importModule; im->base = importBase; im->type = type; im->mutable_ = mutable_; - if (wasm.getGlobalOrNull(im->name)) throw ParseException("duplicate import", s.line, s.col); + if (wasm.getGlobalOrNull(im->name)) + throw ParseException("duplicate import", s.line, s.col); wasm.addGlobal(im.release()); return; } - if (preParseImport) throw ParseException("preParseImport in global"); + if (preParseImport) + throw ParseException("preParseImport in global"); global->type = type; if (i < s.size()) { global->init = parseExpression(s[i++]); @@ -1762,21 +1953,25 @@ void SExpressionWasmBuilder::parseGlobal(Element& s, bool preParseImport) { throw ParseException("global without init", s.line, s.col); } global->mutable_ = mutable_; - if (i != s.size()) throw ParseException("extra import elements"); - if (wasm.getGlobalOrNull(global->name)) throw ParseException("duplicate import", s.line, s.col); + if (i != s.size()) + throw ParseException("extra import elements"); + if (wasm.getGlobalOrNull(global->name)) + throw ParseException("duplicate import", s.line, s.col); wasm.addGlobal(global.release()); } - void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) { - if (wasm.table.exists) throw ParseException("more than one table"); + if (wasm.table.exists) + throw ParseException("more than one table"); wasm.table.exists = true; Index i = 1; - if (i == s.size()) return; // empty table in old notation + if (i == s.size()) + return; // empty table in old notation if (s[i]->dollared()) { wasm.table.name = s[i++]->str(); } - if (i == s.size()) return; + if (i == s.size()) + return; Name importModule, importBase; if (s[i]->isList()) { auto& inner = *s[i]; @@ -1785,11 +1980,13 @@ void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) { ex->name = inner[1]->str(); ex->value = wasm.table.name; ex->kind = ExternalKind::Table; - if (wasm.getExportOrNull(ex->name)) throw ParseException("duplicate export", s.line, s.col); + if (wasm.getExportOrNull(ex->name)) + throw ParseException("duplicate export", s.line, s.col); wasm.addExport(ex.release()); i++; } else if (inner[0]->str() == IMPORT) { - if (!preParseImport) throw ParseException("!preParseImport in table"); + if (!preParseImport) + throw ParseException("!preParseImport in table"); wasm.table.module = inner[1]->str(); wasm.table.base = inner[2]->str(); i++; @@ -1797,19 +1994,23 @@ void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) { throw ParseException("invalid table"); } } - if (i == s.size()) return; + if (i == s.size()) + return; if (!s[i]->dollared()) { if (s[i]->str() == FUNCREF) { // (table type (elem ..)) parseInnerElem(*s[i + 1]); if (wasm.table.segments.size() > 0) { - wasm.table.initial = wasm.table.max = wasm.table.segments[0].data.size(); + wasm.table.initial = wasm.table.max = + wasm.table.segments[0].data.size(); } else { wasm.table.initial = wasm.table.max = 0; } return; } - // first element isn't dollared, and isn't funcref. this could be old syntax for (table 0 1) which means function 0 and 1, or it could be (table initial max? type), look for type + // first element isn't dollared, and isn't funcref. this could be old syntax + // for (table 0 1) which means function 0 and 1, or it could be (table + // initial max? type), look for type if (s[s.size() - 1]->str() == FUNCREF) { // (table initial max? type) if (i < s.size() - 1) { @@ -1840,8 +2041,11 @@ void SExpressionWasmBuilder::parseElem(Element& s) { parseInnerElem(s, i, offset); } -void SExpressionWasmBuilder::parseInnerElem(Element& s, Index i, Expression* offset) { - if (!wasm.table.exists) throw ParseException("elem without table", s.line, s.col); +void SExpressionWasmBuilder::parseInnerElem(Element& s, + Index i, + Expression* offset) { + if (!wasm.table.exists) + throw ParseException("elem without table", s.line, s.col); if (!offset) { offset = allocator.alloc<Const>()->set(Literal(int32_t(0))); } @@ -1867,7 +2071,8 @@ void SExpressionWasmBuilder::parseType(Element& s) { type->params.push_back(stringToType(curr[j]->str())); } } else if (curr[0]->str() == RESULT) { - if (curr.size() > 2) throw ParseException("invalid result arity", curr.line, curr.col); + if (curr.size() > 2) + throw ParseException("invalid result arity", curr.line, curr.col); type->result = stringToType(curr[1]->str()); } } @@ -1875,7 +2080,8 @@ void SExpressionWasmBuilder::parseType(Element& s) { type->name = Name::fromInt(wasm.functionTypes.size()); } functionTypeNames.push_back(type->name); - if (wasm.getFunctionTypeOrNull(type->name)) throw ParseException("duplicate function type", s.line, s.col); + if (wasm.getFunctionTypeOrNull(type->name)) + throw ParseException("duplicate function type", s.line, s.col); wasm.addFunctionType(std::move(type)); } diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index fc393ed98..78ad6f628 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -17,33 +17,46 @@ #include "wasm-type.h" #include "wasm-features.h" -#include <cstdlib> #include "compiler-support.h" +#include <cstdlib> namespace wasm { const char* printType(Type type) { switch (type) { - case Type::none: return "none"; - case Type::i32: return "i32"; - case Type::i64: return "i64"; - case Type::f32: return "f32"; - case Type::f64: return "f64"; - case Type::v128: return "v128"; - case Type::unreachable: return "unreachable"; + case Type::none: + return "none"; + case Type::i32: + return "i32"; + case Type::i64: + return "i64"; + case Type::f32: + return "f32"; + case Type::f64: + return "f64"; + case Type::v128: + return "v128"; + case Type::unreachable: + return "unreachable"; } WASM_UNREACHABLE(); } unsigned getTypeSize(Type type) { switch (type) { - case Type::i32: return 4; - case Type::i64: return 8; - case Type::f32: return 4; - case Type::f64: return 8; - case Type::v128: return 16; + case Type::i32: + return 4; + case Type::i64: + return 8; + case Type::f32: + return 4; + case Type::f64: + return 8; + case Type::v128: + return 16; case Type::none: - case Type::unreachable: WASM_UNREACHABLE(); + case Type::unreachable: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } @@ -56,39 +69,41 @@ FeatureSet getFeatures(Type type) { } Type getType(unsigned size, bool float_) { - if (size < 4) return Type::i32; - if (size == 4) return float_ ? Type::f32 : Type::i32; - if (size == 8) return float_ ? Type::f64 : Type::i64; - if (size == 16) return Type::v128; + if (size < 4) + return Type::i32; + if (size == 4) + return float_ ? Type::f32 : Type::i32; + if (size == 8) + return float_ ? Type::f64 : Type::i64; + if (size == 16) + return Type::v128; WASM_UNREACHABLE(); } -Type getReachableType(Type a, Type b) { - return a != unreachable ? a : b; -} +Type getReachableType(Type a, Type b) { return a != unreachable ? a : b; } -bool isConcreteType(Type type) { - return type != none && type != unreachable; -} +bool isConcreteType(Type type) { return type != none && type != unreachable; } bool isIntegerType(Type type) { switch (type) { case i32: - case i64: return true; - default: return false; + case i64: + return true; + default: + return false; } } bool isFloatType(Type type) { switch (type) { case f32: - case f64: return true; - default: return false; + case f64: + return true; + default: + return false; } } -bool isVectorType(Type type) { - return type == v128; -} +bool isVectorType(Type type) { return type == v128; } } // namespace wasm diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 1e94ead0a..ea8e92047 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -19,29 +19,30 @@ #include <sstream> #include <unordered_set> -#include "wasm.h" -#include "wasm-printing.h" -#include "wasm-validator.h" -#include "ir/utils.h" #include "ir/branch-utils.h" #include "ir/features.h" #include "ir/module-utils.h" +#include "ir/utils.h" #include "support/colors.h" +#include "wasm-printing.h" +#include "wasm-validator.h" +#include "wasm.h" namespace wasm { // Print anything that can be streamed to an ostream template<typename T, - typename std::enable_if< - !std::is_base_of<Expression, typename std::remove_pointer<T>::type>::value - >::type* = nullptr> + typename std::enable_if<!std::is_base_of< + Expression, + typename std::remove_pointer<T>::type>::value>::type* = nullptr> inline std::ostream& printModuleComponent(T curr, std::ostream& stream) { stream << curr << std::endl; return stream; } // Extra overload for Expressions, to print type info too -inline std::ostream& printModuleComponent(Expression* curr, std::ostream& stream) { +inline std::ostream& printModuleComponent(Expression* curr, + std::ostream& stream) { WasmPrinter::printExpression(curr, stream, false, true) << std::endl; return stream; } @@ -60,14 +61,13 @@ struct ValidationInfo { std::mutex mutex; std::unordered_map<Function*, std::unique_ptr<std::ostringstream>> outputs; - ValidationInfo() { - valid.store(true); - } + ValidationInfo() { valid.store(true); } std::ostringstream& getStream(Function* func) { std::unique_lock<std::mutex> lock(mutex); auto iter = outputs.find(func); - if (iter != outputs.end()) return *(iter->second.get()); + if (iter != outputs.end()) + return *(iter->second.get()); auto& ret = outputs[func] = make_unique<std::ostringstream>(); return *ret.get(); } @@ -78,7 +78,8 @@ struct ValidationInfo { std::ostream& fail(S text, T curr, Function* func) { valid.store(false); auto& stream = getStream(func); - if (quiet) return stream; + if (quiet) + return stream; auto& ret = printFailureHeader(func); ret << text << ", on \n"; return printModuleComponent(curr, ret); @@ -86,7 +87,8 @@ struct ValidationInfo { std::ostream& printFailureHeader(Function* func) { auto& stream = getStream(func); - if (quiet) return stream; + if (quiet) + return stream; Colors::red(stream); if (func) { stream << "[wasm-validator error in function "; @@ -104,7 +106,10 @@ struct ValidationInfo { // checking utilities template<typename T> - bool shouldBeTrue(bool result, T curr, const char* text, Function* func = nullptr) { + bool shouldBeTrue(bool result, + T curr, + const char* text, + Function* func = nullptr) { if (!result) { fail("unexpected false: " + std::string(text), curr, func); return false; @@ -112,7 +117,10 @@ struct ValidationInfo { return result; } template<typename T> - bool shouldBeFalse(bool result, T curr, const char* text, Function* func = nullptr) { + bool shouldBeFalse(bool result, + T curr, + const char* text, + Function* func = nullptr) { if (result) { fail("unexpected true: " + std::string(text), curr, func); return false; @@ -121,7 +129,8 @@ struct ValidationInfo { } template<typename T, typename S> - bool shouldBeEqual(S left, S right, T curr, const char* text, Function* func = nullptr) { + bool shouldBeEqual( + S left, S right, T curr, const char* text, Function* func = nullptr) { if (left != right) { std::ostringstream ss; ss << left << " != " << right << ": " << text; @@ -132,7 +141,8 @@ struct ValidationInfo { } template<typename T, typename S> - bool shouldBeEqualOrFirstIsUnreachable(S left, S right, T curr, const char* text, Function* func = nullptr) { + bool shouldBeEqualOrFirstIsUnreachable( + S left, S right, T curr, const char* text, Function* func = nullptr) { if (left != unreachable && left != right) { std::ostringstream ss; ss << left << " != " << right << ": " << text; @@ -143,7 +153,8 @@ struct ValidationInfo { } template<typename T, typename S> - bool shouldBeUnequal(S left, S right, T curr, const char* text, Function* func = nullptr) { + bool shouldBeUnequal( + S left, S right, T curr, const char* text, Function* func = nullptr) { if (left == right) { std::ostringstream ss; ss << left << " == " << right << ": " << text; @@ -153,17 +164,20 @@ struct ValidationInfo { return true; } - void shouldBeIntOrUnreachable(Type ty, Expression* curr, const char* text, Function* func = nullptr) { + void shouldBeIntOrUnreachable(Type ty, + Expression* curr, + const char* text, + Function* func = nullptr) { switch (ty) { case i32: case i64: case unreachable: { break; } - default: fail(text, curr, func); + default: + fail(text, curr, func); } } - }; struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> { @@ -178,10 +192,7 @@ struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> { FunctionValidator(ValidationInfo* info) : info(*info) {} struct BreakInfo { - enum { - UnsetArity = Index(-1), - PoisonArity = Index(-2) - }; + enum { UnsetArity = Index(-1), PoisonArity = Index(-2) }; Type type; Index arity; @@ -198,7 +209,9 @@ struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> { Type returnType = unreachable; // type used in returns - std::unordered_set<Name> labelNames; // Binaryen IR requires that label names must be unique - IR generators must ensure that + // Binaryen IR requires that label names must be unique - IR generators must + // ensure that + std::unordered_set<Name> labelNames; void noteLabelName(Name name); @@ -207,14 +220,16 @@ public: static void visitPreBlock(FunctionValidator* self, Expression** currp) { auto* curr = (*currp)->cast<Block>(); - if (curr->name.is()) self->breakInfos[curr->name]; + if (curr->name.is()) + self->breakInfos[curr->name]; } void visitBlock(Block* curr); static void visitPreLoop(FunctionValidator* self, Expression** currp) { auto* curr = (*currp)->cast<Loop>(); - if (curr->name.is()) self->breakInfos[curr->name]; + if (curr->name.is()) + self->breakInfos[curr->name]; } void visitLoop(Loop* curr); @@ -225,8 +240,10 @@ public: PostWalker<FunctionValidator>::scan(self, currp); auto* curr = *currp; - if (curr->is<Block>()) self->pushTask(visitPreBlock, currp); - if (curr->is<Loop>()) self->pushTask(visitPreLoop, currp); + if (curr->is<Block>()) + self->pushTask(visitPreBlock, currp); + if (curr->is<Loop>()) + self->pushTask(visitPreLoop, currp); } void noteBreak(Name name, Expression* value, Expression* curr); @@ -264,9 +281,7 @@ public: // helpers private: - std::ostream& getStream() { - return info.getStream(getFunction()); - } + std::ostream& getStream() { return info.getStream(getFunction()); } template<typename T> bool shouldBeTrue(bool result, T curr, const char* text) { @@ -283,8 +298,10 @@ private: } template<typename T, typename S> - bool shouldBeEqualOrFirstIsUnreachable(S left, S right, T curr, const char* text) { - return info.shouldBeEqualOrFirstIsUnreachable(left, right, curr, text, getFunction()); + bool + shouldBeEqualOrFirstIsUnreachable(S left, S right, T curr, const char* text) { + return info.shouldBeEqualOrFirstIsUnreachable( + left, right, curr, text, getFunction()); } template<typename T, typename S> @@ -296,16 +313,20 @@ private: return info.shouldBeIntOrUnreachable(ty, curr, text, getFunction()); } - void validateAlignment(size_t align, Type type, Index bytes, bool isAtomic, - Expression* curr); + void validateAlignment( + size_t align, Type type, Index bytes, bool isAtomic, Expression* curr); void validateMemBytes(uint8_t bytes, Type type, Expression* curr); }; void FunctionValidator::noteLabelName(Name name) { - if (!name.is()) return; + if (!name.is()) + return; bool inserted; std::tie(std::ignore, inserted) = labelNames.insert(name); - shouldBeTrue(inserted, name, "names in Binaryen IR must be unique - IR generators must ensure that"); + shouldBeTrue( + inserted, + name, + "names in Binaryen IR must be unique - IR generators must ensure that"); } void FunctionValidator::visitBlock(Block* curr) { @@ -317,25 +338,46 @@ void FunctionValidator::visitBlock(Block* curr) { auto& info = iter->second; if (info.hasBeenSet()) { if (isConcreteType(curr->type)) { - shouldBeTrue(info.arity != 0, curr, "break arities must be > 0 if block has a value"); + shouldBeTrue(info.arity != 0, + curr, + "break arities must be > 0 if block has a value"); } else { - shouldBeTrue(info.arity == 0, curr, "break arities must be 0 if block has no value"); + shouldBeTrue(info.arity == 0, + curr, + "break arities must be 0 if block has no value"); } - // none or unreachable means a poison value that we should ignore - if consumed, it will error + // none or unreachable means a poison value that we should ignore - if + // consumed, it will error if (isConcreteType(info.type) && isConcreteType(curr->type)) { - shouldBeEqual(curr->type, info.type, curr, "block+breaks must have right type if breaks return a value"); + shouldBeEqual( + curr->type, + info.type, + curr, + "block+breaks must have right type if breaks return a value"); } - if (isConcreteType(curr->type) && info.arity && info.type != unreachable) { - shouldBeEqual(curr->type, info.type, curr, "block+breaks must have right type if breaks have arity"); + if (isConcreteType(curr->type) && info.arity && + info.type != unreachable) { + shouldBeEqual(curr->type, + info.type, + curr, + "block+breaks must have right type if breaks have arity"); } - shouldBeTrue(info.arity != BreakInfo::PoisonArity, curr, "break arities must match"); + shouldBeTrue( + info.arity != BreakInfo::PoisonArity, curr, "break arities must match"); if (curr->list.size() > 0) { auto last = curr->list.back()->type; if (isConcreteType(last) && info.type != unreachable) { - shouldBeEqual(last, info.type, curr, "block+breaks must have right type if block ends with a reachable value"); + shouldBeEqual(last, + info.type, + curr, + "block+breaks must have right type if block ends with " + "a reachable value"); } if (last == none) { - shouldBeTrue(info.arity == Index(0), curr, "if block ends with a none, breaks cannot send a value of any type"); + shouldBeTrue(info.arity == Index(0), + curr, + "if block ends with a none, breaks cannot send a value " + "of any type"); } } } @@ -343,25 +385,44 @@ void FunctionValidator::visitBlock(Block* curr) { } if (curr->list.size() > 1) { for (Index i = 0; i < curr->list.size() - 1; i++) { - if (!shouldBeTrue(!isConcreteType(curr->list[i]->type), curr, "non-final block elements returning a value must be drop()ed (binaryen's autodrop option might help you)") && !info.quiet) { - getStream() << "(on index " << i << ":\n" << curr->list[i] << "\n), type: " << curr->list[i]->type << "\n"; + if (!shouldBeTrue( + !isConcreteType(curr->list[i]->type), + curr, + "non-final block elements returning a value must be drop()ed " + "(binaryen's autodrop option might help you)") && + !info.quiet) { + getStream() << "(on index " << i << ":\n" + << curr->list[i] << "\n), type: " << curr->list[i]->type + << "\n"; } } } if (curr->list.size() > 0) { auto backType = curr->list.back()->type; if (!isConcreteType(curr->type)) { - shouldBeFalse(isConcreteType(backType), curr, "if block is not returning a value, final element should not flow out a value"); + shouldBeFalse(isConcreteType(backType), + curr, + "if block is not returning a value, final element should " + "not flow out a value"); } else { if (isConcreteType(backType)) { - shouldBeEqual(curr->type, backType, curr, "block with value and last element with value must match types"); + shouldBeEqual( + curr->type, + backType, + curr, + "block with value and last element with value must match types"); } else { - shouldBeUnequal(backType, none, curr, "block with value must not have last element that is none"); + shouldBeUnequal( + backType, + none, + curr, + "block with value must not have last element that is none"); } } } if (isConcreteType(curr->type)) { - shouldBeTrue(curr->list.size() > 0, curr, "block with a value must not be empty"); + shouldBeTrue( + curr->list.size() > 0, curr, "block with a value must not be empty"); } } @@ -372,44 +433,84 @@ void FunctionValidator::visitLoop(Loop* curr) { assert(iter != breakInfos.end()); // we set it ourselves auto& info = iter->second; if (info.hasBeenSet()) { - shouldBeEqual(info.arity, Index(0), curr, "breaks to a loop cannot pass a value"); + shouldBeEqual( + info.arity, Index(0), curr, "breaks to a loop cannot pass a value"); } breakInfos.erase(iter); } if (curr->type == none) { - shouldBeFalse(isConcreteType(curr->body->type), curr, "bad body for a loop that has no value"); + shouldBeFalse(isConcreteType(curr->body->type), + curr, + "bad body for a loop that has no value"); } } void FunctionValidator::visitIf(If* curr) { - shouldBeTrue(curr->condition->type == unreachable || curr->condition->type == i32, curr, "if condition must be valid"); + shouldBeTrue(curr->condition->type == unreachable || + curr->condition->type == i32, + curr, + "if condition must be valid"); if (!curr->ifFalse) { - shouldBeFalse(isConcreteType(curr->ifTrue->type), curr, "if without else must not return a value in body"); + shouldBeFalse(isConcreteType(curr->ifTrue->type), + curr, + "if without else must not return a value in body"); if (curr->condition->type != unreachable) { - shouldBeEqual(curr->type, none, curr, "if without else and reachable condition must be none"); + shouldBeEqual(curr->type, + none, + curr, + "if without else and reachable condition must be none"); } } else { if (curr->type != unreachable) { - shouldBeEqualOrFirstIsUnreachable(curr->ifTrue->type, curr->type, curr, "returning if-else's true must have right type"); - shouldBeEqualOrFirstIsUnreachable(curr->ifFalse->type, curr->type, curr, "returning if-else's false must have right type"); + shouldBeEqualOrFirstIsUnreachable( + curr->ifTrue->type, + curr->type, + curr, + "returning if-else's true must have right type"); + shouldBeEqualOrFirstIsUnreachable( + curr->ifFalse->type, + curr->type, + curr, + "returning if-else's false must have right type"); } else { if (curr->condition->type != unreachable) { - shouldBeEqual(curr->ifTrue->type, unreachable, curr, "unreachable if-else must have unreachable true"); - shouldBeEqual(curr->ifFalse->type, unreachable, curr, "unreachable if-else must have unreachable false"); + shouldBeEqual(curr->ifTrue->type, + unreachable, + curr, + "unreachable if-else must have unreachable true"); + shouldBeEqual(curr->ifFalse->type, + unreachable, + curr, + "unreachable if-else must have unreachable false"); } } if (isConcreteType(curr->ifTrue->type)) { - shouldBeEqual(curr->type, curr->ifTrue->type, curr, "if type must match concrete ifTrue"); - shouldBeEqualOrFirstIsUnreachable(curr->ifFalse->type, curr->ifTrue->type, curr, "other arm must match concrete ifTrue"); + shouldBeEqual(curr->type, + curr->ifTrue->type, + curr, + "if type must match concrete ifTrue"); + shouldBeEqualOrFirstIsUnreachable(curr->ifFalse->type, + curr->ifTrue->type, + curr, + "other arm must match concrete ifTrue"); } if (isConcreteType(curr->ifFalse->type)) { - shouldBeEqual(curr->type, curr->ifFalse->type, curr, "if type must match concrete ifFalse"); - shouldBeEqualOrFirstIsUnreachable(curr->ifTrue->type, curr->ifFalse->type, curr, "other arm must match concrete ifFalse"); + shouldBeEqual(curr->type, + curr->ifFalse->type, + curr, + "if type must match concrete ifFalse"); + shouldBeEqualOrFirstIsUnreachable( + curr->ifTrue->type, + curr->ifFalse->type, + curr, + "other arm must match concrete ifFalse"); } } } -void FunctionValidator::noteBreak(Name name, Expression* value, Expression* curr) { +void FunctionValidator::noteBreak(Name name, + Expression* value, + Expression* curr) { Type valueType = none; Index arity = 0; if (value) { @@ -418,7 +519,9 @@ void FunctionValidator::noteBreak(Name name, Expression* value, Expression* curr arity = 1; } auto iter = breakInfos.find(name); - if (!shouldBeTrue(iter != breakInfos.end(), curr, "all break targets must be valid")) return; + if (!shouldBeTrue( + iter != breakInfos.end(), curr, "all break targets must be valid")) + return; auto& info = iter->second; if (!info.hasBeenSet()) { info = BreakInfo(valueType, arity); @@ -438,7 +541,10 @@ void FunctionValidator::noteBreak(Name name, Expression* value, Expression* curr void FunctionValidator::visitBreak(Break* curr) { noteBreak(curr->name, curr->value, curr); if (curr->condition) { - shouldBeTrue(curr->condition->type == unreachable || curr->condition->type == i32, curr, "break condition must be i32"); + shouldBeTrue(curr->condition->type == unreachable || + curr->condition->type == i32, + curr, + "break condition must be i32"); } } @@ -447,260 +553,500 @@ void FunctionValidator::visitSwitch(Switch* curr) { noteBreak(target, curr->value, curr); } noteBreak(curr->default_, curr->value, curr); - shouldBeTrue(curr->condition->type == unreachable || curr->condition->type == i32, curr, "br_table condition must be i32"); + shouldBeTrue(curr->condition->type == unreachable || + curr->condition->type == i32, + curr, + "br_table condition must be i32"); } void FunctionValidator::visitCall(Call* curr) { - if (!info.validateGlobally) return; + if (!info.validateGlobally) + return; auto* target = getModule()->getFunctionOrNull(curr->target); - if (!shouldBeTrue(!!target, curr, "call target must exist")) return; - if (!shouldBeTrue(curr->operands.size() == target->params.size(), curr, "call param number must match")) return; + if (!shouldBeTrue(!!target, curr, "call target must exist")) + return; + if (!shouldBeTrue(curr->operands.size() == target->params.size(), + curr, + "call param number must match")) + return; for (size_t i = 0; i < curr->operands.size(); i++) { - if (!shouldBeEqualOrFirstIsUnreachable(curr->operands[i]->type, target->params[i], curr, "call param types must match") && !info.quiet) { + if (!shouldBeEqualOrFirstIsUnreachable(curr->operands[i]->type, + target->params[i], + curr, + "call param types must match") && + !info.quiet) { getStream() << "(on argument " << i << ")\n"; } } } void FunctionValidator::visitCallIndirect(CallIndirect* curr) { - if (!info.validateGlobally) return; + if (!info.validateGlobally) + return; auto* type = getModule()->getFunctionTypeOrNull(curr->fullType); - if (!shouldBeTrue(!!type, curr, "call_indirect type must exist")) return; - shouldBeEqualOrFirstIsUnreachable(curr->target->type, i32, curr, "indirect call target must be an i32"); - if (!shouldBeTrue(curr->operands.size() == type->params.size(), curr, "call param number must match")) return; + if (!shouldBeTrue(!!type, curr, "call_indirect type must exist")) + return; + shouldBeEqualOrFirstIsUnreachable( + curr->target->type, i32, curr, "indirect call target must be an i32"); + if (!shouldBeTrue(curr->operands.size() == type->params.size(), + curr, + "call param number must match")) + return; for (size_t i = 0; i < curr->operands.size(); i++) { - if (!shouldBeEqualOrFirstIsUnreachable(curr->operands[i]->type, type->params[i], curr, "call param types must match") && !info.quiet) { + if (!shouldBeEqualOrFirstIsUnreachable(curr->operands[i]->type, + type->params[i], + curr, + "call param types must match") && + !info.quiet) { getStream() << "(on argument " << i << ")\n"; } } } void FunctionValidator::visitConst(Const* curr) { - shouldBeTrue(getFeatures(curr->type) <= getModule()->features, curr, + shouldBeTrue(getFeatures(curr->type) <= getModule()->features, + curr, "all used features should be allowed"); } void FunctionValidator::visitGetLocal(GetLocal* curr) { - shouldBeTrue(curr->index < getFunction()->getNumLocals(), curr, "local.get index must be small enough"); - shouldBeTrue(isConcreteType(curr->type), curr, "local.get must have a valid type - check what you provided when you constructed the node"); - shouldBeTrue(curr->type == getFunction()->getLocalType(curr->index), curr, "local.get must have proper type"); + shouldBeTrue(curr->index < getFunction()->getNumLocals(), + curr, + "local.get index must be small enough"); + shouldBeTrue(isConcreteType(curr->type), + curr, + "local.get must have a valid type - check what you provided " + "when you constructed the node"); + shouldBeTrue(curr->type == getFunction()->getLocalType(curr->index), + curr, + "local.get must have proper type"); } void FunctionValidator::visitSetLocal(SetLocal* curr) { - shouldBeTrue(curr->index < getFunction()->getNumLocals(), curr, "local.set index must be small enough"); + shouldBeTrue(curr->index < getFunction()->getNumLocals(), + curr, + "local.set index must be small enough"); if (curr->value->type != unreachable) { if (curr->type != none) { // tee is ok anyhow - shouldBeEqualOrFirstIsUnreachable(curr->value->type, curr->type, curr, "local.set type must be correct"); + shouldBeEqualOrFirstIsUnreachable( + curr->value->type, curr->type, curr, "local.set type must be correct"); } - shouldBeEqual(getFunction()->getLocalType(curr->index), curr->value->type, curr, "local.set type must match function"); + shouldBeEqual(getFunction()->getLocalType(curr->index), + curr->value->type, + curr, + "local.set type must match function"); } } void FunctionValidator::visitGetGlobal(GetGlobal* curr) { - if (!info.validateGlobally) return; - shouldBeTrue(getModule()->getGlobalOrNull(curr->name), curr, "global.get name must be valid"); + if (!info.validateGlobally) + return; + shouldBeTrue(getModule()->getGlobalOrNull(curr->name), + curr, + "global.get name must be valid"); } void FunctionValidator::visitSetGlobal(SetGlobal* curr) { - if (!info.validateGlobally) return; + if (!info.validateGlobally) + return; auto* global = getModule()->getGlobalOrNull(curr->name); - if (shouldBeTrue(global, curr, "global.set name must be valid (and not an import; imports can't be modified)")) { + if (shouldBeTrue(global, + curr, + "global.set name must be valid (and not an import; imports " + "can't be modified)")) { shouldBeTrue(global->mutable_, curr, "global.set global must be mutable"); - shouldBeEqualOrFirstIsUnreachable(curr->value->type, global->type, curr, "global.set value must have right type"); + shouldBeEqualOrFirstIsUnreachable(curr->value->type, + global->type, + curr, + "global.set value must have right type"); } } void FunctionValidator::visitLoad(Load* curr) { - shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory"); + shouldBeTrue( + getModule()->memory.exists, curr, "Memory operations require a memory"); if (curr->isAtomic) { - shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); - shouldBeTrue(curr->type == i32 || curr->type == i64 || curr->type == unreachable, curr, "Atomic load should be i32 or i64"); - } - if (curr->type == v128) shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); - shouldBeFalse(curr->isAtomic && !getModule()->memory.shared, curr, "Atomic operation with non-shared memory"); + shouldBeTrue(getModule()->features.hasAtomics(), + curr, + "Atomic operation (atomics are disabled)"); + shouldBeTrue(curr->type == i32 || curr->type == i64 || + curr->type == unreachable, + curr, + "Atomic load should be i32 or i64"); + } + if (curr->type == v128) + shouldBeTrue(getModule()->features.hasSIMD(), + curr, + "SIMD operation (SIMD is disabled)"); + shouldBeFalse(curr->isAtomic && !getModule()->memory.shared, + curr, + "Atomic operation with non-shared memory"); validateMemBytes(curr->bytes, curr->type, curr); validateAlignment(curr->align, curr->type, curr->bytes, curr->isAtomic, curr); - shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "load pointer type must be i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->ptr->type, i32, curr, "load pointer type must be i32"); if (curr->isAtomic) { shouldBeFalse(curr->signed_, curr, "atomic loads must be unsigned"); - shouldBeIntOrUnreachable(curr->type, curr, "atomic loads must be of integers"); + shouldBeIntOrUnreachable( + curr->type, curr, "atomic loads must be of integers"); } } void FunctionValidator::visitStore(Store* curr) { - shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory"); + shouldBeTrue( + getModule()->memory.exists, curr, "Memory operations require a memory"); if (curr->isAtomic) { - shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); - shouldBeTrue(curr->valueType == i32 || curr->valueType == i64 || curr->valueType == unreachable, curr, "Atomic store should be i32 or i64"); - } - if (curr->valueType == v128) shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); - shouldBeFalse(curr->isAtomic && !getModule()->memory.shared, curr, "Atomic operation with non-shared memory"); + shouldBeTrue(getModule()->features.hasAtomics(), + curr, + "Atomic operation (atomics are disabled)"); + shouldBeTrue(curr->valueType == i32 || curr->valueType == i64 || + curr->valueType == unreachable, + curr, + "Atomic store should be i32 or i64"); + } + if (curr->valueType == v128) + shouldBeTrue(getModule()->features.hasSIMD(), + curr, + "SIMD operation (SIMD is disabled)"); + shouldBeFalse(curr->isAtomic && !getModule()->memory.shared, + curr, + "Atomic operation with non-shared memory"); validateMemBytes(curr->bytes, curr->valueType, curr); - validateAlignment(curr->align, curr->valueType, curr->bytes, curr->isAtomic, curr); - shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "store pointer type must be i32"); - shouldBeUnequal(curr->value->type, none, curr, "store value type must not be none"); - shouldBeEqualOrFirstIsUnreachable(curr->value->type, curr->valueType, curr, "store value type must match"); + validateAlignment( + curr->align, curr->valueType, curr->bytes, curr->isAtomic, curr); + shouldBeEqualOrFirstIsUnreachable( + curr->ptr->type, i32, curr, "store pointer type must be i32"); + shouldBeUnequal( + curr->value->type, none, curr, "store value type must not be none"); + shouldBeEqualOrFirstIsUnreachable( + curr->value->type, curr->valueType, curr, "store value type must match"); if (curr->isAtomic) { - shouldBeIntOrUnreachable(curr->valueType, curr, "atomic stores must be of integers"); + shouldBeIntOrUnreachable( + curr->valueType, curr, "atomic stores must be of integers"); } } void FunctionValidator::visitAtomicRMW(AtomicRMW* curr) { - shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory"); - shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); - shouldBeFalse(!getModule()->memory.shared, curr, "Atomic operation with non-shared memory"); + shouldBeTrue( + getModule()->memory.exists, curr, "Memory operations require a memory"); + shouldBeTrue(getModule()->features.hasAtomics(), + curr, + "Atomic operation (atomics are disabled)"); + shouldBeFalse(!getModule()->memory.shared, + curr, + "Atomic operation with non-shared memory"); validateMemBytes(curr->bytes, curr->type, curr); - shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "AtomicRMW pointer type must be i32"); - shouldBeEqualOrFirstIsUnreachable(curr->type, curr->value->type, curr, "AtomicRMW result type must match operand"); - shouldBeIntOrUnreachable(curr->type, curr, "Atomic operations are only valid on int types"); + shouldBeEqualOrFirstIsUnreachable( + curr->ptr->type, i32, curr, "AtomicRMW pointer type must be i32"); + shouldBeEqualOrFirstIsUnreachable(curr->type, + curr->value->type, + curr, + "AtomicRMW result type must match operand"); + shouldBeIntOrUnreachable( + curr->type, curr, "Atomic operations are only valid on int types"); } void FunctionValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) { - shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory"); - shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); - shouldBeFalse(!getModule()->memory.shared, curr, "Atomic operation with non-shared memory"); + shouldBeTrue( + getModule()->memory.exists, curr, "Memory operations require a memory"); + shouldBeTrue(getModule()->features.hasAtomics(), + curr, + "Atomic operation (atomics are disabled)"); + shouldBeFalse(!getModule()->memory.shared, + curr, + "Atomic operation with non-shared memory"); validateMemBytes(curr->bytes, curr->type, curr); - shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "cmpxchg pointer type must be i32"); - if (curr->expected->type != unreachable && curr->replacement->type != unreachable) { - shouldBeEqual(curr->expected->type, curr->replacement->type, curr, "cmpxchg operand types must match"); - } - shouldBeEqualOrFirstIsUnreachable(curr->type, curr->expected->type, curr, "Cmpxchg result type must match expected"); - shouldBeEqualOrFirstIsUnreachable(curr->type, curr->replacement->type, curr, "Cmpxchg result type must match replacement"); - shouldBeIntOrUnreachable(curr->expected->type, curr, "Atomic operations are only valid on int types"); + shouldBeEqualOrFirstIsUnreachable( + curr->ptr->type, i32, curr, "cmpxchg pointer type must be i32"); + if (curr->expected->type != unreachable && + curr->replacement->type != unreachable) { + shouldBeEqual(curr->expected->type, + curr->replacement->type, + curr, + "cmpxchg operand types must match"); + } + shouldBeEqualOrFirstIsUnreachable(curr->type, + curr->expected->type, + curr, + "Cmpxchg result type must match expected"); + shouldBeEqualOrFirstIsUnreachable( + curr->type, + curr->replacement->type, + curr, + "Cmpxchg result type must match replacement"); + shouldBeIntOrUnreachable(curr->expected->type, + curr, + "Atomic operations are only valid on int types"); } void FunctionValidator::visitAtomicWait(AtomicWait* curr) { - shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory"); - shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); - shouldBeFalse(!getModule()->memory.shared, curr, "Atomic operation with non-shared memory"); - shouldBeEqualOrFirstIsUnreachable(curr->type, i32, curr, "AtomicWait must have type i32"); - shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "AtomicWait pointer type must be i32"); - shouldBeIntOrUnreachable(curr->expected->type, curr, "AtomicWait expected type must be int"); - shouldBeEqualOrFirstIsUnreachable(curr->expected->type, curr->expectedType, curr, "AtomicWait expected type must match operand"); - shouldBeEqualOrFirstIsUnreachable(curr->timeout->type, i64, curr, "AtomicWait timeout type must be i64"); + shouldBeTrue( + getModule()->memory.exists, curr, "Memory operations require a memory"); + shouldBeTrue(getModule()->features.hasAtomics(), + curr, + "Atomic operation (atomics are disabled)"); + shouldBeFalse(!getModule()->memory.shared, + curr, + "Atomic operation with non-shared memory"); + shouldBeEqualOrFirstIsUnreachable( + curr->type, i32, curr, "AtomicWait must have type i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->ptr->type, i32, curr, "AtomicWait pointer type must be i32"); + shouldBeIntOrUnreachable( + curr->expected->type, curr, "AtomicWait expected type must be int"); + shouldBeEqualOrFirstIsUnreachable( + curr->expected->type, + curr->expectedType, + curr, + "AtomicWait expected type must match operand"); + shouldBeEqualOrFirstIsUnreachable( + curr->timeout->type, i64, curr, "AtomicWait timeout type must be i64"); } void FunctionValidator::visitAtomicNotify(AtomicNotify* curr) { - shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory"); - shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); - shouldBeFalse(!getModule()->memory.shared, curr, "Atomic operation with non-shared memory"); - shouldBeEqualOrFirstIsUnreachable(curr->type, i32, curr, "AtomicNotify must have type i32"); - shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "AtomicNotify pointer type must be i32"); - shouldBeEqualOrFirstIsUnreachable(curr->notifyCount->type, i32, curr, "AtomicNotify notifyCount type must be i32"); + shouldBeTrue( + getModule()->memory.exists, curr, "Memory operations require a memory"); + shouldBeTrue(getModule()->features.hasAtomics(), + curr, + "Atomic operation (atomics are disabled)"); + shouldBeFalse(!getModule()->memory.shared, + curr, + "Atomic operation with non-shared memory"); + shouldBeEqualOrFirstIsUnreachable( + curr->type, i32, curr, "AtomicNotify must have type i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->ptr->type, i32, curr, "AtomicNotify pointer type must be i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->notifyCount->type, + i32, + curr, + "AtomicNotify notifyCount type must be i32"); } void FunctionValidator::visitSIMDExtract(SIMDExtract* curr) { - shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); - shouldBeEqualOrFirstIsUnreachable(curr->vec->type, v128, curr, "extract_lane must operate on a v128"); + shouldBeTrue( + getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); + shouldBeEqualOrFirstIsUnreachable( + curr->vec->type, v128, curr, "extract_lane must operate on a v128"); Type lane_t = none; size_t lanes = 0; switch (curr->op) { case ExtractLaneSVecI8x16: - case ExtractLaneUVecI8x16: lane_t = i32; lanes = 16; break; + case ExtractLaneUVecI8x16: + lane_t = i32; + lanes = 16; + break; case ExtractLaneSVecI16x8: - case ExtractLaneUVecI16x8: lane_t = i32; lanes = 8; break; - case ExtractLaneVecI32x4: lane_t = i32; lanes = 4; break; - case ExtractLaneVecI64x2: lane_t = i64; lanes = 2; break; - case ExtractLaneVecF32x4: lane_t = f32; lanes = 4; break; - case ExtractLaneVecF64x2: lane_t = f64; lanes = 2; break; + case ExtractLaneUVecI16x8: + lane_t = i32; + lanes = 8; + break; + case ExtractLaneVecI32x4: + lane_t = i32; + lanes = 4; + break; + case ExtractLaneVecI64x2: + lane_t = i64; + lanes = 2; + break; + case ExtractLaneVecF32x4: + lane_t = f32; + lanes = 4; + break; + case ExtractLaneVecF64x2: + lane_t = f64; + lanes = 2; + break; } - shouldBeEqualOrFirstIsUnreachable(curr->type, lane_t, curr, "extract_lane must have same type as vector lane"); + shouldBeEqualOrFirstIsUnreachable( + curr->type, + lane_t, + curr, + "extract_lane must have same type as vector lane"); shouldBeTrue(curr->index < lanes, curr, "invalid lane index"); } void FunctionValidator::visitSIMDReplace(SIMDReplace* curr) { - shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); - shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "replace_lane must have type v128"); - shouldBeEqualOrFirstIsUnreachable(curr->vec->type, v128, curr, "replace_lane must operate on a v128"); + shouldBeTrue( + getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); + shouldBeEqualOrFirstIsUnreachable( + curr->type, v128, curr, "replace_lane must have type v128"); + shouldBeEqualOrFirstIsUnreachable( + curr->vec->type, v128, curr, "replace_lane must operate on a v128"); Type lane_t = none; size_t lanes = 0; switch (curr->op) { - case ReplaceLaneVecI8x16: lane_t = i32; lanes = 16; break; - case ReplaceLaneVecI16x8: lane_t = i32; lanes = 8; break; - case ReplaceLaneVecI32x4: lane_t = i32; lanes = 4; break; - case ReplaceLaneVecI64x2: lane_t = i64; lanes = 2; break; - case ReplaceLaneVecF32x4: lane_t = f32; lanes = 4; break; - case ReplaceLaneVecF64x2: lane_t = f64; lanes = 2; break; - } - shouldBeEqualOrFirstIsUnreachable(curr->value->type, lane_t, curr, "unexpected value type"); + case ReplaceLaneVecI8x16: + lane_t = i32; + lanes = 16; + break; + case ReplaceLaneVecI16x8: + lane_t = i32; + lanes = 8; + break; + case ReplaceLaneVecI32x4: + lane_t = i32; + lanes = 4; + break; + case ReplaceLaneVecI64x2: + lane_t = i64; + lanes = 2; + break; + case ReplaceLaneVecF32x4: + lane_t = f32; + lanes = 4; + break; + case ReplaceLaneVecF64x2: + lane_t = f64; + lanes = 2; + break; + } + shouldBeEqualOrFirstIsUnreachable( + curr->value->type, lane_t, curr, "unexpected value type"); shouldBeTrue(curr->index < lanes, curr, "invalid lane index"); } void FunctionValidator::visitSIMDShuffle(SIMDShuffle* curr) { - shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); - shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "v128.shuffle must have type v128"); - shouldBeEqualOrFirstIsUnreachable(curr->left->type, v128, curr, "expected operand of type v128"); - shouldBeEqualOrFirstIsUnreachable(curr->right->type, v128, curr, "expected operand of type v128"); + shouldBeTrue( + getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); + shouldBeEqualOrFirstIsUnreachable( + curr->type, v128, curr, "v128.shuffle must have type v128"); + shouldBeEqualOrFirstIsUnreachable( + curr->left->type, v128, curr, "expected operand of type v128"); + shouldBeEqualOrFirstIsUnreachable( + curr->right->type, v128, curr, "expected operand of type v128"); for (uint8_t index : curr->mask) { shouldBeTrue(index < 32, curr, "Invalid lane index in mask"); } } void FunctionValidator::visitSIMDBitselect(SIMDBitselect* curr) { - shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); - shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "v128.bitselect must have type v128"); - shouldBeEqualOrFirstIsUnreachable(curr->left->type, v128, curr, "expected operand of type v128"); - shouldBeEqualOrFirstIsUnreachable(curr->right->type, v128, curr, "expected operand of type v128"); - shouldBeEqualOrFirstIsUnreachable(curr->cond->type, v128, curr, "expected operand of type v128"); + shouldBeTrue( + getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); + shouldBeEqualOrFirstIsUnreachable( + curr->type, v128, curr, "v128.bitselect must have type v128"); + shouldBeEqualOrFirstIsUnreachable( + curr->left->type, v128, curr, "expected operand of type v128"); + shouldBeEqualOrFirstIsUnreachable( + curr->right->type, v128, curr, "expected operand of type v128"); + shouldBeEqualOrFirstIsUnreachable( + curr->cond->type, v128, curr, "expected operand of type v128"); } void FunctionValidator::visitSIMDShift(SIMDShift* curr) { - shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); - shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "vector shift must have type v128"); - shouldBeEqualOrFirstIsUnreachable(curr->vec->type, v128, curr, "expected operand of type v128"); - shouldBeEqualOrFirstIsUnreachable(curr->shift->type, i32, curr, "expected shift amount to have type i32"); + shouldBeTrue( + getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); + shouldBeEqualOrFirstIsUnreachable( + curr->type, v128, curr, "vector shift must have type v128"); + shouldBeEqualOrFirstIsUnreachable( + curr->vec->type, v128, curr, "expected operand of type v128"); + shouldBeEqualOrFirstIsUnreachable( + curr->shift->type, i32, curr, "expected shift amount to have type i32"); } void FunctionValidator::visitMemoryInit(MemoryInit* curr) { - shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory"); - shouldBeTrue(getModule()->features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)"); - shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "memory.init must have type none"); - shouldBeEqualOrFirstIsUnreachable(curr->dest->type, i32, curr, "memory.init dest must be an i32"); - shouldBeEqualOrFirstIsUnreachable(curr->offset->type, i32, curr, "memory.init offset must be an i32"); - shouldBeEqualOrFirstIsUnreachable(curr->size->type, i32, curr, "memory.init size must be an i32"); - shouldBeTrue(curr->segment < getModule()->memory.segments.size(), curr, "memory.init segment index out of bounds"); + shouldBeTrue( + getModule()->memory.exists, curr, "Memory operations require a memory"); + shouldBeTrue(getModule()->features.hasBulkMemory(), + curr, + "Bulk memory operation (bulk memory is disabled)"); + shouldBeEqualOrFirstIsUnreachable( + curr->type, none, curr, "memory.init must have type none"); + shouldBeEqualOrFirstIsUnreachable( + curr->dest->type, i32, curr, "memory.init dest must be an i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->offset->type, i32, curr, "memory.init offset must be an i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->size->type, i32, curr, "memory.init size must be an i32"); + shouldBeTrue(curr->segment < getModule()->memory.segments.size(), + curr, + "memory.init segment index out of bounds"); } void FunctionValidator::visitDataDrop(DataDrop* curr) { - shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory"); - shouldBeTrue(getModule()->features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)"); - shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "data.drop must have type none"); - shouldBeTrue(curr->segment < getModule()->memory.segments.size(), curr, "data.drop segment index out of bounds"); + shouldBeTrue( + getModule()->memory.exists, curr, "Memory operations require a memory"); + shouldBeTrue(getModule()->features.hasBulkMemory(), + curr, + "Bulk memory operation (bulk memory is disabled)"); + shouldBeEqualOrFirstIsUnreachable( + curr->type, none, curr, "data.drop must have type none"); + shouldBeTrue(curr->segment < getModule()->memory.segments.size(), + curr, + "data.drop segment index out of bounds"); } void FunctionValidator::visitMemoryCopy(MemoryCopy* curr) { - shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory"); - shouldBeTrue(getModule()->features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)"); - shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "memory.copy must have type none"); - shouldBeEqualOrFirstIsUnreachable(curr->dest->type, i32, curr, "memory.copy dest must be an i32"); - shouldBeEqualOrFirstIsUnreachable(curr->source->type, i32, curr, "memory.copy source must be an i32"); - shouldBeEqualOrFirstIsUnreachable(curr->size->type, i32, curr, "memory.copy size must be an i32"); + shouldBeTrue( + getModule()->memory.exists, curr, "Memory operations require a memory"); + shouldBeTrue(getModule()->features.hasBulkMemory(), + curr, + "Bulk memory operation (bulk memory is disabled)"); + shouldBeEqualOrFirstIsUnreachable( + curr->type, none, curr, "memory.copy must have type none"); + shouldBeEqualOrFirstIsUnreachable( + curr->dest->type, i32, curr, "memory.copy dest must be an i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->source->type, i32, curr, "memory.copy source must be an i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->size->type, i32, curr, "memory.copy size must be an i32"); } void FunctionValidator::visitMemoryFill(MemoryFill* curr) { - shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory"); - shouldBeTrue(getModule()->features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)"); - shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "memory.fill must have type none"); - shouldBeEqualOrFirstIsUnreachable(curr->dest->type, i32, curr, "memory.fill dest must be an i32"); - shouldBeEqualOrFirstIsUnreachable(curr->value->type, i32, curr, "memory.fill value must be an i32"); - shouldBeEqualOrFirstIsUnreachable(curr->size->type, i32, curr, "memory.fill size must be an i32"); + shouldBeTrue( + getModule()->memory.exists, curr, "Memory operations require a memory"); + shouldBeTrue(getModule()->features.hasBulkMemory(), + curr, + "Bulk memory operation (bulk memory is disabled)"); + shouldBeEqualOrFirstIsUnreachable( + curr->type, none, curr, "memory.fill must have type none"); + shouldBeEqualOrFirstIsUnreachable( + curr->dest->type, i32, curr, "memory.fill dest must be an i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->value->type, i32, curr, "memory.fill value must be an i32"); + shouldBeEqualOrFirstIsUnreachable( + curr->size->type, i32, curr, "memory.fill size must be an i32"); } -void FunctionValidator::validateMemBytes(uint8_t bytes, Type type, Expression* curr) { +void FunctionValidator::validateMemBytes(uint8_t bytes, + Type type, + Expression* curr) { switch (type) { - case i32: shouldBeTrue(bytes == 1 || bytes == 2 || bytes == 4, curr, "expected i32 operation to touch 1, 2, or 4 bytes"); break; - case i64: shouldBeTrue(bytes == 1 || bytes == 2 || bytes == 4 || bytes == 8, curr, "expected i64 operation to touch 1, 2, 4, or 8 bytes"); break; - case f32: shouldBeEqual(bytes, uint8_t(4), curr, "expected f32 operation to touch 4 bytes"); break; - case f64: shouldBeEqual(bytes, uint8_t(8), curr, "expected f64 operation to touch 8 bytes"); break; - case v128: shouldBeEqual(bytes, uint8_t(16), curr, "expected v128 operation to touch 16 bytes"); break; - case none: WASM_UNREACHABLE(); - case unreachable: break; + case i32: + shouldBeTrue(bytes == 1 || bytes == 2 || bytes == 4, + curr, + "expected i32 operation to touch 1, 2, or 4 bytes"); + break; + case i64: + shouldBeTrue(bytes == 1 || bytes == 2 || bytes == 4 || bytes == 8, + curr, + "expected i64 operation to touch 1, 2, 4, or 8 bytes"); + break; + case f32: + shouldBeEqual( + bytes, uint8_t(4), curr, "expected f32 operation to touch 4 bytes"); + break; + case f64: + shouldBeEqual( + bytes, uint8_t(8), curr, "expected f64 operation to touch 8 bytes"); + break; + case v128: + shouldBeEqual( + bytes, uint8_t(16), curr, "expected v128 operation to touch 16 bytes"); + break; + case none: + WASM_UNREACHABLE(); + case unreachable: + break; } } void FunctionValidator::visitBinary(Binary* curr) { if (curr->left->type != unreachable && curr->right->type != unreachable) { - shouldBeEqual(curr->left->type, curr->right->type, curr, "binary child types must be equal"); + shouldBeEqual(curr->left->type, + curr->right->type, + curr, + "binary child types must be equal"); } switch (curr->op) { case AddInt32: @@ -866,30 +1212,41 @@ void FunctionValidator::visitBinary(Binary* curr) { case MulVecF64x2: case DivVecF64x2: case MinVecF64x2: - case MaxVecF64x2: { - shouldBeEqualOrFirstIsUnreachable(curr->left->type, v128, curr, "v128 op"); - shouldBeEqualOrFirstIsUnreachable(curr->right->type, v128, curr, "v128 op"); + case MaxVecF64x2: { + shouldBeEqualOrFirstIsUnreachable( + curr->left->type, v128, curr, "v128 op"); + shouldBeEqualOrFirstIsUnreachable( + curr->right->type, v128, curr, "v128 op"); break; } - case InvalidBinary: WASM_UNREACHABLE(); + case InvalidBinary: + WASM_UNREACHABLE(); } - shouldBeTrue(Features::get(curr->op) <= getModule()->features, curr, "all used features should be allowed"); + shouldBeTrue(Features::get(curr->op) <= getModule()->features, + curr, + "all used features should be allowed"); } void FunctionValidator::visitUnary(Unary* curr) { - shouldBeUnequal(curr->value->type, none, curr, "unaries must not receive a none as their input"); - if (curr->value->type == unreachable) return; // nothing to check + shouldBeUnequal(curr->value->type, + none, + curr, + "unaries must not receive a none as their input"); + if (curr->value->type == unreachable) + return; // nothing to check switch (curr->op) { case ClzInt32: case CtzInt32: case PopcntInt32: { - shouldBeEqual(curr->value->type, i32, curr, "i32 unary value type must be correct"); + shouldBeEqual( + curr->value->type, i32, curr, "i32 unary value type must be correct"); break; } case ClzInt64: case CtzInt64: case PopcntInt64: { - shouldBeEqual(curr->value->type, i64, curr, "i64 unary value type must be correct"); + shouldBeEqual( + curr->value->type, i64, curr, "i64 unary value type must be correct"); break; } case NegFloat32: @@ -899,7 +1256,8 @@ void FunctionValidator::visitUnary(Unary* curr) { case TruncFloat32: case NearestFloat32: case SqrtFloat32: { - shouldBeEqual(curr->value->type, f32, curr, "f32 unary value type must be correct"); + shouldBeEqual( + curr->value->type, f32, curr, "f32 unary value type must be correct"); break; } case NegFloat64: @@ -909,7 +1267,8 @@ void FunctionValidator::visitUnary(Unary* curr) { case TruncFloat64: case NearestFloat64: case SqrtFloat64: { - shouldBeEqual(curr->value->type, f64, curr, "f64 unary value type must be correct"); + shouldBeEqual( + curr->value->type, f64, curr, "f64 unary value type must be correct"); break; } case EqZInt32: { @@ -924,13 +1283,15 @@ void FunctionValidator::visitUnary(Unary* curr) { case ExtendUInt32: case ExtendS8Int32: case ExtendS16Int32: { - shouldBeEqual(curr->value->type, i32, curr, "extend type must be correct"); + shouldBeEqual( + curr->value->type, i32, curr, "extend type must be correct"); break; } case ExtendS8Int64: case ExtendS16Int64: case ExtendS32Int64: { - shouldBeEqual(curr->value->type, i64, curr, "extend type must be correct"); + shouldBeEqual( + curr->value->type, i64, curr, "extend type must be correct"); break; } case WrapInt64: { @@ -966,41 +1327,49 @@ void FunctionValidator::visitUnary(Unary* curr) { break; } case ReinterpretFloat32: { - shouldBeEqual(curr->value->type, f32, curr, "reinterpret/f32 type must be correct"); + shouldBeEqual( + curr->value->type, f32, curr, "reinterpret/f32 type must be correct"); break; } case ReinterpretFloat64: { - shouldBeEqual(curr->value->type, f64, curr, "reinterpret/f64 type must be correct"); + shouldBeEqual( + curr->value->type, f64, curr, "reinterpret/f64 type must be correct"); break; } case ConvertUInt32ToFloat32: case ConvertUInt32ToFloat64: case ConvertSInt32ToFloat32: case ConvertSInt32ToFloat64: { - shouldBeEqual(curr->value->type, i32, curr, "convert type must be correct"); + shouldBeEqual( + curr->value->type, i32, curr, "convert type must be correct"); break; } case ConvertUInt64ToFloat32: case ConvertUInt64ToFloat64: case ConvertSInt64ToFloat32: case ConvertSInt64ToFloat64: { - shouldBeEqual(curr->value->type, i64, curr, "convert type must be correct"); + shouldBeEqual( + curr->value->type, i64, curr, "convert type must be correct"); break; } case PromoteFloat32: { - shouldBeEqual(curr->value->type, f32, curr, "promote type must be correct"); + shouldBeEqual( + curr->value->type, f32, curr, "promote type must be correct"); break; } case DemoteFloat64: { - shouldBeEqual(curr->value->type, f64, curr, "demote type must be correct"); + shouldBeEqual( + curr->value->type, f64, curr, "demote type must be correct"); break; } case ReinterpretInt32: { - shouldBeEqual(curr->value->type, i32, curr, "reinterpret/i32 type must be correct"); + shouldBeEqual( + curr->value->type, i32, curr, "reinterpret/i32 type must be correct"); break; } case ReinterpretInt64: { - shouldBeEqual(curr->value->type, i64, curr, "reinterpret/i64 type must be correct"); + shouldBeEqual( + curr->value->type, i64, curr, "reinterpret/i64 type must be correct"); break; } case SplatVecI8x16: @@ -1051,25 +1420,39 @@ void FunctionValidator::visitUnary(Unary* curr) { case AllTrueVecI32x4: case AnyTrueVecI64x2: case AllTrueVecI64x2: - shouldBeEqual(curr->type, i32, curr, "expected boolean reduction to have i32 type"); + shouldBeEqual( + curr->type, i32, curr, "expected boolean reduction to have i32 type"); shouldBeEqual(curr->value->type, v128, curr, "expected v128 operand"); break; - case InvalidUnary: WASM_UNREACHABLE(); + case InvalidUnary: + WASM_UNREACHABLE(); } - shouldBeTrue(Features::get(curr->op) <= getModule()->features, curr, "all used features should be allowed"); + shouldBeTrue(Features::get(curr->op) <= getModule()->features, + curr, + "all used features should be allowed"); } void FunctionValidator::visitSelect(Select* curr) { shouldBeUnequal(curr->ifTrue->type, none, curr, "select left must be valid"); - shouldBeUnequal(curr->ifFalse->type, none, curr, "select right must be valid"); - shouldBeTrue(curr->condition->type == unreachable || curr->condition->type == i32, curr, "select condition must be valid"); + shouldBeUnequal( + curr->ifFalse->type, none, curr, "select right must be valid"); + shouldBeTrue(curr->condition->type == unreachable || + curr->condition->type == i32, + curr, + "select condition must be valid"); if (curr->ifTrue->type != unreachable && curr->ifFalse->type != unreachable) { - shouldBeEqual(curr->ifTrue->type, curr->ifFalse->type, curr, "select sides must be equal"); + shouldBeEqual(curr->ifTrue->type, + curr->ifFalse->type, + curr, + "select sides must be equal"); } } void FunctionValidator::visitDrop(Drop* curr) { - shouldBeTrue(isConcreteType(curr->value->type) || curr->value->type == unreachable, curr, "can only drop a valid value"); + shouldBeTrue(isConcreteType(curr->value->type) || + curr->value->type == unreachable, + curr, + "can only drop a valid value"); } void FunctionValidator::visitReturn(Return* curr) { @@ -1077,7 +1460,8 @@ void FunctionValidator::visitReturn(Return* curr) { if (returnType == unreachable) { returnType = curr->value->type; } else if (curr->value->type != unreachable) { - shouldBeEqual(curr->value->type, returnType, curr, "function results must match"); + shouldBeEqual( + curr->value->type, returnType, curr, "function results must match"); } } else { returnType = none; @@ -1087,11 +1471,18 @@ void FunctionValidator::visitReturn(Return* curr) { void FunctionValidator::visitHost(Host* curr) { switch (curr->op) { case GrowMemory: { - shouldBeEqual(curr->operands.size(), size_t(1), curr, "grow_memory must have 1 operand"); - shouldBeEqualOrFirstIsUnreachable(curr->operands[0]->type, i32, curr, "grow_memory must have i32 operand"); + shouldBeEqual(curr->operands.size(), + size_t(1), + curr, + "grow_memory must have 1 operand"); + shouldBeEqualOrFirstIsUnreachable(curr->operands[0]->type, + i32, + curr, + "grow_memory must have i32 operand"); break; } - case CurrentMemory: break; + case CurrentMemory: + break; } } @@ -1105,27 +1496,42 @@ void FunctionValidator::visitFunction(Function* curr) { typeFeatures |= getFeatures(type); shouldBeTrue(isConcreteType(type), curr, "vars must be concretely typed"); } - shouldBeTrue(typeFeatures <= getModule()->features, curr, + shouldBeTrue(typeFeatures <= getModule()->features, + curr, "all used types should be allowed"); // if function has no result, it is ignored // if body is unreachable, it might be e.g. a return if (curr->body->type != unreachable) { - shouldBeEqual(curr->result, curr->body->type, curr->body, "function body type must match, if function returns"); + shouldBeEqual(curr->result, + curr->body->type, + curr->body, + "function body type must match, if function returns"); } if (returnType != unreachable) { - shouldBeEqual(curr->result, returnType, curr->body, "function result must match, if function has returns"); + shouldBeEqual(curr->result, + returnType, + curr->body, + "function result must match, if function has returns"); } - shouldBeTrue(breakInfos.empty(), curr->body, "all named break targets must exist"); + shouldBeTrue( + breakInfos.empty(), curr->body, "all named break targets must exist"); returnType = unreachable; labelNames.clear(); - // if function has a named type, it must match up with the function's params and result + // if function has a named type, it must match up with the function's params + // and result if (info.validateGlobally && curr->type.is()) { auto* ft = getModule()->getFunctionType(curr->type); - shouldBeTrue(ft->params == curr->params, curr->name, "function params must match its declared type"); - shouldBeTrue(ft->result == curr->result, curr->name, "function result must match its declared type"); + shouldBeTrue(ft->params == curr->params, + curr->name, + "function params must match its declared type"); + shouldBeTrue(ft->result == curr->result, + curr->name, + "function result must match its declared type"); } if (curr->imported()) { - shouldBeTrue(curr->type.is(), curr->name, "imported functions must have a function type"); + shouldBeTrue(curr->type.is(), + curr->name, + "imported functions must have a function type"); } // validate optional local names std::set<Name> seen; @@ -1136,9 +1542,11 @@ void FunctionValidator::visitFunction(Function* curr) { } static bool checkOffset(Expression* curr, Address add, Address max) { - if (curr->is<GetGlobal>()) return true; + if (curr->is<GetGlobal>()) + return true; auto* c = curr->dynCast<Const>(); - if (!c) return false; + if (!c) + return false; uint64_t raw = c->value.getInteger(); if (raw > std::numeric_limits<Address::address_t>::max()) { return false; @@ -1150,10 +1558,13 @@ static bool checkOffset(Expression* curr, Address add, Address max) { return offset + add <= max; } -void FunctionValidator::validateAlignment(size_t align, Type type, Index bytes, - bool isAtomic, Expression* curr) { +void FunctionValidator::validateAlignment( + size_t align, Type type, Index bytes, bool isAtomic, Expression* curr) { if (isAtomic) { - shouldBeEqual(align, (size_t)bytes, curr, "atomic accesses must have natural alignment"); + shouldBeEqual(align, + (size_t)bytes, + curr, + "atomic accesses must have natural alignment"); return; } switch (align) { @@ -1161,8 +1572,9 @@ void FunctionValidator::validateAlignment(size_t align, Type type, Index bytes, case 2: case 4: case 8: - case 16: break; - default:{ + case 16: + break; + default: { info.fail("bad alignment: " + std::to_string(align), curr, getFunction()); break; } @@ -1180,13 +1592,17 @@ void FunctionValidator::validateAlignment(size_t align, Type type, Index bytes, break; } case v128: - case unreachable: break; - case none: WASM_UNREACHABLE(); + case unreachable: + break; + case none: + WASM_UNREACHABLE(); } } static void validateBinaryenIR(Module& wasm, ValidationInfo& info) { - struct BinaryenIRValidator : public PostWalker<BinaryenIRValidator, UnifiedExpressionVisitor<BinaryenIRValidator>> { + struct BinaryenIRValidator + : public PostWalker<BinaryenIRValidator, + UnifiedExpressionVisitor<BinaryenIRValidator>> { ValidationInfo& info; std::unordered_set<Expression*> seen; @@ -1195,7 +1611,8 @@ static void validateBinaryenIR(Module& wasm, ValidationInfo& info) { void visitExpression(Expression* curr) { auto scope = getFunction() ? getFunction()->name : Name("(global scope)"); - // check if a node type is 'stale', i.e., we forgot to finalize() the node. + // check if a node type is 'stale', i.e., we forgot to finalize() the + // node. auto oldType = curr->type; ReFinalizeNode().visit(curr); auto newType = curr->type; @@ -1205,21 +1622,25 @@ static void validateBinaryenIR(Module& wasm, ValidationInfo& info) { // // (drop (block (result i32) (unreachable))) // - // The block has an added type, not derived from the ast itself, so it is - // ok for it to be either i32 or unreachable. + // The block has an added type, not derived from the ast itself, so it + // is ok for it to be either i32 or unreachable. if (!(isConcreteType(oldType) && newType == unreachable)) { std::ostringstream ss; - ss << "stale type found in " << scope << " on " << curr << "\n(marked as " << printType(oldType) << ", should be " << printType(newType) << ")\n"; + ss << "stale type found in " << scope << " on " << curr + << "\n(marked as " << printType(oldType) << ", should be " + << printType(newType) << ")\n"; info.fail(ss.str(), curr, getFunction()); } curr->type = oldType; } - // check if a node is a duplicate - expressions must not be seen more than once + // check if a node is a duplicate - expressions must not be seen more than + // once bool inserted; std::tie(std::ignore, inserted) = seen.insert(curr); if (!inserted) { std::ostringstream ss; - ss << "expression seen more than once in the tree in " << scope << " on " << curr << '\n'; + ss << "expression seen more than once in the tree in " << scope + << " on " << curr << '\n'; info.fail(ss.str(), curr, getFunction()); } } @@ -1234,15 +1655,22 @@ static void validateImports(Module& module, ValidationInfo& info) { ModuleUtils::iterImportedFunctions(module, [&](Function* curr) { if (info.validateWeb) { auto* functionType = module.getFunctionType(curr->type); - info.shouldBeUnequal(functionType->result, i64, curr->name, "Imported function must not have i64 return type"); + info.shouldBeUnequal(functionType->result, + i64, + curr->name, + "Imported function must not have i64 return type"); for (Type param : functionType->params) { - info.shouldBeUnequal(param, i64, curr->name, "Imported function must not have i64 parameters"); + info.shouldBeUnequal(param, + i64, + curr->name, + "Imported function must not have i64 parameters"); } } }); if (!module.features.hasMutableGlobals()) { ModuleUtils::iterImportedGlobals(module, [&](Global* curr) { - info.shouldBeFalse(curr->mutable_, curr->name, "Imported global cannot be mutable"); + info.shouldBeFalse( + curr->mutable_, curr->name, "Imported global cannot be mutable"); }); } } @@ -1252,14 +1680,23 @@ static void validateExports(Module& module, ValidationInfo& info) { if (curr->kind == ExternalKind::Function) { if (info.validateWeb) { Function* f = module.getFunction(curr->value); - info.shouldBeUnequal(f->result, i64, f->name, "Exported function must not have i64 return type"); + info.shouldBeUnequal(f->result, + i64, + f->name, + "Exported function must not have i64 return type"); for (auto param : f->params) { - info.shouldBeUnequal(param, i64, f->name, "Exported function must not have i64 parameters"); + info.shouldBeUnequal( + param, + i64, + f->name, + "Exported function must not have i64 parameters"); } } - } else if (curr->kind == ExternalKind::Global && !module.features.hasMutableGlobals()) { + } else if (curr->kind == ExternalKind::Global && + !module.features.hasMutableGlobals()) { if (Global* g = module.getGlobalOrNull(curr->value)) { - info.shouldBeFalse(g->mutable_, g->name, "Exported global cannot be mutable"); + info.shouldBeFalse( + g->mutable_, g->name, "Exported global cannot be mutable"); } } } @@ -1267,29 +1704,47 @@ static void validateExports(Module& module, ValidationInfo& info) { for (auto& exp : module.exports) { Name name = exp->value; if (exp->kind == ExternalKind::Function) { - info.shouldBeTrue(module.getFunctionOrNull(name), name, "module function exports must be found"); + info.shouldBeTrue(module.getFunctionOrNull(name), + name, + "module function exports must be found"); } else if (exp->kind == ExternalKind::Global) { - info.shouldBeTrue(module.getGlobalOrNull(name), name, "module global exports must be found"); + info.shouldBeTrue(module.getGlobalOrNull(name), + name, + "module global exports must be found"); } else if (exp->kind == ExternalKind::Table) { - info.shouldBeTrue(name == Name("0") || name == module.table.name, name, "module table exports must be found"); + info.shouldBeTrue(name == Name("0") || name == module.table.name, + name, + "module table exports must be found"); } else if (exp->kind == ExternalKind::Memory) { - info.shouldBeTrue(name == Name("0") || name == module.memory.name, name, "module memory exports must be found"); + info.shouldBeTrue(name == Name("0") || name == module.memory.name, + name, + "module memory exports must be found"); } else { WASM_UNREACHABLE(); } Name exportName = exp->name; - info.shouldBeFalse(exportNames.count(exportName) > 0, exportName, "module exports must be unique"); + info.shouldBeFalse(exportNames.count(exportName) > 0, + exportName, + "module exports must be unique"); exportNames.insert(exportName); } } static void validateGlobals(Module& module, ValidationInfo& info) { ModuleUtils::iterDefinedGlobals(module, [&](Global* curr) { - info.shouldBeTrue(getFeatures(curr->type) <= module.features, curr->name, + info.shouldBeTrue(getFeatures(curr->type) <= module.features, + curr->name, "all used types should be allowed"); - info.shouldBeTrue(curr->init != nullptr, curr->name, "global init must be non-null"); - info.shouldBeTrue(curr->init->is<Const>() || curr->init->is<GetGlobal>(), curr->name, "global init must be valid"); - if (!info.shouldBeEqual(curr->type, curr->init->type, curr->init, "global init must have correct type") && !info.quiet) { + info.shouldBeTrue( + curr->init != nullptr, curr->name, "global init must be non-null"); + info.shouldBeTrue(curr->init->is<Const>() || curr->init->is<GetGlobal>(), + curr->name, + "global init must be valid"); + if (!info.shouldBeEqual(curr->type, + curr->init->type, + curr->init, + "global init must have correct type") && + !info.quiet) { info.getStream(nullptr) << "(on global " << curr->name << ")\n"; } }); @@ -1297,30 +1752,57 @@ static void validateGlobals(Module& module, ValidationInfo& info) { static void validateMemory(Module& module, ValidationInfo& info) { auto& curr = module.memory; - info.shouldBeFalse(curr.initial > curr.max, "memory", "memory max >= initial"); - info.shouldBeTrue(curr.initial <= Memory::kMaxSize, "memory", "initial memory must be <= 4GB"); - info.shouldBeTrue(!curr.hasMax() || curr.max <= Memory::kMaxSize, "memory", "max memory must be <= 4GB, or unlimited"); - info.shouldBeTrue(!curr.shared || curr.hasMax(), "memory", "shared memory must have max size"); - if (curr.shared) info.shouldBeTrue(module.features.hasAtomics(), "memory", "memory is shared, but atomics are disabled"); + info.shouldBeFalse( + curr.initial > curr.max, "memory", "memory max >= initial"); + info.shouldBeTrue(curr.initial <= Memory::kMaxSize, + "memory", + "initial memory must be <= 4GB"); + info.shouldBeTrue(!curr.hasMax() || curr.max <= Memory::kMaxSize, + "memory", + "max memory must be <= 4GB, or unlimited"); + info.shouldBeTrue(!curr.shared || curr.hasMax(), + "memory", + "shared memory must have max size"); + if (curr.shared) + info.shouldBeTrue(module.features.hasAtomics(), + "memory", + "memory is shared, but atomics are disabled"); for (auto& segment : curr.segments) { Index size = segment.data.size(); if (segment.isPassive) { - info.shouldBeTrue(module.features.hasBulkMemory(), segment.offset, "nonzero segment flags (bulk memory is disabled)"); - info.shouldBeEqual(segment.offset, (Expression*)nullptr, segment.offset, "passive segment should not have an offset"); + info.shouldBeTrue(module.features.hasBulkMemory(), + segment.offset, + "nonzero segment flags (bulk memory is disabled)"); + info.shouldBeEqual(segment.offset, + (Expression*)nullptr, + segment.offset, + "passive segment should not have an offset"); } else { - if (!info.shouldBeEqual(segment.offset->type, i32, segment.offset, "segment offset should be i32")) continue; - info.shouldBeTrue(checkOffset(segment.offset, segment.data.size(), curr.initial * Memory::kPageSize), segment.offset, "segment offset should be reasonable"); + if (!info.shouldBeEqual(segment.offset->type, + i32, + segment.offset, + "segment offset should be i32")) + continue; + info.shouldBeTrue(checkOffset(segment.offset, + segment.data.size(), + curr.initial * Memory::kPageSize), + segment.offset, + "segment offset should be reasonable"); if (segment.offset->is<Const>()) { Index start = segment.offset->cast<Const>()->value.geti32(); Index end = start + size; - info.shouldBeTrue(end <= curr.initial * Memory::kPageSize, segment.data.size(), "segment size should fit in memory (end)"); + info.shouldBeTrue(end <= curr.initial * Memory::kPageSize, + segment.data.size(), + "segment size should fit in memory (end)"); } } // If the memory is imported we don't actually know its initial size. // Specifically wasm dll's import a zero sized memory which is perfectly // valid. if (!curr.imported()) { - info.shouldBeTrue(size <= curr.initial * Memory::kPageSize, segment.data.size(), "segment size should fit in memory (initial)"); + info.shouldBeTrue(size <= curr.initial * Memory::kPageSize, + segment.data.size(), + "segment size should fit in memory (initial)"); } } } @@ -1328,10 +1810,18 @@ static void validateMemory(Module& module, ValidationInfo& info) { static void validateTable(Module& module, ValidationInfo& info) { auto& curr = module.table; for (auto& segment : curr.segments) { - info.shouldBeEqual(segment.offset->type, i32, segment.offset, "segment offset should be i32"); - info.shouldBeTrue(checkOffset(segment.offset, segment.data.size(), module.table.initial * Table::kPageSize), segment.offset, "segment offset should be reasonable"); + info.shouldBeEqual(segment.offset->type, + i32, + segment.offset, + "segment offset should be i32"); + info.shouldBeTrue(checkOffset(segment.offset, + segment.data.size(), + module.table.initial * Table::kPageSize), + segment.offset, + "segment offset should be reasonable"); for (auto name : segment.data) { - info.shouldBeTrue(module.getFunctionOrNull(name), name, "segment name should be valid"); + info.shouldBeTrue( + module.getFunctionOrNull(name), name, "segment name should be valid"); } } } @@ -1340,16 +1830,19 @@ static void validateModule(Module& module, ValidationInfo& info) { // start if (module.start.is()) { auto func = module.getFunctionOrNull(module.start); - if (info.shouldBeTrue(func != nullptr, module.start, "start must be found")) { - info.shouldBeTrue(func->params.size() == 0, module.start, "start must have 0 params"); - info.shouldBeTrue(func->result == none, module.start, "start must not return a value"); + if (info.shouldBeTrue( + func != nullptr, module.start, "start must be found")) { + info.shouldBeTrue( + func->params.size() == 0, module.start, "start must have 0 params"); + info.shouldBeTrue( + func->result == none, module.start, "start must not return a value"); } } } -// TODO: If we want the validator to be part of libwasm rather than libpasses, then -// Using PassRunner::getPassDebug causes a circular dependence. We should fix that, -// perhaps by moving some of the pass infrastructure into libsupport. +// TODO: If we want the validator to be part of libwasm rather than libpasses, +// then Using PassRunner::getPassDebug causes a circular dependence. We should +// fix that, perhaps by moving some of the pass infrastructure into libsupport. bool WasmValidator::validate(Module& module, Flags flags) { ValidationInfo info; info.validateWeb = (flags & Web) != 0; diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 519814cb7..22cce6e80 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -15,15 +15,15 @@ */ #include "wasm.h" -#include "wasm-traversal.h" #include "ir/branch-utils.h" +#include "wasm-traversal.h" namespace wasm { // shared constants -Name WASM("wasm"), - RETURN_FLOW("*return:)*"); +Name WASM("wasm"); +Name RETURN_FLOW("*return:)*"); namespace BinaryConsts { namespace UserSections { @@ -39,90 +39,127 @@ const char* ExceptionHandlingFeature = "exception-handling"; const char* TruncSatFeature = "nontrapping-fptoint"; const char* SignExtFeature = "sign-ext"; const char* SIMD128Feature = "simd128"; -} -} - -Name GROW_WASM_MEMORY("__growWasmMemory"), - WASM_CALL_CTORS("__wasm_call_ctors"), - MEMORY_BASE("__memory_base"), - TABLE_BASE("__table_base"), - GET_TEMP_RET0("getTempRet0"), - SET_TEMP_RET0("setTempRet0"), - NEW_SIZE("newSize"), - MODULE("module"), - START("start"), - FUNC("func"), - PARAM("param"), - RESULT("result"), - MEMORY("memory"), - DATA("data"), - PASSIVE("passive"), - EXPORT("export"), - IMPORT("import"), - TABLE("table"), - ELEM("elem"), - LOCAL("local"), - TYPE("type"), - CALL("call"), - CALL_INDIRECT("call_indirect"), - BLOCK("block"), - BR_IF("br_if"), - THEN("then"), - ELSE("else"), - _NAN("NaN"), - _INFINITY("Infinity"), - NEG_INFINITY("-infinity"), - NEG_NAN("-nan"), - CASE("case"), - BR("br"), - FUNCREF("funcref"), - FAKE_RETURN("fake_return_waka123"), - MUT("mut"), - SPECTEST("spectest"), - PRINT("print"), - EXIT("exit"); +} // namespace UserSections +} // namespace BinaryConsts + +Name GROW_WASM_MEMORY("__growWasmMemory"); +Name WASM_CALL_CTORS("__wasm_call_ctors"); +Name MEMORY_BASE("__memory_base"); +Name TABLE_BASE("__table_base"); +Name GET_TEMP_RET0("getTempRet0"); +Name SET_TEMP_RET0("setTempRet0"); +Name NEW_SIZE("newSize"); +Name MODULE("module"); +Name START("start"); +Name FUNC("func"); +Name PARAM("param"); +Name RESULT("result"); +Name MEMORY("memory"); +Name DATA("data"); +Name PASSIVE("passive"); +Name EXPORT("export"); +Name IMPORT("import"); +Name TABLE("table"); +Name ELEM("elem"); +Name LOCAL("local"); +Name TYPE("type"); +Name CALL("call"); +Name CALL_INDIRECT("call_indirect"); +Name BLOCK("block"); +Name BR_IF("br_if"); +Name THEN("then"); +Name ELSE("else"); +Name _NAN("NaN"); +Name _INFINITY("Infinity"); +Name NEG_INFINITY("-infinity"); +Name NEG_NAN("-nan"); +Name CASE("case"); +Name BR("br"); +Name FUNCREF("funcref"); +Name FAKE_RETURN("fake_return_waka123"); +Name MUT("mut"); +Name SPECTEST("spectest"); +Name PRINT("print"); +Name EXIT("exit"); // Expressions const char* getExpressionName(Expression* curr) { switch (curr->_id) { - case Expression::Id::InvalidId: WASM_UNREACHABLE(); - case Expression::Id::BlockId: return "block"; - case Expression::Id::IfId: return "if"; - case Expression::Id::LoopId: return "loop"; - case Expression::Id::BreakId: return "break"; - case Expression::Id::SwitchId: return "switch"; - case Expression::Id::CallId: return "call"; - case Expression::Id::CallIndirectId: return "call_indirect"; - case Expression::Id::GetLocalId: return "local.get"; - case Expression::Id::SetLocalId: return "local.set"; - case Expression::Id::GetGlobalId: return "global.get"; - case Expression::Id::SetGlobalId: return "global.set"; - case Expression::Id::LoadId: return "load"; - case Expression::Id::StoreId: return "store"; - case Expression::Id::ConstId: return "const"; - case Expression::Id::UnaryId: return "unary"; - case Expression::Id::BinaryId: return "binary"; - case Expression::Id::SelectId: return "select"; - case Expression::Id::DropId: return "drop"; - case Expression::Id::ReturnId: return "return"; - case Expression::Id::HostId: return "host"; - case Expression::Id::NopId: return "nop"; - case Expression::Id::UnreachableId: return "unreachable"; - case Expression::Id::AtomicCmpxchgId: return "atomic_cmpxchg"; - case Expression::Id::AtomicRMWId: return "atomic_rmw"; - case Expression::Id::AtomicWaitId: return "atomic_wait"; - case Expression::Id::AtomicNotifyId: return "atomic_notify"; - case Expression::Id::SIMDExtractId: return "simd_extract"; - case Expression::Id::SIMDReplaceId: return "simd_replace"; - case Expression::Id::SIMDShuffleId: return "simd_shuffle"; - case Expression::Id::SIMDBitselectId: return "simd_bitselect"; - case Expression::Id::SIMDShiftId: return "simd_shift"; - case Expression::Id::MemoryInitId: return "memory_init"; - case Expression::Id::DataDropId: return "data_drop"; - case Expression::Id::MemoryCopyId: return "memory_copy"; - case Expression::Id::MemoryFillId: return "memory_fill"; - case Expression::Id::NumExpressionIds: WASM_UNREACHABLE(); + case Expression::Id::InvalidId: + WASM_UNREACHABLE(); + case Expression::Id::BlockId: + return "block"; + case Expression::Id::IfId: + return "if"; + case Expression::Id::LoopId: + return "loop"; + case Expression::Id::BreakId: + return "break"; + case Expression::Id::SwitchId: + return "switch"; + case Expression::Id::CallId: + return "call"; + case Expression::Id::CallIndirectId: + return "call_indirect"; + case Expression::Id::GetLocalId: + return "local.get"; + case Expression::Id::SetLocalId: + return "local.set"; + case Expression::Id::GetGlobalId: + return "global.get"; + case Expression::Id::SetGlobalId: + return "global.set"; + case Expression::Id::LoadId: + return "load"; + case Expression::Id::StoreId: + return "store"; + case Expression::Id::ConstId: + return "const"; + case Expression::Id::UnaryId: + return "unary"; + case Expression::Id::BinaryId: + return "binary"; + case Expression::Id::SelectId: + return "select"; + case Expression::Id::DropId: + return "drop"; + case Expression::Id::ReturnId: + return "return"; + case Expression::Id::HostId: + return "host"; + case Expression::Id::NopId: + return "nop"; + case Expression::Id::UnreachableId: + return "unreachable"; + case Expression::Id::AtomicCmpxchgId: + return "atomic_cmpxchg"; + case Expression::Id::AtomicRMWId: + return "atomic_rmw"; + case Expression::Id::AtomicWaitId: + return "atomic_wait"; + case Expression::Id::AtomicNotifyId: + return "atomic_notify"; + case Expression::Id::SIMDExtractId: + return "simd_extract"; + case Expression::Id::SIMDReplaceId: + return "simd_replace"; + case Expression::Id::SIMDShuffleId: + return "simd_shuffle"; + case Expression::Id::SIMDBitselectId: + return "simd_bitselect"; + case Expression::Id::SIMDShiftId: + return "simd_shift"; + case Expression::Id::MemoryInitId: + return "memory_init"; + case Expression::Id::DataDropId: + return "data_drop"; + case Expression::Id::MemoryCopyId: + return "memory_copy"; + case Expression::Id::MemoryFillId: + return "memory_fill"; + case Expression::Id::NumExpressionIds: + WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } @@ -134,8 +171,8 @@ struct TypeSeeker : public PostWalker<TypeSeeker> { Name targetName; std::vector<Type> types; - - TypeSeeker(Expression* target, Name targetName) : target(target), targetName(targetName) { + TypeSeeker(Expression* target, Name targetName) + : target(target), targetName(targetName) { Expression* temp = target; walk(temp); } @@ -148,9 +185,11 @@ struct TypeSeeker : public PostWalker<TypeSeeker> { void visitSwitch(Switch* curr) { for (auto name : curr->targets) { - if (name == targetName) types.push_back(curr->value ? curr->value->type : none); + if (name == targetName) + types.push_back(curr->value ? curr->value->type : none); } - if (curr->default_ == targetName) types.push_back(curr->value ? curr->value->type : none); + if (curr->default_ == targetName) + types.push_back(curr->value ? curr->value->type : none); } void visitBlock(Block* curr) { @@ -161,7 +200,9 @@ struct TypeSeeker : public PostWalker<TypeSeeker> { types.push_back(none); } } else if (curr->name == targetName) { - types.clear(); // ignore all breaks til now, they were captured by someone with the same name + // ignore all breaks til now, they were captured by someone with the same + // name + types.clear(); } } @@ -169,7 +210,9 @@ struct TypeSeeker : public PostWalker<TypeSeeker> { if (curr == target) { types.push_back(curr->body->type); } else if (curr->name == targetName) { - types.clear(); // ignore all breaks til now, they were captured by someone with the same name + // ignore all breaks til now, they were captured by someone with the same + // name + types.clear(); } } }; @@ -177,8 +220,8 @@ struct TypeSeeker : public PostWalker<TypeSeeker> { static Type mergeTypes(std::vector<Type>& types) { Type type = unreachable; for (auto other : types) { - // once none, stop. it then indicates a poison value, that must not be consumed - // and ignore unreachable + // once none, stop. it then indicates a poison value, that must not be + // consumed and ignore unreachable if (type != none) { if (other == none) { type = none; @@ -186,7 +229,8 @@ static Type mergeTypes(std::vector<Type>& types) { if (type == unreachable) { type = other; } else if (type != other) { - type = none; // poison value, we saw multiple types; this should not be consumed + // poison value, we saw multiple types; this should not be consumed + type = none; } } } @@ -196,17 +240,23 @@ static Type mergeTypes(std::vector<Type>& types) { // a block is unreachable if one of its elements is unreachable, // and there are no branches to it -static void handleUnreachable(Block* block, bool breakabilityKnown=false, bool hasBreak=false) { - if (block->type == unreachable) return; // nothing to do - if (block->list.size() == 0) return; // nothing to do +static void handleUnreachable(Block* block, + bool breakabilityKnown = false, + bool hasBreak = false) { + if (block->type == unreachable) + return; // nothing to do + if (block->list.size() == 0) + return; // nothing to do // if we are concrete, stop - even an unreachable child // won't change that (since we have a break with a value, // or the final child flows out a value) - if (isConcreteType(block->type)) return; + if (isConcreteType(block->type)) + return; // look for an unreachable child for (auto* child : block->list) { if (child->type == unreachable) { - // there is an unreachable child, so we are unreachable, unless we have a break + // there is an unreachable child, so we are unreachable, unless we have a + // break if (!breakabilityKnown) { hasBreak = BranchUtils::BranchSeeker::hasNamed(block, block->name); } @@ -230,9 +280,11 @@ void Block::finalize() { // (return) // (i32.const 10) // ) - if (isConcreteType(type)) return; + if (isConcreteType(type)) + return; // if we are unreachable, we are done - if (type == unreachable) return; + if (type == unreachable) + return; // we may still be unreachable if we have an unreachable // child for (auto* child : list) { @@ -268,7 +320,9 @@ void Block::finalize(Type type_, bool hasBreak) { void If::finalize(Type type_) { type = type_; - if (type == none && (condition->type == unreachable || (ifFalse && ifTrue->type == unreachable && ifFalse->type == unreachable))) { + if (type == none && (condition->type == unreachable || + (ifFalse && ifTrue->type == unreachable && + ifFalse->type == unreachable))) { type = unreachable; } } @@ -307,9 +361,7 @@ void Loop::finalize(Type type_) { } } -void Loop::finalize() { - type = body->type; -} +void Loop::finalize() { type = body->type; } void Break::finalize() { if (condition) { @@ -325,12 +377,9 @@ void Break::finalize() { } } -void Switch::finalize() { - type = unreachable; -} +void Switch::finalize() { type = unreachable; } -template<typename T> -void handleUnreachableOperands(T* curr) { +template<typename T> void handleUnreachableOperands(T* curr) { for (auto* child : curr->operands) { if (child->type == unreachable) { curr->type = unreachable; @@ -339,9 +388,7 @@ void handleUnreachableOperands(T* curr) { } } -void Call::finalize() { - handleUnreachableOperands(this); -} +void Call::finalize() { handleUnreachableOperands(this); } void CallIndirect::finalize() { handleUnreachableOperands(this); @@ -351,29 +398,31 @@ void CallIndirect::finalize() { } bool FunctionType::structuralComparison(FunctionType& b) { - if (result != b.result) return false; - if (params.size() != b.params.size()) return false; + if (result != b.result) + return false; + if (params.size() != b.params.size()) + return false; for (size_t i = 0; i < params.size(); i++) { - if (params[i] != b.params[i]) return false; + if (params[i] != b.params[i]) + return false; } return true; } bool FunctionType::operator==(FunctionType& b) { - if (name != b.name) return false; + if (name != b.name) + return false; return structuralComparison(b); } -bool FunctionType::operator!=(FunctionType& b) { - return !(*this == b); -} +bool FunctionType::operator!=(FunctionType& b) { return !(*this == b); } -bool SetLocal::isTee() { - return type != none; -} +bool SetLocal::isTee() { return type != none; } void SetLocal::setTee(bool is) { - if (is) type = value->type; - else type = none; + if (is) + type = value->type; + else + type = none; finalize(); // type may need to be unreachable } @@ -415,14 +464,16 @@ void AtomicRMW::finalize() { } void AtomicCmpxchg::finalize() { - if (ptr->type == unreachable || expected->type == unreachable || replacement->type == unreachable) { + if (ptr->type == unreachable || expected->type == unreachable || + replacement->type == unreachable) { type = unreachable; } } void AtomicWait::finalize() { type = i32; - if (ptr->type == unreachable || expected->type == unreachable || timeout->type == unreachable) { + if (ptr->type == unreachable || expected->type == unreachable || + timeout->type == unreachable) { type = unreachable; } } @@ -441,11 +492,20 @@ void SIMDExtract::finalize() { case ExtractLaneUVecI8x16: case ExtractLaneSVecI16x8: case ExtractLaneUVecI16x8: - case ExtractLaneVecI32x4: type = i32; break; - case ExtractLaneVecI64x2: type = i64; break; - case ExtractLaneVecF32x4: type = f32; break; - case ExtractLaneVecF64x2: type = f64; break; - default: WASM_UNREACHABLE(); + case ExtractLaneVecI32x4: + type = i32; + break; + case ExtractLaneVecI64x2: + type = i64; + break; + case ExtractLaneVecF32x4: + type = f32; + break; + case ExtractLaneVecF64x2: + type = f64; + break; + default: + WASM_UNREACHABLE(); } if (vec->type == unreachable) { type = unreachable; @@ -471,7 +531,8 @@ void SIMDShuffle::finalize() { void SIMDBitselect::finalize() { assert(left && right && cond); type = v128; - if (left->type == unreachable || right->type == unreachable || cond->type == unreachable) { + if (left->type == unreachable || right->type == unreachable || + cond->type == unreachable) { type = unreachable; } } @@ -479,19 +540,19 @@ void SIMDBitselect::finalize() { void MemoryInit::finalize() { assert(dest && offset && size); type = none; - if (dest->type == unreachable || offset->type == unreachable || size->type == unreachable) { + if (dest->type == unreachable || offset->type == unreachable || + size->type == unreachable) { type = unreachable; } } -void DataDrop::finalize() { - type = none; -} +void DataDrop::finalize() { type = none; } void MemoryCopy::finalize() { assert(dest && source && size); type = none; - if (dest->type == unreachable || source->type == unreachable || size->type == unreachable) { + if (dest->type == unreachable || source->type == unreachable || + size->type == unreachable) { type = unreachable; } } @@ -499,7 +560,8 @@ void MemoryCopy::finalize() { void MemoryFill::finalize() { assert(dest && value && size); type = none; - if (dest->type == unreachable || value->type == unreachable || size->type == unreachable) { + if (dest->type == unreachable || value->type == unreachable || + size->type == unreachable) { type = unreachable; } } @@ -518,13 +580,9 @@ Const* Const::set(Literal value_) { return this; } -void Const::finalize() { - type = value.type; -} +void Const::finalize() { type = value.type; } -bool Unary::isRelational() { - return op == EqZInt32 || op == EqZInt64; -} +bool Unary::isRelational() { return op == EqZInt32 || op == EqZInt64; } void Unary::finalize() { if (value->type == unreachable) { @@ -551,14 +609,33 @@ void Unary::finalize() { case FloorFloat64: case TruncFloat64: case NearestFloat64: - case SqrtFloat64: type = value->type; break; + case SqrtFloat64: + type = value->type; + break; case EqZInt32: - case EqZInt64: type = i32; break; - case ExtendS8Int32: case ExtendS16Int32: type = i32; break; - case ExtendSInt32: case ExtendUInt32: case ExtendS8Int64: case ExtendS16Int64: case ExtendS32Int64: type = i64; break; - case WrapInt64: type = i32; break; - case PromoteFloat32: type = f64; break; - case DemoteFloat64: type = f32; break; + case EqZInt64: + type = i32; + break; + case ExtendS8Int32: + case ExtendS16Int32: + type = i32; + break; + case ExtendSInt32: + case ExtendUInt32: + case ExtendS8Int64: + case ExtendS16Int64: + case ExtendS32Int64: + type = i64; + break; + case WrapInt64: + type = i32; + break; + case PromoteFloat32: + type = f64; + break; + case DemoteFloat64: + type = f32; + break; case TruncSFloat32ToInt32: case TruncUFloat32ToInt32: case TruncSFloat64ToInt32: @@ -567,7 +644,9 @@ void Unary::finalize() { case TruncSatUFloat32ToInt32: case TruncSatSFloat64ToInt32: case TruncSatUFloat64ToInt32: - case ReinterpretFloat32: type = i32; break; + case ReinterpretFloat32: + type = i32; + break; case TruncSFloat32ToInt64: case TruncUFloat32ToInt64: case TruncSFloat64ToInt64: @@ -576,17 +655,23 @@ void Unary::finalize() { case TruncSatUFloat32ToInt64: case TruncSatSFloat64ToInt64: case TruncSatUFloat64ToInt64: - case ReinterpretFloat64: type = i64; break; + case ReinterpretFloat64: + type = i64; + break; case ReinterpretInt32: case ConvertSInt32ToFloat32: case ConvertUInt32ToFloat32: case ConvertSInt64ToFloat32: - case ConvertUInt64ToFloat32: type = f32; break; + case ConvertUInt64ToFloat32: + type = f32; + break; case ReinterpretInt64: case ConvertSInt32ToFloat64: case ConvertUInt32ToFloat64: case ConvertSInt64ToFloat64: - case ConvertUInt64ToFloat64: type = f64; break; + case ConvertUInt64ToFloat64: + type = f64; + break; case SplatVecI8x16: case SplatVecI16x8: case SplatVecI32x4: @@ -611,7 +696,9 @@ void Unary::finalize() { case ConvertSVecI32x4ToVecF32x4: case ConvertUVecI32x4ToVecF32x4: case ConvertSVecI64x2ToVecF64x2: - case ConvertUVecI64x2ToVecF64x2: type = v128; break; + case ConvertUVecI64x2ToVecF64x2: + type = v128; + break; case AnyTrueVecI8x16: case AllTrueVecI8x16: case AnyTrueVecI16x8: @@ -619,8 +706,11 @@ void Unary::finalize() { case AnyTrueVecI32x4: case AllTrueVecI32x4: case AnyTrueVecI64x2: - case AllTrueVecI64x2: type = i32; break; - case InvalidUnary: WASM_UNREACHABLE(); + case AllTrueVecI64x2: + type = i32; + break; + case InvalidUnary: + WASM_UNREACHABLE(); } } @@ -657,8 +747,10 @@ bool Binary::isRelational() { case LtFloat32: case LeFloat32: case GtFloat32: - case GeFloat32: return true; - default: return false; + case GeFloat32: + return true; + default: + return false; } } @@ -675,7 +767,8 @@ void Binary::finalize() { void Select::finalize() { assert(ifTrue && ifFalse); - if (ifTrue->type == unreachable || ifFalse->type == unreachable || condition->type == unreachable) { + if (ifTrue->type == unreachable || ifFalse->type == unreachable || + condition->type == unreachable) { type = unreachable; } else { type = ifTrue->type; @@ -708,33 +801,21 @@ void Host::finalize() { } } -size_t Function::getNumParams() { - return params.size(); -} +size_t Function::getNumParams() { return params.size(); } -size_t Function::getNumVars() { - return vars.size(); -} +size_t Function::getNumVars() { return vars.size(); } -size_t Function::getNumLocals() { - return params.size() + vars.size(); -} +size_t Function::getNumLocals() { return params.size() + vars.size(); } -bool Function::isParam(Index index) { - return index < params.size(); -} +bool Function::isParam(Index index) { return index < params.size(); } -bool Function::isVar(Index index) { - return index >= params.size(); -} +bool Function::isVar(Index index) { return index >= params.size(); } bool Function::hasLocalName(Index index) const { return localNames.find(index) != localNames.end(); } -Name Function::getLocalName(Index index) { - return localNames.at(index); -} +Name Function::getLocalName(Index index) { return localNames.at(index); } Name Function::getLocalNameOrDefault(Index index) { auto nameIt = localNames.find(index); @@ -761,9 +842,7 @@ Index Function::getLocalIndex(Name name) { return iter->second; } -Index Function::getVarIndexBase() { - return params.size(); -} +Index Function::getVarIndexBase() { return params.size(); } Type Function::getLocalType(Index index) { if (isParam(index)) { @@ -775,9 +854,7 @@ Type Function::getLocalType(Index index) { } } -void Function::clearNames() { - localNames.clear(); -} +void Function::clearNames() { localNames.clear(); } void Function::clearDebugInfo() { localIndices.clear(); @@ -914,9 +991,7 @@ Global* Module::addGlobal(Global* curr) { return curr; } -void Module::addStart(const Name& s) { - start = s; -} +void Module::addStart(const Name& s) { start = s; } void Module::removeFunctionType(Name name) { for (size_t i = 0; i < functionTypes.size(); i++) { @@ -979,8 +1054,6 @@ void Module::updateMaps() { } } -void Module::clearDebugInfo() { - debugInfoFileNames.clear(); -} +void Module::clearDebugInfo() { debugInfoFileNames.clear(); } } // namespace wasm diff --git a/src/wasm2js.h b/src/wasm2js.h index be794decb..515b9335a 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -25,16 +25,11 @@ #include <cmath> #include <numeric> -#include "asmjs/shared-constants.h" +#include "abi/js.h" +#include "asm_v_wasm.h" #include "asmjs/asmangle.h" -#include "wasm.h" -#include "wasm-builder.h" -#include "wasm-io.h" -#include "wasm-validator.h" +#include "asmjs/shared-constants.h" #include "emscripten-optimizer/optimizer.h" -#include "mixed_arena.h" -#include "asm_v_wasm.h" -#include "abi/js.h" #include "ir/effects.h" #include "ir/find_all.h" #include "ir/import-utils.h" @@ -43,25 +38,34 @@ #include "ir/names.h" #include "ir/table-utils.h" #include "ir/utils.h" +#include "mixed_arena.h" #include "passes/passes.h" #include "support/base64.h" +#include "wasm-builder.h" +#include "wasm-io.h" +#include "wasm-validator.h" +#include "wasm.h" namespace wasm { using namespace cashew; -IString ASM_FUNC("asmFunc"), - ABORT_FUNC("abort"), - FUNCTION_TABLE("FUNCTION_TABLE"), - NO_RESULT("wasm2js$noresult"), // no result at all - EXPRESSION_RESULT("wasm2js$expresult"); // result in an expression, no temp var +IString ASM_FUNC("asmFunc"); +IString ABORT_FUNC("abort"); +IString FUNCTION_TABLE("FUNCTION_TABLE"); +IString NO_RESULT("wasm2js$noresult"); // no result at all +// result in an expression, no temp var +IString EXPRESSION_RESULT("wasm2js$expresult"); // Appends extra to block, flattening out if extra is a block as well void flattenAppend(Ref ast, Ref extra) { int index; - if (ast[0] == BLOCK || ast[0] == TOPLEVEL) index = 1; - else if (ast[0] == DEFUN) index = 3; - else abort(); + if (ast[0] == BLOCK || ast[0] == TOPLEVEL) + index = 1; + else if (ast[0] == DEFUN) + index = 3; + else + abort(); if (extra->isArray() && extra[0] == BLOCK) { for (size_t i = 0; i < extra[1]->size(); i++) { ast[index]->push_back(extra[1][i]); @@ -80,9 +84,7 @@ void sequenceAppend(Ref& ast, Ref extra) { ast = ValueBuilder::makeSeq(ast, extra); } -IString stringToIString(std::string str) { - return IString(str.c_str(), false); -} +IString stringToIString(std::string str) { return IString(str.c_str(), false); } // Used when taking a wasm name and generating a JS identifier. Each scope here // is used to ensure that all names have a unique name but the same wasm name @@ -124,7 +126,7 @@ public: Wasm2JSBuilder(Flags f, PassOptions options) : flags(f), options(options) {} Ref processWasm(Module* wasm, Name funcName = ASM_FUNC); - Ref processFunction(Module* wasm, Function* func, bool standalone=false); + Ref processFunction(Module* wasm, Function* func, bool standalone = false); Ref processStandaloneFunction(Module* wasm, Function* func) { return processFunction(wasm, func, true); } @@ -142,7 +144,9 @@ public: } else { size_t index = temps[type]++; ret = IString((std::string("wasm2js_") + printType(type) + "$" + - std::to_string(index)).c_str(), false); + std::to_string(index)) + .c_str(), + false); } if (func->localIndices.find(ret) == func->localIndices.end()) { Builder::addVar(func, ret, type); @@ -151,9 +155,7 @@ public: } // Free a temp var. - void freeTemp(Type type, IString temp) { - frees[type].push_back(temp); - } + void freeTemp(Type type, IString temp) { frees[type].push_back(temp); } // Generates a mangled name from `name` within the specified scope. // @@ -170,7 +172,7 @@ public: // First up check our cached of mangled names to avoid doing extra work // below - auto &mangledScope = mangledNames[(int) scope]; + auto& mangledScope = mangledNames[(int)scope]; auto it = mangledScope.find(name.c_str()); if (it != mangledScope.end()) { return it->second; @@ -200,13 +202,15 @@ public: // it's a bug currently if they're not globally unique. This should // probably be fixed via a different namespace for exports or something // like that. - // XXX This is not actually a valid check atm, since functions are not in the - // global-most scope, but rather in the "asmFunc" scope which is inside it. - // Also, for emscripten style glue, we emit the exports as a return, so there - // is no name placed into the scope. For these reasons, just warn here, don't - // error. + // XXX This is not actually a valid check atm, since functions are not in + // the global-most scope, but rather in the "asmFunc" scope which is + // inside it. Also, for emscripten style glue, we emit the exports as + // a return, so there is no name placed into the scope. For these + // reasons, just warn here, don't error. if (scope == NameScope::Top) { - std::cerr << "wasm2js: warning: global scope may be colliding with other scope: " << mangled << '\n'; + std::cerr << "wasm2js: warning: global scope may be colliding with " + "other scope: " + << mangled << '\n'; } } allMangledNames.insert(ret); @@ -225,7 +229,7 @@ private: // Mangled names cache by interned names. // Utilizes the usually reused underlying cstring's pointer as the key. - std::unordered_map<const char*, IString> mangledNames[(int) NameScope::Max]; + std::unordered_map<const char*, IString> mangledNames[(int)NameScope::Max]; std::unordered_set<IString> allMangledNames; // If a function is callable from outside, we'll need to cast the inputs @@ -242,8 +246,8 @@ private: void addMemoryGrowthFuncs(Ref ast, Module* wasm); Wasm2JSBuilder() = delete; - Wasm2JSBuilder(const Wasm2JSBuilder &) = delete; - Wasm2JSBuilder &operator=(const Wasm2JSBuilder&) = delete; + Wasm2JSBuilder(const Wasm2JSBuilder&) = delete; + Wasm2JSBuilder& operator=(const Wasm2JSBuilder&) = delete; }; Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) { @@ -299,27 +303,24 @@ Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) { ValueBuilder::appendArgumentToFunction(asmFunc, GLOBAL); ValueBuilder::appendArgumentToFunction(asmFunc, ENV); ValueBuilder::appendArgumentToFunction(asmFunc, BUFFER); - asmFunc[3]->push_back(ValueBuilder::makeStatement(ValueBuilder::makeString(ALMOST_ASM))); + asmFunc[3]->push_back( + ValueBuilder::makeStatement(ValueBuilder::makeString(ALMOST_ASM))); // add memory import if (wasm->memory.exists && wasm->memory.imported()) { Ref theVar = ValueBuilder::makeVar(); asmFunc[3]->push_back(theVar); - ValueBuilder::appendToVar(theVar, + ValueBuilder::appendToVar( + theVar, "memory", - ValueBuilder::makeDot( - ValueBuilder::makeName(ENV), - ValueBuilder::makeName("memory") - ) - ); + ValueBuilder::makeDot(ValueBuilder::makeName(ENV), + ValueBuilder::makeName("memory"))); } // create heaps, etc addBasics(asmFunc[3]); - ModuleUtils::iterImportedFunctions(*wasm, [&](Function* import) { - addFunctionImport(asmFunc[3], import); - }); - ModuleUtils::iterImportedGlobals(*wasm, [&](Global* import) { - addGlobalImport(asmFunc[3], import); - }); + ModuleUtils::iterImportedFunctions( + *wasm, [&](Function* import) { addFunctionImport(asmFunc[3], import); }); + ModuleUtils::iterImportedGlobals( + *wasm, [&](Global* import) { addGlobalImport(asmFunc[3], import); }); // make sure exports get their expected names for (auto& e : wasm->exports) { @@ -350,13 +351,14 @@ Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) { Builder builder(allocator); std::vector<Type> params; std::vector<Type> vars; - asmFunc[3]->push_back(processFunction(wasm, builder.makeFunction( - WASM_FETCH_HIGH_BITS, - std::move(params), - i32, - std::move(vars), - builder.makeReturn(builder.makeGetGlobal(INT64_TO_32_HIGH_BITS, i32)) - ))); + asmFunc[3]->push_back(processFunction( + wasm, + builder.makeFunction(WASM_FETCH_HIGH_BITS, + std::move(params), + i32, + std::move(vars), + builder.makeReturn(builder.makeGetGlobal( + INT64_TO_32_HIGH_BITS, i32))))); auto e = new Export(); e->name = WASM_FETCH_HIGH_BITS; e->value = WASM_FETCH_HIGH_BITS; @@ -378,23 +380,17 @@ void Wasm2JSBuilder::addBasics(Ref ast) { auto addHeap = [&](IString name, IString view) { Ref theVar = ValueBuilder::makeVar(); ast->push_back(theVar); - ValueBuilder::appendToVar(theVar, + ValueBuilder::appendToVar( + theVar, name, - ValueBuilder::makeNew( - ValueBuilder::makeCall( - ValueBuilder::makeDot( - ValueBuilder::makeName(GLOBAL), - view - ), - ValueBuilder::makeName(BUFFER) - ) - ) - ); + ValueBuilder::makeNew(ValueBuilder::makeCall( + ValueBuilder::makeDot(ValueBuilder::makeName(GLOBAL), view), + ValueBuilder::makeName(BUFFER)))); }; - addHeap(HEAP8, INT8ARRAY); + addHeap(HEAP8, INT8ARRAY); addHeap(HEAP16, INT16ARRAY); addHeap(HEAP32, INT32ARRAY); - addHeap(HEAPU8, UINT8ARRAY); + addHeap(HEAPU8, UINT8ARRAY); addHeap(HEAPU16, UINT16ARRAY); addHeap(HEAPU32, UINT32ARRAY); addHeap(HEAPF32, FLOAT32ARRAY); @@ -403,16 +399,11 @@ void Wasm2JSBuilder::addBasics(Ref ast) { auto addMath = [&](IString name, IString base) { Ref theVar = ValueBuilder::makeVar(); ast->push_back(theVar); - ValueBuilder::appendToVar(theVar, + ValueBuilder::appendToVar( + theVar, name, ValueBuilder::makeDot( - ValueBuilder::makeDot( - ValueBuilder::makeName(GLOBAL), - MATH - ), - base - ) - ); + ValueBuilder::makeDot(ValueBuilder::makeName(GLOBAL), MATH), base)); }; addMath(MATH_IMUL, IMUL); addMath(MATH_FROUND, FROUND); @@ -426,61 +417,54 @@ void Wasm2JSBuilder::addBasics(Ref ast) { // abort function Ref abortVar = ValueBuilder::makeVar(); ast->push_back(abortVar); - ValueBuilder::appendToVar(abortVar, + ValueBuilder::appendToVar( + abortVar, "abort", - ValueBuilder::makeDot( - ValueBuilder::makeName(ENV), - ABORT_FUNC - ) - ); + ValueBuilder::makeDot(ValueBuilder::makeName(ENV), ABORT_FUNC)); // TODO: this shouldn't be needed once we stop generating literal asm.js code // NaN and Infinity variables Ref nanVar = ValueBuilder::makeVar(); ast->push_back(nanVar); - ValueBuilder::appendToVar(nanVar, + ValueBuilder::appendToVar( + nanVar, "nan", - ValueBuilder::makeDot(ValueBuilder::makeName(GLOBAL), "NaN") - ); + ValueBuilder::makeDot(ValueBuilder::makeName(GLOBAL), "NaN")); Ref infinityVar = ValueBuilder::makeVar(); ast->push_back(infinityVar); - ValueBuilder::appendToVar(infinityVar, + ValueBuilder::appendToVar( + infinityVar, "infinity", - ValueBuilder::makeDot(ValueBuilder::makeName(GLOBAL), "Infinity") - ); + ValueBuilder::makeDot(ValueBuilder::makeName(GLOBAL), "Infinity")); } void Wasm2JSBuilder::addFunctionImport(Ref ast, Function* import) { - // The scratch memory helpers are emitted in the glue, see code and comments below. + // The scratch memory helpers are emitted in the glue, see code and comments + // below. if (ABI::wasm2js::isScratchMemoryHelper(import->base)) { return; } Ref theVar = ValueBuilder::makeVar(); ast->push_back(theVar); - Ref module = ValueBuilder::makeName(ENV); // TODO: handle nested module imports - ValueBuilder::appendToVar(theVar, + // TODO: handle nested module imports + Ref module = ValueBuilder::makeName(ENV); + ValueBuilder::appendToVar( + theVar, fromName(import->name, NameScope::Top), - ValueBuilder::makeDot( - module, - fromName(import->base, NameScope::Top) - ) - ); + ValueBuilder::makeDot(module, fromName(import->base, NameScope::Top))); } void Wasm2JSBuilder::addGlobalImport(Ref ast, Global* import) { Ref theVar = ValueBuilder::makeVar(); ast->push_back(theVar); - Ref module = ValueBuilder::makeName(ENV); // TODO: handle nested module imports - Ref value = ValueBuilder::makeDot( - module, - fromName(import->base, NameScope::Top) - ); + // TODO: handle nested module imports + Ref module = ValueBuilder::makeName(ENV); + Ref value = + ValueBuilder::makeDot(module, fromName(import->base, NameScope::Top)); if (import->type == i32) { value = makeAsmCoercion(value, ASM_INT); } - ValueBuilder::appendToVar(theVar, - fromName(import->name, NameScope::Top), - value - ); + ValueBuilder::appendToVar( + theVar, fromName(import->name, NameScope::Top), value); } void Wasm2JSBuilder::addTable(Ref ast, Module* wasm) { @@ -505,7 +489,8 @@ void Wasm2JSBuilder::addTable(Ref ast, Module* wasm) { if (!wasm->table.imported()) { Ref theVar = ValueBuilder::makeVar(); ast->push_back(theVar); - ValueBuilder::appendToVar(theVar, FUNCTION_TABLE, ValueBuilder::makeArray()); + ValueBuilder::appendToVar( + theVar, FUNCTION_TABLE, ValueBuilder::makeArray()); } // TODO: optimize for size @@ -519,21 +504,14 @@ void Wasm2JSBuilder::addTable(Ref ast, Module* wasm) { index = ValueBuilder::makeBinary( ValueBuilder::makeName(stringToIString(asmangle(get->name.str))), PLUS, - ValueBuilder::makeNum(i) - ); + ValueBuilder::makeNum(i)); } else { WASM_UNREACHABLE(); } - ast->push_back(ValueBuilder::makeStatement( - ValueBuilder::makeBinary( - ValueBuilder::makeSub( - ValueBuilder::makeName(FUNCTION_TABLE), - index - ), - SET, - ValueBuilder::makeName(fromName(segment.data[i], NameScope::Top)) - ) - )); + ast->push_back(ValueBuilder::makeStatement(ValueBuilder::makeBinary( + ValueBuilder::makeSub(ValueBuilder::makeName(FUNCTION_TABLE), index), + SET, + ValueBuilder::makeName(fromName(segment.data[i], NameScope::Top))))); } } } @@ -546,49 +524,37 @@ void Wasm2JSBuilder::addExports(Ref ast, Module* wasm) { ValueBuilder::appendToObjectWithQuotes( exports, fromName(export_->name, NameScope::Top), - ValueBuilder::makeName(fromName(export_->value, NameScope::Top)) - ); + ValueBuilder::makeName(fromName(export_->value, NameScope::Top))); } if (export_->kind == ExternalKind::Memory) { Ref descs = ValueBuilder::makeObject(); Ref growDesc = ValueBuilder::makeObject(); + ValueBuilder::appendToObjectWithQuotes(descs, IString("grow"), growDesc); ValueBuilder::appendToObjectWithQuotes( - descs, - IString("grow"), - growDesc); - ValueBuilder::appendToObjectWithQuotes( - growDesc, - IString("value"), - ValueBuilder::makeName(WASM_GROW_MEMORY)); + growDesc, IString("value"), ValueBuilder::makeName(WASM_GROW_MEMORY)); Ref bufferDesc = ValueBuilder::makeObject(); Ref bufferGetter = ValueBuilder::makeFunction(IString("")); - bufferGetter[3]->push_back(ValueBuilder::makeReturn( - ValueBuilder::makeName(BUFFER) - )); + bufferGetter[3]->push_back( + ValueBuilder::makeReturn(ValueBuilder::makeName(BUFFER))); ValueBuilder::appendToObjectWithQuotes( - bufferDesc, - IString("get"), - bufferGetter); + bufferDesc, IString("get"), bufferGetter); ValueBuilder::appendToObjectWithQuotes( - descs, - IString("buffer"), - bufferDesc); + descs, IString("buffer"), bufferDesc); Ref memory = ValueBuilder::makeCall( - ValueBuilder::makeDot(ValueBuilder::makeName(IString("Object")), IString("create")), - ValueBuilder::makeDot(ValueBuilder::makeName(IString("Object")), IString("prototype"))); - ValueBuilder::appendToCall( - memory, - descs); + ValueBuilder::makeDot(ValueBuilder::makeName(IString("Object")), + IString("create")), + ValueBuilder::makeDot(ValueBuilder::makeName(IString("Object")), + IString("prototype"))); + ValueBuilder::appendToCall(memory, descs); ValueBuilder::appendToObjectWithQuotes( - exports, - fromName(export_->name, NameScope::Top), - memory); + exports, fromName(export_->name, NameScope::Top), memory); } } if (wasm->memory.exists && wasm->memory.max > wasm->memory.initial) { addMemoryGrowthFuncs(ast, wasm); } - ast->push_back(ValueBuilder::makeStatement(ValueBuilder::makeReturn(exports))); + ast->push_back( + ValueBuilder::makeStatement(ValueBuilder::makeReturn(exports))); } void Wasm2JSBuilder::addGlobal(Ref ast, Global* global) { @@ -600,38 +566,38 @@ void Wasm2JSBuilder::addGlobal(Ref ast, Global* global) { break; } case Type::f32: { - theValue = ValueBuilder::makeCall(MATH_FROUND, - makeAsmCoercion(ValueBuilder::makeDouble(const_->value.getf32()), ASM_DOUBLE) - ); + theValue = ValueBuilder::makeCall( + MATH_FROUND, + makeAsmCoercion(ValueBuilder::makeDouble(const_->value.getf32()), + ASM_DOUBLE)); break; } case Type::f64: { - theValue = makeAsmCoercion(ValueBuilder::makeDouble(const_->value.getf64()), ASM_DOUBLE); + theValue = makeAsmCoercion( + ValueBuilder::makeDouble(const_->value.getf64()), ASM_DOUBLE); break; } - default: { - assert(false && "Top const type not supported"); - } + default: { assert(false && "Top const type not supported"); } } Ref theVar = ValueBuilder::makeVar(); ast->push_back(theVar); - ValueBuilder::appendToVar(theVar, - fromName(global->name, NameScope::Top), - theValue - ); + ValueBuilder::appendToVar( + theVar, fromName(global->name, NameScope::Top), theValue); } else if (auto* get = global->init->dynCast<GetGlobal>()) { Ref theVar = ValueBuilder::makeVar(); ast->push_back(theVar); - ValueBuilder::appendToVar(theVar, + ValueBuilder::appendToVar( + theVar, fromName(global->name, NameScope::Top), - ValueBuilder::makeName(fromName(get->name, NameScope::Top)) - ); + ValueBuilder::makeName(fromName(get->name, NameScope::Top))); } else { assert(false && "Top init type not supported"); } } -Ref Wasm2JSBuilder::processFunction(Module* m, Function* func, bool standaloneFunction) { +Ref Wasm2JSBuilder::processFunction(Module* m, + Function* func, + bool standaloneFunction) { if (standaloneFunction) { // We are only printing a function, not a whole module. Prepare it for // translation now (if there were a module, we'd have done this for all @@ -657,22 +623,17 @@ Ref Wasm2JSBuilder::processFunction(Module* m, Function* func, bool standaloneFu temps.resize(std::max(i32, std::max(f32, f64)) + 1); temps[i32] = temps[f32] = temps[f64] = 0; // arguments - bool needCoercions = options.optimizeLevel == 0 || standaloneFunction || functionsCallableFromOutside.count(func->name); + bool needCoercions = options.optimizeLevel == 0 || standaloneFunction || + functionsCallableFromOutside.count(func->name); for (Index i = 0; i < func->getNumParams(); i++) { IString name = fromName(func->getLocalNameOrGeneric(i), NameScope::Local); ValueBuilder::appendArgumentToFunction(ret, name); if (needCoercions) { - ret[3]->push_back( - ValueBuilder::makeStatement( - ValueBuilder::makeBinary( - ValueBuilder::makeName(name), SET, - makeAsmCoercion( - ValueBuilder::makeName(name), - wasmToAsmType(func->getLocalType(i)) - ) - ) - ) - ); + ret[3]->push_back(ValueBuilder::makeStatement(ValueBuilder::makeBinary( + ValueBuilder::makeName(name), + SET, + makeAsmCoercion(ValueBuilder::makeName(name), + wasmToAsmType(func->getLocalType(i)))))); } } Ref theVar = ValueBuilder::makeVar(); @@ -685,20 +646,21 @@ Ref Wasm2JSBuilder::processFunction(Module* m, Function* func, bool standaloneFu ValueBuilder::appendToVar( theVar, fromName(func->getLocalNameOrGeneric(i), NameScope::Local), - makeAsmCoercedZero(wasmToAsmType(func->getLocalType(i))) - ); + makeAsmCoercedZero(wasmToAsmType(func->getLocalType(i)))); } if (theVar[1]->size() == 0) { ret[3]->splice(theVarIndex, 1); } - // checks - assert(frees[i32].size() == temps[i32]); // all temp vars should be free at the end - assert(frees[f32].size() == temps[f32]); // all temp vars should be free at the end - assert(frees[f64].size() == temps[f64]); // all temp vars should be free at the end + // checks: all temp vars should be free at the end + assert(frees[i32].size() == temps[i32]); + assert(frees[f32].size() == temps[f32]); + assert(frees[f64].size() == temps[f64]); return ret; } -Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standaloneFunction) { +Ref Wasm2JSBuilder::processFunctionBody(Module* m, + Function* func, + bool standaloneFunction) { struct ExpressionProcessor : public Visitor<ExpressionProcessor, Ref> { Wasm2JSBuilder* parent; IString result; // TODO: remove @@ -707,8 +669,12 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo bool standaloneFunction; MixedArena allocator; - ExpressionProcessor(Wasm2JSBuilder* parent, Module* m, Function* func, bool standaloneFunction) - : parent(parent), func(func), module(m), standaloneFunction(standaloneFunction) {} + ExpressionProcessor(Wasm2JSBuilder* parent, + Module* m, + Function* func, + bool standaloneFunction) + : parent(parent), func(func), module(m), + standaloneFunction(standaloneFunction) {} // A scoped temporary variable. struct ScopedTemp { @@ -716,11 +682,15 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo Type type; IString temp; // TODO: switch to indexes; avoid names bool needFree; - // @param possible if provided, this is a variable we can use as our temp. it has already been - // allocated in a higher scope, and we can just assign to it as our result is - // going there anyhow. - ScopedTemp(Type type, Wasm2JSBuilder* parent, Function* func, - IString possible = NO_RESULT) : parent(parent), type(type) { + // @param possible if provided, this is a variable we can use as our temp. + // it has already been allocated in a higher scope, and we + // can just assign to it as our result is going there + // anyhow. + ScopedTemp(Type type, + Wasm2JSBuilder* parent, + Function* func, + IString possible = NO_RESULT) + : parent(parent), type(type) { assert(possible != EXPRESSION_RESULT); if (possible == NO_RESULT) { temp = parent->getTemp(type, func); @@ -736,19 +706,17 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo } } - IString getName() { - return temp; - } - Ref getAstName() { - return ValueBuilder::makeName(temp); - } + IString getName() { return temp; } + Ref getAstName() { return ValueBuilder::makeName(temp); } }; Ref visit(Expression* curr, IString nextResult) { IString old = result; result = nextResult; Ref ret = Visitor::visit(curr); - result = old; // keep it consistent for the rest of this frame, which may call visit on multiple children + // keep it consistent for the rest of this frame, which may call visit on + // multiple children + result = old; return ret; } @@ -759,7 +727,8 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo Ref visitAndAssign(Expression* curr, IString result) { assert(result != NO_RESULT); Ref ret = visit(curr, result); - return ValueBuilder::makeStatement(ValueBuilder::makeBinary(ValueBuilder::makeName(result), SET, ret)); + return ValueBuilder::makeStatement( + ValueBuilder::makeBinary(ValueBuilder::makeName(result), SET, ret)); } Ref visitAndAssign(Expression* curr, ScopedTemp& temp) { @@ -768,18 +737,18 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo // Expressions with control flow turn into a block, which we must // then handle, even if we are an expression. - bool isBlock(Ref ast) { - return !!ast && ast->isArray() && ast[0] == BLOCK; - } + bool isBlock(Ref ast) { return !!ast && ast->isArray() && ast[0] == BLOCK; } Ref blockify(Ref ast) { - if (isBlock(ast)) return ast; + if (isBlock(ast)) + return ast; Ref ret = ValueBuilder::makeBlock(); ret[1]->push_back(ValueBuilder::makeStatement(ast)); return ret; } - // Breaks to the top of a loop should be emitted as continues, to that loop's main label + // Breaks to the top of a loop should be emitted as continues, to that + // loop's main label std::unordered_set<Name> continueLabels; IString fromName(Name name, NameScope scope) { @@ -791,15 +760,17 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo Ref visitBlock(Block* curr) { Ref ret = ValueBuilder::makeBlock(); size_t size = curr->list.size(); - auto noResults = result == NO_RESULT ? size : size-1; + auto noResults = result == NO_RESULT ? size : size - 1; for (size_t i = 0; i < noResults; i++) { - flattenAppend(ret, ValueBuilder::makeStatement(visit(curr->list[i], NO_RESULT))); + flattenAppend( + ret, ValueBuilder::makeStatement(visit(curr->list[i], NO_RESULT))); } if (result != NO_RESULT) { - flattenAppend(ret, visitAndAssign(curr->list[size-1], result)); + flattenAppend(ret, visitAndAssign(curr->list[size - 1], result)); } if (curr->name.is()) { - ret = ValueBuilder::makeLabel(fromName(curr->name, NameScope::Label), ret); + ret = + ValueBuilder::makeLabel(fromName(curr->name, NameScope::Label), ret); } return ret; } @@ -822,7 +793,8 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo if (curr->body->type != unreachable) { assert(curr->body->type == none); // flat IR body = blockify(body); - flattenAppend(body, ValueBuilder::makeBreak(fromName(asmLabel, NameScope::Label))); + flattenAppend( + body, ValueBuilder::makeBreak(fromName(asmLabel, NameScope::Label))); } Ref ret = ValueBuilder::makeWhile(ValueBuilder::makeInt(1), body); return ValueBuilder::makeLabel(fromName(asmLabel, NameScope::Label), ret); @@ -860,17 +832,23 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo ret[1]->push_back(theSwitch); for (size_t i = 0; i < curr->targets.size(); i++) { ValueBuilder::appendCaseToSwitch(theSwitch, ValueBuilder::makeNum(i)); - ValueBuilder::appendCodeToSwitch(theSwitch, blockify(makeBreakOrContinue(curr->targets[i])), false); + ValueBuilder::appendCodeToSwitch( + theSwitch, blockify(makeBreakOrContinue(curr->targets[i])), false); } ValueBuilder::appendDefaultToSwitch(theSwitch); - ValueBuilder::appendCodeToSwitch(theSwitch, blockify(makeBreakOrContinue(curr->default_)), false); + ValueBuilder::appendCodeToSwitch( + theSwitch, blockify(makeBreakOrContinue(curr->default_)), false); return ret; } Ref visitCall(Call* curr) { - Ref theCall = ValueBuilder::makeCall(fromName(curr->target, NameScope::Top)); - // For wasm => wasm calls, we don't need coercions. TODO: even imports might be safe? - bool needCoercions = parent->options.optimizeLevel == 0 || standaloneFunction || module->getFunction(curr->target)->imported(); + Ref theCall = + ValueBuilder::makeCall(fromName(curr->target, NameScope::Top)); + // For wasm => wasm calls, we don't need coercions. TODO: even imports + // might be safe? + bool needCoercions = parent->options.optimizeLevel == 0 || + standaloneFunction || + module->getFunction(curr->target)->imported(); for (auto operand : curr->operands) { auto value = visit(operand, EXPRESSION_RESULT); if (needCoercions) { @@ -885,12 +863,14 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo } Ref visitCallIndirect(CallIndirect* curr) { - // If the target has effects that interact with the operands, we must reorder it to the start. + // If the target has effects that interact with the operands, we must + // reorder it to the start. bool mustReorder = false; EffectAnalyzer targetEffects(parent->options, curr->target); if (targetEffects.hasAnything()) { for (auto* operand : curr->operands) { - if (targetEffects.invalidates(EffectAnalyzer(parent->options, operand))) { + if (targetEffects.invalidates( + EffectAnalyzer(parent->options, operand))) { mustReorder = true; break; } @@ -907,13 +887,12 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo } sequenceAppend(ret, visitAndAssign(curr->target, idx)); Ref theCall = ValueBuilder::makeCall(ValueBuilder::makeSub( - ValueBuilder::makeName(FUNCTION_TABLE), - idx.getAstName() - )); + ValueBuilder::makeName(FUNCTION_TABLE), idx.getAstName())); for (size_t i = 0; i < temps.size(); i++) { IString temp = temps[i]->temp; - auto &operand = curr->operands[i]; - theCall[2]->push_back(makeAsmCoercion(ValueBuilder::makeName(temp), wasmToAsmType(operand->type))); + auto& operand = curr->operands[i]; + theCall[2]->push_back(makeAsmCoercion(ValueBuilder::makeName(temp), + wasmToAsmType(operand->type))); } theCall = makeAsmCoercion(theCall, wasmToAsmType(curr->type)); sequenceAppend(ret, theCall); @@ -923,10 +902,9 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo return ret; } else { // Target has no side effects, emit simple code - Ref theCall = ValueBuilder::makeCall(ValueBuilder::makeSub( - ValueBuilder::makeName(FUNCTION_TABLE), - visit(curr->target, EXPRESSION_RESULT) - )); + Ref theCall = ValueBuilder::makeCall( + ValueBuilder::makeSub(ValueBuilder::makeName(FUNCTION_TABLE), + visit(curr->target, EXPRESSION_RESULT))); for (auto* operand : curr->operands) { theCall[2]->push_back(visit(operand, EXPRESSION_RESULT)); } @@ -936,26 +914,26 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo } // TODO: remove - Ref makeSetVar(Expression* curr, Expression* value, Name name, NameScope scope) { + Ref makeSetVar(Expression* curr, + Expression* value, + Name name, + NameScope scope) { return ValueBuilder::makeBinary( - ValueBuilder::makeName(fromName(name, scope)), SET, - visit(value, EXPRESSION_RESULT) - ); + ValueBuilder::makeName(fromName(name, scope)), + SET, + visit(value, EXPRESSION_RESULT)); } Ref visitGetLocal(GetLocal* curr) { return ValueBuilder::makeName( - fromName(func->getLocalNameOrGeneric(curr->index), NameScope::Local) - ); + fromName(func->getLocalNameOrGeneric(curr->index), NameScope::Local)); } Ref visitSetLocal(SetLocal* curr) { - return makeSetVar( - curr, - curr->value, - func->getLocalNameOrGeneric(curr->index), - NameScope::Local - ); + return makeSetVar(curr, + curr->value, + func->getLocalNameOrGeneric(curr->index), + NameScope::Local); } Ref visitGetGlobal(GetGlobal* curr) { @@ -987,8 +965,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo rest = makeAsmCoercion(visit(&load, EXPRESSION_RESULT), ASM_INT); for (size_t i = 1; i < curr->bytes; i++) { ++load.offset; - Ref add = makeAsmCoercion(visit(&load, EXPRESSION_RESULT), ASM_INT); - add = ValueBuilder::makeBinary(add, LSHIFT, ValueBuilder::makeNum(8*i)); + Ref add = + makeAsmCoercion(visit(&load, EXPRESSION_RESULT), ASM_INT); + add = ValueBuilder::makeBinary( + add, LSHIFT, ValueBuilder::makeNum(8 * i)); rest = ValueBuilder::makeBinary(rest, OR, add); } break; @@ -1008,18 +988,21 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo switch (curr->bytes) { case 1: ret = ValueBuilder::makeSub( - ValueBuilder::makeName(LoadUtils::isSignRelevant(curr) && curr->signed_ ? HEAP8 : HEAPU8), - ValueBuilder::makePtrShift(ptr, 0)); + ValueBuilder::makeName( + LoadUtils::isSignRelevant(curr) && curr->signed_ ? HEAP8 + : HEAPU8), + ValueBuilder::makePtrShift(ptr, 0)); break; case 2: ret = ValueBuilder::makeSub( - ValueBuilder::makeName(LoadUtils::isSignRelevant(curr) && curr->signed_ ? HEAP16 : HEAPU16), - ValueBuilder::makePtrShift(ptr, 1)); + ValueBuilder::makeName( + LoadUtils::isSignRelevant(curr) && curr->signed_ ? HEAP16 + : HEAPU16), + ValueBuilder::makePtrShift(ptr, 1)); break; case 4: - ret = ValueBuilder::makeSub( - ValueBuilder::makeName(HEAP32), - ValueBuilder::makePtrShift(ptr, 2)); + ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAP32), + ValueBuilder::makePtrShift(ptr, 2)); break; default: { std::cerr << "Unhandled number of bytes in i32 load: " @@ -1042,10 +1025,11 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo abort(); } } - // Coercions are not actually needed, as if the user reads beyond valid memory, it's - // undefined behavior anyhow, and so we don't care much about slowness of undefined - // values etc. - bool needCoercions = parent->options.optimizeLevel == 0 || standaloneFunction; + // Coercions are not actually needed, as if the user reads beyond valid + // memory, it's undefined behavior anyhow, and so we don't care much about + // slowness of undefined values etc. + bool needCoercions = + parent->options.optimizeLevel == 0 || standaloneFunction; if (needCoercions) { ret = makeAsmCoercion(ret, wasmToAsmType(curr->type)); } @@ -1053,7 +1037,8 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo } Ref visitStore(Store* curr) { - if (module->memory.initial < module->memory.max && curr->type != unreachable) { + if (module->memory.initial < module->memory.max && + curr->type != unreachable) { // In JS, if memory grows then it is dangerous to write // HEAP[f()] = .. // or @@ -1117,7 +1102,7 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo _255.type = i32; for (size_t i = 0; i < curr->bytes; i++) { Const shift(allocator); - shift.value = Literal(int32_t(8*i)); + shift.value = Literal(int32_t(8 * i)); shift.type = i32; Binary shifted(allocator); shifted.op = ShrUInt32; @@ -1126,7 +1111,8 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo shifted.type = i32; Binary anded(allocator); anded.op = AndInt32; - anded.left = i > 0 ? static_cast<Expression*>(&shifted) : static_cast<Expression*>(&getValue); + anded.left = i > 0 ? static_cast<Expression*>(&shifted) + : static_cast<Expression*>(&getValue); anded.right = &_255; anded.type = i32; store.value = &anded; @@ -1141,12 +1127,13 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo break; } default: { - std::cerr << "Unhandled type in store: " << curr->valueType + std::cerr << "Unhandled type in store: " << curr->valueType << std::endl; abort(); } } - return ValueBuilder::makeSeq(ValueBuilder::makeSeq(ptrSet, valueSet), rest); + return ValueBuilder::makeSeq(ValueBuilder::makeSeq(ptrSet, valueSet), + rest); } // normal store Ref ptr = makePointer(curr->ptr, curr->offset); @@ -1155,15 +1142,31 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo switch (curr->valueType) { case i32: { switch (curr->bytes) { - case 1: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAP8), ValueBuilder::makePtrShift(ptr, 0)); break; - case 2: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAP16), ValueBuilder::makePtrShift(ptr, 1)); break; - case 4: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAP32), ValueBuilder::makePtrShift(ptr, 2)); break; - default: abort(); + case 1: + ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAP8), + ValueBuilder::makePtrShift(ptr, 0)); + break; + case 2: + ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAP16), + ValueBuilder::makePtrShift(ptr, 1)); + break; + case 4: + ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAP32), + ValueBuilder::makePtrShift(ptr, 2)); + break; + default: + abort(); } break; } - case f32: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF32), ValueBuilder::makePtrShift(ptr, 2)); break; - case f64: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF64), ValueBuilder::makePtrShift(ptr, 3)); break; + case f32: + ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF32), + ValueBuilder::makePtrShift(ptr, 2)); + break; + case f64: + ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF64), + ValueBuilder::makePtrShift(ptr, 3)); + break; default: { std::cerr << "Unhandled type in store: " << curr->valueType << std::endl; @@ -1173,19 +1176,18 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo return ValueBuilder::makeBinary(ret, SET, value); } - Ref visitDrop(Drop* curr) { - return visit(curr->value, NO_RESULT); - } + Ref visitDrop(Drop* curr) { return visit(curr->value, NO_RESULT); } Ref visitConst(Const* curr) { switch (curr->type) { - case i32: return ValueBuilder::makeInt(curr->value.geti32()); + case i32: + return ValueBuilder::makeInt(curr->value.geti32()); // An i64 argument translates to two actual arguments to asm.js // functions, so we do a bit of a hack here to get our one `Ref` to look // like two function arguments. case i64: { - auto lo = (unsigned) curr->value.geti64(); - auto hi = (unsigned) (curr->value.geti64() >> 32); + auto lo = (unsigned)curr->value.geti64(); + auto hi = (unsigned)(curr->value.geti64() >> 32); std::ostringstream out; out << lo << "," << hi; std::string os = out.str(); @@ -1203,11 +1205,15 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo case f64: { double d = curr->value.getf64(); if (d == 0 && std::signbit(d)) { // negative zero - return ValueBuilder::makeUnary(PLUS, ValueBuilder::makeUnary(MINUS, ValueBuilder::makeDouble(0))); + return ValueBuilder::makeUnary( + PLUS, + ValueBuilder::makeUnary(MINUS, ValueBuilder::makeDouble(0))); } - return ValueBuilder::makeUnary(PLUS, ValueBuilder::makeDouble(curr->value.getf64())); + return ValueBuilder::makeUnary( + PLUS, ValueBuilder::makeDouble(curr->value.getf64())); } - default: abort(); + default: + abort(); } } @@ -1218,37 +1224,37 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo switch (curr->op) { case ClzInt32: return ValueBuilder::makeCall( - MATH_CLZ32, - visit(curr->value, EXPRESSION_RESULT) - ); + MATH_CLZ32, visit(curr->value, EXPRESSION_RESULT)); case CtzInt32: case PopcntInt32: std::cerr << "i32 unary should have been removed: " << curr << std::endl; WASM_UNREACHABLE(); case EqZInt32: - // XXX !x does change the type to bool, which is correct, but may be slower? - return ValueBuilder::makeUnary(L_NOT, visit(curr->value, EXPRESSION_RESULT)); + // XXX !x does change the type to bool, which is correct, but may + // be slower? + return ValueBuilder::makeUnary( + L_NOT, visit(curr->value, EXPRESSION_RESULT)); case ReinterpretFloat32: { - ABI::wasm2js::ensureScratchMemoryHelpers(module, ABI::wasm2js::SCRATCH_STORE_F32); - ABI::wasm2js::ensureScratchMemoryHelpers(module, ABI::wasm2js::SCRATCH_LOAD_I32); - - Ref store = ValueBuilder::makeCall( - ABI::wasm2js::SCRATCH_STORE_F32, - visit(curr->value, EXPRESSION_RESULT) - ); - Ref load = ValueBuilder::makeCall(ABI::wasm2js::SCRATCH_LOAD_I32, ValueBuilder::makeInt(0)); + ABI::wasm2js::ensureScratchMemoryHelpers( + module, ABI::wasm2js::SCRATCH_STORE_F32); + ABI::wasm2js::ensureScratchMemoryHelpers( + module, ABI::wasm2js::SCRATCH_LOAD_I32); + + Ref store = + ValueBuilder::makeCall(ABI::wasm2js::SCRATCH_STORE_F32, + visit(curr->value, EXPRESSION_RESULT)); + Ref load = ValueBuilder::makeCall(ABI::wasm2js::SCRATCH_LOAD_I32, + ValueBuilder::makeInt(0)); return ValueBuilder::makeSeq(store, load); } // generate (~~expr), what Emscripten does case TruncSFloat32ToInt32: case TruncSFloat64ToInt32: return ValueBuilder::makeUnary( - B_NOT, - ValueBuilder::makeUnary( - B_NOT, - visit(curr->value, EXPRESSION_RESULT) - )); + B_NOT, + ValueBuilder::makeUnary(B_NOT, + visit(curr->value, EXPRESSION_RESULT))); // generate (~~expr >>> 0), what Emscripten does case TruncUFloat32ToInt32: @@ -1257,13 +1263,9 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo ValueBuilder::makeUnary( B_NOT, ValueBuilder::makeUnary( - B_NOT, - visit(curr->value, EXPRESSION_RESULT) - ) - ), + B_NOT, visit(curr->value, EXPRESSION_RESULT))), TRSHIFT, - ValueBuilder::makeNum(0) - ); + ValueBuilder::makeNum(0)); default: { std::cerr << "Unhandled unary i32 operator: " << curr @@ -1279,37 +1281,27 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo case NegFloat32: case NegFloat64: ret = ValueBuilder::makeUnary( - MINUS, - visit(curr->value, EXPRESSION_RESULT) - ); + MINUS, visit(curr->value, EXPRESSION_RESULT)); break; case AbsFloat32: case AbsFloat64: ret = ValueBuilder::makeCall( - MATH_ABS, - visit(curr->value, EXPRESSION_RESULT) - ); + MATH_ABS, visit(curr->value, EXPRESSION_RESULT)); break; case CeilFloat32: case CeilFloat64: ret = ValueBuilder::makeCall( - MATH_CEIL, - visit(curr->value, EXPRESSION_RESULT) - ); + MATH_CEIL, visit(curr->value, EXPRESSION_RESULT)); break; case FloorFloat32: case FloorFloat64: ret = ValueBuilder::makeCall( - MATH_FLOOR, - visit(curr->value, EXPRESSION_RESULT) - ); + MATH_FLOOR, visit(curr->value, EXPRESSION_RESULT)); break; case SqrtFloat32: case SqrtFloat64: ret = ValueBuilder::makeCall( - MATH_SQRT, - visit(curr->value, EXPRESSION_RESULT) - ); + MATH_SQRT, visit(curr->value, EXPRESSION_RESULT)); break; case PromoteFloat32: return makeAsmCoercion(visit(curr->value, EXPRESSION_RESULT), @@ -1318,14 +1310,15 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo return makeAsmCoercion(visit(curr->value, EXPRESSION_RESULT), ASM_FLOAT); case ReinterpretInt32: { - ABI::wasm2js::ensureScratchMemoryHelpers(module, ABI::wasm2js::SCRATCH_STORE_I32); - ABI::wasm2js::ensureScratchMemoryHelpers(module, ABI::wasm2js::SCRATCH_LOAD_F32); - - Ref store = ValueBuilder::makeCall( - ABI::wasm2js::SCRATCH_STORE_I32, - ValueBuilder::makeNum(0), - visit(curr->value, EXPRESSION_RESULT) - ); + ABI::wasm2js::ensureScratchMemoryHelpers( + module, ABI::wasm2js::SCRATCH_STORE_I32); + ABI::wasm2js::ensureScratchMemoryHelpers( + module, ABI::wasm2js::SCRATCH_LOAD_F32); + + Ref store = + ValueBuilder::makeCall(ABI::wasm2js::SCRATCH_STORE_I32, + ValueBuilder::makeNum(0), + visit(curr->value, EXPRESSION_RESULT)); Ref load = ValueBuilder::makeCall(ABI::wasm2js::SCRATCH_LOAD_F32); return ValueBuilder::makeSeq(store, load); } @@ -1333,40 +1326,33 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo case ConvertSInt32ToFloat32: return makeAsmCoercion( makeAsmCoercion(visit(curr->value, EXPRESSION_RESULT), ASM_INT), - ASM_FLOAT - ); + ASM_FLOAT); case ConvertSInt32ToFloat64: return makeAsmCoercion( makeAsmCoercion(visit(curr->value, EXPRESSION_RESULT), ASM_INT), - ASM_DOUBLE - ); + ASM_DOUBLE); // Generate (expr >>> 0), followed by a coercion case ConvertUInt32ToFloat32: return makeAsmCoercion( - ValueBuilder::makeBinary( - visit(curr->value, EXPRESSION_RESULT), - TRSHIFT, - ValueBuilder::makeInt(0) - ), - ASM_FLOAT - ); + ValueBuilder::makeBinary(visit(curr->value, EXPRESSION_RESULT), + TRSHIFT, + ValueBuilder::makeInt(0)), + ASM_FLOAT); case ConvertUInt32ToFloat64: return makeAsmCoercion( - ValueBuilder::makeBinary( - visit(curr->value, EXPRESSION_RESULT), - TRSHIFT, - ValueBuilder::makeInt(0) - ), - ASM_DOUBLE - ); + ValueBuilder::makeBinary(visit(curr->value, EXPRESSION_RESULT), + TRSHIFT, + ValueBuilder::makeInt(0)), + ASM_DOUBLE); // TODO: more complex unary conversions case NearestFloat32: case NearestFloat64: case TruncFloat32: case TruncFloat64: - std::cerr << "operation should have been removed in previous passes" - << std::endl; + std::cerr + << "operation should have been removed in previous passes" + << std::endl; WASM_UNREACHABLE(); default: @@ -1409,19 +1395,23 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo } } case DivSInt32: - ret = ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), DIV, + ret = ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), + DIV, makeSigning(right, ASM_SIGNED)); break; case DivUInt32: - ret = ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), DIV, + ret = ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), + DIV, makeSigning(right, ASM_UNSIGNED)); break; case RemSInt32: - ret = ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), MOD, + ret = ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), + MOD, makeSigning(right, ASM_SIGNED)); break; case RemUInt32: - ret = ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), MOD, + ret = ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), + MOD, makeSigning(right, ASM_UNSIGNED)); break; case AndInt32: @@ -1443,36 +1433,46 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo ret = ValueBuilder::makeBinary(left, RSHIFT, right); break; case EqInt32: { - return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), EQ, + return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), + EQ, makeSigning(right, ASM_SIGNED)); } case NeInt32: { - return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), NE, + return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), + NE, makeSigning(right, ASM_SIGNED)); } case LtSInt32: - return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), LT, + return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), + LT, makeSigning(right, ASM_SIGNED)); case LtUInt32: - return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), LT, + return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), + LT, makeSigning(right, ASM_UNSIGNED)); case LeSInt32: - return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), LE, + return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), + LE, makeSigning(right, ASM_SIGNED)); case LeUInt32: - return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), LE, + return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), + LE, makeSigning(right, ASM_UNSIGNED)); case GtSInt32: - return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), GT, + return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), + GT, makeSigning(right, ASM_SIGNED)); case GtUInt32: - return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), GT, + return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), + GT, makeSigning(right, ASM_UNSIGNED)); case GeSInt32: - return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), GE, + return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), + GE, makeSigning(right, ASM_SIGNED)); case GeUInt32: - return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), GE, + return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), + GE, makeSigning(right, ASM_UNSIGNED)); case EqFloat32: case EqFloat64: @@ -1497,7 +1497,8 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo std::cerr << "should be removed already" << std::endl; WASM_UNREACHABLE(); default: { - std::cerr << "Unhandled i32 binary operator: " << curr << std::endl; + std::cerr << "Unhandled i32 binary operator: " << curr + << std::endl; abort(); } } @@ -1505,10 +1506,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo } case f32: case f64: - switch (curr->op) { - case AddFloat32: - case AddFloat64: - ret = ValueBuilder::makeBinary(left, PLUS, right); + switch (curr->op) { + case AddFloat32: + case AddFloat64: + ret = ValueBuilder::makeBinary(left, PLUS, right); break; case SubFloat32: case SubFloat64: @@ -1533,7 +1534,8 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo case CopySignFloat32: case CopySignFloat64: default: - std::cerr << "Unhandled binary float operator: " << curr << std::endl; + std::cerr << "Unhandled binary float operator: " << curr + << std::endl; abort(); } if (curr->type == f32) { @@ -1548,48 +1550,41 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo } Ref visitSelect(Select* curr) { - // If the condition has effects that interact with the operands, we must reorder it to the start. - // We must also use locals if the values have side effects, as a JS conditional does not - // visit both sides. + // If the condition has effects that interact with the operands, we must + // reorder it to the start. We must also use locals if the values have + // side effects, as a JS conditional does not visit both sides. bool useLocals = false; EffectAnalyzer conditionEffects(parent->options, curr->condition); EffectAnalyzer ifTrueEffects(parent->options, curr->ifTrue); EffectAnalyzer ifFalseEffects(parent->options, curr->ifFalse); if (conditionEffects.invalidates(ifTrueEffects) || conditionEffects.invalidates(ifFalseEffects) || - ifTrueEffects.hasSideEffects() || - ifFalseEffects.hasSideEffects()) { + ifTrueEffects.hasSideEffects() || ifFalseEffects.hasSideEffects()) { useLocals = true; } if (useLocals) { ScopedTemp tempIfTrue(curr->type, parent, func), - tempIfFalse(curr->type, parent, func), - tempCondition(i32, parent, func); + tempIfFalse(curr->type, parent, func), + tempCondition(i32, parent, func); Ref ifTrue = visit(curr->ifTrue, EXPRESSION_RESULT); Ref ifFalse = visit(curr->ifFalse, EXPRESSION_RESULT); Ref condition = visit(curr->condition, EXPRESSION_RESULT); - return + return ValueBuilder::makeSeq( + ValueBuilder::makeBinary(tempIfTrue.getAstName(), SET, ifTrue), ValueBuilder::makeSeq( - ValueBuilder::makeBinary(tempIfTrue.getAstName(), SET, ifTrue), + ValueBuilder::makeBinary(tempIfFalse.getAstName(), SET, ifFalse), ValueBuilder::makeSeq( - ValueBuilder::makeBinary(tempIfFalse.getAstName(), SET, ifFalse), - ValueBuilder::makeSeq( - ValueBuilder::makeBinary(tempCondition.getAstName(), SET, condition), - ValueBuilder::makeConditional( - tempCondition.getAstName(), - tempIfTrue.getAstName(), - tempIfFalse.getAstName() - ) - ) - ) - ); + ValueBuilder::makeBinary( + tempCondition.getAstName(), SET, condition), + ValueBuilder::makeConditional(tempCondition.getAstName(), + tempIfTrue.getAstName(), + tempIfFalse.getAstName())))); } else { // Simple case without reordering. return ValueBuilder::makeConditional( visit(curr->condition, EXPRESSION_RESULT), visit(curr->ifTrue, EXPRESSION_RESULT), - visit(curr->ifFalse, EXPRESSION_RESULT) - ); + visit(curr->ifFalse, EXPRESSION_RESULT)); } } @@ -1598,7 +1593,9 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo return ValueBuilder::makeReturn(Ref()); } Ref val = visit(curr->value, EXPRESSION_RESULT); - bool needCoercion = parent->options.optimizeLevel == 0 || standaloneFunction || parent->functionsCallableFromOutside.count(func->name); + bool needCoercion = + parent->options.optimizeLevel == 0 || standaloneFunction || + parent->functionsCallableFromOutside.count(func->name); if (needCoercion) { val = makeAsmCoercion(val, wasmToAsmType(curr->value->type)); } @@ -1607,11 +1604,12 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo Ref visitHost(Host* curr) { if (curr->op == HostOp::GrowMemory) { - if (module->memory.exists && module->memory.max > module->memory.initial) { - return ValueBuilder::makeCall(WASM_GROW_MEMORY, - makeAsmCoercion( - visit(curr->operands[0], EXPRESSION_RESULT), - wasmToAsmType(curr->operands[0]->type))); + if (module->memory.exists && + module->memory.max > module->memory.initial) { + return ValueBuilder::makeCall( + WASM_GROW_MEMORY, + makeAsmCoercion(visit(curr->operands[0], EXPRESSION_RESULT), + wasmToAsmType(curr->operands[0]->type))); } else { return ValueBuilder::makeCall(ABORT_FUNC); } @@ -1621,9 +1619,7 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo WASM_UNREACHABLE(); // TODO } - Ref visitNop(Nop* curr) { - return ValueBuilder::makeToplevel(); - } + Ref visitNop(Nop* curr) { return ValueBuilder::makeToplevel(); } Ref visitUnreachable(Unreachable* curr) { return ValueBuilder::makeCall(ABORT_FUNC); @@ -1634,14 +1630,15 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo auto ret = visit(ptr, EXPRESSION_RESULT); if (offset) { ret = makeAsmCoercion( - ValueBuilder::makeBinary(ret, PLUS, ValueBuilder::makeNum(offset)), - ASM_INT); + ValueBuilder::makeBinary(ret, PLUS, ValueBuilder::makeNum(offset)), + ASM_INT); } return ret; } }; - return ExpressionProcessor(this, m, func, standaloneFunction).visit(func->body, NO_RESULT); + return ExpressionProcessor(this, m, func, standaloneFunction) + .visit(func->body, NO_RESULT); } void Wasm2JSBuilder::addMemoryGrowthFuncs(Ref ast, Module* wasm) { @@ -1649,50 +1646,43 @@ void Wasm2JSBuilder::addMemoryGrowthFuncs(Ref ast, Module* wasm) { ValueBuilder::appendArgumentToFunction(growMemoryFunc, IString("pagesToAdd")); growMemoryFunc[3]->push_back( - ValueBuilder::makeStatement( - ValueBuilder::makeBinary( - ValueBuilder::makeName(IString("pagesToAdd")), SET, - makeAsmCoercion( - ValueBuilder::makeName(IString("pagesToAdd")), - AsmType::ASM_INT - ) - ) - ) - ); + ValueBuilder::makeStatement(ValueBuilder::makeBinary( + ValueBuilder::makeName(IString("pagesToAdd")), + SET, + makeAsmCoercion(ValueBuilder::makeName(IString("pagesToAdd")), + AsmType::ASM_INT)))); Ref oldPages = ValueBuilder::makeVar(); growMemoryFunc[3]->push_back(oldPages); ValueBuilder::appendToVar( oldPages, IString("oldPages"), - makeAsmCoercion(ValueBuilder::makeCall(WASM_CURRENT_MEMORY), AsmType::ASM_INT)); + makeAsmCoercion(ValueBuilder::makeCall(WASM_CURRENT_MEMORY), + AsmType::ASM_INT)); Ref newPages = ValueBuilder::makeVar(); growMemoryFunc[3]->push_back(newPages); ValueBuilder::appendToVar( newPages, IString("newPages"), - makeAsmCoercion(ValueBuilder::makeBinary( - ValueBuilder::makeName(IString("oldPages")), - PLUS, - ValueBuilder::makeName(IString("pagesToAdd")) - ), AsmType::ASM_INT)); + makeAsmCoercion( + ValueBuilder::makeBinary(ValueBuilder::makeName(IString("oldPages")), + PLUS, + ValueBuilder::makeName(IString("pagesToAdd"))), + AsmType::ASM_INT)); Ref block = ValueBuilder::makeBlock(); growMemoryFunc[3]->push_back(ValueBuilder::makeIf( ValueBuilder::makeBinary( - ValueBuilder::makeBinary( - ValueBuilder::makeName(IString("oldPages")), - LT, - ValueBuilder::makeName(IString("newPages")) - ), + ValueBuilder::makeBinary(ValueBuilder::makeName(IString("oldPages")), + LT, + ValueBuilder::makeName(IString("newPages"))), IString("&&"), - ValueBuilder::makeBinary( - ValueBuilder::makeName(IString("newPages")), - LT, - ValueBuilder::makeInt(Memory::kMaxSize) - ) - ), block, NULL)); + ValueBuilder::makeBinary(ValueBuilder::makeName(IString("newPages")), + LT, + ValueBuilder::makeInt(Memory::kMaxSize))), + block, + NULL)); Ref newBuffer = ValueBuilder::makeVar(); ValueBuilder::appendToBlock(block, newBuffer); @@ -1701,110 +1691,79 @@ void Wasm2JSBuilder::addMemoryGrowthFuncs(Ref ast, Module* wasm) { IString("newBuffer"), ValueBuilder::makeNew(ValueBuilder::makeCall( ARRAY_BUFFER, - ValueBuilder::makeCall( - MATH_IMUL, - ValueBuilder::makeName(IString("newPages")), - ValueBuilder::makeInt(Memory::kPageSize))))); + ValueBuilder::makeCall(MATH_IMUL, + ValueBuilder::makeName(IString("newPages")), + ValueBuilder::makeInt(Memory::kPageSize))))); Ref newHEAP8 = ValueBuilder::makeVar(); ValueBuilder::appendToBlock(block, newHEAP8); ValueBuilder::appendToVar( newHEAP8, IString("newHEAP8"), - ValueBuilder::makeNew( - ValueBuilder::makeCall( - ValueBuilder::makeDot( - ValueBuilder::makeName(GLOBAL), - INT8ARRAY - ), - ValueBuilder::makeName(IString("newBuffer")) - ) - )); - - ValueBuilder::appendToBlock(block, + ValueBuilder::makeNew(ValueBuilder::makeCall( + ValueBuilder::makeDot(ValueBuilder::makeName(GLOBAL), INT8ARRAY), + ValueBuilder::makeName(IString("newBuffer"))))); + + ValueBuilder::appendToBlock( + block, ValueBuilder::makeCall( - ValueBuilder::makeDot( - ValueBuilder::makeName(IString("newHEAP8")), - IString("set") - ), - ValueBuilder::makeName(HEAP8) - ) - ); - - ValueBuilder::appendToBlock(block, - ValueBuilder::makeBinary( - ValueBuilder::makeName(HEAP8), - SET, - ValueBuilder::makeName(IString("newHEAP8")) - ) - ); + ValueBuilder::makeDot(ValueBuilder::makeName(IString("newHEAP8")), + IString("set")), + ValueBuilder::makeName(HEAP8))); + + ValueBuilder::appendToBlock( + block, + ValueBuilder::makeBinary(ValueBuilder::makeName(HEAP8), + SET, + ValueBuilder::makeName(IString("newHEAP8")))); auto setHeap = [&](IString name, IString view) { - ValueBuilder::appendToBlock(block, + ValueBuilder::appendToBlock( + block, ValueBuilder::makeBinary( ValueBuilder::makeName(name), SET, - ValueBuilder::makeNew( - ValueBuilder::makeCall( - ValueBuilder::makeDot( - ValueBuilder::makeName(GLOBAL), - view - ), - ValueBuilder::makeName(IString("newBuffer")) - ) - ) - ) - ); + ValueBuilder::makeNew(ValueBuilder::makeCall( + ValueBuilder::makeDot(ValueBuilder::makeName(GLOBAL), view), + ValueBuilder::makeName(IString("newBuffer")))))); }; setHeap(HEAP16, INT16ARRAY); setHeap(HEAP32, INT32ARRAY); - setHeap(HEAPU8, UINT8ARRAY); + setHeap(HEAPU8, UINT8ARRAY); setHeap(HEAPU16, UINT16ARRAY); setHeap(HEAPU32, UINT32ARRAY); setHeap(HEAPF32, FLOAT32ARRAY); setHeap(HEAPF64, FLOAT64ARRAY); - ValueBuilder::appendToBlock(block, - ValueBuilder::makeBinary( - ValueBuilder::makeName(BUFFER), - SET, - ValueBuilder::makeName(IString("newBuffer")) - ) - ); + ValueBuilder::appendToBlock( + block, + ValueBuilder::makeBinary(ValueBuilder::makeName(BUFFER), + SET, + ValueBuilder::makeName(IString("newBuffer")))); // apply the changes to the memory import if (wasm->memory.imported()) { - ValueBuilder::appendToBlock(block, + ValueBuilder::appendToBlock( + block, ValueBuilder::makeBinary( - ValueBuilder::makeDot( - ValueBuilder::makeName("memory"), - ValueBuilder::makeName(BUFFER) - ), + ValueBuilder::makeDot(ValueBuilder::makeName("memory"), + ValueBuilder::makeName(BUFFER)), SET, - ValueBuilder::makeName(IString("newBuffer")) - ) - ); + ValueBuilder::makeName(IString("newBuffer")))); } growMemoryFunc[3]->push_back( - ValueBuilder::makeReturn( - ValueBuilder::makeName(IString("oldPages")))); + ValueBuilder::makeReturn(ValueBuilder::makeName(IString("oldPages")))); Ref currentMemoryFunc = ValueBuilder::makeFunction(WASM_CURRENT_MEMORY); currentMemoryFunc[3]->push_back(ValueBuilder::makeReturn( - makeAsmCoercion( - ValueBuilder::makeBinary( - ValueBuilder::makeDot( - ValueBuilder::makeName(BUFFER), - IString("byteLength") - ), - DIV, - ValueBuilder::makeInt(Memory::kPageSize) - ), - AsmType::ASM_INT - ) - )); + makeAsmCoercion(ValueBuilder::makeBinary( + ValueBuilder::makeDot(ValueBuilder::makeName(BUFFER), + IString("byteLength")), + DIV, + ValueBuilder::makeInt(Memory::kPageSize)), + AsmType::ASM_INT))); ast->push_back(growMemoryFunc); ast->push_back(currentMemoryFunc); } @@ -1814,7 +1773,11 @@ void Wasm2JSBuilder::addMemoryGrowthFuncs(Ref ast, Module* wasm) { // "glue" around that. class Wasm2JSGlue { public: - Wasm2JSGlue(Module& wasm, Output& out, Wasm2JSBuilder::Flags flags, Name moduleName) : wasm(wasm), out(out), flags(flags), moduleName(moduleName) {} + Wasm2JSGlue(Module& wasm, + Output& out, + Wasm2JSBuilder::Flags flags, + Name moduleName) + : wasm(wasm), out(out), flags(flags), moduleName(moduleName) {} void emitPre(); void emitPost(); @@ -1830,7 +1793,9 @@ private: void emitPostEmscripten(); void emitPostES6(); - void emitMemory(std::string buffer, std::string segmentWriter, std::function<std::string (std::string)> accessGlobal); + void emitMemory(std::string buffer, + std::string segmentWriter, + std::function<std::string(std::string)> accessGlobal); void emitScratchMemorySupport(); }; @@ -1845,7 +1810,8 @@ void Wasm2JSGlue::emitPre() { } void Wasm2JSGlue::emitPreEmscripten() { - out << "function instantiate(asmLibraryArg, wasmMemory, FUNCTION_TABLE) {\n\n"; + out + << "function instantiate(asmLibraryArg, wasmMemory, FUNCTION_TABLE) {\n\n"; } void Wasm2JSGlue::emitPreES6() { @@ -1853,7 +1819,8 @@ void Wasm2JSGlue::emitPreES6() { auto noteImport = [&](Name module, Name base) { // Right now codegen requires a flat namespace going into the module, - // meaning we don't support importing the same name from multiple namespaces yet. + // meaning we don't support importing the same name from multiple namespaces + // yet. if (baseModuleMap.count(base) && baseModuleMap[base] != module) { Fatal() << "the name " << base << " cannot be imported from " << "two different modules yet\n"; @@ -1861,20 +1828,16 @@ void Wasm2JSGlue::emitPreES6() { } baseModuleMap[base] = module; - out << "import { " - << base.str - << " } from '" - << module.str - << "';\n"; + out << "import { " << base.str << " } from '" << module.str << "';\n"; }; ImportInfo imports(wasm); - ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) { - noteImport(import->module, import->base); - }); + ModuleUtils::iterImportedGlobals( + wasm, [&](Global* import) { noteImport(import->module, import->base); }); ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) { - // The scratch memory helpers are emitted in the glue, see code and comments below. + // The scratch memory helpers are emitted in the glue, see code and comments + // below. if (ABI::wasm2js::isScratchMemoryHelper(import->base)) { return; } @@ -1932,8 +1895,7 @@ void Wasm2JSGlue::emitPostES6() { { auto pages = wasm.memory.initial == 0 ? 1 : wasm.memory.initial.addr; out << "var mem" << moduleName.str << " = new ArrayBuffer(" - << pages * Memory::kPageSize - << ");\n"; + << pages * Memory::kPageSize << ");\n"; } emitMemory(std::string("mem") + moduleName.str, @@ -1943,23 +1905,24 @@ void Wasm2JSGlue::emitPostES6() { // Actually invoke the `asmFunc` generated function, passing in all global // values followed by all imports out << "var ret" << moduleName.str << " = " << moduleName.str << "({" - << "Math," - << "Int8Array," - << "Uint8Array," - << "Int16Array," - << "Uint16Array," - << "Int32Array," - << "Uint32Array," - << "Float32Array," - << "Float64Array," - << "NaN," - << "Infinity" - << "}, {"; + << "Math," + << "Int8Array," + << "Uint8Array," + << "Int16Array," + << "Uint16Array," + << "Int32Array," + << "Uint32Array," + << "Float32Array," + << "Float64Array," + << "NaN," + << "Infinity" + << "}, {"; out << "abort:function() { throw new Error('abort'); }"; ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) { - // The scratch memory helpers are emitted in the glue, see code and comments below. + // The scratch memory helpers are emitted in the glue, see code and comments + // below. if (ABI::wasm2js::isScratchMemoryHelper(import->base)) { return; } @@ -1984,25 +1947,24 @@ void Wasm2JSGlue::emitPostES6() { continue; } std::ostringstream export_name; - for (auto *ptr = exp->name.str; *ptr; ptr++) { + for (auto* ptr = exp->name.str; *ptr; ptr++) { if (*ptr == '-') { export_name << '_'; } else { export_name << *ptr; } } - out << "export var " - << asmangle(exp->name.str) - << " = ret" - << moduleName.str - << "." - << asmangle(exp->name.str) - << ";\n"; + out << "export var " << asmangle(exp->name.str) << " = ret" + << moduleName.str << "." << asmangle(exp->name.str) << ";\n"; } } -void Wasm2JSGlue::emitMemory(std::string buffer, std::string segmentWriter, std::function<std::string (std::string)> accessGlobal) { - if (wasm.memory.segments.empty()) return; +void Wasm2JSGlue::emitMemory( + std::string buffer, + std::string segmentWriter, + std::function<std::string(std::string)> accessGlobal) { + if (wasm.memory.segments.empty()) + return; auto expr = R"( function(mem) { @@ -2022,11 +1984,11 @@ void Wasm2JSGlue::emitMemory(std::string buffer, std::string segmentWriter, std: )"; // var assign$name = ($expr)(mem$name); - out << "var " << segmentWriter - << " = (" << expr << ")(" << buffer << ");\n"; + out << "var " << segmentWriter << " = (" << expr << ")(" << buffer << ");\n"; auto globalOffset = [&](const Memory::Segment& segment) { - if (auto* c = segment.offset->template dynCast<Const>()) {; + if (auto* c = segment.offset->template dynCast<Const>()) { + ; return std::to_string(c->value.getInteger()); } if (auto* get = segment.offset->template dynCast<GetGlobal>()) { @@ -2039,11 +2001,8 @@ void Wasm2JSGlue::emitMemory(std::string buffer, std::string segmentWriter, std: for (auto& seg : wasm.memory.segments) { assert(!seg.isPassive && "passive segments not implemented yet"); - out << segmentWriter << "(" - << globalOffset(seg) - << ", \"" - << base64Encode(seg.data) - << "\");\n"; + out << segmentWriter << "(" << globalOffset(seg) << ", \"" + << base64Encode(seg.data) << "\");\n"; } } @@ -2058,7 +2017,8 @@ void Wasm2JSGlue::emitScratchMemorySupport() { needScratchMemory = true; } }); - if (!needScratchMemory) return; + if (!needScratchMemory) + return; out << R"( var scratchBuffer = new ArrayBuffer(8); |