liveout bitvec.BitVec
}
-// A collection of global state used by liveness analysis.
-type liveness struct {
+// A collection of global state used by Liveness analysis.
+type Liveness struct {
fn *ir.Func
f *ssa.Func
vars []*ir.Name
return vars, idx
}
-func (lv *liveness) initcache() {
+func (lv *Liveness) initcache() {
if lv.cache.initialized {
base.Fatalf("liveness cache initialized twice")
return
// valueEffects returns the index of a variable in lv.vars and the
// liveness effects v has on that variable.
// If v does not affect any tracked variables, it returns -1, 0.
-func (lv *liveness) valueEffects(v *ssa.Value) (int32, liveEffect) {
+func (lv *Liveness) valueEffects(v *ssa.Value) (int32, liveEffect) {
n, e := affectedVar(v)
if e == 0 || n == nil { // cheapest checks first
return -1, 0
// Constructs a new liveness structure used to hold the global state of the
// liveness computation. The cfg argument is a slice of *BasicBlocks and the
// vars argument is a slice of *Nodes.
-func newliveness(fn *ir.Func, f *ssa.Func, vars []*ir.Name, idx map[*ir.Name]int32, stkptrsize int64) *liveness {
- lv := &liveness{
+func newliveness(fn *ir.Func, f *ssa.Func, vars []*ir.Name, idx map[*ir.Name]int32, stkptrsize int64) *Liveness {
+ lv := &Liveness{
fn: fn,
f: f,
vars: vars,
return lv
}
-func (lv *liveness) blockEffects(b *ssa.Block) *blockEffects {
+func (lv *Liveness) blockEffects(b *ssa.Block) *blockEffects {
return &lv.be[b.ID]
}
// Generates live pointer value maps for arguments and local variables. The
// this argument and the in arguments are always assumed live. The vars
// argument is a slice of *Nodes.
-func (lv *liveness) pointerMap(liveout bitvec.BitVec, vars []*ir.Name, args, locals bitvec.BitVec) {
+func (lv *Liveness) pointerMap(liveout bitvec.BitVec, vars []*ir.Name, args, locals bitvec.BitVec) {
var slotsSeen map[int64]*ir.Name
checkForDuplicateSlots := base.Debug.MergeLocals != 0
if checkForDuplicateSlots {
}
// markUnsafePoints finds unsafe points and computes lv.unsafePoints.
-func (lv *liveness) markUnsafePoints() {
+func (lv *Liveness) markUnsafePoints() {
if IsUnsafe(lv.f) {
// No complex analysis necessary.
lv.allUnsafe = true
// This does not necessarily mean the instruction is a safe-point. In
// particular, call Values can have a stack map in case the callee
// grows the stack, but not themselves be a safe-point.
-func (lv *liveness) hasStackMap(v *ssa.Value) bool {
+func (lv *Liveness) hasStackMap(v *ssa.Value) bool {
if !v.Op.IsCall() {
return false
}
// Initializes the sets for solving the live variables. Visits all the
// instructions in each basic block to summarizes the information at each basic
// block
-func (lv *liveness) prologue() {
+func (lv *Liveness) prologue() {
lv.initcache()
for _, b := range lv.f.Blocks {
}
// Solve the liveness dataflow equations.
-func (lv *liveness) solve() {
+func (lv *Liveness) solve() {
// These temporary bitvectors exist to avoid successive allocations and
// frees within the loop.
nvars := int32(len(lv.vars))
// Visits all instructions in a basic block and computes a bit vector of live
// variables at each safe point locations.
-func (lv *liveness) epilogue() {
+func (lv *Liveness) epilogue() {
nvars := int32(len(lv.vars))
liveout := bitvec.New(nvars)
livedefer := bitvec.New(nvars) // always-live variables
// is actually a net loss: we save about 50k of argument bitmaps but the new
// PCDATA tables cost about 100k. So for now we keep using a single index for
// both bitmap lists.
-func (lv *liveness) compact(b *ssa.Block) {
+func (lv *Liveness) compact(b *ssa.Block) {
pos := 0
if b == lv.f.Entry {
// Handle entry stack map.
lv.livevars = lv.livevars[:0]
}
-func (lv *liveness) enableClobber() {
+func (lv *Liveness) enableClobber() {
// The clobberdead experiment inserts code to clobber pointer slots in all
// the dead variables (locals and args) at every synchronous safepoint.
if !base.Flag.ClobberDead {
// Inserts code to clobber pointer slots in all the dead variables (locals and args)
// at every synchronous safepoint in b.
-func (lv *liveness) clobber(b *ssa.Block) {
+func (lv *Liveness) clobber(b *ssa.Block) {
// Copy block's values to a temporary.
oldSched := append([]*ssa.Value{}, b.Values...)
b.Values = b.Values[:0]
// clobber generates code to clobber pointer slots in all dead variables
// (those not marked in live). Clobbering instructions are added to the end
// of b.Values.
-func clobber(lv *liveness, b *ssa.Block, live bitvec.BitVec) {
+func clobber(lv *Liveness, b *ssa.Block, live bitvec.BitVec) {
for i, n := range lv.vars {
if !live.Get(int32(i)) && !n.Addrtaken() && !n.OpenDeferSlot() && !n.IsOutputParamHeapAddr() {
// Don't clobber stack objects (address-taken). They are
b.NewValue0IA(src.NoXPos, ssa.OpClobber, types.TypeVoid, offset, v)
}
-func (lv *liveness) showlive(v *ssa.Value, live bitvec.BitVec) {
+func (lv *Liveness) showlive(v *ssa.Value, live bitvec.BitVec) {
if base.Flag.Live == 0 || ir.FuncName(lv.fn) == "init" || strings.HasPrefix(ir.FuncName(lv.fn), ".") {
return
}
return
}
+ pos, s := lv.format(v, live)
+
+ base.WarnfAt(pos, "%s", s)
+}
+
+func (lv *Liveness) Format(v *ssa.Value) string {
+ if v == nil {
+ _, s := lv.format(nil, lv.stackMaps[0])
+ return s
+ }
+ if idx := lv.livenessMap.Get(v); idx.StackMapValid() {
+ _, s := lv.format(v, lv.stackMaps[idx])
+ return s
+ }
+ return ""
+}
+
+func (lv *Liveness) format(v *ssa.Value, live bitvec.BitVec) (src.XPos, string) {
pos := lv.fn.Nname.Pos()
if v != nil {
pos = v.Pos
for _, v := range names {
s += " " + v
}
-
- base.WarnfAt(pos, "%s", s)
+ return pos, s
}
-func (lv *liveness) printbvec(printed bool, name string, live bitvec.BitVec) bool {
+func (lv *Liveness) printbvec(printed bool, name string, live bitvec.BitVec) bool {
if live.IsEmpty() {
return printed
}
}
// printeffect is like printbvec, but for valueEffects.
-func (lv *liveness) printeffect(printed bool, name string, pos int32, x bool) bool {
+func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool) bool {
if !x {
return printed
}
// Prints the computed liveness information and inputs, for debugging.
// This format synthesizes the information used during the multiple passes
// into a single presentation.
-func (lv *liveness) printDebug() {
+func (lv *Liveness) printDebug() {
fmt.Printf("liveness: %s\n", ir.FuncName(lv.fn))
for i, b := range lv.f.Blocks {
// first word dumped is the total number of bitmaps. The second word is the
// length of the bitmaps. All bitmaps are assumed to be of equal length. The
// remaining bytes are the raw bitmaps.
-func (lv *liveness) emit() (argsSym, liveSym *obj.LSym) {
+func (lv *Liveness) emit() (argsSym, liveSym *obj.LSym) {
// Size args bitmaps to be just large enough to hold the largest pointer.
// First, find the largest Xoffset node we care about.
// (Nodes without pointers aren't in lv.vars; see ShouldTrack.)
// structure read by the garbage collector.
// Returns a map from GC safe points to their corresponding stack map index,
// and a map that contains all input parameters that may be partially live.
-func Compute(curfn *ir.Func, f *ssa.Func, stkptrsize int64, pp *objw.Progs) (Map, map[*ir.Name]bool) {
+func Compute(curfn *ir.Func, f *ssa.Func, stkptrsize int64, pp *objw.Progs, retLiveness bool) (Map, map[*ir.Name]bool, *Liveness) {
// Construct the global liveness state.
vars, idx := getvariables(curfn)
lv := newliveness(curfn, f, vars, idx, stkptrsize)
p.To.Sym = x
}
- return lv.livenessMap, lv.partLiveArgs
+ retLv := lv
+ if !retLiveness {
+ retLv = nil
+ }
+
+ return lv.livenessMap, lv.partLiveArgs, retLv
}
-func (lv *liveness) emitStackObjects() *obj.LSym {
+func (lv *Liveness) emitStackObjects() *obj.LSym {
var vars []*ir.Name
for _, n := range lv.fn.Dcl {
if shouldTrack(n) && n.Addrtaken() && n.Esc() != ir.EscHeap {
e := f.Frontend().(*ssafn)
- s.livenessMap, s.partLiveArgs = liveness.Compute(e.curfn, f, e.stkptrsize, pp)
+ gatherPrintInfo := f.PrintOrHtmlSSA || ssa.GenssaDump[f.Name]
+
+ var lv *liveness.Liveness
+ s.livenessMap, s.partLiveArgs, lv = liveness.Compute(e.curfn, f, e.stkptrsize, pp, gatherPrintInfo)
emitArgInfo(e, f, pp)
argLiveBlockMap, argLiveValueMap := liveness.ArgLiveness(e.curfn, f, pp)
var progToValue map[*obj.Prog]*ssa.Value
var progToBlock map[*obj.Prog]*ssa.Block
var valueToProgAfter []*obj.Prog // The first Prog following computation of a value v; v is visible at this point.
- gatherPrintInfo := f.PrintOrHtmlSSA || ssa.GenssaDump[f.Name]
if gatherPrintInfo {
progToValue = make(map[*obj.Prog]*ssa.Value, f.NumValues())
progToBlock = make(map[*obj.Prog]*ssa.Block, f.NumBlocks())
buf.WriteString("<code>")
buf.WriteString("<dl class=\"ssa-gen\">")
filename := ""
+
+ liveness := lv.Format(nil)
+ if liveness != "" {
+ buf.WriteString("<dt class=\"ssa-prog-src\"></dt><dd class=\"ssa-prog\">")
+ buf.WriteString(html.EscapeString("# " + liveness))
+ buf.WriteString("</dd>")
+ }
+
for p := s.pp.Text; p != nil; p = p.Link {
// Don't spam every line with the file name, which is often huge.
// Only print changes, and "unknown" is not a change.
buf.WriteString("<dt class=\"ssa-prog-src\">")
if v, ok := progToValue[p]; ok {
+
+ // Prefix calls with their liveness, if any
+ if p.As != obj.APCDATA {
+ if liveness := lv.Format(v); liveness != "" {
+ // Steal this line, and restart a line
+ buf.WriteString("</dt><dd class=\"ssa-prog\">")
+ buf.WriteString(html.EscapeString("# " + liveness))
+ buf.WriteString("</dd>")
+ // restarting a line
+ buf.WriteString("<dt class=\"ssa-prog-src\">")
+ }
+ }
+
buf.WriteString(v.HTML())
} else if b, ok := progToBlock[p]; ok {
buf.WriteString("<b>" + b.HTML() + "</b>")