summaryrefslogtreecommitdiff
path: root/src/tools/wasm2js.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/wasm2js.cpp')
-rw-r--r--src/tools/wasm2js.cpp321
1 files changed, 170 insertions, 151 deletions
diff --git a/src/tools/wasm2js.cpp b/src/tools/wasm2js.cpp
index 84aa9f024..3d5f484da 100644
--- a/src/tools/wasm2js.cpp
+++ b/src/tools/wasm2js.cpp
@@ -18,13 +18,14 @@
// wasm2js console tool
//
+#include "wasm2js.h"
+#include "optimization-options.h"
+#include "pass.h"
#include "support/colors.h"
#include "support/command-line.h"
#include "support/file.h"
#include "wasm-s-parser.h"
#include "wasm2js.h"
-#include "optimization-options.h"
-#include "pass.h"
using namespace cashew;
using namespace wasm;
@@ -34,8 +35,8 @@ using namespace wasm;
namespace {
static void optimizeWasm(Module& wasm, PassOptions options) {
- // Perform various optimizations that will be good for JS, but would not be great
- // for wasm in general
+ // Perform various optimizations that will be good for JS, but would not be
+ // great for wasm in general
struct OptimizeForJS : public WalkerPass<PostWalker<OptimizeForJS>> {
bool isFunctionParallel() override { return true; }
@@ -60,14 +61,12 @@ static void optimizeWasm(Module& wasm, PassOptions options) {
runner.run();
}
-template<typename T>
-static void printJS(Ref ast, T& output) {
+template<typename T> static void printJS(Ref ast, T& output) {
JSPrinter jser(true, true, ast);
jser.printAst();
output << jser.buffer << std::endl;
}
-
// Traversals
struct TraverseInfo {
@@ -102,7 +101,9 @@ private:
};
// Traverse, calling visit after the children
-static void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::function<void (Ref)> visitPost) {
+static void traversePrePost(Ref node,
+ std::function<void(Ref)> visitPre,
+ std::function<void(Ref)> visitPost) {
std::vector<TraverseInfo> stack;
stack.push_back(TraverseInfo(node));
while (!stack.empty()) {
@@ -123,7 +124,7 @@ static void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::f
}
}
-static void traversePost(Ref node, std::function<void (Ref)> visit) {
+static void traversePost(Ref node, std::function<void(Ref)> visit) {
traversePrePost(node, [](Ref node) {}, visit);
}
@@ -131,24 +132,28 @@ static void optimizeJS(Ref ast) {
// helpers
auto isOrZero = [](Ref node) {
- return node->isArray() && !node->empty() && node[0] == BINARY && node[1] == OR && node[3]->isNumber() && node[3]->getNumber() == 0;
+ return node->isArray() && !node->empty() && node[0] == BINARY &&
+ node[1] == OR && node[3]->isNumber() && node[3]->getNumber() == 0;
};
auto isPlus = [](Ref node) {
- return node->isArray() && !node->empty() && node[0] == UNARY_PREFIX && node[1] == PLUS;
+ return node->isArray() && !node->empty() && node[0] == UNARY_PREFIX &&
+ node[1] == PLUS;
};
auto isBitwise = [](Ref node) {
if (node->isArray() && !node->empty() && node[0] == BINARY) {
auto op = node[1];
- return op == OR || op == AND || op == XOR || op == RSHIFT || op == TRSHIFT || op == LSHIFT;
+ return op == OR || op == AND || op == XOR || op == RSHIFT ||
+ op == TRSHIFT || op == LSHIFT;
}
return false;
};
// x >> 0 => x | 0
traversePost(ast, [](Ref node) {
- if (node->isArray() && !node->empty() && node[0] == BINARY && node[1] == RSHIFT && node[3]->isNumber()) {
+ if (node->isArray() && !node->empty() && node[0] == BINARY &&
+ node[1] == RSHIFT && node[3]->isNumber()) {
if (node[3]->getNumber() == 0) {
node[1]->setString(OR);
}
@@ -190,60 +195,69 @@ static void optimizeJS(Ref ast) {
// XXX IString invalid("__wasm2js$INVALID_LABEL__");
std::vector<Ref> breakCapturers;
std::vector<Ref> continueCapturers;
- std::unordered_map<IString, Ref> labelToValue; // maps the label to the loop/etc.
+ std::unordered_map<IString, Ref>
+ labelToValue; // maps the label to the loop/etc.
std::unordered_set<Value*> labelled; // all things with a label on them.
Value INVALID;
- traversePrePost(ast, [&](Ref node) {
- if (node->isArray() && !node->empty()) {
- if (node[0] == LABEL) {
- auto label = node[1]->getIString();
- labelToValue[label] = node[2];
- labelled.insert(node[2].get());
- } else if (node[0] == WHILE || node[0] == DO || node[0] == FOR) {
- breakCapturers.push_back(node);
- continueCapturers.push_back(node);
- } else if (node[0] == cashew::BLOCK) {
- if (labelled.count(node.get())) {
- // Cannot break to a block without the label.
- breakCapturers.push_back(Ref(&INVALID));
+ traversePrePost(
+ ast,
+ [&](Ref node) {
+ if (node->isArray() && !node->empty()) {
+ if (node[0] == LABEL) {
+ auto label = node[1]->getIString();
+ labelToValue[label] = node[2];
+ labelled.insert(node[2].get());
+ } else if (node[0] == WHILE || node[0] == DO || node[0] == FOR) {
+ breakCapturers.push_back(node);
+ continueCapturers.push_back(node);
+ } else if (node[0] == cashew::BLOCK) {
+ if (labelled.count(node.get())) {
+ // Cannot break to a block without the label.
+ breakCapturers.push_back(Ref(&INVALID));
+ }
+ } else if (node[0] == SWITCH) {
+ breakCapturers.push_back(node);
}
- } else if (node[0] == SWITCH) {
- breakCapturers.push_back(node);
}
- }
- }, [&](Ref node) {
- if (node->isArray() && !node->empty()) {
- if (node[0] == LABEL) {
- auto label = node[1]->getIString();
- labelToValue.erase(label);
- labelled.erase(node[2].get());
- } else if (node[0] == WHILE || node[0] == DO || node[0] == FOR) {
- breakCapturers.pop_back();
- continueCapturers.pop_back();
- } else if (node[0] == cashew::BLOCK) {
- if (labelled.count(node.get())) {
- breakCapturers.pop_back();
- }
- } else if (node[0] == SWITCH) {
- breakCapturers.pop_back();
- } else if (node[0] == BREAK || node[0] == CONTINUE) {
- if (!node[1]->isNull()) {
+ },
+ [&](Ref node) {
+ if (node->isArray() && !node->empty()) {
+ if (node[0] == LABEL) {
auto label = node[1]->getIString();
- assert(labelToValue.count(label));
- auto& capturers = node[0] == BREAK ? breakCapturers : continueCapturers;
- assert(!capturers.empty());
- if (capturers.back() == labelToValue[label]) {
- // Success, the break/continue goes exactly where we would if we
- // didn't have the label!
- node[1]->setNull();
+ labelToValue.erase(label);
+ labelled.erase(node[2].get());
+ } else if (node[0] == WHILE || node[0] == DO || node[0] == FOR) {
+ breakCapturers.pop_back();
+ continueCapturers.pop_back();
+ } else if (node[0] == cashew::BLOCK) {
+ if (labelled.count(node.get())) {
+ breakCapturers.pop_back();
+ }
+ } else if (node[0] == SWITCH) {
+ breakCapturers.pop_back();
+ } else if (node[0] == BREAK || node[0] == CONTINUE) {
+ if (!node[1]->isNull()) {
+ auto label = node[1]->getIString();
+ assert(labelToValue.count(label));
+ auto& capturers =
+ node[0] == BREAK ? breakCapturers : continueCapturers;
+ assert(!capturers.empty());
+ if (capturers.back() == labelToValue[label]) {
+ // Success, the break/continue goes exactly where we would if we
+ // didn't have the label!
+ node[1]->setNull();
+ }
}
}
}
- }
- });
+ });
}
-static void emitWasm(Module& wasm, Output& output, Wasm2JSBuilder::Flags flags, PassOptions options, Name name) {
+static void emitWasm(Module& wasm,
+ Output& output,
+ Wasm2JSBuilder::Flags flags,
+ PassOptions options,
+ Name name) {
if (options.optimizeLevel > 0) {
optimizeWasm(wasm, options);
}
@@ -264,7 +278,9 @@ public:
SExpressionWasmBuilder& sexpBuilder,
Output& out,
Wasm2JSBuilder::Flags flags,
- PassOptions options) : root(root), sexpBuilder(sexpBuilder), out(out), flags(flags), options(options) {}
+ PassOptions options)
+ : root(root), sexpBuilder(sexpBuilder), out(out), flags(flags),
+ options(options) {}
void emit();
@@ -310,11 +326,9 @@ Ref AssertionEmitter::emitAssertReturnFunc(Builder& wasmBuilder,
Expression* actual = sexpBuilder.parseExpression(e[1]);
Expression* body = nullptr;
if (e.size() == 2) {
- if (actual->type == none) {
- body = wasmBuilder.blockify(
- actual,
- wasmBuilder.makeConst(Literal(uint32_t(1)))
- );
+ if (actual->type == none) {
+ body = wasmBuilder.blockify(actual,
+ wasmBuilder.makeConst(Literal(uint32_t(1))));
} else {
body = actual;
}
@@ -330,9 +344,10 @@ Ref AssertionEmitter::emitAssertReturnFunc(Builder& wasmBuilder,
case i64:
body = wasmBuilder.makeCall(
"i64Equal",
- {actual, wasmBuilder.makeCall(WASM_FETCH_HIGH_BITS, {}, i32), expected},
- i32
- );
+ {actual,
+ wasmBuilder.makeCall(WASM_FETCH_HIGH_BITS, {}, i32),
+ expected},
+ i32);
break;
case f32: {
@@ -353,14 +368,11 @@ Ref AssertionEmitter::emitAssertReturnFunc(Builder& wasmBuilder,
assert(false && "Unexpected number of parameters in assert_return");
}
std::unique_ptr<Function> testFunc(
- wasmBuilder.makeFunction(
- testFuncName,
- std::vector<NameType>{},
- body->type,
- std::vector<NameType>{},
- body
- )
- );
+ wasmBuilder.makeFunction(testFuncName,
+ std::vector<NameType>{},
+ body->type,
+ std::vector<NameType>{},
+ body));
Ref jsFunc = processFunction(testFunc.get());
fixCalls(jsFunc, asmModule);
emitFunction(jsFunc);
@@ -374,14 +386,11 @@ Ref AssertionEmitter::emitAssertReturnNanFunc(Builder& wasmBuilder,
Expression* actual = sexpBuilder.parseExpression(e[1]);
Expression* body = wasmBuilder.makeCall("isNaN", {actual}, i32);
std::unique_ptr<Function> testFunc(
- wasmBuilder.makeFunction(
- testFuncName,
- std::vector<NameType>{},
- body->type,
- std::vector<NameType>{},
- body
- )
- );
+ wasmBuilder.makeFunction(testFuncName,
+ std::vector<NameType>{},
+ body->type,
+ std::vector<NameType>{},
+ body));
Ref jsFunc = processFunction(testFunc.get());
fixCalls(jsFunc, asmModule);
emitFunction(jsFunc);
@@ -399,8 +408,7 @@ Ref AssertionEmitter::emitAssertTrapFunc(Builder& wasmBuilder,
std::vector<NameType>{},
expr->type,
std::vector<NameType>{},
- expr)
- );
+ expr));
IString expectedErr = e[2]->str();
Ref innerFunc = processFunction(exprFunc.get());
fixCalls(innerFunc, asmModule);
@@ -411,33 +419,25 @@ Ref AssertionEmitter::emitAssertTrapFunc(Builder& wasmBuilder,
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)
- )
- )
- );
+ 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));
+ tryBlock, ValueBuilder::makeName((IString("e"))), catchBlock));
outerFunc[3]->push_back(ValueBuilder::makeReturn(ValueBuilder::makeInt(0)));
emitFunction(outerFunc);
return outerFunc;
}
bool AssertionEmitter::isAssertHandled(Element& e) {
- return e.isList() && e.size() >= 2 && e[0]->isStr()
- && (e[0]->str() == Name("assert_return") ||
+ return e.isList() && e.size() >= 2 && e[0]->isStr() &&
+ (e[0]->str() == Name("assert_return") ||
e[0]->str() == Name("assert_return_nan") ||
- (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");
+ (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");
}
void AssertionEmitter::fixCalls(Ref asmjs, Name asmModule) {
@@ -446,7 +446,8 @@ void AssertionEmitter::fixCalls(Ref asmjs, Name asmModule) {
for (Ref& r : arr) {
fixCalls(r, asmModule);
}
- if (arr.size() > 0 && arr[0]->isString() && arr[0]->getIString() == cashew::CALL) {
+ if (arr.size() > 0 && arr[0]->isString() &&
+ arr[0]->getIString() == cashew::CALL) {
assert(arr.size() >= 2);
if (arr[1]->getIString() == "f32Equal" ||
arr[1]->getIString() == "f64Equal" ||
@@ -457,7 +458,7 @@ void AssertionEmitter::fixCalls(Ref asmjs, Name asmModule) {
arr[1]->setString("Math.fround");
} else {
Ref fixed = ValueBuilder::makeDot(ValueBuilder::makeName(asmModule),
- arr[1]->getIString());
+ arr[1]->getIString());
arr[1]->setArray(fixed->getArray());
}
}
@@ -522,7 +523,8 @@ void AssertionEmitter::emit() {
Name asmModule = std::string("ret") + ASM_FUNC.str;
for (size_t i = 0; i < root.size(); ++i) {
Element& e = *root[i];
- if (e.isList() && e.size() >= 1 && e[0]->isStr() && e[0]->str() == Name("module")) {
+ if (e.isList() && e.size() >= 1 && e[0]->isStr() &&
+ e[0]->str() == Name("module")) {
std::stringstream funcNameS;
funcNameS << ASM_FUNC.c_str() << i;
std::stringstream moduleNameS;
@@ -555,10 +557,7 @@ void AssertionEmitter::emit() {
emitAssertTrapFunc(wasmBuilder, e, testFuncName, asmModule);
}
- out << "if (!"
- << testFuncName.str
- << "()) throw 'assertion failed: "
- << e
+ out << "if (!" << testFuncName.str << "()) throw 'assertion failed: " << e
<< "';\n";
}
}
@@ -567,38 +566,48 @@ void AssertionEmitter::emit() {
// Main
-int main(int argc, const char *argv[]) {
+int main(int argc, const char* argv[]) {
Wasm2JSBuilder::Flags flags;
- OptimizationOptions options("wasm2js", "Transform .wasm/.wast files to asm.js");
+ OptimizationOptions options("wasm2js",
+ "Transform .wasm/.wast files to asm.js");
options
- .add("--output", "-o", "Output file (stdout if not specified)",
- Options::Arguments::One,
- [](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) {
- flags.allowAsserts = true;
- o->extra["asserts"] = "1";
- })
- .add("--pedantic", "", "Emulate WebAssembly trapping behavior",
- Options::Arguments::Zero,
- [&](Options* o, const std::string& argument) {
- flags.pedantic = true;
- })
- .add("--emscripten", "", "Emulate the glue in emscripten-compatible form (and not ES6 module form)",
- Options::Arguments::Zero,
- [&](Options* o, const std::string& argument) {
- flags.emscripten = true;
- })
- .add_positional("INFILE", Options::Arguments::One,
- [](Options *o, const std::string& argument) {
- o->extra["infile"] = argument;
- });
+ .add("--output",
+ "-o",
+ "Output file (stdout if not specified)",
+ Options::Arguments::One,
+ [](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) {
+ flags.allowAsserts = true;
+ o->extra["asserts"] = "1";
+ })
+ .add(
+ "--pedantic",
+ "",
+ "Emulate WebAssembly trapping behavior",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) { flags.pedantic = true; })
+ .add(
+ "--emscripten",
+ "",
+ "Emulate the glue in emscripten-compatible form (and not ES6 module "
+ "form)",
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& argument) { flags.emscripten = true; })
+ .add_positional("INFILE",
+ Options::Arguments::One,
+ [](Options* o, const std::string& argument) {
+ o->extra["infile"] = argument;
+ });
options.parse(argc, argv);
- if (options.debug) flags.debug = true;
+ if (options.debug)
+ flags.debug = true;
Element* root = nullptr;
Module wasm;
@@ -606,11 +615,11 @@ int main(int argc, const char *argv[]) {
std::unique_ptr<SExpressionParser> sexprParser;
std::unique_ptr<SExpressionWasmBuilder> sexprBuilder;
- auto &input = options.extra["infile"];
+ auto& input = options.extra["infile"];
std::string suffix(".wasm");
bool binaryInput =
- input.size() >= suffix.size() &&
- input.compare(input.size() - suffix.size(), suffix.size(), suffix) == 0;
+ input.size() >= suffix.size() &&
+ input.compare(input.size() - suffix.size(), suffix.size(), suffix) == 0;
try {
// If the input filename ends in `.wasm`, then parse it in binary form,
@@ -627,20 +636,25 @@ int main(int argc, const char *argv[]) {
reader.read(input, wasm, "");
options.applyFeatures(wasm);
} else {
- auto input(
- read_file<std::vector<char>>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release));
- if (options.debug) std::cerr << "s-parsing..." << std::endl;
+ auto input(read_file<std::vector<char>>(options.extra["infile"],
+ Flags::Text,
+ options.debug ? Flags::Debug
+ : Flags::Release));
+ if (options.debug)
+ std::cerr << "s-parsing..." << std::endl;
sexprParser = make_unique<SExpressionParser>(input.data());
root = sexprParser->root;
- if (options.debug) std::cerr << "w-parsing..." << std::endl;
+ if (options.debug)
+ std::cerr << "w-parsing..." << std::endl;
sexprBuilder = make_unique<SExpressionWasmBuilder>(wasm, *(*root)[0]);
}
} catch (ParseException& p) {
p.dump(std::cerr);
Fatal() << "error in parsing input";
} catch (std::bad_alloc&) {
- Fatal() << "error in building module, std::bad_alloc (possibly invalid request for silly amounts of memory)";
+ Fatal() << "error in building module, std::bad_alloc (possibly invalid "
+ "request for silly amounts of memory)";
}
if (options.passOptions.validate) {
@@ -650,13 +664,18 @@ int main(int argc, const char *argv[]) {
}
}
- if (options.debug) std::cerr << "j-printing..." << std::endl;
- Output output(options.extra["output"], Flags::Text, options.debug ? Flags::Debug : Flags::Release);
+ if (options.debug)
+ std::cerr << "j-printing..." << std::endl;
+ Output output(options.extra["output"],
+ Flags::Text,
+ options.debug ? Flags::Debug : Flags::Release);
if (!binaryInput && options.extra["asserts"] == "1") {
- AssertionEmitter(*root, *sexprBuilder, output, flags, options.passOptions).emit();
+ AssertionEmitter(*root, *sexprBuilder, output, flags, options.passOptions)
+ .emit();
} else {
emitWasm(wasm, output, flags, options.passOptions, "asmFunc");
}
- if (options.debug) std::cerr << "done." << std::endl;
+ if (options.debug)
+ std::cerr << "done." << std::endl;
}