summaryrefslogtreecommitdiff
path: root/src/cmd/gc/plive.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/gc/plive.c')
-rw-r--r--src/cmd/gc/plive.c1985
1 files changed, 1985 insertions, 0 deletions
diff --git a/src/cmd/gc/plive.c b/src/cmd/gc/plive.c
new file mode 100644
index 000000000..eb8901733
--- /dev/null
+++ b/src/cmd/gc/plive.c
@@ -0,0 +1,1985 @@
+// 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.
+
+// Garbage collector liveness bitmap generation.
+
+// The command line flag -live causes this code to print debug information.
+// The levels are:
+//
+// -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.
+
+#include <u.h>
+#include <libc.h>
+#include "gg.h"
+#include "opt.h"
+#include "../../pkg/runtime/funcdata.h"
+
+enum { BitsPerPointer = 2 };
+
+enum {
+ 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) {
+// ...
+// }
+typedef struct BasicBlock BasicBlock;
+struct BasicBlock {
+ // An array of preceding blocks. If the length of this array is 0 the
+ // block is probably the start block of the CFG.
+ Array *pred;
+
+ // An array out succeeding blocks. If the length of this array is zero,
+ // the block probably ends in a return instruction.
+ Array *succ;
+
+ // First instruction in the block. When part of a fully initialized
+ // control flow graph, the opt member will be nil.
+ Prog *first;
+
+ // Last instruction in the basic block.
+ Prog *last;
+
+ // The reverse post order number. This value is initialized to -1 and
+ // will be replaced by a non-negative value when the CFG is constructed.
+ // After CFG construction, if rpo is -1 this block is unreachable.
+ int rpo;
+
+ // State to denote whether the block has been visited during a
+ // traversal.
+ int mark;
+
+ // For use during livenessepilogue.
+ int lastbitmapindex;
+};
+
+// A collection of global state used by liveness analysis.
+typedef struct Liveness Liveness;
+struct Liveness {
+ // A pointer to the node corresponding to the function being analyzed.
+ Node *fn;
+
+ // A linked list of instructions for this function.
+ Prog *ptxt;
+
+ // A list of arguments and local variables in this function.
+ Array *vars;
+
+ // A list of basic blocks that are overlayed on the instruction list.
+ // The blocks are roughly in the same order as the instructions
+ // in the function (first block has TEXT instruction, and so on).
+ Array *cfg;
+
+ // Summary sets of block effects.
+ // The Bvec** is indexed by bb->rpo to yield a single Bvec*.
+ // That bit vector is indexed by variable number (same as lv->vars).
+ //
+ // Computed during livenessprologue using only the content of
+ // individual blocks:
+ //
+ // uevar: upward exposed variables (used before set in block)
+ // varkill: killed variables (set in block)
+ // avarinit: addrtaken variables set or used (proof of initialization)
+ //
+ // Computed during livenesssolve using control flow information:
+ //
+ // livein: variables live at block entry
+ // liveout: variables live at block exit
+ // avarinitany: addrtaken variables possibly initialized at block exit
+ // (initialized in block or at exit from any predecessor block)
+ // avarinitall: addrtaken variables certainly initialized at block exit
+ // (initialized in block or at exit from all predecessor blocks)
+ Bvec **uevar;
+ Bvec **varkill;
+ Bvec **livein;
+ Bvec **liveout;
+ Bvec **avarinit;
+ Bvec **avarinitany;
+ Bvec **avarinitall;
+
+ // An array with a bit vector for each safe point tracking live pointers
+ // in the arguments and locals area, indexed by bb->rpo.
+ Array *argslivepointers;
+ Array *livepointers;
+};
+
+static void*
+xmalloc(uintptr size)
+{
+ void *result;
+
+ result = malloc(size);
+ if(result == nil)
+ fatal("malloc failed");
+ return result;
+}
+
+// Constructs a new basic block containing a single instruction.
+static BasicBlock*
+newblock(Prog *prog)
+{
+ BasicBlock *result;
+
+ if(prog == nil)
+ fatal("newblock: prog cannot be nil");
+ result = xmalloc(sizeof(*result));
+ result->rpo = -1;
+ result->mark = UNVISITED;
+ result->first = prog;
+ result->last = prog;
+ result->pred = arraynew(2, sizeof(BasicBlock*));
+ result->succ = arraynew(2, sizeof(BasicBlock*));
+ return result;
+}
+
+// Frees a basic block and all of its leaf data structures.
+static void
+freeblock(BasicBlock *bb)
+{
+ if(bb == nil)
+ fatal("freeblock: cannot free nil");
+ arrayfree(bb->pred);
+ arrayfree(bb->succ);
+ free(bb);
+}
+
+// Adds an edge between two basic blocks by making from a predecessor of to and
+// to a successor of from.
+static void
+addedge(BasicBlock *from, BasicBlock *to)
+{
+ if(from == nil)
+ fatal("addedge: from is nil");
+ if(to == nil)
+ fatal("addedge: to is nil");
+ arrayadd(from->succ, &to);
+ arrayadd(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.
+static void
+splicebefore(Liveness *lv, BasicBlock *bb, Prog *prev, Prog *curr)
+{
+ Prog *next, tmp;
+
+ USED(lv);
+
+ // 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 && next->link->opt == curr)
+ next->link->opt = next;
+
+ if(bb->last == curr)
+ bb->last = next;
+}
+
+// A pretty printer for basic blocks.
+static void
+printblock(BasicBlock *bb)
+{
+ BasicBlock *pred;
+ BasicBlock *succ;
+ Prog *prog;
+ int i;
+
+ print("basic block %d\n", bb->rpo);
+ print("\tpred:");
+ for(i = 0; i < arraylength(bb->pred); i++) {
+ pred = *(BasicBlock**)arrayget(bb->pred, i);
+ print(" %d", pred->rpo);
+ }
+ print("\n");
+ print("\tsucc:");
+ for(i = 0; i < arraylength(bb->succ); i++) {
+ succ = *(BasicBlock**)arrayget(bb->succ, i);
+ print(" %d", succ->rpo);
+ }
+ print("\n");
+ print("\tprog:\n");
+ for(prog = bb->first;; prog=prog->link) {
+ print("\t\t%P\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.
+static int
+blockany(BasicBlock *bb, int (*callback)(Prog*))
+{
+ Prog *p;
+ int result;
+
+ for(p = bb->last; p != nil; p = p->opt) {
+ result = (*callback)(p);
+ if(result != 0)
+ return result;
+ }
+ return 0;
+}
+
+// Collects and returns and array of Node*s for functions arguments and local
+// variables.
+static Array*
+getvariables(Node *fn)
+{
+ Array *result;
+ NodeList *ll;
+
+ result = arraynew(0, sizeof(Node*));
+ for(ll = fn->dcl; ll != nil; ll = ll->next) {
+ 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.
+ switch(ll->n->class) {
+ case PAUTO:
+ if(haspointers(ll->n->type))
+ arrayadd(result, &ll->n);
+ break;
+ case PPARAM:
+ case PPARAMOUT:
+ arrayadd(result, &ll->n);
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+// A pretty printer for control flow graphs. Takes an array of BasicBlock*s.
+static void
+printcfg(Array *cfg)
+{
+ BasicBlock *bb;
+ int32 i;
+
+ for(i = 0; i < arraylength(cfg); i++) {
+ bb = *(BasicBlock**)arrayget(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.
+static void
+reversepostorder(BasicBlock *root, int32 *rpo)
+{
+ BasicBlock *bb;
+ int i;
+
+ root->mark = VISITED;
+ for(i = 0; i < arraylength(root->succ); i++) {
+ bb = *(BasicBlock**)arrayget(root->succ, i);
+ if(bb->mark == UNVISITED)
+ reversepostorder(bb, rpo);
+ }
+ *rpo -= 1;
+ root->rpo = *rpo;
+}
+
+// Comparison predicate used for sorting basic blocks by their rpo in ascending
+// order.
+static int
+blockrpocmp(const void *p1, const void *p2)
+{
+ BasicBlock *bb1;
+ BasicBlock *bb2;
+
+ bb1 = *(BasicBlock**)p1;
+ bb2 = *(BasicBlock**)p2;
+ if(bb1->rpo < bb2->rpo)
+ return -1;
+ if(bb1->rpo > bb2->rpo)
+ return 1;
+ return 0;
+}
+
+// A pattern matcher for call instructions. Returns true when the instruction
+// is a call to a specific package qualified function name.
+static int
+iscall(Prog *prog, LSym *name)
+{
+ if(prog == nil)
+ fatal("iscall: prog is nil");
+ if(name == nil)
+ fatal("iscall: function name is nil");
+ if(prog->as != ACALL)
+ return 0;
+ return name == prog->to.sym;
+}
+
+// Returns true for instructions that call a runtime function implementing a
+// select communication clause.
+static int
+isselectcommcasecall(Prog *prog)
+{
+ static LSym* names[5];
+ int32 i;
+
+ if(names[0] == nil) {
+ names[0] = linksym(pkglookup("selectsend", runtimepkg));
+ names[1] = linksym(pkglookup("selectrecv", runtimepkg));
+ names[2] = linksym(pkglookup("selectrecv2", runtimepkg));
+ names[3] = linksym(pkglookup("selectdefault", runtimepkg));
+ }
+ for(i = 0; names[i] != nil; i++)
+ if(iscall(prog, names[i]))
+ return 1;
+ return 0;
+}
+
+// Returns true for call instructions that target runtime·newselect.
+static int
+isnewselect(Prog *prog)
+{
+ static LSym *sym;
+
+ if(sym == nil)
+ sym = linksym(pkglookup("newselect", runtimepkg));
+ return iscall(prog, sym);
+}
+
+// Returns true for call instructions that target runtime·selectgo.
+static int
+isselectgocall(Prog *prog)
+{
+ static LSym *sym;
+
+ if(sym == nil)
+ sym = linksym(pkglookup("selectgo", runtimepkg));
+ return iscall(prog, sym);
+}
+
+static int
+isdeferreturn(Prog *prog)
+{
+ static LSym *sym;
+
+ if(sym == nil)
+ sym = linksym(pkglookup("deferreturn", runtimepkg));
+ return iscall(prog, sym);
+}
+
+// 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.
+static void
+addselectgosucc(BasicBlock *selectgo)
+{
+ BasicBlock *pred;
+ BasicBlock *succ;
+
+ pred = selectgo;
+ for(;;) {
+ if(arraylength(pred->pred) == 0)
+ fatal("selectgo does not have a newselect");
+ pred = *(BasicBlock**)arrayget(pred->pred, 0);
+ if(blockany(pred, isselectcommcasecall)) {
+ // A select comm case block should have exactly one
+ // successor.
+ if(arraylength(pred->succ) != 1)
+ fatal("select comm case has too many successors");
+ succ = *(BasicBlock**)arrayget(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(arraylength(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);
+ }
+ if(blockany(pred, isnewselect)) {
+ // Reached the matching newselect.
+ break;
+ }
+ }
+}
+
+// The entry point for the missing selectgo control flow algorithm. Takes an
+// array of BasicBlock*s containing selectgo calls.
+static void
+fixselectgo(Array *selectgo)
+{
+ BasicBlock *bb;
+ int32 i;
+
+ for(i = 0; i < arraylength(selectgo); i++) {
+ bb = *(BasicBlock**)arrayget(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).
+static Array*
+newcfg(Prog *firstp)
+{
+ Prog *p;
+ Prog *prev;
+ BasicBlock *bb;
+ Array *cfg;
+ Array *selectgo;
+ int32 i;
+ int32 rpo;
+
+ // 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 != P; p = p->link)
+ 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.
+ selectgo = arraynew(0, sizeof(BasicBlock*));
+
+ // Loop through all instructions identifying branch targets
+ // and fall-throughs and allocate basic blocks.
+ cfg = arraynew(0, sizeof(BasicBlock*));
+ bb = newblock(firstp);
+ arrayadd(cfg, &bb);
+ for(p = firstp; p != P; p = p->link) {
+ if(p->to.type == D_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);
+ arrayadd(cfg, &p->to.u.branch->opt);
+ }
+ if(p->as != AJMP && p->link != nil && p->link->opt == nil) {
+ p->link->opt = newblock(p->link);
+ arrayadd(cfg, &p->link->opt);
+ }
+ } else if(isselectcommcasecall(p) || isselectgocall(p)) {
+ // Accommodate implicit selectgo control flow.
+ if(p->link->opt == nil) {
+ p->link->opt = newblock(p->link);
+ arrayadd(cfg, &p->link->opt);
+ }
+ }
+ }
+
+ // 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(i = 0; i < arraylength(cfg); i++) {
+ bb = *(BasicBlock**)arrayget(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 == ARET && p->link->mode == 1)
+ break;
+
+ // Collect basic blocks with selectgo calls.
+ if(isselectgocall(p))
+ arrayadd(selectgo, &bb);
+ }
+ if(bb->last->to.type == D_BRANCH)
+ addedge(bb, bb->last->to.u.branch->opt);
+ if(bb->last->link != nil) {
+ // Add a fall-through when the instruction is
+ // not an unconditional control transfer.
+ switch(bb->last->as) {
+ case AJMP:
+ case ARET:
+ case AUNDEF:
+ break;
+ default:
+ addedge(bb, bb->last->link->opt);
+ }
+ }
+ }
+
+ // 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(i = 0; i < arraylength(cfg); i++) {
+ bb = *(BasicBlock**)arrayget(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(arraylength(selectgo))
+ fixselectgo(selectgo);
+ arrayfree(selectgo);
+
+ // Find a depth-first order and assign a depth-first number to
+ // all basic blocks.
+ for(i = 0; i < arraylength(cfg); i++) {
+ bb = *(BasicBlock**)arrayget(cfg, i);
+ bb->mark = UNVISITED;
+ }
+ bb = *(BasicBlock**)arrayget(cfg, 0);
+ rpo = arraylength(cfg);
+ 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.
+ arraysort(cfg, blockrpocmp);
+ bb = *(BasicBlock**)arrayget(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) {
+ print("newcfg: unreachable basic block for %P\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.
+static void
+freecfg(Array *cfg)
+{
+ BasicBlock *bb;
+ BasicBlock *bb0;
+ Prog *p;
+ int32 i;
+ int32 len;
+
+ len = arraylength(cfg);
+ if(len > 0) {
+ bb0 = *(BasicBlock**)arrayget(cfg, 0);
+ for(p = bb0->first; p != P; p = p->link) {
+ p->opt = nil;
+ }
+ for(i = 0; i < len; i++) {
+ bb = *(BasicBlock**)arrayget(cfg, i);
+ freeblock(bb);
+ }
+ }
+ arrayfree(cfg);
+}
+
+// Returns true if the node names a variable that is otherwise uninteresting to
+// the liveness computation.
+static int
+isfunny(Node *node)
+{
+ char *names[] = { ".fp", ".args", nil };
+ int i;
+
+ if(node->sym != nil && node->sym->name != nil)
+ for(i = 0; names[i] != nil; i++)
+ if(strcmp(node->sym->name, names[i]) == 0)
+ return 1;
+ return 0;
+}
+
+// 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.
+static void
+progeffects(Prog *prog, Array *vars, Bvec *uevar, Bvec *varkill, Bvec *avarinit)
+{
+ ProgInfo info;
+ Adr *from;
+ Adr *to;
+ Node *node;
+ int32 i;
+ int32 pos;
+
+ bvresetall(uevar);
+ bvresetall(varkill);
+ bvresetall(avarinit);
+
+ proginfo(&info, prog);
+ if(prog->as == 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.
+ for(i = 0; i < arraylength(vars); i++) {
+ node = *(Node**)arrayget(vars, i);
+ switch(node->class & ~PHEAP) {
+ case PPARAM:
+ bvset(uevar, i);
+ break;
+ case PPARAMOUT:
+ // 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 == D_NONE limits the bvset to
+ // non-tail-call return instructions; see note above
+ // the for loop for details.
+ if(!node->addrtaken && prog->to.type == D_NONE)
+ bvset(uevar, i);
+ break;
+ }
+ }
+ return;
+ }
+ if(prog->as == ATEXT) {
+ // A text instruction marks the entry point to a function and
+ // the definition point of all in arguments.
+ for(i = 0; i < arraylength(vars); i++) {
+ node = *(Node**)arrayget(vars, i);
+ switch(node->class & ~PHEAP) {
+ case PPARAM:
+ if(node->addrtaken)
+ bvset(avarinit, i);
+ bvset(varkill, i);
+ break;
+ }
+ }
+ return;
+ }
+ if(info.flags & (LeftRead | LeftWrite | LeftAddr)) {
+ from = &prog->from;
+ if (from->node != nil && from->sym != nil) {
+ switch(from->node->class & ~PHEAP) {
+ case PAUTO:
+ case PPARAM:
+ case PPARAMOUT:
+ pos = arrayindexof(vars, from->node);
+ if(pos == -1)
+ goto Next;
+ if(from->node->addrtaken) {
+ bvset(avarinit, pos);
+ } else {
+ if(info.flags & (LeftRead | LeftAddr))
+ bvset(uevar, pos);
+ if(info.flags & LeftWrite)
+ if(from->node != nil && !isfat(from->node->type))
+ bvset(varkill, pos);
+ }
+ }
+ }
+ }
+Next:
+ if(info.flags & (RightRead | RightWrite | RightAddr)) {
+ to = &prog->to;
+ if (to->node != nil && to->sym != nil) {
+ switch(to->node->class & ~PHEAP) {
+ case PAUTO:
+ case PPARAM:
+ case PPARAMOUT:
+ pos = arrayindexof(vars, to->node);
+ if(pos == -1)
+ goto Next1;
+ if(to->node->addrtaken) {
+ if(prog->as != AVARKILL)
+ bvset(avarinit, pos);
+ if(prog->as == AVARDEF || prog->as == 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) || (info.flags & (RightAddr|RightWrite)) == RightAddr)
+ bvset(uevar, pos);
+ if(info.flags & RightWrite)
+ if(to->node != nil && (!isfat(to->node->type) || prog->as == AVARDEF))
+ 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.
+static Liveness*
+newliveness(Node *fn, Prog *ptxt, Array *cfg, Array *vars)
+{
+ Liveness *result;
+ int32 i;
+ int32 nblocks;
+ int32 nvars;
+
+ result = xmalloc(sizeof(*result));
+ result->fn = fn;
+ result->ptxt = ptxt;
+ result->cfg = cfg;
+ result->vars = vars;
+
+ nblocks = arraylength(cfg);
+ result->uevar = xmalloc(sizeof(Bvec*) * nblocks);
+ result->varkill = xmalloc(sizeof(Bvec*) * nblocks);
+ result->livein = xmalloc(sizeof(Bvec*) * nblocks);
+ result->liveout = xmalloc(sizeof(Bvec*) * nblocks);
+ result->avarinit = xmalloc(sizeof(Bvec*) * nblocks);
+ result->avarinitany = xmalloc(sizeof(Bvec*) * nblocks);
+ result->avarinitall = xmalloc(sizeof(Bvec*) * nblocks);
+
+ nvars = arraylength(vars);
+ for(i = 0; i < nblocks; i++) {
+ 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 = arraynew(0, sizeof(Bvec*));
+ result->argslivepointers = arraynew(0, sizeof(Bvec*));
+ return result;
+}
+
+// Frees the liveness structure and all of its leaf data structures.
+static void
+freeliveness(Liveness *lv)
+{
+ int32 i;
+
+ if(lv == nil)
+ fatal("freeliveness: cannot free nil");
+
+ for(i = 0; i < arraylength(lv->livepointers); i++)
+ free(*(Bvec**)arrayget(lv->livepointers, i));
+ arrayfree(lv->livepointers);
+
+ for(i = 0; i < arraylength(lv->argslivepointers); i++)
+ free(*(Bvec**)arrayget(lv->argslivepointers, i));
+ arrayfree(lv->argslivepointers);
+
+ for(i = 0; i < arraylength(lv->cfg); i++) {
+ free(lv->uevar[i]);
+ free(lv->varkill[i]);
+ free(lv->livein[i]);
+ free(lv->liveout[i]);
+ free(lv->avarinit[i]);
+ free(lv->avarinitany[i]);
+ free(lv->avarinitall[i]);
+ }
+
+ free(lv->uevar);
+ free(lv->varkill);
+ free(lv->livein);
+ free(lv->liveout);
+ free(lv->avarinit);
+ free(lv->avarinitany);
+ free(lv->avarinitall);
+
+ free(lv);
+}
+
+static void
+printeffects(Prog *p, Bvec *uevar, Bvec *varkill, Bvec *avarinit)
+{
+ print("effects of %P", p);
+ print("\nuevar: ");
+ bvprint(uevar);
+ print("\nvarkill: ");
+ bvprint(varkill);
+ print("\navarinit: ");
+ bvprint(avarinit);
+ print("\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.
+static void
+printnode(Node *node)
+{
+ char *p;
+ char *a;
+
+ p = haspointers(node->type) ? "^" : "";
+ a = node->addrtaken ? "@" : "";
+ print(" %N%s%s", node, p, a);
+}
+
+// Pretty print a list of variables. The vars argument is an array of Node*s.
+static void
+printvars(char *name, Bvec *bv, Array *vars)
+{
+ int32 i;
+
+ print("%s:", name);
+ for(i = 0; i < arraylength(vars); i++)
+ if(bvget(bv, i))
+ printnode(*(Node**)arrayget(vars, i));
+ print("\n");
+}
+
+// Prints a basic block annotated with the information computed by liveness
+// analysis.
+static void
+livenessprintblock(Liveness *lv, BasicBlock *bb)
+{
+ BasicBlock *pred;
+ BasicBlock *succ;
+ Prog *prog;
+ Bvec *live;
+ int i;
+ int32 pos;
+
+ print("basic block %d\n", bb->rpo);
+
+ print("\tpred:");
+ for(i = 0; i < arraylength(bb->pred); i++) {
+ pred = *(BasicBlock**)arrayget(bb->pred, i);
+ print(" %d", pred->rpo);
+ }
+ print("\n");
+
+ print("\tsucc:");
+ for(i = 0; i < arraylength(bb->succ); i++) {
+ succ = *(BasicBlock**)arrayget(bb->succ, i);
+ print(" %d", succ->rpo);
+ }
+ print("\n");
+
+ printvars("\tuevar", lv->uevar[bb->rpo], lv->vars);
+ printvars("\tvarkill", lv->varkill[bb->rpo], lv->vars);
+ printvars("\tlivein", lv->livein[bb->rpo], lv->vars);
+ printvars("\tliveout", lv->liveout[bb->rpo], lv->vars);
+ printvars("\tavarinit", lv->avarinit[bb->rpo], lv->vars);
+ printvars("\tavarinitany", lv->avarinitany[bb->rpo], lv->vars);
+ printvars("\tavarinitall", lv->avarinitall[bb->rpo], lv->vars);
+
+ print("\tprog:\n");
+ for(prog = bb->first;; prog = prog->link) {
+ print("\t\t%P", prog);
+ if(prog->as == APCDATA && prog->from.offset == PCDATA_StackMapIndex) {
+ pos = prog->to.offset;
+ live = *(Bvec**)arrayget(lv->livepointers, pos);
+ print(" ");
+ bvprint(live);
+ }
+ print("\n");
+ if(prog == bb->last)
+ break;
+ }
+}
+
+// Prints a control flow graph annotated with any information computed by
+// liveness analysis.
+static void
+livenessprintcfg(Liveness *lv)
+{
+ BasicBlock *bb;
+ int32 i;
+
+ for(i = 0; i < arraylength(lv->cfg); i++) {
+ bb = *(BasicBlock**)arrayget(lv->cfg, i);
+ livenessprintblock(lv, bb);
+ }
+}
+
+static void
+checkauto(Node *fn, Prog *p, Node *n)
+{
+ NodeList *l;
+
+ for(l = fn->dcl; l != nil; l = l->next)
+ if(l->n->op == ONAME && l->n->class == PAUTO && l->n == n)
+ return;
+
+ print("checkauto %N: %N (%p; class=%d) not found in %P\n", curfn, n, n, n->class, p);
+ for(l = fn->dcl; l != nil; l = l->next)
+ print("\t%N (%p; class=%d)\n", l->n, l->n, l->n->class);
+ yyerror("checkauto: invariant lost");
+}
+
+static void
+checkparam(Node *fn, Prog *p, Node *n)
+{
+ NodeList *l;
+ Node *a;
+ int class;
+
+ if(isfunny(n))
+ return;
+ for(l = fn->dcl; l != nil; l = l->next) {
+ a = l->n;
+ class = a->class & ~PHEAP;
+ if(a->op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n)
+ return;
+ }
+
+ print("checkparam %N: %N (%p; class=%d) not found in %P\n", curfn, n, n, n->class, p);
+ for(l = fn->dcl; l != nil; l = l->next)
+ print("\t%N (%p; class=%d)\n", l->n, l->n, l->n->class);
+ yyerror("checkparam: invariant lost");
+}
+
+static void
+checkprog(Node *fn, Prog *p)
+{
+ if(p->from.type == D_AUTO)
+ checkauto(fn, p, p->from.node);
+ if(p->from.type == D_PARAM)
+ checkparam(fn, p, p->from.node);
+ if(p->to.type == D_AUTO)
+ checkauto(fn, p, p->to.node);
+ if(p->to.type == D_PARAM)
+ checkparam(fn, p, p->to.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.
+static void
+checkptxt(Node *fn, Prog *firstp)
+{
+ Prog *p;
+
+ for(p = firstp; p != P; p = p->link) {
+ if(0)
+ print("analyzing '%P'\n", p);
+ switch(p->as) {
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ case ATYPE:
+ continue;
+ }
+ 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.
+static void
+twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv)
+{
+ vlong fieldoffset;
+ vlong i;
+ vlong o;
+ Type *t1;
+
+ if(t->align > 0 && (*xoffset & (t->align - 1)) != 0)
+ fatal("twobitwalktype1: invalid initial alignment, %T", t);
+
+ switch(t->etype) {
+ case TINT8:
+ case TUINT8:
+ case TINT16:
+ case TUINT16:
+ case TINT32:
+ case TUINT32:
+ case TINT64:
+ case TUINT64:
+ case TINT:
+ case TUINT:
+ case TUINTPTR:
+ case TBOOL:
+ case TFLOAT32:
+ case TFLOAT64:
+ case TCOMPLEX64:
+ case TCOMPLEX128:
+ for(i = 0; i < t->width; i++) {
+ bvset(bv, ((*xoffset + i) / widthptr) * BitsPerPointer); // 1 = live scalar
+ }
+ *xoffset += t->width;
+ break;
+
+ case TPTR32:
+ case TPTR64:
+ case TUNSAFEPTR:
+ case TFUNC:
+ case TCHAN:
+ case TMAP:
+ if((*xoffset & (widthptr-1)) != 0)
+ fatal("twobitwalktype1: invalid alignment, %T", t);
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr
+ *xoffset += t->width;
+ break;
+
+ case TSTRING:
+ // struct { byte *str; intgo len; }
+ if((*xoffset & (widthptr-1)) != 0)
+ fatal("twobitwalktype1: invalid alignment, %T", t);
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 0);
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 3:0 = multiword:string
+ *xoffset += t->width;
+ break;
+
+ case TINTER:
+ // struct { Itab *tab; union { void *ptr, uintptr val } data; }
+ // or, when isnilinter(t)==true:
+ // struct { Type *type; union { void *ptr, uintptr val } data; }
+ if((*xoffset & (widthptr-1)) != 0)
+ fatal("twobitwalktype1: invalid alignment, %T", t);
+ bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 0);
+ bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 1); // 3 = multiword
+ // next word contains 2 = Iface, 3 = Eface
+ if(isnilinter(t)) {
+ bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 2);
+ bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 3);
+ } else {
+ bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 3);
+ }
+ *xoffset += t->width;
+ break;
+
+ case TARRAY:
+ // The value of t->bound is -1 for slices types and >0 for
+ // for fixed array types. All other values are invalid.
+ if(t->bound < -1)
+ fatal("twobitwalktype1: invalid bound, %T", t);
+ if(isslice(t)) {
+ // struct { byte *array; uintgo len; uintgo cap; }
+ if((*xoffset & (widthptr-1)) != 0)
+ fatal("twobitwalktype1: invalid TARRAY alignment, %T", t);
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 0);
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1);
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 2); // 3:1 = multiword/slice
+ *xoffset += t->width;
+ } else
+ for(i = 0; i < t->bound; i++)
+ twobitwalktype1(t->type, xoffset, bv);
+ break;
+
+ case TSTRUCT:
+ o = 0;
+ for(t1 = t->type; t1 != T; t1 = t1->down) {
+ fieldoffset = t1->width;
+ *xoffset += fieldoffset - o;
+ twobitwalktype1(t1->type, xoffset, bv);
+ o = fieldoffset + t1->type->width;
+ }
+ *xoffset += t->width - o;
+ break;
+
+ default:
+ fatal("twobitwalktype1: unexpected type, %T", t);
+ }
+}
+
+// Returns the number of words of local variables.
+static int32
+localswords(void)
+{
+ return stkptrsize / widthptr;
+}
+
+// Returns the number of words of in and out arguments.
+static int32
+argswords(void)
+{
+ return curfn->type->argwid / 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.
+static void
+twobitlivepointermap(Liveness *lv, Bvec *liveout, Array *vars, Bvec *args, Bvec *locals)
+{
+ Node *node;
+ Type *thisargtype;
+ Type *inargtype;
+ vlong xoffset;
+ int32 i;
+
+ for(i = 0; i < arraylength(vars); i++) {
+ node = *(Node**)arrayget(vars, i);
+ switch(node->class) {
+ case PAUTO:
+ if(bvget(liveout, i)) {
+ xoffset = node->xoffset + stkptrsize;
+ twobitwalktype1(node->type, &xoffset, locals);
+ }
+ break;
+ case PPARAM:
+ case PPARAMOUT:
+ if(bvget(liveout, i)) {
+ xoffset = node->xoffset;
+ twobitwalktype1(node->type, &xoffset, args);
+ }
+ break;
+ }
+ }
+
+ // 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.
+ thisargtype = getthisx(lv->fn->type);
+ if(thisargtype != nil) {
+ xoffset = 0;
+ twobitwalktype1(thisargtype, &xoffset, args);
+ }
+ inargtype = getinargx(lv->fn->type);
+ if(inargtype != nil) {
+ xoffset = 0;
+ twobitwalktype1(inargtype, &xoffset, args);
+ }
+}
+
+// Construct a disembodied instruction.
+static Prog*
+unlinkedprog(int as)
+{
+ Prog *p;
+
+ p = mal(sizeof(*p));
+ clearp(p);
+ p->as = as;
+ return p;
+}
+
+// Construct a new PCDATA instruction associated with and for the purposes of
+// covering an existing instruction.
+static Prog*
+newpcdataprog(Prog *prog, int32 index)
+{
+ Node from, to;
+ Prog *pcdata;
+
+ nodconst(&from, types[TINT32], PCDATA_StackMapIndex);
+ nodconst(&to, types[TINT32], index);
+ pcdata = unlinkedprog(APCDATA);
+ 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.
+static int
+issafepoint(Prog *prog)
+{
+ return prog->as == ATEXT || prog->as == ACALL;
+}
+
+// Initializes the sets for solving the live variables. Visits all the
+// instructions in each basic block to summarizes the information at each basic
+// block
+static void
+livenessprologue(Liveness *lv)
+{
+ BasicBlock *bb;
+ Bvec *uevar, *varkill, *avarinit;
+ Prog *p;
+ int32 i;
+ int32 nvars;
+
+ nvars = arraylength(lv->vars);
+ uevar = bvalloc(nvars);
+ varkill = bvalloc(nvars);
+ avarinit = bvalloc(nvars);
+ for(i = 0; i < arraylength(lv->cfg); i++) {
+ bb = *(BasicBlock**)arrayget(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) {
+ progeffects(p, 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, 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;
+ }
+ }
+ free(uevar);
+ free(varkill);
+ free(avarinit);
+}
+
+// Solve the liveness dataflow equations.
+static void
+livenesssolve(Liveness *lv)
+{
+ BasicBlock *bb, *succ, *pred;
+ Bvec *newlivein, *newliveout, *any, *all;
+ int32 rpo, i, j, change;
+
+ // These temporary bitvectors exist to avoid successive allocations and
+ // frees within the loop.
+ newlivein = bvalloc(arraylength(lv->vars));
+ newliveout = bvalloc(arraylength(lv->vars));
+ any = bvalloc(arraylength(lv->vars));
+ all = bvalloc(arraylength(lv->vars));
+
+ // 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 = 0; i < arraylength(lv->cfg); i++) {
+ bb = *(BasicBlock**)arrayget(lv->cfg, i);
+ rpo = 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]);
+ }
+
+ change = 1;
+ while(change != 0) {
+ change = 0;
+ for(i = 0; i < arraylength(lv->cfg); i++) {
+ bb = *(BasicBlock**)arrayget(lv->cfg, i);
+ rpo = bb->rpo;
+ bvresetall(any);
+ bvresetall(all);
+ for(j = 0; j < arraylength(bb->pred); j++) {
+ pred = *(BasicBlock**)arrayget(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])) {
+ change = 1;
+ bvcopy(lv->avarinitany[rpo], any);
+ }
+ if(bvcmp(all, lv->avarinitall[rpo])) {
+ 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;
+ while(change != 0) {
+ change = 0;
+ // Walk blocks in the general direction of propagation. This
+ // improves convergence.
+ for(i = arraylength(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 = *(BasicBlock**)arrayget(lv->cfg, i);
+ rpo = bb->rpo;
+ bvresetall(newliveout);
+ for(j = 0; j < arraylength(bb->succ); j++) {
+ succ = *(BasicBlock**)arrayget(bb->succ, j);
+ bvor(newliveout, newliveout, lv->livein[succ->rpo]);
+ }
+ if(bvcmp(lv->liveout[rpo], newliveout)) {
+ 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]);
+ }
+ }
+
+ free(newlivein);
+ free(newliveout);
+ free(any);
+ free(all);
+}
+
+// This function is slow but it is only used for generating debug prints.
+// Check whether n is marked live in args/locals.
+static int
+islive(Node *n, Bvec *args, Bvec *locals)
+{
+ int i;
+
+ switch(n->class) {
+ case PPARAM:
+ case PPARAMOUT:
+ for(i = 0; i < n->type->width/widthptr*BitsPerPointer; i++)
+ if(bvget(args, n->xoffset/widthptr*BitsPerPointer + i))
+ return 1;
+ break;
+ case PAUTO:
+ for(i = 0; i < n->type->width/widthptr*BitsPerPointer; i++)
+ if(bvget(locals, (n->xoffset + stkptrsize)/widthptr*BitsPerPointer + i))
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+// Visits all instructions in a basic block and computes a bit vector of live
+// variables at each safe point locations.
+static void
+livenessepilogue(Liveness *lv)
+{
+ BasicBlock *bb, *pred;
+ Bvec *ambig, *livein, *liveout, *uevar, *varkill, *args, *locals, *avarinit, *any, *all;
+ Node *n;
+ Prog *p, *next;
+ int32 i, j, numlive, startmsg, nmsg, nvars, pos;
+ vlong xoffset;
+ char **msg;
+ Fmt fmt;
+
+ nvars = arraylength(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() * BitsPerPointer);
+ msg = nil;
+ nmsg = 0;
+ startmsg = 0;
+
+ for(i = 0; i < arraylength(lv->cfg); i++) {
+ bb = *(BasicBlock**)arrayget(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 < arraylength(bb->pred); j++) {
+ pred = *(BasicBlock**)arrayget(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, lv->vars, uevar, varkill, avarinit);
+ bvandnot(any, any, varkill);
+ bvandnot(all, all, varkill);
+ bvor(any, any, avarinit);
+ bvor(all, all, avarinit);
+
+ 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.
+ // For now, only enabled when using GOEXPERIMENT=precisestack
+ // during make.bash / all.bash.
+ if(precisestack_enabled) {
+ bvresetall(livein);
+ bvandnot(liveout, any, all);
+ if(!bvisempty(liveout)) {
+ for(pos = 0; pos < liveout->n; pos++) {
+ if(!bvget(liveout, pos))
+ continue;
+ bvset(all, pos); // silence future warnings in this block
+ n = *(Node**)arrayget(lv->vars, pos);
+ if(!n->needzero) {
+ n->needzero = 1;
+ if(debuglive >= 1)
+ warnl(p->lineno, "%N: %lN is ambiguously live", curfn->nname, n);
+ // 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() * BitsPerPointer);
+ arrayadd(lv->argslivepointers, &args);
+ locals = bvalloc(localswords() * BitsPerPointer);
+ arrayadd(lv->livepointers, &locals);
+
+ if(debuglive >= 3) {
+ print("%P\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 = arraylength(lv->livepointers) - 1;
+ }
+
+ for(i = 0; i < arraylength(lv->cfg); i++) {
+ bb = *(BasicBlock**)arrayget(lv->cfg, i);
+
+ if(debuglive >= 1 && strcmp(curfn->nname->sym->name, "init") != 0 && curfn->nname->sym->name[0] != '.') {
+ nmsg = arraylength(lv->livepointers);
+ startmsg = nmsg;
+ msg = xmalloc(nmsg*sizeof msg[0]);
+ for(j=0; j<nmsg; j++)
+ msg[j] = nil;
+ }
+
+ // walk backward, emit pcdata and populate the maps
+ pos = 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; // 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);
+ if(debuglive >= 3 && issafepoint(p)){
+ print("%P\n", p);
+ printvars("uevar", uevar, lv->vars);
+ printvars("varkill", 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.
+
+ // Useful sanity check: on entry to the function,
+ // the only things that can possibly be live are the
+ // input parameters.
+ if(p->as == ATEXT) {
+ for(j = 0; j < liveout->n; j++) {
+ if(!bvget(liveout, j))
+ continue;
+ n = *(Node**)arrayget(lv->vars, j);
+ if(n->class != PPARAM)
+ yyerrorl(p->lineno, "internal error: %N %lN recorded as live on entry", curfn->nname, n);
+ }
+ }
+
+ // Record live pointers.
+ args = *(Bvec**)arrayget(lv->argslivepointers, pos);
+ locals = *(Bvec**)arrayget(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 == 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) {
+ fmtstrinit(&fmt);
+ fmtprint(&fmt, "%L: live at ", p->lineno);
+ if(p->as == ACALL && p->to.node)
+ fmtprint(&fmt, "call to %s:", p->to.node->sym->name);
+ else if(p->as == ACALL)
+ fmtprint(&fmt, "indirect call:");
+ else
+ fmtprint(&fmt, "entry to %s:", p->from.node->sym->name);
+ numlive = 0;
+ for(j = 0; j < arraylength(lv->vars); j++) {
+ n = *(Node**)arrayget(lv->vars, j);
+ if(islive(n, args, locals)) {
+ fmtprint(&fmt, " %N", n);
+ numlive++;
+ }
+ }
+ fmtprint(&fmt, "\n");
+ if(numlive == 0) // squelch message
+ free(fmtstrflush(&fmt));
+ else
+ msg[--startmsg] = fmtstrflush(&fmt);
+ }
+
+ // Only CALL instructions need a PCDATA annotation.
+ // The TEXT instruction annotation is implicit.
+ if(p->as == ACALL) {
+ 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.
+ splicebefore(lv, bb, newpcdataprog(p->opt, pos), p->opt);
+ } else {
+ splicebefore(lv, bb, newpcdataprog(p, pos), p);
+ }
+ }
+
+ pos--;
+ }
+ }
+ if(msg != nil) {
+ for(j=startmsg; j<nmsg; j++)
+ if(msg[j] != nil)
+ print("%s", msg[j]);
+ free(msg);
+ msg = nil;
+ nmsg = 0;
+ startmsg = 0;
+ }
+ }
+
+ free(livein);
+ free(liveout);
+ free(uevar);
+ free(varkill);
+ free(avarinit);
+ free(any);
+ free(all);
+ free(ambig);
+
+ flusherrors();
+}
+
+// FNV-1 hash function constants.
+#define H0 2166136261UL
+#define Hp 16777619UL
+
+static uint32
+hashbitmap(uint32 h, Bvec *bv)
+{
+ uchar *p, *ep;
+
+ p = (uchar*)bv->b;
+ ep = p + 4*((bv->n+31)/32);
+ while(p < ep)
+ h = (h*Hp) ^ *p++;
+ 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.
+static void
+livenesscompact(Liveness *lv)
+{
+ int *table, *remap, i, j, n, tablesize, uniq;
+ uint32 h;
+ Bvec *local, *arg, *jlocal, *jarg;
+ Prog *p;
+
+ // 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.
+ n = arraylength(lv->livepointers);
+ tablesize = 4*n;
+ table = xmalloc(tablesize*sizeof table[0]);
+ memset(table, 0xff, tablesize*sizeof table[0]);
+
+ // remap[i] = the new index of the old bit vector #i.
+ remap = xmalloc(n*sizeof remap[0]);
+ memset(remap, 0xff, n*sizeof remap[0]);
+ uniq = 0; // unique tables found so far
+
+ // 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.
+ for(i=0; i<n; i++) {
+ local = *(Bvec**)arrayget(lv->livepointers, i);
+ arg = *(Bvec**)arrayget(lv->argslivepointers, i);
+ h = hashbitmap(hashbitmap(H0, local), arg) % tablesize;
+
+ for(;;) {
+ j = table[h];
+ if(j < 0)
+ break;
+ jlocal = *(Bvec**)arrayget(lv->livepointers, j);
+ jarg = *(Bvec**)arrayget(lv->argslivepointers, j);
+ if(bvcmp(local, jlocal) == 0 && bvcmp(arg, jarg) == 0) {
+ free(local);
+ free(arg);
+ remap[i] = j;
+ goto Next;
+ }
+ if(++h == tablesize)
+ h = 0;
+ }
+ table[h] = uniq;
+ remap[i] = uniq;
+ *(Bvec**)arrayget(lv->livepointers, uniq) = local;
+ *(Bvec**)arrayget(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.
+ for(j=uniq; j<n; j++) {
+ *(Bvec**)arrayget(lv->livepointers, j) = nil;
+ *(Bvec**)arrayget(lv->argslivepointers, j) = nil;
+ }
+
+ // Rewrite PCDATA instructions to use new numbering.
+ for(p=lv->ptxt; p != P; p=p->link) {
+ if(p->as == APCDATA && p->from.offset == PCDATA_StackMapIndex) {
+ i = p->to.offset;
+ if(i >= 0)
+ p->to.offset = remap[i];
+ }
+ }
+
+ free(table);
+ free(remap);
+}
+
+static int
+printbitset(int printed, char *name, Array *vars, Bvec *bits)
+{
+ int i, started;
+ Node *n;
+
+ started = 0;
+ for(i=0; i<arraylength(vars); i++) {
+ if(!bvget(bits, i))
+ continue;
+ if(!started) {
+ if(!printed)
+ print("\t");
+ else
+ print(" ");
+ started = 1;
+ printed = 1;
+ print("%s=", name);
+ } else {
+ print(",");
+ }
+ n = *(Node**)arrayget(vars, i);
+ print("%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.
+static void
+livenessprintdebug(Liveness *lv)
+{
+ int i, j, pcdata, printed;
+ BasicBlock *bb;
+ Prog *p;
+ Bvec *uevar, *varkill, *avarinit, *args, *locals;
+ Node *n;
+
+ print("liveness: %s\n", curfn->nname->sym->name);
+
+ uevar = bvalloc(arraylength(lv->vars));
+ varkill = bvalloc(arraylength(lv->vars));
+ avarinit = bvalloc(arraylength(lv->vars));
+
+ pcdata = 0;
+ for(i = 0; i < arraylength(lv->cfg); i++) {
+ if(i > 0)
+ print("\n");
+ bb = *(BasicBlock**)arrayget(lv->cfg, i);
+
+ // bb#0 pred=1,2 succ=3,4
+ print("bb#%d pred=", i);
+ for(j = 0; j < arraylength(bb->pred); j++) {
+ if(j > 0)
+ print(",");
+ print("%d", (*(BasicBlock**)arrayget(bb->pred, j))->rpo);
+ }
+ print(" succ=");
+ for(j = 0; j < arraylength(bb->succ); j++) {
+ if(j > 0)
+ print(",");
+ print("%d", (*(BasicBlock**)arrayget(bb->succ, j))->rpo);
+ }
+ print("\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)
+ print("\n");
+
+ // program listing, with individual effects listed
+ for(p = bb->first;; p = p->link) {
+ print("%P\n", p);
+ if(p->as == APCDATA && p->from.offset == PCDATA_StackMapIndex)
+ pcdata = 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)
+ print("\n");
+ if(issafepoint(p)) {
+ args = *(Bvec**)arrayget(lv->argslivepointers, pcdata);
+ locals = *(Bvec**)arrayget(lv->livepointers, pcdata);
+ print("\tlive=");
+ printed = 0;
+ for(j = 0; j < arraylength(lv->vars); j++) {
+ n = *(Node**)arrayget(lv->vars, j);
+ if(islive(n, args, locals)) {
+ if(printed++)
+ print(",");
+ print("%N", n);
+ }
+ }
+ print("\n");
+ }
+ if(p == bb->last)
+ break;
+ }
+
+ // bb bitsets
+ print("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)
+ print("\n");
+ }
+ print("\n");
+
+ free(uevar);
+ free(varkill);
+ free(avarinit);
+}
+
+// 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.
+static void
+twobitwritesymbol(Array *arr, Sym *sym)
+{
+ Bvec *bv;
+ int off, i, j, len;
+ uint32 word;
+
+ len = arraylength(arr);
+ off = 0;
+ off += 4; // number of bitmaps, to fill in later
+ bv = *(Bvec**)arrayget(arr, 0);
+ off = duint32(sym, off, bv->n); // number of bits in each bitmap
+ for(i = 0; i < len; i++) {
+ // bitmap words
+ bv = *(Bvec**)arrayget(arr, i);
+ if(bv == nil)
+ break;
+ for(j = 0; j < bv->n; j += 32) {
+ word = bv->b[j/32];
+ off = duint32(sym, off, word);
+ }
+ }
+ duint32(sym, 0, i); // number of bitmaps
+ ggloblsym(sym, off, 0, 1);
+}
+
+static void
+printprog(Prog *p)
+{
+ while(p != nil) {
+ print("%P\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.
+void
+liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym)
+{
+ Array *cfg, *vars;
+ Liveness *lv;
+ int debugdelta;
+
+ // Change name to dump debugging information only for a specific function.
+ debugdelta = 0;
+ if(strcmp(curfn->nname->sym->name, "!") == 0)
+ debugdelta = 2;
+
+ debuglive += debugdelta;
+ if(debuglive >= 3) {
+ print("liveness: %s\n", curfn->nname->sym->name);
+ printprog(firstp);
+ }
+ checkptxt(fn, firstp);
+
+ // Construct the global liveness state.
+ cfg = newcfg(firstp);
+ if(debuglive >= 3)
+ printcfg(cfg);
+ vars = getvariables(fn);
+ lv = newliveness(fn, firstp, cfg, vars);
+
+ // 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.
+ freeliveness(lv);
+ arrayfree(vars);
+ freecfg(cfg);
+
+ debuglive -= debugdelta;
+}