diff options
author | Thomas Lively <tlively@users.noreply.github.com> | 2017-08-02 20:20:14 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2017-08-02 20:20:14 -0700 |
commit | ffd9a72d28d36915fb173a6d52fbb6e43f7c15db (patch) | |
tree | 0dbd0f9d99fc20ab1c1e63395e8f399fd51cf806 | |
parent | de15161e110f26212095c5cf4faf2e3668d2531b (diff) | |
download | binaryen-ffd9a72d28d36915fb173a6d52fbb6e43f7c15db.tar.gz binaryen-ffd9a72d28d36915fb173a6d52fbb6e43f7c15db.tar.bz2 binaryen-ffd9a72d28d36915fb173a6d52fbb6e43f7c15db.zip |
Get wasm2asm building again (#1107)
* Get wasm2asm building again
Updates CMakeLists.txt to have wasm2asm built by default, updates
wasm2asm.h to account for recent interface changes, and restores
JSPrinter functionality.
* Implement splice for array values
* Clean up wasm2asm testing
* Print semicolons after statements in blocks
* Cleanups and semicolons for condition arms
* Prettify semicolon emission
-rw-r--r-- | CMakeLists.txt | 15 | ||||
-rwxr-xr-x | check.py | 2 | ||||
-rw-r--r-- | scripts/test/shared.py | 1 | ||||
-rwxr-xr-x | scripts/test/support.py | 7 | ||||
-rwxr-xr-x | scripts/test/wasm2asm.py | 51 | ||||
-rw-r--r-- | src/asmjs/shared-constants.cpp | 1 | ||||
-rw-r--r-- | src/asmjs/shared-constants.h | 1 | ||||
-rw-r--r-- | src/emscripten-optimizer/simple_ast.h | 651 | ||||
-rw-r--r-- | src/mixed_arena.h | 21 | ||||
-rw-r--r-- | src/support/name.h | 7 | ||||
-rw-r--r-- | src/wasm-builder.h | 6 | ||||
-rw-r--r-- | src/wasm2asm-main.cpp | 3 | ||||
-rw-r--r-- | src/wasm2asm.h | 335 | ||||
-rw-r--r-- | test/empty_imported_table.2asm.js | 20 | ||||
-rw-r--r-- | test/empty_table.2asm.js | 19 | ||||
-rw-r--r-- | test/min.2asm.js | 0 | ||||
-rw-r--r-- | test/unit.2asm.js | 0 |
17 files changed, 1026 insertions, 114 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 157a2e2c0..999a1f7e7 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) - @@ -31,6 +31,7 @@ from scripts.test.shared import ( ) import scripts.test.s2wasm as s2wasm +import scripts.test.wasm2asm as wasm2asm if options.interpreter: print '[ using wasm interpreter at "%s" ]' % options.interpreter @@ -423,6 +424,7 @@ if MOZJS: 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 b71545965..f94f22fe8 100644 --- a/scripts/test/shared.py +++ b/scripts/test/shared.py @@ -155,6 +155,7 @@ WASM_OPT = [os.path.join(options.binaryen_bin, 'wasm-opt')] 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 5d791d155..97dd3d1c4 100755 --- a/scripts/test/support.py +++ b/scripts/test/support.py @@ -147,7 +147,8 @@ def split_wast(wast): 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 new file mode 100755 index 000000000..4f9c2ab3a --- /dev/null +++ b/scripts/test/wasm2asm.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python2 + +import os + +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, 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 + 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 + # 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) + fail_if_not_identical(out, '') + + +if __name__ == "__main__": + test_wasm2asm() diff --git a/src/asmjs/shared-constants.cpp b/src/asmjs/shared-constants.cpp index f1c5ac536..7e153bada 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 e3108c81e..dae6e7e44 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 efbd7b6f6..43bac281d 100644 --- a/src/emscripten-optimizer/simple_ast.h +++ b/src/emscripten-optimizer/simple_ast.h @@ -428,6 +428,11 @@ struct Value { return arr->back(); } + void splice(int x, int num) { + assert(isArray()); + arr->erase(arr->begin() + x, arr->begin() + x + num); + } + int indexOf(Ref other) { assert(isArray()); for (size_t i = 0; i < arr->size(); i++) { @@ -537,6 +542,294 @@ void traverseFunctions(Ref ast, std::function<void (Ref)> visit); // JS printing support struct JSPrinter { + bool pretty, finalize; + + char *buffer; + size_t size, used; + + int indent; + bool possibleSpace; // add a space to separate identifiers + + Ref ast; + + JSPrinter(bool pretty_, bool finalize_, Ref ast_) : pretty(pretty_), finalize(finalize_), buffer(0), size(0), used(0), indent(0), possibleSpace(false), ast(ast_) {} + + void printAst() { + print(ast); + buffer[used] = 0; + } + + // Utils + + void ensure(int safety=100) { + if (size >= used + safety) { + return; + } + size = std::max((size_t)1024, size * 2) + safety; + if (!buffer) { + buffer = (char*)malloc(size); + if (!buffer) { + errv("Out of memory allocating %zd bytes for output buffer!", size); + abort(); + } + } else { + char *buf = (char*)realloc(buffer, size); + if (!buf) { + free(buffer); + errv("Out of memory allocating %zd bytes for output buffer!", size); + abort(); + } + buffer = buf; + } + } + + void emit(char c) { + maybeSpace(c); + if (!pretty && c == '}' && buffer[used-1] == ';') used--; // optimize ;} into }, the ; is not separating anything + ensure(1); + buffer[used++] = c; + } + + void emit(const char *s) { + maybeSpace(*s); + int len = strlen(s); + ensure(len+1); + strncpy(buffer + used, s, len+1); + used += len; + } + + void newline() { + if (!pretty) return; + emit('\n'); + for (int i = 0; i < indent; i++) emit(' '); + } + + void space() { + if (pretty) emit(' '); + } + + void safeSpace() { + if (pretty) emit(' '); + else possibleSpace = true; + } + + void maybeSpace(char s) { + if (possibleSpace) { + possibleSpace = false; + if (isIdentPart(s)) emit(' '); + } + } + + bool isNothing(Ref node) { + return node->isArray() && node[0] == TOPLEVEL && node[1]->size() == 0; + } + + bool isDefun(Ref node) { + return node->isArray() && node[0] == DEFUN; + } + + bool isBlock(Ref node) { + return node->isArray() && node[0] == BLOCK; + } + + bool isIf(Ref node) { + return node->isArray() && node[0] == IF; + } + + void print(Ref node) { + ensure(); + if (node->isString()) { + printName(node); + return; + } + if (node->isNumber()) { + printNum(node); + return; + } + if (node->isAssignName()) { + printAssignName(node); + return; + } + if (node->isAssign()) { + printAssign(node); + } + IString type = node[0]->getIString(); + switch (type.str[0]) { + case 'a': { + if (type == ARRAY) printArray(node); + else abort(); + break; + } + case 'b': { + if (type == BINARY) printBinary(node); + else if (type == BLOCK) printBlock(node); + else if (type == BREAK) printBreak(node); + else abort(); + break; + } + case 'c': { + if (type == CALL) printCall(node); + else if (type == CONDITIONAL) printConditional(node); + else if (type == CONTINUE) printContinue(node); + else abort(); + break; + } + case 'd': { + if (type == DEFUN) printDefun(node); + else if (type == DO) printDo(node); + else if (type == DOT) printDot(node); + else abort(); + break; + } + case 'i': { + if (type == IF) printIf(node); + else abort(); + break; + } + case 'l': { + if (type == LABEL) printLabel(node); + else abort(); + break; + } + case 'n': { + if (type == NEW) printNew(node); + else abort(); + break; + } + case 'o': { + if (type == OBJECT) printObject(node); + break; + } + case 'r': { + if (type == RETURN) printReturn(node); + else abort(); + break; + } + case 's': { + if (type == SUB) printSub(node); + else if (type == SEQ) printSeq(node); + else if (type == SWITCH) printSwitch(node); + else if (type == STRING) printString(node); + else abort(); + break; + } + case 't': { + if (type == TOPLEVEL) printToplevel(node); + else abort(); + break; + } + case 'u': { + if (type == UNARY_PREFIX) printUnaryPrefix(node); + else abort(); + break; + } + case 'v': { + if (type == VAR) printVar(node); + else abort(); + break; + } + case 'w': { + if (type == WHILE) printWhile(node); + else abort(); + break; + } + default: { + errv("cannot yet print %s\n", type.str); + abort(); + } + } + } + + // print a node, and if nothing is emitted, emit something instead + void print(Ref node, const char *otherwise) { + auto last = used; + print(node); + if (used == last) emit(otherwise); + } + + void printStats(Ref stats) { + bool first = true; + for (size_t i = 0; i < stats->size(); i++) { + Ref curr = stats[i]; + if (!isNothing(curr)) { + if (first) first = false; + else newline(); + print(curr); + if (!isDefun(curr) && !isBlock(curr) && !isIf(curr)) { + emit(';'); + } + } + } + } + + void printToplevel(Ref node) { + if (node[1]->size() > 0) { + printStats(node[1]); + } + } + + void printBlock(Ref node) { + if (node->size() == 1 || node[1]->size() == 0) { + emit("{}"); + return; + } + emit('{'); + indent++; + newline(); + printStats(node[1]); + indent--; + newline(); + emit('}'); + } + + void printDefun(Ref node) { + emit("function "); + emit(node[1]->getCString()); + emit('('); + Ref args = node[2]; + for (size_t i = 0; i < args->size(); i++) { + if (i > 0) (pretty ? emit(", ") : emit(',')); + emit(args[i]->getCString()); + } + emit(')'); + space(); + if (node->size() == 3 || node[3]->size() == 0) { + emit("{}"); + return; + } + emit('{'); + indent++; + newline(); + printStats(node[3]); + indent--; + newline(); + emit('}'); + newline(); + } + + void printAssign(Ref node) { + assert(false && "printAssign still used!"); + auto* assign = node->asAssign(); + printChild(assign->target(), node, -1); + space(); + emit('='); + space(); + printChild(assign->value(), node, 1); + } + + void printAssignName(Ref node) { + auto *assign = node->asAssignName(); + emit(assign->target().c_str()); + space(); + emit('='); + space(); + printChild(assign->value(), node, 1); + } + + void printName(Ref node) { + emit(node->getCString()); + } + static char* numToString(double d, bool finalize=true) { bool neg = d < 0; if (neg) d = -d; @@ -658,8 +951,366 @@ struct JSPrinter { } return ret; } + + void printNum(Ref node) { + emit(numToString(node->getNumber(), finalize)); + } + + void printString(Ref node) { + emit('"'); + emit(node[1]->getCString()); + emit('"'); + } + + // Parens optimizing + + bool capturesOperators(Ref node) { + Ref type = node[0]; + return type == CALL || type == ARRAY || type == OBJECT || type == SEQ; + } + + int getPrecedence(Ref node, bool parent) { + if (node->isAssign() || node->isAssignName()) { + return OperatorClass::getPrecedence(OperatorClass::Binary, SET); + } + if (!node->isArray()) { + // node is a value + return -1; + } + Ref type = node[0]; + if (type == BINARY || type == UNARY_PREFIX) { + return OperatorClass::getPrecedence(type == BINARY ? OperatorClass::Binary : OperatorClass::Prefix, node[1]->getIString()); + } else if (type == SEQ) { + return OperatorClass::getPrecedence(OperatorClass::Binary, COMMA); + } else if (type == CALL) { + return parent ? OperatorClass::getPrecedence(OperatorClass::Binary, COMMA) : -1; // call arguments are split by commas, but call itself is safe + } else if (type == CONDITIONAL) { + return OperatorClass::getPrecedence(OperatorClass::Tertiary, QUESTION); + } + // otherwise, this is something that fixes precedence explicitly, and we can ignore + return -1; // XXX + } + + // check whether we need parens for the child, when rendered in the parent + // @param childPosition -1 means it is printed to the left of parent, 0 means "anywhere", 1 means right + bool needParens(Ref parent, Ref child, int childPosition) { + int parentPrecedence = getPrecedence(parent, true); + int childPrecedence = getPrecedence(child, false); + + if (childPrecedence > parentPrecedence) return true; // child is definitely a danger + if (childPrecedence < parentPrecedence) return false; // definitely cool + // equal precedence, so associativity (rtl/ltr) is what matters + // (except for some exceptions, where multiple operators can combine into confusion) + if (parent[0] == UNARY_PREFIX) { + assert(child[0] == UNARY_PREFIX); + if ((parent[1] == PLUS || parent[1] == MINUS) && child[1] == parent[1]) { + // cannot emit ++x when we mean +(+x) + return true; + } + } + if (childPosition == 0) return true; // child could be anywhere, so always paren + if (childPrecedence < 0) return false; // both precedences are safe + // check if child is on the dangerous side + if (OperatorClass::getRtl(parentPrecedence)) return childPosition < 0; + else return childPosition > 0; + } + + void printChild(Ref child, Ref parent, int childPosition=0) { + bool parens = needParens(parent, child, childPosition); + if (parens) emit('('); + print(child); + if (parens) emit(')'); + } + + void printBinary(Ref node) { + printChild(node[2], node, -1); + space(); + emit(node[1]->getCString()); + space(); + printChild(node[3], node, 1); + } + + void printUnaryPrefix(Ref node) { + if (finalize && node[1] == PLUS && (node[2]->isNumber() || + (node[2][0] == UNARY_PREFIX && node[2][1] == MINUS && node[2][2]->isNumber()))) { + // emit a finalized number + int last = used; + print(node[2]); + ensure(1); // we temporarily append a 0 + char *curr = buffer + last; // ensure might invalidate + buffer[used] = 0; + if (strchr(curr, '.')) return; // already a decimal point, all good + char *e = strchr(curr, 'e'); + if (!e) { + emit(".0"); + return; + } + ensure(3); + curr = buffer + last; // ensure might invalidate + char *end = strchr(curr, 0); + while (end >= e) { + end[2] = end[0]; + end--; + } + e[0] = '.'; + e[1] = '0'; + used += 2; + return; + } + if ((buffer[used-1] == '-' && node[1] == MINUS) || + (buffer[used-1] == '+' && node[1] == PLUS)) { + emit(' '); // cannot join - and - to --, looks like the -- operator + } + emit(node[1]->getCString()); + printChild(node[2], node, 1); + } + + void printConditional(Ref node) { + printChild(node[1], node, -1); + space(); + emit('?'); + space(); + printChild(node[2], node, 0); + space(); + emit(':'); + space(); + printChild(node[3], node, 1); + } + + void printCall(Ref node) { + printChild(node[1], node, 0); + emit('('); + Ref args = node[2]; + for (size_t i = 0; i < args->size(); i++) { + if (i > 0) (pretty ? emit(", ") : emit(',')); + printChild(args[i], node, 0); + } + emit(')'); + } + + void printSeq(Ref node) { + printChild(node[1], node, -1); + emit(','); + space(); + printChild(node[2], node, 1); + } + + void printDot(Ref node) { + print(node[1]); + emit('.'); + emit(node[2]->getCString()); + } + + void printSwitch(Ref node) { + emit("switch"); + space(); + emit('('); + print(node[1]); + emit(')'); + space(); + emit('{'); + newline(); + Ref cases = node[2]; + for (size_t i = 0; i < cases->size(); i++) { + Ref c = cases[i]; + if (!c[0]) { + emit("default:"); + } else { + emit("case "); + print(c[0]); + emit(':'); + } + if (c[1]->size() > 0) { + indent++; + newline(); + auto curr = used; + printStats(c[1]); + indent--; + if (curr != used) newline(); + else used--; // avoid the extra indentation we added tentatively + } else { + newline(); + } + } + emit('}'); + } + + void printSub(Ref node) { + printChild(node[1], node, -1); + emit('['); + print(node[2]); + emit(']'); + } + + void printVar(Ref node) { + emit("var "); + Ref args = node[1]; + for (size_t i = 0; i < args->size(); i++) { + if (i > 0) (pretty ? emit(", ") : emit(',')); + emit(args[i][0]->getCString()); + if (args[i]->size() > 1) { + space(); + emit('='); + space(); + print(args[i][1]); + } + } + } + + static bool ifHasElse(Ref node) { + assert(node->isArray() && node[0] == IF); + return node->size() >= 4 && !!node[3]; + } + + void printIf(Ref node) { + emit("if"); + safeSpace(); + emit('('); + print(node[1]); + emit(')'); + space(); + // special case: we need braces to save us from ambiguity, if () { if () } else. otherwise else binds to inner if + // also need to recurse for if () { if () { } else { if () } else + // (note that this is only a problem if the if body has a single element in it, not a block or such, as then + // the block would be braced) + // this analysis is a little conservative - it assumes any child if could be confused with us, which implies + // all other braces vanished (the worst case for us, we are not saved by other braces). + bool needBraces = false; + bool hasElse = ifHasElse(node); + if (hasElse) { + Ref child = node[2]; + while (child->isArray() && child[0] == IF) { + if (!ifHasElse(child)) { + needBraces = true; + break; + } + child = child[3]; // continue into the else + } + } + if (needBraces) { + emit('{'); + indent++; + newline(); + print(node[2]); + indent--; + newline(); + emit('}'); + } else { + print(node[2], "{}"); + if (!isBlock(node[2])) emit(';'); + } + if (hasElse) { + space(); + emit("else"); + safeSpace(); + print(node[3], "{}"); + if (!isBlock(node[3])) emit(';'); + } + } + + void printDo(Ref node) { + emit("do"); + safeSpace(); + print(node[2], "{}"); + space(); + emit("while"); + space(); + emit('('); + print(node[1]); + emit(')'); + } + + void printWhile(Ref node) { + emit("while"); + space(); + emit('('); + print(node[1]); + emit(')'); + space(); + print(node[2], "{}"); + } + + void printLabel(Ref node) { + emit(node[1]->getCString()); + space(); + emit(':'); + space(); + print(node[2]); + } + + void printReturn(Ref node) { + emit("return"); + if (!!node[1]) { + emit(' '); + print(node[1]); + } + } + + void printBreak(Ref node) { + emit("break"); + if (!!node[1]) { + emit(' '); + emit(node[1]->getCString()); + } + } + + void printContinue(Ref node) { + emit("continue"); + if (!!node[1]) { + emit(' '); + emit(node[1]->getCString()); + } + } + + void printNew(Ref node) { + emit("new "); + print(node[1]); + } + + void printArray(Ref node) { + emit('['); + Ref args = node[1]; + for (size_t i = 0; i < args->size(); i++) { + if (i > 0) (pretty ? emit(", ") : emit(',')); + print(args[i]); + } + emit(']'); + } + + void printObject(Ref node) { + emit('{'); + indent++; + newline(); + Ref args = node[1]; + for (size_t i = 0; i < args->size(); i++) { + if (i > 0) { + pretty ? emit(", ") : emit(','); + newline(); + } + const char *str = args[i][0]->getCString(); + const char *check = str; + bool needQuote = false; + while (*check) { + if (!isalnum(*check) && *check != '_' && *check != '$') { + needQuote = true; + break; + } + check++; + } + if (needQuote) emit('"'); + emit(str); + if (needQuote) emit('"'); + emit(":"); + space(); + print(args[i][1]); + } + indent--; + newline(); + emit('}'); + } }; + // cashew builder class ValueBuilder { diff --git a/src/mixed_arena.h b/src/mixed_arena.h index 47e718454..9354527ec 100644 --- a/src/mixed_arena.h +++ b/src/mixed_arena.h @@ -163,6 +163,8 @@ protected: } public: + struct Iterator; + T& operator[](size_t index) const { assert(index < usedElements); return data[index]; @@ -206,6 +208,16 @@ public: 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 @@ public: 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/support/name.h b/src/support/name.h index 9853f5462..b77397555 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<wasm::Name> : hash<cashew::IString> {}; + +} // namespace std + + #endif // wasm_support_string_h diff --git a/src/wasm-builder.h b/src/wasm-builder.h index a1f2ec9b3..312747b93 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -212,7 +212,7 @@ public: 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<AtomicRMW>(); ret->op = op; ret->bytes = bytes; @@ -224,8 +224,8 @@ public: 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<AtomicCmpxchg>(); ret->bytes = bytes; ret->offset = offset; diff --git a/src/wasm2asm-main.cpp b/src/wasm2asm-main.cpp index fa652cb47..95bdd0b08 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 f328918c2..4ad81816c 100644 --- a/src/wasm2asm.h +++ b/src/wasm2asm.h @@ -23,6 +23,7 @@ #define wasm_wasm2asm_h #include <cmath> +#include <numeric> #include "asmjs/shared-constants.h" #include "wasm.h" @@ -209,7 +210,11 @@ 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 { + return size + seg.data.size(); + }); size_t pow2ed = 1; while (pow2ed < tableSize) { pow2ed <<= 1; @@ -287,19 +292,21 @@ void Wasm2AsmBuilder::addImport(Ref ast, Import *import) { void Wasm2AsmBuilder::addTables(Ref ast, Module *wasm) { std::map<std::string, std::vector<IString>> 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 (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) { @@ -327,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); @@ -340,9 +347,10 @@ 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), + wasmToAsmType(func->getLocalType(i))) ) ) ); @@ -542,7 +550,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; @@ -557,7 +566,8 @@ 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)); } return ret; } @@ -573,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) { @@ -588,7 +598,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { std::map<Name, IString> breakResults; // Breaks to the top of a loop should be emitted as continues, to that loop's main label - std::map<Name, Name> continueLabels; + std::unordered_set<Name> continueLabels; IString fromName(Name name) { return parent->fromName(name); @@ -629,14 +639,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 +660,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 +702,9 @@ 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)); } flattenAppend(ret, theCall); for (auto temp : temps) { @@ -717,10 +726,11 @@ 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) { - 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 +758,17 @@ 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)); } 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))), + SET, temp.getAstName()))); return ret; } Ref visitLoad(Load *curr) { @@ -785,36 +800,63 @@ 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); } break; } - default: abort(); + default: + std::cerr << "Unhandled type in load: " << curr->type << std::endl; + abort(); } return ValueBuilder::makeSeq(ptrSet, rest); } // 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)); } @@ -866,12 +908,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<Expression*>(&shifted) : static_cast<Expression*>(&getValue); anded.right = &_255; anded.type = i32; @@ -882,11 +924,14 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } else { rest = ValueBuilder::makeSeq(rest, part); } - store.offset += 1; + ++store.offset; } break; } - default: abort(); + default: + std::cerr << "Unhandled type in store: " << curr->type + << std::endl; + abort(); } return ValueBuilder::makeSeq(ValueBuilder::makeSeq(ptrSet, valueSet), rest); } @@ -911,7 +956,11 @@ 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 visitDrop(Drop *curr) { + assert(!isStatement(curr)); + return visitAndAssign(curr->value, result); } Ref visitConst(Const *curr) { switch (curr->type) { @@ -950,9 +999,10 @@ 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 PopcntInt32: + return ValueBuilder::makeCall(MATH_POPCNT32, value); default: abort(); } } @@ -960,26 +1010,58 @@ 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, visit(curr->value, + EXPRESSION_RESULT)); + break; + case AbsFloat32: + case AbsFloat64: + ret = ValueBuilder::makeCall(MATH_ABS, visit(curr->value, + EXPRESSION_RESULT)); + break; + case CeilFloat32: + case CeilFloat64: + ret = ValueBuilder::makeCall(MATH_CEIL, visit(curr->value, + EXPRESSION_RESULT)); + break; + case FloorFloat32: + case FloorFloat64: + ret = ValueBuilder::makeCall(MATH_FLOOR, + visit(curr->value, + EXPRESSION_RESULT)); + break; + case TruncFloat32: + case TruncFloat64: + ret = ValueBuilder::makeCall(MATH_TRUNC, + visit(curr->value, + EXPRESSION_RESULT)); + break; + case NearestFloat32: + case NearestFloat64: + ret = ValueBuilder::makeCall(MATH_NEAREST, + visit(curr->value, + EXPRESSION_RESULT)); + break; + case SqrtFloat32: + case SqrtFloat64: + ret = ValueBuilder::makeCall(MATH_SQRT, visit(curr->value, + EXPRESSION_RESULT)); + break; + // TODO: more complex unary conversions + default: + std::cerr << "Unhandled unary float operator: " << curr + << std::endl; + abort(); } if (curr->type == f32) { // doubles need much less coercing return makeAsmCoercion(ret, ASM_FLOAT); } return ret; } - default: abort(); + default: + std::cerr << "Unhandled type: " << curr << std::endl; + abort(); } } Ref visitBinary(Binary *curr) { @@ -1003,55 +1085,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 << "Unhandled binary operator: " << curr << std::endl; + abort(); } return makeAsmCoercion(ret, wasmToAsmType(curr->type)); } @@ -1085,11 +1216,11 @@ 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()) ) ) diff --git a/test/empty_imported_table.2asm.js b/test/empty_imported_table.2asm.js new file mode 100644 index 000000000..a709f3fcc --- /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 000000000..30a200587 --- /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 { + + }; +} + diff --git a/test/min.2asm.js b/test/min.2asm.js deleted file mode 100644 index e69de29bb..000000000 --- a/test/min.2asm.js +++ /dev/null diff --git a/test/unit.2asm.js b/test/unit.2asm.js deleted file mode 100644 index e69de29bb..000000000 --- a/test/unit.2asm.js +++ /dev/null |