1 /* Copyright (C) 1998 - 2000 by Universitaet Karlsruhe
4 * Authors: Martin Trapp, Christian Schaefer
6 * irdump.h: dumping of an intermediate representation graph
18 # include "irnode_t.h"
19 # include "irgraph_t.h"
20 # include "entity_t.h"
22 # include "firm_common_t.h"
27 # include "typewalk.h"
30 # include "type_or_entity.h"
42 /* Attributes of nodes */
43 #define PRINT_DEFAULT_NODE_ATTR
44 #define DEFAULT_NODE_ATTR ""
45 #define DEFAULT_TYPE_ATTRIBUTE ""
47 /* Attributes of edges between Firm nodes */
48 #define BLOCK_EDGE_ATTR "class: 2 priority: 2 linestyle: dotted"
49 #define CF_EDGE_ATTR "color: red"
50 #define MEM_EDGE_ATTR "color: blue"
51 #define DOMINATOR_EDGE_ATTR "color: red"
53 #define BACK_EDGE_ATTR "linestyle: dashed "
55 /* Attributes of edges between Firm nodes and type/entity nodes */
56 #define NODE2TYPE_EDGE_ATTR "class: 2 priority: 2 linestyle: dotted"
58 /* Attributes of edges in type/entity graphs. */
59 #define TYPE_METH_NODE_ATTR "color: lightyellow"
60 #define TYPE_CLASS_NODE_ATTR "color: green"
61 #define TYPE_DESCRIPTION_NODE_ATTR "color: lightgreen"
62 #define ENTITY_NODE_ATTR "color: yellow"
63 #define ENT_TYPE_EDGE_ATTR "class: 3 label: \"type\" color: red"
64 #define ENT_OWN_EDGE_ATTR "class: 4 label: \"owner\" color: black"
65 #define METH_PAR_EDGE_ATTR "class: 5 label: \"param %d\" color: green"
66 #define METH_RES_EDGE_ATTR "class: 6 label: \"res %d\" color: green"
67 #define TYPE_SUPER_EDGE_ATTR "class: 7 label: \"supertype\" color: red"
68 #define UNION_EDGE_ATTR "class: 8 label: \"component\" color: blue"
69 #define PTR_PTS_TO_EDGE_ATTR "class: 9 label: \"points to\" color:green"
70 #define ARR_ELT_TYPE_EDGE_ATTR "class: 10 label: \"arr elt tp\" color:green"
71 #define ARR_ENT_EDGE_ATTR "class: 10 label: \"arr ent\" color: green"
72 #define ENT_OVERWRITES_EDGE_ATTR "class: 11 label: \"overwrites\" color:red"
73 #define ENT_VALUE_EDGE_ATTR "label: \"value %d\""
74 #define ENT_CORR_EDGE_ATTR "label: \"value %d corresponds to \" "
75 #define TYPE_MEMBER_EDGE_ATTR "class: 12 label: \"member\" color:blue"
78 #if DEBUG_libfirm && NODEID_AS_LABEL
79 #define PRINT_NODEID(X) fprintf(F, "n%ld", get_irn_node_nr(X))
80 #define PRINT_TYPEID(X) fprintf(F, "t%ld", get_type_nr(X))
81 #define PRINT_ENTID(X) fprintf(F, "e%ld", get_entity_nr(X))
82 #define PRINT_IRGID(X) fprintf(F,"g%ld", get_irg_graph_nr(X))
84 #define PRINT_NODEID(X) fprintf(F, "%p", X)
85 #define PRINT_TYPEID(X) fprintf(F, "%p", X)
86 #define PRINT_ENTID(X) fprintf(F, "%p", X)
87 #define PRINT_IRGID(X) fprintf(F,"%p",X)
90 #define PRINT_TYPE_TYPE_EDGE(S,T,ATR,...) {fprintf (F, "edge: { sourcename:\""); PRINT_TYPEID(S); fprintf (F, "\" targetname: \""); PRINT_TYPEID(T); fprintf (F,"\" " ATR "}\n",##__VA_ARGS__);}
91 #define PRINT_TYPE_ENT_EDGE(S,T,ATR,...) {fprintf (F, "edge: { sourcename:\""); PRINT_TYPEID(S); fprintf (F, "\" targetname: \""); PRINT_ENTID(T); fprintf (F,"\" " ATR "}\n",##__VA_ARGS__);}
92 #define PRINT_ENT_ENT_EDGE(S,T,ATR,...) {fprintf (F, "edge: { sourcename:\""); PRINT_ENTID(S); fprintf (F, "\" targetname: \""); PRINT_ENTID(T); fprintf (F,"\" " ATR "}\n",##__VA_ARGS__);}
93 #define PRINT_ENT_TYPE_EDGE(S,T,ATR,...) {fprintf (F, "edge: { sourcename:\""); PRINT_ENTID(S); fprintf (F, "\" targetname: \""); PRINT_TYPEID(T); fprintf (F,"\" " ATR "}\n",##__VA_ARGS__);}
94 #define PRINT_NODE_TYPE_EDGE(S,T,ATR,...) {fprintf (F, "edge: { sourcename:\""); PRINT_NODEID(S); fprintf (F, "\" targetname: \""); PRINT_TYPEID(T); fprintf (F,"\" " ATR "}\n",##__VA_ARGS__);}
95 #define PRINT_NODE_ENT_EDGE(S,T,ATR,...) {fprintf (F, "edge: { sourcename:\""); PRINT_NODEID(S); fprintf (F, "\" targetname: \""); PRINT_ENTID(T); fprintf (F,"\" " ATR "}\n",##__VA_ARGS__);}
96 #define PRINT_ENT_NODE_EDGE(S,T,ATR,...) {fprintf (F, "edge: { sourcename:\""); PRINT_ENTID(S); fprintf (F, "\" targetname: \""); PRINT_NODEID(T); fprintf (F,"\" " ATR "}\n",##__VA_ARGS__);}
99 /* A suffix to manipulate the file name. */
100 char *dump_file_suffix = NULL;
102 /* file to dump to */
105 /* A compiler option to turn off edge labels */
107 /* A compiler option to turn off dumping values of constant entities */
108 int const_entities = 1;
109 /* A compiler option to dump the keep alive edges */
110 int dump_keepalive = 0;
111 /* Compiler options to dump analysis information in dump_ir_graph */
112 int dump_out_edge_flag = 0;
113 int dump_dominator_information_flag = 0;
114 int dump_loop_information_flag = 0;
115 int dump_const_local = 0;
117 static INLINE bool dump_const_local_set(void) {
118 if (!dump_out_edge_flag && !dump_loop_information_flag)
119 return dump_const_local;
124 /* A global variable to record output of the Bad node. */
125 static int Bad_dumped;
127 static void dump_ir_blocks_nodes (ir_node *n, void *env);
128 static void dump_whole_node(ir_node *n, void* env);
130 /*******************************************************************/
131 /* routines to dump information about a single node */
132 /*******************************************************************/
137 dump_node_opcode (ir_node *n)
145 if (n->op->code == iro_Const) {
146 res = tarval_snprintf(buf, sizeof(buf), n->attr.con);
147 assert(res < sizeof(buf) && "buffer to small for tarval_snprintf");
151 } else if (n->op->code == iro_SymConst) {
152 if (get_SymConst_kind(n) == linkage_ptr_info) {
153 /* don't use get_SymConst_ptr_info as it mangles the name. */
154 fprintf (F, "SymC %s", id_to_str(n->attr.i.tori.ptrinfo));
156 assert(get_kind(get_SymConst_type(n)) == k_type);
157 assert(get_type_ident(get_SymConst_type(n)));
158 fprintf (F, "SymC %s ", id_to_str(get_type_ident(get_SymConst_type(n))));
159 if (get_SymConst_kind(n) == type_tag)
166 } else if (n->op->code == iro_Filter && !interprocedural_view) {
171 fprintf (F, "%s", id_to_str(get_irn_opident(n)));
176 dump_node_mode (ir_node *n)
178 switch (n->op->code) {
196 fprintf (F, "%s", id_to_str(get_mode_ident(n->mode)));
204 dump_node_nodeattr (ir_node *n)
206 switch (n->op->code) {
208 if (false && interprocedural_view) {
209 fprintf (F, "%s", id_to_str(get_entity_ident(get_irg_ent(current_ir_graph))));
213 if (n->in[1]->op->code == iro_Cmp) {
214 fprintf (F, "%s", get_pnc_string(n->attr.proj));
216 fprintf (F, "%ld", n->attr.proj);
220 fprintf (F, "%ld", n->attr.filter.proj);
223 assert(get_kind(get_Sel_entity(n)) == k_entity);
224 fprintf (F, "%s", id_to_str(get_entity_ident(get_Sel_entity(n))));
232 dump_node_vcgattr (ir_node *n)
234 switch (n->op->code) {
241 fprintf (F, "color: blue");
244 fprintf (F, "color: lightyellow");
247 fprintf (F, "color: green");
253 fprintf (F, "color: yellow");
256 PRINT_DEFAULT_NODE_ATTR;
260 static bool pred_in_wrong_graph(ir_node *n, int pos, pmap *irgmap) {
261 ir_node *block = (is_Block(n)) ? n : get_nodes_Block(n);
264 ((get_irn_op(n) == op_Filter) || (get_irn_op(n) == op_Block))) {
265 ir_node *pred = skip_Proj(get_Block_cfgpred(block, pos));
266 if (is_ip_cfop(pred)) {
267 ir_graph *irg = get_ip_cfop_irg(pred);
268 if (pmap_find(irgmap, irg) == NULL) return true;
277 bool is_constlike_node(ir_node *n) {
278 ir_op *op = get_irn_op(n);
279 return (op == op_Const || op == op_Bad || op == op_SymConst);
283 static void dump_const_node_local(ir_node *n, pmap *irgmap) {
285 if (!dump_const_local_set()) return;
286 /* Use visited flag to avoid outputting nodes twice.
287 initialize it first. */
288 for (i = 0; i < get_irn_arity(n); i++) {
289 ir_node *con = get_irn_n(n, i);
290 if (is_constlike_node(con)) {
291 if (pred_in_wrong_graph(n, i, irgmap)) continue; /* pred not dumped */
292 set_irn_visited(con, get_irg_visited(current_ir_graph)-1);
295 for (i = 0; i < get_irn_arity(n); i++) {
296 ir_node *con = get_irn_n(n, i);
297 if (is_constlike_node(con) && irn_not_visited(con)) {
298 if (pred_in_wrong_graph(n, i, irgmap)) continue; /* pred not dumped */
299 mark_irn_visited(con);
300 /* Generate a new name for the node by appending the names of
302 fprintf (F, "node: {title: \""); PRINT_NODEID(n); PRINT_NODEID(con);
303 fprintf(F, "\" label: \"");
304 dump_node_opcode(con);
305 dump_node_mode (con);
307 dump_node_nodeattr(con);
309 fprintf (F, " %ld", get_irn_node_nr(con));
312 dump_node_vcgattr(con);
319 dump_node (ir_node *n, pmap * map) {
320 if (dump_const_local_set() && is_constlike_node(n)) return;
323 fprintf (F, "node: {title: \""); PRINT_NODEID(n); fprintf(F, "\" label: \"");
328 dump_node_nodeattr(n);
330 fprintf (F, " %ld", get_irn_node_nr(n));
333 dump_node_vcgattr(n);
335 dump_const_node_local(n, map);
339 dump_ir_node (ir_node *n)
345 fprintf (F, "node: {title: \""); PRINT_NODEID(n); fprintf(F, "\" label: ");
347 switch (n->op->code) { /* node label */
349 fprintf (F, "\"%s\" color: blue ", id_to_str(get_irn_opident(n)));
350 PRINT_DEFAULT_NODE_ATTR;
357 fprintf (F, "\"%s\" color: blue ", id_to_str(get_irn_opident(n)));
358 PRINT_DEFAULT_NODE_ATTR;
361 fprintf (F, "\"%s\" color: lightyellow ", id_to_str(get_irn_opident(n)));
362 PRINT_DEFAULT_NODE_ATTR;
365 fprintf (F, "\"%s%s\" color: green", id_to_str(get_irn_opident(n)), id_to_str(get_irn_modeident(n)));
366 if (get_irn_modecode(n) == irm_M)
367 fprintf (F, DEFAULT_NODE_ATTR " color: green");
369 PRINT_DEFAULT_NODE_ATTR;
372 res = tarval_snprintf(buf, sizeof(buf), n->attr.con);
373 assert(res < sizeof(buf) && "buffer to small for tarval_snprintf");
375 fprintf (F, "\"%s%s\" color: yellow ", buf, id_to_str(get_irn_modeident(n)));
376 PRINT_DEFAULT_NODE_ATTR;
379 if (n->in[1]->op->code == iro_Cmp) {
380 fprintf (F, "\"%s%s %s\" color: yellow", id_to_str(get_irn_opident(n)), id_to_str(get_irn_modeident(n)),
381 get_pnc_string(n->attr.proj));
383 fprintf (F, "\"%s%s %ld\"", id_to_str(get_irn_opident(n)), id_to_str(get_irn_modeident(n)), n->attr.proj);
385 PRINT_DEFAULT_NODE_ATTR;
388 fprintf (F, "\"%s%s %ld\"", id_to_str(get_irn_opident(n)), id_to_str(get_irn_modeident(n)), n->attr.filter.proj);
389 PRINT_DEFAULT_NODE_ATTR;
435 fprintf (F, "\"%s%s\"", id_to_str(get_irn_opident(n)), id_to_str(get_irn_modeident(n)));
436 PRINT_DEFAULT_NODE_ATTR;
452 fprintf (F, "\"%s\"", id_to_str(get_irn_opident(n)));
453 PRINT_DEFAULT_NODE_ATTR;
456 assert(get_kind(get_Sel_entity(n)) == k_entity);
457 fprintf (F, "\"%s ", id_to_str(get_irn_opident(n)));
458 fprintf (F, "%s", id_to_str(get_entity_ident(get_Sel_entity(n))));
459 PRINT_DEFAULT_NODE_ATTR;
462 assert(get_kind(get_SymConst_type(n)) == k_type);
463 assert(get_type_ident(get_SymConst_type(n)));
464 fprintf (F, "\"%s ", get_type_name(get_SymConst_type(n)));
465 switch (n->attr.i.num){
467 fprintf (F, "tag\" ");
470 fprintf (F, "size\" ");
476 PRINT_DEFAULT_NODE_ATTR;
479 fprintf (F, "\"%s\" ", id_to_str(get_irn_opident(n)));
480 fprintf (F, DEFAULT_NODE_ATTR " color: green");
483 fprintf (F, "\"%s%s\" ", id_to_str(get_irn_opident(n)), id_to_str(get_irn_modeident(n)));
485 fprintf (F, "}\n"); /* footer */
489 /* dump the edge to the block this node belongs to */
491 dump_ir_block_edge(ir_node *n) {
492 if (dump_const_local_set() && is_constlike_node(n)) return;
493 if (is_no_Block(n)) {
494 fprintf (F, "edge: { sourcename: \"");
496 fprintf (F, "\" targetname: \"");
497 PRINT_NODEID(get_nodes_Block(n));
498 fprintf (F, "\" " BLOCK_EDGE_ATTR "}\n");
502 static void print_edge_vcgattr(ir_node *from, int to) {
505 if (is_backedge(from, to)) fprintf (F, BACK_EDGE_ATTR);
507 switch (get_irn_opcode(from)) {
509 fprintf (F, CF_EDGE_ATTR);
511 case iro_Start: break;
514 if (get_irn_mode(get_End_keepalive(from, to)) == mode_BB)
515 fprintf (F, CF_EDGE_ATTR);
516 if (get_irn_mode(get_End_keepalive(from, to)) == mode_X)
517 fprintf (F, MEM_EDGE_ATTR);
520 case iro_EndReg: break;
521 case iro_EndExcept: break;
523 case iro_Break: break;
524 case iro_Cond: break;
527 if (to == 0) fprintf (F, MEM_EDGE_ATTR);
529 case iro_Const: break;
530 case iro_SymConst:break;
533 if (to == 0) fprintf (F, MEM_EDGE_ATTR);
535 case iro_CallBegin: break;
538 case iro_Minus: break;
544 if (to == 0) fprintf (F, MEM_EDGE_ATTR);
552 case iro_Shrs: break;
555 case iro_Conv: break;
557 if (get_irn_modecode(from) == irm_M) fprintf (F, MEM_EDGE_ATTR);
563 if (to == 0) fprintf (F, MEM_EDGE_ATTR);
566 fprintf (F, MEM_EDGE_ATTR);
568 case iro_Tuple: break;
571 switch (get_irn_modecode(from)) {
573 fprintf (F, CF_EDGE_ATTR);
576 fprintf (F, MEM_EDGE_ATTR);
582 case iro_Unknown: break;
589 /* dump edges to our inputs */
591 dump_ir_data_edges(ir_node *n) {
592 int i, visited = get_irn_visited(n);
594 if ((get_irn_op(n) == op_End) && (!dump_keepalive))
597 for (i = 0; i < get_irn_arity(n); i++) {
598 ir_node * pred = get_irn_n(n, i);
600 if ((interprocedural_view && get_irn_visited(pred) < visited))
601 continue; /* pred not dumped */
602 if (is_backedge(n, i))
603 fprintf (F, "backedge: {sourcename: \"");
605 fprintf (F, "edge: {sourcename: \"");
607 fprintf (F, "\" targetname: \"");
608 if ((dump_const_local_set()) && is_constlike_node(pred))
612 fprintf (F, " label: \"%d\" ", i);
613 print_edge_vcgattr(n, i);
620 dump_out_edge (ir_node *n, void* env) {
622 for (i = 0; i < get_irn_n_outs(n); i++) {
623 assert(get_irn_out(n, i));
624 fprintf (F, "edge: {sourcename: \"");
626 fprintf (F, "\" targetname: \"");
627 PRINT_NODEID(get_irn_out(n, i));
628 fprintf (F, "\" color: red linestyle: dashed");
634 dump_loop_node_edge (ir_loop *loop, int i) {
636 fprintf (F, "edge: {sourcename: \"%p\" targetname: \"", loop);
637 PRINT_NODEID(get_loop_node(loop, i));
638 fprintf (F, "\" color: green");
643 void dump_loops (ir_loop *loop) {
645 /* dump this loop node */
646 fprintf (F, "node: {title: \"%p\" label: \"loop %d, %d sons, %d nodes\" }\n",
647 loop, get_loop_depth(loop), get_loop_n_sons(loop), get_loop_n_nodes(loop));
648 /* dump edges to nodes in loop -- only if it is a real loop */
649 if (get_loop_depth(loop) != 0) {
650 for (i = 0; i < get_loop_n_nodes(loop); i++) {
651 dump_loop_node_edge(loop, i);
654 for (i = 0; i < get_loop_n_sons(loop); i++) {
655 dump_loops(get_loop_son(loop, i));
660 void dump_loop_info(ir_graph *irg) {
661 ir_graph *rem = current_ir_graph;
662 current_ir_graph = irg;
664 if (get_irg_loop(irg))
665 dump_loops(get_irg_loop(irg));
667 current_ir_graph = rem;
671 /* dumps the edges between nodes and their type or entity attributes. */
672 static void dump_node2type_edges (ir_node *n, void *env)
676 switch (get_irn_opcode(n)) {
678 /* @@@ some consts have an entity */
681 if ( (get_SymConst_kind(n) == type_tag)
682 || (get_SymConst_kind(n) == size))
684 PRINT_NODE_TYPE_EDGE(n,get_SymConst_type(n),NODE2TYPE_EDGE_ATTR);
688 PRINT_NODE_ENT_EDGE(n,get_Sel_entity(n),NODE2TYPE_EDGE_ATTR);
691 PRINT_NODE_TYPE_EDGE(n,get_Call_type(n),NODE2TYPE_EDGE_ATTR);
694 PRINT_NODE_TYPE_EDGE(n,get_Alloc_type(n),NODE2TYPE_EDGE_ATTR);
697 PRINT_NODE_TYPE_EDGE(n,get_Free_type(n),NODE2TYPE_EDGE_ATTR);
705 static void dump_const_expression(ir_node *value) {
706 ir_graph *rem = current_ir_graph;
707 int rem_dump_const_local = dump_const_local;
708 dump_const_local = 0;
709 current_ir_graph = get_const_code_irg();
710 irg_walk(value, dump_ir_blocks_nodes, NULL, get_nodes_Block(value));
711 /* Decrease visited flag so that we walk with the same flag for the next
712 expresssion. This guarantees that we don't dump the same node twice,
713 as for const expressions cse is performed to save memory. */
714 set_irg_visited(current_ir_graph, get_irg_visited(current_ir_graph) -1);
715 current_ir_graph = rem;
716 dump_const_local = rem_dump_const_local;
720 static void print_type_info(type *tp) {
721 if (get_type_state(tp) == layout_undefined) {
722 fprintf(F, "state: layout_undefined\n");
724 fprintf(F, "state: layout_fixed,\n");
726 if (get_type_mode(tp))
727 fprintf(F, "mode: %s,\n", id_to_str(get_mode_ident(get_type_mode(tp))));
728 fprintf(F, "size: %dB,\n", get_type_size(tp));
732 static void print_typespecific_info(type *tp) {
733 switch (get_type_tpop_code(tp)) {
736 if(existent == get_class_peculiarity(tp))
737 fprintf (F, " " TYPE_CLASS_NODE_ATTR);
739 fprintf (F, " " TYPE_DESCRIPTION_NODE_ATTR);
743 fprintf (F, " " TYPE_METH_NODE_ATTR);
754 case tpo_enumeration:
767 static void print_type_node(type *tp) {
768 fprintf (F, "node: {title: \"");
770 fprintf (F, "\" label: \"%s %s\"", id_to_str(get_type_tpop_nameid(tp)), id_to_str(get_type_ident(tp)));
771 fprintf (F, "info1: \"");
774 print_typespecific_info(tp);
778 void dump_entity_node(entity *ent) {
779 fprintf (F, "node: {title: \"");
781 fprintf (F, "\"" DEFAULT_TYPE_ATTRIBUTE);
782 fprintf (F, "label: ");
783 fprintf (F, "\"ent %s\" " ENTITY_NODE_ATTR , id_to_str(get_entity_ident(ent)));
784 fprintf (F, "\n info1:\"\nallocation: ");
785 switch (get_entity_allocation(ent)) {
786 case dynamic_allocated: fprintf (F, "dynamic allocated"); break;
787 case automatic_allocated: fprintf (F, "automatic allocated"); break;
788 case static_allocated: fprintf (F, "static allocated"); break;
789 case parameter_allocated: fprintf (F, "parameter allocated"); break;
791 fprintf (F, "\nvisibility: ");
792 switch (get_entity_visibility(ent)) {
793 case local: fprintf (F, "local"); break;
794 case external_visible: fprintf (F, "external_visible"); break;
795 case external_allocated: fprintf (F, "external_allocate"); break;
797 fprintf (F, "\nvariability: ");
798 switch (get_entity_variability(ent)) {
799 case uninitialized: fprintf (F, "uninitialized");break;
800 case initialized: fprintf (F, "initialized"); break;
801 case part_constant: fprintf (F, "part_constant");break;
802 case constant: fprintf (F, "constant"); break;
804 fprintf (F, "\nvolatility: ");
805 switch (get_entity_volatility(ent)) {
806 case non_volatile: fprintf (F, "non_volatile"); break;
807 case is_volatile: fprintf (F, "is_volatile"); break;
809 fprintf (F, "\npeculiarity: ");
810 switch (get_entity_peculiarity(ent)) {
811 case description: fprintf (F, "description"); break;
812 case inherited: fprintf (F, "inherited"); break;
813 case existent: fprintf (F, "existent"); break;
815 fprintf(F, "\nname: %s\nld_name: %s", id_to_str(get_entity_ident(ent)), id_to_str(get_entity_ld_ident(ent)));
816 fprintf(F, "\noffset: %d", get_entity_offset(ent));
817 if (is_method_type(get_entity_type(ent))) {
818 if (get_entity_irg(ent)) /* can be null */
819 { fprintf (F, "\nirg = "); PRINT_IRGID(get_entity_irg(ent)); }
821 { fprintf (F, "\nirg = NULL"); }
823 fprintf(F, "\"\n}\n");
826 /* dumps a type or entity and it's edges. */
828 dump_type_info (type_or_ent *tore, void *env) {
829 int i = 0; /* to shutup gcc */
831 /* dump this type or entity */
833 switch (get_kind(tore)) {
836 entity *ent = (entity *)tore;
839 dump_entity_node(ent);
841 /* skip this to reduce graph. Member edge of type is parallel to this edge. *
842 fprintf (F, "edge: { sourcename: \"%p\" targetname: \"%p\" "
843 ENT_OWN_EDGE_ATTR "}\n", ent, get_entity_owner(ent));*/
844 PRINT_ENT_TYPE_EDGE(ent, get_entity_type(ent), ENT_TYPE_EDGE_ATTR);
845 if(is_class_type(get_entity_owner(ent))) {
846 for(i = 0; i < get_entity_n_overwrites(ent); i++){
847 PRINT_ENT_ENT_EDGE(ent, get_entity_overwrites(ent, i), ENT_OVERWRITES_EDGE_ATTR);
850 /* attached subgraphs */
851 if (const_entities && (get_entity_variability(ent) != uninitialized)) {
852 if (is_atomic_entity(ent)) {
853 value = get_atomic_ent_value(ent);
855 PRINT_ENT_NODE_EDGE(ent, value, ENT_VALUE_EDGE_ATTR, i);
857 fprintf (F, "edge: { sourcename: \"%p\" targetname: \"", GET_ENTID(ent));
859 fprintf(F, "\" " ENT_VALUE_EDGE_ATTR "\"}\n");
861 dump_const_expression(value);
864 if (is_compound_entity(ent)) {
865 for (i = 0; i < get_compound_ent_n_values(ent); i++) {
866 value = get_compound_ent_value(ent, i);
868 PRINT_ENT_NODE_EDGE(ent,value,ENT_VALUE_EDGE_ATTR,i);
869 dump_const_expression(value);
870 PRINT_ENT_ENT_EDGE(ent, get_compound_ent_value_member(ent, i), ENT_CORR_EDGE_ATTR, i);
872 fprintf (F, "edge: { sourcename: \"%p\" targetname: \"%p\" "
873 ENT_CORR_EDGE_ATTR "}\n", GET_ENTID(ent),
874 get_compound_ent_value_member(ent, i), i);
883 type *tp = (type *)tore;
885 /* and now the edges */
886 switch (get_type_tpop_code(tp)) {
889 for (i=0; i < get_class_n_supertypes(tp); i++) {
890 PRINT_TYPE_TYPE_EDGE(tp,get_class_supertype(tp, i),TYPE_SUPER_EDGE_ATTR);
893 for (i=0; i < get_class_n_members(tp); i++) {
894 PRINT_TYPE_ENT_EDGE(tp,get_class_member(tp, i),TYPE_MEMBER_EDGE_ATTR);
899 for (i=0; i < get_struct_n_members(tp); i++) {
900 PRINT_TYPE_ENT_EDGE(tp,get_struct_member(tp, i),TYPE_MEMBER_EDGE_ATTR);
905 for (i = 0; i < get_method_n_params(tp); i++)
907 PRINT_TYPE_TYPE_EDGE(tp,get_method_param_type(tp, i),METH_PAR_EDGE_ATTR,i);
909 for (i = 0; i < get_method_n_ress(tp); i++)
911 PRINT_TYPE_TYPE_EDGE(tp,get_method_res_type(tp, i),METH_RES_EDGE_ATTR,i);
916 for (i = 0; i < get_union_n_members(tp); i++)
918 PRINT_TYPE_ENT_EDGE(tp,get_union_member(tp, i),UNION_EDGE_ATTR);
923 PRINT_TYPE_TYPE_EDGE(tp,get_array_element_type(tp),ARR_ELT_TYPE_EDGE_ATTR);
924 PRINT_TYPE_ENT_EDGE(tp,get_array_element_entity(tp),ARR_ENT_EDGE_ATTR);
926 case tpo_enumeration:
931 PRINT_TYPE_TYPE_EDGE(tp,get_pointer_points_to_type(tp), PTR_PTS_TO_EDGE_ATTR);
939 break; /* case k_type */
942 printf(" *** irdump, dump_type_info(l.%i), faulty type.\n", __LINE__);
944 } /* switch kind_or_entity */
947 /* dumps a class type node and a superclass edge.
948 If env != null dumps entities of classes and overwrites edges. */
950 dump_class_hierarchy_node (type_or_ent *tore, void *env) {
951 int i = 0; /* to shutup gcc */
953 /* dump this type or entity */
954 switch (get_kind(tore)) {
956 entity *ent = (entity *)tore;
957 if (get_entity_owner(ent) == get_glob_type()) break;
958 if ((env) && is_class_type(get_entity_owner(ent))) {
960 dump_entity_node(ent);
962 PRINT_TYPE_ENT_EDGE(get_entity_owner(ent),ent,TYPE_MEMBER_EDGE_ATTR);
963 for(i = 0; i < get_entity_n_overwrites(ent); i++)
965 PRINT_ENT_ENT_EDGE(get_entity_overwrites(ent, i),ent, ENT_OVERWRITES_EDGE_ATTR);
968 } break; /* case k_entity */
971 type *tp = (type *)tore;
972 if (tp == get_glob_type()) break;
973 switch (get_type_tpop_code(tp)) {
976 /* and now the edges */
977 for (i=0; i < get_class_n_supertypes(tp); i++)
979 PRINT_TYPE_TYPE_EDGE(tp,get_class_supertype(tp, i),TYPE_SUPER_EDGE_ATTR);
985 break; /* case k_type */
988 printf(" *** irdump, dump_class_hierarchy_node(l.%i), faulty type.\n", __LINE__);
990 } /* switch kind_or_entity */
993 /************************************************************************/
994 /* open and close vcg file */
995 /************************************************************************/
997 static void vcg_open (ir_graph *irg, char *suffix) {
998 char *fname; /* filename to put the vcg information in */
1005 /** open file for vcg graph */
1006 ent = get_irg_ent(irg);
1007 id = ent->ld_name ? ent->ld_name : ent->name;
1008 /* Don't use get_entity_ld_ident (ent) as it computes the mangled name! */
1009 len = id_to_strlen (id);
1010 cp = id_to_str (id);
1011 if (dump_file_suffix)
1012 fname = malloc (len + 5 + strlen(suffix) + strlen(dump_file_suffix));
1014 fname = malloc (len + 5 + strlen(suffix));
1015 strncpy (fname, cp, len); /* copy the filename */
1017 if (dump_file_suffix) strcat (fname, dump_file_suffix); /* append file suffix */
1018 strcat (fname, suffix); /* append file suffix */
1019 strcat (fname, ".vcg"); /* append the .vcg suffix */
1020 F = fopen (fname, "w"); /* open file for writing */
1022 panic ("cannot open %s for writing (%m)", fname); /* not reached */
1026 strcpy(label, "yes");
1028 strcpy (label, "no");
1033 "graph: { title: \"ir graph of %s\"\n"
1034 "display_edge_labels: %s\n"
1035 "layoutalgorithm: mindepth\n"
1036 "manhattan_edges: yes\n"
1037 "port_sharing: no\n"
1038 "orientation: bottom_to_top\n"
1039 "classname 1: \"Data\"\n"
1040 "classname 2: \"Block\"\n"
1041 "classname 3: \"Entity type\""
1042 "classname 4: \"Entity owner\""
1043 "classname 5: \"Method Param\""
1044 "classname 6: \"Method Res\""
1045 "classname 7: \"Super\""
1046 "classname 8: \"Union\""
1047 "classname 9: \"Points-to\""
1048 "classname 10: \"Array Element Type\""
1049 "classname 11: \"Overwrites\""
1050 "classname 12: \"Member\""
1053 fprintf (F, "\n"); /* a separator */
1056 static void vcg_open_name (const char *name) {
1057 char *fname; /* filename to put the vcg information in */
1061 /** open file for vcg graph */
1063 fname = malloc (len + 5);
1064 if (dump_file_suffix)
1065 fname = malloc (len + 5 + strlen(dump_file_suffix));
1067 fname = malloc (len + 5);
1068 strcpy (fname, name); /* copy the filename */
1069 if (dump_file_suffix) strcat (fname, dump_file_suffix);
1070 strcat (fname, ".vcg"); /* append the .vcg suffix */
1071 F = fopen (fname, "w"); /* open file for writing */
1073 panic ("cannot open %s for writing (%m)", fname); /* not reached */
1077 strcpy(label, "yes");
1079 strcpy (label, "no");
1084 "graph: { title: \"ir graph of %s\"\n"
1085 "display_edge_labels: %s\n"
1086 "layoutalgorithm: mindepth\n"
1087 "manhattan_edges: yes\n"
1088 "port_sharing: no\n"
1089 "orientation: bottom_to_top\n"
1090 "classname 1: \"Data\"\n"
1091 "classname 2: \"Block\"\n"
1092 "classname 3: \"Entity type\"\n"
1093 "classname 4: \"Entity owner\"\n"
1094 "classname 5: \"Method Param\"\n"
1095 "classname 6: \"Method Res\"\n"
1096 "classname 7: \"Super\"\n"
1097 "classname 8: \"Union\"\n"
1098 "classname 9: \"Points-to\"\n"
1099 "classname 10: \"Array Element Type\"\n"
1100 "classname 11: \"Overwrites\"\n"
1101 "classname 12: \"Member\"\n"
1104 fprintf (F, "\n"); /* a separator */
1109 fprintf (F, "}\n"); /* print footer */
1110 fclose (F); /* close vcg file */
1113 /************************************************************************/
1114 /* routines to dump a graph, blocks as conventional nodes. */
1115 /************************************************************************/
1117 static int node_floats(ir_node *n) {
1118 return ((get_op_pinned(get_irn_op(n)) == floats) &&
1119 (get_irg_pinned(current_ir_graph) == floats));
1123 dump_whole_node (ir_node *n, void* env) {
1125 if (!node_floats(n)) dump_ir_block_edge(n);
1126 dump_ir_data_edges(n);
1130 dump_ir_graph (ir_graph *irg)
1133 rem = current_ir_graph;
1134 current_ir_graph = irg;
1138 /* walk over the graph */
1139 /* dump_whole_node must be called in post visiting predecessors */
1140 irg_walk(irg->end, NULL, dump_whole_node, NULL);
1142 /* dump the out edges in a separate walk */
1143 if ((dump_out_edge_flag) && (get_irg_outs_state(irg) != no_outs)) {
1144 irg_out_walk(irg->start, dump_out_edge, NULL, NULL);
1149 current_ir_graph = rem;
1152 /***********************************************************************/
1153 /* the following routines dump the nodes as attached to the blocks. */
1154 /***********************************************************************/
1157 dump_ir_blocks_nodes (ir_node *n, void *env) {
1158 ir_node *block = (ir_node *)env;
1160 if (is_no_Block(n) && get_nodes_Block(n) == block && !node_floats(n)) {
1162 dump_ir_data_edges(n);
1164 if (get_irn_op(n) == op_Bad)
1169 dump_ir_block (ir_node *block, void *env) {
1170 ir_graph *irg = (ir_graph *)env;
1172 if (get_irn_opcode(block) == iro_Block) {
1174 /* This is a block. So dump the vcg information to make a block. */
1175 fprintf(F, "graph: { title: \"");
1176 PRINT_NODEID(block);
1177 fprintf(F, "\" label: \"");
1178 #ifdef DEBUG_libfirm
1179 fprintf (F, "%ld", get_irn_node_nr(block));
1181 fprintf (F, "%s", id_to_str(block->op->name));
1183 if (exc_normal != get_Block_exc (block))
1184 fprintf (F, " (%s)", exc_to_string (get_Block_exc (block)));
1186 fprintf(F, "\" status:clustered color:%s \n",
1187 get_Block_matured (block) ? "yellow" : "red");
1188 /* dump the blocks edges */
1189 dump_ir_data_edges(block);
1191 /* dump the nodes that go into the block */
1192 irg_walk(irg->end, dump_ir_blocks_nodes, NULL, block);
1194 /* Close the vcg information for the block */
1195 fprintf(F, "}\n\n");
1196 dump_const_node_local(block, NULL);
1202 dump_blockless_nodes (ir_node *n, void *env) {
1203 if (is_no_Block(n) && get_irn_op(get_nodes_Block(n)) == op_Bad) {
1205 dump_ir_data_edges(n);
1206 dump_ir_block_edge(n);
1207 if (get_irn_op(n) == op_Bad) Bad_dumped = 1;
1210 if (node_floats(n)) {
1212 dump_ir_data_edges(n);
1213 if (get_irn_op(n) == op_Bad) Bad_dumped = 1;
1217 static void dump_ir_block_graph_2 (ir_graph *irg)
1220 /* walk over the blocks in the graph */
1221 irg_block_walk(irg->end, dump_ir_block, NULL, irg);
1223 /* dump all nodes that are not in a Block */
1224 irg_walk(irg->end, dump_blockless_nodes, NULL, NULL);
1226 /* dump the Bad node */
1228 dump_node(get_irg_bad(irg), NULL);
1232 dump_ir_block_graph (ir_graph *irg)
1235 rem = current_ir_graph;
1236 current_ir_graph = irg;
1240 dump_ir_block_graph_2 (irg);
1242 if (dump_loop_information_flag) dump_loop_info(irg);
1245 current_ir_graph = rem;
1249 /***********************************************************************/
1250 /* the following routines dump a control flow graph */
1251 /***********************************************************************/
1255 dump_block_to_cfg (ir_node *block, void *env) {
1259 if (get_irn_opcode(block) == iro_Block) {
1260 /* This is a block. Dump a node for the block. */
1261 fprintf (F, "node: {title:\""); PRINT_NODEID(block);
1262 fprintf (F, "\" label: \"%s ", id_to_str(block->op->name)); PRINT_NODEID(block);
1264 if (exc_normal != get_Block_exc (block))
1265 fprintf (F, " (%s)", exc_to_string (get_Block_exc (block)));
1268 if (dump_dominator_information_flag)
1269 fprintf(F, "info1:\"dom depth %d\"", get_Block_dom_depth(block));
1271 /* Dump the edges */
1272 for ( i = 0; i < get_Block_n_cfgpreds(block); i++)
1273 if (get_irn_op(skip_Proj(get_Block_cfgpred(block, i))) != op_Bad) {
1274 pred = get_nodes_Block(skip_Proj(get_Block_cfgpred(block, i)));
1275 fprintf (F, "edge: { sourcename: \"");
1276 PRINT_NODEID(block);
1277 fprintf (F, "\" targetname: \"");
1279 fprintf (F, "\" }\n");
1282 /* Dump dominator edge */
1283 if (dump_dominator_information_flag && get_Block_idom(block)) {
1284 pred = get_Block_idom(block);
1285 fprintf (F, "edge: { sourcename: \"");
1286 PRINT_NODEID(block);
1287 fprintf (F, "\" targetname: \"");
1289 fprintf (F, "\" " DOMINATOR_EDGE_ATTR "}\n");
1295 dump_cfg (ir_graph *irg)
1297 ir_graph *rem = current_ir_graph;
1298 int ddif = dump_dominator_information_flag;
1299 current_ir_graph = irg;
1300 vcg_open (irg, "-cfg");
1302 if (get_irg_dom_state(irg) != dom_consistent)
1303 dump_dominator_information_flag = 0;
1305 /* walk over the blocks in the graph */
1306 irg_block_walk(irg->end, dump_block_to_cfg, NULL, NULL);
1307 dump_ir_node (irg->bad);
1309 dump_dominator_information_flag = ddif;
1311 current_ir_graph = rem;
1315 /***********************************************************************/
1316 /* the following routine dumps all type information reachable from an */
1318 /***********************************************************************/
1322 dump_type_graph (ir_graph *irg)
1325 rem = current_ir_graph;
1326 current_ir_graph = irg;
1328 vcg_open (irg, "-type");
1330 /* walk over the blocks in the graph */
1331 type_walk_irg(irg, dump_type_info, NULL, NULL);
1332 /* The walker for the const code can be called several times for the
1333 same (sub) experssion. So that no nodes are dumped several times
1334 we decrease the visited flag of the corresponding graph after each
1335 walk. So now increase it finally. */
1336 inc_irg_visited(get_const_code_irg());
1339 current_ir_graph = rem;
1342 /***********************************************************************/
1343 /* the following routine dumps all type information */
1344 /***********************************************************************/
1348 dump_all_types (void)
1350 vcg_open_name ("All_types");
1351 type_walk(dump_type_info, NULL, NULL);
1352 inc_irg_visited(get_const_code_irg());
1357 dump_class_hierarchy (bool entities)
1359 vcg_open_name ("class_hierarchy");
1361 type_walk(dump_class_hierarchy_node, NULL, (void *)1);
1363 type_walk(dump_class_hierarchy_node, NULL, NULL);
1367 /***********************************************************************/
1368 /* dumps a graph with type information */
1369 /***********************************************************************/
1373 dump_ir_graph_w_types (ir_graph *irg)
1376 rem = current_ir_graph;
1377 current_ir_graph = irg;
1379 vcg_open (irg, "-all");
1381 /* dump common ir graph */
1382 irg_walk(irg->end, dump_whole_node, NULL, NULL);
1383 /* dump type info */
1384 type_walk_irg(irg, dump_type_info, NULL, NULL);
1385 inc_irg_visited(get_const_code_irg());
1386 /* dump edges from graph to type info */
1387 irg_walk(irg->end, dump_node2type_edges, NULL, NULL);
1390 current_ir_graph = rem;
1394 dump_ir_block_graph_w_types (ir_graph *irg)
1397 rem = current_ir_graph;
1398 current_ir_graph = irg;
1400 vcg_open (irg, "-all");
1402 /* dump common blocked ir graph */
1403 dump_ir_block_graph_2(irg);
1404 /* dump type info */
1405 type_walk_irg(irg, dump_type_info, NULL, NULL);
1406 inc_irg_visited(get_const_code_irg());
1407 /* dump edges from graph to type info */
1408 irg_walk(irg->end, dump_node2type_edges, NULL, NULL);
1411 current_ir_graph = rem;
1414 /***********************************************************************/
1415 /* dumps all graphs with the graph-dumper passed. Possible dumpers: */
1417 /* dump_ir_block_graph */
1419 /* dump_type_graph */
1420 /* dump_ir_graph_w_types */
1421 /***********************************************************************/
1422 void dump_all_ir_graphs (dump_graph_func *dump_graph) {
1424 for (i=0; i < get_irp_n_irgs(); i++) {
1425 dump_graph(get_irp_irg(i));
1430 /* To turn off display of edge labels. Edge labels offen cause xvcg to
1431 abort with a segmentation fault. */
1432 void turn_off_edge_labels(void) {
1437 void dump_consts_local(bool b) {
1438 dump_const_local = b;
1441 void turn_off_constant_entity_values(void) {
1445 void dump_keepalive_edges(bool b) {
1449 void dump_out_edges(void) {
1450 dump_out_edge_flag = 1;
1453 void dump_dominator_information(void) {
1454 dump_dominator_information_flag = 1;
1457 void dump_loop_information(void) {
1458 dump_loop_information_flag = 1;
1461 void dont_dump_loop_information(void) {
1462 dump_loop_information_flag = 0;
1465 static void clear_link(ir_node * node, void * env) {
1466 set_irn_link(node, NULL);
1469 static void collect_blocks_floats_cg(ir_node * node, pmap * map) {
1471 || node_floats(node)
1472 || get_irn_op(node) == op_Bad
1473 || get_irn_op(node) == op_Unknown) {
1474 pmap_entry * entry = pmap_find(map, current_ir_graph);
1476 ARR_APP1(ir_node *, (ir_node **) entry->value, node);
1478 ir_node ** arr = NEW_ARR_F(ir_node *, 1);
1480 pmap_insert(map, current_ir_graph, arr);
1483 ir_node * block = get_nodes_Block(node);
1484 set_irn_link(node, get_irn_link(block));
1485 set_irn_link(block, node);
1490 static void dump_cg_ir_block(ir_node * block, void * env) {
1492 pmap *irgmap = (pmap *)env;
1493 assert(is_Block(block));
1494 fprintf(F, "graph: { title: \"");
1495 PRINT_NODEID(block);
1496 fprintf(F, "\" label: \"");
1497 #ifdef DEBUG_libfirm
1498 fprintf (F, "%ld", get_irn_node_nr(block));
1500 fprintf (F, "%s", id_to_str(block->op->name));
1502 if (exc_normal != get_Block_exc(block)) {
1503 fprintf (F, " (%s)", exc_to_string (get_Block_exc(block)));
1506 fprintf(F, "\" status:clustered color:%s \n",
1507 get_Block_matured(block) ? "yellow" : "red");
1509 /* dump the blocks edges */
1510 dump_ir_data_edges(block);
1512 /* dump the nodes that go into the block */
1513 for (node = get_irn_link(block); node; node = get_irn_link(node)) {
1514 dump_node(node, irgmap);
1515 dump_ir_data_edges(node);
1518 /* Close the vcg information for the block */
1519 fprintf(F, "}\n\n");
1522 static void d_cg_block_graph(ir_graph *irg, ir_node **arr, pmap *irgmap) {
1525 fprintf(F, "graph: { title: \"%p\" label: \"%s\" status:clustered color:white \n",
1526 irg, id_to_str(get_entity_ident(get_irg_ent(irg))));
1528 for (i = ARR_LEN(arr) - 1; i >= 0; --i) {
1529 ir_node * node = arr[i];
1530 if (is_Block(node)) {
1531 /* Dumps the block and all the nodes in the block , which are to
1532 be found in Block->link. */
1533 dump_cg_ir_block(node, irgmap);
1535 /* Nodes that are not in a Block. */
1536 dump_node(node, NULL);
1537 dump_ir_data_edges(node);
1540 /* Close the vcg information for the irg */
1541 fprintf(F, "}\n\n");
1544 /* dump interprocedural graph with surrounding methods */
1545 void dump_cg_block_graph(ir_graph * irg) {
1546 pmap * map = pmap_create();
1547 pmap * map2 = pmap_create();
1552 irg_walk_graph(irg, clear_link, (irg_walk_func *) collect_blocks_floats_cg, map);
1553 for (entry = pmap_first(map); entry; entry = pmap_next(map))
1554 pmap_insert(map2, entry->key, entry->value);
1555 for (entry = pmap_first(map); entry; entry = pmap_next(map)) {
1556 d_cg_block_graph(entry->key, entry->value, map2);
1557 DEL_ARR_F(entry->value);
1563 if (dump_loop_information_flag) dump_loop_info(irg);
1567 static void collect_node(ir_node * node, void *env) {
1569 || node_floats(node)
1570 || get_irn_op(node) == op_Bad
1571 || get_irn_op(node) == op_Unknown) {
1572 ir_node ** arr = (ir_node **) get_irg_link(current_ir_graph);
1573 ARR_APP1(ir_node *, arr, node);
1574 set_irg_link(current_ir_graph, arr); /* arr is an l-value, APP_ARR might change it! */
1576 ir_node * block = get_nodes_Block(node);
1577 set_irn_link(node, get_irn_link(block));
1578 set_irn_link(block, node);
1582 /* Links all nodes that have the block field set in the link field of
1583 the block. Adds all blocks and nodes not associated with a block
1584 in a array in irg->link. */
1585 static void collect_nodes(void) {
1587 for (i = 0; i < get_irp_n_irgs(); i++)
1588 set_irg_link(get_irp_irg(i), NEW_ARR_F(ir_node *, 0));
1589 cg_walk(clear_link, collect_node, NULL);
1592 static void dump_graphs(void) {
1594 for (i = 0; i < get_irp_n_irgs(); i++) {
1595 current_ir_graph = get_irp_irg(i);
1596 d_cg_block_graph(current_ir_graph, get_irg_link(current_ir_graph), NULL);
1600 /* Dump all irgs in interprocedural view to a single file. */
1601 void dump_all_cg_block_graph(void) {
1603 int rem_view = interprocedural_view;
1604 interprocedural_view = 1;
1605 vcg_open_name ("All_graphs");
1610 if (dump_loop_information_flag)
1611 for (i = 0; i < get_irp_n_irgs(); i++)
1612 dump_loop_info(get_irp_irg(i));
1615 interprocedural_view = rem_view;
1618 /* dump interprocedural block graph with surrounding methods */
1619 void dump_cg_graph(ir_graph * irg) {
1620 pmap * map = pmap_create();
1621 pmap * map2 = pmap_create(); /* We can not iterate in the same map twice! */
1625 irg_walk_graph(irg, clear_link, (irg_walk_func *) collect_blocks_floats_cg, map);
1626 for (entry = pmap_first(map); entry; entry = pmap_next(map))
1627 pmap_insert(map2, entry->key, entry->value);
1628 for (entry = pmap_first(map); entry; entry = pmap_next(map)) {
1629 ir_node ** arr = entry->value;
1631 ident * irg_ident = get_entity_ident(get_irg_ent(entry->key));
1633 fprintf(F, "graph: { title: \"%s\" label: \"%s\" status:clustered color:white \n",
1634 id_to_str(irg_ident), id_to_str(irg_ident));
1636 for (i = ARR_LEN(arr) - 1; i >= 0; --i) {
1637 ir_node * node = arr[i];
1638 dump_node(node, map2);
1639 dump_ir_data_edges(node);
1640 if (is_Block(node)) {
1641 for (node = get_irn_link(node); node; node = get_irn_link(node)) {
1642 dump_node(node, map2);
1643 dump_ir_block_edge(node);
1644 dump_ir_data_edges(node);
1651 /* Close the vcg information for the irg */
1652 fprintf(F, "}\n\n");