idealGraphPrinter.cpp revision 222:2a1a77d3458f
1/*
2 * Copyright 2007 Sun Microsystems, Inc.  All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 *
23 */
24
25#include "incls/_precompiled.incl"
26#include "incls/_idealGraphPrinter.cpp.incl"
27
28#ifndef PRODUCT
29
30// Constants
31// Keep consistent with Java constants
32const char *IdealGraphPrinter::INDENT = "  ";
33const char *IdealGraphPrinter::TOP_ELEMENT = "graphDocument";
34const char *IdealGraphPrinter::GROUP_ELEMENT = "group";
35const char *IdealGraphPrinter::GRAPH_ELEMENT = "graph";
36const char *IdealGraphPrinter::PROPERTIES_ELEMENT = "properties";
37const char *IdealGraphPrinter::EDGES_ELEMENT = "edges";
38const char *IdealGraphPrinter::PROPERTY_ELEMENT = "p";
39const char *IdealGraphPrinter::EDGE_ELEMENT = "edge";
40const char *IdealGraphPrinter::NODE_ELEMENT = "node";
41const char *IdealGraphPrinter::NODES_ELEMENT = "nodes";
42const char *IdealGraphPrinter::REMOVE_EDGE_ELEMENT = "removeEdge";
43const char *IdealGraphPrinter::REMOVE_NODE_ELEMENT = "removeNode";
44const char *IdealGraphPrinter::METHOD_NAME_PROPERTY = "name";
45const char *IdealGraphPrinter::METHOD_IS_PUBLIC_PROPERTY = "public";
46const char *IdealGraphPrinter::METHOD_IS_STATIC_PROPERTY = "static";
47const char *IdealGraphPrinter::TRUE_VALUE = "true";
48const char *IdealGraphPrinter::NODE_NAME_PROPERTY = "name";
49const char *IdealGraphPrinter::EDGE_NAME_PROPERTY = "name";
50const char *IdealGraphPrinter::NODE_ID_PROPERTY = "id";
51const char *IdealGraphPrinter::FROM_PROPERTY = "from";
52const char *IdealGraphPrinter::TO_PROPERTY = "to";
53const char *IdealGraphPrinter::PROPERTY_NAME_PROPERTY = "name";
54const char *IdealGraphPrinter::GRAPH_NAME_PROPERTY = "name";
55const char *IdealGraphPrinter::INDEX_PROPERTY = "index";
56const char *IdealGraphPrinter::METHOD_ELEMENT = "method";
57const char *IdealGraphPrinter::INLINE_ELEMENT = "inline";
58const char *IdealGraphPrinter::BYTECODES_ELEMENT = "bytecodes";
59const char *IdealGraphPrinter::METHOD_BCI_PROPERTY = "bci";
60const char *IdealGraphPrinter::METHOD_SHORT_NAME_PROPERTY = "shortName";
61const char *IdealGraphPrinter::CONTROL_FLOW_ELEMENT = "controlFlow";
62const char *IdealGraphPrinter::BLOCK_NAME_PROPERTY = "name";
63const char *IdealGraphPrinter::BLOCK_DOMINATOR_PROPERTY = "dom";
64const char *IdealGraphPrinter::BLOCK_ELEMENT = "block";
65const char *IdealGraphPrinter::SUCCESSORS_ELEMENT = "successors";
66const char *IdealGraphPrinter::SUCCESSOR_ELEMENT = "successor";
67const char *IdealGraphPrinter::ASSEMBLY_ELEMENT = "assembly";
68
69int IdealGraphPrinter::_file_count = 0;
70
71IdealGraphPrinter *IdealGraphPrinter::printer() {
72  if (PrintIdealGraphLevel == 0) return NULL;
73
74  JavaThread *thread = JavaThread::current();
75  if (!thread->is_Compiler_thread()) return NULL;
76
77  CompilerThread *compiler_thread = (CompilerThread *)thread;
78  if (compiler_thread->ideal_graph_printer() == NULL) {
79    IdealGraphPrinter *printer = new IdealGraphPrinter();
80    compiler_thread->set_ideal_graph_printer(printer);
81  }
82
83  return compiler_thread->ideal_graph_printer();
84}
85
86void IdealGraphPrinter::clean_up() {
87  JavaThread *p;
88  for (p = Threads::first(); p; p = p->next()) {
89    if (p->is_Compiler_thread()) {
90      CompilerThread *c = (CompilerThread *)p;
91      IdealGraphPrinter *printer = c->ideal_graph_printer();
92      if (printer) {
93        delete printer;
94      }
95      c->set_ideal_graph_printer(NULL);
96    }
97  }
98}
99
100// Constructor, either file or network output
101IdealGraphPrinter::IdealGraphPrinter() {
102
103  // By default dump both ins and outs since dead or unreachable code
104  // needs to appear in the graph.  There are also some special cases
105  // in the mach where kill projections have no users but should
106  // appear in the dump.
107  _traverse_outs = true;
108  _should_send_method = true;
109  _output = NULL;
110  buffer[0] = 0;
111  _depth = 0;
112  _current_method = NULL;
113  assert(!_current_method, "current method must be initialized to NULL");
114  _stream = NULL;
115
116  if (PrintIdealGraphFile != NULL) {
117    ThreadCritical tc;
118    // User wants all output to go to files
119    if (_file_count != 0) {
120      ResourceMark rm;
121      stringStream st;
122      const char* dot = strrchr(PrintIdealGraphFile, '.');
123      if (dot) {
124        st.write(PrintIdealGraphFile, dot - PrintIdealGraphFile);
125        st.print("%d%s", _file_count, dot);
126      } else {
127        st.print("%s%d", PrintIdealGraphFile, _file_count);
128      }
129      fileStream *stream = new (ResourceObj::C_HEAP) fileStream(st.as_string());
130      _output = stream;
131    } else {
132      fileStream *stream = new (ResourceObj::C_HEAP) fileStream(PrintIdealGraphFile);
133      _output = stream;
134    }
135    _file_count++;
136  } else {
137    _stream = new (ResourceObj::C_HEAP) networkStream();
138
139    // Try to connect to visualizer
140    if (_stream->connect(PrintIdealGraphAddress, PrintIdealGraphPort)) {
141      char c = 0;
142      _stream->read(&c, 1);
143      if (c != 'y') {
144        tty->print_cr("Client available, but does not want to receive data!");
145        _stream->close();
146        delete _stream;
147        _stream = NULL;
148        return;
149      }
150      _output = _stream;
151    } else {
152      // It would be nice if we could shut down cleanly but it should
153      // be an error if we can't connect to the visualizer.
154      fatal2("Couldn't connect to visualizer at %s:%d", PrintIdealGraphAddress, PrintIdealGraphPort);
155    }
156  }
157
158  _xml = new (ResourceObj::C_HEAP) xmlStream(_output);
159
160  head(TOP_ELEMENT);
161}
162
163// Destructor, close file or network stream
164IdealGraphPrinter::~IdealGraphPrinter() {
165
166  tail(TOP_ELEMENT);
167
168  // tty->print_cr("Walk time: %d", (int)_walk_time.milliseconds());
169  // tty->print_cr("Output time: %d", (int)_output_time.milliseconds());
170  // tty->print_cr("Build blocks time: %d", (int)_build_blocks_time.milliseconds());
171
172  if(_xml) {
173    delete _xml;
174    _xml = NULL;
175  }
176
177  if (_stream) {
178    delete _stream;
179    if (_stream == _output) {
180      _output = NULL;
181    }
182    _stream = NULL;
183  }
184
185  if (_output) {
186    delete _output;
187    _output = NULL;
188  }
189}
190
191
192void IdealGraphPrinter::begin_elem(const char *s) {
193  _xml->begin_elem(s);
194}
195
196void IdealGraphPrinter::end_elem() {
197  _xml->end_elem();
198}
199
200void IdealGraphPrinter::begin_head(const char *s) {
201  _xml->begin_head(s);
202}
203
204void IdealGraphPrinter::end_head() {
205  _xml->end_head();
206}
207
208void IdealGraphPrinter::print_attr(const char *name, intptr_t val) {
209  stringStream stream;
210  stream.print(INTX_FORMAT, val);
211  print_attr(name, stream.as_string());
212}
213
214void IdealGraphPrinter::print_attr(const char *name, const char *val) {
215  _xml->print(" %s='", name);
216  text(val);
217  _xml->print("'");
218}
219
220void IdealGraphPrinter::head(const char *name) {
221  _xml->head(name);
222}
223
224void IdealGraphPrinter::tail(const char *name) {
225  _xml->tail(name);
226}
227
228void IdealGraphPrinter::text(const char *s) {
229  _xml->text(s);
230}
231
232void IdealGraphPrinter::print_prop(const char *name, int val) {
233
234  stringStream stream;
235  stream.print("%d", val);
236  print_prop(name, stream.as_string());
237}
238
239void IdealGraphPrinter::print_prop(const char *name, const char *val) {
240  begin_head(PROPERTY_ELEMENT);
241  print_attr(PROPERTY_NAME_PROPERTY, name);
242  end_head();
243  text(val);
244  tail(PROPERTY_ELEMENT);
245}
246
247void IdealGraphPrinter::print_method(ciMethod *method, int bci, InlineTree *tree) {
248  begin_head(METHOD_ELEMENT);
249
250  stringStream str;
251  method->print_name(&str);
252
253  stringStream shortStr;
254  method->print_short_name(&shortStr);
255
256  print_attr(METHOD_NAME_PROPERTY, str.as_string());
257  print_attr(METHOD_SHORT_NAME_PROPERTY, shortStr.as_string());
258  print_attr(METHOD_BCI_PROPERTY, bci);
259
260  end_head();
261
262  head(BYTECODES_ELEMENT);
263  output()->print_cr("<![CDATA[");
264  method->print_codes_on(output());
265  output()->print_cr("]]>");
266  tail(BYTECODES_ELEMENT);
267
268  head(INLINE_ELEMENT);
269  if (tree != NULL) {
270    GrowableArray<InlineTree *> subtrees = tree->subtrees();
271    for (int i = 0; i < subtrees.length(); i++) {
272      print_inline_tree(subtrees.at(i));
273    }
274  }
275  tail(INLINE_ELEMENT);
276
277  tail(METHOD_ELEMENT);
278  output()->flush();
279}
280
281void IdealGraphPrinter::print_inline_tree(InlineTree *tree) {
282
283  if (tree == NULL) return;
284
285  ciMethod *method = tree->method();
286  print_method(tree->method(), tree->caller_bci(), tree);
287
288}
289
290void IdealGraphPrinter::print_inlining(Compile* compile) {
291
292  // Print inline tree
293  if (_should_send_method) {
294    InlineTree *inlineTree = compile->ilt();
295    if (inlineTree != NULL) {
296      print_inline_tree(inlineTree);
297    } else {
298      // print this method only
299    }
300  }
301}
302
303// Has to be called whenever a method is compiled
304void IdealGraphPrinter::begin_method(Compile* compile) {
305
306  ciMethod *method = compile->method();
307  assert(_output, "output stream must exist!");
308  assert(method, "null methods are not allowed!");
309  assert(!_current_method, "current method must be null!");
310
311  head(GROUP_ELEMENT);
312
313  head(PROPERTIES_ELEMENT);
314
315  // Print properties
316  // Add method name
317  stringStream strStream;
318  method->print_name(&strStream);
319  print_prop(METHOD_NAME_PROPERTY, strStream.as_string());
320
321  if (method->flags().is_public()) {
322    print_prop(METHOD_IS_PUBLIC_PROPERTY, TRUE_VALUE);
323  }
324
325  if (method->flags().is_static()) {
326    print_prop(METHOD_IS_STATIC_PROPERTY, TRUE_VALUE);
327  }
328
329  tail(PROPERTIES_ELEMENT);
330
331  if (_stream) {
332    char answer = 0;
333    _xml->flush();
334    int result = _stream->read(&answer, 1);
335    _should_send_method = (answer == 'y');
336  }
337
338  this->_current_method = method;
339
340  _xml->flush();
341}
342
343// Has to be called whenever a method has finished compilation
344void IdealGraphPrinter::end_method() {
345
346  nmethod* method = (nmethod*)this->_current_method->code();
347
348  tail(GROUP_ELEMENT);
349  _current_method = NULL;
350  _xml->flush();
351}
352
353// Print indent
354void IdealGraphPrinter::print_indent() {
355  tty->print_cr("printing ident %d", _depth);
356  for (int i = 0; i < _depth; i++) {
357    _xml->print(INDENT);
358  }
359}
360
361bool IdealGraphPrinter::traverse_outs() {
362  return _traverse_outs;
363}
364
365void IdealGraphPrinter::set_traverse_outs(bool b) {
366  _traverse_outs = b;
367}
368
369intptr_t IdealGraphPrinter::get_node_id(Node *n) {
370  return (intptr_t)(n);
371}
372
373void IdealGraphPrinter::visit_node(Node *n, void *param) {
374
375  if(param) {
376
377    // Output edge
378    intptr_t dest_id = get_node_id(n);
379    for ( uint i = 0; i < n->len(); i++ ) {
380      if ( n->in(i) ) {
381        Node *source = n->in(i);
382        begin_elem(EDGE_ELEMENT);
383        intptr_t source_id = get_node_id(source);
384        print_attr(FROM_PROPERTY, source_id);
385        print_attr(TO_PROPERTY, dest_id);
386        print_attr(INDEX_PROPERTY, i);
387        end_elem();
388      }
389    }
390
391  } else {
392
393    // Output node
394    begin_head(NODE_ELEMENT);
395    print_attr(NODE_ID_PROPERTY, get_node_id(n));
396    end_head();
397
398    head(PROPERTIES_ELEMENT);
399
400    Node *node = n;
401#ifndef PRODUCT
402    node->_in_dump_cnt++;
403    print_prop(NODE_NAME_PROPERTY, (const char *)node->Name());
404    const Type *t = node->bottom_type();
405    print_prop("type", (const char *)Type::msg[t->base()]);
406    print_prop("idx", node->_idx);
407#ifdef ASSERT
408    print_prop("debug_idx", node->_debug_idx);
409#endif
410
411    if(C->cfg() != NULL) {
412      Block *block = C->cfg()->_bbs[node->_idx];
413      if(block == NULL) {
414        print_prop("block", C->cfg()->_blocks[0]->_pre_order);
415      } else {
416        print_prop("block", block->_pre_order);
417      }
418    }
419
420    const jushort flags = node->flags();
421    if (flags & Node::Flag_is_Copy) {
422      print_prop("is_copy", "true");
423    }
424    if (flags & Node::Flag_is_Call) {
425      print_prop("is_call", "true");
426    }
427    if (flags & Node::Flag_rematerialize) {
428      print_prop("rematerialize", "true");
429    }
430    if (flags & Node::Flag_needs_anti_dependence_check) {
431      print_prop("needs_anti_dependence_check", "true");
432    }
433    if (flags & Node::Flag_is_macro) {
434      print_prop("is_macro", "true");
435    }
436    if (flags & Node::Flag_is_Con) {
437      print_prop("is_con", "true");
438    }
439    if (flags & Node::Flag_is_cisc_alternate) {
440      print_prop("is_cisc_alternate", "true");
441    }
442    if (flags & Node::Flag_is_Branch) {
443      print_prop("is_branch", "true");
444    }
445    if (flags & Node::Flag_is_block_start) {
446      print_prop("is_block_start", "true");
447    }
448    if (flags & Node::Flag_is_Goto) {
449      print_prop("is_goto", "true");
450    }
451    if (flags & Node::Flag_is_dead_loop_safe) {
452      print_prop("is_dead_loop_safe", "true");
453    }
454    if (flags & Node::Flag_may_be_short_branch) {
455      print_prop("may_be_short_branch", "true");
456    }
457    if (flags & Node::Flag_is_safepoint_node) {
458      print_prop("is_safepoint_node", "true");
459    }
460    if (flags & Node::Flag_is_pc_relative) {
461      print_prop("is_pc_relative", "true");
462    }
463
464    if (C->matcher() != NULL) {
465      if (C->matcher()->is_shared(node)) {
466        print_prop("is_shared", "true");
467      } else {
468        print_prop("is_shared", "false");
469      }
470      if (C->matcher()->is_dontcare(node)) {
471        print_prop("is_dontcare", "true");
472      } else {
473        print_prop("is_dontcare", "false");
474      }
475
476      Node* old = C->matcher()->find_old_node(node);
477      if (old != NULL) {
478        print_prop("old_node_idx", old->_idx);
479      }
480    }
481
482    if (node->is_Proj()) {
483      print_prop("con", (int)node->as_Proj()->_con);
484    }
485
486    if (node->is_Mach()) {
487      print_prop("idealOpcode", (const char *)NodeClassNames[node->as_Mach()->ideal_Opcode()]);
488    }
489
490    buffer[0] = 0;
491    stringStream s2(buffer, sizeof(buffer) - 1);
492
493    node->dump_spec(&s2);
494    if (t != NULL && (t->isa_instptr() || t->isa_klassptr())) {
495      const TypeInstPtr  *toop = t->isa_instptr();
496      const TypeKlassPtr *tkls = t->isa_klassptr();
497      ciKlass*           klass = toop ? toop->klass() : (tkls ? tkls->klass() : NULL );
498      if( klass && klass->is_loaded() && klass->is_interface() ) {
499        s2.print("  Interface:");
500      } else if( toop ) {
501        s2.print("  Oop:");
502      } else if( tkls ) {
503        s2.print("  Klass:");
504      }
505      t->dump_on(&s2);
506    } else if( t == Type::MEMORY ) {
507      s2.print("  Memory:");
508      MemNode::dump_adr_type(node, node->adr_type(), &s2);
509    }
510
511    assert(s2.size() < sizeof(buffer), "size in range");
512    print_prop("dump_spec", buffer);
513
514    if (node->is_block_proj()) {
515      print_prop("is_block_proj", "true");
516    }
517
518    if (node->is_block_start()) {
519      print_prop("is_block_start", "true");
520    }
521
522    const char *short_name = "short_name";
523    if (strcmp(node->Name(), "Parm") == 0 && node->as_Proj()->_con >= TypeFunc::Parms) {
524      int index = node->as_Proj()->_con - TypeFunc::Parms;
525      if (index >= 10) {
526        print_prop(short_name, "PA");
527      } else {
528        sprintf(buffer, "P%d", index);
529        print_prop(short_name, buffer);
530      }
531    } else if (strcmp(node->Name(), "IfTrue") == 0) {
532      print_prop(short_name, "T");
533    } else if (strcmp(node->Name(), "IfFalse") == 0) {
534      print_prop(short_name, "F");
535    } else if ((node->is_Con() && node->is_Type()) || node->is_Proj()) {
536
537      if (t->base() == Type::Int && t->is_int()->is_con()) {
538        const TypeInt *typeInt = t->is_int();
539        assert(typeInt->is_con(), "must be constant");
540        jint value = typeInt->get_con();
541
542        // max. 2 chars allowed
543        if (value >= -9 && value <= 99) {
544          sprintf(buffer, "%d", value);
545          print_prop(short_name, buffer);
546        } else {
547          print_prop(short_name, "I");
548        }
549      } else if (t == Type::TOP) {
550        print_prop(short_name, "^");
551      } else if (t->base() == Type::Long && t->is_long()->is_con()) {
552        const TypeLong *typeLong = t->is_long();
553        assert(typeLong->is_con(), "must be constant");
554        jlong value = typeLong->get_con();
555
556        // max. 2 chars allowed
557        if (value >= -9 && value <= 99) {
558          sprintf(buffer, "%d", value);
559          print_prop(short_name, buffer);
560        } else {
561          print_prop(short_name, "L");
562        }
563      } else if (t->base() == Type::KlassPtr) {
564        const TypeKlassPtr *typeKlass = t->is_klassptr();
565        print_prop(short_name, "CP");
566      } else if (t->base() == Type::Control) {
567        print_prop(short_name, "C");
568      } else if (t->base() == Type::Memory) {
569        print_prop(short_name, "M");
570      } else if (t->base() == Type::Abio) {
571        print_prop(short_name, "IO");
572      } else if (t->base() == Type::Return_Address) {
573        print_prop(short_name, "RA");
574      } else if (t->base() == Type::AnyPtr) {
575        print_prop(short_name, "P");
576      } else if (t->base() == Type::RawPtr) {
577        print_prop(short_name, "RP");
578      } else if (t->base() == Type::AryPtr) {
579        print_prop(short_name, "AP");
580      }
581    }
582
583    JVMState* caller = NULL;
584    if (node->is_SafePoint()) {
585      caller = node->as_SafePoint()->jvms();
586    } else {
587      Node_Notes* notes = C->node_notes_at(node->_idx);
588      if (notes != NULL) {
589        caller = notes->jvms();
590      }
591    }
592
593    if (caller != NULL) {
594      stringStream bciStream;
595      while(caller) {
596        bciStream.print("%d ", caller->bci());
597        caller = caller->caller();
598      }
599      print_prop("bci", bciStream.as_string());
600    }
601
602    if (_chaitin && _chaitin != (PhaseChaitin *)0xdeadbeef) {
603      buffer[0] = 0;
604      _chaitin->dump_register(node, buffer);
605      print_prop("reg", buffer);
606      print_prop("lrg", _chaitin->n2lidx(node));
607    }
608
609    node->_in_dump_cnt--;
610#endif
611
612    tail(PROPERTIES_ELEMENT);
613    tail(NODE_ELEMENT);
614  }
615}
616
617void IdealGraphPrinter::walk_nodes(Node *start, void *param) {
618
619
620  VectorSet visited(Thread::current()->resource_area());
621  GrowableArray<Node *> nodeStack(Thread::current()->resource_area(), 0, 0, NULL);
622  nodeStack.push(start);
623  visited.test_set(start->_idx);
624  while(nodeStack.length() > 0) {
625
626    Node *n = nodeStack.pop();
627    visit_node(n, param);
628
629    if (_traverse_outs) {
630      for (DUIterator i = n->outs(); n->has_out(i); i++) {
631        Node* p = n->out(i);
632        if (!visited.test_set(p->_idx)) {
633          nodeStack.push(p);
634        }
635      }
636    }
637
638    for ( uint i = 0; i < n->len(); i++ ) {
639      if ( n->in(i) ) {
640        if (!visited.test_set(n->in(i)->_idx)) {
641          nodeStack.push(n->in(i));
642        }
643      }
644    }
645  }
646}
647
648void IdealGraphPrinter::print_method(Compile* compile, const char *name, int level, bool clear_nodes) {
649  print(compile, name, (Node *)compile->root(), level, clear_nodes);
650}
651
652// Print current ideal graph
653void IdealGraphPrinter::print(Compile* compile, const char *name, Node *node, int level, bool clear_nodes) {
654
655  if (!_current_method || !_should_send_method || level > PrintIdealGraphLevel) return;
656
657  this->C = compile;
658
659  // Warning, unsafe cast?
660  _chaitin = (PhaseChaitin *)C->regalloc();
661
662  begin_head(GRAPH_ELEMENT);
663  print_attr(GRAPH_NAME_PROPERTY, (const char *)name);
664  end_head();
665
666  head(NODES_ELEMENT);
667  walk_nodes(node, NULL);
668  tail(NODES_ELEMENT);
669
670  head(EDGES_ELEMENT);
671  walk_nodes(node, (void *)1);
672  tail(EDGES_ELEMENT);
673  if (C->cfg() != NULL) {
674    head(CONTROL_FLOW_ELEMENT);
675    for (uint i = 0; i < C->cfg()->_blocks.size(); i++) {
676      Block *b = C->cfg()->_blocks[i];
677      begin_head(BLOCK_ELEMENT);
678      print_attr(BLOCK_NAME_PROPERTY, b->_pre_order);
679      end_head();
680
681      head(SUCCESSORS_ELEMENT);
682      for (uint s = 0; s < C->cfg()->_blocks[i]->_num_succs; s++) {
683        begin_elem(SUCCESSOR_ELEMENT);
684        print_attr(BLOCK_NAME_PROPERTY, b->_succs[s]->_pre_order);
685        end_elem();
686      }
687      tail(SUCCESSORS_ELEMENT);
688
689      tail(BLOCK_ELEMENT);
690    }
691
692    tail(CONTROL_FLOW_ELEMENT);
693  }
694  tail(GRAPH_ELEMENT);
695  output()->flush();
696}
697
698extern const char *NodeClassNames[];
699
700outputStream *IdealGraphPrinter::output() {
701  return _xml;
702}
703
704#endif
705