aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilippo Valsorda <filippo@golang.org>2020-06-09 13:01:40 -0400
committerFilippo Valsorda <filippo@golang.org>2020-06-09 13:01:40 -0400
commit488ca930b24a12eb33fa7162b5907044f8f78bb9 (patch)
tree083a64a1cf268eee58cf6818eb547a3b4d4b075e
parentb129f40bb33f0061f2820553a6d168a9ef1d2405 (diff)
parent6be4a5eb4898c7b5e7557dda061cc09ba310698b (diff)
downloadgo-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.go28
-rw-r--r--src/math/big/rat.go70
-rw-r--r--src/math/big/rat_test.go56
-rw-r--r--src/runtime/crash_test.go14
-rw-r--r--src/runtime/proc.go6
-rw-r--r--src/runtime/proc_test.go24
-rw-r--r--src/runtime/testdata/testprog/lockosthread.go49
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")
+}