1
0
mirror of https://github.com/golang/go synced 2024-11-26 15:06:52 -07:00

cmd/compile: provide types for all order-allocated temporaries

Ensure that we correctly type the stack temps for regular closures,
method function closures, and slice literals.

Then we don't need to override the dummy types later.
Furthermore, this allows order to reuse temporaries of these types.

OARRAYLIT doesn't need a temporary as far as I can tell, so I
removed that case from order.

Change-Id: Ic58520fa50c90639393ff78f33d3c831d5c4acb9
Reviewed-on: https://go-review.googlesource.com/c/140306
Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
Keith Randall 2018-10-06 14:31:08 -07:00
parent 296b7aeae0
commit 63e964e174
3 changed files with 66 additions and 42 deletions

View File

@ -337,18 +337,10 @@ func closuredebugruntimecheck(clo *Node) {
} }
} }
func walkclosure(clo *Node, init *Nodes) *Node { // closureType returns the struct type used to hold all the information
xfunc := clo.Func.Closure // needed in the closure for clo (clo must be a OCLOSURE node).
// The address of a variable of the returned type can be cast to a func.
// If no closure vars, don't bother wrapping. func closureType(clo *Node) *types.Type {
if hasemptycvars(clo) {
if Debug_closure > 0 {
Warnl(clo.Pos, "closure converted to global")
}
return xfunc.Func.Nname
}
closuredebugruntimecheck(clo)
// Create closure in the form of a composite literal. // Create closure in the form of a composite literal.
// supposing the closure captures an int i and a string s // supposing the closure captures an int i and a string s
// and has one float64 argument and no results, // and has one float64 argument and no results,
@ -362,11 +354,10 @@ func walkclosure(clo *Node, init *Nodes) *Node {
// The information appears in the binary in the form of type descriptors; // The information appears in the binary in the form of type descriptors;
// the struct is unnamed so that closures in multiple packages with the // the struct is unnamed so that closures in multiple packages with the
// same struct type can share the descriptor. // same struct type can share the descriptor.
fields := []*Node{ fields := []*Node{
namedfield(".F", types.Types[TUINTPTR]), namedfield(".F", types.Types[TUINTPTR]),
} }
for _, v := range xfunc.Func.Cvars.Slice() { for _, v := range clo.Func.Closure.Func.Cvars.Slice() {
typ := v.Type typ := v.Type
if !v.Name.Byval() { if !v.Name.Byval() {
typ = types.NewPtr(typ) typ = types.NewPtr(typ)
@ -375,6 +366,22 @@ func walkclosure(clo *Node, init *Nodes) *Node {
} }
typ := tostruct(fields) typ := tostruct(fields)
typ.SetNoalg(true) typ.SetNoalg(true)
return typ
}
func walkclosure(clo *Node, init *Nodes) *Node {
xfunc := clo.Func.Closure
// If no closure vars, don't bother wrapping.
if hasemptycvars(clo) {
if Debug_closure > 0 {
Warnl(clo.Pos, "closure converted to global")
}
return xfunc.Func.Nname
}
closuredebugruntimecheck(clo)
typ := closureType(clo)
clos := nod(OCOMPLIT, nil, nod(OIND, typenod(typ), nil)) clos := nod(OCOMPLIT, nil, nod(OIND, typenod(typ), nil))
clos.Esc = clo.Esc clos.Esc = clo.Esc
@ -389,10 +396,10 @@ func walkclosure(clo *Node, init *Nodes) *Node {
clos.Left.Esc = clo.Esc clos.Left.Esc = clo.Esc
// non-escaping temp to use, if any. // non-escaping temp to use, if any.
// orderexpr did not compute the type; fill it in now.
if x := prealloc[clo]; x != nil { if x := prealloc[clo]; x != nil {
x.Type = clos.Left.Left.Type if !eqtype(typ, x.Type) {
x.Orig.Type = x.Type panic("closure type does not match order's assigned type")
}
clos.Left.Right = x clos.Left.Right = x
delete(prealloc, clo) delete(prealloc, clo)
} }
@ -479,6 +486,18 @@ func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node {
return xfunc return xfunc
} }
// partialCallType returns the struct type used to hold all the information
// needed in the closure for n (n must be a OCALLPART node).
// The address of a variable of the returned type can be cast to a func.
func partialCallType(n *Node) *types.Type {
t := tostruct([]*Node{
namedfield("F", types.Types[TUINTPTR]),
namedfield("R", n.Left.Type),
})
t.SetNoalg(true)
return t
}
func walkpartialcall(n *Node, init *Nodes) *Node { func walkpartialcall(n *Node, init *Nodes) *Node {
// Create closure in the form of a composite literal. // Create closure in the form of a composite literal.
// For x.M with receiver (x) type T, the generated code looks like: // For x.M with receiver (x) type T, the generated code looks like:
@ -495,30 +514,25 @@ func walkpartialcall(n *Node, init *Nodes) *Node {
checknil(n.Left, init) checknil(n.Left, init)
} }
typ := tostruct([]*Node{ typ := partialCallType(n)
namedfield("F", types.Types[TUINTPTR]),
namedfield("R", n.Left.Type),
})
typ.SetNoalg(true)
clos := nod(OCOMPLIT, nil, nod(OIND, typenod(typ), nil)) clos := nod(OCOMPLIT, nil, nod(OIND, typenod(typ), nil))
clos.Esc = n.Esc clos.Esc = n.Esc
clos.Right.SetImplicit(true) clos.Right.SetImplicit(true)
clos.List.Set1(nod(OCFUNC, n.Func.Nname, nil)) clos.List.Set2(nod(OCFUNC, n.Func.Nname, nil), n.Left)
clos.List.Append(n.Left)
// Force type conversion from *struct to the func type. // Force type conversion from *struct to the func type.
clos = convnop(clos, n.Type) clos = convnop(clos, n.Type)
// typecheck will insert a PTRLIT node under CONVNOP, // The typecheck inside convnop will insert a PTRLIT node under CONVNOP.
// tag it with escape analysis result. // Tag it with escape analysis result.
clos.Left.Esc = n.Esc clos.Left.Esc = n.Esc
// non-escaping temp to use, if any. // non-escaping temp to use, if any.
// orderexpr did not compute the type; fill it in now.
if x := prealloc[n]; x != nil { if x := prealloc[n]; x != nil {
x.Type = clos.Left.Left.Type if !eqtype(typ, x.Type) {
x.Orig.Type = x.Type panic("partial call type does not match order's assigned type")
}
clos.Left.Right = x clos.Left.Right = x
delete(prealloc, n) delete(prealloc, n)
} }

View File

@ -244,12 +244,6 @@ func (o *Order) markTemp() ordermarker {
// which must have been returned by marktemp. // which must have been returned by marktemp.
func (o *Order) popTemp(mark ordermarker) { func (o *Order) popTemp(mark ordermarker) {
for _, n := range o.temp[mark:] { for _, n := range o.temp[mark:] {
if n.Type.Etype == types.TUINT8 {
// Don't recycle temps of this type. TUINT8 is used
// as a placeholder for a type to be determined later.
// TODO: fix
continue
}
key := n.Type.LongString() key := n.Type.LongString()
o.free[key] = append(o.free[key], n) o.free[key] = append(o.free[key], n)
} }
@ -1170,16 +1164,23 @@ func (o *Order) expr(n, lhs *Node) *Node {
case OCLOSURE: case OCLOSURE:
if n.Noescape() && n.Func.Closure.Func.Cvars.Len() > 0 { if n.Noescape() && n.Func.Closure.Func.Cvars.Len() > 0 {
prealloc[n] = o.newTemp(types.Types[TUINT8], false) // walk will fill in correct type prealloc[n] = o.newTemp(closureType(n), false)
} }
case OARRAYLIT, OSLICELIT, OCALLPART: case OSLICELIT, OCALLPART:
n.Left = o.expr(n.Left, nil) n.Left = o.expr(n.Left, nil)
n.Right = o.expr(n.Right, nil) n.Right = o.expr(n.Right, nil)
o.exprList(n.List) o.exprList(n.List)
o.exprList(n.Rlist) o.exprList(n.Rlist)
if n.Noescape() { if n.Noescape() {
prealloc[n] = o.newTemp(types.Types[TUINT8], false) // walk will fill in correct type var t *types.Type
switch n.Op {
case OSLICELIT:
t = types.NewArray(n.Type.Elem(), n.Right.Int64())
case OCALLPART:
t = partialCallType(n)
}
prealloc[n] = o.newTemp(t, false)
} }
case ODDDARG: case ODDDARG:

View File

@ -404,8 +404,8 @@ func f27(b bool) {
if b { if b {
call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{" call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{"
} }
call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{" call27(func() { x++ })
call27(func() { x++ }) // ERROR "stack object .autotmp_[0-9]+ struct \{" call27(func() { x++ })
printnl() printnl()
} }
@ -521,8 +521,8 @@ func f32(b bool) {
if b { if b {
call32(t32.Inc) // ERROR "stack object .autotmp_[0-9]+ struct \{" call32(t32.Inc) // ERROR "stack object .autotmp_[0-9]+ struct \{"
} }
call32(t32.Inc) // ERROR "stack object .autotmp_[0-9]+ struct \{" call32(t32.Inc)
call32(t32.Inc) // ERROR "stack object .autotmp_[0-9]+ struct \{" call32(t32.Inc)
} }
//go:noescape //go:noescape
@ -694,3 +694,12 @@ func f41(p, q *int) (r *int) { // ERROR "live at entry to f41: p q$"
r = q r = q
return // ERROR "live at call to deferreturn: r$" return // ERROR "live at call to deferreturn: r$"
} }
func f42() {
var p, q, r int
f43([]*int{&p,&q,&r}) // ERROR "stack object .autotmp_[0-9]+ \[3\]\*int$"
f43([]*int{&p,&r,&q})
f43([]*int{&q,&p,&r})
}
//go:noescape
func f43(a []*int)