8283775: better dump: VM support for graph querying in debugger with BFS traversal and node filtering
Reviewed-by: kvn, chagedorn, thartmann, rcastanedalo
This commit is contained in:
parent
ac28be721f
commit
33ed0365c3
@ -192,13 +192,18 @@ uint ReturnNode::match_edge(uint idx) const {
|
||||
|
||||
|
||||
#ifndef PRODUCT
|
||||
void ReturnNode::dump_req(outputStream *st) const {
|
||||
// Dump the required inputs, enclosed in '(' and ')'
|
||||
void ReturnNode::dump_req(outputStream *st, DumpConfig* dc) const {
|
||||
// Dump the required inputs, after printing "returns"
|
||||
uint i; // Exit value of loop
|
||||
for (i = 0; i < req(); i++) { // For all required inputs
|
||||
if (i == TypeFunc::Parms) st->print("returns");
|
||||
if (in(i)) st->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx);
|
||||
else st->print("_ ");
|
||||
if (i == TypeFunc::Parms) st->print("returns ");
|
||||
Node* p = in(i);
|
||||
if (p != nullptr) {
|
||||
p->dump_idx(false, st, dc);
|
||||
st->print(" ");
|
||||
} else {
|
||||
st->print("_ ");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -235,13 +240,18 @@ uint RethrowNode::match_edge(uint idx) const {
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
void RethrowNode::dump_req(outputStream *st) const {
|
||||
// Dump the required inputs, enclosed in '(' and ')'
|
||||
void RethrowNode::dump_req(outputStream *st, DumpConfig* dc) const {
|
||||
// Dump the required inputs, after printing "exception"
|
||||
uint i; // Exit value of loop
|
||||
for (i = 0; i < req(); i++) { // For all required inputs
|
||||
if (i == TypeFunc::Parms) st->print("exception");
|
||||
if (in(i)) st->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx);
|
||||
else st->print("_ ");
|
||||
if (i == TypeFunc::Parms) st->print("exception ");
|
||||
Node* p = in(i);
|
||||
if (p != nullptr) {
|
||||
p->dump_idx(false, st, dc);
|
||||
st->print(" ");
|
||||
} else {
|
||||
st->print("_ ");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -689,13 +699,18 @@ int JVMState::interpreter_frame_size() const {
|
||||
bool CallNode::cmp( const Node &n ) const
|
||||
{ return _tf == ((CallNode&)n)._tf && _jvms == ((CallNode&)n)._jvms; }
|
||||
#ifndef PRODUCT
|
||||
void CallNode::dump_req(outputStream *st) const {
|
||||
void CallNode::dump_req(outputStream *st, DumpConfig* dc) const {
|
||||
// Dump the required inputs, enclosed in '(' and ')'
|
||||
uint i; // Exit value of loop
|
||||
for (i = 0; i < req(); i++) { // For all required inputs
|
||||
if (i == TypeFunc::Parms) st->print("(");
|
||||
if (in(i)) st->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx);
|
||||
else st->print("_ ");
|
||||
Node* p = in(i);
|
||||
if (p != nullptr) {
|
||||
p->dump_idx(false, st, dc);
|
||||
st->print(" ");
|
||||
} else {
|
||||
st->print("_ ");
|
||||
}
|
||||
}
|
||||
st->print(")");
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ public:
|
||||
virtual uint ideal_reg() const { return NotAMachineReg; }
|
||||
virtual uint match_edge(uint idx) const;
|
||||
#ifndef PRODUCT
|
||||
virtual void dump_req(outputStream *st = tty) const;
|
||||
virtual void dump_req(outputStream *st = tty, DumpConfig* dc = nullptr) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -148,7 +148,7 @@ class RethrowNode : public Node {
|
||||
virtual uint match_edge(uint idx) const;
|
||||
virtual uint ideal_reg() const { return NotAMachineReg; }
|
||||
#ifndef PRODUCT
|
||||
virtual void dump_req(outputStream *st = tty) const;
|
||||
virtual void dump_req(outputStream *st = tty, DumpConfig* dc = nullptr) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -655,7 +655,7 @@ public:
|
||||
virtual void copy_call_debug_info(PhaseIterGVN* phase, SafePointNode* sfpt) {}
|
||||
|
||||
#ifndef PRODUCT
|
||||
virtual void dump_req(outputStream* st = tty) const;
|
||||
virtual void dump_req(outputStream* st = tty, DumpConfig* dc = nullptr) const;
|
||||
virtual void dump_spec(outputStream* st) const;
|
||||
#endif
|
||||
};
|
||||
|
@ -1699,6 +1699,688 @@ bool Node::add_to_worklist(Node* n, Node_List* worklist, Arena* old_arena, Vecto
|
||||
return false;
|
||||
}
|
||||
|
||||
class PrintBFS {
|
||||
public:
|
||||
PrintBFS(Node* start, const int max_distance, Node* target, const char* options)
|
||||
: _start(start), _max_distance(max_distance), _target(target), _options(options),
|
||||
_dcc(this), _info_uid(cmpkey, hashkey) {}
|
||||
|
||||
void run();
|
||||
private:
|
||||
// pipeline steps
|
||||
bool configure();
|
||||
void collect();
|
||||
void select();
|
||||
void select_all();
|
||||
void select_all_paths();
|
||||
void select_shortest_path();
|
||||
void sort();
|
||||
void print();
|
||||
|
||||
// inputs
|
||||
Node* _start;
|
||||
const int _max_distance;
|
||||
Node* _target;
|
||||
const char* _options;
|
||||
|
||||
// options
|
||||
bool _traverse_inputs = false;
|
||||
bool _traverse_outputs = false;
|
||||
struct Filter {
|
||||
bool _control = false;
|
||||
bool _memory = false;
|
||||
bool _data = false;
|
||||
bool _mixed = false;
|
||||
bool _other = false;
|
||||
bool is_empty() const {
|
||||
return !(_control || _memory || _data || _mixed || _other);
|
||||
}
|
||||
void set_all() {
|
||||
_control = true;
|
||||
_memory = true;
|
||||
_data = true;
|
||||
_mixed = true;
|
||||
_other = true;
|
||||
}
|
||||
};
|
||||
Filter _filter_visit;
|
||||
Filter _filter_boundary;
|
||||
bool _sort_idx = false;
|
||||
bool _all_paths = false;
|
||||
bool _use_color = false;
|
||||
bool _print_blocks = false;
|
||||
bool _print_old = false;
|
||||
static void print_options_help(bool print_examples);
|
||||
bool parse_options();
|
||||
|
||||
// node category (filter / color)
|
||||
static bool filter_category(Node* n, Filter& filter); // filter node category against options
|
||||
public:
|
||||
class DumpConfigColored : public Node::DumpConfig {
|
||||
public:
|
||||
DumpConfigColored(PrintBFS* bfs) : _bfs(bfs) {};
|
||||
virtual void pre_dump(outputStream* st, const Node* n);
|
||||
virtual void post_dump(outputStream* st);
|
||||
private:
|
||||
PrintBFS* _bfs;
|
||||
};
|
||||
private:
|
||||
DumpConfigColored _dcc;
|
||||
|
||||
// node info
|
||||
static Node* old_node(Node* n); // mach node -> prior IR node
|
||||
static void print_node_idx(Node* n); // to tty
|
||||
static void print_block_id(Block* b); // to tty
|
||||
static void print_node_block(Node* n); // to tty: _pre_order, head idx, _idom, _dom_depth
|
||||
|
||||
// traversal data structures
|
||||
Node_List _worklist; // BFS queue
|
||||
void maybe_traverse(Node* src, Node* dst);
|
||||
|
||||
// node info annotation
|
||||
class Info {
|
||||
public:
|
||||
Info() : Info(nullptr, 0) {};
|
||||
Info(Node* node, int distance)
|
||||
: _node(node), _distance_from_start(distance) {};
|
||||
Node* node() { return _node; };
|
||||
int distance() const { return _distance_from_start; };
|
||||
int distance_from_target() const { return _distance_from_target; }
|
||||
void set_distance_from_target(int d) { _distance_from_target = d; }
|
||||
Node_List edge_bwd; // pointing toward _start
|
||||
bool is_marked() const { return _mark; } // marked to keep during select
|
||||
void set_mark() { _mark = true; }
|
||||
private:
|
||||
Node* _node;
|
||||
int _distance_from_start; // distance from _start
|
||||
int _distance_from_target = 0; // distance from _target if _all_paths
|
||||
bool _mark = false;
|
||||
};
|
||||
Dict _info_uid; // Node -> uid
|
||||
GrowableArray<Info> _info; // uid -> info
|
||||
|
||||
Info* find_info(const Node* n) {
|
||||
size_t uid = (size_t)_info_uid[n];
|
||||
if (uid == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return &_info.at((int)uid);
|
||||
}
|
||||
|
||||
void make_info(Node* node, const int distance) {
|
||||
assert(find_info(node) == nullptr, "node does not yet have info");
|
||||
size_t uid = _info.length() + 1;
|
||||
_info_uid.Insert(node, (void*)uid);
|
||||
_info.at_put_grow((int)uid, Info(node, distance));
|
||||
assert(find_info(node)->node() == node, "stored correct node");
|
||||
};
|
||||
|
||||
// filled by sort, printed by print
|
||||
GrowableArray<Node*> _print_list;
|
||||
|
||||
// print header + node table
|
||||
void print_header() const;
|
||||
void print_node(Node* n);
|
||||
};
|
||||
|
||||
void PrintBFS::run() {
|
||||
if (!configure()) {
|
||||
return;
|
||||
}
|
||||
collect();
|
||||
select();
|
||||
sort();
|
||||
print();
|
||||
}
|
||||
|
||||
// set up configuration for BFS and print
|
||||
bool PrintBFS::configure() {
|
||||
if (_max_distance < 0) {
|
||||
tty->print("dump_bfs: max_distance must be non-negative!\n");
|
||||
return false;
|
||||
}
|
||||
return parse_options();
|
||||
}
|
||||
|
||||
// BFS traverse according to configuration, fill worklist and info
|
||||
void PrintBFS::collect() {
|
||||
maybe_traverse(_start, _start);
|
||||
uint pos = 0;
|
||||
while (pos < _worklist.size()) {
|
||||
Node* n = _worklist.at(pos++); // next node to traverse
|
||||
Info* info = find_info(n);
|
||||
if (!filter_category(n, _filter_visit) && n != _start) {
|
||||
continue; // we hit boundary, do not traverse further
|
||||
}
|
||||
if (n != _start && n->is_Root()) {
|
||||
continue; // traversing through root node would lead to unrelated nodes
|
||||
}
|
||||
if (_traverse_inputs && _max_distance > info->distance()) {
|
||||
for (uint i = 0; i < n->req(); i++) {
|
||||
maybe_traverse(n, n->in(i));
|
||||
}
|
||||
}
|
||||
if (_traverse_outputs && _max_distance > info->distance()) {
|
||||
for (uint i = 0; i < n->outcnt(); i++) {
|
||||
maybe_traverse(n, n->raw_out(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// go through work list, mark those that we want to print
|
||||
void PrintBFS::select() {
|
||||
if (_target == nullptr ) {
|
||||
select_all();
|
||||
} else {
|
||||
if (find_info(_target) == nullptr) {
|
||||
tty->print("Could not find target in BFS.\n");
|
||||
return;
|
||||
}
|
||||
if (_all_paths) {
|
||||
select_all_paths();
|
||||
} else {
|
||||
select_shortest_path();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// take all nodes from BFS
|
||||
void PrintBFS::select_all() {
|
||||
for (uint i = 0; i < _worklist.size(); i++) {
|
||||
Node* n = _worklist.at(i);
|
||||
Info* info = find_info(n);
|
||||
info->set_mark();
|
||||
}
|
||||
}
|
||||
|
||||
// traverse backward from target, along edges found in BFS
|
||||
void PrintBFS::select_all_paths() {
|
||||
uint pos = 0;
|
||||
Node_List backtrace;
|
||||
// start from target
|
||||
backtrace.push(_target);
|
||||
find_info(_target)->set_mark();
|
||||
// traverse backward
|
||||
while (pos < backtrace.size()) {
|
||||
Node* n = backtrace.at(pos++);
|
||||
Info* info = find_info(n);
|
||||
for (uint i = 0; i < info->edge_bwd.size(); i++) {
|
||||
// all backward edges
|
||||
Node* back = info->edge_bwd.at(i);
|
||||
Info* back_info = find_info(back);
|
||||
if (!back_info->is_marked()) {
|
||||
// not yet found this on way back.
|
||||
back_info->set_distance_from_target(info->distance_from_target() + 1);
|
||||
if (back_info->distance_from_target() + back_info->distance() <= _max_distance) {
|
||||
// total distance is small enough
|
||||
back_info->set_mark();
|
||||
backtrace.push(back);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintBFS::select_shortest_path() {
|
||||
Node* current = _target;
|
||||
while (true) {
|
||||
Info* info = find_info(current);
|
||||
info->set_mark();
|
||||
if (current == _start) {
|
||||
break;
|
||||
}
|
||||
// first edge -> leads us one step closer to _start
|
||||
current = info->edge_bwd.at(0);
|
||||
}
|
||||
}
|
||||
|
||||
int node_idx_cmp(Node** n1, Node** n2) {
|
||||
return (*n1)->_idx - (*n2)->_idx;
|
||||
}
|
||||
|
||||
// go through worklist in desired order, put the marked ones in print list
|
||||
void PrintBFS::sort() {
|
||||
if (_traverse_inputs && !_traverse_outputs) {
|
||||
// reverse order
|
||||
for (int i = _worklist.size() - 1; i >= 0; i--) {
|
||||
Node* n = _worklist.at(i);
|
||||
Info* info = find_info(n);
|
||||
if (info->is_marked()) {
|
||||
_print_list.push(n);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// same order as worklist
|
||||
for (uint i = 0; i < _worklist.size(); i++) {
|
||||
Node* n = _worklist.at(i);
|
||||
Info* info = find_info(n);
|
||||
if (info->is_marked()) {
|
||||
_print_list.push(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_sort_idx) {
|
||||
_print_list.sort(node_idx_cmp);
|
||||
}
|
||||
}
|
||||
|
||||
// go through printlist and print
|
||||
void PrintBFS::print() {
|
||||
if (_print_list.length() > 0 ) {
|
||||
print_header();
|
||||
for (int i = 0; i < _print_list.length(); i++) {
|
||||
Node* n = _print_list.at(i);
|
||||
print_node(n);
|
||||
}
|
||||
} else {
|
||||
tty->print("No nodes to print.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void PrintBFS::print_options_help(bool print_examples) {
|
||||
tty->print("Usage: node->dump_bfs(int max_distance, Node* target, char* options)\n");
|
||||
tty->print("\n");
|
||||
tty->print("Use cases:\n");
|
||||
tty->print(" BFS traversal: no target required\n");
|
||||
tty->print(" shortest path: set target\n");
|
||||
tty->print(" all paths: set target and put 'A' in options\n");
|
||||
tty->print(" detect loop: subcase of all paths, have start==target\n");
|
||||
tty->print("\n");
|
||||
tty->print("Arguments:\n");
|
||||
tty->print(" this/start: staring point of BFS\n");
|
||||
tty->print(" target:\n");
|
||||
tty->print(" if nullptr: simple BFS\n");
|
||||
tty->print(" else: shortest path or all paths between this/start and target\n");
|
||||
tty->print(" options:\n");
|
||||
tty->print(" if nullptr: same as \"cdmxo@B\"\n");
|
||||
tty->print(" else: use combination of following characters\n");
|
||||
tty->print(" h: display this help info\n");
|
||||
tty->print(" H: display this help info, with examples\n");
|
||||
tty->print(" +: traverse in-edges (on if neither + nor -)\n");
|
||||
tty->print(" -: traverse out-edges\n");
|
||||
tty->print(" c: visit control nodes\n");
|
||||
tty->print(" m: visit memory nodes\n");
|
||||
tty->print(" d: visit data nodes\n");
|
||||
tty->print(" x: visit mixed nodes\n");
|
||||
tty->print(" o: visit other nodes\n");
|
||||
tty->print(" C: boundary control nodes\n");
|
||||
tty->print(" M: boundary memory nodes\n");
|
||||
tty->print(" D: boundary data nodes\n");
|
||||
tty->print(" X: boundary mixed nodes\n");
|
||||
tty->print(" O: boundary other nodes\n");
|
||||
tty->print(" S: sort displayed nodes by node idx\n");
|
||||
tty->print(" A: all paths (not just shortest path to target)\n");
|
||||
tty->print(" #: display node category in color (not supported in all terminals)\n");
|
||||
tty->print(" @: print old nodes - before matching (if available)\n");
|
||||
tty->print(" B: print scheduling blocks (if available)\n");
|
||||
tty->print("\n");
|
||||
tty->print("recursively follow edges to nodes with permitted visit types,\n");
|
||||
tty->print("on the boundary additionally display nodes allowed in boundary types\n");
|
||||
tty->print("\n");
|
||||
tty->print("output columns:\n");
|
||||
tty->print(" dist: BFS distance to this/start\n");
|
||||
tty->print(" apd: all paths distance (d_start + d_target)\n");
|
||||
tty->print(" block: block identifier, based on _pre_order\n");
|
||||
tty->print(" head: first node in block\n");
|
||||
tty->print(" idom: head node of idom block\n");
|
||||
tty->print(" depth: depth of block (_dom_depth)\n");
|
||||
tty->print(" old: old IR node - before matching\n");
|
||||
tty->print(" dump: node->dump()\n");
|
||||
tty->print("\n");
|
||||
tty->print("Note: if none of the \"cmdxo\" characters are in the options string\n");
|
||||
tty->print(" then we set all of them.\n");
|
||||
tty->print(" This allows for short strings like \"#\" for colored input traversal\n");
|
||||
tty->print(" or \"-#\" for colored output traversal.\n");
|
||||
if (print_examples) {
|
||||
tty->print("\n");
|
||||
tty->print("Examples:\n");
|
||||
tty->print(" if->dump_bfs(10, 0, \"+cxo\")\n");
|
||||
tty->print(" starting at some if node, traverse inputs recursively\n");
|
||||
tty->print(" only along control (mixed and other can also be control)\n");
|
||||
tty->print(" phi->dump_bfs(5, 0, \"-dxo\")\n");
|
||||
tty->print(" starting at phi node, traverse outputs recursively\n");
|
||||
tty->print(" only along data (mixed and other can also have data flow)\n");
|
||||
tty->print(" find_node(385)->dump_bfs(3, 0, \"cdmox+#@B\")\n");
|
||||
tty->print(" find inputs of node 385, up to 3 nodes up (+)\n");
|
||||
tty->print(" traverse all nodes (cdmox), use colors (#)\n");
|
||||
tty->print(" display old nodes and blocks, if they exist\n");
|
||||
tty->print(" useful call to start with\n");
|
||||
tty->print(" find_node(102)->dump_bfs(10, 0, \"dCDMOX-\")\n");
|
||||
tty->print(" find non-data dependencies of a data node\n");
|
||||
tty->print(" follow data node outputs until we find another category\n");
|
||||
tty->print(" node as the boundary\n");
|
||||
tty->print(" x->dump_bfs(10, y, 0)\n");
|
||||
tty->print(" find shortest path from x to y, along any edge or node\n");
|
||||
tty->print(" will not find a path if it is longer than 10\n");
|
||||
tty->print(" useful to find how x and y are related\n");
|
||||
tty->print(" find_node(741)->dump_bfs(20, find_node(746), \"c+\")\n");
|
||||
tty->print(" find shortest control path between two nodes\n");
|
||||
tty->print(" find_node(741)->dump_bfs(8, find_node(746), \"cdmxo+A\")\n");
|
||||
tty->print(" find all paths (A) between two nodes of length at most 8\n");
|
||||
tty->print(" find_node(741)->dump_bfs(7, find_node(741), \"c+A\")\n");
|
||||
tty->print(" find all control loops for this node\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool PrintBFS::parse_options() {
|
||||
if (_options == nullptr) {
|
||||
_options = "cmdxo@B"; // default options
|
||||
}
|
||||
size_t len = strlen(_options);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
switch (_options[i]) {
|
||||
case '+':
|
||||
_traverse_inputs = true;
|
||||
break;
|
||||
case '-':
|
||||
_traverse_outputs = true;
|
||||
break;
|
||||
case 'c':
|
||||
_filter_visit._control = true;
|
||||
break;
|
||||
case 'm':
|
||||
_filter_visit._memory = true;
|
||||
break;
|
||||
case 'd':
|
||||
_filter_visit._data = true;
|
||||
break;
|
||||
case 'x':
|
||||
_filter_visit._mixed = true;
|
||||
break;
|
||||
case 'o':
|
||||
_filter_visit._other = true;
|
||||
break;
|
||||
case 'C':
|
||||
_filter_boundary._control = true;
|
||||
break;
|
||||
case 'M':
|
||||
_filter_boundary._memory = true;
|
||||
break;
|
||||
case 'D':
|
||||
_filter_boundary._data = true;
|
||||
break;
|
||||
case 'X':
|
||||
_filter_boundary._mixed = true;
|
||||
break;
|
||||
case 'O':
|
||||
_filter_boundary._other = true;
|
||||
break;
|
||||
case 'S':
|
||||
_sort_idx = true;
|
||||
break;
|
||||
case 'A':
|
||||
_all_paths = true;
|
||||
break;
|
||||
case '#':
|
||||
_use_color = true;
|
||||
break;
|
||||
case 'B':
|
||||
_print_blocks = true;
|
||||
break;
|
||||
case '@':
|
||||
_print_old = true;
|
||||
break;
|
||||
case 'h':
|
||||
print_options_help(false);
|
||||
return false;
|
||||
case 'H':
|
||||
print_options_help(true);
|
||||
return false;
|
||||
default:
|
||||
tty->print_cr("dump_bfs: Unrecognized option \'%c\'", _options[i]);
|
||||
tty->print_cr("for help, run: find_node(0)->dump_bfs(0,0,\"H\")");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!_traverse_inputs && !_traverse_outputs) {
|
||||
_traverse_inputs = true;
|
||||
}
|
||||
if (_filter_visit.is_empty()) {
|
||||
_filter_visit.set_all();
|
||||
}
|
||||
Compile* C = Compile::current();
|
||||
_print_old &= (C->matcher() != nullptr); // only show old if there are new
|
||||
_print_blocks &= (C->cfg() != nullptr); // only show blocks if available
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PrintBFS::filter_category(Node* n, Filter& filter) {
|
||||
const Type* t = n->bottom_type();
|
||||
switch (t->category()) {
|
||||
case Type::Category::Data:
|
||||
return filter._data;
|
||||
case Type::Category::Memory:
|
||||
return filter._memory;
|
||||
case Type::Category::Mixed:
|
||||
return filter._mixed;
|
||||
case Type::Category::Control:
|
||||
return filter._control;
|
||||
case Type::Category::Other:
|
||||
return filter._other;
|
||||
case Type::Category::Undef:
|
||||
n->dump();
|
||||
assert(false, "category undef ??");
|
||||
default:
|
||||
n->dump();
|
||||
assert(false, "not covered");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PrintBFS::DumpConfigColored::pre_dump(outputStream* st, const Node* n) {
|
||||
if (!_bfs->_use_color) {
|
||||
return;
|
||||
}
|
||||
Info* info = _bfs->find_info(n);
|
||||
if (info == nullptr || !info->is_marked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Type* t = n->bottom_type();
|
||||
switch (t->category()) {
|
||||
case Type::Category::Data:
|
||||
st->print("\u001b[34m");
|
||||
break;
|
||||
case Type::Category::Memory:
|
||||
st->print("\u001b[32m");
|
||||
break;
|
||||
case Type::Category::Mixed:
|
||||
st->print("\u001b[35m");
|
||||
break;
|
||||
case Type::Category::Control:
|
||||
st->print("\u001b[31m");
|
||||
break;
|
||||
case Type::Category::Other:
|
||||
st->print("\u001b[33m");
|
||||
break;
|
||||
case Type::Category::Undef:
|
||||
n->dump();
|
||||
assert(false, "category undef ??");
|
||||
break;
|
||||
default:
|
||||
n->dump();
|
||||
assert(false, "not covered");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintBFS::DumpConfigColored::post_dump(outputStream* st) {
|
||||
if (!_bfs->_use_color) {
|
||||
return;
|
||||
}
|
||||
st->print("\u001b[0m"); // white
|
||||
}
|
||||
|
||||
Node* PrintBFS::old_node(Node* n) {
|
||||
Compile* C = Compile::current();
|
||||
if (C->matcher() == nullptr || !C->node_arena()->contains(n)) {
|
||||
return (Node*)nullptr;
|
||||
} else {
|
||||
return C->matcher()->find_old_node(n);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintBFS::print_node_idx(Node* n) {
|
||||
Compile* C = Compile::current();
|
||||
char buf[30];
|
||||
if (n == nullptr) {
|
||||
sprintf(buf,"_"); // null
|
||||
} else if (C->node_arena()->contains(n)) {
|
||||
sprintf(buf, "%d", n->_idx); // new node
|
||||
} else {
|
||||
sprintf(buf, "o%d", n->_idx); // old node
|
||||
}
|
||||
tty->print("%6s", buf);
|
||||
}
|
||||
|
||||
void PrintBFS::print_block_id(Block* b) {
|
||||
Compile* C = Compile::current();
|
||||
char buf[30];
|
||||
sprintf(buf, "B%d", b->_pre_order);
|
||||
tty->print("%7s", buf);
|
||||
}
|
||||
|
||||
void PrintBFS::print_node_block(Node* n) {
|
||||
Compile* C = Compile::current();
|
||||
Block* b = C->node_arena()->contains(n)
|
||||
? C->cfg()->get_block_for_node(n)
|
||||
: nullptr; // guard against old nodes
|
||||
if (b == nullptr) {
|
||||
tty->print(" _"); // Block
|
||||
tty->print(" _"); // head
|
||||
tty->print(" _"); // idom
|
||||
tty->print(" _"); // depth
|
||||
} else {
|
||||
print_block_id(b);
|
||||
print_node_idx(b->head());
|
||||
if (b->_idom) {
|
||||
print_node_idx(b->_idom->head());
|
||||
} else {
|
||||
tty->print(" _"); // idom
|
||||
}
|
||||
tty->print("%6d ", b->_dom_depth);
|
||||
}
|
||||
}
|
||||
|
||||
// filter, and add to worklist, add info, note traversal edges
|
||||
void PrintBFS::maybe_traverse(Node* src, Node* dst) {
|
||||
if (dst != nullptr &&
|
||||
(filter_category(dst, _filter_visit) ||
|
||||
filter_category(dst, _filter_boundary) ||
|
||||
dst == _start)) { // correct category or start?
|
||||
if (find_info(dst) == nullptr) {
|
||||
// never visited - set up info
|
||||
_worklist.push(dst);
|
||||
int d = 0;
|
||||
if (dst != _start) {
|
||||
d = find_info(src)->distance() + 1;
|
||||
}
|
||||
make_info(dst, d);
|
||||
}
|
||||
if (src != dst) {
|
||||
// traversal edges useful during select
|
||||
find_info(dst)->edge_bwd.push(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintBFS::print_header() const {
|
||||
tty->print("dist"); // distance
|
||||
if (_all_paths) {
|
||||
tty->print(" apd"); // all paths distance
|
||||
}
|
||||
if (_print_blocks) {
|
||||
tty->print(" [block head idom depth]"); // block
|
||||
}
|
||||
if (_print_old) {
|
||||
tty->print(" old"); // old node
|
||||
}
|
||||
tty->print(" dump\n"); // node dump
|
||||
tty->print("---------------------------------------------\n");
|
||||
}
|
||||
|
||||
void PrintBFS::print_node(Node* n) {
|
||||
tty->print("%4d", find_info(n)->distance());// distance
|
||||
if (_all_paths) {
|
||||
Info* info = find_info(n);
|
||||
int apd = info->distance() + info->distance_from_target();
|
||||
tty->print("%4d", apd); // all paths distance
|
||||
}
|
||||
if (_print_blocks) {
|
||||
print_node_block(n); // block
|
||||
}
|
||||
if (_print_old) {
|
||||
print_node_idx(old_node(n)); // old node
|
||||
}
|
||||
tty->print(" ");
|
||||
n->dump("\n", false, tty, &_dcc); // node dump
|
||||
}
|
||||
|
||||
//------------------------------dump_bfs--------------------------------------
|
||||
// Call this from debugger
|
||||
// Useful for BFS traversal, shortest path, all path, loop detection, etc
|
||||
// Designed to be more readable, and provide additional info
|
||||
// To find all options, run:
|
||||
// find_node(0)->dump_bfs(0,0,"H")
|
||||
void Node::dump_bfs(const int max_distance, Node* target, const char* options) {
|
||||
PrintBFS bfs(this, max_distance, target, options);
|
||||
bfs.run();
|
||||
}
|
||||
|
||||
// Call this from debugger, with default arguments
|
||||
void Node::dump_bfs(const int max_distance) {
|
||||
dump_bfs(max_distance, nullptr, nullptr);
|
||||
}
|
||||
|
||||
// log10 rounded down
|
||||
uint log10(const uint i) {
|
||||
uint v = 10;
|
||||
uint e = 0;
|
||||
while(v <= i) {
|
||||
v *= 10;
|
||||
e++;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
// -----------------------------dump_idx---------------------------------------
|
||||
void Node::dump_idx(bool align, outputStream* st, DumpConfig* dc) const {
|
||||
if (dc != nullptr) {
|
||||
dc->pre_dump(st, this);
|
||||
}
|
||||
Compile* C = Compile::current();
|
||||
bool is_new = C->node_arena()->contains(this);
|
||||
if (align) { // print prefix empty spaces$
|
||||
// +1 for leading digit, +1 for "o"
|
||||
uint max_width = log10(C->unique()) + 2;
|
||||
// +1 for leading digit, maybe +1 for "o"
|
||||
uint width = log10(_idx) + 1 + (is_new ? 0 : 1);
|
||||
while (max_width > width) {
|
||||
st->print(" ");
|
||||
width++;
|
||||
}
|
||||
}
|
||||
if (!is_new) {
|
||||
st->print("o");
|
||||
}
|
||||
st->print("%d", _idx);
|
||||
if (dc != nullptr) {
|
||||
dc->post_dump(st);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------dump_name--------------------------------------
|
||||
void Node::dump_name(outputStream* st, DumpConfig* dc) const {
|
||||
if (dc != nullptr) {
|
||||
dc->pre_dump(st, this);
|
||||
}
|
||||
st->print("%s", Name());
|
||||
if (dc != nullptr) {
|
||||
dc->post_dump(st);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------Name-------------------------------------------
|
||||
extern const char *NodeClassNames[];
|
||||
const char *Node::Name() const { return NodeClassNames[Opcode()]; }
|
||||
@ -1769,7 +2451,7 @@ void Node::set_debug_orig(Node* orig) {
|
||||
|
||||
//------------------------------dump------------------------------------------
|
||||
// Dump a Node
|
||||
void Node::dump(const char* suffix, bool mark, outputStream *st) const {
|
||||
void Node::dump(const char* suffix, bool mark, outputStream* st, DumpConfig* dc) const {
|
||||
Compile* C = Compile::current();
|
||||
bool is_new = C->node_arena()->contains(this);
|
||||
C->_in_dump_cnt++;
|
||||
@ -1778,13 +2460,17 @@ void Node::dump(const char* suffix, bool mark, outputStream *st) const {
|
||||
st->print("%*s", (_indent << 1), " ");
|
||||
}
|
||||
|
||||
st->print("%c%d%s%s === ", is_new ? ' ' : 'o', _idx, mark ? " >" : " ", Name());
|
||||
// idx mark name ===
|
||||
dump_idx(true, st, dc);
|
||||
st->print(mark ? " >" : " ");
|
||||
dump_name(st, dc);
|
||||
st->print(" === ");
|
||||
|
||||
// Dump the required and precedence inputs
|
||||
dump_req(st);
|
||||
dump_prec(st);
|
||||
dump_req(st, dc);
|
||||
dump_prec(st, dc);
|
||||
// Dump the outputs
|
||||
dump_out(st);
|
||||
dump_out(st, dc);
|
||||
|
||||
if (is_disconnected(this)) {
|
||||
#ifdef ASSERT
|
||||
@ -1851,7 +2537,7 @@ void Node::dump(const char* suffix, bool mark, outputStream *st) const {
|
||||
}
|
||||
|
||||
//------------------------------dump_req--------------------------------------
|
||||
void Node::dump_req(outputStream *st) const {
|
||||
void Node::dump_req(outputStream* st, DumpConfig* dc) const {
|
||||
// Dump the required input edges
|
||||
for (uint i = 0; i < req(); i++) { // For all required inputs
|
||||
Node* d = in(i);
|
||||
@ -1860,14 +2546,15 @@ void Node::dump_req(outputStream *st) const {
|
||||
} else if (not_a_node(d)) {
|
||||
st->print("not_a_node "); // uninitialized, sentinel, garbage, etc.
|
||||
} else {
|
||||
st->print("%c%d ", Compile::current()->node_arena()->contains(d) ? ' ' : 'o', d->_idx);
|
||||
d->dump_idx(false, st, dc);
|
||||
st->print(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------dump_prec-------------------------------------
|
||||
void Node::dump_prec(outputStream *st) const {
|
||||
void Node::dump_prec(outputStream* st, DumpConfig* dc) const {
|
||||
// Dump the precedence edges
|
||||
int any_prec = 0;
|
||||
for (uint i = req(); i < len(); i++) { // For all precedence inputs
|
||||
@ -1875,15 +2562,16 @@ void Node::dump_prec(outputStream *st) const {
|
||||
if (p != NULL) {
|
||||
if (!any_prec++) st->print(" |");
|
||||
if (not_a_node(p)) { st->print("not_a_node "); continue; }
|
||||
st->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx);
|
||||
p->dump_idx(false, st, dc);
|
||||
st->print(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------dump_out--------------------------------------
|
||||
void Node::dump_out(outputStream *st) const {
|
||||
void Node::dump_out(outputStream* st, DumpConfig* dc) const {
|
||||
// Delimit the output edges
|
||||
st->print(" [[");
|
||||
st->print(" [[ ");
|
||||
// Dump the output edges
|
||||
for (uint i = 0; i < _outcnt; i++) { // For all outputs
|
||||
Node* u = _out[i];
|
||||
@ -1892,7 +2580,8 @@ void Node::dump_out(outputStream *st) const {
|
||||
} else if (not_a_node(u)) {
|
||||
st->print("not_a_node ");
|
||||
} else {
|
||||
st->print("%c%d ", Compile::current()->node_arena()->contains(u) ? ' ' : 'o', u->_idx);
|
||||
u->dump_idx(false, st, dc);
|
||||
st->print(" ");
|
||||
}
|
||||
}
|
||||
st->print("]] ");
|
||||
|
@ -1196,16 +1196,26 @@ public:
|
||||
public:
|
||||
Node* find(int idx, bool only_ctrl = false); // Search the graph for the given idx.
|
||||
Node* find_ctrl(int idx); // Search control ancestors for the given idx.
|
||||
void dump_bfs(const int max_distance, Node* target, const char* options); // Print BFS traversal
|
||||
void dump_bfs(const int max_distance); // dump_bfs(max_distance, nullptr, nullptr)
|
||||
class DumpConfig {
|
||||
public:
|
||||
// overridden to implement coloring of node idx
|
||||
virtual void pre_dump(outputStream *st, const Node* n) = 0;
|
||||
virtual void post_dump(outputStream *st) = 0;
|
||||
};
|
||||
void dump_idx(bool align = false, outputStream* st = tty, DumpConfig* dc = nullptr) const;
|
||||
void dump_name(outputStream* st = tty, DumpConfig* dc = nullptr) const;
|
||||
void dump() const { dump("\n"); } // Print this node.
|
||||
void dump(const char* suffix, bool mark = false, outputStream *st = tty) const; // Print this node.
|
||||
void dump(const char* suffix, bool mark = false, outputStream* st = tty, DumpConfig* dc = nullptr) const; // Print this node.
|
||||
void dump(int depth) const; // Print this node, recursively to depth d
|
||||
void dump_ctrl(int depth) const; // Print control nodes, to depth d
|
||||
void dump_comp() const; // Print this node in compact representation.
|
||||
// Print this node in compact representation.
|
||||
void dump_comp(const char* suffix, outputStream *st = tty) const;
|
||||
virtual void dump_req(outputStream *st = tty) const; // Print required-edge info
|
||||
virtual void dump_prec(outputStream *st = tty) const; // Print precedence-edge info
|
||||
virtual void dump_out(outputStream *st = tty) const; // Print the output edge info
|
||||
virtual void dump_req(outputStream* st = tty, DumpConfig* dc = nullptr) const; // Print required-edge info
|
||||
virtual void dump_prec(outputStream* st = tty, DumpConfig* dc = nullptr) const; // Print precedence-edge info
|
||||
virtual void dump_out(outputStream* st = tty, DumpConfig* dc = nullptr) const; // Print the output edge info
|
||||
virtual void dump_spec(outputStream *st) const {}; // Print per-node info
|
||||
// Print compact per-node info
|
||||
virtual void dump_compact_spec(outputStream *st) const { dump_spec(st); }
|
||||
|
Loading…
x
Reference in New Issue
Block a user