[JSC] AI should not propagate AbstractValue relying on constant folding phase
[WebKit-https.git] / JSTests / stress / arith-floor-on-various-types.js
1 //@ skip if not $jitTests
2 //@ defaultNoEagerRun
3 "use strict";
4
5 let validInputTestCases = [
6     // input as string, expected result as string.
7     ["undefined", "NaN"],
8     ["null", "0"],
9     ["0", "0"],
10     ["-0.", "-0"],
11     ["0.5", "0"],
12     ["-0.5", "-1"],
13     ["4", "4"],
14     ["42.1", "42"],
15     ["42.5", "42"],
16     ["42.9", "42"],
17     ["-42.1", "-43"],
18     ["-42.5", "-43"],
19     ["-42.9", "-43"],
20     ["Math.PI", "3"],
21     ["Infinity", "Infinity"],
22     ["-Infinity", "-Infinity"],
23     ["NaN", "NaN"],
24     ["\"WebKit\"", "NaN"],
25     ["\"4\"", "4"],
26     ["\"42.5\"", "42"],
27     ["{ valueOf: () => { return 4; } }", "4"],
28     ["{ valueOf: () => { return 0; } }", "0"],
29     ["{ valueOf: () => { return -0; } }", "-0"],
30     ["{ valueOf: () => { return 0.5; } }", "0"],
31     ["{ valueOf: () => { return -0.5; } }", "-1"],
32     ["{ valueOf: () => { return Number.MIN_SAFE_INTEGER; } }", "-9007199254740991"],
33     ["{ valueOf: () => { return Number.MAX_SAFE_INTEGER; } }", "9007199254740991"],
34     ["{ valueOf: () => { return 0x80000000|0; } }", "-2147483648"],
35     ["{ valueOf: () => { return 0x7fffffff|0; } }", "2147483647"],
36     ["{ valueOf: () => { return (0x80000000|0) - 0.5; } }", "-2147483649"],
37     ["{ valueOf: () => { return (0x7fffffff|0) + 0.5; } }", "2147483647"],
38 ];
39
40 let validInputTypedTestCases = validInputTestCases.map((element) => { return [eval("(" + element[0] + ")"), eval(element[1])] });
41
42 function isIdentical(result, expected)
43 {
44     if (expected === expected) {
45         if (result !== expected)
46             return false;
47         if (!expected && (1 / expected) !== (1 / result))
48             return false;
49
50         return true;
51     }
52     return result !== result;
53 }
54
55
56 // Test Math.floor() without arguments.
57 function opaqueFloorNoArgument() {
58     return Math.floor();
59 }
60 noInline(opaqueFloorNoArgument);
61 noOSRExitFuzzing(opaqueFloorNoArgument);
62
63 function testNoArgument() {
64     for (let i = 0; i < 1e4; ++i) {
65         let output = opaqueFloorNoArgument();
66         if (!isIdentical(output, NaN)) {
67             throw "Failed opaqueFloorNoArgument";
68         }
69     }
70     if (numberOfDFGCompiles(opaqueFloorNoArgument) > 1)
71         throw "The call without arguments should never exit.";
72 }
73 testNoArgument();
74
75
76 // Test Math.floor() with a very polymorphic input. All test cases are seen at each iteration.
77 function opaqueAllTypesFloor(argument) {
78     return Math.floor(argument);
79 }
80 noInline(opaqueAllTypesFloor);
81 noOSRExitFuzzing(opaqueAllTypesFloor);
82
83 function testAllTypesCall() {
84     for (let i = 0; i < 1e3; ++i) {
85         for (let testCaseInput of validInputTypedTestCases) {
86             let output = opaqueAllTypesFloor(testCaseInput[0]);
87             if (!isIdentical(output, testCaseInput[1]))
88                 throw "Failed testAllTypesCall for input " + testCaseInput[0] + " expected " + testCaseInput[1] + " got " + output;
89         }
90     }
91     if (numberOfDFGCompiles(opaqueAllTypesFloor) > 3)
92         throw "We should have detected floor() was polymorphic and generated a generic version.";
93 }
94 testAllTypesCall();
95
96
97 // Polymorphic input but negative zero is not observable.
98 function opaqueAllTypesFloorWithoutNegativeZero(argument) {
99     return Math.floor(argument) + 0;
100 }
101 noInline(opaqueAllTypesFloorWithoutNegativeZero);
102 noOSRExitFuzzing(opaqueAllTypesFloorWithoutNegativeZero);
103
104 function testAllTypesWithoutNegativeZeroCall() {
105     for (let i = 0; i < 1e3; ++i) {
106         for (let testCaseInput of validInputTypedTestCases) {
107             let output = opaqueAllTypesFloorWithoutNegativeZero(testCaseInput[0]);
108             if (!isIdentical(output, testCaseInput[1] + 0))
109                 throw "Failed testAllTypesWithoutNegativeZeroCall for input " + testCaseInput[0] + " expected " + testCaseInput[1] + " got " + output;
110         }
111     }
112     if (numberOfDFGCompiles(opaqueAllTypesFloor) > 3)
113         throw "We should have detected floor() was polymorphic and generated a generic version.";
114 }
115 testAllTypesWithoutNegativeZeroCall();
116
117
118 // Test Math.floor() on a completely typed input. Every call see only one type.
119 function testSingleTypeCall() {
120     for (let testCaseInput of validInputTestCases) {
121         eval(`
122             function opaqueFloor(argument) {
123                 return Math.floor(argument);
124             }
125             noInline(opaqueFloor);
126             noOSRExitFuzzing(opaqueFloor);
127
128             for (let i = 0; i < 1e4; ++i) {
129                 if (!isIdentical(opaqueFloor(${testCaseInput[0]}), ${testCaseInput[1]})) {
130                     throw "Failed testSingleTypeCall()";
131                 }
132             }
133             if (numberOfDFGCompiles(opaqueFloor) > 1)
134                 throw "We should have compiled a single floor for the expected type.";
135         `);
136     }
137 }
138 testSingleTypeCall();
139
140
141 function checkCompileCountForUselessNegativeZero(testFunction)
142 {
143     if (jscOptions().useMaximalFlushInsertionPhase) {
144         // If we forced a flush after the operation, the negative zero becomes
145         // observable and we may be overly optimistic.
146         return numberOfDFGCompiles(testFunction) <= 2;
147     }
148     return numberOfDFGCompiles(testFunction) <= 1;
149 }
150
151
152 // Test Math.floor() on a completely typed input, but without negative zero.
153 function testSingleTypeWithoutNegativeZeroCall() {
154     for (let testCaseInput of validInputTestCases) {
155         eval(`
156             function opaqueFloor(argument) {
157                 return Math.floor(argument) + 0;
158             }
159             noInline(opaqueFloor);
160             noOSRExitFuzzing(opaqueFloor);
161
162             for (let i = 0; i < 1e4; ++i) {
163                 if (!isIdentical(opaqueFloor(${testCaseInput[0]}), ${testCaseInput[1]} + 0)) {
164                     throw "Failed testSingleTypeWithoutNegativeZeroCall()";
165                 }
166             }
167             if (!checkCompileCountForUselessNegativeZero(opaqueFloor))
168                 throw "We should have compiled a single floor for the expected type.";
169         `);
170     }
171 }
172 testSingleTypeWithoutNegativeZeroCall();
173
174
175 // Test Math.floor() on constants
176 function testConstant() {
177     for (let testCaseInput of validInputTestCases) {
178         eval(`
179             function opaqueFloorOnConstant() {
180                 return Math.floor(${testCaseInput[0]});
181             }
182             noInline(opaqueFloorOnConstant);
183             noOSRExitFuzzing(opaqueFloorOnConstant);
184
185             for (let i = 0; i < 1e4; ++i) {
186                 if (!isIdentical(opaqueFloorOnConstant(), ${testCaseInput[1]})) {
187                     throw "Failed testConstant()";
188                 }
189             }
190             if (numberOfDFGCompiles(opaqueFloorOnConstant) > 1)
191                 throw "We should have compiled a single floor for the expected type.";
192         `);
193     }
194 }
195 testConstant();
196
197
198 // Verify we call valueOf() exactly once per call.
199 function opaqueFloorForSideEffects(argument) {
200     return Math.floor(argument);
201 }
202 noInline(opaqueFloorForSideEffects);
203 noOSRExitFuzzing(opaqueFloorForSideEffects);
204
205 function testSideEffect() {
206     let testObject = {
207         counter: 0,
208         valueOf: function() { ++this.counter; return 16; }
209     };
210     let floor16 = Math.floor(16);
211     for (let i = 0; i < 1e4; ++i) {
212         if (opaqueFloorForSideEffects(testObject) !== floor16)
213             throw "Incorrect result in testSideEffect()";
214     }
215     if (testObject.counter !== 1e4)
216         throw "Failed testSideEffect()";
217     if (numberOfDFGCompiles(opaqueFloorForSideEffects) > 1)
218         throw "opaqueFloorForSideEffects() is predictable, it should only be compiled once.";
219 }
220 testSideEffect();
221
222
223 // Verify floor() is not subject to CSE if the argument has side effects.
224 function opaqueFloorForCSE(argument) {
225     return Math.floor(argument) + Math.floor(argument) + Math.floor(argument);
226 }
227 noInline(opaqueFloorForCSE);
228 noOSRExitFuzzing(opaqueFloorForCSE);
229
230 function testCSE() {
231     let testObject = {
232         counter: 0,
233         valueOf: function() { ++this.counter; return 16; }
234     };
235     let floor16 = Math.floor(16);
236     let threeFloor16 = floor16 + floor16 + floor16;
237     for (let i = 0; i < 1e4; ++i) {
238         if (opaqueFloorForCSE(testObject) !== threeFloor16)
239             throw "Incorrect result in testCSE()";
240     }
241     if (testObject.counter !== 3e4)
242         throw "Failed testCSE()";
243     if (numberOfDFGCompiles(opaqueFloorForCSE) > 1)
244         throw "opaqueFloorForCSE() is predictable, it should only be compiled once.";
245 }
246 testCSE();
247
248
249 // Verify floor() is not subject to DCE if the argument has side effects.
250 function opaqueFloorForDCE(argument) {
251     Math.floor(argument);
252 }
253 noInline(opaqueFloorForDCE);
254 noOSRExitFuzzing(opaqueFloorForDCE);
255
256 function testDCE() {
257     let testObject = {
258         counter: 0,
259         valueOf: function() { ++this.counter; return 16; }
260     };
261     for (let i = 0; i < 1e4; ++i) {
262         opaqueFloorForDCE(testObject);
263     }
264     if (testObject.counter !== 1e4)
265         throw "Failed testDCE()";
266     if (numberOfDFGCompiles(opaqueFloorForDCE) > 1)
267         throw "opaqueFloorForDCE() is predictable, it should only be compiled once.";
268 }
269 testDCE();
270
271
272 // Test exceptions in the argument.
273 function testException() {
274     let counter = 0;
275     function opaqueFloorWithException(argument) {
276         let result = Math.floor(argument);
277         ++counter;
278         return result;
279     }
280     noInline(opaqueFloorWithException);
281
282     let testObject = { valueOf: () => {  return 64; } };
283     let floor64 = Math.floor(64);
284
285     // Warm up without exception.
286     for (let i = 0; i < 1e3; ++i) {
287         if (opaqueFloorWithException(testObject) !== floor64)
288             throw "Incorrect result in opaqueFloorWithException()";
289     }
290
291     let testThrowObject = { valueOf: () => { throw testObject; return 64; } };
292
293     for (let i = 0; i < 1e2; ++i) {
294         try {
295             if (opaqueFloorWithException(testThrowObject) !== 8)
296                 throw "This code should not be reached!!";
297         } catch (e) {
298             if (e !== testObject) {
299                 throw "Wrong object thrown from opaqueFloorWithException."
300             }
301         }
302     }
303
304     if (counter !== 1e3) {
305         throw "Invalid count in testException()";
306     }
307 }
308 testException();