Bug 322529 part 3 - Fix LRandom JIT code to use the new algorithm. r=arai,jwalden

This commit is contained in:
Jan de Mooij 2015-12-02 13:56:00 +01:00
parent 3c2424e82f
commit 90cb097793
8 changed files with 85 additions and 120 deletions

View File

@ -10344,127 +10344,74 @@ CodeGenerator::visitCheckReturn(LCheckReturn* ins)
masm.bind(&noChecks);
}
// Out-of-line math_random_no_outparam call for LRandom.
class OutOfLineRandom : public OutOfLineCodeBase<CodeGenerator>
{
LRandom* lir_;
public:
explicit OutOfLineRandom(LRandom* lir)
: lir_(lir)
{ }
void accept(CodeGenerator* codegen) {
codegen->visitOutOfLineRandom(this);
}
LRandom* lir() const {
return lir_;
}
};
static const uint64_t RNG_HIGH_MASK = (0xFFFFFFFFFFFFFFFFULL >>
(RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
static const double RNG_DSCALE_INV = 1 / RNG_DSCALE;
void
CodeGenerator::visitRandom(LRandom* ins)
{
using mozilla::non_crypto::XorShift128PlusRNG;
FloatRegister output = ToFloatRegister(ins->output());
Register JSCompartmentReg = ToRegister(ins->temp1());
Register tempReg = ToRegister(ins->temp0());
#ifdef JS_PUNBOX64
Register64 rngStateReg = Register64(ToRegister(ins->tempMaybeEAX()));
Register64 highReg = Register64(ToRegister(ins->tempMaybeEDX()));
Register64 s0Reg(ToRegister(ins->temp1()));
Register64 s1Reg(ToRegister(ins->temp2()));
#else
Register64 rngStateReg = Register64(ToRegister(ins->temp2()), ToRegister(ins->temp3()));
Register64 highReg = Register64(ToRegister(ins->tempMaybeEAX()), ToRegister(ins->tempMaybeEDX()));
#endif
// tempReg is used only on x86.
Register tempReg = ToRegister(ins->tempMaybeEAX());
// rngState = cx->compartment()->rngState;
masm.loadJSContext(JSCompartmentReg);
masm.loadPtr(Address(JSCompartmentReg, JSContext::offsetOfCompartment()), JSCompartmentReg);
masm.load64(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.branchTest64(Assembler::Zero, rngStateReg, rngStateReg, tempReg, ool->entry());
// rngState = rngState * RNG_MULTIPLIER;
masm.mul64(Imm64(RNG_MULTIPLIER), rngStateReg);
// rngState += RNG_ADDEND;
masm.add64(Imm32(RNG_ADDEND), rngStateReg);
// rngState &= RNG_MASK;
masm.and64(Imm64(RNG_MASK), rngStateReg);
// if rngState == 0, escape from inlined code and call
// math_random_no_outparam.
masm.branchTest64(Assembler::Zero, rngStateReg, rngStateReg, tempReg, ool->entry());
// high = (rngState >> (RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
masm.move64(rngStateReg, highReg);
masm.lshift64(Imm32(RNG_LOW_BITS - (RNG_STATE_WIDTH - RNG_HIGH_BITS)), highReg);
masm.and64(Imm64(RNG_HIGH_MASK), highReg);
#ifdef JS_CODEGEN_X86
// eax and edx are overwritten by mul64 on x86.
masm.push64(highReg);
Register64 s0Reg(ToRegister(ins->temp1()), ToRegister(ins->temp2()));
Register64 s1Reg(ToRegister(ins->temp3()), ToRegister(ins->temp4()));
#endif
// rngState = rngState * RNG_MULTIPLIER;
masm.mul64(Imm64(RNG_MULTIPLIER), rngStateReg);
const void* rng = gen->compartment->addressOfRandomNumberGenerator();
masm.movePtr(ImmPtr(rng), tempReg);
// rngState += RNG_ADDEND;
masm.add64(Imm32(RNG_ADDEND), rngStateReg);
static_assert(sizeof(XorShift128PlusRNG) == 2 * sizeof(uint64_t),
"Code below assumes XorShift128PlusRNG contains two uint64_t values");
// rngState &= RNG_MASK;
masm.and64(Imm64(RNG_MASK), rngStateReg);
Address state0Addr(tempReg, XorShift128PlusRNG::offsetOfState0());
Address state1Addr(tempReg, XorShift128PlusRNG::offsetOfState1());
// cx->compartment()->rngState = rngState;
masm.store64(rngStateReg, Address(JSCompartmentReg, JSCompartment::offsetOfRngState()));
// uint64_t s1 = mState[0];
masm.load64(state0Addr, s1Reg);
// low = rngState >> (RNG_STATE_WIDTH - RNG_LOW_BITS);
const Register64& lowReg = rngStateReg;
masm.rshift64(Imm32(RNG_STATE_WIDTH - RNG_LOW_BITS), lowReg);
// s1 ^= s1 << 23;
masm.move64(s1Reg, s0Reg);
masm.lshift64(Imm32(23), s1Reg);
masm.xor64(s0Reg, s1Reg);
// output = double(high | low);
#ifdef JS_CODEGEN_X86
masm.pop64(highReg);
#endif
masm.or64(highReg, lowReg);
masm.convertUInt64ToDouble(lowReg, tempReg, output);
// s1 ^= s1 >> 17
masm.move64(s1Reg, s0Reg);
masm.rshift64(Imm32(17), s1Reg);
masm.xor64(s0Reg, s1Reg);
// output = output * RNG_DSCALE_INV;
masm.mulDoublePtr(ImmPtr(&RNG_DSCALE_INV), tempReg, output);
// const uint64_t s0 = mState[1];
masm.load64(state1Addr, s0Reg);
masm.bind(ool->rejoin());
}
// mState[0] = s0;
masm.store64(s0Reg, state0Addr);
void
CodeGenerator::visitOutOfLineRandom(OutOfLineRandom* ool)
{
LRandom* ins = ool->lir();
Register temp1 = ToRegister(ins->tempMaybeEAX());
Register temp2 = ToRegister(ins->tempMaybeEDX());
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
// s1 ^= s0
masm.xor64(s0Reg, s1Reg);
LiveRegisterSet regs;
setReturnDoubleRegs(&regs);
saveVolatile(regs);
// s1 ^= s0 >> 26
masm.rshift64(Imm32(26), s0Reg);
masm.xor64(s0Reg, s1Reg);
masm.loadJSContext(temp1);
// mState[1] = s1
masm.store64(s1Reg, state1Addr);
masm.setupUnalignedABICall(temp2);
masm.passABIArg(temp1);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
// s1 += mState[0]
masm.load64(state0Addr, s0Reg);
masm.add64(s0Reg, s1Reg);
restoreVolatile(regs);
// See comment in XorShift128PlusRNG::nextDouble().
static const int MantissaBits = FloatingPoint<double>::kExponentShift + 1;
static const double ScaleInv = double(1) / (1ULL << MantissaBits);
masm.jump(ool->rejoin());
masm.and64(Imm64((1ULL << MantissaBits) - 1), s1Reg);
masm.convertUInt64ToDouble(s1Reg, tempReg, output);
// output *= ScaleInv
masm.mulDoublePtr(ImmPtr(&ScaleInv), tempReg, output);
}
} // namespace jit

View File

@ -47,7 +47,6 @@ class OutOfLineIsCallable;
class OutOfLineRegExpExec;
class OutOfLineRegExpTest;
class OutOfLineLambdaArrow;
class OutOfLineRandom;
class CodeGenerator : public CodeGeneratorSpecific
{
@ -388,7 +387,6 @@ class CodeGenerator : public CodeGeneratorSpecific
void visitRecompileCheck(LRecompileCheck* ins);
void visitRandom(LRandom* ins);
void visitOutOfLineRandom(OutOfLineRandom* ool);
IonScriptCounts* extractScriptCounts() {
IonScriptCounts* counts = scriptCounts_;

View File

@ -256,6 +256,12 @@ CompileCompartment::addressOfEnumerators()
return &compartment()->enumerators;
}
const void*
CompileCompartment::addressOfRandomNumberGenerator()
{
return compartment()->randomNumberGenerator.ptr();
}
const JitCompartment*
CompileCompartment::jitCompartment()
{

View File

@ -115,6 +115,7 @@ class CompileCompartment
CompileRuntime* runtime();
const void* addressOfEnumerators();
const void* addressOfRandomNumberGenerator();
const JitCompartment* jitCompartment();

View File

@ -1372,6 +1372,9 @@ IonBuilder::inlineMathRandom(CallInfo& callInfo)
if (getInlineReturnType() != MIRType_Double)
return InliningStatus_NotInlined;
MOZ_ASSERT(script()->compartment()->randomNumberGenerator.isSome(),
"MRandom JIT code depends on RNG being initialized");
callInfo.setImplicitlyUsedUnchecked();
MRandom* rand = MRandom::New(alloc());

View File

@ -7316,37 +7316,35 @@ class LRandom : public LInstructionHelper<1, 0, LRANDOM_NUM_TEMPS>
{
public:
LIR_HEADER(Random)
LRandom(const LDefinition &tempMaybeEAX, const LDefinition &tempMaybeEDX,
const LDefinition &temp1
LRandom(const LDefinition &temp0, const LDefinition &temp1,
const LDefinition &temp2
#ifndef JS_PUNBOX64
, const LDefinition &temp2, const LDefinition &temp3
, const LDefinition &temp3, const LDefinition &temp4
#endif
)
{
setTemp(0, tempMaybeEAX);
setTemp(1, tempMaybeEDX);
setTemp(2, temp1);
setTemp(0, temp0);
setTemp(1, temp1);
setTemp(2, temp2);
#ifndef JS_PUNBOX64
setTemp(3, temp2);
setTemp(4, temp3);
setTemp(3, temp3);
setTemp(4, temp4);
#endif
}
// On x86, following 2 methods return eax and edx necessary for mull.
// On others, following 2 methods return ordinary temporary registers.
const LDefinition* tempMaybeEAX() {
const LDefinition* temp0() {
return getTemp(0);
}
const LDefinition* tempMaybeEDX() {
const LDefinition* temp1() {
return getTemp(1);
}
const LDefinition *temp1() {
const LDefinition *temp2() {
return getTemp(2);
}
#ifndef JS_PUNBOX64
const LDefinition *temp2() {
const LDefinition *temp3() {
return getTemp(3);
}
const LDefinition *temp3() {
const LDefinition *temp4() {
return getTemp(4);
}
#endif

View File

@ -441,9 +441,8 @@ LIRGeneratorX86::visitSubstr(MSubstr* ins)
void
LIRGeneratorX86::visitRandom(MRandom* ins)
{
// eax and edx are necessary for mull.
LRandom *lir = new(alloc()) LRandom(tempFixed(eax),
tempFixed(edx),
LRandom *lir = new(alloc()) LRandom(temp(),
temp(),
temp(),
temp(),
temp());

View File

@ -52,6 +52,12 @@ class XorShift128PlusRNG {
/* Return a pseudo-random 64-bit number. */
uint64_t next() {
/*
* The offsetOfState*() methods below are provided so that exceedingly-rare
* callers that want to observe or poke at RNG state in C++ type-system-
* ignoring means can do so. Don't change the next() or nextDouble()
* algorithms without altering code that uses offsetOfState*()!
*/
uint64_t s1 = mState[0];
const uint64_t s0 = mState[1];
mState[0] = s0;
@ -89,6 +95,13 @@ class XorShift128PlusRNG {
mState[0] = aState0;
mState[1] = aState1;
}
static size_t offsetOfState0() {
return offsetof(XorShift128PlusRNG, mState[0]);
}
static size_t offsetOfState1() {
return offsetof(XorShift128PlusRNG, mState[1]);
}
};
} // namespace non_crypto