1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8 #include "jit/IonAnalysis.h"
9 #include "jit/Linker.h"
10 #include "jit/MacroAssembler.h"
11 #include "jit/MIRGenerator.h"
12 #include "jit/MIRGraph.h"
13 #include "jit/ValueNumbering.h"
14 #include "js/Value.h"
15
16 #include "jsapi-tests/tests.h"
17
18 #include "jit/MacroAssembler-inl.h"
19
20 using namespace js;
21 using namespace js::jit;
22
23 using mozilla::PositiveInfinity;
24 using mozilla::NegativeInfinity;
25
26 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
27
28 typedef void (*EnterTest)();
29
Prepare(MacroAssembler & masm)30 static bool Prepare(MacroAssembler& masm)
31 {
32 AllocatableRegisterSet regs(RegisterSet::Volatile());
33 LiveRegisterSet save(regs.asLiveSet());
34 masm.PushRegsInMask(save);
35 return true;
36 }
37
Execute(JSContext * cx,MacroAssembler & masm)38 static bool Execute(JSContext* cx, MacroAssembler& masm)
39 {
40 AllocatableRegisterSet regs(RegisterSet::Volatile());
41 LiveRegisterSet save(regs.asLiveSet());
42 masm.PopRegsInMask(save);
43 masm.ret(); // Add return statement to be sure.
44
45 if (masm.oom())
46 return false;
47
48 Linker linker(masm);
49 JitCode* code = linker.newCode<CanGC>(cx, OTHER_CODE);
50 if (!code)
51 return false;
52 if (!ExecutableAllocator::makeExecutable(code->raw(), code->bufferSize()))
53 return false;
54
55 EnterTest test = code->as<EnterTest>();
56 test();
57 return true;
58 }
59
BEGIN_TEST(testJitMacroAssembler_truncateDoubleToInt64)60 BEGIN_TEST(testJitMacroAssembler_truncateDoubleToInt64)
61 {
62 MacroAssembler masm(cx);
63
64 if (!Prepare(masm))
65 return false;
66
67 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
68 AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
69 FloatRegister input = allFloatRegs.takeAny();
70 #ifdef JS_NUNBOX32
71 Register64 output(allRegs.takeAny(), allRegs.takeAny());
72 #else
73 Register64 output(allRegs.takeAny());
74 #endif
75 Register temp = allRegs.takeAny();
76
77 masm.reserveStack(sizeof(int32_t));
78
79 #define TEST(INPUT, OUTPUT) \
80 { \
81 Label next; \
82 masm.loadConstantDouble(double(INPUT), input); \
83 masm.storeDouble(input, Operand(esp, 0)); \
84 masm.truncateDoubleToInt64(Address(esp, 0), Address(esp, 0), temp); \
85 masm.branch64(Assembler::Equal, Address(esp, 0), Imm64(OUTPUT), &next); \
86 masm.printf("truncateDoubleToInt64("#INPUT") failed\n"); \
87 masm.breakpoint(); \
88 masm.bind(&next); \
89 }
90
91 TEST(0, 0);
92 TEST(-0, 0);
93 TEST(1, 1);
94 TEST(9223372036854774784.0, 9223372036854774784);
95 TEST(-9223372036854775808.0, 0x8000000000000000);
96 TEST(9223372036854775808.0, 0x8000000000000000);
97 TEST(JS::GenericNaN(), 0x8000000000000000);
98 TEST(PositiveInfinity<double>(), 0x8000000000000000);
99 TEST(NegativeInfinity<double>(), 0x8000000000000000);
100 #undef TEST
101
102 masm.freeStack(sizeof(int32_t));
103
104 return Execute(cx, masm);
105 }
106 END_TEST(testJitMacroAssembler_truncateDoubleToInt64)
107
BEGIN_TEST(testJitMacroAssembler_truncateDoubleToUInt64)108 BEGIN_TEST(testJitMacroAssembler_truncateDoubleToUInt64)
109 {
110 MacroAssembler masm(cx);
111
112 if (!Prepare(masm))
113 return false;
114
115 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
116 AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
117 FloatRegister input = allFloatRegs.takeAny();
118 FloatRegister floatTemp = allFloatRegs.takeAny();
119 #ifdef JS_NUNBOX32
120 Register64 output(allRegs.takeAny(), allRegs.takeAny());
121 #else
122 Register64 output(allRegs.takeAny());
123 #endif
124 Register temp = allRegs.takeAny();
125
126 masm.reserveStack(sizeof(int32_t));
127
128 #define TEST(INPUT, OUTPUT) \
129 { \
130 Label next; \
131 masm.loadConstantDouble(double(INPUT), input); \
132 masm.storeDouble(input, Operand(esp, 0)); \
133 masm.truncateDoubleToUInt64(Address(esp, 0), Address(esp, 0), temp, floatTemp); \
134 masm.branch64(Assembler::Equal, Address(esp, 0), Imm64(OUTPUT), &next); \
135 masm.printf("truncateDoubleToUInt64("#INPUT") failed\n"); \
136 masm.breakpoint(); \
137 masm.bind(&next); \
138 }
139
140 TEST(0, 0);
141 TEST(1, 1);
142 TEST(9223372036854774784.0, 9223372036854774784);
143 TEST((uint64_t)0x8000000000000000, 0x8000000000000000);
144 TEST((uint64_t)0x8000000000000001, 0x8000000000000000);
145 TEST((uint64_t)0x8006004000000001, 0x8006004000000000);
146 TEST(-0.0, 0);
147 TEST(-0.5, 0);
148 TEST(-0.99, 0);
149 TEST(JS::GenericNaN(), 0x8000000000000000);
150 TEST(PositiveInfinity<double>(), 0x8000000000000000);
151 TEST(NegativeInfinity<double>(), 0x8000000000000000);
152 #undef TEST
153
154 masm.freeStack(sizeof(int32_t));
155
156 return Execute(cx, masm);
157 }
158 END_TEST(testJitMacroAssembler_truncateDoubleToUInt64)
159
BEGIN_TEST(testJitMacroAssembler_branchDoubleNotInInt64Range)160 BEGIN_TEST(testJitMacroAssembler_branchDoubleNotInInt64Range)
161 {
162 MacroAssembler masm(cx);
163
164 if (!Prepare(masm))
165 return false;
166
167 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
168 AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
169 FloatRegister input = allFloatRegs.takeAny();
170 #ifdef JS_NUNBOX32
171 Register64 output(allRegs.takeAny(), allRegs.takeAny());
172 #else
173 Register64 output(allRegs.takeAny());
174 #endif
175 Register temp = allRegs.takeAny();
176
177 masm.reserveStack(sizeof(int32_t));
178
179 #define TEST(INPUT, OUTPUT) \
180 { \
181 Label next; \
182 masm.loadConstantDouble(double(INPUT), input); \
183 masm.storeDouble(input, Operand(esp, 0)); \
184 if (OUTPUT) { \
185 masm.branchDoubleNotInInt64Range(Address(esp, 0), temp, &next); \
186 } else { \
187 Label fail; \
188 masm.branchDoubleNotInInt64Range(Address(esp, 0), temp, &fail); \
189 masm.jump(&next); \
190 masm.bind(&fail); \
191 } \
192 masm.printf("branchDoubleNotInInt64Range("#INPUT") failed\n"); \
193 masm.breakpoint(); \
194 masm.bind(&next); \
195 }
196
197 TEST(0, false);
198 TEST(-0, false);
199 TEST(1, false);
200 TEST(9223372036854774784.0, false);
201 TEST(-9223372036854775808.0, true);
202 TEST(9223372036854775808.0, true);
203 TEST(JS::GenericNaN(), true);
204 TEST(PositiveInfinity<double>(), true);
205 TEST(NegativeInfinity<double>(), true);
206 #undef TEST
207
208 masm.freeStack(sizeof(int32_t));
209
210 return Execute(cx, masm);
211 }
212 END_TEST(testJitMacroAssembler_branchDoubleNotInInt64Range)
213
BEGIN_TEST(testJitMacroAssembler_branchDoubleNotInUInt64Range)214 BEGIN_TEST(testJitMacroAssembler_branchDoubleNotInUInt64Range)
215 {
216 MacroAssembler masm(cx);
217
218 if (!Prepare(masm))
219 return false;
220
221 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
222 AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
223 FloatRegister input = allFloatRegs.takeAny();
224 #ifdef JS_NUNBOX32
225 Register64 output(allRegs.takeAny(), allRegs.takeAny());
226 #else
227 Register64 output(allRegs.takeAny());
228 #endif
229 Register temp = allRegs.takeAny();
230
231 masm.reserveStack(sizeof(int32_t));
232
233 #define TEST(INPUT, OUTPUT) \
234 { \
235 Label next; \
236 masm.loadConstantDouble(double(INPUT), input); \
237 masm.storeDouble(input, Operand(esp, 0)); \
238 if (OUTPUT) { \
239 masm.branchDoubleNotInUInt64Range(Address(esp, 0), temp, &next); \
240 } else { \
241 Label fail; \
242 masm.branchDoubleNotInUInt64Range(Address(esp, 0), temp, &fail); \
243 masm.jump(&next); \
244 masm.bind(&fail); \
245 } \
246 masm.printf("branchDoubleNotInUInt64Range("#INPUT") failed\n"); \
247 masm.breakpoint(); \
248 masm.bind(&next); \
249 }
250
251 TEST(0, false);
252 TEST(1, false);
253 TEST(9223372036854774784.0, false);
254 TEST((uint64_t)0x8000000000000000, false);
255 TEST((uint64_t)0x8000000000000001, false);
256 TEST((uint64_t)0x8006004000000001, false);
257 TEST(-0.0, true);
258 TEST(-0.5, true);
259 TEST(-0.99, true);
260 TEST(JS::GenericNaN(), true);
261 TEST(PositiveInfinity<double>(), true);
262 TEST(NegativeInfinity<double>(), true);
263 #undef TEST
264
265 masm.freeStack(sizeof(int32_t));
266
267 return Execute(cx, masm);
268 }
269 END_TEST(testJitMacroAssembler_branchDoubleNotInUInt64Range)
270
BEGIN_TEST(testJitMacroAssembler_lshift64)271 BEGIN_TEST(testJitMacroAssembler_lshift64)
272 {
273 MacroAssembler masm(cx);
274
275 if (!Prepare(masm))
276 return false;
277
278 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
279 AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
280 #if defined(JS_CODEGEN_X86)
281 Register shift = ecx;
282 allRegs.take(shift);
283 #elif defined(JS_CODEGEN_X64)
284 Register shift = rcx;
285 allRegs.take(shift);
286 #else
287 Register shift = allRegs.takeAny();
288 #endif
289
290 #ifdef JS_NUNBOX32
291 Register64 input(allRegs.takeAny(), allRegs.takeAny());
292 #else
293 Register64 input(allRegs.takeAny());
294 #endif
295
296 masm.reserveStack(sizeof(int32_t));
297
298 #define TEST(SHIFT, INPUT, OUTPUT) \
299 { \
300 Label next; \
301 masm.move64(Imm64(INPUT), input); \
302 masm.move32(Imm32(SHIFT), shift); \
303 masm.lshift64(shift, input); \
304 masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next); \
305 masm.printf("lshift64("#SHIFT", "#INPUT") failed\n"); \
306 masm.breakpoint(); \
307 masm.bind(&next); \
308 } \
309 { \
310 Label next; \
311 masm.move64(Imm64(INPUT), input); \
312 masm.lshift64(Imm32(SHIFT & 0x3f), input); \
313 masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next); \
314 masm.printf("lshift64(Imm32("#SHIFT"&0x3f), "#INPUT") failed\n"); \
315 masm.breakpoint(); \
316 masm.bind(&next); \
317 }
318
319 TEST(0, 1, 1);
320 TEST(1, 1, 2);
321 TEST(2, 1, 4);
322 TEST(32, 1, 0x0000000100000000);
323 TEST(33, 1, 0x0000000200000000);
324 TEST(0, -1, 0xffffffffffffffff);
325 TEST(1, -1, 0xfffffffffffffffe);
326 TEST(2, -1, 0xfffffffffffffffc);
327 TEST(32, -1, 0xffffffff00000000);
328 TEST(0xffffffff, 1, 0x8000000000000000);
329 TEST(0xfffffffe, 1, 0x4000000000000000);
330 TEST(0xfffffffd, 1, 0x2000000000000000);
331 TEST(0x80000001, 1, 2);
332 #undef TEST
333
334 masm.freeStack(sizeof(int32_t));
335
336 return Execute(cx, masm);
337 }
338 END_TEST(testJitMacroAssembler_lshift64)
339
BEGIN_TEST(testJitMacroAssembler_rshift64Arithmetic)340 BEGIN_TEST(testJitMacroAssembler_rshift64Arithmetic)
341 {
342 MacroAssembler masm(cx);
343
344 if (!Prepare(masm))
345 return false;
346
347 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
348 AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
349 #if defined(JS_CODEGEN_X86)
350 Register shift = ecx;
351 allRegs.take(shift);
352 #elif defined(JS_CODEGEN_X64)
353 Register shift = rcx;
354 allRegs.take(shift);
355 #else
356 Register shift = allRegs.takeAny();
357 #endif
358
359 #ifdef JS_NUNBOX32
360 Register64 input(allRegs.takeAny(), allRegs.takeAny());
361 #else
362 Register64 input(allRegs.takeAny());
363 #endif
364
365 masm.reserveStack(sizeof(int32_t));
366
367 #define TEST(SHIFT, INPUT, OUTPUT) \
368 { \
369 Label next; \
370 masm.move64(Imm64(INPUT), input); \
371 masm.move32(Imm32(SHIFT), shift); \
372 masm.rshift64Arithmetic(shift, input); \
373 masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next); \
374 masm.printf("rshift64Arithmetic("#SHIFT", "#INPUT") failed\n"); \
375 masm.breakpoint(); \
376 masm.bind(&next); \
377 } \
378 { \
379 Label next; \
380 masm.move64(Imm64(INPUT), input); \
381 masm.rshift64Arithmetic(Imm32(SHIFT & 0x3f), input); \
382 masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next); \
383 masm.printf("rshift64Arithmetic(Imm32("#SHIFT"&0x3f), "#INPUT") failed\n"); \
384 masm.breakpoint(); \
385 masm.bind(&next); \
386 }
387
388 TEST(0, 0x4000000000000000, 0x4000000000000000);
389 TEST(1, 0x4000000000000000, 0x2000000000000000);
390 TEST(2, 0x4000000000000000, 0x1000000000000000);
391 TEST(32, 0x4000000000000000, 0x0000000040000000);
392 TEST(0, 0x8000000000000000, 0x8000000000000000);
393 TEST(1, 0x8000000000000000, 0xc000000000000000);
394 TEST(2, 0x8000000000000000, 0xe000000000000000);
395 TEST(32, 0x8000000000000000, 0xffffffff80000000);
396 TEST(0xffffffff, 0x8000000000000000, 0xffffffffffffffff);
397 TEST(0xfffffffe, 0x8000000000000000, 0xfffffffffffffffe);
398 TEST(0xfffffffd, 0x8000000000000000, 0xfffffffffffffffc);
399 TEST(0x80000001, 0x8000000000000000, 0xc000000000000000);
400 #undef TEST
401
402 masm.freeStack(sizeof(int32_t));
403
404 return Execute(cx, masm);
405 }
406 END_TEST(testJitMacroAssembler_rshift64Arithmetic)
407
BEGIN_TEST(testJitMacroAssembler_rshift64)408 BEGIN_TEST(testJitMacroAssembler_rshift64)
409 {
410 MacroAssembler masm(cx);
411
412 if (!Prepare(masm))
413 return false;
414
415 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
416 AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
417 #if defined(JS_CODEGEN_X86)
418 Register shift = ecx;
419 allRegs.take(shift);
420 #elif defined(JS_CODEGEN_X64)
421 Register shift = rcx;
422 allRegs.take(shift);
423 #else
424 Register shift = allRegs.takeAny();
425 #endif
426
427 #ifdef JS_NUNBOX32
428 Register64 input(allRegs.takeAny(), allRegs.takeAny());
429 #else
430 Register64 input(allRegs.takeAny());
431 #endif
432
433 masm.reserveStack(sizeof(int32_t));
434
435 #define TEST(SHIFT, INPUT, OUTPUT) \
436 { \
437 Label next; \
438 masm.move64(Imm64(INPUT), input); \
439 masm.move32(Imm32(SHIFT), shift); \
440 masm.rshift64(shift, input); \
441 masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next); \
442 masm.printf("rshift64("#SHIFT", "#INPUT") failed\n"); \
443 masm.breakpoint(); \
444 masm.bind(&next); \
445 } \
446 { \
447 Label next; \
448 masm.move64(Imm64(INPUT), input); \
449 masm.rshift64(Imm32(SHIFT & 0x3f), input); \
450 masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next); \
451 masm.printf("rshift64(Imm32("#SHIFT"&0x3f), "#INPUT") failed\n"); \
452 masm.breakpoint(); \
453 masm.bind(&next); \
454 }
455
456 TEST(0, 0x4000000000000000, 0x4000000000000000);
457 TEST(1, 0x4000000000000000, 0x2000000000000000);
458 TEST(2, 0x4000000000000000, 0x1000000000000000);
459 TEST(32, 0x4000000000000000, 0x0000000040000000);
460 TEST(0, 0x8000000000000000, 0x8000000000000000);
461 TEST(1, 0x8000000000000000, 0x4000000000000000);
462 TEST(2, 0x8000000000000000, 0x2000000000000000);
463 TEST(32, 0x8000000000000000, 0x0000000080000000);
464 TEST(0xffffffff, 0x8000000000000000, 0x0000000000000001);
465 TEST(0xfffffffe, 0x8000000000000000, 0x0000000000000002);
466 TEST(0xfffffffd, 0x8000000000000000, 0x0000000000000004);
467 TEST(0x80000001, 0x8000000000000000, 0x4000000000000000);
468 #undef TEST
469
470 masm.freeStack(sizeof(int32_t));
471
472 return Execute(cx, masm);
473 }
474 END_TEST(testJitMacroAssembler_rshift64)
475
476 #endif
477