Bug 640318 - LIR control-flow graph gml output (r+nnethercote,wmaddox)

--HG--
extra : convert_revision : 45ca084d9cbe063ce96e2b0bd2f7df8a562d0b2f
This commit is contained in:
Rick Reitmaier 2011-05-04 08:48:53 -07:00
parent a7253cc393
commit 8880e03d8b
6 changed files with 457 additions and 34 deletions

View File

@ -89,7 +89,6 @@ namespace nanojit
if (mem) {
Chunk* chunk = (Chunk*) mem;
chunk->prev = current_chunk;
chunk->size = chunkbytes;
current_chunk = chunk;
current_top = (char*)chunk->data;
current_limit = (char*)mem + chunkbytes;
@ -99,17 +98,6 @@ namespace nanojit
return false;
}
}
size_t Allocator::getBytesAllocated()
{
size_t n = 0;
Chunk *c = current_chunk;
while (c) {
n += c->size;
c = c->prev;
}
return n;
}
}
#endif // FEATURE_NANOJIT

View File

@ -90,8 +90,6 @@ namespace nanojit
return p;
}
size_t getBytesAllocated();
protected:
void* allocSlow(size_t nbytes, bool fallible = false);
bool fill(size_t minbytes, bool fallible);
@ -99,7 +97,6 @@ namespace nanojit
class Chunk {
public:
Chunk* prev;
size_t size;
int64_t data[1]; // int64_t forces 8-byte alignment.
};

View File

@ -98,10 +98,10 @@ namespace nanojit
return (int)((x + 512) >> 10);
}
void CodeAlloc::getStats(size_t& total, size_t& frag_size, size_t& free_size) {
total = 0;
frag_size = 0;
free_size = 0;
void CodeAlloc::logStats() {
size_t total = 0;
size_t frag_size = 0;
size_t free_size = 0;
int free_count = 0;
for (CodeList* hb = heapblocks; hb != 0; hb = hb->next) {
total += bytesPerAlloc;
@ -114,11 +114,6 @@ namespace nanojit
}
}
}
}
void CodeAlloc::logStats() {
size_t total, frag_size, free_size;
getStats(total, frag_size, free_size);
avmplus::AvmLog("code-heap: %dk free %dk fragmented %d\n",
round(total), round(free_size), frag_size);
}

View File

@ -217,9 +217,6 @@ namespace nanojit
/** return the total number of bytes held by this CodeAlloc. */
size_t size();
/** get stats about heap usage */
void getStats(size_t& total, size_t& frag_size, size_t& free_size);
/** print out stats about heap usage */
void logStats();

View File

@ -157,6 +157,350 @@ namespace nanojit
_prevIns = ins;
return ins;
}
// build a control flow graph that can be used for display
CfgLister::CfgLister(LirFilter* in, Allocator& alloc, CfgMode mode)
: LirFilter(in), _alloc(alloc), _alt(alloc), _edges(alloc), _vertices(alloc), _ids(alloc), _mode(mode)
{
_count = 1; // real programmers start with 1
_prior = 0;
}
LIns* CfgLister::read()
{
bool priorAsVertex = false; // true, implies that the last processed instruction is a vertex
LIns *ins = in->read();
_ids.put(ins, _count++);
LOpcode op = ins->opcode();
LIns* target;
switch (op) {
case LIR_j:
case LIR_jt:
case LIR_jf:
case LIR_addjovi:
case LIR_subjovi:
case LIR_muljovi:
CASE64(LIR_addjovq:)
CASE64(LIR_subjovq:)
target = ins->getTarget();
addEdge(ins, target);
_vertices.put(target,true);
priorAsVertex = (_mode == CFG_BB);
break;
case LIR_jtbl: {
uint32_t tableSize = ins->getTableSize();
NanoAssert(tableSize > 0);
for (uint32_t i = 0; i < tableSize; i++)
{
target = ins->getTarget();
addEdge(ins, ins->getTarget());
_vertices.put(target,true);
}
priorAsVertex = (_mode == CFG_BB);
break;
}
default:
;
}
// each instruction is a vertex in mode CFG_INS (except imm's which are ignored)
priorAsVertex = ( _prior && _mode == CFG_INS && !_prior->isImmAny() ) ? true : priorAsVertex;
if (_prior && priorAsVertex)
_vertices.put(_prior,true);
_prior = ins;
return ins;
}
void CfgLister::addEdge(LIns* from, LIns* to)
{
InsList* list = _edges.get(from);
if (!list) {
list = new (_alloc) InsList(_alloc);
_edges.put(from,list);
}
NanoAssert( from && to && (from != to) ); // valid instructions
NanoAssert( _ids.containsKey(to) || to->isop(LIR_label) ); // we should have seen the instruction already or its a backwards jump to label
list->add(to);
// identify both sides of edge to later allow them to be re-mapped to other instructions
_alt.put(from,from);
_alt.put(to,to);
}
uint32_t CfgLister::node2id(LIns* i)
{
NanoAssert( _ids.containsKey(i) );
uint32_t id = _ids.get(i);
return _count - id;
}
const char* CfgLister::nodeName(LIns* i, InsBuf& b, LInsPrinter* printer)
{
// short names used in block per instruction mode
const char* str = ( _mode == CFG_INS ) ? printer->lirNameMap->lookupName(i) : 0;
str = !str ? printer->formatIns(&b, i) : str;
return str;
}
const char* CfgLister::nodeShape(LIns* i, InsSet* pseudos)
{
const char* shape = "roundrectangle";
if (i->isGuard())
shape = "hexagon";
else if ( pseudos->containsKey(i) )
shape = "ellipse";
else if ( edgeCountOf(i) > 1 )
shape = "diamond";
return shape;
}
uint32_t CfgLister::edgeCountOf(LIns* i)
{
uint32_t a = 0;
Seq<LIns*>* l = _edges.get(i) ? _edges.get(i)->get() : 0;
for(; l; l=l->tail)
a++;
return a;
}
void CfgLister::printGmlCfg(FILE* f, LInsPrinter* printer, InsSet* makeProxyNodesFor)
{
fprintf(f, "graph [ directed 1 hierarchic 1\n");
// now on the 2nd pass, walk each instruction and add
// it to a list until we reach the top of the block,
// at which point we print out the node contents.
// We also track any incoming edges into any instruction
// of this node and map them to the top instruction of the node,
// so that our edges get displayed correctly.
InsList n(_alloc); // running list of instruction for the block
InsSet incoming(_alloc); // running list of incoming edges into the block
bool last = true; // true, when we're processing the last instruction of a block
LIns* priorBlock = 0; // prior block that was just processed.
LIns* priorIns = 0; // last instruction encountered
LirReader rdr(finalIns());
LIns* ins = rdr.read();
for (; !ins->isop(LIR_start); ins = rdr.read())
{
// if last instruction of the block is not terminal then add a fall-thru edge
if (priorBlock && last)
{
last = false;
LOpcode op = ins->opcode();
if ( !ins->isUnConditionalBranch() && !isRetOpcode(op))
addEdge(ins, priorBlock); // fall-thru
}
// process new block?
if ( _vertices.containsKey(ins) )
{
// re-map all incoming edges to any instruction in the block, to the blocks' first instruction
InsSet::Iter inc(incoming);
while(inc.next())
_alt.put(inc.key(), ins);
n.insert(ins);
printNode(f, &n, printer, makeProxyNodesFor);
// ready to process the next block
n.clear();
incoming.clear();
priorBlock = ins;
last = true;
}
else
{
// add the instruction to our running list and note any incoming edges to the instruction
bool ignore = ins->isImmAny();
if (!ignore)
n.insert(ins);
if ( _alt.containsKey(ins) )
incoming.put(ins,true);
priorIns = ins;
}
}
// after once against re-mapping all edges, print the final node
InsSet::Iter inc(incoming);
while(inc.next())
_alt.put(inc.key(), ins);
n.insert(ins);
printNode(f, &n, printer, makeProxyNodesFor);
// now write out the edge list
printEdges(f, printer, makeProxyNodesFor);
fprintf(f, "]\n");
}
// prints a node that consists of a sequence of instructions, the first of which is used
// to populate the node attributes, such as shape.
void CfgLister::printNode(FILE* f, InsList* nodeIns, LInsPrinter* printer, InsSet* pseudo)
{
// first instruction in the list is the 'node ins'
Seq<LIns*>* list = nodeIns->get();
LIns* ins = list->head;
InsBuf str;
uint32_t id = node2id(ins);
const char* text = nodeName(ins, str, printer);
const char* shape = nodeShape(ins, pseudo);
gmlNodePrefix(f, id, shape);
gmlNodeTextLine(f, text, 0);
for (list=list->tail; list; list=list->tail)
{
LIns* i = list->head;
text = nodeName(i, str, printer);
gmlNodeTextLine(f, text, 1);
}
gmlNodeSuffix(f);
}
void CfgLister::printEdges(FILE* f, LInsPrinter* printer, InsSet* makeProxyNodesFor)
{
uint32_t pseudoId = ~0; // ids for proxy nodes
InsBuf str;
HashMap<LIns*, InsList*>::Iter ite(_edges);
while(ite.next())
{
LIns* origSrc = ite.key();
LIns* src = _alt.containsKey(origSrc) ? _alt.get(origSrc) : src;
uint32_t sid = node2id(src);
Seq<LIns*>* l = ite.value()->get();
while (l)
{
LIns* origDst = l->head;
LIns* dst = _alt.get(origDst); // destination mapping
uint32_t did = node2id(dst);
bool proxyNode = makeProxyNodesFor->containsKey(origDst);
if (_mode != CFG_INS && proxyNode)
{
// in most modes, edges to proxies are ignored.
l = l->tail;
continue;
}
// do we want to add a pseudo node for destination; removes clutter from the graph
if (proxyNode)
{
did = --pseudoId;
gmlNode(f, did, "ellipse", nodeName(dst, str, printer));
NanoAssert(did > _count); // graph is too large to render correctly
}
const char* str_dashed = "dashed";
const char* str_solid = "solid";
const char* str_red = "#E00000";
const char* str_gray = "#AAAAAA";
const char* text = 0;
const char* style = str_dashed;
const char* fill = str_gray;
const char* width = 0;
if ( origSrc->isConditionalBranch() )
{
bool explicitEdge = origSrc->getTarget() == origDst;
if (explicitEdge)
{
// this is the non fall-thru edge
style = str_solid;
fill = 0; //default fill
text = origSrc->isop(LIR_jf) ? "0" : "1";
}
else
{
// not taken edge
text = origSrc->isop(LIR_jf) ? "1" : "0";
}
}
if (did < sid)
{
// 'backwards' movements are red and large by default
style = str_solid;
fill = str_red;
width = "2";
}
gmlEdge(f, sid, did, style, fill, width, text);
l = l->tail;
}
}
}
void CfgLister::gmlNodePrefix(FILE* f, uint32_t id, const char* shape)
{
fprintf(f, " node [\n");
fprintf(f, " id %d\n", id);
fprintf(f, " graphics [\n");
fprintf(f, " type \"%s\"\n", shape);
fprintf(f, " ]\n");
fprintf(f, " LabelGraphics [\n");
fprintf(f, " alignment \"left\"\n");
fprintf(f, " fontName \"Consolas\"\n");
fprintf(f, " anchor \"tl\"\n"); // Top Left
fprintf(f, " text \"");
}
void CfgLister::gmlNodeTextLine(FILE* f, const char* text, int32_t tabCount)
{
while(tabCount-->0)
fprintf(f, "\t");
fprintf(f, "%s\n", text);
}
void CfgLister::gmlNodeSuffix(FILE* f)
{
fprintf(f, " \"\n");
fprintf(f, " ]\n");
fprintf(f, " ]\n");
}
void CfgLister::gmlNode(FILE* f, uint32_t id, const char* shape, const char* title)
{
fprintf(f, " node [\n");
fprintf(f, " id %d\n", id);
fprintf(f, " graphics [\n");
fprintf(f, " type \"%s\"\n", shape);
fprintf(f, " ]\n");
fprintf(f, " LabelGraphics [\n");
fprintf(f, " text \"%s\"\n", title);
fprintf(f, " alignment \"left\"\n");
fprintf(f, " fontName \"Consolas\"\n");
fprintf(f, " anchor \"tl\"\n"); // Top Left
fprintf(f, " ]\n");
fprintf(f, " ]\n");
}
void CfgLister::gmlEdge(FILE* f, uint32_t srcId, uint32_t dstId, const char* style, const char* fill, const char* width, const char* text)
{
fprintf(f, " edge [\n");
fprintf(f, " source %d\n", srcId);
fprintf(f, " target %d\n", dstId);
fprintf(f, " graphics [\n");
fprintf(f, " arrow \"last\"\n");
if (style) fprintf(f, " style \"%s\"\n", style);
if (fill) fprintf(f, " fill \"%s\"\n", fill);
if (width) fprintf(f, " width %s\n", width);
fprintf(f, " ]\n");
fprintf(f, " LabelGraphics [\n");
if (text) fprintf(f, " text \"%s\"\n", text);
fprintf(f, " model \"three_center\"\n");
fprintf(f, " fontStyle \"bold\"\n");
fprintf(f, " ]\n");
fprintf(f, " ]\n");
}
#endif
// LCompressedBuffer
@ -1376,7 +1720,7 @@ namespace nanojit
void retire(LIns* i) {
RetiredEntry *e = new (alloc) RetiredEntry();
e->i = i;
SeqBuilder<LIns*> livelist(alloc);
InsList livelist(alloc);
HashMap<LIns*, LIns*>::Iter iter(live);
int live_count = 0;
while (iter.next()) {
@ -3092,7 +3436,7 @@ namespace nanojit
LIns *args[] = { split(a) };
return out->insCall(call, args);
}
LIns* SoftFloatFilter::callD2(const CallInfo *call, LIns *a, LIns *b) {
LIns *args[] = { split(b), split(a) };
return split(call, args);
@ -3106,7 +3450,7 @@ namespace nanojit
LIns* SoftFloatFilter::ins1(LOpcode op, LIns *a) {
const CallInfo *ci = softFloatOps.opmap[op];
if (ci) {
if (ci->returnType() == ARGTYPE_D)
if (ci->returnType() == ARGTYPE_D)
return callD1(ci, a);
else
return callI1(ci, a);

View File

@ -745,7 +745,7 @@ namespace nanojit
if (isInReg()) {
Register r = { sharedFields.regnum };
return r;
} else {
} else {
return deprecated_UnknownReg;
}
}
@ -985,8 +985,16 @@ namespace nanojit
return isImmI() || isImmQorD();
}
bool isConditionalBranch() const {
return isop(LIR_jt) || isop(LIR_jf) || isJov();
}
bool isUnConditionalBranch() const {
return isop(LIR_j) || isop(LIR_jtbl);
}
bool isBranch() const {
return isop(LIR_jt) || isop(LIR_jf) || isop(LIR_j) || isop(LIR_jtbl) || isJov();
return isConditionalBranch() || isUnConditionalBranch();
}
LTy retType() const {
@ -1035,7 +1043,7 @@ namespace nanojit
typedef SeqBuilder<LIns*> InsList;
typedef SeqBuilder<char*> StringList;
typedef HashMap<LIns*,bool> InsSet;
// 0-operand form. Used for LIR_start and LIR_label.
class LInsOp0
@ -1990,7 +1998,7 @@ namespace nanojit
// be true, else we would have side-exited. So if we see 'cmp' again
// we can treat it like a constant. This table records such
// comparisons.
HashMap <LIns*, bool> knownCmpValues;
InsSet knownCmpValues;
// If true, we will not add new instructions to the CSE tables, but we
// will continue to CSE instructions that match existing table
@ -2469,6 +2477,100 @@ namespace nanojit
void finish();
LIns* read();
};
/**
* A reverse filter for LIR that generates a control-flow graph in gml format.
* More information on Graph Modelling Language (gml) can be found here:
* http://en.wikipedia.org/wiki/Graph_Modelling_Language
* An excellent tool for manipulating the graphs produced by this code
* is yED (http://www.yworks.com/en/products_yed_about.html).
*
* The raw output produced by this class contains connectively (i,e edge)
* information (including formatting), and node (i.e. vertex) data, but
* does not contain any positional information.
* Thus when opening the .gml file, all the nodes will likely appear stacked
* on one another. An auto-layout tool like yEd can then re-position the
* nodes for better viewing. E.g. Tools->Fit Node to Label followed by
* Layout->Hierarchical->Interactive produces a relatively convential
* looking flow-control graph.
*
* Usage:
*
* LirReader reader(frag->lastIns);
* CfgLister cfg(&reader, alloc);
*
* for (LIns* ins = cfg.read(); !ins->isop(LIR_start); ins = cfg.read()) {}
*
* cfg.printGmlCfg(f, frag->lirbuf->printer, proxiesFor);
* fclose(f);
*
* The first and second parameters to printGmlCfg() are an open FILE* and
* a populated LInsPrinter, respectively. The printer must be able to produce
* a name from either the lirNameMap or a call to formatIns().
*
* The 3rd parameter to primtGmlCfg(), proxiesFor can be used to limit
* the number of edges in the graph. If an edge points to a vertex (LIns) in
* this set, then a new node is created, with the same name as the original,
* and the edge will point to that node.
*
* Algortihm:
*
* During the first pass (i.e. CfgLister::read()), we capture edges and vertices
* at an instruction level. Recall that this is during a backwards pass, so for
* forward branches we won't know whether an instruction we've seen already is
* the target of a branch or not, until we reach the branch. I.e. we see the
* target instructions first and don't yet know they are targets.
*
* On the 2nd pass (i.e. printGmlCfg) we use the list of branch targets from the
* first pass (i.e. _vertices) and group instructions into them. This is where
* _alt comes into play, it provides a map from any instruction to the first
* instruction of the containing block.
*/
class CfgLister : public LirFilter
{
public:
virtual LIns* read();
typedef enum _CfgMode
{
CFG_EBB // extended basic blocks
, CFG_BB // basic blocks
, CFG_INS // 1 block per instruction
}
CfgMode;
CfgLister(LirFilter* in, Allocator& alloc, CfgMode mode=CFG_EBB);
void printGmlCfg(FILE* f, LInsPrinter* printer, InsSet* makeProxyNodesFor);
private:
void addEdge(LIns* from, LIns* to);
uint32_t node2id(LIns* i);
const char* nodeName(LIns* i, InsBuf& b, LInsPrinter* printer);
const char* nodeShape(LIns* i, InsSet* pseudo);
uint32_t edgeCountOf(LIns* i);
void printEdges(FILE* f, LInsPrinter* printer, InsSet* pseudo);
void printNode(FILE* f, InsList* nodeIns, LInsPrinter* printer, InsSet* pseudo);
void gmlNode(FILE* f, uint32_t id, const char* shape, const char* title);
void gmlEdge(FILE* f, uint32_t srcId, uint32_t dstId, const char* style, const char* fill, const char* width, const char* text);
//alternate form of gmlNode
void gmlNodePrefix(FILE* f, uint32_t id, const char* shape);
void gmlNodeSuffix(FILE* f);
void gmlNodeTextLine(FILE* f, const char* text, int32_t tabCount);
Allocator& _alloc;
HashMap<LIns*, LIns*> _alt; // allow edge src/dst to be re-mapped to another instruction
HashMap<LIns*, InsList*> _edges; // from,to list
InsSet _vertices; // node list
HashMap<LIns*, uint32_t> _ids; // ins -> unique id
LIns* _prior; // state maintained during read()
uint32_t _count; // state maintained during read()
CfgMode _mode; // mode selector
};
#endif
}