From 37c42449b4ece3f55f1f7dcb8078e7cd6cda090a Mon Sep 17 00:00:00 2001 From: roytam1 Date: Sat, 17 Jan 2026 23:02:38 +0800 Subject: [PATCH] Revert "Issue #2895 - Replace XorShift128+ with Xoroshiro128++" This reverts commit 1ba1395ca39fc4470c50e24866aa42f53cb05877. non-64bit portion seems causing huge memory allocation and OOM later. --- js/src/jit/CodeGenerator.cpp | 93 ++++++------- js/src/jit/ExecutableAllocator.h | 27 ++-- js/src/jit/ProcessExecutableMemory.cpp | 6 +- js/src/jit/arm/Lowering-arm.cpp | 6 - js/src/jit/arm64/Lowering-arm64.cpp | 3 - js/src/jit/mips32/Lowering-mips32.cpp | 6 - js/src/jit/mips64/Lowering-mips64.cpp | 7 +- js/src/jit/shared/LIR-shared.h | 45 ++----- js/src/jit/x64/Lowering-x64.cpp | 3 - js/src/jit/x64/MacroAssembler-x64-inl.h | 4 + js/src/jit/x86/Lowering-x86.cpp | 6 - js/src/jit/x86/MacroAssembler-x86-inl.h | 6 + js/src/jscompartment.h | 6 +- js/src/jsmath.cpp | 6 +- js/src/jsmath.h | 4 +- .../tests/manual/random-uniformity-test.html | 110 --------------- js/src/vm/Runtime.cpp | 8 +- js/src/vm/Runtime.h | 6 +- js/src/vm/SavedStacks.cpp | 2 +- js/src/vm/SavedStacks.h | 2 +- mfbt/FastBernoulliTrial.h | 4 +- mfbt/XorShift128PlusRNG.h | 121 +++++++++++++++++ mfbt/Xoroshiro128PlusPlusRNG.h | 125 ------------------ mfbt/moz.build | 2 +- 24 files changed, 217 insertions(+), 391 deletions(-) delete mode 100644 js/src/tests/manual/random-uniformity-test.html create mode 100644 mfbt/XorShift128PlusRNG.h delete mode 100644 mfbt/Xoroshiro128PlusPlusRNG.h diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 301a2bc34..fe37a22a3 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -12260,84 +12260,71 @@ CodeGenerator::visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins) void CodeGenerator::visitRandom(LRandom* ins) { - using mozilla::non_crypto::Xoroshiro128PlusPlusRNG; + using mozilla::non_crypto::XorShift128PlusRNG; FloatRegister output = ToFloatRegister(ins->output()); Register tempReg = ToRegister(ins->temp0()); + #ifdef JS_PUNBOX64 Register64 s0Reg(ToRegister(ins->temp1())); Register64 s1Reg(ToRegister(ins->temp2())); - // Helper registers for intermediate and final results - Register64 imr1Reg(ToRegister(ins->temp3())); - Register64 imr2Reg(ToRegister(ins->temp4())); - Register64 resultReg(ToRegister(ins->temp5())); #else Register64 s0Reg(ToRegister(ins->temp1()), ToRegister(ins->temp2())); Register64 s1Reg(ToRegister(ins->temp3()), ToRegister(ins->temp4())); - // Helper registers for intermediate and final results - Register64 imr1Reg(ToRegister(ins->temp5()), ToRegister(ins->temp6())); - Register64 imr2Reg(ToRegister(ins->temp7()), ToRegister(ins->temp8())); - Register64 resultReg(ToRegister(ins->temp9()), ToRegister(ins->temp10())); #endif const void* rng = gen->compartment->addressOfRandomNumberGenerator(); masm.movePtr(ImmPtr(rng), tempReg); - static_assert(sizeof(Xoroshiro128PlusPlusRNG) == 2 * sizeof(uint64_t), - "Code below assumes Xoroshiro128PlusPlusRNG contains two uint64_t values"); + static_assert(sizeof(XorShift128PlusRNG) == 2 * sizeof(uint64_t), + "Code below assumes XorShift128PlusRNG contains two uint64_t values"); - Address state0Addr(tempReg, Xoroshiro128PlusPlusRNG::offsetOfState0()); - Address state1Addr(tempReg, Xoroshiro128PlusPlusRNG::offsetOfState1()); + Address state0Addr(tempReg, XorShift128PlusRNG::offsetOfState0()); + Address state1Addr(tempReg, XorShift128PlusRNG::offsetOfState1()); - // const uint64_t s0 = mState[0]; - masm.load64(state0Addr, s0Reg); - // 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); -#ifdef JS_PUNBOX64 - masm.rotateLeft64(Imm32(17), imr1Reg, resultReg); -#else - masm.rotateLeft64(Imm32(17), imr1Reg, resultReg, tempReg); -#endif - masm.add64(s0Reg, resultReg); - - // s1 ^= s0; + // 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); - - // mState[0] = rotl(s0, 49) ^ s1 ^ (s1 << 21); // a, b -#ifdef JS_PUNBOX64 - masm.rotateLeft64(Imm32(49), s0Reg, imr1Reg); // imr = s0 rotl 49 -#else - masm.rotateLeft64(Imm32(49), s0Reg, imr1Reg, tempReg); // imr = s0 rotl 49 -#endif - 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); - // mState[1] = rotl(s1, 28); // c -#ifdef JS_PUNBOX64 - masm.rotateLeft64(Imm32(28), s1Reg, imr1Reg); -#else - masm.rotateLeft64(Imm32(28), s1Reg, imr1Reg, tempReg); -#endif - masm.store64(imr1Reg, state1Addr); + // s1 ^= s1 >> 17 + masm.move64(s1Reg, s0Reg); + masm.rshift64(Imm32(17), s1Reg); + masm.xor64(s0Reg, s1Reg); - // See comment in Xoroshiro128PlusPlusRNG::nextDouble(). + // 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] + masm.load64(state0Addr, s0Reg); + masm.add64(s0Reg, s1Reg); + + // See comment in XorShift128PlusRNG::nextDouble(). static const int MantissaBits = FloatingPoint::kExponentShift + 1; static const double ScaleInv = double(1) / (1ULL << MantissaBits); - // Mask the result bits to mantissa size - masm.and64(Imm64((1ULL << MantissaBits) - 1), resultReg); + masm.and64(Imm64((1ULL << MantissaBits) - 1), s1Reg); if (masm.convertUInt64ToDoubleNeedsTemp()) - masm.convertUInt64ToDouble(resultReg, output, tempReg); + masm.convertUInt64ToDouble(s1Reg, output, tempReg); else - masm.convertUInt64ToDouble(resultReg, output, Register::Invalid()); + masm.convertUInt64ToDouble(s1Reg, output, Register::Invalid()); // output *= ScaleInv masm.mulDoublePtr(ImmPtr(&ScaleInv), tempReg, output); diff --git a/js/src/jit/ExecutableAllocator.h b/js/src/jit/ExecutableAllocator.h index daf65f69f..209094d7e 100644 --- a/js/src/jit/ExecutableAllocator.h +++ b/js/src/jit/ExecutableAllocator.h @@ -1,7 +1,6 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * - * Copyright (C) 2008 Apple Inc. - * Copyright (C) 2026 Moonchild Productions. + * Copyright (C) 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -12,24 +11,24 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL APPLE INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef jit_ExecutableAllocator_h #define jit_ExecutableAllocator_h #include "mozilla/Maybe.h" -#include "mozilla/Xoroshiro128PlusPlusRNG.h" +#include "mozilla/XorShift128PlusRNG.h" #include #include // for ptrdiff_t diff --git a/js/src/jit/ProcessExecutableMemory.cpp b/js/src/jit/ProcessExecutableMemory.cpp index 7acfc1f80..92c2e44d8 100644 --- a/js/src/jit/ProcessExecutableMemory.cpp +++ b/js/src/jit/ProcessExecutableMemory.cpp @@ -10,7 +10,7 @@ #include "mozilla/DebugOnly.h" #include "mozilla/Maybe.h" #include "mozilla/TaggedAnonymousMemory.h" -#include "mozilla/Xoroshiro128PlusPlusRNG.h" +#include "mozilla/XorShift128PlusRNG.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 rng_; + mozilla::Maybe rng_; PageBitSet pages_; public: @@ -467,7 +467,7 @@ class ProcessExecutableMemory base_ = static_cast(p); mozilla::Array seed; - GenerateXoroshiro128PlusPlusSeed(seed); + GenerateXorShift128PlusSeed(seed); rng_.emplace(seed[0], seed[1]); return true; } diff --git a/js/src/jit/arm/Lowering-arm.cpp b/js/src/jit/arm/Lowering-arm.cpp index 5e07983c1..6a58e55c4 100644 --- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -978,12 +978,6 @@ void LIRGeneratorARM::visitRandom(MRandom* ins) { LRandom *lir = new(alloc()) LRandom(temp(), - temp(), - temp(), - temp(), - temp(), - temp(), - temp(), temp(), temp(), temp(), diff --git a/js/src/jit/arm64/Lowering-arm64.cpp b/js/src/jit/arm64/Lowering-arm64.cpp index 2e688c8e1..59c6a6a25 100644 --- a/js/src/jit/arm64/Lowering-arm64.cpp +++ b/js/src/jit/arm64/Lowering-arm64.cpp @@ -326,9 +326,6 @@ void LIRGeneratorARM64::visitRandom(MRandom* ins) { LRandom *lir = new(alloc()) LRandom(temp(), - temp(), - temp(), - temp(), temp(), temp()); defineFixed(lir, ins, LFloatReg(ReturnDoubleReg)); diff --git a/js/src/jit/mips32/Lowering-mips32.cpp b/js/src/jit/mips32/Lowering-mips32.cpp index 8d2af2ef8..b8a2a0f13 100644 --- a/js/src/jit/mips32/Lowering-mips32.cpp +++ b/js/src/jit/mips32/Lowering-mips32.cpp @@ -249,12 +249,6 @@ void LIRGeneratorMIPS::visitRandom(MRandom* ins) { LRandom *lir = new(alloc()) LRandom(temp(), - temp(), - temp(), - temp(), - temp(), - temp(), - temp(), temp(), temp(), temp(), diff --git a/js/src/jit/mips64/Lowering-mips64.cpp b/js/src/jit/mips64/Lowering-mips64.cpp index 945cb8e6a..cf4e005db 100644 --- a/js/src/jit/mips64/Lowering-mips64.cpp +++ b/js/src/jit/mips64/Lowering-mips64.cpp @@ -178,11 +178,6 @@ LIRGeneratorMIPS64::lowerTruncateFToInt32(MTruncateToInt32* ins) void LIRGeneratorMIPS64::visitRandom(MRandom* ins) { - LRandom *lir = new(alloc()) LRandom(temp(), - temp(), - temp(), - temp(), - temp(), - temp()); + LRandom *lir = new(alloc()) LRandom(temp(), temp(), temp()); defineFixed(lir, ins, LFloatReg(ReturnDoubleReg)); } diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 2fc250afc..3ff9758cd 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -8244,9 +8244,9 @@ class LArrowNewTarget : public LInstructionHelper // Math.random(). #ifdef JS_PUNBOX64 -# define LRANDOM_NUM_TEMPS 6 +# define LRANDOM_NUM_TEMPS 3 #else -# define LRANDOM_NUM_TEMPS 11 +# define LRANDOM_NUM_TEMPS 5 #endif class LRandom : public LInstructionHelper<1, 0, LRANDOM_NUM_TEMPS> @@ -8254,27 +8254,18 @@ 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 &temp3, - const LDefinition &temp4, const LDefinition &temp5 + const LDefinition &temp2 #ifndef JS_PUNBOX64 - , const LDefinition &temp6, const LDefinition &temp7 - , const LDefinition &temp8, const LDefinition &temp9 - , const LDefinition &temp10 + , const LDefinition &temp3, const LDefinition &temp4 #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); - setTemp(8, temp8); - setTemp(9, temp9); - setTemp(10, temp10); #endif } const LDefinition* temp0() { @@ -8283,34 +8274,16 @@ class LRandom : public LInstructionHelper<1, 0, LRANDOM_NUM_TEMPS> const LDefinition* temp1() { return getTemp(1); } - const LDefinition* temp2() { + const LDefinition *temp2() { return getTemp(2); } - const LDefinition* temp3() { +#ifndef JS_PUNBOX64 + 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); - } - const LDefinition* temp8() { - return getTemp(8); - } - const LDefinition* temp9() { - return getTemp(9); - } - const LDefinition* temp10() { - return getTemp(10); - } #endif MRandom* mir() const { diff --git a/js/src/jit/x64/Lowering-x64.cpp b/js/src/jit/x64/Lowering-x64.cpp index 6ad54fa27..ee9f9ac41 100644 --- a/js/src/jit/x64/Lowering-x64.cpp +++ b/js/src/jit/x64/Lowering-x64.cpp @@ -412,9 +412,6 @@ void LIRGeneratorX64::visitRandom(MRandom* ins) { LRandom *lir = new(alloc()) LRandom(temp(), - temp(), - temp(), - temp(), temp(), temp()); defineFixed(lir, ins, LFloatReg(ReturnDoubleReg)); diff --git a/js/src/jit/x64/MacroAssembler-x64-inl.h b/js/src/jit/x64/MacroAssembler-x64-inl.h index f013b42c3..d40d9e093 100644 --- a/js/src/jit/x64/MacroAssembler-x64-inl.h +++ b/js/src/jit/x64/MacroAssembler-x64-inl.h @@ -412,6 +412,7 @@ MacroAssembler::rshift64Arithmetic(Register shift, Register64 srcDest) void MacroAssembler::rotateLeft64(Register count, Register64 src, Register64 dest) { + MOZ_ASSERT(src == dest, "defineReuseInput"); MOZ_ASSERT(count == ecx, "defineFixed(ecx)"); rolq_cl(dest.reg); @@ -427,6 +428,7 @@ MacroAssembler::rotateLeft64(Register count, Register64 src, Register64 dest, Re void MacroAssembler::rotateRight64(Register count, Register64 src, Register64 dest) { + MOZ_ASSERT(src == dest, "defineReuseInput"); MOZ_ASSERT(count == ecx, "defineFixed(ecx)"); rorq_cl(dest.reg); @@ -442,6 +444,7 @@ MacroAssembler::rotateRight64(Register count, Register64 src, Register64 dest, R void MacroAssembler::rotateLeft64(Imm32 count, Register64 src, Register64 dest) { + MOZ_ASSERT(src == dest, "defineReuseInput"); rolq(count, dest.reg); } @@ -455,6 +458,7 @@ MacroAssembler::rotateLeft64(Imm32 count, Register64 src, Register64 dest, Regis void MacroAssembler::rotateRight64(Imm32 count, Register64 src, Register64 dest) { + MOZ_ASSERT(src == dest, "defineReuseInput"); rorq(count, dest.reg); } diff --git a/js/src/jit/x86/Lowering-x86.cpp b/js/src/jit/x86/Lowering-x86.cpp index 4080d3d8c..8cfc59a50 100644 --- a/js/src/jit/x86/Lowering-x86.cpp +++ b/js/src/jit/x86/Lowering-x86.cpp @@ -607,12 +607,6 @@ void LIRGeneratorX86::visitRandom(MRandom* ins) { LRandom *lir = new(alloc()) LRandom(temp(), - temp(), - temp(), - temp(), - temp(), - temp(), - temp(), temp(), temp(), temp(), diff --git a/js/src/jit/x86/MacroAssembler-x86-inl.h b/js/src/jit/x86/MacroAssembler-x86-inl.h index 9b3b67d51..cd6384eaa 100644 --- a/js/src/jit/x86/MacroAssembler-x86-inl.h +++ b/js/src/jit/x86/MacroAssembler-x86-inl.h @@ -497,6 +497,7 @@ MacroAssembler::rshift64Arithmetic(Register shift, Register64 srcDest) void MacroAssembler::rotateLeft64(Register count, Register64 src, Register64 dest, Register temp) { + MOZ_ASSERT(src == dest, "defineReuseInput"); MOZ_ASSERT(count == ecx, "defineFixed(ecx)"); Label done; @@ -515,6 +516,7 @@ MacroAssembler::rotateLeft64(Register count, Register64 src, Register64 dest, Re void MacroAssembler::rotateRight64(Register count, Register64 src, Register64 dest, Register temp) { + MOZ_ASSERT(src == dest, "defineReuseInput"); MOZ_ASSERT(count == ecx, "defineFixed(ecx)"); Label done; @@ -533,6 +535,8 @@ MacroAssembler::rotateRight64(Register count, Register64 src, Register64 dest, R void MacroAssembler::rotateLeft64(Imm32 count, Register64 src, Register64 dest, Register temp) { + MOZ_ASSERT(src == dest, "defineReuseInput"); + int32_t amount = count.value & 0x3f; if ((amount & 0x1f) != 0) { movl(dest.high, temp); @@ -547,6 +551,8 @@ MacroAssembler::rotateLeft64(Imm32 count, Register64 src, Register64 dest, Regis void MacroAssembler::rotateRight64(Imm32 count, Register64 src, Register64 dest, Register temp) { + MOZ_ASSERT(src == dest, "defineReuseInput"); + int32_t amount = count.value & 0x3f; if ((amount & 0x1f) != 0) { movl(dest.high, temp); diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index dbecb3c73..bfb2004a4 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -11,7 +11,7 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/Tuple.h" #include "mozilla/Variant.h" -#include "mozilla/Xoroshiro128PlusPlusRNG.h" +#include "mozilla/XorShift128PlusRNG.h" #include "builtin/RegExp.h" #include "gc/Barrier.h" @@ -696,13 +696,13 @@ struct JSCompartment js::DtoaCache dtoaCache; // Random number generator for Math.random(). - mozilla::Maybe randomNumberGenerator; + mozilla::Maybe randomNumberGenerator; // Initialize randomNumberGenerator if needed. void ensureRandomNumberGenerator(); private: - mozilla::non_crypto::Xoroshiro128PlusPlusRNG randomKeyGenerator_; + mozilla::non_crypto::XorShift128PlusRNG randomKeyGenerator_; public: js::HashNumber randomHashCode(); diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index 1817f6d19..0e47796ea 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -737,9 +737,9 @@ js::GenerateRandomSeed() } void -js::GenerateXoroshiro128PlusPlusSeed(mozilla::Array& seed) +js::GenerateXorShift128PlusSeed(mozilla::Array& seed) { - // Xoroshiro128PlusPlusRNG must be initialized with a non-zero seed. + // XorShift128PlusRNG 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 seed; - GenerateXoroshiro128PlusPlusSeed(seed); + GenerateXorShift128PlusSeed(seed); randomNumberGenerator.emplace(seed[0], seed[1]); } } diff --git a/js/src/jsmath.h b/js/src/jsmath.h index 6fdc7833e..558d3d356 100644 --- a/js/src/jsmath.h +++ b/js/src/jsmath.h @@ -89,9 +89,9 @@ extern uint64_t GenerateRandomSeed(); // Fill |seed[0]| and |seed[1]| with random bits, suitable for -// seeding a Xoroshiro128++ random number generator. +// seeding a XorShift128+ random number generator. extern void -GenerateXoroshiro128PlusPlusSeed(mozilla::Array& seed); +GenerateXorShift128PlusSeed(mozilla::Array& seed); extern uint64_t random_next(uint64_t* rngState, int bits); diff --git a/js/src/tests/manual/random-uniformity-test.html b/js/src/tests/manual/random-uniformity-test.html deleted file mode 100644 index 8a4bb1c67..000000000 --- a/js/src/tests/manual/random-uniformity-test.html +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - Math.random() uniformity and speed test using canvas - - - -
- - - - - - - - - - - -
-
- - - diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 512885ed7..9cc608717 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -781,13 +781,13 @@ JSRuntime::removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promis PromiseRejectionHandlingState::Handled, data); } -mozilla::non_crypto::Xoroshiro128PlusPlusRNG& +mozilla::non_crypto::XorShift128PlusRNG& JSRuntime::randomKeyGenerator() { MOZ_ASSERT(CurrentThreadCanAccessRuntime(this)); if (randomKeyGenerator_.isNothing()) { mozilla::Array seed; - GenerateXoroshiro128PlusPlusSeed(seed); + GenerateXorShift128PlusSeed(seed); randomKeyGenerator_.emplace(seed[0], seed[1]); } return randomKeyGenerator_.ref(); @@ -800,11 +800,11 @@ JSRuntime::randomHashCodeScrambler() return mozilla::HashCodeScrambler(rng.next(), rng.next()); } -mozilla::non_crypto::Xoroshiro128PlusPlusRNG +mozilla::non_crypto::XorShift128PlusRNG JSRuntime::forkRandomKeyGenerator() { auto& rng = randomKeyGenerator(); - return mozilla::non_crypto::Xoroshiro128PlusPlusRNG(rng.next(), rng.next()); + return mozilla::non_crypto::XorShift128PlusRNG(rng.next(), rng.next()); } void diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 3e8cfd349..ea5748814 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -778,12 +778,12 @@ struct JSRuntime : public JS::shadow::Runtime, private: // Used to generate random keys for hash tables. - mozilla::Maybe randomKeyGenerator_; - mozilla::non_crypto::Xoroshiro128PlusPlusRNG& randomKeyGenerator(); + mozilla::Maybe randomKeyGenerator_; + mozilla::non_crypto::XorShift128PlusRNG& randomKeyGenerator(); public: mozilla::HashCodeScrambler randomHashCodeScrambler(); - mozilla::non_crypto::Xoroshiro128PlusPlusRNG forkRandomKeyGenerator(); + mozilla::non_crypto::XorShift128PlusRNG forkRandomKeyGenerator(); //------------------------------------------------------------------------- // Self-hosting support diff --git a/js/src/vm/SavedStacks.cpp b/js/src/vm/SavedStacks.cpp index 11af0388e..e82cc46e9 100644 --- a/js/src/vm/SavedStacks.cpp +++ b/js/src/vm/SavedStacks.cpp @@ -1595,7 +1595,7 @@ SavedStacks::chooseSamplingProbability(JSCompartment* compartment) if (!bernoulliSeeded) { mozilla::Array seed; - GenerateXoroshiro128PlusPlusSeed(seed); + GenerateXorShift128PlusSeed(seed); bernoulli.setRandomState(seed[0], seed[1]); bernoulliSeeded = true; } diff --git a/js/src/vm/SavedStacks.h b/js/src/vm/SavedStacks.h index 0810073ca..3ea6c4087 100644 --- a/js/src/vm/SavedStacks.h +++ b/js/src/vm/SavedStacks.h @@ -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::Xoroshiro128PlusPlusRNG::setState for details. + // mozilla::non_crypto::XorShift128PlusRNG::setState for details. void setRNGState(uint64_t state0, uint64_t state1) { bernoulli.setRandomState(state0, state1); } size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf); diff --git a/mfbt/FastBernoulliTrial.h b/mfbt/FastBernoulliTrial.h index 157900d6b..7e38b70ab 100644 --- a/mfbt/FastBernoulliTrial.h +++ b/mfbt/FastBernoulliTrial.h @@ -8,7 +8,7 @@ #define mozilla_FastBernoulliTrial_h #include "mozilla/Assertions.h" -#include "mozilla/Xoroshiro128PlusPlusRNG.h" +#include "mozilla/XorShift128PlusRNG.h" #include #include @@ -304,7 +304,7 @@ class FastBernoulliTrial { double mInvLogNotProbability; /* Our random number generator. */ - non_crypto::Xoroshiro128PlusPlusRNG mGenerator; + non_crypto::XorShift128PlusRNG mGenerator; /* The number of times |trial| should return false before next returning true. */ size_t mSkipCount; diff --git a/mfbt/XorShift128PlusRNG.h b/mfbt/XorShift128PlusRNG.h new file mode 100644 index 000000000..2f182f0fb --- /dev/null +++ b/mfbt/XorShift128PlusRNG.h @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 + +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 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::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::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 diff --git a/mfbt/Xoroshiro128PlusPlusRNG.h b/mfbt/Xoroshiro128PlusPlusRNG.h deleted file mode 100644 index 655cec258..000000000 --- a/mfbt/Xoroshiro128PlusPlusRNG.h +++ /dev/null @@ -1,125 +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 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 - -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 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::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::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 diff --git a/mfbt/moz.build b/mfbt/moz.build index aab03ef6f..09add9f10 100644 --- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -103,7 +103,7 @@ EXPORTS.mozilla = [ 'Vector.h', 'WeakPtr.h', 'WrappingOperations.h', - 'Xoroshiro128PlusPlusRNG.h', + 'XorShift128PlusRNG.h', ] if CONFIG['OS_ARCH'] == 'WINNT':