1
0
mirror of https://github.com/golang/go synced 2024-11-18 18:04:46 -07:00

cmd/compile: refactor Escape.tagHole

This CL refactors tagHole to handle all three call situations (unknown
function; known function in same analysis batch; known function in
previous analysis batch). This will make it somewhat easier to reuse
in a followup CL.

Passes toolstash-check.

Change-Id: I764d047a333dfc593d721a881361683e94b485df
Reviewed-on: https://go-review.googlesource.com/c/go/+/229059
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
Matthew Dempsky 2020-04-20 11:14:15 -07:00
parent 300ff5d8ac
commit 1811533695

View File

@ -718,8 +718,8 @@ func (e *Escape) assignHeap(src *Node, why string, where *Node) {
// should contain the holes representing where the function callee's
// results flows; where is the OGO/ODEFER context of the call, if any.
func (e *Escape) call(ks []EscHole, call, where *Node) {
// First, pick out the function callee, its type, and receiver
// (if any) and normal arguments list.
// First, pick out the function callee (if statically known),
// its type, and receiver (if any) and normal arguments list.
var fn, recv *Node
var fntype *types.Type
args := call.List.Slice()
@ -730,6 +730,9 @@ func (e *Escape) call(ks []EscHole, call, where *Node) {
fn = fn.Func.Closure.Func.Nname
}
fntype = fn.Type
if !(fn.Op == ONAME && fn.Class() == PFUNC) {
fn = nil // dynamic call
}
case OCALLMETH:
fn = asNode(call.Left.Type.FuncType().Nname)
fntype = fn.Type
@ -747,41 +750,22 @@ func (e *Escape) call(ks []EscHole, call, where *Node) {
Fatalf("unexpected call op: %v", call.Op)
}
static := fn != nil && fn.Op == ONAME && fn.Class() == PFUNC
// Setup evaluation holes for each receiver/argument.
var recvK EscHole
var paramKs []EscHole
if static && fn.Name.Defn != nil && fn.Name.Defn.Esc < EscFuncTagged {
// Static call to function in same mutually recursive
// group; incorporate into data flow graph.
if fn.Name.Defn.Esc == EscFuncUnknown {
Fatalf("graph inconsistency")
}
if ks != nil {
for i, result := range fntype.Results().FieldSlice() {
if call.Op == OCALLFUNC || call.Op == OCALLMETH || call.Op == OCALLINTER {
if ks != nil && fn != nil && e.inMutualBatch(fn) {
for i, result := range fn.Type.Results().FieldSlice() {
e.expr(ks[i], asNode(result.Nname))
}
}
if r := fntype.Recv(); r != nil {
recvK = e.addr(asNode(r.Nname))
recvK = e.tagHole(ks, fn, r)
}
for _, param := range fntype.Params().FieldSlice() {
paramKs = append(paramKs, e.addr(asNode(param.Nname)))
}
} else if call.Op == OCALLFUNC || call.Op == OCALLMETH || call.Op == OCALLINTER {
// Dynamic call, or call to previously tagged
// function. Setup flows to heap and/or ks according
// to parameter tags.
if r := fntype.Recv(); r != nil {
recvK = e.tagHole(ks, r, static)
}
for _, param := range fntype.Params().FieldSlice() {
paramKs = append(paramKs, e.tagHole(ks, param, static))
paramKs = append(paramKs, e.tagHole(ks, fn, param))
}
} else {
// Handle escape analysis for builtins.
@ -862,7 +846,7 @@ func (e *Escape) call(ks []EscHole, call, where *Node) {
// For arguments to go:uintptrescapes, peel
// away an unsafe.Pointer->uintptr conversion,
// if present.
if static && arg.Op == OCONVNOP && arg.Type.Etype == TUINTPTR && arg.Left.Type.Etype == TUNSAFEPTR {
if fn != nil && arg.Op == OCONVNOP && arg.Type.Etype == TUINTPTR && arg.Left.Type.Etype == TUNSAFEPTR {
x := i
if fntype.IsVariadic() && x >= fntype.NumParams() {
x = fntype.NumParams() - 1
@ -901,14 +885,19 @@ func (e *Escape) augmentParamHole(k EscHole, call, where *Node) EscHole {
// tagHole returns a hole for evaluating an argument passed to param.
// ks should contain the holes representing where the function
// callee's results flows; static indicates whether this is a static
// call.
func (e *Escape) tagHole(ks []EscHole, param *types.Field, static bool) EscHole {
// callee's results flows. fn is the statically-known callee function,
// if any.
func (e *Escape) tagHole(ks []EscHole, fn *Node, param *types.Field) EscHole {
// If this is a dynamic call, we can't rely on param.Note.
if !static {
if fn == nil {
return e.heapHole()
}
if e.inMutualBatch(fn) {
return e.addr(asNode(param.Nname))
}
// Call to previously tagged function.
var tagKs []EscHole
esc := ParseLeaks(param.Note)
@ -927,6 +916,21 @@ func (e *Escape) tagHole(ks []EscHole, param *types.Field, static bool) EscHole
return e.teeHole(tagKs...)
}
// inMutualBatch reports whether function fn is in the batch of
// mutually recursive functions being analyzed. When this is true,
// fn has not yet been analyzed, so its parameters and results
// should be incorporated directly into the flow graph instead of
// relying on its escape analysis tagging.
func (e *Escape) inMutualBatch(fn *Node) bool {
if fn.Name.Defn != nil && fn.Name.Defn.Esc < EscFuncTagged {
if fn.Name.Defn.Esc == EscFuncUnknown {
Fatalf("graph inconsistency")
}
return true
}
return false
}
// An EscHole represents a context for evaluation a Go
// expression. E.g., when evaluating p in "x = **p", we'd have a hole
// with dst==x and derefs==2.