aboutsummaryrefslogtreecommitdiff
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.c2005
1 files changed, 0 insertions, 2005 deletions
diff --git a/src/cmd/gc/plive.c b/src/cmd/gc/plive.c
deleted file mode 100644
index c0d1e57932..0000000000
--- a/src/cmd/gc/plive.c
+++ /dev/null
@@ -1,2005 +0,0 @@
-// 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 "go.h"
-#include "../ld/textflag.h"
-#include "../../runtime/funcdata.h"
-#include "../../runtime/mgc0.h"
-
-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.
- //
- // 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 = (void*)(uintptr)(arraylength(result)+1);
- arrayadd(result, &ll->n);
- }
- break;
- case PPARAM:
- case PPARAMOUT:
- ll->n->opt = (void*)(uintptr)(arraylength(result)+1);
- 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 == 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);
- 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 == TYPE_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.
- if(bb->last->as != AJMP && bb->last->as != ARET && bb->last->as != AUNDEF)
- 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 n;
-
- n = arraylength(cfg);
- if(n > 0) {
- bb0 = *(BasicBlock**)arrayget(cfg, 0);
- for(p = bb0->first; p != P; p = p->link) {
- p->opt = nil;
- }
- for(i = 0; i < n; 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;
- Addr *from;
- Addr *to;
- Node *node;
- int32 i;
- int32 pos;
-
- bvresetall(uevar);
- bvresetall(varkill);
- bvresetall(avarinit);
-
- thearch.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 == thearch.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 == TYPE_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 && ((Node*)(from->node))->curfn == curfn) {
- switch(((Node*)(from->node))->class & ~PHEAP) {
- case PAUTO:
- case PPARAM:
- case PPARAMOUT:
- pos = (int)(uintptr)((Node*)(from->node))->opt - 1; // index in vars
- if(pos == -1)
- goto Next;
- if(pos >= arraylength(vars) || *(Node**)arrayget(vars, pos) != from->node)
- fatal("bad bookkeeping in liveness %N %d", from->node, pos);
- if(((Node*)(from->node))->addrtaken) {
- bvset(avarinit, pos);
- } else {
- if(info.flags & (LeftRead | LeftAddr))
- bvset(uevar, pos);
- if(info.flags & LeftWrite)
- if(from->node != nil && !isfat(((Node*)(from->node))->type))
- bvset(varkill, pos);
- }
- }
- }
- }
-Next:
- if(info.flags & (RightRead | RightWrite | RightAddr)) {
- to = &prog->to;
- if (to->node != nil && to->sym != nil && ((Node*)(to->node))->curfn == curfn) {
- switch(((Node*)(to->node))->class & ~PHEAP) {
- case PAUTO:
- case PPARAM:
- case PPARAMOUT:
- pos = (int)(uintptr)((Node*)(to->node))->opt - 1; // index in vars
- if(pos == -1)
- goto Next1;
- if(pos >= arraylength(vars) || *(Node**)arrayget(vars, pos) != to->node)
- fatal("bad bookkeeping in liveness %N %d", to->node, pos);
- if(((Node*)(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(((Node*)(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 = "";
- if(haspointers(node->type))
- p = "^";
- a = "";
- if(node->addrtaken)
- a = "@";
- 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;
-
- if(n == nil) {
- print("%L: checkauto %N: nil node in %P\n", p->lineno, curfn, p);
- 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.name == NAME_AUTO)
- checkauto(fn, p, p->from.node);
- if(p->from.name == NAME_PARAM)
- checkparam(fn, p, p->from.node);
- if(p->to.name == NAME_AUTO)
- checkauto(fn, p, p->to.node);
- if(p->to.name == NAME_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;
-
- if(debuglive == 0)
- return;
-
- for(p = firstp; p != P; p = p->link) {
- if(0)
- print("analyzing '%P'\n", p);
- if(p->as != ADATA && p->as != AGLOBL && p->as != 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.
-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 (BitsScalar)
- }
- *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 (BitsPointer)
- *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 + 1); // 2 = live ptr in first slot (BitsPointer)
- *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 + 1); // 2 = live ptr in first slot (BitsPointer)
- bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 3); // 2 = live ptr in second slot (BitsPointer)
- *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 + 1); // 2 = live ptr in first slot (BitsPointer)
- *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 = bvnext(liveout, i)) >= 0; i++) {
- node = *(Node**)arrayget(vars, i);
- switch(node->class) {
- case PAUTO:
- xoffset = node->xoffset + stkptrsize;
- twobitwalktype1(node->type, &xoffset, locals);
- break;
- case PPARAM:
- case PPARAMOUT:
- 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.
- 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:", ((Node*)(p->to.node))->sym->name);
- else if(p->as == ACALL)
- fmtprint(&fmt, "indirect call:");
- else
- fmtprint(&fmt, "entry to %s:", ((Node*)(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
-/*c2go
-enum
-{
- H0 = 2166136261,
- Hp = 16777619,
-};
-*/
-
-static uint32
-hashbitmap(uint32 h, Bvec *bv)
-{
- int i, n;
- uint32 w;
-
- n = (bv->n+31)/32;
- for(i=0; i<n; i++) {
- 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.
-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, n;
- uint32 word;
-
- n = 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 < n; 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];
- // Runtime reads the bitmaps as byte arrays. Oblige.
- off = duint8(sym, off, word);
- off = duint8(sym, off, word>>8);
- off = duint8(sym, off, word>>16);
- off = duint8(sym, off, word>>24);
- }
- }
- duint32(sym, 0, i); // number of bitmaps
- ggloblsym(sym, off, RODATA);
-}
-
-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;
- NodeList *l;
-
- // 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.
- for(l=fn->dcl; l != nil; l = l->next)
- if(l->n != N)
- l->n->opt = nil;
- freeliveness(lv);
- arrayfree(vars);
- freecfg(cfg);
-
- debuglive -= debugdelta;
-}