summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt14
-rwxr-xr-xcheck.py2
-rw-r--r--src/asm_v_wasm.h106
-rw-r--r--src/asmjs/CMakeLists.txt4
-rw-r--r--src/asmjs/asm_v_wasm.cpp128
-rw-r--r--src/mixed_arena.h1
-rw-r--r--src/support/utilities.h4
-rw-r--r--src/wasm-linker.cpp277
-rw-r--r--src/wasm-linker.h255
9 files changed, 441 insertions, 350 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5440db9ab..9e6834c57 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -75,6 +75,9 @@ IF (UNIX AND
ADD_COMPILE_FLAG("-fcolor-diagnostics")
ENDIF()
+# Static libraries
+ADD_SUBDIRECTORY(src/asmjs)
+
# Sources.
SET(support_SOURCES
@@ -106,7 +109,7 @@ SET(binaryen-shell_SOURCES
)
ADD_EXECUTABLE(binaryen-shell
${binaryen-shell_SOURCES})
-TARGET_LINK_LIBRARIES(binaryen-shell support)
+TARGET_LINK_LIBRARIES(binaryen-shell asmjs support)
SET_PROPERTY(TARGET binaryen-shell PROPERTY CXX_STANDARD 11)
SET_PROPERTY(TARGET binaryen-shell PROPERTY CXX_STANDARD_REQUIRED ON)
INSTALL(TARGETS binaryen-shell DESTINATION bin)
@@ -129,7 +132,7 @@ SET(asm2wasm_SOURCES
)
ADD_EXECUTABLE(asm2wasm
${asm2wasm_SOURCES})
-TARGET_LINK_LIBRARIES(asm2wasm support)
+TARGET_LINK_LIBRARIES(asm2wasm asmjs support)
SET_PROPERTY(TARGET asm2wasm PROPERTY CXX_STANDARD 11)
SET_PROPERTY(TARGET asm2wasm PROPERTY CXX_STANDARD_REQUIRED ON)
INSTALL(TARGETS asm2wasm DESTINATION bin)
@@ -142,7 +145,7 @@ SET(wasm2asm_SOURCES
)
ADD_EXECUTABLE(wasm2asm
${wasm2asm_SOURCES})
-TARGET_LINK_LIBRARIES(wasm2asm support)
+TARGET_LINK_LIBRARIES(wasm2asm asmjs support)
SET_PROPERTY(TARGET wasm2asm PROPERTY CXX_STANDARD 11)
SET_PROPERTY(TARGET wasm2asm PROPERTY CXX_STANDARD_REQUIRED ON)
INSTALL(TARGETS wasm2asm DESTINATION bin)
@@ -150,11 +153,12 @@ INSTALL(TARGETS wasm2asm DESTINATION bin)
SET(s2wasm_SOURCES
src/pass.cpp
src/passes/Print.cpp
+ src/wasm-linker.cpp
src/s2wasm-main.cpp
)
ADD_EXECUTABLE(s2wasm
${s2wasm_SOURCES})
-TARGET_LINK_LIBRARIES(s2wasm support)
+TARGET_LINK_LIBRARIES(s2wasm asmjs support)
SET_PROPERTY(TARGET s2wasm PROPERTY CXX_STANDARD 11)
SET_PROPERTY(TARGET s2wasm PROPERTY CXX_STANDARD_REQUIRED ON)
INSTALL(TARGETS s2wasm DESTINATION bin)
@@ -164,7 +168,7 @@ SET(wasm_as_SOURCES
)
ADD_EXECUTABLE(wasm-as
${wasm_as_SOURCES})
-TARGET_LINK_LIBRARIES(wasm-as support)
+TARGET_LINK_LIBRARIES(wasm-as asmjs support)
SET_PROPERTY(TARGET wasm-as PROPERTY CXX_STANDARD 11)
SET_PROPERTY(TARGET wasm-as PROPERTY CXX_STANDARD_REQUIRED ON)
INSTALL(TARGETS wasm-as DESTINATION bin)
diff --git a/check.py b/check.py
index 634b68fbd..025fc60e9 100755
--- a/check.py
+++ b/check.py
@@ -573,7 +573,7 @@ cmd = [os.environ.get('CXX') or 'g++', '-std=c++11',
os.path.join('test', 'example', 'find_div0s.cpp'),
os.path.join('src', 'pass.cpp'),
os.path.join('src', 'passes', 'Print.cpp'),
- '-Isrc', '-g', '-lsupport', '-Llib/.', '-pthread']
+ '-Isrc', '-g', '-lasmjs', '-lsupport', '-Llib/.', '-pthread']
if os.environ.get('COMPILER_FLAGS'):
for f in os.environ.get('COMPILER_FLAGS').split(' '):
cmd.append(f)
diff --git a/src/asm_v_wasm.h b/src/asm_v_wasm.h
index 440c9ec89..27acf3dcc 100644
--- a/src/asm_v_wasm.h
+++ b/src/asm_v_wasm.h
@@ -19,113 +19,29 @@
#include "mixed_arena.h"
#include "emscripten-optimizer/optimizer.h"
+#include "wasm.h"
namespace wasm {
-WasmType asmToWasmType(AsmType asmType) {
- switch (asmType) {
- case ASM_INT: return WasmType::i32;
- case ASM_DOUBLE: return WasmType::f64;
- case ASM_FLOAT: return WasmType::f32;
- case ASM_NONE: return WasmType::none;
- default: {}
- }
- abort();
-}
+WasmType asmToWasmType(AsmType asmType);
-AsmType wasmToAsmType(WasmType type) {
- switch (type) {
- case WasmType::i32: return ASM_INT;
- case WasmType::f32: return ASM_FLOAT;
- case WasmType::f64: return ASM_DOUBLE;
- case WasmType::none: return ASM_NONE;
- default: {}
- }
- abort();
-}
+AsmType wasmToAsmType(WasmType type);
-char getSig(WasmType type) {
- switch (type) {
- case i32: return 'i';
- case i64: return 'j';
- case f32: return 'f';
- case f64: return 'd';
- case none: return 'v';
- default: abort();
- }
-}
+char getSig(WasmType type);
-std::string getSig(FunctionType *type) {
- std::string ret;
- ret += getSig(type->result);
- for (auto param : type->params) {
- ret += getSig(param);
- }
- return ret;
-}
+std::string getSig(FunctionType *type);
-std::string getSig(Function *func) {
- std::string ret;
- ret += getSig(func->result);
- for (auto type : func->params) {
- ret += getSig(type);
- }
- return ret;
-}
+std::string getSig(Function *func);
-std::string getSig(CallBase *call) {
- std::string ret;
- ret += getSig(call->type);
- for (auto operand : call->operands) {
- ret += getSig(operand->type);
- }
- return ret;
-}
+std::string getSig(CallBase *call);
-std::string getSig(WasmType result, const ExpressionList& operands) {
- std::string ret;
- ret += getSig(result);
- for (auto operand : operands) {
- ret += getSig(operand->type);
- }
- return ret;
-}
+std::string getSig(WasmType result, const ExpressionList& operands);
-WasmType sigToWasmType(char sig) {
- switch (sig) {
- case 'i': return i32;
- case 'j': return i64;
- case 'f': return f32;
- case 'd': return f64;
- case 'v': return none;
- default: abort();
- }
-}
+WasmType sigToWasmType(char sig);
-FunctionType sigToFunctionType(std::string sig) {
- FunctionType ret;
- ret.result = sigToWasmType(sig[0]);
- for (size_t i = 1; i < sig.size(); i++) {
- ret.params.push_back(sigToWasmType(sig[i]));
- }
- return ret;
-}
+FunctionType sigToFunctionType(std::string sig);
-FunctionType* ensureFunctionType(std::string sig, Module* wasm, MixedArena& allocator) {
- cashew::IString name(("FUNCSIG$" + sig).c_str(), false);
- if (wasm->checkFunctionType(name)) {
- return wasm->getFunctionType(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;
-}
+FunctionType* ensureFunctionType(std::string sig, Module* wasm, MixedArena& allocator);
} // namespace wasm
diff --git a/src/asmjs/CMakeLists.txt b/src/asmjs/CMakeLists.txt
new file mode 100644
index 000000000..8be8ea2c4
--- /dev/null
+++ b/src/asmjs/CMakeLists.txt
@@ -0,0 +1,4 @@
+SET(asmjs_SOURCES
+ asm_v_wasm.cpp
+)
+ADD_LIBRARY(asmjs STATIC ${asmjs_SOURCES}) \ No newline at end of file
diff --git a/src/asmjs/asm_v_wasm.cpp b/src/asmjs/asm_v_wasm.cpp
new file mode 100644
index 000000000..317a8b625
--- /dev/null
+++ b/src/asmjs/asm_v_wasm.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2015 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 "asm_v_wasm.h"
+#include "wasm.h"
+
+
+namespace wasm {
+
+WasmType asmToWasmType(AsmType asmType) {
+ switch (asmType) {
+ case ASM_INT: return WasmType::i32;
+ case ASM_DOUBLE: return WasmType::f64;
+ case ASM_FLOAT: return WasmType::f32;
+ case ASM_NONE: return WasmType::none;
+ default: {}
+ }
+ abort();
+}
+
+AsmType wasmToAsmType(WasmType type) {
+ switch (type) {
+ case WasmType::i32: return ASM_INT;
+ case WasmType::f32: return ASM_FLOAT;
+ case WasmType::f64: return ASM_DOUBLE;
+ case WasmType::none: return ASM_NONE;
+ default: {}
+ }
+ abort();
+}
+
+char getSig(WasmType type) {
+ switch (type) {
+ case i32: return 'i';
+ case i64: return 'j';
+ case f32: return 'f';
+ case f64: return 'd';
+ case none: return 'v';
+ default: abort();
+ }
+}
+
+std::string getSig(FunctionType *type) {
+ std::string ret;
+ ret += getSig(type->result);
+ for (auto param : type->params) {
+ ret += getSig(param);
+ }
+ return ret;
+}
+
+std::string getSig(Function *func) {
+ std::string ret;
+ ret += getSig(func->result);
+ for (auto type : func->params) {
+ ret += getSig(type);
+ }
+ return ret;
+}
+
+std::string getSig(CallBase *call) {
+ std::string ret;
+ ret += getSig(call->type);
+ for (auto operand : call->operands) {
+ ret += getSig(operand->type);
+ }
+ return ret;
+}
+
+std::string getSig(WasmType result, const 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;
+ case 'j': return i64;
+ case 'f': return f32;
+ case 'd': return f64;
+ case 'v': return none;
+ default: abort();
+ }
+}
+
+FunctionType sigToFunctionType(std::string sig) {
+ FunctionType ret;
+ ret.result = sigToWasmType(sig[0]);
+ for (size_t i = 1; i < sig.size(); i++) {
+ ret.params.push_back(sigToWasmType(sig[i]));
+ }
+ return ret;
+}
+
+FunctionType* ensureFunctionType(std::string sig, Module* wasm, MixedArena& allocator) {
+ cashew::IString name(("FUNCSIG$" + sig).c_str(), false);
+ if (wasm->checkFunctionType(name)) {
+ return wasm->getFunctionType(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
diff --git a/src/mixed_arena.h b/src/mixed_arena.h
index dc8cbad5b..d03612df5 100644
--- a/src/mixed_arena.h
+++ b/src/mixed_arena.h
@@ -17,6 +17,7 @@
#ifndef wasm_mixed_arena_h
#define wasm_mixed_arena_h
+#include <cassert>
#include <vector>
#include "support/threads.h"
diff --git a/src/support/utilities.h b/src/support/utilities.h
index 3ff1f9c5e..92e7c226f 100644
--- a/src/support/utilities.h
+++ b/src/support/utilities.h
@@ -17,7 +17,11 @@
#ifndef wasm_support_utilities_h
#define wasm_support_utilities_h
+#include <cassert>
+#include <cstdint>
#include <cstring>
+#include <memory>
+#include <iostream>
#include <type_traits>
namespace wasm {
diff --git a/src/wasm-linker.cpp b/src/wasm-linker.cpp
new file mode 100644
index 000000000..90ef4c2b9
--- /dev/null
+++ b/src/wasm-linker.cpp
@@ -0,0 +1,277 @@
+/*
+ * 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-linker.h"
+#include "asm_v_wasm.h"
+#include "support/utilities.h"
+#include "wasm-builder.h"
+#include "wasm-printing.h"
+
+using namespace wasm;
+
+cashew::IString EMSCRIPTEN_ASM_CONST("emscripten_asm_const");
+namespace wasm {
+// These are defined (not just declared) in shared-constants.h, so we can't just
+// include that header. TODO: Move the definitions into a cpp file.
+extern cashew::IString ENV;
+extern cashew::IString MEMORY;
+}
+
+
+void Linker::placeStackPointer(size_t stackAllocation) {
+ // ensure this is the first allocation
+ assert(nextStatic == globalBase || nextStatic == 1);
+ const size_t pointerSize = 4;
+ // Unconditionally allocate space for the stack pointer. Emscripten
+ // allocates the stack itself, and initializes the stack pointer itself.
+ size_t address = allocateStatic(pointerSize, pointerSize, "__stack_pointer");
+ if (stackAllocation) {
+ // If we are allocating the stack, set up a relocation to initialize the
+ // stack pointer to point to one past-the-end of the stack allocation.
+ auto* raw = new uint32_t;
+ relocations.emplace_back(
+ make_unique<Relocation>(raw, ".stack", stackAllocation));
+ assert(wasm.memory.segments.size() == 0);
+ addressSegments[address] = wasm.memory.segments.size();
+ wasm.memory.segments.emplace_back(
+ address, reinterpret_cast<char*>(raw), pointerSize);
+ }
+}
+
+void Linker::layout() {
+ // Place the stack after the user's static data, to keep those addresses
+ // small.
+ if (stackAllocation) allocateStatic(stackAllocation, 16, ".stack");
+
+ // The minimum initial memory size is the amount of static variables we have
+ // allocated. Round it up to a page, and update the page-increment versions
+ // of initial and max
+ size_t initialMem = roundUpToPageSize(nextStatic);
+ if (userInitialMemory) {
+ if (initialMem > userInitialMemory) {
+ Fatal() << "Specified initial memory size " << userInitialMemory <<
+ " is smaller than required size " << initialMem;
+ }
+ wasm.memory.initial = userInitialMemory / Memory::kPageSize;
+ } else {
+ wasm.memory.initial = initialMem / Memory::kPageSize;
+ }
+
+ if (userMaxMemory) wasm.memory.max = userMaxMemory / Memory::kPageSize;
+ wasm.memory.exportName = MEMORY;
+
+ // XXX For now, export all functions marked .globl.
+ for (Name name : globls) exportFunction(name, false);
+ for (Name name : initializerFunctions) exportFunction(name, true);
+
+ auto ensureFunctionIndex = [this](Name name) {
+ if (functionIndexes.count(name) == 0) {
+ functionIndexes[name] = wasm.table.names.size();
+ wasm.table.names.push_back(name);
+ if (debug) {
+ std::cerr << "function index: " << name << ": "
+ << functionIndexes[name] << '\n';
+ }
+ }
+ };
+ for (auto& relocation : relocations) {
+ Name name = relocation->value;
+ if (debug) std::cerr << "fix relocation " << name << '\n';
+ const auto& symbolAddress = staticAddresses.find(name);
+ if (symbolAddress != staticAddresses.end()) {
+ *(relocation->data) = symbolAddress->second + relocation->offset;
+ if (debug) std::cerr << " ==> " << *(relocation->data) << '\n';
+ } else {
+ // must be a function address
+ auto aliased = aliasedFunctions.find(name);
+ if (aliased != aliasedFunctions.end()) name = aliased->second;
+ if (!wasm.checkFunction(name)) {
+ std::cerr << "Unknown symbol: " << name << '\n';
+ if (!ignoreUnknownSymbols) abort();
+ *(relocation->data) = 0;
+ } else {
+ ensureFunctionIndex(name);
+ *(relocation->data) = functionIndexes[name] + relocation->offset;
+ }
+ }
+ }
+ if (!!startFunction) {
+ if (implementedFunctions.count(startFunction) == 0) {
+ std::cerr << "Unknown start function: `" << startFunction << "`\n";
+ abort();
+ }
+ const auto *target = wasm.getFunction(startFunction);
+ Name start("_start");
+ if (implementedFunctions.count(start) != 0) {
+ std::cerr << "Start function already present: `" << start << "`\n";
+ abort();
+ }
+ auto* func = wasm.allocator.alloc<Function>();
+ func->name = start;
+ wasm.addFunction(func);
+ exportFunction(start, true);
+ wasm.addStart(start);
+ auto* block = wasm.allocator.alloc<Block>();
+ func->body = block;
+ {
+ // Create the call, matching its parameters.
+ // TODO allow calling with non-default values.
+ auto* call = wasm.allocator.alloc<Call>();
+ call->target = startFunction;
+ size_t paramNum = 0;
+ for (WasmType type : target->params) {
+ Name name = Name::fromInt(paramNum++);
+ Builder::addVar(func, name, type);
+ auto* param = wasm.allocator.alloc<GetLocal>();
+ param->index = func->getLocalIndex(name);
+ param->type = type;
+ call->operands.push_back(param);
+ }
+ block->list.push_back(call);
+ block->finalize();
+ }
+ }
+
+ // ensure an explicit function type for indirect call targets
+ for (auto& name : wasm.table.names) {
+ auto* func = wasm.getFunction(name);
+ func->type = ensureFunctionType(getSig(func), &wasm, wasm.allocator)->name;
+ }
+}
+
+void Linker::emscriptenGlue(std::ostream& o) {
+ if (debug) {
+ WasmPrinter::printModule(&wasm, std::cerr);
+ }
+
+ wasm.removeImport(EMSCRIPTEN_ASM_CONST); // we create _sig versions
+
+ makeDynCallThunks();
+
+ o << ";; METADATA: { ";
+ // find asmConst calls, and emit their metadata
+ struct AsmConstWalker : public PostWalker<AsmConstWalker, Visitor<AsmConstWalker>> {
+ Linker* parent;
+
+ std::map<std::string, std::set<std::string>> sigsForCode;
+ std::map<std::string, size_t> ids;
+ std::set<std::string> allSigs;
+
+ void visitCallImport(CallImport* curr) {
+ if (curr->target == EMSCRIPTEN_ASM_CONST) {
+ auto arg = curr->operands[0]->cast<Const>();
+ size_t segmentIndex = parent->addressSegments[arg->value.geti32()];
+ std::string code = escape(parent->wasm.memory.segments[segmentIndex].data);
+ 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 = parent->wasm.allocator.alloc<Import>();
+ import->name = import->base = curr->target;
+ import->module = ENV;
+ import->type = ensureFunctionType(getSig(curr), &parent->wasm, parent->wasm.allocator);
+ parent->wasm.addImport(import);
+ }
+ }
+ }
+
+ std::string 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;
+ }
+ };
+ AsmConstWalker walker;
+ walker.parent = this;
+ walker.startWalk(&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\": " << (nextStatic - globalBase) << ", ";
+
+ o << "\"initializers\": [";
+ first = true;
+ for (const auto& func : initializerFunctions) {
+ if (first) first = false;
+ else o << ", ";
+ o << "\"" << func.c_str() << "\"";
+ }
+ o << "]";
+
+ o << " }";
+}
+
+void Linker::makeDynCallThunks() {
+ std::unordered_set<std::string> sigs;
+ wasm::Builder wasmBuilder(wasm);
+ for (const auto& indirectFunc : wasm.table.names) {
+ std::string sig(getSig(wasm.getFunction(indirectFunc)));
+ auto* funcType = ensureFunctionType(sig, &wasm, wasm.allocator);
+ 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, std::move(args));
+ f->body = funcType->result == none ? call : wasmBuilder.makeReturn(call);
+ wasm.addFunction(f);
+ exportFunction(f->name, true);
+ }
+}
diff --git a/src/wasm-linker.h b/src/wasm-linker.h
index ceb948c4e..9929a6f70 100644
--- a/src/wasm-linker.h
+++ b/src/wasm-linker.h
@@ -21,19 +21,14 @@
// is no merging of multiple modules). Currently this is only inteded to turn
// a .s file produced by LLVM into a usable wast file.
-
#ifndef WASM_WASM_LINK_H
#define WASM_WASM_LINK_H
#include "support/utilities.h"
#include "wasm.h"
-#include "wasm-printing.h"
namespace wasm {
-cashew::IString EMSCRIPTEN_ASM_CONST("emscripten_asm_const");
-
-
// Wasm module linking/layout information
class Linker {
public:
@@ -90,6 +85,9 @@ class Linker {
return address;
}
+ // Allocate space for a stack pointer and (if stackAllocation > 0) set up a
+ // relocation for it to point to the top of the stack.
+ void placeStackPointer(size_t stackAllocation);
void addGlobal(Name name) {
globls.push_back(name);
}
@@ -131,232 +129,13 @@ class Linker {
// Allocate the user stack, set up the initial memory size of the module, lay
// out the linear memory, process the relocations, and set up the indirect
// function table.
- void layout() {
- // Place the stack after the user's static data, to keep those addresses
- // small.
- if (stackAllocation) allocateStatic(stackAllocation, 16, ".stack");
-
- // The minimum initial memory size is the amount of static variables we have
- // allocated. Round it up to a page, and update the page-increment versions
- // of initial and max
- size_t initialMem = roundUpToPageSize(nextStatic);
- if (userInitialMemory) {
- if (initialMem > userInitialMemory) {
- Fatal() << "Specified initial memory size " << userInitialMemory <<
- " is smaller than required size " << initialMem;
- }
- wasm.memory.initial = userInitialMemory / Memory::kPageSize;
- } else {
- wasm.memory.initial = initialMem / Memory::kPageSize;
- }
-
- if (userMaxMemory) wasm.memory.max = userMaxMemory / Memory::kPageSize;
- wasm.memory.exportName = MEMORY;
-
- // XXX For now, export all functions marked .globl.
- for (Name name : globls) exportFunction(name, false);
- for (Name name : initializerFunctions) exportFunction(name, true);
-
- auto ensureFunctionIndex = [this](Name name) {
- if (functionIndexes.count(name) == 0) {
- functionIndexes[name] = wasm.table.names.size();
- wasm.table.names.push_back(name);
- if (debug) {
- std::cerr << "function index: " << name << ": "
- << functionIndexes[name] << '\n';
- }
- }
- };
- for (auto& relocation : relocations) {
- Name name = relocation->value;
- if (debug) std::cerr << "fix relocation " << name << '\n';
- const auto& symbolAddress = staticAddresses.find(name);
- if (symbolAddress != staticAddresses.end()) {
- *(relocation->data) = symbolAddress->second + relocation->offset;
- if (debug) std::cerr << " ==> " << *(relocation->data) << '\n';
- } else {
- // must be a function address
- auto aliased = aliasedFunctions.find(name);
- if (aliased != aliasedFunctions.end()) name = aliased->second;
- if (!wasm.checkFunction(name)) {
- std::cerr << "Unknown symbol: " << name << '\n';
- if (!ignoreUnknownSymbols) abort();
- *(relocation->data) = 0;
- } else {
- ensureFunctionIndex(name);
- *(relocation->data) = functionIndexes[name] + relocation->offset;
- }
- }
- }
- if (!!startFunction) {
- if (implementedFunctions.count(startFunction) == 0) {
- std::cerr << "Unknown start function: `" << startFunction << "`\n";
- abort();
- }
- const auto *target = wasm.getFunction(startFunction);
- Name start("_start");
- if (implementedFunctions.count(start) != 0) {
- std::cerr << "Start function already present: `" << start << "`\n";
- abort();
- }
- auto* func = wasm.allocator.alloc<Function>();
- func->name = start;
- wasm.addFunction(func);
- exportFunction(start, true);
- wasm.addStart(start);
- auto* block = wasm.allocator.alloc<Block>();
- func->body = block;
- {
- // Create the call, matching its parameters.
- // TODO allow calling with non-default values.
- auto* call = wasm.allocator.alloc<Call>();
- call->target = startFunction;
- size_t paramNum = 0;
- for (WasmType type : target->params) {
- Name name = Name::fromInt(paramNum++);
- Builder::addVar(func, name, type);
- auto* param = wasm.allocator.alloc<GetLocal>();
- param->index = func->getLocalIndex(name);
- param->type = type;
- call->operands.push_back(param);
- }
- block->list.push_back(call);
- block->finalize();
- }
- }
-
- // ensure an explicit function type for indirect call targets
- for (auto& name : wasm.table.names) {
- auto* func = wasm.getFunction(name);
- func->type = ensureFunctionType(getSig(func), &wasm, wasm.allocator)->name;
- }
- }
+ void layout();
// Support for emscripten integration: generates dyncall thunks, emits
// metadata for asmConsts, staticBump and initializer functions.
- void emscriptenGlue(std::ostream& o) {
- if (debug) {
- WasmPrinter::printModule(&wasm, std::cerr);
- }
-
- wasm.removeImport(EMSCRIPTEN_ASM_CONST); // we create _sig versions
-
- makeDynCallThunks();
-
- o << ";; METADATA: { ";
- // find asmConst calls, and emit their metadata
- struct AsmConstWalker : public PostWalker<AsmConstWalker, Visitor<AsmConstWalker>> {
- Linker* parent;
-
- std::map<std::string, std::set<std::string>> sigsForCode;
- std::map<std::string, size_t> ids;
- std::set<std::string> allSigs;
-
- void visitCallImport(CallImport* curr) {
- if (curr->target == EMSCRIPTEN_ASM_CONST) {
- auto arg = curr->operands[0]->cast<Const>();
- size_t segmentIndex = parent->addressSegments[arg->value.geti32()];
- std::string code = escape(parent->wasm.memory.segments[segmentIndex].data);
- 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 = parent->wasm.allocator.alloc<Import>();
- import->name = import->base = curr->target;
- import->module = ENV;
- import->type = ensureFunctionType(getSig(curr), &parent->wasm, parent->wasm.allocator);
- parent->wasm.addImport(import);
- }
- }
- }
-
- std::string 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;
- }
- };
- AsmConstWalker walker;
- walker.parent = this;
- walker.startWalk(&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\": " << (nextStatic - globalBase) << ", ";
-
- o << "\"initializers\": [";
- first = true;
- for (const auto& func : initializerFunctions) {
- if (first) first = false;
- else o << ", ";
- o << "\"" << func.c_str() << "\"";
- }
- o << "]";
-
- o << " }";
- }
+ void emscriptenGlue(std::ostream& o);
private:
- // Allocate space for a stack pointer and (if stackAllocation > 0) set up a
- // relocation for it to point to the top of the stack.
- void placeStackPointer(size_t stackAllocation) {
- // ensure this is the first allocation
- assert(nextStatic == globalBase || nextStatic == 1);
- const size_t pointerSize = 4;
- // Unconditionally allocate space for the stack pointer. Emscripten
- // allocates the stack itself, and initializes the stack pointer itself.
- size_t address = allocateStatic(pointerSize, pointerSize, "__stack_pointer");
- if (stackAllocation) {
- // If we are allocating the stack, set up a relocation to initialize the
- // stack pointer to point to one past-the-end of the stack allocation.
- auto* raw = new uint32_t;
- relocations.emplace_back(
- make_unique<Relocation>(raw, ".stack", stackAllocation));
- assert(wasm.memory.segments.size() == 0);
- addressSegments[address] = wasm.memory.segments.size();
- wasm.memory.segments.emplace_back(
- address, reinterpret_cast<char*>(raw), pointerSize);
- }
- }
-
template<class C>
void printSet(std::ostream& o, C& c) {
o << "[";
@@ -371,29 +150,7 @@ class Linker {
// Create thunks for use with emscripten Runtime.dynCall. Creates one for each
// signature in the indirect function table.
- void makeDynCallThunks() {
- std::unordered_set<std::string> sigs;
- wasm::Builder wasmBuilder(wasm);
- for (const auto& indirectFunc : wasm.table.names) {
- std::string sig(getSig(wasm.getFunction(indirectFunc)));
- auto* funcType = ensureFunctionType(sig, &wasm, wasm.allocator);
- 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, std::move(args));
- f->body = funcType->result == none ? call : wasmBuilder.makeReturn(call);
- wasm.addFunction(f);
- exportFunction(f->name, true);
- }
- }
+ void makeDynCallThunks();
static size_t roundUpToPageSize(size_t size) {
return (size + Memory::kPageSize - 1) & Memory::kPageMask;