[dev.regabi] cmd/compile: cleanup for concrete types - escape

An automated rewrite will add concrete type assertions after
a test of n.Op(), when n can be safely type-asserted
(meaning, n is not reassigned a different type, n is not reassigned
and then used outside the scope of the type assertion,
and so on).

This sequence of CLs handles the code that the automated
rewrite does not: adding specific types to function arguments,
adjusting code not to call n.Left() etc when n may have multiple
representations, and so on.

This CL focuses on escape.go.

Passes buildall w/ toolstash -cmp.

Change-Id: I3e76e1ef9b72f28e3adad9633929699635d852dd
Reviewed-on: https://go-review.googlesource.com/c/go/+/277924
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Russ Cox 2020-12-10 18:46:13 -05:00
parent 846740c17f
commit aa55d4e54b

View file

@ -228,6 +228,7 @@ func (e *Escape) walkFunc(fn *ir.Func) {
ir.Visit(fn, func(n ir.Node) {
switch n.Op() {
case ir.OLABEL:
n := n.(*ir.LabelStmt)
if e.labels == nil {
e.labels = make(map[*types.Sym]labelState)
}
@ -236,6 +237,7 @@ func (e *Escape) walkFunc(fn *ir.Func) {
case ir.OGOTO:
// If we visited the label before the goto,
// then this is a looping label.
n := n.(*ir.BranchStmt)
if e.labels[n.Sym()] == nonlooping {
e.labels[n.Sym()] = looping
}
@ -305,15 +307,18 @@ func (e *Escape) stmt(n ir.Node) {
// TODO(mdempsky): Handle dead code?
case ir.OBLOCK:
n := n.(*ir.BlockStmt)
e.stmts(n.List())
case ir.ODCL:
// Record loop depth at declaration.
n := n.(*ir.Decl)
if !ir.IsBlank(n.Left()) {
e.dcl(n.Left())
}
case ir.OLABEL:
n := n.(*ir.LabelStmt)
switch e.labels[n.Sym()] {
case nonlooping:
if base.Flag.LowerM > 2 {
@ -330,11 +335,13 @@ func (e *Escape) stmt(n ir.Node) {
delete(e.labels, n.Sym())
case ir.OIF:
n := n.(*ir.IfStmt)
e.discard(n.Left())
e.block(n.Body())
e.block(n.Rlist())
case ir.OFOR, ir.OFORUNTIL:
n := n.(*ir.ForStmt)
e.loopDepth++
e.discard(n.Left())
e.stmt(n.Right())
@ -343,6 +350,7 @@ func (e *Escape) stmt(n ir.Node) {
case ir.ORANGE:
// for List = range Right { Nbody }
n := n.(*ir.RangeStmt)
e.loopDepth++
ks := e.addrs(n.List())
e.block(n.Body())
@ -360,11 +368,13 @@ func (e *Escape) stmt(n ir.Node) {
e.expr(e.later(k), n.Right())
case ir.OSWITCH:
n := n.(*ir.SwitchStmt)
typesw := n.Left() != nil && n.Left().Op() == ir.OTYPESW
var ks []EscHole
for _, cas := range n.List().Slice() { // cases
if typesw && n.Left().Left() != nil {
cas := cas.(*ir.CaseStmt)
if typesw && n.Left().(*ir.TypeSwitchGuard).Left() != nil {
cv := cas.Rlist().First()
k := e.dcl(cv) // type switch variables have no ODCL.
if cv.Type().HasPointers() {
@ -377,50 +387,65 @@ func (e *Escape) stmt(n ir.Node) {
}
if typesw {
e.expr(e.teeHole(ks...), n.Left().Right())
e.expr(e.teeHole(ks...), n.Left().(*ir.TypeSwitchGuard).Right())
} else {
e.discard(n.Left())
}
case ir.OSELECT:
n := n.(*ir.SelectStmt)
for _, cas := range n.List().Slice() {
cas := cas.(*ir.CaseStmt)
e.stmt(cas.Left())
e.block(cas.Body())
}
case ir.OSELRECV:
n := n.(*ir.AssignStmt)
e.assign(n.Left(), n.Right(), "selrecv", n)
case ir.OSELRECV2:
n := n.(*ir.AssignListStmt)
e.assign(n.List().First(), n.Rlist().First(), "selrecv", n)
e.assign(n.List().Second(), nil, "selrecv", n)
case ir.ORECV:
// TODO(mdempsky): Consider e.discard(n.Left).
n := n.(*ir.UnaryExpr)
e.exprSkipInit(e.discardHole(), n) // already visited n.Ninit
case ir.OSEND:
n := n.(*ir.SendStmt)
e.discard(n.Left())
e.assignHeap(n.Right(), "send", n)
case ir.OAS, ir.OASOP:
case ir.OAS:
n := n.(*ir.AssignStmt)
e.assign(n.Left(), n.Right(), "assign", n)
case ir.OASOP:
n := n.(*ir.AssignOpStmt)
e.assign(n.Left(), n.Right(), "assign", n)
case ir.OAS2:
n := n.(*ir.AssignListStmt)
for i, nl := range n.List().Slice() {
e.assign(nl, n.Rlist().Index(i), "assign-pair", n)
}
case ir.OAS2DOTTYPE: // v, ok = x.(type)
n := n.(*ir.AssignListStmt)
e.assign(n.List().First(), n.Rlist().First(), "assign-pair-dot-type", n)
e.assign(n.List().Second(), nil, "assign-pair-dot-type", n)
case ir.OAS2MAPR: // v, ok = m[k]
n := n.(*ir.AssignListStmt)
e.assign(n.List().First(), n.Rlist().First(), "assign-pair-mapr", n)
e.assign(n.List().Second(), nil, "assign-pair-mapr", n)
case ir.OAS2RECV: // v, ok = <-ch
n := n.(*ir.AssignListStmt)
e.assign(n.List().First(), n.Rlist().First(), "assign-pair-receive", n)
e.assign(n.List().Second(), nil, "assign-pair-receive", n)
case ir.OAS2FUNC:
n := n.(*ir.AssignListStmt)
e.stmts(n.Rlist().First().Init())
e.call(e.addrs(n.List()), n.Rlist().First(), nil)
case ir.ORETURN:
n := n.(*ir.ReturnStmt)
results := e.curfn.Type().Results().FieldSlice()
for i, v := range n.List().Slice() {
e.assign(ir.AsNode(results[i].Nname), v, "return", n)
@ -428,6 +453,7 @@ func (e *Escape) stmt(n ir.Node) {
case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OCLOSE, ir.OCOPY, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
e.call(nil, n, nil)
case ir.OGO, ir.ODEFER:
n := n.(*ir.GoDeferStmt)
e.stmts(n.Left().Init())
e.call(nil, n.Left(), n)
@ -472,7 +498,7 @@ func (e *Escape) exprSkipInit(k EscHole, n ir.Node) {
uintptrEscapesHack := k.uintptrEscapesHack
k.uintptrEscapesHack = false
if uintptrEscapesHack && n.Op() == ir.OCONVNOP && n.Left().Type().IsUnsafePtr() {
if uintptrEscapesHack && n.Op() == ir.OCONVNOP && n.(*ir.ConvExpr).Left().Type().IsUnsafePtr() {
// nop
} else if k.derefs >= 0 && !n.Type().HasPointers() {
k = e.discardHole()
@ -486,28 +512,40 @@ func (e *Escape) exprSkipInit(k EscHole, n ir.Node) {
// nop
case ir.ONAME:
n := n.(*ir.Name)
if n.Class() == ir.PFUNC || n.Class() == ir.PEXTERN {
return
}
e.flow(k, e.oldLoc(n))
case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT:
n := n.(*ir.UnaryExpr)
e.discard(n.Left())
case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.ODIV, ir.OMOD, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE, ir.OANDAND, ir.OOROR:
case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.ODIV, ir.OMOD, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
n := n.(*ir.BinaryExpr)
e.discard(n.Left())
e.discard(n.Right())
case ir.OANDAND, ir.OOROR:
n := n.(*ir.LogicalExpr)
e.discard(n.Left())
e.discard(n.Right())
case ir.OADDR:
n := n.(*ir.AddrExpr)
e.expr(k.addr(n, "address-of"), n.Left()) // "address-of"
case ir.ODEREF:
n := n.(*ir.StarExpr)
e.expr(k.deref(n, "indirection"), n.Left()) // "indirection"
case ir.ODOT, ir.ODOTMETH, ir.ODOTINTER:
n := n.(*ir.SelectorExpr)
e.expr(k.note(n, "dot"), n.Left())
case ir.ODOTPTR:
n := n.(*ir.SelectorExpr)
e.expr(k.deref(n, "dot of pointer"), n.Left()) // "dot of pointer"
case ir.ODOTTYPE, ir.ODOTTYPE2:
n := n.(*ir.TypeAssertExpr)
e.expr(k.dotType(n.Type(), n, "dot"), n.Left())
case ir.OINDEX:
n := n.(*ir.IndexExpr)
if n.Left().Type().IsArray() {
e.expr(k.note(n, "fixed-array-index-of"), n.Left())
} else {
@ -516,9 +554,11 @@ func (e *Escape) exprSkipInit(k EscHole, n ir.Node) {
}
e.discard(n.Right())
case ir.OINDEXMAP:
n := n.(*ir.IndexExpr)
e.discard(n.Left())
e.discard(n.Right())
case ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR:
n := n.(*ir.SliceExpr)
e.expr(k.note(n, "slice"), n.Left())
low, high, max := n.SliceBounds()
e.discard(low)
@ -526,6 +566,7 @@ func (e *Escape) exprSkipInit(k EscHole, n ir.Node) {
e.discard(max)
case ir.OCONV, ir.OCONVNOP:
n := n.(*ir.ConvExpr)
if checkPtr(e.curfn, 2) && n.Type().IsUnsafePtr() && n.Left().Type().IsPtr() {
// When -d=checkptr=2 is enabled, treat
// conversions to unsafe.Pointer as an
@ -540,27 +581,33 @@ func (e *Escape) exprSkipInit(k EscHole, n ir.Node) {
e.expr(k, n.Left())
}
case ir.OCONVIFACE:
n := n.(*ir.ConvExpr)
if !n.Left().Type().IsInterface() && !isdirectiface(n.Left().Type()) {
k = e.spill(k, n)
}
e.expr(k.note(n, "interface-converted"), n.Left())
case ir.ORECV:
n := n.(*ir.UnaryExpr)
e.discard(n.Left())
case ir.OCALLMETH, ir.OCALLFUNC, ir.OCALLINTER, ir.OLEN, ir.OCAP, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY:
e.call([]EscHole{k}, n, nil)
case ir.ONEW:
n := n.(*ir.UnaryExpr)
e.spill(k, n)
case ir.OMAKESLICE:
n := n.(*ir.MakeExpr)
e.spill(k, n)
e.discard(n.Left())
e.discard(n.Right())
case ir.OMAKECHAN:
n := n.(*ir.MakeExpr)
e.discard(n.Left())
case ir.OMAKEMAP:
n := n.(*ir.MakeExpr)
e.spill(k, n)
e.discard(n.Left())
@ -571,6 +618,7 @@ func (e *Escape) exprSkipInit(k EscHole, n ir.Node) {
// Flow the receiver argument to both the closure and
// to the receiver parameter.
n := n.(*ir.CallPartExpr)
closureK := e.spill(k, n)
m := callpartMethod(n)
@ -591,37 +639,43 @@ func (e *Escape) exprSkipInit(k EscHole, n ir.Node) {
e.expr(e.teeHole(paramK, closureK), n.Left())
case ir.OPTRLIT:
n := n.(*ir.AddrExpr)
e.expr(e.spill(k, n), n.Left())
case ir.OARRAYLIT:
n := n.(*ir.CompLitExpr)
for _, elt := range n.List().Slice() {
if elt.Op() == ir.OKEY {
elt = elt.Right()
elt = elt.(*ir.KeyExpr).Right()
}
e.expr(k.note(n, "array literal element"), elt)
}
case ir.OSLICELIT:
n := n.(*ir.CompLitExpr)
k = e.spill(k, n)
k.uintptrEscapesHack = uintptrEscapesHack // for ...uintptr parameters
for _, elt := range n.List().Slice() {
if elt.Op() == ir.OKEY {
elt = elt.Right()
elt = elt.(*ir.KeyExpr).Right()
}
e.expr(k.note(n, "slice-literal-element"), elt)
}
case ir.OSTRUCTLIT:
n := n.(*ir.CompLitExpr)
for _, elt := range n.List().Slice() {
e.expr(k.note(n, "struct literal element"), elt.Left())
e.expr(k.note(n, "struct literal element"), elt.(*ir.StructKeyExpr).Left())
}
case ir.OMAPLIT:
n := n.(*ir.CompLitExpr)
e.spill(k, n)
// Map keys and values are always stored in the heap.
for _, elt := range n.List().Slice() {
elt := elt.(*ir.KeyExpr)
e.assignHeap(elt.Left(), "map literal key", n)
e.assignHeap(elt.Right(), "map literal value", n)
}
@ -640,10 +694,12 @@ func (e *Escape) exprSkipInit(k EscHole, n ir.Node) {
}
case ir.ORUNES2STR, ir.OBYTES2STR, ir.OSTR2RUNES, ir.OSTR2BYTES, ir.ORUNESTR:
n := n.(*ir.ConvExpr)
e.spill(k, n)
e.discard(n.Left())
case ir.OADDSTR:
n := n.(*ir.AddStringExpr)
e.spill(k, n)
// Arguments of OADDSTR never escape;
@ -663,23 +719,28 @@ func (e *Escape) unsafeValue(k EscHole, n ir.Node) {
switch n.Op() {
case ir.OCONV, ir.OCONVNOP:
n := n.(*ir.ConvExpr)
if n.Left().Type().IsUnsafePtr() {
e.expr(k, n.Left())
} else {
e.discard(n.Left())
}
case ir.ODOTPTR:
n := n.(*ir.SelectorExpr)
if isReflectHeaderDataField(n) {
e.expr(k.deref(n, "reflect.Header.Data"), n.Left())
} else {
e.discard(n.Left())
}
case ir.OPLUS, ir.ONEG, ir.OBITNOT:
n := n.(*ir.UnaryExpr)
e.unsafeValue(k, n.Left())
case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.ODIV, ir.OMOD, ir.OAND, ir.OANDNOT:
n := n.(*ir.BinaryExpr)
e.unsafeValue(k, n.Left())
e.unsafeValue(k, n.Right())
case ir.OLSH, ir.ORSH:
n := n.(*ir.BinaryExpr)
e.unsafeValue(k, n.Left())
// RHS need not be uintptr-typed (#32959) and can't meaningfully
// flow pointers anyway.
@ -715,13 +776,16 @@ func (e *Escape) addr(n ir.Node) EscHole {
default:
base.Fatalf("unexpected addr: %v", n)
case ir.ONAME:
n := n.(*ir.Name)
if n.Class() == ir.PEXTERN {
break
}
k = e.oldLoc(n).asHole()
case ir.ODOT:
n := n.(*ir.SelectorExpr)
k = e.addr(n.Left())
case ir.OINDEX:
n := n.(*ir.IndexExpr)
e.discard(n.Right())
if n.Left().Type().IsArray() {
k = e.addr(n.Left())
@ -731,6 +795,7 @@ func (e *Escape) addr(n ir.Node) EscHole {
case ir.ODEREF, ir.ODOTPTR:
e.discard(n)
case ir.OINDEXMAP:
n := n.(*ir.IndexExpr)
e.discard(n.Left())
e.assignHeap(n.Right(), "key of map put", n)
}
@ -803,6 +868,7 @@ func (e *Escape) call(ks []EscHole, call, where ir.Node) {
base.Fatalf("unexpected call op: %v", call.Op())
case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
call := call.(*ir.CallExpr)
fixVariadicCall(call)
// Pick out the function callee, if statically known.
@ -810,7 +876,7 @@ func (e *Escape) call(ks []EscHole, call, where ir.Node) {
switch call.Op() {
case ir.OCALLFUNC:
switch v := staticValue(call.Left()); {
case v.Op() == ir.ONAME && v.Class() == ir.PFUNC:
case v.Op() == ir.ONAME && v.(*ir.Name).Class() == ir.PFUNC:
fn = v.(*ir.Name)
case v.Op() == ir.OCLOSURE:
fn = v.Func().Nname
@ -831,7 +897,7 @@ func (e *Escape) call(ks []EscHole, call, where ir.Node) {
}
if r := fntype.Recv(); r != nil {
argument(e.tagHole(ks, fn, r), call.Left().Left())
argument(e.tagHole(ks, fn, r), call.Left().(*ir.SelectorExpr).Left())
} else {
// Evaluate callee function expression.
argument(e.discardHole(), call.Left())
@ -843,6 +909,7 @@ func (e *Escape) call(ks []EscHole, call, where ir.Node) {
}
case ir.OAPPEND:
call := call.(*ir.CallExpr)
args := call.List().Slice()
// Appendee slice may flow directly to the result, if
@ -868,6 +935,7 @@ func (e *Escape) call(ks []EscHole, call, where ir.Node) {
}
case ir.OCOPY:
call := call.(*ir.BinaryExpr)
argument(e.discardHole(), call.Left())
copiedK := e.discardHole()
@ -877,16 +945,20 @@ func (e *Escape) call(ks []EscHole, call, where ir.Node) {
argument(copiedK, call.Right())
case ir.OPANIC:
call := call.(*ir.UnaryExpr)
argument(e.heapHole(), call.Left())
case ir.OCOMPLEX:
call := call.(*ir.BinaryExpr)
argument(e.discardHole(), call.Left())
argument(e.discardHole(), call.Right())
case ir.ODELETE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
call := call.(*ir.CallExpr)
for _, arg := range call.List().Slice() {
argument(e.discardHole(), arg)
}
case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE:
call := call.(*ir.UnaryExpr)
argument(e.discardHole(), call.Left())
}
}
@ -1082,6 +1154,7 @@ func (e *Escape) newLoc(n ir.Node, transient bool) *EscLocation {
e.allLocs = append(e.allLocs, loc)
if n != nil {
if n.Op() == ir.ONAME && n.Name().Curfn != e.curfn {
n := n.(*ir.Name)
base.Fatalf("curfn mismatch: %v != %v", n.Name().Curfn, e.curfn)
}
@ -1466,14 +1539,24 @@ func (e *Escape) finish(fns []*ir.Func) {
}
n.SetEsc(EscNone)
if loc.transient {
n.SetTransient(true)
switch n.Op() {
case ir.OCLOSURE:
n := n.(*ir.ClosureExpr)
n.SetTransient(true)
case ir.OCALLPART:
n := n.(*ir.CallPartExpr)
n.SetTransient(true)
case ir.OSLICELIT:
n := n.(*ir.CompLitExpr)
n.SetTransient(true)
}
}
}
}
}
func (l *EscLocation) isName(c ir.Class) bool {
return l.n != nil && l.n.Op() == ir.ONAME && l.n.Class() == c
return l.n != nil && l.n.Op() == ir.ONAME && l.n.(*ir.Name).Class() == c
}
const numEscResults = 7
@ -1636,7 +1719,18 @@ func isSliceSelfAssign(dst, src ir.Node) bool {
// when we evaluate it for dst and for src.
// dst is ONAME dereference.
if dst.Op() != ir.ODEREF && dst.Op() != ir.ODOTPTR || dst.Left().Op() != ir.ONAME {
var dstX ir.Node
switch dst.Op() {
default:
return false
case ir.ODEREF:
dst := dst.(*ir.StarExpr)
dstX = dst.Left()
case ir.ODOTPTR:
dst := dst.(*ir.SelectorExpr)
dstX = dst.Left()
}
if dstX.Op() != ir.ONAME {
return false
}
// src is a slice operation.
@ -1653,6 +1747,7 @@ func isSliceSelfAssign(dst, src ir.Node) bool {
// Pointer to an array is OK since it's not stored inside b directly.
// For slicing an array (not pointer to array), there is an implicit OADDR.
// We check that to determine non-pointer array slicing.
src := src.(*ir.SliceExpr)
if src.Left().Op() == ir.OADDR {
return false
}
@ -1660,11 +1755,22 @@ func isSliceSelfAssign(dst, src ir.Node) bool {
return false
}
// slice is applied to ONAME dereference.
if src.Left().Op() != ir.ODEREF && src.Left().Op() != ir.ODOTPTR || src.Left().Left().Op() != ir.ONAME {
var baseX ir.Node
switch base := src.(*ir.SliceExpr).Left(); base.Op() {
default:
return false
case ir.ODEREF:
base := base.(*ir.StarExpr)
baseX = base.Left()
case ir.ODOTPTR:
base := base.(*ir.SelectorExpr)
baseX = base.Left()
}
if baseX.Op() != ir.ONAME {
return false
}
// dst and src reference the same base ONAME.
return dst.Left() == src.Left().Left()
return dstX.(*ir.Name) == baseX.(*ir.Name)
}
// isSelfAssign reports whether assignment from src to dst can
@ -1688,19 +1794,23 @@ func isSelfAssign(dst, src ir.Node) bool {
return false
}
// The expression prefix must be both "safe" and identical.
switch dst.Op() {
case ir.ODOT, ir.ODOTPTR:
// Safe trailing accessors that are permitted to differ.
dst := dst.(*ir.SelectorExpr)
src := src.(*ir.SelectorExpr)
return samesafeexpr(dst.Left(), src.Left())
case ir.OINDEX:
dst := dst.(*ir.IndexExpr)
src := src.(*ir.IndexExpr)
if mayAffectMemory(dst.Right()) || mayAffectMemory(src.Right()) {
return false
}
return samesafeexpr(dst.Left(), src.Left())
default:
return false
}
// The expression prefix must be both "safe" and identical.
return samesafeexpr(dst.Left(), src.Left())
}
// mayAffectMemory reports whether evaluation of n may affect the program's
@ -1713,17 +1823,36 @@ func mayAffectMemory(n ir.Node) bool {
//
// We're ignoring things like division by zero, index out of range,
// and nil pointer dereference here.
// TODO(rsc): It seems like it should be possible to replace this with
// an ir.Any looking for any op that's not the ones in the case statement.
// But that produces changes in the compiled output detected by buildall.
switch n.Op() {
case ir.ONAME, ir.OCLOSUREREAD, ir.OLITERAL, ir.ONIL:
return false
// Left+Right group.
case ir.OINDEX, ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD:
case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD:
n := n.(*ir.BinaryExpr)
return mayAffectMemory(n.Left()) || mayAffectMemory(n.Right())
// Left group.
case ir.ODOT, ir.ODOTPTR, ir.ODEREF, ir.OCONVNOP, ir.OCONV, ir.OLEN, ir.OCAP,
ir.ONOT, ir.OBITNOT, ir.OPLUS, ir.ONEG, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
case ir.OINDEX:
n := n.(*ir.IndexExpr)
return mayAffectMemory(n.Left()) || mayAffectMemory(n.Right())
case ir.OCONVNOP, ir.OCONV:
n := n.(*ir.ConvExpr)
return mayAffectMemory(n.Left())
case ir.OLEN, ir.OCAP, ir.ONOT, ir.OBITNOT, ir.OPLUS, ir.ONEG, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
n := n.(*ir.UnaryExpr)
return mayAffectMemory(n.Left())
case ir.ODOT, ir.ODOTPTR:
n := n.(*ir.SelectorExpr)
return mayAffectMemory(n.Left())
case ir.ODEREF:
n := n.(*ir.StarExpr)
return mayAffectMemory(n.Left())
default:
@ -1739,8 +1868,11 @@ func heapAllocReason(n ir.Node) string {
}
// Parameters are always passed via the stack.
if n.Op() == ir.ONAME && (n.Class() == ir.PPARAM || n.Class() == ir.PPARAMOUT) {
return ""
if n.Op() == ir.ONAME {
n := n.(*ir.Name)
if n.Class() == ir.PPARAM || n.Class() == ir.PPARAMOUT {
return ""
}
}
if n.Type().Width > maxStackVarSize {
@ -1754,11 +1886,12 @@ func heapAllocReason(n ir.Node) string {
if n.Op() == ir.OCLOSURE && closureType(n).Size() >= maxImplicitStackVarSize {
return "too large for stack"
}
if n.Op() == ir.OCALLPART && partialCallType(n).Size() >= maxImplicitStackVarSize {
if n.Op() == ir.OCALLPART && partialCallType(n.(*ir.CallPartExpr)).Size() >= maxImplicitStackVarSize {
return "too large for stack"
}
if n.Op() == ir.OMAKESLICE {
n := n.(*ir.MakeExpr)
r := n.Right()
if r == nil {
r = n.Left()
@ -1833,10 +1966,20 @@ func addrescapes(n ir.Node) {
// In &x[0], if x is a slice, then x does not
// escape--the pointer inside x does, but that
// is always a heap pointer anyway.
case ir.ODOT, ir.OINDEX, ir.OPAREN, ir.OCONVNOP:
case ir.ODOT:
n := n.(*ir.SelectorExpr)
addrescapes(n.Left())
case ir.OINDEX:
n := n.(*ir.IndexExpr)
if !n.Left().Type().IsSlice() {
addrescapes(n.Left())
}
case ir.OPAREN:
n := n.(*ir.ParenExpr)
addrescapes(n.Left())
case ir.OCONVNOP:
n := n.(*ir.ConvExpr)
addrescapes(n.Left())
}
}
@ -1857,7 +2000,6 @@ func moveToHeap(n *ir.Name) {
// temp will add it to the function declaration list automatically.
heapaddr := temp(types.NewPtr(n.Type()))
heapaddr.SetSym(lookup("&" + n.Sym().Name))
ir.Orig(heapaddr).SetSym(heapaddr.Sym())
heapaddr.SetPos(n.Pos())
// Unset AutoTemp to persist the &foo variable name through SSA to
@ -1933,7 +2075,7 @@ const unsafeUintptrTag = "unsafe-uintptr"
// marked go:uintptrescapes.
const uintptrEscapesTag = "uintptr-escapes"
func (e *Escape) paramTag(fn ir.Node, narg int, f *types.Field) string {
func (e *Escape) paramTag(fn *ir.Func, narg int, f *types.Field) string {
name := func() string {
if f.Sym != nil {
return f.Sym.Name