]> Cypherpunks repositories - gostls13.git/commitdiff
math/rand, math/rand/v2: use ChaCha8 for global rand
authorRuss Cox <rsc@golang.org>
Sun, 6 Aug 2023 03:26:28 +0000 (13:26 +1000)
committerRuss Cox <rsc@golang.org>
Tue, 5 Dec 2023 20:34:30 +0000 (20:34 +0000)
Move ChaCha8 code into internal/chacha8rand and use it to implement
runtime.rand, which is used for the unseeded global source for
both math/rand and math/rand/v2. This also affects the calculation of
the start point for iteration over very very large maps (when the
32-bit fastrand is not big enough).

The benefit is that misuse of the global random number generators
in math/rand and math/rand/v2 in contexts where non-predictable
randomness is important for security reasons is no longer a
security problem, removing a common mistake among programmers
who are unaware of the different kinds of randomness.

The cost is an extra 304 bytes per thread stored in the m struct
plus 2-3ns more per random uint64 due to the more sophisticated
algorithm. Using PCG looks like it would cost about the same,
although I haven't benchmarked that.

Before this, the math/rand and math/rand/v2 global generator
was wyrand (https://github.com/wangyi-fudan/wyhash).
For math/rand, using wyrand instead of the Mitchell/Reeds/Thompson
ALFG was justifiable, since the latter was not any better.
But for math/rand/v2, the global generator really should be
at least as good as one of the well-studied, specific algorithms
provided directly by the package, and it's not.

(Wyrand is still reasonable for scheduling and cache decisions.)

Good randomness does have a cost: about twice wyrand.

Also rationalize the various runtime rand references.

goos: linux
goarch: amd64
pkg: math/rand/v2
cpu: AMD Ryzen 9 7950X 16-Core Processor
                        │ bbb48afeb7.amd64 │           5cf807d1ea.amd64           │
                        │      sec/op      │    sec/op     vs base                │
ChaCha8-32                     1.862n ± 2%    1.861n ± 2%        ~ (p=0.825 n=20)
PCG_DXSM-32                    1.471n ± 1%    1.460n ± 2%        ~ (p=0.153 n=20)
SourceUint64-32                1.636n ± 2%    1.582n ± 1%   -3.30% (p=0.000 n=20)
GlobalInt64-32                 2.087n ± 1%    3.663n ± 1%  +75.54% (p=0.000 n=20)
GlobalInt64Parallel-32        0.1042n ± 1%   0.2026n ± 1%  +94.48% (p=0.000 n=20)
GlobalUint64-32                2.263n ± 2%    3.724n ± 1%  +64.57% (p=0.000 n=20)
GlobalUint64Parallel-32       0.1019n ± 1%   0.1973n ± 1%  +93.67% (p=0.000 n=20)
Int64-32                       1.771n ± 1%    1.774n ± 1%        ~ (p=0.449 n=20)
Uint64-32                      1.863n ± 2%    1.866n ± 1%        ~ (p=0.364 n=20)
GlobalIntN1000-32              3.134n ± 3%    4.730n ± 2%  +50.95% (p=0.000 n=20)
IntN1000-32                    2.489n ± 1%    2.489n ± 1%        ~ (p=0.683 n=20)
Int64N1000-32                  2.521n ± 1%    2.516n ± 1%        ~ (p=0.394 n=20)
Int64N1e8-32                   2.479n ± 1%    2.478n ± 2%        ~ (p=0.743 n=20)
Int64N1e9-32                   2.530n ± 2%    2.514n ± 2%        ~ (p=0.193 n=20)
Int64N2e9-32                   2.501n ± 1%    2.494n ± 1%        ~ (p=0.616 n=20)
Int64N1e18-32                  3.227n ± 1%    3.205n ± 1%        ~ (p=0.101 n=20)
Int64N2e18-32                  3.647n ± 1%    3.599n ± 1%        ~ (p=0.019 n=20)
Int64N4e18-32                  5.135n ± 1%    5.069n ± 2%        ~ (p=0.034 n=20)
Int32N1000-32                  2.657n ± 1%    2.637n ± 1%        ~ (p=0.180 n=20)
Int32N1e8-32                   2.636n ± 1%    2.636n ± 1%        ~ (p=0.763 n=20)
Int32N1e9-32                   2.660n ± 2%    2.638n ± 1%        ~ (p=0.358 n=20)
Int32N2e9-32                   2.662n ± 2%    2.618n ± 2%        ~ (p=0.064 n=20)
Float32-32                     2.272n ± 2%    2.239n ± 2%        ~ (p=0.194 n=20)
Float64-32                     2.272n ± 1%    2.286n ± 2%        ~ (p=0.763 n=20)
ExpFloat64-32                  3.762n ± 1%    3.744n ± 1%        ~ (p=0.171 n=20)
NormFloat64-32                 3.706n ± 1%    3.655n ± 2%        ~ (p=0.066 n=20)
Perm3-32                       32.93n ± 3%    34.62n ± 1%   +5.13% (p=0.000 n=20)
Perm30-32                      202.9n ± 1%    204.0n ± 1%        ~ (p=0.482 n=20)
Perm30ViaShuffle-32            115.0n ± 1%    114.9n ± 1%        ~ (p=0.358 n=20)
ShuffleOverhead-32             112.8n ± 1%    112.7n ± 1%        ~ (p=0.692 n=20)
Concurrent-32                  2.107n ± 0%    3.725n ± 1%  +76.75% (p=0.000 n=20)

goos: darwin
goarch: arm64
pkg: math/rand/v2
                       │ bbb48afeb7.arm64 │           5cf807d1ea.arm64            │
                       │      sec/op      │    sec/op     vs base                 │
ChaCha8-8                     2.480n ± 0%    2.429n ± 0%    -2.04% (p=0.000 n=20)
PCG_DXSM-8                    2.531n ± 0%    2.530n ± 0%         ~ (p=0.877 n=20)
SourceUint64-8                2.534n ± 0%    2.533n ± 0%         ~ (p=0.732 n=20)
GlobalInt64-8                 2.172n ± 1%    4.794n ± 0%  +120.67% (p=0.000 n=20)
GlobalInt64Parallel-8        0.4320n ± 0%   0.9605n ± 0%  +122.32% (p=0.000 n=20)
GlobalUint64-8                2.182n ± 0%    4.770n ± 0%  +118.58% (p=0.000 n=20)
GlobalUint64Parallel-8       0.4307n ± 0%   0.9583n ± 0%  +122.51% (p=0.000 n=20)
Int64-8                       4.107n ± 0%    4.104n ± 0%         ~ (p=0.416 n=20)
Uint64-8                      4.080n ± 0%    4.080n ± 0%         ~ (p=0.052 n=20)
GlobalIntN1000-8              2.814n ± 2%    5.643n ± 0%  +100.50% (p=0.000 n=20)
IntN1000-8                    4.141n ± 0%    4.139n ± 0%         ~ (p=0.140 n=20)
Int64N1000-8                  4.140n ± 0%    4.140n ± 0%         ~ (p=0.313 n=20)
Int64N1e8-8                   4.140n ± 0%    4.139n ± 0%         ~ (p=0.103 n=20)
Int64N1e9-8                   4.139n ± 0%    4.140n ± 0%         ~ (p=0.761 n=20)
Int64N2e9-8                   4.140n ± 0%    4.140n ± 0%         ~ (p=0.636 n=20)
Int64N1e18-8                  5.266n ± 0%    5.326n ± 1%    +1.14% (p=0.001 n=20)
Int64N2e18-8                  6.052n ± 0%    6.167n ± 0%    +1.90% (p=0.000 n=20)
Int64N4e18-8                  8.826n ± 0%    9.051n ± 0%    +2.55% (p=0.000 n=20)
Int32N1000-8                  4.127n ± 0%    4.132n ± 0%    +0.12% (p=0.000 n=20)
Int32N1e8-8                   4.126n ± 0%    4.131n ± 0%    +0.12% (p=0.000 n=20)
Int32N1e9-8                   4.127n ± 0%    4.132n ± 0%    +0.12% (p=0.000 n=20)
Int32N2e9-8                   4.132n ± 0%    4.131n ± 0%         ~ (p=0.017 n=20)
Float32-8                     4.109n ± 0%    4.105n ± 0%         ~ (p=0.379 n=20)
Float64-8                     4.107n ± 0%    4.106n ± 0%         ~ (p=0.867 n=20)
ExpFloat64-8                  5.339n ± 0%    5.383n ± 0%    +0.82% (p=0.000 n=20)
NormFloat64-8                 5.735n ± 0%    5.737n ± 1%         ~ (p=0.856 n=20)
Perm3-8                       26.65n ± 0%    26.80n ± 1%    +0.58% (p=0.000 n=20)
Perm30-8                      194.8n ± 1%    197.0n ± 0%    +1.18% (p=0.000 n=20)
Perm30ViaShuffle-8            156.6n ± 0%    157.6n ± 1%    +0.61% (p=0.000 n=20)
ShuffleOverhead-8             124.9n ± 0%    125.5n ± 0%    +0.52% (p=0.000 n=20)
Concurrent-8                  2.434n ± 3%    5.066n ± 0%  +108.09% (p=0.000 n=20)

goos: linux
goarch: 386
pkg: math/rand/v2
cpu: AMD Ryzen 9 7950X 16-Core Processor
                        │ bbb48afeb7.386 │            5cf807d1ea.386             │
                        │     sec/op     │    sec/op     vs base                 │
ChaCha8-32                  11.295n ± 1%    4.748n ± 2%   -57.96% (p=0.000 n=20)
PCG_DXSM-32                  7.693n ± 1%    7.738n ± 2%         ~ (p=0.542 n=20)
SourceUint64-32              7.658n ± 2%    7.622n ± 2%         ~ (p=0.344 n=20)
GlobalInt64-32               3.473n ± 2%    7.526n ± 2%  +116.73% (p=0.000 n=20)
GlobalInt64Parallel-32      0.3198n ± 0%   0.5444n ± 0%   +70.22% (p=0.000 n=20)
GlobalUint64-32              3.612n ± 0%    7.575n ± 1%  +109.69% (p=0.000 n=20)
GlobalUint64Parallel-32     0.3168n ± 0%   0.5403n ± 0%   +70.51% (p=0.000 n=20)
Int64-32                     7.673n ± 2%    7.789n ± 1%         ~ (p=0.122 n=20)
Uint64-32                    7.773n ± 1%    7.827n ± 2%         ~ (p=0.920 n=20)
GlobalIntN1000-32            6.268n ± 1%    9.581n ± 1%   +52.87% (p=0.000 n=20)
IntN1000-32                  10.33n ± 2%    10.45n ± 1%         ~ (p=0.233 n=20)
Int64N1000-32                10.98n ± 2%    11.01n ± 1%         ~ (p=0.401 n=20)
Int64N1e8-32                 11.19n ± 2%    10.97n ± 1%         ~ (p=0.033 n=20)
Int64N1e9-32                 11.06n ± 1%    11.08n ± 1%         ~ (p=0.498 n=20)
Int64N2e9-32                 11.10n ± 1%    11.01n ± 2%         ~ (p=0.995 n=20)
Int64N1e18-32                15.23n ± 2%    15.04n ± 1%         ~ (p=0.973 n=20)
Int64N2e18-32                15.89n ± 1%    15.85n ± 1%         ~ (p=0.409 n=20)
Int64N4e18-32                18.96n ± 2%    19.34n ± 2%         ~ (p=0.048 n=20)
Int32N1000-32                10.46n ± 2%    10.44n ± 2%         ~ (p=0.480 n=20)
Int32N1e8-32                 10.46n ± 2%    10.49n ± 2%         ~ (p=0.951 n=20)
Int32N1e9-32                 10.28n ± 2%    10.26n ± 1%         ~ (p=0.431 n=20)
Int32N2e9-32                 10.50n ± 2%    10.44n ± 2%         ~ (p=0.249 n=20)
Float32-32                   13.80n ± 2%    13.80n ± 2%         ~ (p=0.751 n=20)
Float64-32                   23.55n ± 2%    23.87n ± 0%         ~ (p=0.408 n=20)
ExpFloat64-32                15.36n ± 1%    15.29n ± 2%         ~ (p=0.316 n=20)
NormFloat64-32               13.57n ± 1%    13.79n ± 1%    +1.66% (p=0.005 n=20)
Perm3-32                     45.70n ± 2%    46.99n ± 2%    +2.81% (p=0.001 n=20)
Perm30-32                    399.0n ± 1%    403.8n ± 1%    +1.19% (p=0.006 n=20)
Perm30ViaShuffle-32          349.0n ± 1%    350.4n ± 1%         ~ (p=0.909 n=20)
ShuffleOverhead-32           322.3n ± 1%    323.8n ± 1%         ~ (p=0.410 n=20)
Concurrent-32                3.331n ± 1%    7.312n ± 1%  +119.50% (p=0.000 n=20)

For #61716.

Change-Id: Ibdddeed85c34d9ae397289dc899e04d4845f9ed2
Reviewed-on: https://go-review.googlesource.com/c/go/+/516860
Reviewed-by: Michael Pratt <mpratt@google.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

66 files changed:
src/cmd/compile/internal/test/inl_test.go
src/cmd/compile/internal/typecheck/_builtin/runtime.go
src/cmd/compile/internal/typecheck/builtin.go
src/cmd/compile/internal/walk/builtin.go
src/cmd/internal/goobj/builtinlist.go
src/cmd/internal/objabi/pkgspecial.go
src/cmd/link/internal/ld/data.go
src/hash/maphash/maphash_runtime.go
src/internal/chacha8rand/chacha8.go
src/internal/chacha8rand/chacha8_amd64.s
src/internal/chacha8rand/chacha8_arm64.s
src/internal/chacha8rand/export_test.go
src/internal/chacha8rand/rand_test.go
src/internal/coverage/pkid.go
src/math/rand/rand.go
src/math/rand/v2/rand.go
src/net/dnsclient.go
src/os/tempfile.go
src/runtime/alg.go
src/runtime/export_test.go
src/runtime/iface.go
src/runtime/malloc.go
src/runtime/map.go
src/runtime/map_fast32.go
src/runtime/map_fast64.go
src/runtime/map_faststr.go
src/runtime/mbitmap_allocheaders.go
src/runtime/mgcpacer.go
src/runtime/mprof.go
src/runtime/os3_solaris.go
src/runtime/os_aix.go
src/runtime/os_darwin.go
src/runtime/os_darwin_arm64.go
src/runtime/os_dragonfly.go
src/runtime/os_freebsd.go
src/runtime/os_freebsd_arm.go
src/runtime/os_freebsd_arm64.go
src/runtime/os_js.go
src/runtime/os_linux.go
src/runtime/os_linux_arm.go
src/runtime/os_linux_arm64.go
src/runtime/os_linux_mips64x.go
src/runtime/os_linux_mipsx.go
src/runtime/os_netbsd.go
src/runtime/os_netbsd_arm.go
src/runtime/os_netbsd_arm64.go
src/runtime/os_openbsd.go
src/runtime/os_openbsd_arm.go
src/runtime/os_openbsd_arm64.go
src/runtime/os_openbsd_mips64.go
src/runtime/os_plan9.go
src/runtime/os_plan9_arm.go
src/runtime/os_wasip1.go
src/runtime/os_wasm.go
src/runtime/os_windows.go
src/runtime/proc.go
src/runtime/rand.go [new file with mode: 0644]
src/runtime/rand_test.go
src/runtime/runtime2.go
src/runtime/select.go
src/runtime/sema.go
src/runtime/stubs.go
src/runtime/symtab.go
src/sync/pool.go
test/live.go
test/live_regabi.go

index 6d10f6c54c572dafc2b15c45f873ace192e1e10c..0ccc7b3761995d0f80d03796692ad4ee8fcf49bc 100644 (file)
@@ -44,7 +44,6 @@ func TestIntendedInlining(t *testing.T) {
                        "chanbuf",
                        "evacuated",
                        "fastlog2",
-                       "fastrand",
                        "float64bits",
                        "funcspdelta",
                        "getm",
@@ -54,6 +53,7 @@ func TestIntendedInlining(t *testing.T) {
                        "nextslicecap",
                        "noescape",
                        "pcvalueCacheKey",
+                       "rand32",
                        "readUnaligned32",
                        "readUnaligned64",
                        "releasem",
index f27a773a88c0eeb766c88951c966389e19f41e57..421152967cd61db724c6a36df937271f5b592807 100644 (file)
@@ -122,7 +122,7 @@ func panicrangeexit()
 // defer in range over func
 func deferrangefunc() interface{}
 
-func fastrand() uint32
+func rand32() uint32
 
 // *byte is really *runtime.Type
 func makemap64(mapType *byte, hint int64, mapbuf *any) (hmap map[any]any)
index 142fc26d2e8cd0d6d4357f42d6e714a8b083593e..09f60c68c0fb9dd17642ac9e5bcb01c4dc401d1f 100644 (file)
@@ -104,7 +104,7 @@ var runtimeDecls = [...]struct {
        {"efaceeq", funcTag, 72},
        {"panicrangeexit", funcTag, 9},
        {"deferrangefunc", funcTag, 73},
-       {"fastrand", funcTag, 74},
+       {"rand32", funcTag, 74},
        {"makemap64", funcTag, 76},
        {"makemap", funcTag, 77},
        {"makemap_small", funcTag, 78},
index 90c32154b93b080846e188a9597a5ea6254b5161..37143baa28ceaf3c2e25eb08b4783ee6f2ebab8e 100644 (file)
@@ -358,8 +358,8 @@ func walkMakeMap(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
                if n.Esc() == ir.EscNone {
                        // Only need to initialize h.hash0 since
                        // hmap h has been allocated on the stack already.
-                       // h.hash0 = fastrand()
-                       rand := mkcall("fastrand", types.Types[types.TUINT32], init)
+                       // h.hash0 = rand32()
+                       rand := mkcall("rand32", types.Types[types.TUINT32], init)
                        hashsym := hmapType.Field(4).Sym // hmap.hash0 see reflect.go:hmap
                        appendWalkStmt(init, ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, h, hashsym), rand))
                        return typecheck.ConvNop(h, t)
index 03982d54f25672c50540facf0b2d416376387771..fb729f512ed7c625af7c500961d040a74060a58a 100644 (file)
@@ -83,7 +83,7 @@ var builtins = [...]struct {
        {"runtime.efaceeq", 1},
        {"runtime.panicrangeexit", 1},
        {"runtime.deferrangefunc", 1},
-       {"runtime.fastrand", 1},
+       {"runtime.rand32", 1},
        {"runtime.makemap64", 1},
        {"runtime.makemap", 1},
        {"runtime.makemap_small", 1},
index 9bf07153a4642e0b6d23ddb9b244921b68864f9c..6df95f33f9369254f43fc378d77f30cfafd37324 100644 (file)
@@ -50,6 +50,7 @@ var runtimePkgs = []string{
 
        "internal/abi",
        "internal/bytealg",
+       "internal/chacha8rand",
        "internal/coverage/rtcov",
        "internal/cpu",
        "internal/goarch",
@@ -79,6 +80,7 @@ var allowAsmABIPkgs = []string{
        "reflect",
        "syscall",
        "internal/bytealg",
+       "internal/chacha8rand",
        "runtime/internal/syscall",
        "runtime/internal/startlinetest",
 }
index 2d761c7ee7da57b1a798aa9cae0ab2414927e991..f4ea8407c83d057d163bb5d71c6dce2dabec81ee 100644 (file)
@@ -56,10 +56,11 @@ import (
 func isRuntimeDepPkg(pkg string) bool {
        switch pkg {
        case "runtime",
-               "sync/atomic",      // runtime may call to sync/atomic, due to go:linkname
-               "internal/abi",     // used by reflectcall (and maybe more)
-               "internal/bytealg", // for IndexByte
-               "internal/cpu":     // for cpu features
+               "sync/atomic",          // runtime may call to sync/atomic, due to go:linkname
+               "internal/abi",         // used by reflectcall (and maybe more)
+               "internal/bytealg",     // for IndexByte
+               "internal/chacha8rand", // for rand
+               "internal/cpu":         // for cpu features
                return true
        }
        return strings.HasPrefix(pkg, "runtime/internal/") && !strings.HasSuffix(pkg, "_test")
index 98097ff9c34be5974605f8ad853c0b5d70d9f567..b831df2cf4a9c4fdc4389f6d1e67c94358733df8 100644 (file)
@@ -10,8 +10,8 @@ import (
        "unsafe"
 )
 
-//go:linkname runtime_fastrand64 runtime.fastrand64
-func runtime_fastrand64() uint64
+//go:linkname runtime_rand runtime.rand
+func runtime_rand() uint64
 
 //go:linkname runtime_memhash runtime.memhash
 //go:noescape
@@ -39,5 +39,5 @@ func rthashString(s string, state uint64) uint64 {
 }
 
 func randUint64() uint64 {
-       return runtime_fastrand64()
+       return runtime_rand()
 }
index 654ac1bc9db9d010d34803d5acb087c0cf89e2b1..ce55c07d058ad538a0c1d972f62b79ef1d14bded 100644 (file)
@@ -93,6 +93,25 @@ func (s *State) Refill() {
        }
 }
 
+// Reseed reseeds the state with new random values.
+// After a call to Reseed, any previously returned random values
+// have been erased from the memory of the state and cannot be
+// recovered.
+func (s *State) Reseed() {
+       var seed [4]uint64
+       for i := range seed {
+               for {
+                       x, ok := s.Next()
+                       if ok {
+                               seed[i] = x
+                               break
+                       }
+                       s.Refill()
+               }
+       }
+       s.Init64(seed)
+}
+
 // Marshal marshals the state into a byte slice.
 // Marshal and Unmarshal are functions, not methods,
 // so that they will not be linked into the runtime
index cadd516c09fc787d1a3abf6b46585cdfc4bb6c00..b56deb3b0b8df37f6eb3d5afc194dddd4f67161e 100644 (file)
 // block runs 4 ChaCha8 block transformations in the four stripes of the X registers.
 
 // func block(seed *[8]uint32, blocks *[16][4]uint32, counter uint32)
-TEXT ·block(SB), NOSPLIT, $16
+TEXT ·block<ABIInternal>(SB), NOSPLIT, $16
        // seed in AX
        // blocks in BX
        // counter in CX
-       MOVQ seed+0(FP), AX
-       MOVQ blocks+8(FP), BX
-       MOVL counter+16(FP), CX
 
        // Load initial constants into top row.
        REPL(0x61707865, X0)
index 4f36a7021c4567157b5951d1caa0bb1f9da9b891..18e34dd1481a87b26c5e6b3531cf8a6f34e5a0fa 100644 (file)
 // block runs 4 ChaCha8 block transformations in the four stripes of the V registers.
 
 // func block(seed *[8]uint32, blocks *[4][16]uint32, counter uint32)
-TEXT ·block(SB), NOSPLIT, $16
+TEXT ·block<ABIInternal>(SB), NOSPLIT, $16
        // seed in R0
        // blocks in R1
-       MOVD seed+0(FP), R0
-       MOVD blocks+8(FP), R1
-       MOVW counter+16(FP), R2
+       // counter in R2
 
        // Load initial constants into top row.
        MOVD $·chachaConst(SB), R10
index 70478a45c35966f9b01155881b66b995737fc671..728aded682389083953788c152be815d04a1418c 100644 (file)
@@ -6,3 +6,7 @@ package chacha8rand
 
 var Block = block
 var Block_generic = block_generic
+
+func Seed(s *State) [4]uint64 {
+       return s.seed
+}
index f4770999c99282a02474748a8d740e5abaab9afb..2975013bfa9cde5796d7829c59ea08312e6ce537 100644 (file)
@@ -53,6 +53,16 @@ func TestMarshal(t *testing.T) {
        }
 }
 
+func TestReseed(t *testing.T) {
+       var s State
+       s.Init(seed)
+       old := Seed(&s)
+       s.Reseed()
+       if Seed(&s) == old {
+               t.Errorf("Reseed did not change seed")
+       }
+}
+
 func BenchmarkBlock(b *testing.B) {
        var seed [4]uint64
        var blocks [32]uint64
index 8ddd44d6bbc08ec77666dc356ea11902b6eaf4e0..372a9cb19fa935b73f66d2cff075e4b10f07b8f3 100644 (file)
@@ -49,6 +49,7 @@ var rtPkgs = [...]string{
        "internal/goarch",
        "runtime/internal/atomic",
        "internal/goos",
+       "internal/chacha8rand",
        "runtime/internal/sys",
        "internal/abi",
        "runtime/internal/math",
index 78e176e78fa5ea5d6a4f0d11dc2b9cba0294de48..a8ed9c0cb7cfa9beed536cc146294e9e79f5d30a 100644 (file)
@@ -273,7 +273,7 @@ func (r *Rand) Read(p []byte) (n int, err error) {
        switch src := r.src.(type) {
        case *lockedSource:
                return src.read(p, &r.readVal, &r.readPos)
-       case *fastSource:
+       case *runtimeSource:
                return src.read(p, &r.readVal, &r.readPos)
        }
        return read(p, r.src, &r.readVal, &r.readPos)
@@ -328,8 +328,8 @@ func globalRand() *Rand {
                r.Seed(1)
        } else {
                r = &Rand{
-                       src: &fastSource{},
-                       s64: &fastSource{},
+                       src: &runtimeSource{},
+                       s64: &runtimeSource{},
                }
        }
 
@@ -346,29 +346,29 @@ func globalRand() *Rand {
        return r
 }
 
-//go:linkname fastrand64
-func fastrand64() uint64
+//go:linkname runtime_rand runtime.rand
+func runtime_rand() uint64
 
-// fastSource is an implementation of Source64 that uses the runtime
+// runtimeSource is an implementation of Source64 that uses the runtime
 // fastrand functions.
-type fastSource struct {
+type runtimeSource struct {
        // The mutex is used to avoid race conditions in Read.
        mu sync.Mutex
 }
 
-func (*fastSource) Int63() int64 {
-       return int64(fastrand64() & rngMask)
+func (*runtimeSource) Int63() int64 {
+       return int64(runtime_rand() & rngMask)
 }
 
-func (*fastSource) Seed(int64) {
-       panic("internal error: call to fastSource.Seed")
+func (*runtimeSource) Seed(int64) {
+       panic("internal error: call to runtimeSource.Seed")
 }
 
-func (*fastSource) Uint64() uint64 {
-       return fastrand64()
+func (*runtimeSource) Uint64() uint64 {
+       return runtime_rand()
 }
 
-func (fs *fastSource) read(p []byte, readVal *int64, readPos *int8) (n int, err error) {
+func (fs *runtimeSource) read(p []byte, readVal *int64, readPos *int8) (n int, err error) {
        fs.mu.Lock()
        n, err = read(p, fs, readVal, readPos)
        fs.mu.Unlock()
@@ -405,7 +405,7 @@ func Seed(seed int64) {
        // Otherwise either
        // 1) orig == nil, which is the normal case when Seed is the first
        // top-level function to be called, or
-       // 2) orig is already a fastSource, in which case we need to change
+       // 2) orig is already a runtimeSource, in which case we need to change
        // to a lockedSource.
        // Either way we do the same thing.
 
index 5382f809e0debdee4c2bcf68cf916c6b30103fc1..f490408472bada0efaf14b604ca5bd8b854a87f1 100644 (file)
@@ -250,20 +250,16 @@ func (r *Rand) Shuffle(n int, swap func(i, j int)) {
 
 // globalRand is the source of random numbers for the top-level
 // convenience functions.
-var globalRand = &Rand{src: &fastSource{}}
+var globalRand = &Rand{src: &runtimeSource{}}
 
-//go:linkname fastrand64
-func fastrand64() uint64
+//go:linkname runtime_rand runtime.rand
+func runtime_rand() uint64
 
-// fastSource is a Source that uses the runtime fastrand functions.
-type fastSource struct{}
+// runtimeSource is a Source that uses the runtime fastrand functions.
+type runtimeSource struct{}
 
-func (*fastSource) Int64() int64 {
-       return int64(fastrand64() << 1 >> 1)
-}
-
-func (*fastSource) Uint64() uint64 {
-       return fastrand64()
+func (*runtimeSource) Uint64() uint64 {
+       return runtime_rand()
 }
 
 // Int64 returns a non-negative pseudo-random 63-bit integer as an int64
index b609dbd468f698a7e80770866a0a423be5979fad..204620b2edb8d0480d9aea392dfdaa580eca2992 100644 (file)
@@ -8,15 +8,17 @@ import (
        "internal/bytealg"
        "internal/itoa"
        "sort"
+       _ "unsafe" // for go:linkname
 
        "golang.org/x/net/dns/dnsmessage"
 )
 
 // provided by runtime
-func fastrandu() uint
+//go:linkname runtime_rand runtime.rand
+func runtime_rand() uint64
 
 func randInt() int {
-       return int(fastrandu() >> 1) // clear sign bit
+       return int(uint(runtime_rand()) >> 1) // clear sign bit
 }
 
 func randIntn(n int) int {
index 315f65ad9ceed84e76c092edcbb8fd8967c64afa..7f2b6a883cc3a09ca06fed9bec00ad67513b2a3e 100644 (file)
@@ -8,16 +8,18 @@ import (
        "errors"
        "internal/bytealg"
        "internal/itoa"
+       _ "unsafe" // for go:linkname
 )
 
-// fastrand provided by runtime.
+// random number source provided by runtime.
 // We generate random temporary file names so that there's a good
 // chance the file doesn't exist yet - keeps the number of tries in
 // TempFile to a minimum.
-func fastrand() uint32
+//go:linkname runtime_rand runtime.rand
+func runtime_rand() uint64
 
 func nextRandom() string {
-       return itoa.Uitoa(uint(fastrand()))
+       return itoa.Uitoa(uint(runtime_rand()))
 }
 
 // CreateTemp creates a new temporary file in the directory dir,
index 336058d159e0ddea38ad9be72d5ca0cbbf59c830..eaf9c91490138c85bc990854b6a0d2bcbd058d7a 100644 (file)
@@ -66,7 +66,7 @@ func f32hash(p unsafe.Pointer, h uintptr) uintptr {
        case f == 0:
                return c1 * (c0 ^ h) // +0, -0
        case f != f:
-               return c1 * (c0 ^ h ^ uintptr(fastrand())) // any kind of NaN
+               return c1 * (c0 ^ h ^ uintptr(rand())) // any kind of NaN
        default:
                return memhash(p, h, 4)
        }
@@ -78,7 +78,7 @@ func f64hash(p unsafe.Pointer, h uintptr) uintptr {
        case f == 0:
                return c1 * (c0 ^ h) // +0, -0
        case f != f:
-               return c1 * (c0 ^ h ^ uintptr(fastrand())) // any kind of NaN
+               return c1 * (c0 ^ h ^ uintptr(rand())) // any kind of NaN
        default:
                return memhash(p, h, 8)
        }
@@ -390,17 +390,18 @@ func alginit() {
                initAlgAES()
                return
        }
-       getRandomData((*[len(hashkey) * goarch.PtrSize]byte)(unsafe.Pointer(&hashkey))[:])
-       hashkey[0] |= 1 // make sure these numbers are odd
-       hashkey[1] |= 1
-       hashkey[2] |= 1
-       hashkey[3] |= 1
+       for i := range hashkey {
+               hashkey[i] = uintptr(rand()) | 1 // make sure these numbers are odd
+       }
 }
 
 func initAlgAES() {
        useAeshash = true
        // Initialize with random data so hash collisions will be hard to engineer.
-       getRandomData(aeskeysched[:])
+       key := (*[hashRandomBytes / 8]uint64)(unsafe.Pointer(&aeskeysched))
+       for i := range key {
+               key[i] = bootstrapRand()
+       }
 }
 
 // Note: These routines perform the read with a native endianness.
index 9249550fd7908c3be48ef8d7bc9d89495fc4969d..2e707b96e295007157b95c51ce95ed439e61a6f6 100644 (file)
@@ -31,6 +31,8 @@ var Exitsyscall = exitsyscall
 var LockedOSThread = lockedOSThread
 var Xadduintptr = atomic.Xadduintptr
 
+var ReadRandomFailed = &readRandomFailed
+
 var Fastlog2 = fastlog2
 
 var Atoi = atoi
@@ -398,9 +400,9 @@ func CountPagesInUse() (pagesInUse, counted uintptr) {
        return
 }
 
-func Fastrand() uint32          { return fastrand() }
-func Fastrand64() uint64        { return fastrand64() }
-func Fastrandn(n uint32) uint32 { return fastrandn(n) }
+func Fastrand() uint32          { return uint32(rand()) }
+func Fastrand64() uint64        { return rand() }
+func Fastrandn(n uint32) uint32 { return randn(n) }
 
 type ProfBuf profBuf
 
index b8c7caeebcea80ceec0cfa068e7f2154b73bc160..bad49a346e86d3af8327847a1460300fd40f23d0 100644 (file)
@@ -440,14 +440,14 @@ func typeAssert(s *abi.TypeAssert, t *_type) *itab {
 
        // Maybe update the cache, so the next time the generated code
        // doesn't need to call into the runtime.
-       if fastrand()&1023 != 0 {
+       if cheaprand()&1023 != 0 {
                // Only bother updating the cache ~1 in 1000 times.
                return tab
        }
        // Load the current cache.
        oldC := (*abi.TypeAssertCache)(atomic.Loadp(unsafe.Pointer(&s.Cache)))
 
-       if fastrand()&uint32(oldC.Mask) != 0 {
+       if cheaprand()&uint32(oldC.Mask) != 0 {
                // As cache gets larger, choose to update it less often
                // so we can amortize the cost of building a new cache.
                return tab
@@ -540,7 +540,7 @@ func interfaceSwitch(s *abi.InterfaceSwitch, t *_type) (int, *itab) {
 
        // Maybe update the cache, so the next time the generated code
        // doesn't need to call into the runtime.
-       if fastrand()&1023 != 0 {
+       if cheaprand()&1023 != 0 {
                // Only bother updating the cache ~1 in 1000 times.
                // This ensures we don't waste memory on switches, or
                // switch arguments, that only happen a few times.
@@ -549,7 +549,7 @@ func interfaceSwitch(s *abi.InterfaceSwitch, t *_type) (int, *itab) {
        // Load the current cache.
        oldC := (*abi.InterfaceSwitchCache)(atomic.Loadp(unsafe.Pointer(&s.Cache)))
 
-       if fastrand()&uint32(oldC.Mask) != 0 {
+       if cheaprand()&uint32(oldC.Mask) != 0 {
                // As cache gets larger, choose to update it less often
                // so we can amortize the cost of building a new cache
                // (that cost is linear in oldc.Mask).
index ce03114edc93a12afe811486931c6afe67d4ff99..e2cb2e456e86107d1ef5a7e9bbfc1886806714c9 100644 (file)
@@ -1472,7 +1472,7 @@ func fastexprand(mean int) int32 {
        // x = -log_e(q) * mean
        // x = log_2(q) * (-log_e(2)) * mean    ; Using log_2 for efficiency
        const randomBitCount = 26
-       q := fastrandn(1<<randomBitCount) + 1
+       q := cheaprandn(1<<randomBitCount) + 1
        qlog := fastlog2(float64(q)) - randomBitCount
        if qlog > 0 {
                qlog = 0
@@ -1490,7 +1490,7 @@ func nextSampleNoFP() uintptr {
                rate = 0x3fffffff
        }
        if rate != 0 {
-               return uintptr(fastrandn(uint32(2 * rate)))
+               return uintptr(cheaprandn(uint32(2 * rate)))
        }
        return 0
 }
index 11daeb7568f53d0d5249cd8413831abbb19d472a..cd3f838fa19c6b0f61b7c1a3926e2716649ac19e 100644 (file)
@@ -238,8 +238,8 @@ func (h *hmap) incrnoverflow() {
        // as many overflow buckets as buckets.
        mask := uint32(1)<<(h.B-15) - 1
        // Example: if h.B == 18, then mask == 7,
-       // and fastrand & 7 == 0 with probability 1/8.
-       if fastrand()&mask == 0 {
+       // and rand() & 7 == 0 with probability 1/8.
+       if uint32(rand())&mask == 0 {
                h.noverflow++
        }
 }
@@ -293,7 +293,7 @@ func makemap64(t *maptype, hint int64, h *hmap) *hmap {
 // at compile time and the map needs to be allocated on the heap.
 func makemap_small() *hmap {
        h := new(hmap)
-       h.hash0 = fastrand()
+       h.hash0 = uint32(rand())
        return h
 }
 
@@ -312,7 +312,7 @@ func makemap(t *maptype, hint int, h *hmap) *hmap {
        if h == nil {
                h = new(hmap)
        }
-       h.hash0 = fastrand()
+       h.hash0 = uint32(rand())
 
        // Find the size parameter B which will hold the requested # of elements.
        // For hint < 0 overLoadFactor returns false since hint < bucketCnt.
@@ -797,7 +797,7 @@ search:
                        // Reset the hash seed to make it more difficult for attackers to
                        // repeatedly trigger hash collisions. See issue 25237.
                        if h.count == 0 {
-                               h.hash0 = fastrand()
+                               h.hash0 = uint32(rand())
                        }
                        break search
                }
@@ -843,12 +843,7 @@ func mapiterinit(t *maptype, h *hmap, it *hiter) {
        }
 
        // decide where to start
-       var r uintptr
-       if h.B > 31-bucketCntBits {
-               r = uintptr(fastrand64())
-       } else {
-               r = uintptr(fastrand())
-       }
+       r := uintptr(rand())
        it.startBucket = r & bucketMask(h.B)
        it.offset = uint8(r >> h.B & (bucketCnt - 1))
 
@@ -1032,7 +1027,7 @@ func mapclear(t *maptype, h *hmap) {
 
        // Reset the hash seed to make it more difficult for attackers to
        // repeatedly trigger hash collisions. See issue 25237.
-       h.hash0 = fastrand()
+       h.hash0 = uint32(rand())
 
        // Keep the mapextra allocation but clear any extra information.
        if h.extra != nil {
@@ -1619,7 +1614,7 @@ func keys(m any, p unsafe.Pointer) {
                return
        }
        s := (*slice)(p)
-       r := int(fastrand())
+       r := int(rand())
        offset := uint8(r >> h.B & (bucketCnt - 1))
        if h.B == 0 {
                copyKeys(t, h, (*bmap)(h.buckets), s, offset)
@@ -1682,7 +1677,7 @@ func values(m any, p unsafe.Pointer) {
                return
        }
        s := (*slice)(p)
-       r := int(fastrand())
+       r := int(rand())
        offset := uint8(r >> h.B & (bucketCnt - 1))
        if h.B == 0 {
                copyValues(t, h, (*bmap)(h.buckets), s, offset)
index d10dca3e91415dfa7d7aedc764e3e071e427e2a3..e1dd495365b39c5369268ae6d21993ee3e106465 100644 (file)
@@ -348,7 +348,7 @@ search:
                        // Reset the hash seed to make it more difficult for attackers to
                        // repeatedly trigger hash collisions. See issue 25237.
                        if h.count == 0 {
-                               h.hash0 = fastrand()
+                               h.hash0 = uint32(rand())
                        }
                        break search
                }
index d771e0b74719c5879b1f1758cdd3d576f67a7fdb..7ca35ec6cb137da301a7347342baf17dca6c69f0 100644 (file)
@@ -350,7 +350,7 @@ search:
                        // Reset the hash seed to make it more difficult for attackers to
                        // repeatedly trigger hash collisions. See issue 25237.
                        if h.count == 0 {
-                               h.hash0 = fastrand()
+                               h.hash0 = uint32(rand())
                        }
                        break search
                }
index ef71da859a36f1664da90686b29551b28c93b7ee..22e1f61f066054b05dde3468d9d220f2cb75ff7f 100644 (file)
@@ -376,7 +376,7 @@ search:
                        // Reset the hash seed to make it more difficult for attackers to
                        // repeatedly trigger hash collisions. See issue 25237.
                        if h.count == 0 {
-                               h.hash0 = fastrand()
+                               h.hash0 = uint32(rand())
                        }
                        break search
                }
index 33535a515a5cf4028431eb07bae8d3ea8aed17be..2151c12b858ed2777bffa29d1edac05cce07ebb6 100644 (file)
@@ -907,14 +907,14 @@ func heapSetType(x, dataSize uintptr, typ *_type, header **_type, span *mspan) (
                if header == nil {
                        maxIterBytes = dataSize
                }
-               off := alignUp(uintptr(fastrand())%dataSize, goarch.PtrSize)
+               off := alignUp(uintptr(cheaprand())%dataSize, goarch.PtrSize)
                size := dataSize - off
                if size == 0 {
                        off -= goarch.PtrSize
                        size += goarch.PtrSize
                }
                interior := x + off
-               size -= alignDown(uintptr(fastrand())%size, goarch.PtrSize)
+               size -= alignDown(uintptr(cheaprand())%size, goarch.PtrSize)
                if size == 0 {
                        size = goarch.PtrSize
                }
index 3d07cc70e80b5e7236f0d1208a5f836c547b4979..e9af3d60cdc2586ddb558e1572b0865188ef7b3c 100644 (file)
@@ -712,7 +712,7 @@ func (c *gcControllerState) enlistWorker() {
        }
        myID := gp.m.p.ptr().id
        for tries := 0; tries < 5; tries++ {
-               id := int32(fastrandn(uint32(gomaxprocs - 1)))
+               id := int32(cheaprandn(uint32(gomaxprocs - 1)))
                if id >= myID {
                        id++
                }
index b1930b30202c007a25cf0e2b71f1d3be80f15efd..aeb03985ccb03ea32d7b2f8c687ab99668ebf296 100644 (file)
@@ -498,7 +498,7 @@ func blockevent(cycles int64, skip int) {
 // blocksampled returns true for all events where cycles >= rate. Shorter
 // events have a cycles/rate random chance of returning true.
 func blocksampled(cycles, rate int64) bool {
-       if rate <= 0 || (rate > cycles && int64(fastrand())%rate > cycles) {
+       if rate <= 0 || (rate > cycles && cheaprand64()%rate > cycles) {
                return false
        }
        return true
@@ -589,11 +589,11 @@ func (lt *lockTimer) begin() {
        if rate != 0 && rate < lt.timeRate {
                lt.timeRate = rate
        }
-       if int64(fastrand())%lt.timeRate == 0 {
+       if int64(cheaprand())%lt.timeRate == 0 {
                lt.timeStart = nanotime()
        }
 
-       if rate > 0 && int64(fastrand())%rate == 0 {
+       if rate > 0 && int64(cheaprand())%rate == 0 {
                lt.tickStart = cputicks()
        }
 }
@@ -645,8 +645,8 @@ func (prof *mLockProfile) recordLock(cycles int64, l *mutex) {
                // We can only store one call stack for runtime-internal lock contention
                // on this M, and we've already got one. Decide which should stay, and
                // add the other to the report for runtime._LostContendedLock.
-               prevScore := fastrand64() % uint64(prev)
-               thisScore := fastrand64() % uint64(cycles)
+               prevScore := uint64(cheaprand64()) % uint64(prev)
+               thisScore := uint64(cheaprand64()) % uint64(cycles)
                if prevScore > thisScore {
                        prof.cyclesLost += cycles
                        return
@@ -790,7 +790,7 @@ func mutexevent(cycles int64, skip int) {
                cycles = 0
        }
        rate := int64(atomic.Load64(&mutexprofilerate))
-       if rate > 0 && int64(fastrand())%rate == 0 {
+       if rate > 0 && cheaprand64()%rate == 0 {
                saveblockevent(cycles, rate, skip+1, mutexProfile)
        }
 }
index 81629f02a22674218eeac2780c39671ec9d5adbe..92daf13b1a2ddc8d8e0d07267f844dbfc9bbf268 100644 (file)
@@ -198,11 +198,11 @@ func exitThread(wait *atomic.Uint32) {
 var urandom_dev = []byte("/dev/urandom\x00")
 
 //go:nosplit
-func getRandomData(r []byte) {
+func readRandom(r []byte) int {
        fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
        n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
        closefd(fd)
-       extendRandom(r, int(n))
+       return int(n)
 }
 
 func goenvs() {
index b26922c908edde8cb6bf81cf34292836a85cdb43..3a5078a64c45c0f4ece5d6a1c3d75ab215679981 100644 (file)
@@ -239,11 +239,11 @@ func exitThread(wait *atomic.Uint32) {
 var urandom_dev = []byte("/dev/urandom\x00")
 
 //go:nosplit
-func getRandomData(r []byte) {
+func readRandom(r []byte) int {
        fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
        n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
        closefd(fd)
-       extendRandom(r, int(n))
+       return int(n)
 }
 
 func goenvs() {
index ff33db084b0250fcfe69ac7c57a5a3d9a1f670ed..430d1865dffb410a34e89dc2578a37dd1709051f 100644 (file)
@@ -194,11 +194,11 @@ func getPageSize() uintptr {
 var urandom_dev = []byte("/dev/urandom\x00")
 
 //go:nosplit
-func getRandomData(r []byte) {
+func readRandom(r []byte) int {
        fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
        n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
        closefd(fd)
-       extendRandom(r, int(n))
+       return int(n)
 }
 
 func goenvs() {
index b808150de01dcde36567b32e1fee908fe5e9b7e5..ebc1b139a665aeb7550cb04b53c69f1581763d18 100644 (file)
@@ -6,7 +6,6 @@ package runtime
 
 //go:nosplit
 func cputicks() int64 {
-       // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
        return nanotime()
 }
index 80c1267765caf2a1041875cbb6e1b9bf3fd48d46..2aeea1775523c1ecafcef173b56e33ca2f68f51b 100644 (file)
@@ -181,11 +181,11 @@ func osinit() {
 var urandom_dev = []byte("/dev/urandom\x00")
 
 //go:nosplit
-func getRandomData(r []byte) {
+func readRandom(r []byte) int {
        fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
        n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
        closefd(fd)
-       extendRandom(r, int(n))
+       return int(n)
 }
 
 func goenvs() {
index c05e00f6ac3a34daadb378baee5b29256449737a..d0d6f14fa0cb5abe8f27b40b2ecbca90dd894831 100644 (file)
@@ -283,11 +283,11 @@ func osinit() {
 var urandom_dev = []byte("/dev/urandom\x00")
 
 //go:nosplit
-func getRandomData(r []byte) {
+func readRandom(r []byte) int {
        fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
        n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
        closefd(fd)
-       extendRandom(r, int(n))
+       return int(n)
 }
 
 func goenvs() {
index ae80119fe1cb4af76485246b7baf7e97cecaa9fe..5f6bf46798c9ee561b1f623c6cbec724188dfe33 100644 (file)
@@ -49,7 +49,6 @@ func archauxv(tag, val uintptr) {
 
 //go:nosplit
 func cputicks() int64 {
-       // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
        return nanotime()
 }
index b5b25f0dc5f8c6d985db8bfc39dfe173cecd5ad7..58bc5d34b76b4642038e90394ccb29e64ab4355d 100644 (file)
@@ -6,7 +6,6 @@ package runtime
 
 //go:nosplit
 func cputicks() int64 {
-       // Currently cputicks() is used in blocking profiler and to seed fastrand().
        // nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
        return nanotime()
 }
index 65fb499de676dcced1b4235d2035f08c7fd3bc8c..099c5265a083b6bebb245ca45aa1a34b6ef1706b 100644 (file)
@@ -32,6 +32,11 @@ func usleep(usec uint32) {
 //go:noescape
 func getRandomData(r []byte)
 
+func readRandom(r []byte) int {
+       getRandomData(r)
+       return len(r)
+}
+
 func goenvs() {
        goenvs_unix()
 }
index 6386b82a85db0d5e690bd17b79c89063b69a76e6..0ba607fe1f18ee5ff6ca2dfaf4c14a5a6caa7516 100644 (file)
@@ -288,10 +288,6 @@ func sysargs(argc int32, argv **byte) {
        auxv = auxvreadbuf[: pairs*2 : pairs*2]
 }
 
-// startupRandomData holds random bytes initialized at startup. These come from
-// the ELF AT_RANDOM auxiliary vector.
-var startupRandomData []byte
-
 // secureMode holds the value of AT_SECURE passed in the auxiliary vector.
 var secureMode bool
 
@@ -303,7 +299,7 @@ func sysauxv(auxv []uintptr) (pairs int) {
                case _AT_RANDOM:
                        // The kernel provides a pointer to 16-bytes
                        // worth of random data.
-                       startupRandomData = (*[16]byte)(unsafe.Pointer(val))[:]
+                       startupRand = (*[16]byte)(unsafe.Pointer(val))[:]
 
                case _AT_PAGESZ:
                        physPageSize = val
@@ -352,16 +348,11 @@ func osinit() {
 
 var urandom_dev = []byte("/dev/urandom\x00")
 
-func getRandomData(r []byte) {
-       if startupRandomData != nil {
-               n := copy(r, startupRandomData)
-               extendRandom(r, n)
-               return
-       }
+func readRandom(r []byte) int {
        fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
        n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
        closefd(fd)
-       extendRandom(r, int(n))
+       return int(n)
 }
 
 func goenvs() {
@@ -656,7 +647,7 @@ func setThreadCPUProfiler(hz int32) {
        // activates may do a couple milliseconds of GC-related work and nothing
        // else in the few seconds that the profiler observes.
        spec := new(itimerspec)
-       spec.it_value.setNsec(1 + int64(fastrandn(uint32(1e9/hz))))
+       spec.it_value.setNsec(1 + int64(cheaprandn(uint32(1e9/hz))))
        spec.it_interval.setNsec(1e9 / int64(hz))
 
        var timerid int32
index b9779159ad5585d637f8a600b1541747da90d26b..5e1274ebab0fdf1ef4c89030ea10d6edeced55a0 100644 (file)
@@ -52,7 +52,6 @@ func osArchInit() {}
 
 //go:nosplit
 func cputicks() int64 {
-       // Currently cputicks() is used in blocking profiler and to seed fastrand().
        // nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
        return nanotime()
 }
index 2daa56fce75e533b8546f9dd75603bc5d8137243..62cead1d221c17cf1e983f899189b7fe6f7d8c99 100644 (file)
@@ -19,7 +19,6 @@ func osArchInit() {}
 
 //go:nosplit
 func cputicks() int64 {
-       // Currently cputicks() is used in blocking profiler and to seed fastrand().
        // nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
        return nanotime()
 }
index 11d35bc0204a2f7293e91d9ca8229909b1e3cff5..770cc27ba78915762859ff45d80d9dc8c9816c1c 100644 (file)
@@ -19,7 +19,6 @@ func osArchInit() {}
 
 //go:nosplit
 func cputicks() int64 {
-       // Currently cputicks() is used in blocking profiler and to seed fastrand().
        // nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
        return nanotime()
 }
index cdf83ff71dda01d48a7ed0fd8d5647f58b7ae351..3807e6d05103c83609c96b1e101288167c26a68a 100644 (file)
@@ -13,7 +13,6 @@ func osArchInit() {}
 
 //go:nosplit
 func cputicks() int64 {
-       // Currently cputicks() is used in blocking profiler and to seed fastrand().
        // nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
        return nanotime()
 }
index 7cbba48194a8ac31650f1ba30745cf295b40632f..8abb688aae37ddb4af5d50fa6d87343214141b89 100644 (file)
@@ -274,11 +274,11 @@ func osinit() {
 var urandom_dev = []byte("/dev/urandom\x00")
 
 //go:nosplit
-func getRandomData(r []byte) {
+func readRandom(r []byte) int {
        fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
        n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
        closefd(fd)
-       extendRandom(r, int(n))
+       return int(n)
 }
 
 func goenvs() {
index 5fb4e08d66a9158755227d0d9f53cc13132e9a0e..7494a387e33b99abdc44fc9edc3dbc307de0888c 100644 (file)
@@ -31,7 +31,6 @@ func checkgoarm() {
 
 //go:nosplit
 func cputicks() int64 {
-       // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
        return nanotime()
 }
index 2dda9c9274d73bdcd44b145bf67d9379995932b1..48841afdb617100a9f830887457f3e510787fc5e 100644 (file)
@@ -20,7 +20,6 @@ func lwp_mcontext_init(mc *mcontextt, stk unsafe.Pointer, mp *m, gp *g, fn uintp
 
 //go:nosplit
 func cputicks() int64 {
-       // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
        return nanotime()
 }
index aa2ba859a8ace513ac712445a9d32a16132ee21c..856979910a9fb0546b01e6146ffd179c92dc159c 100644 (file)
@@ -142,11 +142,11 @@ func osinit() {
 var urandom_dev = []byte("/dev/urandom\x00")
 
 //go:nosplit
-func getRandomData(r []byte) {
+func readRandom(r []byte) int {
        fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
        n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
        closefd(fd)
-       extendRandom(r, int(n))
+       return int(n)
 }
 
 func goenvs() {
index 0a2409676c18009be2f41c14fba53e8f3261ea7b..d5dc8cb4792406e4484504297bf5c8a176508c8a 100644 (file)
@@ -17,7 +17,6 @@ func checkgoarm() {
 
 //go:nosplit
 func cputicks() int64 {
-       // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
        return nanotime()
 }
index d71de7d196f3ada9a8bc5f7484126f486e977095..4b2c6e3fe9619c82e3ca2f788a3bf308119e35c1 100644 (file)
@@ -6,7 +6,6 @@ package runtime
 
 //go:nosplit
 func cputicks() int64 {
-       // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
        return nanotime()
 }
index ae220cd6838e6d2d1bae9b3f6c5c8b030c01a61a..e5eeb2dcd1070b5a742a81b1b01960407085bddd 100644 (file)
@@ -6,7 +6,6 @@ package runtime
 
 //go:nosplit
 func cputicks() int64 {
-       // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
        return nanotime()
 }
index f4ff4d5f454fbf2b48d632fd8d805f1598e35708..77446d09d3b51e9661774fea7e6d538949d0db0a 100644 (file)
@@ -327,24 +327,8 @@ func crash() {
 }
 
 //go:nosplit
-func getRandomData(r []byte) {
-       // inspired by wyrand see hash32.go for detail
-       t := nanotime()
-       v := getg().m.procid ^ uint64(t)
-
-       for len(r) > 0 {
-               v ^= 0xa0761d6478bd642f
-               v *= 0xe7037ed1a0b428db
-               size := 8
-               if len(r) < 8 {
-                       size = len(r)
-               }
-               for i := 0; i < size; i++ {
-                       r[i] = byte(v >> (8 * i))
-               }
-               r = r[size:]
-               v = v>>32 | v<<32
-       }
+func readRandom(r []byte) int {
+       return 0
 }
 
 func initsig(preinit bool) {
index f165a34151f04181221a03142cc322d1c5868cf2..cce622932361acdb1ea93b12df45b48862cbbf79 100644 (file)
@@ -10,7 +10,6 @@ func checkgoarm() {
 
 //go:nosplit
 func cputicks() int64 {
-       // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
        return nanotime()
 }
index 8811bb617854b878292b6d741f3ffa69dcac62b5..acac2b3f7ad91250f5c9efae299907c29caf306f 100644 (file)
@@ -180,10 +180,11 @@ func usleep(usec uint32) {
        }
 }
 
-func getRandomData(r []byte) {
+func readRandom(r []byte) int {
        if random_get(unsafe.Pointer(&r[0]), size(len(r))) != 0 {
-               throw("random_get failed")
+               return 0
        }
+       return len(r)
 }
 
 func goenvs() {
index bf78dfb5f9f275042c6c9fccbd9c2e99cea00d21..ce260de67e5c03f16fd1c6a34682a06d39e1f51b 100644 (file)
@@ -122,9 +122,7 @@ func syscall_now() (sec int64, nsec int32) {
 
 //go:nosplit
 func cputicks() int64 {
-       // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
        // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
-       // TODO: need more entropy to better seed fastrand.
        return nanotime()
 }
 
index 3772a864b2ff44d3ba0cce20c67745721fa289bb..6533b6400490d140b9b419d7d0e750e682e9fdfa 100644 (file)
@@ -468,7 +468,10 @@ func initLongPathSupport() {
        // strictly necessary, but is a nice validity check for the near to
        // medium term, when this functionality is still relatively new in
        // Windows.
-       getRandomData(longFileName[len(longFileName)-33 : len(longFileName)-1])
+       targ := longFileName[len(longFileName)-33 : len(longFileName)-1]
+       if readRandom(targ) != len(targ) {
+               readTimeRandom(targ)
+       }
        start := copy(longFileName[:], sysDirectory[:sysDirectoryLen])
        const dig = "0123456789abcdef"
        for i := 0; i < 32; i++ {
@@ -519,12 +522,12 @@ func osinit() {
 }
 
 //go:nosplit
-func getRandomData(r []byte) {
+func readRandom(r []byte) int {
        n := 0
        if stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {
                n = len(r)
        }
-       extendRandom(r, n)
+       return n
 }
 
 func goenvs() {
index 63483358040e440e57e774a0aa09d20e1153d8c5..7bb8b81c26cbfea011eee32973d46093f2282db7 100644 (file)
@@ -784,8 +784,8 @@ func schedinit() {
        godebug := getGodebugEarly()
        initPageTrace(godebug) // must run after mallocinit but before anything allocates
        cpuinit(godebug)       // must run before alginit
-       alginit()              // maps, hash, fastrand must not be used before this call
-       fastrandinit()         // must run before mcommoninit
+       randinit()             // must run before alginit, mcommoninit
+       alginit()              // maps, hash, rand must not be used before this call
        mcommoninit(gp.m, -1)
        modulesinit()   // provides activeModules
        typelinksinit() // uses maps, activeModules
@@ -900,18 +900,7 @@ func mcommoninit(mp *m, id int64) {
                mp.id = mReserveID()
        }
 
-       lo := uint32(int64Hash(uint64(mp.id), fastrandseed))
-       hi := uint32(int64Hash(uint64(cputicks()), ^fastrandseed))
-       if lo|hi == 0 {
-               hi = 1
-       }
-       // Same behavior as for 1.17.
-       // TODO: Simplify this.
-       if goarch.BigEndian {
-               mp.fastrand = uint64(lo)<<32 | uint64(hi)
-       } else {
-               mp.fastrand = uint64(hi)<<32 | uint64(lo)
-       }
+       mrandinit(mp)
 
        mpreinit(mp)
        if mp.gsignal != nil {
@@ -957,13 +946,6 @@ const (
        osHasLowResClock = osHasLowResClockInt > 0
 )
 
-var fastrandseed uintptr
-
-func fastrandinit() {
-       s := (*[unsafe.Sizeof(fastrandseed)]byte)(unsafe.Pointer(&fastrandseed))[:]
-       getRandomData(s)
-}
-
 // Mark gp ready to run.
 func ready(gp *g, traceskip int, next bool) {
        status := readgstatus(gp)
@@ -3566,7 +3548,7 @@ func stealWork(now int64) (gp *g, inheritTime bool, rnow, pollUntil int64, newWo
        for i := 0; i < stealTries; i++ {
                stealTimersOrRunNextG := i == stealTries-1
 
-               for enum := stealOrder.start(fastrand()); !enum.done(); enum.next() {
+               for enum := stealOrder.start(cheaprand()); !enum.done(); enum.next() {
                        if sched.gcwaiting.Load() {
                                // GC work may be available.
                                return nil, false, now, pollUntil, true
@@ -4955,7 +4937,7 @@ func newproc1(fn *funcval, callergp *g, callerpc uintptr) *g {
                }
        }
        // Track initial transition?
-       newg.trackingSeq = uint8(fastrand())
+       newg.trackingSeq = uint8(cheaprand())
        if newg.trackingSeq%gTrackingPeriod == 0 {
                newg.tracking = true
        }
@@ -6636,7 +6618,7 @@ const randomizeScheduler = raceenabled
 // If the run queue is full, runnext puts g on the global queue.
 // Executed only by the owner P.
 func runqput(pp *p, gp *g, next bool) {
-       if randomizeScheduler && next && fastrandn(2) == 0 {
+       if randomizeScheduler && next && randn(2) == 0 {
                next = false
        }
 
@@ -6689,7 +6671,7 @@ func runqputslow(pp *p, gp *g, h, t uint32) bool {
 
        if randomizeScheduler {
                for i := uint32(1); i <= n; i++ {
-                       j := fastrandn(i + 1)
+                       j := cheaprandn(i + 1)
                        batch[i], batch[j] = batch[j], batch[i]
                }
        }
@@ -6730,7 +6712,7 @@ func runqputbatch(pp *p, q *gQueue, qsize int) {
                        return (pp.runqtail + o) % uint32(len(pp.runq))
                }
                for i := uint32(1); i < n; i++ {
-                       j := fastrandn(i + 1)
+                       j := cheaprandn(i + 1)
                        pp.runq[off(i)], pp.runq[off(j)] = pp.runq[off(j)], pp.runq[off(i)]
                }
        }
diff --git a/src/runtime/rand.go b/src/runtime/rand.go
new file mode 100644 (file)
index 0000000..6cb8dee
--- /dev/null
@@ -0,0 +1,225 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Random number generation
+
+package runtime
+
+import (
+       "internal/chacha8rand"
+       "internal/goarch"
+       "runtime/internal/math"
+       "unsafe"
+       _ "unsafe" // for go:linkname
+)
+
+// OS-specific startup can set startupRand if the OS passes
+// random data to the process at startup time.
+// For example Linux passes 16 bytes in the auxv vector.
+var startupRand []byte
+
+// globalRand holds the global random state.
+// It is only used at startup and for creating new m's.
+// Otherwise the per-m random state should be used
+// by calling goodrand.
+var globalRand struct {
+       lock  mutex
+       seed  [32]byte
+       state chacha8rand.State
+       init  bool
+}
+
+var readRandomFailed bool
+
+// randinit initializes the global random state.
+// It must be called before any use of grand.
+func randinit() {
+       lock(&globalRand.lock)
+       if globalRand.init {
+               fatal("randinit twice")
+       }
+
+       seed := &globalRand.seed
+       if startupRand != nil {
+               for i, c := range startupRand {
+                       seed[i%len(seed)] ^= c
+               }
+               clear(startupRand)
+               startupRand = nil
+       } else {
+               if readRandom(seed[:]) != len(seed) {
+                       // readRandom should never fail, but if it does we'd rather
+                       // not make Go binaries completely unusable, so make up
+                       // some random data based on the current time.
+                       readRandomFailed = true
+                       readTimeRandom(seed[:])
+               }
+       }
+       globalRand.state.Init(*seed)
+       clear(seed[:])
+       globalRand.init = true
+       unlock(&globalRand.lock)
+}
+
+// readTimeRandom stretches any entropy in the current time
+// into entropy the length of r and XORs it into r.
+// This is a fallback for when readRandom does not read
+// the full requested amount.
+// Whatever entropy r already contained is preserved.
+func readTimeRandom(r []byte) {
+       // Inspired by wyrand.
+       // An earlier version of this code used getg().m.procid as well,
+       // but note that this is called so early in startup that procid
+       // is not initialized yet.
+       v := uint64(nanotime())
+       for len(r) > 0 {
+               v ^= 0xa0761d6478bd642f
+               v *= 0xe7037ed1a0b428db
+               size := 8
+               if len(r) < 8 {
+                       size = len(r)
+               }
+               for i := 0; i < size; i++ {
+                       r[i] ^= byte(v >> (8 * i))
+               }
+               r = r[size:]
+               v = v>>32 | v<<32
+       }
+}
+
+// bootstrapRand returns a random uint64 from the global random generator.
+func bootstrapRand() uint64 {
+       lock(&globalRand.lock)
+       if !globalRand.init {
+               fatal("randinit missed")
+       }
+       for {
+               if x, ok := globalRand.state.Next(); ok {
+                       unlock(&globalRand.lock)
+                       return x
+               }
+               globalRand.state.Refill()
+       }
+}
+
+// bootstrapRandReseed reseeds the bootstrap random number generator,
+// clearing from memory any trace of previously returned random numbers.
+func bootstrapRandReseed() {
+       lock(&globalRand.lock)
+       if !globalRand.init {
+               fatal("randinit missed")
+       }
+       globalRand.state.Reseed()
+       unlock(&globalRand.lock)
+}
+
+// rand32 is uint32(rand()), called from compiler-generated code.
+//go:nosplit
+func rand32() uint32 {
+       return uint32(rand())
+}
+
+// rand returns a random uint64 from the per-m chacha8 state.
+// Do not change signature: used via linkname from other packages.
+//go:nosplit
+//go:linkname rand
+func rand() uint64 {
+       // Note: We avoid acquirem here so that in the fast path
+       // there is just a getg, an inlined c.Next, and a return.
+       // The performance difference on a 16-core AMD is
+       // 3.7ns/call this way versus 4.3ns/call with acquirem (+16%).
+       mp := getg().m
+       c := &mp.chacha8
+       for {
+               // Note: c.Next is marked nosplit,
+               // so we don't need to use mp.locks
+               // on the fast path, which is that the
+               // first attempt succeeds.
+               x, ok := c.Next()
+               if ok {
+                       return x
+               }
+               mp.locks++ // hold m even though c.Refill may do stack split checks
+               c.Refill()
+               mp.locks--
+       }
+}
+
+// mrandinit initializes the random state of an m.
+func mrandinit(mp *m) {
+       var seed [4]uint64
+       for i := range seed {
+               seed[i] = bootstrapRand()
+       }
+       bootstrapRandReseed() // erase key we just extracted
+       mp.chacha8.Init64(seed)
+       mp.cheaprand = rand()
+}
+
+// randn is like rand() % n but faster.
+// Do not change signature: used via linkname from other packages.
+//go:nosplit
+//go:linkname randn
+func randn(n uint32) uint32 {
+       // See https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
+       return uint32((uint64(uint32(rand())) * uint64(n)) >> 32)
+}
+
+// cheaprand is a non-cryptographic-quality 32-bit random generator
+// suitable for calling at very high frequency (such as during scheduling decisions)
+// and at sensitive moments in the runtime (such as during stack unwinding).
+// it is "cheap" in the sense of both expense and quality.
+//
+// cheaprand must not be exported to other packages:
+// the rule is that other packages using runtime-provided
+// randomness must always use rand.
+//go:nosplit
+func cheaprand() uint32 {
+       mp := getg().m
+       // Implement wyrand: https://github.com/wangyi-fudan/wyhash
+       // Only the platform that math.Mul64 can be lowered
+       // by the compiler should be in this list.
+       if goarch.IsAmd64|goarch.IsArm64|goarch.IsPpc64|
+               goarch.IsPpc64le|goarch.IsMips64|goarch.IsMips64le|
+               goarch.IsS390x|goarch.IsRiscv64|goarch.IsLoong64 == 1 {
+               mp.cheaprand += 0xa0761d6478bd642f
+               hi, lo := math.Mul64(mp.cheaprand, mp.cheaprand^0xe7037ed1a0b428db)
+               return uint32(hi ^ lo)
+       }
+
+       // Implement xorshift64+: 2 32-bit xorshift sequences added together.
+       // Shift triplet [17,7,16] was calculated as indicated in Marsaglia's
+       // Xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf
+       // This generator passes the SmallCrush suite, part of TestU01 framework:
+       // http://simul.iro.umontreal.ca/testu01/tu01.html
+       t := (*[2]uint32)(unsafe.Pointer(&mp.cheaprand))
+       s1, s0 := t[0], t[1]
+       s1 ^= s1 << 17
+       s1 = s1 ^ s0 ^ s1>>7 ^ s0>>16
+       t[0], t[1] = s0, s1
+       return s0 + s1
+}
+
+// cheaprand64 is a non-cryptographic-quality 63-bit random generator
+// suitable for calling at very high frequency (such as during sampling decisions).
+// it is "cheap" in the sense of both expense and quality.
+//
+// cheaprand64 must not be exported to other packages:
+// the rule is that other packages using runtime-provided
+// randomness must always use rand.
+//go:nosplit
+func cheaprand64() int64 {
+       return int64(cheaprand())<<31 ^ int64(cheaprand())
+}
+
+// cheaprandn is like cheaprand() % n but faster.
+//
+// cheaprandn must not be exported to other packages:
+// the rule is that other packages using runtime-provided
+// randomness must always use randn.
+//go:nosplit
+func cheaprandn(n uint32) uint32 {
+       // See https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
+       return uint32((uint64(cheaprand()) * uint64(n)) >> 32)
+}
index 92d07ebadac95c2377e661d542eb61f871d5296b..94648c5216a64d6c1565308600103d60bbbf390d 100644 (file)
@@ -10,6 +10,17 @@ import (
        "testing"
 )
 
+func TestReadRandom(t *testing.T) {
+       if *ReadRandomFailed {
+               switch GOOS {
+               default:
+                       t.Fatalf("readRandom failed at startup")
+               case "plan9":
+                       // ok
+               }
+       }
+}
+
 func BenchmarkFastrand(b *testing.B) {
        b.RunParallel(func(pb *testing.PB) {
                for pb.Next() {
index 01f1a50670fcfcc6e694259a6b856cf186f9d998..e64be992b055e53d1b02669cad129d473fa0d05d 100644 (file)
@@ -6,6 +6,7 @@ package runtime
 
 import (
        "internal/abi"
+       "internal/chacha8rand"
        "internal/goarch"
        "runtime/internal/atomic"
        "runtime/internal/sys"
@@ -577,7 +578,6 @@ type m struct {
        isExtraInC    bool          // m is an extra m that is not executing Go code
        isExtraInSig  bool          // m is an extra m in a signal handler
        freeWait      atomic.Uint32 // Whether it is safe to free g0 and delete m (one of freeMRef, freeMStack, freeMWait)
-       fastrand      uint64
        needextram    bool
        traceback     uint8
        ncgocall      uint64        // number of cgo calls in total
@@ -632,6 +632,9 @@ type m struct {
 
        mOS
 
+       chacha8   chacha8rand.State
+       cheaprand uint64
+
        // Up to 10 locks held by this m, maintained by the lock ranking code.
        locksHeldLen int
        locksHeld    [10]heldLockInfo
@@ -1009,27 +1012,6 @@ type forcegcstate struct {
        idle atomic.Bool
 }
 
-// extendRandom extends the random numbers in r[:n] to the whole slice r.
-// Treats n<0 as n==0.
-func extendRandom(r []byte, n int) {
-       if n < 0 {
-               n = 0
-       }
-       for n < len(r) {
-               // Extend random bits using hash function & time seed
-               w := n
-               if w > 16 {
-                       w = 16
-               }
-               h := memhash(unsafe.Pointer(&r[n-w]), uintptr(nanotime()), uintptr(w))
-               for i := 0; i < goarch.PtrSize && n < len(r); i++ {
-                       r[n] = byte(h)
-                       n++
-                       h >>= 8
-               }
-       }
-}
-
 // A _defer holds an entry on the list of deferred calls.
 // If you add a field here, add code to clear it in deferProcStack.
 // This struct must match the code in cmd/compile/internal/ssagen/ssa.go:deferstruct
index 34c06375c23a7c82bc327622fcead86afb145b40..b3a3085cb03bf912985d7edca5bee0c3d7661953 100644 (file)
@@ -173,7 +173,7 @@ func selectgo(cas0 *scase, order0 *uint16, pc0 *uintptr, nsends, nrecvs int, blo
                        continue
                }
 
-               j := fastrandn(uint32(norder + 1))
+               j := cheaprandn(uint32(norder + 1))
                pollorder[norder] = pollorder[j]
                pollorder[j] = uint16(i)
                norder++
index 3b6874ca1192c16c5f1e7333c991bf35bcb0b5d3..c87fc7658e9a831d830aed765080be99d9a58add 100644 (file)
@@ -338,7 +338,7 @@ func (root *semaRoot) queue(addr *uint32, s *sudog, lifo bool) {
        //
        // s.ticket compared with zero in couple of places, therefore set lowest bit.
        // It will not affect treap's quality noticeably.
-       s.ticket = fastrand() | 1
+       s.ticket = cheaprand() | 1
        s.parent = last
        *pt = s
 
index cf856e135f35fd382f21c5f975c2ad306a9d470f..34984d86ffafdf40e2573202dca3651bcc113210 100644 (file)
@@ -6,8 +6,6 @@ package runtime
 
 import (
        "internal/abi"
-       "internal/goarch"
-       "runtime/internal/math"
        "unsafe"
 )
 
@@ -120,94 +118,6 @@ func reflect_memmove(to, from unsafe.Pointer, n uintptr) {
 // exported value for testing
 const hashLoad = float32(loadFactorNum) / float32(loadFactorDen)
 
-//go:nosplit
-func fastrand() uint32 {
-       mp := getg().m
-       // Implement wyrand: https://github.com/wangyi-fudan/wyhash
-       // Only the platform that math.Mul64 can be lowered
-       // by the compiler should be in this list.
-       if goarch.IsAmd64|goarch.IsArm64|goarch.IsPpc64|
-               goarch.IsPpc64le|goarch.IsMips64|goarch.IsMips64le|
-               goarch.IsS390x|goarch.IsRiscv64|goarch.IsLoong64 == 1 {
-               mp.fastrand += 0xa0761d6478bd642f
-               hi, lo := math.Mul64(mp.fastrand, mp.fastrand^0xe7037ed1a0b428db)
-               return uint32(hi ^ lo)
-       }
-
-       // Implement xorshift64+: 2 32-bit xorshift sequences added together.
-       // Shift triplet [17,7,16] was calculated as indicated in Marsaglia's
-       // Xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf
-       // This generator passes the SmallCrush suite, part of TestU01 framework:
-       // http://simul.iro.umontreal.ca/testu01/tu01.html
-       t := (*[2]uint32)(unsafe.Pointer(&mp.fastrand))
-       s1, s0 := t[0], t[1]
-       s1 ^= s1 << 17
-       s1 = s1 ^ s0 ^ s1>>7 ^ s0>>16
-       t[0], t[1] = s0, s1
-       return s0 + s1
-}
-
-//go:nosplit
-func fastrandn(n uint32) uint32 {
-       // This is similar to fastrand() % n, but faster.
-       // See https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
-       return uint32(uint64(fastrand()) * uint64(n) >> 32)
-}
-
-func fastrand64() uint64 {
-       mp := getg().m
-       // Implement wyrand: https://github.com/wangyi-fudan/wyhash
-       // Only the platform that math.Mul64 can be lowered
-       // by the compiler should be in this list.
-       if goarch.IsAmd64|goarch.IsArm64|goarch.IsPpc64|
-               goarch.IsPpc64le|goarch.IsMips64|goarch.IsMips64le|
-               goarch.IsS390x|goarch.IsRiscv64 == 1 {
-               mp.fastrand += 0xa0761d6478bd642f
-               hi, lo := math.Mul64(mp.fastrand, mp.fastrand^0xe7037ed1a0b428db)
-               return hi ^ lo
-       }
-
-       // Implement xorshift64+: 2 32-bit xorshift sequences added together.
-       // Xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf
-       // This generator passes the SmallCrush suite, part of TestU01 framework:
-       // http://simul.iro.umontreal.ca/testu01/tu01.html
-       t := (*[2]uint32)(unsafe.Pointer(&mp.fastrand))
-       s1, s0 := t[0], t[1]
-       s1 ^= s1 << 17
-       s1 = s1 ^ s0 ^ s1>>7 ^ s0>>16
-       r := uint64(s0 + s1)
-
-       s0, s1 = s1, s0
-       s1 ^= s1 << 17
-       s1 = s1 ^ s0 ^ s1>>7 ^ s0>>16
-       r += uint64(s0+s1) << 32
-
-       t[0], t[1] = s0, s1
-       return r
-}
-
-func fastrandu() uint {
-       if goarch.PtrSize == 4 {
-               return uint(fastrand())
-       }
-       return uint(fastrand64())
-}
-
-//go:linkname rand_fastrand64 math/rand.fastrand64
-func rand_fastrand64() uint64 { return fastrand64() }
-
-//go:linkname rand2_fastrand64 math/rand/v2.fastrand64
-func rand2_fastrand64() uint64 { return fastrand64() }
-
-//go:linkname sync_fastrandn sync.fastrandn
-func sync_fastrandn(n uint32) uint32 { return fastrandn(n) }
-
-//go:linkname net_fastrandu net.fastrandu
-func net_fastrandu() uint { return fastrandu() }
-
-//go:linkname os_fastrand os.fastrand
-func os_fastrand() uint32 { return fastrand() }
-
 // in internal/bytealg/equal_*.s
 //
 //go:noescape
index 87b687a196e64d7db0e00581b9d8d687ca31c4af..8b878525d099024cdce27d041ad4346e18a4d288 100644 (file)
@@ -931,7 +931,7 @@ func pcvalue(f funcInfo, off uint32, targetpc uintptr, strict bool) (int32, uint
                                cache.inUse++
                                if cache.inUse == 1 {
                                        e := &cache.entries[ck]
-                                       ci := fastrandn(uint32(len(cache.entries[ck])))
+                                       ci := cheaprandn(uint32(len(cache.entries[ck])))
                                        e[ci] = e[0]
                                        e[0] = pcvalueCacheEnt{
                                                targetpc: targetpc,
index ffab67bf19eb673c91068e312a51823cbc643404..3359aba57b312ecacd22938963fcbd6a9ce665be 100644 (file)
@@ -76,7 +76,8 @@ type poolLocal struct {
 }
 
 // from runtime
-func fastrandn(n uint32) uint32
+//go:linkname runtime_randn runtime.randn
+func runtime_randn(n uint32) uint32
 
 var poolRaceHash [128]uint64
 
@@ -97,7 +98,7 @@ func (p *Pool) Put(x any) {
                return
        }
        if race.Enabled {
-               if fastrandn(4) == 0 {
+               if runtime_randn(4) == 0 {
                        // Randomly drop x on floor.
                        return
                }
index 6badb011b006a39ab77639e57b6a9fbe56c46ee3..5658c8ba06763ec1568a5391c7047c3e609fbfbe 100644 (file)
@@ -667,7 +667,7 @@ func bad40() {
 
 func good40() {
        ret := T40{}              // ERROR "stack object ret T40$"
-       ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hmap$"
+       ret.m = make(map[int]int) // ERROR "live at call to rand32: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hmap$"
        t := &ret
        printnl() // ERROR "live at call to printnl: ret$"
        // Note: ret is live at the printnl because the compiler moves &ret
index 80a9cc1002ba93c3a105a117db73b2bfae78c3aa..a335126b3f558f1f700f4371f936a666be181338 100644 (file)
@@ -664,7 +664,7 @@ func bad40() {
 
 func good40() {
        ret := T40{}              // ERROR "stack object ret T40$"
-       ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hmap$"
+       ret.m = make(map[int]int) // ERROR "live at call to rand32: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hmap$"
        t := &ret
        printnl() // ERROR "live at call to printnl: ret$"
        // Note: ret is live at the printnl because the compiler moves &ret