summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml40
-rw-r--r--CMakeLists.txt1
-rwxr-xr-xcheck.py11
-rw-r--r--src/asm2wasm.h9
-rw-r--r--src/emscripten-optimizer/optimizer-shared.cpp21
-rw-r--r--src/emscripten-optimizer/optimizer.h4
-rw-r--r--src/emscripten-optimizer/parser.h18
-rw-r--r--src/emscripten-optimizer/simple_ast.h29
-rw-r--r--src/s2wasm.h28
-rw-r--r--src/support/safe_integer.cpp70
-rw-r--r--src/support/safe_integer.h34
-rw-r--r--src/wasm.h5
-rw-r--r--src/wasm2asm.h4
-rw-r--r--test/emcc_hello_world.2asm.js2
-rw-r--r--test/emcc_hello_world.fromasm2
-rw-r--r--test/emcc_hello_world.wast2
-rw-r--r--test/llvm_autogenerated/global.wast2
-rw-r--r--test/passes/O.txt2
-rw-r--r--ubsan.blacklist3
19 files changed, 210 insertions, 77 deletions
diff --git a/.travis.yml b/.travis.yml
index 02b7efd86..ece8abb59 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,24 +2,38 @@ sudo: false
language: cpp
matrix:
include:
- - addons: &gcc5
+
+ - env: COMPILER_VERSION=3.6
+ compiler: clang
+ addons: &clang36
apt:
- sources: ['ubuntu-toolchain-r-test']
- packages: ['cmake', 'g++-5']
+ sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6']
+ packages: ['cmake', 'clang-3.6']
+
+ - env: COMPILER_VERSION=3.6 COMPILER_FLAGS="-fsanitize=undefined -fsanitize-blacklist=`pwd`/ubsan.blacklist"
+ compiler: clang
+ addons: *clang36
+
+# - env: COMPILER_VERSION=3.6 COMPILER_FLAGS="-fsanitize=address"
+# compiler: clang
+# addons: *clang36
+#
+# - env: COMPILER_VERSION=3.6 COMPILER_FLAGS="-fsanitize=thread"
+# compiler: clang
+# addons: *clang36
+
+ - env: COMPILER_VERSION=5
compiler: gcc
- env: COMPILER_VERSION=5
- - addons: &clang37
+ addons: &gcc5
apt:
- sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7']
- packages: ['cmake', 'clang-3.7']
- compiler: clang
- env: COMPILER_VERSION=3.7
-before_script:
- - git submodule update --init # Initialize the submodules recorded in the index.
+ sources: ['ubuntu-toolchain-r-test']
+ packages: ['cmake', 'g++-5']
+
+before_install:
- export CC="${CC}-${COMPILER_VERSION}"
- export CXX="${CXX}-${COMPILER_VERSION}"
- - $CC --version
+
script:
- - cmake .
+ - cmake . -DCMAKE_C_FLAGS="$COMPILER_FLAGS" -DCMAKE_CXX_FLAGS="$COMPILER_FLAGS"
- make -j2
- ./check.py
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3771f7090..bcd9eb917 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,6 +27,7 @@ SET(support_SOURCES
src/support/colors.cpp
src/support/command-line.cpp
src/support/file.cpp
+ src/support/safe_integer.cpp
)
ADD_LIBRARY(support STATIC ${support_SOURCES})
diff --git a/check.py b/check.py
index ed43458ef..f3d8b02e0 100755
--- a/check.py
+++ b/check.py
@@ -144,7 +144,9 @@ for t in tests:
if t.endswith('.wast') and not t.startswith('spec'):
print '..', t
t = os.path.join('test', t)
- actual, err = subprocess.Popen([os.path.join('bin', 'binaryen-shell'), t, '-print-before'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+ cmd = [os.path.join('bin', 'binaryen-shell'), t, '-print-before']
+ print ' ', ' '.join(cmd)
+ actual, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
assert err.replace('printing before:', '').strip() == '', 'bad err:' + err
actual = actual.replace('printing before:\n', '')
@@ -271,7 +273,12 @@ if unexpected_result_count:
print '\n[ checking example testcases... ]\n'
-subprocess.check_call([os.environ.get('CXX') or 'g++', '-std=c++11', os.path.join('test', 'example', 'find_div0s.cpp'), '-Isrc', '-g', '-lsupport', '-Llib/.'])
+cmd = [os.environ.get('CXX') or 'g++', '-std=c++11', os.path.join('test', 'example', 'find_div0s.cpp'), '-Isrc', '-g', '-lsupport', '-Llib/.']
+if os.environ.get('COMPILER_FLAGS'):
+ for f in os.environ.get('COMPILER_FLAGS').split(' '):
+ cmd.append(f)
+print ' '.join(cmd)
+subprocess.check_call(cmd)
actual = subprocess.Popen(['./a.out'], stdout=subprocess.PIPE).communicate()[0]
expected = open(os.path.join('test', 'example', 'find_div0s.txt')).read()
if actual != expected:
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index db7cc1ae0..c72c7e98a 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -385,7 +385,7 @@ private:
}
if (ast[1] == MINUS && ast[2][0] == NUM) {
double num = -ast[2][1]->getNumber();
- assert(isInteger32(num));
+ assert(isSInteger32(num));
return Literal((int32_t)num);
}
if (ast[1] == PLUS && ast[2][0] == UNARY_PREFIX && ast[2][1] == MINUS && ast[2][2][0] == NUM) {
@@ -912,9 +912,12 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
} else if (what == NUM) {
auto ret = allocator.alloc<Const>();
double num = ast[1]->getNumber();
- if (isInteger32(num)) {
+ if (isSInteger32(num)) {
ret->value.type = WasmType::i32;
- ret->value.i32 = toInteger32(num);
+ ret->value.i32 = toSInteger32(num);
+ } else if (isUInteger32(num)) {
+ ret->value.type = WasmType::i32;
+ ret->value.i32 = toUInteger32(num);
} else {
ret->value.type = WasmType::f64;
ret->value.f64 = num;
diff --git a/src/emscripten-optimizer/optimizer-shared.cpp b/src/emscripten-optimizer/optimizer-shared.cpp
index 4466fb0e9..6831d81b0 100644
--- a/src/emscripten-optimizer/optimizer-shared.cpp
+++ b/src/emscripten-optimizer/optimizer-shared.cpp
@@ -14,7 +14,10 @@
* limitations under the License.
*/
+#include <limits>
+
#include "optimizer.h"
+#include "support/safe_integer.h"
using namespace cashew;
@@ -26,20 +29,6 @@ IString SIMD_INT8X16_CHECK("SIMD_Int8x16_check"),
SIMD_FLOAT32X4_CHECK("SIMD_Float32x4_check"),
SIMD_FLOAT64X2_CHECK("SIMD_Float64x2_check");
-bool isInteger(double x) {
- return fmod(x, 1) == 0;
-}
-
-bool isInteger32(double x) {
- return isInteger(x) && (x == (int32_t)x || x == (uint32_t)x);
-}
-
-int32_t toInteger32(double x) {
- if (x == (int32_t)x) return (int32_t)x;
- assert(x == (uint32_t)x);
- return (uint32_t)x;
-}
-
int parseInt(const char *str) {
int ret = *str - '0';
while (*(++str)) {
@@ -67,7 +56,7 @@ AsmType detectType(Ref node, AsmData *asmData, bool inVarDef, IString minifiedFr
switch (node[0]->getCString()[0]) {
case 'n': {
if (node[0] == NUM) {
- if (!isInteger(node[1]->getNumber())) return ASM_DOUBLE;
+ if (!wasm::isInteger(node[1]->getNumber())) return ASM_DOUBLE;
return ASM_INT;
} else if (node[0] == NAME) {
if (asmData) {
@@ -176,7 +165,7 @@ AsmSign detectSign(Ref node, IString minifiedFround) {
double value = node[1]->getNumber();
if (value < 0) return ASM_SIGNED;
if (value > uint32_t(-1) || fmod(value, 1) != 0) return ASM_NONSIGNED;
- if (value == int32_t(value)) return ASM_FLEXIBLE;
+ if (wasm::isSInteger32(value)) return ASM_FLEXIBLE;
return ASM_UNSIGNED;
} else if (type == NAME) {
return ASM_FLEXIBLE;
diff --git a/src/emscripten-optimizer/optimizer.h b/src/emscripten-optimizer/optimizer.h
index 451a9e286..684fc0164 100644
--- a/src/emscripten-optimizer/optimizer.h
+++ b/src/emscripten-optimizer/optimizer.h
@@ -116,10 +116,6 @@ struct AsmData {
}
};
-bool isInteger(double x);
-bool isInteger32(double x);
-int32_t toInteger32(double x);
-
extern cashew::IString ASM_FLOAT_ZERO;
extern cashew::IString SIMD_INT8X16_CHECK,
diff --git a/src/emscripten-optimizer/parser.h b/src/emscripten-optimizer/parser.h
index c805ca9f6..da3e6f5c7 100644
--- a/src/emscripten-optimizer/parser.h
+++ b/src/emscripten-optimizer/parser.h
@@ -22,13 +22,14 @@
#ifndef wasm_parser_h
#define wasm_parser_h
-#include <vector>
-#include <iostream>
#include <algorithm>
-
-#include <stdio.h>
+#include <cstdio>
+#include <iostream>
+#include <limits>
+#include <vector>
#include "istring.h"
+#include "support/safe_integer.h"
namespace cashew {
@@ -179,10 +180,6 @@ class Parser {
static bool hasChar(const char* list, char x) { while (*list) if (*list++ == x) return true; return false; }
- static bool is32Bit(double x) {
- return x == (int)x || x == (unsigned int)x;
- }
-
// An atomic fragment of something. Stops at a natural boundary.
enum FragType {
KEYWORD = 0,
@@ -249,7 +246,10 @@ class Parser {
// for valid asm.js input, the '.' should be enough, and for uglify
// in the emscripten optimizer pipeline, we use simple_ast where INT/DOUBLE
// is quite the same at this point anyhow
- type = (std::find(start, src, '.') == src && is32Bit(num)) ? INT : DOUBLE;
+ type = (std::find(start, src, '.') == src &&
+ (wasm::isSInteger32(num) || wasm::isUInteger32(num)))
+ ? INT
+ : DOUBLE;
assert(src > start);
} else if (hasChar(OPERATOR_INITS, *src)) {
switch (*src) {
diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h
index 712845dea..73037815f 100644
--- a/src/emscripten-optimizer/simple_ast.h
+++ b/src/emscripten-optimizer/simple_ast.h
@@ -17,26 +17,25 @@
#ifndef wasm_simple_ast_h
#define wasm_simple_ast_h
-#include <assert.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-
-#include <vector>
-#include <ostream>
-#include <iostream>
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <functional>
#include <iomanip>
+#include <iostream>
#include <limits>
-#include <functional>
-#include <algorithm>
+#include <ostream>
#include <set>
-#include <unordered_set>
#include <unordered_map>
+#include <unordered_set>
+#include <vector>
#include "parser.h"
-
#include "snprintf.h"
+#include "support/safe_integer.h"
#define err(str) fprintf(stderr, str "\n");
#define errv(str, ...) fprintf(stderr, str "\n", __VA_ARGS__);
@@ -870,8 +869,8 @@ struct JSPrinter {
} else {
// integer
assert(d >= 0);
- unsigned long long uu = (unsigned long long)d;
- if (uu == d) {
+ if (wasm::isUInteger64(d)) {
+ unsigned long long uu = wasm::toUInteger64(d);
bool asHex = e && !finalize;
snprintf(buffer, BUFFERSIZE-1, asHex ? "0x%llx" : "%llu", uu);
if (asHex) {
diff --git a/src/s2wasm.h b/src/s2wasm.h
index bd3f12e73..d1836b274 100644
--- a/src/s2wasm.h
+++ b/src/s2wasm.h
@@ -159,16 +159,23 @@ private:
return cashew::IString(str.c_str(), false);
}
- int32_t getInt() {
- int32_t ret = 0;
+ uint32_t getInt() {
+ uint32_t ret = 0;
bool neg = false;
if (*s == '-') {
neg = true;
s++;
}
while (isdigit(*s)) {
+ uint32_t digit = *s - '0';
+ if (ret > std::numeric_limits<uint32_t>::max() / 10) {
+ abort_on("overflow");
+ }
ret *= 10;
- ret += (*s - '0');
+ if (ret > std::numeric_limits<uint32_t>::max() - digit) {
+ abort_on("overflow");
+ }
+ ret += digit;
s++;
}
if (neg) ret = -ret;
@@ -197,16 +204,23 @@ private:
}
}
- int64_t getInt64() {
- int64_t ret = 0;
+ uint64_t getInt64() {
+ uint64_t ret = 0;
bool neg = false;
if (*s == '-') {
neg = true;
s++;
}
while (isdigit(*s)) {
+ uint64_t digit = *s - '0';
+ if (ret > std::numeric_limits<uint64_t>::max() / 10) {
+ abort_on("overflow");
+ }
ret *= 10;
- ret += (*s - '0');
+ if (ret > std::numeric_limits<uint64_t>::max() - digit) {
+ abort_on("overflow");
+ }
+ ret += digit;
s++;
}
if (neg) ret = -ret;
@@ -933,7 +947,7 @@ private:
} else if (match(".int64")) {
size_t size = raw->size();
raw->resize(size + 8);
- (*(int64_t*)(&(*raw)[size])) = getInt();
+ (*(int64_t*)(&(*raw)[size])) = getInt64();
zero = false;
} else {
break;
diff --git a/src/support/safe_integer.cpp b/src/support/safe_integer.cpp
new file mode 100644
index 000000000..46057cede
--- /dev/null
+++ b/src/support/safe_integer.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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 <cmath>
+#include <limits>
+
+#include "support/safe_integer.h"
+
+using namespace wasm;
+
+bool wasm::isInteger(double x) { return fmod(x, 1) == 0; }
+
+bool wasm::isUInteger32(double x) {
+ return isInteger(x) && x >= 0 && x <= std::numeric_limits<uint32_t>::max();
+}
+
+bool wasm::isSInteger32(double x) {
+ return isInteger(x) && x >= std::numeric_limits<int32_t>::min() &&
+ x <= std::numeric_limits<int32_t>::max();
+}
+
+uint32_t wasm::toUInteger32(double x) {
+ return x < std::numeric_limits<uint32_t>::max()
+ ? x
+ : std::numeric_limits<uint32_t>::max();
+}
+
+int32_t wasm::toSInteger32(double x) {
+ return x > std::numeric_limits<int32_t>::min() &&
+ x < std::numeric_limits<int32_t>::max()
+ ? x
+ : (x < 0 ? std::numeric_limits<int32_t>::min()
+ : std::numeric_limits<int32_t>::max());
+}
+
+bool wasm::isUInteger64(double x) {
+ return isInteger(x) && x >= 0 && x <= std::numeric_limits<uint64_t>::max();
+}
+
+bool wasm::isSInteger64(double x) {
+ return isInteger(x) && x >= std::numeric_limits<int64_t>::min() &&
+ x <= std::numeric_limits<int64_t>::max();
+}
+
+uint64_t wasm::toUInteger64(double x) {
+ return x < (double)std::numeric_limits<uint64_t>::max()
+ ? (uint64_t)x
+ : std::numeric_limits<uint64_t>::max();
+}
+
+int64_t wasm::toSInteger64(double x) {
+ return x > (double)std::numeric_limits<int64_t>::min() &&
+ x < (double)std::numeric_limits<int64_t>::max()
+ ? (int64_t)x
+ : (x < 0 ? std::numeric_limits<int64_t>::min()
+ : std::numeric_limits<int64_t>::max());
+}
diff --git a/src/support/safe_integer.h b/src/support/safe_integer.h
new file mode 100644
index 000000000..f240644c8
--- /dev/null
+++ b/src/support/safe_integer.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#ifndef wasm_safe_integer_h
+#define wasm_safe_integer_h
+
+#include <cstdint>
+
+namespace wasm {
+bool isInteger(double x);
+bool isUInteger32(double x);
+bool isSInteger32(double x);
+uint32_t toUInteger32(double x);
+int32_t toSInteger32(double x);
+bool isUInteger64(double x);
+bool isSInteger64(double x);
+uint64_t toUInteger64(double x);
+int64_t toSInteger64(double x);
+} // namespace wasm
+
+#endif // wasm_safe_integer_h
diff --git a/src/wasm.h b/src/wasm.h
index b1b8d84d4..c4654580a 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -45,13 +45,14 @@
#define wasm_wasm_h
#include <cassert>
+#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <fstream>
#include <map>
-#include <vector>
#include <string>
+#include <vector>
#include "compiler-support.h"
#include "emscripten-optimizer/simple_ast.h"
@@ -200,7 +201,7 @@ struct Literal {
}
static void printDouble(std::ostream &o, double d) {
- if (d == 0 && 1/d < 0) {
+ if (d == 0 && std::signbit(d)) {
o << "-0";
return;
}
diff --git a/src/wasm2asm.h b/src/wasm2asm.h
index 53cb6df78..1a85bc177 100644
--- a/src/wasm2asm.h
+++ b/src/wasm2asm.h
@@ -22,6 +22,8 @@
#ifndef wasm_wasm2asm_h
#define wasm_wasm2asm_h
+#include <cmath>
+
#include "wasm.h"
#include "emscripten-optimizer/optimizer.h"
#include "mixed_arena.h"
@@ -914,7 +916,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Expression* curr, IString result) {
}
case f64: {
double d = curr->value.f64;
- if (d == 0 && 1/d < 0) { // negative zero
+ if (d == 0 && std::signbit(d)) { // negative zero
return ValueBuilder::makeUnary(PLUS, ValueBuilder::makeUnary(MINUS, ValueBuilder::makeDouble(0)));
}
return ValueBuilder::makeUnary(PLUS, ValueBuilder::makeDouble(curr->value.f64));
diff --git a/test/emcc_hello_world.2asm.js b/test/emcc_hello_world.2asm.js
index dbc002e3e..3dfeecd19 100644
--- a/test/emcc_hello_world.2asm.js
+++ b/test/emcc_hello_world.2asm.js
@@ -137,7 +137,7 @@ function asmFunc(global, env, buffer) {
block0 : {
$tobool1 = $x != 0.0;
if ($tobool1) block1 : {
- $mul = $x - 18446744073709551616.0;
+ $mul = $x - 18446744073709551615.0;
$call = +_frexp(+$mul, $e | 0);
$4 = HEAPU32[$e >> 2] | 0;
$sub = $4 + 4294967232 | 0;
diff --git a/test/emcc_hello_world.fromasm b/test/emcc_hello_world.fromasm
index 3b0331011..9dbe000df 100644
--- a/test/emcc_hello_world.fromasm
+++ b/test/emcc_hello_world.fromasm
@@ -430,7 +430,7 @@
(set_local $$mul
(f64.mul
(get_local $$x)
- (f64.const 18446744073709551616)
+ (f64.const 18446744073709551615)
)
)
(set_local $$call
diff --git a/test/emcc_hello_world.wast b/test/emcc_hello_world.wast
index bd0adc06f..01cd2f4c1 100644
--- a/test/emcc_hello_world.wast
+++ b/test/emcc_hello_world.wast
@@ -441,7 +441,7 @@
(set_local $$mul
(f64.mul
(get_local $$x)
- (f64.const 18446744073709551616)
+ (f64.const 18446744073709551615)
)
)
(set_local $$call
diff --git a/test/llvm_autogenerated/global.wast b/test/llvm_autogenerated/global.wast
index 09cf1a1a2..cc453456c 100644
--- a/test/llvm_autogenerated/global.wast
+++ b/test/llvm_autogenerated/global.wast
@@ -1,5 +1,5 @@
(module
- (memory 1184 4294967295 (segment 4 "9\05\00\00") (segment 20 "\01\00\00\00") (segment 24 "*\00\00\00") (segment 28 "\ff\ff\ff\ff") (segment 56 "\00\00\00\00\00\00\00\00") (segment 64 "\ff\ff\ff\ff\ff\ff\ff\ff") (segment 84 "\00\00\00\80") (segment 88 "\00\00\00@") (segment 120 "\00\00\00\00\00\00\00\00") (segment 128 "\00\00\00\00\00\00\00\00") (segment 656 "\e0\00\00\00"))
+ (memory 1184 4294967295 (segment 4 "9\05\00\00") (segment 20 "\01\00\00\00") (segment 24 "*\00\00\00") (segment 28 "\ff\ff\ff\ff") (segment 56 "\00\00\00\00\01\00\00\00") (segment 64 "\ff\ff\ff\ff\ff\ff\ff\ff") (segment 84 "\00\00\00\80") (segment 88 "\00\00\00@") (segment 120 "\00\00\00\00\00\00\00\80") (segment 128 "\00\00\00\00\00\00\00@") (segment 656 "\e0\00\00\00"))
(import $memcpy "env" "memcpy" (param i32 i32 i32))
(export "foo" $foo)
(export "call_memcpy" $call_memcpy)
diff --git a/test/passes/O.txt b/test/passes/O.txt
index dec081fd4..23b39e920 100644
--- a/test/passes/O.txt
+++ b/test/passes/O.txt
@@ -20203,7 +20203,7 @@
(call $_frexp
(f64.mul
(get_local $d1)
- (f64.const 18446744073709551616)
+ (f64.const 18446744073709551615)
)
(get_local $i5)
)
diff --git a/ubsan.blacklist b/ubsan.blacklist
new file mode 100644
index 000000000..cd23680dd
--- /dev/null
+++ b/ubsan.blacklist
@@ -0,0 +1,3 @@
+# Work around libstdc++ bug: https://llvm.org/bugs/show_bug.cgi?id=18156
+# Also see: http://lists.llvm.org/pipermail/cfe-dev/2015-January/040945.html
+src:*/ios_base.h