go.tools/pointer: make sole callsite available to intrinsics in non-shared contours.
This information can be used to specialize such calls, e.g.
- report location of unsound calls (done for reflect.NewAt)
- exploit argument information (done for constant 'dir' parameter to reflect.ChanOf)
+ tests.
R=crawshaw
CC=golang-dev
https://golang.org/cl/14517046
diff --git a/pointer/gen.go b/pointer/gen.go
index 154cde6..8c82cf3 100644
--- a/pointer/gen.go
+++ b/pointer/gen.go
@@ -115,18 +115,21 @@
return o
}
-// makeFunctionObject creates and returns a new function object for
-// fn, and returns the id of its first node. It also enqueues fn for
-// subsequent constraint generation.
+// makeFunctionObject creates and returns a new function object
+// (contour) for fn, and returns the id of its first node. It also
+// enqueues fn for subsequent constraint generation.
//
-func (a *analysis) makeFunctionObject(fn *ssa.Function) nodeid {
+// For a context-sensitive contour, callersite identifies the sole
+// callsite; for shared contours, caller is nil.
+//
+func (a *analysis) makeFunctionObject(fn *ssa.Function, callersite *callsite) nodeid {
if a.log != nil {
fmt.Fprintf(a.log, "\t---- makeFunctionObject %s\n", fn)
}
// obj is the function object (identity, params, results).
obj := a.nextNode()
- cgn := a.makeCGNode(fn, obj)
+ cgn := a.makeCGNode(fn, obj, callersite)
sig := fn.Signature
a.addOneNode(sig, "func.cgnode", nil) // (scalar with Signature type)
if recv := sig.Recv(); recv != nil {
@@ -568,18 +571,15 @@
return true
}
-// genStaticCall generates constraints for a statically dispatched
-// function call. It returns a node whose pts() will be the set of
-// possible call targets (in this case, a singleton).
-//
-func (a *analysis) genStaticCall(call *ssa.CallCommon, result nodeid) nodeid {
+// genStaticCall generates constraints for a statically dispatched function call.
+func (a *analysis) genStaticCall(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) {
// Ascertain the context (contour/CGNode) for a particular call.
var obj nodeid
fn := call.StaticCallee()
if a.shouldUseContext(fn) {
- obj = a.makeFunctionObject(fn) // new contour for this call
+ obj = a.makeFunctionObject(fn, site) // new contour
} else {
- obj = a.objectNode(nil, fn) // ordinary (shared) contour
+ obj = a.objectNode(nil, fn) // shared contour
}
sig := call.Signature()
@@ -609,13 +609,12 @@
a.copy(result, a.funcResults(obj), a.sizeof(sig.Results()))
}
- return targets
+ // pts(targets) will be the (singleton) set of possible call targets.
+ site.targets = targets
}
// genDynamicCall generates constraints for a dynamic function call.
-// It returns a node whose pts() will be the set of possible call targets.
-//
-func (a *analysis) genDynamicCall(caller *cgnode, call *ssa.CallCommon, result nodeid) nodeid {
+func (a *analysis) genDynamicCall(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) {
fn := a.valueNode(call.Value)
sig := call.Signature()
@@ -632,22 +631,24 @@
if result != 0 {
a.genLoad(caller, result, call.Value, offset, a.sizeof(sig.Results()))
}
- return fn
+
+ // pts(targets) will be the (singleton) set of possible call targets.
+ site.targets = fn
}
// genInvoke generates constraints for a dynamic method invocation.
-// It returns a node whose pts() will be the set of possible call targets.
-//
-func (a *analysis) genInvoke(call *ssa.CallCommon, result nodeid) nodeid {
+func (a *analysis) genInvoke(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) {
if call.Value.Type() == a.reflectType {
- return a.genInvokeReflectType(call, result)
+ a.genInvokeReflectType(caller, site, call, result)
+ return
}
sig := call.Signature()
// Allocate a contiguous targets/params/results block for this call.
block := a.nextNode()
- targets := a.addOneNode(sig, "invoke.targets", nil)
+ // pts(targets) will be the set of possible call targets
+ site.targets = a.addOneNode(sig, "invoke.targets", nil)
p := a.addNodes(sig.Params(), "invoke.params")
r := a.addNodes(sig.Results(), "invoke.results")
@@ -666,8 +667,6 @@
// edges from the caller's P/R block to the callee's
// P/R block for each discovered call target.
a.addConstraint(&invokeConstraint{call.Method, a.valueNode(call.Value), block})
-
- return targets
}
// genInvokeReflectType is a specialization of genInvoke where the
@@ -684,10 +683,7 @@
// as this:
// rt.(*reflect.rtype).F()
//
-// It returns a node whose pts() will be the (singleton) set of
-// possible call targets.
-//
-func (a *analysis) genInvokeReflectType(call *ssa.CallCommon, result nodeid) nodeid {
+func (a *analysis) genInvokeReflectType(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) {
// Unpack receiver into rtype
rtype := a.addOneNode(a.reflectRtypePtr, "rtype.recv", nil)
recv := a.valueNode(call.Value)
@@ -697,7 +693,10 @@
meth := a.reflectRtypePtr.MethodSet().Lookup(call.Method.Pkg(), call.Method.Name())
fn := a.prog.Method(meth)
- obj := a.makeFunctionObject(fn) // new contour for this call
+ obj := a.makeFunctionObject(fn, site) // new contour for this call
+
+ // pts(targets) will be the (singleton) set of possible call targets.
+ site.targets = obj
// From now on, it's essentially a static call, but little is
// gained by factoring together the code for both cases.
@@ -723,8 +722,6 @@
if result != 0 {
a.copy(result, a.funcResults(obj), a.sizeof(sig.Results()))
}
-
- return obj
}
// genCall generates contraints for call instruction instr.
@@ -742,22 +739,19 @@
result = a.valueNode(v)
}
- // The node whose pts(ยท) will contain all targets of the call.
- var targets nodeid
+ site := &callsite{instr: instr}
+
switch {
case call.StaticCallee() != nil:
- targets = a.genStaticCall(call, result)
+ a.genStaticCall(caller, site, call, result)
case call.IsInvoke():
- targets = a.genInvoke(call, result)
+ a.genInvoke(caller, site, call, result)
default:
- targets = a.genDynamicCall(caller, call, result)
+ a.genDynamicCall(caller, site, call, result)
}
- site := &callsite{
- targets: targets,
- instr: instr,
- }
caller.sites = append(caller.sites, site)
+
if a.log != nil {
fmt.Fprintf(a.log, "\t%s to targets %s from %s\n", site, site.targets, caller)
}
@@ -791,7 +785,7 @@
a.endObject(obj, nil, v)
case *ssa.Function:
- obj = a.makeFunctionObject(v)
+ obj = a.makeFunctionObject(v, nil)
case *ssa.Const:
if t, ok := v.Type().Underlying().(*types.Slice); ok && !v.IsNil() {
@@ -1075,8 +1069,8 @@
}
}
-func (a *analysis) makeCGNode(fn *ssa.Function, obj nodeid) *cgnode {
- cgn := &cgnode{fn: fn, obj: obj}
+func (a *analysis) makeCGNode(fn *ssa.Function, obj nodeid, callersite *callsite) *cgnode {
+ cgn := &cgnode{fn: fn, obj: obj, callersite: callersite}
a.cgnodes = append(a.cgnodes, cgn)
return cgn
}
@@ -1090,7 +1084,7 @@
r.Prog = a.prog // hack.
r.Enclosing = r // hack, so Function.String() doesn't crash
r.String() // (asserts that it doesn't crash)
- root := a.makeCGNode(r, 0)
+ root := a.makeCGNode(r, 0, nil)
// For each main package, call main.init(), main.main().
for _, mainPkg := range a.config.Mains {