Permalink
Browse files

Update the underlying pseudo-random number generator (PRNG) used by M…

…ath.random() from

linear congruential generator (LCG) to xorshift128+ (http://vigna.di.unimi.it/ftp/papers/xorshiftplus.pdf)
to be interoperable with other browsers:

http://jandemooij.nl/blog/2015/11/30/testing-math-random-crushing-the-browser/
https://bugzilla.mozilla.org/show_bug.cgi?id=322529
http://v8project.blogspot.com/2015/12/theres-mathrandom-and-then-theres.html

Also included:
- inlining of Math.random() for X64
- override switches for PRNG seeds

This change pass all 96 TestU01 Crush tests.
  • Loading branch information...
Suwei Chen
Suwei Chen committed Jan 21, 2016
1 parent 0928bc9 commit dbda0182dc0a983dfb37d90c05000e79b6fc75b0
View
@@ -10254,6 +10254,76 @@ Lowerer::GenerateFastInlineBuiltInMathRandom(IR::Instr* instr)
AssertMsg(instr->GetDst()->IsFloat(), "dst must be float.");
IR::Instr* retInstr = instr->m_prev;
IR::Opnd* dst = instr->GetDst();
#if defined(_M_X64)
static const uint64 mExp = 0x3FF0000000000000;
static const uint64 mMant = 0x000FFFFFFFFFFFFF;
IR::RegOpnd* r0 = IR::RegOpnd::New(TyUint64, this->m_func); // s0
IR::RegOpnd* r1 = IR::RegOpnd::New(TyUint64, this->m_func); // s1
IR::RegOpnd* r3 = IR::RegOpnd::New(TyUint64, this->m_func); // helper uint64 reg
IR::RegOpnd* r4 = IR::RegOpnd::New(TyFloat64, this->m_func); // helper float64 reg
// ===========================================================
// s0 = scriptContext->GetLibrary()->GetRandSeed1();
// s1 = scriptContext->GetLibrary()->GetRandSeed0();
// ===========================================================
this->m_lowererMD.CreateAssign(r0,
IR::MemRefOpnd::New((BYTE*)m_func->GetScriptContext()->GetLibrary() + Js::JavascriptLibrary::GetRandSeed1Offset(), TyUint64, instr->m_func), instr);
this->m_lowererMD.CreateAssign(r1,
IR::MemRefOpnd::New((BYTE*)m_func->GetScriptContext()->GetLibrary() + Js::JavascriptLibrary::GetRandSeed0Offset(), TyUint64, instr->m_func), instr);
// ===========================================================
// s1 ^= s1 << 23;
// ===========================================================
this->m_lowererMD.CreateAssign(r3, r1, instr);
this->InsertShift(Js::OpCode::Shl_A, false, r3, r3, IR::IntConstOpnd::New(23, TyInt8, this->m_func), instr);
this->InsertXor(r1, r1, r3, instr);
// ===========================================================
// s1 ^= s1 >> 17;
// ===========================================================
this->m_lowererMD.CreateAssign(r3, r1, instr);
this->InsertShift(Js::OpCode::ShrU_A, false, r3, r3, IR::IntConstOpnd::New(17, TyInt8, this->m_func), instr);
this->InsertXor(r1, r1, r3, instr);
// ===========================================================
// s1 ^= s0;
// ===========================================================
this->InsertXor(r1, r1, r0, instr);
// ===========================================================
// s1 ^= s0 >> 26;
// ===========================================================
this->m_lowererMD.CreateAssign(r3, r0, instr);
this->InsertShift(Js::OpCode::ShrU_A, false, r3, r3, IR::IntConstOpnd::New(26, TyInt8, this->m_func), instr);
this->InsertXor(r1, r1, r3, instr);
// ===========================================================
// scriptContext->GetLibrary()->SetRandSeed0(s0);
// scriptContext->GetLibrary()->SetRandSeed1(s1);
// ===========================================================
this->m_lowererMD.CreateAssign(
IR::MemRefOpnd::New((BYTE*)m_func->GetScriptContext()->GetLibrary() + Js::JavascriptLibrary::GetRandSeed0Offset(), TyUint64, this->m_func), r0, instr);
this->m_lowererMD.CreateAssign(
IR::MemRefOpnd::New((BYTE*)m_func->GetScriptContext()->GetLibrary() + Js::JavascriptLibrary::GetRandSeed1Offset(), TyUint64, this->m_func), r1, instr);
// ===========================================================
// dst = bit_cast<float64>(((s0 + s1) & mMant) | mExp);
// ===========================================================
this->InsertAdd(false, r1, r1, r0, instr);
this->m_lowererMD.CreateAssign(r3, IR::AddrOpnd::New((Js::Var)mMant, IR::AddrOpndKindConstantVar, m_func, true), instr);
this->InsertAnd(r1, r1, r3, instr);
this->m_lowererMD.CreateAssign(r3, IR::AddrOpnd::New((Js::Var)mExp, IR::AddrOpndKindConstantVar, m_func, true), instr);
this->InsertOr(r1, r1, r3, instr);
this->InsertMoveBitCast(dst, r1, instr);
// ===================================================================
// dst -= 1.0;
// ===================================================================
this->m_lowererMD.CreateAssign(r4, IR::MemRefOpnd::New((double*)&Js::JavascriptNumber::ONE_POINT_ZERO, TyFloat64, m_func, IR::AddrOpndKindDynamicDoubleRef), instr);
this->InsertSub(false, dst, dst, r4, instr);
#else
IR::Opnd* tmpdst = dst;
if(!dst->IsRegOpnd())
{
@@ -10269,6 +10339,7 @@ Lowerer::GenerateFastInlineBuiltInMathRandom(IR::Instr* instr)
{
InsertMove(dst, tmpdst, instr);
}
#endif
instr->Remove();
return retInstr;
@@ -13626,6 +13697,48 @@ IR::Instr *Lowerer::InsertLea(IR::RegOpnd *const dst, IR::Opnd *const src, IR::I
return LowererMD::ChangeToLea(instr);
}
#if _M_X64
IR::Instr *Lowerer::InsertMoveBitCast(
IR::Opnd *const dst,
IR::Opnd *const src1,
IR::Instr *const insertBeforeInstr)
{
Assert(dst);
Assert(dst->GetType() == TyFloat64);
Assert(src1);
Assert(src1->GetType() == TyUint64);
Assert(insertBeforeInstr);
Func *const func = insertBeforeInstr->m_func;
IR::Instr *const instr = IR::Instr::New(LowererMD::MDMovUint64ToFloat64Opcode, dst, src1, func);
insertBeforeInstr->InsertBefore(instr);
LowererMD::Legalize(instr);
return instr;
}
#endif
IR::Instr *Lowerer::InsertXor(
IR::Opnd *const dst,
IR::Opnd *const src1,
IR::Opnd *const src2,
IR::Instr *const insertBeforeInstr)
{
Assert(dst);
Assert(src1);
Assert(src2);
Assert(insertBeforeInstr);
Func *const func = insertBeforeInstr->m_func;
IR::Instr *const instr = IR::Instr::New(LowererMD::MDXorOpcode, dst, src1, src2, func);
insertBeforeInstr->InsertBefore(instr);
LowererMD::Legalize(instr);
return instr;
}
IR::Instr *Lowerer::InsertAnd(
IR::Opnd *const dst,
IR::Opnd *const src1,
View
@@ -298,6 +298,9 @@ class Lowerer
static IR::Instr * InsertMove(IR::Opnd *dst, IR::Opnd *src, IR::Instr *const insertBeforeInstr, bool generateWriteBarrier = false);
static IR::Instr * InsertMoveWithBarrier(IR::Opnd *dst, IR::Opnd *src, IR::Instr *const insertBeforeInstr);
#if _M_X64
static IR::Instr * InsertMoveBitCast(IR::Opnd *const dst, IR::Opnd *const src1, IR::Instr *const insertBeforeInstr);
#endif
static IR::BranchInstr * InsertBranch(const Js::OpCode opCode, IR::LabelInstr *const target, IR::Instr *const insertBeforeInstr);
static IR::BranchInstr * InsertBranch(const Js::OpCode opCode, const bool isUnsigned, IR::LabelInstr *const target, IR::Instr *const insertBeforeInstr);
static IR::Instr * InsertCompare(IR::Opnd *const src1, IR::Opnd *const src2, IR::Instr *const insertBeforeInstr);
@@ -309,6 +312,7 @@ class Lowerer
static IR::Instr * InsertAdd(const bool needFlags, IR::Opnd *const dst, IR::Opnd *src1, IR::Opnd *src2, IR::Instr *const insertBeforeInstr);
static IR::Instr * InsertSub(const bool needFlags, IR::Opnd *const dst, IR::Opnd *src1, IR::Opnd *src2, IR::Instr *const insertBeforeInstr);
static IR::Instr * InsertLea(IR::RegOpnd *const dst, IR::Opnd *const src, IR::Instr *const insertBeforeInstr);
static IR::Instr * InsertXor(IR::Opnd *const dst, IR::Opnd *const src1, IR::Opnd *const src2, IR::Instr *const insertBeforeInstr);
static IR::Instr * InsertAnd(IR::Opnd *const dst, IR::Opnd *const src1, IR::Opnd *const src2, IR::Instr *const insertBeforeInstr);
static IR::Instr * InsertOr(IR::Opnd *const dst, IR::Opnd *const src1, IR::Opnd *const src2, IR::Instr *const insertBeforeInstr);
static IR::Instr * InsertShift(const Js::OpCode opCode, const bool needFlags, IR::Opnd *const dst, IR::Opnd *const src1, IR::Opnd *const src2, IR::Instr *const insertBeforeInstr);
@@ -13,6 +13,10 @@
const Js::OpCode LowererMD::MDUncondBranchOpcode = Js::OpCode::JMP;
const Js::OpCode LowererMD::MDTestOpcode = Js::OpCode::TEST;
const Js::OpCode LowererMD::MDOrOpcode = Js::OpCode::OR;
const Js::OpCode LowererMD::MDXorOpcode = Js::OpCode::XOR;
#if _M_X64
const Js::OpCode LowererMD::MDMovUint64ToFloat64Opcode = Js::OpCode::MOVQ;
#endif
const Js::OpCode LowererMD::MDOverflowBranchOpcode = Js::OpCode::JO;
const Js::OpCode LowererMD::MDNotOverflowBranchOpcode = Js::OpCode::JNO;
const Js::OpCode LowererMD::MDConvertFloat32ToFloat64Opcode = Js::OpCode::CVTSS2SD;
@@ -55,6 +55,10 @@ class LowererMD
static const Js::OpCode MDExtend32Opcode;
static const Js::OpCode MDTestOpcode;
static const Js::OpCode MDOrOpcode;
static const Js::OpCode MDXorOpcode;
#if _M_X64
static const Js::OpCode MDMovUint64ToFloat64Opcode;
#endif
static const Js::OpCode MDOverflowBranchOpcode;
static const Js::OpCode MDNotOverflowBranchOpcode;
static const Js::OpCode MDConvertFloat32ToFloat64Opcode;
@@ -590,7 +590,7 @@ EncoderMD::Encode(IR::Instr *instr, BYTE *pc, BYTE* beginCodeAddress)
const BYTE *form = EncoderMD::GetFormTemplate(instr);
const BYTE *opcodeTemplate = EncoderMD::GetOpbyte(instr);
const uint32 leadIn = EncoderMD::GetLeadIn(instr);
const uint32 opdope = EncoderMD::GetOpdope(instr);
uint32 opdope = EncoderMD::GetOpdope(instr);
//
// Canonicalize operands.
@@ -1053,6 +1053,60 @@ EncoderMD::Encode(IR::Instr *instr, BYTE *pc, BYTE* beginCodeAddress)
}
break;
case Js::OpCode::MOVQ:
{
// The usual mechanism for encoding SSE/SSE2 instructions
// doesn't work quite right for this one, so we must fixup
// the encoding.
if (REGNUM_ISXMMXREG(opr1->AsRegOpnd()->GetReg())) {
if (opr2->IsRegOpnd() && this->GetOpndSize(opr2) == 8) { // QWORD_SIZE
// Encoded as 66 REX.W 0F 6E ModRM
opdope &= ~(DF2 | DF3);
opdope |= D66;
opcodeByte = 0x6E;
goto modrm;
}
else {
// Encoded as F3 0F 7E ModRM
opdope &= ~(D66 | D66EX | DF2);
opdope |= DF3;
opcodeByte = 0x7E;
goto modrm;
}
}
//else if (TU_ISXMMXREG(opr2)) {
else if (REGNUM_ISXMMXREG(opr2->AsRegOpnd()->GetReg())) {
//if (TU_ISREG(opr1) && (TU_SIZE(opr1) == QWORD_SIZE)) {
if (opr1->IsRegOpnd() && this->GetOpndSize(opr1) == 8) {// QWORD_SIZE
// Encoded as 66 REX.W 0F 7E ModRM
opdope &= ~(DF2 | DF3);
opdope |= D66;
opcodeByte = 0x7E;
// Swap operands to get right behavior from modrm encode.
IR::Opnd* tmp = opr1;
opr1 = opr2;
opr2 = tmp;
goto modrm;
}
else {
// Encoded as 66 0F D6
opdope &= ~(DF2 | DF3);
opdope |= D66;
opcodeByte = 0xD6;
goto modrm;
}
}
}
// FALL-THROUGH to MOVD
case Js::OpCode::MOVD:
if (opr2->IsRegOpnd() && REGNUM_ISXMMXREG(opr2->AsRegOpnd()->GetReg()))
{
@@ -138,6 +138,7 @@ MACRO(MOVLHPS, Reg2, None, RNON, f(SPECIAL), o(MOVLHPS), DDST
MACRO(MOVMSKPD, Reg2, None, RNON, f(SPECIAL), o(MOVMSKPD), DDST|DNO16|D66, OLB_0F)
MACRO(MOVMSKPS, Reg2, None, RNON, f(SPECIAL), o(MOVMSKPS), DDST|DNO16, OLB_0F)
MACRO(MOVQ, Reg2, None, RNON, f(SPECIAL), o(MOVQ), DDST|DNO16|D66, OLB_0F)
MACRO(MOVSD, Reg2, None, RNON, f(SPECIAL), o(MOVSD), DDST|DNO16|DF2, OLB_0F)
MACRO(MOVSD_ZERO,Reg2, None, RNON, f(MODRM), o(MOVSD), 0, OLB_NONE)
@@ -233,6 +233,8 @@ enum Forms : BYTE
#define OPBYTE_MOVMSKPD {0x50} // modrm
#define OPBYTE_MOVMSKPS {0x50} // modrm
#define OPBYTE_MOVQ {0x6f} // special
#define OPBYTE_MOVSD {0x10} // modrm
#define OPBYTE_MOVSS {0x10} // special, modrm
#define OPBYTE_MOVAPD {0x28} // special, modrm
@@ -13,6 +13,7 @@
const Js::OpCode LowererMD::MDUncondBranchOpcode = Js::OpCode::B;
const Js::OpCode LowererMD::MDTestOpcode = Js::OpCode::TST;
const Js::OpCode LowererMD::MDOrOpcode = Js::OpCode::ORR;
const Js::OpCode LowererMD::MDXorOpcode = Js::OpCode::EOR;
const Js::OpCode LowererMD::MDOverflowBranchOpcode = Js::OpCode::BVS;
const Js::OpCode LowererMD::MDNotOverflowBranchOpcode = Js::OpCode::BVC;
const Js::OpCode LowererMD::MDConvertFloat32ToFloat64Opcode = Js::OpCode::VCVTF64F32;
@@ -52,6 +52,7 @@ class LowererMD
static const Js::OpCode MDUncondBranchOpcode;
static const Js::OpCode MDTestOpcode;
static const Js::OpCode MDOrOpcode;
static const Js::OpCode MDXorOpcode;
static const Js::OpCode MDOverflowBranchOpcode;
static const Js::OpCode MDNotOverflowBranchOpcode;
static const Js::OpCode MDConvertFloat32ToFloat64Opcode;
@@ -7,6 +7,7 @@
const Js::OpCode LowererMD::MDUncondBranchOpcode = Js::OpCode::B;
const Js::OpCode LowererMD::MDTestOpcode = Js::OpCode::TST;
const Js::OpCode LowererMD::MDOrOpcode = Js::OpCode::ORR;
const Js::OpCode LowererMD::MDXorOpcode = Js::OpCode::EOR;
const Js::OpCode LowererMD::MDOverflowBranchOpcode = Js::OpCode::BVS;
const Js::OpCode LowererMD::MDNotOverflowBranchOpcode = Js::OpCode::BVC;
const Js::OpCode LowererMD::MDConvertFloat32ToFloat64Opcode = Js::OpCode::VCVTF64F32;
@@ -47,6 +47,7 @@ class LowererMD
static const Js::OpCode MDUncondBranchOpcode;
static const Js::OpCode MDTestOpcode;
static const Js::OpCode MDOrOpcode;
static const Js::OpCode MDXorOpcode;
static const Js::OpCode MDOverflowBranchOpcode;
static const Js::OpCode MDNotOverflowBranchOpcode;
static const Js::OpCode MDConvertFloat32ToFloat64Opcode;
@@ -1235,7 +1235,8 @@ namespace Js
booleanTrue = RecyclerNew(recycler, JavascriptBoolean, true, booleanTypeStatic);
booleanFalse = RecyclerNew(recycler, JavascriptBoolean, false, booleanTypeStatic);
randSeed = 0;
randSeed0 = 0;
randSeed1 = 0;
AddMember(globalObject, PropertyIds::NaN, nan, PropertyNone);
AddMember(globalObject, PropertyIds::Infinity, positiveInfinite, PropertyNone);
@@ -162,6 +162,8 @@ namespace Js
static DWORD GetCharStringCacheAOffset() { return GetCharStringCacheOffset() + CharStringCache::GetCharStringCacheAOffset(); }
const JavascriptLibraryBase* GetLibraryBase() const { return static_cast<const JavascriptLibraryBase*>(this); }
void SetGlobalObject(GlobalObject* globalObject) {globalObject = globalObject; }
static DWORD GetRandSeed0Offset() { return offsetof(JavascriptLibrary, randSeed0); }
static DWORD GetRandSeed1Offset() { return offsetof(JavascriptLibrary, randSeed1); }
typedef bool (CALLBACK *PromiseContinuationCallback)(Var task, void *callbackState);
@@ -384,7 +386,7 @@ namespace Js
// and prototypes contain only writable data properties, which is reset upon invalidating the status.
JsUtil::List<Type *> *typesEnsuredToHaveOnlyWritableDataPropertiesInItAndPrototypeChain;
uint64 randSeed;
uint64 randSeed0, randSeed1;
bool inProfileMode;
bool inDispatchProfileMode;
bool arrayObjectHasUserDefinedSpecies;
@@ -844,8 +846,10 @@ namespace Js
void SetCrossSiteForSharedFunctionType(JavascriptFunction * function);
uint64 GetRandSeed(){return randSeed;}
void SetRandSeed(uint64 rs){randSeed = rs;}
uint64 GetRandSeed0() { return randSeed0; }
uint64 GetRandSeed1() { return randSeed1; }
void SetRandSeed0(uint64 rs) { randSeed0 = rs;}
void SetRandSeed1(uint64 rs) { randSeed1 = rs; }
void SetProfileMode(bool fSet);
void SetDispatchProfile(bool fSet, JavascriptMethod dispatchInvoke);
Oops, something went wrong.

0 comments on commit dbda018

Please sign in to comment.