Skip to content

Commit 4b30fce

Browse files
committed
cmd/compile: add ast nodes and basic block counters
Current patch adds the counters to the AST and SSA nodes. The counters are loaded from the pprof file, no profile format changes needed. To use basic block counters you should add an option -bbpgo: go build -a -bbpgo -pgo=file.prof Fixes golang#65466 Change-Id: I5d6be7d87f384625259a9ba794744a652060de4e
1 parent 334ce51 commit 4b30fce

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1405
-206
lines changed

src/cmd/compile/internal/base/flag.go

+1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ type CmdFlags struct {
125125
TrimPath string "help:\"remove `prefix` from recorded source file paths\""
126126
WB bool "help:\"enable write barrier\"" // TODO: remove
127127
PgoProfile string "help:\"read profile or pre-process profile from `file`\""
128+
BbPgoProfile bool "help:\"use basic block couters in pgo mode\""
128129
ErrorURL bool "help:\"print explanatory URL with error message if applicable\""
129130

130131
// Configuration derived from flags; not a flag itself.

src/cmd/compile/internal/devirtualize/pgo.go

+4
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ func copyInputs(curfn *ir.Func, pos src.XPos, recvOrFn ir.Node, args []ir.Node,
430430
}
431431

432432
asList := ir.NewAssignListStmt(pos, ir.OAS2, lhs, rhs)
433+
asList.SetCounter(recvOrFn.Counter())
433434
init.Append(typecheck.Stmt(asList))
434435

435436
return newRecvOrFn, lhs[1:]
@@ -461,10 +462,12 @@ func condCall(curfn *ir.Func, pos src.XPos, cond ir.Node, thenCall, elseCall *ir
461462
// Copy slice so edits in one location don't affect another.
462463
thenRet := append([]ir.Node(nil), retvars...)
463464
thenAsList := ir.NewAssignListStmt(pos, ir.OAS2, thenRet, []ir.Node{thenCall})
465+
thenAsList.SetCounter(thenCall.Counter())
464466
thenBlock.Append(typecheck.Stmt(thenAsList))
465467

466468
elseRet := append([]ir.Node(nil), retvars...)
467469
elseAsList := ir.NewAssignListStmt(pos, ir.OAS2, elseRet, []ir.Node{elseCall})
470+
elseAsList.SetCounter(elseCall.Counter())
468471
elseBlock.Append(typecheck.Stmt(elseAsList))
469472
}
470473

@@ -534,6 +537,7 @@ func rewriteInterfaceCall(call *ir.CallExpr, curfn, callee *ir.Func, concretetyp
534537
assert := ir.NewTypeAssertExpr(pos, recv, concretetyp)
535538

536539
assertAsList := ir.NewAssignListStmt(pos, ir.OAS2, []ir.Node{tmpnode, tmpok}, []ir.Node{typecheck.Expr(assert)})
540+
assertAsList.SetCounter(call.Counter())
537541
init.Append(typecheck.Stmt(assertAsList))
538542

539543
concreteCallee := typecheck.XDotMethod(pos, tmpnode, method, true)

src/cmd/compile/internal/escape/call.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,13 @@ func (e *escape) copyExpr(pos src.XPos, expr ir.Node, init *ir.Nodes) *ir.Name {
304304

305305
tmp := typecheck.TempAt(pos, e.curfn, expr.Type())
306306

307+
d := ir.NewDecl(pos, ir.ODCL, tmp, expr.Counter())
308+
d.SetCounter(expr.Counter())
309+
as := ir.NewAssignStmt(pos, tmp, expr)
310+
as.SetCounter(expr.Counter())
307311
stmts := []ir.Node{
308-
ir.NewDecl(pos, ir.ODCL, tmp),
309-
ir.NewAssignStmt(pos, tmp, expr),
312+
d,
313+
as,
310314
}
311315
typecheck.Stmts(stmts)
312316
init.Append(stmts...)

src/cmd/compile/internal/gc/main.go

+3
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ func Main(archInit func(*ssagen.ArchInfo)) {
223223
log.Fatalf("%s: PGO error: %v", base.Flag.PgoProfile, err)
224224
}
225225
}
226+
for _, fn := range typecheck.Target.Funcs {
227+
ir.CheckIR(fn)
228+
}
226229

227230
// Interleaved devirtualization and inlining.
228231
base.Timer.Start("fe", "devirtualize-and-inline")

src/cmd/compile/internal/inline/inl.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ func TryInlineCall(callerfn *ir.Func, call *ir.CallExpr, bigCaller bool, profile
798798
return nil
799799
}
800800
if fn := inlCallee(callerfn, call.Fun, profile); fn != nil && typecheck.HaveInlineBody(fn) {
801-
return mkinlcall(callerfn, call, fn, bigCaller)
801+
return mkinlcall(callerfn, call, fn, bigCaller, profile)
802802
}
803803
return nil
804804
}
@@ -843,7 +843,7 @@ var SSADumpInline = func(*ir.Func) {}
843843

844844
// InlineCall allows the inliner implementation to be overridden.
845845
// If it returns nil, the function will not be inlined.
846-
var InlineCall = func(callerfn *ir.Func, call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr {
846+
var InlineCall = func(callerfn *ir.Func, call *ir.CallExpr, fn *ir.Func, inlIndex int, profile *pgoir.Profile) *ir.InlinedCallExpr {
847847
base.Fatalf("inline.InlineCall not overridden")
848848
panic("unreachable")
849849
}
@@ -1000,7 +1000,7 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa
10001000
// The result of mkinlcall MUST be assigned back to n, e.g.
10011001
//
10021002
// n.Left = mkinlcall(n.Left, fn, isddd)
1003-
func mkinlcall(callerfn *ir.Func, n *ir.CallExpr, fn *ir.Func, bigCaller bool) *ir.InlinedCallExpr {
1003+
func mkinlcall(callerfn *ir.Func, n *ir.CallExpr, fn *ir.Func, bigCaller bool, profile *pgoir.Profile) *ir.InlinedCallExpr {
10041004
ok, score := canInlineCallExpr(callerfn, n, fn, bigCaller, true)
10051005
if !ok {
10061006
return nil
@@ -1072,7 +1072,7 @@ func mkinlcall(callerfn *ir.Func, n *ir.CallExpr, fn *ir.Func, bigCaller bool) *
10721072
fmt.Printf("%v: Before inlining: %+v\n", ir.Line(n), n)
10731073
}
10741074

1075-
res := InlineCall(callerfn, n, fn, inlIndex)
1075+
res := InlineCall(callerfn, n, fn, inlIndex, profile)
10761076

10771077
if res == nil {
10781078
base.FatalfAt(n.Pos(), "inlining call to %v failed", fn)

src/cmd/compile/internal/ir/expr.go

+1
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ func NewBinaryExpr(pos src.XPos, op Op, x, y Node) *BinaryExpr {
166166
n := &BinaryExpr{X: x, Y: y}
167167
n.pos = pos
168168
n.SetOp(op)
169+
n.SetCounter(max(x.Counter(), y.Counter()))
169170
return n
170171
}
171172

src/cmd/compile/internal/ir/fmt.go

+16-9
Original file line numberDiff line numberDiff line change
@@ -916,8 +916,11 @@ func FDumpList(w io.Writer, s string, list Nodes) {
916916
}
917917

918918
// indent prints indentation to w.
919-
func indent(w io.Writer, depth int) {
919+
func indent(w io.Writer, depth int, counter int64) {
920920
fmt.Fprint(w, "\n")
921+
if base.Flag.BbPgoProfile {
922+
fmt.Fprintf(w, "%d ", counter)
923+
}
921924
for i := 0; i < depth; i++ {
922925
fmt.Fprint(w, ". ")
923926
}
@@ -1049,7 +1052,7 @@ func dumpNodeHeader(w io.Writer, n Node) {
10491052
}
10501053

10511054
func dumpNode(w io.Writer, n Node, depth int) {
1052-
indent(w, depth)
1055+
indent(w, depth, n.Counter())
10531056
if depth > 40 {
10541057
fmt.Fprint(w, "...")
10551058
return
@@ -1063,7 +1066,7 @@ func dumpNode(w io.Writer, n Node, depth int) {
10631066
if len(n.Init()) != 0 {
10641067
fmt.Fprintf(w, "%+v-init", n.Op())
10651068
dumpNodes(w, n.Init(), depth+1)
1066-
indent(w, depth)
1069+
indent(w, depth, n.Counter())
10671070
}
10681071

10691072
switch n.Op() {
@@ -1116,21 +1119,21 @@ func dumpNode(w io.Writer, n Node, depth int) {
11161119
dumpNodeHeader(w, n)
11171120
fn := n
11181121
if len(fn.Dcl) > 0 {
1119-
indent(w, depth)
1122+
indent(w, depth, n.Counter())
11201123
fmt.Fprintf(w, "%+v-Dcl", n.Op())
11211124
for _, dcl := range n.Dcl {
11221125
dumpNode(w, dcl, depth+1)
11231126
}
11241127
}
11251128
if len(fn.ClosureVars) > 0 {
1126-
indent(w, depth)
1129+
indent(w, depth, n.Counter())
11271130
fmt.Fprintf(w, "%+v-ClosureVars", n.Op())
11281131
for _, cv := range fn.ClosureVars {
11291132
dumpNode(w, cv, depth+1)
11301133
}
11311134
}
11321135
if len(fn.Body) > 0 {
1133-
indent(w, depth)
1136+
indent(w, depth, n.Counter())
11341137
fmt.Fprintf(w, "%+v-body", n.Op())
11351138
dumpNodes(w, fn.Body, depth+1)
11361139
}
@@ -1164,7 +1167,7 @@ func dumpNode(w io.Writer, n Node, depth int) {
11641167
switch val := vf.Interface().(type) {
11651168
case Node:
11661169
if name != "" {
1167-
indent(w, depth)
1170+
indent(w, depth, n.Counter())
11681171
fmt.Fprintf(w, "%+v-%s", n.Op(), name)
11691172
}
11701173
dumpNode(w, val, depth+1)
@@ -1173,7 +1176,11 @@ func dumpNode(w io.Writer, n Node, depth int) {
11731176
continue
11741177
}
11751178
if name != "" {
1176-
indent(w, depth)
1179+
c := n.Counter()
1180+
if len(val) > 0 {
1181+
c = val[0].Counter()
1182+
}
1183+
indent(w, depth, c)
11771184
fmt.Fprintf(w, "%+v-%s", n.Op(), name)
11781185
}
11791186
dumpNodes(w, val, depth+1)
@@ -1183,7 +1190,7 @@ func dumpNode(w io.Writer, n Node, depth int) {
11831190
continue
11841191
}
11851192
if name != "" {
1186-
indent(w, depth)
1193+
indent(w, depth, n.Counter())
11871194
fmt.Fprintf(w, "%+v-%s", n.Op(), name)
11881195
}
11891196
for i, n := 0, vf.Len(); i < n; i++ {
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// “Abstract” syntax representation.
6+
7+
package ir
8+
9+
import (
10+
"cmd/compile/internal/base"
11+
)
12+
13+
// CheckIR checks function IR concistency
14+
func CheckIR(fn *Func) {
15+
if !base.Flag.BbPgoProfile {
16+
return
17+
}
18+
return
19+
VisitList(fn.Body, func(n Node) {
20+
switch n.Op() {
21+
case OIF:
22+
n := n.(*IfStmt)
23+
var c, bC, eC int64
24+
c = n.Counter()
25+
if n.Body != nil && len(n.Body) > 0 {
26+
bC = n.Body[0].Counter()
27+
}
28+
if n.Body != nil && len(n.Else) > 0 {
29+
eC = n.Else[0].Counter()
30+
}
31+
if c < bC+eC {
32+
base.FatalfAt(n.Pos(), "Incorrect edges counter for IF node %d + %d > %d", bC, eC, c)
33+
}
34+
case OCHECKNIL:
35+
if n.Counter() != 0 {
36+
base.FatalfAt(n.Pos(), "Non-zero NilCheck counter")
37+
}
38+
}
39+
})
40+
}

src/cmd/compile/internal/ir/mini.go

+12-9
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ import (
2828
// for more useful panic messages when invalid methods are called,
2929
// instead of implementing Op itself.
3030
type miniNode struct {
31-
pos src.XPos // uint32
32-
op Op // uint8
33-
bits bitset8
34-
esc uint16
31+
counter int64
32+
pos src.XPos // uint32
33+
op Op // uint8
34+
bits bitset8
35+
esc uint16
3536
}
3637

3738
// posOr returns pos if known, or else n.pos.
@@ -46,11 +47,13 @@ func (n *miniNode) posOr(pos src.XPos) src.XPos {
4647
// op can be read, but not written.
4748
// An embedding implementation can provide a SetOp if desired.
4849
// (The panicking SetOp is with the other panics below.)
49-
func (n *miniNode) Op() Op { return n.op }
50-
func (n *miniNode) Pos() src.XPos { return n.pos }
51-
func (n *miniNode) SetPos(x src.XPos) { n.pos = x }
52-
func (n *miniNode) Esc() uint16 { return n.esc }
53-
func (n *miniNode) SetEsc(x uint16) { n.esc = x }
50+
func (n *miniNode) Counter() int64 { return n.counter }
51+
func (n *miniNode) SetCounter(c int64) { n.counter = c }
52+
func (n *miniNode) Op() Op { return n.op }
53+
func (n *miniNode) Pos() src.XPos { return n.pos }
54+
func (n *miniNode) SetPos(x src.XPos) { n.pos = x }
55+
func (n *miniNode) Esc() uint16 { return n.esc }
56+
func (n *miniNode) SetEsc(x uint16) { n.esc = x }
5457

5558
const (
5659
miniTypecheckShift = 0

src/cmd/compile/internal/ir/node.go

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ type Node interface {
2121
// Formatting
2222
Format(s fmt.State, verb rune)
2323

24+
// Node profile counter.
25+
Counter() int64
26+
SetCounter(c int64)
27+
2428
// Source position.
2529
Pos() src.XPos
2630
SetPos(x src.XPos)

src/cmd/compile/internal/ir/sizeof_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ func TestSizeof(t *testing.T) {
2020
_32bit uintptr // size on 32bit platforms
2121
_64bit uintptr // size on 64bit platforms
2222
}{
23-
{Func{}, 168, 288},
24-
{Name{}, 96, 168},
23+
{Func{}, 176, 296},
24+
{Name{}, 104, 176},
2525
}
2626

2727
for _, tt := range tests {

0 commit comments

Comments
 (0)