diff options
-rw-r--r-- | src/compiler.js | 4 | ||||
-rw-r--r-- | src/jsifier.js | 20 | ||||
-rw-r--r-- | src/parseTools.js | 10 | ||||
-rw-r--r-- | src/preamble.js | 6 | ||||
-rw-r--r-- | src/settings.js | 3 | ||||
-rw-r--r-- | tests/freetype/ref.txt | 32 | ||||
-rw-r--r-- | tests/runner.py | 54 |
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 |