]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: port liveness analysis to SSA
authorMatthew Dempsky <mdempsky@google.com>
Fri, 10 Mar 2017 02:32:17 +0000 (18:32 -0800)
committerMatthew Dempsky <mdempsky@google.com>
Mon, 20 Mar 2017 22:58:50 +0000 (22:58 +0000)
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: Keith Randall <khr@golang.org>
src/cmd/compile/fmt_test.go
src/cmd/compile/internal/gc/gsubr.go
src/cmd/compile/internal/gc/pgen.go
src/cmd/compile/internal/gc/plive.go
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/ssa/func.go

index 17affeb111fae78f85f71a85b40b329a86d52458..c4bee55dcaa6ccba886e84bed61a457642c5178e 100644 (file)
@@ -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":                               "",
index f1d6673b867059f8309559515404b7169f849d1d..3f3e23ed3d7389edf4df9f778bfdab9c746c246b 100644 (file)
@@ -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)
        }
index 1bde027679fa009335f46574b148a344a044360a..e588ab210c247cd8f279f49ce37d48153e2cd57a 100644 (file)
@@ -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
index bf3129cf2193d91a83e8cf70632b8d58817cc239..ddd617f5fc4000ca0837a11fc4439d7879d9de2b 100644 (file)
@@ -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
 }
index ac5afbbad3c1a748ada28762dd8c171b749266c0..2bddfca37d80cea5a9efcf02016d2238422f1dcb 100644 (file)
@@ -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
index 74a74f2fd41cb466ecb5fd63f7dddd32b50272fa..d37540e180c0acb2da89e1d1023883806af9651f 100644 (file)
@@ -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 {