]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: refactor liveness analysis for moving to SSA
authorMatthew Dempsky <mdempsky@google.com>
Thu, 9 Mar 2017 20:15:41 +0000 (12:15 -0800)
committerMatthew Dempsky <mdempsky@google.com>
Tue, 14 Mar 2017 22:55:22 +0000 (22:55 +0000)
In the SSA CFG, TEXT, RET, and JMP instructions correspond to Blocks,
not Values. Rework liveness analysis so that progeffects only cares
about Progs that result from Values, and handle Blocks separately.

Passes toolstash-check -all.

Change-Id: Ic23719c75b0421fdb51382a08dac18c3ba042b32
Reviewed-on: https://go-review.googlesource.com/38085
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
src/cmd/compile/internal/gc/plive.go

index d575076c7f13dd4f7bf6bddd3b9e351481d9ec1d..ec8de83c51d840e04789a62df1f2d483df71049b 100644 (file)
@@ -98,10 +98,9 @@ type Liveness struct {
 }
 
 type progeffectscache struct {
-       tailuevar    []int32
-       retuevar     []int32
-       textvarkill  []int32
        textavarinit []int32
+       retuevar     []int32
+       tailuevar    []int32
        uevar        [3]int32
        varkill      [3]int32
        avarinit     [3]int32
@@ -458,12 +457,12 @@ func (lv *Liveness) initcache() {
                        // all the parameters for correctness, and similarly it must not
                        // read the out arguments - they won't be set until the new
                        // function runs.
+
                        lv.cache.tailuevar = append(lv.cache.tailuevar, int32(i))
 
                        if node.Addrtaken() {
                                lv.cache.textavarinit = append(lv.cache.textavarinit, int32(i))
                        }
-                       lv.cache.textvarkill = append(lv.cache.textvarkill, int32(i))
 
                case PPARAMOUT:
                        // If the result had its address taken, it is being tracked
@@ -500,31 +499,11 @@ func (lv *Liveness) progeffects(prog *obj.Prog) (uevar, varkill, avarinit []int3
                return
        }
 
-       // A return instruction with a p.to is a tail return, which brings
-       // the stack pointer back up (if it ever went down) and then jumps
-       // to a new function entirely. That form of instruction must read
-       // all the parameters for correctness, and similarly it must not
-       // read the out arguments - they won't be set until the new
-       // function runs.
-       if (prog.As == obj.AJMP || prog.As == obj.ARET) && prog.To.Type == obj.TYPE_MEM && prog.To.Name == obj.NAME_EXTERN {
-               // This is a tail call. Ensure the arguments are still alive.
-               // See issue 16016.
-               return lv.cache.tailuevar, nil, nil
-       }
-
-       if prog.As == obj.ARET {
-               if prog.To.Type == obj.TYPE_NONE {
-                       return lv.cache.retuevar, nil, nil
-               }
+       switch prog.As {
+       case obj.ATEXT, obj.ARET, obj.AJMP, obj.AUNDEF:
                return nil, nil, nil
        }
 
-       if prog.As == obj.ATEXT {
-               // A text instruction marks the entry point to a function and
-               // the definition point of all in arguments.
-               return nil, lv.cache.textvarkill, lv.cache.textavarinit
-       }
-
        uevar = lv.cache.uevar[:0]
        varkill = lv.cache.varkill[:0]
        avarinit = lv.cache.avarinit[:0]
@@ -1020,17 +999,7 @@ func livenesssolve(lv *Liveness) {
        for change := true; change; {
                change = false
                for _, bb := range lv.cfg {
-                       any.Clear()
-                       all.Clear()
-                       for j, pred := range bb.pred {
-                               if j == 0 {
-                                       any.Copy(pred.avarinitany)
-                                       all.Copy(pred.avarinitall)
-                               } else {
-                                       any.Or(any, pred.avarinitany)
-                                       all.And(all, pred.avarinitall)
-                               }
-                       }
+                       lv.avarinitanyall(bb, any, all)
 
                        any.AndNot(any, bb.varkill)
                        all.AndNot(all, bb.varkill)
@@ -1060,13 +1029,34 @@ func livenesssolve(lv *Liveness) {
                for i := len(lv.cfg) - 1; i >= 0; i-- {
                        bb := lv.cfg[i]
 
-                       // 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.Clear()
-                       for _, succ := range bb.succ {
-                               newliveout.Or(newliveout, succ.livein)
+                       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)
+                               }
+                       } else {
+                               // 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)
+                               }
                        }
 
                        if !bb.liveout.Eq(newliveout) {
@@ -1128,18 +1118,7 @@ func livenessepilogue(lv *Liveness) {
                // Compute avarinitany and avarinitall for entry to block.
                // This duplicates information known during livenesssolve
                // but avoids storing two more vectors for each block.
-               any.Clear()
-               all.Clear()
-               for j := 0; j < len(bb.pred); j++ {
-                       pred := bb.pred[j]
-                       if j == 0 {
-                               any.Copy(pred.avarinitany)
-                               all.Copy(pred.avarinitall)
-                       } else {
-                               any.Or(any, pred.avarinitany)
-                               all.And(all, pred.avarinitall)
-                       }
-               }
+               lv.avarinitanyall(bb, any, all)
 
                // Walk forward through the basic block instructions and
                // allocate liveness maps for those instructions that need them.
@@ -1248,21 +1227,6 @@ func livenessepilogue(lv *Liveness) {
                                // Found an interesting instruction, record the
                                // corresponding liveness information.
 
-                               // Useful sanity check: on entry to the function,
-                               // the only things that can possibly be live are the
-                               // input parameters.
-                               if p.As == obj.ATEXT {
-                                       for j := int32(0); j < liveout.n; j++ {
-                                               if !liveout.Get(j) {
-                                                       continue
-                                               }
-                                               n := lv.vars[j]
-                                               if n.Class != PPARAM {
-                                                       Fatalf("internal error: %v %L recorded as live on entry, p.Pc=%v", Curfn.Func.Nname, n, p.Pc)
-                                               }
-                                       }
-                               }
-
                                // Record live variables.
                                live := lv.livevars[pos]
                                live.Or(live, liveout)
@@ -1344,6 +1308,34 @@ func livenessepilogue(lv *Liveness) {
        }
 
        flusherrors()
+
+       // Useful sanity check: on entry to the function,
+       // the only things that can possibly be live are the
+       // input parameters.
+       for j, n := range lv.vars {
+               if n.Class != PPARAM && lv.livevars[0].Get(int32(j)) {
+                       Fatalf("internal error: %v %L recorded as live on entry", Curfn.Func.Nname, n)
+               }
+       }
+}
+
+func (lv *Liveness) avarinitanyall(bb *BasicBlock, any, all bvec) {
+       if len(bb.pred) == 0 {
+               any.Clear()
+               all.Clear()
+               for _, pos := range lv.cache.textavarinit {
+                       any.Set(pos)
+                       all.Set(pos)
+               }
+               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)
+       }
 }
 
 // FNV-1 hash function constants.