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

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 subr.go.

Passes buildall w/ toolstash -cmp.

Change-Id: I435082167c91e20a4d490aa5d5945c7454f71d61
Reviewed-on: https://go-review.googlesource.com/c/go/+/277930
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Russ Cox 2020-12-10 18:47:58 -05:00
parent dd67b13d07
commit 5024396563
2 changed files with 92 additions and 39 deletions

View file

@ -555,7 +555,7 @@ func assignconvfn(n ir.Node, t *types.Type, context func() string) ir.Node {
// backingArrayPtrLen extracts the pointer and length from a slice or string. // backingArrayPtrLen extracts the pointer and length from a slice or string.
// This constructs two nodes referring to n, so n must be a cheapexpr. // This constructs two nodes referring to n, so n must be a cheapexpr.
func backingArrayPtrLen(n ir.Node) (ptr, len ir.Node) { func backingArrayPtrLen(n ir.Node) (ptr, length ir.Node) {
var init ir.Nodes var init ir.Nodes
c := cheapexpr(n, &init) c := cheapexpr(n, &init)
if c != n || init.Len() != 0 { if c != n || init.Len() != 0 {
@ -567,9 +567,9 @@ func backingArrayPtrLen(n ir.Node) (ptr, len ir.Node) {
} else { } else {
ptr.SetType(n.Type().Elem().PtrTo()) ptr.SetType(n.Type().Elem().PtrTo())
} }
len = ir.Nod(ir.OLEN, n, nil) length = ir.Nod(ir.OLEN, n, nil)
len.SetType(types.Types[types.TINT]) length.SetType(types.Types[types.TINT])
return ptr, len return ptr, length
} }
func syslook(name string) ir.Node { func syslook(name string) ir.Node {
@ -605,6 +605,10 @@ func calcHasCall(n ir.Node) bool {
} }
switch n.Op() { switch n.Op() {
default:
base.Fatalf("calcHasCall %+v", n)
panic("unreachable")
case ir.OLITERAL, ir.ONIL, ir.ONAME, ir.OTYPE: case ir.OLITERAL, ir.ONIL, ir.ONAME, ir.OTYPE:
if n.HasCall() { if n.HasCall() {
base.Fatalf("OLITERAL/ONAME/OTYPE should never have calls: %+v", n) base.Fatalf("OLITERAL/ONAME/OTYPE should never have calls: %+v", n)
@ -617,6 +621,7 @@ func calcHasCall(n ir.Node) bool {
if instrumenting { if instrumenting {
return true return true
} }
return n.Left().HasCall() || n.Right().HasCall()
case ir.OINDEX, ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR, case ir.OINDEX, ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR,
ir.ODEREF, ir.ODOTPTR, ir.ODOTTYPE, ir.ODIV, ir.OMOD: ir.ODEREF, ir.ODOTPTR, ir.ODOTTYPE, ir.ODIV, ir.OMOD:
// These ops might panic, make sure they are done // These ops might panic, make sure they are done
@ -625,27 +630,68 @@ func calcHasCall(n ir.Node) bool {
// When using soft-float, these ops might be rewritten to function calls // When using soft-float, these ops might be rewritten to function calls
// so we ensure they are evaluated first. // so we ensure they are evaluated first.
case ir.OADD, ir.OSUB, ir.ONEG, ir.OMUL: case ir.OADD, ir.OSUB, ir.OMUL:
if thearch.SoftFloat && (isFloat[n.Type().Kind()] || isComplex[n.Type().Kind()]) { if thearch.SoftFloat && (isFloat[n.Type().Kind()] || isComplex[n.Type().Kind()]) {
return true return true
} }
return n.Left().HasCall() || n.Right().HasCall()
case ir.ONEG:
if thearch.SoftFloat && (isFloat[n.Type().Kind()] || isComplex[n.Type().Kind()]) {
return true
}
return n.Left().HasCall()
case ir.OLT, ir.OEQ, ir.ONE, ir.OLE, ir.OGE, ir.OGT: case ir.OLT, ir.OEQ, ir.ONE, ir.OLE, ir.OGE, ir.OGT:
if thearch.SoftFloat && (isFloat[n.Left().Type().Kind()] || isComplex[n.Left().Type().Kind()]) { if thearch.SoftFloat && (isFloat[n.Left().Type().Kind()] || isComplex[n.Left().Type().Kind()]) {
return true return true
} }
return n.Left().HasCall() || n.Right().HasCall()
case ir.OCONV: case ir.OCONV:
if thearch.SoftFloat && ((isFloat[n.Type().Kind()] || isComplex[n.Type().Kind()]) || (isFloat[n.Left().Type().Kind()] || isComplex[n.Left().Type().Kind()])) { if thearch.SoftFloat && ((isFloat[n.Type().Kind()] || isComplex[n.Type().Kind()]) || (isFloat[n.Left().Type().Kind()] || isComplex[n.Left().Type().Kind()])) {
return true return true
} }
} return n.Left().HasCall()
if n.Left() != nil && n.Left().HasCall() { case ir.OAND, ir.OANDNOT, ir.OLSH, ir.OOR, ir.ORSH, ir.OXOR, ir.OCOPY, ir.OCOMPLEX, ir.OEFACE:
return true return n.Left().HasCall() || n.Right().HasCall()
}
if n.Right() != nil && n.Right().HasCall() { case ir.OAS:
return true return n.Left().HasCall() || n.Right() != nil && n.Right().HasCall()
}
case ir.OADDR:
return n.Left().HasCall()
case ir.OPAREN:
return n.Left().HasCall()
case ir.OBITNOT, ir.ONOT, ir.OPLUS, ir.ORECV,
ir.OALIGNOF, ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.ONEW,
ir.OOFFSETOF, ir.OPANIC, ir.OREAL, ir.OSIZEOF,
ir.OCHECKNIL, ir.OCFUNC, ir.OIDATA, ir.OITAB, ir.ONEWOBJ, ir.OSPTR, ir.OVARDEF, ir.OVARKILL, ir.OVARLIVE:
return n.Left().HasCall()
case ir.ODOT, ir.ODOTMETH, ir.ODOTINTER:
return n.Left().HasCall()
case ir.OGETG, ir.OCLOSUREREAD, ir.OMETHEXPR:
return false return false
// TODO(rsc): These look wrong in various ways but are what calcHasCall has always done.
case ir.OADDSTR:
// TODO(rsc): This used to check left and right, which are not part of OADDSTR.
return false
case ir.OBLOCK:
// TODO(rsc): Surely the block's statements matter.
return false
case ir.OCONVIFACE, ir.OCONVNOP, ir.OBYTES2STR, ir.OBYTES2STRTMP, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2BYTESTMP, ir.OSTR2RUNES, ir.ORUNESTR:
// TODO(rsc): Some conversions are themselves calls, no?
return n.Left().HasCall()
case ir.ODOTTYPE2:
// TODO(rsc): Shouldn't this be up with ODOTTYPE above?
return n.Left().HasCall()
case ir.OSLICEHEADER:
// TODO(rsc): What about len and cap?
return n.Left().HasCall()
case ir.OAS2DOTTYPE, ir.OAS2FUNC:
// TODO(rsc): Surely we need to check List and Rlist.
return false
}
} }
func badtype(op ir.Op, tl, tr *types.Type) { func badtype(op ir.Op, tl, tr *types.Type) {
@ -727,26 +773,32 @@ func safeexpr(n ir.Node, init *ir.Nodes) ir.Node {
case ir.ONAME, ir.OLITERAL, ir.ONIL: case ir.ONAME, ir.OLITERAL, ir.ONIL:
return n return n
case ir.ODOT, ir.OLEN, ir.OCAP: case ir.OLEN, ir.OCAP:
l := safeexpr(n.Left(), init) l := safeexpr(n.Left(), init)
if l == n.Left() { if l == n.Left() {
return n return n
} }
r := ir.Copy(n) a := ir.Copy(n).(*ir.UnaryExpr)
r.SetLeft(l)
r = typecheck(r, ctxExpr)
r = walkexpr(r, init)
return r
case ir.ODOTPTR, ir.ODEREF:
l := safeexpr(n.Left(), init)
if l == n.Left() {
return n
}
a := ir.Copy(n)
a.SetLeft(l) a.SetLeft(l)
a = walkexpr(a, init) return walkexpr(typecheck(a, ctxExpr), init)
return a
case ir.ODOT, ir.ODOTPTR:
l := safeexpr(n.Left(), init)
if l == n.Left() {
return n
}
a := ir.Copy(n).(*ir.SelectorExpr)
a.SetLeft(l)
return walkexpr(typecheck(a, ctxExpr), init)
case ir.ODEREF:
l := safeexpr(n.Left(), init)
if l == n.Left() {
return n
}
a := ir.Copy(n).(*ir.StarExpr)
a.SetLeft(l)
return walkexpr(typecheck(a, ctxExpr), init)
case ir.OINDEX, ir.OINDEXMAP: case ir.OINDEX, ir.OINDEXMAP:
l := safeexpr(n.Left(), init) l := safeexpr(n.Left(), init)
@ -754,11 +806,10 @@ func safeexpr(n ir.Node, init *ir.Nodes) ir.Node {
if l == n.Left() && r == n.Right() { if l == n.Left() && r == n.Right() {
return n return n
} }
a := ir.Copy(n) a := ir.Copy(n).(*ir.IndexExpr)
a.SetLeft(l) a.SetLeft(l)
a.SetRight(r) a.SetRight(r)
a = walkexpr(a, init) return walkexpr(typecheck(a, ctxExpr), init)
return a
case ir.OSTRUCTLIT, ir.OARRAYLIT, ir.OSLICELIT: case ir.OSTRUCTLIT, ir.OARRAYLIT, ir.OSLICELIT:
if isStaticCompositeLiteral(n) { if isStaticCompositeLiteral(n) {
@ -927,7 +978,7 @@ func dotpath(s *types.Sym, t *types.Type, save **types.Field, ignorecase bool) (
// find missing fields that // find missing fields that
// will give shortest unique addressing. // will give shortest unique addressing.
// modify the tree with missing type names. // modify the tree with missing type names.
func adddot(n ir.Node) ir.Node { func adddot(n *ir.SelectorExpr) *ir.SelectorExpr {
n.SetLeft(typecheck(n.Left(), ctxType|ctxExpr)) n.SetLeft(typecheck(n.Left(), ctxType|ctxExpr))
if n.Left().Diag() { if n.Left().Diag() {
n.SetDiag(true) n.SetDiag(true)
@ -950,8 +1001,9 @@ func adddot(n ir.Node) ir.Node {
case path != nil: case path != nil:
// rebuild elided dots // rebuild elided dots
for c := len(path) - 1; c >= 0; c-- { for c := len(path) - 1; c >= 0; c-- {
n.SetLeft(nodSym(ir.ODOT, n.Left(), path[c].field.Sym)) dot := nodSym(ir.ODOT, n.Left(), path[c].field.Sym)
n.Left().SetImplicit(true) dot.SetImplicit(true)
n.SetLeft(dot)
} }
case ambig: case ambig:
base.Errorf("ambiguous selector %v", n) base.Errorf("ambiguous selector %v", n)
@ -1179,12 +1231,12 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) {
// value for that function. // value for that function.
if !instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !isifacemethod(method.Type) && !(thearch.LinkArch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) { if !instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !isifacemethod(method.Type) && !(thearch.LinkArch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) {
// generate tail call: adjust pointer receiver and jump to embedded method. // generate tail call: adjust pointer receiver and jump to embedded method.
dot = dot.Left() // skip final .M left := dot.Left() // skip final .M
// TODO(mdempsky): Remove dependency on dotlist. // TODO(mdempsky): Remove dependency on dotlist.
if !dotlist[0].field.Type.IsPtr() { if !dotlist[0].field.Type.IsPtr() {
dot = nodAddr(dot) left = ir.Nod(ir.OADDR, left, nil)
} }
as := ir.Nod(ir.OAS, nthis, convnop(dot, rcvr)) as := ir.Nod(ir.OAS, nthis, convnop(left, rcvr))
fn.PtrBody().Append(as) fn.PtrBody().Append(as)
fn.PtrBody().Append(nodSym(ir.ORETJMP, nil, methodSym(methodrcvr, method.Sym))) fn.PtrBody().Append(nodSym(ir.ORETJMP, nil, methodSym(methodrcvr, method.Sym)))
} else { } else {
@ -1387,8 +1439,9 @@ func initExpr(init []ir.Node, n ir.Node) ir.Node {
} }
if ir.MayBeShared(n) { if ir.MayBeShared(n) {
// Introduce OCONVNOP to hold init list. // Introduce OCONVNOP to hold init list.
n = ir.Nod(ir.OCONVNOP, n, nil) old := n
n.SetType(n.Left().Type()) n = ir.Nod(ir.OCONVNOP, old, nil)
n.SetType(old.Type())
n.SetTypecheck(1) n.SetTypecheck(1)
} }

View file

@ -957,7 +957,7 @@ func typecheck1(n ir.Node, top int) (res ir.Node) {
case ir.OXDOT, ir.ODOT: case ir.OXDOT, ir.ODOT:
n := n.(*ir.SelectorExpr) n := n.(*ir.SelectorExpr)
if n.Op() == ir.OXDOT { if n.Op() == ir.OXDOT {
n = adddot(n).(*ir.SelectorExpr) n = adddot(n)
n.SetOp(ir.ODOT) n.SetOp(ir.ODOT)
if n.Left() == nil { if n.Left() == nil {
n.SetType(nil) n.SetType(nil)