summaryrefslogtreecommitdiff
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
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
-rw-r--r--CMakeLists.txt22
-rwxr-xr-xauto_update_tests.py22
-rwxr-xr-xcheck.py3
-rwxr-xr-xscripts/test/generate_lld_tests.py84
-rwxr-xr-xscripts/test/lld.py62
-rw-r--r--scripts/test/shared.py8
-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
-rw-r--r--test/lld/em_asm.cpp8
-rw-r--r--test/lld/em_asm.json1
-rw-r--r--test/lld/em_asm.obin0 -> 797 bytes
-rw-r--r--test/lld/em_asm.wast41
-rw-r--r--test/lld/em_asm.wast.out75
-rw-r--r--test/lld/hello_world.c6
-rw-r--r--test/lld/hello_world.json1
-rw-r--r--test/lld/hello_world.obin0 -> 314 bytes
-rw-r--r--test/lld/hello_world.wast27
-rw-r--r--test/lld/hello_world.wast.out59
-rw-r--r--test/lld/init.c15
-rw-r--r--test/lld/init.json1
-rw-r--r--test/lld/init.obin0 -> 407 bytes
-rw-r--r--test/lld/init.wast41
-rw-r--r--test/lld/init.wast.out71
-rw-r--r--test/lld/recursive.c15
-rw-r--r--test/lld/recursive.json1
-rw-r--r--test/lld/recursive.obin0 -> 557 bytes
-rw-r--r--test/lld/recursive.wast85
-rw-r--r--test/lld/recursive.wast.out117
36 files changed, 1159 insertions, 62 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5e27b602f..13f963106 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -275,6 +275,28 @@ SET_PROPERTY(TARGET s2wasm PROPERTY CXX_STANDARD 11)
SET_PROPERTY(TARGET s2wasm PROPERTY CXX_STANDARD_REQUIRED ON)
INSTALL(TARGETS s2wasm DESTINATION ${CMAKE_INSTALL_BINDIR})
+SET(wasm-link-metadata_SOURCES
+ src/tools/wasm-link-metadata.cpp
+ src/wasm-emscripten.cpp
+)
+ADD_EXECUTABLE(wasm-link-metadata
+ ${wasm-link-metadata_SOURCES})
+TARGET_LINK_LIBRARIES(wasm-link-metadata passes wasm asmjs ir cfg support)
+SET_PROPERTY(TARGET wasm-link-metadata PROPERTY CXX_STANDARD 11)
+SET_PROPERTY(TARGET wasm-link-metadata PROPERTY CXX_STANDARD_REQUIRED ON)
+INSTALL(TARGETS wasm-link-metadata DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+SET(wasm-emscripten-finalize_SOURCES
+ src/tools/wasm-emscripten-finalize.cpp
+ src/wasm-emscripten.cpp
+)
+ADD_EXECUTABLE(wasm-emscripten-finalize
+ ${wasm-emscripten-finalize_SOURCES})
+TARGET_LINK_LIBRARIES(wasm-emscripten-finalize passes wasm asmjs ir cfg support)
+SET_PROPERTY(TARGET wasm-emscripten-finalize PROPERTY CXX_STANDARD 11)
+SET_PROPERTY(TARGET wasm-emscripten-finalize PROPERTY CXX_STANDARD_REQUIRED ON)
+INSTALL(TARGETS wasm-emscripten-finalize DESTINATION ${CMAKE_INSTALL_BINDIR})
+
SET(wasm_as_SOURCES
src/tools/wasm-as.cpp
)
diff --git a/auto_update_tests.py b/auto_update_tests.py
index c2cdc1601..10481cab5 100755
--- a/auto_update_tests.py
+++ b/auto_update_tests.py
@@ -6,7 +6,8 @@ from scripts.test.support import run_command, split_wast, node_test_glue, node_h
from scripts.test.shared import (
ASM2WASM, MOZJS, NODEJS, S2WASM, WASM_SHELL, WASM_OPT, WASM_AS, WASM_DIS,
WASM_CTOR_EVAL, WASM_MERGE, WASM_REDUCE, WASM2ASM, WASM_METADCE,
- BINARYEN_INSTALL_DIR, has_shell_timeout)
+ WASM_LINK_METADATA, WASM_EMSCRIPTEN_FINALIZE, BINARYEN_INSTALL_DIR,
+ files_with_pattern, has_shell_timeout)
from scripts.test.wasm2asm import tests, spec_tests, extra_tests, assert_tests
print '[ processing and updating testcases... ]\n'
@@ -72,6 +73,25 @@ for dot_s_dir in ['dot_s', 'llvm_autogenerated']:
with open(expected_file, 'w') as o: o.write(actual)
+print '\n[ checking wasm-link-metadata testcases... ]\n'
+
+for obj_path in files_with_pattern('test', 'lld', '*.o'):
+ print '..', obj_path
+ json_path = obj_path.replace('.o', '.json')
+
+ cmd = WASM_LINK_METADATA + [obj_path]
+ actual = run_command(cmd)
+ with open(json_path, 'w') as o: o.write(actual)
+
+print '\n[ checking wasm-emscripten-finalize testcases... ]\n'
+
+for wast_path in files_with_pattern('test', 'lld', '*.wast'):
+ print '..', wast_path
+ out_path = wast_path + '.out'
+ cmd = WASM_EMSCRIPTEN_FINALIZE + [wast_path, '-S']
+ actual = run_command(cmd)
+ with open(out_path, 'w') as o: o.write(actual)
+
for t in sorted(os.listdir(os.path.join('test', 'print'))):
if t.endswith('.wast'):
print '..', t
diff --git a/check.py b/check.py
index 7455a2cac..5a926bf8e 100755
--- a/check.py
+++ b/check.py
@@ -31,6 +31,7 @@ from scripts.test.shared import (
)
import scripts.test.asm2wasm as asm2wasm
+import scripts.test.lld as lld
import scripts.test.s2wasm as s2wasm
import scripts.test.wasm2asm as wasm2asm
@@ -604,6 +605,8 @@ def main():
run_binaryen_js_tests()
s2wasm.test_s2wasm()
s2wasm.test_linker()
+ lld.test_wasm_link_metadata()
+ lld.test_wasm_emscripten_finalize()
wasm2asm.test_wasm2asm()
run_validator_tests()
if options.torture and options.test_waterfall:
diff --git a/scripts/test/generate_lld_tests.py b/scripts/test/generate_lld_tests.py
new file mode 100755
index 000000000..c959bd857
--- /dev/null
+++ b/scripts/test/generate_lld_tests.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+
+# 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.
+
+import os
+import sys
+from support import run_command
+import shared
+
+
+def files_with_extensions(path, extensions):
+ for file in sorted(os.listdir(path)):
+ _, ext = os.path.splitext(file)
+ if ext in extensions:
+ yield file, ext
+
+
+def generate_object_files(clang_bin):
+ print '\n[ building object files from C sources... ]\n'
+
+ lld_path = os.path.join(shared.options.binaryen_test, 'lld')
+ for src_file, ext in files_with_extensions(lld_path, ['.c', '.cpp']):
+ print '..', src_file
+ obj_file = src_file.replace(ext, '.o')
+
+ src_path = os.path.join(lld_path, src_file)
+ obj_path = os.path.join(lld_path, obj_file)
+ run_command([
+ clang_bin, src_path, '-o', obj_path,
+ '--target=wasm32-unknown-unknown-wasm',
+ '-c',
+ '-nostdinc',
+ '-Xclang', '-nobuiltininc',
+ '-Xclang', '-nostdsysteminc',
+ '-Xclang', '-I/s/work/emscripten/system/include',
+ '-O1',
+ ])
+
+
+def generate_wast_files(lld_bin):
+ print '\n[ linking wasm files from object files... ]\n'
+
+ lld_path = os.path.join(shared.options.binaryen_test, 'lld')
+ for obj_file, ext in files_with_extensions(lld_path, ['.o']):
+ print '..', obj_file
+ wasm_file = obj_file.replace(ext, '.wasm')
+ wast_file = obj_file.replace(ext, '.wast')
+
+ obj_path = os.path.join(lld_path, obj_file)
+ wasm_path = os.path.join(lld_path, wasm_file)
+ wast_path = os.path.join(lld_path, wast_file)
+ run_command([
+ lld_bin, '-flavor', 'wasm',
+ '-z', '-stack-size=1048576',
+ obj_path, '-o', wasm_path,
+ '--entry=main',
+ '--allow-undefined',
+ '--export', '__wasm_call_ctors',
+ ])
+ try:
+ run_command(shared.WASM_DIS + [wasm_path, '-o', wast_path])
+ finally:
+ # Don't need the .wasm file, don't leave it around
+ shared.delete_from_orbit(wasm_path)
+
+
+if __name__ == '__main__':
+ if len(sys.argv) != 3:
+ print 'Usage: generate_lld_tests.py [path/to/clang] [path/to/lld]'
+ sys.exit(1)
+ generate_object_files(sys.argv[1])
+ generate_wast_files(sys.argv[2])
diff --git a/scripts/test/lld.py b/scripts/test/lld.py
new file mode 100755
index 000000000..793031ca6
--- /dev/null
+++ b/scripts/test/lld.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+
+# 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.
+
+import os
+from support import run_command
+from shared import (
+ fail, fail_with_error, files_with_pattern, options,
+ WASM_LINK_METADATA, WASM_EMSCRIPTEN_FINALIZE
+)
+
+
+def test_wasm_link_metadata():
+ print '\n[ checking wasm-link-metadata testcases... ]\n'
+
+ for obj_path in files_with_pattern(options.binaryen_test, 'lld', '*.o'):
+ print '..', obj_path
+ expected_file = obj_path.replace('.o', '.json')
+
+ cmd = WASM_LINK_METADATA + [obj_path]
+ actual = run_command(cmd)
+
+ if not os.path.exists(expected_file):
+ print actual
+ fail_with_error('output ' + expected_file + ' does not exist')
+ expected = open(expected_file, 'rb').read()
+ if actual != expected:
+ fail(actual, expected)
+
+
+def test_wasm_emscripten_finalize():
+ print '\n[ checking wasm-emscripten-finalize testcases... ]\n'
+
+ for wast_path in files_with_pattern(options.binaryen_test, 'lld', '*.wast'):
+ print '..', wast_path
+ expected_file = wast_path + '.out'
+ cmd = WASM_EMSCRIPTEN_FINALIZE + [wast_path, '-S']
+ actual = run_command(cmd)
+
+ if not os.path.exists(expected_file):
+ print actual
+ fail_with_error('output ' + expected_file + ' does not exist')
+ expected = open(expected_file, 'rb').read()
+ if actual != expected:
+ fail(actual, expected)
+
+
+if __name__ == '__main__':
+ test_wasm_link_metadata()
+ test_wasm_emscripten_finalize()
diff --git a/scripts/test/shared.py b/scripts/test/shared.py
index 9933f85e1..863e97ec0 100644
--- a/scripts/test/shared.py
+++ b/scripts/test/shared.py
@@ -1,5 +1,6 @@
import argparse
import difflib
+import glob
import os
import shutil
import subprocess
@@ -169,6 +170,9 @@ WASM_MERGE = [os.path.join(options.binaryen_bin, 'wasm-merge')]
S2WASM = [os.path.join(options.binaryen_bin, 's2wasm')]
WASM_REDUCE = [os.path.join(options.binaryen_bin, 'wasm-reduce')]
WASM_METADCE = [os.path.join(options.binaryen_bin, 'wasm-metadce')]
+WASM_LINK_METADATA = [os.path.join(options.binaryen_bin, 'wasm-link-metadata')]
+WASM_EMSCRIPTEN_FINALIZE = [os.path.join(options.binaryen_bin,
+ 'wasm-emscripten-finalize')]
S2WASM_EXE = S2WASM[0]
WASM_SHELL_EXE = WASM_SHELL[0]
@@ -429,3 +433,7 @@ def minify_check(wast, verify_final_result=True):
os.unlink('a.wast')
if os.path.exists('b.wast'):
os.unlink('b.wast')
+
+
+def files_with_pattern(*path_pattern):
+ return sorted(glob.glob(os.path.join(*path_pattern)))
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
diff --git a/test/lld/em_asm.cpp b/test/lld/em_asm.cpp
new file mode 100644
index 000000000..ed89783f8
--- /dev/null
+++ b/test/lld/em_asm.cpp
@@ -0,0 +1,8 @@
+#include <emscripten/em_asm.h>
+
+int main() {
+ EM_ASM({ Module.print("Hello world"); });
+ int x = EM_ASM_INT({ return $0 + $1; }, 13, 27);
+ EM_ASM_({ Module.print("Got " + $0); }, x);
+ return 0;
+}
diff --git a/test/lld/em_asm.json b/test/lld/em_asm.json
new file mode 100644
index 000000000..f5027845d
--- /dev/null
+++ b/test/lld/em_asm.json
@@ -0,0 +1 @@
+{ "asmConsts": {"2": ["{ Module.print(\"Got \" + $0); }", ["ii"]],"0": ["{ Module.print(\"Hello world\"); }", ["i"]],"1": ["{ return $0 + $1; }", ["iii"]]},"staticBump": 84, "initializers": ["__wasm_call_ctors"] }
diff --git a/test/lld/em_asm.o b/test/lld/em_asm.o
new file mode 100644
index 000000000..f95befdee
--- /dev/null
+++ b/test/lld/em_asm.o
Binary files differ
diff --git a/test/lld/em_asm.wast b/test/lld/em_asm.wast
new file mode 100644
index 000000000..da1d8c3b6
--- /dev/null
+++ b/test/lld/em_asm.wast
@@ -0,0 +1,41 @@
+(module
+ (type $0 (func (result i32)))
+ (type $1 (func (param i32) (result i32)))
+ (type $2 (func (param i32 i32 i32) (result i32)))
+ (type $3 (func (param i32 i32) (result i32)))
+ (type $4 (func))
+ (import "env" "_Z24emscripten_asm_const_intIJEEiPKcDpT_" (func $_Z24emscripten_asm_const_intIJEEiPKcDpT_ (param i32) (result i32)))
+ (import "env" "_Z24emscripten_asm_const_intIJiiEEiPKcDpT_" (func $_Z24emscripten_asm_const_intIJiiEEiPKcDpT_ (param i32 i32 i32) (result i32)))
+ (import "env" "_Z24emscripten_asm_const_intIJiEEiPKcDpT_" (func $_Z24emscripten_asm_const_intIJiEEiPKcDpT_ (param i32 i32) (result i32)))
+ (global $global$0 (mut i32) (i32.const 66656))
+ (global $global$1 i32 (i32.const 66656))
+ (table 1 1 anyfunc)
+ (memory $0 2)
+ (data (i32.const 1024) "{ Module.print(\"Hello world\"); }\00{ return $0 + $1; }\00{ Module.print(\"Got \" + $0); }\00")
+ (export "memory" (memory $0))
+ (export "main" (func $main))
+ (export "__wasm_call_ctors" (func $__wasm_call_ctors))
+ (export "__heap_base" (global $global$1))
+ (func $main (; 3 ;) (type $0) (result i32)
+ (drop
+ (call $_Z24emscripten_asm_const_intIJEEiPKcDpT_
+ (i32.const 1024)
+ )
+ )
+ (drop
+ (call $_Z24emscripten_asm_const_intIJiEEiPKcDpT_
+ (i32.const 1077)
+ (call $_Z24emscripten_asm_const_intIJiiEEiPKcDpT_
+ (i32.const 1057)
+ (i32.const 13)
+ (i32.const 27)
+ )
+ )
+ )
+ (i32.const 0)
+ )
+ (func $__wasm_call_ctors (; 4 ;) (type $4)
+ )
+ ;; custom section "linking", size 3
+)
+
diff --git a/test/lld/em_asm.wast.out b/test/lld/em_asm.wast.out
new file mode 100644
index 000000000..921cd9782
--- /dev/null
+++ b/test/lld/em_asm.wast.out
@@ -0,0 +1,75 @@
+(module
+ (type $0 (func (result i32)))
+ (type $1 (func (param i32) (result i32)))
+ (type $2 (func (param i32 i32 i32) (result i32)))
+ (type $3 (func (param i32 i32) (result i32)))
+ (type $4 (func))
+ (type $FUNCSIG$ii (func (param i32) (result i32)))
+ (type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32)))
+ (type $FUNCSIG$iii (func (param i32 i32) (result i32)))
+ (import "env" "emscripten_asm_const_i" (func $emscripten_asm_const_i (param i32) (result i32)))
+ (import "env" "emscripten_asm_const_iii" (func $emscripten_asm_const_iii (param i32 i32 i32) (result i32)))
+ (import "env" "emscripten_asm_const_ii" (func $emscripten_asm_const_ii (param i32 i32) (result i32)))
+ (global $global$0 (mut i32) (i32.const 66656))
+ (global $global$1 i32 (i32.const 66656))
+ (table 1 1 anyfunc)
+ (memory $0 2)
+ (data (i32.const 1024) "{ Module.print(\"Hello world\"); }\00{ return $0 + $1; }\00{ Module.print(\"Got \" + $0); }\00")
+ (export "memory" (memory $0))
+ (export "main" (func $main))
+ (export "__wasm_call_ctors" (func $__wasm_call_ctors))
+ (export "__heap_base" (global $global$1))
+ (export "stackSave" (func $stackSave))
+ (export "stackAlloc" (func $stackAlloc))
+ (export "stackRestore" (func $stackRestore))
+ (export "__growWasmMemory" (func $__growWasmMemory))
+ (func $main (; 3 ;) (type $0) (result i32)
+ (drop
+ (call $emscripten_asm_const_i
+ (i32.const 0)
+ )
+ )
+ (drop
+ (call $emscripten_asm_const_ii
+ (i32.const 2)
+ (call $emscripten_asm_const_iii
+ (i32.const 1)
+ (i32.const 13)
+ (i32.const 27)
+ )
+ )
+ )
+ (i32.const 0)
+ )
+ (func $__wasm_call_ctors (; 4 ;) (type $4)
+ (nop)
+ )
+ (func $stackSave (; 5 ;) (result i32)
+ (get_global $global$0)
+ )
+ (func $stackAlloc (; 6 ;) (param $0 i32) (result i32)
+ (local $1 i32)
+ (set_global $global$0
+ (tee_local $1
+ (i32.and
+ (i32.sub
+ (get_global $global$0)
+ (get_local $0)
+ )
+ (i32.const -16)
+ )
+ )
+ )
+ (get_local $1)
+ )
+ (func $stackRestore (; 7 ;) (param $0 i32)
+ (set_global $global$0
+ (get_local $0)
+ )
+ )
+ (func $__growWasmMemory (; 8 ;) (param $newSize i32) (result i32)
+ (grow_memory
+ (get_local $newSize)
+ )
+ )
+)
diff --git a/test/lld/hello_world.c b/test/lld/hello_world.c
new file mode 100644
index 000000000..babd8d43c
--- /dev/null
+++ b/test/lld/hello_world.c
@@ -0,0 +1,6 @@
+int puts(const char* str);
+
+int main() {
+ puts("Hello, world");
+ return 0;
+}
diff --git a/test/lld/hello_world.json b/test/lld/hello_world.json
new file mode 100644
index 000000000..c0a391b6e
--- /dev/null
+++ b/test/lld/hello_world.json
@@ -0,0 +1 @@
+{ "asmConsts": {},"staticBump": 13, "initializers": ["__wasm_call_ctors"] }
diff --git a/test/lld/hello_world.o b/test/lld/hello_world.o
new file mode 100644
index 000000000..a14f95993
--- /dev/null
+++ b/test/lld/hello_world.o
Binary files differ
diff --git a/test/lld/hello_world.wast b/test/lld/hello_world.wast
new file mode 100644
index 000000000..d1610cece
--- /dev/null
+++ b/test/lld/hello_world.wast
@@ -0,0 +1,27 @@
+(module
+ (type $0 (func (result i32)))
+ (type $1 (func (param i32) (result i32)))
+ (type $2 (func))
+ (import "env" "puts" (func $puts (param i32) (result i32)))
+ (global $global$0 (mut i32) (i32.const 66576))
+ (global $global$1 i32 (i32.const 66576))
+ (table 1 1 anyfunc)
+ (memory $0 2)
+ (data (i32.const 1024) "Hello, world\00")
+ (export "memory" (memory $0))
+ (export "main" (func $main))
+ (export "__wasm_call_ctors" (func $__wasm_call_ctors))
+ (export "__heap_base" (global $global$1))
+ (func $main (; 1 ;) (type $0) (result i32)
+ (drop
+ (call $puts
+ (i32.const 1024)
+ )
+ )
+ (i32.const 0)
+ )
+ (func $__wasm_call_ctors (; 2 ;) (type $2)
+ )
+ ;; custom section "linking", size 3
+)
+
diff --git a/test/lld/hello_world.wast.out b/test/lld/hello_world.wast.out
new file mode 100644
index 000000000..ab861cc94
--- /dev/null
+++ b/test/lld/hello_world.wast.out
@@ -0,0 +1,59 @@
+(module
+ (type $0 (func (result i32)))
+ (type $1 (func (param i32) (result i32)))
+ (type $2 (func))
+ (type $FUNCSIG$ii (func (param i32) (result i32)))
+ (import "env" "puts" (func $puts (param i32) (result i32)))
+ (global $global$0 (mut i32) (i32.const 66576))
+ (global $global$1 i32 (i32.const 66576))
+ (table 1 1 anyfunc)
+ (memory $0 2)
+ (data (i32.const 1024) "Hello, world\00")
+ (export "memory" (memory $0))
+ (export "main" (func $main))
+ (export "__wasm_call_ctors" (func $__wasm_call_ctors))
+ (export "__heap_base" (global $global$1))
+ (export "stackSave" (func $stackSave))
+ (export "stackAlloc" (func $stackAlloc))
+ (export "stackRestore" (func $stackRestore))
+ (export "__growWasmMemory" (func $__growWasmMemory))
+ (func $main (; 1 ;) (type $0) (result i32)
+ (drop
+ (call $puts
+ (i32.const 1024)
+ )
+ )
+ (i32.const 0)
+ )
+ (func $__wasm_call_ctors (; 2 ;) (type $2)
+ (nop)
+ )
+ (func $stackSave (; 3 ;) (result i32)
+ (get_global $global$0)
+ )
+ (func $stackAlloc (; 4 ;) (param $0 i32) (result i32)
+ (local $1 i32)
+ (set_global $global$0
+ (tee_local $1
+ (i32.and
+ (i32.sub
+ (get_global $global$0)
+ (get_local $0)
+ )
+ (i32.const -16)
+ )
+ )
+ )
+ (get_local $1)
+ )
+ (func $stackRestore (; 5 ;) (param $0 i32)
+ (set_global $global$0
+ (get_local $0)
+ )
+ )
+ (func $__growWasmMemory (; 6 ;) (param $newSize i32) (result i32)
+ (grow_memory
+ (get_local $newSize)
+ )
+ )
+)
diff --git a/test/lld/init.c b/test/lld/init.c
new file mode 100644
index 000000000..3a9311e3c
--- /dev/null
+++ b/test/lld/init.c
@@ -0,0 +1,15 @@
+volatile int x, y;
+
+__attribute__((constructor))
+void init_x() {
+ x = 14;
+}
+
+__attribute__((constructor))
+void init_y() {
+ y = 144;
+}
+
+int main() {
+ return x + y;
+}
diff --git a/test/lld/init.json b/test/lld/init.json
new file mode 100644
index 000000000..f4d13ada1
--- /dev/null
+++ b/test/lld/init.json
@@ -0,0 +1 @@
+{ "asmConsts": {},"staticBump": 8, "initializers": ["__wasm_call_ctors"] }
diff --git a/test/lld/init.o b/test/lld/init.o
new file mode 100644
index 000000000..e86c68092
--- /dev/null
+++ b/test/lld/init.o
Binary files differ
diff --git a/test/lld/init.wast b/test/lld/init.wast
new file mode 100644
index 000000000..be25ed464
--- /dev/null
+++ b/test/lld/init.wast
@@ -0,0 +1,41 @@
+(module
+ (type $0 (func))
+ (type $1 (func (result i32)))
+ (global $global$0 (mut i32) (i32.const 66576))
+ (global $global$1 i32 (i32.const 66576))
+ (table 1 1 anyfunc)
+ (memory $0 2)
+ (data (i32.const 1024) "\00\00\00\00\00\00\00\00")
+ (export "memory" (memory $0))
+ (export "main" (func $main))
+ (export "__wasm_call_ctors" (func $__wasm_call_ctors))
+ (export "__heap_base" (global $global$1))
+ (func $init_x (; 0 ;) (type $0)
+ (i32.store offset=1024
+ (i32.const 0)
+ (i32.const 14)
+ )
+ )
+ (func $init_y (; 1 ;) (type $0)
+ (i32.store offset=1028
+ (i32.const 0)
+ (i32.const 144)
+ )
+ )
+ (func $main (; 2 ;) (type $1) (result i32)
+ (i32.add
+ (i32.load offset=1024
+ (i32.const 0)
+ )
+ (i32.load offset=1028
+ (i32.const 0)
+ )
+ )
+ )
+ (func $__wasm_call_ctors (; 3 ;) (type $0)
+ (call $init_x)
+ (call $init_y)
+ )
+ ;; custom section "linking", size 3
+)
+
diff --git a/test/lld/init.wast.out b/test/lld/init.wast.out
new file mode 100644
index 000000000..3b46d26d4
--- /dev/null
+++ b/test/lld/init.wast.out
@@ -0,0 +1,71 @@
+(module
+ (type $0 (func))
+ (type $1 (func (result i32)))
+ (global $global$0 (mut i32) (i32.const 66576))
+ (global $global$1 i32 (i32.const 66576))
+ (table 1 1 anyfunc)
+ (memory $0 2)
+ (data (i32.const 1024) "\00\00\00\00\00\00\00\00")
+ (export "memory" (memory $0))
+ (export "main" (func $main))
+ (export "__wasm_call_ctors" (func $__wasm_call_ctors))
+ (export "__heap_base" (global $global$1))
+ (export "stackSave" (func $stackSave))
+ (export "stackAlloc" (func $stackAlloc))
+ (export "stackRestore" (func $stackRestore))
+ (export "__growWasmMemory" (func $__growWasmMemory))
+ (func $init_x (; 0 ;) (type $0)
+ (i32.store offset=1024
+ (i32.const 0)
+ (i32.const 14)
+ )
+ )
+ (func $init_y (; 1 ;) (type $0)
+ (i32.store offset=1028
+ (i32.const 0)
+ (i32.const 144)
+ )
+ )
+ (func $main (; 2 ;) (type $1) (result i32)
+ (i32.add
+ (i32.load offset=1024
+ (i32.const 0)
+ )
+ (i32.load offset=1028
+ (i32.const 0)
+ )
+ )
+ )
+ (func $__wasm_call_ctors (; 3 ;) (type $0)
+ (call $init_x)
+ (call $init_y)
+ )
+ (func $stackSave (; 4 ;) (result i32)
+ (get_global $global$0)
+ )
+ (func $stackAlloc (; 5 ;) (param $0 i32) (result i32)
+ (local $1 i32)
+ (set_global $global$0
+ (tee_local $1
+ (i32.and
+ (i32.sub
+ (get_global $global$0)
+ (get_local $0)
+ )
+ (i32.const -16)
+ )
+ )
+ )
+ (get_local $1)
+ )
+ (func $stackRestore (; 6 ;) (param $0 i32)
+ (set_global $global$0
+ (get_local $0)
+ )
+ )
+ (func $__growWasmMemory (; 7 ;) (param $newSize i32) (result i32)
+ (grow_memory
+ (get_local $newSize)
+ )
+ )
+)
diff --git a/test/lld/recursive.c b/test/lld/recursive.c
new file mode 100644
index 000000000..c0cdaa425
--- /dev/null
+++ b/test/lld/recursive.c
@@ -0,0 +1,15 @@
+// This test emits a stack pointer, which tests global importing in object files
+// (which are mutable and not normally allowed).
+
+int printf(const char* fmt, ...);
+
+__attribute__((noinline))
+int foo(int a, int b) {
+ printf("%d:%d\n", a, b);
+ return a + b;
+}
+
+int main() {
+ printf("Result: %d\n", foo(1, 2));
+ return 0;
+}
diff --git a/test/lld/recursive.json b/test/lld/recursive.json
new file mode 100644
index 000000000..a976be232
--- /dev/null
+++ b/test/lld/recursive.json
@@ -0,0 +1 @@
+{ "asmConsts": {},"staticBump": 19, "initializers": ["__wasm_call_ctors"] }
diff --git a/test/lld/recursive.o b/test/lld/recursive.o
new file mode 100644
index 000000000..409257fd4
--- /dev/null
+++ b/test/lld/recursive.o
Binary files differ
diff --git a/test/lld/recursive.wast b/test/lld/recursive.wast
new file mode 100644
index 000000000..21b46a31f
--- /dev/null
+++ b/test/lld/recursive.wast
@@ -0,0 +1,85 @@
+(module
+ (type $0 (func (param i32 i32) (result i32)))
+ (type $1 (func (result i32)))
+ (type $2 (func))
+ (import "env" "printf" (func $printf (param i32 i32) (result i32)))
+ (global $global$0 (mut i32) (i32.const 66592))
+ (global $global$1 i32 (i32.const 66592))
+ (table 1 1 anyfunc)
+ (memory $0 2)
+ (data (i32.const 1024) "%d:%d\n\00Result: %d\n\00")
+ (export "memory" (memory $0))
+ (export "main" (func $main))
+ (export "__wasm_call_ctors" (func $__wasm_call_ctors))
+ (export "__heap_base" (global $global$1))
+ (func $foo (; 1 ;) (type $0) (param $var$0 i32) (param $var$1 i32) (result i32)
+ (local $var$2 i32)
+ (set_global $global$0
+ (tee_local $var$2
+ (i32.sub
+ (get_global $global$0)
+ (i32.const 16)
+ )
+ )
+ )
+ (i32.store offset=4
+ (get_local $var$2)
+ (get_local $var$1)
+ )
+ (i32.store
+ (get_local $var$2)
+ (get_local $var$0)
+ )
+ (drop
+ (call $printf
+ (i32.const 1024)
+ (get_local $var$2)
+ )
+ )
+ (set_global $global$0
+ (i32.add
+ (get_local $var$2)
+ (i32.const 16)
+ )
+ )
+ (i32.add
+ (get_local $var$1)
+ (get_local $var$0)
+ )
+ )
+ (func $main (; 2 ;) (type $1) (result i32)
+ (local $var$0 i32)
+ (set_global $global$0
+ (tee_local $var$0
+ (i32.sub
+ (get_global $global$0)
+ (i32.const 16)
+ )
+ )
+ )
+ (i32.store
+ (get_local $var$0)
+ (call $foo
+ (i32.const 1)
+ (i32.const 2)
+ )
+ )
+ (drop
+ (call $printf
+ (i32.const 1031)
+ (get_local $var$0)
+ )
+ )
+ (set_global $global$0
+ (i32.add
+ (get_local $var$0)
+ (i32.const 16)
+ )
+ )
+ (i32.const 0)
+ )
+ (func $__wasm_call_ctors (; 3 ;) (type $2)
+ )
+ ;; custom section "linking", size 3
+)
+
diff --git a/test/lld/recursive.wast.out b/test/lld/recursive.wast.out
new file mode 100644
index 000000000..4920a7622
--- /dev/null
+++ b/test/lld/recursive.wast.out
@@ -0,0 +1,117 @@
+(module
+ (type $0 (func (param i32 i32) (result i32)))
+ (type $1 (func (result i32)))
+ (type $2 (func))
+ (type $FUNCSIG$iii (func (param i32 i32) (result i32)))
+ (import "env" "printf" (func $printf (param i32 i32) (result i32)))
+ (global $global$0 (mut i32) (i32.const 66592))
+ (global $global$1 i32 (i32.const 66592))
+ (table 1 1 anyfunc)
+ (memory $0 2)
+ (data (i32.const 1024) "%d:%d\n\00Result: %d\n\00")
+ (export "memory" (memory $0))
+ (export "main" (func $main))
+ (export "__wasm_call_ctors" (func $__wasm_call_ctors))
+ (export "__heap_base" (global $global$1))
+ (export "stackSave" (func $stackSave))
+ (export "stackAlloc" (func $stackAlloc))
+ (export "stackRestore" (func $stackRestore))
+ (export "__growWasmMemory" (func $__growWasmMemory))
+ (func $foo (; 1 ;) (type $0) (param $var$0 i32) (param $var$1 i32) (result i32)
+ (local $var$2 i32)
+ (set_global $global$0
+ (tee_local $var$2
+ (i32.sub
+ (get_global $global$0)
+ (i32.const 16)
+ )
+ )
+ )
+ (i32.store offset=4
+ (get_local $var$2)
+ (get_local $var$1)
+ )
+ (i32.store
+ (get_local $var$2)
+ (get_local $var$0)
+ )
+ (drop
+ (call $printf
+ (i32.const 1024)
+ (get_local $var$2)
+ )
+ )
+ (set_global $global$0
+ (i32.add
+ (get_local $var$2)
+ (i32.const 16)
+ )
+ )
+ (i32.add
+ (get_local $var$1)
+ (get_local $var$0)
+ )
+ )
+ (func $main (; 2 ;) (type $1) (result i32)
+ (local $var$0 i32)
+ (set_global $global$0
+ (tee_local $var$0
+ (i32.sub
+ (get_global $global$0)
+ (i32.const 16)
+ )
+ )
+ )
+ (i32.store
+ (get_local $var$0)
+ (call $foo
+ (i32.const 1)
+ (i32.const 2)
+ )
+ )
+ (drop
+ (call $printf
+ (i32.const 1031)
+ (get_local $var$0)
+ )
+ )
+ (set_global $global$0
+ (i32.add
+ (get_local $var$0)
+ (i32.const 16)
+ )
+ )
+ (i32.const 0)
+ )
+ (func $__wasm_call_ctors (; 3 ;) (type $2)
+ (nop)
+ )
+ (func $stackSave (; 4 ;) (result i32)
+ (get_global $global$0)
+ )
+ (func $stackAlloc (; 5 ;) (param $0 i32) (result i32)
+ (local $1 i32)
+ (set_global $global$0
+ (tee_local $1
+ (i32.and
+ (i32.sub
+ (get_global $global$0)
+ (get_local $0)
+ )
+ (i32.const -16)
+ )
+ )
+ )
+ (get_local $1)
+ )
+ (func $stackRestore (; 6 ;) (param $0 i32)
+ (set_global $global$0
+ (get_local $0)
+ )
+ )
+ (func $__growWasmMemory (; 7 ;) (param $newSize i32) (result i32)
+ (grow_memory
+ (get_local $newSize)
+ )
+ )
+)