aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2013-10-29 18:38:52 -0700
committerAlon Zakai <alonzakai@gmail.com>2013-10-29 18:52:24 -0700
commit0154b1effe2cde0ad81a1adcbe83b7c9d018dbbd (patch)
tree1dee9c9ad2edc3d860dc4deebb0696376a70b1ce
parent2e50e7ca8ae5f6e18894c74fe4d33c90e404c6bc (diff)
add test for precise float32 support, allow 3 modes of precise float32 support, and rename option to PRECISE_F32 to be consistent with other precision options
-rwxr-xr-xemscripten.py4
-rw-r--r--src/parseTools.js14
-rw-r--r--src/preamble.js7
-rw-r--r--src/settings.js8
-rw-r--r--tests/test_core.py29
-rw-r--r--tools/shared.py4
6 files changed, 53 insertions, 13 deletions
diff --git a/emscripten.py b/emscripten.py
index 24e38f44..3383ca66 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -451,7 +451,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
math_envs = ['Math.min'] # TODO: move min to maths
asm_setup += '\n'.join(['var %s = %s;' % (f.replace('.', '_'), f) for f in math_envs])
- if settings['FROUND']: maths += ['Math.fround']
+ if settings['PRECISE_F32']: maths += ['Math.fround']
basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs]
if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall')
@@ -580,7 +580,7 @@ var asm = (function(global, env, buffer) {
var undef = 0;
var tempInt = 0, tempBigInt = 0, tempBigIntP = 0, tempBigIntS = 0, tempBigIntR = 0.0, tempBigIntI = 0, tempBigIntD = 0, tempValue = 0, tempDouble = 0.0;
''' + ''.join(['''
- var tempRet%d = 0;''' % i for i in range(10)]) + '\n' + asm_global_funcs] + [' var tempFloat = %s;\n' % ('Math_fround(0)' if settings.get('FROUND') else '0.0')] + ['''
+ var tempRet%d = 0;''' % i for i in range(10)]) + '\n' + asm_global_funcs] + [' var tempFloat = %s;\n' % ('Math_fround(0)' if settings.get('PRECISE_F32') else '0.0')] + ['''
// EMSCRIPTEN_START_FUNCS
function stackAlloc(size) {
size = size|0;
diff --git a/src/parseTools.js b/src/parseTools.js
index 79a88373..4dbabbb0 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -1187,7 +1187,7 @@ function ensureDot(value) {
function asmEnsureFloat(value, type) { // ensures that a float type has either 5.5 (clearly a float) or +5 (float due to asm coercion)
if (!ASM_JS) return value;
if (!isNumber(value)) return value;
- if (FROUND && type === 'float') {
+ if (PRECISE_F32 && type === 'float') {
// normally ok to just emit Math_fround(0), but if the constant is large we may need a .0 (if it can't fit in an int)
if (value == 0) return 'Math_fround(0)';
value = ensureDot(value);
@@ -1207,7 +1207,7 @@ function asmEnsureFloat(value, type) { // ensures that a float type has either 5
function asmInitializer(type) {
if (type in Runtime.FLOAT_TYPES) {
- if (FROUND && type === 'float') return 'Math_fround(0)';
+ if (PRECISE_F32 && type === 'float') return 'Math_fround(0)';
return RUNNING_JS_OPTS ? '+0' : '.0';
} else {
return '0';
@@ -1229,7 +1229,7 @@ function asmCoercion(value, type, signedness) {
value = '(' + value + ')|0';
}
}
- if (FROUND && type === 'float') {
+ if (PRECISE_F32 && type === 'float') {
return 'Math_fround(' + value + ')';
} else {
return '(+(' + value + '))';
@@ -2153,7 +2153,7 @@ function makeIsNaN(value, type) {
}
function makeFloat(value, type) {
- if (FROUND && type == 'float') {
+ if (PRECISE_F32 && type == 'float') {
return 'Math_fround(' + value + ')';
}
return value;
@@ -2502,11 +2502,11 @@ function processMathop(item) {
}
case 'sext': return idents[0];
case 'fpext': {
- if (FROUND) return '+(' + idents[0] + ')';
+ if (PRECISE_F32) return '+(' + idents[0] + ')';
return idents[0];
}
case 'fptrunc': {
- if (FROUND) return 'Math_fround(' + idents[0] + ')';
+ if (PRECISE_F32) return 'Math_fround(' + idents[0] + ')';
return idents[0];
}
case 'select': return idents[0] + '?' + asmEnsureFloat(idents[1], item.type) + ':' + asmEnsureFloat(idents[2], item.type);
@@ -2706,7 +2706,7 @@ function ensureValidFFIType(type) {
// FFI return values must arrive as doubles, and we can force them to floats afterwards
function asmFFICoercion(value, type) {
value = asmCoercion(value, ensureValidFFIType(type));
- if (FROUND && type === 'float') value = asmCoercion(value, 'float');
+ if (PRECISE_F32 && type === 'float') value = asmCoercion(value, 'float');
return value;
}
diff --git a/src/preamble.js b/src/preamble.js
index f00e59e0..8ab6d604 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -1074,8 +1074,13 @@ Math['imul'] = function(a, b) {
#endif
Math.imul = Math['imul'];
-#if FROUND
+#if PRECISE_F32
+#if PRECISE_F32 == 1
+var froundBuffer = new Float32Array(1);
+if (!Math['fround']) Math['fround'] = function(x) { froundBuffer[0] = x; return froundBuffer[0] };
+#else // 2
if (!Math['fround']) Math['fround'] = function(x) { return x };
+#endif
Math.fround = Math['fround'];
#endif
diff --git a/src/settings.js b/src/settings.js
index 3ea513cb..de9ab46c 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -115,7 +115,13 @@ var PRECISE_I64_MATH = 1; // If enabled, i64 addition etc. is emulated - which i
var PRECISE_I32_MUL = 1; // If enabled, i32 multiplication is done with full precision, which means it is
// correct even if the value exceeds the JS double-integer limit of ~52 bits (otherwise,
// rounding will occur above that range).
-var FROUND = 0; // Use Math.fround (polyfilling when necessary)
+var PRECISE_F32 = 0; // 0: Use JS numbers for floating-point values. These are 64-bit and do not model C++
+ // floats exactly, which are 32-bit.
+ // 1: Model C++ floats precisely, using Math.fround, polyfilling when necessary. This
+ // can be slow if the polyfill is used on heavy float32 computation.
+ // 2: Model C++ floats precisely using Math.fround if available in the JS engine, otherwise
+ // use an empty polyfill. This will have less of a speed penalty than using the full
+ // polyfill in cases where engine support is not present.
var CLOSURE_ANNOTATIONS = 0; // If set, the generated code will be annotated for the closure
// compiler. This potentially lets closure optimize the code better.
diff --git a/tests/test_core.py b/tests/test_core.py
index 7a53098c..f2376b53 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -882,6 +882,35 @@ nada
'''
self.do_run(src, 'OK!\n');
+ def test_float32_precise(self):
+ assert 'asm1' in test_modes
+ if self.run_name != 'asm1': return self.skip('todo')
+
+ Settings.PRECISE_F32 = 1
+
+ src = r'''
+ #include <stdio.h>
+
+ int main(int argc, char **argv) {
+ float x = 1.23456789123456789;
+ float y = 5.20456089123406709;
+ while (argc > 10 || argc % 19 == 15) {
+ // confuse optimizer
+ x /= y;
+ y = 2*y - 1;
+ argc--;
+ }
+ x = x - y;
+ y = 3*y - x/2;
+ x = x*y;
+ y += 0.000000000123123123123;
+ x -= y/7.654;
+ printf("\n%.20f, %.20f\n", x, y);
+ return 0;
+ }
+ '''
+ self.do_run(src, '\n-72.16590881347656250000, 17.59867858886718750000\n')
+
def test_negative_zero(self):
src = r'''
#include <stdio.h>
diff --git a/tools/shared.py b/tools/shared.py
index a923f88a..6ba6ef50 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -1515,7 +1515,7 @@ class JS:
settings = settings or Settings
if sig == 'i':
return '0'
- elif sig == 'f' and settings.get('FROUND'):
+ elif sig == 'f' and settings.get('PRECISE_F32'):
return 'Math_fround(0)'
else:
return '+0'
@@ -1525,7 +1525,7 @@ class JS:
settings = settings or Settings
if sig == 'i':
return value + '|0'
- elif sig == 'f' and settings.get('FROUND'):
+ elif sig == 'f' and settings.get('PRECISE_F32'):
return 'Math_fround(' + value + ')'
elif sig == 'd' or sig == 'f':
return '+' + value