mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 13:58:49 +00:00
Issue #2895 - Part 1: Replace XorShift128+ with Xoroshiro128++
Surrounding structures remain the same because the bit width remains 128. The more complicated algorithm requires more temp registers in LRandom to account for intermediate result placeholders in the JITed version of this algorithm.
This commit is contained in:
@@ -12051,71 +12051,69 @@ CodeGenerator::visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins)
|
||||
void
|
||||
CodeGenerator::visitRandom(LRandom* ins)
|
||||
{
|
||||
using mozilla::non_crypto::XorShift128PlusRNG;
|
||||
using mozilla::non_crypto::Xoroshiro128PlusPlusRNG;
|
||||
|
||||
FloatRegister output = ToFloatRegister(ins->output());
|
||||
Register tempReg = ToRegister(ins->temp0());
|
||||
// Helper registers for intermediate and final results
|
||||
Register64 imr1Reg(ToRegister(ins->temp1()));
|
||||
Register64 imr2Reg(ToRegister(ins->temp2()));
|
||||
Register64 resultReg(ToRegister(ins->temp3()));
|
||||
|
||||
#ifdef JS_PUNBOX64
|
||||
Register64 s0Reg(ToRegister(ins->temp1()));
|
||||
Register64 s1Reg(ToRegister(ins->temp2()));
|
||||
Register64 s0Reg(ToRegister(ins->temp4()));
|
||||
Register64 s1Reg(ToRegister(ins->temp5()));
|
||||
#else
|
||||
Register64 s0Reg(ToRegister(ins->temp1()), ToRegister(ins->temp2()));
|
||||
Register64 s1Reg(ToRegister(ins->temp3()), ToRegister(ins->temp4()));
|
||||
Register64 s0Reg(ToRegister(ins->temp4()), ToRegister(ins->temp5()));
|
||||
Register64 s1Reg(ToRegister(ins->temp6()), ToRegister(ins->temp7()));
|
||||
#endif
|
||||
|
||||
const void* rng = gen->compartment->addressOfRandomNumberGenerator();
|
||||
masm.movePtr(ImmPtr(rng), tempReg);
|
||||
|
||||
static_assert(sizeof(XorShift128PlusRNG) == 2 * sizeof(uint64_t),
|
||||
"Code below assumes XorShift128PlusRNG contains two uint64_t values");
|
||||
static_assert(sizeof(Xoroshiro128PlusPlusRNG) == 2 * sizeof(uint64_t),
|
||||
"Code below assumes Xoroshiro128PlusPlusRNG contains two uint64_t values");
|
||||
|
||||
Address state0Addr(tempReg, XorShift128PlusRNG::offsetOfState0());
|
||||
Address state1Addr(tempReg, XorShift128PlusRNG::offsetOfState1());
|
||||
Address state0Addr(tempReg, Xoroshiro128PlusPlusRNG::offsetOfState0());
|
||||
Address state1Addr(tempReg, Xoroshiro128PlusPlusRNG::offsetOfState1());
|
||||
|
||||
// uint64_t s1 = mState[0];
|
||||
masm.load64(state0Addr, s1Reg);
|
||||
|
||||
// s1 ^= s1 << 23;
|
||||
masm.move64(s1Reg, s0Reg);
|
||||
masm.lshift64(Imm32(23), s1Reg);
|
||||
masm.xor64(s0Reg, s1Reg);
|
||||
|
||||
// s1 ^= s1 >> 17
|
||||
masm.move64(s1Reg, s0Reg);
|
||||
masm.rshift64(Imm32(17), s1Reg);
|
||||
masm.xor64(s0Reg, s1Reg);
|
||||
|
||||
// const uint64_t s0 = mState[1];
|
||||
masm.load64(state1Addr, s0Reg);
|
||||
|
||||
// mState[0] = s0;
|
||||
masm.store64(s0Reg, state0Addr);
|
||||
|
||||
// s1 ^= s0
|
||||
masm.xor64(s0Reg, s1Reg);
|
||||
|
||||
// s1 ^= s0 >> 26
|
||||
masm.rshift64(Imm32(26), s0Reg);
|
||||
masm.xor64(s0Reg, s1Reg);
|
||||
|
||||
// mState[1] = s1
|
||||
masm.store64(s1Reg, state1Addr);
|
||||
|
||||
// s1 += mState[0]
|
||||
// const uint64_t s0 = mState[0];
|
||||
masm.load64(state0Addr, s0Reg);
|
||||
masm.add64(s0Reg, s1Reg);
|
||||
// uint64_t s1 = mState[1];
|
||||
masm.load64(state1Addr, s1Reg);
|
||||
|
||||
// const uint64_t result = rotl(s0 + s1, 17) + s0;
|
||||
masm.move64(s0Reg, imr1Reg);
|
||||
masm.add64(s1Reg, imr1Reg);
|
||||
masm.rotateLeft64(Imm32(17), imr1Reg, resultReg);
|
||||
masm.add64(s0Reg, resultReg);
|
||||
|
||||
// s1 ^= s0;
|
||||
masm.xor64(s0Reg, s1Reg);
|
||||
|
||||
// mState[0] = rotl(s0, 49) ^ s1 ^ (s1 << 21); // a, b
|
||||
masm.rotateLeft64(Imm32(49), s0Reg, imr1Reg); // imr = s0 rotl 49
|
||||
masm.xor64(s1Reg, imr1Reg); // imr ^ s1
|
||||
masm.move64(s1Reg, imr2Reg); // imr2 = s1
|
||||
masm.lshift64(Imm32(21), imr2Reg); // imr2 << 21
|
||||
masm.xor64(imr2Reg, imr1Reg); // imr ^ imr2
|
||||
masm.store64(imr1Reg, state0Addr);
|
||||
|
||||
// See comment in XorShift128PlusRNG::nextDouble().
|
||||
// mState[1] = rotl(s1, 28); // c
|
||||
masm.rotateLeft64(Imm32(28), s1Reg, imr1Reg);
|
||||
masm.store64(imr1Reg, state1Addr);
|
||||
|
||||
// See comment in Xoroshiro128PlusPlusRNG::nextDouble().
|
||||
static const int MantissaBits = FloatingPoint<double>::kExponentShift + 1;
|
||||
static const double ScaleInv = double(1) / (1ULL << MantissaBits);
|
||||
|
||||
masm.and64(Imm64((1ULL << MantissaBits) - 1), s1Reg);
|
||||
// Mask the result bits to mantissa size
|
||||
masm.and64(Imm64((1ULL << MantissaBits) - 1), resultReg);
|
||||
|
||||
if (masm.convertUInt64ToDoubleNeedsTemp())
|
||||
masm.convertUInt64ToDouble(s1Reg, output, tempReg);
|
||||
masm.convertUInt64ToDouble(resultReg, output, tempReg);
|
||||
else
|
||||
masm.convertUInt64ToDouble(s1Reg, output, Register::Invalid());
|
||||
masm.convertUInt64ToDouble(resultReg, output, Register::Invalid());
|
||||
|
||||
// output *= ScaleInv
|
||||
masm.mulDoublePtr(ImmPtr(&ScaleInv), tempReg, output);
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#define jit_ExecutableAllocator_h
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/XorShift128PlusRNG.h"
|
||||
#include "mozilla/Xoroshiro128PlusPlusRNG.h"
|
||||
|
||||
#include <limits>
|
||||
#include <stddef.h> // for ptrdiff_t
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/TaggedAnonymousMemory.h"
|
||||
#include "mozilla/XorShift128PlusRNG.h"
|
||||
#include "mozilla/Xoroshiro128PlusPlusRNG.h"
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
#include "jsmath.h"
|
||||
@@ -441,7 +441,7 @@ class ProcessExecutableMemory
|
||||
// Page where we should try to allocate next.
|
||||
size_t cursor_;
|
||||
|
||||
mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> rng_;
|
||||
mozilla::Maybe<mozilla::non_crypto::Xoroshiro128PlusPlusRNG> rng_;
|
||||
PageBitSet<MaxCodePages> pages_;
|
||||
|
||||
public:
|
||||
@@ -467,7 +467,7 @@ class ProcessExecutableMemory
|
||||
base_ = static_cast<uint8_t*>(p);
|
||||
|
||||
mozilla::Array<uint64_t, 2> seed;
|
||||
GenerateXorShift128PlusSeed(seed);
|
||||
GenerateXoroshiro128PlusPlusSeed(seed);
|
||||
rng_.emplace(seed[0], seed[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -968,6 +968,9 @@ void
|
||||
LIRGeneratorARM::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom *lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
|
||||
@@ -326,6 +326,9 @@ void
|
||||
LIRGeneratorARM64::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom *lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
|
||||
@@ -249,6 +249,9 @@ void
|
||||
LIRGeneratorMIPS::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom *lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
|
||||
@@ -178,6 +178,11 @@ LIRGeneratorMIPS64::lowerTruncateFToInt32(MTruncateToInt32* ins)
|
||||
void
|
||||
LIRGeneratorMIPS64::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom *lir = new(alloc()) LRandom(temp(), temp(), temp());
|
||||
LRandom *lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
}
|
||||
|
||||
@@ -8220,9 +8220,9 @@ class LArrowNewTarget : public LInstructionHelper<BOX_PIECES, 1, 0>
|
||||
|
||||
// Math.random().
|
||||
#ifdef JS_PUNBOX64
|
||||
# define LRANDOM_NUM_TEMPS 3
|
||||
# define LRANDOM_NUM_TEMPS 6
|
||||
#else
|
||||
# define LRANDOM_NUM_TEMPS 5
|
||||
# define LRANDOM_NUM_TEMPS 8
|
||||
#endif
|
||||
|
||||
class LRandom : public LInstructionHelper<1, 0, LRANDOM_NUM_TEMPS>
|
||||
@@ -8230,18 +8230,22 @@ class LRandom : public LInstructionHelper<1, 0, LRANDOM_NUM_TEMPS>
|
||||
public:
|
||||
LIR_HEADER(Random)
|
||||
LRandom(const LDefinition &temp0, const LDefinition &temp1,
|
||||
const LDefinition &temp2
|
||||
const LDefinition &temp2, const LDefinition &temp3,
|
||||
const LDefinition &temp4, const LDefinition &temp5
|
||||
#ifndef JS_PUNBOX64
|
||||
, const LDefinition &temp3, const LDefinition &temp4
|
||||
, const LDefinition &temp6, const LDefinition &temp7
|
||||
#endif
|
||||
)
|
||||
{
|
||||
setTemp(0, temp0);
|
||||
setTemp(1, temp1);
|
||||
setTemp(2, temp2);
|
||||
#ifndef JS_PUNBOX64
|
||||
setTemp(3, temp3);
|
||||
setTemp(4, temp4);
|
||||
setTemp(5, temp5);
|
||||
#ifndef JS_PUNBOX64
|
||||
setTemp(6, temp6);
|
||||
setTemp(7, temp7);
|
||||
#endif
|
||||
}
|
||||
const LDefinition* temp0() {
|
||||
@@ -8250,16 +8254,25 @@ class LRandom : public LInstructionHelper<1, 0, LRANDOM_NUM_TEMPS>
|
||||
const LDefinition* temp1() {
|
||||
return getTemp(1);
|
||||
}
|
||||
const LDefinition *temp2() {
|
||||
const LDefinition* temp2() {
|
||||
return getTemp(2);
|
||||
}
|
||||
#ifndef JS_PUNBOX64
|
||||
const LDefinition *temp3() {
|
||||
const LDefinition* temp3() {
|
||||
return getTemp(3);
|
||||
}
|
||||
const LDefinition *temp4() {
|
||||
const LDefinition* temp4() {
|
||||
return getTemp(4);
|
||||
}
|
||||
const LDefinition* temp5() {
|
||||
return getTemp(5);
|
||||
}
|
||||
#ifndef JS_PUNBOX64
|
||||
const LDefinition* temp6() {
|
||||
return getTemp(6);
|
||||
}
|
||||
const LDefinition* temp7() {
|
||||
return getTemp(7);
|
||||
}
|
||||
#endif
|
||||
|
||||
MRandom* mir() const {
|
||||
|
||||
@@ -412,6 +412,9 @@ void
|
||||
LIRGeneratorX64::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom *lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
|
||||
@@ -607,6 +607,9 @@ void
|
||||
LIRGeneratorX86::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom *lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Tuple.h"
|
||||
#include "mozilla/Variant.h"
|
||||
#include "mozilla/XorShift128PlusRNG.h"
|
||||
#include "mozilla/Xoroshiro128PlusPlusRNG.h"
|
||||
|
||||
#include "builtin/RegExp.h"
|
||||
#include "gc/Barrier.h"
|
||||
@@ -688,13 +688,13 @@ struct JSCompartment
|
||||
js::DtoaCache dtoaCache;
|
||||
|
||||
// Random number generator for Math.random().
|
||||
mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomNumberGenerator;
|
||||
mozilla::Maybe<mozilla::non_crypto::Xoroshiro128PlusPlusRNG> randomNumberGenerator;
|
||||
|
||||
// Initialize randomNumberGenerator if needed.
|
||||
void ensureRandomNumberGenerator();
|
||||
|
||||
private:
|
||||
mozilla::non_crypto::XorShift128PlusRNG randomKeyGenerator_;
|
||||
mozilla::non_crypto::Xoroshiro128PlusPlusRNG randomKeyGenerator_;
|
||||
|
||||
public:
|
||||
js::HashNumber randomHashCode();
|
||||
|
||||
+3
-3
@@ -737,9 +737,9 @@ js::GenerateRandomSeed()
|
||||
}
|
||||
|
||||
void
|
||||
js::GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed)
|
||||
js::GenerateXoroshiro128PlusPlusSeed(mozilla::Array<uint64_t, 2>& seed)
|
||||
{
|
||||
// XorShift128PlusRNG must be initialized with a non-zero seed.
|
||||
// Xoroshiro128PlusPlusRNG must be initialized with a non-zero seed.
|
||||
do {
|
||||
seed[0] = GenerateRandomSeed();
|
||||
seed[1] = GenerateRandomSeed();
|
||||
@@ -751,7 +751,7 @@ JSCompartment::ensureRandomNumberGenerator()
|
||||
{
|
||||
if (randomNumberGenerator.isNothing()) {
|
||||
mozilla::Array<uint64_t, 2> seed;
|
||||
GenerateXorShift128PlusSeed(seed);
|
||||
GenerateXoroshiro128PlusPlusSeed(seed);
|
||||
randomNumberGenerator.emplace(seed[0], seed[1]);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -89,9 +89,9 @@ extern uint64_t
|
||||
GenerateRandomSeed();
|
||||
|
||||
// Fill |seed[0]| and |seed[1]| with random bits, suitable for
|
||||
// seeding a XorShift128+ random number generator.
|
||||
// seeding a Xoroshiro128++ random number generator.
|
||||
extern void
|
||||
GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed);
|
||||
GenerateXoroshiro128PlusPlusSeed(mozilla::Array<uint64_t, 2>& seed);
|
||||
|
||||
extern uint64_t
|
||||
random_next(uint64_t* rngState, int bits);
|
||||
|
||||
@@ -765,13 +765,13 @@ JSRuntime::removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promis
|
||||
PromiseRejectionHandlingState::Handled, data);
|
||||
}
|
||||
|
||||
mozilla::non_crypto::XorShift128PlusRNG&
|
||||
mozilla::non_crypto::Xoroshiro128PlusPlusRNG&
|
||||
JSRuntime::randomKeyGenerator()
|
||||
{
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
|
||||
if (randomKeyGenerator_.isNothing()) {
|
||||
mozilla::Array<uint64_t, 2> seed;
|
||||
GenerateXorShift128PlusSeed(seed);
|
||||
GenerateXoroshiro128PlusPlusSeed(seed);
|
||||
randomKeyGenerator_.emplace(seed[0], seed[1]);
|
||||
}
|
||||
return randomKeyGenerator_.ref();
|
||||
@@ -784,11 +784,11 @@ JSRuntime::randomHashCodeScrambler()
|
||||
return mozilla::HashCodeScrambler(rng.next(), rng.next());
|
||||
}
|
||||
|
||||
mozilla::non_crypto::XorShift128PlusRNG
|
||||
mozilla::non_crypto::Xoroshiro128PlusPlusRNG
|
||||
JSRuntime::forkRandomKeyGenerator()
|
||||
{
|
||||
auto& rng = randomKeyGenerator();
|
||||
return mozilla::non_crypto::XorShift128PlusRNG(rng.next(), rng.next());
|
||||
return mozilla::non_crypto::Xoroshiro128PlusPlusRNG(rng.next(), rng.next());
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
+3
-3
@@ -767,12 +767,12 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
|
||||
private:
|
||||
// Used to generate random keys for hash tables.
|
||||
mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomKeyGenerator_;
|
||||
mozilla::non_crypto::XorShift128PlusRNG& randomKeyGenerator();
|
||||
mozilla::Maybe<mozilla::non_crypto::Xoroshiro128PlusPlusRNG> randomKeyGenerator_;
|
||||
mozilla::non_crypto::Xoroshiro128PlusPlusRNG& randomKeyGenerator();
|
||||
|
||||
public:
|
||||
mozilla::HashCodeScrambler randomHashCodeScrambler();
|
||||
mozilla::non_crypto::XorShift128PlusRNG forkRandomKeyGenerator();
|
||||
mozilla::non_crypto::Xoroshiro128PlusPlusRNG forkRandomKeyGenerator();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Self-hosting support
|
||||
|
||||
@@ -1595,7 +1595,7 @@ SavedStacks::chooseSamplingProbability(JSCompartment* compartment)
|
||||
|
||||
if (!bernoulliSeeded) {
|
||||
mozilla::Array<uint64_t, 2> seed;
|
||||
GenerateXorShift128PlusSeed(seed);
|
||||
GenerateXoroshiro128PlusPlusSeed(seed);
|
||||
bernoulli.setRandomState(seed[0], seed[1]);
|
||||
bernoulliSeeded = true;
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ class SavedStacks {
|
||||
|
||||
// Set the sampling random number generator's state to |state0| and
|
||||
// |state1|. One or the other must be non-zero. See the comments for
|
||||
// mozilla::non_crypto::XorShift128PlusRNG::setState for details.
|
||||
// mozilla::non_crypto::Xoroshiro128PlusPlusRNG::setState for details.
|
||||
void setRNGState(uint64_t state0, uint64_t state1) { bernoulli.setRandomState(state0, state1); }
|
||||
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#define mozilla_FastBernoulliTrial_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/XorShift128PlusRNG.h"
|
||||
#include "mozilla/Xoroshiro128PlusPlusRNG.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <stdint.h>
|
||||
@@ -303,7 +303,7 @@ class FastBernoulliTrial {
|
||||
double mInvLogNotProbability;
|
||||
|
||||
/* Our random number generator. */
|
||||
non_crypto::XorShift128PlusRNG mGenerator;
|
||||
non_crypto::Xoroshiro128PlusPlusRNG mGenerator;
|
||||
|
||||
/* The number of times |trial| should return false before next returning true. */
|
||||
size_t mSkipCount;
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
/* The xorshift128+ pseudo-random number generator. */
|
||||
|
||||
#ifndef mozilla_XorShift128Plus_h
|
||||
#define mozilla_XorShift128Plus_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace non_crypto {
|
||||
|
||||
/*
|
||||
* A stream of pseudo-random numbers generated using the xorshift+ technique
|
||||
* described here:
|
||||
*
|
||||
* Vigna, Sebastiano (2014). "Further scramblings of Marsaglia's xorshift
|
||||
* generators". arXiv:1404.0390 (http://arxiv.org/abs/1404.0390)
|
||||
*
|
||||
* That paper says:
|
||||
*
|
||||
* In particular, we propose a tightly coded xorshift128+ generator that
|
||||
* does not fail systematically any test from the BigCrush suite of TestU01
|
||||
* (even reversed) and generates 64 pseudorandom bits in 1.10 ns on an
|
||||
* Intel(R) Core(TM) i7-4770 CPU @3.40GHz (Haswell). It is the fastest
|
||||
* generator we are aware of with such empirical statistical properties.
|
||||
*
|
||||
* The stream of numbers produced by this method repeats every 2**128 - 1 calls
|
||||
* (i.e. never, for all practical purposes). Zero appears 2**64 - 1 times in
|
||||
* this period; all other numbers appear 2**64 times. Additionally, each *bit*
|
||||
* in the produced numbers repeats every 2**128 - 1 calls.
|
||||
*
|
||||
* This generator is not suitable as a cryptographically secure random number
|
||||
* generator.
|
||||
*/
|
||||
class XorShift128PlusRNG {
|
||||
uint64_t mState[2];
|
||||
|
||||
public:
|
||||
/*
|
||||
* Construct a xorshift128+ pseudo-random number stream using |aInitial0| and
|
||||
* |aInitial1| as the initial state. These MUST NOT both be zero.
|
||||
*
|
||||
* If the initial states contain many zeros, for a few iterations you'll see
|
||||
* many zeroes in the generated numbers. It's suggested to seed a SplitMix64
|
||||
* generator <http://xorshift.di.unimi.it/splitmix64.c> and use its first two
|
||||
* outputs to seed xorshift128+.
|
||||
*/
|
||||
XorShift128PlusRNG(uint64_t aInitial0, uint64_t aInitial1) {
|
||||
setState(aInitial0, aInitial1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
s1 ^= s1 << 23;
|
||||
mState[1] = s1 ^ s0 ^ (s1 >> 17) ^ (s0 >> 26);
|
||||
return mState[1] + s0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a pseudo-random floating-point value in the range [0, 1). More
|
||||
* precisely, choose an integer in the range [0, 2**53) and divide it by
|
||||
* 2**53. Given the 2**128 - 1 period noted above, the produced doubles are
|
||||
* all but uniformly distributed in this range.
|
||||
*/
|
||||
double nextDouble() {
|
||||
/*
|
||||
* Because the IEEE 64-bit floating point format stores the leading '1' bit
|
||||
* of the mantissa implicitly, it effectively represents a mantissa in the
|
||||
* range [0, 2**53) in only 52 bits. FloatingPoint<double>::kExponentShift
|
||||
* is the width of the bitfield in the in-memory format, so we must add one
|
||||
* to get the mantissa's range.
|
||||
*/
|
||||
static constexpr int kMantissaBits =
|
||||
mozilla::FloatingPoint<double>::kExponentShift + 1;
|
||||
uint64_t mantissa = next() & ((UINT64_C(1) << kMantissaBits) - 1);
|
||||
return double(mantissa) / (UINT64_C(1) << kMantissaBits);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the stream's current state to |aState0| and |aState1|. These must not
|
||||
* both be zero; ideally, they should have an almost even mix of zero and one
|
||||
* bits.
|
||||
*/
|
||||
void setState(uint64_t aState0, uint64_t aState1) {
|
||||
MOZ_ASSERT(aState0 || aState1);
|
||||
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
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_XorShift128Plus_h
|
||||
@@ -0,0 +1,125 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
/* The xoroshiro128++ pseudo-random number generator. */
|
||||
|
||||
#ifndef mozilla_Xoroshiro128PlusPlus_h
|
||||
#define mozilla_Xoroshiro128PlusPlus_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace non_crypto {
|
||||
|
||||
/*
|
||||
* A stream of pseudo-random numbers generated using the xoroshiro128++ technique described here:
|
||||
*
|
||||
* Blackman, David and Vigna, Sebastiano (2019). "A PRNG Shootout"
|
||||
* https://prng.di.unimi.it/
|
||||
*
|
||||
* That paper says:
|
||||
*
|
||||
* xoshiro256++/xoshiro256** (XOR/shift/rotate) are our all-purpose generators (not cryptographically secure
|
||||
* generators, though, like all PRNGs in these pages). They have excellent (sub-ns) speed, a state space
|
||||
* (256 bits) that is large enough for any parallel application, and they pass all tests we are aware of.
|
||||
* If you are tight on space, xoroshiro128++/xoroshiro128** (XOR/rotate/shift/rotate) and xoroshiro128+ have
|
||||
* the same speed and use half of the space; the same comments apply.
|
||||
* They are suitable only for low-scale parallel applications.
|
||||
*
|
||||
* The stream of numbers produced by this method repeats every 2**256 - 1 calls (i.e. never, for all practical
|
||||
* purposes).
|
||||
*
|
||||
*/
|
||||
class Xoroshiro128PlusPlusRNG {
|
||||
uint64_t mState[2];
|
||||
|
||||
public:
|
||||
/*
|
||||
* Construct a xoroshiro128++ pseudo-random number stream using |aInitial0| and |aInitial1| as the initial state.
|
||||
* These MUST NOT both be zero.
|
||||
*
|
||||
* If the initial states contain many zeros, for a few iterations you'll see many zeroes in the generated numbers.
|
||||
* It's suggested to seed a SplitMix64 generator <http://xorshift.di.unimi.it/splitmix64.c> and use its first two
|
||||
* outputs to seed xoroshiro128++.
|
||||
*/
|
||||
Xoroshiro128PlusPlusRNG(uint64_t aInitial0, uint64_t aInitial1) {
|
||||
setState(aInitial0, aInitial1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function:
|
||||
* Rotate a 64-bit number left by a specified number of positions
|
||||
*/
|
||||
static inline uint64_t rotl(const uint64_t x, int k) {
|
||||
return (x << k) | (x >> (64 - k));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pseudo-random 64-bit number.
|
||||
*/
|
||||
uint64_t next() {
|
||||
/*
|
||||
* The offsetOfState*() methods below are provided so that exceedingly-rare callers (like our JIT...) 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*()!
|
||||
*/
|
||||
const uint64_t s0 = mState[0];
|
||||
uint64_t s1 = mState[1];
|
||||
const uint64_t result = rotl(s0 + s1, 17) + s0;
|
||||
|
||||
s1 ^= s0;
|
||||
mState[0] = rotl(s0, 49) ^ s1 ^ (s1 << 21); // a, b
|
||||
mState[1] = rotl(s1, 28); // c
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a pseudo-random floating-point value in the range [0, 1). More
|
||||
* precisely, choose an integer in the range [0, 2**53) and divide it by
|
||||
* 2**53. Given the 2**256 - 1 period noted above, the produced doubles are
|
||||
* all but uniformly distributed in this range.
|
||||
*/
|
||||
double nextDouble() {
|
||||
/*
|
||||
* Because the IEEE 64-bit floating point format stores the leading '1' bit
|
||||
* of the mantissa implicitly, it effectively represents a mantissa in the
|
||||
* range [0, 2**53) in only 52 bits. FloatingPoint<double>::kExponentShift
|
||||
* is the width of the bitfield in the in-memory format, so we must add one
|
||||
* to get the mantissa's range.
|
||||
*/
|
||||
static constexpr int kMantissaBits =
|
||||
mozilla::FloatingPoint<double>::kExponentShift + 1;
|
||||
uint64_t mantissa = next() & ((UINT64_C(1) << kMantissaBits) - 1);
|
||||
return double(mantissa) / (UINT64_C(1) << kMantissaBits);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the stream's current state to |aState0| and |aState1|. These must not
|
||||
* both be zero; ideally, they should have an almost even mix of zero and one
|
||||
* bits.
|
||||
*/
|
||||
void setState(uint64_t aState0, uint64_t aState1) {
|
||||
MOZ_ASSERT(aState0 || aState1);
|
||||
mState[0] = aState0;
|
||||
mState[1] = aState1;
|
||||
}
|
||||
|
||||
static size_t offsetOfState0() {
|
||||
return offsetof(Xoroshiro128PlusPlusRNG, mState[0]);
|
||||
}
|
||||
static size_t offsetOfState1() {
|
||||
return offsetof(Xoroshiro128PlusPlusRNG, mState[1]);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace non_crypto
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_Xoroshiro128PlusPlus_h
|
||||
+1
-1
@@ -103,7 +103,7 @@ EXPORTS.mozilla = [
|
||||
'Vector.h',
|
||||
'WeakPtr.h',
|
||||
'WrappingOperations.h',
|
||||
'XorShift128PlusRNG.h',
|
||||
'Xoroshiro128PlusPlusRNG.h',
|
||||
]
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
|
||||
Reference in New Issue
Block a user