cmd/compile: dowidth more in the front end

dowidth is fundamentally unsafe to call from the back end;
it will cause data races.

Replace all calls to dowidth in the backend with
assertions that the width has been calculated.

Then fix all the cases in which that was not so,
including the cases from #20145.

Fixes #20145.

Change-Id: Idba3d19d75638851a30ec2ebcdb703c19da3e92b
Reviewed-on: https://go-review.googlesource.com/41970
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Josh Bleecher Snyder 2017-04-27 09:27:52 -07:00
parent be2ee2a4b4
commit 0b6a10ef24
5 changed files with 52 additions and 17 deletions

View File

@ -642,13 +642,6 @@ func (s *state) stmt(n *Node) {
return return
} }
var t *types.Type
if n.Right != nil {
t = n.Right.Type
} else {
t = n.Left.Type
}
// Evaluate RHS. // Evaluate RHS.
rhs := n.Right rhs := n.Right
if rhs != nil { if rhs != nil {
@ -682,6 +675,23 @@ func (s *state) stmt(n *Node) {
} }
} }
} }
if isblank(n.Left) {
// _ = rhs
// Just evaluate rhs for side-effects.
if rhs != nil {
s.expr(rhs)
}
return
}
var t *types.Type
if n.Right != nil {
t = n.Right.Type
} else {
t = n.Left.Type
}
var r *ssa.Value var r *ssa.Value
deref := !canSSAType(t) deref := !canSSAType(t)
if deref { if deref {
@ -1509,8 +1519,8 @@ func (s *state) expr(n *Node) *ssa.Value {
return v return v
} }
dowidth(from) from.AssertWidthCalculated()
dowidth(to) to.AssertWidthCalculated()
if from.Width != to.Width { if from.Width != to.Width {
s.Fatalf("CONVNOP width mismatch %v (%d) -> %v (%d)\n", from, from.Width, to, to.Width) s.Fatalf("CONVNOP width mismatch %v (%d) -> %v (%d)\n", from, from.Width, to, to.Width)
return nil return nil
@ -2311,7 +2321,7 @@ func (s *state) assign(left *Node, right *ssa.Value, deref bool, skip skipMask)
return return
} }
t := left.Type t := left.Type
dowidth(t) t.AssertWidthCalculated()
if s.canSSA(left) { if s.canSSA(left) {
if deref { if deref {
s.Fatalf("can SSA LHS %v but not RHS %s", left, right) s.Fatalf("can SSA LHS %v but not RHS %s", left, right)
@ -3085,7 +3095,7 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
} }
rcvr = s.newValue1(ssa.OpIData, types.Types[TUINTPTR], i) rcvr = s.newValue1(ssa.OpIData, types.Types[TUINTPTR], i)
} }
dowidth(fn.Type) fn.Type.AssertWidthCalculated()
stksize := fn.Type.ArgWidth() // includes receiver stksize := fn.Type.ArgWidth() // includes receiver
// Run all argument assignments. The arg slots have already // Run all argument assignments. The arg slots have already
@ -3341,7 +3351,7 @@ func (s *state) canSSA(n *Node) bool {
// canSSA reports whether variables of type t are SSA-able. // canSSA reports whether variables of type t are SSA-able.
func canSSAType(t *types.Type) bool { func canSSAType(t *types.Type) bool {
dowidth(t) t.AssertWidthCalculated()
if t.Width > int64(4*Widthptr) { if t.Width > int64(4*Widthptr) {
// 4*Widthptr is an arbitrary constant. We want it // 4*Widthptr is an arbitrary constant. We want it
// to be at least 3*Widthptr so slices can be registerized. // to be at least 3*Widthptr so slices can be registerized.
@ -4962,8 +4972,7 @@ func (e *ssafn) namedAuto(name string, typ ssa.Type, pos src.XPos) ssa.GCNode {
n.Esc = EscNever n.Esc = EscNever
n.Name.Curfn = e.curfn n.Name.Curfn = e.curfn
e.curfn.Func.Dcl = append(e.curfn.Func.Dcl, n) e.curfn.Func.Dcl = append(e.curfn.Func.Dcl, n)
t.AssertWidthCalculated()
dowidth(t)
return n return n
} }

View File

@ -1621,6 +1621,7 @@ OpSwitch:
continue continue
} }
as[i] = assignconv(n, t.Elem(), "append") as[i] = assignconv(n, t.Elem(), "append")
checkwidth(as[i].Type) // ensure width is calculated for backend
} }
} }
@ -1691,6 +1692,7 @@ OpSwitch:
case OCONV: case OCONV:
ok |= Erv ok |= Erv
saveorignode(n) saveorignode(n)
checkwidth(n.Type) // ensure width is calculated for backend
n.Left = typecheck(n.Left, Erv) n.Left = typecheck(n.Left, Erv)
n.Left = convlit1(n.Left, n.Type, true, noReuse) n.Left = convlit1(n.Left, n.Type, true, noReuse)
t := n.Left.Type t := n.Left.Type
@ -3307,6 +3309,9 @@ func typecheckas(n *Node) {
if n.Left.Typecheck() == 0 { if n.Left.Typecheck() == 0 {
n.Left = typecheck(n.Left, Erv|Easgn) n.Left = typecheck(n.Left, Erv|Easgn)
} }
if !isblank(n.Left) {
checkwidth(n.Left.Type) // ensure width is calculated for backend
}
} }
func checkassignto(src *types.Type, dst *Node) { func checkassignto(src *types.Type, dst *Node) {

View File

@ -2611,7 +2611,7 @@ func vmatch1(l *Node, r *Node) bool {
} }
// paramstoheap returns code to allocate memory for heap-escaped parameters // paramstoheap returns code to allocate memory for heap-escaped parameters
// and to copy non-result prameters' values from the stack. // and to copy non-result parameters' values from the stack.
func paramstoheap(params *types.Type) []*Node { func paramstoheap(params *types.Type) []*Node {
var nn []*Node var nn []*Node
for _, t := range params.Fields().Slice() { for _, t := range params.Fields().Slice() {

View File

@ -849,6 +849,13 @@ func (t *Type) WidthCalculated() bool {
return t.Align > 0 return t.Align > 0
} }
// AssertWidthCalculated calls Fatalf if t's width has not yet been calculated.
func (t *Type) AssertWidthCalculated() {
if !t.WidthCalculated() {
Fatalf("width not calculated: %v", t)
}
}
// ArgWidth returns the total aligned argument size for a function. // ArgWidth returns the total aligned argument size for a function.
// It includes the receiver, parameters, and results. // It includes the receiver, parameters, and results.
func (t *Type) ArgWidth() int64 { func (t *Type) ArgWidth() int64 {
@ -857,12 +864,12 @@ func (t *Type) ArgWidth() int64 {
} }
func (t *Type) Size() int64 { func (t *Type) Size() int64 {
Dowidth(t) t.AssertWidthCalculated()
return t.Width return t.Width
} }
func (t *Type) Alignment() int64 { func (t *Type) Alignment() int64 {
Dowidth(t) t.AssertWidthCalculated()
return int64(t.Align) return int64(t.Align)
} }

View File

@ -0,0 +1,14 @@
// compile
// Copyright 2017 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.
// Issue 20145: some func types weren't dowidth-ed by the front end,
// leading to races in the backend.
package p
func f() {
_ = (func())(nil)
}