pow-base-power-of-two-bailouts.js (2754B)
1 // Lowering provides a specialisation when the base operand is a constant which 2 // is a power of two. 3 // 4 // Test bailout conditions for this optimisation. 5 6 function test(x) { 7 function pow(x, y) { return `Math.pow(${x}, ${y})` }; 8 function exp(x, y) { return `((${x}) ** ${y})` }; 9 10 function make(fn) { 11 return Function("y, z", ` 12 // Load from array to prevent constant-folding. 13 // (Ion is currently not smart enough to realise that both array 14 // values are the same.) 15 var ys = [y, y]; 16 var zs = [z, z]; 17 for (var i = 0; i < 1000; ++i) { 18 assertEq(${fn(x, "ys[i & 1]")}, zs[i & 1]); 19 } 20 `); 21 } 22 23 function double(v) { 24 // NB: numberToDouble() always returns a double value. 25 return numberToDouble(v); 26 } 27 28 // Find the first power which will exceed the Int32 range by computing ⌈log_x(2 ^ 31)⌉. 29 var limit = Math.ceil(Math.log2(2 ** 31) / Math.log2(x)); 30 assertEq(Math.pow(x, limit - 1) < 2 ** 31, true); 31 assertEq(Math.pow(x, limit) >= 2 ** 31, true); 32 33 function* args(first, last) { 34 // Run the test function a few times without a bailout. 35 for (var i = 0; i < 3; ++i) { 36 yield first; 37 } 38 39 // |last| should trigger a bailout. 40 yield last; 41 } 42 43 // Test precision loss when the result exceeds 2**31. 44 for (var fn of [make(pow), make(exp)]) { 45 for (var y of args(limit - 1, limit)) { 46 // Ensure the callee always sees a double to avoid an early Bailout_ArgumentCheck. 47 var z = double(Math.pow(x, y)); 48 fn(y, z); 49 } 50 } 51 52 // Test precision loss when the result is a fractional number. 53 for (var fn of [make(pow), make(exp)]) { 54 for (var y of args(0, -1)) { 55 // Ensure the callee always sees a double to avoid an early Bailout_ArgumentCheck. 56 var z = double(Math.pow(x, y)); 57 fn(y, z); 58 } 59 } 60 61 // Find the first negative power which can be represented as a double 62 var negLimit = -Math.floor(1074 / Math.log2(x)); 63 64 // Test precision loss when the result is a non-zero, fractional number. 65 for (var fn of [make(pow), make(exp)]) { 66 for (var y of args(limit - 1, limit)) { 67 // Ensure the callee always sees a double to avoid an early Bailout_ArgumentCheck. 68 var z = double(Math.pow(x, y)); 69 fn(y, z); 70 } 71 } 72 } 73 74 function* range(a, b, fn) { 75 for (var i = a; i <= b; ++i) { 76 yield fn(i); 77 } 78 } 79 80 // Only 2^i with |i| ∈ {1..8} currently triggers the optimisation, but also test 81 // the next power-of-two values. 82 83 for (var x of range(1, 10, i => 2 ** i)) { 84 test(x); 85 }