diff options
author | JF Bastien <jfb@chromium.org> | 2016-01-13 10:48:44 -0800 |
---|---|---|
committer | JF Bastien <jfb@chromium.org> | 2016-01-13 10:48:44 -0800 |
commit | 80d0b8eb31b1bb15ecd2245ef6ad57ca4d3a2575 (patch) | |
tree | d6dc07b081ed5c7e3a44bf1827c9b7d285d1a456 /src | |
parent | 2a0404579a2c061a965e2ac032801e250ab7f2bc (diff) | |
parent | 3da40ef949ddfac78df2db33e6b35e14f54cb78c (diff) | |
download | binaryen-80d0b8eb31b1bb15ecd2245ef6ad57ca4d3a2575.tar.gz binaryen-80d0b8eb31b1bb15ecd2245ef6ad57ca4d3a2575.tar.bz2 binaryen-80d0b8eb31b1bb15ecd2245ef6ad57ca4d3a2575.zip |
Merge branch 'master' of github.com:WebAssembly/binaryen
Diffstat (limited to 'src')
-rw-r--r-- | src/asm2wasm-main.cpp | 81 | ||||
-rw-r--r-- | src/asm2wasm.h | 82 | ||||
-rw-r--r-- | src/asm_v_wasm.h | 26 | ||||
-rw-r--r-- | src/passes/RemoveImports.cpp | 2 | ||||
-rw-r--r-- | src/s2wasm-main.cpp | 2 | ||||
-rw-r--r-- | src/s2wasm.h | 86 | ||||
-rw-r--r-- | src/support/command-line.cpp | 25 | ||||
-rw-r--r-- | src/support/command-line.h | 4 | ||||
-rw-r--r-- | src/support/file.cpp | 9 | ||||
-rw-r--r-- | src/support/file.h | 9 | ||||
-rw-r--r-- | src/wasm-as.cpp | 64 | ||||
-rw-r--r-- | src/wasm-binary.h | 355 | ||||
-rw-r--r-- | src/wasm-js.cpp | 4 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 13 | ||||
-rw-r--r-- | src/wasm.h | 8 |
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 << ')'; } }; |