mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
This includes the following information in the per-function summary:
outK = paramJ encoded in outK bits for paramJ
outK = *paramJ encoded in outK bits for paramJ
heap = paramJ EscHeap
heap = *paramJ EscContentEscapes
Note that (currently) if the address of a parameter is taken and
returned, necessarily a heap allocation occurred to contain that
reference, and the heap can never refer to stack, therefore the
parameter and everything downstream from it escapes to the heap.
The per-function summary information now has a tuneable number of bits
(2 is probably noticeably better than 1, 3 is likely overkill, but it
is now easy to check and the -m debugging output includes information
that allows you to figure out if more would be better.)
A new test was added to check pointer flow through struct-typed and
*struct-typed parameters and returns; some of these are sensitive to
the number of summary bits, and ought to yield better results with a
more competent escape analysis algorithm. Another new test checks
(some) correctness with array parameters, results, and operations.
The old analysis inferred a piece of plan9 runtime was non-escaping by
counteracting overconservative analysis with buggy analysis; with the
bug fixed, the result was too conservative (and it's not easy to fix
in this framework) so the source code was tweaked to get the desired
result. A test was added against the discovered bug.
The escape analysis was further improved splitting the "level" into
3 parts, one tracking the conventional "level" and the other two
computing the highest-level-suffix-from-copy, which is used to
generally model the cancelling effect of indirection applied to
address-of.
With the improved escape analysis enabled, it was necessary to
modify one of the runtime tests because it now attempts to allocate
too much on the (small, fixed-size) G0 (system) stack and this
failed the test.
Compiling src/std after touching src/runtime/*.go with -m logging
turned on shows 420 fewer heap allocation sites (10538 vs 10968).
Profiling allocations in src/html/template with
for i in {1..5} ;
do go tool 6g -memprofile=mastx.${i}.prof -memprofilerate=1 *.go;
go tool pprof -alloc_objects -text mastx.${i}.prof ;
done
showed a 15% reduction in allocations performed by the compiler.
Update #3753
Update #4720
Fixes #10466
Change-Id: I0fd97d5f5ac527b45f49e2218d158a6e89951432
Reviewed-on: https://go-review.googlesource.com/8202
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
456 lines
13 KiB
Go
456 lines
13 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// “Abstract” syntax representation.
|
|
|
|
package gc
|
|
|
|
// A Node is a single node in the syntax tree.
|
|
// Actually the syntax tree is a syntax DAG, because there is only one
|
|
// node with Op=ONAME for a given instance of a variable x.
|
|
// The same is true for Op=OTYPE and Op=OLITERAL.
|
|
type Node struct {
|
|
// Tree structure.
|
|
// Generic recursive walks should follow these fields.
|
|
Left *Node
|
|
Right *Node
|
|
Ntest *Node
|
|
Nincr *Node
|
|
Ninit *NodeList
|
|
Nbody *NodeList
|
|
Nelse *NodeList
|
|
List *NodeList
|
|
Rlist *NodeList
|
|
|
|
Op uint8
|
|
Nointerface bool
|
|
Ullman uint8 // sethi/ullman number
|
|
Addable bool // addressable
|
|
Etype uint8 // op for OASOP, etype for OTYPE, exclam for export
|
|
Bounded bool // bounds check unnecessary
|
|
Class uint8 // PPARAM, PAUTO, PEXTERN, etc
|
|
Method bool // OCALLMETH is direct method call
|
|
Embedded uint8 // ODCLFIELD embedded type
|
|
Colas bool // OAS resulting from :=
|
|
Diag uint8 // already printed error about this
|
|
Noescape bool // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360)
|
|
Walkdef uint8
|
|
Typecheck uint8
|
|
Local bool
|
|
Dodata uint8
|
|
Initorder uint8
|
|
Used bool
|
|
Isddd bool // is the argument variadic
|
|
Readonly bool
|
|
Implicit bool
|
|
Addrtaken bool // address taken, even if not moved to heap
|
|
Assigned bool // is the variable ever assigned to
|
|
Captured bool // is the variable captured by a closure
|
|
Byval bool // is the variable captured by value or by reference
|
|
Reslice bool // this is a reslice x = x[0:y] or x = append(x, ...)
|
|
Likely int8 // likeliness of if statement
|
|
Hasbreak bool // has break statement
|
|
Needzero bool // if it contains pointers, needs to be zeroed on function entry
|
|
Esc uint16 // EscXXX
|
|
Funcdepth int32
|
|
|
|
// most nodes
|
|
Type *Type
|
|
Orig *Node // original form, for printing, and tracking copies of ONAMEs
|
|
Nname *Node
|
|
|
|
// func
|
|
Func *Func
|
|
|
|
// OLITERAL
|
|
Val Val
|
|
|
|
// OREGISTER, OINDREG
|
|
Reg int16
|
|
|
|
// ONAME
|
|
Ntype *Node
|
|
Defn *Node // ONAME: initializing assignment; OLABEL: labeled statement
|
|
Pack *Node // real package for import . names
|
|
Curfn *Node // function for local variables
|
|
Paramfld *Type // TFIELD for this PPARAM; also for ODOT, curfn
|
|
Decldepth int // declaration loop depth, increased for every loop or label
|
|
|
|
// ONAME func param with PHEAP
|
|
Heapaddr *Node // temp holding heap address of param
|
|
Outerexpr *Node // expression copied into closure for variable
|
|
Stackparam *Node // OPARAM node referring to stack copy of param
|
|
Alloc *Node // allocation call
|
|
|
|
// ONAME closure param with PPARAMREF
|
|
Outer *Node // outer PPARAMREF in nested closure
|
|
Closure *Node // ONAME/PHEAP <-> ONAME/PPARAMREF
|
|
Top int // top context (Ecall, Eproc, etc)
|
|
|
|
// ONAME substitute while inlining
|
|
Inlvar *Node
|
|
|
|
// OPACK
|
|
Pkg *Pkg
|
|
|
|
// OARRAYLIT, OMAPLIT, OSTRUCTLIT.
|
|
Initplan *InitPlan
|
|
|
|
// Escape analysis.
|
|
Escflowsrc *NodeList // flow(this, src)
|
|
Escretval *NodeList // on OCALLxxx, list of dummy return values
|
|
Escloopdepth int // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
|
|
|
|
Sym *Sym // various
|
|
Vargen int32 // unique name for OTYPE/ONAME within a function. Function outputs are numbered starting at one.
|
|
Lineno int32
|
|
Xoffset int64
|
|
Stkdelta int64 // offset added by stack frame compaction phase.
|
|
Ostk int32 // 6g only
|
|
Iota int32
|
|
Walkgen uint32
|
|
Esclevel Level
|
|
Opt interface{} // for optimization passes
|
|
}
|
|
|
|
// Func holds Node fields used only with function-like nodes.
|
|
type Func struct {
|
|
Shortname *Node
|
|
Enter *NodeList
|
|
Exit *NodeList
|
|
Cvars *NodeList // closure params
|
|
Dcl *NodeList // autodcl for this func/closure
|
|
Inldcl *NodeList // copy of dcl for use in inlining
|
|
Closgen int
|
|
Outerfunc *Node
|
|
|
|
Inl *NodeList // copy of the body for use in inlining
|
|
InlCost int32
|
|
|
|
Endlineno int32
|
|
|
|
Nosplit bool // func should not execute on separate stack
|
|
Nowritebarrier bool // emit compiler error instead of write barrier
|
|
Dupok bool // duplicate definitions ok
|
|
Wrapper bool // is method wrapper
|
|
Needctxt bool // function uses context register (has closure variables)
|
|
}
|
|
|
|
// Node ops.
|
|
const (
|
|
OXXX = iota
|
|
|
|
// names
|
|
ONAME // var, const or func name
|
|
ONONAME // unnamed arg or return value: f(int, string) (int, error) { etc }
|
|
OTYPE // type name
|
|
OPACK // import
|
|
OLITERAL // literal
|
|
|
|
// expressions
|
|
OADD // x + y
|
|
OSUB // x - y
|
|
OOR // x | y
|
|
OXOR // x ^ y
|
|
OADDSTR // s + "foo"
|
|
OADDR // &x
|
|
OANDAND // b0 && b1
|
|
OAPPEND // append
|
|
OARRAYBYTESTR // string(bytes)
|
|
OARRAYBYTESTRTMP // string(bytes) ephemeral
|
|
OARRAYRUNESTR // string(runes)
|
|
OSTRARRAYBYTE // []byte(s)
|
|
OSTRARRAYBYTETMP // []byte(s) ephemeral
|
|
OSTRARRAYRUNE // []rune(s)
|
|
OAS // x = y or x := y
|
|
OAS2 // x, y, z = xx, yy, zz
|
|
OAS2FUNC // x, y = f()
|
|
OAS2RECV // x, ok = <-c
|
|
OAS2MAPR // x, ok = m["foo"]
|
|
OAS2DOTTYPE // x, ok = I.(int)
|
|
OASOP // x += y
|
|
OASWB // OAS but with write barrier
|
|
OCALL // function call, method call or type conversion, possibly preceded by defer or go.
|
|
OCALLFUNC // f()
|
|
OCALLMETH // t.Method()
|
|
OCALLINTER // err.Error()
|
|
OCALLPART // t.Method (without ())
|
|
OCAP // cap
|
|
OCLOSE // close
|
|
OCLOSURE // f = func() { etc }
|
|
OCMPIFACE // err1 == err2
|
|
OCMPSTR // s1 == s2
|
|
OCOMPLIT // composite literal, typechecking may convert to a more specific OXXXLIT.
|
|
OMAPLIT // M{"foo":3, "bar":4}
|
|
OSTRUCTLIT // T{x:3, y:4}
|
|
OARRAYLIT // [2]int{3, 4}
|
|
OPTRLIT // &T{x:3, y:4}
|
|
OCONV // var i int; var u uint; i = int(u)
|
|
OCONVIFACE // I(t)
|
|
OCONVNOP // type Int int; var i int; var j Int; i = int(j)
|
|
OCOPY // copy
|
|
ODCL // var x int
|
|
ODCLFUNC // func f() or func (r) f()
|
|
ODCLFIELD // struct field, interface field, or func/method argument/return value.
|
|
ODCLCONST // const pi = 3.14
|
|
ODCLTYPE // type Int int
|
|
ODELETE // delete
|
|
ODOT // t.x
|
|
ODOTPTR // p.x that is implicitly (*p).x
|
|
ODOTMETH // t.Method
|
|
ODOTINTER // err.Error
|
|
OXDOT // t.x, typechecking may convert to a more specific ODOTXXX.
|
|
ODOTTYPE // e = err.(MyErr)
|
|
ODOTTYPE2 // e, ok = err.(MyErr)
|
|
OEQ // x == y
|
|
ONE // x != y
|
|
OLT // x < y
|
|
OLE // x <= y
|
|
OGE // x >= y
|
|
OGT // x > y
|
|
OIND // *p
|
|
OINDEX // a[i]
|
|
OINDEXMAP // m[s]
|
|
OKEY // The x:3 in t{x:3, y:4}, the 1:2 in a[1:2], the 2:20 in [3]int{2:20}, etc.
|
|
OPARAM // The on-stack copy of a parameter or return value that escapes.
|
|
OLEN // len
|
|
OMAKE // make, typechecking may convert to a more specific OMAKEXXX.
|
|
OMAKECHAN // make(chan int)
|
|
OMAKEMAP // make(map[string]int)
|
|
OMAKESLICE // make([]int, 0)
|
|
OMUL // *
|
|
ODIV // x / y
|
|
OMOD // x % y
|
|
OLSH // x << u
|
|
ORSH // x >> u
|
|
OAND // x & y
|
|
OANDNOT // x &^ y
|
|
ONEW // new
|
|
ONOT // !b
|
|
OCOM // ^x
|
|
OPLUS // +x
|
|
OMINUS // -y
|
|
OOROR // b1 || b2
|
|
OPANIC // panic
|
|
OPRINT // print
|
|
OPRINTN // println
|
|
OPAREN // (x)
|
|
OSEND // c <- x
|
|
OSLICE // v[1:2], typechecking may convert to a more specific OSLICEXXX.
|
|
OSLICEARR // a[1:2]
|
|
OSLICESTR // s[1:2]
|
|
OSLICE3 // v[1:2:3], typechecking may convert to OSLICE3ARR.
|
|
OSLICE3ARR // a[1:2:3]
|
|
ORECOVER // recover
|
|
ORECV // <-c
|
|
ORUNESTR // string(i)
|
|
OSELRECV // case x = <-c:
|
|
OSELRECV2 // case x, ok = <-c:
|
|
OIOTA // iota
|
|
OREAL // real
|
|
OIMAG // imag
|
|
OCOMPLEX // complex
|
|
|
|
// statements
|
|
OBLOCK // block of code
|
|
OBREAK // break
|
|
OCASE // case, after being verified by swt.c's casebody.
|
|
OXCASE // case, before verification.
|
|
OCONTINUE // continue
|
|
ODEFER // defer
|
|
OEMPTY // no-op
|
|
OFALL // fallthrough, after being verified by swt.c's casebody.
|
|
OXFALL // fallthrough, before verification.
|
|
OFOR // for
|
|
OGOTO // goto
|
|
OIF // if
|
|
OLABEL // label:
|
|
OPROC // go
|
|
ORANGE // range
|
|
ORETURN // return
|
|
OSELECT // select
|
|
OSWITCH // switch x
|
|
OTYPESW // switch err.(type)
|
|
|
|
// types
|
|
OTCHAN // chan int
|
|
OTMAP // map[string]int
|
|
OTSTRUCT // struct{}
|
|
OTINTER // interface{}
|
|
OTFUNC // func()
|
|
OTARRAY // []int, [8]int, [N]int or [...]int
|
|
|
|
// misc
|
|
ODDD // func f(args ...int) or f(l...) or var a = [...]int{0, 1, 2}.
|
|
ODDDARG // func f(args ...int), introduced by escape analysis.
|
|
OINLCALL // intermediary representation of an inlined call.
|
|
OEFACE // itable and data words of an empty-interface value.
|
|
OITAB // itable word of an interface value.
|
|
OSPTR // base pointer of a slice or string.
|
|
OCLOSUREVAR // variable reference at beginning of closure function
|
|
OCFUNC // reference to c function pointer (not go func value)
|
|
OCHECKNIL // emit code to ensure pointer/interface not nil
|
|
OVARKILL // variable is dead
|
|
|
|
// thearch-specific registers
|
|
OREGISTER // a register, such as AX.
|
|
OINDREG // offset plus indirect of a register, such as 8(SP).
|
|
|
|
// arch-specific opcodes
|
|
OCMP // compare: ACMP.
|
|
ODEC // decrement: ADEC.
|
|
OINC // increment: AINC.
|
|
OEXTEND // extend: ACWD/ACDQ/ACQO.
|
|
OHMUL // high mul: AMUL/AIMUL for unsigned/signed (OMUL uses AIMUL for both).
|
|
OLROT // left rotate: AROL.
|
|
ORROTC // right rotate-carry: ARCR.
|
|
ORETJMP // return to other function
|
|
OPS // compare parity set (for x86 NaN check)
|
|
OPC // compare parity clear (for x86 NaN check)
|
|
OSQRT // sqrt(float64), on systems that have hw support
|
|
OGETG // runtime.getg() (read g pointer)
|
|
|
|
OEND
|
|
)
|
|
|
|
/*
|
|
* Every node has a walkgen field.
|
|
* If you want to do a traversal of a node graph that
|
|
* might contain duplicates and want to avoid
|
|
* visiting the same nodes twice, increment walkgen
|
|
* before starting. Then before processing a node, do
|
|
*
|
|
* if(n->walkgen == walkgen)
|
|
* return;
|
|
* n->walkgen = walkgen;
|
|
*
|
|
* Such a walk cannot call another such walk recursively,
|
|
* because of the use of the global walkgen.
|
|
*/
|
|
var walkgen uint32
|
|
|
|
// A NodeList is a linked list of nodes.
|
|
// TODO(rsc): Some uses of NodeList should be made into slices.
|
|
// The remaining ones probably just need a simple linked list,
|
|
// not one with concatenation support.
|
|
type NodeList struct {
|
|
N *Node
|
|
Next *NodeList
|
|
End *NodeList
|
|
}
|
|
|
|
// concat returns the concatenation of the lists a and b.
|
|
// The storage taken by both is reused for the result.
|
|
func concat(a *NodeList, b *NodeList) *NodeList {
|
|
if a == nil {
|
|
return b
|
|
}
|
|
if b == nil {
|
|
return a
|
|
}
|
|
|
|
a.End.Next = b
|
|
a.End = b.End
|
|
b.End = nil
|
|
return a
|
|
}
|
|
|
|
// list1 returns a one-element list containing n.
|
|
func list1(n *Node) *NodeList {
|
|
if n == nil {
|
|
return nil
|
|
}
|
|
if n.Op == OBLOCK && n.Ninit == nil {
|
|
// Flatten list and steal storage.
|
|
// Poison pointer to catch errant uses.
|
|
l := n.List
|
|
|
|
n.List = nil
|
|
return l
|
|
}
|
|
|
|
l := new(NodeList)
|
|
l.N = n
|
|
l.End = l
|
|
return l
|
|
}
|
|
|
|
// list returns the result of appending n to l.
|
|
func list(l *NodeList, n *Node) *NodeList {
|
|
return concat(l, list1(n))
|
|
}
|
|
|
|
// listsort sorts *l in place according to the 3-way comparison function f.
|
|
// The algorithm is mergesort, so it is guaranteed to be O(n log n).
|
|
func listsort(l **NodeList, f func(*Node, *Node) int) {
|
|
if *l == nil || (*l).Next == nil {
|
|
return
|
|
}
|
|
|
|
l1 := *l
|
|
l2 := *l
|
|
for {
|
|
l2 = l2.Next
|
|
if l2 == nil {
|
|
break
|
|
}
|
|
l2 = l2.Next
|
|
if l2 == nil {
|
|
break
|
|
}
|
|
l1 = l1.Next
|
|
}
|
|
|
|
l2 = l1.Next
|
|
l1.Next = nil
|
|
l2.End = (*l).End
|
|
(*l).End = l1
|
|
|
|
l1 = *l
|
|
listsort(&l1, f)
|
|
listsort(&l2, f)
|
|
|
|
if f(l1.N, l2.N) < 0 {
|
|
*l = l1
|
|
} else {
|
|
*l = l2
|
|
l2 = l1
|
|
l1 = *l
|
|
}
|
|
|
|
// now l1 == *l; and l1 < l2
|
|
|
|
var le *NodeList
|
|
for (l1 != nil) && (l2 != nil) {
|
|
for (l1.Next != nil) && f(l1.Next.N, l2.N) < 0 {
|
|
l1 = l1.Next
|
|
}
|
|
|
|
// l1 is last one from l1 that is < l2
|
|
le = l1.Next // le is the rest of l1, first one that is >= l2
|
|
if le != nil {
|
|
le.End = (*l).End
|
|
}
|
|
|
|
(*l).End = l1 // cut *l at l1
|
|
*l = concat(*l, l2) // glue l2 to *l's tail
|
|
|
|
l1 = l2 // l1 is the first element of *l that is < the new l2
|
|
l2 = le // ... because l2 now is the old tail of l1
|
|
}
|
|
|
|
*l = concat(*l, l2) // any remainder
|
|
}
|
|
|
|
// count returns the length of the list l.
|
|
func count(l *NodeList) int {
|
|
n := int64(0)
|
|
for ; l != nil; l = l.Next {
|
|
n++
|
|
}
|
|
if int64(int(n)) != n { // Overflow.
|
|
Yyerror("too many elements in list")
|
|
}
|
|
return int(n)
|
|
}
|