1
0
mirror of https://github.com/golang/go synced 2024-11-05 11:36:10 -07:00

go.tools/go/pointer: add intrinsic for time.startTimer, which is implemented in C.

Without it, no value appears to be sent on NewTicker/NewTimer channels.

+ test

Also:
- add (callgraph.Edge).{Description,Pos} convenience methods
  to simplify client code when Site==nil.

LGTM=gri
R=gri, friestein68503
CC=golang-codereviews
https://golang.org/cl/112610043
This commit is contained in:
Alan Donovan 2014-07-22 18:30:06 -04:00
parent f9612295cb
commit e5d15a9781
6 changed files with 91 additions and 22 deletions

View File

@ -41,6 +41,7 @@ package callgraph
import (
"fmt"
"go/token"
"code.google.com/p/go.tools/go/ssa"
)
@ -99,6 +100,20 @@ func (e Edge) String() string {
return fmt.Sprintf("%s --> %s", e.Caller, e.Callee)
}
func (e Edge) Description() string {
if e.Site == nil {
return "synthetic call"
}
return e.Site.Common().Description()
}
func (e Edge) Pos() token.Pos {
if e.Site == nil {
return token.NoPos
}
return e.Site.Pos()
}
// AddEdge adds the edge (caller, site, callee) to the call graph.
// Elimination of duplicate edges is the caller's responsibility.
func AddEdge(caller *Node, site ssa.CallInstruction, callee *Node) {

View File

@ -174,7 +174,7 @@ func init() {
"syscall.setenv_c": ext۰NoEffect,
"time.Sleep": ext۰NoEffect,
"time.now": ext۰NoEffect,
"time.startTimer": ext۰NoEffect,
"time.startTimer": ext۰time۰startTimer,
"time.stopTimer": ext۰NoEffect,
} {
intrinsicsByName[name] = fn
@ -243,12 +243,8 @@ func (a *analysis) isReflect(fn *ssa.Function) bool {
func ext۰NoEffect(a *analysis, cgn *cgnode) {}
func ext۰NotYetImplemented(a *analysis, cgn *cgnode) {
// TODO(adonovan): enable this warning when we've implemented
// enough that it's not unbearably annoying.
if true {
fn := cgn.fn
a.warnf(fn.Pos(), "unsound: intrinsic treatment of %s not yet implemented", fn)
}
fn := cgn.fn
a.warnf(fn.Pos(), "unsound: intrinsic treatment of %s not yet implemented", fn)
}
// ---------- func runtime.SetFinalizer(x, f interface{}) ----------
@ -320,3 +316,65 @@ func ext۰runtime۰SetFinalizer(a *analysis, cgn *cgnode) {
f: params + 1,
})
}
// ---------- func time.startTimer(t *runtimeTimer) ----------
// time.StartTimer(t)
type timeStartTimerConstraint struct {
targets nodeid // (indirect)
t nodeid // (ptr)
}
func (c *timeStartTimerConstraint) ptr() nodeid { return c.t }
func (c *timeStartTimerConstraint) presolve(h *hvn) {
h.markIndirect(onodeid(c.targets), "StartTimer.targets")
}
func (c *timeStartTimerConstraint) renumber(mapping []nodeid) {
c.targets = mapping[c.targets]
c.t = mapping[c.t]
}
func (c *timeStartTimerConstraint) String() string {
return fmt.Sprintf("time.startTimer(n%d)", c.t)
}
func (c *timeStartTimerConstraint) solve(a *analysis, delta *nodeset) {
for _, tObj := range delta.AppendTo(a.deltaSpace) {
t := nodeid(tObj)
// We model startTimer as if it was defined thus:
// func startTimer(t *runtimeTimer) { t.f(0, t.arg) }
// We hard-code the field offsets of time.runtimeTimer:
// type runtimeTimer struct {
// 0 __identity__
// 1 i int32
// 2 when int64
// 3 period int64
// 4 f func(int64, interface{})
// 5 arg interface{}
// }
f := t + 4
arg := t + 5
// store t.arg to t.f.params[1]
// (offset 2 => skip identity and int64 param)
a.store(f, arg, 2, 1)
// Add dynamic call target.
if a.onlineCopy(c.targets, f) {
a.addWork(c.targets)
}
}
}
func ext۰time۰startTimer(a *analysis, cgn *cgnode) {
// This is the shared contour, used for dynamic calls.
targets := a.addOneNode(tInvalid, "startTimer.targets", nil)
cgn.sites = append(cgn.sites, &callsite{targets: targets})
params := a.funcParams(cgn.obj)
a.addConstraint(&timeStartTimerConstraint{
targets: targets,
t: params,
})
}

View File

@ -54,6 +54,7 @@ var inputs = []string{
"testdata/rtti.go",
"testdata/structreflect.go",
"testdata/structs.go",
"testdata/timer.go",
}
// Expectation grammar:

View File

@ -43,7 +43,7 @@ func (a *analysis) doCallgraph(cg *callgraph.Graph) {
for _, n := range cg.Nodes {
for _, e := range n.Out {
if e.Site == nil {
continue // a call from the <root> node
continue // a call from a synthetic node such as <root>
}
// Add (site pos, callee) to calledFuncs.

View File

@ -63,25 +63,20 @@ func (r *callersResult) display(printf printfFunc) {
if edge.Caller == root {
printf(r.target, "the root of the call graph")
} else {
printf(edge.Site, "\t%s from %s", edge.Site.Common().Description(), edge.Caller.Func)
printf(edge, "\t%s from %s", edge.Description(), edge.Caller.Func)
}
}
}
}
func (r *callersResult) toSerial(res *serial.Result, fset *token.FileSet) {
root := r.callgraph.Root
var callers []serial.Caller
for _, edge := range r.edges {
var c serial.Caller
c.Caller = edge.Caller.Func.String()
if edge.Caller == root {
c.Desc = "synthetic call"
} else {
c.Pos = fset.Position(edge.Site.Pos()).String()
c.Desc = edge.Site.Common().Description()
}
callers = append(callers, c)
callers = append(callers, serial.Caller{
Caller: edge.Caller.Func.String(),
Pos: fset.Position(edge.Pos()).String(),
Desc: edge.Description(),
})
}
res.Callers = callers
}

View File

@ -71,7 +71,7 @@ func (r *callstackResult) display(printf printfFunc) {
printf(r.target, "%s", r.target)
for i := len(r.callpath) - 1; i >= 0; i-- {
edge := r.callpath[i]
printf(edge.Site, "%s from %s", edge.Site.Common().Description(), edge.Caller.Func)
printf(edge, "%s from %s", edge.Description(), edge.Caller.Func)
}
} else {
printf(r.target, "%s is unreachable in this analysis scope", r.target)
@ -83,9 +83,9 @@ func (r *callstackResult) toSerial(res *serial.Result, fset *token.FileSet) {
for i := len(r.callpath) - 1; i >= 0; i-- { // (innermost first)
edge := r.callpath[i]
callers = append(callers, serial.Caller{
Pos: fset.Position(edge.Site.Pos()).String(),
Pos: fset.Position(edge.Pos()).String(),
Caller: edge.Caller.Func.String(),
Desc: edge.Site.Common().Description(),
Desc: edge.Description(),
})
}
res.Callstack = &serial.CallStack{