summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/asm2wasm-main.cpp81
-rw-r--r--src/asm2wasm.h82
-rw-r--r--src/asm_v_wasm.h26
-rw-r--r--src/passes/RemoveImports.cpp2
-rw-r--r--src/s2wasm-main.cpp2
-rw-r--r--src/s2wasm.h86
-rw-r--r--src/support/command-line.cpp25
-rw-r--r--src/support/command-line.h4
-rw-r--r--src/support/file.cpp9
-rw-r--r--src/support/file.h9
-rw-r--r--src/wasm-as.cpp64
-rw-r--r--src/wasm-binary.h355
-rw-r--r--src/wasm-js.cpp4
-rw-r--r--src/wasm-s-parser.h13
-rw-r--r--src/wasm.h8
15 files changed, 464 insertions, 306 deletions
diff --git a/src/asm2wasm-main.cpp b/src/asm2wasm-main.cpp
index 6a784bd76..1fd1273c0 100644
--- a/src/asm2wasm-main.cpp
+++ b/src/asm2wasm-main.cpp
@@ -18,64 +18,71 @@
// asm2wasm console tool
//
+#include "support/colors.h"
+#include "support/command-line.h"
+#include "support/file.h"
+
#include "asm2wasm.h"
using namespace cashew;
using namespace wasm;
-namespace wasm {
-int debug = 0;
-}
-
-int main(int argc, char **argv) {
- debug = getenv("ASM2WASM_DEBUG") ? getenv("ASM2WASM_DEBUG")[0] - '0' : 0;
-
- char *infile = argv[1];
- char *mappedGlobals = argc < 3 ? nullptr : argv[2];
+int main(int argc, const char *argv[]) {
+ Options options("asm2wasm", "Translate asm.js files to .wast files");
+ 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("--mapped-globals", "-m", "Mapped globals", Options::Arguments::One,
+ [](Options *o, const std::string &argument) {
+ o->extra["mapped globals"] = argument;
+ })
+ .add_positional("INFILE", Options::Arguments::One,
+ [](Options *o, const std::string &argument) {
+ o->extra["infile"] = argument;
+ });
+ options.parse(argc, argv);
- if (debug) std::cerr << "loading '" << infile << "'...\n";
- FILE *f = fopen(argv[1], "r");
- assert(f);
- fseek(f, 0, SEEK_END);
- int size = ftell(f);
- char *input = new char[size+1];
- rewind(f);
- int num = fread(input, 1, size, f);
- // On Windows, ftell() gives the byte position (\r\n counts as two bytes), but when
- // reading, fread() returns the number of characters read (\r\n is read as one char \n, and counted as one),
- // so return value of fread can be less than size reported by ftell, and that is normal.
- assert((num > 0 || size == 0) && num <= size);
- fclose(f);
- input[num] = 0;
+ const auto &mg_it = options.extra.find("mapped globals");
+ const char *mappedGlobals =
+ mg_it == options.extra.end() ? nullptr : mg_it->second.c_str();
Asm2WasmPreProcessor pre;
- input = pre.process(input);
+ auto input(
+ read_file<std::vector<char>>(options.extra["infile"], options.debug));
+ char *start = pre.process(input.data());
- if (debug) std::cerr << "parsing...\n";
+ if (options.debug) std::cerr << "parsing..." << std::endl;
cashew::Parser<Ref, DotZeroValueBuilder> builder;
- Ref asmjs = builder.parseToplevel(input);
- if (debug) {
- std::cerr << "parsed:\n";
+ Ref asmjs = builder.parseToplevel(start);
+ if (options.debug) {
+ std::cerr << "parsed:" << std::endl;
asmjs->stringify(std::cerr, true);
- std::cerr << '\n';
+ std::cerr << std::endl;
}
- if (debug) std::cerr << "wasming...\n";
+ if (options.debug) std::cerr << "wasming..." << std::endl;
AllocatingModule wasm;
- wasm.memory.initial = wasm.memory.max = 16*1024*1024; // we would normally receive this from the compiler
- Asm2WasmBuilder asm2wasm(wasm, pre.memoryGrowth);
+ wasm.memory.initial = wasm.memory.max =
+ 16 * 1024 * 1024; // we would normally receive this from the compiler
+ Asm2WasmBuilder asm2wasm(wasm, pre.memoryGrowth, options.debug);
asm2wasm.processAsm(asmjs);
- if (debug) std::cerr << "optimizing...\n";
+ if (options.debug) std::cerr << "optimizing..." << std::endl;
asm2wasm.optimize();
- if (debug) std::cerr << "printing...\n";
- std::cout << wasm;
+ if (options.debug) std::cerr << "printing..." << std::endl;
+ Output output(options.extra["output"], options.debug);
+ output << wasm;
if (mappedGlobals) {
- if (debug) std::cerr << "serializing mapped globals...\n";
+ if (options.debug)
+ std::cerr << "serializing mapped globals..." << std::endl;
asm2wasm.serializeMappedGlobals(mappedGlobals);
}
- if (debug) std::cerr << "done.\n";
+ if (options.debug) std::cerr << "done." << std::endl;
}
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index 7992de8af..75862abc8 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -33,8 +33,6 @@ namespace wasm {
using namespace cashew;
-extern int debug; // wasm::debug is set in main(), typically from an env var
-
// Utilities
static void abort_on(std::string why, Ref element) {
@@ -151,6 +149,7 @@ class Asm2WasmBuilder {
std::map<CallIndirect*, IString> callIndirects; // track these, as we need to fix them after we know the functionTableStarts. this maps call => its function table
bool memoryGrowth;
+ int debug;
public:
std::map<IString, MappedGlobal> mappedGlobals;
@@ -244,33 +243,23 @@ private:
}
}
- FunctionType *getFunctionType(Ref parent, ExpressionList& operands) {
+ FunctionType* getFunctionType(Ref parent, ExpressionList& operands) {
// generate signature
WasmType result = !!parent ? detectWasmType(parent, nullptr) : none;
- std::string str = "FUNCSIG$";
- str += getSig(result);
- for (auto operand : operands) {
- str += getSig(operand->type);
- }
- IString sig(str.c_str(), false);
- if (wasm.functionTypesMap.find(sig) == wasm.functionTypesMap.end()) {
- // add new type
- auto type = allocator.alloc<FunctionType>();
- type->name = sig;
- type->result = result;
- for (auto operand : operands) {
- type->params.push_back(operand->type);
- }
- wasm.addFunctionType(type);
- }
- return wasm.functionTypesMap[sig];
+ return ensureFunctionType(getSig(result, operands), &wasm, allocator);
}
public:
- Asm2WasmBuilder(AllocatingModule& wasm, bool memoryGrowth) : wasm(wasm), allocator(wasm.allocator), nextGlobal(8), maxGlobal(1000), memoryGrowth(memoryGrowth) {}
+ Asm2WasmBuilder(AllocatingModule& wasm, bool memoryGrowth, int debug)
+ : wasm(wasm),
+ allocator(wasm.allocator),
+ nextGlobal(8),
+ maxGlobal(1000),
+ memoryGrowth(memoryGrowth),
+ debug(debug) {}
- void processAsm(Ref ast);
- void optimize();
+ void processAsm(Ref ast);
+ void optimize();
private:
AsmType detectAsmType(Ref ast, AsmData *data) {
@@ -416,34 +405,9 @@ private:
if (base == ABS) {
assert(operands && operands->size() == 1);
WasmType type = (*operands)[0]->type;
- if (type == i32) {
- static FunctionType* builtin = nullptr;
- if (!builtin) {
- builtin = new FunctionType();
- builtin->params.push_back(i32);
- builtin->result = i32;
- }
- return builtin;
- }
- if (type == f32) {
- static FunctionType* builtin = nullptr;
- if (!builtin) {
- builtin = new FunctionType();
- builtin->params.push_back(f32);
- builtin->result = f32;
- }
- return builtin;
- }
- if (type == f64) {
- static FunctionType* builtin = nullptr;
- if (!builtin) {
- builtin = new FunctionType();
- builtin->params.push_back(f64);
- builtin->result = f64;
- }
- return builtin;
- }
-
+ if (type == i32) return ensureFunctionType("ii", &wasm, allocator);
+ if (type == f32) return ensureFunctionType("ff", &wasm, allocator);
+ if (type == f64) return ensureFunctionType("dd", &wasm, allocator);
}
}
return nullptr;
@@ -687,10 +651,10 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
// special math builtins
FunctionType* builtin = getBuiltinFunctionType(import.module, import.base);
if (builtin) {
- import.type = *builtin;
+ import.type = builtin;
continue;
}
- import.type = importedFunctionTypes[name];
+ import.type = ensureFunctionType(getSig(&importedFunctionTypes[name]), &wasm, allocator);
} else if (import.module != ASM2WASM) { // special-case the special module
// never actually used
toErase.push_back(name);
@@ -901,10 +865,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
import->name = F64_REM;
import->module = ASM2WASM;
import->base = F64_REM;
- import->type.name = F64_REM;
- import->type.result = f64;
- import->type.params.push_back(f64);
- import->type.params.push_back(f64);
+ import->type = ensureFunctionType("ddd", &wasm, allocator);
wasm.addImport(import);
}
return call;
@@ -945,8 +906,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
import->name = DEBUGGER;
import->module = ASM2WASM;
import->base = DEBUGGER;
- import->type.name = DEBUGGER;
- import->type.result = none;
+ import->type = ensureFunctionType("v", &wasm, allocator);
wasm.addImport(import);
}
return call;
@@ -1052,9 +1012,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
import->name = F64_TO_INT;
import->module = ASM2WASM;
import->base = F64_TO_INT;
- import->type.name = F64_TO_INT;
- import->type.result = i32;
- import->type.params.push_back(f64);
+ import->type = ensureFunctionType("id", &wasm, allocator);
wasm.addImport(import);
}
return ret;
diff --git a/src/asm_v_wasm.h b/src/asm_v_wasm.h
index f0065bbf9..394c7a321 100644
--- a/src/asm_v_wasm.h
+++ b/src/asm_v_wasm.h
@@ -17,6 +17,7 @@
#ifndef wasm_asm_v_wasm_h
#define wasm_asm_v_wasm_h
+#include "mixed_arena.h"
#include "emscripten-optimizer/optimizer.h"
namespace wasm {
@@ -81,6 +82,15 @@ std::string getSig(CallBase *call) {
return ret;
}
+std::string getSig(WasmType result, ExpressionList& operands) {
+ std::string ret;
+ ret += getSig(result);
+ for (auto operand : operands) {
+ ret += getSig(operand->type);
+ }
+ return ret;
+}
+
WasmType sigToWasmType(char sig) {
switch (sig) {
case 'i': return i32;
@@ -101,6 +111,22 @@ FunctionType sigToFunctionType(std::string sig) {
return ret;
}
+FunctionType* ensureFunctionType(std::string sig, Module* wasm, MixedArena& allocator) {
+ cashew::IString name(("FUNCSIG$" + sig).c_str(), false);
+ if (wasm->functionTypesMap.find(name) != wasm->functionTypesMap.end()) {
+ return wasm->functionTypesMap[name];
+ }
+ // add new type
+ auto type = allocator.alloc<FunctionType>();
+ type->name = name;
+ type->result = sigToWasmType(sig[0]);
+ for (size_t i = 1; i < sig.size(); i++) {
+ type->params.push_back(sigToWasmType(sig[i]));
+ }
+ wasm->addFunctionType(type);
+ return type;
+}
+
} // namespace wasm
#endif // wasm_asm_v_wasm_h
diff --git a/src/passes/RemoveImports.cpp b/src/passes/RemoveImports.cpp
index 44b7fc1ed..0c4924a45 100644
--- a/src/passes/RemoveImports.cpp
+++ b/src/passes/RemoveImports.cpp
@@ -37,7 +37,7 @@ struct RemoveImports : public Pass {
}
void visitCallImport(CallImport *curr) override {
- WasmType type = importsMap[curr->target]->type.result;
+ WasmType type = importsMap[curr->target]->type->result;
if (type == none) {
replaceCurrent(allocator->alloc<Nop>());
} else {
diff --git a/src/s2wasm-main.cpp b/src/s2wasm-main.cpp
index 277cee365..d0dd60bbf 100644
--- a/src/s2wasm-main.cpp
+++ b/src/s2wasm-main.cpp
@@ -45,7 +45,7 @@ int main(int argc, const char *argv[]) {
});
options.parse(argc, argv);
- std::string input(read_file(options.extra["infile"], options.debug));
+ auto input(read_file<std::string>(options.extra["infile"], options.debug));
if (options.debug) std::cerr << "Parsing and wasming..." << std::endl;
AllocatingModule wasm;
diff --git a/src/s2wasm.h b/src/s2wasm.h
index b99a71159..c192effc2 100644
--- a/src/s2wasm.h
+++ b/src/s2wasm.h
@@ -437,8 +437,15 @@ private:
}
// parse body
func->body = allocator.alloc<Block>();
- std::vector<Block*> bstack;
- bstack.push_back(func->body->dyn_cast<Block>());
+ std::vector<Expression*> bstack;
+ auto addToBlock = [&bstack](Expression* curr) {
+ Expression* last = bstack.back();
+ if (last->is<Loop>()) {
+ last = last->cast<Loop>()->body;
+ }
+ last->cast<Block>()->list.push_back(curr);
+ };
+ bstack.push_back(func->body);
std::vector<Expression*> estack;
auto push = [&](Expression* curr) {
//std::cerr << "push " << curr << '\n';
@@ -491,7 +498,7 @@ private:
};
auto setOutput = [&](Expression* curr, Name assign) {
if (assign.isNull() || assign.str[1] == 'd') { // discard
- bstack.back()->list.push_back(curr);
+ addToBlock(curr);
} else if (assign.str[1] == 'p') { // push
push(curr);
} else { // set to a local
@@ -499,7 +506,7 @@ private:
set->name = assign;
set->value = curr;
set->type = curr->type;
- bstack.back()->list.push_back(set);
+ addToBlock(set);
}
};
auto makeBinary = [&](BinaryOp op, WasmType type) {
@@ -627,7 +634,7 @@ private:
auto import = allocator.alloc<Import>();
import->name = import->base = target;
import->module = ENV;
- import->type = sigToFunctionType(getSig(curr));
+ import->type = ensureFunctionType(getSig(curr), &wasm, allocator);
wasm.addImport(import);
}
}
@@ -774,10 +781,22 @@ private:
default: abort_on("type.?");
}
};
+ // labels
+ size_t nextLabel = 0;
+ auto getNextLabel = [&nextLabel]() {
+ return cashew::IString(("label$" + std::to_string(nextLabel++)).c_str(), false);
+ };
+ auto getBranchLabel = [&](uint32_t offset) {
+ assert(offset < bstack.size());
+ Expression* target = bstack[bstack.size() - 1 - offset];
+ if (target->is<Block>()) {
+ return target->cast<Block>()->name;
+ } else {
+ return target->cast<Loop>()->in;
+ }
+ };
// fixups
std::vector<Block*> loopBlocks; // we need to clear their names
- std::set<Name> seenLabels; // if we already used a label, we don't need it in a loop (there is a block above it, with that label)
- Name lastLabel; // A loop has an 'in' label which appears before it. There might also be a block in between it and the loop, so we have to remember the last label
// main loop
while (1) {
skipWhitespace();
@@ -792,38 +811,27 @@ private:
handleTyped(f64);
} else if (match("block")) {
auto curr = allocator.alloc<Block>();
- curr->name = getStr();
- bstack.back()->list.push_back(curr);
+ curr->name = getNextLabel();
+ addToBlock(curr);
bstack.push_back(curr);
- seenLabels.insert(curr->name);
+ } else if (match("end_block")) {
+ bstack.pop_back();
} else if (match(".LBB")) {
- s -= 4;
- lastLabel = getStrToColon();
- s++;
- skipWhitespace();
- // pop all blocks/loops that reach this target
- // pop all targets with this label
- while (!bstack.empty()) {
- auto curr = bstack.back();
- if (curr->name == lastLabel) {
- bstack.pop_back();
- continue;
- }
- break;
- }
+ s = strchr(s, '\n');
} else if (match("loop")) {
auto curr = allocator.alloc<Loop>();
- bstack.back()->list.push_back(curr);
- curr->in = lastLabel;
- Name out = getStr();
- if (seenLabels.count(out) == 0) {
- curr->out = out;
- }
+ addToBlock(curr);
+ curr->in = getNextLabel();
+ curr->out = getNextLabel();
auto block = allocator.alloc<Block>();
- block->name = out; // temporary, fake
+ block->name = curr->out; // temporary, fake - this way, on bstack we have the right label at the right offset for a br
curr->body = block;
loopBlocks.push_back(block);
bstack.push_back(block);
+ bstack.push_back(curr);
+ } else if (match("end_loop")) {
+ bstack.pop_back();
+ bstack.pop_back();
} else if (match("br")) {
auto curr = allocator.alloc<Break>();
if (*s == '_') {
@@ -831,8 +839,8 @@ private:
curr->condition = getInput();
skipComma();
}
- curr->name = getStr();
- bstack.back()->list.push_back(curr);
+ curr->name = getBranchLabel(getInt());
+ addToBlock(curr);
} else if (match("call")) {
makeCall(none);
} else if (match("copy_local")) {
@@ -853,18 +861,18 @@ private:
if (*s == '$') {
curr->value = getInput();
}
- bstack.back()->list.push_back(curr);
+ addToBlock(curr);
} else if (match("tableswitch")) {
auto curr = allocator.alloc<Switch>();
curr->value = getInput();
skipComma();
- curr->default_ = getCommaSeparated();
+ curr->default_ = getBranchLabel(getInt());
while (skipComma()) {
- curr->targets.push_back(getCommaSeparated());
+ curr->targets.push_back(getBranchLabel(getInt()));
}
- bstack.back()->list.push_back(curr);
+ addToBlock(curr);
} else if (match("unreachable")) {
- bstack.back()->list.push_back(allocator.alloc<Unreachable>());
+ addToBlock(allocator.alloc<Unreachable>());
} else if (match("memory_size")) {
makeHost(MemorySize);
} else if (match("grow_memory")) {
@@ -1120,7 +1128,7 @@ public:
auto import = parent->allocator.alloc<Import>();
import->name = import->base = curr->target;
import->module = ENV;
- import->type = sigToFunctionType(getSig(curr));
+ import->type = ensureFunctionType(getSig(curr), &parent->wasm, parent->allocator);
parent->wasm.addImport(import);
}
}
diff --git a/src/support/command-line.cpp b/src/support/command-line.cpp
index 659c055c9..c078ef45f 100644
--- a/src/support/command-line.cpp
+++ b/src/support/command-line.cpp
@@ -19,7 +19,7 @@
using namespace wasm;
Options::Options(const std::string &command, const std::string &description)
- : debug(false), positional(Arguments::Zero) {
+ : debug(0), positional(Arguments::Zero) {
add("--help", "-h", "Show this help message and exit", Arguments::Zero,
[this, command, description](Options *o, const std::string &) {
std::cerr << command;
@@ -39,8 +39,10 @@ Options::Options(const std::string &command, const std::string &description)
std::cerr << '\n';
exit(EXIT_SUCCESS);
});
- add("--debug", "-d", "Print debug information to stderr", Arguments::Zero,
- [](Options *o, const std::string &) { o->debug = true; });
+ add("--debug", "-d", "Print debug information to stderr", Arguments::Optional,
+ [](Options *o, const std::string &arguments) {
+ o->debug = arguments.size() ? std::stoi(arguments) : 1;
+ });
}
Options::~Options() {}
@@ -79,6 +81,7 @@ void Options::parse(int argc, const char *argv[]) {
<< "'\n";
exit(EXIT_FAILURE);
case Arguments::One:
+ case Arguments::Optional:
if (positionalsSeen) {
std::cerr << "Unexpected second positional argument '"
<< currentOption << "' for " << positionalName << '\n';
@@ -88,6 +91,7 @@ void Options::parse(int argc, const char *argv[]) {
case Arguments::N:
positionalAction(this, currentOption);
++positionalsSeen;
+ break;
}
continue;
}
@@ -110,8 +114,8 @@ void Options::parse(int argc, const char *argv[]) {
switch (option->arguments) {
case Arguments::Zero:
if (argument.size()) {
- std::cerr << "Unexpected argument '" << argument
- << "' for option '" << currentOption << "'\n";
+ std::cerr << "Unexpected argument '" << argument << "' for option '"
+ << currentOption << "'\n";
exit(EXIT_FAILURE);
}
break;
@@ -121,15 +125,22 @@ void Options::parse(int argc, const char *argv[]) {
<< currentOption << "'\n";
exit(EXIT_FAILURE);
}
- // Fallthrough.
+ // Fallthrough.
case Arguments::N:
if (!argument.size()) {
if (i + 1 == e) {
- std::cerr << "Couldn't find expected argument for '" << currentOption << "'\n";
+ std::cerr << "Couldn't find expected argument for '"
+ << currentOption << "'\n";
exit(EXIT_FAILURE);
}
argument = argv[++i];
}
+ break;
+ case Arguments::Optional:
+ if (!argument.size()) {
+ if (i + 1 != e) argument = argv[++i];
+ }
+ break;
}
option->action(this, argument);
++option->seen;
diff --git a/src/support/command-line.h b/src/support/command-line.h
index 14dbce57f..6e0af846e 100644
--- a/src/support/command-line.h
+++ b/src/support/command-line.h
@@ -35,9 +35,9 @@ namespace wasm {
class Options {
public:
typedef std::function<void(Options *, const std::string &)> Action;
- enum class Arguments { Zero, One, N };
+ enum class Arguments { Zero, One, N, Optional };
- bool debug;
+ int debug;
std::map<std::string, std::string> extra;
Options(const std::string &command, const std::string &description);
diff --git a/src/support/file.cpp b/src/support/file.cpp
index 470d07737..8813750d4 100644
--- a/src/support/file.cpp
+++ b/src/support/file.cpp
@@ -18,7 +18,8 @@
#include <cstdlib>
-std::string wasm::read_file(const std::string &filename, bool debug) {
+template <typename T>
+T wasm::read_file(const std::string &filename, bool debug) {
if (debug) std::cerr << "Loading '" << filename << "'..." << std::endl;
std::ifstream infile(filename);
if (!infile.is_open()) {
@@ -27,12 +28,16 @@ std::string wasm::read_file(const std::string &filename, bool debug) {
}
infile.seekg(0, std::ios::end);
size_t insize = infile.tellg();
- std::string input(insize + 1, '\0');
+ T input(insize + 1, '\0');
infile.seekg(0);
infile.read(&input[0], insize);
return input;
}
+// Explicit instantiations for the explicit specializations.
+template std::string wasm::read_file<>(const std::string &, bool);
+template std::vector<char> wasm::read_file<>(const std::string &, bool);
+
wasm::Output::Output(const std::string &filename, bool debug)
: outfile(), out([this, filename, debug]() {
std::streambuf *buffer;
diff --git a/src/support/file.h b/src/support/file.h
index e9c35f5f3..5e464d6c1 100644
--- a/src/support/file.h
+++ b/src/support/file.h
@@ -25,9 +25,14 @@
#include <iostream>
#include <string>
#include <utility>
+#include <vector>
namespace wasm {
-std::string read_file(const std::string &filename, bool debug);
+template <typename T>
+T read_file(const std::string &filename, bool debug);
+// Declare the valid explicit specializations.
+extern template std::string read_file<>(const std::string &, bool);
+extern template std::vector<char> read_file<>(const std::string &, bool);
class Output {
public:
@@ -48,4 +53,4 @@ class Output {
};
}
-#endif // wasm_support_file_h
+#endif // wasm_support_file_h
diff --git a/src/wasm-as.cpp b/src/wasm-as.cpp
new file mode 100644
index 000000000..4ebb26639
--- /dev/null
+++ b/src/wasm-as.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// wasm2asm console tool
+//
+
+#include "support/colors.h"
+#include "support/command-line.h"
+#include "support/file.h"
+#include "wasm-binary.h"
+#include "wasm-s-parser.h"
+
+using namespace cashew;
+using namespace wasm;
+
+int main(int argc, const char *argv[]) {
+ Options options("wasm-as", "Assemble a .wast (WebAssembly text format) into a .wasm (WebAssembly binary format)");
+ 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_positional("INFILE", Options::Arguments::One,
+ [](Options *o, const std::string &argument) {
+ o->extra["infile"] = argument;
+ });
+ options.parse(argc, argv);
+
+ auto input(read_file<std::string>(options.extra["infile"], options.debug));
+
+ if (options.debug) std::cerr << "s-parsing..." << std::endl;
+ SExpressionParser parser(const_cast<char*>(input.c_str()));
+ Element& root = *parser.root;
+
+ if (options.debug) std::cerr << "w-parsing..." << std::endl;
+ AllocatingModule wasm;
+ SExpressionWasmBuilder builder(wasm, *root[0], [&]() { abort(); });
+
+ if (options.debug) std::cerr << "binarification..." << std::endl;
+ BufferWithRandomAccess buffer;
+ WasmBinaryWriter writer(&wasm, buffer);
+ writer.write();
+
+ if (options.debug) std::cerr << "writing to output..." << std::endl;
+ Output output(options.extra["output"], options.debug);
+ buffer.writeTo(output);
+
+ if (options.debug) std::cerr << "Done." << std::endl;
+}
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index d923b9ecb..9accbfbc0 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -26,6 +26,7 @@
#include "wasm.h"
#include "shared-constants.h"
+#include "asm_v_wasm.h"
namespace wasm {
@@ -42,7 +43,7 @@ struct LEB128 {
if (temp) {
byte = byte | 128;
}
- out.push_back(byte);
+ out->push_back(byte);
} while (temp);
}
@@ -97,22 +98,45 @@ public:
return *this;
}
- void writeAt(size_t i, int16_t x) {
+ BufferWithRandomAccess& operator<<(uint8_t x) {
+ return *this << (int8_t)x;
+ }
+ BufferWithRandomAccess& operator<<(uint16_t x) {
+ return *this << (int16_t)x;
+ }
+ BufferWithRandomAccess& operator<<(uint32_t x) {
+ return *this << (int32_t)x;
+ }
+ BufferWithRandomAccess& operator<<(uint64_t x) {
+ return *this << (int64_t)x;
+ }
+
+ BufferWithRandomAccess& operator<<(float x) {
+ return *this << Literal(x).reinterpreti32();
+ }
+ BufferWithRandomAccess& operator<<(double x) {
+ return *this << Literal(x).reinterpreti64();
+ }
+
+ void writeAt(size_t i, uint16_t x) {
(*this)[i] = x & 0xff;
(*this)[i+1] = x >> 8;
}
- void writeAt(size_t i, int32_t x) {
+ void writeAt(size_t i, uint32_t x) {
(*this)[i] = x & 0xff; x >>= 8;
(*this)[i+1] = x & 0xff; x >>= 8;
(*this)[i+2] = x & 0xff; x >>= 8;
(*this)[i+3] = x & 0xff;
}
- friend ostream& operator<<(ostream& o, BufferWithRandomAccess& b) {
- for (auto c : b) o << c;
+ template <typename T>
+ void writeTo(T& o) {
+ for (auto c : *this) o << c;
}
};
+namespace BinaryConsts {
+
enum Section {
Memory = 0,
Signatures = 1,
@@ -140,7 +164,7 @@ enum ASTNodes {
I32RemS = 0x45,
I32RemU = 0x46,
I32And = 0x47,
- I32Ior = 0x48,
+ I32Or = 0x48,
I32Xor = 0x49,
I32Shl = 0x4a,
I32ShrU = 0x4b,
@@ -167,7 +191,7 @@ enum ASTNodes {
I64RemS = 0x60,
I64RemU = 0x61,
I64And = 0x62,
- I64Ior = 0x63,
+ I64Or = 0x63,
I64Xor = 0x64,
I64Shl = 0x65,
I64ShrU = 0x66,
@@ -286,6 +310,8 @@ enum ASTNodes {
Unreachable = 0x15
};
+} // namespace BinaryConsts
+
char binaryWasmType(WasmType type) {
switch (type) {
case none: return 0;
@@ -301,8 +327,21 @@ class WasmBinaryWriter : public WasmVisitor<void> {
Module* wasm;
BufferWithRandomAccess& o;
+ MixedArena allocator;
+
+ void prepare() {
+ // we need function types for all our functions
+ for (auto func : wasm->functions) {
+ func->type = ensureFunctionType(getSig(func), wasm, allocator)->name;
+ }
+ }
+
public:
- WasmBinaryWriter(Module* wasm, BufferWithRandomAccess& o) : wasm(wasm), o(o) {}
+ WasmBinaryWriter(Module* input, BufferWithRandomAccess& o) : o(o) {
+ wasm = allocator.alloc<Module>();
+ *wasm = *input; // simple shallow copy; we won't be modifying any internals, just adding some function types, so this is fine
+ prepare();
+ }
void write() {
writeMemory();
@@ -314,15 +353,15 @@ public:
finishUp();
}
- writeMemory() {
- o << Section::Memory << int8_t(log2(wasm.memory.initial)) <<
- int8_t(log2(wasm.memory.max)) <<
+ void writeMemory() {
+ o << BinaryConsts::Memory << int8_t(log2(wasm->memory.initial)) <<
+ int8_t(log2(wasm->memory.max)) <<
int8_t(1); // export memory
}
- writeSignatures() {
- o << Section::Signatures << LEB128(wasm.functionTypes.size());
- for (auto type : wasm.functionTypes) {
+ void writeSignatures() {
+ o << BinaryConsts::Signatures << LEB128(wasm->functionTypes.size());
+ for (auto type : wasm->functionTypes) {
o << int8_t(type->params.size());
o << binaryWasmType(type->result);
for (auto param : type->params) {
@@ -333,8 +372,8 @@ public:
int16_t getFunctionTypeIndex(Name type) {
// TODO: optimize
- for (size_t i = 0; i < wasm.functionTypes.size(); i++) {
- if (wasm.functionTypes[i].name == type) return i;
+ for (size_t i = 0; i < wasm->functionTypes.size(); i++) {
+ if (wasm->functionTypes[i]->name == type) return i;
}
abort();
}
@@ -378,26 +417,26 @@ public:
}
}
- writeFunctions() {
- size_t total = wasm.imports.size() + wasm.functions.size();
- o << Section::Functions << LEB128(total);
+ void writeFunctions() {
+ size_t total = wasm->imports.size() + wasm->functions.size();
+ o << BinaryConsts::Functions << LEB128(total);
for (size_t i = 0; i < total; i++) {
- Import* import = i < wasm.imports.size() ? wasm.imports[i] : nullptr;
- Function* function = i >= wasm.imports.size() ? wasm.functions[i - wasm.imports.size()] : nullptr;
+ Import* import = i < wasm->imports.size() ? wasm->imports[i] : nullptr;
+ Function* function = i >= wasm->imports.size() ? wasm->functions[i - wasm->imports.size()] : nullptr;
Name name, type;
if (import) {
name = import->name;
- type = import->type.name;
+ type = import->type->name;
} else {
name = function->name;
type = function->type;
}
o << getFunctionTypeIndex(type);
- o << int8_t(FunctionEntry::Named |
- (FunctionEntry::Import * !!import) |
- (FunctionEntry::Locals * (function && function->locals.size() > 0) |
- (FunctionEntry::Export) * (wasm.exportsMap[name].count(name) > 0)));
- emitString(Name.str);
+ o << int8_t(BinaryConsts::Named |
+ (BinaryConsts::Import * !!import) |
+ (BinaryConsts::Locals * (function && function->locals.size() > 0)) |
+ (BinaryConsts::Export * (wasm->exportsMap.count(name) > 0)));
+ emitString(name.str);
if (function && function->locals.size() > 0) {
mapLocals(function);
o << uint16_t(numLocalsByType[i32])
@@ -413,9 +452,9 @@ public:
}
}
- writeDataSegments() {
- o << Section::DataSegments << LEB128(wasm.memory.segments.size());
- for (auto& segment : wasm.memory.segments) {
+ void writeDataSegments() {
+ o << BinaryConsts::DataSegments << LEB128(wasm->memory.segments.size());
+ for (auto& segment : wasm->memory.segments) {
o << int32_t(segment.offset);
emitBuffer(segment.data, segment.size);
o << int32_t(segment.size);
@@ -425,24 +464,24 @@ public:
uint16_t getFunctionIndex(Name name) {
// TODO: optimize
- for (size_t i = 0; i < wasm.imports.size()) {
- if (wasm.imports[i]->name == name) return i;
+ for (size_t i = 0; i < wasm->imports.size(); i++) {
+ if (wasm->imports[i]->name == name) return i;
}
- for (size_t i = 0; i < wasm.functions.size()) {
- if (wasm.functions[i]->name == name) return wasm.imports.size() + i;
+ for (size_t i = 0; i < wasm->functions.size(); i++) {
+ if (wasm->functions[i]->name == name) return wasm->imports.size() + i;
}
abort();
}
- writeFunctionTable() {
- o << Section::FunctionTable << LEB128(wasm.table.names.size());
- for (auto name : wasm.table.names) {
+ void writeFunctionTable() {
+ o << BinaryConsts::FunctionTable << LEB128(wasm->table.names.size());
+ for (auto name : wasm->table.names) {
o << getFunctionIndex(name);
}
}
- writeEnd() {
- o << Section::End;
+ void writeEnd() {
+ o << BinaryConsts::End;
}
// helpers
@@ -481,7 +520,7 @@ public:
std::vector<Name> breakStack;
void visitBlock(Block *curr) {
- o << int8_t(ASTNodes::Block) << int8_t(curr->list.size());
+ o << int8_t(BinaryConsts::Block) << int8_t(curr->list.size());
breakStack.push_back(curr->name);
for (auto child : curr->list) {
visit(child);
@@ -489,14 +528,14 @@ public:
breakStack.pop_back();
}
void visitIf(If *curr) {
- o << int8_t(curr->ifFalse ? ASTNodes::IfElse : ASTNodes::If);
+ o << int8_t(curr->ifFalse ? BinaryConsts::IfElse : BinaryConsts::If);
visit(curr->condition);
visit(curr->ifTrue);
if (curr->ifFalse) visit(curr->ifFalse);
}
void visitLoop(Loop *curr) {
// TODO: optimize, as we usually have a block as our singleton child
- o << int8_t(ASTNodes::Loop) << int8_t(1);
+ o << int8_t(BinaryConsts::Loop) << int8_t(1);
breakStack.push_back(curr->out);
breakStack.push_back(curr->in);
visit(curr->body);
@@ -504,49 +543,59 @@ public:
breakStack.pop_back();
}
void visitBreak(Break *curr) {
- o << int8_t(ASTNodes::Br);
+ o << int8_t(curr->condition ? BinaryConsts::BrIf : BinaryConsts::Br);
for (int i = breakStack.size() - 1; i >= 0; i--) {
if (breakStack[i] == curr->name) {
o << int8_t(breakStack.size() - 1 - i);
return;
}
}
- abort();
+ if (curr->condition) visit(curr->condition);
}
void visitSwitch(Switch *curr) {
- o << int8_t(ASTNodes::TableSwitch) << int16_t(curr->cases.size())
- << int16_t(curr->targets.size());
- abort(); // WTF
+ o << int8_t(BinaryConsts::TableSwitch) << int16_t(curr->cases.size())
+ << int16_t(curr->targets.size());
+ std::map<Name, int16_t> mapping; // target name => index in cases
+ for (size_t i = 0; i < curr->cases.size(); i++) {
+ mapping[curr->cases[i].name] = i;
+ }
+ for (auto target : curr->targets) {
+ o << mapping[target];
+ }
+ visit(curr->value);
+ for (auto c : curr->cases) {
+ visit(c.body);
+ }
}
void visitCall(Call *curr) {
- o << int8_t(ASTNodes::CallFunction) << LEB128(getFunctionIndex(curr->target));
+ o << int8_t(BinaryConsts::CallFunction) << LEB128(getFunctionIndex(curr->target));
for (auto operand : curr->operands) {
visit(operand);
}
}
void visitCallImport(CallImport *curr) {
- o << int8_t(ASTNodes::CallFunction) << LEB128(getFunctionIndex(curr->target));
+ o << int8_t(BinaryConsts::CallFunction) << LEB128(getFunctionIndex(curr->target));
for (auto operand : curr->operands) {
visit(operand);
}
}
void visitCallIndirect(CallIndirect *curr) {
- o << int8_t(ASTNodes::CallFunction) << LEB128(getFunctionTypeIndex(curr->functionType));
+ o << int8_t(BinaryConsts::CallFunction) << LEB128(getFunctionTypeIndex(curr->fullType->name));
for (auto operand : curr->operands) {
visit(operand);
}
}
void visitGetLocal(GetLocal *curr) {
- o << int8_t(ASTNodes::GetLocal) << LEB128(mappedLocals[curr->name]);
+ o << int8_t(BinaryConsts::GetLocal) << LEB128(mappedLocals[curr->name]);
}
void visitSetLocal(SetLocal *curr) {
- o << int8_t(ASTNodes::SetLocal) << LEB128(mappedLocals[curr->name]);
+ o << int8_t(BinaryConsts::SetLocal) << LEB128(mappedLocals[curr->name]);
visit(curr->value);
}
- void emitMemoryAccess(size_t alignment, size_t bytes, uint32_t offset)
- o << int8_t( (alignment == bytes || alignment == 0) ? 0 : 128) |
- (offset ? 8 : 0) );
+ void emitMemoryAccess(size_t alignment, size_t bytes, uint32_t offset) {
+ o << int8_t( ((alignment == bytes || alignment == 0) ? 0 : 128) |
+ (offset ? 8 : 0) );
if (offset) o << LEB128(offset);
}
@@ -554,56 +603,56 @@ public:
switch (curr->type) {
case i32: {
switch (curr->bytes) {
- case 1: o << int8_t(curr->signed_ ? ASTNodes::I32LoadMem8S : ASTNodes::I32LoadMem8U); break;
- case 2: o << int8_t(curr->signed_ ? ASTNodes::I32LoadMem16S : ASTNodes::I32LoadMem16U); break;
- case 4: o << int8_t(ASTNodes::I32LoadMem); break;
+ case 1: o << int8_t(curr->signed_ ? BinaryConsts::I32LoadMem8S : BinaryConsts::I32LoadMem8U); break;
+ case 2: o << int8_t(curr->signed_ ? BinaryConsts::I32LoadMem16S : BinaryConsts::I32LoadMem16U); break;
+ case 4: o << int8_t(BinaryConsts::I32LoadMem); break;
default: abort();
}
break;
}
case i64: {
switch (curr->bytes) {
- case 1: o << int8_t(curr->signed_ ? ASTNodes::I64LoadMem8S : ASTNodes::I64LoadMem8U); break;
- case 2: o << int8_t(curr->signed_ ? ASTNodes::I64LoadMem16S : ASTNodes::I64LoadMem16U); break;
- case 4: o << int8_t(curr->signed_ ? ASTNodes::I64LoadMem32S : ASTNodes::I64LoadMem32U); break;
- case 8: o << int8_t(ASTNodes::I64LoadMem); break;
+ case 1: o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem8S : BinaryConsts::I64LoadMem8U); break;
+ case 2: o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem16S : BinaryConsts::I64LoadMem16U); break;
+ case 4: o << int8_t(curr->signed_ ? BinaryConsts::I64LoadMem32S : BinaryConsts::I64LoadMem32U); break;
+ case 8: o << int8_t(BinaryConsts::I64LoadMem); break;
default: abort();
}
break;
}
- case f32: o << int8_t(ASTNodes::F32LoadMem); break;
- case f64: o << int8_t(ASTNodes::F64LoadMem); break;
+ case f32: o << int8_t(BinaryConsts::F32LoadMem); break;
+ case f64: o << int8_t(BinaryConsts::F64LoadMem); break;
default: abort();
}
- emitMemoryAccess(curr->alignment, curr->bytes, curr->offset);
+ emitMemoryAccess(curr->align, curr->bytes, curr->offset);
visit(curr->ptr);
}
void visitStore(Store *curr) {
switch (curr->type) {
case i32: {
switch (curr->bytes) {
- case 1: o << int8_t(ASTNodes::I32StoreMem8); break;
- case 2: o << int8_t(ASTNodes::I32StoreMem16); break;
- case 4: o << int8_t(ASTNodes::I32StoreMem); break;
+ case 1: o << int8_t(BinaryConsts::I32StoreMem8); break;
+ case 2: o << int8_t(BinaryConsts::I32StoreMem16); break;
+ case 4: o << int8_t(BinaryConsts::I32StoreMem); break;
default: abort();
}
break;
}
case i64: {
switch (curr->bytes) {
- case 1: o << int8_t(ASTNodes::I64StoreMem8); break;
- case 2: o << int8_t(ASTNodes::I64StoreMem16); break;
- case 4: o << int8_t(ASTNodes::I64StoreMem32); break;
- case 8: o << int8_t(ASTNodes::I64StoreMem); break;
+ case 1: o << int8_t(BinaryConsts::I64StoreMem8); break;
+ case 2: o << int8_t(BinaryConsts::I64StoreMem16); break;
+ case 4: o << int8_t(BinaryConsts::I64StoreMem32); break;
+ case 8: o << int8_t(BinaryConsts::I64StoreMem); break;
default: abort();
}
break;
}
- case f32: o << int8_t(ASTNodes::F32StoreMem); break;
- case f64: o << int8_t(ASTNodes::F64StoreMem); break;
+ case f32: o << int8_t(BinaryConsts::F32StoreMem); break;
+ case f64: o << int8_t(BinaryConsts::F64StoreMem); break;
default: abort();
}
- emitMemoryAccess(curr->alignment, curr->bytes, curr->offset);
+ emitMemoryAccess(curr->align, curr->bytes, curr->offset);
visit(curr->ptr);
visit(curr->value);
}
@@ -612,54 +661,54 @@ public:
case i32: {
int32_t value = curr->value.i32;
if (value >= -128 && value <= 127) {
- o << int8_t(ASTNodes::I8Const) << int8_t(value);
+ o << int8_t(BinaryConsts::I8Const) << int8_t(value);
break;
}
- o << int8_t(ASTNodes::I32Const) << value;
+ o << int8_t(BinaryConsts::I32Const) << value;
break;
}
case i64: {
- o << int8_t(ASTNodes::I64Const) << curr->value.i64;
+ o << int8_t(BinaryConsts::I64Const) << curr->value.i64;
break;
}
case f32: {
- o << int8_t(ASTNodes::F32Const) << curr->value.f32;
+ o << int8_t(BinaryConsts::F32Const) << curr->value.f32;
break;
}
case f64: {
- o << int8_t(ASTNodes::F64Const) << curr->value.f64;
+ o << int8_t(BinaryConsts::F64Const) << curr->value.f64;
break;
}
default: abort();
}
}
void visitUnary(Unary *curr) {
- switch (op) {
- case Clz: o << int8_t(curr->type == i32 ? I32Clz : I64Clz); break;
- case Ctz: o << int8_t(curr->type == i32 ? I32Ctz : I64Ctz); break;
- case Popcnt: o << int8_t(curr->type == i32 ? I32Popcnt : I64Popcnt); break;
- case Neg: o << int8_t(curr->type == f32 ? F32Neg : F64Neg); break;
- case Abs: o << int8_t(curr->type == f32 ? F32Abs : F64Abs); break;
- case Ceil: o << int8_t(curr->type == f32 ? F32Ceil : F64Ceil); break;
- case Floor: o << int8_t(curr->type == f32 ? F32Floor : F64Floor); break;
- case Trunc: o << int8_t(curr->type == f32 ? F32Trunc : F64Trunc); break;;
- case Nearest: o << int8_t(curr->type == f32 ? F32NearestInt : F64NearestInt); break;
- case Sqrt: o << int8_t(curr->type == f32 ? F32Sqrt : F64Sqrt); break;
- case ExtendSInt32: o << "extend_s/i32"; break;
- case ExtendUInt32: o << "extend_u/i32"; break;
- case WrapInt64: o << "wrap/i64"; break;
- case TruncSFloat32: // XXX no signe/dunsigned versions of trunc?
- case TruncUFloat32: // XXX
- case TruncSFloat64: // XXX
- case TruncUFloat64: // XXX
- case ReinterpretFloat: // XXX missing
- case ConvertUInt32: o << int8_t(curr->type == f32 ? I32UConvertF32 : I32UConvertF64); break;
- case ConvertSInt32: o << int8_t(curr->type == f32 ? I32SConvertF32 : I32SConvertF64); break;
- case ConvertUInt64: o << int8_t(curr->type == f32 ? I64UConvertF32 : I64UConvertF64); break;
- case ConvertSInt64: o << int8_t(curr->type == f32 ? I64UConvertF32 : I64UConvertF64); break;
- case PromoteFloat32: // XXX
- case DemoteFloat64: // XXX
- case ReinterpretInt: // XXX
+ switch (curr->op) {
+ case Clz: o << int8_t(curr->type == i32 ? BinaryConsts::I32Clz : BinaryConsts::I64Clz); break;
+ case Ctz: o << int8_t(curr->type == i32 ? BinaryConsts::I32Ctz : BinaryConsts::I64Ctz); break;
+ case Popcnt: o << int8_t(curr->type == i32 ? BinaryConsts::I32Popcnt : BinaryConsts::I64Popcnt); break;
+ case Neg: o << int8_t(curr->type == f32 ? BinaryConsts::F32Neg : BinaryConsts::F64Neg); break;
+ case Abs: o << int8_t(curr->type == f32 ? BinaryConsts::F32Abs : BinaryConsts::F64Abs); break;
+ case Ceil: o << int8_t(curr->type == f32 ? BinaryConsts::F32Ceil : BinaryConsts::F64Ceil); break;
+ case Floor: o << int8_t(curr->type == f32 ? BinaryConsts::F32Floor : BinaryConsts::F64Floor); break;
+ case Trunc: o << int8_t(curr->type == f32 ? BinaryConsts::F32Trunc : BinaryConsts::F64Trunc); break;;
+ case Nearest: o << int8_t(curr->type == f32 ? BinaryConsts::F32NearestInt : BinaryConsts::F64NearestInt); break;
+ case Sqrt: o << int8_t(curr->type == f32 ? BinaryConsts::F32Sqrt : BinaryConsts::F64Sqrt); break;
+ case ExtendSInt32: abort(); break;
+ case ExtendUInt32: abort(); break;
+ case WrapInt64: abort(); break;
+ case TruncSFloat32: abort(); // XXX no signe/dunsigned versions of trunc?
+ case TruncUFloat32: abort(); // XXX
+ case TruncSFloat64: abort(); // XXX
+ case TruncUFloat64: abort(); // XXX
+ case ReinterpretFloat: abort(); // XXX missing
+ case ConvertUInt32: o << int8_t(curr->type == f32 ? BinaryConsts::I32UConvertF32 : BinaryConsts::I32UConvertF64); break;
+ case ConvertSInt32: o << int8_t(curr->type == f32 ? BinaryConsts::I32SConvertF32 : BinaryConsts::I32SConvertF64); break;
+ case ConvertUInt64: o << int8_t(curr->type == f32 ? BinaryConsts::I64UConvertF32 : BinaryConsts::I64UConvertF64); break;
+ case ConvertSInt64: o << int8_t(curr->type == f32 ? BinaryConsts::I64UConvertF32 : BinaryConsts::I64UConvertF64); break;
+ case PromoteFloat32: abort(); // XXX
+ case DemoteFloat64: abort(); // XXX
+ case ReinterpretInt: abort(); // XXX
default: abort();
}
visit(curr->value);
@@ -667,53 +716,70 @@ public:
void visitBinary(Binary *curr) {
#define TYPED_CODE(code) { \
switch (curr->left->type) { \
- case i32: o << int8_t(I32##code); break; \
- case i64: o << int8_t(I64##code); break; \
- case f32: o << int8_t(F32##code); break; \
- case f64: o << int8_t(F64##code); break; \
+ case i32: o << int8_t(BinaryConsts::I32##code); break; \
+ case i64: o << int8_t(BinaryConsts::I64##code); break; \
+ case f32: o << int8_t(BinaryConsts::F32##code); break; \
+ case f64: o << int8_t(BinaryConsts::F64##code); break; \
default: abort(); \
} \
+ break; \
+ }
+ #define INT_TYPED_CODE(code) { \
+ switch (curr->left->type) { \
+ case i32: o << int8_t(BinaryConsts::I32##code); break; \
+ case i64: o << int8_t(BinaryConsts::I64##code); break; \
+ default: abort(); \
+ } \
+ break; \
+ }
+ #define FLOAT_TYPED_CODE(code) { \
+ switch (curr->left->type) { \
+ case f32: o << int8_t(BinaryConsts::F32##code); break; \
+ case f64: o << int8_t(BinaryConsts::F64##code); break; \
+ default: abort(); \
+ } \
+ break; \
}
- switch (op) {
+ switch (curr->op) {
case Add: TYPED_CODE(Add);
case Sub: TYPED_CODE(Sub);
case Mul: TYPED_CODE(Mul);
- case DivS: int8_t(curr->type == i32 ? I32DivS : I64DivS); break;
- case DivU: int8_t(curr->type == i32 ? I32DivU : I64DivU); break;
- case RemS: int8_t(curr->type == i32 ? I32RemS : I64RemS); break;
- case RemU: int8_t(curr->type == i32 ? I32RemU : I64RemU); break;
- case And: int8_t(curr->type == i32 ? I32And : I64And); break;
- case Or: int8_t(curr->type == i32 ? I32Or : I64Or); break;
- case Xor: int8_t(curr->type == i32 ? I32Xor : I64Xor); break;
- case Shl: int8_t(curr->type == i32 ? I32Shl : I64Shl); break;
- case ShrU: int8_t(curr->type == i32 ? I32ShrU : I64ShrU); break;
- case ShrS: int8_t(curr->type == i32 ? I32ShrS : I64ShrS); break;
- case Div: int8_t(curr->type == f32 ? F32Div : F64Div); break;
- case CopySign: int8_t(curr->type == f32 ? F32CopySign : F64CopySign); break;
- case Min: int8_t(curr->type == f32 ? F32Min : F64Min); break;
- case Max: int8_t(curr->type == f32 ? F32Max : F64Max); break;
+ case DivS: o << int8_t(curr->type == i32 ? BinaryConsts::I32DivS : BinaryConsts::I64DivS); break;
+ case DivU: o << int8_t(curr->type == i32 ? BinaryConsts::I32DivU : BinaryConsts::I64DivU); break;
+ case RemS: o << int8_t(curr->type == i32 ? BinaryConsts::I32RemS : BinaryConsts::I64RemS); break;
+ case RemU: o << int8_t(curr->type == i32 ? BinaryConsts::I32RemU : BinaryConsts::I64RemU); break;
+ case And: o << int8_t(curr->type == i32 ? BinaryConsts::I32And : BinaryConsts::I64And); break;
+ case Or: o << int8_t(curr->type == i32 ? BinaryConsts::I32Or : BinaryConsts::I64Or); break;
+ case Xor: o << int8_t(curr->type == i32 ? BinaryConsts::I32Xor : BinaryConsts::I64Xor); break;
+ case Shl: o << int8_t(curr->type == i32 ? BinaryConsts::I32Shl : BinaryConsts::I64Shl); break;
+ case ShrU: o << int8_t(curr->type == i32 ? BinaryConsts::I32ShrU : BinaryConsts::I64ShrU); break;
+ case ShrS: o << int8_t(curr->type == i32 ? BinaryConsts::I32ShrS : BinaryConsts::I64ShrS); break;
+ case Div: o << int8_t(curr->type == f32 ? BinaryConsts::F32Div : BinaryConsts::F64Div); break;
+ case CopySign: o << int8_t(curr->type == f32 ? BinaryConsts::F32CopySign : BinaryConsts::F64CopySign); break;
+ case Min: o << int8_t(curr->type == f32 ? BinaryConsts::F32Min : BinaryConsts::F64Min); break;
+ case Max: o << int8_t(curr->type == f32 ? BinaryConsts::F32Max : BinaryConsts::F64Max); break;
case Eq: TYPED_CODE(Eq);
case Ne: TYPED_CODE(Ne);
- case LtS: TYPED_CODE(LtS);
- case LtU: TYPED_CODE(LtU);
- case LeS: TYPED_CODE(LeS);
- case LeU: TYPED_CODE(LeU);
- case GtS: TYPED_CODE(GtS);
- case GtU: TYPED_CODE(GtU);
- case GeS: TYPED_CODE(GeS);
- case GeU: TYPED_CODE(GeU);
- case Lt: TYPED_CODE(Lt);
- case Le: TYPED_CODE(Le);
- case Gt: TYPED_CODE(Gt);
- case Ge: TYPED_CODE(Ge);
+ case LtS: INT_TYPED_CODE(LtS);
+ case LtU: INT_TYPED_CODE(LtU);
+ case LeS: INT_TYPED_CODE(LeS);
+ case LeU: INT_TYPED_CODE(LeU);
+ case GtS: INT_TYPED_CODE(GtS);
+ case GtU: INT_TYPED_CODE(GtU);
+ case GeS: INT_TYPED_CODE(GeS);
+ case GeU: INT_TYPED_CODE(GeU);
+ case Lt: FLOAT_TYPED_CODE(Lt);
+ case Le: FLOAT_TYPED_CODE(Le);
+ case Gt: FLOAT_TYPED_CODE(Gt);
+ case Ge: FLOAT_TYPED_CODE(Ge);
default: abort();
}
visit(curr->left);
visit(curr->right);
}
void visitSelect(Select *curr) {
- o << int8_t(ASTNodes::Select);
+ o << int8_t(BinaryConsts::Select);
visit(curr->ifTrue);
visit(curr->ifFalse);
visit(curr->condition);
@@ -721,37 +787,38 @@ public:
void visitHost(Host *curr) {
switch (curr->op) {
case MemorySize: {
- o << int8_t(ASTNodes::MemorySize);
+ o << int8_t(BinaryConsts::MemorySize);
break;
}
case GrowMemory: {
- o << int8_t(ASTNodes::GrowMemory);
+ o << int8_t(BinaryConsts::GrowMemory);
visit(curr->operands[0]);
break;
}
default: abort();
}
- return o;
}
void visitNop(Nop *curr) {
- o << int8_t(ASTNodes::Nop);
+ o << int8_t(BinaryConsts::Nop);
}
void visitUnreachable(Unreachable *curr) {
- o << int8_t(ASTNodes::Unreachable);
+ o << int8_t(BinaryConsts::Unreachable);
}
};
+/*
class WasmBinaryBuilder {
AllocatingModule& wasm;
MixedArena& allocator;
istream& i;
public:
- WasmBinaryBuilder(AllocatingModule& wasm, istream& i) : wasm(wasm), allocator(wasm.allocator), i(i) {}
+ WasmBinaryBuilder(AllocatingModule& wasm, istream& i) : wasm(wasm), allocator(wasm->allocator), i(i) {}
void read() {
abort(); // TODO
}
};
+*/
} // namespace wasm
diff --git a/src/wasm-js.cpp b/src/wasm-js.cpp
index 760f00c46..b767b8a25 100644
--- a/src/wasm-js.cpp
+++ b/src/wasm-js.cpp
@@ -72,7 +72,7 @@ extern "C" void EMSCRIPTEN_KEEPALIVE load_asm2wasm(char *input) {
module->memory.max = pre.memoryGrowth ? -1 : module->memory.initial;
if (wasmJSDebug) std::cerr << "wasming...\n";
- asm2wasm = new Asm2WasmBuilder(*module, pre.memoryGrowth);
+ asm2wasm = new Asm2WasmBuilder(*module, pre.memoryGrowth, debug);
asm2wasm->processAsm(asmjs);
if (wasmJSDebug) std::cerr << "optimizing...\n";
@@ -190,7 +190,7 @@ extern "C" void EMSCRIPTEN_KEEPALIVE instantiate() {
if (wasmJSDebug) std::cout << "calling import returning " << ret << '\n';
- switch (import->type.result) {
+ switch (import->type->result) {
case none: return Literal(0);
case i32: return Literal((int32_t)ret);
case f32: return Literal((float)ret);
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index 719a6598b..1fb126ad0 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -28,6 +28,7 @@
#include "mixed_arena.h"
#include "shared-constants.h"
#include "parsing.h"
+#include "asm_v_wasm.h"
namespace wasm {
@@ -848,7 +849,7 @@ private:
auto ret = allocator.alloc<CallImport>();
ret->target = s[1]->str();
Import* import = wasm.importsMap[ret->target];
- ret->type = import->type.result;
+ ret->type = import->type->result;
parseCallOperands(s, 2, ret);
return ret;
}
@@ -996,28 +997,30 @@ private:
im->module = s[2]->str();
if (!s[3]->isStr()) onError();
im->base = s[3]->str();
+ FunctionType type;
if (s.size() > 4) {
Element& params = *s[4];
IString id = params[0]->str();
if (id == PARAM) {
for (size_t i = 1; i < params.size(); i++) {
- im->type.params.push_back(stringToWasmType(params[i]->str()));
+ type.params.push_back(stringToWasmType(params[i]->str()));
}
} else if (id == RESULT) {
- im->type.result = stringToWasmType(params[1]->str());
+ type.result = stringToWasmType(params[1]->str());
} else if (id == TYPE) {
IString name = params[1]->str();
assert(wasm.functionTypesMap.find(name) != wasm.functionTypesMap.end());
- im->type = *wasm.functionTypesMap[name];
+ type = *wasm.functionTypesMap[name];
} else {
onError();
}
if (s.size() > 5) {
Element& result = *s[5];
assert(result[0]->str() == RESULT);
- im->type.result = stringToWasmType(result[1]->str());
+ type.result = stringToWasmType(result[1]->str());
}
}
+ im->type = ensureFunctionType(getSig(&type), &wasm, allocator);
wasm.addImport(im);
}
diff --git a/src/wasm.h b/src/wasm.h
index c4654580a..306804c66 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -540,6 +540,8 @@ public:
WasmType result;
std::vector<WasmType> params;
+ FunctionType() : result(none) {}
+
std::ostream& print(std::ostream &o, unsigned indent, bool full=false) {
if (full) {
printOpening(o, "type") << ' ' << name << " (func";
@@ -938,13 +940,15 @@ public:
class Import {
public:
Name name, module, base; // name = module.base
- FunctionType type;
+ FunctionType* type;
+
+ Import() : type(nullptr) {}
std::ostream& print(std::ostream &o, unsigned indent) {
printOpening(o, "import ") << name << ' ';
printText(o, module.str) << ' ';
printText(o, base.str);
- type.print(o, indent);
+ if (type) type->print(o, indent);
return o << ')';
}
};