| | |
| | |
| | |
| |
|
| | package devirtualize |
| |
|
| | import ( |
| | "cmd/compile/internal/base" |
| | "cmd/compile/internal/ir" |
| | "cmd/compile/internal/pgoir" |
| | "cmd/compile/internal/typecheck" |
| | "cmd/compile/internal/types" |
| | "cmd/internal/obj" |
| | "cmd/internal/pgo" |
| | "cmd/internal/src" |
| | "cmd/internal/sys" |
| | "testing" |
| | ) |
| |
|
| | func init() { |
| | |
| | |
| | |
| | types.PtrSize = 8 |
| | types.RegSize = 8 |
| | types.MaxWidth = 1 << 50 |
| | base.Ctxt = &obj.Link{Arch: &obj.LinkArch{Arch: &sys.Arch{Alignment: 1, CanMergeLoads: true}}} |
| | typecheck.InitUniverse() |
| | base.Debug.PGODebug = 3 |
| | } |
| |
|
| | func makePos(b *src.PosBase, line, col uint) src.XPos { |
| | return base.Ctxt.PosTable.XPos(src.MakePos(b, line, col)) |
| | } |
| |
|
| | type profileBuilder struct { |
| | p *pgoir.Profile |
| | } |
| |
|
| | func newProfileBuilder() *profileBuilder { |
| | |
| | |
| | return &profileBuilder{ |
| | p: &pgoir.Profile{ |
| | WeightedCG: &pgoir.IRGraph{ |
| | IRNodes: make(map[string]*pgoir.IRNode), |
| | }, |
| | }, |
| | } |
| | } |
| |
|
| | |
| | func (p *profileBuilder) Profile() *pgoir.Profile { |
| | return p.p |
| | } |
| |
|
| | |
| | |
| | |
| | func (p *profileBuilder) NewNode(name string, fn *ir.Func) *pgoir.IRNode { |
| | n := &pgoir.IRNode{ |
| | OutEdges: make(map[pgo.NamedCallEdge]*pgoir.IREdge), |
| | } |
| | if fn != nil { |
| | n.AST = fn |
| | } else { |
| | n.LinkerSymbolName = name |
| | } |
| | p.p.WeightedCG.IRNodes[name] = n |
| | return n |
| | } |
| |
|
| | |
| | func addEdge(caller, callee *pgoir.IRNode, offset int, weight int64) { |
| | namedEdge := pgo.NamedCallEdge{ |
| | CallerName: caller.Name(), |
| | CalleeName: callee.Name(), |
| | CallSiteOffset: offset, |
| | } |
| | irEdge := &pgoir.IREdge{ |
| | Src: caller, |
| | Dst: callee, |
| | CallSiteOffset: offset, |
| | Weight: weight, |
| | } |
| | caller.OutEdges[namedEdge] = irEdge |
| | } |
| |
|
| | |
| | |
| | func makeStructWithMethod(pkg *types.Pkg, structName, methName string) *ir.Func { |
| | |
| | structType := types.NewStruct(nil) |
| |
|
| | |
| | recv := types.NewField(src.NoXPos, typecheck.Lookup(structName), structType) |
| | sig := types.NewSignature(recv, nil, nil) |
| | fn := ir.NewFunc(src.NoXPos, src.NoXPos, pkg.Lookup(structName+"."+methName), sig) |
| |
|
| | |
| | structType.SetMethods([]*types.Field{types.NewField(src.NoXPos, typecheck.Lookup(methName), sig)}) |
| |
|
| | return fn |
| | } |
| |
|
| | func TestFindHotConcreteInterfaceCallee(t *testing.T) { |
| | p := newProfileBuilder() |
| |
|
| | pkgFoo := types.NewPkg("example.com/foo", "foo") |
| | basePos := src.NewFileBase("foo.go", "/foo.go") |
| |
|
| | const ( |
| | |
| | callerStart = 42 |
| |
|
| | |
| | callOffset = 1 |
| |
|
| | |
| | wrongCallOffset = 2 |
| | ) |
| |
|
| | |
| | |
| | |
| | fooSig := types.NewSignature(types.FakeRecv(), nil, nil) |
| | method := types.NewField(src.NoXPos, typecheck.Lookup("Foo"), fooSig) |
| | iface := types.NewInterface([]*types.Field{method}) |
| |
|
| | callerFn := ir.NewFunc(makePos(basePos, callerStart, 1), src.NoXPos, pkgFoo.Lookup("Caller"), types.NewSignature(nil, nil, nil)) |
| |
|
| | hotCalleeFn := makeStructWithMethod(pkgFoo, "HotCallee", "Foo") |
| | coldCalleeFn := makeStructWithMethod(pkgFoo, "ColdCallee", "Foo") |
| | wrongLineCalleeFn := makeStructWithMethod(pkgFoo, "WrongLineCallee", "Foo") |
| | wrongMethodCalleeFn := makeStructWithMethod(pkgFoo, "WrongMethodCallee", "Bar") |
| |
|
| | callerNode := p.NewNode("example.com/foo.Caller", callerFn) |
| | hotCalleeNode := p.NewNode("example.com/foo.HotCallee.Foo", hotCalleeFn) |
| | coldCalleeNode := p.NewNode("example.com/foo.ColdCallee.Foo", coldCalleeFn) |
| | wrongLineCalleeNode := p.NewNode("example.com/foo.WrongCalleeLine.Foo", wrongLineCalleeFn) |
| | wrongMethodCalleeNode := p.NewNode("example.com/foo.WrongCalleeMethod.Foo", wrongMethodCalleeFn) |
| |
|
| | hotMissingCalleeNode := p.NewNode("example.com/bar.HotMissingCallee.Foo", nil) |
| |
|
| | addEdge(callerNode, wrongLineCalleeNode, wrongCallOffset, 100) |
| | addEdge(callerNode, wrongMethodCalleeNode, callOffset, 100) |
| | addEdge(callerNode, hotCalleeNode, callOffset, 10) |
| | addEdge(callerNode, coldCalleeNode, callOffset, 1) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | addEdge(callerNode, hotMissingCalleeNode, callOffset, 10) |
| |
|
| | |
| | sel := typecheck.NewMethodExpr(src.NoXPos, iface, typecheck.Lookup("Foo")) |
| | call := ir.NewCallExpr(makePos(basePos, callerStart+callOffset, 1), ir.OCALLINTER, sel, nil) |
| |
|
| | gotFn, gotWeight := findHotConcreteInterfaceCallee(p.Profile(), callerFn, call) |
| | if gotFn != hotCalleeFn { |
| | t.Errorf("findHotConcreteInterfaceCallee func got %v want %v", gotFn, hotCalleeFn) |
| | } |
| | if gotWeight != 10 { |
| | t.Errorf("findHotConcreteInterfaceCallee weight got %v want 10", gotWeight) |
| | } |
| | } |
| |
|
| | func TestFindHotConcreteFunctionCallee(t *testing.T) { |
| | |
| | |
| | |
| |
|
| | p := newProfileBuilder() |
| |
|
| | pkgFoo := types.NewPkg("example.com/foo", "foo") |
| | basePos := src.NewFileBase("foo.go", "/foo.go") |
| |
|
| | const ( |
| | |
| | callerStart = 42 |
| |
|
| | |
| | callOffset = 1 |
| | ) |
| |
|
| | callerFn := ir.NewFunc(makePos(basePos, callerStart, 1), src.NoXPos, pkgFoo.Lookup("Caller"), types.NewSignature(nil, nil, nil)) |
| |
|
| | |
| | hotCalleeFn := ir.NewFunc(src.NoXPos, src.NoXPos, pkgFoo.Lookup("HotCallee"), types.NewSignature(nil, nil, nil)) |
| |
|
| | |
| | wrongCalleeFn := ir.NewFunc(src.NoXPos, src.NoXPos, pkgFoo.Lookup("WrongCallee"), types.NewSignature(nil, nil, |
| | []*types.Field{ |
| | types.NewField(src.NoXPos, nil, types.Types[types.TBOOL]), |
| | }, |
| | )) |
| |
|
| | callerNode := p.NewNode("example.com/foo.Caller", callerFn) |
| | hotCalleeNode := p.NewNode("example.com/foo.HotCallee", hotCalleeFn) |
| | wrongCalleeNode := p.NewNode("example.com/foo.WrongCallee", wrongCalleeFn) |
| |
|
| | addEdge(callerNode, wrongCalleeNode, callOffset, 100) |
| | addEdge(callerNode, hotCalleeNode, callOffset, 10) |
| |
|
| | |
| | name := ir.NewNameAt(src.NoXPos, typecheck.Lookup("fn"), types.NewSignature(nil, nil, nil)) |
| | |
| | call := ir.NewCallExpr(makePos(basePos, callerStart+callOffset, 1), ir.OCALL, name, nil) |
| |
|
| | gotFn, gotWeight := findHotConcreteFunctionCallee(p.Profile(), callerFn, call) |
| | if gotFn != hotCalleeFn { |
| | t.Errorf("findHotConcreteFunctionCallee func got %v want %v", gotFn, hotCalleeFn) |
| | } |
| | if gotWeight != 10 { |
| | t.Errorf("findHotConcreteFunctionCallee weight got %v want 10", gotWeight) |
| | } |
| | } |
| |
|