summaryrefslogtreecommitdiffstats
path: root/js/src/jsapi-tests/testJitMacroAssembler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jsapi-tests/testJitMacroAssembler.cpp')
-rw-r--r--js/src/jsapi-tests/testJitMacroAssembler.cpp816
1 files changed, 816 insertions, 0 deletions
diff --git a/js/src/jsapi-tests/testJitMacroAssembler.cpp b/js/src/jsapi-tests/testJitMacroAssembler.cpp
new file mode 100644
index 0000000000..0cfa71700f
--- /dev/null
+++ b/js/src/jsapi-tests/testJitMacroAssembler.cpp
@@ -0,0 +1,816 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jit/IonAnalysis.h"
+#include "jit/Linker.h"
+#include "jit/MacroAssembler.h"
+#include "jit/MIRGenerator.h"
+#include "jit/MIRGraph.h"
+#include "jit/ValueNumbering.h"
+#include "js/Value.h"
+
+#include "jsapi-tests/tests.h"
+#include "jsapi-tests/testsJit.h"
+
+#include "jit/MacroAssembler-inl.h"
+
+using namespace js;
+using namespace js::jit;
+
+using mozilla::NegativeInfinity;
+using mozilla::PositiveInfinity;
+
+#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
+
+BEGIN_TEST(testJitMacroAssembler_flexibleDivMod) {
+ TempAllocator tempAlloc(&cx->tempLifoAlloc());
+ JitContext jcx(cx);
+ StackMacroAssembler masm(cx, tempAlloc);
+ AutoCreatedBy acb(masm, __func__);
+
+ PrepareJit(masm);
+
+ // Test case divides 9/2;
+ const uintptr_t quotient_result = 4;
+ const uintptr_t remainder_result = 1;
+ const uintptr_t dividend = 9;
+ const uintptr_t divisor = 2;
+
+ AllocatableGeneralRegisterSet leftOutputHandSides(GeneralRegisterSet::All());
+
+ while (!leftOutputHandSides.empty()) {
+ Register lhsOutput = leftOutputHandSides.takeAny();
+
+ AllocatableGeneralRegisterSet rightHandSides(GeneralRegisterSet::All());
+ while (!rightHandSides.empty()) {
+ Register rhs = rightHandSides.takeAny();
+
+ AllocatableGeneralRegisterSet remainders(GeneralRegisterSet::All());
+ while (!remainders.empty()) {
+ Register remainderOutput = remainders.takeAny();
+ if (lhsOutput == rhs || lhsOutput == remainderOutput ||
+ rhs == remainderOutput) {
+ continue;
+ }
+
+ AllocatableRegisterSet regs(RegisterSet::Volatile());
+ LiveRegisterSet save(regs.asLiveSet());
+
+ Label next, fail;
+ masm.mov(ImmWord(dividend), lhsOutput);
+ masm.mov(ImmWord(divisor), rhs);
+ masm.flexibleDivMod32(rhs, lhsOutput, remainderOutput, false, save);
+ masm.branch32(Assembler::NotEqual, AbsoluteAddress(&quotient_result),
+ lhsOutput, &fail);
+ masm.branch32(Assembler::NotEqual, AbsoluteAddress(&remainder_result),
+ remainderOutput, &fail);
+ // Ensure RHS was not clobbered
+ masm.branch32(Assembler::NotEqual, AbsoluteAddress(&divisor), rhs,
+ &fail);
+ masm.jump(&next);
+ masm.bind(&fail);
+ masm.printf("Failed");
+ masm.breakpoint();
+
+ masm.bind(&next);
+ }
+ }
+ }
+
+ return ExecuteJit(cx, masm);
+}
+END_TEST(testJitMacroAssembler_flexibleDivMod)
+
+BEGIN_TEST(testJitMacroAssembler_flexibleRemainder) {
+ TempAllocator tempAlloc(&cx->tempLifoAlloc());
+ JitContext jcx(cx);
+ StackMacroAssembler masm(cx, tempAlloc);
+ AutoCreatedBy acb(masm, __func__);
+
+ PrepareJit(masm);
+
+ // Test case divides 9/2;
+ const uintptr_t dividend = 9;
+ const uintptr_t divisor = 2;
+ const uintptr_t remainder_result = 1;
+
+ AllocatableGeneralRegisterSet leftOutputHandSides(GeneralRegisterSet::All());
+
+ while (!leftOutputHandSides.empty()) {
+ Register lhsOutput = leftOutputHandSides.takeAny();
+
+ AllocatableGeneralRegisterSet rightHandSides(GeneralRegisterSet::All());
+ while (!rightHandSides.empty()) {
+ Register rhs = rightHandSides.takeAny();
+
+ if (lhsOutput == rhs) {
+ continue;
+ }
+
+ AllocatableRegisterSet regs(RegisterSet::Volatile());
+ LiveRegisterSet save(regs.asLiveSet());
+
+ Label next, fail;
+ masm.mov(ImmWord(dividend), lhsOutput);
+ masm.mov(ImmWord(divisor), rhs);
+ masm.flexibleRemainder32(rhs, lhsOutput, false, save);
+ masm.branch32(Assembler::NotEqual, AbsoluteAddress(&remainder_result),
+ lhsOutput, &fail);
+ // Ensure RHS was not clobbered
+ masm.branch32(Assembler::NotEqual, AbsoluteAddress(&divisor), rhs, &fail);
+ masm.jump(&next);
+ masm.bind(&fail);
+ masm.printf("Failed\n");
+ masm.breakpoint();
+
+ masm.bind(&next);
+ }
+ }
+
+ return ExecuteJit(cx, masm);
+}
+END_TEST(testJitMacroAssembler_flexibleRemainder)
+
+BEGIN_TEST(testJitMacroAssembler_flexibleQuotient) {
+ TempAllocator tempAlloc(&cx->tempLifoAlloc());
+ JitContext jcx(cx);
+ StackMacroAssembler masm(cx, tempAlloc);
+ AutoCreatedBy acb(masm, __func__);
+
+ PrepareJit(masm);
+
+ // Test case divides 9/2;
+ const uintptr_t dividend = 9;
+ const uintptr_t divisor = 2;
+ const uintptr_t quotient_result = 4;
+
+ AllocatableGeneralRegisterSet leftOutputHandSides(GeneralRegisterSet::All());
+
+ while (!leftOutputHandSides.empty()) {
+ Register lhsOutput = leftOutputHandSides.takeAny();
+
+ AllocatableGeneralRegisterSet rightHandSides(GeneralRegisterSet::All());
+ while (!rightHandSides.empty()) {
+ Register rhs = rightHandSides.takeAny();
+
+ if (lhsOutput == rhs) {
+ continue;
+ }
+
+ AllocatableRegisterSet regs(RegisterSet::Volatile());
+ LiveRegisterSet save(regs.asLiveSet());
+
+ Label next, fail;
+ masm.mov(ImmWord(dividend), lhsOutput);
+ masm.mov(ImmWord(divisor), rhs);
+ masm.flexibleQuotient32(rhs, lhsOutput, false, save);
+ masm.branch32(Assembler::NotEqual, AbsoluteAddress(&quotient_result),
+ lhsOutput, &fail);
+ // Ensure RHS was not clobbered
+ masm.branch32(Assembler::NotEqual, AbsoluteAddress(&divisor), rhs, &fail);
+ masm.jump(&next);
+ masm.bind(&fail);
+ masm.printf("Failed\n");
+ masm.breakpoint();
+
+ masm.bind(&next);
+ }
+ }
+
+ return ExecuteJit(cx, masm);
+}
+END_TEST(testJitMacroAssembler_flexibleQuotient)
+
+// To make sure ecx isn't being clobbered; globally scoped to ensure it has the
+// right lifetime.
+const uintptr_t guardEcx = 0xfeedbad;
+
+bool shiftTest(JSContext* cx, const char* name,
+ void (*operation)(StackMacroAssembler& masm, Register, Register),
+ const uintptr_t* lhsInput, const uintptr_t* rhsInput,
+ const uintptr_t* result) {
+ TempAllocator tempAlloc(&cx->tempLifoAlloc());
+ JitContext jcx(cx);
+ StackMacroAssembler masm(cx, tempAlloc);
+ AutoCreatedBy acb(masm, __func__);
+
+ PrepareJit(masm);
+
+ JS::AutoSuppressGCAnalysis suppress;
+ AllocatableGeneralRegisterSet leftOutputHandSides(GeneralRegisterSet::All());
+
+ while (!leftOutputHandSides.empty()) {
+ Register lhsOutput = leftOutputHandSides.takeAny();
+
+ AllocatableGeneralRegisterSet rightHandSides(GeneralRegisterSet::All());
+ while (!rightHandSides.empty()) {
+ Register rhs = rightHandSides.takeAny();
+
+ // You can only use shift as the same reg if the values are the same
+ if (lhsOutput == rhs && *lhsInput != *rhsInput) {
+ continue;
+ }
+
+ Label next, outputFail, clobberRhs, clobberEcx, dump;
+ masm.mov(ImmWord(guardEcx), ecx);
+ masm.mov(ImmWord(*lhsInput), lhsOutput);
+ masm.mov(ImmWord(*rhsInput), rhs);
+
+ operation(masm, rhs, lhsOutput);
+
+ // Ensure Result is correct
+ masm.branch32(Assembler::NotEqual, AbsoluteAddress(result), lhsOutput,
+ &outputFail);
+
+ // Ensure RHS was not clobbered, unless it's also the output register.
+ if (lhsOutput != rhs) {
+ masm.branch32(Assembler::NotEqual, AbsoluteAddress(rhsInput), rhs,
+ &clobberRhs);
+ }
+
+ if (lhsOutput != ecx && rhs != ecx) {
+ // If neither lhsOutput nor rhs is ecx, make sure ecx has been
+ // preserved, otherwise it's expected to be covered by the RHS clobber
+ // check above, or intentionally clobbered as the output.
+ masm.branch32(Assembler::NotEqual, AbsoluteAddress(&guardEcx), ecx,
+ &clobberEcx);
+ }
+
+ masm.jump(&next);
+
+ masm.bind(&outputFail);
+ masm.printf("Incorrect output (got %d) ", lhsOutput);
+ masm.jump(&dump);
+
+ masm.bind(&clobberRhs);
+ masm.printf("rhs clobbered %d", rhs);
+ masm.jump(&dump);
+
+ masm.bind(&clobberEcx);
+ masm.printf("ecx clobbered");
+ masm.jump(&dump);
+
+ masm.bind(&dump);
+ masm.mov(ImmPtr(lhsOutput.name()), lhsOutput);
+ masm.printf("(lhsOutput/srcDest) %s ", lhsOutput);
+ masm.mov(ImmPtr(name), lhsOutput);
+ masm.printf("%s ", lhsOutput);
+ masm.mov(ImmPtr(rhs.name()), lhsOutput);
+ masm.printf("(shift/rhs) %s \n", lhsOutput);
+ // Breakpoint to force test failure.
+ masm.breakpoint();
+ masm.bind(&next);
+ }
+ }
+
+ return ExecuteJit(cx, masm);
+}
+
+BEGIN_TEST(testJitMacroAssembler_flexibleRshift) {
+ {
+ // Test case 16 >> 2 == 4;
+ const uintptr_t lhsInput = 16;
+ const uintptr_t rhsInput = 2;
+ const uintptr_t result = 4;
+
+ bool res = shiftTest(
+ cx, "flexibleRshift32",
+ [](StackMacroAssembler& masm, Register rhs, Register lhsOutput) {
+ masm.flexibleRshift32(rhs, lhsOutput);
+ },
+ &lhsInput, &rhsInput, &result);
+ if (!res) {
+ return false;
+ }
+ }
+
+ {
+ // Test case 16 >> 16 == 0 -- this helps cover the case where the same
+ // register can be passed for source and dest.
+ const uintptr_t lhsInput = 16;
+ const uintptr_t rhsInput = 16;
+ const uintptr_t result = 0;
+
+ bool res = shiftTest(
+ cx, "flexibleRshift32",
+ [](StackMacroAssembler& masm, Register rhs, Register lhsOutput) {
+ masm.flexibleRshift32(rhs, lhsOutput);
+ },
+ &lhsInput, &rhsInput, &result);
+ if (!res) {
+ return false;
+ }
+ }
+
+ return true;
+}
+END_TEST(testJitMacroAssembler_flexibleRshift)
+
+BEGIN_TEST(testJitMacroAssembler_flexibleRshiftArithmetic) {
+ {
+ // Test case 4294967295 >> 2 == 4294967295;
+ const uintptr_t lhsInput = 4294967295;
+ const uintptr_t rhsInput = 2;
+ const uintptr_t result = 4294967295;
+ bool res = shiftTest(
+ cx, "flexibleRshift32Arithmetic",
+ [](StackMacroAssembler& masm, Register rhs, Register lhsOutput) {
+ masm.flexibleRshift32Arithmetic(rhs, lhsOutput);
+ },
+ &lhsInput, &rhsInput, &result);
+ if (!res) {
+ return false;
+ }
+ }
+
+ {
+ // Test case 16 >> 16 == 0 -- this helps cover the case where the same
+ // register can be passed for source and dest.
+ const uintptr_t lhsInput = 16;
+ const uintptr_t rhsInput = 16;
+ const uintptr_t result = 0;
+
+ bool res = shiftTest(
+ cx, "flexibleRshift32Arithmetic",
+ [](StackMacroAssembler& masm, Register rhs, Register lhsOutput) {
+ masm.flexibleRshift32Arithmetic(rhs, lhsOutput);
+ },
+ &lhsInput, &rhsInput, &result);
+ if (!res) {
+ return false;
+ }
+ }
+
+ return true;
+}
+END_TEST(testJitMacroAssembler_flexibleRshiftArithmetic)
+
+BEGIN_TEST(testJitMacroAssembler_flexibleLshift) {
+ {
+ // Test case 16 << 2 == 64;
+ const uintptr_t lhsInput = 16;
+ const uintptr_t rhsInput = 2;
+ const uintptr_t result = 64;
+
+ bool res = shiftTest(
+ cx, "flexibleLshift32",
+ [](StackMacroAssembler& masm, Register rhs, Register lhsOutput) {
+ masm.flexibleLshift32(rhs, lhsOutput);
+ },
+ &lhsInput, &rhsInput, &result);
+ if (!res) {
+ return false;
+ }
+ }
+
+ {
+ // Test case 4 << 4 == 64; duplicated input case
+ const uintptr_t lhsInput = 4;
+ const uintptr_t rhsInput = 4;
+ const uintptr_t result = 64;
+
+ bool res = shiftTest(
+ cx, "flexibleLshift32",
+ [](StackMacroAssembler& masm, Register rhs, Register lhsOutput) {
+ masm.flexibleLshift32(rhs, lhsOutput);
+ },
+ &lhsInput, &rhsInput, &result);
+ if (!res) {
+ return false;
+ }
+ }
+
+ return true;
+}
+END_TEST(testJitMacroAssembler_flexibleLshift)
+
+BEGIN_TEST(testJitMacroAssembler_truncateDoubleToInt64) {
+ TempAllocator tempAlloc(&cx->tempLifoAlloc());
+ JitContext jcx(cx);
+ StackMacroAssembler masm(cx, tempAlloc);
+ AutoCreatedBy acb(masm, __func__);
+
+ PrepareJit(masm);
+
+ AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
+ AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
+ FloatRegister input = allFloatRegs.takeAny();
+# ifdef JS_NUNBOX32
+ Register64 output(allRegs.takeAny(), allRegs.takeAny());
+# else
+ Register64 output(allRegs.takeAny());
+# endif
+ Register temp = allRegs.takeAny();
+
+ masm.reserveStack(sizeof(int32_t));
+
+# define TEST(INPUT, OUTPUT) \
+ { \
+ Label next; \
+ masm.loadConstantDouble(double(INPUT), input); \
+ masm.storeDouble(input, Operand(esp, 0)); \
+ masm.truncateDoubleToInt64(Address(esp, 0), Address(esp, 0), temp); \
+ masm.branch64(Assembler::Equal, Address(esp, 0), Imm64(OUTPUT), &next); \
+ masm.printf("truncateDoubleToInt64(" #INPUT ") failed\n"); \
+ masm.breakpoint(); \
+ masm.bind(&next); \
+ }
+
+ TEST(0, 0);
+ TEST(-0, 0);
+ TEST(1, 1);
+ TEST(9223372036854774784.0, 9223372036854774784);
+ TEST(-9223372036854775808.0, 0x8000000000000000);
+ TEST(9223372036854775808.0, 0x8000000000000000);
+ TEST(JS::GenericNaN(), 0x8000000000000000);
+ TEST(PositiveInfinity<double>(), 0x8000000000000000);
+ TEST(NegativeInfinity<double>(), 0x8000000000000000);
+# undef TEST
+
+ masm.freeStack(sizeof(int32_t));
+
+ return ExecuteJit(cx, masm);
+}
+END_TEST(testJitMacroAssembler_truncateDoubleToInt64)
+
+BEGIN_TEST(testJitMacroAssembler_truncateDoubleToUInt64) {
+ TempAllocator tempAlloc(&cx->tempLifoAlloc());
+ JitContext jcx(cx);
+ StackMacroAssembler masm(cx, tempAlloc);
+ AutoCreatedBy acb(masm, __func__);
+
+ PrepareJit(masm);
+
+ AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
+ AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
+ FloatRegister input = allFloatRegs.takeAny();
+ FloatRegister floatTemp = allFloatRegs.takeAny();
+# ifdef JS_NUNBOX32
+ Register64 output(allRegs.takeAny(), allRegs.takeAny());
+# else
+ Register64 output(allRegs.takeAny());
+# endif
+ Register temp = allRegs.takeAny();
+
+ masm.reserveStack(sizeof(int32_t));
+
+# define TEST(INPUT, OUTPUT) \
+ { \
+ Label next; \
+ masm.loadConstantDouble(double(INPUT), input); \
+ masm.storeDouble(input, Operand(esp, 0)); \
+ masm.truncateDoubleToUInt64(Address(esp, 0), Address(esp, 0), temp, \
+ floatTemp); \
+ masm.branch64(Assembler::Equal, Address(esp, 0), Imm64(OUTPUT), &next); \
+ masm.printf("truncateDoubleToUInt64(" #INPUT ") failed\n"); \
+ masm.breakpoint(); \
+ masm.bind(&next); \
+ }
+
+ TEST(0, 0);
+ TEST(1, 1);
+ TEST(9223372036854774784.0, 9223372036854774784);
+ TEST((uint64_t)0x8000000000000000, 0x8000000000000000);
+ TEST((uint64_t)0x8000000000000001, 0x8000000000000000);
+ TEST((uint64_t)0x8006004000000001, 0x8006004000000000);
+ TEST(-0.0, 0);
+ TEST(-0.5, 0);
+ TEST(-0.99, 0);
+ TEST(JS::GenericNaN(), 0x8000000000000000);
+ TEST(PositiveInfinity<double>(), 0x8000000000000000);
+ TEST(NegativeInfinity<double>(), 0x8000000000000000);
+# undef TEST
+
+ masm.freeStack(sizeof(int32_t));
+
+ return ExecuteJit(cx, masm);
+}
+END_TEST(testJitMacroAssembler_truncateDoubleToUInt64)
+
+BEGIN_TEST(testJitMacroAssembler_branchDoubleNotInInt64Range) {
+ TempAllocator tempAlloc(&cx->tempLifoAlloc());
+ JitContext jcx(cx);
+ StackMacroAssembler masm(cx, tempAlloc);
+ AutoCreatedBy acb(masm, __func__);
+
+ PrepareJit(masm);
+
+ AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
+ AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
+ FloatRegister input = allFloatRegs.takeAny();
+# ifdef JS_NUNBOX32
+ Register64 output(allRegs.takeAny(), allRegs.takeAny());
+# else
+ Register64 output(allRegs.takeAny());
+# endif
+ Register temp = allRegs.takeAny();
+
+ masm.reserveStack(sizeof(int32_t));
+
+# define TEST(INPUT, OUTPUT) \
+ { \
+ Label next; \
+ masm.loadConstantDouble(double(INPUT), input); \
+ masm.storeDouble(input, Operand(esp, 0)); \
+ if (OUTPUT) { \
+ masm.branchDoubleNotInInt64Range(Address(esp, 0), temp, &next); \
+ } else { \
+ Label fail; \
+ masm.branchDoubleNotInInt64Range(Address(esp, 0), temp, &fail); \
+ masm.jump(&next); \
+ masm.bind(&fail); \
+ } \
+ masm.printf("branchDoubleNotInInt64Range(" #INPUT ") failed\n"); \
+ masm.breakpoint(); \
+ masm.bind(&next); \
+ }
+
+ TEST(0, false);
+ TEST(-0, false);
+ TEST(1, false);
+ TEST(9223372036854774784.0, false);
+ TEST(-9223372036854775808.0, true);
+ TEST(9223372036854775808.0, true);
+ TEST(JS::GenericNaN(), true);
+ TEST(PositiveInfinity<double>(), true);
+ TEST(NegativeInfinity<double>(), true);
+# undef TEST
+
+ masm.freeStack(sizeof(int32_t));
+
+ return ExecuteJit(cx, masm);
+}
+END_TEST(testJitMacroAssembler_branchDoubleNotInInt64Range)
+
+BEGIN_TEST(testJitMacroAssembler_branchDoubleNotInUInt64Range) {
+ TempAllocator tempAlloc(&cx->tempLifoAlloc());
+ JitContext jcx(cx);
+ StackMacroAssembler masm(cx, tempAlloc);
+ AutoCreatedBy acb(masm, __func__);
+
+ PrepareJit(masm);
+
+ AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
+ AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
+ FloatRegister input = allFloatRegs.takeAny();
+# ifdef JS_NUNBOX32
+ Register64 output(allRegs.takeAny(), allRegs.takeAny());
+# else
+ Register64 output(allRegs.takeAny());
+# endif
+ Register temp = allRegs.takeAny();
+
+ masm.reserveStack(sizeof(int32_t));
+
+# define TEST(INPUT, OUTPUT) \
+ { \
+ Label next; \
+ masm.loadConstantDouble(double(INPUT), input); \
+ masm.storeDouble(input, Operand(esp, 0)); \
+ if (OUTPUT) { \
+ masm.branchDoubleNotInUInt64Range(Address(esp, 0), temp, &next); \
+ } else { \
+ Label fail; \
+ masm.branchDoubleNotInUInt64Range(Address(esp, 0), temp, &fail); \
+ masm.jump(&next); \
+ masm.bind(&fail); \
+ } \
+ masm.printf("branchDoubleNotInUInt64Range(" #INPUT ") failed\n"); \
+ masm.breakpoint(); \
+ masm.bind(&next); \
+ }
+
+ TEST(0, false);
+ TEST(1, false);
+ TEST(9223372036854774784.0, false);
+ TEST((uint64_t)0x8000000000000000, false);
+ TEST((uint64_t)0x8000000000000001, false);
+ TEST((uint64_t)0x8006004000000001, false);
+ TEST(-0.0, true);
+ TEST(-0.5, true);
+ TEST(-0.99, true);
+ TEST(JS::GenericNaN(), true);
+ TEST(PositiveInfinity<double>(), true);
+ TEST(NegativeInfinity<double>(), true);
+# undef TEST
+
+ masm.freeStack(sizeof(int32_t));
+
+ return ExecuteJit(cx, masm);
+}
+END_TEST(testJitMacroAssembler_branchDoubleNotInUInt64Range)
+
+BEGIN_TEST(testJitMacroAssembler_lshift64) {
+ TempAllocator tempAlloc(&cx->tempLifoAlloc());
+ JitContext jcx(cx);
+ StackMacroAssembler masm(cx, tempAlloc);
+ AutoCreatedBy acb(masm, __func__);
+
+ PrepareJit(masm);
+
+ AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
+ AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
+# if defined(JS_CODEGEN_X86)
+ Register shift = ecx;
+ allRegs.take(shift);
+# elif defined(JS_CODEGEN_X64)
+ Register shift = rcx;
+ allRegs.take(shift);
+# else
+ Register shift = allRegs.takeAny();
+# endif
+
+# ifdef JS_NUNBOX32
+ Register64 input(allRegs.takeAny(), allRegs.takeAny());
+# else
+ Register64 input(allRegs.takeAny());
+# endif
+
+ masm.reserveStack(sizeof(int32_t));
+
+# define TEST(SHIFT, INPUT, OUTPUT) \
+ { \
+ Label next; \
+ masm.move64(Imm64(INPUT), input); \
+ masm.move32(Imm32(SHIFT), shift); \
+ masm.lshift64(shift, input); \
+ masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next); \
+ masm.printf("lshift64(" #SHIFT ", " #INPUT ") failed\n"); \
+ masm.breakpoint(); \
+ masm.bind(&next); \
+ } \
+ { \
+ Label next; \
+ masm.move64(Imm64(INPUT), input); \
+ masm.lshift64(Imm32(SHIFT & 0x3f), input); \
+ masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next); \
+ masm.printf("lshift64(Imm32(" #SHIFT "&0x3f), " #INPUT ") failed\n"); \
+ masm.breakpoint(); \
+ masm.bind(&next); \
+ }
+
+ TEST(0, 1, 1);
+ TEST(1, 1, 2);
+ TEST(2, 1, 4);
+ TEST(32, 1, 0x0000000100000000);
+ TEST(33, 1, 0x0000000200000000);
+ TEST(0, -1, 0xffffffffffffffff);
+ TEST(1, -1, 0xfffffffffffffffe);
+ TEST(2, -1, 0xfffffffffffffffc);
+ TEST(32, -1, 0xffffffff00000000);
+ TEST(0xffffffff, 1, 0x8000000000000000);
+ TEST(0xfffffffe, 1, 0x4000000000000000);
+ TEST(0xfffffffd, 1, 0x2000000000000000);
+ TEST(0x80000001, 1, 2);
+# undef TEST
+
+ masm.freeStack(sizeof(int32_t));
+
+ return ExecuteJit(cx, masm);
+}
+END_TEST(testJitMacroAssembler_lshift64)
+
+BEGIN_TEST(testJitMacroAssembler_rshift64Arithmetic) {
+ TempAllocator tempAlloc(&cx->tempLifoAlloc());
+ JitContext jcx(cx);
+ StackMacroAssembler masm(cx, tempAlloc);
+ AutoCreatedBy acb(masm, __func__);
+
+ PrepareJit(masm);
+
+ AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
+ AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
+# if defined(JS_CODEGEN_X86)
+ Register shift = ecx;
+ allRegs.take(shift);
+# elif defined(JS_CODEGEN_X64)
+ Register shift = rcx;
+ allRegs.take(shift);
+# else
+ Register shift = allRegs.takeAny();
+# endif
+
+# ifdef JS_NUNBOX32
+ Register64 input(allRegs.takeAny(), allRegs.takeAny());
+# else
+ Register64 input(allRegs.takeAny());
+# endif
+
+ masm.reserveStack(sizeof(int32_t));
+
+# define TEST(SHIFT, INPUT, OUTPUT) \
+ { \
+ Label next; \
+ masm.move64(Imm64(INPUT), input); \
+ masm.move32(Imm32(SHIFT), shift); \
+ masm.rshift64Arithmetic(shift, input); \
+ masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next); \
+ masm.printf("rshift64Arithmetic(" #SHIFT ", " #INPUT ") failed\n"); \
+ masm.breakpoint(); \
+ masm.bind(&next); \
+ } \
+ { \
+ Label next; \
+ masm.move64(Imm64(INPUT), input); \
+ masm.rshift64Arithmetic(Imm32(SHIFT & 0x3f), input); \
+ masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next); \
+ masm.printf("rshift64Arithmetic(Imm32(" #SHIFT "&0x3f), " #INPUT \
+ ") failed\n"); \
+ masm.breakpoint(); \
+ masm.bind(&next); \
+ }
+
+ TEST(0, 0x4000000000000000, 0x4000000000000000);
+ TEST(1, 0x4000000000000000, 0x2000000000000000);
+ TEST(2, 0x4000000000000000, 0x1000000000000000);
+ TEST(32, 0x4000000000000000, 0x0000000040000000);
+ TEST(0, 0x8000000000000000, 0x8000000000000000);
+ TEST(1, 0x8000000000000000, 0xc000000000000000);
+ TEST(2, 0x8000000000000000, 0xe000000000000000);
+ TEST(32, 0x8000000000000000, 0xffffffff80000000);
+ TEST(0xffffffff, 0x8000000000000000, 0xffffffffffffffff);
+ TEST(0xfffffffe, 0x8000000000000000, 0xfffffffffffffffe);
+ TEST(0xfffffffd, 0x8000000000000000, 0xfffffffffffffffc);
+ TEST(0x80000001, 0x8000000000000000, 0xc000000000000000);
+# undef TEST
+
+ masm.freeStack(sizeof(int32_t));
+
+ return ExecuteJit(cx, masm);
+}
+END_TEST(testJitMacroAssembler_rshift64Arithmetic)
+
+BEGIN_TEST(testJitMacroAssembler_rshift64) {
+ TempAllocator tempAlloc(&cx->tempLifoAlloc());
+ JitContext jcx(cx);
+ StackMacroAssembler masm(cx, tempAlloc);
+ AutoCreatedBy acb(masm, __func__);
+
+ PrepareJit(masm);
+
+ AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
+ AllocatableFloatRegisterSet allFloatRegs(FloatRegisterSet::All());
+# if defined(JS_CODEGEN_X86)
+ Register shift = ecx;
+ allRegs.take(shift);
+# elif defined(JS_CODEGEN_X64)
+ Register shift = rcx;
+ allRegs.take(shift);
+# else
+ Register shift = allRegs.takeAny();
+# endif
+
+# ifdef JS_NUNBOX32
+ Register64 input(allRegs.takeAny(), allRegs.takeAny());
+# else
+ Register64 input(allRegs.takeAny());
+# endif
+
+ masm.reserveStack(sizeof(int32_t));
+
+# define TEST(SHIFT, INPUT, OUTPUT) \
+ { \
+ Label next; \
+ masm.move64(Imm64(INPUT), input); \
+ masm.move32(Imm32(SHIFT), shift); \
+ masm.rshift64(shift, input); \
+ masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next); \
+ masm.printf("rshift64(" #SHIFT ", " #INPUT ") failed\n"); \
+ masm.breakpoint(); \
+ masm.bind(&next); \
+ } \
+ { \
+ Label next; \
+ masm.move64(Imm64(INPUT), input); \
+ masm.rshift64(Imm32(SHIFT & 0x3f), input); \
+ masm.branch64(Assembler::Equal, input, Imm64(OUTPUT), &next); \
+ masm.printf("rshift64(Imm32(" #SHIFT "&0x3f), " #INPUT ") failed\n"); \
+ masm.breakpoint(); \
+ masm.bind(&next); \
+ }
+
+ TEST(0, 0x4000000000000000, 0x4000000000000000);
+ TEST(1, 0x4000000000000000, 0x2000000000000000);
+ TEST(2, 0x4000000000000000, 0x1000000000000000);
+ TEST(32, 0x4000000000000000, 0x0000000040000000);
+ TEST(0, 0x8000000000000000, 0x8000000000000000);
+ TEST(1, 0x8000000000000000, 0x4000000000000000);
+ TEST(2, 0x8000000000000000, 0x2000000000000000);
+ TEST(32, 0x8000000000000000, 0x0000000080000000);
+ TEST(0xffffffff, 0x8000000000000000, 0x0000000000000001);
+ TEST(0xfffffffe, 0x8000000000000000, 0x0000000000000002);
+ TEST(0xfffffffd, 0x8000000000000000, 0x0000000000000004);
+ TEST(0x80000001, 0x8000000000000000, 0x4000000000000000);
+# undef TEST
+
+ masm.freeStack(sizeof(int32_t));
+
+ return ExecuteJit(cx, masm);
+}
+END_TEST(testJitMacroAssembler_rshift64)
+
+#endif