1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
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::NegativeInfinity;
24 using mozilla::PositiveInfinity;
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   AllocatableRegisterSet regs(RegisterSet::All());
32   LiveRegisterSet save(regs.asLiveSet());
33   masm.PushRegsInMask(save);
34   return true;
35 }
36 
Execute(JSContext * cx,MacroAssembler & masm)37 static bool Execute(JSContext* cx, MacroAssembler& masm) {
38   AllocatableRegisterSet regs(RegisterSet::All());
39   LiveRegisterSet save(regs.asLiveSet());
40   masm.PopRegsInMask(save);
41   masm.ret();  // Add return statement to be sure.
42 
43   if (masm.oom()) {
44     return false;
45   }
46 
47   Linker linker(masm);
48   JitCode* code = linker.newCode(cx, CodeKind::Other);
49   if (!code) {
50     return false;
51   }
52   if (!ExecutableAllocator::makeExecutableAndFlushICache(code->raw(),
53                                                          code->bufferSize())) {
54     return false;
55   }
56 
57   JS::AutoSuppressGCAnalysis suppress;
58   EnterTest test = code->as<EnterTest>();
59   test();
60   return true;
61 }
62 
BEGIN_TEST(testJitMacroAssembler_flexibleDivMod)63 BEGIN_TEST(testJitMacroAssembler_flexibleDivMod) {
64   StackMacroAssembler masm(cx);
65 
66   if (!Prepare(masm)) {
67     return false;
68   }
69 
70   // Test case divides 9/2;
71   const uintptr_t quotient_result = 4;
72   const uintptr_t remainder_result = 1;
73   const uintptr_t dividend = 9;
74   const uintptr_t divisor = 2;
75 
76   AllocatableGeneralRegisterSet leftOutputHandSides(GeneralRegisterSet::All());
77 
78   while (!leftOutputHandSides.empty()) {
79     Register lhsOutput = leftOutputHandSides.takeAny();
80 
81     AllocatableGeneralRegisterSet rightHandSides(GeneralRegisterSet::All());
82     while (!rightHandSides.empty()) {
83       Register rhs = rightHandSides.takeAny();
84 
85       AllocatableGeneralRegisterSet remainders(GeneralRegisterSet::All());
86       while (!remainders.empty()) {
87         Register remainderOutput = remainders.takeAny();
88         if (lhsOutput == rhs || lhsOutput == remainderOutput ||
89             rhs == remainderOutput) {
90           continue;
91         }
92 
93         AllocatableRegisterSet regs(RegisterSet::Volatile());
94         LiveRegisterSet save(regs.asLiveSet());
95 
96         Label next, fail;
97         masm.mov(ImmWord(dividend), lhsOutput);
98         masm.mov(ImmWord(divisor), rhs);
99         masm.flexibleDivMod32(rhs, lhsOutput, remainderOutput, false, save);
100         masm.branch32(Assembler::NotEqual, AbsoluteAddress(&quotient_result),
101                       lhsOutput, &fail);
102         masm.branch32(Assembler::NotEqual, AbsoluteAddress(&remainder_result),
103                       remainderOutput, &fail);
104         // Ensure RHS was not clobbered
105         masm.branch32(Assembler::NotEqual, AbsoluteAddress(&divisor), rhs,
106                       &fail);
107         masm.jump(&next);
108         masm.bind(&fail);
109         masm.printf("Failed");
110         masm.breakpoint();
111 
112         masm.bind(&next);
113       }
114     }
115   }
116 
117   return Execute(cx, masm);
118 }
119 END_TEST(testJitMacroAssembler_flexibleDivMod)
120 
BEGIN_TEST(testJitMacroAssembler_flexibleRemainder)121 BEGIN_TEST(testJitMacroAssembler_flexibleRemainder) {
122   StackMacroAssembler masm(cx);
123 
124   if (!Prepare(masm)) {
125     return false;
126   }
127 
128   // Test case divides 9/2;
129   const uintptr_t dividend = 9;
130   const uintptr_t divisor = 2;
131   const uintptr_t remainder_result = 1;
132 
133   AllocatableGeneralRegisterSet leftOutputHandSides(GeneralRegisterSet::All());
134 
135   while (!leftOutputHandSides.empty()) {
136     Register lhsOutput = leftOutputHandSides.takeAny();
137 
138     AllocatableGeneralRegisterSet rightHandSides(GeneralRegisterSet::All());
139     while (!rightHandSides.empty()) {
140       Register rhs = rightHandSides.takeAny();
141 
142       if (lhsOutput == rhs) {
143         continue;
144       }
145 
146       AllocatableRegisterSet regs(RegisterSet::Volatile());
147       LiveRegisterSet save(regs.asLiveSet());
148 
149       Label next, fail;
150       masm.mov(ImmWord(dividend), lhsOutput);
151       masm.mov(ImmWord(divisor), rhs);
152       masm.flexibleRemainder32(rhs, lhsOutput, false, save);
153       masm.branch32(Assembler::NotEqual, AbsoluteAddress(&remainder_result),
154                     lhsOutput, &fail);
155       // Ensure RHS was not clobbered
156       masm.branch32(Assembler::NotEqual, AbsoluteAddress(&divisor), rhs, &fail);
157       masm.jump(&next);
158       masm.bind(&fail);
159       masm.printf("Failed\n");
160       masm.breakpoint();
161 
162       masm.bind(&next);
163     }
164   }
165 
166   return Execute(cx, masm);
167 }
168 END_TEST(testJitMacroAssembler_flexibleRemainder)
169 
BEGIN_TEST(testJitMacroAssembler_flexibleQuotient)170 BEGIN_TEST(testJitMacroAssembler_flexibleQuotient) {
171   StackMacroAssembler masm(cx);
172 
173   if (!Prepare(masm)) {
174     return false;
175   }
176 
177   // Test case divides 9/2;
178   const uintptr_t dividend = 9;
179   const uintptr_t divisor = 2;
180   const uintptr_t quotient_result = 4;
181 
182   AllocatableGeneralRegisterSet leftOutputHandSides(GeneralRegisterSet::All());
183 
184   while (!leftOutputHandSides.empty()) {
185     Register lhsOutput = leftOutputHandSides.takeAny();
186 
187     AllocatableGeneralRegisterSet rightHandSides(GeneralRegisterSet::All());
188     while (!rightHandSides.empty()) {
189       Register rhs = rightHandSides.takeAny();
190 
191       if (lhsOutput == rhs) {
192         continue;
193       }
194 
195       AllocatableRegisterSet regs(RegisterSet::Volatile());
196       LiveRegisterSet save(regs.asLiveSet());
197 
198       Label next, fail;
199       masm.mov(ImmWord(dividend), lhsOutput);
200       masm.mov(ImmWord(divisor), rhs);
201       masm.flexibleQuotient32(rhs, lhsOutput, false, save);
202       masm.branch32(Assembler::NotEqual, AbsoluteAddress(&quotient_result),
203                     lhsOutput, &fail);
204       // Ensure RHS was not clobbered
205       masm.branch32(Assembler::NotEqual, AbsoluteAddress(&divisor), rhs, &fail);
206       masm.jump(&next);
207       masm.bind(&fail);
208       masm.printf("Failed\n");
209       masm.breakpoint();
210 
211       masm.bind(&next);
212     }
213   }
214 
215   return Execute(cx, masm);
216 }
217 END_TEST(testJitMacroAssembler_flexibleQuotient)
218 
219 // To make sure ecx isn't being clobbered; globally scoped to ensure it has the
220 // right lifetime.
221 const uintptr_t guardEcx = 0xfeedbad;
222 
shiftTest(JSContext * cx,const char * name,void (* operation)(StackMacroAssembler & masm,Register,Register),const uintptr_t * lhsInput,const uintptr_t * rhsInput,const uintptr_t * result)223 bool shiftTest(JSContext* cx, const char* name,
224                void (*operation)(StackMacroAssembler& masm, Register, Register),
225                const uintptr_t* lhsInput, const uintptr_t* rhsInput,
226                const uintptr_t* result) {
227   StackMacroAssembler masm(cx);
228 
229   if (!Prepare(masm)) {
230     return false;
231   }
232 
233   JS::AutoSuppressGCAnalysis suppress;
234   AllocatableGeneralRegisterSet leftOutputHandSides(GeneralRegisterSet::All());
235 
236   while (!leftOutputHandSides.empty()) {
237     Register lhsOutput = leftOutputHandSides.takeAny();
238 
239     AllocatableGeneralRegisterSet rightHandSides(GeneralRegisterSet::All());
240     while (!rightHandSides.empty()) {
241       Register rhs = rightHandSides.takeAny();
242 
243       // You can only use shift as the same reg if the values are the same
244       if (lhsOutput == rhs && *lhsInput != *rhsInput) {
245         continue;
246       }
247 
248       Label next, outputFail, clobberRhs, clobberEcx, dump;
249       masm.mov(ImmWord(guardEcx), ecx);
250       masm.mov(ImmWord(*lhsInput), lhsOutput);
251       masm.mov(ImmWord(*rhsInput), rhs);
252 
253       operation(masm, rhs, lhsOutput);
254 
255       // Ensure Result is correct
256       masm.branch32(Assembler::NotEqual, AbsoluteAddress(result), lhsOutput,
257                     &outputFail);
258 
259       // Ensure RHS was not clobbered, unless it's also the output register.
260       if (lhsOutput != rhs) {
261         masm.branch32(Assembler::NotEqual, AbsoluteAddress(rhsInput), rhs,
262                       &clobberRhs);
263       }
264 
265       if (lhsOutput != ecx && rhs != ecx) {
266         // If neither lhsOutput nor rhs is ecx, make sure ecx has been
267         // preserved, otherwise it's expected to be covered by the RHS clobber
268         // check above, or intentionally clobbered as the output.
269         masm.branch32(Assembler::NotEqual, AbsoluteAddress(&guardEcx), ecx,
270                       &clobberEcx);
271       }
272 
273       masm.jump(&next);
274 
275       masm.bind(&outputFail);
276       masm.printf("Incorrect output (got %d) ", lhsOutput);
277       masm.jump(&dump);
278 
279       masm.bind(&clobberRhs);
280       masm.printf("rhs clobbered %d", rhs);
281       masm.jump(&dump);
282 
283       masm.bind(&clobberEcx);
284       masm.printf("ecx clobbered");
285       masm.jump(&dump);
286 
287       masm.bind(&dump);
288       masm.mov(ImmPtr(lhsOutput.name()), lhsOutput);
289       masm.printf("(lhsOutput/srcDest) %s ", lhsOutput);
290       masm.mov(ImmPtr(name), lhsOutput);
291       masm.printf("%s ", lhsOutput);
292       masm.mov(ImmPtr(rhs.name()), lhsOutput);
293       masm.printf("(shift/rhs) %s \n", lhsOutput);
294       // Breakpoint to force test failure.
295       masm.breakpoint();
296       masm.bind(&next);
297     }
298   }
299 
300   return Execute(cx, masm);
301 }
302 
BEGIN_TEST(testJitMacroAssembler_flexibleRshift)303 BEGIN_TEST(testJitMacroAssembler_flexibleRshift) {
304   {
305     // Test case  16 >> 2 == 4;
306     const uintptr_t lhsInput = 16;
307     const uintptr_t rhsInput = 2;
308     const uintptr_t result = 4;
309 
310     bool res = shiftTest(
311         cx, "flexibleRshift32",
312         [](StackMacroAssembler& masm, Register rhs, Register lhsOutput) {
313           masm.flexibleRshift32(rhs, lhsOutput);
314         },
315         &lhsInput, &rhsInput, &result);
316     if (!res) {
317       return false;
318     }
319   }
320 
321   {
322     // Test case  16 >> 16 == 0 -- this helps cover the case where the same
323     // register can be passed for source and dest.
324     const uintptr_t lhsInput = 16;
325     const uintptr_t rhsInput = 16;
326     const uintptr_t result = 0;
327 
328     bool res = shiftTest(
329         cx, "flexibleRshift32",
330         [](StackMacroAssembler& masm, Register rhs, Register lhsOutput) {
331           masm.flexibleRshift32(rhs, lhsOutput);
332         },
333         &lhsInput, &rhsInput, &result);
334     if (!res) {
335       return false;
336     }
337   }
338 
339   return true;
340 }
341 END_TEST(testJitMacroAssembler_flexibleRshift)
342 
BEGIN_TEST(testJitMacroAssembler_flexibleRshiftArithmetic)343 BEGIN_TEST(testJitMacroAssembler_flexibleRshiftArithmetic) {
344   {
345     // Test case  4294967295 >> 2 == 4294967295;
346     const uintptr_t lhsInput = 4294967295;
347     const uintptr_t rhsInput = 2;
348     const uintptr_t result = 4294967295;
349     bool res = shiftTest(
350         cx, "flexibleRshift32Arithmetic",
351         [](StackMacroAssembler& masm, Register rhs, Register lhsOutput) {
352           masm.flexibleRshift32Arithmetic(rhs, lhsOutput);
353         },
354         &lhsInput, &rhsInput, &result);
355     if (!res) {
356       return false;
357     }
358   }
359 
360   {
361     // Test case  16 >> 16 == 0 -- this helps cover the case where the same
362     // register can be passed for source and dest.
363     const uintptr_t lhsInput = 16;
364     const uintptr_t rhsInput = 16;
365     const uintptr_t result = 0;
366 
367     bool res = shiftTest(
368         cx, "flexibleRshift32Arithmetic",
369         [](StackMacroAssembler& masm, Register rhs, Register lhsOutput) {
370           masm.flexibleRshift32Arithmetic(rhs, lhsOutput);
371         },
372         &lhsInput, &rhsInput, &result);
373     if (!res) {
374       return false;
375     }
376   }
377 
378   return true;
379 }
380 END_TEST(testJitMacroAssembler_flexibleRshiftArithmetic)
381 
BEGIN_TEST(testJitMacroAssembler_flexibleLshift)382 BEGIN_TEST(testJitMacroAssembler_flexibleLshift) {
383   {
384     // Test case  16 << 2 == 64;
385     const uintptr_t lhsInput = 16;
386     const uintptr_t rhsInput = 2;
387     const uintptr_t result = 64;
388 
389     bool res = shiftTest(
390         cx, "flexibleLshift32",
391         [](StackMacroAssembler& masm, Register rhs, Register lhsOutput) {
392           masm.flexibleLshift32(rhs, lhsOutput);
393         },
394         &lhsInput, &rhsInput, &result);
395     if (!res) {
396       return false;
397     }
398   }
399 
400   {
401     // Test case  4 << 4 == 64; duplicated input case
402     const uintptr_t lhsInput = 4;
403     const uintptr_t rhsInput = 4;
404     const uintptr_t result = 64;
405 
406     bool res = shiftTest(
407         cx, "flexibleLshift32",
408         [](StackMacroAssembler& masm, Register rhs, Register lhsOutput) {
409           masm.flexibleLshift32(rhs, lhsOutput);
410         },
411         &lhsInput, &rhsInput, &result);
412     if (!res) {
413       return false;
414     }
415   }
416 
417   return true;
418 }
419 END_TEST(testJitMacroAssembler_flexibleLshift)
420 
BEGIN_TEST(testJitMacroAssembler_truncateDoubleToInt64)421 BEGIN_TEST(testJitMacroAssembler_truncateDoubleToInt64) {
422   StackMacroAssembler masm(cx);
423 
424   if (!Prepare(masm)) {
425     return false;
426   }
427 
428   AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
429   AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
430   FloatRegister input = allFloatRegs.takeAny();
431 #  ifdef JS_NUNBOX32
432   Register64 output(allRegs.takeAny(), allRegs.takeAny());
433 #  else
434   Register64 output(allRegs.takeAny());
435 #  endif
436   Register temp = allRegs.takeAny();
437 
438   masm.reserveStack(sizeof(int32_t));
439 
440 #  define TEST(INPUT, OUTPUT)                                                 \
441     {                                                                         \
442       Label next;                                                             \
443       masm.loadConstantDouble(double(INPUT), input);                          \
444       masm.storeDouble(input, Operand(esp, 0));                               \
445       masm.truncateDoubleToInt64(Address(esp, 0), Address(esp, 0), temp);     \
446       masm.branch64(Assembler::Equal, Address(esp, 0), Imm64(OUTPUT), &next); \
447       masm.printf("truncateDoubleToInt64(" #INPUT ") failed\n");              \
448       masm.breakpoint();                                                      \
449       masm.bind(&next);                                                       \
450     }
451 
452   TEST(0, 0);
453   TEST(-0, 0);
454   TEST(1, 1);
455   TEST(9223372036854774784.0, 9223372036854774784);
456   TEST(-9223372036854775808.0, 0x8000000000000000);
457   TEST(9223372036854775808.0, 0x8000000000000000);
458   TEST(JS::GenericNaN(), 0x8000000000000000);
459   TEST(PositiveInfinity<double>(), 0x8000000000000000);
460   TEST(NegativeInfinity<double>(), 0x8000000000000000);
461 #  undef TEST
462 
463   masm.freeStack(sizeof(int32_t));
464 
465   return Execute(cx, masm);
466 }
467 END_TEST(testJitMacroAssembler_truncateDoubleToInt64)
468 
BEGIN_TEST(testJitMacroAssembler_truncateDoubleToUInt64)469 BEGIN_TEST(testJitMacroAssembler_truncateDoubleToUInt64) {
470   StackMacroAssembler masm(cx);
471 
472   if (!Prepare(masm)) {
473     return false;
474   }
475 
476   AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
477   AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
478   FloatRegister input = allFloatRegs.takeAny();
479   FloatRegister floatTemp = allFloatRegs.takeAny();
480 #  ifdef JS_NUNBOX32
481   Register64 output(allRegs.takeAny(), allRegs.takeAny());
482 #  else
483   Register64 output(allRegs.takeAny());
484 #  endif
485   Register temp = allRegs.takeAny();
486 
487   masm.reserveStack(sizeof(int32_t));
488 
489 #  define TEST(INPUT, OUTPUT)                                                 \
490     {                                                                         \
491       Label next;                                                             \
492       masm.loadConstantDouble(double(INPUT), input);                          \
493       masm.storeDouble(input, Operand(esp, 0));                               \
494       masm.truncateDoubleToUInt64(Address(esp, 0), Address(esp, 0), temp,     \
495                                   floatTemp);                                 \
496       masm.branch64(Assembler::Equal, Address(esp, 0), Imm64(OUTPUT), &next); \
497       masm.printf("truncateDoubleToUInt64(" #INPUT ") failed\n");             \
498       masm.breakpoint();                                                      \
499       masm.bind(&next);                                                       \
500     }
501 
502   TEST(0, 0);
503   TEST(1, 1);
504   TEST(9223372036854774784.0, 9223372036854774784);
505   TEST((uint64_t)0x8000000000000000, 0x8000000000000000);
506   TEST((uint64_t)0x8000000000000001, 0x8000000000000000);
507   TEST((uint64_t)0x8006004000000001, 0x8006004000000000);
508   TEST(-0.0, 0);
509   TEST(-0.5, 0);
510   TEST(-0.99, 0);
511   TEST(JS::GenericNaN(), 0x8000000000000000);
512   TEST(PositiveInfinity<double>(), 0x8000000000000000);
513   TEST(NegativeInfinity<double>(), 0x8000000000000000);
514 #  undef TEST
515 
516   masm.freeStack(sizeof(int32_t));
517 
518   return Execute(cx, masm);
519 }
520 END_TEST(testJitMacroAssembler_truncateDoubleToUInt64)
521 
BEGIN_TEST(testJitMacroAssembler_branchDoubleNotInInt64Range)522 BEGIN_TEST(testJitMacroAssembler_branchDoubleNotInInt64Range) {
523   StackMacroAssembler masm(cx);
524 
525   if (!Prepare(masm)) {
526     return false;
527   }
528 
529   AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
530   AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
531   FloatRegister input = allFloatRegs.takeAny();
532 #  ifdef JS_NUNBOX32
533   Register64 output(allRegs.takeAny(), allRegs.takeAny());
534 #  else
535   Register64 output(allRegs.takeAny());
536 #  endif
537   Register temp = allRegs.takeAny();
538 
539   masm.reserveStack(sizeof(int32_t));
540 
541 #  define TEST(INPUT, OUTPUT)                                           \
542     {                                                                   \
543       Label next;                                                       \
544       masm.loadConstantDouble(double(INPUT), input);                    \
545       masm.storeDouble(input, Operand(esp, 0));                         \
546       if (OUTPUT) {                                                     \
547         masm.branchDoubleNotInInt64Range(Address(esp, 0), temp, &next); \
548       } else {                                                          \
549         Label fail;                                                     \
550         masm.branchDoubleNotInInt64Range(Address(esp, 0), temp, &fail); \
551         masm.jump(&next);                                               \
552         masm.bind(&fail);                                               \
553       }                                                                 \
554       masm.printf("branchDoubleNotInInt64Range(" #INPUT ") failed\n");  \
555       masm.breakpoint();                                                \
556       masm.bind(&next);                                                 \
557     }
558 
559   TEST(0, false);
560   TEST(-0, false);
561   TEST(1, false);
562   TEST(9223372036854774784.0, false);
563   TEST(-9223372036854775808.0, true);
564   TEST(9223372036854775808.0, true);
565   TEST(JS::GenericNaN(), true);
566   TEST(PositiveInfinity<double>(), true);
567   TEST(NegativeInfinity<double>(), true);
568 #  undef TEST
569 
570   masm.freeStack(sizeof(int32_t));
571 
572   return Execute(cx, masm);
573 }
574 END_TEST(testJitMacroAssembler_branchDoubleNotInInt64Range)
575 
BEGIN_TEST(testJitMacroAssembler_branchDoubleNotInUInt64Range)576 BEGIN_TEST(testJitMacroAssembler_branchDoubleNotInUInt64Range) {
577   StackMacroAssembler masm(cx);
578 
579   if (!Prepare(masm)) {
580     return false;
581   }
582 
583   AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
584   AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
585   FloatRegister input = allFloatRegs.takeAny();
586 #  ifdef JS_NUNBOX32
587   Register64 output(allRegs.takeAny(), allRegs.takeAny());
588 #  else
589   Register64 output(allRegs.takeAny());
590 #  endif
591   Register temp = allRegs.takeAny();
592 
593   masm.reserveStack(sizeof(int32_t));
594 
595 #  define TEST(INPUT, OUTPUT)                                            \
596     {                                                                    \
597       Label next;                                                        \
598       masm.loadConstantDouble(double(INPUT), input);                     \
599       masm.storeDouble(input, Operand(esp, 0));                          \
600       if (OUTPUT) {                                                      \
601         masm.branchDoubleNotInUInt64Range(Address(esp, 0), temp, &next); \
602       } else {                                                           \
603         Label fail;                                                      \
604         masm.branchDoubleNotInUInt64Range(Address(esp, 0), temp, &fail); \
605         masm.jump(&next);                                                \
606         masm.bind(&fail);                                                \
607       }                                                                  \
608       masm.printf("branchDoubleNotInUInt64Range(" #INPUT ") failed\n");  \
609       masm.breakpoint();                                                 \
610       masm.bind(&next);                                                  \
611     }
612 
613   TEST(0, false);
614   TEST(1, false);
615   TEST(9223372036854774784.0, false);
616   TEST((uint64_t)0x8000000000000000, false);
617   TEST((uint64_t)0x8000000000000001, false);
618   TEST((uint64_t)0x8006004000000001, false);
619   TEST(-0.0, true);
620   TEST(-0.5, true);
621   TEST(-0.99, true);
622   TEST(JS::GenericNaN(), true);
623   TEST(PositiveInfinity<double>(), true);
624   TEST(NegativeInfinity<double>(), true);
625 #  undef TEST
626 
627   masm.freeStack(sizeof(int32_t));
628 
629   return Execute(cx, masm);
630 }
631 END_TEST(testJitMacroAssembler_branchDoubleNotInUInt64Range)
632 
BEGIN_TEST(testJitMacroAssembler_lshift64)633 BEGIN_TEST(testJitMacroAssembler_lshift64) {
634   StackMacroAssembler masm(cx);
635 
636   if (!Prepare(masm)) {
637     return false;
638   }
639 
640   AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
641   AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
642 #  if defined(JS_CODEGEN_X86)
643   Register shift = ecx;
644   allRegs.take(shift);
645 #  elif defined(JS_CODEGEN_X64)
646   Register shift = rcx;
647   allRegs.take(shift);
648 #  else
649   Register shift = allRegs.takeAny();
650 #  endif
651 
652 #  ifdef JS_NUNBOX32
653   Register64 input(allRegs.takeAny(), allRegs.takeAny());
654 #  else
655   Register64 input(allRegs.takeAny());
656 #  endif
657 
658   masm.reserveStack(sizeof(int32_t));
659 
660 #  define TEST(SHIFT, INPUT, OUTPUT)                                        \
661     {                                                                       \
662       Label next;                                                           \
663       masm.move64(Imm64(INPUT), input);                                     \
664       masm.move32(Imm32(SHIFT), shift);                                     \
665       masm.lshift64(shift, input);                                          \
666       masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next);         \
667       masm.printf("lshift64(" #SHIFT ", " #INPUT ") failed\n");             \
668       masm.breakpoint();                                                    \
669       masm.bind(&next);                                                     \
670     }                                                                       \
671     {                                                                       \
672       Label next;                                                           \
673       masm.move64(Imm64(INPUT), input);                                     \
674       masm.lshift64(Imm32(SHIFT & 0x3f), input);                            \
675       masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next);         \
676       masm.printf("lshift64(Imm32(" #SHIFT "&0x3f), " #INPUT ") failed\n"); \
677       masm.breakpoint();                                                    \
678       masm.bind(&next);                                                     \
679     }
680 
681   TEST(0, 1, 1);
682   TEST(1, 1, 2);
683   TEST(2, 1, 4);
684   TEST(32, 1, 0x0000000100000000);
685   TEST(33, 1, 0x0000000200000000);
686   TEST(0, -1, 0xffffffffffffffff);
687   TEST(1, -1, 0xfffffffffffffffe);
688   TEST(2, -1, 0xfffffffffffffffc);
689   TEST(32, -1, 0xffffffff00000000);
690   TEST(0xffffffff, 1, 0x8000000000000000);
691   TEST(0xfffffffe, 1, 0x4000000000000000);
692   TEST(0xfffffffd, 1, 0x2000000000000000);
693   TEST(0x80000001, 1, 2);
694 #  undef TEST
695 
696   masm.freeStack(sizeof(int32_t));
697 
698   return Execute(cx, masm);
699 }
700 END_TEST(testJitMacroAssembler_lshift64)
701 
BEGIN_TEST(testJitMacroAssembler_rshift64Arithmetic)702 BEGIN_TEST(testJitMacroAssembler_rshift64Arithmetic) {
703   StackMacroAssembler masm(cx);
704 
705   if (!Prepare(masm)) {
706     return false;
707   }
708 
709   AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
710   AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
711 #  if defined(JS_CODEGEN_X86)
712   Register shift = ecx;
713   allRegs.take(shift);
714 #  elif defined(JS_CODEGEN_X64)
715   Register shift = rcx;
716   allRegs.take(shift);
717 #  else
718   Register shift = allRegs.takeAny();
719 #  endif
720 
721 #  ifdef JS_NUNBOX32
722   Register64 input(allRegs.takeAny(), allRegs.takeAny());
723 #  else
724   Register64 input(allRegs.takeAny());
725 #  endif
726 
727   masm.reserveStack(sizeof(int32_t));
728 
729 #  define TEST(SHIFT, INPUT, OUTPUT)                                      \
730     {                                                                     \
731       Label next;                                                         \
732       masm.move64(Imm64(INPUT), input);                                   \
733       masm.move32(Imm32(SHIFT), shift);                                   \
734       masm.rshift64Arithmetic(shift, input);                              \
735       masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next);       \
736       masm.printf("rshift64Arithmetic(" #SHIFT ", " #INPUT ") failed\n"); \
737       masm.breakpoint();                                                  \
738       masm.bind(&next);                                                   \
739     }                                                                     \
740     {                                                                     \
741       Label next;                                                         \
742       masm.move64(Imm64(INPUT), input);                                   \
743       masm.rshift64Arithmetic(Imm32(SHIFT & 0x3f), input);                \
744       masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next);       \
745       masm.printf("rshift64Arithmetic(Imm32(" #SHIFT "&0x3f), " #INPUT    \
746                   ") failed\n");                                          \
747       masm.breakpoint();                                                  \
748       masm.bind(&next);                                                   \
749     }
750 
751   TEST(0, 0x4000000000000000, 0x4000000000000000);
752   TEST(1, 0x4000000000000000, 0x2000000000000000);
753   TEST(2, 0x4000000000000000, 0x1000000000000000);
754   TEST(32, 0x4000000000000000, 0x0000000040000000);
755   TEST(0, 0x8000000000000000, 0x8000000000000000);
756   TEST(1, 0x8000000000000000, 0xc000000000000000);
757   TEST(2, 0x8000000000000000, 0xe000000000000000);
758   TEST(32, 0x8000000000000000, 0xffffffff80000000);
759   TEST(0xffffffff, 0x8000000000000000, 0xffffffffffffffff);
760   TEST(0xfffffffe, 0x8000000000000000, 0xfffffffffffffffe);
761   TEST(0xfffffffd, 0x8000000000000000, 0xfffffffffffffffc);
762   TEST(0x80000001, 0x8000000000000000, 0xc000000000000000);
763 #  undef TEST
764 
765   masm.freeStack(sizeof(int32_t));
766 
767   return Execute(cx, masm);
768 }
769 END_TEST(testJitMacroAssembler_rshift64Arithmetic)
770 
BEGIN_TEST(testJitMacroAssembler_rshift64)771 BEGIN_TEST(testJitMacroAssembler_rshift64) {
772   StackMacroAssembler masm(cx);
773 
774   if (!Prepare(masm)) {
775     return false;
776   }
777 
778   AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
779   AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
780 #  if defined(JS_CODEGEN_X86)
781   Register shift = ecx;
782   allRegs.take(shift);
783 #  elif defined(JS_CODEGEN_X64)
784   Register shift = rcx;
785   allRegs.take(shift);
786 #  else
787   Register shift = allRegs.takeAny();
788 #  endif
789 
790 #  ifdef JS_NUNBOX32
791   Register64 input(allRegs.takeAny(), allRegs.takeAny());
792 #  else
793   Register64 input(allRegs.takeAny());
794 #  endif
795 
796   masm.reserveStack(sizeof(int32_t));
797 
798 #  define TEST(SHIFT, INPUT, OUTPUT)                                        \
799     {                                                                       \
800       Label next;                                                           \
801       masm.move64(Imm64(INPUT), input);                                     \
802       masm.move32(Imm32(SHIFT), shift);                                     \
803       masm.rshift64(shift, input);                                          \
804       masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next);         \
805       masm.printf("rshift64(" #SHIFT ", " #INPUT ") failed\n");             \
806       masm.breakpoint();                                                    \
807       masm.bind(&next);                                                     \
808     }                                                                       \
809     {                                                                       \
810       Label next;                                                           \
811       masm.move64(Imm64(INPUT), input);                                     \
812       masm.rshift64(Imm32(SHIFT & 0x3f), input);                            \
813       masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next);         \
814       masm.printf("rshift64(Imm32(" #SHIFT "&0x3f), " #INPUT ") failed\n"); \
815       masm.breakpoint();                                                    \
816       masm.bind(&next);                                                     \
817     }
818 
819   TEST(0, 0x4000000000000000, 0x4000000000000000);
820   TEST(1, 0x4000000000000000, 0x2000000000000000);
821   TEST(2, 0x4000000000000000, 0x1000000000000000);
822   TEST(32, 0x4000000000000000, 0x0000000040000000);
823   TEST(0, 0x8000000000000000, 0x8000000000000000);
824   TEST(1, 0x8000000000000000, 0x4000000000000000);
825   TEST(2, 0x8000000000000000, 0x2000000000000000);
826   TEST(32, 0x8000000000000000, 0x0000000080000000);
827   TEST(0xffffffff, 0x8000000000000000, 0x0000000000000001);
828   TEST(0xfffffffe, 0x8000000000000000, 0x0000000000000002);
829   TEST(0xfffffffd, 0x8000000000000000, 0x0000000000000004);
830   TEST(0x80000001, 0x8000000000000000, 0x4000000000000000);
831 #  undef TEST
832 
833   masm.freeStack(sizeof(int32_t));
834 
835   return Execute(cx, masm);
836 }
837 END_TEST(testJitMacroAssembler_rshift64)
838 
839 #endif
840