summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lively <tlively@users.noreply.github.com>2017-08-02 20:20:14 -0700
committerAlon Zakai <alonzakai@gmail.com>2017-08-02 20:20:14 -0700
commitffd9a72d28d36915fb173a6d52fbb6e43f7c15db (patch)
tree0dbd0f9d99fc20ab1c1e63395e8f399fd51cf806
parentde15161e110f26212095c5cf4faf2e3668d2531b (diff)
downloadbinaryen-ffd9a72d28d36915fb173a6d52fbb6e43f7c15db.tar.gz
binaryen-ffd9a72d28d36915fb173a6d52fbb6e43f7c15db.tar.bz2
binaryen-ffd9a72d28d36915fb173a6d52fbb6e43f7c15db.zip
Get wasm2asm building again (#1107)
* Get wasm2asm building again Updates CMakeLists.txt to have wasm2asm built by default, updates wasm2asm.h to account for recent interface changes, and restores JSPrinter functionality. * Implement splice for array values * Clean up wasm2asm testing * Print semicolons after statements in blocks * Cleanups and semicolons for condition arms * Prettify semicolon emission
-rw-r--r--CMakeLists.txt15
-rwxr-xr-xcheck.py2
-rw-r--r--scripts/test/shared.py1
-rwxr-xr-xscripts/test/support.py7
-rwxr-xr-xscripts/test/wasm2asm.py51
-rw-r--r--src/asmjs/shared-constants.cpp1
-rw-r--r--src/asmjs/shared-constants.h1
-rw-r--r--src/emscripten-optimizer/simple_ast.h651
-rw-r--r--src/mixed_arena.h21
-rw-r--r--src/support/name.h7
-rw-r--r--src/wasm-builder.h6
-rw-r--r--src/wasm2asm-main.cpp3
-rw-r--r--src/wasm2asm.h335
-rw-r--r--test/empty_imported_table.2asm.js20
-rw-r--r--test/empty_table.2asm.js19
-rw-r--r--test/min.2asm.js0
-rw-r--r--test/unit.2asm.js0
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)
-
diff --git a/check.py b/check.py
index 105c177c7..6f6f23138 100755
--- a/check.py
+++ b/check.py
@@ -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