Bug 322529 part 1 - Use XorShift128PlusRNG for Math.random(). r=jwalden
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 02 Dec 2015 13:56:00 +0100
changeset 309392 d0948f31ce271a7c482c169b602f4f01b52eba41
parent 309391 e2a08ea6aace395ad095a0324338f21e24e29315
child 309393 182369db983e66c276948d44bc8251b73b68c0ba
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs322529
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 322529 part 1 - Use XorShift128PlusRNG for Math.random(). r=jwalden
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsmath.cpp
js/src/jsmath.h
js/src/vm/SavedStacks.cpp
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -67,17 +67,16 @@ JSCompartment::JSCompartment(Zone* zone,
     propertyTree(thisForCtor()),
     selfHostingScriptSource(nullptr),
     objectMetadataTable(nullptr),
     lazyArrayBuffers(nullptr),
     nonSyntacticLexicalScopes_(nullptr),
     gcIncomingGrayPointers(nullptr),
     gcPreserveJitCode(options.preserveJitCode()),
     debugModeBits(0),
-    rngState(0),
     watchpointMap(nullptr),
     scriptCountsMap(nullptr),
     debugScriptMap(nullptr),
     debugScopes(nullptr),
     enumerators(nullptr),
     compartmentStats(nullptr),
     scheduledForDestruction(false),
     maybeAlive(true),
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -2,18 +2,20 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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/. */
 
 #ifndef jscompartment_h
 #define jscompartment_h
 
+#include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Variant.h"
+#include "mozilla/XorShift128PlusRNG.h"
 
 #include "builtin/RegExp.h"
 #include "gc/Barrier.h"
 #include "gc/Zone.h"
 #include "vm/GlobalObject.h"
 #include "vm/PIC.h"
 #include "vm/SavedStacks.h"
 #include "vm/Time.h"
@@ -587,22 +589,21 @@ struct JSCompartment
     }
 
     js::SavedStacks& savedStacks() { return savedStacks_; }
 
     void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone>& finder);
 
     js::DtoaCache dtoaCache;
 
-    /* Random number generator state, used by jsmath.cpp. */
-    uint64_t rngState;
+    // Random number generator for Math.random().
+    mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomNumberGenerator;
 
-    static size_t offsetOfRngState() {
-        return offsetof(JSCompartment, rngState);
-    }
+    // Initialize randomNumberGenerator if needed.
+    void ensureRandomNumberGenerator();
 
   private:
     JSCompartment* thisForCtor() { return this; }
 
   public:
     //
     // The Debugger observes execution on a frame-by-frame basis. The
     // invariants of JSCompartment's debug mode bits, JSScript::isDebuggee,
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -729,21 +729,20 @@ js::math_pow_handle(JSContext* cx, Handl
 bool
 js::math_pow(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     return math_pow_handle(cx, args.get(0), args.get(1), args.rval());
 }
 
-void
-js::random_generateSeed(uint64_t* seedBuffer, size_t length)
+static void
+GenerateSeed(uint64_t* seedBuffer, size_t length)
 {
-    if (length == 0)
-        return;
+    MOZ_ASSERT(length > 0);
 
 #if defined(XP_WIN)
     /*
      * Temporary diagnostic for bug 1167248: Test whether the injected hooks
      * react differently to LoadLibraryW / LoadLibraryExW.
      */
     HMODULE oldWay = LoadLibraryW(L"ADVAPI32.DLL");
     HMODULE newWay = LoadLibraryExW(L"ADVAPI32.DLL",
@@ -808,58 +807,44 @@ js::random_generateSeed(uint64_t* seedBu
         mozilla::Unused << nread;
         close(fd);
     }
 #else
 # error "Platform needs to implement random_generateSeed()"
 #endif
 }
 
-/*
- * Math.random() support, lifted from java.util.Random.java.
- */
 void
-js::random_initState(uint64_t* rngState)
+js::GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed)
 {
-    /* Our PRNG only uses 48 bits, so squeeze our entropy into those bits. */
-    uint64_t seed;
-    random_generateSeed(&seed, 1);
-    seed ^= (seed >> 16);
-    *rngState = (seed ^ RNG_MULTIPLIER) & RNG_MASK;
+    // XorShift128PlusRNG must be initialized with a non-zero seed.
+    do {
+        GenerateSeed(seed.begin(), mozilla::ArrayLength(seed));
+    } while (seed[0] == 0 && seed[1] == 0);
 }
 
-uint64_t
-js::random_next(uint64_t* rngState, int bits)
+void
+JSCompartment::ensureRandomNumberGenerator()
 {
-    MOZ_ASSERT((*rngState & 0xffff000000000000ULL) == 0, "Bad rngState");
-    MOZ_ASSERT(bits > 0 && bits <= RNG_STATE_WIDTH, "bits is out of range");
-
-    if (*rngState == 0) {
-        random_initState(rngState);
+    if (randomNumberGenerator.isNothing()) {
+        mozilla::Array<uint64_t, 2> seed;
+        GenerateXorShift128PlusSeed(seed);
+        randomNumberGenerator.emplace(seed[0], seed[1]);
     }
-
-    uint64_t nextstate = *rngState * RNG_MULTIPLIER;
-    nextstate += RNG_ADDEND;
-    nextstate &= RNG_MASK;
-    *rngState = nextstate;
-    return nextstate >> (RNG_STATE_WIDTH - bits);
-}
-
-double
-js::math_random_no_outparam(JSContext* cx)
-{
-    /* Calculate random without memory traffic, for use in the JITs. */
-    return random_nextDouble(&cx->compartment()->rngState);
 }
 
 bool
 js::math_random(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    double z = random_nextDouble(&cx->compartment()->rngState);
+
+    JSCompartment* comp = cx->compartment();
+    comp->ensureRandomNumberGenerator();
+
+    double z = comp->randomNumberGenerator.ref().nextDouble();
     args.rval().setDouble(z);
     return true;
 }
 
 bool
 js::math_round_handle(JSContext* cx, HandleValue arg, MutableHandleValue res)
 {
     double d;
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -104,47 +104,24 @@ class MathCache
 
 /*
  * JS math functions.
  */
 
 extern JSObject*
 InitMathClass(JSContext* cx, HandleObject obj);
 
-/*
- * Fill |seed[0]| through |seed[length-1]| with random bits, suitable for
- * seeding a random number generator.
- */
+// Fill |seed[0]| and |seed[1]| with random bits, suitable for
+// seeding a XorShift128+ random number generator.
 extern void
-random_generateSeed(uint64_t* seed, size_t length);
-
-extern void
-random_initState(uint64_t* rngState);
+GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed);
 
 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, RNG_HIGH_BITS) << RNG_LOW_BITS) +
-                  random_next(rng, RNG_LOW_BITS)) / RNG_DSCALE;
-}
-
-extern double
-math_random_no_outparam(JSContext* cx);
-
 extern bool
 math_random(JSContext* cx, unsigned argc, js::Value* vp);
 
 extern bool
 math_abs_handle(JSContext* cx, js::HandleValue v, js::MutableHandleValue r);
 
 extern bool
 math_abs(JSContext* cx, unsigned argc, js::Value* vp);
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -984,18 +984,18 @@ SavedFrame::toStringMethod(JSContext* cx
         return false;
     args.rval().setString(string);
     return true;
 }
 
 bool
 SavedStacks::init()
 {
-    uint64_t seed[2];
-    random_generateSeed(seed, mozilla::ArrayLength(seed));
+    mozilla::Array<uint64_t, 2> seed;
+    GenerateXorShift128PlusSeed(seed);
     bernoulli.setRandomState(seed[0], seed[1]);
 
     if (!pcLocationMap.init())
         return false;
 
     return frames.init();
 }