mirror of https://github.com/golang/go.git
[dev.regabi] cmd/compile: use ir.Find for "search" traversals
This CL converts all the generic searching traversal to use ir.Find instead of relying on direct access to Left, Right, and so on. Passes buildall w/ toolstash -cmp. Change-Id: I4d951aef630c00bf333f24be79565cc564694d04 Reviewed-on: https://go-review.googlesource.com/c/go/+/275372 Trust: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
parent
0d1b44c645
commit
b9df26d7a8
|
|
@ -782,37 +782,14 @@ func geneq(t *types.Type) *obj.LSym {
|
||||||
return closure
|
return closure
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasCall(n ir.Node) bool {
|
func hasCall(fn *ir.Func) bool {
|
||||||
if n.Op() == ir.OCALL || n.Op() == ir.OCALLFUNC {
|
found := ir.Find(fn, func(n ir.Node) interface{} {
|
||||||
return true
|
if op := n.Op(); op == ir.OCALL || op == ir.OCALLFUNC {
|
||||||
}
|
return n
|
||||||
if n.Left() != nil && hasCall(n.Left()) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if n.Right() != nil && hasCall(n.Right()) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, x := range n.Init().Slice() {
|
|
||||||
if hasCall(x) {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
for _, x := range n.Body().Slice() {
|
})
|
||||||
if hasCall(x) {
|
return found != nil
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, x := range n.List().Slice() {
|
|
||||||
if hasCall(x) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, x := range n.Rlist().Slice() {
|
|
||||||
if hasCall(x) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// eqfield returns the node
|
// eqfield returns the node
|
||||||
|
|
|
||||||
|
|
@ -553,7 +553,7 @@ func evalConst(n ir.Node) ir.Node {
|
||||||
return origIntConst(n, int64(len(ir.StringVal(nl))))
|
return origIntConst(n, int64(len(ir.StringVal(nl))))
|
||||||
}
|
}
|
||||||
case types.TARRAY:
|
case types.TARRAY:
|
||||||
if !hascallchan(nl) {
|
if !hasCallOrChan(nl) {
|
||||||
return origIntConst(n, nl.Type().NumElem())
|
return origIntConst(n, nl.Type().NumElem())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -779,49 +779,35 @@ func isGoConst(n ir.Node) bool {
|
||||||
return n.Op() == ir.OLITERAL
|
return n.Op() == ir.OLITERAL
|
||||||
}
|
}
|
||||||
|
|
||||||
func hascallchan(n ir.Node) bool {
|
// hasCallOrChan reports whether n contains any calls or channel operations.
|
||||||
if n == nil {
|
func hasCallOrChan(n ir.Node) bool {
|
||||||
return false
|
found := ir.Find(n, func(n ir.Node) interface{} {
|
||||||
}
|
switch n.Op() {
|
||||||
switch n.Op() {
|
case ir.OAPPEND,
|
||||||
case ir.OAPPEND,
|
ir.OCALL,
|
||||||
ir.OCALL,
|
ir.OCALLFUNC,
|
||||||
ir.OCALLFUNC,
|
ir.OCALLINTER,
|
||||||
ir.OCALLINTER,
|
ir.OCALLMETH,
|
||||||
ir.OCALLMETH,
|
ir.OCAP,
|
||||||
ir.OCAP,
|
ir.OCLOSE,
|
||||||
ir.OCLOSE,
|
ir.OCOMPLEX,
|
||||||
ir.OCOMPLEX,
|
ir.OCOPY,
|
||||||
ir.OCOPY,
|
ir.ODELETE,
|
||||||
ir.ODELETE,
|
ir.OIMAG,
|
||||||
ir.OIMAG,
|
ir.OLEN,
|
||||||
ir.OLEN,
|
ir.OMAKE,
|
||||||
ir.OMAKE,
|
ir.ONEW,
|
||||||
ir.ONEW,
|
ir.OPANIC,
|
||||||
ir.OPANIC,
|
ir.OPRINT,
|
||||||
ir.OPRINT,
|
ir.OPRINTN,
|
||||||
ir.OPRINTN,
|
ir.OREAL,
|
||||||
ir.OREAL,
|
ir.ORECOVER,
|
||||||
ir.ORECOVER,
|
ir.ORECV:
|
||||||
ir.ORECV:
|
return n
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if hascallchan(n.Left()) || hascallchan(n.Right()) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, n1 := range n.List().Slice() {
|
|
||||||
if hascallchan(n1) {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
for _, n2 := range n.Rlist().Slice() {
|
})
|
||||||
if hascallchan(n2) {
|
return found != nil
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A constSet represents a set of Go constant expressions.
|
// A constSet represents a set of Go constant expressions.
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"cmd/compile/internal/types"
|
"cmd/compile/internal/types"
|
||||||
"cmd/internal/obj"
|
"cmd/internal/obj"
|
||||||
"cmd/internal/src"
|
"cmd/internal/src"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -206,14 +207,10 @@ func caninl(fn *ir.Func) {
|
||||||
extraCallCost: cc,
|
extraCallCost: cc,
|
||||||
usedLocals: make(map[ir.Node]bool),
|
usedLocals: make(map[ir.Node]bool),
|
||||||
}
|
}
|
||||||
if visitor.visitList(fn.Body()) {
|
if visitor.tooHairy(fn) {
|
||||||
reason = visitor.reason
|
reason = visitor.reason
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if visitor.budget < 0 {
|
|
||||||
reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", inlineMaxBudget-visitor.budget, inlineMaxBudget)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n.Func().Inl = &ir.Inline{
|
n.Func().Inl = &ir.Inline{
|
||||||
Cost: inlineMaxBudget - visitor.budget,
|
Cost: inlineMaxBudget - visitor.budget,
|
||||||
|
|
@ -296,21 +293,29 @@ type hairyVisitor struct {
|
||||||
reason string
|
reason string
|
||||||
extraCallCost int32
|
extraCallCost int32
|
||||||
usedLocals map[ir.Node]bool
|
usedLocals map[ir.Node]bool
|
||||||
|
do func(ir.Node) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for anything we want to punt on.
|
var errBudget = errors.New("too expensive")
|
||||||
func (v *hairyVisitor) visitList(ll ir.Nodes) bool {
|
|
||||||
for _, n := range ll.Slice() {
|
func (v *hairyVisitor) tooHairy(fn *ir.Func) bool {
|
||||||
if v.visit(n) {
|
v.do = v.doNode // cache closure
|
||||||
return true
|
|
||||||
}
|
err := ir.DoChildren(fn, v.do)
|
||||||
|
if err != nil {
|
||||||
|
v.reason = err.Error()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if v.budget < 0 {
|
||||||
|
v.reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", inlineMaxBudget-v.budget, inlineMaxBudget)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *hairyVisitor) visit(n ir.Node) bool {
|
func (v *hairyVisitor) doNode(n ir.Node) error {
|
||||||
if n == nil {
|
if n == nil {
|
||||||
return false
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch n.Op() {
|
switch n.Op() {
|
||||||
|
|
@ -323,8 +328,7 @@ func (v *hairyVisitor) visit(n ir.Node) bool {
|
||||||
if n.Left().Op() == ir.ONAME && n.Left().Class() == ir.PFUNC && isRuntimePkg(n.Left().Sym().Pkg) {
|
if n.Left().Op() == ir.ONAME && n.Left().Class() == ir.PFUNC && isRuntimePkg(n.Left().Sym().Pkg) {
|
||||||
fn := n.Left().Sym().Name
|
fn := n.Left().Sym().Name
|
||||||
if fn == "getcallerpc" || fn == "getcallersp" {
|
if fn == "getcallerpc" || fn == "getcallersp" {
|
||||||
v.reason = "call to " + fn
|
return errors.New("call to " + fn)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
if fn == "throw" {
|
if fn == "throw" {
|
||||||
v.budget -= inlineExtraThrowCost
|
v.budget -= inlineExtraThrowCost
|
||||||
|
|
@ -380,8 +384,7 @@ func (v *hairyVisitor) visit(n ir.Node) bool {
|
||||||
case ir.ORECOVER:
|
case ir.ORECOVER:
|
||||||
// recover matches the argument frame pointer to find
|
// recover matches the argument frame pointer to find
|
||||||
// the right panic value, so it needs an argument frame.
|
// the right panic value, so it needs an argument frame.
|
||||||
v.reason = "call to recover"
|
return errors.New("call to recover")
|
||||||
return true
|
|
||||||
|
|
||||||
case ir.OCLOSURE,
|
case ir.OCLOSURE,
|
||||||
ir.ORANGE,
|
ir.ORANGE,
|
||||||
|
|
@ -390,21 +393,19 @@ func (v *hairyVisitor) visit(n ir.Node) bool {
|
||||||
ir.ODEFER,
|
ir.ODEFER,
|
||||||
ir.ODCLTYPE, // can't print yet
|
ir.ODCLTYPE, // can't print yet
|
||||||
ir.ORETJMP:
|
ir.ORETJMP:
|
||||||
v.reason = "unhandled op " + n.Op().String()
|
return errors.New("unhandled op " + n.Op().String())
|
||||||
return true
|
|
||||||
|
|
||||||
case ir.OAPPEND:
|
case ir.OAPPEND:
|
||||||
v.budget -= inlineExtraAppendCost
|
v.budget -= inlineExtraAppendCost
|
||||||
|
|
||||||
case ir.ODCLCONST, ir.OFALL:
|
case ir.ODCLCONST, ir.OFALL:
|
||||||
// These nodes don't produce code; omit from inlining budget.
|
// These nodes don't produce code; omit from inlining budget.
|
||||||
return false
|
return nil
|
||||||
|
|
||||||
case ir.OFOR, ir.OFORUNTIL, ir.OSWITCH:
|
case ir.OFOR, ir.OFORUNTIL, ir.OSWITCH:
|
||||||
// ORANGE, OSELECT in "unhandled" above
|
// ORANGE, OSELECT in "unhandled" above
|
||||||
if n.Sym() != nil {
|
if n.Sym() != nil {
|
||||||
v.reason = "labeled control"
|
return errors.New("labeled control")
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case ir.OBREAK, ir.OCONTINUE:
|
case ir.OBREAK, ir.OCONTINUE:
|
||||||
|
|
@ -416,8 +417,17 @@ func (v *hairyVisitor) visit(n ir.Node) bool {
|
||||||
case ir.OIF:
|
case ir.OIF:
|
||||||
if ir.IsConst(n.Left(), constant.Bool) {
|
if ir.IsConst(n.Left(), constant.Bool) {
|
||||||
// This if and the condition cost nothing.
|
// This if and the condition cost nothing.
|
||||||
return v.visitList(n.Init()) || v.visitList(n.Body()) ||
|
// TODO(rsc): It seems strange that we visit the dead branch.
|
||||||
v.visitList(n.Rlist())
|
if err := ir.DoList(n.Init(), v.do); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ir.DoList(n.Body(), v.do); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ir.DoList(n.Rlist(), v.do); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
case ir.ONAME:
|
case ir.ONAME:
|
||||||
|
|
@ -439,34 +449,22 @@ func (v *hairyVisitor) visit(n ir.Node) bool {
|
||||||
|
|
||||||
// When debugging, don't stop early, to get full cost of inlining this function
|
// When debugging, don't stop early, to get full cost of inlining this function
|
||||||
if v.budget < 0 && base.Flag.LowerM < 2 && !logopt.Enabled() {
|
if v.budget < 0 && base.Flag.LowerM < 2 && !logopt.Enabled() {
|
||||||
return true
|
return errBudget
|
||||||
}
|
}
|
||||||
|
|
||||||
return v.visit(n.Left()) || v.visit(n.Right()) ||
|
return ir.DoChildren(n, v.do)
|
||||||
v.visitList(n.List()) || v.visitList(n.Rlist()) ||
|
|
||||||
v.visitList(n.Init()) || v.visitList(n.Body())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func countNodes(n ir.Node) int {
|
func isBigFunc(fn *ir.Func) bool {
|
||||||
if n == nil {
|
budget := inlineBigFunctionNodes
|
||||||
return 0
|
over := ir.Find(fn, func(n ir.Node) interface{} {
|
||||||
}
|
budget--
|
||||||
cnt := 1
|
if budget <= 0 {
|
||||||
cnt += countNodes(n.Left())
|
return n
|
||||||
cnt += countNodes(n.Right())
|
}
|
||||||
for _, n1 := range n.Init().Slice() {
|
return nil
|
||||||
cnt += countNodes(n1)
|
})
|
||||||
}
|
return over != nil
|
||||||
for _, n1 := range n.Body().Slice() {
|
|
||||||
cnt += countNodes(n1)
|
|
||||||
}
|
|
||||||
for _, n1 := range n.List().Slice() {
|
|
||||||
cnt += countNodes(n1)
|
|
||||||
}
|
|
||||||
for _, n1 := range n.Rlist().Slice() {
|
|
||||||
cnt += countNodes(n1)
|
|
||||||
}
|
|
||||||
return cnt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
|
// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
|
||||||
|
|
@ -475,7 +473,7 @@ func inlcalls(fn *ir.Func) {
|
||||||
savefn := Curfn
|
savefn := Curfn
|
||||||
Curfn = fn
|
Curfn = fn
|
||||||
maxCost := int32(inlineMaxBudget)
|
maxCost := int32(inlineMaxBudget)
|
||||||
if countNodes(fn) >= inlineBigFunctionNodes {
|
if isBigFunc(fn) {
|
||||||
maxCost = inlineBigFunctionMaxCost
|
maxCost = inlineBigFunctionMaxCost
|
||||||
}
|
}
|
||||||
// Map to keep track of functions that have been inlined at a particular
|
// Map to keep track of functions that have been inlined at a particular
|
||||||
|
|
@ -742,82 +740,45 @@ FindRHS:
|
||||||
base.Fatalf("RHS is nil: %v", defn)
|
base.Fatalf("RHS is nil: %v", defn)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe, _ := reassigned(n.(*ir.Name))
|
if reassigned(n.(*ir.Name)) {
|
||||||
if unsafe {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return rhs
|
return rhs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errFound = errors.New("found")
|
||||||
|
|
||||||
// reassigned takes an ONAME node, walks the function in which it is defined, and returns a boolean
|
// reassigned takes an ONAME node, walks the function in which it is defined, and returns a boolean
|
||||||
// indicating whether the name has any assignments other than its declaration.
|
// indicating whether the name has any assignments other than its declaration.
|
||||||
// The second return value is the first such assignment encountered in the walk, if any. It is mostly
|
// The second return value is the first such assignment encountered in the walk, if any. It is mostly
|
||||||
// useful for -m output documenting the reason for inhibited optimizations.
|
// useful for -m output documenting the reason for inhibited optimizations.
|
||||||
// NB: global variables are always considered to be re-assigned.
|
// NB: global variables are always considered to be re-assigned.
|
||||||
// TODO: handle initial declaration not including an assignment and followed by a single assignment?
|
// TODO: handle initial declaration not including an assignment and followed by a single assignment?
|
||||||
func reassigned(n *ir.Name) (bool, ir.Node) {
|
func reassigned(name *ir.Name) bool {
|
||||||
if n.Op() != ir.ONAME {
|
if name.Op() != ir.ONAME {
|
||||||
base.Fatalf("reassigned %v", n)
|
base.Fatalf("reassigned %v", name)
|
||||||
}
|
}
|
||||||
// no way to reliably check for no-reassignment of globals, assume it can be
|
// no way to reliably check for no-reassignment of globals, assume it can be
|
||||||
if n.Curfn == nil {
|
if name.Curfn == nil {
|
||||||
return true, nil
|
return true
|
||||||
}
|
}
|
||||||
f := n.Curfn
|
a := ir.Find(name.Curfn, func(n ir.Node) interface{} {
|
||||||
v := reassignVisitor{name: n}
|
switch n.Op() {
|
||||||
a := v.visitList(f.Body())
|
case ir.OAS:
|
||||||
return a != nil, a
|
if n.Left() == name && n != name.Defn {
|
||||||
}
|
|
||||||
|
|
||||||
type reassignVisitor struct {
|
|
||||||
name ir.Node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *reassignVisitor) visit(n ir.Node) ir.Node {
|
|
||||||
if n == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
switch n.Op() {
|
|
||||||
case ir.OAS:
|
|
||||||
if n.Left() == v.name && n != v.name.Name().Defn {
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
case ir.OAS2, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2DOTTYPE:
|
|
||||||
for _, p := range n.List().Slice() {
|
|
||||||
if p == v.name && n != v.name.Name().Defn {
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
case ir.OAS2, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2DOTTYPE:
|
||||||
|
for _, p := range n.List().Slice() {
|
||||||
|
if p == name && n != name.Defn {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
if a := v.visit(n.Left()); a != nil {
|
})
|
||||||
return a
|
return a != nil
|
||||||
}
|
|
||||||
if a := v.visit(n.Right()); a != nil {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
if a := v.visitList(n.List()); a != nil {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
if a := v.visitList(n.Rlist()); a != nil {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
if a := v.visitList(n.Init()); a != nil {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
if a := v.visitList(n.Body()); a != nil {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *reassignVisitor) visitList(l ir.Nodes) ir.Node {
|
|
||||||
for _, n := range l.Slice() {
|
|
||||||
if a := v.visit(n); a != nil {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func inlParam(t *types.Field, as ir.Node, inlvars map[*ir.Name]ir.Node) ir.Node {
|
func inlParam(t *types.Field, as ir.Node, inlvars map[*ir.Name]ir.Node) ir.Node {
|
||||||
|
|
@ -1140,6 +1101,7 @@ func mkinlcall(n ir.Node, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]bool)
|
||||||
bases: make(map[*src.PosBase]*src.PosBase),
|
bases: make(map[*src.PosBase]*src.PosBase),
|
||||||
newInlIndex: newIndex,
|
newInlIndex: newIndex,
|
||||||
}
|
}
|
||||||
|
subst.edit = subst.node
|
||||||
|
|
||||||
body := subst.list(ir.AsNodes(fn.Inl.Body))
|
body := subst.list(ir.AsNodes(fn.Inl.Body))
|
||||||
|
|
||||||
|
|
@ -1248,6 +1210,8 @@ type inlsubst struct {
|
||||||
// newInlIndex is the index of the inlined call frame to
|
// newInlIndex is the index of the inlined call frame to
|
||||||
// insert for inlined nodes.
|
// insert for inlined nodes.
|
||||||
newInlIndex int
|
newInlIndex int
|
||||||
|
|
||||||
|
edit func(ir.Node) ir.Node // cached copy of subst.node method value closure
|
||||||
}
|
}
|
||||||
|
|
||||||
// list inlines a list of nodes.
|
// list inlines a list of nodes.
|
||||||
|
|
@ -1334,21 +1298,13 @@ func (subst *inlsubst) node(n ir.Node) ir.Node {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
m := ir.Copy(n)
|
|
||||||
m.SetPos(subst.updatedPos(m.Pos()))
|
|
||||||
m.PtrInit().Set(nil)
|
|
||||||
|
|
||||||
if n.Op() == ir.OCLOSURE {
|
if n.Op() == ir.OCLOSURE {
|
||||||
base.Fatalf("cannot inline function containing closure: %+v", n)
|
base.Fatalf("cannot inline function containing closure: %+v", n)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.SetLeft(subst.node(n.Left()))
|
m := ir.Copy(n)
|
||||||
m.SetRight(subst.node(n.Right()))
|
m.SetPos(subst.updatedPos(m.Pos()))
|
||||||
m.PtrList().Set(subst.list(n.List()))
|
ir.EditChildren(m, subst.edit)
|
||||||
m.PtrRlist().Set(subst.list(n.Rlist()))
|
|
||||||
m.PtrInit().Set(append(m.Init().Slice(), subst.list(n.Init())...))
|
|
||||||
m.PtrBody().Set(subst.list(n.Body()))
|
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1062,6 +1062,10 @@ func (o *Order) exprListInPlace(l ir.Nodes) {
|
||||||
// prealloc[x] records the allocation to use for x.
|
// prealloc[x] records the allocation to use for x.
|
||||||
var prealloc = map[ir.Node]ir.Node{}
|
var prealloc = map[ir.Node]ir.Node{}
|
||||||
|
|
||||||
|
func (o *Order) exprNoLHS(n ir.Node) ir.Node {
|
||||||
|
return o.expr(n, nil)
|
||||||
|
}
|
||||||
|
|
||||||
// expr orders a single expression, appending side
|
// expr orders a single expression, appending side
|
||||||
// effects to o.out as needed.
|
// effects to o.out as needed.
|
||||||
// If this is part of an assignment lhs = *np, lhs is given.
|
// If this is part of an assignment lhs = *np, lhs is given.
|
||||||
|
|
@ -1079,10 +1083,7 @@ func (o *Order) expr(n, lhs ir.Node) ir.Node {
|
||||||
|
|
||||||
switch n.Op() {
|
switch n.Op() {
|
||||||
default:
|
default:
|
||||||
n.SetLeft(o.expr(n.Left(), nil))
|
ir.EditChildren(n, o.exprNoLHS)
|
||||||
n.SetRight(o.expr(n.Right(), nil))
|
|
||||||
o.exprList(n.List())
|
|
||||||
o.exprList(n.Rlist())
|
|
||||||
|
|
||||||
// Addition of strings turns into a function call.
|
// Addition of strings turns into a function call.
|
||||||
// Allocate a temporary to hold the strings.
|
// Allocate a temporary to hold the strings.
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,8 @@ func (s *InitSchedule) tryStaticInit(n ir.Node) bool {
|
||||||
if n.Op() != ir.OAS {
|
if n.Op() != ir.OAS {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if ir.IsBlank(n.Left()) && candiscard(n.Right()) {
|
if ir.IsBlank(n.Left()) && !hasSideEffects(n.Right()) {
|
||||||
|
// Discard.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
lno := setlineno(n)
|
lno := setlineno(n)
|
||||||
|
|
@ -548,7 +549,8 @@ func fixedlit(ctxt initContext, kind initKind, n ir.Node, var_ ir.Node, init *ir
|
||||||
|
|
||||||
for _, r := range n.List().Slice() {
|
for _, r := range n.List().Slice() {
|
||||||
a, value := splitnode(r)
|
a, value := splitnode(r)
|
||||||
if a == ir.BlankNode && candiscard(value) {
|
if a == ir.BlankNode && !hasSideEffects(value) {
|
||||||
|
// Discard.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3669,51 +3669,52 @@ func checkmake(t *types.Type, arg string, np *ir.Node) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func markbreak(labels *map[*types.Sym]ir.Node, n ir.Node, implicit ir.Node) {
|
// markBreak marks control statements containing break statements with SetHasBreak(true).
|
||||||
if n == nil {
|
func markBreak(fn *ir.Func) {
|
||||||
return
|
var labels map[*types.Sym]ir.Node
|
||||||
|
var implicit ir.Node
|
||||||
|
|
||||||
|
var mark func(ir.Node) error
|
||||||
|
mark = func(n ir.Node) error {
|
||||||
|
switch n.Op() {
|
||||||
|
default:
|
||||||
|
ir.DoChildren(n, mark)
|
||||||
|
|
||||||
|
case ir.OBREAK:
|
||||||
|
if n.Sym() == nil {
|
||||||
|
if implicit != nil {
|
||||||
|
implicit.SetHasBreak(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if lab := labels[n.Sym()]; lab != nil {
|
||||||
|
lab.SetHasBreak(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case ir.OFOR, ir.OFORUNTIL, ir.OSWITCH, ir.OTYPESW, ir.OSELECT, ir.ORANGE:
|
||||||
|
old := implicit
|
||||||
|
implicit = n
|
||||||
|
sym := n.Sym()
|
||||||
|
if sym != nil {
|
||||||
|
if labels == nil {
|
||||||
|
// Map creation delayed until we need it - most functions don't.
|
||||||
|
labels = make(map[*types.Sym]ir.Node)
|
||||||
|
}
|
||||||
|
labels[sym] = n
|
||||||
|
}
|
||||||
|
ir.DoChildren(n, mark)
|
||||||
|
if sym != nil {
|
||||||
|
delete(labels, sym)
|
||||||
|
}
|
||||||
|
implicit = old
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch n.Op() {
|
mark(fn)
|
||||||
case ir.OBREAK:
|
|
||||||
if n.Sym() == nil {
|
|
||||||
if implicit != nil {
|
|
||||||
implicit.SetHasBreak(true)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if lab := (*labels)[n.Sym()]; lab != nil {
|
|
||||||
lab.SetHasBreak(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case ir.OFOR, ir.OFORUNTIL, ir.OSWITCH, ir.OTYPESW, ir.OSELECT, ir.ORANGE:
|
|
||||||
implicit = n
|
|
||||||
if sym := n.Sym(); sym != nil {
|
|
||||||
if *labels == nil {
|
|
||||||
// Map creation delayed until we need it - most functions don't.
|
|
||||||
*labels = make(map[*types.Sym]ir.Node)
|
|
||||||
}
|
|
||||||
(*labels)[sym] = n
|
|
||||||
defer delete(*labels, sym)
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
markbreak(labels, n.Left(), implicit)
|
|
||||||
markbreak(labels, n.Right(), implicit)
|
|
||||||
markbreaklist(labels, n.Init(), implicit)
|
|
||||||
markbreaklist(labels, n.Body(), implicit)
|
|
||||||
markbreaklist(labels, n.List(), implicit)
|
|
||||||
markbreaklist(labels, n.Rlist(), implicit)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func markbreaklist(labels *map[*types.Sym]ir.Node, l ir.Nodes, implicit ir.Node) {
|
// isTermNodes reports whether the Nodes list ends with a terminating statement.
|
||||||
s := l.Slice()
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
markbreak(labels, s[i], implicit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// isterminating reports whether the Nodes list ends with a terminating statement.
|
|
||||||
func isTermNodes(l ir.Nodes) bool {
|
func isTermNodes(l ir.Nodes) bool {
|
||||||
s := l.Slice()
|
s := l.Slice()
|
||||||
c := len(s)
|
c := len(s)
|
||||||
|
|
@ -3723,7 +3724,7 @@ func isTermNodes(l ir.Nodes) bool {
|
||||||
return isTermNode(s[c-1])
|
return isTermNode(s[c-1])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Isterminating reports whether the node n, the last one in a
|
// isTermNode reports whether the node n, the last one in a
|
||||||
// statement list, is a terminating statement.
|
// statement list, is a terminating statement.
|
||||||
func isTermNode(n ir.Node) bool {
|
func isTermNode(n ir.Node) bool {
|
||||||
switch n.Op() {
|
switch n.Op() {
|
||||||
|
|
@ -3776,8 +3777,7 @@ func isTermNode(n ir.Node) bool {
|
||||||
// checkreturn makes sure that fn terminates appropriately.
|
// checkreturn makes sure that fn terminates appropriately.
|
||||||
func checkreturn(fn *ir.Func) {
|
func checkreturn(fn *ir.Func) {
|
||||||
if fn.Type().NumResults() != 0 && fn.Body().Len() != 0 {
|
if fn.Type().NumResults() != 0 && fn.Body().Len() != 0 {
|
||||||
var labels map[*types.Sym]ir.Node
|
markBreak(fn)
|
||||||
markbreaklist(&labels, fn.Body(), nil)
|
|
||||||
if !isTermNodes(fn.Body()) {
|
if !isTermNodes(fn.Body()) {
|
||||||
base.ErrorfAt(fn.Endlineno, "missing return at end of function")
|
base.ErrorfAt(fn.Endlineno, "missing return at end of function")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3786,107 +3786,91 @@ func usefield(n ir.Node) {
|
||||||
Curfn.FieldTrack[sym] = struct{}{}
|
Curfn.FieldTrack[sym] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func candiscardlist(l ir.Nodes) bool {
|
// hasSideEffects reports whether n contains any operations that could have observable side effects.
|
||||||
for _, n := range l.Slice() {
|
func hasSideEffects(n ir.Node) bool {
|
||||||
if !candiscard(n) {
|
found := ir.Find(n, func(n ir.Node) interface{} {
|
||||||
return false
|
switch n.Op() {
|
||||||
|
// Assume side effects unless we know otherwise.
|
||||||
|
default:
|
||||||
|
return n
|
||||||
|
|
||||||
|
// No side effects here (arguments are checked separately).
|
||||||
|
case ir.ONAME,
|
||||||
|
ir.ONONAME,
|
||||||
|
ir.OTYPE,
|
||||||
|
ir.OPACK,
|
||||||
|
ir.OLITERAL,
|
||||||
|
ir.ONIL,
|
||||||
|
ir.OADD,
|
||||||
|
ir.OSUB,
|
||||||
|
ir.OOR,
|
||||||
|
ir.OXOR,
|
||||||
|
ir.OADDSTR,
|
||||||
|
ir.OADDR,
|
||||||
|
ir.OANDAND,
|
||||||
|
ir.OBYTES2STR,
|
||||||
|
ir.ORUNES2STR,
|
||||||
|
ir.OSTR2BYTES,
|
||||||
|
ir.OSTR2RUNES,
|
||||||
|
ir.OCAP,
|
||||||
|
ir.OCOMPLIT,
|
||||||
|
ir.OMAPLIT,
|
||||||
|
ir.OSTRUCTLIT,
|
||||||
|
ir.OARRAYLIT,
|
||||||
|
ir.OSLICELIT,
|
||||||
|
ir.OPTRLIT,
|
||||||
|
ir.OCONV,
|
||||||
|
ir.OCONVIFACE,
|
||||||
|
ir.OCONVNOP,
|
||||||
|
ir.ODOT,
|
||||||
|
ir.OEQ,
|
||||||
|
ir.ONE,
|
||||||
|
ir.OLT,
|
||||||
|
ir.OLE,
|
||||||
|
ir.OGT,
|
||||||
|
ir.OGE,
|
||||||
|
ir.OKEY,
|
||||||
|
ir.OSTRUCTKEY,
|
||||||
|
ir.OLEN,
|
||||||
|
ir.OMUL,
|
||||||
|
ir.OLSH,
|
||||||
|
ir.ORSH,
|
||||||
|
ir.OAND,
|
||||||
|
ir.OANDNOT,
|
||||||
|
ir.ONEW,
|
||||||
|
ir.ONOT,
|
||||||
|
ir.OBITNOT,
|
||||||
|
ir.OPLUS,
|
||||||
|
ir.ONEG,
|
||||||
|
ir.OOROR,
|
||||||
|
ir.OPAREN,
|
||||||
|
ir.ORUNESTR,
|
||||||
|
ir.OREAL,
|
||||||
|
ir.OIMAG,
|
||||||
|
ir.OCOMPLEX:
|
||||||
|
return nil
|
||||||
|
|
||||||
|
// Only possible side effect is division by zero.
|
||||||
|
case ir.ODIV, ir.OMOD:
|
||||||
|
if n.Right().Op() != ir.OLITERAL || constant.Sign(n.Right().Val()) == 0 {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only possible side effect is panic on invalid size,
|
||||||
|
// but many makechan and makemap use size zero, which is definitely OK.
|
||||||
|
case ir.OMAKECHAN, ir.OMAKEMAP:
|
||||||
|
if !ir.IsConst(n.Left(), constant.Int) || constant.Sign(n.Left().Val()) != 0 {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only possible side effect is panic on invalid size.
|
||||||
|
// TODO(rsc): Merge with previous case (probably breaks toolstash -cmp).
|
||||||
|
case ir.OMAKESLICE, ir.OMAKESLICECOPY:
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
return true
|
})
|
||||||
}
|
return found != nil
|
||||||
|
|
||||||
func candiscard(n ir.Node) bool {
|
|
||||||
if n == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
switch n.Op() {
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
|
|
||||||
// Discardable as long as the subpieces are.
|
|
||||||
case ir.ONAME,
|
|
||||||
ir.ONONAME,
|
|
||||||
ir.OTYPE,
|
|
||||||
ir.OPACK,
|
|
||||||
ir.OLITERAL,
|
|
||||||
ir.ONIL,
|
|
||||||
ir.OADD,
|
|
||||||
ir.OSUB,
|
|
||||||
ir.OOR,
|
|
||||||
ir.OXOR,
|
|
||||||
ir.OADDSTR,
|
|
||||||
ir.OADDR,
|
|
||||||
ir.OANDAND,
|
|
||||||
ir.OBYTES2STR,
|
|
||||||
ir.ORUNES2STR,
|
|
||||||
ir.OSTR2BYTES,
|
|
||||||
ir.OSTR2RUNES,
|
|
||||||
ir.OCAP,
|
|
||||||
ir.OCOMPLIT,
|
|
||||||
ir.OMAPLIT,
|
|
||||||
ir.OSTRUCTLIT,
|
|
||||||
ir.OARRAYLIT,
|
|
||||||
ir.OSLICELIT,
|
|
||||||
ir.OPTRLIT,
|
|
||||||
ir.OCONV,
|
|
||||||
ir.OCONVIFACE,
|
|
||||||
ir.OCONVNOP,
|
|
||||||
ir.ODOT,
|
|
||||||
ir.OEQ,
|
|
||||||
ir.ONE,
|
|
||||||
ir.OLT,
|
|
||||||
ir.OLE,
|
|
||||||
ir.OGT,
|
|
||||||
ir.OGE,
|
|
||||||
ir.OKEY,
|
|
||||||
ir.OSTRUCTKEY,
|
|
||||||
ir.OLEN,
|
|
||||||
ir.OMUL,
|
|
||||||
ir.OLSH,
|
|
||||||
ir.ORSH,
|
|
||||||
ir.OAND,
|
|
||||||
ir.OANDNOT,
|
|
||||||
ir.ONEW,
|
|
||||||
ir.ONOT,
|
|
||||||
ir.OBITNOT,
|
|
||||||
ir.OPLUS,
|
|
||||||
ir.ONEG,
|
|
||||||
ir.OOROR,
|
|
||||||
ir.OPAREN,
|
|
||||||
ir.ORUNESTR,
|
|
||||||
ir.OREAL,
|
|
||||||
ir.OIMAG,
|
|
||||||
ir.OCOMPLEX:
|
|
||||||
break
|
|
||||||
|
|
||||||
// Discardable as long as we know it's not division by zero.
|
|
||||||
case ir.ODIV, ir.OMOD:
|
|
||||||
if n.Right().Op() == ir.OLITERAL && constant.Sign(n.Right().Val()) != 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
|
|
||||||
// Discardable as long as we know it won't fail because of a bad size.
|
|
||||||
case ir.OMAKECHAN, ir.OMAKEMAP:
|
|
||||||
if ir.IsConst(n.Left(), constant.Int) && constant.Sign(n.Left().Val()) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
|
|
||||||
// Difficult to tell what sizes are okay.
|
|
||||||
case ir.OMAKESLICE:
|
|
||||||
return false
|
|
||||||
|
|
||||||
case ir.OMAKESLICECOPY:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !candiscard(n.Left()) || !candiscard(n.Right()) || !candiscardlist(n.Init()) || !candiscardlist(n.Body()) || !candiscardlist(n.List()) || !candiscardlist(n.Rlist()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewrite
|
// Rewrite
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue