summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJacob Gravelle <jgravelle@google.com>2018-01-22 12:50:36 -0800
committerGitHub <noreply@github.com>2018-01-22 12:50:36 -0800
commit02729a12e1735f629d3066b51c96a056f712b080 (patch)
tree353a495836776695d0f86f08b6292635c4dba101 /src
parentb01f2bb237e086fe4ae852c6004297fa8f8b39c2 (diff)
downloadbinaryen-02729a12e1735f629d3066b51c96a056f712b080.tar.gz
binaryen-02729a12e1735f629d3066b51c96a056f712b080.tar.bz2
binaryen-02729a12e1735f629d3066b51c96a056f712b080.zip
First pass at LLD support for Emscripten (#1346)
* Skeleton of a beginning of o2wasm, WIP and probably not going to be used * Get building post-cherry-pick * ast->ir, remove commented out code, include a debug module print because linking * Read linking section, print emscripten metadata json * WasmBinaryWriter emits user sections on Module * Remove debugging prints, everything that isn't needed to build metadata * Rename o2wasm to lld-metadata * lld-metadata support for outputting to file * Use tables index instead of function index for initializer functions * Add lld-emscripten tool to add emscripten-runtime functions to wasm modules (built with lld) * Handle EM_ASM in lld-emscripten * Add a list of functions to forcibly export (for initializer functions) * Disable incorrect initializer function reading * Add error printing when parsing .o files in lld-metadata * Remove ';; METADATA: ' prefix from lld-metadata, output is now standalone json * Support em_asm consts that aren't at the start of a segment * Initial test framework for lld-metadata tool * Add em_asm test * Add support for WASM_INIT_FUNCS in the linking section * Remove reloc section parsing because it's unused * lld-emscripten can read and write text * Add test harness for lld-emscripten * Export all functions for now * Add missing lld test output * Add support for reading object files differently Only difference so far is in importing mutable globals being an object file representation for symbols, but invalid wasm. * Update help strings * Update linking tests for stackAlloc fix * Rename lld-emscripten,lld-metadata to wasm-emscripten-finalize,wasm-link-metadata * Add help text to header comments * auto& instead of auto & * Extract LinkType to abi/wasm-object.h * Remove special handling for wasm object file reading, allow mutable globals * Add braces around default switch case * Fix flake8 errors * Handle generating dyncall thunks for imports as well * Use explicit bool for stackPointerGlobal * Use glob patterns for lld file iteration * Use __wasm_call_ctors for all initializer functions
Diffstat (limited to 'src')
-rw-r--r--src/abi/wasm-object.h40
-rw-r--r--src/tools/s2wasm.cpp2
-rw-r--r--src/tools/wasm-emscripten-finalize.cpp103
-rw-r--r--src/tools/wasm-link-metadata.cpp118
-rw-r--r--src/wasm-binary.h10
-rw-r--r--src/wasm-emscripten.cpp139
-rw-r--r--src/wasm-emscripten.h12
-rw-r--r--src/wasm-io.h3
-rw-r--r--src/wasm/wasm-binary.cpp20
-rw-r--r--src/wasm/wasm-io.cpp9
10 files changed, 395 insertions, 61 deletions
diff --git a/src/abi/wasm-object.h b/src/abi/wasm-object.h
new file mode 100644
index 000000000..0ce814030
--- /dev/null
+++ b/src/abi/wasm-object.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2018 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.
+ */
+
+//
+// Contains definitions used for wasm object files.
+// See: https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md
+//
+
+#ifndef wasm_abi_wasm_object_h
+#define wasm_abi_wasm_object_h
+
+namespace wasm {
+
+namespace ABI {
+ enum LinkType : unsigned {
+ WASM_STACK_POINTER = 0x1,
+ WASM_SYMBOL_INFO = 0x2,
+ WASM_DATA_SIZE = 0x3,
+ WASM_DATA_ALIGNMENT = 0x4,
+ WASM_SEGMENT_INFO = 0x5,
+ WASM_INIT_FUNCS = 0x6,
+ };
+} // namespace ABI
+
+} // namespace wasm
+
+#endif // wasm_abi_wasm_object_h
diff --git a/src/tools/s2wasm.cpp b/src/tools/s2wasm.cpp
index 6e7b2c05e..32af57dba 100644
--- a/src/tools/s2wasm.cpp
+++ b/src/tools/s2wasm.cpp
@@ -184,7 +184,7 @@ int main(int argc, const char *argv[]) {
std::cerr << "Emscripten gluing..." << std::endl;
WasmPrinter::printModule(&wasm, std::cerr);
}
- metadata = emscriptenGlue(
+ metadata = ";; METADATA: " + emscriptenGlue(
wasm,
allowMemoryGrowth,
linker.getStackPointerAddress(),
diff --git a/src/tools/wasm-emscripten-finalize.cpp b/src/tools/wasm-emscripten-finalize.cpp
new file mode 100644
index 000000000..87505748c
--- /dev/null
+++ b/src/tools/wasm-emscripten-finalize.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2017 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.
+ */
+
+//
+// wasm-emscripten-finalize console tool
+// Performs Emscripten-specific transforms on .wasm files
+//
+
+#include <exception>
+
+#include "ir/trapping.h"
+#include "support/colors.h"
+#include "support/command-line.h"
+#include "support/file.h"
+#include "wasm-binary.h"
+#include "wasm-emscripten.h"
+#include "wasm-io.h"
+#include "wasm-linker.h"
+#include "wasm-printing.h"
+#include "wasm-validator.h"
+
+using namespace cashew;
+using namespace wasm;
+
+int main(int argc, const char *argv[]) {
+ std::string infile;
+ std::string outfile;
+ bool emitBinary = true;
+ std::vector<Name> forcedExports;
+ Options options("wasm-emscripten-finalize",
+ "Performs Emscripten-specific transforms on .wasm files");
+ options
+ .add("--output", "-o", "Output file",
+ Options::Arguments::One,
+ [&outfile](Options*, const std::string &argument) {
+ outfile = argument;
+ Colors::disable();
+ })
+ .add("--emit-text", "-S", "Emit text instead of binary for the output file",
+ Options::Arguments::Zero,
+ [&emitBinary](Options*, const std::string &) {
+ emitBinary = false;
+ })
+ .add_positional("INFILE", Options::Arguments::One,
+ [&infile](Options *o, const std::string &argument) {
+ infile = argument;
+ });
+ options.parse(argc, argv);
+
+ if (infile == "") {
+ Fatal() << "Need to specify an infile\n";
+ }
+ if (outfile == "" && emitBinary) {
+ Fatal() << "Need to specify an outfile, or use text output\n";
+ }
+
+ Module wasm;
+ ModuleReader reader;
+ reader.read(infile, wasm);
+
+ if (options.debug) {
+ std::cerr << "Module before:\n";
+ WasmPrinter::printModule(&wasm, std::cerr);
+ }
+
+ EmscriptenGlueGenerator generator(wasm);
+ generator.generateRuntimeFunctions();
+ generator.generateMemoryGrowthFunction();
+ generator.generateDynCallThunks();
+ generator.fixEmAsmConsts();
+
+ if (options.debug) {
+ std::cerr << "Module after:\n";
+ WasmPrinter::printModule(&wasm, std::cerr);
+ }
+
+ ModuleWriter writer;
+ // writer.setDebug(options.debug);
+ writer.setDebugInfo(true);
+ // writer.setDebugInfo(options.passOptions.debugInfo);
+ // writer.setSymbolMap(symbolMap);
+ writer.setBinary(emitBinary);
+ // if (emitBinary) {
+ // writer.setSourceMapFilename(sourceMapFilename);
+ // writer.setSourceMapUrl(sourceMapUrl);
+ // }
+ writer.write(wasm, outfile);
+
+ return 0;
+}
diff --git a/src/tools/wasm-link-metadata.cpp b/src/tools/wasm-link-metadata.cpp
new file mode 100644
index 000000000..1ca64880f
--- /dev/null
+++ b/src/tools/wasm-link-metadata.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2017 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.
+ */
+
+//
+// wasm-link-metadata console tool
+// Reads wasm .o file and emits .json metadata
+//
+
+#include <exception>
+
+#include "abi/wasm-object.h"
+#include "ir/trapping.h"
+#include "support/colors.h"
+#include "support/command-line.h"
+#include "support/file.h"
+#include "wasm-binary.h"
+#include "wasm-emscripten.h"
+#include "wasm-io.h"
+#include "wasm-linker.h"
+#include "wasm-printing.h"
+#include "wasm-validator.h"
+
+using namespace cashew;
+using namespace wasm;
+
+void parseLinkingSection(std::vector<char> const& data, uint32_t &dataSize) {
+ unsigned idx = 0;
+ auto get = [&idx, &data](){ return data[idx++]; };
+ auto readNext = [get](){
+ U32LEB leb;
+ leb.read(get);
+ return leb.value;
+ };
+
+ while (idx < data.size()) {
+ ABI::LinkType type = static_cast<ABI::LinkType>(readNext());
+ uint32_t size = readNext();
+ uint32_t startIdx = idx;
+
+ switch(type) {
+ case ABI::WASM_DATA_SIZE: {
+ dataSize = readNext();
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ // Always go to the end of the subsection based on size, not contents.
+ idx = startIdx + size;
+ }
+}
+
+int main(int argc, const char *argv[]) {
+ std::string infile;
+ std::string outfile;
+ Options options("wasm-link-metadata",
+ "Reads wasm .o file and emits .json metadata");
+ options
+ .add("--output", "-o", "Output file",
+ Options::Arguments::One,
+ [&outfile](Options *o, const std::string &argument) {
+ outfile = argument;
+ Colors::disable();
+ })
+ .add_positional("INFILE", Options::Arguments::One,
+ [&infile](Options *o, const std::string &argument) {
+ infile = argument;
+ });
+ options.parse(argc, argv);
+
+ if (infile == "") {
+ Fatal() << "Need to specify an infile\n";
+ }
+
+ Module wasm;
+ try {
+ ModuleReader reader;
+ reader.readBinary(infile, wasm);
+ } catch (ParseException& p) {
+ p.dump(std::cerr);
+ Fatal() << "error in parsing wasm binary";
+ }
+
+ if (options.debug) {
+ WasmPrinter::printModule(&wasm, std::cerr);
+ }
+
+ uint32_t dataSize = 0;
+ for (auto &section : wasm.userSections) {
+ if (section.name == "linking") {
+ parseLinkingSection(section.data, dataSize);
+ }
+ }
+
+ std::vector<Name> initializerFunctions;
+ initializerFunctions.push_back("__wasm_call_ctors");
+
+ EmscriptenGlueGenerator generator(wasm);
+ std::string metadata = generator.generateEmscriptenMetadata(dataSize, initializerFunctions);
+ Output output(outfile, Flags::Text, Flags::Release);
+ output << metadata;
+
+ return 0;
+}
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index e5bfd9f2d..7e932c305 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -724,6 +724,7 @@ public:
void writeNames();
void writeSourceMapUrl();
void writeSymbolMap();
+ void writeUserSections();
void writeSourceMapProlog();
void writeSourceMapEpilog();
@@ -814,7 +815,14 @@ class WasmBinaryBuilder {
std::set<BinaryConsts::Section> seenSections;
public:
- WasmBinaryBuilder(Module& wasm, const std::vector<char>& input, bool debug) : wasm(wasm), allocator(wasm.allocator), input(input), debug(debug), sourceMap(nullptr), nextDebugLocation(0, { 0, 0, 0 }), useDebugLocation(false) {}
+ WasmBinaryBuilder(Module& wasm, const std::vector<char>& input, bool debug)
+ : wasm(wasm),
+ allocator(wasm.allocator),
+ input(input),
+ debug(debug),
+ sourceMap(nullptr),
+ nextDebugLocation(0, { 0, 0, 0 }),
+ useDebugLocation(false) {}
void read();
void readUserSection(size_t payloadLen);
diff --git a/src/wasm-emscripten.cpp b/src/wasm-emscripten.cpp
index a07254061..60bf5cce3 100644
--- a/src/wasm-emscripten.cpp
+++ b/src/wasm-emscripten.cpp
@@ -40,28 +40,39 @@ void addExportedFunction(Module& wasm, Function* function) {
wasm.addExport(export_);
}
-Load* EmscriptenGlueGenerator::generateLoadStackPointer() {
- Load* load = builder.makeLoad(
- /* bytes =*/ 4,
- /* signed =*/ false,
- /* offset =*/ stackPointerOffset,
- /* align =*/ 4,
- /* ptr =*/ builder.makeConst(Literal(0)),
- /* type =*/ i32
- );
- return load;
+Global* EmscriptenGlueGenerator::getStackPointerGlobal() {
+ // Assumption: first global is __stack_pointer
+ return wasm.globals[0].get();
}
-Store* EmscriptenGlueGenerator::generateStoreStackPointer(Expression* value) {
- Store* store = builder.makeStore(
- /* bytes =*/ 4,
- /* offset =*/ stackPointerOffset,
- /* align =*/ 4,
- /* ptr =*/ builder.makeConst(Literal(0)),
- /* value =*/ value,
- /* type =*/ i32
- );
- return store;
+Expression* EmscriptenGlueGenerator::generateLoadStackPointer() {
+ if (!useStackPointerGlobal) {
+ return builder.makeLoad(
+ /* bytes =*/ 4,
+ /* signed =*/ false,
+ /* offset =*/ stackPointerOffset,
+ /* align =*/ 4,
+ /* ptr =*/ builder.makeConst(Literal(0)),
+ /* type =*/ i32
+ );
+ }
+ Global* stackPointer = getStackPointerGlobal();
+ return builder.makeGetGlobal(stackPointer->name, i32);
+}
+
+Expression* EmscriptenGlueGenerator::generateStoreStackPointer(Expression* value) {
+ if (!useStackPointerGlobal) {
+ return builder.makeStore(
+ /* bytes =*/ 4,
+ /* offset =*/ stackPointerOffset,
+ /* align =*/ 4,
+ /* ptr =*/ builder.makeConst(Literal(0)),
+ /* value =*/ value,
+ /* type =*/ i32
+ );
+ }
+ Global* stackPointer = getStackPointerGlobal();
+ return builder.makeSetGlobal(stackPointer->name, value);
}
void EmscriptenGlueGenerator::generateStackSaveFunction() {
@@ -82,7 +93,7 @@ void EmscriptenGlueGenerator::generateStackAllocFunction() {
Function* function = builder.makeFunction(
name, std::move(params), i32, { { "1", i32 } }
);
- Load* loadStack = generateLoadStackPointer();
+ Expression* loadStack = generateLoadStackPointer();
GetLocal* getSizeArg = builder.makeGetLocal(0, i32);
Binary* sub = builder.makeBinary(SubInt32, loadStack, getSizeArg);
const static uint32_t bitAlignment = 16;
@@ -90,7 +101,7 @@ void EmscriptenGlueGenerator::generateStackAllocFunction() {
Const* subConst = builder.makeConst(Literal(~bitMask));
Binary* maskedSub = builder.makeBinary(AndInt32, sub, subConst);
SetLocal* teeStackLocal = builder.makeTeeLocal(1, maskedSub);
- Store* storeStack = generateStoreStackPointer(teeStackLocal);
+ Expression* storeStack = generateStoreStackPointer(teeStackLocal);
Block* block = builder.makeBlock();
block->list.push_back(storeStack);
@@ -109,7 +120,7 @@ void EmscriptenGlueGenerator::generateStackRestoreFunction() {
name, std::move(params), none, {}
);
GetLocal* getArg = builder.makeGetLocal(0, i32);
- Store* store = generateStoreStackPointer(getArg);
+ Expression* store = generateStoreStackPointer(getArg);
function->body = store;
@@ -147,21 +158,7 @@ static bool hasI64ResultOrParam(FunctionType* ft) {
return false;
}
-void removeImportsWithSubstring(Module& module, Name name) {
- std::vector<Name> toRemove;
- for (auto& import : module.imports) {
- if (import->name.hasSubstring(name)) {
- toRemove.push_back(import->name);
- }
- }
- for (auto importName : toRemove) {
- module.removeImport(importName);
- }
-}
-
void EmscriptenGlueGenerator::generateDynCallThunks() {
- removeImportsWithSubstring(wasm, EMSCRIPTEN_ASM_CONST); // we create _sig versions
-
std::unordered_set<std::string> sigs;
Builder builder(wasm);
std::vector<Name> tableSegmentData;
@@ -172,7 +169,12 @@ void EmscriptenGlueGenerator::generateDynCallThunks() {
if (indirectFunc == dummyFunction) {
continue;
}
- std::string sig(getSig(wasm.getFunction(indirectFunc)));
+ std::string sig;
+ if (auto import = wasm.getImportOrNull(indirectFunc)) {
+ sig = getSig(wasm.getFunctionType(import->functionType));
+ } else {
+ 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
@@ -196,7 +198,7 @@ void EmscriptenGlueGenerator::generateDynCallThunks() {
struct AsmConstWalker : public PostWalker<AsmConstWalker> {
Module& wasm;
- std::unordered_map<Address, Address> segmentsByAddress; // address => segment index
+ std::vector<Address> segmentOffsets; // segment index => address offset
std::map<std::string, std::set<std::string>> sigsForCode;
std::map<std::string, Address> ids;
@@ -206,7 +208,7 @@ struct AsmConstWalker : public PostWalker<AsmConstWalker> {
for (unsigned i = 0; i < wasm.memory.segments.size(); ++i) {
Const* addrConst = wasm.memory.segments[i].offset->cast<Const>();
auto address = addrConst->value.geti32();
- segmentsByAddress[address] = Address(i);
+ segmentOffsets.push_back(address);
}
}
@@ -214,6 +216,7 @@ struct AsmConstWalker : public PostWalker<AsmConstWalker> {
private:
std::string codeForConstAddr(Const* addrConst);
+ const char* stringAtAddr(Address adddress);
Literal idLiteralForCode(std::string code);
std::string asmConstSig(std::string baseSig);
Name nameForImportWithSig(std::string sig);
@@ -222,7 +225,8 @@ private:
};
void AsmConstWalker::visitCallImport(CallImport* curr) {
- if (curr->target.hasSubstring(EMSCRIPTEN_ASM_CONST)) {
+ Import* import = wasm.getImport(curr->target);
+ if (import->base.hasSubstring(EMSCRIPTEN_ASM_CONST)) {
auto arg = curr->operands[0]->cast<Const>();
auto code = codeForConstAddr(arg);
arg->value = idLiteralForCode(code);
@@ -241,13 +245,25 @@ void AsmConstWalker::visitCallImport(CallImport* curr) {
std::string AsmConstWalker::codeForConstAddr(Const* addrConst) {
auto address = addrConst->value.geti32();
- auto segmentIterator = segmentsByAddress.find(address);
- if (segmentIterator == segmentsByAddress.end()) {
- // If we can't find the segment corresponding with the address, then we omitted the segment and the address points to an empty string.
+ const char* str = stringAtAddr(address);
+ if (!str) {
+ // If we can't find the segment corresponding with the address, then we
+ // omitted the segment and the address points to an empty string.
return escape("");
}
- Address segmentIndex = segmentsByAddress[address];
- return escape(&wasm.memory.segments[segmentIndex].data[0]);
+ auto result = escape(str);
+ return result;
+}
+
+const char* AsmConstWalker::stringAtAddr(Address address) {
+ for (unsigned i = 0; i < wasm.memory.segments.size(); ++i) {
+ Memory::Segment &segment = wasm.memory.segments[i];
+ Address offset = segmentOffsets[i];
+ if (address >= offset && address < offset + segment.data.size()) {
+ return &segment.data[address - offset];
+ }
+ }
+ return nullptr;
}
std::string AsmConstWalker::escape(const char *input) {
@@ -308,6 +324,31 @@ void AsmConstWalker::addImport(Name importName, std::string baseSig) {
wasm.addImport(import);
}
+AsmConstWalker fixEmAsmConstsAndReturnWalker(Module& wasm) {
+ // Collect imports to remove
+ // This would find our generated functions if we ran it later
+ std::vector<Name> toRemove;
+ for (auto& import : wasm.imports) {
+ if (import->base.hasSubstring(EMSCRIPTEN_ASM_CONST)) {
+ toRemove.push_back(import->name);
+ }
+ }
+
+ // Walk the module, generate _sig versions of EM_ASM functions
+ AsmConstWalker walker(wasm);
+ walker.walkModule(&wasm);
+
+ // Remove the base functions that we didn't generate
+ for (auto importName : toRemove) {
+ wasm.removeImport(importName);
+ }
+ return walker;
+}
+
+void EmscriptenGlueGenerator::fixEmAsmConsts() {
+ fixEmAsmConstsAndReturnWalker(wasm);
+}
+
template<class C>
void printSet(std::ostream& o, C& c) {
o << "[";
@@ -324,11 +365,9 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata(
Address staticBump,
std::vector<Name> const& initializerFunctions) {
std::stringstream meta;
- meta << ";; METADATA: { ";
+ meta << "{ ";
- // find asmConst calls, and emit their metadata
- AsmConstWalker walker(wasm);
- walker.walkModule(&wasm);
+ AsmConstWalker walker = fixEmAsmConstsAndReturnWalker(wasm);
// print
meta << "\"asmConsts\": {";
diff --git a/src/wasm-emscripten.h b/src/wasm-emscripten.h
index 0a0f88574..1878e6531 100644
--- a/src/wasm-emscripten.h
+++ b/src/wasm-emscripten.h
@@ -29,7 +29,8 @@ public:
EmscriptenGlueGenerator(Module& wasm, Address stackPointerOffset = Address(0))
: wasm(wasm),
builder(wasm),
- stackPointerOffset(stackPointerOffset) { }
+ stackPointerOffset(stackPointerOffset),
+ useStackPointerGlobal(stackPointerOffset == 0) { }
void generateRuntimeFunctions();
Function* generateMemoryGrowthFunction();
@@ -41,13 +42,18 @@ public:
std::string generateEmscriptenMetadata(
Address staticBump, std::vector<Name> const& initializerFunctions);
+ // Replace placeholder emscripten_asm_const functions with *_signature versions.
+ void fixEmAsmConsts();
+
private:
Module& wasm;
Builder builder;
Address stackPointerOffset;
+ bool useStackPointerGlobal;
- Load* generateLoadStackPointer();
- Store* generateStoreStackPointer(Expression* value);
+ Global* getStackPointerGlobal();
+ Expression* generateLoadStackPointer();
+ Expression* generateStoreStackPointer(Expression* value);
void generateStackSaveFunction();
void generateStackAllocFunction();
void generateStackRestoreFunction();
diff --git a/src/wasm-io.h b/src/wasm-io.h
index afdc4503c..17a722472 100644
--- a/src/wasm-io.h
+++ b/src/wasm-io.h
@@ -42,6 +42,9 @@ public:
void readBinary(std::string filename, Module& wasm);
// read text or binary, checking the contents for what it is
void read(std::string filename, Module& wasm);
+
+ // check whether a file is a wasm binary
+ bool isBinaryFile(std::string filename);
};
class ModuleWriter : public ModuleIO {
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 514b489a9..44ce9a2d1 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -61,6 +61,9 @@ void WasmBinaryWriter::write() {
if (sourceMap) {
writeSourceMapEpilog();
}
+
+ writeUserSections();
+
finishUp();
}
@@ -551,6 +554,17 @@ static void writeBase64VLQ(std::ostream& out, int32_t n) {
}
}
+void WasmBinaryWriter::writeUserSections() {
+ for (auto& section : wasm->userSections) {
+ auto start = startSection(0);
+ writeInlineString(section.name.c_str());
+ for (size_t i = 0; i < section.data.size(); i++) {
+ o << uint8_t(section.data[i]);
+ }
+ finishSection(start);
+ }
+}
+
void WasmBinaryWriter::writeDebugLocation(size_t offset, const Function::DebugLocation& loc) {
if (lastBytecodeOffset > 0) {
*sourceMap << ",";
@@ -1626,9 +1640,9 @@ void WasmBinaryBuilder::readImports() {
case ExternalKind::Global: {
curr->globalType = getWasmType();
auto globalMutable = getU32LEB();
- if (globalMutable) {
- throw ParseException("imported globals cannot be mutable");
- }
+ // TODO: actually use the globalMutable flag. Currently mutable global
+ // imports is a future feature, to be implemented with thread support.
+ (void)globalMutable;
break;
}
default: {
diff --git a/src/wasm/wasm-io.cpp b/src/wasm/wasm-io.cpp
index ebc9af8de..1f50d7140 100644
--- a/src/wasm/wasm-io.cpp
+++ b/src/wasm/wasm-io.cpp
@@ -46,15 +46,18 @@ void ModuleReader::readBinary(std::string filename, Module& wasm) {
parser.read();
}
-void ModuleReader::read(std::string filename, Module& wasm) {
- // see if this is a wasm binary
+bool ModuleReader::isBinaryFile(std::string filename) {
std::ifstream infile;
std::ios_base::openmode flags = std::ifstream::in | std::ifstream::binary;
infile.open(filename, flags);
char buffer[4] = { 1, 2, 3, 4 };
infile.read(buffer, 4);
infile.close();
- if (buffer[0] == '\0' && buffer[1] == 'a' && buffer[2] == 's' && buffer[3] == 'm') {
+ return buffer[0] == '\0' && buffer[1] == 'a' && buffer[2] == 's' && buffer[3] == 'm';
+}
+
+void ModuleReader::read(std::string filename, Module& wasm) {
+ if (isBinaryFile(filename)) {
readBinary(filename, wasm);
} else {
// default to text