// 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 #include #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; jlastbitmapindex; 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; jb; 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; ilivepointers, 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; jlivepointers, 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; isym->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; }