From 279fdeec6fd935bd93dbea70b2d7d291cfb8d1a5 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Thu, 20 Jul 2017 13:28:58 -0700 Subject: [PATCH 01/18] 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. --- CMakeLists.txt | 15 +- src/emscripten-optimizer/simple_ast.h | 618 +++++++++++++++++++++++++- src/support/name.h | 7 + src/wasm2asm-main.cpp | 3 +- src/wasm2asm.h | 285 ++++++++---- 5 files changed, 830 insertions(+), 98 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 157a2e2c02d..999a1f7e7c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,7 @@ FOREACH(SUFFIX "_DEBUG" "_RELEASE" "_RELWITHDEBINFO" "_MINSIZEREL" "") ENDFOREACH() IF(MSVC) - IF(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.0") # VS2013 and older explicitly need /arch:sse2 set, VS2015 no longer has that option, but always enabled. + IF(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.0") # VS2013 and older explicitly need /arch:sse2 set, VS2015 no longer has that option, but always enabled. ADD_COMPILE_FLAG("/arch:sse2") ENDIF() ADD_COMPILE_FLAG("/wd4146") # Ignore warning "warning C4146: unary minus operator applied to unsigned type, result still unsigned", this pattern is used somewhat commonly in the code. @@ -189,7 +189,7 @@ SET(binaryen_SOURCES src/binaryen-c.cpp ) IF(BUILD_STATIC_LIB) - ADD_LIBRARY(binaryen STATIC ${binaryen_SOURCES}) + ADD_LIBRARY(binaryen STATIC ${binaryen_SOURCES}) ELSE() ADD_LIBRARY(binaryen SHARED ${binaryen_SOURCES}) ENDIF() @@ -243,6 +243,16 @@ SET_PROPERTY(TARGET asm2wasm PROPERTY CXX_STANDARD 11) SET_PROPERTY(TARGET asm2wasm PROPERTY CXX_STANDARD_REQUIRED ON) INSTALL(TARGETS asm2wasm DESTINATION ${CMAKE_INSTALL_BINDIR}) +SET(wasm2asm_SOURCES + src/wasm2asm-main.cpp +) +ADD_EXECUTABLE(wasm2asm + ${wasm2asm_SOURCES}) +TARGET_LINK_LIBRARIES(wasm2asm passes wasm asmjs emscripten-optimizer ast cfg support) +SET_PROPERTY(TARGET wasm2asm PROPERTY CXX_STANDARD 11) +SET_PROPERTY(TARGET wasm2asm PROPERTY CXX_STANDARD_REQUIRED ON) +INSTALL(TARGETS wasm2asm DESTINATION ${CMAKE_INSTALL_BINDIR}) + SET(s2wasm_SOURCES src/tools/s2wasm.cpp src/wasm-emscripten.cpp @@ -284,4 +294,3 @@ TARGET_LINK_LIBRARIES(wasm-ctor-eval emscripten-optimizer passes wasm asmjs ast SET_PROPERTY(TARGET wasm-ctor-eval PROPERTY CXX_STANDARD 11) SET_PROPERTY(TARGET wasm-ctor-eval PROPERTY CXX_STANDARD_REQUIRED ON) INSTALL(TARGETS wasm-ctor-eval DESTINATION bin) - diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h index efbd7b6f652..77fe25e5144 100644 --- a/src/emscripten-optimizer/simple_ast.h +++ b/src/emscripten-optimizer/simple_ast.h @@ -537,7 +537,266 @@ void traverseFunctions(Ref ast, std::function visit); // JS printing support struct JSPrinter { - static char* numToString(double d, bool finalize=true) { + 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 == 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; + } + + 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; // try to emit the fewest necessary characters @@ -658,8 +917,365 @@ 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 { diff --git a/src/support/name.h b/src/support/name.h index 9853f546271..b7739755554 100644 --- a/src/support/name.h +++ b/src/support/name.h @@ -55,4 +55,11 @@ struct Name : public cashew::IString { } // namespace wasm +namespace std { + +template <> struct hash : hash {}; + +} // namespace std + + #endif // wasm_support_string_h diff --git a/src/wasm2asm-main.cpp b/src/wasm2asm-main.cpp index fa652cb470c..95bdd0b08c4 100644 --- a/src/wasm2asm-main.cpp +++ b/src/wasm2asm-main.cpp @@ -18,7 +18,6 @@ // wasm2asm console tool // - #include "support/colors.h" #include "support/command-line.h" #include "support/file.h" @@ -52,7 +51,7 @@ int main(int argc, const char *argv[]) { if (options.debug) std::cerr << "w-parsing..." << std::endl; Module wasm; - SExpressionWasmBuilder builder(wasm, *root[0], [&]() { abort(); }); + SExpressionWasmBuilder builder(wasm, *root[0]); if (options.debug) std::cerr << "asming..." << std::endl; Wasm2AsmBuilder wasm2asm(options.debug); diff --git a/src/wasm2asm.h b/src/wasm2asm.h index f328918c281..f75f3f7105a 100644 --- a/src/wasm2asm.h +++ b/src/wasm2asm.h @@ -209,7 +209,12 @@ Ref Wasm2AsmBuilder::processWasm(Module* wasm) { addImport(asmFunc[3], import.get()); } // figure out the table size - tableSize = wasm->table.names.size(); + // tableSize = wasm->table.names.size(); + tableSize = std::accumulate(wasm->table.segments.begin(), + wasm->table.segments.end(), + 0, [&](size_t size, Table::Segment seg) -> size_t { + return size + seg.data.size(); + }); size_t pow2ed = 1; while (pow2ed < tableSize) { pow2ed <<= 1; @@ -287,19 +292,22 @@ void Wasm2AsmBuilder::addImport(Ref ast, Import *import) { void Wasm2AsmBuilder::addTables(Ref ast, Module *wasm) { std::map> tables; // asm.js tables, sig => contents of table - for (size_t i = 0; i < wasm->table.names.size(); i++) { - Name name = wasm->table.names[i]; - auto func = wasm->getFunction(name); - std::string sig = getSig(func); - auto& table = tables[sig]; - if (table.size() == 0) { - // fill it with the first of its type seen. we have to fill with something; and for asm2wasm output, the first is the null anyhow - table.resize(tableSize); - for (size_t j = 0; j < tableSize; j++) { - table[j] = fromName(name); + // for (size_t i = 0; i < wasm->table.names.size(); i++) { + for (Table::Segment &seg : wasm->table.segments) { + for (size_t i = 0; i < seg.data.size(); i++) { + Name name = seg.data[i]; + auto func = wasm->getFunction(name); + std::string sig = getSig(func); + auto& table = tables[sig]; + if (table.size() == 0) { + // fill it with the first of its type seen. we have to fill with something; and for asm2wasm output, the first is the null anyhow + table.resize(tableSize); + for (size_t j = 0; j < tableSize; j++) { + table[j] = fromName(name); + } + } else { + table[i] = fromName(name); } - } else { - table[i] = fromName(name); } } for (auto& pair : tables) { @@ -340,9 +348,13 @@ Ref Wasm2AsmBuilder::processFunction(Function* func) { ValueBuilder::appendArgumentToFunction(ret, name); ret[3]->push_back( ValueBuilder::makeStatement( - ValueBuilder::makeAssign( - ValueBuilder::makeName(name), - makeAsmCoercion(ValueBuilder::makeName(name), wasmToAsmType(func->getLocalType(i))) + // ValueBuilder::makeAssign( + // ValueBuilder::makeName(name), + // makeAsmCoercion(ValueBuilder::makeName(name), wasmToAsmType(func->getLocalType(i))) + ValueBuilder::makeBinary( + ValueBuilder::makeName(name), SET, + makeAsmCoercion(ValueBuilder::makeName(name), + wasmToAsmType(func->getLocalType(i))) ) ) ); @@ -381,9 +393,10 @@ Ref Wasm2AsmBuilder::processFunction(Function* func) { for (auto f : frees[f64]) { ValueBuilder::appendToVar(theVar, f, makeAsmCoercedZero(ASM_DOUBLE)); } - if (theVar[1]->size() == 0) { - ret[3]->splice(theVarIndex, 1); - } + // if (theVar[1]->size() == 0) { XXX ??? + // ret[3]->splice(theVarIndex, 1); + // } + (void)theVarIndex; // 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 @@ -557,7 +570,9 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { // if it's not already a statement, then it's an expression, and we need to assign it // (if it is a statement, it already assigns to the result var) if (!isStatement(curr) && result != NO_RESULT) { - ret = ValueBuilder::makeStatement(ValueBuilder::makeAssign(ValueBuilder::makeName(result), ret)); + // ret = ValueBuilder::makeStatement(ValueBuilder::makeAssign(ValueBuilder::makeName(result), ret)); + ret = ValueBuilder::makeStatement( + ValueBuilder::makeBinary(ValueBuilder::makeName(result), SET, ret)); } return ret; } @@ -588,7 +603,8 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { std::map breakResults; // Breaks to the top of a loop should be emitted as continues, to that loop's main label - std::map continueLabels; + // std::map continueLabels; + std::unordered_set continueLabels; IString fromName(Name name) { return parent->fromName(name); @@ -629,14 +645,11 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { return condition; } Ref visitLoop(Loop *curr) { - Name asmLabel = curr->out.is() ? curr->out : curr->in; // label using the outside, normal for breaks. if no outside, then inside - if (curr->in.is()) continueLabels[curr->in] = asmLabel; + Name asmLabel = curr->name; + continueLabels.insert(asmLabel); Ref body = visit(curr->body, result); Ref ret = ValueBuilder::makeDo(body, ValueBuilder::makeInt(0)); - if (asmLabel.is()) { - ret = ValueBuilder::makeLabel(fromName(asmLabel), ret); - } - return ret; + return ValueBuilder::makeLabel(fromName(asmLabel), ret); } Ref visitBreak(Break *curr) { if (curr->condition) { @@ -653,7 +666,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { if (iter == continueLabels.end()) { theBreak = ValueBuilder::makeBreak(fromName(curr->name)); } else { - theBreak = ValueBuilder::makeContinue(fromName(iter->second)); + theBreak = ValueBuilder::makeContinue(fromName(curr->name)); } if (!curr->value) return theBreak; // generate the value, including assigning to the result, and then do the break @@ -695,7 +708,10 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } theCall = makeAsmCoercion(theCall, wasmToAsmType(type)); if (result != NO_RESULT) { - theCall = ValueBuilder::makeStatement(ValueBuilder::makeAssign(ValueBuilder::makeName(result), theCall)); + // theCall = ValueBuilder::makeStatement(ValueBuilder::makeAssign(ValueBuilder::makeName(result), theCall)); + theCall = ValueBuilder::makeStatement( + ValueBuilder::makeBinary( + ValueBuilder::makeName(result), SET, theCall)); } flattenAppend(ret, theCall); for (auto temp : temps) { @@ -720,7 +736,8 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { abort(); } Ref visitCallIndirect(CallIndirect *curr) { - std::string stable = std::string("FUNCTION_TABLE_") + getSig(curr->fullType); + // std::string stable = std::string("FUNCTION_TABLE_") + getSig(curr->fullType); + std::string stable = std::string("FUNCTION_TABLE_") + curr->fullType.c_str(); IString table = IString(stable.c_str(), false); auto makeTableCall = [&](Ref target) { return ValueBuilder::makeCall(ValueBuilder::makeSub( @@ -748,12 +765,19 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } Ref visitSetLocal(SetLocal *curr) { if (!isStatement(curr)) { - return ValueBuilder::makeAssign(ValueBuilder::makeName(fromName(func->getLocalName(curr->index))), visit(curr->value, EXPRESSION_RESULT)); + // return ValueBuilder::makeAssign(ValueBuilder::makeName(fromName(func->getLocalName(curr->index))), visit(curr->value, EXPRESSION_RESULT)); + return ValueBuilder::makeBinary( + ValueBuilder::makeName(fromName(func->getLocalName(curr->index))), + SET, visit(curr->value, EXPRESSION_RESULT)); } ScopedTemp temp(curr->type, parent, result); // if result was provided, our child can just assign there. otherwise, allocate a temp for it to assign to. Ref ret = blockify(visit(curr->value, temp)); // the output was assigned to result, so we can just assign it to our target - ret[1]->push_back(ValueBuilder::makeStatement(ValueBuilder::makeAssign(ValueBuilder::makeName(fromName(func->getLocalName(curr->index))), temp.getAstName()))); + // ret[1]->push_back(ValueBuilder::makeStatement(ValueBuilder::makeAssign(ValueBuilder::makeName(fromName(func->getLocalName(curr->index))), temp.getAstName()))); + ret[1]->push_back(ValueBuilder::makeStatement( + ValueBuilder::makeBinary( + ValueBuilder::makeName(fromName(func->getLocalName(curr->index))), + SET, temp.getAstName()))); return ret; } Ref visitLoad(Load *curr) { @@ -785,7 +809,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { case i32: { rest = makeAsmCoercion(visit(&load, EXPRESSION_RESULT), ASM_INT); for (size_t i = 1; i < curr->bytes; i++) { - load.offset += 1; + ++load.offset; Ref add = makeAsmCoercion(visit(&load, EXPRESSION_RESULT), ASM_INT); add = ValueBuilder::makeBinary(add, LSHIFT, ValueBuilder::makeNum(8*i)); rest = ValueBuilder::makeBinary(rest, OR, add); @@ -866,12 +890,12 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { shift.value = Literal(int32_t(8*i)); shift.type = i32; Binary shifted(allocator); - shifted.op = ShrU; + shifted.op = ShrUInt64; shifted.left = &getValue; shifted.right = &shift; shifted.type = i32; Binary anded(allocator); - anded.op = And; + anded.op = AndInt32; anded.left = i > 0 ? static_cast(&shifted) : static_cast(&getValue); anded.right = &_255; anded.type = i32; @@ -882,7 +906,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } else { rest = ValueBuilder::makeSeq(rest, part); } - store.offset += 1; + ++store.offset; } break; } @@ -911,7 +935,8 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { case f64: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF64), ValueBuilder::makePtrShift(ptr, 3)); break; default: abort(); } - return ValueBuilder::makeAssign(ret, value); + // return ValueBuilder::makeAssign(ret, value); + return ValueBuilder::makeBinary(ret, SET, value); } Ref visitConst(Const *curr) { switch (curr->type) { @@ -950,9 +975,12 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { switch (curr->type) { case i32: { switch (curr->op) { - case Clz: return ValueBuilder::makeCall(MATH_CLZ32, value); - case Ctz: return ValueBuilder::makeCall(MATH_CTZ32, value); - case Popcnt: return ValueBuilder::makeCall(MATH_POPCNT32, value); + case ClzInt32: + return ValueBuilder::makeCall(MATH_CLZ32, value); + case CtzInt32: + return ValueBuilder::makeCall(MATH_CTZ32, value); + case PopcntInt32: + return ValueBuilder::makeCall(MATH_POPCNT32, value); default: abort(); } } @@ -960,26 +988,47 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { case f64: { Ref ret; switch (curr->op) { - case Neg: ret = ValueBuilder::makeUnary(MINUS, value); break; - case Abs: ret = ValueBuilder::makeCall(MATH_ABS, value); break; - case Ceil: ret = ValueBuilder::makeCall(MATH_CEIL, value); break; - case Floor: ret = ValueBuilder::makeCall(MATH_FLOOR, value); break; - case Trunc: ret = ValueBuilder::makeCall(MATH_TRUNC, value); break; - case Nearest: ret = ValueBuilder::makeCall(MATH_NEAREST, value); break; - case Sqrt: ret = ValueBuilder::makeCall(MATH_SQRT, value); break; - //case TruncSFloat32: ret = ValueBuilder::makePrefix(B_NOT, ValueBuilder::makePrefix(B_NOT, value)); break; - case PromoteFloat32: - //case ConvertSInt32: ret = ValueBuilder::makePrefix(PLUS, ValueBuilder::makeBinary(value, OR, ValueBuilder::makeNum(0))); break; - //case ConvertUInt32: ret = ValueBuilder::makePrefix(PLUS, ValueBuilder::makeBinary(value, TRSHIFT, ValueBuilder::makeNum(0))); break; - case DemoteFloat64: ret = value; break; - default: std::cerr << curr << '\n'; abort(); + case NegFloat32: + case NegFloat64: + ret = ValueBuilder::makeUnary(MINUS, value); + break; + case AbsFloat32: + case AbsFloat64: + ret = ValueBuilder::makeCall(MATH_ABS, value); + break; + case CeilFloat32: + case CeilFloat64: + ret = ValueBuilder::makeCall(MATH_CEIL, value); + break; + case FloorFloat32: + case FloorFloat64: + ret = ValueBuilder::makeCall(MATH_FLOOR, value); + break; + case TruncFloat32: + case TruncFloat64: + ret = ValueBuilder::makeCall(MATH_TRUNC, value); + break; + case NearestFloat32: + case NearestFloat64: + ret = ValueBuilder::makeCall(MATH_NEAREST, value); + break; + case SqrtFloat32: + case SqrtFloat64: + ret = ValueBuilder::makeCall(MATH_SQRT, value); + break; + // TODO: more complex unary conversions + default: + std::cerr << curr << '\n'; + abort(); } if (curr->type == f32) { // doubles need much less coercing return makeAsmCoercion(ret, ASM_FLOAT); } return ret; } - default: abort(); + default: + std::cerr << curr << '\n'; + abort(); } } Ref visitBinary(Binary *curr) { @@ -1003,55 +1052,104 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { Ref right = visit(curr->right, EXPRESSION_RESULT); Ref ret; switch (curr->op) { - case Add: ret = ValueBuilder::makeBinary(left, PLUS, right); break; - case Sub: ret = ValueBuilder::makeBinary(left, MINUS, right); break; - case Mul: { + case AddInt32: + ret = ValueBuilder::makeBinary(left, PLUS, right); + break; + case SubInt32: + ret = ValueBuilder::makeBinary(left, MINUS, right); + break; + case MulInt32: { if (curr->type == i32) { - return ValueBuilder::makeCall(MATH_IMUL, left, right); // TODO: when one operand is a small int, emit a multiply + // TODO: when one operand is a small int, emit a multiply + return ValueBuilder::makeCall(MATH_IMUL, left, right); } else { - return ValueBuilder::makeBinary(left, MINUS, right); break; + return ValueBuilder::makeBinary(left, MUL, right); } } - case DivS: ret = ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), DIV, makeSigning(right, ASM_SIGNED)); break; - case DivU: ret = ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), DIV, makeSigning(right, ASM_UNSIGNED)); break; - case RemS: ret = ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), MOD, makeSigning(right, ASM_SIGNED)); break; - case RemU: ret = ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), MOD, makeSigning(right, ASM_UNSIGNED)); break; - case And: ret = ValueBuilder::makeBinary(left, AND, right); break; - case Or: ret = ValueBuilder::makeBinary(left, OR, right); break; - case Xor: ret = ValueBuilder::makeBinary(left, XOR, right); break; - case Shl: ret = ValueBuilder::makeBinary(left, LSHIFT, right); break; - case ShrU: ret = ValueBuilder::makeBinary(left, TRSHIFT, right); break; - case ShrS: ret = ValueBuilder::makeBinary(left, RSHIFT, right); break; - case Div: ret = ValueBuilder::makeBinary(left, DIV, right); break; - case Min: ret = ValueBuilder::makeCall(MATH_MIN, left, right); break; - case Max: ret = ValueBuilder::makeCall(MATH_MAX, left, right); break; - case Eq: { + case DivSInt32: + ret = ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), DIV, + makeSigning(right, ASM_SIGNED)); + break; + case DivUInt32: + ret = ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), DIV, + makeSigning(right, ASM_UNSIGNED)); + break; + case RemSInt32: + ret = ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), MOD, + makeSigning(right, ASM_SIGNED)); + break; + case RemUInt32: + ret = ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), MOD, + makeSigning(right, ASM_UNSIGNED)); + break; + case AndInt32: + ret = ValueBuilder::makeBinary(left, AND, right); + break; + case OrInt32: + ret = ValueBuilder::makeBinary(left, OR, right); + break; + case XorInt32: + ret = ValueBuilder::makeBinary(left, XOR, right); + break; + case ShlInt32: + ret = ValueBuilder::makeBinary(left, LSHIFT, right); + break; + case ShrUInt32: + ret = ValueBuilder::makeBinary(left, TRSHIFT, right); + break; + case ShrSInt32: + ret = ValueBuilder::makeBinary(left, RSHIFT, right); + break; + case MinFloat32: + ret = ValueBuilder::makeCall(MATH_MIN, left, right); + break; + case MaxFloat32: + ret = ValueBuilder::makeCall(MATH_MAX, left, right); + break; + case EqInt32: { + // TODO: check if this condition is still valid/necessary if (curr->left->type == i32) { - return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), EQ, makeSigning(right, ASM_SIGNED)); + return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), EQ, + makeSigning(right, ASM_SIGNED)); } else { return ValueBuilder::makeBinary(left, EQ, right); } } - case Ne: { + case NeInt32: { if (curr->left->type == i32) { - return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), NE, makeSigning(right, ASM_SIGNED)); + return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), NE, + makeSigning(right, ASM_SIGNED)); } else { return ValueBuilder::makeBinary(left, NE, right); } } - case LtS: return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), LT, makeSigning(right, ASM_SIGNED)); - case LtU: return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), LT, makeSigning(right, ASM_UNSIGNED)); - case LeS: return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), LE, makeSigning(right, ASM_SIGNED)); - case LeU: return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), LE, makeSigning(right, ASM_UNSIGNED)); - case GtS: return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), GT, makeSigning(right, ASM_SIGNED)); - case GtU: return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), GT, makeSigning(right, ASM_UNSIGNED)); - case GeS: return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), GE, makeSigning(right, ASM_SIGNED)); - case GeU: return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), GE, makeSigning(right, ASM_UNSIGNED)); - case Lt: return ValueBuilder::makeBinary(left, LT, right); - case Le: return ValueBuilder::makeBinary(left, LE, right); - case Gt: return ValueBuilder::makeBinary(left, GT, right); - case Ge: return ValueBuilder::makeBinary(left, GE, right); - default: abort(); + case LtSInt32: + return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), LT, + makeSigning(right, ASM_SIGNED)); + case LtUInt32: + return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), LT, + makeSigning(right, ASM_UNSIGNED)); + case LeSInt32: + return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), LE, + makeSigning(right, ASM_SIGNED)); + case LeUInt32: + return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), LE, + makeSigning(right, ASM_UNSIGNED)); + case GtSInt32: + return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), GT, + makeSigning(right, ASM_SIGNED)); + case GtUInt32: + return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), GT, + makeSigning(right, ASM_UNSIGNED)); + case GeSInt32: + return ValueBuilder::makeBinary(makeSigning(left, ASM_SIGNED), GE, + makeSigning(right, ASM_SIGNED)); + case GeUInt32: + return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), GE, + makeSigning(right, ASM_UNSIGNED)); + default: + std::cerr << curr << '\n'; + abort(); } return makeAsmCoercion(ret, wasmToAsmType(curr->type)); } @@ -1085,11 +1183,14 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { tempCondition(i32, parent); return ValueBuilder::makeSeq( - ValueBuilder::makeAssign(tempCondition.getAstName(), condition), + // ValueBuilder::makeAssign(tempCondition.getAstName(), condition), + ValueBuilder::makeBinary(tempCondition.getAstName(), SET, condition), ValueBuilder::makeSeq( - ValueBuilder::makeAssign(tempIfTrue.getAstName(), ifTrue), + // ValueBuilder::makeAssign(tempIfTrue.getAstName(), ifTrue), + ValueBuilder::makeBinary(tempIfTrue.getAstName(), SET, ifTrue), ValueBuilder::makeSeq( - ValueBuilder::makeAssign(tempIfFalse.getAstName(), ifFalse), + // ValueBuilder::makeAssign(tempIfFalse.getAstName(), ifFalse), + ValueBuilder::makeBinary(tempIfFalse.getAstName(), SET, ifFalse), ValueBuilder::makeConditional(tempCondition.getAstName(), tempIfTrue.getAstName(), tempIfFalse.getAstName()) ) ) From 262b51db59ecd3ce66d90b874956559e15969208 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 21 Jul 2017 14:10:42 -0700 Subject: [PATCH 02/18] Add missing header --- src/wasm2asm.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wasm2asm.h b/src/wasm2asm.h index f75f3f7105a..29215f1c9cd 100644 --- a/src/wasm2asm.h +++ b/src/wasm2asm.h @@ -23,6 +23,7 @@ #define wasm_wasm2asm_h #include +#include #include "asmjs/shared-constants.h" #include "wasm.h" From d792ee129a25a946eb7a5b2aa543c7d5ac99436b Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Mon, 24 Jul 2017 13:07:32 -0700 Subject: [PATCH 03/18] cleanups --- src/emscripten-optimizer/simple_ast.h | 36 +++++++++++++-------------- src/wasm2asm.h | 25 +++++-------------- 2 files changed, 24 insertions(+), 37 deletions(-) diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h index 77fe25e5144..bce114fea1d 100644 --- a/src/emscripten-optimizer/simple_ast.h +++ b/src/emscripten-optimizer/simple_ast.h @@ -557,23 +557,24 @@ struct JSPrinter { // Utils void ensure(int safety=100) { - if (size < used + safety) { - size = std::max((size_t)1024, size * 2) + safety; + if (size >= used + safety) { + return; + } + size = std::max((size_t)1024, size * 2) + safety; + if (!buffer) { + buffer = (char*)malloc(size); 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; + 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; } } @@ -628,7 +629,6 @@ struct JSPrinter { 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); @@ -709,7 +709,7 @@ struct JSPrinter { break; } default: { - printf("cannot yet print %s\n", type.str); + errv("cannot yet print %s\n", type.str); abort(); } } @@ -796,7 +796,7 @@ struct JSPrinter { emit(node->getCString()); } - static char* numToString(double d, bool finalize=true) { + static char* numToString(double d, bool finalize=true) { bool neg = d < 0; if (neg) d = -d; // try to emit the fewest necessary characters diff --git a/src/wasm2asm.h b/src/wasm2asm.h index 29215f1c9cd..10b0c24b577 100644 --- a/src/wasm2asm.h +++ b/src/wasm2asm.h @@ -210,7 +210,6 @@ Ref Wasm2AsmBuilder::processWasm(Module* wasm) { addImport(asmFunc[3], import.get()); } // figure out the table size - // tableSize = wasm->table.names.size(); tableSize = std::accumulate(wasm->table.segments.begin(), wasm->table.segments.end(), 0, [&](size_t size, Table::Segment seg) -> size_t { @@ -293,7 +292,6 @@ void Wasm2AsmBuilder::addImport(Ref ast, Import *import) { void Wasm2AsmBuilder::addTables(Ref ast, Module *wasm) { std::map> tables; // asm.js tables, sig => contents of table - // for (size_t i = 0; i < wasm->table.names.size(); i++) { for (Table::Segment &seg : wasm->table.segments) { for (size_t i = 0; i < seg.data.size(); i++) { Name name = seg.data[i]; @@ -336,7 +334,7 @@ void Wasm2AsmBuilder::addExports(Ref ast, Module *wasm) { } Ref Wasm2AsmBuilder::processFunction(Function* func) { - if (debug) std::cerr << " processFunction " << func->name << '\n'; + if (debug) std::cerr << " processFunction " << func->name << std::endl; Ref ret = ValueBuilder::makeFunction(fromName(func->name)); frees.clear(); frees.resize(std::max(i32, std::max(f32, f64)) + 1); @@ -349,9 +347,6 @@ Ref Wasm2AsmBuilder::processFunction(Function* func) { ValueBuilder::appendArgumentToFunction(ret, name); ret[3]->push_back( ValueBuilder::makeStatement( - // ValueBuilder::makeAssign( - // ValueBuilder::makeName(name), - // makeAsmCoercion(ValueBuilder::makeName(name), wasmToAsmType(func->getLocalType(i))) ValueBuilder::makeBinary( ValueBuilder::makeName(name), SET, makeAsmCoercion(ValueBuilder::makeName(name), @@ -394,7 +389,8 @@ Ref Wasm2AsmBuilder::processFunction(Function* func) { for (auto f : frees[f64]) { ValueBuilder::appendToVar(theVar, f, makeAsmCoercedZero(ASM_DOUBLE)); } - // if (theVar[1]->size() == 0) { XXX ??? + // if (theVar[1]->size() == 0) { + // XXX What should splice do??? // ret[3]->splice(theVarIndex, 1); // } (void)theVarIndex; @@ -571,7 +567,6 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { // if it's not already a statement, then it's an expression, and we need to assign it // (if it is a statement, it already assigns to the result var) if (!isStatement(curr) && result != NO_RESULT) { - // ret = ValueBuilder::makeStatement(ValueBuilder::makeAssign(ValueBuilder::makeName(result), ret)); ret = ValueBuilder::makeStatement( ValueBuilder::makeBinary(ValueBuilder::makeName(result), SET, ret)); } @@ -604,7 +599,6 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { std::map breakResults; // Breaks to the top of a loop should be emitted as continues, to that loop's main label - // std::map continueLabels; std::unordered_set continueLabels; IString fromName(Name name) { @@ -709,7 +703,6 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } theCall = makeAsmCoercion(theCall, wasmToAsmType(type)); if (result != NO_RESULT) { - // theCall = ValueBuilder::makeStatement(ValueBuilder::makeAssign(ValueBuilder::makeName(result), theCall)); theCall = ValueBuilder::makeStatement( ValueBuilder::makeBinary( ValueBuilder::makeName(result), SET, theCall)); @@ -737,7 +730,6 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { abort(); } Ref visitCallIndirect(CallIndirect *curr) { - // std::string stable = std::string("FUNCTION_TABLE_") + getSig(curr->fullType); std::string stable = std::string("FUNCTION_TABLE_") + curr->fullType.c_str(); IString table = IString(stable.c_str(), false); auto makeTableCall = [&](Ref target) { @@ -774,7 +766,6 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { ScopedTemp temp(curr->type, parent, result); // if result was provided, our child can just assign there. otherwise, allocate a temp for it to assign to. Ref ret = blockify(visit(curr->value, temp)); // the output was assigned to result, so we can just assign it to our target - // ret[1]->push_back(ValueBuilder::makeStatement(ValueBuilder::makeAssign(ValueBuilder::makeName(fromName(func->getLocalName(curr->index))), temp.getAstName()))); ret[1]->push_back(ValueBuilder::makeStatement( ValueBuilder::makeBinary( ValueBuilder::makeName(fromName(func->getLocalName(curr->index))), @@ -936,7 +927,6 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { case f64: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF64), ValueBuilder::makePtrShift(ptr, 3)); break; default: abort(); } - // return ValueBuilder::makeAssign(ret, value); return ValueBuilder::makeBinary(ret, SET, value); } Ref visitConst(Const *curr) { @@ -1019,7 +1009,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { break; // TODO: more complex unary conversions default: - std::cerr << curr << '\n'; + std::cerr << "Unhandled unary operator: " << curr << std::endl; abort(); } if (curr->type == f32) { // doubles need much less coercing @@ -1028,7 +1018,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { return ret; } default: - std::cerr << curr << '\n'; + std::cerr << "Unhandled type: " << curr << std::endl; abort(); } } @@ -1149,7 +1139,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { return ValueBuilder::makeBinary(makeSigning(left, ASM_UNSIGNED), GE, makeSigning(right, ASM_UNSIGNED)); default: - std::cerr << curr << '\n'; + std::cerr << "Unhandled binary operator: " << curr << std::endl; abort(); } return makeAsmCoercion(ret, wasmToAsmType(curr->type)); @@ -1184,13 +1174,10 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { tempCondition(i32, parent); return ValueBuilder::makeSeq( - // ValueBuilder::makeAssign(tempCondition.getAstName(), condition), ValueBuilder::makeBinary(tempCondition.getAstName(), SET, condition), ValueBuilder::makeSeq( - // ValueBuilder::makeAssign(tempIfTrue.getAstName(), ifTrue), ValueBuilder::makeBinary(tempIfTrue.getAstName(), SET, ifTrue), ValueBuilder::makeSeq( - // ValueBuilder::makeAssign(tempIfFalse.getAstName(), ifFalse), ValueBuilder::makeBinary(tempIfFalse.getAstName(), SET, ifFalse), ValueBuilder::makeConditional(tempCondition.getAstName(), tempIfTrue.getAstName(), tempIfFalse.getAstName()) ) From 662f376a5329abed397fd73fc8377c1a0c601d50 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Wed, 26 Jul 2017 07:44:57 -0700 Subject: [PATCH 04/18] Update for AssignName refactor, add visitDrop --- src/emscripten-optimizer/simple_ast.h | 20 ++++++++++- src/wasm2asm.h | 48 ++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h index bce114fea1d..88d358a57c4 100644 --- a/src/emscripten-optimizer/simple_ast.h +++ b/src/emscripten-optimizer/simple_ast.h @@ -625,6 +625,10 @@ struct JSPrinter { printNum(node); return; } + if (node->isAssignName()) { + printAssignName(node); + return; + } if (node->isAssign()) { printAssign(node); } @@ -780,10 +784,11 @@ struct JSPrinter { } bool isNothing(Ref node) { - return node[0] == TOPLEVEL && node[1]->size() == 0; + return node->isArray() && node[0] == TOPLEVEL && node[1]->size() == 0; } void printAssign(Ref node) { + assert(false && "printAssign still used!"); auto* assign = node->asAssign(); printChild(assign->target(), node, -1); space(); @@ -792,6 +797,15 @@ struct JSPrinter { 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()); } @@ -939,6 +953,10 @@ struct JSPrinter { if (node->isAssign()) { 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()); diff --git a/src/wasm2asm.h b/src/wasm2asm.h index 10b0c24b577..71d8c5125f1 100644 --- a/src/wasm2asm.h +++ b/src/wasm2asm.h @@ -552,7 +552,8 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { return visit(curr, temp.temp); } - Ref visitForExpression(Expression* curr, WasmType type, IString& tempName) { // this result is for an asm expression slot, but it might be a statement + // this result is for an asm expression slot, but it might be a statement + Ref visitForExpression(Expression* curr, WasmType type, IString& tempName) { if (isStatement(curr)) { ScopedTemp temp(type, parent); tempName = temp.temp; @@ -815,22 +816,47 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { // normal load Ref ptr = visit(curr->ptr, EXPRESSION_RESULT); if (curr->offset) { - ptr = makeAsmCoercion(ValueBuilder::makeBinary(ptr, PLUS, ValueBuilder::makeNum(curr->offset)), ASM_INT); + ptr = makeAsmCoercion( + ValueBuilder::makeBinary(ptr, PLUS, ValueBuilder::makeNum(curr->offset)), + ASM_INT); } Ref ret; switch (curr->type) { case i32: { switch (curr->bytes) { - case 1: ret = ValueBuilder::makeSub(ValueBuilder::makeName(curr->signed_ ? HEAP8 : HEAPU8 ), ValueBuilder::makePtrShift(ptr, 0)); break; - case 2: ret = ValueBuilder::makeSub(ValueBuilder::makeName(curr->signed_ ? HEAP16 : HEAPU16), ValueBuilder::makePtrShift(ptr, 1)); break; - case 4: ret = ValueBuilder::makeSub(ValueBuilder::makeName(curr->signed_ ? HEAP32 : HEAPU32), ValueBuilder::makePtrShift(ptr, 2)); break; - default: abort(); + case 1: + ret = ValueBuilder::makeSub( + ValueBuilder::makeName(curr->signed_ ? HEAP8 : HEAPU8 ), + ValueBuilder::makePtrShift(ptr, 0)); + break; + case 2: + ret = ValueBuilder::makeSub( + ValueBuilder::makeName(curr->signed_ ? HEAP16 : HEAPU16), + ValueBuilder::makePtrShift(ptr, 1)); + break; + case 4: + ret = ValueBuilder::makeSub( + ValueBuilder::makeName(curr->signed_ ? HEAP32 : HEAPU32), + ValueBuilder::makePtrShift(ptr, 2)); + break; + default: + std::cerr << "Unhandled number of bytes in i32 load: " + << curr->bytes << std::endl; + 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; - default: abort(); + 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 load: " << curr->type << std::endl; + abort(); } return makeAsmCoercion(ret, wasmToAsmType(curr->type)); } @@ -929,6 +955,10 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } return ValueBuilder::makeBinary(ret, SET, value); } + Ref visitDrop(Drop *curr) { + assert(!isStatement(curr)); + return visitAndAssign(curr->value, result); + } Ref visitConst(Const *curr) { switch (curr->type) { case i32: return ValueBuilder::makeInt(curr->value.geti32()); From 891581b97403f9f2eab1142e20343c54255fa86a Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Wed, 26 Jul 2017 10:52:42 -0700 Subject: [PATCH 05/18] Implement splice for array values --- src/emscripten-optimizer/simple_ast.h | 5 +++++ src/mixed_arena.h | 21 +++++++++++++++++++++ src/wasm2asm.h | 8 +++----- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h index 88d358a57c4..7fe8b85c8d2 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++) { diff --git a/src/mixed_arena.h b/src/mixed_arena.h index 47e718454f2..9354527ec68 100644 --- a/src/mixed_arena.h +++ b/src/mixed_arena.h @@ -163,6 +163,8 @@ class ArenaVectorBase { } public: + struct Iterator; + T& operator[](size_t index) const { assert(index < usedElements); return data[index]; @@ -206,6 +208,16 @@ class ArenaVectorBase { usedElements++; } + void erase(Iterator start_it, Iterator end_it) { + assert(start_it.parent == end_it.parent && start_it.parent == this); + assert(start_it.index <= end_it.index && end_it.index <= usedElements); + size_t size = end_it.index - start_it.index; + for (size_t cur = start_it.index; cur + size < usedElements; ++cur) { + data[cur] = data[cur + size]; + } + usedElements -= size; + } + void clear() { usedElements = 0; } @@ -257,6 +269,15 @@ class ArenaVectorBase { index++; } + Iterator& operator+=(int off) { + index += off; + return *this; + } + + const Iterator operator+(int off) const { + return Iterator(*this) += off; + } + T& operator*() { return (*parent)[index]; } diff --git a/src/wasm2asm.h b/src/wasm2asm.h index 71d8c5125f1..c9a847c5603 100644 --- a/src/wasm2asm.h +++ b/src/wasm2asm.h @@ -389,11 +389,9 @@ Ref Wasm2AsmBuilder::processFunction(Function* func) { for (auto f : frees[f64]) { ValueBuilder::appendToVar(theVar, f, makeAsmCoercedZero(ASM_DOUBLE)); } - // if (theVar[1]->size() == 0) { - // XXX What should splice do??? - // ret[3]->splice(theVarIndex, 1); - // } - (void)theVarIndex; + 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 From b49dda47af141ed6e836f5e68a06adf32a069e44 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Wed, 26 Jul 2017 18:08:18 -0700 Subject: [PATCH 06/18] Enable initial wasm2asm testing --- check.py | 48 ++++++++++++++++++++++++ scripts/test/wasm2asm.py | 61 +++++++++++++++++++++++++++++++ test/empty_imported_table.2asm.js | 20 ++++++++++ test/empty_table.2asm.js | 19 ++++++++++ 4 files changed, 148 insertions(+) create mode 100755 scripts/test/wasm2asm.py create mode 100644 test/empty_imported_table.2asm.js create mode 100644 test/empty_table.2asm.js diff --git a/check.py b/check.py index 5c42c8812d2..493984d0e78 100755 --- a/check.py +++ b/check.py @@ -224,6 +224,54 @@ def do_asm2wasm_test(): run_command(ASM2WASM + [asmjs, '-o', 'b.wast', '-S']) assert open('b.wast', 'rb').read()[0] != '\0', 'we emit text with -S' +#''' wasm2asm tests disabled for now. Use scripts/test/wasm2asm.py to test +print '\n[ checking wasm2asm testcases... ]\n' + +# tests with i64s +blacklist = ['atomics.wast'] +spec_tests = [os.path.join('spec', t) for t in + sorted(os.listdir(os.path.join('test', 'spec')))] +for wasm in tests + [os.path.join('spec', name) for name in spec_tests]: + if wasm.endswith('.wast') and os.path.basename(wasm) not in blacklist: + print '..', wasm + asm = os.path.basename(wasm).replace('.wast', '.2asm.js') + actual, err = subprocess.Popen([os.path.join('bin', 'wasm2asm'), + os.path.join('test', wasm)], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate() + assert err == '', 'bad err:' + err + + # verify output + expected_file = os.path.join('test', asm) + if not os.path.exists(expected_file): + print actual + raise Exception('output ' + expected_file + ' does not exist') + expected = open(expected_file).read() + if actual != expected: + fail(actual, expected) + + open('a.2asm.js', 'w').write(actual) + + if NODEJS: + # verify asm.js is valid js + proc = subprocess.Popen([NODEJS, 'a.2asm.js'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, err = proc.communicate() + assert proc.returncode == 0 + assert not out and not err, [out, err] + + if MOZJS: + # verify asm.js validates + open('a.2asm.js', 'w').write(actual) + proc = subprocess.Popen([MOZJS, '-w', 'a.2asm.js'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, err = proc.communicate() + assert proc.returncode == 0 + fail_if_not_contained(err, 'Successfully compiled asm.js code') +#''' + print '\n[ checking wasm-opt parsing & printing... ]\n' for t in sorted(os.listdir(os.path.join(options.binaryen_test, 'print'))): diff --git a/scripts/test/wasm2asm.py b/scripts/test/wasm2asm.py new file mode 100755 index 00000000000..c7890ce30a9 --- /dev/null +++ b/scripts/test/wasm2asm.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python2 + +import os +import subprocess +import sys + +from support import run_command, split_wast +from shared import ( + ASM2WASM, BIN_DIR, EMCC, MOZJS, NATIVECC, NATIVEXX, NODEJS, S2WASM_EXE, + WASM_AS, WASM_CTOR_EVAL, WASM_OPT, WASM_SHELL, WASM_MERGE, WASM_SHELL_EXE, + WASM_DIS, binary_format_check, delete_from_orbit, fail, fail_with_error, + fail_if_not_identical, fail_if_not_contained, has_vanilla_emcc, + has_vanilla_llvm, minify_check, num_failures, options, tests, + requested, warnings +) + +print '\n[ checking wasm2asm testcases... ]\n' + +# tests with i64s +blacklist = ['atomics.wast'] +spec_tests = [os.path.join('spec', t) for t in + sorted(os.listdir(os.path.join('test', 'spec')))] +for wasm in tests + [os.path.join('spec', name) for name in spec_tests]: + if wasm.endswith('.wast') and os.path.basename(wasm) not in blacklist: + print '..', wasm + asm = os.path.basename(wasm).replace('.wast', '.2asm.js') + actual, err = subprocess.Popen([os.path.join('bin', 'wasm2asm'), + os.path.join('test', wasm)], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate() + assert err == '', 'bad err:' + err + + # verify output + expected_file = os.path.join('test', asm) + if not os.path.exists(expected_file): + print actual + raise Exception('output ' + expected_file + ' does not exist') + expected = open(expected_file).read() + if actual != expected: + fail(actual, expected) + + open('a.2asm.js', 'w').write(actual) + + if NODEJS: + # verify asm.js is valid js + proc = subprocess.Popen([NODEJS, 'a.2asm.js'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, err = proc.communicate() + assert proc.returncode == 0 + assert not out and not err, [out, err] + + if MOZJS: + # verify asm.js validates + open('a.2asm.js', 'w').write(actual) + proc = subprocess.Popen([MOZJS, '-w', 'a.2asm.js'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, err = proc.communicate() + assert proc.returncode == 0 + fail_if_not_contained(err, 'Successfully compiled asm.js code') diff --git a/test/empty_imported_table.2asm.js b/test/empty_imported_table.2asm.js new file mode 100644 index 00000000000..a0d8d0b84dd --- /dev/null +++ b/test/empty_imported_table.2asm.js @@ -0,0 +1,20 @@ +function asmFunc(global, env, buffer) { + "use asm" + var HEAP8 = new global.Int8Array(buffer); + var HEAP16 = new global.Int16Array(buffer); + var HEAP32 = new global.Int32Array(buffer); + var HEAPU8 = new global.Uint8Array(buffer); + var HEAPU16 = new global.Uint16Array(buffer); + var HEAPU32 = new global.Uint32Array(buffer); + var HEAPF32 = new global.Float32Array(buffer); + var HEAPF64 = new global.Float64Array(buffer); + var Math_imul = global.Math.imul; + var Math_fround = global.Math.fround; + var Math_abs = global.Math.abs; + var Math_clz32 = global.Math.clz32; + var import$table$0 = env.table; + return { + + }; +} + diff --git a/test/empty_table.2asm.js b/test/empty_table.2asm.js new file mode 100644 index 00000000000..67547950eaa --- /dev/null +++ b/test/empty_table.2asm.js @@ -0,0 +1,19 @@ +function asmFunc(global, env, buffer) { + "use asm" + var HEAP8 = new global.Int8Array(buffer); + var HEAP16 = new global.Int16Array(buffer); + var HEAP32 = new global.Int32Array(buffer); + var HEAPU8 = new global.Uint8Array(buffer); + var HEAPU16 = new global.Uint16Array(buffer); + var HEAPU32 = new global.Uint32Array(buffer); + var HEAPF32 = new global.Float32Array(buffer); + var HEAPF64 = new global.Float64Array(buffer); + var Math_imul = global.Math.imul; + var Math_fround = global.Math.fround; + var Math_abs = global.Math.abs; + var Math_clz32 = global.Math.clz32; + return { + + }; +} + From 899def2adf056786b485722e387e9a612693a5b1 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Thu, 27 Jul 2017 08:49:26 -0700 Subject: [PATCH 07/18] Disable wasm2asm in check.py --- check.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/check.py b/check.py index 493984d0e78..e3ca51f2bbf 100755 --- a/check.py +++ b/check.py @@ -224,7 +224,7 @@ def do_asm2wasm_test(): run_command(ASM2WASM + [asmjs, '-o', 'b.wast', '-S']) assert open('b.wast', 'rb').read()[0] != '\0', 'we emit text with -S' -#''' wasm2asm tests disabled for now. Use scripts/test/wasm2asm.py to test +''' wasm2asm tests disabled for now. Use scripts/test/wasm2asm.py to test print '\n[ checking wasm2asm testcases... ]\n' # tests with i64s @@ -270,7 +270,7 @@ def do_asm2wasm_test(): out, err = proc.communicate() assert proc.returncode == 0 fail_if_not_contained(err, 'Successfully compiled asm.js code') -#''' +''' print '\n[ checking wasm-opt parsing & printing... ]\n' From 5b85fd0604ef5ead0c8a82b9aede264cf5c04291 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Thu, 27 Jul 2017 11:24:27 -0700 Subject: [PATCH 08/18] Clean up wasm2asm testing --- check.py | 50 +------------------- scripts/test/shared.py | 1 + scripts/test/support.py | 7 ++- scripts/test/wasm2asm.py | 100 ++++++++++++++++----------------------- 4 files changed, 50 insertions(+), 108 deletions(-) diff --git a/check.py b/check.py index e3ca51f2bbf..d0f5843afee 100755 --- a/check.py +++ b/check.py @@ -31,6 +31,7 @@ ) import scripts.test.s2wasm as s2wasm +# import scripts.test.wasm2asm as wasm2asm if options.interpreter: print '[ using wasm interpreter at "%s" ]' % options.interpreter @@ -224,54 +225,6 @@ def do_asm2wasm_test(): run_command(ASM2WASM + [asmjs, '-o', 'b.wast', '-S']) assert open('b.wast', 'rb').read()[0] != '\0', 'we emit text with -S' -''' wasm2asm tests disabled for now. Use scripts/test/wasm2asm.py to test -print '\n[ checking wasm2asm testcases... ]\n' - -# tests with i64s -blacklist = ['atomics.wast'] -spec_tests = [os.path.join('spec', t) for t in - sorted(os.listdir(os.path.join('test', 'spec')))] -for wasm in tests + [os.path.join('spec', name) for name in spec_tests]: - if wasm.endswith('.wast') and os.path.basename(wasm) not in blacklist: - print '..', wasm - asm = os.path.basename(wasm).replace('.wast', '.2asm.js') - actual, err = subprocess.Popen([os.path.join('bin', 'wasm2asm'), - os.path.join('test', wasm)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE).communicate() - assert err == '', 'bad err:' + err - - # verify output - expected_file = os.path.join('test', asm) - if not os.path.exists(expected_file): - print actual - raise Exception('output ' + expected_file + ' does not exist') - expected = open(expected_file).read() - if actual != expected: - fail(actual, expected) - - open('a.2asm.js', 'w').write(actual) - - if NODEJS: - # verify asm.js is valid js - proc = subprocess.Popen([NODEJS, 'a.2asm.js'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - out, err = proc.communicate() - assert proc.returncode == 0 - assert not out and not err, [out, err] - - if MOZJS: - # verify asm.js validates - open('a.2asm.js', 'w').write(actual) - proc = subprocess.Popen([MOZJS, '-w', 'a.2asm.js'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - out, err = proc.communicate() - assert proc.returncode == 0 - fail_if_not_contained(err, 'Successfully compiled asm.js code') -''' - print '\n[ checking wasm-opt parsing & printing... ]\n' for t in sorted(os.listdir(os.path.join(options.binaryen_test, 'print'))): @@ -471,6 +424,7 @@ def fix(x): s2wasm.test_s2wasm() s2wasm.test_linker() +# wasm2asm.test_wasm2asm() print '\n[ running validation tests... ]\n' # Ensure the tests validate by default diff --git a/scripts/test/shared.py b/scripts/test/shared.py index b71545965a4..f94f22fe8f4 100644 --- a/scripts/test/shared.py +++ b/scripts/test/shared.py @@ -155,6 +155,7 @@ def is_exe(fpath): WASM_AS = [os.path.join(options.binaryen_bin, 'wasm-as')] WASM_DIS = [os.path.join(options.binaryen_bin, 'wasm-dis')] ASM2WASM = [os.path.join(options.binaryen_bin, 'asm2wasm')] +WASM2ASM = [os.path.join(options.binaryen_bin, 'wasm2asm')] WASM_CTOR_EVAL = [os.path.join(options.binaryen_bin, 'wasm-ctor-eval')] WASM_SHELL = [os.path.join(options.binaryen_bin, 'wasm-shell')] WASM_MERGE = [os.path.join(options.binaryen_bin, 'wasm-merge')] diff --git a/scripts/test/support.py b/scripts/test/support.py index 5d791d155d4..586967ad96e 100755 --- a/scripts/test/support.py +++ b/scripts/test/support.py @@ -147,7 +147,8 @@ def to_end(j): return ret -def run_command(cmd, expected_status=0, stderr=None, expected_err=None): +def run_command(cmd, expected_status=0, stderr=None, + expected_err=None, err_contains=False): if expected_err is not None: assert stderr == subprocess.PIPE or stderr is None,\ "Can't redirect stderr if using expected_err" @@ -157,7 +158,9 @@ def run_command(cmd, expected_status=0, stderr=None, expected_err=None): out, err = proc.communicate() if proc.returncode != expected_status: raise Exception(('run_command failed', err)) - if expected_err is not None and err != expected_err: + err_correct = expected_err is None or \ + (expected_err in err if err_contains else expected_err == err) + if not err_correct: raise Exception(('run_command unexpected stderr', "expected '%s', actual '%s'" % (expected_err, err))) return out diff --git a/scripts/test/wasm2asm.py b/scripts/test/wasm2asm.py index c7890ce30a9..42ad89e31ec 100755 --- a/scripts/test/wasm2asm.py +++ b/scripts/test/wasm2asm.py @@ -1,61 +1,45 @@ #!/usr/bin/env python2 import os -import subprocess -import sys - -from support import run_command, split_wast -from shared import ( - ASM2WASM, BIN_DIR, EMCC, MOZJS, NATIVECC, NATIVEXX, NODEJS, S2WASM_EXE, - WASM_AS, WASM_CTOR_EVAL, WASM_OPT, WASM_SHELL, WASM_MERGE, WASM_SHELL_EXE, - WASM_DIS, binary_format_check, delete_from_orbit, fail, fail_with_error, - fail_if_not_identical, fail_if_not_contained, has_vanilla_emcc, - has_vanilla_llvm, minify_check, num_failures, options, tests, - requested, warnings -) - -print '\n[ checking wasm2asm testcases... ]\n' - -# tests with i64s -blacklist = ['atomics.wast'] -spec_tests = [os.path.join('spec', t) for t in - sorted(os.listdir(os.path.join('test', 'spec')))] -for wasm in tests + [os.path.join('spec', name) for name in spec_tests]: - if wasm.endswith('.wast') and os.path.basename(wasm) not in blacklist: - print '..', wasm - asm = os.path.basename(wasm).replace('.wast', '.2asm.js') - actual, err = subprocess.Popen([os.path.join('bin', 'wasm2asm'), - os.path.join('test', wasm)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE).communicate() - assert err == '', 'bad err:' + err - - # verify output - expected_file = os.path.join('test', asm) - if not os.path.exists(expected_file): - print actual - raise Exception('output ' + expected_file + ' does not exist') - expected = open(expected_file).read() - if actual != expected: - fail(actual, expected) - - open('a.2asm.js', 'w').write(actual) - - if NODEJS: - # verify asm.js is valid js - proc = subprocess.Popen([NODEJS, 'a.2asm.js'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - out, err = proc.communicate() - assert proc.returncode == 0 - assert not out and not err, [out, err] - - if MOZJS: - # verify asm.js validates - open('a.2asm.js', 'w').write(actual) - proc = subprocess.Popen([MOZJS, '-w', 'a.2asm.js'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - out, err = proc.communicate() - assert proc.returncode == 0 - fail_if_not_contained(err, 'Successfully compiled asm.js code') + +from support import run_command +from shared import (WASM2ASM, MOZJS, NODEJS, fail_if_not_identical, tests) + +def test_wasm2asm(): + print '\n[ checking wasm2asm testcases... ]\n' + + # tests with i64s + blacklist = ['atomics.wast'] + spec_tests = [os.path.join('spec', t) for t in + sorted(os.listdir(os.path.join('test', 'spec')))] + for wasm in tests + [os.path.join('spec', name) for name in spec_tests]: + if wasm.endswith('.wast') and os.path.basename(wasm) not in blacklist: + print '..', wasm + asm = os.path.basename(wasm).replace('.wast', '.2asm.js') + cmd = WASM2ASM + [os.path.join('test', wasm)] + out = run_command(cmd) + + # verify output + expected_file = os.path.join('test', asm) + if not os.path.exists(expected_file): + print out + raise Exception('output ' + expected_file + ' does not exist') + expected = open(expected_file).read() + fail_if_not_identical(out, expected) + + open('a.2asm.js', 'w').write(out) + + if NODEJS: + # verify asm.js is valid js + out = run_command([NODEJS, 'a.2asm.js']) + fail_if_not_identical(out, '') + + if MOZJS: + # verify asm.js validates + out = run_command([MOZJS, '-w', 'a.2asm.js'], + expected_err='Successfully compiled asm.js code', + err_contains=True) + fail_if_not_identical(out, '') + +if __name__ == "__main__": + test_wasm2asm() From 9fc9144c2951e6e5eb0ec4f2ddb10b440257f513 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Thu, 27 Jul 2017 13:23:39 -0700 Subject: [PATCH 09/18] Make test script finish successfully --- check.py | 2 +- scripts/test/wasm2asm.py | 65 +++++++++++++++++++++------------------- test/hello_world.2asm.js | 6 ++-- test/min.2asm.js | 0 test/unit.2asm.js | 0 5 files changed, 39 insertions(+), 34 deletions(-) delete mode 100644 test/min.2asm.js delete mode 100644 test/unit.2asm.js diff --git a/check.py b/check.py index d0f5843afee..cac0456edaa 100755 --- a/check.py +++ b/check.py @@ -31,7 +31,7 @@ ) import scripts.test.s2wasm as s2wasm -# import scripts.test.wasm2asm as wasm2asm +import scripts.test.wasm2asm as wasm2asm if options.interpreter: print '[ using wasm interpreter at "%s" ]' % options.interpreter diff --git a/scripts/test/wasm2asm.py b/scripts/test/wasm2asm.py index 42ad89e31ec..ec9f910705b 100755 --- a/scripts/test/wasm2asm.py +++ b/scripts/test/wasm2asm.py @@ -8,38 +8,43 @@ def test_wasm2asm(): print '\n[ checking wasm2asm testcases... ]\n' - # tests with i64s - blacklist = ['atomics.wast'] + # tests with i64s, invokes, etc. + blacklist = ['atomics.wast', 'address.wast'] spec_tests = [os.path.join('spec', t) for t in sorted(os.listdir(os.path.join('test', 'spec')))] - for wasm in tests + [os.path.join('spec', name) for name in spec_tests]: - if wasm.endswith('.wast') and os.path.basename(wasm) not in blacklist: - print '..', wasm - asm = os.path.basename(wasm).replace('.wast', '.2asm.js') - cmd = WASM2ASM + [os.path.join('test', wasm)] - out = run_command(cmd) - - # verify output - expected_file = os.path.join('test', asm) - if not os.path.exists(expected_file): - print out - raise Exception('output ' + expected_file + ' does not exist') - expected = open(expected_file).read() - fail_if_not_identical(out, expected) - - open('a.2asm.js', 'w').write(out) - - if NODEJS: - # verify asm.js is valid js - out = run_command([NODEJS, 'a.2asm.js']) - fail_if_not_identical(out, '') - - if MOZJS: - # verify asm.js validates - out = run_command([MOZJS, '-w', 'a.2asm.js'], - expected_err='Successfully compiled asm.js code', - err_contains=True) - fail_if_not_identical(out, '') + print "spec_tests:", spec_tests + print "all tests:", (tests + spec_tests) + for wasm in tests + spec_tests: + if not wasm.endswith('.wast') or os.path.basename(wasm) in blacklist: + continue + + asm = os.path.basename(wasm).replace('.wast', '.2asm.js') + expected_file = os.path.join('test', asm) + if not os.path.exists(expected_file): + continue + + print '..', wasm + + cmd = WASM2ASM + [os.path.join('test', wasm)] + out = run_command(cmd) + + # verify output + expected = open(expected_file).read() + fail_if_not_identical(out, expected) + + open('a.2asm.js', 'w').write(out) + + if NODEJS: + # verify asm.js is valid js + out = run_command([NODEJS, 'a.2asm.js']) + fail_if_not_identical(out, '') + + if MOZJS: + # verify asm.js validates + out = run_command([MOZJS, '-w', 'a.2asm.js'], + expected_err='Successfully compiled asm.js code', + err_contains=True) + fail_if_not_identical(out, '') if __name__ == "__main__": test_wasm2asm() diff --git a/test/hello_world.2asm.js b/test/hello_world.2asm.js index c11126e2445..08f5a556c0e 100644 --- a/test/hello_world.2asm.js +++ b/test/hello_world.2asm.js @@ -1,5 +1,5 @@ function asmFunc(global, env, buffer) { - "use asm"; + "use asm" var HEAP8 = new global.Int8Array(buffer); var HEAP16 = new global.Int16Array(buffer); var HEAP32 = new global.Int32Array(buffer); @@ -13,8 +13,8 @@ function asmFunc(global, env, buffer) { var Math_abs = global.Math.abs; var Math_clz32 = global.Math.clz32; function add(x, y) { - x = x | 0; - y = y | 0; + x = (x | 0) + y = (y | 0) return x + y | 0 | 0; } diff --git a/test/min.2asm.js b/test/min.2asm.js deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test/unit.2asm.js b/test/unit.2asm.js deleted file mode 100644 index e69de29bb2d..00000000000 From a072c3d3b61eb49af64645c8eb9d1f9f1875f641 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Thu, 27 Jul 2017 13:42:24 -0700 Subject: [PATCH 10/18] Please the python linter --- scripts/test/support.py | 2 +- scripts/test/wasm2asm.py | 2 ++ setup.cfg | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/test/support.py b/scripts/test/support.py index 586967ad96e..d75017ca00c 100755 --- a/scripts/test/support.py +++ b/scripts/test/support.py @@ -159,7 +159,7 @@ def run_command(cmd, expected_status=0, stderr=None, if proc.returncode != expected_status: raise Exception(('run_command failed', err)) err_correct = expected_err is None or \ - (expected_err in err if err_contains else expected_err == err) + (expected_err in err if err_contains else expected_err == err) if not err_correct: raise Exception(('run_command unexpected stderr', "expected '%s', actual '%s'" % (expected_err, err))) diff --git a/scripts/test/wasm2asm.py b/scripts/test/wasm2asm.py index ec9f910705b..7957fb62adc 100755 --- a/scripts/test/wasm2asm.py +++ b/scripts/test/wasm2asm.py @@ -5,6 +5,7 @@ from support import run_command from shared import (WASM2ASM, MOZJS, NODEJS, fail_if_not_identical, tests) + def test_wasm2asm(): print '\n[ checking wasm2asm testcases... ]\n' @@ -46,5 +47,6 @@ def test_wasm2asm(): err_contains=True) fail_if_not_identical(out, '') + if __name__ == "__main__": test_wasm2asm() diff --git a/setup.cfg b/setup.cfg index 46134f4707f..a52e1cf377e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,4 @@ [pep8] ignore = E111,E114 [flake8] -ignore = E111,E114 +ignore = E111,E114,E127 From 19bce003c490bb1efb265562d5704de5677f3ca7 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Thu, 27 Jul 2017 16:13:39 -0700 Subject: [PATCH 11/18] Re-enable flake8 E127 --- scripts/test/support.py | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/test/support.py b/scripts/test/support.py index d75017ca00c..97dd3d1c413 100755 --- a/scripts/test/support.py +++ b/scripts/test/support.py @@ -159,7 +159,7 @@ def run_command(cmd, expected_status=0, stderr=None, if proc.returncode != expected_status: raise Exception(('run_command failed', err)) err_correct = expected_err is None or \ - (expected_err in err if err_contains else expected_err == err) + (expected_err in err if err_contains else expected_err == err) if not err_correct: raise Exception(('run_command unexpected stderr', "expected '%s', actual '%s'" % (expected_err, err))) diff --git a/setup.cfg b/setup.cfg index a52e1cf377e..46134f4707f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,4 @@ [pep8] ignore = E111,E114 [flake8] -ignore = E111,E114,E127 +ignore = E111,E114 From 9abb406bf6d1c683e2f1ffd65fe0ff4b14a8323d Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 28 Jul 2017 14:04:39 -0700 Subject: [PATCH 12/18] Print semicolons after statements in blocks --- src/emscripten-optimizer/simple_ast.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h index 7fe8b85c8d2..62b98c9e98e 100644 --- a/src/emscripten-optimizer/simple_ast.h +++ b/src/emscripten-optimizer/simple_ast.h @@ -731,7 +731,7 @@ struct JSPrinter { if (used == last) emit(otherwise); } - void printStats(Ref stats) { + void printStats(Ref stats, bool semicolons=false) { bool first = true; for (size_t i = 0; i < stats->size(); i++) { Ref curr = stats[i]; @@ -739,6 +739,7 @@ struct JSPrinter { if (first) first = false; else newline(); print(stats[i]); + if (semicolons) emit(';'); } } } @@ -757,7 +758,7 @@ struct JSPrinter { emit('{'); indent++; newline(); - printStats(node[1]); + printStats(node[1], true); indent--; newline(); emit('}'); From cb57f4fb437aa36011c79e67a9509a697580f622 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 28 Jul 2017 14:09:40 -0700 Subject: [PATCH 13/18] Clean up printing in wasm2asm test --- scripts/test/wasm2asm.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/test/wasm2asm.py b/scripts/test/wasm2asm.py index 7957fb62adc..079bb7fadc2 100755 --- a/scripts/test/wasm2asm.py +++ b/scripts/test/wasm2asm.py @@ -13,8 +13,6 @@ def test_wasm2asm(): blacklist = ['atomics.wast', 'address.wast'] spec_tests = [os.path.join('spec', t) for t in sorted(os.listdir(os.path.join('test', 'spec')))] - print "spec_tests:", spec_tests - print "all tests:", (tests + spec_tests) for wasm in tests + spec_tests: if not wasm.endswith('.wast') or os.path.basename(wasm) in blacklist: continue From e75d5467a5f7b29de27da83df72d4ca3fc63a36f Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Mon, 31 Jul 2017 17:19:05 -0700 Subject: [PATCH 14/18] Cleanups and semicolons for condition arms --- src/asmjs/shared-constants.cpp | 1 - src/asmjs/shared-constants.h | 1 - src/emscripten-optimizer/simple_ast.h | 8 ++++-- src/wasm-builder.h | 6 ++-- src/wasm2asm.h | 41 ++++++++++++++++++--------- test/hello_world.2asm.js | 4 +-- 6 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/asmjs/shared-constants.cpp b/src/asmjs/shared-constants.cpp index f1c5ac53668..7e153bada6f 100644 --- a/src/asmjs/shared-constants.cpp +++ b/src/asmjs/shared-constants.cpp @@ -59,7 +59,6 @@ cashew::IString GLOBAL("global"), INSTRUMENT("instrument"), MATH_IMUL("Math_imul"), MATH_CLZ32("Math_clz32"), - MATH_CTZ32("Math_ctz32"), MATH_POPCNT32("Math_popcnt32"), MATH_ABS("Math_abs"), MATH_CEIL("Math_ceil"), diff --git a/src/asmjs/shared-constants.h b/src/asmjs/shared-constants.h index e3108c81eaf..dae6e7e4447 100644 --- a/src/asmjs/shared-constants.h +++ b/src/asmjs/shared-constants.h @@ -62,7 +62,6 @@ extern cashew::IString GLOBAL, INSTRUMENT, MATH_IMUL, MATH_CLZ32, - MATH_CTZ32, MATH_POPCNT32, MATH_ABS, MATH_CEIL, diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h index 62b98c9e98e..e5826d924f2 100644 --- a/src/emscripten-optimizer/simple_ast.h +++ b/src/emscripten-optimizer/simple_ast.h @@ -956,7 +956,7 @@ struct JSPrinter { } int getPrecedence(Ref node, bool parent) { - if (node->isAssign()) { + if (node->isAssign() || node->isAssignName()) { return OperatorClass::getPrecedence(OperatorClass::Binary, SET); } if (!node->isArray()) { @@ -1145,7 +1145,7 @@ struct JSPrinter { } static bool ifHasElse(Ref node) { - assert(node[0] == IF); + assert(node->isArray() && node[0] == IF); return node->size() >= 4 && !!node[3]; } @@ -1166,7 +1166,7 @@ struct JSPrinter { bool hasElse = ifHasElse(node); if (hasElse) { Ref child = node[2]; - while (child[0] == IF) { + while (child->isArray() && child[0] == IF) { if (!ifHasElse(child)) { needBraces = true; break; @@ -1185,11 +1185,13 @@ struct JSPrinter { } else { print(node[2], "{}"); } + emit(';'); if (hasElse) { space(); emit("else"); safeSpace(); print(node[3], "{}"); + emit(';'); } } diff --git a/src/wasm-builder.h b/src/wasm-builder.h index a1f2ec9b3d8..312747b9360 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -212,7 +212,7 @@ class Builder { return store; } AtomicRMW* makeAtomicRMW(AtomicRMWOp op, unsigned bytes, uint32_t offset, - Expression* ptr, Expression* value, WasmType type) { + Expression* ptr, Expression* value, WasmType type) { auto* ret = allocator.alloc(); ret->op = op; ret->bytes = bytes; @@ -224,8 +224,8 @@ class Builder { return ret; } AtomicCmpxchg* makeAtomicCmpxchg(unsigned bytes, uint32_t offset, - Expression* ptr, Expression* expected, Expression* replacement, - WasmType type) { + Expression* ptr, Expression* expected, + Expression* replacement, WasmType type) { auto* ret = allocator.alloc(); ret->bytes = bytes; ret->offset = offset; diff --git a/src/wasm2asm.h b/src/wasm2asm.h index c9a847c5603..94c6ebcd3cd 100644 --- a/src/wasm2asm.h +++ b/src/wasm2asm.h @@ -583,7 +583,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { // 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[0] == BLOCK; + return !!ast && ast->isArray() && ast[0] == BLOCK; } Ref blockify(Ref ast) { @@ -726,6 +726,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { return makeStatementizedCall(curr->operands, ValueBuilder::makeBlock(), theCall, result, curr->type); } Ref visitCallImport(CallImport *curr) { + std::cerr << "visitCallImport not implemented yet" << std::endl; abort(); } Ref visitCallIndirect(CallIndirect *curr) { @@ -807,7 +808,9 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } break; } - default: abort(); + default: + std::cerr << "Unhandled type in load: " << curr->type << std::endl; + abort(); } return ValueBuilder::makeSeq(ptrSet, rest); } @@ -926,7 +929,10 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } break; } - default: abort(); + default: + std::cerr << "Unhandled type in store: " << curr->type + << std::endl; + abort(); } return ValueBuilder::makeSeq(ValueBuilder::makeSeq(ptrSet, valueSet), rest); } @@ -996,8 +1002,6 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { switch (curr->op) { case ClzInt32: return ValueBuilder::makeCall(MATH_CLZ32, value); - case CtzInt32: - return ValueBuilder::makeCall(MATH_CTZ32, value); case PopcntInt32: return ValueBuilder::makeCall(MATH_POPCNT32, value); default: abort(); @@ -1009,35 +1013,46 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { switch (curr->op) { case NegFloat32: case NegFloat64: - ret = ValueBuilder::makeUnary(MINUS, value); + ret = ValueBuilder::makeUnary(MINUS, visit(curr->value, + EXPRESSION_RESULT)); break; case AbsFloat32: case AbsFloat64: - ret = ValueBuilder::makeCall(MATH_ABS, value); + ret = ValueBuilder::makeCall(MATH_ABS, visit(curr->value, + EXPRESSION_RESULT)); break; case CeilFloat32: case CeilFloat64: - ret = ValueBuilder::makeCall(MATH_CEIL, value); + ret = ValueBuilder::makeCall(MATH_CEIL, visit(curr->value, + EXPRESSION_RESULT)); break; case FloorFloat32: case FloorFloat64: - ret = ValueBuilder::makeCall(MATH_FLOOR, value); + ret = ValueBuilder::makeCall(MATH_FLOOR, + visit(curr->value, + EXPRESSION_RESULT)); break; case TruncFloat32: case TruncFloat64: - ret = ValueBuilder::makeCall(MATH_TRUNC, value); + ret = ValueBuilder::makeCall(MATH_TRUNC, + visit(curr->value, + EXPRESSION_RESULT)); break; case NearestFloat32: case NearestFloat64: - ret = ValueBuilder::makeCall(MATH_NEAREST, value); + ret = ValueBuilder::makeCall(MATH_NEAREST, + visit(curr->value, + EXPRESSION_RESULT)); break; case SqrtFloat32: case SqrtFloat64: - ret = ValueBuilder::makeCall(MATH_SQRT, value); + ret = ValueBuilder::makeCall(MATH_SQRT, visit(curr->value, + EXPRESSION_RESULT)); break; // TODO: more complex unary conversions default: - std::cerr << "Unhandled unary operator: " << curr << std::endl; + std::cerr << "Unhandled unary float operator: " << curr + << std::endl; abort(); } if (curr->type == f32) { // doubles need much less coercing diff --git a/test/hello_world.2asm.js b/test/hello_world.2asm.js index 08f5a556c0e..75ff4af253b 100644 --- a/test/hello_world.2asm.js +++ b/test/hello_world.2asm.js @@ -13,8 +13,8 @@ function asmFunc(global, env, buffer) { var Math_abs = global.Math.abs; var Math_clz32 = global.Math.clz32; function add(x, y) { - x = (x | 0) - y = (y | 0) + x = x | 0 + y = y | 0 return x + y | 0 | 0; } From b803ad94ecc1ef27e13444481efe82dc93aa9381 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Mon, 31 Jul 2017 18:05:15 -0700 Subject: [PATCH 15/18] Move ampersand --- src/wasm2asm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasm2asm.h b/src/wasm2asm.h index 94c6ebcd3cd..ed660466b12 100644 --- a/src/wasm2asm.h +++ b/src/wasm2asm.h @@ -292,7 +292,7 @@ void Wasm2AsmBuilder::addImport(Ref ast, Import *import) { void Wasm2AsmBuilder::addTables(Ref ast, Module *wasm) { std::map> tables; // asm.js tables, sig => contents of table - for (Table::Segment &seg : wasm->table.segments) { + for (Table::Segment& seg : wasm->table.segments) { for (size_t i = 0; i < seg.data.size(); i++) { Name name = seg.data[i]; auto func = wasm->getFunction(name); From 77434a0928efe6166a8512cbacb00aa59c9bfe4e Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 1 Aug 2017 17:08:51 -0700 Subject: [PATCH 16/18] Prettify semicolon emission --- src/emscripten-optimizer/simple_ast.h | 37 +++++++++++++++++---------- src/wasm2asm.h | 1 - test/empty_imported_table.2asm.js | 2 +- test/empty_table.2asm.js | 2 +- test/hello_world.2asm.js | 6 ++--- 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h index e5826d924f2..5cc989e8593 100644 --- a/src/emscripten-optimizer/simple_ast.h +++ b/src/emscripten-optimizer/simple_ast.h @@ -620,6 +620,22 @@ struct JSPrinter { } } + 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()) { @@ -731,7 +747,7 @@ struct JSPrinter { if (used == last) emit(otherwise); } - void printStats(Ref stats, bool semicolons=false) { + void printStats(Ref stats) { bool first = true; for (size_t i = 0; i < stats->size(); i++) { Ref curr = stats[i]; @@ -739,7 +755,9 @@ struct JSPrinter { if (first) first = false; else newline(); print(stats[i]); - if (semicolons) emit(';'); + if (!isDefun(stats[i]) && !isBlock(stats[i]) && !isIf(stats[i])) { + emit(';'); + } } } } @@ -758,7 +776,7 @@ struct JSPrinter { emit('{'); indent++; newline(); - printStats(node[1], true); + printStats(node[1]); indent--; newline(); emit('}'); @@ -789,10 +807,6 @@ struct JSPrinter { newline(); } - bool isNothing(Ref node) { - return node->isArray() && node[0] == TOPLEVEL && node[1]->size() == 0; - } - void printAssign(Ref node) { assert(false && "printAssign still used!"); auto* assign = node->asAssign(); @@ -1141,7 +1155,6 @@ struct JSPrinter { print(args[i][1]); } } - emit(';'); } static bool ifHasElse(Ref node) { @@ -1184,14 +1197,14 @@ struct JSPrinter { emit('}'); } else { print(node[2], "{}"); + if (!isBlock(node[2])) emit(';'); } - emit(';'); if (hasElse) { space(); emit("else"); safeSpace(); print(node[3], "{}"); - emit(';'); + if (!isBlock(node[3])) emit(';'); } } @@ -1205,7 +1218,6 @@ struct JSPrinter { emit('('); print(node[1]); emit(')'); - emit(';'); } void printWhile(Ref node) { @@ -1232,7 +1244,6 @@ struct JSPrinter { emit(' '); print(node[1]); } - emit(';'); } void printBreak(Ref node) { @@ -1241,7 +1252,6 @@ struct JSPrinter { emit(' '); emit(node[1]->getCString()); } - emit(';'); } void printContinue(Ref node) { @@ -1250,7 +1260,6 @@ struct JSPrinter { emit(' '); emit(node[1]->getCString()); } - emit(';'); } void printNew(Ref node) { diff --git a/src/wasm2asm.h b/src/wasm2asm.h index ed660466b12..4ad81816c73 100644 --- a/src/wasm2asm.h +++ b/src/wasm2asm.h @@ -758,7 +758,6 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } Ref visitSetLocal(SetLocal *curr) { if (!isStatement(curr)) { - // return ValueBuilder::makeAssign(ValueBuilder::makeName(fromName(func->getLocalName(curr->index))), visit(curr->value, EXPRESSION_RESULT)); return ValueBuilder::makeBinary( ValueBuilder::makeName(fromName(func->getLocalName(curr->index))), SET, visit(curr->value, EXPRESSION_RESULT)); diff --git a/test/empty_imported_table.2asm.js b/test/empty_imported_table.2asm.js index a0d8d0b84dd..a709f3fccd9 100644 --- a/test/empty_imported_table.2asm.js +++ b/test/empty_imported_table.2asm.js @@ -1,5 +1,5 @@ function asmFunc(global, env, buffer) { - "use asm" + "use asm"; var HEAP8 = new global.Int8Array(buffer); var HEAP16 = new global.Int16Array(buffer); var HEAP32 = new global.Int32Array(buffer); diff --git a/test/empty_table.2asm.js b/test/empty_table.2asm.js index 67547950eaa..30a200587c5 100644 --- a/test/empty_table.2asm.js +++ b/test/empty_table.2asm.js @@ -1,5 +1,5 @@ function asmFunc(global, env, buffer) { - "use asm" + "use asm"; var HEAP8 = new global.Int8Array(buffer); var HEAP16 = new global.Int16Array(buffer); var HEAP32 = new global.Int32Array(buffer); diff --git a/test/hello_world.2asm.js b/test/hello_world.2asm.js index 75ff4af253b..c11126e2445 100644 --- a/test/hello_world.2asm.js +++ b/test/hello_world.2asm.js @@ -1,5 +1,5 @@ function asmFunc(global, env, buffer) { - "use asm" + "use asm"; var HEAP8 = new global.Int8Array(buffer); var HEAP16 = new global.Int16Array(buffer); var HEAP32 = new global.Int32Array(buffer); @@ -13,8 +13,8 @@ function asmFunc(global, env, buffer) { var Math_abs = global.Math.abs; var Math_clz32 = global.Math.clz32; function add(x, y) { - x = x | 0 - y = y | 0 + x = x | 0; + y = y | 0; return x + y | 0 | 0; } From 281b6e0c8a38567ce88ff64d7a8da5fb4bbf00da Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 1 Aug 2017 17:25:50 -0700 Subject: [PATCH 17/18] Add comment explaining subset test --- scripts/test/wasm2asm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/test/wasm2asm.py b/scripts/test/wasm2asm.py index 079bb7fadc2..4f9c2ab3a5e 100755 --- a/scripts/test/wasm2asm.py +++ b/scripts/test/wasm2asm.py @@ -40,6 +40,7 @@ def test_wasm2asm(): if MOZJS: # verify asm.js validates + # check only subset of err because mozjs emits timing info out = run_command([MOZJS, '-w', 'a.2asm.js'], expected_err='Successfully compiled asm.js code', err_contains=True) From c5ba2eaece3daaa851fab5836ba9b25ced90d240 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 1 Aug 2017 17:54:04 -0700 Subject: [PATCH 18/18] Tiny cleanup --- src/emscripten-optimizer/simple_ast.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h index 5cc989e8593..43bac281da9 100644 --- a/src/emscripten-optimizer/simple_ast.h +++ b/src/emscripten-optimizer/simple_ast.h @@ -754,8 +754,8 @@ struct JSPrinter { if (!isNothing(curr)) { if (first) first = false; else newline(); - print(stats[i]); - if (!isDefun(stats[i]) && !isBlock(stats[i]) && !isIf(stats[i])) { + print(curr); + if (!isDefun(curr) && !isBlock(curr) && !isIf(curr)) { emit(';'); } }