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