diff options
author | Thomas Lively <tlively@users.noreply.github.com> | 2017-08-16 15:01:56 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2017-08-16 15:01:56 -0700 |
commit | 5fafb87a2819cebd94941c77c07ebe067471eb7d (patch) | |
tree | 232edd6e144a43094bf2127a6a6e4913a5674884 /src | |
parent | 21d06aea04244fa71b5053bd87f2ec6a2ac9d5c2 (diff) | |
download | binaryen-5fafb87a2819cebd94941c77c07ebe067471eb7d.tar.gz binaryen-5fafb87a2819cebd94941c77c07ebe067471eb7d.tar.bz2 binaryen-5fafb87a2819cebd94941c77c07ebe067471eb7d.zip |
wasm2asm test generation (#1124)
* Translate assert_return invokes to asm
* Translate assert_trap tests to JS
* Enable wasm2asm tests
* Fix wasm2asm translation of store
* Update ubuntu nodejs in Travis
* Free JSPrinter buffer
* Use unique_ptr for Functions to prevent leaks
* Add tests for assert translation
Diffstat (limited to 'src')
-rw-r--r-- | src/asmjs/shared-constants.cpp | 2 | ||||
-rw-r--r-- | src/asmjs/shared-constants.h | 2 | ||||
-rw-r--r-- | src/emscripten-optimizer/parser.cpp | 1 | ||||
-rw-r--r-- | src/emscripten-optimizer/parser.h | 1 | ||||
-rw-r--r-- | src/emscripten-optimizer/simple_ast.h | 102 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 4 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 2 | ||||
-rw-r--r-- | src/wasm2asm-main.cpp | 21 | ||||
-rw-r--r-- | src/wasm2asm.h | 280 |
9 files changed, 300 insertions, 115 deletions
diff --git a/src/asmjs/shared-constants.cpp b/src/asmjs/shared-constants.cpp index b355fecb7..6e92f1b3e 100644 --- a/src/asmjs/shared-constants.cpp +++ b/src/asmjs/shared-constants.cpp @@ -32,6 +32,8 @@ cashew::IString GLOBAL("global"), UINT32ARRAY("Uint32Array"), FLOAT32ARRAY("Float32Array"), FLOAT64ARRAY("Float64Array"), + ARRAY_BUFFER("ArrayBuffer"), + ASM_MODULE("asmModule"), IMPOSSIBLE_CONTINUE("impossible-continue"), MATH("Math"), IMUL("imul"), diff --git a/src/asmjs/shared-constants.h b/src/asmjs/shared-constants.h index 92dab0b69..555efce6a 100644 --- a/src/asmjs/shared-constants.h +++ b/src/asmjs/shared-constants.h @@ -35,6 +35,8 @@ extern cashew::IString GLOBAL, UINT32ARRAY, FLOAT32ARRAY, FLOAT64ARRAY, + ARRAY_BUFFER, + ASM_MODULE, IMPOSSIBLE_CONTINUE, MATH, IMUL, diff --git a/src/emscripten-optimizer/parser.cpp b/src/emscripten-optimizer/parser.cpp index cc7d4ff37..eb4abf6b3 100644 --- a/src/emscripten-optimizer/parser.cpp +++ b/src/emscripten-optimizer/parser.cpp @@ -41,6 +41,7 @@ IString TOPLEVEL("toplevel"), CONTINUE("continue"), SWITCH("switch"), STRING("string"), + TRY("try"), INF("inf"), NaN("nan"), TEMP_RET0("tempRet0"), diff --git a/src/emscripten-optimizer/parser.h b/src/emscripten-optimizer/parser.h index cac9896cc..1c12bf944 100644 --- a/src/emscripten-optimizer/parser.h +++ b/src/emscripten-optimizer/parser.h @@ -56,6 +56,7 @@ extern IString TOPLEVEL, CONTINUE, SWITCH, STRING, + TRY, INF, NaN, TEMP_RET0, diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h index 43bac281d..870765323 100644 --- a/src/emscripten-optimizer/simple_ast.h +++ b/src/emscripten-optimizer/simple_ast.h @@ -554,6 +554,10 @@ struct JSPrinter { JSPrinter(bool pretty_, bool finalize_, Ref ast_) : pretty(pretty_), finalize(finalize_), buffer(0), size(0), used(0), indent(0), possibleSpace(false), ast(ast_) {} + ~JSPrinter() { + free(buffer); + } + void printAst() { print(ast); buffer[used] = 0; @@ -652,6 +656,7 @@ struct JSPrinter { } if (node->isAssign()) { printAssign(node); + return; } IString type = node[0]->getIString(); switch (type.str[0]) { @@ -715,6 +720,7 @@ struct JSPrinter { } case 't': { if (type == TOPLEVEL) printToplevel(node); + else if (type == TRY) printTry(node); else abort(); break; } @@ -808,7 +814,6 @@ struct JSPrinter { } void printAssign(Ref node) { - assert(false && "printAssign still used!"); auto* assign = node->asAssign(); printChild(assign->target(), node, -1); space(); @@ -1135,6 +1140,15 @@ struct JSPrinter { emit('}'); } + void printTry(Ref node) { + emit("try "); + printBlock(node[1]); + emit(" catch ("); + printName(node[2]); + emit(") "); + printBlock(node[3]); + } + void printSub(Ref node) { printChild(node[1], node, -1); emit('['); @@ -1377,66 +1391,18 @@ public: .push_back(makeRawArray()); return ret; } - static Ref makeCall(IString target, Ref arg) { - Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) - .push_back(makeName(target)) - .push_back(makeRawArray(1)); - ret[2]->push_back(arg); - return ret; - } - static Ref makeCall(IString target, Ref arg1, Ref arg2) { - Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) - .push_back(makeName(target)) - .push_back(makeRawArray(2)); - ret[2]->push_back(arg1); - ret[2]->push_back(arg2); - return ret; - } - static Ref makeCall(IString target, Ref arg1, Ref arg2, Ref arg3, Ref arg4) { - Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) - .push_back(makeName(target)) - .push_back(makeRawArray(4)); - ret[2]->push_back(arg1); - ret[2]->push_back(arg2); - ret[2]->push_back(arg3); - ret[2]->push_back(arg4); - return ret; - } - static Ref makeCall(IString target, Ref arg1, Ref arg2, Ref arg3, Ref arg4, Ref arg5, Ref arg6, Ref arg7, Ref arg8) { - Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) - .push_back(makeName(target)) - .push_back(makeRawArray(8)); - ret[2]->push_back(arg1); - ret[2]->push_back(arg2); - ret[2]->push_back(arg3); - ret[2]->push_back(arg4); - ret[2]->push_back(arg5); - ret[2]->push_back(arg6); - ret[2]->push_back(arg7); - ret[2]->push_back(arg8); - return ret; - } - static Ref makeCall(IString target, Ref arg1, Ref arg2, Ref arg3, Ref arg4, Ref arg5, Ref arg6, Ref arg7, Ref arg8, Ref arg9, Ref arg10, Ref arg11, Ref arg12, Ref arg13, Ref arg14, Ref arg15, Ref arg16) { - Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) - .push_back(makeName(target)) - .push_back(makeRawArray(16)); - ret[2]->push_back(arg1); - ret[2]->push_back(arg2); - ret[2]->push_back(arg3); - ret[2]->push_back(arg4); - ret[2]->push_back(arg5); - ret[2]->push_back(arg6); - ret[2]->push_back(arg7); - ret[2]->push_back(arg8); - ret[2]->push_back(arg9); - ret[2]->push_back(arg10); - ret[2]->push_back(arg11); - ret[2]->push_back(arg12); - ret[2]->push_back(arg13); - ret[2]->push_back(arg14); - ret[2]->push_back(arg15); - ret[2]->push_back(arg16); - return ret; + + template<typename ...Ts> + static Ref makeCall(IString target, Ts... args) { + size_t nArgs = sizeof...(Ts); + Ref callArgs = makeRawArray(nArgs); + Ref argArray[] = {args...}; + for (size_t i = 0; i < nArgs; ++i) { + callArgs->push_back(argArray[i]); + } + return &makeRawArray(3)->push_back(makeRawString(CALL)) + .push_back(makeName(target)) + .push_back(callArgs); } static void appendToCall(Ref call, Ref element) { @@ -1610,12 +1576,26 @@ public: } } + static Ref makeTry(Ref try_, Ref arg, Ref catch_) { + assert(try_[0] == BLOCK); + assert(catch_[0] == BLOCK); + return &makeRawArray(3)->push_back(makeRawString(TRY)) + .push_back(try_) + .push_back(arg) + .push_back(catch_); + } + static Ref makeDot(Ref obj, IString key) { return &makeRawArray(3)->push_back(makeRawString(DOT)) .push_back(obj) .push_back(makeRawString(key)); } + template<typename ...Ts> + static Ref makeDot(Ref obj, Ref key, Ts... args) { + return makeDot(makeDot(obj, key), args...); + } + static Ref makeDot(Ref obj, Ref key) { assert(key->isString()); return makeDot(obj, key->getIString()); diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index d621128de..6be17f561 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -159,6 +159,10 @@ public: } Expression* parseExpression(Element& s); + MixedArena& getAllocator() { + return allocator; + } + private: Expression* makeExpression(Element& s); Expression* makeBinary(Element& s, BinaryOp op, WasmType type); diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index e991f71de..044507e86 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -495,7 +495,7 @@ void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) { std::move(params), result, std::move(vars) - )); + )); }; auto ensureAutoBlock = [&]() { if (!autoBlock) { diff --git a/src/wasm2asm-main.cpp b/src/wasm2asm-main.cpp index 95bdd0b08..10e059d8d 100644 --- a/src/wasm2asm-main.cpp +++ b/src/wasm2asm-main.cpp @@ -28,19 +28,31 @@ using namespace cashew; using namespace wasm; int main(int argc, const char *argv[]) { + Wasm2AsmBuilder::Flags builderFlags; Options options("wasm2asm", "Transform .wast files to asm.js"); options .add("--output", "-o", "Output file (stdout if not specified)", Options::Arguments::One, - [](Options *o, const std::string &argument) { + [](Options* o, const std::string& argument) { o->extra["output"] = argument; Colors::disable(); }) + .add("--allow-asserts", "", "Allow compilation of .wast testing asserts", + Options::Arguments::Zero, + [](Options* o, const std::string& argument) { + o->extra["asserts"] = "1"; + }) + .add("--pedantic", "", "Emulate WebAssembly trapping behavior", + Options::Arguments::Zero, + [&](Options* o, const std::string& argument) { + builderFlags.pedantic = true; + }) .add_positional("INFILE", Options::Arguments::One, [](Options *o, const std::string &argument) { o->extra["infile"] = argument; }); options.parse(argc, argv); + if (options.debug) builderFlags.debug = true; auto input( read_file<std::vector<char>>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release)); @@ -54,9 +66,14 @@ int main(int argc, const char *argv[]) { SExpressionWasmBuilder builder(wasm, *root[0]); if (options.debug) std::cerr << "asming..." << std::endl; - Wasm2AsmBuilder wasm2asm(options.debug); + Wasm2AsmBuilder wasm2asm(builderFlags); Ref asmjs = wasm2asm.processWasm(&wasm); + if (options.extra["asserts"] == "1") { + if (options.debug) std::cerr << "asserting..." << std::endl; + flattenAppend(asmjs, wasm2asm.processAsserts(root, builder)); + } + if (options.debug) { std::cerr << "a-printing..." << std::endl; asmjs->stringify(std::cout, true); diff --git a/src/wasm2asm.h b/src/wasm2asm.h index e2bb2bca7..7ba9d5175 100644 --- a/src/wasm2asm.h +++ b/src/wasm2asm.h @@ -45,8 +45,8 @@ IString ASM_FUNC("asmFunc"), // Appends extra to block, flattening out if extra is a block as well void flattenAppend(Ref ast, Ref extra) { int index; - if (ast->isArray() && ast[0] == BLOCK) index = 1; - else if (ast->isArray() && ast[0] == DEFUN) index = 3; + if (ast[0] == BLOCK || ast[0] == TOPLEVEL) index = 1; + else if (ast[0] == DEFUN) index = 3; else abort(); if (extra->isArray() && extra[0] == BLOCK) { for (size_t i = 0; i < extra[1]->size(); i++) { @@ -108,7 +108,12 @@ class Wasm2AsmBuilder { MixedArena allocator; public: - Wasm2AsmBuilder(bool debug) : debug(debug), tableSize(-1) {} + struct Flags { + bool debug: 1; + bool pedantic: 1; + }; + + Wasm2AsmBuilder(Flags f) : flags(f) {} Ref processWasm(Module* wasm); Ref processFunction(Function* func); @@ -125,15 +130,21 @@ public: // if we have one. Ref processFunctionBody(Function* func, IString result); + Ref processAsserts(Element& e, SExpressionWasmBuilder& sexpBuilder); + // Get a temp var. - IString getTemp(WasmType type) { + IString getTemp(WasmType type, Function* func) { IString ret; if (frees[type].size() > 0) { ret = frees[type].back(); frees[type].pop_back(); } else { size_t index = temps[type]++; - ret = IString((std::string("wasm2asm_") + printWasmType(type) + "$" + std::to_string(index)).c_str(), false); + ret = IString((std::string("wasm2asm_") + printWasmType(type) + "$" + + std::to_string(index)).c_str(), false); + } + if (func->localIndices.find(ret) == func->localIndices.end()) { + Builder::addVar(func, ret, type); } return ret; } @@ -175,7 +186,8 @@ public: } private: - bool debug; + Flags flags; + // How many temp vars we need std::vector<size_t> temps; // type => num temps // Which are currently free to use @@ -192,7 +204,13 @@ private: void addTables(Ref ast, Module *wasm); void addExports(Ref ast, Module *wasm); void addWasmCompatibilityFuncs(Module *wasm); - + bool isAssertHandled(Element& e); + Ref makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder, + Builder& wasmBuilder, + Element& e, Name testFuncName); + Ref makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder, + Builder& wasmBuilder, + Element& e, Name testFuncName); Wasm2AsmBuilder() = delete; Wasm2AsmBuilder(const Wasm2AsmBuilder &) = delete; Wasm2AsmBuilder &operator=(const Wasm2AsmBuilder&) = delete; @@ -496,7 +514,7 @@ void Wasm2AsmBuilder::addExports(Ref ast, Module *wasm) { } Ref Wasm2AsmBuilder::processFunction(Function* func) { - if (debug) std::cerr << " processFunction " << func->name << std::endl; + if (flags.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); @@ -523,7 +541,8 @@ Ref Wasm2AsmBuilder::processFunction(Function* func) { // body scanFunctionBody(func->body); if (isStatement(func->body)) { - IString result = func->result != none ? getTemp(func->result) : NO_RESULT; + IString result = + func->result != none ? getTemp(func->result, func) : NO_RESULT; flattenAppend(ret, ValueBuilder::makeStatement(processFunctionBody(func, result))); if (func->result != none) { // do the actual return @@ -542,15 +561,6 @@ Ref Wasm2AsmBuilder::processFunction(Function* func) { for (Index i = func->getVarIndexBase(); i < func->getNumLocals(); i++) { ValueBuilder::appendToVar(theVar, fromName(func->getLocalName(i)), makeAsmCoercedZero(wasmToAsmType(func->getLocalType(i)))); } - for (auto f : frees[i32]) { - ValueBuilder::appendToVar(theVar, f, makeAsmCoercedZero(ASM_INT)); - } - for (auto f : frees[f32]) { - ValueBuilder::appendToVar(theVar, f, makeAsmCoercedZero(ASM_FLOAT)); - } - for (auto f : frees[f64]) { - ValueBuilder::appendToVar(theVar, f, makeAsmCoercedZero(ASM_DOUBLE)); - } if (theVar[1]->size() == 0) { ret[3]->splice(theVarIndex, 1); } @@ -676,10 +686,11 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { // @param possible if provided, this is a variable we can use as our temp. it has already been // allocated in a higher scope, and we can just assign to it as our result is // going there anyhow. - ScopedTemp(WasmType type, Wasm2AsmBuilder* parent, IString possible = NO_RESULT) : parent(parent), type(type) { + ScopedTemp(WasmType type, Wasm2AsmBuilder* parent, Function* func, + IString possible = NO_RESULT) : parent(parent), type(type) { assert(possible != EXPRESSION_RESULT); if (possible == NO_RESULT) { - temp = parent->getTemp(type); + temp = parent->getTemp(type, func); needFree = true; } else { temp = possible; @@ -715,7 +726,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { // 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); + ScopedTemp temp(type, parent, func); tempName = temp.temp; return visit(curr, temp); } else { @@ -838,7 +849,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { Ref ret = ValueBuilder::makeBlock(); Ref condition; if (isStatement(curr->condition)) { - ScopedTemp temp(i32, parent); + ScopedTemp temp(i32, parent, func); flattenAppend(ret[2], visit(curr->condition, temp)); condition = temp.getAstName(); } else { @@ -858,7 +869,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { Ref makeStatementizedCall(ExpressionList& operands, Ref ret, Ref theCall, IString result, WasmType type) { std::vector<ScopedTemp*> temps; // TODO: utility class, with destructor? for (auto& operand : operands) { - temps.push_back(new ScopedTemp(operand->type, parent)); + temps.push_back(new ScopedTemp(operand->type, parent, func)); IString temp = temps.back()->temp; flattenAppend(ret, visitAndAssign(operand, temp)); theCall[2]->push_back(makeAsmCoercion(ValueBuilder::makeName(temp), wasmToAsmType(operand->type))); @@ -911,7 +922,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } // we must statementize them all Ref ret = ValueBuilder::makeBlock(); - ScopedTemp temp(i32, parent); + ScopedTemp temp(i32, parent, func); flattenAppend(ret, visit(curr->target, temp)); Ref theCall = makeTableCall(temp.getAstName()); return makeStatementizedCall(curr->operands, ret, theCall, result, curr->type); @@ -925,7 +936,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { 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. + ScopedTemp temp(curr->type, parent, func, 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( @@ -936,7 +947,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } Ref visitLoad(Load *curr) { if (isStatement(curr)) { - ScopedTemp temp(i32, parent); + ScopedTemp temp(i32, parent, func); GetLocal fakeLocal(allocator); fakeLocal.index = func->getLocalIndex(temp.getName()); Load fakeLoad = *curr; @@ -947,7 +958,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } if (curr->align != 0 && curr->align < curr->bytes) { // set the pointer to a local - ScopedTemp temp(i32, parent); + ScopedTemp temp(i32, parent, func); SetLocal set(allocator); set.index = func->getLocalIndex(temp.getName()); set.value = curr->ptr; @@ -970,9 +981,10 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } break; } - default: + default: { std::cerr << "Unhandled type in load: " << curr->type << std::endl; abort(); + } } return ValueBuilder::makeSeq(ptrSet, rest); } @@ -1002,10 +1014,11 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { ValueBuilder::makeName(curr->signed_ ? HEAP32 : HEAPU32), ValueBuilder::makePtrShift(ptr, 2)); break; - default: + default: { std::cerr << "Unhandled number of bytes in i32 load: " << curr->bytes << std::endl; abort(); + } } break; } @@ -1017,16 +1030,17 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF64), ValueBuilder::makePtrShift(ptr, 3)); break; - default: + default: { std::cerr << "Unhandled type in load: " << curr->type << std::endl; abort(); + } } return makeAsmCoercion(ret, wasmToAsmType(curr->type)); } Ref visitStore(Store *curr) { if (isStatement(curr)) { - ScopedTemp tempPtr(i32, parent); - ScopedTemp tempValue(curr->type, parent); + ScopedTemp tempPtr(i32, parent, func); + ScopedTemp tempValue(curr->valueType, parent, func); GetLocal fakeLocalPtr(allocator); fakeLocalPtr.index = func->getLocalIndex(tempPtr.getName()); GetLocal fakeLocalValue(allocator); @@ -1041,7 +1055,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } if (curr->align != 0 && curr->align < curr->bytes) { // set the pointer to a local - ScopedTemp temp(i32, parent); + ScopedTemp temp(i32, parent, func); SetLocal set(allocator); set.index = func->getLocalIndex(temp.getName()); set.value = curr->ptr; @@ -1049,7 +1063,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { GetLocal get(allocator); get.index = func->getLocalIndex(temp.getName()); // set the value to a local - ScopedTemp tempValue(curr->value->type, parent); + ScopedTemp tempValue(curr->value->type, parent, func); SetLocal setValue(allocator); setValue.index = func->getLocalIndex(tempValue.getName()); setValue.value = curr->value; @@ -1061,7 +1075,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { store.ptr = &get; store.bytes = 1; // do the worst Ref rest; - switch (curr->type) { + switch (curr->valueType) { case i32: { Const _255(allocator); _255.value = Literal(int32_t(255)); @@ -1091,10 +1105,11 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } break; } - default: - std::cerr << "Unhandled type in store: " << curr->type + default: { + std::cerr << "Unhandled type in store: " << curr->valueType << std::endl; abort(); + } } return ValueBuilder::makeSeq(ValueBuilder::makeSeq(ptrSet, valueSet), rest); } @@ -1105,7 +1120,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } Ref value = visit(curr->value, EXPRESSION_RESULT); Ref ret; - switch (curr->type) { + switch (curr->valueType) { case i32: { switch (curr->bytes) { case 1: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAP8), ValueBuilder::makePtrShift(ptr, 0)); break; @@ -1117,7 +1132,11 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } 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(); + default: { + std::cerr << "Unhandled type in store: " << curr->valueType + << std::endl; + abort(); + } } return ValueBuilder::makeBinary(ret, SET, value); } @@ -1148,7 +1167,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } Ref visitUnary(Unary *curr) { if (isStatement(curr)) { - ScopedTemp temp(curr->value->type, parent); + ScopedTemp temp(curr->value->type, parent, func); GetLocal fakeLocal(allocator); fakeLocal.index = func->getLocalIndex(temp.getName()); Unary fakeUnary = *curr; @@ -1176,10 +1195,11 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { makeAsmCoercion(visit(curr->value, EXPRESSION_RESULT), ASM_INT), EQ, makeAsmCoercion(ValueBuilder::makeInt(0), ASM_INT)); - default: + default: { std::cerr << "Unhandled unary i32 operator: " << curr << std::endl; abort(); + } } } case f32: @@ -1246,17 +1266,18 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } return ret; } - default: + default: { std::cerr << "Unhandled type: " << curr << std::endl; abort(); + } } } Ref visitBinary(Binary *curr) { if (isStatement(curr)) { - ScopedTemp tempLeft(curr->left->type, parent); + ScopedTemp tempLeft(curr->left->type, parent, func); GetLocal fakeLocalLeft(allocator); fakeLocalLeft.index = func->getLocalIndex(tempLeft.getName()); - ScopedTemp tempRight(curr->right->type, parent); + ScopedTemp tempRight(curr->right->type, parent, func); GetLocal fakeLocalRight(allocator); fakeLocalRight.index = func->getLocalIndex(tempRight.getName()); Binary fakeBinary = *curr; @@ -1371,21 +1392,22 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { return ValueBuilder::makeCall(ROTL32, left, right); case RotRInt32: return ValueBuilder::makeCall(ROTR32, left, right); - default: + default: { std::cerr << "Unhandled binary operator: " << curr << std::endl; abort(); + } } return makeAsmCoercion(ret, wasmToAsmType(curr->type)); } Ref visitSelect(Select *curr) { if (isStatement(curr)) { - ScopedTemp tempIfTrue(curr->ifTrue->type, parent); + ScopedTemp tempIfTrue(curr->ifTrue->type, parent, func); GetLocal fakeLocalIfTrue(allocator); fakeLocalIfTrue.index = func->getLocalIndex(tempIfTrue.getName()); - ScopedTemp tempIfFalse(curr->ifFalse->type, parent); + ScopedTemp tempIfFalse(curr->ifFalse->type, parent, func); GetLocal fakeLocalIfFalse(allocator); fakeLocalIfFalse.index = func->getLocalIndex(tempIfFalse.getName()); - ScopedTemp tempCondition(i32, parent); + ScopedTemp tempCondition(i32, parent, func); GetLocal fakeCondition(allocator); fakeCondition.index = func->getLocalIndex(tempCondition.getName()); Select fakeSelect = *curr; @@ -1402,9 +1424,9 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { Ref ifTrue = visit(curr->ifTrue, EXPRESSION_RESULT); Ref ifFalse = visit(curr->ifFalse, EXPRESSION_RESULT); Ref condition = visit(curr->condition, EXPRESSION_RESULT); - ScopedTemp tempIfTrue(curr->type, parent), - tempIfFalse(curr->type, parent), - tempCondition(i32, parent); + ScopedTemp tempIfTrue(curr->type, parent, func), + tempIfFalse(curr->type, parent, func), + tempCondition(i32, parent, func); return ValueBuilder::makeSeq( ValueBuilder::makeBinary(tempCondition.getAstName(), SET, condition), @@ -1433,6 +1455,162 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { return ExpressionProcessor(this, func).visit(func->body, result); } +static Ref makeInstantiation() { + Ref lib = ValueBuilder::makeObject(); + auto insertItem = [&](IString item) { + ValueBuilder::appendToObject(lib, item, ValueBuilder::makeName(item)); + }; + insertItem(MATH); + insertItem(INT8ARRAY); + insertItem(INT16ARRAY); + insertItem(INT32ARRAY); + insertItem(UINT8ARRAY); + insertItem(UINT16ARRAY); + insertItem(UINT32ARRAY); + insertItem(FLOAT32ARRAY); + insertItem(FLOAT64ARRAY); + Ref env = ValueBuilder::makeObject(); + Ref mem = ValueBuilder::makeNew( + ValueBuilder::makeCall(ARRAY_BUFFER, ValueBuilder::makeInt(0x10000))); + Ref call = ValueBuilder::makeCall(IString(ASM_FUNC), lib, env, mem); + Ref ret = ValueBuilder::makeVar(); + ValueBuilder::appendToVar(ret, ASM_MODULE, call); + return ret; +} + +static void prefixCalls(Ref asmjs) { + if (asmjs->isArray()) { + ArrayStorage& arr = asmjs->getArray(); + for (Ref& r : arr) { + prefixCalls(r); + } + if (arr.size() > 0 && arr[0]->isString() && arr[0]->getIString() == CALL) { + assert(arr.size() >= 2); + Ref prefixed = ValueBuilder::makeDot(ValueBuilder::makeName(ASM_MODULE), + arr[1]->getIString()); + arr[1]->setArray(prefixed->getArray()); + } + } +} + +Ref Wasm2AsmBuilder::makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder, + Builder& wasmBuilder, + Element& e, Name testFuncName) { + Expression* actual = sexpBuilder.parseExpression(e[1]); + Expression* expected = sexpBuilder.parseExpression(e[2]); + WasmType resType = expected->type; + actual->type = resType; + BinaryOp eqOp; + switch (resType) { + case i32: eqOp = EqInt32; break; + case i64: eqOp = EqInt64; break; + case f32: eqOp = EqFloat32; break; + case f64: eqOp = EqFloat64; break; + default: { + std::cerr << "Unhandled type in assert: " << resType << std::endl; + abort(); + } + } + Binary* test = wasmBuilder.makeBinary(eqOp, actual, expected); + std::unique_ptr<Function> testFunc( + wasmBuilder.makeFunction(testFuncName, + std::vector<NameType>{}, + i32, + std::vector<NameType>{}, + test) + ); + Ref jsFunc = processFunction(testFunc.get()); + prefixCalls(jsFunc); + return jsFunc; +} + +Ref Wasm2AsmBuilder::makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder, + Builder& wasmBuilder, + Element& e, Name testFuncName) { + Name innerFuncName("f"); + Expression* expr = sexpBuilder.parseExpression(e[1]); + std::unique_ptr<Function> exprFunc( + wasmBuilder.makeFunction(innerFuncName, + std::vector<NameType>{}, + expr->type, + std::vector<NameType>{}, + expr) + ); + IString expectedErr = e[2]->str(); + Ref innerFunc = processFunction(exprFunc.get()); + Ref outerFunc = ValueBuilder::makeFunction(testFuncName); + outerFunc[3]->push_back(innerFunc); + Ref tryBlock = ValueBuilder::makeBlock(); + ValueBuilder::appendToBlock(tryBlock, ValueBuilder::makeCall(innerFuncName)); + Ref catchBlock = ValueBuilder::makeBlock(); + ValueBuilder::appendToBlock( + catchBlock, + ValueBuilder::makeReturn( + ValueBuilder::makeCall( + ValueBuilder::makeDot( + ValueBuilder::makeName(IString("e")), + ValueBuilder::makeName(IString("message")), + ValueBuilder::makeName(IString("includes")) + ), + ValueBuilder::makeString(expectedErr) + ) + ) + ); + outerFunc[3]->push_back(ValueBuilder::makeTry( + tryBlock, + ValueBuilder::makeName((IString("e"))), + catchBlock)); + outerFunc[3]->push_back(ValueBuilder::makeReturn(ValueBuilder::makeInt(0))); + return outerFunc; +} + +bool Wasm2AsmBuilder::isAssertHandled(Element& e) { + return e.isList() && e.size() >= 2 && e[0]->isStr() + && (e[0]->str() == Name("assert_return") || + (flags.pedantic && e[0]->str() == Name("assert_trap"))) + && e[1]->isList() && e[1]->size() >= 2 && (*e[1])[0]->isStr() + && (*e[1])[0]->str() == Name("invoke"); +} + +Ref Wasm2AsmBuilder::processAsserts(Element& root, + SExpressionWasmBuilder& sexpBuilder) { + Builder wasmBuilder(sexpBuilder.getAllocator()); + Ref ret = ValueBuilder::makeBlock(); + flattenAppend(ret, makeInstantiation()); + for (size_t i = 1; i < root.size(); ++i) { + Element& e = *root[i]; + if (!isAssertHandled(e)) { + std::cerr << "skipping " << e << std::endl; + continue; + } + Name testFuncName(IString(("check" + std::to_string(i)).c_str(), false)); + bool isReturn = (e[0]->str() == Name("assert_return")); + Element& testOp = *e[1]; + // Replace "invoke" with "call" + testOp[0]->setString(IString("call"), false, false); + // Need to claim dollared to get string as function target + testOp[1]->setString(testOp[1]->str(), /*dollared=*/true, false); + + Ref testFunc = isReturn ? + makeAssertReturnFunc(sexpBuilder, wasmBuilder, e, testFuncName) : + makeAssertTrapFunc(sexpBuilder, wasmBuilder, e, testFuncName); + + flattenAppend(ret, testFunc); + std::stringstream failFuncName; + failFuncName << "fail" << std::to_string(i); + flattenAppend( + ret, + ValueBuilder::makeIf( + ValueBuilder::makeUnary(L_NOT, ValueBuilder::makeCall(testFuncName)), + ValueBuilder::makeCall(IString(failFuncName.str().c_str(), false)), + Ref() + ) + ); + } + return ret; +} + + } // namespace wasm #endif // wasm_wasm2asm_h |