diff options
-rw-r--r-- | src/parseTools.js | 38 | ||||
-rw-r--r-- | src/preamble.js | 8 | ||||
-rw-r--r-- | tests/runner.py | 13 |
3 files changed, 40 insertions, 19 deletions
diff --git a/src/parseTools.js b/src/parseTools.js index 03a529a1..ade22214 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -541,6 +541,7 @@ function makeI64(low, high) { // Splits a number (an integer in a double, possibly > 32 bits) into an I64_MODE 1 i64 value. // Will suffer from rounding. margeI64 does the opposite. +// TODO: optimize I64 calcs function splitI64(value) { assert(I64_MODE == 1); return '(tempInt=' + value + ',' + makeI64('tempInt>>>0', 'Math.floor(tempInt/4294967296)') + ')'; @@ -1439,24 +1440,35 @@ function makeSignOp(value, type, op, force) { return value; } -function makeRounding(value, bits, signed) { +// @param floatConversion Means that we are receiving a float and rounding it to +// an integer. We must be careful here, the input has *not* +// already been converted to a signed/unsigned value (that +// would already do rounding, before us!) +function makeRounding(value, bits, signed, floatConversion) { // TODO: handle roundings of i64s - + assert(bits); // 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 + ')'; -} + if (bits <= 32 && signed) return '((' + value + ')|0)'; // This is fast and even correct, for all cases + // Do Math.floor, which is reasonably fast, if we either don't care, or if we can be sure + // the value is non-negative + if (!correctRoundings() || (!signed && !floatConversion)) return 'Math.floor(' + value + ')'; + // We are left with >32 bits signed, or a float conversion. Check and correct inline + // Note that if converting a float, we may have the wrong sign at this point! But, we have + // been rounded properly regardless, and we will be sign-corrected later when actually used, if + // necessary. + return '(tempBigInt='+value+',tempBigInt >= 0 ? Math.floor(tempBigInt) : Math.ceil(tempBigInt))'; +} + +// fptoui and fptosi are not in these, because we need to be careful about what we do there. We can't +// just sign/unsign the input first. +var UNSIGNED_OP = set('udiv', 'urem', 'uitofp', 'zext', 'lshr'); +var SIGNED_OP = set('sdiv', 'srem', 'sitofp', 'sext', 'ashr'); function isUnsignedOp(op, variant) { - return op in set('udiv', 'urem', 'uitofp', 'zext', 'lshr') || (variant && variant[0] == 'u'); + return op in UNSIGNED_OP || (variant && variant[0] == 'u'); } - function isSignedOp(op, variant) { - return op in set('sdiv', 'srem', 'sitofp', 'sext', 'ashr') || (variant && variant[0] == 's'); + return op in SIGNED_OP || (variant && variant[0] == 's'); } function processMathop(item) { with(item) { @@ -1622,7 +1634,7 @@ function processMathop(item) { with(item) { case 'fdiv': return getFastValue(ident1, '/', ident2, item.type); case 'fmul': return getFastValue(ident1, '*', ident2, item.type); case 'uitofp': case 'sitofp': return ident1; - case 'fptoui': case 'fptosi': return makeRounding(ident1, bitsLeft, op === 'fptosi'); + case 'fptoui': case 'fptosi': return makeRounding(ident1, bitsLeft, op === 'fptosi', true); // 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. diff --git a/src/preamble.js b/src/preamble.js index 9df6bf60..3fcdf1f8 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -230,12 +230,6 @@ 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 @@ -381,6 +375,8 @@ var __ATEXIT__ = []; var ABORT = false; var undef = 0; +// tempInt is used for 32-bit signed values or smaller. tempBigInt is used +// for 32-bit unsigned values or more than 32 bits. TODO: audit all uses of tempInt var tempValue, tempInt, tempBigInt, tempInt2, tempBigInt2, tempPair; #if I64_MODE == 1 var tempI64, tempI64b; diff --git a/tests/runner.py b/tests/runner.py index 9f1893ec..a808681e 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -4233,6 +4233,8 @@ Child2:9 Settings.CORRECT_SIGNS_LINES = ["src.cpp:9"] self.do_run(src, '*1*') + Settings.CORRECT_SIGNS = 0 + # Overflows src = ''' @@ -4287,6 +4289,8 @@ Child2:9 assert 'UNEXPECTED' not in str(e), str(e) assert 'Expected to find' in str(e), str(e) + Settings.CORRECT_OVERFLOWS = 0 + # Roundings src = ''' @@ -4314,21 +4318,30 @@ Child2:9 Settings.CORRECT_ROUNDINGS = 0 self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-6**5*') # JS floor operations, always to the negative. This is an undetected error here! self.do_run(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 + self.do_run(src.replace('TYPE', 'unsigned int'), '*-3**2**-6**5*') # We fail, since no fast shortcut for 32-bit unsigneds Settings.CORRECT_ROUNDINGS = 1 self.do_run(src.replace('TYPE', 'long long'), '*-2**2**-5**5*') # Correct self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # Correct + Settings.CORRECT_SIGNS = 1 # To be correct here, we need sign corrections as well + self.do_run(src.replace('TYPE', 'unsigned int'), '*2147483645**2**-5**5*') # Correct + return + Settings.CORRECT_SIGNS = 0 Settings.CORRECT_ROUNDINGS = 2 Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:13"] # Fix just the last mistake self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-5**5*') self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # Here we are lucky and also get the first one right + self.do_run(src.replace('TYPE', 'unsigned int'), '*-3**2**-5**5*') # No such luck here # And reverse the check with = 2 Settings.CORRECT_ROUNDINGS = 3 Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:999"] self.do_run(src.replace('TYPE', 'long long'), '*-2**2**-5**5*') self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') + Settings.CORRECT_SIGNS = 1 # To be correct here, we need sign corrections as well + self.do_run(src.replace('TYPE', 'unsigned int'), '*2147483645**2**-5**5*') + Settings.CORRECT_SIGNS = 0 def test_pgo(self): if Settings.USE_TYPED_ARRAYS == 2: return self.skip('LLVM opts optimize out the things we check') |