4d2d32eff8c52534fbf1c67a4a1c565d715e833e
[libfirm] / ir / ana2 / ecg.c
1 /* -*- c -*- */
2
3 /*
4  * Project:     libFIRM
5  * File name:   ir/ana/ecg.c
6  * Purpose:     Extended Call Graph
7  * Author:      Florian
8  * Modified by:
9  * Created:     14.09.2004
10  * CVS-ID:      $$
11  * Copyright:   (c) 1999-2004 Universität Karlsruhe
12  * Licence:     This file is protected by GPL -  GNU GENERAL PUBLIC LICENSE.
13  */
14
15 #ifdef HAVE_CONFIG_H
16 # include <config.h>
17 #endif
18
19 /**
20    Erweiterter Aufrufgraph.
21  */
22
23 #include "irnode.h"
24 #include "pmap.h"
25 /* #include "eset.h" */
26 #include "irgwalk.h"
27 #include "irgmod.h"
28 #include "irvrfy.h"
29 #include "trvrfy.h"
30 #include "xmalloc.h"
31
32 # define TRUE 1
33 # define FALSE 0
34
35 # define BUF_SIZE 1024
36
37 # include "ecg.h"
38
39 #include <string.h>             /* need memset */
40
41 /*
42   data structures
43 */
44
45 /* Lists, err, Sets */
46 typedef struct lset_entry
47 {
48   void *data;
49   struct lset_entry *next;
50 } lset_entry_t;
51
52 typedef struct lset
53 {
54   lset_entry_t *first;
55   lset_entry_t *last;           /* useful for lset_append */
56   lset_entry_t *curs;           /* for lset_first/lset_next */
57   int n_entries;
58 } lset_t;
59
60
61 typedef enum typalise_kind_enum {
62   type_invalid = 0,             /* invalid (only set at deletion time) */
63   type_exact = 1,               /* this and only this type (res.type) */
64   type_types = 2,               /* these types (res.types) */
65   type_type  = 3                /* this type and sub types (res.type) */
66 } typalise_kind;
67
68 typedef struct typalise
69 {
70   typalise_kind kind;
71   union
72   {
73     type *type;                 /* for kind == kind_exact and kind == kind_type */
74     lset_t *types;              /* for kind == kind_types */
75   } res;
76   int id;
77 } typalise_t;
78
79 /*
80    le flag
81 */
82 /* static int verbose     = 0; */
83 static int do_typalise = 0;
84
85 /*
86    globals
87 */
88
89 /* mapping from method graphs (callR) to method graphs (lset_ts of callEds) */
90 /* static pmap *calls; */
91 static pmap *graph_infos;
92
93 /** Counters for ecg_ecg and friends */
94 static long _graphs = 0;
95 static long _calls  = 0;
96 static long _allocs = 0;
97
98 static int _depth = 0;
99 static int _max_depth = 0;
100
101 static int _max_callEds = 0;
102 static entity* _max_callEds_callR = NULL;
103
104 static long ta_id = 0;
105
106 /*
107   Lists, err, Sets
108  */
109
110
111 /* create a new lset */
112 static lset_t *lset_create (void)
113 {
114   lset_t *lset = xmalloc (sizeof (lset_t));
115
116   return (lset);
117 }
118
119 /* check whether the lset contains an entry for the given data */
120 static int lset_contains (lset_t *lset, void *data)
121 {
122   lset_entry_t *entry = lset->first;
123
124   while (NULL != entry) {
125     if (data == entry->data) {
126       return (TRUE);
127     }
128
129     entry = entry->next;
130   }
131
132   return (FALSE);
133 }
134
135 /* check whether the given lset is empty */
136 static int lset_empty (lset_t *lset)
137 {
138   return (NULL == lset->first);
139 }
140
141
142 /* insert the data into the lset (unless there's an entry for it
143    already) */
144 static void lset_insert (lset_t *lset, void *data)
145 {
146   if (! lset_contains (lset, data)) {
147     lset_entry_t *entry = xmalloc (sizeof (lset_entry_t));
148     entry->data = data;
149     entry->next = lset->first;
150     lset->first = entry;
151
152     if (NULL == lset->last) {
153       lset->last = entry;
154     }
155
156     lset->n_entries ++;
157   }
158 }
159
160 /* insert all entries from src into tgt */
161 static void lset_insert_all (lset_t *tgt, lset_t *src)
162 {
163   lset_entry_t *curs = src->first;
164
165   while (NULL != curs) {
166     lset_insert (tgt, curs->data);
167
168     curs = curs->next;
169   }
170 }
171
172 /* append src to tgt. src is deallocated. */
173 static void lset_append (lset_t *tgt, lset_t *src)
174 {
175   assert (! tgt->last->next);
176
177   tgt->last->next = src->first;
178   tgt->last = src->last;
179   tgt->n_entries += src->n_entries;
180
181   memset (src, 0x00, sizeof (lset_t));
182   free (src);
183 }
184
185 /* remove the entry for the given data element from the lset. return
186    TRUE iff it was on the list in the first place, FALSE else */
187 # ifdef SHUT_UP_GCC
188 static int lset_remove (lset_t *lset, void *data)
189 {
190   lset_entry_t *entry = lset->first;
191   lset_entry_t *prev = NULL;
192
193   while (NULL != entry) {
194     if (data == entry->data) {
195       /* ok, dike it out */
196
197       if (NULL == prev) { /* ok, it's lset->first that needs diking */
198         lset->first = entry->next;
199       } else {
200         prev->next = entry->next;
201       }
202
203       memset (entry, 0x00, sizeof (lset_entry_t));
204       free (entry);
205
206       lset->n_entries --;
207
208       return (TRUE);
209     }
210
211     prev = entry;
212     entry = entry->next;
213   }
214
215   return (FALSE);
216 }
217 # endif /* def SHUT_UP_GCC */
218 /* prepare the given lset for an iteration. return the first element. */
219 static void *lset_first (lset_t *lset)
220 {
221   lset->curs = lset->first;
222
223   if (lset->first) {
224     return (lset->first->data);
225   } else {
226     return (NULL);
227   }
228 }
229
230 /* after calling lset_first, get the next element, if applicable, or
231    NULL */
232 static void *lset_next (lset_t *lset)
233 {
234   lset->curs = lset->curs->next;
235
236   if (lset->curs) {
237     return (lset->curs->data);
238   } else {
239     return (NULL);
240   }
241 }
242
243 /* say how many entries there are in the given lset */
244 static int lset_n_entries (lset_t *lset)
245 {
246   return (lset->n_entries);
247 }
248
249 /* deallocate the lset and all of its entries */
250 static void lset_destroy (lset_t *lset)
251 {
252   lset_entry_t *curs = lset->first;
253
254   while (NULL != curs) {
255     lset_entry_t *tmp = curs->next;
256
257     memset (curs, 0x00, sizeof (lset_entry_t));
258     free (curs);
259
260     curs = tmp;
261   }
262
263   memset (lset, 0x00, sizeof (lset_t));
264   free (lset);
265 }
266
267 /* ====================
268   Typalize Ptr
269  ==================== */
270 /**
271    Find out whether the given clazz uses the given implementation of a
272    method.  Presumably, this is because clazz inherits the graph as
273    the implementation for a method.
274 */
275 static int uses_graph (type *clazz, entity *meth, ir_graph *graph)
276 {
277   type *g_clazz = get_entity_owner (meth);
278
279   if (g_clazz == clazz) {
280     return (TRUE);
281   }
282
283   if (peculiarity_existent == get_entity_peculiarity (meth)) {
284     ir_graph *g_graph = get_entity_irg (meth);
285
286     if (g_graph != graph) {
287       return (FALSE);
288     }
289   }
290
291   /* else inherited or description */
292   int use = FALSE;
293   int i;
294   int n_over = get_entity_n_overwrittenby (meth); /* DOWN-wards */
295
296   for (i = 0; (i < n_over) && (!use); i ++) {
297     entity *over = get_entity_overwrittenby (meth, i);
298
299     use |= uses_graph (clazz, over, graph);
300   }
301
302   return (use);
303 }
304
305
306 /**
307    Find out whether otype is a subtype of stype.
308    Return non-zero iff otype is a subtype of stype.
309 */
310 static int is_subtype (type *otype, type *stype)
311 {
312   int n_sub = get_class_n_subtypes (stype);
313   int is_sub = FALSE;
314   int i;
315
316   if (otype == stype) {
317     return (TRUE);
318   }
319
320   for (i = 0; (!is_sub) && (i < n_sub); i ++) {
321     type *sub = get_class_subtype (stype, i);
322
323     is_sub |= is_subtype (otype, sub);
324   }
325
326
327   return (is_sub);
328 }
329
330 /**
331     Compute the closure of all subtypes of otype (including otype
332     itself)
333 */
334 static void _collect_subtypes (type *otype, lset_t *set)
335 {
336   lset_insert (set, otype);
337
338   int n_sub = get_class_n_subtypes (otype);
339   int i;
340
341   for (i = 0; i < n_sub; i ++) {
342     type *sub = get_class_subtype (otype, i);
343
344     _collect_subtypes (sub, set);
345   }
346 }
347
348 static lset_t *subtype_closure (type *otype)
349 {
350   lset_t *set = lset_create ();
351
352   _collect_subtypes (otype, set);
353
354   return (set);
355 }
356
357 /**
358    Helper method for get_owner_types
359 */
360 static void _collect_owner_types (entity *method, ir_graph *graph, lset_t *tps)
361 {
362   /* search DOWNwards in clazz hierarchy */
363
364   if ((peculiarity_description == get_entity_peculiarity (method)) ||
365       (peculiarity_inherited   == get_entity_peculiarity (method))) {
366     lset_insert (tps, get_entity_owner (method));
367   } else if (peculiarity_existent == get_entity_peculiarity (method)) {
368     ir_graph *ex_graph = get_entity_irg (method);
369
370     if ((NULL == ex_graph) || (ex_graph == graph)) {
371       /* wtf? they define the same graph again? well, whatever: */
372       lset_insert (tps, get_entity_owner (method));
373     } else {
374       /* aha: they define a new graph. can't have that, so bail out */
375       return;
376     }
377   }
378
379   int n_over = get_entity_n_overwrittenby (method);
380   int i;
381
382   for (i = 0; i < n_over; i ++) {
383     entity *ometh = get_entity_overwrittenby (method, i);
384
385     _collect_owner_types (ometh, graph, tps);
386   }
387 }
388
389
390 /**
391    Collect all classes that use the given implementation of a method.
392 */
393 static lset_t *get_owner_types (ir_graph *graph)
394 {
395   lset_t *tps = lset_create ();
396   entity *meth = get_irg_entity (graph);
397
398   _collect_owner_types (meth, graph, tps);
399
400   return (tps);
401 }
402
403
404 /**
405    Convenience funcs to create a typalise_t
406 */
407 static typalise_t *ta_exact (type *tp)
408 {
409   typalise_t *ta = (typalise_t*) xmalloc (sizeof (typalise_t));
410   ta->kind = type_exact;
411   ta->res.type = tp;
412   ta->id = ta_id ++;
413
414   assert (is_class_type (tp));
415
416   return (ta);
417 }
418
419 static typalise_t *ta_types (lset_t *set)
420 {
421   typalise_t *ta = (typalise_t*) xmalloc (sizeof (typalise_t));
422   ta->kind = type_types;
423   ta->res.types = set;
424   ta->id = ta_id ++;
425
426   return (ta);
427 }
428
429 static typalise_t *ta_type (type *tp)
430 {
431   typalise_t *ta = (typalise_t*) xmalloc (sizeof (typalise_t));
432   ta->kind = type_type;
433   ta->res.type = tp;
434   ta->id = ta_id ++;
435
436   assert (is_class_type (tp));
437
438   return (ta);
439 }
440
441 static void ta_delete (typalise_t *ta)
442 {
443   if (type_types == ta->kind) {
444     lset_destroy (ta->res.types);
445     ta->res.types = NULL;
446   } else {
447     ta->res.type = NULL;
448   }
449
450   ta->kind = type_invalid;
451
452   free (ta);
453 }
454
455 /**
456     Join 'one' and 'two'; both args are deallocated, result is freshly
457     allocated.
458 */
459 static typalise_t *ta_join (typalise_t *one, typalise_t *two)
460 {
461   typalise_t *res = NULL;
462
463   switch (one->kind) {
464   case (type_invalid): { /* shut up, gcc */ }
465   case (type_exact): {
466     switch (two->kind) {
467     case (type_invalid): { /* shut up, gcc */ }
468     case (type_exact): {
469       if (one->res.type == two->res.type) {
470         res = one;
471       } else {
472         lset_t *set = lset_create ();
473         lset_insert (set, one->res.type);
474         lset_insert (set, two->res.type);
475         res = ta_types (set);
476
477         ta_delete (one);
478       }
479
480       ta_delete (two);
481     } break;
482     case (type_types): {
483       lset_insert (two->res.types, one->res.type);
484       ta_delete (one);
485
486       res = two;
487     } break;
488     case (type_type): {
489       if (is_subtype (one->res.type, two->res.type)) {
490         ta_delete (one);
491         res = two;
492       } else {
493         lset_t *closure = subtype_closure (two->res.type);
494         lset_insert (closure, one->res.type);
495
496         ta_delete (two);
497
498         res = one;
499       }
500     } break;
501     }
502   } break;
503   case (type_types): {
504     switch (two->kind) {
505     case (type_invalid): { /* shut up, gcc */ }
506     case (type_exact): {
507       res = ta_join (two, one);
508     } break;
509     case (type_types): {
510       lset_insert_all (one->res.types, two->res.types);
511       ta_delete (two);
512
513       res = one;
514     } break;
515     case (type_type): {
516       lset_t *closure = subtype_closure (two->res.type);
517       lset_append (one->res.types, closure);
518
519       ta_delete (two);
520
521       res = one;
522     } break;
523     }
524   } break;
525   case (type_type): {
526     switch (two->kind) {
527     case (type_invalid): { /* shut up, gcc */ }
528     case (type_exact): {
529       res = ta_join (two, one);
530     } break;
531     case (type_types): {
532       res = ta_join (two, one);
533     } break;
534     case (type_type): {
535       type *one_type = one->res.type;
536       type *two_type = two->res.type;
537
538       if (is_subtype (one_type, two_type)) {
539         ta_delete (one);
540         res = two;
541       } else if (is_subtype (two_type, one_type)) {
542         ta_delete (two);
543         res = one;
544       } else {
545         lset_t *one_closure = subtype_closure (one->res.type);
546         lset_t *two_closure = subtype_closure (two->res.type);
547
548         lset_append (one_closure, two_closure);
549
550         ta_delete (two);
551         ta_delete (one);
552
553         res = ta_types (one_closure);
554       }
555     } break;
556     }
557   } break;
558   }
559
560   assert (res && "no result");
561
562   return (res);
563 }
564
565
566 # ifdef SHUT_UP_GCC
567 static const char *ta_name (typalise_t *ta)
568 {
569   /* # define BUF_SIZE 1024 */
570   static char buf [BUF_SIZE];
571
572   int len = sprintf (buf, "[%d] ", ta->id);
573
574   switch (ta->kind) {
575   case (type_invalid): { /* shut up, gcc */ }
576   case (type_exact): {
577     len += sprintf (buf+len, "only ");
578     strncat (buf, get_type_name (ta->res.type), BUF_SIZE);
579   } break;
580   case (type_types): {
581     len += sprintf (buf+len, "one_of ");
582
583     type *iter = lset_first (ta->res.types);
584
585     int size = BUF_SIZE - len - 1;
586     while ((NULL != iter) && (0 < size)) {
587       char *dest = strncat (buf, get_type_name (iter), size);
588       size = (dest - buf);
589
590       iter = lset_next (ta->res.types);
591     }
592   } break;
593   case (type_type): {
594     len += sprintf (buf+len, "poly ");
595     strncat (buf, get_type_name (ta->res.type), BUF_SIZE);
596   } break;
597   }
598
599   return (buf);
600   /* # undef BUF_SIZE */
601 }
602 # endif /* SHUT_UP_GCC */
603
604 /**
605    Check whether the given typalise_t includes the given type.
606 */
607 static int ta_supports (typalise_t *ta, ir_graph *graph)
608 {
609   switch (ta->kind) {
610   case (type_invalid): { /* shut up, gcc */ }
611   case (type_exact): {
612     int res = FALSE;
613     lset_t *tps = get_owner_types (graph);
614
615     if (lset_contains (tps, ta->res.type)) {
616       res = TRUE;
617     }
618
619     lset_destroy (tps);
620
621     return (res);
622   }
623   case (type_type): {
624     entity *meth = get_irg_entity (graph);
625     type *tp = get_entity_owner (meth);
626     int res = is_subtype (tp, ta->res.type);
627
628     if (res) {
629       return (TRUE);
630     } else {
631       res = uses_graph (ta->res.type, meth, graph);
632     }
633
634     return (res);
635   }
636   case (type_types): {
637     type *tp = get_entity_owner (get_irg_entity (graph));
638
639     return (lset_contains (ta->res.types, tp));
640   }
641   }
642
643   assert (0 && "invalid ta");
644 }
645
646
647 /**
648    Given a set of graphs and a typalise_t,  return the method (s) in
649    the set that are supported by the typalise_t.  Also, deallocates
650    the given set.
651 */
652 static lset_t *filter_for_ta (lset_t *set, typalise_t *ta)
653 {
654   lset_t *res = lset_create ();
655   ir_graph *curs = (ir_graph*) lset_first (set);
656
657   while (NULL != curs) {
658     if (ta_supports (ta, curs)) {
659       lset_insert (res, curs);
660     }
661
662     curs = lset_next (set);
663   }
664
665   lset_destroy (set);
666
667   return (res);
668 }
669
670
671 /**
672    Return a list containing all types of 'set' which are a subtype of 'type'.
673 */
674 static lset_t *filter_for_type (lset_t *set, type *stype)
675 {
676   type *curs = (type*) lset_first (set);
677   lset_t *lset = lset_create ();
678
679   while (NULL != curs) {
680     if (is_subtype (curs, stype)) {
681       lset_insert (lset, curs);
682     }
683
684     curs = lset_next (set);
685   }
686
687   return (lset);
688 }
689
690 /**
691     Find an approximation to the given node's value's types
692 */
693 static typalise_t *typalise (ir_node*);
694
695 /**
696     Find an approximation to the given proj node's value's types
697 */
698 static typalise_t *typalise_proj (ir_node *proj)
699 {
700   typalise_t *res = NULL;
701   ir_node *proj_in = get_Proj_pred (proj);
702
703   if (iro_Proj  == get_irn_opcode (proj_in)) {
704     /* fprintf (stdout, "\tProj (Proj)\n"); */
705
706     proj_in = get_Proj_pred (proj_in);
707     if (iro_Start == get_irn_opcode (proj_in)) {
708       long n = get_Proj_proj (proj);
709       if (1 == n) {
710         /* yay proj this */
711         ir_graph *graph = get_irn_irg (proj);
712         entity   *meth  = get_irg_entity (graph);
713         type     *tp    = get_entity_owner (meth);
714
715         /* res = ta_exact (tp); */
716         res = ta_type (tp);     /* TODO */
717       } else {
718         /* ugh proj arg */
719         /* hey, even 'filtering' this NULL by the select of the actual
720            call is probably as "precise" as anything: */
721         return (NULL);
722       }
723     } else if (iro_Call == get_irn_opcode (proj_in)) {
724       /* call result ... 'whatever' */
725       ir_node *call_ptr = get_Call_ptr (proj_in);
726
727       res = typalise (call_ptr);
728     } else {
729       fprintf (stdout, "\n Proj (Proj (%s)) not handled\n",
730                get_op_name (get_irn_op (proj_in)));
731       assert (0);
732     }
733   } else {
734     opcode op = get_irn_opcode (proj_in);
735     if ((iro_Load != op) && (iro_Alloc != op) && (iro_Call != op)) {
736       fprintf (stdout, "\n Proj (%s) not handled\n",
737                get_op_name (get_irn_op (proj_in)));
738       assert (0);
739     }
740     res = typalise (proj_in);      /* everything else */
741     /* Proj (Load), Proj (New), Proj (Call) */
742   }
743
744   return (res);
745 }
746
747
748 /**
749    For the given ptr, do a quick check about what (class) types may be
750    brought along on it.
751 */
752 static typalise_t *typalise (ir_node *node)
753 {
754   opcode op = get_irn_opcode (node);
755   typalise_t *res = NULL;
756
757   switch (op) {
758   case (iro_Cast): {
759     /* casts always succeed */
760     typalise_t *ta = NULL;
761     type *tp = get_Cast_type (node);
762
763     if (is_pointer_type (tp)) {
764       tp = get_pointer_points_to_type (tp);
765     }
766     assert (is_class_type (tp));
767
768     ta = typalise (get_Cast_op (node));
769
770     if (NULL == ta) {           /* no type found */
771       ta = ta_type (tp);
772     } else if (type_exact == ta->kind) { /* one type found */
773       /* nothing (maybe check cast? */
774     } else if (type_type == ta->kind) { /* some types found */
775       if (is_subtype (tp, ta->res.type)) {
776         ta->res.type = tp;     /* assume cast is correct */
777       } else {
778         /* should assert (is_subtype (ta->res.type, tp)) */
779       }
780     } else if (type_types == ta->kind) {
781       lset_t *ftp = filter_for_type (ta->res.types, tp);
782       lset_destroy (ta->res.types);
783       ta->res.types = ftp;
784     }
785
786     res = ta;
787   } break;
788
789   case (iro_Proj): {
790     res = typalise_proj (node);
791   } break;
792
793   case (iro_Load): {
794     /* presumably it's call (load (ptr)) we're analyzing. */
795     ir_node *load_ptr = get_Load_ptr (node);
796
797     res = typalise (load_ptr);
798   } break;
799
800   case (iro_Sel): {
801     /* FILTER */
802     /* it's call (sel (ptr)) or load (sel (ptr)) */
803     entity *ent = get_Sel_entity (node);
804     type *tp = get_entity_type (ent);
805
806     if (is_method_type (tp)) {
807       tp = get_entity_type (ent);
808       tp = get_method_res_type (tp, 0);
809
810       if (is_pointer_type (tp)) {
811         tp = get_pointer_points_to_type (tp);
812       }
813
814       res = ta_type (tp);
815     } else if (is_class_type (tp)) {
816       tp = get_entity_type (ent);
817
818       if (is_pointer_type (tp)) {
819         tp = get_pointer_points_to_type (tp);
820       }
821
822       res = ta_type (tp);
823     } else if (is_pointer_type (tp)) {
824       tp = get_pointer_points_to_type (tp);
825       res = ta_type (tp);
826     } else {
827       assert (0 && "select not handled");
828     }
829   } break;
830
831   case (iro_Phi): {
832     int n_ins = get_irn_arity (node);
833     int i;
834     ir_node *phi_in = NULL;
835     typalise_t *ta = NULL;
836     /* assert (0 && "Do we ever get here?"); */ /* apparently, we do. */
837
838     for (i = 0; i < n_ins; i ++) {
839       phi_in = get_irn_n (node, i);
840       ta = (NULL == ta) ? typalise (phi_in) : ta_join (ta, typalise (phi_in));
841     }
842
843     res = ta;
844   } break;
845
846   case (iro_Alloc): {
847     type *type = get_Alloc_type (node);
848     res = ta_exact (type);
849   } break;
850
851   case (iro_Call): {
852     /* presumably call (sel (proj (call))) */
853     ir_node *ptr = get_Call_ptr (node);
854     entity *meth = NULL;
855     if (iro_Sel == get_irn_opcode (ptr)) {
856       meth = get_Sel_entity (ptr);
857     } else if (iro_SymConst == get_irn_opcode (ptr)) {
858       if (get_SymConst_kind (ptr) == symconst_addr_ent) {
859         meth = get_SymConst_entity (ptr);
860       } else {
861         meth = NULL;            /* WTF? */
862       }
863     }
864
865     if (NULL != meth) {
866       type *tp = get_method_res_type ((type*) meth, 0);
867       res = ta_type (tp);
868     } else {
869       /* could be anything */
870       /* fprintf (stdout, "meth= (null)"); */
871       res = NULL;
872     }
873
874     fprintf (stdout, "]\n");
875
876   } break;
877
878   case (iro_SymConst): {
879     if (get_SymConst_kind (node) == symconst_type_tag) {
880       type *tp = get_SymConst_type (node);
881
882       res = ta_type (tp);
883     } else if (get_SymConst_kind (node) == symconst_addr_ent) {
884       entity *ent = get_SymConst_entity (node);
885       type *tp = get_entity_type (ent);
886       tp = get_pointer_points_to_type (tp);
887       assert (is_class_type (tp));
888
889       res = ta_type (tp);       /* can't use ta_exact */
890     } else {
891       fprintf (stdout, "can't handle SymConst %s?\n",
892                get_op_name (get_irn_op (node)));
893       res = NULL;
894     }
895   } break;
896
897   /* template:
898      case (iro_Cast): {}
899      break;
900   */
901
902   default: {
903     fprintf (stdout, "what's with %s?\n", get_op_name (get_irn_op (node)));
904     assert (0);
905   } break;
906   }
907
908   return (res);
909 }
910
911
912 /* ====================
913   Alloc stuff
914   ==================== */
915 static void append_alloc (graph_info_t *ginfo, ir_node *alloc, type *tp)
916 {
917   alloc_info_t *ainfo = (alloc_info_t*) xmalloc (sizeof (alloc_info_t));
918
919   ainfo->graph = ginfo->graph;
920   ainfo->alloc = alloc;
921   ainfo->tp    = tp;
922
923   ainfo->prev = ginfo->allocs;
924   ginfo->allocs = ainfo;
925 }
926
927
928 /* ====================
929   CallEd stuff
930   ==================== */
931 /**
932    Append the given callEd to the given callEd info.
933 */
934 static callEd_info_t *append_callEd_info (callEd_info_t *ced, ir_graph *callEd)
935 {
936   callEd_info_t *nced = (callEd_info_t*) xmalloc (sizeof (sizeof (callEd_info_t)));
937
938   nced->callEd = callEd;
939   nced->prev = ced;
940
941   return (nced);
942 }
943
944 /**
945    Append all callEd methods of the given (call) node to the given graph_info.
946 */
947 static void append_calls (graph_info_t *info, ir_node *call, lset_t *callEds)
948 {
949   call_info_t *cinfo = (call_info_t*) xmalloc (sizeof (call_info_t));
950
951   /* setup */
952   cinfo->call = call;
953   cinfo->prev = info->calls;
954   info->calls = cinfo;
955   cinfo->callEds = NULL;
956
957   /* enter */
958   ir_graph *callEd = lset_first (callEds);
959   while (callEd) {
960     cinfo->callEds = append_callEd_info (cinfo->callEds, callEd);
961
962     callEd = lset_next (callEds);
963   }
964 }
965
966 /**
967    Append the (single) callEd to the given (call) node of the given graph_info.
968 */
969 static void append_call (graph_info_t *info, ir_node *call, ir_graph *callEd)
970 {
971   call_info_t *cinfo = (call_info_t*) xmalloc (sizeof (call_info_t));
972
973   cinfo->call = call;
974   cinfo->prev = info->calls;
975   info->calls = cinfo;
976
977   cinfo->callEds = append_callEd_info (cinfo->callEds, callEd);
978 }
979
980 /**
981    Given a method, find the firm graph that implements that method.
982    Return NULL for abstract and native methods.
983 */
984 static ir_graph *_get_implementing_graph (entity *method)
985 {
986   ir_graph *graph = NULL;
987
988   /* What's up with the fenced out stuff in rta? */
989   if (peculiarity_existent == get_entity_peculiarity (method)) {
990     if (visibility_external_allocated == get_entity_visibility (method)) {
991       /* Todo: native implementation */
992
993       return (NULL);
994     } else {
995       graph = get_entity_irg (get_SymConst_entity (get_atomic_ent_value (method)));
996       assert (graph && "no graph");
997
998       return (graph);
999     }
1000   } else if (0 && (peculiarity_description == get_entity_peculiarity (method))) {
1001     /* abstract --- can't find an implementation */
1002     graph = get_entity_irg (method);
1003     assert (!graph && "graph in abstract method");
1004
1005     return (NULL);
1006   } else if ((peculiarity_description == get_entity_peculiarity (method)) ||
1007              (peculiarity_inherited == get_entity_peculiarity (method))) {
1008     /* search UPWARDS */
1009     int i;
1010     int n_over = get_entity_n_overwrites (method);
1011
1012     assert (!graph);
1013
1014     for (i = 0; (NULL == graph) && (i < n_over); i ++) {
1015       entity *over = get_entity_overwrites (method, i);
1016
1017       graph = _get_implementing_graph (over);
1018     }
1019   } else {
1020     assert (0 && "invalid peculiarity");
1021   }
1022
1023
1024   return (graph);
1025 }
1026
1027 /**
1028    Collect all graphs of 'method' in the given set.
1029 */
1030 static void _collect_implementing_graphs (entity *method, lset_t *set)
1031 {
1032   /* search DOWN-wards in clazz hierarchy */
1033   int i;
1034   int n_over = get_entity_n_overwrittenby (method);
1035   ir_graph *graph = get_entity_irg (method);
1036
1037   if (NULL == graph) {
1038     graph = _get_implementing_graph (method);
1039   }
1040
1041   if (graph) {
1042     lset_insert (set, graph);
1043   }
1044
1045   for (i = 0; i < n_over; i ++) {
1046     entity *over = get_entity_overwrittenby (method, i);
1047
1048     _collect_implementing_graphs (over, set);
1049   }
1050 }
1051
1052
1053 /**
1054    Collect all graphs that could possibly be executed when 'method' is called.
1055 */
1056 static lset_t *get_implementing_graphs (entity *method, ir_node *select)
1057 {
1058   lset_t *set = lset_create ();
1059   {
1060     ir_graph *impl = _get_implementing_graph (method);
1061
1062     if (NULL != impl) {
1063       lset_insert (set, impl);
1064     } else {
1065       /* actually, abstract OR native */
1066     }
1067   }
1068
1069   _collect_implementing_graphs (method, set);
1070
1071   if (lset_empty (set)) {
1072     /* then it's a method which is only implemented natively, and we
1073        don' bother to analyse anything */
1074     return (set);
1075   }
1076
1077   /* void *tmp = lset_first (set); */
1078   int n_graphs = lset_n_entries (set);
1079
1080   /* typalise select_in */
1081   if (do_typalise) {
1082     ir_node *select_in = get_Sel_ptr (select);
1083     typalise_t *ta = typalise (select_in);
1084     assert (ta && "typalise failed (go figure)");
1085
1086     /* const char *res = ta_name (ta); */
1087
1088     /* fprintf (stdout, "typalyse res = %s\n", res); */
1089
1090     if (1 != n_graphs) {
1091       set = filter_for_ta (set, ta);
1092
1093       int n_filtered_graphs = lset_n_entries (set);
1094
1095       /*
1096       fprintf (stdout, "%s: %02d %02d\n",
1097                __FUNCTION__,
1098                n_graphs,
1099                n_filtered_graphs,
1100                n_graphs - n_filtered_graphs);
1101       */
1102       n_graphs = n_filtered_graphs;
1103     }
1104   }
1105
1106   if (n_graphs > _max_callEds) {
1107     _max_callEds = n_graphs;
1108     _max_callEds_callR = method;
1109   }
1110
1111
1112   if (visibility_external_allocated != get_entity_visibility (method)) {
1113     if (0 == n_graphs) {
1114       /* fprintf (stdout, "no graphs for method %s\n", get_entity_name (method)); */
1115       assert (n_graphs && "no graphs for method");
1116     }
1117   }
1118
1119   return (set);
1120 }
1121
1122 /**
1123    Action for the graph.
1124 */
1125 static void ecg_calls_act (ir_node *node, void *env)
1126 {
1127   opcode op = get_irn_opcode (node);
1128   graph_info_t *graph_info = (graph_info_t*) env;
1129
1130   if (iro_Call == op) {         /* CALL */
1131     entity *ent = NULL;
1132     ir_node *ptr = get_Call_ptr (node);
1133
1134     /* CALL SEL */
1135     if (iro_Sel == get_irn_opcode (ptr)) {
1136       ent = get_Sel_entity (ptr);
1137       lset_t *graphs = get_implementing_graphs (ent, ptr);
1138
1139       append_calls (graph_info, node, graphs);
1140     } else if (iro_SymConst == get_irn_opcode (ptr)) {
1141       if (get_SymConst_kind (ptr) == symconst_addr_ent) {
1142         ent = get_SymConst_entity (ptr);
1143         ir_graph *graph = get_entity_irg (ent);
1144
1145         if (graph) {
1146           append_call (graph_info, node, graph);
1147         } else {
1148           /* it's an externally allocated thingy */
1149         }
1150       } else if (get_SymConst_kind (ptr) == symconst_addr_name) {
1151         /* If this SymConst refers to a method the method is external_visible
1152            and therefore must be considered live anyways. */
1153         if (get_SymConst_name (ptr) != new_id_from_str ("iro_Catch")) {
1154           assert (ent && "couldn't determine entity of call to symConst");
1155         }
1156       } else {
1157         /* other symconst. */
1158         assert (0 && "This SymConst can not be an address for a method call.");
1159       }
1160
1161       /* STRANGE, no less ... */
1162     } else {
1163       DDMN (ptr);
1164       assert (0 && "Unexpected address expression");
1165     }
1166   } else if (iro_Alloc == op) {
1167     type *tp = get_Alloc_type (node);
1168     const char *name = get_type_name (tp);
1169
1170     append_alloc (graph_info, node, tp);
1171
1172     fprintf (stdout, "NEW \"%s\"\n", name);
1173   }
1174 }
1175
1176 /**
1177    Collect called graphs for the given graph.
1178 */
1179 static void ecg_fill_graph_calls (ir_graph *graph)
1180 {
1181   graph_info_t *graph_info = (graph_info_t*) xmalloc (sizeof (graph_info_t));
1182
1183   graph_info->graph = graph;
1184   graph_info->calls = NULL;
1185   graph_info->ecg_seen = 0;
1186
1187   /* entity *method  = get_irg_entity (graph); */
1188   /* type   *clazz   = get_entity_owner (method); */
1189
1190   irg_walk_graph (graph, ecg_calls_act, NULL, graph_info);
1191
1192   pmap_insert (graph_infos, graph, graph_info);
1193 }
1194
1195 /**
1196    For each graph, collect called graphs, and enter them into calls.
1197 */
1198 static void ecg_fill_calls (void)
1199 {
1200   int i;
1201
1202   for (i = 0; i < get_irp_n_irgs (); i++) {
1203     ir_graph *graph = get_irp_irg (i);
1204
1205     ecg_fill_graph_calls (graph);
1206   }
1207 }
1208
1209 /* ====================
1210   ECG stuff
1211   ==================== */
1212
1213 /*
1214   get the call infos for the given graph
1215 */
1216 graph_info_t *ecg_get_info (ir_graph *graph)
1217 {
1218   graph_info_t *ginfo = (graph_info_t*) pmap_get (graph_infos, graph);
1219
1220   assert (ginfo && "no info for graph");
1221
1222   return (ginfo);
1223 }
1224
1225
1226
1227 /**
1228    Dump the given graph and it's calls and it's calls callEds to the given file.
1229 */
1230 static int ecg_ecg_graph (FILE *dot, ir_graph *graph)
1231 {
1232   const char *name = get_irg_entity (graph) ?
1233     get_entity_name (get_irg_entity (graph)) : "noEntity";
1234   const char *color =
1235     (get_entity_stickyness
1236      (get_irg_entity (graph)) == stickyness_sticky) ?
1237     "red" : "lightyellow";
1238
1239   graph_info_t *ginfo = (graph_info_t*) pmap_get (graph_infos, graph);
1240
1241   if (0 != ginfo->ecg_seen) {
1242     fprintf (dot, "\t/* recursive call to \"%s\" (%d) */\n",
1243              name, (int) ginfo->ecg_seen);
1244 # if 0
1245     fprintf (dot, "\t/* recursive call to \"%s\" (0x%08x) */\n",
1246              name, (int) graph);
1247 # endif /* 0 */
1248     return (ginfo->ecg_seen);
1249   }
1250
1251   assert (0L <= _graphs);
1252
1253   const int graph_no = _graphs ++;
1254   ginfo->ecg_seen = graph_no;
1255
1256   fprintf (dot, "\t/* Graph of \"%s.%s\" */\n",
1257            get_type_name (get_entity_owner (get_irg_entity (graph))),
1258            name);
1259   fprintf (dot, "\tgraph_%i [label=\"%s\\l%s\", color=\"%s\"];\n",
1260            graph_no,
1261            get_type_name (get_entity_owner (get_irg_entity (graph))),
1262            name,
1263            color);
1264   fprintf (dot, "\n");
1265
1266   if (visibility_external_allocated ==
1267       get_entity_visibility (get_irg_entity (graph))) {
1268     fprintf (dot, "\t/* graph \"%s\" is external */\n", name);
1269
1270     return (graph_no);
1271   }
1272
1273   call_info_t *cinfo = ginfo->calls;
1274   while (NULL != cinfo) {
1275     ir_node *call = cinfo->call;
1276     callEd_info_t *ced = cinfo->callEds;
1277     const int call_no = _calls ++;
1278
1279     fprintf (dot, "\t/* Call 0x%08x */\n", (int) call);
1280     fprintf (dot, "\tcall_%i [label=\"call\\l0x%08x\"];\n",
1281              call_no, (int) call);
1282     fprintf (dot, "\tgraph_%i -> call_%i [color=\"black\"];\n", graph_no, call_no);
1283
1284     while (NULL != ced) {
1285       ir_graph *callEd_graph = ced->callEd;
1286       const int callEd_no = ecg_ecg_graph (dot, callEd_graph);
1287       const char *callEd_name = get_irg_entity (callEd_graph) ?
1288         get_entity_name (get_irg_entity (callEd_graph)) : "noEntity";
1289       const char *direction = (callEd_no <= graph_no) ? "forward" : "forward";
1290       const char *callEd_color     = (callEd_no <= graph_no) ? "red" : "black";
1291
1292       fprintf (dot, "\t/* Call from graph \"%s\" to graph \"%s\" */\n",
1293                name,
1294                callEd_name);
1295       /* Check for recursive calls */
1296       /* if (callEd_no > graph_no) */ { /* do recursive calls (for now) */
1297         fprintf (dot, "\tcall_%i -> graph_%i [color=\"%s\", dir=\"%s\"];\n",
1298                  call_no, callEd_no, callEd_color, direction);
1299       }
1300
1301       ced = ced->prev;
1302       /* ced = NULL; */
1303     } /* done all calEds (call) */
1304
1305     cinfo = cinfo->prev;
1306   } /* done all calls (graph) */
1307
1308   /* now the allocs */
1309   alloc_info_t *ainfo = ginfo->allocs;
1310   if (ainfo) {
1311     fprintf (dot, "\t/* now the allocs */\n");
1312   } else {
1313     fprintf (dot, "\t/* no allocs */\n");
1314   }
1315
1316   while (NULL != ainfo) {
1317     ir_node *alloc = ainfo->alloc;
1318     const char *name = get_type_name (ainfo->tp);
1319     const char *color = "red1";
1320
1321     /* if (0 == ginfo->allocs_seen) { */
1322     _allocs ++;
1323       fprintf (dot, "\talloc_0x%08x_%i [label=\"%s\", color=\"%s\"]\n",
1324                (int) alloc, graph_no, name, color);
1325     /* } */
1326
1327     fprintf (dot, "\tgraph_%i -> alloc_0x%08x_%i\n", graph_no, (int) alloc, graph_no);
1328
1329     ainfo = ainfo->prev;
1330   }
1331
1332   if (0 == ginfo->allocs_seen) {
1333     ginfo->allocs_seen = 1;
1334   }
1335
1336   fprintf (dot, "\t/* done with graph of \"%s\" */\n\n", name);
1337
1338   fflush (dot);
1339   ginfo->ecg_seen = 0;
1340
1341   return (graph_no);
1342 }
1343
1344 /**
1345    Count how many nodes the ECG will have
1346 */
1347 static char spaces [BUF_SIZE];
1348
1349 static void ecg_ecg_count (ir_graph *graph)
1350 {
1351   graph_info_t *ginfo = (graph_info_t*) pmap_get (graph_infos, graph);
1352
1353   if (0 != ginfo->ecg_seen) {
1354     return;
1355   }
1356
1357   _depth ++;
1358   if (_depth > _max_depth) {
1359     _max_depth = _depth;
1360
1361     /*
1362       fprintf (stdout, "_max_depth = %i\n", _max_depth);
1363       fprintf (stdout, "\tn_graphs: %i\n", _graphs);
1364     */
1365   }
1366
1367   assert (0L <= _graphs);
1368
1369   /*
1370     if (0 == (_graphs % 1000000)) {
1371     fprintf (stdout, "\tn_graphs: %i\n", _graphs);
1372     fprintf (stdout, "_depth = %i\n", _depth);
1373     }
1374   */
1375
1376   const int graph_no = _graphs ++;
1377   ginfo->ecg_seen = graph_no;
1378
1379   fprintf (stdout, "%sMethod \"%s.%s\"\n",
1380            spaces + BUF_SIZE - _depth,
1381            get_type_name (get_entity_owner (get_irg_entity (graph))),
1382            get_entity_name (get_irg_entity (graph)));
1383
1384   call_info_t *cinfo = ginfo->calls;
1385   while (NULL != cinfo) {
1386
1387     callEd_info_t *ced = cinfo->callEds;
1388
1389     fprintf (stdout, "%sCall \"0x%08x\"\n",
1390              spaces + BUF_SIZE - _depth,
1391              (int) cinfo->call);
1392
1393     while (NULL != ced) {
1394       ir_graph *callEd_graph = ced->callEd;
1395
1396       fprintf (stdout, "%sCall Target \"%s.%s\"\n",
1397                spaces + BUF_SIZE - _depth,
1398                get_type_name (get_entity_owner (get_irg_entity (callEd_graph))),
1399                get_entity_name (get_irg_entity (callEd_graph)));
1400
1401       ecg_ecg_count (callEd_graph);
1402
1403       ced = ced->prev;
1404     } /* done all calEds (call) */
1405     cinfo = cinfo->prev;
1406   } /* done all calls (graph) */
1407
1408   ginfo->ecg_seen = 0;
1409   _depth --;
1410 }
1411
1412 /* ====================
1413   Public Interface
1414   ==================== */
1415
1416 /**
1417    Initialise our data structures.
1418 */
1419 void ecg_init (int typalise)
1420 {
1421   do_typalise = typalise;
1422
1423   graph_infos = pmap_create ();
1424
1425   ecg_fill_calls ();
1426 }
1427
1428 /**
1429    Clean up our mess.
1430 */
1431 void ecg_cleanup ()
1432 {
1433   int i;
1434
1435   for (i = 0; i < get_irp_n_irgs (); i++) {
1436     ir_graph *graph = get_irp_irg (i);
1437
1438     graph_info_t *info = pmap_get (graph_infos, graph);
1439     call_info_t *cinfo = info->calls;
1440
1441     while (NULL != cinfo) {
1442       free (cinfo->callEds);
1443       cinfo->call = NULL;
1444
1445       callEd_info_t *ced = cinfo->callEds;
1446
1447       while (NULL != ced) {
1448         callEd_info_t *nced = ced->prev;
1449         free (ced);
1450         ced->prev = NULL;
1451         ced->callEd = NULL;
1452         ced = nced;
1453       }
1454
1455       /* Todo: delete callEds */
1456       cinfo->callEds = NULL;
1457
1458       free (cinfo);
1459       cinfo = cinfo->prev;
1460     }
1461
1462     free (info);
1463     pmap_insert (graph_infos, graph, NULL);
1464   }
1465
1466
1467   pmap_destroy (graph_infos);
1468
1469   /* BEGIN mild paranoia mode */
1470   graph_infos = NULL;
1471   /* END mild paranoia mode */
1472 }
1473
1474 /**
1475    Show what we have found.
1476 */
1477 void ecg_report ()
1478 {
1479   int i;
1480
1481   FILE *dot = fopen ("calls.dot", "w");
1482
1483   fprintf (dot, "digraph \"calls\" {\n");
1484   fprintf (dot, "\tnode [shape = \"record\", style = \"filled\"];\n");
1485   fprintf (dot, "\tedge [color = \"black\"];\n");
1486   fprintf (dot, "\n");
1487   fprintf (dot, "\tsize = \"11, 7\";\n");
1488   fprintf (dot, "\trotate = \"90\";\n");
1489   fprintf (dot, "\tratio = \"fill\";\n");
1490   fprintf (dot, "\trankdir = \"LR\";\n");
1491   fprintf (dot, "\n");
1492
1493   for (i = 0; i < get_irp_n_irgs (); i++) {
1494     ir_graph *graph = get_irp_irg (i);
1495     graph_info_t *info = (graph_info_t*) pmap_get (graph_infos, graph);
1496
1497     const char *name = get_irg_entity (graph) ?
1498       get_entity_name (get_irg_entity (graph)) : "noEntity";
1499
1500     const char *oname = get_type_name
1501       (get_entity_owner (get_irg_entity (graph)));
1502
1503     const char *color =
1504       (get_entity_stickyness
1505        (get_irg_entity (graph)) == stickyness_sticky) ?
1506       "red3" : "lightyellow";
1507
1508     fprintf (dot, "\t/* graph_0x%08x (\"%s\") */\n", (int) graph, name);
1509     fprintf (dot,
1510              "\tgraph_0x%08x [label=\"%s\\l%s\", color=\"%s\"];\n",
1511              (int) graph, oname, name, color);
1512     fprintf (dot, "\n");
1513
1514     call_info_t *cinfo = info->calls;
1515     if (cinfo) {
1516       fprintf (dot, "\t/* now the calls */\n");
1517     } else {
1518       fprintf (dot, "\t/* no calls, nothing to see, move along! */\n");
1519     }
1520
1521     while (NULL != cinfo) {
1522       ir_node *call = cinfo->call;
1523
1524       fprintf (dot, "\t/* call_0x%08x */\n", (int) call);
1525       fprintf (dot, "\tcall_0x%08x [label=\"call\\l0x%08x\"];\n",
1526                (int) call, (int) call);
1527       fprintf (dot, "\tgraph_0x%08x -> call_0x%08x;\n",
1528                (int) graph, (int) call);
1529
1530       callEd_info_t *ced = cinfo->callEds;
1531       while (NULL != ced) {
1532         fprintf (dot, "\tcall_0x%08x -> graph_0x%08x;\n",
1533                  (int) call, (int) ced->callEd);
1534         ced = ced->prev;
1535       }
1536       fprintf (dot, "\n");
1537
1538       cinfo = cinfo->prev;
1539     }
1540     fprintf (dot, "\n");
1541
1542     alloc_info_t *ainfo = info->allocs;
1543     if (ainfo) {
1544       fprintf (dot, "\t/* now the allocs */\n");
1545     } else {
1546       fprintf (dot, "\t/* no allocs */\n");
1547     }
1548
1549
1550     while (NULL != ainfo) {
1551       ir_node *alloc = ainfo->alloc;
1552       const char *name = get_type_name (ainfo->tp);
1553       const char *color = "red1";
1554
1555       fprintf (dot, "\talloc_0x%08x [label=\"%s\", color=\"%s\"]\n",
1556                (int) alloc, name, color);
1557       fprintf (dot, "\tgraph_0x%08x -> alloc_0x%08x\n",
1558                (int) graph, (int) alloc);
1559
1560       ainfo = ainfo->prev;
1561     }
1562   }
1563   fprintf (dot, "}\n");
1564
1565   /*
1566     fprintf (stdout, " max_callEds: %i\n", _max_callEds);
1567     fprintf (stdout, " max_callEds_callR: \"%s\"\n",
1568     get_entity_name (_max_callEds_callR));
1569   */
1570   fclose (dot);
1571
1572   ecg_ecg ();
1573 }
1574
1575 /**
1576    Experimental:  Print the ecg
1577 */
1578 void ecg_ecg ()
1579 {
1580   _graphs = 0;
1581   _calls  = 0;
1582
1583   ir_graph *main_graph = get_irp_main_irg ();
1584
1585   /*
1586   memset (spaces, '.', BUF_SIZE);
1587   spaces [BUF_SIZE-1] = '\0';
1588
1589   ecg_ecg_count (main_graph);
1590   fprintf (stdout, "n_graphs: %i\n", _graphs);
1591   fprintf (stdout, "max_depth = %i\n", _max_depth);
1592   */
1593
1594   /* return; */
1595
1596   _graphs = 0;
1597   _calls  = 0;
1598
1599   FILE *dot = fopen ("ecg.dot", "w");
1600
1601   fprintf (dot, "digraph \"ecg\" {\n");
1602   fprintf (dot, "\tnode [shape = \"record\", style = \"filled\"];\n");
1603   fprintf (dot, "\tedge [color = \"black\"];\n");
1604   fprintf (dot, "\n");
1605   fprintf (dot, "\tsize = \"11, 7\";\n");
1606   fprintf (dot, "\trotate = \"90\";\n");
1607   fprintf (dot, "\tratio = \"fill\";\n");
1608   fprintf (dot, "\trankdir = \"LR\";\n");
1609   fprintf (dot, "\n");
1610
1611   /* ir_graph *main_graph = get_irp_main_irg (); */
1612   ecg_ecg_graph (dot, main_graph);
1613
1614   fprintf (dot, "\t/* Grand Total: */\n");
1615   fprintf (dot, "\t/* calls:  %i */\n", (int) _calls);
1616   fprintf (dot, "\t/* graphs: %i */\n", (int) _graphs);
1617   fprintf (dot, "\t/* allocs: %i */\n", (int) _allocs);
1618   fprintf (dot, "\t/* (sales tax not included) */\n");
1619
1620   fprintf (dot, "}\n");
1621
1622   fclose (dot);
1623 }
1624
1625 \f
1626
1627 /*
1628   $Log$
1629   Revision 1.1  2004/10/20 14:59:41  liekweg
1630   Added ana2, added ecg and pto
1631
1632   Revision 1.6  2004/10/18 12:47:19  liekweg
1633   minor fix
1634
1635   Revision 1.5  2004/10/14 11:31:28  liekweg
1636   SHUTUP_GCC
1637
1638   Revision 1.4  2004/10/12 11:02:01  liekweg
1639   wtf?
1640
1641 */