2015-02-13 14:40:36 -05:00
|
|
|
// Copyright 2013 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package gc
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"cmd/internal/obj"
|
|
|
|
|
"fmt"
|
|
|
|
|
"sort"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
UNVISITED = 0
|
|
|
|
|
VISITED = 1
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
succ []*BasicBlock
|
|
|
|
|
first *obj.Prog
|
|
|
|
|
last *obj.Prog
|
|
|
|
|
rpo int
|
|
|
|
|
mark int
|
|
|
|
|
lastbitmapindex int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A collection of global state used by liveness analysis.
|
|
|
|
|
type Liveness struct {
|
|
|
|
|
fn *Node
|
|
|
|
|
ptxt *obj.Prog
|
|
|
|
|
vars []*Node
|
|
|
|
|
cfg []*BasicBlock
|
|
|
|
|
uevar []*Bvec
|
|
|
|
|
varkill []*Bvec
|
|
|
|
|
livein []*Bvec
|
|
|
|
|
liveout []*Bvec
|
|
|
|
|
avarinit []*Bvec
|
|
|
|
|
avarinitany []*Bvec
|
|
|
|
|
avarinitall []*Bvec
|
|
|
|
|
argslivepointers []*Bvec
|
|
|
|
|
livepointers []*Bvec
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func xmalloc(size uint32) interface{} {
|
2015-02-23 16:07:24 -05:00
|
|
|
result := (interface{})(make([]byte, size))
|
2015-02-13 14:40:36 -05:00
|
|
|
if result == nil {
|
|
|
|
|
Fatal("malloc failed")
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Constructs a new basic block containing a single instruction.
|
|
|
|
|
func newblock(prog *obj.Prog) *BasicBlock {
|
|
|
|
|
if prog == nil {
|
|
|
|
|
Fatal("newblock: prog cannot be nil")
|
|
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
result := new(BasicBlock)
|
2015-02-13 14:40:36 -05:00
|
|
|
result.rpo = -1
|
|
|
|
|
result.mark = UNVISITED
|
|
|
|
|
result.first = prog
|
|
|
|
|
result.last = prog
|
|
|
|
|
result.pred = make([]*BasicBlock, 0, 2)
|
|
|
|
|
result.succ = make([]*BasicBlock, 0, 2)
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Frees a basic block and all of its leaf data structures.
|
|
|
|
|
func freeblock(bb *BasicBlock) {
|
|
|
|
|
if bb == nil {
|
|
|
|
|
Fatal("freeblock: cannot free nil")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 {
|
|
|
|
|
Fatal("addedge: from is nil")
|
|
|
|
|
}
|
|
|
|
|
if to == nil {
|
|
|
|
|
Fatal("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.
|
2015-02-23 16:07:24 -05:00
|
|
|
tmp := *curr
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
*curr = *prev
|
|
|
|
|
curr.Opt = tmp.Opt
|
|
|
|
|
curr.Link = tmp.Link
|
|
|
|
|
|
|
|
|
|
// Overwrite prev (now next) with curr's old data.
|
2015-02-23 16:07:24 -05:00
|
|
|
next := prev
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
*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) {
|
|
|
|
|
var pred *BasicBlock
|
|
|
|
|
|
|
|
|
|
fmt.Printf("basic block %d\n", bb.rpo)
|
|
|
|
|
fmt.Printf("\tpred:")
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := 0; i < len(bb.pred); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
pred = bb.pred[i]
|
|
|
|
|
fmt.Printf(" %d", pred.rpo)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
fmt.Printf("\tsucc:")
|
2015-02-23 16:07:24 -05:00
|
|
|
var succ *BasicBlock
|
|
|
|
|
for i := 0; i < len(bb.succ); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
succ = bb.succ[i]
|
|
|
|
|
fmt.Printf(" %d", succ.rpo)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
fmt.Printf("\tprog:\n")
|
2015-02-23 16:07:24 -05:00
|
|
|
for prog := bb.first; ; prog = prog.Link {
|
2015-02-13 14:40:36 -05:00
|
|
|
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.
|
2015-02-17 22:13:49 -05:00
|
|
|
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
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Collects and returns and array of Node*s for functions arguments and local
|
|
|
|
|
// variables.
|
|
|
|
|
func getvariables(fn *Node) []*Node {
|
2015-02-23 16:07:24 -05:00
|
|
|
result := make([]*Node, 0, 0)
|
|
|
|
|
for ll := fn.Dcl; ll != nil; ll = ll.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
if ll.N.Op == ONAME {
|
|
|
|
|
// In order for GODEBUG=gcdead=1 to work, each bitmap needs
|
|
|
|
|
// to contain information about all variables covered by the bitmap.
|
|
|
|
|
// For local variables, the bitmap only covers the stkptrsize
|
|
|
|
|
// bytes in the frame where variables containing pointers live.
|
|
|
|
|
// For arguments and results, the bitmap covers all variables,
|
|
|
|
|
// so we must include all the variables, even the ones without
|
|
|
|
|
// pointers.
|
|
|
|
|
//
|
|
|
|
|
// The Node.opt field is available for use by optimization passes.
|
|
|
|
|
// We use it to hold the index of the node in the variables array, plus 1
|
|
|
|
|
// (so that 0 means the Node is not in the variables array).
|
|
|
|
|
// Each pass should clear opt when done, but you never know,
|
|
|
|
|
// so clear them all ourselves too.
|
|
|
|
|
// The Node.curfn field is supposed to be set to the current function
|
|
|
|
|
// already, but for some compiler-introduced names it seems not to be,
|
|
|
|
|
// so fix that here.
|
|
|
|
|
// Later, when we want to find the index of a node in the variables list,
|
|
|
|
|
// we will check that n->curfn == curfn and n->opt > 0. Then n->opt - 1
|
|
|
|
|
// is the index in the variables list.
|
|
|
|
|
ll.N.Opt = nil
|
|
|
|
|
|
|
|
|
|
ll.N.Curfn = Curfn
|
|
|
|
|
switch ll.N.Class {
|
|
|
|
|
case PAUTO:
|
|
|
|
|
if haspointers(ll.N.Type) {
|
|
|
|
|
ll.N.Opt = int32(len(result))
|
|
|
|
|
result = append(result, ll.N)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case PPARAM,
|
|
|
|
|
PPARAMOUT:
|
|
|
|
|
ll.N.Opt = int32(len(result))
|
|
|
|
|
result = append(result, ll.N)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A pretty printer for control flow graphs. Takes an array of BasicBlock*s.
|
|
|
|
|
func printcfg(cfg []*BasicBlock) {
|
|
|
|
|
var bb *BasicBlock
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := int32(0); i < int32(len(cfg)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
bb = cfg[i]
|
|
|
|
|
printblock(bb)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Assigns a reverse post order number to each connected basic block using the
|
|
|
|
|
// standard algorithm. Unconnected blocks will not be affected.
|
|
|
|
|
func reversepostorder(root *BasicBlock, rpo *int32) {
|
|
|
|
|
var bb *BasicBlock
|
|
|
|
|
|
|
|
|
|
root.mark = VISITED
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := 0; i < len(root.succ); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
bb = root.succ[i]
|
|
|
|
|
if bb.mark == UNVISITED {
|
|
|
|
|
reversepostorder(bb, rpo)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*rpo -= 1
|
|
|
|
|
root.rpo = int(*rpo)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 {
|
|
|
|
|
Fatal("iscall: prog is nil")
|
|
|
|
|
}
|
|
|
|
|
if name == nil {
|
|
|
|
|
Fatal("iscall: function name is nil")
|
|
|
|
|
}
|
|
|
|
|
if prog.As != obj.ACALL {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return name == prog.To.Sym
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns true for instructions that call a runtime function implementing a
|
|
|
|
|
// select communication clause.
|
|
|
|
|
|
|
|
|
|
var isselectcommcasecall_names [5]*obj.LSym
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
func isselectcommcasecall(prog *obj.Prog) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
if isselectcommcasecall_names[0] == nil {
|
|
|
|
|
isselectcommcasecall_names[0] = Linksym(Pkglookup("selectsend", Runtimepkg))
|
|
|
|
|
isselectcommcasecall_names[1] = Linksym(Pkglookup("selectrecv", Runtimepkg))
|
|
|
|
|
isselectcommcasecall_names[2] = Linksym(Pkglookup("selectrecv2", Runtimepkg))
|
|
|
|
|
isselectcommcasecall_names[3] = Linksym(Pkglookup("selectdefault", Runtimepkg))
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := int32(0); isselectcommcasecall_names[i] != nil; i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
if iscall(prog, isselectcommcasecall_names[i]) {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns true for call instructions that target runtime·newselect.
|
|
|
|
|
|
|
|
|
|
var isnewselect_sym *obj.LSym
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
func isnewselect(prog *obj.Prog) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
if isnewselect_sym == nil {
|
|
|
|
|
isnewselect_sym = Linksym(Pkglookup("newselect", Runtimepkg))
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
return iscall(prog, isnewselect_sym)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns true for call instructions that target runtime·selectgo.
|
|
|
|
|
|
|
|
|
|
var isselectgocall_sym *obj.LSym
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
func isselectgocall(prog *obj.Prog) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
if isselectgocall_sym == nil {
|
|
|
|
|
isselectgocall_sym = Linksym(Pkglookup("selectgo", Runtimepkg))
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
return iscall(prog, isselectgocall_sym)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var isdeferreturn_sym *obj.LSym
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
func isdeferreturn(prog *obj.Prog) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
if isdeferreturn_sym == nil {
|
|
|
|
|
isdeferreturn_sym = Linksym(Pkglookup("deferreturn", Runtimepkg))
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
return iscall(prog, isdeferreturn_sym)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Walk backwards from a runtime·selectgo call up to its immediately dominating
|
|
|
|
|
// runtime·newselect call. Any successor nodes of communication clause nodes
|
|
|
|
|
// are implicit successors of the runtime·selectgo call node. The goal of this
|
|
|
|
|
// analysis is to add these missing edges to complete the control flow graph.
|
|
|
|
|
func addselectgosucc(selectgo *BasicBlock) {
|
|
|
|
|
var succ *BasicBlock
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
pred := selectgo
|
2015-02-13 14:40:36 -05:00
|
|
|
for {
|
|
|
|
|
if len(pred.pred) == 0 {
|
|
|
|
|
Fatal("selectgo does not have a newselect")
|
|
|
|
|
}
|
|
|
|
|
pred = pred.pred[0]
|
2015-02-17 22:13:49 -05:00
|
|
|
if blockany(pred, isselectcommcasecall) {
|
2015-02-13 14:40:36 -05:00
|
|
|
// A select comm case block should have exactly one
|
|
|
|
|
// successor.
|
|
|
|
|
if len(pred.succ) != 1 {
|
|
|
|
|
Fatal("select comm case has too many successors")
|
|
|
|
|
}
|
|
|
|
|
succ = pred.succ[0]
|
|
|
|
|
|
|
|
|
|
// Its successor should have exactly two successors.
|
|
|
|
|
// The drop through should flow to the selectgo block
|
|
|
|
|
// and the branch should lead to the select case
|
|
|
|
|
// statements block.
|
|
|
|
|
if len(succ.succ) != 2 {
|
|
|
|
|
Fatal("select comm case successor has too many successors")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add the block as a successor of the selectgo block.
|
|
|
|
|
addedge(selectgo, succ)
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if blockany(pred, isnewselect) {
|
2015-02-13 14:40:36 -05:00
|
|
|
// Reached the matching newselect.
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The entry point for the missing selectgo control flow algorithm. Takes an
|
|
|
|
|
// array of BasicBlock*s containing selectgo calls.
|
|
|
|
|
func fixselectgo(selectgo []*BasicBlock) {
|
|
|
|
|
var bb *BasicBlock
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := int32(0); i < int32(len(selectgo)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
bb = selectgo[i]
|
|
|
|
|
addselectgosucc(bb)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 an
|
|
|
|
|
// array of BasicBlock*s 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.
|
2015-02-23 16:07:24 -05:00
|
|
|
for p := firstp; p != nil; p = p.Link {
|
2015-02-13 14:40:36 -05:00
|
|
|
p.Opt = nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Allocate an array to remember where we have seen selectgo calls.
|
|
|
|
|
// These blocks will be revisited to add successor control flow edges.
|
2015-02-23 16:07:24 -05:00
|
|
|
selectgo := make([]*BasicBlock, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// Loop through all instructions identifying branch targets
|
|
|
|
|
// and fall-throughs and allocate basic blocks.
|
2015-02-23 16:07:24 -05:00
|
|
|
cfg := make([]*BasicBlock, 0, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
bb := newblock(firstp)
|
2015-02-13 14:40:36 -05:00
|
|
|
cfg = append(cfg, bb)
|
2015-02-23 16:07:24 -05:00
|
|
|
for p := firstp; p != nil; p = p.Link {
|
2015-02-13 14:40:36 -05:00
|
|
|
if p.To.Type == obj.TYPE_BRANCH {
|
|
|
|
|
if p.To.U.Branch == nil {
|
|
|
|
|
Fatal("prog branch to nil")
|
|
|
|
|
}
|
|
|
|
|
if p.To.U.Branch.Opt == nil {
|
|
|
|
|
p.To.U.Branch.Opt = newblock(p.To.U.Branch)
|
|
|
|
|
cfg = append(cfg, p.To.U.Branch.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))
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
} else if isselectcommcasecall(p) || isselectgocall(p) {
|
2015-02-13 14:40:36 -05:00
|
|
|
// Accommodate implicit selectgo control flow.
|
|
|
|
|
if p.Link.Opt == nil {
|
|
|
|
|
p.Link.Opt = newblock(p.Link)
|
|
|
|
|
cfg = append(cfg, p.Link.Opt.(*BasicBlock))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.
|
2015-02-23 16:07:24 -05:00
|
|
|
var p *obj.Prog
|
|
|
|
|
for i := int32(0); i < int32(len(cfg)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
bb = cfg[i]
|
|
|
|
|
for p = bb.last; p != nil; 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 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Collect basic blocks with selectgo calls.
|
2015-02-17 22:13:49 -05:00
|
|
|
if isselectgocall(p) {
|
2015-02-13 14:40:36 -05:00
|
|
|
selectgo = append(selectgo, bb)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if bb.last.To.Type == obj.TYPE_BRANCH {
|
|
|
|
|
addedge(bb, bb.last.To.U.Branch.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.
|
2015-02-23 16:07:24 -05:00
|
|
|
var prev *obj.Prog
|
|
|
|
|
for i := int32(0); i < int32(len(cfg)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
bb = cfg[i]
|
|
|
|
|
p = bb.first
|
|
|
|
|
prev = nil
|
|
|
|
|
for {
|
|
|
|
|
p.Opt = prev
|
|
|
|
|
if p == bb.last {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
prev = p
|
|
|
|
|
p = p.Link
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add missing successor edges to the selectgo blocks.
|
|
|
|
|
if len(selectgo) != 0 {
|
|
|
|
|
fixselectgo([]*BasicBlock(selectgo))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find a depth-first order and assign a depth-first number to
|
|
|
|
|
// all basic blocks.
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := int32(0); i < int32(len(cfg)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
bb = cfg[i]
|
|
|
|
|
bb.mark = UNVISITED
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bb = cfg[0]
|
2015-02-23 16:07:24 -05:00
|
|
|
rpo := int32(len(cfg))
|
2015-02-13 14:40:36 -05:00
|
|
|
reversepostorder(bb, &rpo)
|
|
|
|
|
|
|
|
|
|
// Sort the basic blocks by their depth first number. The
|
|
|
|
|
// array is now a depth-first spanning tree with the first
|
|
|
|
|
// node being the root.
|
|
|
|
|
sort.Sort(blockrpocmp(cfg))
|
|
|
|
|
|
|
|
|
|
bb = cfg[0]
|
|
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
if bb.rpo == -1 {
|
|
|
|
|
fmt.Printf("newcfg: unreachable basic block for %v\n", bb.last)
|
|
|
|
|
printcfg(cfg)
|
|
|
|
|
Fatal("newcfg: invalid control flow graph")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cfg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Frees a control flow graph (an array of BasicBlock*s) and all of its leaf
|
|
|
|
|
// data structures.
|
|
|
|
|
func freecfg(cfg []*BasicBlock) {
|
2015-02-23 16:07:24 -05:00
|
|
|
n := int32(len(cfg))
|
2015-02-13 14:40:36 -05:00
|
|
|
if n > 0 {
|
2015-02-23 16:07:24 -05:00
|
|
|
bb0 := cfg[0]
|
|
|
|
|
for p := bb0.first; p != nil; p = p.Link {
|
2015-02-13 14:40:36 -05:00
|
|
|
p.Opt = nil
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
var bb *BasicBlock
|
|
|
|
|
for i := int32(0); i < n; i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
bb = cfg[i]
|
|
|
|
|
freeblock(bb)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Computes the effects of an instruction on a set of
|
|
|
|
|
// variables. The vars argument is an array of Node*s.
|
|
|
|
|
//
|
|
|
|
|
// The output vectors give bits for variables:
|
|
|
|
|
// uevar - used by this instruction
|
|
|
|
|
// varkill - killed by this 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,
|
|
|
|
|
// 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 progeffects(prog *obj.Prog, vars []*Node, uevar *Bvec, varkill *Bvec, avarinit *Bvec) {
|
|
|
|
|
var info ProgInfo
|
|
|
|
|
|
|
|
|
|
bvresetall(uevar)
|
|
|
|
|
bvresetall(varkill)
|
|
|
|
|
bvresetall(avarinit)
|
|
|
|
|
|
|
|
|
|
Thearch.Proginfo(&info, prog)
|
|
|
|
|
if prog.As == obj.ARET {
|
|
|
|
|
// Return instructions implicitly read all the arguments. For
|
|
|
|
|
// the sake of correctness, out arguments must be read. For the
|
|
|
|
|
// sake of backtrace quality, we read in arguments as well.
|
|
|
|
|
//
|
|
|
|
|
// 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.
|
2015-02-23 16:07:24 -05:00
|
|
|
var node *Node
|
|
|
|
|
for i := int32(0); i < int32(len(vars)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
node = vars[i]
|
|
|
|
|
switch node.Class &^ PHEAP {
|
|
|
|
|
case PPARAM:
|
|
|
|
|
bvset(uevar, i)
|
|
|
|
|
|
|
|
|
|
// If the result had its address taken, it is being tracked
|
|
|
|
|
// by the avarinit code, which does not use uevar.
|
|
|
|
|
// If we added it to uevar too, we'd not see any kill
|
|
|
|
|
// and decide that the varible was live entry, which it is not.
|
|
|
|
|
// So only use uevar in the non-addrtaken case.
|
|
|
|
|
// The p->to.type == thearch.D_NONE limits the bvset to
|
|
|
|
|
// non-tail-call return instructions; see note above
|
|
|
|
|
// the for loop for details.
|
|
|
|
|
case PPARAMOUT:
|
2015-02-17 22:13:49 -05:00
|
|
|
if node.Addrtaken == 0 && prog.To.Type == obj.TYPE_NONE {
|
2015-02-13 14:40:36 -05:00
|
|
|
bvset(uevar, i)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if prog.As == obj.ATEXT {
|
|
|
|
|
// A text instruction marks the entry point to a function and
|
|
|
|
|
// the definition point of all in arguments.
|
2015-02-23 16:07:24 -05:00
|
|
|
var node *Node
|
|
|
|
|
for i := int32(0); i < int32(len(vars)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
node = vars[i]
|
|
|
|
|
switch node.Class &^ PHEAP {
|
|
|
|
|
case PPARAM:
|
|
|
|
|
if node.Addrtaken != 0 {
|
|
|
|
|
bvset(avarinit, i)
|
|
|
|
|
}
|
|
|
|
|
bvset(varkill, i)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if info.Flags&(LeftRead|LeftWrite|LeftAddr) != 0 {
|
2015-02-23 16:07:24 -05:00
|
|
|
from := &prog.From
|
2015-02-13 14:40:36 -05:00
|
|
|
if from.Node != nil && from.Sym != nil && ((from.Node).(*Node)).Curfn == Curfn {
|
|
|
|
|
switch ((from.Node).(*Node)).Class &^ PHEAP {
|
|
|
|
|
case PAUTO,
|
|
|
|
|
PPARAM,
|
|
|
|
|
PPARAMOUT:
|
|
|
|
|
pos, ok := from.Node.(*Node).Opt.(int32) // index in vars
|
|
|
|
|
if !ok {
|
|
|
|
|
goto Next
|
|
|
|
|
}
|
|
|
|
|
if pos >= int32(len(vars)) || vars[pos] != from.Node {
|
|
|
|
|
Fatal("bad bookkeeping in liveness %v %d", Nconv(from.Node.(*Node), 0), pos)
|
|
|
|
|
}
|
|
|
|
|
if ((from.Node).(*Node)).Addrtaken != 0 {
|
|
|
|
|
bvset(avarinit, pos)
|
|
|
|
|
} else {
|
|
|
|
|
if info.Flags&(LeftRead|LeftAddr) != 0 {
|
|
|
|
|
bvset(uevar, pos)
|
|
|
|
|
}
|
|
|
|
|
if info.Flags&LeftWrite != 0 {
|
2015-02-17 22:13:49 -05:00
|
|
|
if from.Node != nil && !Isfat(((from.Node).(*Node)).Type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
bvset(varkill, pos)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Next:
|
|
|
|
|
if info.Flags&(RightRead|RightWrite|RightAddr) != 0 {
|
2015-02-23 16:07:24 -05:00
|
|
|
to := &prog.To
|
2015-02-13 14:40:36 -05:00
|
|
|
if to.Node != nil && to.Sym != nil && ((to.Node).(*Node)).Curfn == Curfn {
|
|
|
|
|
switch ((to.Node).(*Node)).Class &^ PHEAP {
|
|
|
|
|
case PAUTO,
|
|
|
|
|
PPARAM,
|
|
|
|
|
PPARAMOUT:
|
|
|
|
|
pos, ok := to.Node.(*Node).Opt.(int32) // index in vars
|
|
|
|
|
if !ok {
|
|
|
|
|
goto Next1
|
|
|
|
|
}
|
|
|
|
|
if pos >= int32(len(vars)) || vars[pos] != to.Node {
|
|
|
|
|
Fatal("bad bookkeeping in liveness %v %d", Nconv(to.Node.(*Node), 0), pos)
|
|
|
|
|
}
|
|
|
|
|
if ((to.Node).(*Node)).Addrtaken != 0 {
|
|
|
|
|
if prog.As != obj.AVARKILL {
|
|
|
|
|
bvset(avarinit, pos)
|
|
|
|
|
}
|
|
|
|
|
if prog.As == obj.AVARDEF || prog.As == obj.AVARKILL {
|
|
|
|
|
bvset(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 {
|
|
|
|
|
bvset(uevar, pos)
|
|
|
|
|
}
|
|
|
|
|
if info.Flags&RightWrite != 0 {
|
2015-02-17 22:13:49 -05:00
|
|
|
if to.Node != nil && (!Isfat(((to.Node).(*Node)).Type) || prog.As == obj.AVARDEF) {
|
2015-02-13 14:40:36 -05:00
|
|
|
bvset(varkill, pos)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Next1:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Constructs a new liveness structure used to hold the global state of the
|
|
|
|
|
// liveness computation. The cfg argument is an array of BasicBlock*s and the
|
|
|
|
|
// vars argument is an array of Node*s.
|
|
|
|
|
func newliveness(fn *Node, ptxt *obj.Prog, cfg []*BasicBlock, vars []*Node) *Liveness {
|
2015-02-23 16:07:24 -05:00
|
|
|
result := new(Liveness)
|
2015-02-13 14:40:36 -05:00
|
|
|
result.fn = fn
|
|
|
|
|
result.ptxt = ptxt
|
|
|
|
|
result.cfg = cfg
|
|
|
|
|
result.vars = vars
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
nblocks := int32(len(cfg))
|
2015-02-13 14:40:36 -05:00
|
|
|
result.uevar = make([]*Bvec, nblocks)
|
|
|
|
|
result.varkill = make([]*Bvec, nblocks)
|
|
|
|
|
result.livein = make([]*Bvec, nblocks)
|
|
|
|
|
result.liveout = make([]*Bvec, nblocks)
|
|
|
|
|
result.avarinit = make([]*Bvec, nblocks)
|
|
|
|
|
result.avarinitany = make([]*Bvec, nblocks)
|
|
|
|
|
result.avarinitall = make([]*Bvec, nblocks)
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
nvars := int32(len(vars))
|
|
|
|
|
for i := int32(0); i < nblocks; i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
result.uevar[i] = bvalloc(nvars)
|
|
|
|
|
result.varkill[i] = bvalloc(nvars)
|
|
|
|
|
result.livein[i] = bvalloc(nvars)
|
|
|
|
|
result.liveout[i] = bvalloc(nvars)
|
|
|
|
|
result.avarinit[i] = bvalloc(nvars)
|
|
|
|
|
result.avarinitany[i] = bvalloc(nvars)
|
|
|
|
|
result.avarinitall[i] = bvalloc(nvars)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.livepointers = make([]*Bvec, 0, 0)
|
|
|
|
|
result.argslivepointers = make([]*Bvec, 0, 0)
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Frees the liveness structure and all of its leaf data structures.
|
|
|
|
|
func freeliveness(lv *Liveness) {
|
|
|
|
|
if lv == nil {
|
|
|
|
|
Fatal("freeliveness: cannot free nil")
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := int32(0); i < int32(len(lv.livepointers)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := int32(0); i < int32(len(lv.argslivepointers)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := int32(0); i < int32(len(lv.cfg)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func printeffects(p *obj.Prog, uevar *Bvec, varkill *Bvec, avarinit *Bvec) {
|
|
|
|
|
fmt.Printf("effects of %v", p)
|
|
|
|
|
fmt.Printf("\nuevar: ")
|
|
|
|
|
bvprint(uevar)
|
|
|
|
|
fmt.Printf("\nvarkill: ")
|
|
|
|
|
bvprint(varkill)
|
|
|
|
|
fmt.Printf("\navarinit: ")
|
|
|
|
|
bvprint(avarinit)
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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) {
|
2015-02-23 16:07:24 -05:00
|
|
|
p := ""
|
2015-02-13 14:40:36 -05:00
|
|
|
if haspointers(node.Type) {
|
|
|
|
|
p = "^"
|
|
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
a := ""
|
2015-02-13 14:40:36 -05:00
|
|
|
if node.Addrtaken != 0 {
|
|
|
|
|
a = "@"
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf(" %v%s%s", Nconv(node, 0), p, a)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pretty print a list of variables. The vars argument is an array of Node*s.
|
|
|
|
|
func printvars(name string, bv *Bvec, vars []*Node) {
|
|
|
|
|
fmt.Printf("%s:", name)
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := int32(0); i < int32(len(vars)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
if bvget(bv, i) != 0 {
|
|
|
|
|
printnode(vars[i])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Prints a basic block annotated with the information computed by liveness
|
|
|
|
|
// analysis.
|
|
|
|
|
func livenessprintblock(lv *Liveness, bb *BasicBlock) {
|
|
|
|
|
var pred *BasicBlock
|
|
|
|
|
|
|
|
|
|
fmt.Printf("basic block %d\n", bb.rpo)
|
|
|
|
|
|
|
|
|
|
fmt.Printf("\tpred:")
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := 0; i < len(bb.pred); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
pred = bb.pred[i]
|
|
|
|
|
fmt.Printf(" %d", pred.rpo)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
|
|
|
|
|
fmt.Printf("\tsucc:")
|
2015-02-23 16:07:24 -05:00
|
|
|
var succ *BasicBlock
|
|
|
|
|
for i := 0; i < len(bb.succ); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
succ = bb.succ[i]
|
|
|
|
|
fmt.Printf(" %d", succ.rpo)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
|
|
|
|
|
printvars("\tuevar", lv.uevar[bb.rpo], []*Node(lv.vars))
|
|
|
|
|
printvars("\tvarkill", lv.varkill[bb.rpo], []*Node(lv.vars))
|
|
|
|
|
printvars("\tlivein", lv.livein[bb.rpo], []*Node(lv.vars))
|
|
|
|
|
printvars("\tliveout", lv.liveout[bb.rpo], []*Node(lv.vars))
|
|
|
|
|
printvars("\tavarinit", lv.avarinit[bb.rpo], []*Node(lv.vars))
|
|
|
|
|
printvars("\tavarinitany", lv.avarinitany[bb.rpo], []*Node(lv.vars))
|
|
|
|
|
printvars("\tavarinitall", lv.avarinitall[bb.rpo], []*Node(lv.vars))
|
|
|
|
|
|
|
|
|
|
fmt.Printf("\tprog:\n")
|
2015-02-23 16:07:24 -05:00
|
|
|
var live *Bvec
|
|
|
|
|
var pos int32
|
|
|
|
|
for prog := bb.first; ; prog = prog.Link {
|
2015-02-13 14:40:36 -05:00
|
|
|
fmt.Printf("\t\t%v", prog)
|
|
|
|
|
if prog.As == obj.APCDATA && prog.From.Offset == obj.PCDATA_StackMapIndex {
|
|
|
|
|
pos = int32(prog.To.Offset)
|
|
|
|
|
live = lv.livepointers[pos]
|
|
|
|
|
fmt.Printf(" ")
|
|
|
|
|
bvprint(live)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
if prog == bb.last {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Prints a control flow graph annotated with any information computed by
|
|
|
|
|
// liveness analysis.
|
|
|
|
|
func livenessprintcfg(lv *Liveness) {
|
|
|
|
|
var bb *BasicBlock
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := int32(0); i < int32(len(lv.cfg)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
bb = lv.cfg[i]
|
|
|
|
|
livenessprintblock(lv, bb)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func checkauto(fn *Node, p *obj.Prog, n *Node) {
|
2015-02-23 16:07:24 -05:00
|
|
|
for l := fn.Dcl; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
if l.N.Op == ONAME && l.N.Class == PAUTO && l.N == n {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if n == nil {
|
|
|
|
|
fmt.Printf("%v: checkauto %v: nil node in %v\n", p.Line(), Nconv(Curfn, 0), p)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("checkauto %v: %v (%p; class=%d) not found in %v\n", Nconv(Curfn, 0), Nconv(n, 0), n, n.Class, p)
|
2015-02-23 16:07:24 -05:00
|
|
|
for l := fn.Dcl; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
fmt.Printf("\t%v (%p; class=%d)\n", Nconv(l.N, 0), l.N, l.N.Class)
|
|
|
|
|
}
|
|
|
|
|
Yyerror("checkauto: invariant lost")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func checkparam(fn *Node, p *obj.Prog, n *Node) {
|
|
|
|
|
if isfunny(n) {
|
|
|
|
|
return
|
|
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
var a *Node
|
|
|
|
|
var class int
|
|
|
|
|
for l := fn.Dcl; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
a = l.N
|
|
|
|
|
class = int(a.Class) &^ PHEAP
|
|
|
|
|
if a.Op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("checkparam %v: %v (%p; class=%d) not found in %v\n", Nconv(Curfn, 0), Nconv(n, 0), n, n.Class, p)
|
2015-02-23 16:07:24 -05:00
|
|
|
for l := fn.Dcl; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
fmt.Printf("\t%v (%p; class=%d)\n", Nconv(l.N, 0), l.N, l.N.Class)
|
|
|
|
|
}
|
|
|
|
|
Yyerror("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
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
for p := firstp; p != nil; p = p.Link {
|
2015-02-13 14:40:36 -05:00
|
|
|
if false {
|
|
|
|
|
fmt.Printf("analyzing '%v'\n", p)
|
|
|
|
|
}
|
|
|
|
|
if p.As != obj.ADATA && p.As != obj.AGLOBL && p.As != obj.ATYPE {
|
|
|
|
|
checkprog(fn, p)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NOTE: The bitmap for a specific type t should be cached in t after the first run
|
|
|
|
|
// and then simply copied into bv at the correct offset on future calls with
|
|
|
|
|
// the same type t. On https://rsc.googlecode.com/hg/testdata/slow.go, twobitwalktype1
|
|
|
|
|
// accounts for 40% of the 6g execution time.
|
|
|
|
|
func twobitwalktype1(t *Type, xoffset *int64, bv *Bvec) {
|
|
|
|
|
if t.Align > 0 && *xoffset&int64(t.Align-1) != 0 {
|
|
|
|
|
Fatal("twobitwalktype1: invalid initial alignment, %v", Tconv(t, 0))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch t.Etype {
|
|
|
|
|
case TINT8,
|
|
|
|
|
TUINT8,
|
|
|
|
|
TINT16,
|
|
|
|
|
TUINT16,
|
|
|
|
|
TINT32,
|
|
|
|
|
TUINT32,
|
|
|
|
|
TINT64,
|
|
|
|
|
TUINT64,
|
|
|
|
|
TINT,
|
|
|
|
|
TUINT,
|
|
|
|
|
TUINTPTR,
|
|
|
|
|
TBOOL,
|
|
|
|
|
TFLOAT32,
|
|
|
|
|
TFLOAT64,
|
|
|
|
|
TCOMPLEX64,
|
|
|
|
|
TCOMPLEX128:
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := int64(0); i < t.Width; i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
bvset(bv, int32(((*xoffset+i)/int64(Widthptr))*obj.BitsPerPointer)) // 1 = live scalar (BitsScalar)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*xoffset += t.Width
|
|
|
|
|
|
|
|
|
|
case TPTR32,
|
|
|
|
|
TPTR64,
|
|
|
|
|
TUNSAFEPTR,
|
|
|
|
|
TFUNC,
|
|
|
|
|
TCHAN,
|
|
|
|
|
TMAP:
|
|
|
|
|
if *xoffset&int64(Widthptr-1) != 0 {
|
|
|
|
|
Fatal("twobitwalktype1: invalid alignment, %v", Tconv(t, 0))
|
|
|
|
|
}
|
|
|
|
|
bvset(bv, int32((*xoffset/int64(Widthptr))*obj.BitsPerPointer+1)) // 2 = live ptr (BitsPointer)
|
|
|
|
|
*xoffset += t.Width
|
|
|
|
|
|
|
|
|
|
// struct { byte *str; intgo len; }
|
|
|
|
|
case TSTRING:
|
|
|
|
|
if *xoffset&int64(Widthptr-1) != 0 {
|
|
|
|
|
Fatal("twobitwalktype1: invalid alignment, %v", Tconv(t, 0))
|
|
|
|
|
}
|
|
|
|
|
bvset(bv, int32((*xoffset/int64(Widthptr))*obj.BitsPerPointer+1)) // 2 = live ptr in first slot (BitsPointer)
|
|
|
|
|
*xoffset += t.Width
|
|
|
|
|
|
|
|
|
|
// struct { Itab *tab; union { void *ptr, uintptr val } data; }
|
|
|
|
|
// or, when isnilinter(t)==true:
|
|
|
|
|
// struct { Type *type; union { void *ptr, uintptr val } data; }
|
|
|
|
|
case TINTER:
|
|
|
|
|
if *xoffset&int64(Widthptr-1) != 0 {
|
|
|
|
|
Fatal("twobitwalktype1: invalid alignment, %v", Tconv(t, 0))
|
|
|
|
|
}
|
|
|
|
|
bvset(bv, int32((*xoffset/int64(Widthptr))*obj.BitsPerPointer+1)) // 2 = live ptr in first slot (BitsPointer)
|
|
|
|
|
bvset(bv, int32((*xoffset/int64(Widthptr))*obj.BitsPerPointer+3)) // 2 = live ptr in second slot (BitsPointer)
|
|
|
|
|
*xoffset += t.Width
|
|
|
|
|
|
|
|
|
|
// The value of t->bound is -1 for slices types and >0 for
|
|
|
|
|
// for fixed array types. All other values are invalid.
|
|
|
|
|
case TARRAY:
|
|
|
|
|
if t.Bound < -1 {
|
|
|
|
|
Fatal("twobitwalktype1: invalid bound, %v", Tconv(t, 0))
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isslice(t) {
|
2015-02-13 14:40:36 -05:00
|
|
|
// struct { byte *array; uintgo len; uintgo cap; }
|
|
|
|
|
if *xoffset&int64(Widthptr-1) != 0 {
|
|
|
|
|
Fatal("twobitwalktype1: invalid TARRAY alignment, %v", Tconv(t, 0))
|
|
|
|
|
}
|
|
|
|
|
bvset(bv, int32((*xoffset/int64(Widthptr))*obj.BitsPerPointer+1)) // 2 = live ptr in first slot (BitsPointer)
|
|
|
|
|
*xoffset += t.Width
|
|
|
|
|
} else {
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := int64(0); i < t.Bound; i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
twobitwalktype1(t.Type, xoffset, bv)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case TSTRUCT:
|
2015-02-23 16:07:24 -05:00
|
|
|
o := int64(0)
|
|
|
|
|
var fieldoffset int64
|
|
|
|
|
for t1 := t.Type; t1 != nil; t1 = t1.Down {
|
2015-02-13 14:40:36 -05:00
|
|
|
fieldoffset = t1.Width
|
|
|
|
|
*xoffset += fieldoffset - o
|
|
|
|
|
twobitwalktype1(t1.Type, xoffset, bv)
|
|
|
|
|
o = fieldoffset + t1.Type.Width
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*xoffset += t.Width - o
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
Fatal("twobitwalktype1: unexpected type, %v", Tconv(t, 0))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns the number of words of local variables.
|
|
|
|
|
func localswords() int32 {
|
|
|
|
|
return int32(stkptrsize / int64(Widthptr))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns the number of words of in and out arguments.
|
|
|
|
|
func argswords() int32 {
|
|
|
|
|
return int32(Curfn.Type.Argwid / int64(Widthptr))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generates live pointer value maps for arguments and local variables. The
|
|
|
|
|
// this argument and the in arguments are always assumed live. The vars
|
|
|
|
|
// argument is an array of Node*s.
|
|
|
|
|
func twobitlivepointermap(lv *Liveness, liveout *Bvec, vars []*Node, args *Bvec, locals *Bvec) {
|
|
|
|
|
var node *Node
|
|
|
|
|
var xoffset int64
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := int32(0); ; i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
i = int32(bvnext(liveout, i))
|
2015-02-17 22:13:49 -05:00
|
|
|
if i < 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
node = vars[i]
|
|
|
|
|
switch node.Class {
|
|
|
|
|
case PAUTO:
|
|
|
|
|
xoffset = node.Xoffset + stkptrsize
|
|
|
|
|
twobitwalktype1(node.Type, &xoffset, locals)
|
|
|
|
|
|
|
|
|
|
case PPARAM,
|
|
|
|
|
PPARAMOUT:
|
|
|
|
|
xoffset = node.Xoffset
|
|
|
|
|
twobitwalktype1(node.Type, &xoffset, args)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The node list only contains declared names.
|
|
|
|
|
// If the receiver or arguments are unnamed, they will be omitted
|
|
|
|
|
// from the list above. Preserve those values - even though they are unused -
|
|
|
|
|
// in order to keep their addresses live for use in stack traces.
|
2015-02-23 16:07:24 -05:00
|
|
|
thisargtype := getthisx(lv.fn.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if thisargtype != nil {
|
|
|
|
|
xoffset = 0
|
|
|
|
|
twobitwalktype1(thisargtype, &xoffset, args)
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
inargtype := getinargx(lv.fn.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
if inargtype != nil {
|
|
|
|
|
xoffset = 0
|
|
|
|
|
twobitwalktype1(inargtype, &xoffset, args)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Construct a disembodied instruction.
|
|
|
|
|
func unlinkedprog(as int) *obj.Prog {
|
2015-02-23 16:07:24 -05:00
|
|
|
p := Ctxt.NewProg()
|
2015-02-13 14:40:36 -05:00
|
|
|
Clearp(p)
|
|
|
|
|
p.As = int16(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 {
|
|
|
|
|
var from Node
|
|
|
|
|
var to Node
|
|
|
|
|
|
|
|
|
|
Nodconst(&from, Types[TINT32], obj.PCDATA_StackMapIndex)
|
|
|
|
|
Nodconst(&to, Types[TINT32], int64(index))
|
2015-02-23 16:07:24 -05:00
|
|
|
pcdata := unlinkedprog(obj.APCDATA)
|
2015-02-13 14:40:36 -05:00
|
|
|
pcdata.Lineno = prog.Lineno
|
|
|
|
|
Naddr(&from, &pcdata.From, 0)
|
|
|
|
|
Naddr(&to, &pcdata.To, 0)
|
|
|
|
|
return pcdata
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns true for instructions that are safe points that must be annotated
|
|
|
|
|
// with liveness information.
|
2015-02-17 22:13:49 -05:00
|
|
|
func issafepoint(prog *obj.Prog) bool {
|
|
|
|
|
return prog.As == obj.ATEXT || prog.As == obj.ACALL
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initializes the sets for solving the live variables. Visits all the
|
|
|
|
|
// instructions in each basic block to summarizes the information at each basic
|
|
|
|
|
// block
|
|
|
|
|
func livenessprologue(lv *Liveness) {
|
|
|
|
|
var bb *BasicBlock
|
|
|
|
|
var p *obj.Prog
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
nvars := int32(len(lv.vars))
|
|
|
|
|
uevar := bvalloc(nvars)
|
|
|
|
|
varkill := bvalloc(nvars)
|
|
|
|
|
avarinit := bvalloc(nvars)
|
|
|
|
|
for i := int32(0); i < int32(len(lv.cfg)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
bb = lv.cfg[i]
|
|
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
|
progeffects(p, []*Node(lv.vars), uevar, varkill, avarinit)
|
|
|
|
|
if debuglive >= 3 {
|
|
|
|
|
printeffects(p, uevar, varkill, avarinit)
|
|
|
|
|
}
|
|
|
|
|
bvor(lv.varkill[i], lv.varkill[i], varkill)
|
|
|
|
|
bvandnot(lv.uevar[i], lv.uevar[i], varkill)
|
|
|
|
|
bvor(lv.uevar[i], lv.uevar[i], uevar)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Walk the block instructions forward to update avarinit bits.
|
|
|
|
|
// avarinit describes the effect at the end of the block, not the beginning.
|
|
|
|
|
bvresetall(varkill)
|
|
|
|
|
|
|
|
|
|
for p = bb.first; ; p = p.Link {
|
|
|
|
|
progeffects(p, []*Node(lv.vars), uevar, varkill, avarinit)
|
|
|
|
|
if debuglive >= 3 {
|
|
|
|
|
printeffects(p, uevar, varkill, avarinit)
|
|
|
|
|
}
|
|
|
|
|
bvandnot(lv.avarinit[i], lv.avarinit[i], varkill)
|
|
|
|
|
bvor(lv.avarinit[i], lv.avarinit[i], avarinit)
|
|
|
|
|
if p == bb.last {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Solve the liveness dataflow equations.
|
|
|
|
|
func livenesssolve(lv *Liveness) {
|
|
|
|
|
var bb *BasicBlock
|
|
|
|
|
var rpo int32
|
|
|
|
|
|
|
|
|
|
// These temporary bitvectors exist to avoid successive allocations and
|
|
|
|
|
// frees within the loop.
|
2015-02-23 16:07:24 -05:00
|
|
|
newlivein := bvalloc(int32(len(lv.vars)))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
newliveout := bvalloc(int32(len(lv.vars)))
|
|
|
|
|
any := bvalloc(int32(len(lv.vars)))
|
|
|
|
|
all := bvalloc(int32(len(lv.vars)))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// 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.
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := int32(0); i < int32(len(lv.cfg)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
bb = lv.cfg[i]
|
|
|
|
|
rpo = int32(bb.rpo)
|
|
|
|
|
if i == 0 {
|
|
|
|
|
bvcopy(lv.avarinitall[rpo], lv.avarinit[rpo])
|
|
|
|
|
} else {
|
|
|
|
|
bvresetall(lv.avarinitall[rpo])
|
|
|
|
|
bvnot(lv.avarinitall[rpo])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bvcopy(lv.avarinitany[rpo], lv.avarinit[rpo])
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
change := int32(1)
|
|
|
|
|
var j int32
|
|
|
|
|
var i int32
|
|
|
|
|
var pred *BasicBlock
|
2015-02-13 14:40:36 -05:00
|
|
|
for change != 0 {
|
|
|
|
|
change = 0
|
|
|
|
|
for i = 0; i < int32(len(lv.cfg)); i++ {
|
|
|
|
|
bb = lv.cfg[i]
|
|
|
|
|
rpo = int32(bb.rpo)
|
|
|
|
|
bvresetall(any)
|
|
|
|
|
bvresetall(all)
|
|
|
|
|
for j = 0; j < int32(len(bb.pred)); j++ {
|
|
|
|
|
pred = bb.pred[j]
|
|
|
|
|
if j == 0 {
|
|
|
|
|
bvcopy(any, lv.avarinitany[pred.rpo])
|
|
|
|
|
bvcopy(all, lv.avarinitall[pred.rpo])
|
|
|
|
|
} else {
|
|
|
|
|
bvor(any, any, lv.avarinitany[pred.rpo])
|
|
|
|
|
bvand(all, all, lv.avarinitall[pred.rpo])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bvandnot(any, any, lv.varkill[rpo])
|
|
|
|
|
bvandnot(all, all, lv.varkill[rpo])
|
|
|
|
|
bvor(any, any, lv.avarinit[rpo])
|
|
|
|
|
bvor(all, all, lv.avarinit[rpo])
|
|
|
|
|
if bvcmp(any, lv.avarinitany[rpo]) != 0 {
|
|
|
|
|
change = 1
|
|
|
|
|
bvcopy(lv.avarinitany[rpo], any)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if bvcmp(all, lv.avarinitall[rpo]) != 0 {
|
|
|
|
|
change = 1
|
|
|
|
|
bvcopy(lv.avarinitall[rpo], all)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Iterate through the blocks in reverse round-robin fashion. A work
|
|
|
|
|
// queue might be slightly faster. As is, the number of iterations is
|
|
|
|
|
// so low that it hardly seems to be worth the complexity.
|
|
|
|
|
change = 1
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
var succ *BasicBlock
|
2015-02-13 14:40:36 -05:00
|
|
|
for change != 0 {
|
|
|
|
|
change = 0
|
|
|
|
|
|
|
|
|
|
// Walk blocks in the general direction of propagation. This
|
|
|
|
|
// improves convergence.
|
|
|
|
|
for i = int32(len(lv.cfg)) - 1; i >= 0; 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]
|
|
|
|
|
bb = lv.cfg[i]
|
|
|
|
|
|
|
|
|
|
rpo = int32(bb.rpo)
|
|
|
|
|
bvresetall(newliveout)
|
|
|
|
|
for j = 0; j < int32(len(bb.succ)); j++ {
|
|
|
|
|
succ = bb.succ[j]
|
|
|
|
|
bvor(newliveout, newliveout, lv.livein[succ.rpo])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if bvcmp(lv.liveout[rpo], newliveout) != 0 {
|
|
|
|
|
change = 1
|
|
|
|
|
bvcopy(lv.liveout[rpo], newliveout)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A variable is live on input to this block
|
|
|
|
|
// if it is live on output from this block and
|
|
|
|
|
// not set by the code in this block.
|
|
|
|
|
//
|
|
|
|
|
// in[b] = uevar[b] \cup (out[b] \setminus varkill[b])
|
|
|
|
|
bvandnot(newlivein, lv.liveout[rpo], lv.varkill[rpo])
|
|
|
|
|
|
|
|
|
|
bvor(lv.livein[rpo], newlivein, lv.uevar[rpo])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This function is slow but it is only used for generating debug prints.
|
|
|
|
|
// Check whether n is marked live in args/locals.
|
2015-02-17 22:13:49 -05:00
|
|
|
func islive(n *Node, args *Bvec, locals *Bvec) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
switch n.Class {
|
|
|
|
|
case PPARAM,
|
|
|
|
|
PPARAMOUT:
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := 0; int64(i) < n.Type.Width/int64(Widthptr)*obj.BitsPerPointer; i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
if bvget(args, int32(n.Xoffset/int64(Widthptr)*obj.BitsPerPointer+int64(i))) != 0 {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case PAUTO:
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := 0; int64(i) < n.Type.Width/int64(Widthptr)*obj.BitsPerPointer; i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
if bvget(locals, int32((n.Xoffset+stkptrsize)/int64(Widthptr)*obj.BitsPerPointer+int64(i))) != 0 {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Visits all instructions in a basic block and computes a bit vector of live
|
|
|
|
|
// variables at each safe point locations.
|
|
|
|
|
func livenessepilogue(lv *Liveness) {
|
|
|
|
|
var bb *BasicBlock
|
|
|
|
|
var pred *BasicBlock
|
|
|
|
|
var args *Bvec
|
|
|
|
|
var locals *Bvec
|
|
|
|
|
var n *Node
|
|
|
|
|
var p *obj.Prog
|
|
|
|
|
var j int32
|
|
|
|
|
var pos int32
|
|
|
|
|
var xoffset int64
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
nvars := int32(len(lv.vars))
|
|
|
|
|
livein := bvalloc(nvars)
|
|
|
|
|
liveout := bvalloc(nvars)
|
|
|
|
|
uevar := bvalloc(nvars)
|
|
|
|
|
varkill := bvalloc(nvars)
|
|
|
|
|
avarinit := bvalloc(nvars)
|
|
|
|
|
any := bvalloc(nvars)
|
|
|
|
|
all := bvalloc(nvars)
|
|
|
|
|
ambig := bvalloc(localswords() * obj.BitsPerPointer)
|
|
|
|
|
msg := []string(nil)
|
|
|
|
|
nmsg := int32(0)
|
|
|
|
|
startmsg := int32(0)
|
|
|
|
|
|
|
|
|
|
for i := int32(0); i < int32(len(lv.cfg)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
bb = lv.cfg[i]
|
|
|
|
|
|
|
|
|
|
// Compute avarinitany and avarinitall for entry to block.
|
|
|
|
|
// This duplicates information known during livenesssolve
|
|
|
|
|
// but avoids storing two more vectors for each block.
|
|
|
|
|
bvresetall(any)
|
|
|
|
|
|
|
|
|
|
bvresetall(all)
|
|
|
|
|
for j = 0; j < int32(len(bb.pred)); j++ {
|
|
|
|
|
pred = bb.pred[j]
|
|
|
|
|
if j == 0 {
|
|
|
|
|
bvcopy(any, lv.avarinitany[pred.rpo])
|
|
|
|
|
bvcopy(all, lv.avarinitall[pred.rpo])
|
|
|
|
|
} else {
|
|
|
|
|
bvor(any, any, lv.avarinitany[pred.rpo])
|
|
|
|
|
bvand(all, all, lv.avarinitall[pred.rpo])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 {
|
|
|
|
|
progeffects(p, []*Node(lv.vars), uevar, varkill, avarinit)
|
|
|
|
|
bvandnot(any, any, varkill)
|
|
|
|
|
bvandnot(all, all, varkill)
|
|
|
|
|
bvor(any, any, avarinit)
|
|
|
|
|
bvor(all, all, avarinit)
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if issafepoint(p) {
|
2015-02-13 14:40:36 -05:00
|
|
|
// Annotate ambiguously live variables so that they can
|
|
|
|
|
// be zeroed at function entry.
|
|
|
|
|
// livein and liveout are dead here and used as temporaries.
|
|
|
|
|
bvresetall(livein)
|
|
|
|
|
|
|
|
|
|
bvandnot(liveout, any, all)
|
2015-02-17 22:13:49 -05:00
|
|
|
if !bvisempty(liveout) {
|
2015-02-13 14:40:36 -05:00
|
|
|
for pos = 0; pos < liveout.n; pos++ {
|
2015-02-17 22:13:49 -05:00
|
|
|
if bvget(liveout, pos) == 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
bvset(all, pos) // silence future warnings in this block
|
|
|
|
|
n = lv.vars[pos]
|
2015-02-17 22:13:49 -05:00
|
|
|
if n.Needzero == 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
n.Needzero = 1
|
|
|
|
|
if debuglive >= 1 {
|
|
|
|
|
Warnl(int(p.Lineno), "%v: %v is ambiguously live", Nconv(Curfn.Nname, 0), Nconv(n, obj.FmtLong))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Record in 'ambiguous' bitmap.
|
|
|
|
|
xoffset = n.Xoffset + stkptrsize
|
|
|
|
|
|
|
|
|
|
twobitwalktype1(n.Type, &xoffset, ambig)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Allocate a bit vector for each class and facet of
|
|
|
|
|
// value we are tracking.
|
|
|
|
|
|
|
|
|
|
// Live stuff first.
|
|
|
|
|
args = bvalloc(argswords() * obj.BitsPerPointer)
|
|
|
|
|
|
|
|
|
|
lv.argslivepointers = append(lv.argslivepointers, args)
|
|
|
|
|
locals = bvalloc(localswords() * obj.BitsPerPointer)
|
|
|
|
|
lv.livepointers = append(lv.livepointers, locals)
|
|
|
|
|
|
|
|
|
|
if debuglive >= 3 {
|
|
|
|
|
fmt.Printf("%v\n", p)
|
|
|
|
|
printvars("avarinitany", any, lv.vars)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Record any values with an "address taken" reaching
|
|
|
|
|
// this code position as live. Must do now instead of below
|
|
|
|
|
// because the any/all calculation requires walking forward
|
|
|
|
|
// over the block (as this loop does), while the liveout
|
|
|
|
|
// requires walking backward (as the next loop does).
|
|
|
|
|
twobitlivepointermap(lv, any, lv.vars, args, locals)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if p == bb.last {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bb.lastbitmapindex = len(lv.livepointers) - 1
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
var fmt_ string
|
|
|
|
|
var next *obj.Prog
|
|
|
|
|
var numlive int32
|
|
|
|
|
for i := int32(0); i < int32(len(lv.cfg)); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
bb = lv.cfg[i]
|
|
|
|
|
|
|
|
|
|
if debuglive >= 1 && Curfn.Nname.Sym.Name != "init" && Curfn.Nname.Sym.Name[0] != '.' {
|
|
|
|
|
nmsg = int32(len(lv.livepointers))
|
|
|
|
|
startmsg = nmsg
|
|
|
|
|
msg = make([]string, nmsg)
|
|
|
|
|
for j = 0; j < nmsg; j++ {
|
|
|
|
|
msg[j] = ""
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// walk backward, emit pcdata and populate the maps
|
|
|
|
|
pos = int32(bb.lastbitmapindex)
|
|
|
|
|
|
|
|
|
|
if pos < 0 {
|
|
|
|
|
// the first block we encounter should have the ATEXT so
|
|
|
|
|
// at no point should pos ever be less than zero.
|
|
|
|
|
Fatal("livenessepilogue")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bvcopy(livein, lv.liveout[bb.rpo])
|
|
|
|
|
for p = bb.last; p != nil; p = next {
|
|
|
|
|
next = p.Opt.(*obj.Prog) // splicebefore modifies p->opt
|
|
|
|
|
|
|
|
|
|
// Propagate liveness information
|
|
|
|
|
progeffects(p, lv.vars, uevar, varkill, avarinit)
|
|
|
|
|
|
|
|
|
|
bvcopy(liveout, livein)
|
|
|
|
|
bvandnot(livein, liveout, varkill)
|
|
|
|
|
bvor(livein, livein, uevar)
|
2015-02-17 22:13:49 -05:00
|
|
|
if debuglive >= 3 && issafepoint(p) {
|
2015-02-13 14:40:36 -05:00
|
|
|
fmt.Printf("%v\n", p)
|
|
|
|
|
printvars("uevar", uevar, lv.vars)
|
|
|
|
|
printvars("varkill", varkill, lv.vars)
|
|
|
|
|
printvars("livein", livein, lv.vars)
|
|
|
|
|
printvars("liveout", liveout, lv.vars)
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if issafepoint(p) {
|
2015-02-13 14:40:36 -05:00
|
|
|
// 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 = 0; j < liveout.n; j++ {
|
2015-02-17 22:13:49 -05:00
|
|
|
if bvget(liveout, j) == 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
n = lv.vars[j]
|
|
|
|
|
if n.Class != PPARAM {
|
|
|
|
|
yyerrorl(int(p.Lineno), "internal error: %v %v recorded as live on entry", Nconv(Curfn.Nname, 0), Nconv(n, obj.FmtLong))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Record live pointers.
|
|
|
|
|
args = lv.argslivepointers[pos]
|
|
|
|
|
|
|
|
|
|
locals = lv.livepointers[pos]
|
|
|
|
|
twobitlivepointermap(lv, liveout, lv.vars, args, locals)
|
|
|
|
|
|
|
|
|
|
// Ambiguously live variables are zeroed immediately after
|
|
|
|
|
// function entry. Mark them live for all the non-entry bitmaps
|
|
|
|
|
// so that GODEBUG=gcdead=1 mode does not poison them.
|
|
|
|
|
if p.As == obj.ACALL {
|
|
|
|
|
bvor(locals, locals, ambig)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Show live pointer bitmaps.
|
|
|
|
|
// We're interpreting the args and locals bitmap instead of liveout so that we
|
|
|
|
|
// include the bits added by the avarinit logic in the
|
|
|
|
|
// previous loop.
|
|
|
|
|
if msg != nil {
|
|
|
|
|
fmt_ = ""
|
|
|
|
|
fmt_ += fmt.Sprintf("%v: live at ", p.Line())
|
|
|
|
|
if p.As == obj.ACALL && p.To.Node != nil {
|
|
|
|
|
fmt_ += fmt.Sprintf("call to %s:", ((p.To.Node).(*Node)).Sym.Name)
|
|
|
|
|
} else if p.As == obj.ACALL {
|
|
|
|
|
fmt_ += fmt.Sprintf("indirect call:")
|
|
|
|
|
} else {
|
|
|
|
|
fmt_ += fmt.Sprintf("entry to %s:", ((p.From.Node).(*Node)).Sym.Name)
|
|
|
|
|
}
|
|
|
|
|
numlive = 0
|
|
|
|
|
for j = 0; j < int32(len(lv.vars)); j++ {
|
|
|
|
|
n = lv.vars[j]
|
2015-02-17 22:13:49 -05:00
|
|
|
if islive(n, args, locals) {
|
2015-02-13 14:40:36 -05:00
|
|
|
fmt_ += fmt.Sprintf(" %v", Nconv(n, 0))
|
|
|
|
|
numlive++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt_ += fmt.Sprintf("\n")
|
|
|
|
|
if numlive == 0 { // squelch message
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
startmsg--
|
|
|
|
|
msg[startmsg] = fmt_
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only CALL instructions need a PCDATA annotation.
|
|
|
|
|
// The TEXT instruction annotation is implicit.
|
|
|
|
|
if p.As == obj.ACALL {
|
2015-02-17 22:13:49 -05:00
|
|
|
if isdeferreturn(p) {
|
2015-02-13 14:40:36 -05:00
|
|
|
// 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.
|
|
|
|
|
splicebefore(lv, bb, newpcdataprog(p.Opt.(*obj.Prog), pos), p.Opt.(*obj.Prog))
|
|
|
|
|
} else {
|
|
|
|
|
splicebefore(lv, bb, newpcdataprog(p, pos), p)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pos--
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if msg != nil {
|
|
|
|
|
for j = startmsg; j < nmsg; j++ {
|
|
|
|
|
if msg[j] != "" {
|
|
|
|
|
fmt.Printf("%s", msg[j])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
msg = nil
|
|
|
|
|
nmsg = 0
|
|
|
|
|
startmsg = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Flusherrors()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FNV-1 hash function constants.
|
|
|
|
|
const (
|
|
|
|
|
H0 = 2166136261
|
|
|
|
|
Hp = 16777619
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func hashbitmap(h uint32, bv *Bvec) uint32 {
|
|
|
|
|
var w uint32
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
n := int((bv.n + 31) / 32)
|
|
|
|
|
for i := 0; i < n; i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
w = bv.b[i]
|
|
|
|
|
h = (h * Hp) ^ (w & 0xff)
|
|
|
|
|
h = (h * Hp) ^ ((w >> 8) & 0xff)
|
|
|
|
|
h = (h * Hp) ^ ((w >> 16) & 0xff)
|
|
|
|
|
h = (h * Hp) ^ ((w >> 24) & 0xff)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return h
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compact liveness information by coalescing identical per-call-site bitmaps.
|
|
|
|
|
// The merging only happens for a single function, not across the entire binary.
|
|
|
|
|
//
|
|
|
|
|
// There are actually two lists of bitmaps, one list for the local variables and one
|
|
|
|
|
// list for the function arguments. Both lists are indexed by the same PCDATA
|
|
|
|
|
// index, so the corresponding pairs must be considered together when
|
|
|
|
|
// merging duplicates. The argument bitmaps change much less often during
|
|
|
|
|
// function execution than the local variable bitmaps, so it is possible that
|
|
|
|
|
// we could introduce a separate PCDATA index for arguments vs locals and
|
|
|
|
|
// then compact the set of argument bitmaps separately from the set of
|
|
|
|
|
// local variable bitmaps. As of 2014-04-02, doing this to the godoc binary
|
|
|
|
|
// is actually a net loss: we save about 50k of argument bitmaps but the new
|
|
|
|
|
// PCDATA tables cost about 100k. So for now we keep using a single index for
|
|
|
|
|
// both bitmap lists.
|
|
|
|
|
func livenesscompact(lv *Liveness) {
|
|
|
|
|
// Linear probing hash table of bitmaps seen so far.
|
|
|
|
|
// The hash table has 4n entries to keep the linear
|
|
|
|
|
// scan short. An entry of -1 indicates an empty slot.
|
2015-02-23 16:07:24 -05:00
|
|
|
n := len(lv.livepointers)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
tablesize := 4 * n
|
|
|
|
|
table := make([]int, tablesize)
|
2015-02-13 14:40:36 -05:00
|
|
|
for i := range table {
|
|
|
|
|
table[i] = -1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// remap[i] = the new index of the old bit vector #i.
|
2015-02-23 16:07:24 -05:00
|
|
|
remap := make([]int, n)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
for i := range remap {
|
|
|
|
|
remap[i] = -1
|
|
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
uniq := 0 // unique tables found so far
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// Consider bit vectors in turn.
|
|
|
|
|
// If new, assign next number using uniq,
|
|
|
|
|
// record in remap, record in lv->livepointers and lv->argslivepointers
|
|
|
|
|
// under the new index, and add entry to hash table.
|
|
|
|
|
// If already seen, record earlier index in remap and free bitmaps.
|
2015-02-23 16:07:24 -05:00
|
|
|
var jarg *Bvec
|
|
|
|
|
var j int
|
|
|
|
|
var h uint32
|
|
|
|
|
var arg *Bvec
|
|
|
|
|
var jlocal *Bvec
|
|
|
|
|
var local *Bvec
|
|
|
|
|
for i := 0; i < n; i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
local = lv.livepointers[i]
|
|
|
|
|
arg = lv.argslivepointers[i]
|
|
|
|
|
h = hashbitmap(hashbitmap(H0, local), arg) % uint32(tablesize)
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
j = table[h]
|
|
|
|
|
if j < 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
jlocal = lv.livepointers[j]
|
|
|
|
|
jarg = lv.argslivepointers[j]
|
|
|
|
|
if bvcmp(local, jlocal) == 0 && bvcmp(arg, jarg) == 0 {
|
|
|
|
|
remap[i] = j
|
|
|
|
|
goto Next
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h++
|
|
|
|
|
if h == uint32(tablesize) {
|
|
|
|
|
h = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table[h] = uniq
|
|
|
|
|
remap[i] = uniq
|
|
|
|
|
lv.livepointers[uniq] = local
|
|
|
|
|
lv.argslivepointers[uniq] = arg
|
|
|
|
|
uniq++
|
|
|
|
|
Next:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We've already reordered lv->livepointers[0:uniq]
|
|
|
|
|
// and lv->argslivepointers[0:uniq] and freed the bitmaps
|
|
|
|
|
// we don't need anymore. Clear the pointers later in the
|
|
|
|
|
// array so that we can tell where the coalesced bitmaps stop
|
|
|
|
|
// and so that we don't double-free when cleaning up.
|
2015-02-23 16:07:24 -05:00
|
|
|
for j := uniq; j < n; j++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
lv.livepointers[j] = nil
|
|
|
|
|
lv.argslivepointers[j] = nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rewrite PCDATA instructions to use new numbering.
|
2015-02-23 16:07:24 -05:00
|
|
|
var i int
|
|
|
|
|
for p := lv.ptxt; p != nil; p = p.Link {
|
2015-02-13 14:40:36 -05:00
|
|
|
if p.As == obj.APCDATA && p.From.Offset == obj.PCDATA_StackMapIndex {
|
|
|
|
|
i = int(p.To.Offset)
|
|
|
|
|
if i >= 0 {
|
|
|
|
|
p.To.Offset = int64(remap[i])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func printbitset(printed int, name string, vars []*Node, bits *Bvec) int {
|
|
|
|
|
var n *Node
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
started := 0
|
|
|
|
|
for i := 0; i < len(vars); i++ {
|
2015-02-17 22:13:49 -05:00
|
|
|
if bvget(bits, int32(i)) == 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
continue
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
if started == 0 {
|
|
|
|
|
if printed == 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
fmt.Printf("\t")
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Printf(" ")
|
|
|
|
|
}
|
|
|
|
|
started = 1
|
|
|
|
|
printed = 1
|
|
|
|
|
fmt.Printf("%s=", name)
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Printf(",")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n = vars[i]
|
|
|
|
|
fmt.Printf("%s", n.Sym.Name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return printed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Prints the computed liveness information and inputs, for debugging.
|
|
|
|
|
// This format synthesizes the information used during the multiple passes
|
|
|
|
|
// into a single presentation.
|
|
|
|
|
func livenessprintdebug(lv *Liveness) {
|
|
|
|
|
var j int
|
|
|
|
|
var printed int
|
|
|
|
|
var bb *BasicBlock
|
|
|
|
|
var p *obj.Prog
|
|
|
|
|
var args *Bvec
|
|
|
|
|
var locals *Bvec
|
|
|
|
|
var n *Node
|
|
|
|
|
|
|
|
|
|
fmt.Printf("liveness: %s\n", Curfn.Nname.Sym.Name)
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
uevar := bvalloc(int32(len(lv.vars)))
|
|
|
|
|
varkill := bvalloc(int32(len(lv.vars)))
|
|
|
|
|
avarinit := bvalloc(int32(len(lv.vars)))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
pcdata := 0
|
|
|
|
|
for i := 0; i < len(lv.cfg); i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
if i > 0 {
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
}
|
|
|
|
|
bb = lv.cfg[i]
|
|
|
|
|
|
|
|
|
|
// bb#0 pred=1,2 succ=3,4
|
|
|
|
|
fmt.Printf("bb#%d pred=", i)
|
|
|
|
|
|
|
|
|
|
for j = 0; j < len(bb.pred); j++ {
|
|
|
|
|
if j > 0 {
|
|
|
|
|
fmt.Printf(",")
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("%d", (bb.pred[j]).rpo)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf(" succ=")
|
|
|
|
|
for j = 0; j < len(bb.succ); j++ {
|
|
|
|
|
if j > 0 {
|
|
|
|
|
fmt.Printf(",")
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("%d", (bb.succ[j]).rpo)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
|
|
|
|
|
// initial settings
|
|
|
|
|
printed = 0
|
|
|
|
|
|
|
|
|
|
printed = printbitset(printed, "uevar", lv.vars, lv.uevar[bb.rpo])
|
|
|
|
|
printed = printbitset(printed, "livein", lv.vars, lv.livein[bb.rpo])
|
|
|
|
|
if printed != 0 {
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
progeffects(p, lv.vars, uevar, varkill, avarinit)
|
|
|
|
|
printed = 0
|
|
|
|
|
printed = printbitset(printed, "uevar", lv.vars, uevar)
|
|
|
|
|
printed = printbitset(printed, "varkill", lv.vars, varkill)
|
|
|
|
|
printed = printbitset(printed, "avarinit", lv.vars, avarinit)
|
|
|
|
|
if printed != 0 {
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
if issafepoint(p) {
|
2015-02-13 14:40:36 -05:00
|
|
|
args = lv.argslivepointers[pcdata]
|
|
|
|
|
locals = lv.livepointers[pcdata]
|
|
|
|
|
fmt.Printf("\tlive=")
|
|
|
|
|
printed = 0
|
|
|
|
|
for j = 0; j < len(lv.vars); j++ {
|
|
|
|
|
n = lv.vars[j]
|
2015-02-17 22:13:49 -05:00
|
|
|
if islive(n, args, locals) {
|
2015-02-13 14:40:36 -05:00
|
|
|
tmp9 := printed
|
|
|
|
|
printed++
|
|
|
|
|
if tmp9 != 0 {
|
|
|
|
|
fmt.Printf(",")
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("%v", Nconv(n, 0))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if p == bb.last {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// bb bitsets
|
|
|
|
|
fmt.Printf("end\n")
|
|
|
|
|
|
|
|
|
|
printed = printbitset(printed, "varkill", lv.vars, lv.varkill[bb.rpo])
|
|
|
|
|
printed = printbitset(printed, "liveout", lv.vars, lv.liveout[bb.rpo])
|
|
|
|
|
printed = printbitset(printed, "avarinit", lv.vars, lv.avarinit[bb.rpo])
|
|
|
|
|
printed = printbitset(printed, "avarinitany", lv.vars, lv.avarinitany[bb.rpo])
|
|
|
|
|
printed = printbitset(printed, "avarinitall", lv.vars, lv.avarinitall[bb.rpo])
|
|
|
|
|
if printed != 0 {
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Dumps an array of bitmaps to a symbol as a sequence of uint32 values. The
|
|
|
|
|
// first word dumped is the total number of bitmaps. The second word is the
|
|
|
|
|
// length of the bitmaps. All bitmaps are assumed to be of equal length. The
|
|
|
|
|
// words that are followed are the raw bitmap words. The arr argument is an
|
|
|
|
|
// array of Node*s.
|
|
|
|
|
func twobitwritesymbol(arr []*Bvec, sym *Sym) {
|
|
|
|
|
var i int
|
|
|
|
|
var j int
|
|
|
|
|
var word uint32
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
n := len(arr)
|
|
|
|
|
off := 0
|
2015-02-13 14:40:36 -05:00
|
|
|
off += 4 // number of bitmaps, to fill in later
|
2015-02-23 16:07:24 -05:00
|
|
|
bv := arr[0]
|
2015-02-13 14:40:36 -05:00
|
|
|
off = duint32(sym, off, uint32(bv.n)) // number of bits in each bitmap
|
|
|
|
|
for i = 0; i < n; i++ {
|
|
|
|
|
// bitmap words
|
|
|
|
|
bv = arr[i]
|
|
|
|
|
|
|
|
|
|
if bv == nil {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
for j = 0; int32(j) < bv.n; j += 32 {
|
|
|
|
|
word = bv.b[j/32]
|
|
|
|
|
|
|
|
|
|
// Runtime reads the bitmaps as byte arrays. Oblige.
|
|
|
|
|
off = duint8(sym, off, uint8(word))
|
|
|
|
|
|
|
|
|
|
off = duint8(sym, off, uint8(word>>8))
|
|
|
|
|
off = duint8(sym, off, uint8(word>>16))
|
|
|
|
|
off = duint8(sym, off, uint8(word>>24))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
duint32(sym, 0, uint32(i)) // number of bitmaps
|
|
|
|
|
ggloblsym(sym, int32(off), obj.RODATA)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
// structure read by the garbage collector.
|
|
|
|
|
func liveness(fn *Node, firstp *obj.Prog, argssym *Sym, livesym *Sym) {
|
|
|
|
|
// Change name to dump debugging information only for a specific function.
|
2015-02-23 16:07:24 -05:00
|
|
|
debugdelta := 0
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if Curfn.Nname.Sym.Name == "!" {
|
|
|
|
|
debugdelta = 2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
debuglive += debugdelta
|
|
|
|
|
if debuglive >= 3 {
|
|
|
|
|
fmt.Printf("liveness: %s\n", Curfn.Nname.Sym.Name)
|
|
|
|
|
printprog(firstp)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
checkptxt(fn, firstp)
|
|
|
|
|
|
|
|
|
|
// Construct the global liveness state.
|
2015-02-23 16:07:24 -05:00
|
|
|
cfg := newcfg(firstp)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if debuglive >= 3 {
|
|
|
|
|
printcfg([]*BasicBlock(cfg))
|
|
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
vars := getvariables(fn)
|
|
|
|
|
lv := newliveness(fn, firstp, cfg, vars)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit the live pointer map data structures
|
|
|
|
|
twobitwritesymbol(lv.livepointers, livesym)
|
|
|
|
|
|
|
|
|
|
twobitwritesymbol(lv.argslivepointers, argssym)
|
|
|
|
|
|
|
|
|
|
// Free everything.
|
2015-02-23 16:07:24 -05:00
|
|
|
for l := fn.Dcl; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
if l.N != nil {
|
|
|
|
|
l.N.Opt = nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
freeliveness(lv)
|
|
|
|
|
|
|
|
|
|
freecfg([]*BasicBlock(cfg))
|
|
|
|
|
|
|
|
|
|
debuglive -= debugdelta
|
|
|
|
|
}
|