Commit 325904fe authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: port liveness analysis to SSA

Passes toolstash-check -all.

Change-Id: I92c3c25d6c053f971f346f4fa3bbc76419b58183
Reviewed-on: https://go-review.googlesource.com/38087
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarKeith Randall <khr@golang.org>
parent ea8c7dae
......@@ -593,7 +593,6 @@ var knownFormats = map[string]string{
"*cmd/compile/internal/ssa.sparseTreeMapEntry %v": "",
"*cmd/internal/obj.Addr %v": "",
"*cmd/internal/obj.LSym %v": "",
"*cmd/internal/obj.Prog %p": "",
"*cmd/internal/obj.Prog %s": "",
"*cmd/internal/obj.Prog %v": "",
"*math/big.Int %#x": "",
......
......@@ -282,7 +282,7 @@ func Patch(p *obj.Prog, to *obj.Prog) {
// Gins inserts instruction as. f is from, t is to.
func Gins(as obj.As, f, t *Node) *obj.Prog {
switch as {
case obj.AVARKILL, obj.AVARLIVE, obj.AVARDEF, obj.ATEXT, obj.AFUNCDATA:
case obj.ATEXT, obj.AFUNCDATA:
default:
Fatalf("unhandled gins op %v", as)
}
......
......@@ -22,14 +22,17 @@ var makefuncdatasym_nsym int
func makefuncdatasym(nameprefix string, funcdatakind int64) *Sym {
sym := lookupN(nameprefix, makefuncdatasym_nsym)
makefuncdatasym_nsym++
pnod := newname(sym)
pnod.Class = PEXTERN
p := Gins(obj.AFUNCDATA, nil, pnod)
p := Prog(obj.AFUNCDATA)
Addrconst(&p.From, funcdatakind)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = Linksym(sym)
return sym
}
// gvardef inserts a VARDEF for n into the instruction stream.
// TODO(mdempsky): Update to reference OpVar{Def,Kill,Live} instead
// and move to plive.go.
// VARDEF is an annotation for the liveness analysis, marking a place
// where a complete initialization (definition) of a variable begins.
// Since the liveness analysis can see initialization of single-word
......@@ -85,55 +88,6 @@ func makefuncdatasym(nameprefix string, funcdatakind int64) *Sym {
// that its argument is certainly dead, for use when the liveness analysis
// would not otherwise be able to deduce that fact.
func gvardefx(n *Node, as obj.As) {
if n == nil {
Fatalf("gvardef nil")
}
if n.Op != ONAME {
Fatalf("gvardef %#v; %v", n.Op, n)
return
}
switch n.Class {
case PAUTO, PPARAM, PPARAMOUT:
if !n.Used() {
Prog(obj.ANOP)
return
}
if as == obj.AVARLIVE {
Gins(as, n, nil)
} else {
Gins(as, nil, n)
}
}
}
func Gvardef(n *Node) {
gvardefx(n, obj.AVARDEF)
}
func Gvarkill(n *Node) {
gvardefx(n, obj.AVARKILL)
}
func Gvarlive(n *Node) {
gvardefx(n, obj.AVARLIVE)
}
func removevardef(firstp *obj.Prog) {
for p := firstp; p != nil; p = p.Link {
for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL || p.Link.As == obj.AVARLIVE) {
p.Link = p.Link.Link
}
if p.To.Type == obj.TYPE_BRANCH {
for p.To.Val.(*obj.Prog) != nil && (p.To.Val.(*obj.Prog).As == obj.AVARDEF || p.To.Val.(*obj.Prog).As == obj.AVARKILL || p.To.Val.(*obj.Prog).As == obj.AVARLIVE) {
p.To.Val = p.To.Val.(*obj.Prog).Link
}
}
}
}
func emitptrargsmap() {
if Curfn.Func.Nname.Sym.Name == "_" {
return
......@@ -407,10 +361,7 @@ func compile(fn *Node) {
}
}
gcargs := makefuncdatasym("gcargs·", obj.FUNCDATA_ArgsPointerMaps)
gclocals := makefuncdatasym("gclocals·", obj.FUNCDATA_LocalsPointerMaps)
genssa(ssafn, ptxt, gcargs, gclocals)
genssa(ssafn, ptxt)
obj.Flushplist(Ctxt, plist) // convert from Prog list to machine code
ptxt = nil // nil to prevent misuse; Prog may have been freed by Flushplist
......
......@@ -9,49 +9,22 @@
//
// -live (aka -live=1): print liveness lists as code warnings at safe points
// -live=2: print an assembly listing with liveness annotations
// -live=3: print information during each computation phase (much chattier)
//
// Each level includes the earlier output as well.
package gc
import (
"cmd/compile/internal/ssa"
"cmd/internal/obj"
"cmd/internal/sys"
"crypto/md5"
"fmt"
"sort"
"strings"
)
// An ordinary basic block.
//
// Instructions are threaded together in a doubly-linked list. To iterate in
// program order follow the link pointer from the first node and stop after the
// last node has been visited
//
// for p = bb.first; ; p = p.link {
// ...
// if p == bb.last {
// break
// }
// }
//
// To iterate in reverse program order by following the opt pointer from the
// last node
//
// for p = bb.last; p != nil; p = p.opt {
// ...
// }
type BasicBlock struct {
pred []*BasicBlock // predecessors; if none, probably start of CFG
succ []*BasicBlock // successors; if none, probably ends in return statement
first *obj.Prog // first instruction in block
last *obj.Prog // last instruction in block
rpo int // reverse post-order number (also index in cfg)
lastbitmapindex int // for livenessepilogue
// Summary sets of block effects.
// BlockEffects summarizes the liveness effects on an SSA block.
type BlockEffects struct {
lastbitmapindex int // for livenessepilogue
// Computed during livenessprologue using only the content of
// individual blocks:
......@@ -80,11 +53,16 @@ type BasicBlock struct {
// A collection of global state used by liveness analysis.
type Liveness struct {
fn *Node
ptxt *obj.Prog
f *ssa.Func
vars []*Node
cfg []*BasicBlock
stkptrsize int64
be []BlockEffects
// stackMapIndex maps from safe points (i.e., CALLs) to their
// index within the stack maps.
stackMapIndex map[*ssa.Value]int
// An array with a bit vector for each safe point tracking
// live variables, indexed by bb.rpo.
livevars []bvec
......@@ -110,115 +88,6 @@ type ProgInfo struct {
Flags uint32 // flag bits
}
// Constructs a new basic block containing a single instruction.
func newblock(prog *obj.Prog) *BasicBlock {
if prog == nil {
Fatalf("newblock: prog cannot be nil")
}
// type block allows us to allocate a BasicBlock
// and its pred/succ slice together.
type block struct {
result BasicBlock
pred [2]*BasicBlock
succ [2]*BasicBlock
}
b := new(block)
result := &b.result
result.rpo = -1
result.first = prog
result.last = prog
result.pred = b.pred[:0]
result.succ = b.succ[:0]
return result
}
// Adds an edge between two basic blocks by making from a predecessor of to and
// to a successor of from.
func addedge(from *BasicBlock, to *BasicBlock) {
if from == nil {
Fatalf("addedge: from is nil")
}
if to == nil {
Fatalf("addedge: to is nil")
}
from.succ = append(from.succ, to)
to.pred = append(to.pred, from)
}
// Inserts prev before curr in the instruction
// stream. Any control flow, such as branches or fall-throughs, that target the
// existing instruction are adjusted to target the new instruction.
func splicebefore(lv *Liveness, bb *BasicBlock, prev *obj.Prog, curr *obj.Prog) {
// There may be other instructions pointing at curr,
// and we want them to now point at prev. Instead of
// trying to find all such instructions, swap the contents
// so that the problem becomes inserting next after curr.
// The "opt" field is the backward link in the linked list.
// Overwrite curr's data with prev, but keep the list links.
tmp := *curr
*curr = *prev
curr.Opt = tmp.Opt
curr.Link = tmp.Link
// Overwrite prev (now next) with curr's old data.
next := prev
*next = tmp
next.Opt = nil
next.Link = nil
// Now insert next after curr.
next.Link = curr.Link
next.Opt = curr
curr.Link = next
if next.Link != nil && next.Link.Opt == curr {
next.Link.Opt = next
}
if bb.last == curr {
bb.last = next
}
}
// A pretty printer for basic blocks.
func printblock(bb *BasicBlock) {
fmt.Printf("basic block %d\n", bb.rpo)
fmt.Printf("\tpred:")
for _, pred := range bb.pred {
fmt.Printf(" %d", pred.rpo)
}
fmt.Printf("\n")
fmt.Printf("\tsucc:")
for _, succ := range bb.succ {
fmt.Printf(" %d", succ.rpo)
}
fmt.Printf("\n")
fmt.Printf("\tprog:\n")
for prog := bb.first; ; prog = prog.Link {
fmt.Printf("\t\t%v\n", prog)
if prog == bb.last {
break
}
}
}
// Iterates over a basic block applying a callback to each instruction. There
// are two criteria for termination. If the end of basic block is reached a
// value of zero is returned. If the callback returns a non-zero value, the
// iteration is stopped and the value of the callback is returned.
func blockany(bb *BasicBlock, f func(*obj.Prog) bool) bool {
for p := bb.last; p != nil; p = p.Opt.(*obj.Prog) {
if f(p) {
return true
}
}
return false
}
// livenessShouldTrack reports whether the liveness analysis
// should track the variable n.
// We don't care about variables that have no pointers,
......@@ -256,175 +125,6 @@ func getvariables(fn *Node) []*Node {
return vars
}
// A pretty printer for control flow graphs. Takes a slice of *BasicBlocks.
func printcfg(cfg []*BasicBlock) {
for _, bb := range cfg {
printblock(bb)
}
}
// Comparison predicate used for sorting basic blocks by their rpo in ascending
// order.
type blockrpocmp []*BasicBlock
func (x blockrpocmp) Len() int { return len(x) }
func (x blockrpocmp) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x blockrpocmp) Less(i, j int) bool { return x[i].rpo < x[j].rpo }
// A pattern matcher for call instructions. Returns true when the instruction
// is a call to a specific package qualified function name.
func iscall(prog *obj.Prog, name *obj.LSym) bool {
if prog == nil {
Fatalf("iscall: prog is nil")
}
if name == nil {
Fatalf("iscall: function name is nil")
}
if prog.As != obj.ACALL {
return false
}
return name == prog.To.Sym
}
var isdeferreturn_sym *obj.LSym
func isdeferreturn(prog *obj.Prog) bool {
if isdeferreturn_sym == nil {
isdeferreturn_sym = Linksym(Pkglookup("deferreturn", Runtimepkg))
}
return iscall(prog, isdeferreturn_sym)
}
// Constructs a control flow graph from a sequence of instructions. This
// procedure is complicated by various sources of implicit control flow that are
// not accounted for using the standard cfg construction algorithm. Returns a
// slice of *BasicBlocks in control flow graph form (basic blocks ordered by
// their RPO number).
func newcfg(firstp *obj.Prog) []*BasicBlock {
// Reset the opt field of each prog to nil. In the first and second
// passes, instructions that are labels temporarily use the opt field to
// point to their basic block. In the third pass, the opt field reset
// to point to the predecessor of an instruction in its basic block.
for p := firstp; p != nil; p = p.Link {
p.Opt = nil
}
// Loop through all instructions identifying branch targets
// and fall-throughs and allocate basic blocks.
var cfg []*BasicBlock
bb := newblock(firstp)
cfg = append(cfg, bb)
for p := firstp; p != nil && p.As != obj.AEND; p = p.Link {
if p.To.Type == obj.TYPE_BRANCH {
if p.To.Val == nil {
Fatalf("prog branch to nil")
}
if p.To.Val.(*obj.Prog).Opt == nil {
p.To.Val.(*obj.Prog).Opt = newblock(p.To.Val.(*obj.Prog))
cfg = append(cfg, p.To.Val.(*obj.Prog).Opt.(*BasicBlock))
}
if p.As != obj.AJMP && p.Link != nil && p.Link.Opt == nil {
p.Link.Opt = newblock(p.Link)
cfg = append(cfg, p.Link.Opt.(*BasicBlock))
}
}
}
bb.rpo = 0
rpo := 1
for p := firstp; p != nil && p.As != obj.AEND; p = p.Link {
if p.Opt != nil {
p.Opt.(*BasicBlock).rpo = rpo
rpo++
}
}
if rpo != len(cfg) {
Fatalf("newcfg: inconsistent block counts: %d != %d", rpo, len(cfg))
}
// Loop through all basic blocks maximally growing the list of
// contained instructions until a label is reached. Add edges
// for branches and fall-through instructions.
for _, bb := range cfg {
for p := bb.last; p != nil && p.As != obj.AEND; p = p.Link {
if p.Opt != nil && p != bb.last {
break
}
bb.last = p
// Stop before an unreachable RET, to avoid creating
// unreachable control flow nodes.
if p.Link != nil && p.Link.As == obj.ARET && p.Link.Mode == 1 {
// TODO: remove after SSA is done. SSA does not
// generate any unreachable RET instructions.
break
}
}
if bb.last.To.Type == obj.TYPE_BRANCH {
addedge(bb, bb.last.To.Val.(*obj.Prog).Opt.(*BasicBlock))
}
if bb.last.Link != nil {
// Add a fall-through when the instruction is
// not an unconditional control transfer.
if bb.last.As != obj.AJMP && bb.last.As != obj.ARET && bb.last.As != obj.AUNDEF {
addedge(bb, bb.last.Link.Opt.(*BasicBlock))
}
}
}
// Add back links so the instructions in a basic block can be traversed
// backward. This is the final state of the instruction opt field.
for _, bb := range cfg {
p := bb.first
var prev *obj.Prog
for {
p.Opt = prev
if p == bb.last {
break
}
prev = p
p = p.Link
}
}
// Sort the basic blocks by their depth first number. The
// slice is now a depth-first spanning tree with the first
// node being the root.
sort.Sort(blockrpocmp(cfg))
// Unreachable control flow nodes are indicated by a -1 in the rpo
// field. If we see these nodes something must have gone wrong in an
// upstream compilation phase.
bb = cfg[0]
if bb.rpo == -1 {
fmt.Printf("newcfg: unreachable basic block for %v\n", bb.last)
printcfg(cfg)
Fatalf("newcfg: invalid control flow graph")
}
return cfg
}
// Frees a control flow graph (a slice of *BasicBlocks) and all of its leaf
// data structures.
func freecfg(cfg []*BasicBlock) {
if len(cfg) > 0 {
bb0 := cfg[0]
for p := bb0.first; p != nil; p = p.Link {
p.Opt = nil
}
}
}
// Returns true if the node names a variable that is otherwise uninteresting to
// the liveness computation.
func isfunny(n *Node) bool {
return n.Sym != nil && (n.Sym.Name == ".fp" || n.Sym.Name == ".args")
}
func (lv *Liveness) initcache() {
if lv.cache.initialized {
Fatalf("liveness cache initialized twice")
......@@ -463,112 +163,113 @@ func (lv *Liveness) initcache() {
}
}
// Computes the effects of an instruction on a set of
// variables. The vars argument is a slice of *Nodes.
// A liveEffect is a set of flags that describe an instruction's
// liveness effects on a variable.
//
// The output vectors give bits for variables:
// uevar - used by this instruction
// varkill - killed by this instruction
// The possible flags are:
// uevar - used by the instruction
// varkill - killed by the instruction
// for variables without address taken, means variable was set
// for variables with address taken, means variable was marked dead
// avarinit - initialized or referred to by this instruction,
// avarinit - initialized or referred to by the instruction,
// only for variables with address taken but not escaping to heap
//
// The avarinit output serves as a signal that the data has been
// initialized, because any use of a variable must come after its
// initialization.
func (lv *Liveness) progeffects(prog *obj.Prog) (uevar, varkill, avarinit []int32) {
if !lv.cache.initialized {
Fatalf("liveness progeffects cache not initialized")
return
}
type liveEffect int
switch prog.As {
case obj.ATEXT, obj.ARET, obj.AJMP, obj.AUNDEF:
return nil, nil, nil
const (
uevar liveEffect = 1 << iota
varkill
avarinit
)
// 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) (pos int32, effect liveEffect) {
n, e := affectedNode(v)
if e == 0 {
return -1, 0
}
uevar = lv.cache.uevar[:0]
varkill = lv.cache.varkill[:0]
avarinit = lv.cache.avarinit[:0]
info := thearch.Proginfo(prog)
if info.Flags&(LeftRead|LeftWrite|LeftAddr) != 0 {
from := &prog.From
if from.Node != nil && from.Sym != nil {
n := from.Node.(*Node)
if pos := liveIndex(n, lv.vars); pos >= 0 {
if n.Addrtaken() {
avarinit = append(avarinit, pos)
} else {
if info.Flags&(LeftRead|LeftAddr) != 0 {
uevar = append(uevar, pos)
}
if info.Flags&LeftWrite != 0 && !isfat(n.Type) {
varkill = append(varkill, pos)
}
}
}
}
pos = liveIndex(n, lv.vars)
if pos < 0 {
return -1, 0
}
if info.Flags&From3Read != 0 {
from := prog.From3
if from.Node != nil && from.Sym != nil {
n := from.Node.(*Node)
if pos := liveIndex(n, lv.vars); pos >= 0 {
if n.Addrtaken() {
avarinit = append(avarinit, pos)
} else {
uevar = append(uevar, pos)
}
}
if n.Addrtaken() {
if v.Op != ssa.OpVarKill {
effect |= avarinit
}
if v.Op == ssa.OpVarDef || v.Op == ssa.OpVarKill {
effect |= varkill
}
} else {
// Read is a read, obviously.
// Addr by itself is also implicitly a read.
//
// Addr|Write means that the address is being taken
// but only so that the instruction can write to the value.
// It is not a read.
if e&ssa.SymRead != 0 || e&(ssa.SymAddr|ssa.SymWrite) == ssa.SymAddr {
effect |= uevar
}
if e&ssa.SymWrite != 0 && (!isfat(n.Type) || v.Op == ssa.OpVarDef) {
effect |= varkill
}
}
if info.Flags&(RightRead|RightWrite|RightAddr) != 0 {
to := &prog.To
if to.Node != nil && to.Sym != nil {
n := to.Node.(*Node)
if pos := liveIndex(n, lv.vars); pos >= 0 {
if n.Addrtaken() {
if prog.As != obj.AVARKILL {
avarinit = append(avarinit, pos)
}
if prog.As == obj.AVARDEF || prog.As == obj.AVARKILL {
varkill = append(varkill, pos)
}
} else {
// RightRead is a read, obviously.
// RightAddr by itself is also implicitly a read.
//
// RightAddr|RightWrite means that the address is being taken
// but only so that the instruction can write to the value.
// It is not a read. It is equivalent to RightWrite except that
// having the RightAddr bit set keeps the registerizer from
// trying to substitute a register for the memory location.
if (info.Flags&RightRead != 0) || info.Flags&(RightAddr|RightWrite) == RightAddr {
uevar = append(uevar, pos)
}
if info.Flags&RightWrite != 0 {
if !isfat(n.Type) || prog.As == obj.AVARDEF {
varkill = append(varkill, pos)
}
}
}
}
}
return
}
// affectedNode returns the *Node affected by v
func affectedNode(v *ssa.Value) (*Node, ssa.SymEffect) {
// Special cases.
switch v.Op {
case ssa.OpLoadReg:
n, _ := AutoVar(v.Args[0])
return n, ssa.SymRead
case ssa.OpStoreReg:
n, _ := AutoVar(v)
return n, ssa.SymWrite
case ssa.OpVarLive:
return v.Aux.(*Node), ssa.SymRead
case ssa.OpVarDef, ssa.OpVarKill:
return v.Aux.(*Node), ssa.SymWrite
case ssa.OpKeepAlive:
n, _ := AutoVar(v.Args[0])
return n, ssa.SymRead
}
e := v.Op.SymEffect()
if e == 0 {
return nil, 0
}
var n *Node
switch a := v.Aux.(type) {
case nil, *ssa.ExternSymbol:
// ok, but no node
case *ssa.ArgSymbol:
n = a.Node.(*Node)
case *ssa.AutoSymbol:
n = a.Node.(*Node)
default:
Fatalf("weird aux: %s", v.LongString())
}
return uevar, varkill, avarinit
return n, e
}
// liveIndex returns the index of n in the set of tracked vars.
// If n is not a tracked var, liveIndex returns -1.
// If n is not a tracked var but should be tracked, liveIndex crashes.
func liveIndex(n *Node, vars []*Node) int32 {
if n.Name.Curfn != Curfn || !livenessShouldTrack(n) {
if n == nil || n.Name.Curfn != Curfn || !livenessShouldTrack(n) {
return -1
}
......@@ -585,187 +286,34 @@ func liveIndex(n *Node, vars []*Node) int32 {
// 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 *Node, ptxt *obj.Prog, cfg []*BasicBlock, vars []*Node, stkptrsize int64) *Liveness {
result := Liveness{
func newliveness(fn *Node, f *ssa.Func, vars []*Node, stkptrsize int64) *Liveness {
lv := &Liveness{
fn: fn,
ptxt: ptxt,
cfg: cfg,
f: f,
vars: vars,
stkptrsize: stkptrsize,
be: make([]BlockEffects, f.NumBlocks()),
}
nblocks := int32(len(cfg))
nblocks := int32(len(f.Blocks))
nvars := int32(len(vars))
bulk := bvbulkalloc(nvars, nblocks*7)
for _, bb := range cfg {
bb.uevar = bulk.next()
bb.varkill = bulk.next()
bb.livein = bulk.next()
bb.liveout = bulk.next()
bb.avarinit = bulk.next()
bb.avarinitany = bulk.next()
bb.avarinitall = bulk.next()
}
return &result
}
func (lv *Liveness) printeffects(p *obj.Prog, uevar, varkill, avarinit []int32) {
fmt.Printf("effects of %v\n", p)
fmt.Println("uevar:", lv.slice2bvec(uevar))
fmt.Println("varkill:", lv.slice2bvec(varkill))
fmt.Println("avarinit:", lv.slice2bvec(avarinit))
}
// Pretty print a variable node. Uses Pascal like conventions for pointers and
// addresses to avoid confusing the C like conventions used in the node variable
// names.
func printnode(node *Node) {
p := ""
if haspointers(node.Type) {
p = "^"
}
a := ""
if node.Addrtaken() {
a = "@"
}
fmt.Printf(" %v%s%s", node, p, a)
}
// Pretty print a list of variables. The vars argument is a slice of *Nodes.
func printvars(name string, bv bvec, vars []*Node) {
fmt.Printf("%s:", name)
for i, node := range vars {
if bv.Get(int32(i)) {
printnode(node)
}
}
fmt.Printf("\n")
}
func (lv *Liveness) slice2bvec(vars []int32) bvec {
bv := bvalloc(int32(len(lv.vars)))
for _, id := range vars {
bv.Set(id)
}
return bv
}
// Prints a basic block annotated with the information computed by liveness
// analysis.
func livenessprintblock(lv *Liveness, bb *BasicBlock) {
fmt.Printf("basic block %d\n", bb.rpo)
fmt.Printf("\tpred:")
for _, pred := range bb.pred {
fmt.Printf(" %d", pred.rpo)
}
fmt.Printf("\n")
fmt.Printf("\tsucc:")
for _, succ := range bb.succ {
fmt.Printf(" %d", succ.rpo)
}
fmt.Printf("\n")
printvars("\tuevar", bb.uevar, lv.vars)
printvars("\tvarkill", bb.varkill, lv.vars)
printvars("\tlivein", bb.livein, lv.vars)
printvars("\tliveout", bb.liveout, lv.vars)
printvars("\tavarinit", bb.avarinit, lv.vars)
printvars("\tavarinitany", bb.avarinitany, lv.vars)
printvars("\tavarinitall", bb.avarinitall, lv.vars)
fmt.Printf("\tprog:\n")
for prog := bb.first; ; prog = prog.Link {
fmt.Printf("\t\t%v", prog)
if prog.As == obj.APCDATA && prog.From.Offset == obj.PCDATA_StackMapIndex {
pos := int32(prog.To.Offset)
live := lv.livevars[pos]
fmt.Printf(" %s", live.String())
}
fmt.Printf("\n")
if prog == bb.last {
break
}
}
for _, b := range f.Blocks {
be := lv.blockEffects(b)
be.uevar = bulk.next()
be.varkill = bulk.next()
be.livein = bulk.next()
be.liveout = bulk.next()
be.avarinit = bulk.next()
be.avarinitany = bulk.next()
be.avarinitall = bulk.next()
}
return lv
}
// Prints a control flow graph annotated with any information computed by
// liveness analysis.
func livenessprintcfg(lv *Liveness) {
for _, bb := range lv.cfg {
livenessprintblock(lv, bb)
}
}
func checkauto(fn *Node, p *obj.Prog, n *Node) {
for _, ln := range fn.Func.Dcl {
if ln.Op == ONAME && ln.Class == PAUTO && ln == n {
return
}
}
if n == nil {
fmt.Printf("%v: checkauto %v: nil node in %v\n", p.Line(), Curfn, p)
return
}
fmt.Printf("checkauto %v: %v (%p; class=%d) not found in %p %v\n", funcSym(Curfn), n, n, n.Class, p, p)
for _, ln := range fn.Func.Dcl {
fmt.Printf("\t%v (%p; class=%d)\n", ln, ln, ln.Class)
}
Fatalf("checkauto: invariant lost")
}
func checkparam(fn *Node, p *obj.Prog, n *Node) {
if isfunny(n) {
return
}
for _, a := range fn.Func.Dcl {
if a.Op == ONAME && (a.Class == PPARAM || a.Class == PPARAMOUT) && a == n {
return
}
}
fmt.Printf("checkparam %v: %v (%p; class=%d) not found in %v\n", Curfn, n, n, n.Class, p)
for _, ln := range fn.Func.Dcl {
fmt.Printf("\t%v (%p; class=%d)\n", ln, ln, ln.Class)
}
Fatalf("checkparam: invariant lost")
}
func checkprog(fn *Node, p *obj.Prog) {
if p.From.Name == obj.NAME_AUTO {
checkauto(fn, p, p.From.Node.(*Node))
}
if p.From.Name == obj.NAME_PARAM {
checkparam(fn, p, p.From.Node.(*Node))
}
if p.To.Name == obj.NAME_AUTO {
checkauto(fn, p, p.To.Node.(*Node))
}
if p.To.Name == obj.NAME_PARAM {
checkparam(fn, p, p.To.Node.(*Node))
}
}
// Check instruction invariants. We assume that the nodes corresponding to the
// sources and destinations of memory operations will be declared in the
// function. This is not strictly true, as is the case for the so-called funny
// nodes and there are special cases to skip over that stuff. The analysis will
// fail if this invariant blindly changes.
func checkptxt(fn *Node, firstp *obj.Prog) {
if debuglive == 0 {
return
}
for p := firstp; p != nil; p = p.Link {
if false {
fmt.Printf("analyzing '%v'\n", p)
}
checkprog(fn, p)
}
func (lv *Liveness) blockEffects(b *ssa.Block) *BlockEffects {
return &lv.be[b.ID]
}
// NOTE: The bitmap for a specific type t should be cached in t after the first run
......@@ -890,30 +438,10 @@ func onebitlivepointermap(lv *Liveness, liveout bvec, vars []*Node, args bvec, l
}
}
// Construct a disembodied instruction.
func unlinkedprog(as obj.As) *obj.Prog {
p := Ctxt.NewProg()
Clearp(p)
p.As = as
return p
}
// Construct a new PCDATA instruction associated with and for the purposes of
// covering an existing instruction.
func newpcdataprog(prog *obj.Prog, index int32) *obj.Prog {
pcdata := unlinkedprog(obj.APCDATA)
pcdata.Pos = prog.Pos
pcdata.From.Type = obj.TYPE_CONST
pcdata.From.Offset = obj.PCDATA_StackMapIndex
pcdata.To.Type = obj.TYPE_CONST
pcdata.To.Offset = int64(index)
return pcdata
}
// Returns true for instructions that are safe points that must be annotated
// with liveness information.
func issafepoint(prog *obj.Prog) bool {
return prog.As == obj.ATEXT || prog.As == obj.ACALL
func issafepoint(v *ssa.Value) bool {
return v.Op.IsCall() || v.Op == ssa.OpARMCALLudiv
}
// Initializes the sets for solving the live variables. Visits all the
......@@ -922,38 +450,31 @@ func issafepoint(prog *obj.Prog) bool {
func livenessprologue(lv *Liveness) {
lv.initcache()
for _, bb := range lv.cfg {
for _, b := range lv.f.Blocks {
be := lv.blockEffects(b)
// Walk the block instructions backward and update the block
// effects with the each prog effects.
for p := bb.last; p != nil; p = p.Opt.(*obj.Prog) {
uevar, varkill, _ := lv.progeffects(p)
if debuglive >= 3 {
lv.printeffects(p, uevar, varkill, nil)
for j := len(b.Values) - 1; j >= 0; j-- {
pos, e := lv.valueEffects(b.Values[j])
if e&varkill != 0 {
be.varkill.Set(pos)
be.uevar.Unset(pos)
}
for _, pos := range varkill {
bb.varkill.Set(pos)
bb.uevar.Unset(pos)
}
for _, pos := range uevar {
bb.uevar.Set(pos)
if e&uevar != 0 {
be.uevar.Set(pos)
}
}
// Walk the block instructions forward to update avarinit bits.
// avarinit describes the effect at the end of the block, not the beginning.
for p := bb.first; ; p = p.Link {
_, varkill, avarinit := lv.progeffects(p)
if debuglive >= 3 {
lv.printeffects(p, nil, varkill, avarinit)
}
for _, pos := range varkill {
bb.avarinit.Unset(pos)
for j := 0; j < len(b.Values); j++ {
pos, e := lv.valueEffects(b.Values[j])
if e&varkill != 0 {
be.avarinit.Unset(pos)
}
for _, pos := range avarinit {
bb.avarinit.Set(pos)
}
if p == bb.last {
break
if e&avarinit != 0 {
be.avarinit.Set(pos)
}
}
}
......@@ -971,33 +492,41 @@ func livenesssolve(lv *Liveness) {
// Push avarinitall, avarinitany forward.
// avarinitall says the addressed var is initialized along all paths reaching the block exit.
// avarinitany says the addressed var is initialized along some path reaching the block exit.
for i, bb := range lv.cfg {
if i == 0 {
bb.avarinitall.Copy(bb.avarinit)
for _, b := range lv.f.Blocks {
be := lv.blockEffects(b)
if b == lv.f.Entry {
be.avarinitall.Copy(be.avarinit)
} else {
bb.avarinitall.Clear()
bb.avarinitall.Not()
be.avarinitall.Clear()
be.avarinitall.Not()
}
bb.avarinitany.Copy(bb.avarinit)
be.avarinitany.Copy(be.avarinit)
}
// Walk blocks in the general direction of propagation (RPO
// for avarinit{any,all}, and PO for live{in,out}). This
// improves convergence.
po := lv.f.Postorder()
for change := true; change; {
change = false
for _, bb := range lv.cfg {
lv.avarinitanyall(bb, any, all)
any.AndNot(any, bb.varkill)
all.AndNot(all, bb.varkill)
any.Or(any, bb.avarinit)
all.Or(all, bb.avarinit)
if !any.Eq(bb.avarinitany) {
for i := len(po) - 1; i >= 0; i-- {
b := po[i]
be := lv.blockEffects(b)
lv.avarinitanyall(b, any, all)
any.AndNot(any, be.varkill)
all.AndNot(all, be.varkill)
any.Or(any, be.avarinit)
all.Or(all, be.avarinit)
if !any.Eq(be.avarinitany) {
change = true
bb.avarinitany.Copy(any)
be.avarinitany.Copy(any)
}
if !all.Eq(bb.avarinitall) {
if !all.Eq(be.avarinitall) {
change = true
bb.avarinitall.Copy(all)
be.avarinitall.Copy(all)
}
}
}
......@@ -1008,45 +537,35 @@ func livenesssolve(lv *Liveness) {
for change := true; change; {
change = false
// Walk blocks in the general direction of propagation. This
// improves convergence.
for i := len(lv.cfg) - 1; i >= 0; i-- {
bb := lv.cfg[i]
for _, b := range po {
be := lv.blockEffects(b)
newliveout.Clear()
if len(bb.succ) == 0 {
switch prog := bb.last; {
case prog.As == obj.ARET && prog.To.Type == obj.TYPE_NONE:
// ssa.BlockRet
for _, pos := range lv.cache.retuevar {
newliveout.Set(pos)
}
case (prog.As == obj.AJMP || prog.As == obj.ARET) && prog.To.Type == obj.TYPE_MEM && prog.To.Name == obj.NAME_EXTERN:
// ssa.BlockRetJmp
for _, pos := range lv.cache.tailuevar {
newliveout.Set(pos)
}
case prog.As == obj.AUNDEF:
// ssa.BlockExit
// nothing to do
default:
Fatalf("unexpected terminal prog: %v", prog)
switch b.Kind {
case ssa.BlockRet:
for _, pos := range lv.cache.retuevar {
newliveout.Set(pos)
}
} else {
case ssa.BlockRetJmp:
for _, pos := range lv.cache.tailuevar {
newliveout.Set(pos)
}
case ssa.BlockExit:
// nothing to do
default:
// A variable is live on output from this block
// if it is live on input to some successor.
//
// out[b] = \bigcup_{s \in succ[b]} in[s]
newliveout.Copy(bb.succ[0].livein)
for _, succ := range bb.succ[1:] {
newliveout.Or(newliveout, succ.livein)
newliveout.Copy(lv.blockEffects(b.Succs[0].Block()).livein)
for _, succ := range b.Succs[1:] {
newliveout.Or(newliveout, lv.blockEffects(succ.Block()).livein)
}
}
if !bb.liveout.Eq(newliveout) {
if !be.liveout.Eq(newliveout) {
change = true
bb.liveout.Copy(newliveout)
be.liveout.Copy(newliveout)
}
// A variable is live on input to this block
......@@ -1054,8 +573,8 @@ func livenesssolve(lv *Liveness) {
// not set by the code in this block.
//
// in[b] = uevar[b] \cup (out[b] \setminus varkill[b])
newlivein.AndNot(bb.liveout, bb.varkill)
bb.livein.Or(newlivein, bb.uevar)
newlivein.AndNot(be.liveout, be.varkill)
be.livein.Or(newlivein, be.uevar)
}
}
}
......@@ -1099,201 +618,125 @@ func livenessepilogue(lv *Liveness) {
}
}
for _, bb := range lv.cfg {
{
// Reserve an entry for function entry.
live := bvalloc(nvars)
for _, pos := range lv.cache.textavarinit {
live.Set(pos)
}
lv.livevars = append(lv.livevars, live)
}
for _, b := range lv.f.Blocks {
be := lv.blockEffects(b)
// Compute avarinitany and avarinitall for entry to block.
// This duplicates information known during livenesssolve
// but avoids storing two more vectors for each block.
lv.avarinitanyall(bb, any, all)
lv.avarinitanyall(b, any, all)
// Walk forward through the basic block instructions and
// allocate liveness maps for those instructions that need them.
// Seed the maps with information about the addrtaken variables.
for p := bb.first; ; p = p.Link {
_, varkill, avarinit := lv.progeffects(p)
for _, pos := range varkill {
for _, v := range b.Values {
pos, e := lv.valueEffects(v)
if e&varkill != 0 {
any.Unset(pos)
all.Unset(pos)
}
for _, pos := range avarinit {
if e&avarinit != 0 {
any.Set(pos)
all.Set(pos)
}
if issafepoint(p) {
// Annotate ambiguously live variables so that they can
// be zeroed at function entry.
// livein and liveout are dead here and used as temporaries.
livein.Clear()
liveout.AndNot(any, all)
if !liveout.IsEmpty() {
for pos := int32(0); pos < liveout.n; pos++ {
if !liveout.Get(pos) {
continue
}
all.Set(pos) // silence future warnings in this block
n := lv.vars[pos]
if !n.Name.Needzero() {
n.Name.SetNeedzero(true)
if debuglive >= 1 {
Warnl(p.Pos, "%v: %L is ambiguously live", Curfn.Func.Nname, n)
}
}
}
}
// Allocate a bit vector for each class and facet of
// value we are tracking.
if !issafepoint(v) {
continue
}
// Live stuff first.
live := bvalloc(nvars)
live.Copy(any)
lv.livevars = append(lv.livevars, live)
// Annotate ambiguously live variables so that they can
// be zeroed at function entry.
// livein and liveout are dead here and used as temporaries.
livein.Clear()
if debuglive >= 3 {
fmt.Printf("%v\n", p)
printvars("avarinitany", any, lv.vars)
liveout.AndNot(any, all)
if !liveout.IsEmpty() {
for pos := int32(0); pos < liveout.n; pos++ {
if !liveout.Get(pos) {
continue
}
all.Set(pos) // silence future warnings in this block
n := lv.vars[pos]
if !n.Name.Needzero() {
n.Name.SetNeedzero(true)
if debuglive >= 1 {
Warnl(v.Pos, "%v: %L is ambiguously live", Curfn.Func.Nname, n)
}
}
}
}
if p == bb.last {
break
}
// Live stuff first.
live := bvalloc(nvars)
live.Copy(any)
lv.livevars = append(lv.livevars, live)
}
bb.lastbitmapindex = len(lv.livevars) - 1
be.lastbitmapindex = len(lv.livevars) - 1
}
var msg []string
var nmsg, startmsg int
for _, bb := range lv.cfg {
if debuglive >= 1 && Curfn.Func.Nname.Sym.Name != "init" && Curfn.Func.Nname.Sym.Name[0] != '.' {
nmsg = len(lv.livevars)
startmsg = nmsg
msg = make([]string, nmsg)
for j := 0; j < nmsg; j++ {
msg[j] = ""
}
}
for _, b := range lv.f.Blocks {
be := lv.blockEffects(b)
// walk backward, emit pcdata and populate the maps
pos := int32(bb.lastbitmapindex)
pos := int32(be.lastbitmapindex)
if pos < 0 {
// the first block we encounter should have the ATEXT so
// at no point should pos ever be less than zero.
Fatalf("livenessepilogue")
}
livein.Copy(bb.liveout)
var next *obj.Prog
for p := bb.last; p != nil; p = next {
next = p.Opt.(*obj.Prog) // splicebefore modifies p.opt
livein.Copy(be.liveout)
for i := len(b.Values) - 1; i >= 0; i-- {
v := b.Values[i]
// Propagate liveness information
uevar, varkill, _ := lv.progeffects(p)
liveout.Copy(livein)
for _, pos := range varkill {
livein.Unset(pos)
}
for _, pos := range uevar {
livein.Set(pos)
}
if debuglive >= 3 && issafepoint(p) {
fmt.Printf("%v\n", p)
printvars("uevar", lv.slice2bvec(uevar), lv.vars)
printvars("varkill", lv.slice2bvec(varkill), lv.vars)
printvars("livein", livein, lv.vars)
printvars("liveout", liveout, lv.vars)
}
if issafepoint(p) {
// Found an interesting instruction, record the
// corresponding liveness information.
// Record live variables.
live := lv.livevars[pos]
live.Or(live, liveout)
// Mark pparamout variables (as described above)
if p.As == obj.ACALL {
live.Or(live, livedefer)
{
pos, e := lv.valueEffects(v)
liveout.Copy(livein)
if e&varkill != 0 {
livein.Unset(pos)
}
if e&uevar != 0 {
livein.Set(pos)
}
}
// Show live variables.
if msg != nil {
fmt_ := fmt.Sprintf("%v: live at ", p.Line())
if p.As == obj.ACALL && p.To.Sym != nil {
name := p.To.Sym.Name
i := strings.Index(name, ".")
if i >= 0 {
name = name[i+1:]
}
fmt_ += fmt.Sprintf("call to %s:", name)
} else if p.As == obj.ACALL {
fmt_ += "indirect call:"
} else {
fmt_ += fmt.Sprintf("entry to %s:", ((p.From.Node).(*Node)).Sym.Name)
}
numlive := 0
for j, n := range lv.vars {
if live.Get(int32(j)) {
fmt_ += fmt.Sprintf(" %v", n)
numlive++
}
}
fmt_ += "\n"
if numlive == 0 { // squelch message
if !issafepoint(v) {
continue
}
} else {
startmsg--
msg[startmsg] = fmt_
}
}
// Found an interesting instruction, record the
// corresponding liveness information.
// Only CALL instructions need a PCDATA annotation.
// The TEXT instruction annotation is implicit.
if p.As == obj.ACALL {
before := p
if isdeferreturn(p) {
// runtime.deferreturn modifies its return address to return
// back to the CALL, not to the subsequent instruction.
// Because the return comes back one instruction early,
// the PCDATA must begin one instruction early too.
// The instruction before a call to deferreturn is always a
// no-op, to keep PC-specific data unambiguous.
before = p.Opt.(*obj.Prog)
if Ctxt.Arch.Family == sys.PPC64 {
// On ppc64 there is an additional instruction
// (another no-op or reload of toc pointer) before
// the call.
before = before.Opt.(*obj.Prog)
}
}
splicebefore(lv, bb, newpcdataprog(before, pos), before)
}
// Record live variables.
live := lv.livevars[pos]
live.Or(live, liveout)
live.Or(live, livedefer) // only for non-entry safe points
pos--
}
pos--
}
if msg != nil {
for j := startmsg; j < nmsg; j++ {
if msg[j] != "" {
fmt.Printf("%s", msg[j])
}
if b == lv.f.Entry {
if pos != 0 {
Fatalf("bad pos for entry point: %v", pos)
}
msg = nil
nmsg = 0
startmsg = 0
// Record live variables.
live := lv.livevars[pos]
live.Or(live, liveout)
}
}
flusherrors()
// Useful sanity check: on entry to the function,
// the only things that can possibly be live are the
// input parameters.
......@@ -1304,8 +747,8 @@ func livenessepilogue(lv *Liveness) {
}
}
func (lv *Liveness) avarinitanyall(bb *BasicBlock, any, all bvec) {
if len(bb.pred) == 0 {
func (lv *Liveness) avarinitanyall(b *ssa.Block, any, all bvec) {
if len(b.Preds) == 0 {
any.Clear()
all.Clear()
for _, pos := range lv.cache.textavarinit {
......@@ -1315,11 +758,14 @@ func (lv *Liveness) avarinitanyall(bb *BasicBlock, any, all bvec) {
return
}
any.Copy(bb.pred[0].avarinitany)
all.Copy(bb.pred[0].avarinitall)
for _, pred := range bb.pred[1:] {
any.Or(any, pred.avarinitany)
all.And(all, pred.avarinitall)
be := lv.blockEffects(b.Preds[0].Block())
any.Copy(be.avarinitany)
all.Copy(be.avarinitall)
for _, pred := range b.Preds[1:] {
be := lv.blockEffects(pred.Block())
any.Or(any, be.avarinitany)
all.And(all, be.avarinitall)
}
}
......@@ -1416,20 +862,59 @@ Outer:
lv.livevars = lv.livevars[:uniq]
// Rewrite PCDATA instructions to use new numbering.
for p := lv.ptxt; p != nil; p = p.Link {
if p.As == obj.APCDATA && p.From.Offset == obj.PCDATA_StackMapIndex {
i := p.To.Offset
if i >= 0 {
p.To.Offset = int64(remap[i])
lv.showlive(nil, lv.livevars[0])
pos := 1
lv.stackMapIndex = make(map[*ssa.Value]int)
for _, b := range lv.f.Blocks {
for _, v := range b.Values {
if issafepoint(v) {
lv.showlive(v, lv.livevars[remap[pos]])
lv.stackMapIndex[v] = int(remap[pos])
pos++
}
}
}
}
func printbitset(printed bool, name string, vars []*Node, bits bvec) bool {
func (lv *Liveness) showlive(v *ssa.Value, live bvec) {
if debuglive == 0 || Curfn.Func.Nname.Sym.Name == "init" || strings.HasPrefix(Curfn.Func.Nname.Sym.Name, ".") {
return
}
if live.IsEmpty() {
return
}
pos := Curfn.Func.Nname.Pos
if v != nil {
pos = v.Pos
}
s := "live at "
if v == nil {
s += fmt.Sprintf("entry to %s:", Curfn.Func.Nname.Sym.Name)
} else if sym, ok := v.Aux.(*obj.LSym); ok {
fn := sym.Name
if pos := strings.Index(fn, "."); pos >= 0 {
fn = fn[pos+1:]
}
s += fmt.Sprintf("call to %s:", fn)
} else {
s += "indirect call:"
}
for j, n := range lv.vars {
if live.Get(int32(j)) {
s += fmt.Sprintf(" %v", n)
}
}
Warnl(pos, s)
}
func (lv *Liveness) printbvec(printed bool, name string, live bvec) bool {
started := false
for i, n := range vars {
if !bits.Get(int32(i)) {
for i, n := range lv.vars {
if !live.Get(int32(i)) {
continue
}
if !started {
......@@ -1447,10 +932,23 @@ func printbitset(printed bool, name string, vars []*Node, bits bvec) bool {
fmt.Printf("%s", n.Sym.Name)
}
return printed
}
// printeffect is like printbvec, but for a single variable.
func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool) bool {
if !x {
return printed
}
if !printed {
fmt.Printf("\t")
} else {
fmt.Printf(" ")
}
fmt.Printf("%s=%s", name, lv.vars[pos].Sym.Name)
return true
}
// Prints the computed liveness information and inputs, for debugging.
// This format synthesizes the information used during the multiple passes
// into a single presentation.
......@@ -1458,83 +956,102 @@ func livenessprintdebug(lv *Liveness) {
fmt.Printf("liveness: %s\n", Curfn.Func.Nname.Sym.Name)
pcdata := 0
for i, bb := range lv.cfg {
for i, b := range lv.f.Blocks {
if i > 0 {
fmt.Printf("\n")
}
// bb#0 pred=1,2 succ=3,4
fmt.Printf("bb#%d pred=", i)
for j := 0; j < len(bb.pred); j++ {
fmt.Printf("bb#%d pred=", b.ID)
for j, pred := range b.Preds {
if j > 0 {
fmt.Printf(",")
}
fmt.Printf("%d", (bb.pred[j]).rpo)
fmt.Printf("%d", pred.Block().ID)
}
fmt.Printf(" succ=")
for j := 0; j < len(bb.succ); j++ {
for j, succ := range b.Succs {
if j > 0 {
fmt.Printf(",")
}
fmt.Printf("%d", (bb.succ[j]).rpo)
fmt.Printf("%d", succ.Block().ID)
}
fmt.Printf("\n")
// initial settings
var printed bool
be := lv.blockEffects(b)
printed = printbitset(printed, "uevar", lv.vars, bb.uevar)
printed = printbitset(printed, "livein", lv.vars, bb.livein)
// initial settings
printed := false
printed = lv.printbvec(printed, "uevar", be.uevar)
printed = lv.printbvec(printed, "livein", be.livein)
if printed {
fmt.Printf("\n")
}
// program listing, with individual effects listed
for p := bb.first; ; p = p.Link {
fmt.Printf("%v\n", p)
if p.As == obj.APCDATA && p.From.Offset == obj.PCDATA_StackMapIndex {
pcdata = int(p.To.Offset)
if b == lv.f.Entry {
live := lv.livevars[pcdata]
fmt.Printf("(%s) function entry\n", linestr(Curfn.Func.Nname.Pos))
fmt.Printf("\tlive=")
printed = false
for j, n := range lv.vars {
if !live.Get(int32(j)) {
continue
}
if printed {
fmt.Printf(",")
}
fmt.Printf("%v", n)
printed = true
}
fmt.Printf("\n")
}
for _, v := range b.Values {
fmt.Printf("(%s) %v\n", linestr(v.Pos), v.LongString())
if pos, ok := lv.stackMapIndex[v]; ok {
pcdata = pos
}
uevar, varkill, avarinit := lv.progeffects(p)
pos, effect := lv.valueEffects(v)
printed = false
printed = printbitset(printed, "uevar", lv.vars, lv.slice2bvec(uevar))
printed = printbitset(printed, "varkill", lv.vars, lv.slice2bvec(varkill))
printed = printbitset(printed, "avarinit", lv.vars, lv.slice2bvec(avarinit))
printed = lv.printeffect(printed, "uevar", pos, effect&uevar != 0)
printed = lv.printeffect(printed, "varkill", pos, effect&varkill != 0)
printed = lv.printeffect(printed, "avarinit", pos, effect&avarinit != 0)
if printed {
fmt.Printf("\n")
}
if issafepoint(p) {
live := lv.livevars[pcdata]
fmt.Printf("\tlive=")
printed = false
for j, n := range lv.vars {
if live.Get(int32(j)) {
if printed {
fmt.Printf(",")
}
fmt.Printf("%v", n)
printed = true
}
}
fmt.Printf("\n")
if !issafepoint(v) {
continue
}
if p == bb.last {
break
live := lv.livevars[pcdata]
fmt.Printf("\tlive=")
printed = false
for j, n := range lv.vars {
if !live.Get(int32(j)) {
continue
}
if printed {
fmt.Printf(",")
}
fmt.Printf("%v", n)
printed = true
}
fmt.Printf("\n")
}
// bb bitsets
fmt.Printf("end\n")
printed = printbitset(printed, "varkill", lv.vars, bb.varkill)
printed = printbitset(printed, "liveout", lv.vars, bb.liveout)
printed = printbitset(printed, "avarinit", lv.vars, bb.avarinit)
printed = printbitset(printed, "avarinitany", lv.vars, bb.avarinitany)
printed = printbitset(printed, "avarinitall", lv.vars, bb.avarinitall)
printed = false
printed = lv.printbvec(printed, "varkill", be.varkill)
printed = lv.printbvec(printed, "liveout", be.liveout)
printed = lv.printbvec(printed, "avarinit", be.avarinit)
printed = lv.printbvec(printed, "avarinitany", be.avarinitany)
printed = lv.printbvec(printed, "avarinitall", be.avarinitall)
if printed {
fmt.Printf("\n")
}
......@@ -1584,17 +1101,11 @@ func livenessemit(lv *Liveness, argssym, livesym *Sym) {
finishgclocals(argssym)
}
func printprog(p *obj.Prog) {
for p != nil {
fmt.Printf("%v\n", p)
p = p.Link
}
}
// Entry pointer for liveness analysis. Constructs a complete CFG, solves for
// the liveness of pointer variables in the function, and emits a runtime data
// Entry pointer for liveness analysis. Solves for the liveness of
// pointer variables in the function and emits a runtime data
// structure read by the garbage collector.
func liveness(e *ssafn, firstp *obj.Prog, argssym *Sym, livesym *Sym) {
// Returns a map from GC safe points to their corresponding stack map index.
func liveness(e *ssafn, f *ssa.Func, argssym, livesym *Sym) map[*ssa.Value]int {
// Change name to dump debugging information only for a specific function.
debugdelta := 0
......@@ -1603,38 +1114,16 @@ func liveness(e *ssafn, firstp *obj.Prog, argssym *Sym, livesym *Sym) {
}
debuglive += debugdelta
if debuglive >= 3 {
fmt.Printf("liveness: %s\n", e.curfn.Func.Nname.Sym.Name)
printprog(firstp)
}
checkptxt(e.curfn, firstp)
// Construct the global liveness state.
cfg := newcfg(firstp)
if debuglive >= 3 {
printcfg(cfg)
}
vars := getvariables(e.curfn)
lv := newliveness(e.curfn, firstp, cfg, vars, e.stkptrsize)
lv := newliveness(e.curfn, f, vars, e.stkptrsize)
// Run the dataflow framework.
livenessprologue(lv)
if debuglive >= 3 {
livenessprintcfg(lv)
}
livenesssolve(lv)
if debuglive >= 3 {
livenessprintcfg(lv)
}
livenessepilogue(lv)
if debuglive >= 3 {
livenessprintcfg(lv)
}
livenesscompact(lv)
if debuglive >= 2 {
livenessprintdebug(lv)
}
......@@ -1642,14 +1131,7 @@ func liveness(e *ssafn, firstp *obj.Prog, argssym *Sym, livesym *Sym) {
// Emit the live pointer map data structures
livenessemit(lv, argssym, livesym)
// Free everything.
for _, ln := range e.curfn.Func.Dcl {
if ln != nil {
ln.SetOpt(nil)
}
}
freecfg(cfg)
debuglive -= debugdelta
return lv.stackMapIndex
}
......@@ -4231,6 +4231,10 @@ type SSAGenState struct {
ScratchFpMem *Node
maxarg int64 // largest frame size for arguments to calls made by the function
// Map from GC safe points to stack map index, generated by
// liveness analysis.
stackMapIndex map[*ssa.Value]int
}
// Pc returns the current Prog.
......@@ -4244,12 +4248,16 @@ func (s *SSAGenState) SetPos(pos src.XPos) {
}
// genssa appends entries to ptxt for each instruction in f.
// gcargs and gclocals are filled in with pointer maps for the frame.
func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) {
func genssa(f *ssa.Func, ptxt *obj.Prog) {
var s SSAGenState
e := f.Frontend().(*ssafn)
// Generate GC bitmaps.
gcargs := makefuncdatasym("gcargs·", obj.FUNCDATA_ArgsPointerMaps)
gclocals := makefuncdatasym("gclocals·", obj.FUNCDATA_LocalsPointerMaps)
s.stackMapIndex = liveness(e, f, gcargs, gclocals)
// Remember where each block starts.
s.bstart = make([]*obj.Prog, f.NumBlocks())
......@@ -4291,14 +4299,8 @@ func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) {
case ssa.OpGetG:
// nothing to do when there's a g register,
// and checkLower complains if there's not
case ssa.OpVarDef:
Gvardef(v.Aux.(*Node))
case ssa.OpVarKill:
Gvarkill(v.Aux.(*Node))
case ssa.OpVarLive:
Gvarlive(v.Aux.(*Node))
case ssa.OpKeepAlive:
KeepAlive(v)
case ssa.OpVarDef, ssa.OpVarKill, ssa.OpVarLive, ssa.OpKeepAlive:
// nothing to do; already used by liveness
case ssa.OpPhi:
CheckLoweredPhi(v)
......@@ -4378,18 +4380,12 @@ func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) {
}
}
// Generate gc bitmaps.
liveness(e, ptxt, gcargs, gclocals)
// Add frame prologue. Zero ambiguously live variables.
thearch.Defframe(ptxt, e.curfn, e.stksize+s.maxarg)
if Debug['f'] != 0 {
frame(0)
}
// Remove leftover instrumentation from the instruction stream.
removevardef(ptxt)
f.HTMLWriter.Close()
f.HTMLWriter = nil
}
......@@ -4572,26 +4568,6 @@ func CheckLoweredGetClosurePtr(v *ssa.Value) {
}
}
// KeepAlive marks the variable referenced by OpKeepAlive as live.
// Called during ssaGenValue.
func KeepAlive(v *ssa.Value) {
if v.Op != ssa.OpKeepAlive {
v.Fatalf("KeepAlive called with non-KeepAlive value: %v", v.LongString())
}
if !v.Args[0].Type.IsPtrShaped() {
v.Fatalf("keeping non-pointer alive %v", v.Args[0])
}
n, _ := AutoVar(v.Args[0])
if n == nil {
v.Fatalf("KeepAlive with non-spilled value %s %s", v, v.Args[0])
}
// Note: KeepAlive arg may be a small part of a larger variable n. We keep the
// whole variable n alive at this point. (Typically, this happens when
// we are requested to keep the idata portion of an interface{} alive, and
// we end up keeping the whole interface{} alive. That's ok.)
Gvarlive(n)
}
// AutoVar returns a *Node and int64 representing the auto variable and offset within it
// where v should be spilled.
func AutoVar(v *ssa.Value) (*Node, int64) {
......@@ -4629,6 +4605,14 @@ func (s *SSAGenState) AddrScratch(a *obj.Addr) {
}
func (s *SSAGenState) Call(v *ssa.Value) *obj.Prog {
idx, ok := s.stackMapIndex[v]
if !ok {
Fatalf("missing stack map index for %v", v.LongString())
}
p := Prog(obj.APCDATA)
Addrconst(&p.From, obj.PCDATA_StackMapIndex)
Addrconst(&p.To, int64(idx))
if sym, _ := v.Aux.(*obj.LSym); sym == Deferreturn {
// Deferred calls will appear to be returning to
// the CALL deferreturn(SB) that we are about to emit.
......@@ -4641,7 +4625,7 @@ func (s *SSAGenState) Call(v *ssa.Value) *obj.Prog {
thearch.Ginsnop()
}
p := Prog(obj.ACALL)
p = Prog(obj.ACALL)
if sym, ok := v.Aux.(*obj.LSym); ok {
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
......
......@@ -497,6 +497,10 @@ func (f *Func) postorder() []*Block {
return f.cachedPostorder
}
func (f *Func) Postorder() []*Block {
return f.postorder()
}
// Idom returns a map from block ID to the immediate dominator of that block.
// f.Entry.ID maps to nil. Unreachable blocks map to nil as well.
func (f *Func) Idom() []*Block {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment