summaryrefslogtreecommitdiff
path: root/src/emscripten-optimizer/simple_ast.h
diff options
context:
space:
mode:
authorAlon Zakai (kripken) <alonzakai@gmail.com>2017-01-31 20:03:41 -0800
committerAlon Zakai (kripken) <alonzakai@gmail.com>2017-01-31 20:25:01 -0800
commitf6c0bc26b65c6cfd58d1bb011c1587d343c42ab5 (patch)
treefd98892bcb3e3510831a6772d095543ddb9ebe18 /src/emscripten-optimizer/simple_ast.h
parentf2af5a9d587cbdeb8f164fb5d37d20daef527213 (diff)
downloadbinaryen-f6c0bc26b65c6cfd58d1bb011c1587d343c42ab5.tar.gz
binaryen-f6c0bc26b65c6cfd58d1bb011c1587d343c42ab5.tar.bz2
binaryen-f6c0bc26b65c6cfd58d1bb011c1587d343c42ab5.zip
refactor asm.js ast to not use STAT nodes - we don't need to print the asm.js anyhow, so knowing where ;s are is unnecessary bloat
Diffstat (limited to 'src/emscripten-optimizer/simple_ast.h')
-rw-r--r--src/emscripten-optimizer/simple_ast.h634
1 files changed, 2 insertions, 632 deletions
diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h
index 784f49632..aa02dd8fc 100644
--- a/src/emscripten-optimizer/simple_ast.h
+++ b/src/emscripten-optimizer/simple_ast.h
@@ -510,276 +510,9 @@ void traversePrePostConditional(Ref node, std::function<bool (Ref)> visitPre, st
// Traverses all the top-level functions in the document
void traverseFunctions(Ref ast, std::function<void (Ref)> visit);
-// JS printer
+// 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) {
- size = std::max((size_t)1024, size * 2) + safety;
- if (!buffer) {
- buffer = (char*)malloc(size);
- if (!buffer) {
- printf("Out of memory allocating %zd bytes for output buffer!", size);
- abort();
- }
- } else {
- char *buf = (char*)realloc(buffer, size);
- if (!buf) {
- free(buffer);
- printf("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(' ');
- }
- }
-
- void print(Ref node) {
- ensure();
- if (node->isString()) {
- printName(node);
- return;
- }
- if (node->isNumber()) {
- printNum(node);
- return;
- }
- if (node->isAssign()) {
- printAssign(node);
- }
- IString type = node[0]->getIString();
- //fprintf(stderr, "printing %s\n", type.str);
- 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 == STAT) printStat(node);
- else 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: {
- printf("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(stats[i]);
- }
- }
- }
-
- 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();
- }
-
- bool isNothing(Ref node) {
- return (node[0] == TOPLEVEL && node[1]->size() == 0) || (node[0] == STAT && isNothing(node[1]));
- }
-
- void printStat(Ref node) {
- if (!isNothing(node[1])) {
- print(node[1]);
- if (buffer[used-1] != ';') emit(';');
- }
- }
-
- void printAssign(Ref node) {
- auto* assign = node->asAssign();
- printChild(assign->target(), node, -1);
- 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;
@@ -901,369 +634,11 @@ 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()) {
- return OperatorClass::getPrecedence(OperatorClass::Binary, SET);
- }
- 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]);
- }
- }
- emit(';');
- }
-
- static bool ifHasElse(Ref node) {
- assert(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[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 (hasElse) {
- space();
- emit("else");
- safeSpace();
- print(node[3], "{}");
- }
- }
-
- void printDo(Ref node) {
- emit("do");
- safeSpace();
- print(node[2], "{}");
- space();
- emit("while");
- space();
- emit('(');
- print(node[1]);
- emit(')');
- 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]);
- }
- emit(';');
- }
-
- void printBreak(Ref node) {
- emit("break");
- if (!!node[1]) {
- emit(' ');
- emit(node[1]->getCString());
- }
- emit(';');
- }
-
- void printContinue(Ref node) {
- emit("continue");
- if (!!node[1]) {
- emit(' ');
- emit(node[1]->getCString());
- }
- emit(';');
- }
-
- 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 {
- static IStringSet statable;
-
static Ref makeRawString(const IString& s) {
return &arena.alloc<Value>()->setString(s);
}
@@ -1395,12 +770,7 @@ public:
}
static Ref makeStatement(Ref contents) {
- if (contents->isNumber() || contents->isString() || contents->isAssign() || statable.has(contents[0]->getIString())) {
- return &makeRawArray(2)->push_back(makeRawString(STAT))
- .push_back(contents);
- } else {
- return contents; // only very specific things actually need to be stat'ed
- }
+ return contents;
}
static Ref makeDouble(double num) {