diff options
author | Filippo Valsorda <filippo@golang.org> | 2020-06-09 13:01:40 -0400 |
---|---|---|
committer | Filippo Valsorda <filippo@golang.org> | 2020-06-09 13:01:40 -0400 |
commit | 488ca930b24a12eb33fa7162b5907044f8f78bb9 (patch) | |
tree | 083a64a1cf268eee58cf6818eb547a3b4d4b075e | |
parent | b129f40bb33f0061f2820553a6d168a9ef1d2405 (diff) | |
parent | 6be4a5eb4898c7b5e7557dda061cc09ba310698b (diff) | |
download | go-488ca930b24a12eb33fa7162b5907044f8f78bb9.tar.gz go-488ca930b24a12eb33fa7162b5907044f8f78bb9.zip |
[dev.boringcrypto.go1.13] all: merge go1.13.12 into dev.boringcrypto.go1.13
Change-Id: I34cc756b36cad8c9583d64db2d23c74c14b894c3
-rw-r--r-- | src/cmd/compile/internal/ssa/regalloc.go | 28 | ||||
-rw-r--r-- | src/math/big/rat.go | 70 | ||||
-rw-r--r-- | src/math/big/rat_test.go | 56 | ||||
-rw-r--r-- | src/runtime/crash_test.go | 14 | ||||
-rw-r--r-- | src/runtime/proc.go | 6 | ||||
-rw-r--r-- | src/runtime/proc_test.go | 24 | ||||
-rw-r--r-- | src/runtime/testdata/testprog/lockosthread.go | 49 |
7 files changed, 197 insertions, 50 deletions
diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index 8abbf61507..e92ab00e95 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -976,25 +976,22 @@ func (s *regAllocState) regalloc(f *Func) { } } - // Second pass - deallocate any phi inputs which are now dead. + // Second pass - deallocate all in-register phi inputs. for i, v := range phis { if !s.values[v.ID].needReg { continue } a := v.Args[idx] - if !regValLiveSet.contains(a.ID) { - // Input is dead beyond the phi, deallocate - // anywhere else it might live. - s.freeRegs(s.values[a.ID].regs) - } else { - // Input is still live. + r := phiRegs[i] + if r == noRegister { + continue + } + if regValLiveSet.contains(a.ID) { + // Input value is still live (it is used by something other than Phi). // Try to move it around before kicking out, if there is a free register. // We generate a Copy in the predecessor block and record it. It will be - // deleted if never used. - r := phiRegs[i] - if r == noRegister { - continue - } + // deleted later if never used. + // // Pick a free register. At this point some registers used in the predecessor // block may have been deallocated. Those are the ones used for Phis. Exclude // them (and they are not going to be helpful anyway). @@ -1010,8 +1007,8 @@ func (s *regAllocState) regalloc(f *Func) { s.assignReg(r2, a, c) s.endRegs[p.ID] = append(s.endRegs[p.ID], endReg{r2, a, c}) } - s.freeReg(r) } + s.freeReg(r) } // Copy phi ops into new schedule. @@ -1842,6 +1839,11 @@ func (s *regAllocState) shuffle(stacklive [][]ID) { e.process() } } + + if s.f.pass.debug > regDebug { + fmt.Printf("post shuffle %s\n", s.f.Name) + fmt.Println(s.f.String()) + } } type edgeState struct { diff --git a/src/math/big/rat.go b/src/math/big/rat.go index c8bf698b18..d35cd4cbd1 100644 --- a/src/math/big/rat.go +++ b/src/math/big/rat.go @@ -22,7 +22,9 @@ import ( // of Rats are not supported and may lead to errors. type Rat struct { // To make zero values for Rat work w/o initialization, - // a zero value of b (len(b) == 0) acts like b == 1. + // a zero value of b (len(b) == 0) acts like b == 1. At + // the earliest opportunity (when an assignment to the Rat + // is made), such uninitialized denominators are set to 1. // a.neg determines the sign of the Rat, b.neg is ignored. a, b Int } @@ -271,7 +273,7 @@ func quotToFloat64(a, b nat) (f float64, exact bool) { func (x *Rat) Float32() (f float32, exact bool) { b := x.b.abs if len(b) == 0 { - b = b.set(natOne) // materialize denominator + b = natOne } f, exact = quotToFloat32(x.a.abs, b) if x.a.neg { @@ -287,7 +289,7 @@ func (x *Rat) Float32() (f float32, exact bool) { func (x *Rat) Float64() (f float64, exact bool) { b := x.b.abs if len(b) == 0 { - b = b.set(natOne) // materialize denominator + b = natOne } f, exact = quotToFloat64(x.a.abs, b) if x.a.neg { @@ -297,6 +299,7 @@ func (x *Rat) Float64() (f float64, exact bool) { } // SetFrac sets z to a/b and returns z. +// If b == 0, SetFrac panics. func (z *Rat) SetFrac(a, b *Int) *Rat { z.a.neg = a.neg != b.neg babs := b.abs @@ -312,11 +315,12 @@ func (z *Rat) SetFrac(a, b *Int) *Rat { } // SetFrac64 sets z to a/b and returns z. +// If b == 0, SetFrac64 panics. func (z *Rat) SetFrac64(a, b int64) *Rat { - z.a.SetInt64(a) if b == 0 { panic("division by zero") } + z.a.SetInt64(a) if b < 0 { b = -b z.a.neg = !z.a.neg @@ -328,21 +332,21 @@ func (z *Rat) SetFrac64(a, b int64) *Rat { // SetInt sets z to x (by making a copy of x) and returns z. func (z *Rat) SetInt(x *Int) *Rat { z.a.Set(x) - z.b.abs = z.b.abs[:0] + z.b.abs = z.b.abs.setWord(1) return z } // SetInt64 sets z to x and returns z. func (z *Rat) SetInt64(x int64) *Rat { z.a.SetInt64(x) - z.b.abs = z.b.abs[:0] + z.b.abs = z.b.abs.setWord(1) return z } // SetUint64 sets z to x and returns z. func (z *Rat) SetUint64(x uint64) *Rat { z.a.SetUint64(x) - z.b.abs = z.b.abs[:0] + z.b.abs = z.b.abs.setWord(1) return z } @@ -352,6 +356,9 @@ func (z *Rat) Set(x *Rat) *Rat { z.a.Set(&x.a) z.b.Set(&x.b) } + if len(z.b.abs) == 0 { + z.b.abs = z.b.abs.setWord(1) + } return z } @@ -370,20 +377,13 @@ func (z *Rat) Neg(x *Rat) *Rat { } // Inv sets z to 1/x and returns z. +// If x == 0, Inv panics. func (z *Rat) Inv(x *Rat) *Rat { if len(x.a.abs) == 0 { panic("division by zero") } z.Set(x) - a := z.b.abs - if len(a) == 0 { - a = a.set(natOne) // materialize numerator - } - b := z.a.abs - if b.cmp(natOne) == 0 { - b = b[:0] // normalize denominator - } - z.a.abs, z.b.abs = a, b // sign doesn't change + z.a.abs, z.b.abs = z.b.abs, z.a.abs return z } @@ -411,12 +411,19 @@ func (x *Rat) Num() *Int { } // Denom returns the denominator of x; it is always > 0. -// The result is a reference to x's denominator; it +// The result is a reference to x's denominator, unless +// x is an uninitialized (zero value) Rat, in which case +// the result is a new Int of value 1. (To initialize x, +// any operation that sets x will do, including x.Set(x).) +// If the result is a reference to x's denominator it // may change if a new value is assigned to x, and vice versa. func (x *Rat) Denom() *Int { x.b.neg = false // the result is always >= 0 if len(x.b.abs) == 0 { - x.b.abs = x.b.abs.set(natOne) // materialize denominator + // Note: If this proves problematic, we could + // panic instead and require the Rat to + // be explicitly initialized. + return &Int{abs: nat{1}} } return &x.b } @@ -424,25 +431,20 @@ func (x *Rat) Denom() *Int { func (z *Rat) norm() *Rat { switch { case len(z.a.abs) == 0: - // z == 0 - normalize sign and denominator + // z == 0; normalize sign and denominator z.a.neg = false - z.b.abs = z.b.abs[:0] + fallthrough case len(z.b.abs) == 0: - // z is normalized int - nothing to do - case z.b.abs.cmp(natOne) == 0: - // z is int - normalize denominator - z.b.abs = z.b.abs[:0] + // z is integer; normalize denominator + z.b.abs = z.b.abs.setWord(1) default: + // z is fraction; normalize numerator and denominator neg := z.a.neg z.a.neg = false z.b.neg = false if f := NewInt(0).lehmerGCD(nil, nil, &z.a, &z.b); f.Cmp(intOne) != 0 { z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f.abs) z.b.abs, _ = z.b.abs.div(nil, z.b.abs, f.abs) - if z.b.abs.cmp(natOne) == 0 { - // z is int - normalize denominator - z.b.abs = z.b.abs[:0] - } } z.a.neg = neg } @@ -454,6 +456,8 @@ func (z *Rat) norm() *Rat { // returns z. func mulDenom(z, x, y nat) nat { switch { + case len(x) == 0 && len(y) == 0: + return z.setWord(1) case len(x) == 0: return z.set(y) case len(y) == 0: @@ -509,10 +513,14 @@ func (z *Rat) Sub(x, y *Rat) *Rat { // Mul sets z to the product x*y and returns z. func (z *Rat) Mul(x, y *Rat) *Rat { if x == y { - // a squared Rat is positive and can't be reduced + // a squared Rat is positive and can't be reduced (no need to call norm()) z.a.neg = false z.a.abs = z.a.abs.sqr(x.a.abs) - z.b.abs = z.b.abs.sqr(x.b.abs) + if len(x.b.abs) == 0 { + z.b.abs = z.b.abs.setWord(1) + } else { + z.b.abs = z.b.abs.sqr(x.b.abs) + } return z } z.a.Mul(&x.a, &y.a) @@ -521,7 +529,7 @@ func (z *Rat) Mul(x, y *Rat) *Rat { } // Quo sets z to the quotient x/y and returns z. -// If y == 0, a division-by-zero run-time panic occurs. +// If y == 0, Quo panics. func (z *Rat) Quo(x, y *Rat) *Rat { if len(y.a.abs) == 0 { panic("division by zero") diff --git a/src/math/big/rat_test.go b/src/math/big/rat_test.go index 83c5d5cfea..02569c1b16 100644 --- a/src/math/big/rat_test.go +++ b/src/math/big/rat_test.go @@ -329,18 +329,40 @@ func TestIssue3521(t *testing.T) { t.Errorf("0) got %s want %s", zero.Denom(), one) } - // 1a) a zero value remains zero independent of denominator + // 1a) the denominator of an (uninitialized) zero value is not shared with the value + s := &zero.b + d := zero.Denom() + if d == s { + t.Errorf("1a) got %s (%p) == %s (%p) want different *Int values", d, d, s, s) + } + + // 1b) the denominator of an (uninitialized) value is a new 1 each time + d1 := zero.Denom() + d2 := zero.Denom() + if d1 == d2 { + t.Errorf("1b) got %s (%p) == %s (%p) want different *Int values", d1, d1, d2, d2) + } + + // 1c) the denominator of an initialized zero value is shared with the value x := new(Rat) + x.Set(x) // initialize x (any operation that sets x explicitly will do) + s = &x.b + d = x.Denom() + if d != s { + t.Errorf("1c) got %s (%p) != %s (%p) want identical *Int values", d, d, s, s) + } + + // 1d) a zero value remains zero independent of denominator x.Denom().Set(new(Int).Neg(b)) if x.Cmp(zero) != 0 { - t.Errorf("1a) got %s want %s", x, zero) + t.Errorf("1d) got %s want %s", x, zero) } - // 1b) a zero value may have a denominator != 0 and != 1 + // 1e) a zero value may have a denominator != 0 and != 1 x.Num().Set(a) qab := new(Rat).SetFrac(a, b) if x.Cmp(qab) != 0 { - t.Errorf("1b) got %s want %s", x, qab) + t.Errorf("1e) got %s want %s", x, qab) } // 2a) an integral value becomes a fraction depending on denominator @@ -678,3 +700,29 @@ func BenchmarkRatCmp(b *testing.B) { x.Cmp(y) } } + +// TestIssue34919 verifies that a Rat's denominator is not modified +// when simply accessing the Rat value. +func TestIssue34919(t *testing.T) { + for _, acc := range []struct { + name string + f func(*Rat) + }{ + {"Float32", func(x *Rat) { x.Float32() }}, + {"Float64", func(x *Rat) { x.Float64() }}, + {"Inv", func(x *Rat) { new(Rat).Inv(x) }}, + {"Sign", func(x *Rat) { x.Sign() }}, + {"IsInt", func(x *Rat) { x.IsInt() }}, + {"Num", func(x *Rat) { x.Num() }}, + // {"Denom", func(x *Rat) { x.Denom() }}, TODO(gri) should we change the API? See issue #33792. + } { + // A denominator of length 0 is interpreted as 1. Make sure that + // "materialization" of the denominator doesn't lead to setting + // the underlying array element 0 to 1. + r := &Rat{Int{abs: nat{991}}, Int{abs: make(nat, 0, 1)}} + acc.f(r) + if d := r.b.abs[:1][0]; d != 0 { + t.Errorf("%s modified denominator: got %d, want 0", acc.name, d) + } + } +} diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index c54bb57da2..ac0344e9a3 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -55,6 +55,16 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string { t.Fatal(err) } + return runBuiltTestProg(t, exe, name, env...) +} + +func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string { + if *flagQuick { + t.Skip("-quick") + } + + testenv.MustHaveGoBuild(t) + cmd := testenv.CleanCmdEnv(exec.Command(exe, name)) cmd.Env = append(cmd.Env, env...) if testing.Short() { @@ -64,7 +74,7 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string { cmd.Stdout = &b cmd.Stderr = &b if err := cmd.Start(); err != nil { - t.Fatalf("starting %s %s: %v", binary, name, err) + t.Fatalf("starting %s %s: %v", exe, name, err) } // If the process doesn't complete within 1 minute, @@ -92,7 +102,7 @@ func runTestProg(t *testing.T, binary, name string, env ...string) string { }() if err := cmd.Wait(); err != nil { - t.Logf("%s %s exit status: %v", binary, name, err) + t.Logf("%s %s exit status: %v", exe, name, err) } close(done) diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 93d329d15e..3723f84a05 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -1862,10 +1862,16 @@ func startTemplateThread() { if GOARCH == "wasm" { // no threads on wasm yet return } + + // Disable preemption to guarantee that the template thread will be + // created before a park once haveTemplateThread is set. + mp := acquirem() if !atomic.Cas(&newmHandoff.haveTemplateThread, 0, 1) { + releasem(mp) return } newm(templateThread, nil) + releasem(mp) } // templateThread is a thread in a known-good state that exists solely diff --git a/src/runtime/proc_test.go b/src/runtime/proc_test.go index 6e6272e80a..108be6a881 100644 --- a/src/runtime/proc_test.go +++ b/src/runtime/proc_test.go @@ -6,6 +6,7 @@ package runtime_test import ( "fmt" + "internal/testenv" "math" "net" "runtime" @@ -912,6 +913,29 @@ func TestLockOSThreadAvoidsStatePropagation(t *testing.T) { } } +func TestLockOSThreadTemplateThreadRace(t *testing.T) { + testenv.MustHaveGoRun(t) + + exe, err := buildTestProg(t, "testprog") + if err != nil { + t.Fatal(err) + } + + iterations := 100 + if testing.Short() { + // Reduce run time to ~100ms, with much lower probability of + // catching issues. + iterations = 5 + } + for i := 0; i < iterations; i++ { + want := "OK\n" + output := runBuiltTestProg(t, exe, "LockOSThreadTemplateThreadRace") + if output != want { + t.Fatalf("run %d: want %q, got %q", i, want, output) + } + } +} + // fakeSyscall emulates a system call. //go:nosplit func fakeSyscall(duration time.Duration) { diff --git a/src/runtime/testdata/testprog/lockosthread.go b/src/runtime/testdata/testprog/lockosthread.go index fd3123e647..098cc4dd72 100644 --- a/src/runtime/testdata/testprog/lockosthread.go +++ b/src/runtime/testdata/testprog/lockosthread.go @@ -7,6 +7,7 @@ package main import ( "os" "runtime" + "sync" "time" ) @@ -30,6 +31,7 @@ func init() { runtime.LockOSThread() }) register("LockOSThreadAvoidsStatePropagation", LockOSThreadAvoidsStatePropagation) + register("LockOSThreadTemplateThreadRace", LockOSThreadTemplateThreadRace) } func LockOSThreadMain() { @@ -195,3 +197,50 @@ func LockOSThreadAvoidsStatePropagation() { runtime.UnlockOSThread() println("OK") } + +func LockOSThreadTemplateThreadRace() { + // This test attempts to reproduce the race described in + // golang.org/issue/38931. To do so, we must have a stop-the-world + // (achieved via ReadMemStats) racing with two LockOSThread calls. + // + // While this test attempts to line up the timing, it is only expected + // to fail (and thus hang) around 2% of the time if the race is + // present. + + // Ensure enough Ps to actually run everything in parallel. Though on + // <4 core machines, we are still at the whim of the kernel scheduler. + runtime.GOMAXPROCS(4) + + go func() { + // Stop the world; race with LockOSThread below. + var m runtime.MemStats + for { + runtime.ReadMemStats(&m) + } + }() + + // Try to synchronize both LockOSThreads. + start := time.Now().Add(10*time.Millisecond) + + var wg sync.WaitGroup + wg.Add(2) + + for i := 0; i < 2; i++ { + go func() { + for time.Now().Before(start) { + } + + // Add work to the local runq to trigger early startm + // in handoffp. + go func(){}() + + runtime.LockOSThread() + runtime.Gosched() // add a preemption point. + wg.Done() + }() + } + + wg.Wait() + // If both LockOSThreads completed then we did not hit the race. + println("OK") +} |