summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xauto_update_tests.py2
-rwxr-xr-xcheck.py6
-rw-r--r--src/js/wasm.js-post.js2
-rw-r--r--src/passes/SimplifyLocals.cpp17
-rw-r--r--src/support/archive.cpp4
-rw-r--r--src/wasm-linker.cpp38
-rw-r--r--src/wasm.h34
-rw-r--r--test/linker/archive/bar.s5
-rw-r--r--test/linker/archive/foobar.a24
-rw-r--r--test/linker/bar.c4
-rw-r--r--test/linker/quux.c1
-rw-r--r--test/passes/simplify-locals.txt19
-rw-r--r--test/passes/simplify-locals.wast19
13 files changed, 137 insertions, 38 deletions
diff --git a/auto_update_tests.py b/auto_update_tests.py
index 7162bd8fd..d435dcdaf 100755
--- a/auto_update_tests.py
+++ b/auto_update_tests.py
@@ -2,7 +2,7 @@
import os, sys, subprocess, difflib
-os.environ['LD_LIBRARY_PATH'] = 'lib' # find our dynamic libraries
+os.environ['LD_LIBRARY_PATH'] += os.pathsep + 'lib' # find our dynamic libraries
print '[ processing and updating testcases... ]\n'
diff --git a/check.py b/check.py
index b02ce14ce..1cc3eea17 100755
--- a/check.py
+++ b/check.py
@@ -49,7 +49,7 @@ WATERFALL_BUILD_DIR = os.path.join(BASE_DIR, 'wasm-install')
BIN_DIR = os.path.abspath(os.path.join(WATERFALL_BUILD_DIR, 'wasm-install', 'bin'))
os.environ['BINARYEN'] = os.getcwd()
-os.environ['LD_LIBRARY_PATH'] = 'lib' # find our dynamic libraries
+os.environ['LD_LIBRARY_PATH'] += os.pathsep + 'lib' # find our dynamic libraries
def fetch_waterfall():
rev = open(os.path.join('test', 'revision')).read().strip()
@@ -500,7 +500,7 @@ for dot_s_dir in ['dot_s', 'llvm_autogenerated']:
print '\n[ running linker tests... ]\n'
# The {main,foo,bar,baz}.s files were created by running clang over the respective
# c files. The foobar.bar archive was created by running:
-# llvm-ar -format=gnu rc foobar.a foo.s bar.s baz.s
+# llvm-ar -format=gnu rc foobar.a quux.s foo.s bar.s baz.s
s2wasm = os.path.join('bin', 's2wasm')
cmd = [s2wasm, os.path.join('test', 'linker', 'main.s'), '-l', os.path.join('test', 'linker', 'archive', 'foobar.a')]
output = run_command(cmd)
@@ -509,6 +509,8 @@ fail_if_not_contained(output, '(func $foo')
fail_if_not_contained(output, '(i32.const 42)')
# bar should be linked in from bar.s
fail_if_not_contained(output, '(func $bar')
+# quux should be linked in from bar.s even though it comes before bar.s in the archive
+fail_if_not_contained(output, '(func $quux')
# baz should not be linked in at all
if 'baz' in output:
raise Exception('output should not contain "baz": ' + output)
diff --git a/src/js/wasm.js-post.js b/src/js/wasm.js-post.js
index a0c026c91..6d73981f1 100644
--- a/src/js/wasm.js-post.js
+++ b/src/js/wasm.js-post.js
@@ -188,7 +188,7 @@ function integrateWasmJS(Module) {
info['env'] = env;
var instance;
try {
- instance = Wasm.instantiateModule(getBinary(), info);
+ instance = Wasm['instantiateModule'](getBinary(), info);
} catch (e) {
Module['printErr']('failed to compile wasm module: ' + e);
return false;
diff --git a/src/passes/SimplifyLocals.cpp b/src/passes/SimplifyLocals.cpp
index 785bccf06..fbd1e7e57 100644
--- a/src/passes/SimplifyLocals.cpp
+++ b/src/passes/SimplifyLocals.cpp
@@ -126,6 +126,7 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals,
for (auto target : sw->targets) {
self->unoptimizableBlocks.insert(target);
}
+ self->unoptimizableBlocks.insert(sw->default_);
// TODO: we could use this info to stop gathering data on these blocks
}
self->sinkables.clear();
@@ -161,12 +162,16 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals,
// post-block cleanups
if (curr->name.is()) {
- unoptimizableBlocks.erase(curr->name);
- }
- if (hasBreaks) {
- // more than one path to here, so nonlinear
- sinkables.clear();
- blockBreaks.erase(curr->name);
+ if (unoptimizableBlocks.count(curr->name)) {
+ sinkables.clear();
+ unoptimizableBlocks.erase(curr->name);
+ }
+
+ if (hasBreaks) {
+ // more than one path to here, so nonlinear
+ sinkables.clear();
+ blockBreaks.erase(curr->name);
+ }
}
}
diff --git a/src/support/archive.cpp b/src/support/archive.cpp
index c0351bc05..126f4e2ce 100644
--- a/src/support/archive.cpp
+++ b/src/support/archive.cpp
@@ -210,8 +210,8 @@ static uint32_t read32be(const uint8_t* buf) {
}
void Archive::dump() const {
- printf("Archive data %p len %lu, firstRegularData %p\n", data.data(),
- (long unsigned)data.size(), firstRegularData);
+ printf("Archive data %p len %zu, firstRegularData %p\n", data.data(),
+ data.size(), firstRegularData);
printf("Symbol table %p, len %u\n", symbolTable.data, symbolTable.len);
printf("string table %p, len %u\n", stringTable.data, stringTable.len);
const uint8_t* buf = symbolTable.data;
diff --git a/src/wasm-linker.cpp b/src/wasm-linker.cpp
index f4f516073..a4bd97e2f 100644
--- a/src/wasm-linker.cpp
+++ b/src/wasm-linker.cpp
@@ -217,23 +217,31 @@ bool Linker::linkObject(S2WasmBuilder& builder) {
}
bool Linker::linkArchive(Archive& archive) {
- for (auto child = archive.child_begin(), end = archive.child_end();
- child != end; ++child) {
- Archive::SubBuffer memberBuf = child->getBuffer();
- // S2WasmBuilder expects its input to be NUL-terminated. Archive members are
- // not NUL-terminated. So we have to copy the contents out before parsing.
- std::vector<char> memberString(memberBuf.len + 1);
- memcpy(memberString.data(), memberBuf.data, memberBuf.len);
- memberString[memberBuf.len] = '\0';
- S2WasmBuilder memberBuilder(memberString.data(), false);
- auto* memberSymbols = memberBuilder.getSymbolInfo();
- for (const Name& symbol : memberSymbols->implementedFunctions) {
- if (out.symbolInfo.undefinedFunctions.count(symbol)) {
- if (!linkObject(memberBuilder)) return false;
- break;
+ bool selected;
+ do {
+ selected = false;
+ for (auto child = archive.child_begin(), end = archive.child_end();
+ child != end; ++child) {
+ Archive::SubBuffer memberBuf = child->getBuffer();
+ // S2WasmBuilder expects its input to be NUL-terminated. Archive members
+ // are
+ // not NUL-terminated. So we have to copy the contents out before parsing.
+ std::vector<char> memberString(memberBuf.len + 1);
+ memcpy(memberString.data(), memberBuf.data, memberBuf.len);
+ memberString[memberBuf.len] = '\0';
+ S2WasmBuilder memberBuilder(memberString.data(), false);
+ auto* memberSymbols = memberBuilder.getSymbolInfo();
+ for (const Name& symbol : memberSymbols->implementedFunctions) {
+ if (out.symbolInfo.undefinedFunctions.count(symbol)) {
+ if (!linkObject(memberBuilder)) return false;
+ selected = true;
+ break;
+ }
}
}
- }
+ // If we selected an archive member, it may depend on another archive member
+ // so continue to make passes over the members until no more are added.
+ } while (selected);
return true;
}
diff --git a/src/wasm.h b/src/wasm.h
index f7ed6b42f..dff0bcd5a 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -246,11 +246,37 @@ private:
}
}
+ static uint32_t NaNPayload(float f) {
+ assert(std::isnan(f) && "expected a NaN");
+ // SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF
+ // NaN has all-one exponent and non-zero fraction.
+ return ~0xff800000u & bit_cast<uint32_t>(f);
+ }
+
+ static uint64_t NaNPayload(double f) {
+ assert(std::isnan(f) && "expected a NaN");
+ // SEEEEEEE EEEEFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
+ // NaN has all-one exponent and non-zero fraction.
+ return ~0xfff0000000000000ull & bit_cast<uint64_t>(f);
+ }
+
+ static float setQuietNaN(float f) {
+ assert(std::isnan(f) && "expected a NaN");
+ // An SNaN is a NaN with the most significant fraction bit clear.
+ return bit_cast<float>(0x00400000u | bit_cast<uint32_t>(f));
+ }
+
+ static double setQuietNaN(double f) {
+ assert(std::isnan(f) && "expected a NaN");
+ // An SNaN is a NaN with the most significant fraction bit clear.
+ return bit_cast<double>(0x0008000000000000ull | bit_cast<uint64_t>(f));
+ }
+
static void printFloat(std::ostream &o, float f) {
if (std::isnan(f)) {
const char *sign = std::signbit(f) ? "-" : "";
o << sign << "nan";
- if (uint32_t payload = ~0xff800000u & bit_cast<uint32_t>(f)) {
+ if (uint32_t payload = NaNPayload(f)) {
o << ":0x" << std::hex << payload << std::dec;
}
return;
@@ -266,7 +292,7 @@ private:
if (std::isnan(d)) {
const char *sign = std::signbit(d) ? "-" : "";
o << sign << "nan";
- if (uint64_t payload = ~0xfff0000000000000ull & bit_cast<uint64_t>(d)) {
+ if (uint64_t payload = NaNPayload(d)) {
o << ":0x" << std::hex << payload << std::dec;
}
return;
@@ -448,7 +474,7 @@ private:
switch (std::fpclassify(rhs)) {
case FP_ZERO:
switch (std::fpclassify(lhs)) {
- case FP_NAN: return *this;
+ case FP_NAN: return Literal(setQuietNaN(lhs));
case FP_ZERO: return Literal(std::copysign(std::numeric_limits<float>::quiet_NaN(), sign));
case FP_NORMAL: // fallthrough
case FP_SUBNORMAL: // fallthrough
@@ -468,7 +494,7 @@ private:
switch (std::fpclassify(rhs)) {
case FP_ZERO:
switch (std::fpclassify(lhs)) {
- case FP_NAN: return *this;
+ case FP_NAN: return Literal(setQuietNaN(lhs));
case FP_ZERO: return Literal(std::copysign(std::numeric_limits<double>::quiet_NaN(), sign));
case FP_NORMAL: // fallthrough
case FP_SUBNORMAL: // fallthrough
diff --git a/test/linker/archive/bar.s b/test/linker/archive/bar.s
index 7c39347e0..5012d4f14 100644
--- a/test/linker/archive/bar.s
+++ b/test/linker/archive/bar.s
@@ -1,15 +1,16 @@
.text
- .file "src/work/binaryen/test/linker/archive/bar.c"
+ .file "test/linker/bar.c"
.section .text.bar,"ax",@progbits
.hidden bar
.globl bar
.type bar,@function
bar: # @bar
# BB#0: # %entry
+ call quux@FUNCTION
return
.endfunc
.Lfunc_end0:
.size bar, .Lfunc_end0-bar
- .ident "clang version 3.9.0 (trunk 267883) (llvm/trunk 267901)"
+ .ident "clang version 3.9.0 (trunk 268553) (llvm/trunk 268561)"
diff --git a/test/linker/archive/foobar.a b/test/linker/archive/foobar.a
index 6028e8ce5..0da380708 100644
--- a/test/linker/archive/foobar.a
+++ b/test/linker/archive/foobar.a
@@ -1,4 +1,21 @@
!<arch>
+quux.s/ 0 0 0 644 345 `
+ .text
+ .file "test/linker/quux.c"
+ .section .text.quux,"ax",@progbits
+ .hidden quux
+ .globl quux
+ .type quux,@function
+quux: # @quux
+# BB#0: # %entry
+ return
+ .endfunc
+.Lfunc_end0:
+ .size quux, .Lfunc_end0-quux
+
+
+ .ident "clang version 3.9.0 (trunk 268553) (llvm/trunk 268561)"
+
foo.s/ 0 0 0 644 407 `
.text
.file "src/work/binaryen/test/linker/foo.c"
@@ -19,22 +36,23 @@ foo: # @foo
.ident "clang version 3.9.0 (trunk 267883) (llvm/trunk 267901)"
-bar.s/ 0 0 0 644 363 `
+bar.s/ 0 0 0 644 361 `
.text
- .file "src/work/binaryen/test/linker/archive/bar.c"
+ .file "test/linker/bar.c"
.section .text.bar,"ax",@progbits
.hidden bar
.globl bar
.type bar,@function
bar: # @bar
# BB#0: # %entry
+ call quux@FUNCTION
return
.endfunc
.Lfunc_end0:
.size bar, .Lfunc_end0-bar
- .ident "clang version 3.9.0 (trunk 267883) (llvm/trunk 267901)"
+ .ident "clang version 3.9.0 (trunk 268553) (llvm/trunk 268561)"
baz.s/ 0 0 0 644 355 `
.text
diff --git a/test/linker/bar.c b/test/linker/bar.c
index 0476ca2b0..8f32349da 100644
--- a/test/linker/bar.c
+++ b/test/linker/bar.c
@@ -1,2 +1,2 @@
-void bar() {
-}
+void quux();
+void bar() { quux(); }
diff --git a/test/linker/quux.c b/test/linker/quux.c
new file mode 100644
index 000000000..d9baa902a
--- /dev/null
+++ b/test/linker/quux.c
@@ -0,0 +1 @@
+void quux() {}
diff --git a/test/passes/simplify-locals.txt b/test/passes/simplify-locals.txt
index cc84c35d5..d97e8fc33 100644
--- a/test/passes/simplify-locals.txt
+++ b/test/passes/simplify-locals.txt
@@ -517,4 +517,23 @@
(get_local $m)
(get_local $t)
)
+ (func $switch-def (param $i3 i32) (result i32)
+ (local $i1 i32)
+ (set_local $i1
+ (i32.const 10)
+ )
+ (block $switch$def
+ (block $switch-case$1
+ (br_table $switch-case$1 $switch$def
+ (get_local $i3)
+ )
+ )
+ (set_local $i1
+ (i32.const 1)
+ )
+ )
+ (return
+ (get_local $i1)
+ )
+ )
)
diff --git a/test/passes/simplify-locals.wast b/test/passes/simplify-locals.wast
index 5f1db4bdb..521e8bac4 100644
--- a/test/passes/simplify-locals.wast
+++ b/test/passes/simplify-locals.wast
@@ -451,5 +451,24 @@
(get_local $s)
(get_local $t)
)
+ (func $switch-def (param $i3 i32) (result i32)
+ (local $i1 i32)
+ (set_local $i1
+ (i32.const 10)
+ )
+ (block $switch$def
+ (block $switch-case$1
+ (br_table $switch-case$1 $switch$def
+ (get_local $i3)
+ )
+ )
+ (set_local $i1
+ (i32.const 1)
+ )
+ )
+ (return
+ (get_local $i1)
+ )
+ )
)