}
func hasCall(fn *ir.Func) bool {
- found := ir.Find(fn, func(n ir.Node) interface{} {
- if op := n.Op(); op == ir.OCALL || op == ir.OCALLFUNC {
- return n
- }
- return nil
+ return ir.Find(fn, func(n ir.Node) bool {
+ // TODO(rsc): No methods?
+ op := n.Op()
+ return op == ir.OCALL || op == ir.OCALLFUNC
})
- return found != nil
}
// eqfield returns the node
// hasCallOrChan reports whether n contains any calls or channel operations.
func hasCallOrChan(n ir.Node) bool {
- found := ir.Find(n, func(n ir.Node) interface{} {
+ return ir.Find(n, func(n ir.Node) bool {
switch n.Op() {
case ir.OAPPEND,
ir.OCALL,
ir.OREAL,
ir.ORECOVER,
ir.ORECV:
- return n
+ return true
}
- return nil
+ return false
})
- return found != nil
}
// A constSet represents a set of Go constant expressions.
continue
}
c.curfn = n.(*ir.Func)
- ir.Inspect(n, c.findExtraCalls)
+ ir.Visit(n, c.findExtraCalls)
}
c.curfn = nil
return c
}
-func (c *nowritebarrierrecChecker) findExtraCalls(n ir.Node) bool {
+func (c *nowritebarrierrecChecker) findExtraCalls(n ir.Node) {
if n.Op() != ir.OCALLFUNC {
- return true
+ return
}
fn := n.Left()
if fn == nil || fn.Op() != ir.ONAME || fn.Class() != ir.PFUNC || fn.Name().Defn == nil {
- return true
+ return
}
if !isRuntimePkg(fn.Sym().Pkg) || fn.Sym().Name != "systemstack" {
- return true
+ return
}
var callee *ir.Func
base.Fatalf("expected ODCLFUNC node, got %+v", callee)
}
c.extraCalls[c.curfn] = append(c.extraCalls[c.curfn], nowritebarrierrecCall{callee, n.Pos()})
- return true
}
// recordCall records a call from ODCLFUNC node "from", to function
fn.SetEsc(EscFuncStarted)
// Identify labels that mark the head of an unstructured loop.
- ir.InspectList(fn.Body(), func(n ir.Node) bool {
+ ir.Visit(fn, func(n ir.Node) {
switch n.Op() {
case ir.OLABEL:
if e.labels == nil {
e.labels[n.Sym()] = looping
}
}
-
- return true
})
e.curfn = fn
type initDeps struct {
transitive bool
seen ir.NameSet
+ cvisit func(ir.Node)
}
-func (d *initDeps) inspect(n ir.Node) { ir.Inspect(n, d.visit) }
-func (d *initDeps) inspectList(l ir.Nodes) { ir.InspectList(l, d.visit) }
+func (d *initDeps) cachedVisit() func(ir.Node) {
+ if d.cvisit == nil {
+ d.cvisit = d.visit // cache closure
+ }
+ return d.cvisit
+}
+
+func (d *initDeps) inspect(n ir.Node) { ir.Visit(n, d.cachedVisit()) }
+func (d *initDeps) inspectList(l ir.Nodes) { ir.VisitList(l, d.cachedVisit()) }
// visit calls foundDep on any package-level functions or variables
// referenced by n, if any.
-func (d *initDeps) visit(n ir.Node) bool {
+func (d *initDeps) visit(n ir.Node) {
switch n.Op() {
case ir.OMETHEXPR:
d.foundDep(methodExprName(n))
- return false
case ir.ONAME:
n := n.(*ir.Name)
case ir.ODOTMETH, ir.OCALLPART:
d.foundDep(methodExprName(n))
}
-
- return true
}
// foundDep records that we've found a dependency on n by adding it to
// Recursively identify all referenced functions for
// reexport. We want to include even non-called functions,
// because after inlining they might be callable.
- ir.InspectList(ir.AsNodes(fn.Inl.Body), func(n ir.Node) bool {
+ ir.VisitList(ir.AsNodes(fn.Inl.Body), func(n ir.Node) {
switch n.Op() {
case ir.OMETHEXPR, ir.ODOTMETH:
inlFlood(methodExprName(n))
// inlFlood(n.Func.Closure.Func.Nname)
base.Fatalf("unexpected closure in inlinable function")
}
- return true
})
}
func isBigFunc(fn *ir.Func) bool {
budget := inlineBigFunctionNodes
- over := ir.Find(fn, func(n ir.Node) interface{} {
+ return ir.Find(fn, func(n ir.Node) bool {
budget--
- if budget <= 0 {
- return n
- }
- return nil
+ return budget <= 0
})
- return over != nil
}
// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
return rhs
}
-var errFound = errors.New("found")
-
// 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.
// The second return value is the first such assignment encountered in the walk, if any. It is mostly
if name.Curfn == nil {
return true
}
- a := ir.Find(name.Curfn, func(n ir.Node) interface{} {
+ return ir.Find(name.Curfn, func(n ir.Node) bool {
switch n.Op() {
case ir.OAS:
if n.Left() == name && n != name.Defn {
- return n
+ return true
}
case ir.OAS2, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2DOTTYPE:
for _, p := range n.List().Slice() {
if p == name && n != name.Defn {
- return n
+ return true
}
}
}
- return nil
+ return false
})
- return a != nil
}
func inlParam(t *types.Field, as ir.Node, inlvars map[*ir.Name]ir.Node) ir.Node {
}
nreturns := 0
- ir.InspectList(ir.AsNodes(fn.Inl.Body), func(n ir.Node) bool {
+ ir.VisitList(ir.AsNodes(fn.Inl.Body), func(n ir.Node) {
if n != nil && n.Op() == ir.ORETURN {
nreturns++
}
- return true
})
// We can delay declaring+initializing result parameters if:
// concrete-type method calls where applicable.
func devirtualize(fn *ir.Func) {
Curfn = fn
- ir.InspectList(fn.Body(), func(n ir.Node) bool {
+ ir.VisitList(fn.Body(), func(n ir.Node) {
if n.Op() == ir.OCALLINTER {
devirtualizeCall(n)
}
- return true
})
}
min := v.visitgen
v.stack = append(v.stack, n)
- ir.InspectList(n.Body(), func(n ir.Node) bool {
+ ir.Visit(n, func(n ir.Node) {
switch n.Op() {
case ir.ONAME:
if n.Class() == ir.PFUNC {
min = m
}
}
- return true
})
if (min == id || min == id+1) && !n.IsHiddenClosure() {
// hasSideEffects reports whether n contains any operations that could have observable side effects.
func hasSideEffects(n ir.Node) bool {
- found := ir.Find(n, func(n ir.Node) interface{} {
+ return ir.Find(n, func(n ir.Node) bool {
switch n.Op() {
// Assume side effects unless we know otherwise.
default:
- return n
+ return true
// No side effects here (arguments are checked separately).
case ir.ONAME,
ir.OREAL,
ir.OIMAG,
ir.OCOMPLEX:
- return nil
+ return false
// 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
+ return true
}
// 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
+ return true
}
// 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 true
}
- return nil
+ return false
})
- return found != nil
}
// Rewrite
// }
// do(root)
//
-// The Inspect function illustrates a further simplification of the pattern,
-// only considering processing before visiting children, and letting
-// that processing decide whether children are visited at all:
+// The Visit function illustrates a further simplification of the pattern,
+// only processing before visiting children and never stopping:
//
-// func Inspect(n ir.Node, inspect func(ir.Node) bool) {
+// func Visit(n ir.Node, visit func(ir.Node)) {
// var do func(ir.Node) error
// do = func(x ir.Node) error {
-// if inspect(x) {
-// ir.DoChildren(x, do)
-// }
-// return nil
+// visit(x)
+// return ir.DoChildren(x, do)
// }
// if n != nil {
-// do(n)
+// visit(n)
// }
// }
//
// The Find function illustrates a different simplification of the pattern,
// visiting each node and then its children, recursively, until finding
-// a node x such that find(x) returns a non-nil result,
-// at which point the entire traversal stops:
+// a node x for which find(x) returns true, at which point the entire
+// traversal stops and returns true.
//
-// func Find(n ir.Node, find func(ir.Node) interface{}) interface{} {
+// func Find(n ir.Node, find func(ir.Node)) bool {
// stop := errors.New("stop")
-// var found interface{}
// var do func(ir.Node) error
// do = func(x ir.Node) error {
-// if v := find(x); v != nil {
-// found = v
+// if find(x) {
// return stop
// }
// return ir.DoChildren(x, do)
// }
-// do(n)
-// return found
+// return do(n) == stop
// }
//
-// Inspect and Find are presented above as examples of how to use
+// Visit and Find are presented above as examples of how to use
// DoChildren effectively, but of course, usage that fits within the
-// simplifications captured by Inspect or Find will be best served
+// simplifications captured by Visit or Find will be best served
// by directly calling the ones provided by this package.
func DoChildren(n Node, do func(Node) error) error {
if n == nil {
return nil
}
-// Inspect visits each node x in the IR tree rooted at n
-// in a depth-first preorder traversal, calling inspect on each node visited.
-// If inspect(x) returns false, then Inspect skips over x's children.
-//
-// Note that the meaning of the boolean result in the callback function
-// passed to Inspect differs from that of Scan.
-// During Scan, if scan(x) returns false, then Scan stops the scan.
-// During Inspect, if inspect(x) returns false, then Inspect skips x's children
-// but continues with the remainder of the tree (x's siblings and so on).
-func Inspect(n Node, inspect func(Node) bool) {
+// Visit visits each non-nil node x in the IR tree rooted at n
+// in a depth-first preorder traversal, calling visit on each node visited.
+func Visit(n Node, visit func(Node)) {
var do func(Node) error
do = func(x Node) error {
- if inspect(x) {
- DoChildren(x, do)
- }
- return nil
+ visit(x)
+ return DoChildren(x, do)
}
if n != nil {
do(n)
}
}
-// InspectList calls Inspect(x, inspect) for each node x in the list.
-func InspectList(list Nodes, inspect func(Node) bool) {
+// VisitList calls Visit(x, visit) for each node x in the list.
+func VisitList(list Nodes, visit func(Node)) {
for _, x := range list.Slice() {
- Inspect(x, inspect)
+ Visit(x, visit)
}
}
var stop = errors.New("stop")
// Find looks for a non-nil node x in the IR tree rooted at n
-// for which find(x) returns a non-nil value.
+// for which find(x) returns true.
// Find considers nodes in a depth-first, preorder traversal.
-// When Find finds a node x such that find(x) != nil,
-// Find ends the traversal and returns the value of find(x) immediately.
-// Otherwise Find returns nil.
-func Find(n Node, find func(Node) interface{}) interface{} {
+// When Find finds a node x such that find(x) is true,
+// Find ends the traversal and returns true immediately.
+// Otherwise Find returns false after completing the entire traversal.
+func Find(n Node, find func(Node) bool) bool {
if n == nil {
- return nil
+ return false
}
- var found interface{}
var do func(Node) error
do = func(x Node) error {
- if v := find(x); v != nil {
- found = v
+ if find(x) {
return stop
}
return DoChildren(x, do)
}
- do(n)
- return found
+ return do(n) == stop
}
-// FindList calls Find(x, ok) for each node x in the list, in order.
-// If any call find(x) returns a non-nil result, FindList stops and
+// FindList calls Find(x, find) for each node x in the list, in order.
+// If any call Find(x, find) returns true, FindList stops and
// returns that result, skipping the remainder of the list.
-// Otherwise FindList returns nil.
-func FindList(list Nodes, find func(Node) interface{}) interface{} {
+// Otherwise FindList returns false.
+func FindList(list Nodes, find func(Node) bool) bool {
for _, x := range list.Slice() {
- if v := Find(x, find); v != nil {
- return v
+ if Find(x, find) {
+ return true
}
}
- return nil
+ return false
}
// EditChildren edits the child nodes of n, replacing each child x with edit(x).