Skip to content

Commit 7472b4c

Browse files
committed
cmd/compile: include liveness info in GOSSAFUNC output
For this function ``` func test(a, b int, c string, s []int, r [3]int, f ifn) { in(a) in(b) sl(s) ar(r) fu(f) } ``` this output ``` HASH live at entry to test: f s HASH /Users/drchase/work/src/live/main.go 00000 (15) TEXT main.test(SB), ABIInternal 00001 (15) FUNCDATA $0, gclocals·vYpXgR4/KsH5nhFsqkHG1Q==(SB) 00002 (15) FUNCDATA $1, gclocals·Soq6RzO4SX8YA1O9euewoQ==(SB) 00003 (15) FUNCDATA $5, main.test.arginfo1(SB) 00004 (15) FUNCDATA $6, main.test.argliveinfo(SB) b1 00005 (15) PCDATA $3, $1 v32 00006 (21) MOVD R6, main.s+72(RSP) v27 00007 (21) MOVD R5, main.s+64(RSP) v30 00008 (21) MOVD R4, main.s+56(RSP) v7 00009 (21) MOVD R1, main.b+32(RSP) v34 00010 (21) MOVD R7, main.f+80(RSP) v34 00011 (21) PCDATA $3, $2 v15 00012 (+16) PCDATA $1, $0 HASH live at call to in: f s v15 00013 (+16) CALL main.in(SB) v3 00014 (+17) MOVD main.b+32(RSP), R0 HASH live at call to in: f s v17 00015 (+17) CALL main.in(SB) v8 00016 (+18) MOVD main.s+56(RSP), R0 v21 00017 (18) MOVD main.s+64(RSP), R1 v33 00018 (18) MOVD main.s+72(RSP), R2 v19 00019 (+18) PCDATA $1, $1 HASH live at call to sl: f v19 00020 (+18) CALL main.sl(SB) v29 00021 (+19) LDP main.r(RSP), (R1, R2) v9 00022 (19) STP (R1, R2), 8(RSP) v12 00023 (19) MOVD main.r+16(RSP), R1 v31 00024 (19) MOVD R1, 24(RSP) HASH live at call to ar: f v22 00025 (+19) CALL main.ar(SB) v35 00026 (+20) MOVD main.f+80(RSP), R0 v24 00027 (+20) PCDATA $1, $2 HASH live at call to fu: v24 00028 (+20) CALL main.fu(SB) b1 00029 (21) RET 00030 (?) END ``` Where "HASH" is the git commit comment character I don't know how to escape and this was easier than fighting with git. Change-Id: I0691a3f7988db111d11d69388ace83641a841e57 Reviewed-on: https://go-review.googlesource.com/c/go/+/641360 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Cuong Manh Le <[email protected]> Reviewed-by: Michael Knyszek <[email protected]>
1 parent 46fd6b4 commit 7472b4c

File tree

3 files changed

+75
-30
lines changed

3 files changed

+75
-30
lines changed

src/cmd/compile/internal/liveness/mergelocals.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ type candRegion struct {
5656
type cstate struct {
5757
fn *ir.Func
5858
f *ssa.Func
59-
lv *liveness
59+
lv *Liveness
6060
cands []*ir.Name
6161
nameToSlot map[*ir.Name]int32
6262
regions []candRegion

src/cmd/compile/internal/liveness/plive.go

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ type blockEffects struct {
102102
liveout bitvec.BitVec
103103
}
104104

105-
// A collection of global state used by liveness analysis.
106-
type liveness struct {
105+
// A collection of global state used by Liveness analysis.
106+
type Liveness struct {
107107
fn *ir.Func
108108
f *ssa.Func
109109
vars []*ir.Name
@@ -235,7 +235,7 @@ func getvariables(fn *ir.Func) ([]*ir.Name, map[*ir.Name]int32) {
235235
return vars, idx
236236
}
237237

238-
func (lv *liveness) initcache() {
238+
func (lv *Liveness) initcache() {
239239
if lv.cache.initialized {
240240
base.Fatalf("liveness cache initialized twice")
241241
return
@@ -281,7 +281,7 @@ const (
281281
// valueEffects returns the index of a variable in lv.vars and the
282282
// liveness effects v has on that variable.
283283
// If v does not affect any tracked variables, it returns -1, 0.
284-
func (lv *liveness) valueEffects(v *ssa.Value) (int32, liveEffect) {
284+
func (lv *Liveness) valueEffects(v *ssa.Value) (int32, liveEffect) {
285285
n, e := affectedVar(v)
286286
if e == 0 || n == nil { // cheapest checks first
287287
return -1, 0
@@ -392,8 +392,8 @@ type livenessFuncCache struct {
392392
// Constructs a new liveness structure used to hold the global state of the
393393
// liveness computation. The cfg argument is a slice of *BasicBlocks and the
394394
// vars argument is a slice of *Nodes.
395-
func newliveness(fn *ir.Func, f *ssa.Func, vars []*ir.Name, idx map[*ir.Name]int32, stkptrsize int64) *liveness {
396-
lv := &liveness{
395+
func newliveness(fn *ir.Func, f *ssa.Func, vars []*ir.Name, idx map[*ir.Name]int32, stkptrsize int64) *Liveness {
396+
lv := &Liveness{
397397
fn: fn,
398398
f: f,
399399
vars: vars,
@@ -447,14 +447,14 @@ func newliveness(fn *ir.Func, f *ssa.Func, vars []*ir.Name, idx map[*ir.Name]int
447447
return lv
448448
}
449449

450-
func (lv *liveness) blockEffects(b *ssa.Block) *blockEffects {
450+
func (lv *Liveness) blockEffects(b *ssa.Block) *blockEffects {
451451
return &lv.be[b.ID]
452452
}
453453

454454
// Generates live pointer value maps for arguments and local variables. The
455455
// this argument and the in arguments are always assumed live. The vars
456456
// argument is a slice of *Nodes.
457-
func (lv *liveness) pointerMap(liveout bitvec.BitVec, vars []*ir.Name, args, locals bitvec.BitVec) {
457+
func (lv *Liveness) pointerMap(liveout bitvec.BitVec, vars []*ir.Name, args, locals bitvec.BitVec) {
458458
var slotsSeen map[int64]*ir.Name
459459
checkForDuplicateSlots := base.Debug.MergeLocals != 0
460460
if checkForDuplicateSlots {
@@ -504,7 +504,7 @@ func IsUnsafe(f *ssa.Func) bool {
504504
}
505505

506506
// markUnsafePoints finds unsafe points and computes lv.unsafePoints.
507-
func (lv *liveness) markUnsafePoints() {
507+
func (lv *Liveness) markUnsafePoints() {
508508
if IsUnsafe(lv.f) {
509509
// No complex analysis necessary.
510510
lv.allUnsafe = true
@@ -647,7 +647,7 @@ func (lv *liveness) markUnsafePoints() {
647647
// This does not necessarily mean the instruction is a safe-point. In
648648
// particular, call Values can have a stack map in case the callee
649649
// grows the stack, but not themselves be a safe-point.
650-
func (lv *liveness) hasStackMap(v *ssa.Value) bool {
650+
func (lv *Liveness) hasStackMap(v *ssa.Value) bool {
651651
if !v.Op.IsCall() {
652652
return false
653653
}
@@ -663,7 +663,7 @@ func (lv *liveness) hasStackMap(v *ssa.Value) bool {
663663
// Initializes the sets for solving the live variables. Visits all the
664664
// instructions in each basic block to summarizes the information at each basic
665665
// block
666-
func (lv *liveness) prologue() {
666+
func (lv *Liveness) prologue() {
667667
lv.initcache()
668668

669669
for _, b := range lv.f.Blocks {
@@ -685,7 +685,7 @@ func (lv *liveness) prologue() {
685685
}
686686

687687
// Solve the liveness dataflow equations.
688-
func (lv *liveness) solve() {
688+
func (lv *Liveness) solve() {
689689
// These temporary bitvectors exist to avoid successive allocations and
690690
// frees within the loop.
691691
nvars := int32(len(lv.vars))
@@ -745,7 +745,7 @@ func (lv *liveness) solve() {
745745

746746
// Visits all instructions in a basic block and computes a bit vector of live
747747
// variables at each safe point locations.
748-
func (lv *liveness) epilogue() {
748+
func (lv *Liveness) epilogue() {
749749
nvars := int32(len(lv.vars))
750750
liveout := bitvec.New(nvars)
751751
livedefer := bitvec.New(nvars) // always-live variables
@@ -914,7 +914,7 @@ func (lv *liveness) epilogue() {
914914
// is actually a net loss: we save about 50k of argument bitmaps but the new
915915
// PCDATA tables cost about 100k. So for now we keep using a single index for
916916
// both bitmap lists.
917-
func (lv *liveness) compact(b *ssa.Block) {
917+
func (lv *Liveness) compact(b *ssa.Block) {
918918
pos := 0
919919
if b == lv.f.Entry {
920920
// Handle entry stack map.
@@ -939,7 +939,7 @@ func (lv *liveness) compact(b *ssa.Block) {
939939
lv.livevars = lv.livevars[:0]
940940
}
941941

942-
func (lv *liveness) enableClobber() {
942+
func (lv *Liveness) enableClobber() {
943943
// The clobberdead experiment inserts code to clobber pointer slots in all
944944
// the dead variables (locals and args) at every synchronous safepoint.
945945
if !base.Flag.ClobberDead {
@@ -994,7 +994,7 @@ func (lv *liveness) enableClobber() {
994994

995995
// Inserts code to clobber pointer slots in all the dead variables (locals and args)
996996
// at every synchronous safepoint in b.
997-
func (lv *liveness) clobber(b *ssa.Block) {
997+
func (lv *Liveness) clobber(b *ssa.Block) {
998998
// Copy block's values to a temporary.
999999
oldSched := append([]*ssa.Value{}, b.Values...)
10001000
b.Values = b.Values[:0]
@@ -1029,7 +1029,7 @@ func (lv *liveness) clobber(b *ssa.Block) {
10291029
// clobber generates code to clobber pointer slots in all dead variables
10301030
// (those not marked in live). Clobbering instructions are added to the end
10311031
// of b.Values.
1032-
func clobber(lv *liveness, b *ssa.Block, live bitvec.BitVec) {
1032+
func clobber(lv *Liveness, b *ssa.Block, live bitvec.BitVec) {
10331033
for i, n := range lv.vars {
10341034
if !live.Get(int32(i)) && !n.Addrtaken() && !n.OpenDeferSlot() && !n.IsOutputParamHeapAddr() {
10351035
// Don't clobber stack objects (address-taken). They are
@@ -1102,7 +1102,7 @@ func clobberPtr(b *ssa.Block, v *ir.Name, offset int64) {
11021102
b.NewValue0IA(src.NoXPos, ssa.OpClobber, types.TypeVoid, offset, v)
11031103
}
11041104

1105-
func (lv *liveness) showlive(v *ssa.Value, live bitvec.BitVec) {
1105+
func (lv *Liveness) showlive(v *ssa.Value, live bitvec.BitVec) {
11061106
if base.Flag.Live == 0 || ir.FuncName(lv.fn) == "init" || strings.HasPrefix(ir.FuncName(lv.fn), ".") {
11071107
return
11081108
}
@@ -1119,6 +1119,24 @@ func (lv *liveness) showlive(v *ssa.Value, live bitvec.BitVec) {
11191119
return
11201120
}
11211121

1122+
pos, s := lv.format(v, live)
1123+
1124+
base.WarnfAt(pos, "%s", s)
1125+
}
1126+
1127+
func (lv *Liveness) Format(v *ssa.Value) string {
1128+
if v == nil {
1129+
_, s := lv.format(nil, lv.stackMaps[0])
1130+
return s
1131+
}
1132+
if idx := lv.livenessMap.Get(v); idx.StackMapValid() {
1133+
_, s := lv.format(v, lv.stackMaps[idx])
1134+
return s
1135+
}
1136+
return ""
1137+
}
1138+
1139+
func (lv *Liveness) format(v *ssa.Value, live bitvec.BitVec) (src.XPos, string) {
11221140
pos := lv.fn.Nname.Pos()
11231141
if v != nil {
11241142
pos = v.Pos
@@ -1149,11 +1167,10 @@ func (lv *liveness) showlive(v *ssa.Value, live bitvec.BitVec) {
11491167
for _, v := range names {
11501168
s += " " + v
11511169
}
1152-
1153-
base.WarnfAt(pos, "%s", s)
1170+
return pos, s
11541171
}
11551172

1156-
func (lv *liveness) printbvec(printed bool, name string, live bitvec.BitVec) bool {
1173+
func (lv *Liveness) printbvec(printed bool, name string, live bitvec.BitVec) bool {
11571174
if live.IsEmpty() {
11581175
return printed
11591176
}
@@ -1177,7 +1194,7 @@ func (lv *liveness) printbvec(printed bool, name string, live bitvec.BitVec) boo
11771194
}
11781195

11791196
// printeffect is like printbvec, but for valueEffects.
1180-
func (lv *liveness) printeffect(printed bool, name string, pos int32, x bool) bool {
1197+
func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool) bool {
11811198
if !x {
11821199
return printed
11831200
}
@@ -1197,7 +1214,7 @@ func (lv *liveness) printeffect(printed bool, name string, pos int32, x bool) bo
11971214
// Prints the computed liveness information and inputs, for debugging.
11981215
// This format synthesizes the information used during the multiple passes
11991216
// into a single presentation.
1200-
func (lv *liveness) printDebug() {
1217+
func (lv *Liveness) printDebug() {
12011218
fmt.Printf("liveness: %s\n", ir.FuncName(lv.fn))
12021219

12031220
for i, b := range lv.f.Blocks {
@@ -1309,7 +1326,7 @@ func (lv *liveness) printDebug() {
13091326
// first word dumped is the total number of bitmaps. The second word is the
13101327
// length of the bitmaps. All bitmaps are assumed to be of equal length. The
13111328
// remaining bytes are the raw bitmaps.
1312-
func (lv *liveness) emit() (argsSym, liveSym *obj.LSym) {
1329+
func (lv *Liveness) emit() (argsSym, liveSym *obj.LSym) {
13131330
// Size args bitmaps to be just large enough to hold the largest pointer.
13141331
// First, find the largest Xoffset node we care about.
13151332
// (Nodes without pointers aren't in lv.vars; see ShouldTrack.)
@@ -1370,7 +1387,7 @@ func (lv *liveness) emit() (argsSym, liveSym *obj.LSym) {
13701387
// structure read by the garbage collector.
13711388
// Returns a map from GC safe points to their corresponding stack map index,
13721389
// and a map that contains all input parameters that may be partially live.
1373-
func Compute(curfn *ir.Func, f *ssa.Func, stkptrsize int64, pp *objw.Progs) (Map, map[*ir.Name]bool) {
1390+
func Compute(curfn *ir.Func, f *ssa.Func, stkptrsize int64, pp *objw.Progs, retLiveness bool) (Map, map[*ir.Name]bool, *Liveness) {
13741391
// Construct the global liveness state.
13751392
vars, idx := getvariables(curfn)
13761393
lv := newliveness(curfn, f, vars, idx, stkptrsize)
@@ -1432,10 +1449,15 @@ func Compute(curfn *ir.Func, f *ssa.Func, stkptrsize int64, pp *objw.Progs) (Map
14321449
p.To.Sym = x
14331450
}
14341451

1435-
return lv.livenessMap, lv.partLiveArgs
1452+
retLv := lv
1453+
if !retLiveness {
1454+
retLv = nil
1455+
}
1456+
1457+
return lv.livenessMap, lv.partLiveArgs, retLv
14361458
}
14371459

1438-
func (lv *liveness) emitStackObjects() *obj.LSym {
1460+
func (lv *Liveness) emitStackObjects() *obj.LSym {
14391461
var vars []*ir.Name
14401462
for _, n := range lv.fn.Dcl {
14411463
if shouldTrack(n) && n.Addrtaken() && n.Esc() != ir.EscHeap {

src/cmd/compile/internal/ssagen/ssa.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6335,7 +6335,10 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
63356335

63366336
e := f.Frontend().(*ssafn)
63376337

6338-
s.livenessMap, s.partLiveArgs = liveness.Compute(e.curfn, f, e.stkptrsize, pp)
6338+
gatherPrintInfo := f.PrintOrHtmlSSA || ssa.GenssaDump[f.Name]
6339+
6340+
var lv *liveness.Liveness
6341+
s.livenessMap, s.partLiveArgs, lv = liveness.Compute(e.curfn, f, e.stkptrsize, pp, gatherPrintInfo)
63396342
emitArgInfo(e, f, pp)
63406343
argLiveBlockMap, argLiveValueMap := liveness.ArgLiveness(e.curfn, f, pp)
63416344

@@ -6358,7 +6361,6 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
63586361
var progToValue map[*obj.Prog]*ssa.Value
63596362
var progToBlock map[*obj.Prog]*ssa.Block
63606363
var valueToProgAfter []*obj.Prog // The first Prog following computation of a value v; v is visible at this point.
6361-
gatherPrintInfo := f.PrintOrHtmlSSA || ssa.GenssaDump[f.Name]
63626364
if gatherPrintInfo {
63636365
progToValue = make(map[*obj.Prog]*ssa.Value, f.NumValues())
63646366
progToBlock = make(map[*obj.Prog]*ssa.Block, f.NumBlocks())
@@ -6766,6 +6768,14 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
67666768
buf.WriteString("<code>")
67676769
buf.WriteString("<dl class=\"ssa-gen\">")
67686770
filename := ""
6771+
6772+
liveness := lv.Format(nil)
6773+
if liveness != "" {
6774+
buf.WriteString("<dt class=\"ssa-prog-src\"></dt><dd class=\"ssa-prog\">")
6775+
buf.WriteString(html.EscapeString("# " + liveness))
6776+
buf.WriteString("</dd>")
6777+
}
6778+
67696779
for p := s.pp.Text; p != nil; p = p.Link {
67706780
// Don't spam every line with the file name, which is often huge.
67716781
// Only print changes, and "unknown" is not a change.
@@ -6778,6 +6788,19 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
67786788

67796789
buf.WriteString("<dt class=\"ssa-prog-src\">")
67806790
if v, ok := progToValue[p]; ok {
6791+
6792+
// Prefix calls with their liveness, if any
6793+
if p.As != obj.APCDATA {
6794+
if liveness := lv.Format(v); liveness != "" {
6795+
// Steal this line, and restart a line
6796+
buf.WriteString("</dt><dd class=\"ssa-prog\">")
6797+
buf.WriteString(html.EscapeString("# " + liveness))
6798+
buf.WriteString("</dd>")
6799+
// restarting a line
6800+
buf.WriteString("<dt class=\"ssa-prog-src\">")
6801+
}
6802+
}
6803+
67816804
buf.WriteString(v.HTML())
67826805
} else if b, ok := progToBlock[p]; ok {
67836806
buf.WriteString("<b>" + b.HTML() + "</b>")

0 commit comments

Comments
 (0)