Bug 774364 - Part 1: Inline Math.random() in Ion on x86_64. r=sstangl

This commit is contained in:
Tooru Fujisawa 2015-06-02 17:28:32 +09:00
parent 093be883f1
commit 145f71713c
32 changed files with 345 additions and 52 deletions

View File

@ -843,10 +843,6 @@ HasChild(JSContext* cx, unsigned argc, jsval* vp)
return true;
}
// Stolen from jsmath.cpp
static const uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL;
static const uint64_t RNG_MASK = (1LL << 48) - 1;
static bool
SetSavedStacksRNGState(JSContext* cx, unsigned argc, jsval* vp)
{

View File

@ -5155,21 +5155,6 @@ CodeGenerator::visitPowD(LPowD* ins)
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
}
void
CodeGenerator::visitRandom(LRandom* ins)
{
Register temp = ToRegister(ins->temp());
Register temp2 = ToRegister(ins->temp2());
masm.loadJSContext(temp);
masm.setupUnalignedABICall(1, temp2);
masm.passABIArg(temp);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
}
void
CodeGenerator::visitMathFunctionD(LMathFunctionD* ins)
{

View File

@ -215,7 +215,6 @@ class CodeGenerator : public CodeGeneratorSpecific
void visitHypot(LHypot* lir);
void visitPowI(LPowI* lir);
void visitPowD(LPowD* lir);
void visitRandom(LRandom* lir);
void visitMathFunctionD(LMathFunctionD* ins);
void visitMathFunctionF(LMathFunctionF* ins);
void visitModD(LModD* ins);

View File

@ -3126,23 +3126,6 @@ class LPowD : public LCallInstructionHelper<1, 2, 1>
}
};
// Math.random().
class LRandom : public LCallInstructionHelper<1, 0, 2>
{
public:
LIR_HEADER(Random)
LRandom(const LDefinition& temp, const LDefinition& temp2) {
setTemp(0, temp);
setTemp(1, temp2);
}
const LDefinition* temp() {
return getTemp(0);
}
const LDefinition* temp2() {
return getTemp(1);
}
};
class LMathFunctionD : public LCallInstructionHelper<1, 1, 1>
{
public:

View File

@ -1399,13 +1399,6 @@ LIRGenerator::visitPow(MPow* ins)
defineReturn(lir, ins);
}
void
LIRGenerator::visitRandom(MRandom* ins)
{
LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
defineReturn(lir, ins);
}
void
LIRGenerator::visitMathFunction(MMathFunction* ins)
{

View File

@ -130,7 +130,6 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitAtan2(MAtan2* ins);
void visitHypot(MHypot* ins);
void visitPow(MPow* ins);
void visitRandom(MRandom* ins);
void visitMathFunction(MMathFunction* ins);
void visitAdd(MAdd* ins);
void visitSub(MSub* ins);

View File

@ -10,6 +10,7 @@
#include "jscntxt.h"
#include "jscompartment.h"
#include "jsmath.h"
#include "jsnum.h"
#include "jit/CodeGenerator.h"
@ -2320,3 +2321,18 @@ CodeGeneratorARM::visitMemoryBarrier(LMemoryBarrier* ins)
{
memoryBarrier(ins->type());
}
void
CodeGeneratorARM::visitRandom(LRandom* ins)
{
Register temp = ToRegister(ins->temp());
Register temp2 = ToRegister(ins->temp2());
masm.loadJSContext(temp);
masm.setupUnalignedABICall(1, temp2);
masm.passABIArg(temp);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
}

View File

@ -216,6 +216,8 @@ class CodeGeneratorARM : public CodeGeneratorShared
void generateInvalidateEpilogue();
void visitRandom(LRandom* ins);
protected:
void visitEffectiveAddress(LEffectiveAddress* ins);
void visitUDiv(LUDiv* ins);

View File

@ -512,6 +512,23 @@ class LAsmJSAtomicBinopCallout : public LInstructionHelper<1, 2, 0>
}
};
// Math.random().
class LRandom : public LCallInstructionHelper<1, 0, 2>
{
public:
LIR_HEADER(Random)
LRandom(const LDefinition& temp, const LDefinition& temp2) {
setTemp(0, temp);
setTemp(1, temp2);
}
const LDefinition* temp() {
return getTemp(0);
}
const LDefinition* temp2() {
return getTemp(1);
}
};
} // namespace jit
} // namespace js

View File

@ -715,3 +715,10 @@ LIRGeneratorARM::visitSubstr(MSubstr* ins)
define(lir, ins);
assignSafepoint(lir, ins);
}
void
LIRGeneratorARM::visitRandom(MRandom* ins)
{
LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
defineReturn(lir, ins);
}

View File

@ -105,6 +105,7 @@ class LIRGeneratorARM : public LIRGeneratorShared
void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
void visitSubstr(MSubstr* ins);
void visitRandom(MRandom* ins);
};
typedef LIRGeneratorARM LIRGeneratorSpecific;

View File

@ -10,6 +10,7 @@
#include "jscntxt.h"
#include "jscompartment.h"
#include "jsmath.h"
#include "jsnum.h"
#include "jit/CodeGenerator.h"
@ -2152,3 +2153,18 @@ CodeGeneratorMIPS::visitNegF(LNegF* ins)
masm.as_negs(output, input);
}
void
CodeGeneratorMIPS::visitRandom(LRandom* ins)
{
Register temp = ToRegister(ins->temp());
Register temp2 = ToRegister(ins->temp2());
masm.loadJSContext(temp);
masm.setupUnalignedABICall(1, temp2);
masm.passABIArg(temp);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
}

View File

@ -245,6 +245,8 @@ class CodeGeneratorMIPS : public CodeGeneratorShared
void generateInvalidateEpilogue();
void visitRandom(LRandom* ins);
protected:
void visitEffectiveAddress(LEffectiveAddress* ins);
void visitUDivOrMod(LUDivOrMod* ins);

View File

@ -387,6 +387,23 @@ class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 0>
}
};
// Math.random().
class LRandom : public LCallInstructionHelper<1, 0, 2>
{
public:
LIR_HEADER(Random)
LRandom(const LDefinition& temp, const LDefinition& temp2) {
setTemp(0, temp);
setTemp(1, temp2);
}
const LDefinition* temp() {
return getTemp(0);
}
const LDefinition* temp2() {
return getTemp(1);
}
};
} // namespace jit
} // namespace js

View File

@ -584,3 +584,10 @@ LIRGeneratorMIPS::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBino
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorMIPS::visitRandom(MRandom* ins)
{
LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
defineReturn(lir, ins);
}

View File

@ -105,6 +105,7 @@ class LIRGeneratorMIPS : public LIRGeneratorShared
void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
void visitSubstr(MSubstr* ins);
void visitRandom(MRandom* ins);
};
typedef LIRGeneratorMIPS LIRGeneratorSpecific;

View File

@ -565,6 +565,13 @@ class Assembler : public AssemblerX86Shared
masm.xorq_ir(imm.value, dest.encoding());
}
void imulq(Register src, Register dest) {
masm.imulq_rr(src.encoding(), dest.encoding());
}
void vcvtsi2sdq(Register src, FloatRegister dest) {
masm.vcvtsi2sdq_rr(src.encoding(), dest.encoding());
}
void mov(ImmWord word, Register dest) {
// Use xor for setting registers to zero, as it is specially optimized
// for this purpose on modern hardware. Note that it does clobber FLAGS

View File

@ -819,3 +819,129 @@ CodeGeneratorX64::visitTruncateFToInt32(LTruncateFToInt32* ins)
// call a stub if it fails.
emitTruncateFloat32(input, output, ins->mir());
}
namespace js {
namespace jit {
// Out-of-line math_random_no_outparam call for LRandom.
class OutOfLineRandom : public OutOfLineCodeBase<CodeGeneratorX64>
{
LRandom* lir_;
public:
explicit OutOfLineRandom(LRandom* lir)
: lir_(lir)
{ }
void accept(CodeGeneratorX64* codegen) {
codegen->visitOutOfLineRandom(this);
}
LRandom* lir() const {
return lir_;
}
};
} // namespace jit
} // namespace js
static const double RNG_DSCALE_INV = 1 / RNG_DSCALE;
void
CodeGeneratorX64::visitRandom(LRandom* ins)
{
FloatRegister output = ToFloatRegister(ins->output());
Register JSCompartmentReg = ToRegister(ins->temp());
Register rngStateReg = ToRegister(ins->temp2());
Register highReg = ToRegister(ins->temp3());
Register lowReg = ToRegister(ins->temp4());
Register rngMaskReg = ToRegister(ins->temp5());
// rngState = cx->compartment()->rngState
masm.loadJSContext(JSCompartmentReg);
masm.loadPtr(Address(JSCompartmentReg, JSContext::offsetOfCompartment()), JSCompartmentReg);
masm.loadPtr(Address(JSCompartmentReg, JSCompartment::offsetOfRngState()), rngStateReg);
// if rngState == 0, escape from inlined code and call
// math_random_no_outparam.
OutOfLineRandom* ool = new(alloc()) OutOfLineRandom(ins);
addOutOfLineCode(ool, ins->mir());
masm.branchTestPtr(Assembler::Zero, rngStateReg, rngStateReg, ool->entry());
// nextstate = rngState * RNG_MULTIPLIER;
Register& rngMultiplierReg = lowReg;
masm.movq(ImmWord(RNG_MULTIPLIER), rngMultiplierReg);
masm.imulq(rngMultiplierReg, rngStateReg);
// nextstate += RNG_ADDEND;
masm.addq(Imm32(RNG_ADDEND), rngStateReg);
// nextstate &= RNG_MASK;
masm.movq(ImmWord(RNG_MASK), rngMaskReg);
masm.andq(rngMaskReg, rngStateReg);
// rngState = nextstate
// if rngState == 0, escape from inlined code and call
// math_random_no_outparam.
masm.j(Assembler::Zero, ool->entry());
// high = (nextstate >> (RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
masm.movq(rngStateReg, highReg);
masm.shrq(Imm32(RNG_STATE_WIDTH - RNG_HIGH_BITS), highReg);
masm.shlq(Imm32(RNG_LOW_BITS), highReg);
// nextstate = rngState * RNG_MULTIPLIER;
masm.imulq(rngMultiplierReg, rngStateReg);
// nextstate += RNG_ADDEND;
masm.addq(Imm32(RNG_ADDEND), rngStateReg);
// nextstate &= RNG_MASK;
masm.andq(rngMaskReg, rngStateReg);
// low = nextstate >> (RNG_STATE_WIDTH - RNG_LOW_BITS);
masm.movq(rngStateReg, lowReg);
masm.shrq(Imm32(RNG_STATE_WIDTH - RNG_LOW_BITS), lowReg);
// output = double(high | low);
masm.orq(highReg, lowReg);
masm.vcvtsi2sdq(lowReg, output);
// output = output * RNG_DSCALE_INV;
Register& rngDscaleInvReg = lowReg;
masm.movq(ImmPtr(&RNG_DSCALE_INV), rngDscaleInvReg);
masm.vmulsd(Operand(rngDscaleInvReg, 0), output, output);
// cx->compartment()->rngState = nextstate
masm.storePtr(rngStateReg, Address(JSCompartmentReg, JSCompartment::offsetOfRngState()));
masm.bind(ool->rejoin());
}
void
CodeGeneratorX64::visitOutOfLineRandom(OutOfLineRandom* ool)
{
LRandom* ins = ool->lir();
Register temp = ToRegister(ins->temp());
Register temp2 = ToRegister(ins->temp2());
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
LiveRegisterSet regs;
regs.add(ReturnFloat32Reg);
regs.add(ReturnDoubleReg);
regs.add(ReturnInt32x4Reg);
regs.add(ReturnFloat32x4Reg);
saveVolatile(regs);
masm.loadJSContext(temp);
masm.setupUnalignedABICall(1, temp2);
masm.passABIArg(temp);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
restoreVolatile(regs);
masm.jump(ool->rejoin());
}

View File

@ -12,6 +12,8 @@
namespace js {
namespace jit {
class OutOfLineRandom;
class CodeGeneratorX64 : public CodeGeneratorX86Shared
{
CodeGeneratorX64* thisFromCtor() {
@ -58,6 +60,8 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared
void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
void visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir);
void visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir);
void visitRandom(LRandom* ins);
void visitOutOfLineRandom(OutOfLineRandom* ool);
};
typedef CodeGeneratorX64 CodeGeneratorSpecific;

View File

@ -121,6 +121,41 @@ class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 1>
}
};
// Math.random().
class LRandom : public LInstructionHelper<1, 0, 5>
{
public:
LIR_HEADER(Random)
LRandom(const LDefinition &temp, const LDefinition &temp2, const LDefinition &temp3,
const LDefinition &temp4, const LDefinition &temp5)
{
setTemp(0, temp);
setTemp(1, temp2);
setTemp(2, temp3);
setTemp(3, temp4);
setTemp(4, temp5);
}
const LDefinition* temp() {
return getTemp(0);
}
const LDefinition* temp2() {
return getTemp(1);
}
const LDefinition *temp3() {
return getTemp(2);
}
const LDefinition *temp4() {
return getTemp(3);
}
const LDefinition *temp5() {
return getTemp(4);
}
MRandom* mir() const {
return mir_->toRandom();
}
};
} // namespace jit
} // namespace js

View File

@ -309,3 +309,14 @@ LIRGeneratorX64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic
{
MOZ_CRASH("NYI");
}
void
LIRGeneratorX64::visitRandom(MRandom* ins)
{
LRandom *lir = new(alloc()) LRandom(temp(),
temp(),
temp(),
temp(),
temp());
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
}

View File

@ -51,6 +51,7 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared
void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
void visitSubstr(MSubstr* ins);
void visitRandom(MRandom* ins);
};
typedef LIRGeneratorX64 LIRGeneratorSpecific;

View File

@ -1311,6 +1311,14 @@ public:
}
}
#ifdef JS_CODEGEN_X64
void imulq_rr(RegisterID src, RegisterID dst)
{
spew("imulq %s, %s", GPReg64Name(src), GPReg64Name(dst));
m_formatter.twoByteOp64(OP2_IMUL_GvEv, src, dst);
}
#endif
void idivl_r(RegisterID divisor)
{
spew("idivl %s", GPReg32Name(divisor));
@ -2771,6 +2779,13 @@ public:
}
#endif
#ifdef JS_CODEGEN_X64
void vcvtsi2sdq_rr(RegisterID src, XMMRegisterID dst)
{
twoByteOpInt64Simd("vcvtsi2sdq", VEX_SD, OP2_CVTSI2SD_VsdEd, src, invalid_xmm, dst);
}
#endif
void vcvttsd2si_rr(XMMRegisterID src, RegisterID dst)
{
twoByteOpSimdInt32("vcvttsd2si", VEX_SD, OP2_CVTTSD2SI_GdWsd, src, dst);

View File

@ -9,6 +9,7 @@
#include "mozilla/Casting.h"
#include "mozilla/DebugOnly.h"
#include "jsmath.h"
#include "jsnum.h"
#include "jit/IonCaches.h"
@ -1112,3 +1113,18 @@ CodeGeneratorX86::visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32* ool)
masm.jump(ool->rejoin());
}
void
CodeGeneratorX86::visitRandom(LRandom* ins)
{
Register temp = ToRegister(ins->temp());
Register temp2 = ToRegister(ins->temp2());
masm.loadJSContext(temp);
masm.setupUnalignedABICall(1, temp2);
masm.passABIArg(temp);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
}

View File

@ -71,6 +71,8 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared
void visitOutOfLineTruncate(OutOfLineTruncate* ool);
void visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32* ool);
void visitRandom(LRandom* ins);
private:
void asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg, bool boundsCheck,
int32_t offset, int32_t endOffset, Register out,

View File

@ -143,6 +143,23 @@ class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 0>
}
};
// Math.random().
class LRandom : public LCallInstructionHelper<1, 0, 2>
{
public:
LIR_HEADER(Random)
LRandom(const LDefinition& temp, const LDefinition& temp2) {
setTemp(0, temp);
setTemp(1, temp2);
}
const LDefinition* temp() {
return getTemp(0);
}
const LDefinition* temp2() {
return getTemp(1);
}
};
} // namespace jit
} // namespace js

View File

@ -413,3 +413,10 @@ LIRGeneratorX86::visitSubstr(MSubstr* ins)
define(lir, ins);
assignSafepoint(lir, ins);
}
void
LIRGeneratorX86::visitRandom(MRandom* ins)
{
LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
defineReturn(lir, ins);
}

View File

@ -57,6 +57,7 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared
void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
void visitSubstr(MSubstr* ins);
void visitRandom(MRandom* ins);
void lowerPhi(MPhi* phi);
static bool allowTypedElementHoleCheck() {

View File

@ -292,6 +292,9 @@ struct JSContext : public js::ExclusiveContext,
static size_t offsetOfRuntime() {
return offsetof(JSContext, runtime_);
}
static size_t offsetOfCompartment() {
return offsetof(JSContext, compartment_);
}
friend class js::ExclusiveContext;
friend class JS::AutoSaveExceptionState;

View File

@ -455,6 +455,10 @@ struct JSCompartment
/* Random number generator state, used by jsmath.cpp. */
uint64_t rngState;
static size_t offsetOfRngState() {
return offsetof(JSCompartment, rngState);
}
private:
JSCompartment* thisForCtor() { return this; }

View File

@ -765,10 +765,6 @@ random_generateSeed()
return seed.u64;
}
static const uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL;
static const uint64_t RNG_ADDEND = 0xBLL;
static const uint64_t RNG_MASK = (1LL << 48) - 1;
/*
* Math.random() support, lifted from java.util.Random.java.
*/
@ -785,7 +781,7 @@ uint64_t
js::random_next(uint64_t* rngState, int bits)
{
MOZ_ASSERT((*rngState & 0xffff000000000000ULL) == 0, "Bad rngState");
MOZ_ASSERT(bits > 0 && bits <= 48, "bits is out of range");
MOZ_ASSERT(bits > 0 && bits <= RNG_STATE_WIDTH, "bits is out of range");
if (*rngState == 0) {
random_initState(rngState);
@ -795,7 +791,7 @@ js::random_next(uint64_t* rngState, int bits)
nextstate += RNG_ADDEND;
nextstate &= RNG_MASK;
*rngState = nextstate;
return nextstate >> (48 - bits);
return nextstate >> (RNG_STATE_WIDTH - bits);
}
double

View File

@ -97,11 +97,18 @@ extern uint64_t
random_next(uint64_t* rngState, int bits);
static const double RNG_DSCALE = double(1LL << 53);
static const int RNG_STATE_WIDTH = 48;
static const int RNG_HIGH_BITS = 26;
static const int RNG_LOW_BITS = 27;
static const uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL;
static const uint64_t RNG_ADDEND = 0xBLL;
static const uint64_t RNG_MASK = (1LL << RNG_STATE_WIDTH) - 1;
inline double
random_nextDouble(uint64_t* rng)
{
return double((random_next(rng, 26) << 27) + random_next(rng, 27)) / RNG_DSCALE;
return double((random_next(rng, RNG_HIGH_BITS) << RNG_LOW_BITS) +
random_next(rng, RNG_LOW_BITS)) / RNG_DSCALE;
}
extern double