aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler.js4
-rw-r--r--src/jsifier.js20
-rw-r--r--src/parseTools.js10
-rw-r--r--src/preamble.js6
-rw-r--r--src/settings.js3
-rw-r--r--tests/freetype/ref.txt32
-rw-r--r--tests/runner.py54
7 files changed, 100 insertions, 29 deletions
diff --git a/src/compiler.js b/src/compiler.js
index e18492d5..b60f650e 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -25,10 +25,12 @@ var CONSTANTS = { 'QUANTUM_SIZE': QUANTUM_SIZE };
if (CORRECT_SIGNS === 2) {
CORRECT_SIGNS_LINES = set(CORRECT_SIGNS_LINES); // for fast checking
}
-
if (CORRECT_OVERFLOWS === 2) {
CORRECT_OVERFLOWS_LINES = set(CORRECT_OVERFLOWS_LINES); // for fast checking
}
+if (CORRECT_ROUNDINGS === 2) {
+ CORRECT_ROUNDINGS_LINES = set(CORRECT_ROUNDINGS_LINES); // for fast checking
+}
// Load compiler code
diff --git a/src/jsifier.js b/src/jsifier.js
index 0c7e56be..44f2ffe0 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -797,6 +797,16 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
return 'CHECK_OVERFLOW(' + text + ', ' + bits + ')';
}
+ function makeRounding(value, bits, signed) {
+ // C rounds to 0 (-5.5 to -5, +5.5 to 5), while JS has no direct way to do that.
+ // With 32 bits and less, and a signed value, |0 will round it like C does.
+ if (bits && bits <= 32 && signed) return '('+value+'|0)';
+ // If the value may be negative, and we care about proper rounding, then use a slow but correct function
+ if (signed && correctRoundings()) return 'cRound(' + value + ')';
+ // Either this must be positive, so Math.Floor is correct, or we don't care
+ return 'Math.floor(' + value + ')';
+ }
+
var mathop = makeFuncLineActor('mathop', function(item) { with(item) {
for (var i = 1; i <= 4; i++) {
if (item['param'+i]) {
@@ -819,11 +829,13 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
if (item.type[0] === 'i') {
bits = parseInt(item.type.substr(1));
}
+ var bitsLeft = ident2 ? ident2.substr(2, ident2.length-3) : null; // remove (i and ), to leave number. This value is important in float ops
+
switch (op) {
// basic integer ops
case 'add': return handleOverflow(ident1 + ' + ' + ident2, bits);
case 'sub': return handleOverflow(ident1 + ' - ' + ident2, bits);
- case 'sdiv': case 'udiv': return 'Math.floor(' + ident1 + ' / ' + ident2 + ')';
+ case 'sdiv': case 'udiv': return makeRounding(ident1 + '/' + ident2, bits, op[0] === 's');
case 'mul': return handleOverflow(ident1 + ' * ' + ident2, bits);
case 'urem': case 'srem': return ident1 + ' % ' + ident2;
case 'or': return ident1 + ' | ' + ident2; // TODO this forces into a 32-bit int - add overflow-style checks? also other bitops below us
@@ -851,10 +863,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
case 'fdiv': return ident1 + ' / ' + ident2;
case 'fmul': return ident1 + ' * ' + ident2;
case 'uitofp': case 'sitofp': return ident1;
- case 'fptoui': case 'fptosi': return 'Math.floor(' + ident1 + ')'; // Note that this is different than C/C++ style rounding - they
- // round -2.75 to -2 and +2.75 to +2, in other words, they
- // floor the absolute value then restore the sign. JS doesn't
- // have a fast operator to do that
+ case 'fptoui': case 'fptosi': return makeRounding(ident1, bitsLeft, op === 'fptosi');
// TODO: We sometimes generate false instead of 0, etc., in the *cmps. It seemed slightly faster before, but worth rechecking
// Note that with typed arrays, these become 0 when written. So that is a potential difference with non-typed array runs.
@@ -895,7 +904,6 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) {
// truncating can change the number, e.g. by truncating to an i1
// in order to get the first bit
assert(ident2[1] == 'i');
- var bitsLeft = ident2.substr(2, ident2.length-3); // remove (i and ), to leave number
assert(bitsLeft <= 32, 'Cannot truncate to more than 32 bits, since we use a native & op');
return '((' + ident1 + ') & ' + (Math.pow(2, bitsLeft)-1) + ')';
}
diff --git a/src/parseTools.js b/src/parseTools.js
index 8b30554d..be3a6b4c 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -564,10 +564,18 @@ function correctSigns() {
}
function correctSpecificOverflow() {
- assert(!(CORRECT_SIGNS === 2 && !Debugging.on), 'Need debugging for line-specific corrections');
+ assert(!(CORRECT_OVERFLOWS === 2 && !Debugging.on), 'Need debugging for line-specific corrections');
return CORRECT_OVERFLOWS === 2 && Debugging.getIdentifier(Framework.currItem.lineNum) in CORRECT_OVERFLOWS_LINES;
}
function correctOverflows() {
return CORRECT_OVERFLOWS === 1 || correctSpecificOverflow();
}
+function correctSpecificRounding() {
+ assert(!(CORRECT_ROUNDINGS === 2 && !Debugging.on), 'Need debugging for line-specific corrections');
+ return CORRECT_ROUNDINGS === 2 && Debugging.getIdentifier(Framework.currItem.lineNum) in CORRECT_ROUNDINGS_LINES;
+}
+function correctRoundings() {
+ return CORRECT_ROUNDINGS === 1 || correctSpecificRounding();
+}
+
diff --git a/src/preamble.js b/src/preamble.js
index 92997d20..ea0c8fa4 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -118,6 +118,12 @@ var CorrectionsMonitor = {
}
};
+#if CORRECT_ROUNDINGS
+function cRound(x) {
+ return x >= 0 ? Math.floor(x) : Math.ceil(x);
+}
+#endif
+
#if CHECK_OVERFLOWS
//========================================
// Debugging tools - Mathop overflows
diff --git a/src/settings.js b/src/settings.js
index b5be8d3d..ceda7ec8 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -64,6 +64,9 @@ CORRECT_OVERFLOWS = 1; // Experimental code that tries to prevent unexpected JS
// NOTE: You can introduce signing issues by using this option. If you
// take a large enough 32-bit value, and correct it for overflows,
// you may get a negative number, as JS & operations are signed.
+CORRECT_ROUNDINGS = 1; // C rounds to 0 (-5.5 to -5, +5.5 to 5), while JS has no direct way to do that:
+ // Math.floor is to negative, ceil to positive. With CORRECT_ROUNDINGS,
+ // we will do slow but correct C rounding operations.
SHOW_LABELS = 0; // Show labels in the generated code
diff --git a/tests/freetype/ref.txt b/tests/freetype/ref.txt
index a45fbf2b..2bd1e7bc 100644
--- a/tests/freetype/ref.txt
+++ b/tests/freetype/ref.txt
@@ -1,4 +1,4 @@
-
+
+*+
++****
++******+
@@ -23,12 +23,12 @@
+****************+ **********+
+****************+ +**********
+*****************+ **********+
- +*****************++ +*********+
- +*****************+ **********+
+ +*****************+ +*********+
+ +*****************+ +*********+
*****************+ +*********+
+***************+ +*********
+**************** +*********+
- ****************+ +*********
+ +***************+ +*********
+***************+ +*********
++++++ ****++**********+ +******+
++*********++ +*+ **********+ +***++
@@ -38,12 +38,12 @@
++**********************+ **********+ ++*****
+***********************+ +********** ++*******+
+************++********+ **********+ +***********
- +**********++ +****+ +********** +***********+
+ +**********++ +****+ +********** ***********+
*********++ +**+ +**********+ +***********
+********+ + +********** +***********+
********+ +**********+ +**********+
+********+ +********** +*********+
- +********+ +**********+ ++ *******+
+ +********+ +**********+ ++ +******+
+********+ +++******++ ***********+ +**+ +****+
+*********+ +++**************++ +************+***** +*+
+++++++ +************+*********************+ ******************+
@@ -53,35 +53,35 @@
+*******************+ ***********************************+ +***************+
+**********************+ +*********************************** +*************+
+************************+ +**********************************+ +***********+
- +++ +***********+++************+ +****************+++ +**********+ ++*******+
+ +++ +**********++++************+ +****************+++ +**********+ ++*******+
++***+ *********++ +***********+ +*********+++ +*********+ +++++
+*****+ +********+ +*********** +********+
- ******* ********* +**********+ +********
+ ******* ********* +**********+ +********+
*******+ +********+ +********** +********
- ******** ++ +******** **********+ +********+
+ +******* ++ +******** **********+ +********+
+*******+ +***+ ********* +************ +*********
+******** +*****+ ********* +**************+ +++ +*********+
+****************+ *********+ +**************++ +***+ +**********+
- +****************+ *********+ +**************++ ++*****+++++***********+
+ ****************+ *********+ +**************++ ++*****+++++***********+
***************** ********** ++**************+ +**********************+
*****************+ **********+**************+ +***********************+
+*****************+ ***********************+ **********************++
- +*****************+ *********************+ +********************+
+ +*****************+ +********************+ +********************+
+****************+ +*****************++ +*+ +*****************+
+***************+ +***************++ ++***+ ++************++
****************+ *************++ ++******+ ++*****+++
+***************+ +*********** +********+
**************** +***********+ +********+
- +***+ **********+ ************ +********
- ++ +********** +***********+ +*******+
+ +***+ **********+ +*********** +********
+ ++ +********** +***********+ +********
**********+ +***********+ ********+
+********** +************+ +********+
- +**********+ +************+ +********+
+ **********+ +************+ +********+
+********** **************+++***********
+**********+ +**************************+
+********** +************************+
+**********+ +**********************+
- **********+ +*******************+
+ +*********+ +*******************+
+**********+ +****************++
**********+ +************++
+**********+ ++*****+++
@@ -100,4 +100,4 @@
-Non-0s: 3365
+Non-0s: 3362
diff --git a/tests/runner.py b/tests/runner.py
index ba223c1c..ebd4e8ba 100644
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -168,7 +168,7 @@ class RunnerCore(unittest.TestCase):
def do_emscripten(self, filename, output_processor=None):
# Run Emscripten
exported_settings = {}
- for setting in ['QUANTUM_SIZE', 'RELOOP', 'OPTIMIZE', 'GUARD_MEMORY', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS', 'CORRECT_OVERFLOWS', 'CORRECT_SIGNS', 'CHECK_SIGNS', 'CORRECT_OVERFLOWS_LINES', 'CORRECT_SIGNS_LINES']:
+ for setting in ['QUANTUM_SIZE', 'RELOOP', 'OPTIMIZE', 'GUARD_MEMORY', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS', 'CORRECT_OVERFLOWS', 'CORRECT_SIGNS', 'CHECK_SIGNS', 'CORRECT_OVERFLOWS_LINES', 'CORRECT_SIGNS_LINES', 'CORRECT_ROUNDINGS', 'CORRECT_ROUNDINGS_LINES']:
value = eval(setting)
exported_settings[setting] = value
out = open(filename + '.o.js', 'w') if not OUTPUT_TO_SCREEN else None
@@ -1906,7 +1906,9 @@ if 'benchmark' not in sys.argv:
global CHECK_SIGNS; CHECK_SIGNS = 0
global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0
- global CORRECT_SIGNS, CORRECT_OVERFLOWS, CORRECT_SIGNS_LINES, CORRECT_OVERFLOWS_LINES
+ global CORRECT_SIGNS, CORRECT_OVERFLOWS, CORRECT_ROUNDINGS, CORRECT_SIGNS_LINES, CORRECT_OVERFLOWS_LINES, CORRECT_ROUNDINGS_LINES
+
+ # Signs
src = '''
#include <stdio.h>
@@ -1948,6 +1950,8 @@ if 'benchmark' not in sys.argv:
}
'''
+ # Overflows
+
correct = '*186854335,63*'
CORRECT_OVERFLOWS = 0
try:
@@ -1975,12 +1979,50 @@ if 'benchmark' not in sys.argv:
assert 'UNEXPECTED' not in str(e), str(e)
assert 'Expected to find' in str(e), str(e)
+ # Roundings
+
+ src = '''
+ #include <stdio.h>
+ #include <assert.h>
+
+ int main()
+ {
+ TYPE x = -5;
+ printf("*%d*", x/2);
+ x = 5;
+ printf("*%d*", x/2);
+
+ float y = -5.33;
+ x = y;
+ printf("*%d*", x);
+ y = 5.33;
+ x = y;
+ printf("*%d*", x);
+
+ printf("\\n");
+ }
+ '''
+
+ CORRECT_ROUNDINGS = 0
+ self.do_test(src.replace('TYPE', 'long long'), '*-3**2**-6**5*') # JS floor operations, always to the negative. This is an undetected error here!
+ self.do_test(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # We get these right, since they are 32-bit and we can shortcut using the |0 trick
+
+ CORRECT_ROUNDINGS = 1
+ self.do_test(src.replace('TYPE', 'long long'), '*-2**2**-5**5*') # Correct
+ self.do_test(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # Correct
+
+ CORRECT_ROUNDINGS = 2
+ CORRECT_ROUNDINGS_LINES = ["src.cpp:13"] # Fix just the last mistake
+ self.do_test(src.replace('TYPE', 'long long'), '*-3**2**-5**5*')
+ self.do_test(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # Here we are lucky and also get the first one right
+
+
# Generate tests for all our compilers
def make_test(name, compiler, llvm_opts, embetter):
exec('''
class %s(T):
def setUp(self):
- global COMPILER, QUANTUM_SIZE, RELOOP, OPTIMIZE, GUARD_MEMORY, USE_TYPED_ARRAYS, LLVM_OPTS, SAFE_HEAP, CHECK_OVERFLOWS, CORRECT_OVERFLOWS, CORRECT_OVERFLOWS_LINES, CORRECT_SIGNS, CORRECT_SIGNS_LINES, CHECK_SIGNS, COMPILER_TEST_OPTS
+ global COMPILER, QUANTUM_SIZE, RELOOP, OPTIMIZE, GUARD_MEMORY, USE_TYPED_ARRAYS, LLVM_OPTS, SAFE_HEAP, CHECK_OVERFLOWS, CORRECT_OVERFLOWS, CORRECT_OVERFLOWS_LINES, CORRECT_SIGNS, CORRECT_SIGNS_LINES, CHECK_SIGNS, COMPILER_TEST_OPTS, CORRECT_ROUNDINGS, CORRECT_ROUNDINGS_LINES
COMPILER = '%s'
QUANTUM_SIZE = %d
@@ -1993,7 +2035,8 @@ class %s(T):
CHECK_OVERFLOWS = 1-(embetter or llvm_opts)
CORRECT_OVERFLOWS = 1-(embetter and llvm_opts)
CORRECT_SIGNS = 0
- CORRECT_OVERFLOWS_LINES = CORRECT_SIGNS_LINES = []
+ CORRECT_ROUNDINGS = 0
+ CORRECT_OVERFLOWS_LINES = CORRECT_SIGNS_LINES = CORRECT_ROUNDINGS_LINES = []
CHECK_SIGNS = 0 #1-(embetter or llvm_opts)
if LLVM_OPTS:
self.pick_llvm_opts(3, True)
@@ -2031,7 +2074,8 @@ else:
USE_TYPED_ARRAYS = 0
GUARD_MEMORY = SAFE_HEAP = CHECK_OVERFLOWS = CORRECT_OVERFLOWS = CHECK_SIGNS = 0
CORRECT_SIGNS = 0
- CORRECT_OVERFLOWS_LINES = CORRECT_SIGNS_LINES = []
+ CORRECT_ROUNDINGS = 0
+ CORRECT_OVERFLOWS_LINES = CORRECT_SIGNS_LINES = CORRECT_ROUNDINGS_LINES = []
LLVM_OPTS = 1
USE_CLOSURE_COMPILER = 1