1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit-test/tests/jaeger/loops/multiply-by-int32min.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,59 @@ 1.4 +function foo() 1.5 +{ 1.6 + // Range analysis incorrectly computes a range for the multiplication. Once 1.7 + // that incorrect range is computed, the goal is to compute a new value whose 1.8 + // range analysis *thinks* is in int32_t range, but which goes past it using 1.9 + // JS semantics. 1.10 + // 1.11 + // On the final iteration, in JS semantics, the multiplication produces 0, and 1.12 + // the next addition 0x7fffffff. Adding any positive integer to that goes 1.13 + // past int32_t range: here, (0x7fffffff + 5) or 2147483652. 1.14 + // 1.15 + // Range analysis instead thinks the multiplication produces a value in the 1.16 + // range [INT32_MIN, INT32_MIN], and the next addition a value in the range 1.17 + // [-1, -1]. Adding any positive value to that doesn't overflow int32_t range 1.18 + // but *does* overflow the actual range in JS semantics. Thus omitting 1.19 + // overflow checks produces the value 0x80000004, which interpreting as signed 1.20 + // is (INT32_MIN + 4) or -2147483644. 1.21 + // 1.22 + // For this test to trigger the bug it was supposed to trigger: 1.23 + // 1.24 + // * 0x7fffffff must be the LHS, not RHS, of the addition in the loop, and 1.25 + // * i must not be incremented using ++ 1.26 + // 1.27 + // The first is required because JM LoopState doesn't treat *both* V + mul and 1.28 + // mul + V as not overflowing, when V is known to be int32_t -- only V + mul. 1.29 + // (JM pessimally assumes V's type might change before it's evaluated. This 1.30 + // obviously can't happen if V is a constant, but JM's puny little mind 1.31 + // doesn't detect this possibility now.) 1.32 + // 1.33 + // The second is required because JM LoopState only ignores integer overflow 1.34 + // on multiplications if the enclosing loop is a "constrainedLoop" (the name 1.35 + // of the relevant field). Loops become unconstrained when unhandled ops are 1.36 + // found in the loop. Increment operators generate a DUP op, which is not 1.37 + // presently a handled op, causing the loop to become unconstrained. 1.38 + for (var i = 0; i < 15; i = i + 1) { 1.39 + var y = (0x7fffffff + ((i & 1) * -2147483648)) + 5; 1.40 + } 1.41 + return y; 1.42 +} 1.43 +assertEq(foo(), (0x7fffffff + ((14 & 1) * -2147483648)) + 5); 1.44 + 1.45 +function bar() 1.46 +{ 1.47 + // Variation on the theme of the above test with -1 as the other half of the 1.48 + // INT32_MIN multiplication, which *should* result in -INT32_MIN on multiply 1.49 + // (exceeding int32_t range). 1.50 + // 1.51 + // Here, range analysis again thinks the range of the multiplication is 1.52 + // INT32_MIN. We'd overflow-check except that adding zero (on the LHS, see 1.53 + // above) prevents overflow checking, so range analysis thinks the range is 1.54 + // [INT32_MIN, INT32_MIN] when -INT32_MIN is actually possible. This direct 1.55 + // result of the multiplication is already out of int32_t range, so no need to 1.56 + // add anything to bias it outside int32_t range to get a wrong result. 1.57 + for (var i = 0; i < 17; i = i + 1) { 1.58 + var y = (0 + ((-1 + (i & 1)) * -2147483648)); 1.59 + } 1.60 + return y; 1.61 +} 1.62 +assertEq(bar(), (0 + ((-1 + (16 & 1)) * -2147483648)));