summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/emscripten-optimizer/optimizer.h11
-rw-r--r--src/emscripten-optimizer/simple_ast.cpp211
-rw-r--r--src/emscripten-optimizer/simple_ast.h13
-rw-r--r--src/tools/wasm2js.cpp70
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;