tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

testJitMacroAssembler.cpp (31779B)


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