diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/emscripten-optimizer/optimizer.h | 11 | ||||
-rw-r--r-- | src/emscripten-optimizer/simple_ast.cpp | 211 | ||||
-rw-r--r-- | src/emscripten-optimizer/simple_ast.h | 13 | ||||
-rw-r--r-- | src/tools/wasm2js.cpp | 70 |
4 files changed, 108 insertions, 197 deletions
diff --git a/src/emscripten-optimizer/optimizer.h b/src/emscripten-optimizer/optimizer.h index 12d7c1433..f6b3aa536 100644 --- a/src/emscripten-optimizer/optimizer.h +++ b/src/emscripten-optimizer/optimizer.h @@ -27,17 +27,6 @@ extern bool preciseF32, extern cashew::Ref extraInfo; -void eliminateDeadFuncs(cashew::Ref ast); -void eliminate(cashew::Ref ast, bool memSafe=false); -void eliminateMemSafe(cashew::Ref ast); -void simplifyExpressions(cashew::Ref ast); -void optimizeFrounds(cashew::Ref ast); -void simplifyIfs(cashew::Ref ast); -void registerize(cashew::Ref ast); -void registerizeHarder(cashew::Ref ast); -void minifyLocals(cashew::Ref ast); -void asmLastOpts(cashew::Ref ast); - // enum AsmType { diff --git a/src/emscripten-optimizer/simple_ast.cpp b/src/emscripten-optimizer/simple_ast.cpp index c9659f964..d6dc5e51a 100644 --- a/src/emscripten-optimizer/simple_ast.cpp +++ b/src/emscripten-optimizer/simple_ast.cpp @@ -183,189 +183,64 @@ void dump(const char *str, Ref node, bool pretty) { std::cerr << std::endl; } -// AST traversals - // Traversals struct TraverseInfo { TraverseInfo() = default; - TraverseInfo(Ref node, ArrayStorage* arr) : node(node), arr(arr), index(0) {} - Ref node; - ArrayStorage* arr; - int index; -}; - -template<class T, int init> -struct StackedStack { // a stack, on the stack - T stackStorage[init]; - T* storage; - int used = 0; - int available = init; // used amount, available amount - bool alloced = false; - - StackedStack() { - storage = stackStorage; - } - ~StackedStack() { - if (alloced) free(storage); - } - - int size() { return used; } - - void push_back(const T& t) { - assert(used <= available); - if (used == available) { - available *= 2; - if (!alloced) { - T* old = storage; - storage = (T*)malloc(sizeof(T)*available); - memcpy(storage, old, sizeof(T)*used); - alloced = true; - } else { - T *newStorage = (T*)realloc(storage, sizeof(T)*available); - assert(newStorage); - storage = newStorage; - } - } - assert(used < available); - assert(storage); - storage[used++] = t; - } - - T& back() { - assert(used > 0); - return storage[used-1]; - } - - void pop_back() { - assert(used > 0); - used--; - } -}; - -#define visitable(node) (node->isArray() && node->size() > 0) - -#define TRAV_STACK 40 - -// Traverse, calling visit before the children -void traversePre(Ref node, std::function<void (Ref)> visit) { - if (!visitable(node)) return; - visit(node); - StackedStack<TraverseInfo, TRAV_STACK> stack; - int index = 0; - ArrayStorage* arr = &node->getArray(); - int arrsize = (int)arr->size(); - Ref* arrdata = &(*arr)[0]; - stack.push_back(TraverseInfo(node, arr)); - while (1) { - if (index < arrsize) { - Ref sub = *(arrdata+index); - index++; - if (visitable(sub)) { - stack.back().index = index; - index = 0; - visit(sub); - arr = &sub->getArray(); - arrsize = (int)arr->size(); - arrdata = &(*arr)[0]; - stack.push_back(TraverseInfo(sub, arr)); + TraverseInfo(Ref node) : node(node) { + assert(node.get()); + if (node->isArray()) { + for (size_t i = 0; i < node->size(); i++) { + maybeAdd(node[i]); } + } else if (node->isAssign()) { + auto assign = node->asAssign(); + maybeAdd(assign->target()); + maybeAdd(assign->value()); + } else if (node->isAssignName()) { + auto assign = node->asAssignName(); + maybeAdd(assign->value()); } else { - stack.pop_back(); - if (stack.size() == 0) break; - TraverseInfo& back = stack.back(); - index = back.index; - arr = back.arr; - arrsize = (int)arr->size(); - arrdata = &(*arr)[0]; + // no children } } -} + Ref node; + size_t index = -1; + std::vector<Ref> children; -// Traverse, calling visitPre before the children and visitPost after -void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::function<void (Ref)> visitPost) { - if (!visitable(node)) return; - visitPre(node); - StackedStack<TraverseInfo, TRAV_STACK> stack; - int index = 0; - ArrayStorage* arr = &node->getArray(); - int arrsize = (int)arr->size(); - Ref* arrdata = &(*arr)[0]; - stack.push_back(TraverseInfo(node, arr)); - while (1) { - if (index < arrsize) { - Ref sub = *(arrdata+index); - index++; - if (visitable(sub)) { - stack.back().index = index; - index = 0; - visitPre(sub); - arr = &sub->getArray(); - arrsize = (int)arr->size(); - arrdata = &(*arr)[0]; - stack.push_back(TraverseInfo(sub, arr)); - } - } else { - visitPost(stack.back().node); - stack.pop_back(); - if (stack.size() == 0) break; - TraverseInfo& back = stack.back(); - index = back.index; - arr = back.arr; - arrsize = (int)arr->size(); - arrdata = &(*arr)[0]; +private: + void maybeAdd(Ref child) { + if (child.get()) { + children.push_back(child); } } -} +}; -// Traverse, calling visitPre before the children and visitPost after. If pre returns false, do not traverse children -void traversePrePostConditional(Ref node, std::function<bool (Ref)> visitPre, std::function<void (Ref)> visitPost) { - if (!visitable(node)) return; - if (!visitPre(node)) return; - StackedStack<TraverseInfo, TRAV_STACK> stack; - int index = 0; - ArrayStorage* arr = &node->getArray(); - int arrsize = (int)arr->size(); - Ref* arrdata = &(*arr)[0]; - stack.push_back(TraverseInfo(node, arr)); - while (1) { - if (index < arrsize) { - Ref sub = *(arrdata+index); - index++; - if (visitable(sub)) { - if (visitPre(sub)) { - stack.back().index = index; - index = 0; - arr = &sub->getArray(); - arrsize = (int)arr->size(); - arrdata = &(*arr)[0]; - stack.push_back(TraverseInfo(sub, arr)); - } +// Traverse, calling visit after the children +void traversePost(Ref node, std::function<void (Ref)> visit) { + std::vector<TraverseInfo> stack; + stack.push_back(TraverseInfo(node)); + while (!stack.empty()) { + TraverseInfo& back = stack.back(); + if (back.index == size_t(-1)) { + // This is the first time we see this. Push its children. + back.index = 0; + for (auto child : back.children) { + stack.emplace_back(child); } - } else { - visitPost(stack.back().node); - stack.pop_back(); - if (stack.size() == 0) break; - TraverseInfo& back = stack.back(); - index = back.index; - arr = back.arr; - arrsize = (int)arr->size(); - arrdata = &(*arr)[0]; + continue; } - } -} - -// Traverses all the top-level functions in the document -void traverseFunctions(Ref ast, std::function<void (Ref)> visit) { - if (!ast || ast->size() == 0) return; - if (ast[0] == TOPLEVEL) { - Ref stats = ast[1]; - for (size_t i = 0; i < stats->size(); i++) { - Ref curr = stats[i]; - if (curr[0] == DEFUN) visit(curr); + if (back.index < back.children.size()) { + // Visit this child. + back.index++; + visit(back.children[back.index - 1]); + continue; } - } else if (ast[0] == DEFUN) { - visit(ast); + assert(back.index == back.children.size()); + // Time to visit the node itself + auto node = back.node; + stack.pop_back(); + visit(node); } } diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h index e5e8c4e80..3d8508a15 100644 --- a/src/emscripten-optimizer/simple_ast.h +++ b/src/emscripten-optimizer/simple_ast.h @@ -527,17 +527,8 @@ struct AssignName : public Value { // AST traversals -// Traverse, calling visit before the children -void traversePre(Ref node, std::function<void (Ref)> visit); - -// Traverse, calling visitPre before the children and visitPost after -void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::function<void (Ref)> visitPost); - -// Traverse, calling visitPre before the children and visitPost after. If pre returns false, do not traverse children -void traversePrePostConditional(Ref node, std::function<bool (Ref)> visitPre, std::function<void (Ref)> visitPost); - -// Traverses all the top-level functions in the document -void traverseFunctions(Ref ast, std::function<void (Ref)> visit); +// Traverse, calling visit after the children +void traversePost(Ref node, std::function<void (Ref)> visit); // JS printing support diff --git a/src/tools/wasm2js.cpp b/src/tools/wasm2js.cpp index bc35ae4eb..f55f2586b 100644 --- a/src/tools/wasm2js.cpp +++ b/src/tools/wasm2js.cpp @@ -23,7 +23,7 @@ #include "support/file.h" #include "wasm-s-parser.h" #include "wasm2js.h" -#include "tool-options.h" +#include "optimization-options.h" using namespace cashew; using namespace wasm; @@ -32,14 +32,70 @@ using namespace wasm; namespace { -static void emitWasm(Module& wasm, Output& output, Wasm2JSBuilder::Flags flags, Name name) { +template<typename T> +static void printJS(Ref ast, T& output) { + JSPrinter jser(true, true, ast); + jser.printAst(); + output << jser.buffer << std::endl; +} + +static void optimizeJS(Ref ast) { + // helpers + auto isOrZero = [](Ref node) { + return node->isArray() && node->size() > 0 && node[0] == BINARY && node[1] == OR && node[3]->isNumber() && node[3]->getNumber() == 0; + }; + + auto isBitwise = [](Ref node) { + if (node->isArray() && node->size() > 0 && node[0] == BINARY) { + auto op = node[1]; + 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->size() > 0 && node[0] == BINARY && node[1] == RSHIFT && node[3]->isNumber()) { + if (node[3]->getNumber() == 0) { + node[1]->setString(OR); + } + } + }); + + traversePost(ast, [&](Ref node) { + // x | 0 | 0 => x | 0 + if (isOrZero(node)) { + while (isOrZero(node[2])) { + node[2] = node[2][2]; + } + if (isBitwise(node[2])) { + auto child = node[2]; + node[1] = child[1]; + node[2] = child[2]; + node[3] = child[3]; + } + } + // x | 0 going into a bitwise op => skip the | 0 + else if (isBitwise(node)) { + while (isOrZero(node[2])) { + node[2] = node[2][2]; + } + while (isOrZero(node[3])) { + node[3] = node[3][2]; + } + } + }); +} + +static void emitWasm(Module& wasm, Output& output, Wasm2JSBuilder::Flags flags, Name name, bool optimize=false) { Wasm2JSBuilder wasm2js(flags); auto js = wasm2js.processWasm(&wasm, name); + if (optimize) { + optimizeJS(js); + } Wasm2JSGlue glue(wasm, output, flags, name); glue.emitPre(); - JSPrinter jser(true, true, js); - jser.printAst(); - output << jser.buffer << std::endl; + printJS(js, output); glue.emitPost(); } @@ -352,7 +408,7 @@ void AssertionEmitter::emit() { int main(int argc, const char *argv[]) { Wasm2JSBuilder::Flags flags; - ToolOptions 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, @@ -438,7 +494,7 @@ int main(int argc, const char *argv[]) { if (!binaryInput && options.extra["asserts"] == "1") { AssertionEmitter(*root, *sexprBuilder, output, flags).emit(); } else { - emitWasm(wasm, output, flags, "asmFunc"); + emitWasm(wasm, output, flags, "asmFunc", options.passOptions.optimizeLevel > 0); } if (options.debug) std::cerr << "done." << std::endl; |