summaryrefslogtreecommitdiff
path: root/src/emscripten-optimizer
diff options
context:
space:
mode:
authorThomas Lively <tlively@users.noreply.github.com>2017-08-02 20:20:14 -0700
committerAlon Zakai <alonzakai@gmail.com>2017-08-02 20:20:14 -0700
commitffd9a72d28d36915fb173a6d52fbb6e43f7c15db (patch)
tree0dbd0f9d99fc20ab1c1e63395e8f399fd51cf806 /src/emscripten-optimizer
parentde15161e110f26212095c5cf4faf2e3668d2531b (diff)
downloadbinaryen-ffd9a72d28d36915fb173a6d52fbb6e43f7c15db.tar.gz
binaryen-ffd9a72d28d36915fb173a6d52fbb6e43f7c15db.tar.bz2
binaryen-ffd9a72d28d36915fb173a6d52fbb6e43f7c15db.zip
Get wasm2asm building again (#1107)
* Get wasm2asm building again Updates CMakeLists.txt to have wasm2asm built by default, updates wasm2asm.h to account for recent interface changes, and restores JSPrinter functionality. * Implement splice for array values * Clean up wasm2asm testing * Print semicolons after statements in blocks * Cleanups and semicolons for condition arms * Prettify semicolon emission
Diffstat (limited to 'src/emscripten-optimizer')
-rw-r--r--src/emscripten-optimizer/simple_ast.h651
1 files changed, 651 insertions, 0 deletions
diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h
index efbd7b6f6..43bac281d 100644
--- a/src/emscripten-optimizer/simple_ast.h
+++ b/src/emscripten-optimizer/simple_ast.h
@@ -428,6 +428,11 @@ struct Value {
return arr->back();
}
+ void splice(int x, int num) {
+ assert(isArray());
+ arr->erase(arr->begin() + x, arr->begin() + x + num);
+ }
+
int indexOf(Ref other) {
assert(isArray());
for (size_t i = 0; i < arr->size(); i++) {
@@ -537,6 +542,294 @@ void traverseFunctions(Ref ast, std::function<void (Ref)> visit);
// JS printing support
struct JSPrinter {
+ bool pretty, finalize;
+
+ char *buffer;
+ size_t size, used;
+
+ int indent;
+ bool possibleSpace; // add a space to separate identifiers
+
+ Ref ast;
+
+ JSPrinter(bool pretty_, bool finalize_, Ref ast_) : pretty(pretty_), finalize(finalize_), buffer(0), size(0), used(0), indent(0), possibleSpace(false), ast(ast_) {}
+
+ void printAst() {
+ print(ast);
+ buffer[used] = 0;
+ }
+
+ // Utils
+
+ void ensure(int safety=100) {
+ if (size >= used + safety) {
+ return;
+ }
+ size = std::max((size_t)1024, size * 2) + safety;
+ if (!buffer) {
+ buffer = (char*)malloc(size);
+ if (!buffer) {
+ errv("Out of memory allocating %zd bytes for output buffer!", size);
+ abort();
+ }
+ } else {
+ char *buf = (char*)realloc(buffer, size);
+ if (!buf) {
+ free(buffer);
+ errv("Out of memory allocating %zd bytes for output buffer!", size);
+ abort();
+ }
+ buffer = buf;
+ }
+ }
+
+ void emit(char c) {
+ maybeSpace(c);
+ if (!pretty && c == '}' && buffer[used-1] == ';') used--; // optimize ;} into }, the ; is not separating anything
+ ensure(1);
+ buffer[used++] = c;
+ }
+
+ void emit(const char *s) {
+ maybeSpace(*s);
+ int len = strlen(s);
+ ensure(len+1);
+ strncpy(buffer + used, s, len+1);
+ used += len;
+ }
+
+ void newline() {
+ if (!pretty) return;
+ emit('\n');
+ for (int i = 0; i < indent; i++) emit(' ');
+ }
+
+ void space() {
+ if (pretty) emit(' ');
+ }
+
+ void safeSpace() {
+ if (pretty) emit(' ');
+ else possibleSpace = true;
+ }
+
+ void maybeSpace(char s) {
+ if (possibleSpace) {
+ possibleSpace = false;
+ if (isIdentPart(s)) emit(' ');
+ }
+ }
+
+ bool isNothing(Ref node) {
+ return node->isArray() && node[0] == TOPLEVEL && node[1]->size() == 0;
+ }
+
+ bool isDefun(Ref node) {
+ return node->isArray() && node[0] == DEFUN;
+ }
+
+ bool isBlock(Ref node) {
+ return node->isArray() && node[0] == BLOCK;
+ }
+
+ bool isIf(Ref node) {
+ return node->isArray() && node[0] == IF;
+ }
+
+ void print(Ref node) {
+ ensure();
+ if (node->isString()) {
+ printName(node);
+ return;
+ }
+ if (node->isNumber()) {
+ printNum(node);
+ return;
+ }
+ if (node->isAssignName()) {
+ printAssignName(node);
+ return;
+ }
+ if (node->isAssign()) {
+ printAssign(node);
+ }
+ IString type = node[0]->getIString();
+ switch (type.str[0]) {
+ case 'a': {
+ 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();
+ break;
+ }
+ case 'c': {
+ 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();
+ break;
+ }
+ case 'i': {
+ if (type == IF) printIf(node);
+ else abort();
+ break;
+ }
+ case 'l': {
+ if (type == LABEL) printLabel(node);
+ else abort();
+ break;
+ }
+ case 'n': {
+ if (type == NEW) printNew(node);
+ else abort();
+ break;
+ }
+ case 'o': {
+ if (type == OBJECT) printObject(node);
+ break;
+ }
+ case 'r': {
+ 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();
+ break;
+ }
+ case 't': {
+ if (type == TOPLEVEL) printToplevel(node);
+ else abort();
+ break;
+ }
+ case 'u': {
+ if (type == UNARY_PREFIX) printUnaryPrefix(node);
+ else abort();
+ break;
+ }
+ case 'v': {
+ if (type == VAR) printVar(node);
+ else abort();
+ break;
+ }
+ case 'w': {
+ if (type == WHILE) printWhile(node);
+ else abort();
+ break;
+ }
+ default: {
+ errv("cannot yet print %s\n", type.str);
+ abort();
+ }
+ }
+ }
+
+ // print a node, and if nothing is emitted, emit something instead
+ void print(Ref node, const char *otherwise) {
+ auto last = used;
+ print(node);
+ if (used == last) emit(otherwise);
+ }
+
+ void printStats(Ref stats) {
+ bool first = true;
+ for (size_t i = 0; i < stats->size(); i++) {
+ Ref curr = stats[i];
+ if (!isNothing(curr)) {
+ if (first) first = false;
+ else newline();
+ print(curr);
+ if (!isDefun(curr) && !isBlock(curr) && !isIf(curr)) {
+ emit(';');
+ }
+ }
+ }
+ }
+
+ void printToplevel(Ref node) {
+ if (node[1]->size() > 0) {
+ printStats(node[1]);
+ }
+ }
+
+ void printBlock(Ref node) {
+ if (node->size() == 1 || node[1]->size() == 0) {
+ emit("{}");
+ return;
+ }
+ emit('{');
+ indent++;
+ newline();
+ printStats(node[1]);
+ indent--;
+ newline();
+ emit('}');
+ }
+
+ void printDefun(Ref node) {
+ emit("function ");
+ emit(node[1]->getCString());
+ emit('(');
+ Ref args = node[2];
+ for (size_t i = 0; i < args->size(); i++) {
+ if (i > 0) (pretty ? emit(", ") : emit(','));
+ emit(args[i]->getCString());
+ }
+ emit(')');
+ space();
+ if (node->size() == 3 || node[3]->size() == 0) {
+ emit("{}");
+ return;
+ }
+ emit('{');
+ indent++;
+ newline();
+ printStats(node[3]);
+ indent--;
+ newline();
+ emit('}');
+ newline();
+ }
+
+ void printAssign(Ref node) {
+ assert(false && "printAssign still used!");
+ auto* assign = node->asAssign();
+ printChild(assign->target(), node, -1);
+ space();
+ emit('=');
+ space();
+ printChild(assign->value(), node, 1);
+ }
+
+ void printAssignName(Ref node) {
+ auto *assign = node->asAssignName();
+ emit(assign->target().c_str());
+ space();
+ emit('=');
+ space();
+ printChild(assign->value(), node, 1);
+ }
+
+ void printName(Ref node) {
+ emit(node->getCString());
+ }
+
static char* numToString(double d, bool finalize=true) {
bool neg = d < 0;
if (neg) d = -d;
@@ -658,8 +951,366 @@ struct JSPrinter {
}
return ret;
}
+
+ void printNum(Ref node) {
+ emit(numToString(node->getNumber(), finalize));
+ }
+
+ void printString(Ref node) {
+ emit('"');
+ emit(node[1]->getCString());
+ emit('"');
+ }
+
+ // Parens optimizing
+
+ bool capturesOperators(Ref node) {
+ Ref type = node[0];
+ return type == CALL || type == ARRAY || type == OBJECT || type == SEQ;
+ }
+
+ int getPrecedence(Ref node, bool parent) {
+ if (node->isAssign() || node->isAssignName()) {
+ return OperatorClass::getPrecedence(OperatorClass::Binary, SET);
+ }
+ if (!node->isArray()) {
+ // node is a value
+ return -1;
+ }
+ Ref type = node[0];
+ if (type == BINARY || type == UNARY_PREFIX) {
+ 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
+ } else if (type == CONDITIONAL) {
+ return OperatorClass::getPrecedence(OperatorClass::Tertiary, QUESTION);
+ }
+ // 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
+ 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
+ // equal precedence, so associativity (rtl/ltr) is what matters
+ // (except for some exceptions, where multiple operators can combine into confusion)
+ if (parent[0] == UNARY_PREFIX) {
+ assert(child[0] == UNARY_PREFIX);
+ if ((parent[1] == PLUS || parent[1] == MINUS) && child[1] == parent[1]) {
+ // cannot emit ++x when we mean +(+x)
+ return true;
+ }
+ }
+ 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;
+ }
+
+ void printChild(Ref child, Ref parent, int childPosition=0) {
+ bool parens = needParens(parent, child, childPosition);
+ if (parens) emit('(');
+ print(child);
+ if (parens) emit(')');
+ }
+
+ void printBinary(Ref node) {
+ printChild(node[2], node, -1);
+ space();
+ emit(node[1]->getCString());
+ space();
+ printChild(node[3], node, 1);
+ }
+
+ void printUnaryPrefix(Ref node) {
+ if (finalize && node[1] == PLUS && (node[2]->isNumber() ||
+ (node[2][0] == UNARY_PREFIX && node[2][1] == MINUS && node[2][2]->isNumber()))) {
+ // emit a finalized number
+ int last = used;
+ print(node[2]);
+ ensure(1); // we temporarily append a 0
+ char *curr = buffer + last; // ensure might invalidate
+ buffer[used] = 0;
+ 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);
+ while (end >= e) {
+ end[2] = end[0];
+ end--;
+ }
+ e[0] = '.';
+ e[1] = '0';
+ used += 2;
+ return;
+ }
+ 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());
+ printChild(node[2], node, 1);
+ }
+
+ void printConditional(Ref node) {
+ printChild(node[1], node, -1);
+ space();
+ emit('?');
+ space();
+ printChild(node[2], node, 0);
+ space();
+ emit(':');
+ space();
+ printChild(node[3], node, 1);
+ }
+
+ void printCall(Ref node) {
+ printChild(node[1], node, 0);
+ emit('(');
+ Ref args = node[2];
+ for (size_t i = 0; i < args->size(); i++) {
+ if (i > 0) (pretty ? emit(", ") : emit(','));
+ printChild(args[i], node, 0);
+ }
+ emit(')');
+ }
+
+ void printSeq(Ref node) {
+ printChild(node[1], node, -1);
+ emit(',');
+ space();
+ printChild(node[2], node, 1);
+ }
+
+ void printDot(Ref node) {
+ print(node[1]);
+ emit('.');
+ emit(node[2]->getCString());
+ }
+
+ void printSwitch(Ref node) {
+ emit("switch");
+ space();
+ emit('(');
+ print(node[1]);
+ emit(')');
+ space();
+ emit('{');
+ newline();
+ Ref cases = node[2];
+ for (size_t i = 0; i < cases->size(); i++) {
+ Ref c = cases[i];
+ if (!c[0]) {
+ emit("default:");
+ } else {
+ emit("case ");
+ print(c[0]);
+ emit(':');
+ }
+ if (c[1]->size() > 0) {
+ indent++;
+ newline();
+ auto curr = used;
+ printStats(c[1]);
+ indent--;
+ if (curr != used) newline();
+ else used--; // avoid the extra indentation we added tentatively
+ } else {
+ newline();
+ }
+ }
+ emit('}');
+ }
+
+ void printSub(Ref node) {
+ printChild(node[1], node, -1);
+ emit('[');
+ print(node[2]);
+ emit(']');
+ }
+
+ void printVar(Ref node) {
+ emit("var ");
+ Ref args = node[1];
+ for (size_t i = 0; i < args->size(); i++) {
+ if (i > 0) (pretty ? emit(", ") : emit(','));
+ emit(args[i][0]->getCString());
+ if (args[i]->size() > 1) {
+ space();
+ emit('=');
+ space();
+ print(args[i][1]);
+ }
+ }
+ }
+
+ static bool ifHasElse(Ref node) {
+ assert(node->isArray() && node[0] == IF);
+ return node->size() >= 4 && !!node[3];
+ }
+
+ void printIf(Ref node) {
+ emit("if");
+ safeSpace();
+ emit('(');
+ print(node[1]);
+ emit(')');
+ space();
+ // special case: we need braces to save us from ambiguity, if () { if () } else. otherwise else binds to inner if
+ // also need to recurse for if () { if () { } else { if () } else
+ // (note that this is only a problem if the if body has a single element in it, not a block or such, as then
+ // the block would be braced)
+ // this analysis is a little conservative - it assumes any child if could be confused with us, which implies
+ // all other braces vanished (the worst case for us, we are not saved by other braces).
+ bool needBraces = false;
+ bool hasElse = ifHasElse(node);
+ if (hasElse) {
+ Ref child = node[2];
+ while (child->isArray() && child[0] == IF) {
+ if (!ifHasElse(child)) {
+ needBraces = true;
+ break;
+ }
+ child = child[3]; // continue into the else
+ }
+ }
+ if (needBraces) {
+ emit('{');
+ indent++;
+ newline();
+ print(node[2]);
+ indent--;
+ newline();
+ emit('}');
+ } else {
+ print(node[2], "{}");
+ if (!isBlock(node[2])) emit(';');
+ }
+ if (hasElse) {
+ space();
+ emit("else");
+ safeSpace();
+ print(node[3], "{}");
+ if (!isBlock(node[3])) emit(';');
+ }
+ }
+
+ void printDo(Ref node) {
+ emit("do");
+ safeSpace();
+ print(node[2], "{}");
+ space();
+ emit("while");
+ space();
+ emit('(');
+ print(node[1]);
+ emit(')');
+ }
+
+ void printWhile(Ref node) {
+ emit("while");
+ space();
+ emit('(');
+ print(node[1]);
+ emit(')');
+ space();
+ print(node[2], "{}");
+ }
+
+ void printLabel(Ref node) {
+ emit(node[1]->getCString());
+ space();
+ emit(':');
+ space();
+ print(node[2]);
+ }
+
+ void printReturn(Ref node) {
+ emit("return");
+ if (!!node[1]) {
+ emit(' ');
+ print(node[1]);
+ }
+ }
+
+ void printBreak(Ref node) {
+ emit("break");
+ if (!!node[1]) {
+ emit(' ');
+ emit(node[1]->getCString());
+ }
+ }
+
+ void printContinue(Ref node) {
+ emit("continue");
+ if (!!node[1]) {
+ emit(' ');
+ emit(node[1]->getCString());
+ }
+ }
+
+ void printNew(Ref node) {
+ emit("new ");
+ print(node[1]);
+ }
+
+ void printArray(Ref node) {
+ emit('[');
+ Ref args = node[1];
+ for (size_t i = 0; i < args->size(); i++) {
+ if (i > 0) (pretty ? emit(", ") : emit(','));
+ print(args[i]);
+ }
+ emit(']');
+ }
+
+ void printObject(Ref node) {
+ emit('{');
+ indent++;
+ newline();
+ Ref args = node[1];
+ for (size_t i = 0; i < args->size(); i++) {
+ if (i > 0) {
+ pretty ? emit(", ") : emit(',');
+ newline();
+ }
+ const char *str = args[i][0]->getCString();
+ const char *check = str;
+ bool needQuote = false;
+ while (*check) {
+ if (!isalnum(*check) && *check != '_' && *check != '$') {
+ needQuote = true;
+ break;
+ }
+ check++;
+ }
+ if (needQuote) emit('"');
+ emit(str);
+ if (needQuote) emit('"');
+ emit(":");
+ space();
+ print(args[i][1]);
+ }
+ indent--;
+ newline();
+ emit('}');
+ }
};
+
// cashew builder
class ValueBuilder {