summaryrefslogtreecommitdiff
path: root/src/wasm-emscripten.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm-emscripten.cpp')
-rw-r--r--src/wasm-emscripten.cpp208
1 files changed, 208 insertions, 0 deletions
diff --git a/src/wasm-emscripten.cpp b/src/wasm-emscripten.cpp
new file mode 100644
index 000000000..912ca6d61
--- /dev/null
+++ b/src/wasm-emscripten.cpp
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ */
+
+#include "wasm-emscripten.h"
+
+#include "asm_v_wasm.h"
+#include "asmjs/shared-constants.h"
+#include "shared-constants.h"
+#include "wasm-builder.h"
+#include "wasm-traversal.h"
+#include "wasm.h"
+
+namespace wasm {
+
+namespace emscripten {
+
+cashew::IString EMSCRIPTEN_ASM_CONST("emscripten_asm_const");
+
+void generateMemoryGrowthFunction(Module& wasm) {
+ Builder wasmBuilder(wasm);
+ Name name(GROW_WASM_MEMORY);
+ std::vector<NameType> params { { NEW_SIZE, i32 } };
+ Function* growFunction = wasmBuilder.makeFunction(
+ name, std::move(params), i32, {}
+ );
+ growFunction->body = wasmBuilder.makeHost(
+ GrowMemory,
+ Name(),
+ { wasmBuilder.makeGetLocal(0, i32) }
+ );
+
+ wasm.addFunction(growFunction);
+ auto export_ = new Export;
+ export_->name = export_->value = name;
+ export_->kind = Export::Function;
+ wasm.addExport(export_);
+}
+
+static bool hasI64ResultOrParam(FunctionType* ft) {
+ if (ft->result == i64) return true;
+ for (auto ty : ft->params) {
+ if (ty == i64) return true;
+ }
+ return false;
+}
+
+std::vector<Function*> makeDynCallThunks(Module& wasm, std::vector<Name> const& tableSegmentData) {
+ wasm.removeImport(EMSCRIPTEN_ASM_CONST); // we create _sig versions
+
+ std::vector<Function*> generatedFunctions;
+ std::unordered_set<std::string> sigs;
+ Builder wasmBuilder(wasm);
+ for (const auto& indirectFunc : tableSegmentData) {
+ std::string sig(getSig(wasm.getFunction(indirectFunc)));
+ auto* funcType = ensureFunctionType(sig, &wasm);
+ if (hasI64ResultOrParam(funcType)) continue; // Can't export i64s on the web.
+ if (!sigs.insert(sig).second) continue; // Sig is already in the set
+ std::vector<NameType> params;
+ params.emplace_back("fptr", i32); // function pointer param
+ int p = 0;
+ for (const auto& ty : funcType->params) params.emplace_back(std::to_string(p++), ty);
+ Function* f = wasmBuilder.makeFunction(std::string("dynCall_") + sig, std::move(params), funcType->result, {});
+ Expression* fptr = wasmBuilder.makeGetLocal(0, i32);
+ std::vector<Expression*> args;
+ for (unsigned i = 0; i < funcType->params.size(); ++i) {
+ args.push_back(wasmBuilder.makeGetLocal(i + 1, funcType->params[i]));
+ }
+ Expression* call = wasmBuilder.makeCallIndirect(funcType, fptr, args);
+ f->body = call;
+ wasm.addFunction(f);
+ generatedFunctions.push_back(f);
+ }
+ return generatedFunctions;
+}
+
+struct AsmConstWalker : public PostWalker<AsmConstWalker, Visitor<AsmConstWalker>> {
+ Module& wasm;
+ std::unordered_map<Address, Address> segmentsByAddress; // address => segment index
+
+ std::map<std::string, std::set<std::string>> sigsForCode;
+ std::map<std::string, Address> ids;
+ std::set<std::string> allSigs;
+
+ AsmConstWalker(Module& _wasm, std::unordered_map<Address, Address> _segmentsByAddress) :
+ wasm(_wasm), segmentsByAddress(_segmentsByAddress) { }
+
+ void visitCallImport(CallImport* curr);
+
+ std::string escape(const char *input);
+};
+
+void AsmConstWalker::visitCallImport(CallImport* curr) {
+ if (curr->target == EMSCRIPTEN_ASM_CONST) {
+ auto arg = curr->operands[0]->cast<Const>();
+ Address segmentIndex = segmentsByAddress[arg->value.geti32()];
+ std::string code = escape(&wasm.memory.segments[segmentIndex].data[0]);
+ int32_t id;
+ if (ids.count(code) == 0) {
+ id = ids.size();
+ ids[code] = id;
+ } else {
+ id = ids[code];
+ }
+ std::string sig = getSig(curr);
+ sigsForCode[code].insert(sig);
+ std::string fixedTarget = EMSCRIPTEN_ASM_CONST.str + std::string("_") + sig;
+ curr->target = cashew::IString(fixedTarget.c_str(), false);
+ arg->value = Literal(id);
+ // add import, if necessary
+ if (allSigs.count(sig) == 0) {
+ allSigs.insert(sig);
+ auto import = new Import;
+ import->name = import->base = curr->target;
+ import->module = ENV;
+ import->functionType = ensureFunctionType(getSig(curr), &wasm);
+ import->kind = Import::Function;
+ wasm.addImport(import);
+ }
+ }
+}
+
+std::string AsmConstWalker::escape(const char *input) {
+ std::string code = input;
+ // replace newlines quotes with escaped newlines
+ size_t curr = 0;
+ while ((curr = code.find("\\n", curr)) != std::string::npos) {
+ code = code.replace(curr, 2, "\\\\n");
+ curr += 3; // skip this one
+ }
+ // replace double quotes with escaped single quotes
+ curr = 0;
+ while ((curr = code.find('"', curr)) != std::string::npos) {
+ if (curr == 0 || code[curr-1] != '\\') {
+ code = code.replace(curr, 1, "\\" "\"");
+ curr += 2; // skip this one
+ } else { // already escaped, escape the slash as well
+ code = code.replace(curr, 1, "\\" "\\" "\"");
+ curr += 3; // skip this one
+ }
+ }
+ return code;
+}
+
+template<class C>
+void printSet(std::ostream& o, C& c) {
+ o << "[";
+ bool first = true;
+ for (auto& item : c) {
+ if (first) first = false;
+ else o << ",";
+ o << '"' << item << '"';
+ }
+ o << "]";
+}
+
+void generateEmscriptenMetadata(std::ostream& o,
+ Module& wasm,
+ std::unordered_map<Address, Address> segmentsByAddress,
+ Address staticBump,
+ std::vector<Name> const& initializerFunctions) {
+ o << ";; METADATA: { ";
+ // find asmConst calls, and emit their metadata
+ AsmConstWalker walker(wasm, segmentsByAddress);
+ walker.walkModule(&wasm);
+ // print
+ o << "\"asmConsts\": {";
+ bool first = true;
+ for (auto& pair : walker.sigsForCode) {
+ auto& code = pair.first;
+ auto& sigs = pair.second;
+ if (first) first = false;
+ else o << ",";
+ o << '"' << walker.ids[code] << "\": [\"" << code << "\", ";
+ printSet(o, sigs);
+ o << "]";
+ }
+ o << "}";
+ o << ",";
+ o << "\"staticBump\": " << staticBump << ", ";
+
+ o << "\"initializers\": [";
+ first = true;
+ for (const auto& func : initializerFunctions) {
+ if (first) first = false;
+ else o << ", ";
+ o << "\"" << func.c_str() << "\"";
+ }
+ o << "]";
+
+ o << " }\n";
+}
+
+} // namespace emscripten
+
+} // namespace wasm