Added the new state pin_state_exc_pinned t some Operations
[libfirm] / ir / ana / 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     typalise_t *ta = NULL;
760     type *tp = get_Cast_type (node);
761
762     if (is_pointer_type (tp)) {
763       tp = get_pointer_points_to_type (tp);
764     }
765     assert (is_class_type (tp));
766
767     ta = typalise (get_Cast_op (node));
768
769     if (NULL == ta) {           /* no type found */
770       ta = ta_type (tp);
771     } else if (type_exact == ta->kind) { /* one type found */
772       /* nothing (maybe check cast? */
773     } else if (type_type == ta->kind) { /* some types found */
774       if (is_subtype (tp, ta->res.type)) {
775         ta->res.type = tp;     /* assume cast is correct */
776       } else {
777         /* should assert (is_subtype (ta->res.type, tp)) */
778       }
779     } else if (type_types == ta->kind) {
780       lset_t *ftp = filter_for_type (ta->res.types, tp);
781       lset_destroy (ta->res.types);
782       ta->res.types = ftp;
783     }
784
785     res = ta;
786   } break;
787
788   case (iro_Proj): {
789     res = typalise_proj (node);
790   } break;
791
792   case (iro_Load): {
793     /* presumably it's call(load(ptr)) we're analyzing. */
794     ir_node *load_ptr = get_Load_ptr (node);
795
796     res = typalise (load_ptr);
797   } break;
798
799   case (iro_Sel): {
800     /* FILTER */
801     /* it's call(sel(ptr)) or load(sel(ptr)) */
802     entity *ent = get_Sel_entity (node);
803     type *tp = get_entity_type (ent);
804
805     if (is_method_type (tp)) {
806       tp = get_entity_type (ent);
807       tp = get_method_res_type (tp, 0);
808
809       if (is_pointer_type (tp)) {
810         tp = get_pointer_points_to_type (tp);
811       }
812
813       res = ta_type (tp);
814     } else if (is_class_type (tp)) {
815       tp = get_entity_type (ent);
816
817       if (is_pointer_type (tp)) {
818         tp = get_pointer_points_to_type (tp);
819       }
820
821       res = ta_type (tp);
822     } else if (is_pointer_type (tp)) {
823       tp = get_pointer_points_to_type (tp);
824       res = ta_type (tp);
825     } else {
826       assert (0 && "select not handled");
827     }
828   } break;
829
830   case (iro_Phi): {
831     int n_ins = get_irn_arity (node);
832     int i;
833     ir_node *phi_in = NULL;
834     typalise_t *ta = NULL;
835     /* assert (0 && "Do we ever get here?"); */ /* apparently, we do. */
836
837     for (i = 0; i < n_ins; i ++) {
838       phi_in = get_irn_n (node, i);
839       ta = (NULL == ta) ? typalise (phi_in) : ta_join (ta, typalise (phi_in));
840     }
841
842     res = ta;
843   } break;
844
845   case (iro_Alloc): {
846     type *type = get_Alloc_type (node);
847     res = ta_exact (type);
848   } break;
849
850   case (iro_Call): {
851     /* presumably call(sel(proj(call))) */
852     ir_node *ptr = get_Call_ptr (node);
853     entity *meth = NULL;
854     if (iro_Sel == get_irn_opcode (ptr)) {
855       meth = get_Sel_entity (ptr);
856     } else if (iro_SymConst == get_irn_opcode (ptr)) {
857       if (get_SymConst_kind(ptr) == symconst_addr_ent) {
858         meth = get_SymConst_entity (ptr);
859       } else {
860         meth = NULL;            /* WTF? */
861       }
862     }
863
864     if (NULL != meth) {
865       type *tp = get_method_res_type ((type*) meth, 0);
866       res = ta_type (tp);
867     } else {
868       /* could be anything */
869       /* fprintf (stdout, "meth=(null)"); */
870       res = NULL;
871     }
872
873     fprintf (stdout, "]\n");
874
875   } break;
876
877   case (iro_SymConst): {
878     if (get_SymConst_kind(node) == symconst_type_tag) {
879       type *tp = get_SymConst_type (node);
880
881       res = ta_type (tp);
882     } else if (get_SymConst_kind (node) == symconst_addr_ent) {
883       entity *ent = get_SymConst_entity (node);
884       type *tp = get_entity_type (ent);
885       tp = get_pointer_points_to_type (tp);
886       assert (is_class_type (tp));
887
888       res = ta_type (tp);       /* can't use ta_exact */
889     } else {
890       fprintf (stdout, "can't handle SymConst %s?\n",
891                get_op_name (get_irn_op (node)));
892       res = NULL;
893     }
894   } break;
895
896   /* template:
897      case (iro_Cast): {}
898      break;
899   */
900
901   default: {
902     fprintf (stdout, "what's with %s?\n", get_op_name (get_irn_op (node)));
903     assert (0);
904   } break;
905   }
906
907   return (res);
908 }
909
910
911 /* ====================
912   Alloc stuff
913   ==================== */
914 static void append_alloc (graph_info_t *ginfo, ir_node *alloc, type *tp)
915 {
916   alloc_info_t *ainfo = (alloc_info_t*) xmalloc (sizeof (alloc_info_t));
917
918   ainfo->graph = ginfo->graph;
919   ainfo->alloc = alloc;
920   ainfo->tp    = tp;
921
922   ainfo->prev = ginfo->allocs;
923   ginfo->allocs = ainfo;
924 }
925
926
927 /* ====================
928   CallEd stuff
929   ==================== */
930 /**
931    Append the given callEd to the given callEd info.
932 */
933 static callEd_info_t *append_callEd_info (callEd_info_t *ced, ir_graph *callEd)
934 {
935   callEd_info_t *nced = (callEd_info_t*) xmalloc (sizeof (sizeof (callEd_info_t)));
936
937   nced->callEd = callEd;
938   nced->prev = ced;
939
940   return (nced);
941 }
942
943 /**
944    Append all callEd methods of the given (call) node to the given graph_info.
945 */
946 static void append_calls (graph_info_t *info, ir_node *call, lset_t *callEds)
947 {
948   call_info_t *cinfo = (call_info_t*) xmalloc (sizeof (call_info_t));
949
950   /* setup */
951   cinfo->call = call;
952   cinfo->prev = info->calls;
953   info->calls = cinfo;
954   cinfo->callEds = NULL;
955
956   /* enter */
957   ir_graph *callEd = lset_first (callEds);
958   while (callEd) {
959     cinfo->callEds = append_callEd_info (cinfo->callEds, callEd);
960
961     callEd = lset_next (callEds);
962   }
963 }
964
965 /**
966    Append the (single) callEd to the given (call) node of the given graph_info.
967 */
968 static void append_call (graph_info_t *info, ir_node *call, ir_graph *callEd)
969 {
970   call_info_t *cinfo = (call_info_t*) xmalloc (sizeof (call_info_t));
971
972   cinfo->call = call;
973   cinfo->prev = info->calls;
974   info->calls = cinfo;
975
976   cinfo->callEds = append_callEd_info (cinfo->callEds, callEd);
977 }
978
979 /**
980    Given a method, find the firm graph that implements that method.
981    Return NULL for abstract and native methods.
982 */
983 static ir_graph *_get_implementing_graph (entity *method)
984 {
985   ir_graph *graph = NULL;
986
987   /* What's up with the fenced out stuff in rta? */
988   if (peculiarity_existent == get_entity_peculiarity (method)) {
989     if (visibility_external_allocated == get_entity_visibility (method)) {
990       /* Todo: native implementation */
991
992       return (NULL);
993     } else {
994       graph = get_entity_irg (get_SymConst_entity (get_atomic_ent_value (method)));
995       assert (graph && "no graph");
996
997       return (graph);
998     }
999   } else if (0 && (peculiarity_description == get_entity_peculiarity (method))) {
1000     /* abstract --- can't find an implementation */
1001     graph = get_entity_irg (method);
1002     assert (!graph && "graph in abstract method");
1003
1004     return (NULL);
1005   } else if ((peculiarity_description == get_entity_peculiarity (method)) ||
1006              (peculiarity_inherited == get_entity_peculiarity (method))) {
1007     /* search UPWARDS */
1008     int i;
1009     int n_over = get_entity_n_overwrites (method);
1010
1011     assert (!graph);
1012
1013     for (i = 0; (NULL == graph) && (i < n_over); i ++) {
1014       entity *over = get_entity_overwrites (method, i);
1015
1016       graph = _get_implementing_graph (over);
1017     }
1018   } else {
1019     assert (0 && "invalid peculiarity");
1020   }
1021
1022
1023   return (graph);
1024 }
1025
1026 /**
1027    Collect all graphs of 'method' in the given set.
1028 */
1029 static void _collect_implementing_graphs (entity *method, lset_t *set)
1030 {
1031   /* search DOWN-wards in clazz hierarchy */
1032   int i;
1033   int n_over = get_entity_n_overwrittenby (method);
1034   ir_graph *graph = get_entity_irg (method);
1035
1036   if (NULL == graph) {
1037     graph = _get_implementing_graph (method);
1038   }
1039
1040   if (graph) {
1041     lset_insert (set, graph);
1042   }
1043
1044   for (i = 0; i < n_over; i ++) {
1045     entity *over = get_entity_overwrittenby (method, i);
1046
1047     _collect_implementing_graphs (over, set);
1048   }
1049 }
1050
1051
1052 /**
1053    Collect all graphs that could possibly be executed when 'method' is called.
1054 */
1055 static lset_t *get_implementing_graphs (entity *method, ir_node *select)
1056 {
1057   lset_t *set = lset_create ();
1058   {
1059     ir_graph *impl = _get_implementing_graph (method);
1060
1061     if (NULL != impl) {
1062       lset_insert (set, impl);
1063     } else {
1064       /* actually, abstract OR native */
1065     }
1066   }
1067
1068   _collect_implementing_graphs (method, set);
1069
1070   if (lset_empty (set)) {
1071     /* then it's a method which is only implemented natively, and we
1072        don' bother to analyse anything */
1073     return (set);
1074   }
1075
1076   /* void *tmp = lset_first (set); */
1077   int n_graphs = lset_n_entries (set);
1078
1079   /* typalise select_in */
1080   if (do_typalise) {
1081     ir_node *select_in = get_Sel_ptr (select);
1082     typalise_t *ta = typalise (select_in);
1083     assert (ta && "typalise failed (go figure)");
1084
1085     /* const char *res = ta_name (ta); */
1086
1087     /* fprintf (stdout, "typalyse res = %s\n", res); */
1088
1089     if (1 != n_graphs) {
1090       set = filter_for_ta (set, ta);
1091
1092       int n_filtered_graphs = lset_n_entries (set);
1093
1094       /*
1095       fprintf (stdout, "%s: %02d %02d\n",
1096                __FUNCTION__,
1097                n_graphs,
1098                n_filtered_graphs,
1099                n_graphs - n_filtered_graphs);
1100       */
1101       n_graphs = n_filtered_graphs;
1102     }
1103   }
1104
1105   if (n_graphs > _max_callEds) {
1106     _max_callEds = n_graphs;
1107     _max_callEds_callR = method;
1108   }
1109
1110
1111   if (visibility_external_allocated != get_entity_visibility (method)) {
1112     if (0 == n_graphs) {
1113       /* fprintf (stdout, "no graphs for method %s\n", get_entity_name (method)); */
1114       assert (n_graphs && "no graphs for method");
1115     }
1116   }
1117
1118   return (set);
1119 }
1120
1121 /**
1122    Action for the graph.
1123 */
1124 static void ecg_calls_act (ir_node *node, void *env)
1125 {
1126   opcode op = get_irn_opcode (node);
1127   graph_info_t *graph_info = (graph_info_t*) env;
1128
1129   if (iro_Call == op) {         /* CALL */
1130     entity *ent = NULL;
1131     ir_node *ptr = get_Call_ptr (node);
1132
1133     /* CALL SEL */
1134     if (iro_Sel == get_irn_opcode (ptr)) {
1135       ent = get_Sel_entity (ptr);
1136       lset_t *graphs = get_implementing_graphs (ent, ptr);
1137
1138       append_calls (graph_info, node, graphs);
1139     } else if (iro_SymConst == get_irn_opcode (ptr)) {
1140       if (get_SymConst_kind(ptr) == symconst_addr_ent) {
1141         ent = get_SymConst_entity (ptr);
1142         ir_graph *graph = get_entity_irg (ent);
1143
1144         if (graph) {
1145           append_call (graph_info, node, graph);
1146         } else {
1147           /* it's an externally allocated thingy */
1148         }
1149       } else if (get_SymConst_kind(ptr) == symconst_addr_name) {
1150         /* If this SymConst refers to a method the method is external_visible
1151            and therefore must be considered live anyways. */
1152         if (get_SymConst_name(ptr) != new_id_from_str("iro_Catch")) {
1153           assert (ent && "couldn't determine entity of call to symConst");
1154         }
1155       } else {
1156         /* other symconst. */
1157         assert (0 && "This SymConst can not be an address for a method call.");
1158       }
1159
1160       /* STRANGE, no less ... */
1161     } else {
1162       DDMN(ptr);
1163       assert(0 && "Unexpected address expression");
1164     }
1165   } else if (iro_Alloc == op) {
1166     type *tp = get_Alloc_type (node);
1167     const char *name = get_type_name (tp);
1168
1169     append_alloc (graph_info, node, tp);
1170
1171     fprintf (stdout, "NEW \"%s\"\n", name);
1172   }
1173 }
1174
1175 /**
1176    Collect called graphs for the given graph.
1177 */
1178 static void ecg_fill_graph_calls (ir_graph *graph)
1179 {
1180   graph_info_t *graph_info = (graph_info_t*) xmalloc (sizeof (graph_info_t));
1181
1182   graph_info->graph = graph;
1183   graph_info->calls = NULL;
1184   graph_info->ecg_seen = 0;
1185
1186   /* entity *method  = get_irg_entity (graph); */
1187   /* type   *clazz   = get_entity_owner (method); */
1188
1189   irg_walk_graph (graph, ecg_calls_act, NULL, graph_info);
1190
1191   pmap_insert (graph_infos, graph, graph_info);
1192 }
1193
1194 /**
1195    For each graph, collect called graphs, and enter them into calls.
1196 */
1197 static void ecg_fill_calls (void)
1198 {
1199   int i;
1200
1201   for (i = 0; i < get_irp_n_irgs (); i++) {
1202     ir_graph *graph = get_irp_irg (i);
1203
1204     ecg_fill_graph_calls (graph);
1205   }
1206 }
1207
1208 /* ====================
1209   ECG stuff
1210   ==================== */
1211
1212 /*
1213   get the call infos for the given graph
1214 */
1215 graph_info_t *ecg_get_info (ir_graph *graph)
1216 {
1217   graph_info_t *ginfo = (graph_info_t*) pmap_get (graph_infos, graph);
1218
1219   assert (ginfo && "no info for graph");
1220
1221   return (ginfo);
1222 }
1223
1224
1225
1226 /**
1227    Dump the given graph and it's calls and it's calls callEds to the given file.
1228 */
1229 static int ecg_ecg_graph (FILE *dot, ir_graph *graph)
1230 {
1231   const char *name = get_irg_entity (graph) ?
1232     get_entity_name (get_irg_entity (graph)) : "noEntity";
1233   const char *color =
1234     (get_entity_stickyness
1235      (get_irg_entity (graph)) == stickyness_sticky) ?
1236     "red" : "lightyellow";
1237
1238   graph_info_t *ginfo = (graph_info_t*) pmap_get (graph_infos, graph);
1239
1240   if (0 != ginfo->ecg_seen) {
1241     fprintf (dot, "\t/* recursive call to \"%s\" (%d) */\n",
1242              name, (int) ginfo->ecg_seen);
1243 # if 0
1244     fprintf (dot, "\t/* recursive call to \"%s\" (0x%08x) */\n",
1245              name, (int) graph);
1246 # endif /* 0 */
1247     return (ginfo->ecg_seen);
1248   }
1249
1250   assert (0L <= _graphs);
1251
1252   const int graph_no = _graphs ++;
1253   ginfo->ecg_seen = graph_no;
1254
1255   fprintf (dot, "\t/* Graph of \"%s.%s\" */\n",
1256            get_type_name (get_entity_owner (get_irg_entity (graph))),
1257            name);
1258   fprintf (dot, "\tgraph_%i [label=\"%s\\l%s\", color=\"%s\"];\n",
1259            graph_no,
1260            get_type_name (get_entity_owner (get_irg_entity (graph))),
1261            name,
1262            color);
1263   fprintf (dot, "\n");
1264
1265   if (visibility_external_allocated ==
1266       get_entity_visibility (get_irg_entity (graph))) {
1267     fprintf (dot, "\t/* graph \"%s\" is external */\n", name);
1268
1269     return (graph_no);
1270   }
1271
1272   call_info_t *cinfo = ginfo->calls;
1273   while (NULL != cinfo) {
1274     ir_node *call = cinfo->call;
1275     callEd_info_t *ced = cinfo->callEds;
1276     const int call_no = _calls ++;
1277
1278     fprintf (dot, "\t/* Call 0x%08x */\n", (int) call);
1279     fprintf (dot, "\tcall_%i [label=\"call\\l0x%08x\"];\n",
1280              call_no, (int) call);
1281     fprintf (dot, "\tgraph_%i -> call_%i [color=\"black\"];\n", graph_no, call_no);
1282
1283     while (NULL != ced) {
1284       ir_graph *callEd_graph = ced->callEd;
1285       const int callEd_no = ecg_ecg_graph (dot, callEd_graph);
1286       const char *callEd_name = get_irg_entity (callEd_graph) ?
1287         get_entity_name (get_irg_entity (callEd_graph)) : "noEntity";
1288       const char *direction = (callEd_no <= graph_no) ? "forward" : "forward";
1289       const char *callEd_color     = (callEd_no <= graph_no) ? "red" : "black";
1290
1291       fprintf (dot, "\t/* Call from graph \"%s\" to graph \"%s\" */\n",
1292                name,
1293                callEd_name);
1294       /* Check for recursive calls */
1295       /* if (callEd_no > graph_no) */ { /* do recursive calls (for now) */
1296         fprintf (dot, "\tcall_%i -> graph_%i [color=\"%s\", dir=\"%s\"];\n",
1297                  call_no, callEd_no, callEd_color, direction);
1298       }
1299
1300       ced = ced->prev;
1301       /* ced = NULL; */
1302     } /* done all calEds(call) */
1303
1304     cinfo = cinfo->prev;
1305   } /* done all calls(graph) */
1306
1307   /* now the allocs */
1308   alloc_info_t *ainfo = ginfo->allocs;
1309   if (ainfo) {
1310     fprintf (dot, "\t/* now the allocs */\n");
1311   } else {
1312     fprintf (dot, "\t/* no allocs */\n");
1313   }
1314
1315   while (NULL != ainfo) {
1316     ir_node *alloc = ainfo->alloc;
1317     const char *name = get_type_name (ainfo->tp);
1318     const char *color = "red1";
1319
1320     /* if (0 == ginfo->allocs_seen) { */
1321     _allocs ++;
1322       fprintf (dot, "\talloc_0x%08x_%i [label=\"%s\", color=\"%s\"]\n",
1323                (int) alloc, graph_no, name, color);
1324     /* } */
1325
1326     fprintf (dot, "\tgraph_%i -> alloc_0x%08x_%i\n", graph_no, (int) alloc, graph_no);
1327
1328     ainfo = ainfo->prev;
1329   }
1330
1331   if (0 == ginfo->allocs_seen) {
1332     ginfo->allocs_seen = 1;
1333   }
1334
1335   fprintf (dot, "\t/* done with graph of \"%s\" */\n\n", name);
1336
1337   fflush (dot);
1338   ginfo->ecg_seen = 0;
1339
1340   return (graph_no);
1341 }
1342
1343 /**
1344    Count how many nodes the ECG will have
1345 */
1346 static char spaces [BUF_SIZE];
1347
1348 static void ecg_ecg_count (ir_graph *graph)
1349 {
1350   graph_info_t *ginfo = (graph_info_t*) pmap_get (graph_infos, graph);
1351
1352   if (0 != ginfo->ecg_seen) {
1353     return;
1354   }
1355
1356   _depth ++;
1357   if (_depth > _max_depth) {
1358     _max_depth = _depth;
1359
1360     /*
1361       fprintf (stdout, "_max_depth = %i\n", _max_depth);
1362       fprintf (stdout, "\tn_graphs: %i\n", _graphs);
1363     */
1364   }
1365
1366   assert (0L <= _graphs);
1367
1368   /*
1369     if (0 == (_graphs % 1000000)) {
1370     fprintf (stdout, "\tn_graphs: %i\n", _graphs);
1371     fprintf (stdout, "_depth = %i\n", _depth);
1372     }
1373   */
1374
1375   const int graph_no = _graphs ++;
1376   ginfo->ecg_seen = graph_no;
1377
1378   fprintf (stdout, "%sMethod \"%s.%s\"\n",
1379            spaces + BUF_SIZE - _depth,
1380            get_type_name (get_entity_owner (get_irg_entity (graph))),
1381            get_entity_name (get_irg_entity (graph)));
1382
1383   call_info_t *cinfo = ginfo->calls;
1384   while (NULL != cinfo) {
1385
1386     callEd_info_t *ced = cinfo->callEds;
1387
1388     fprintf (stdout, "%sCall \"0x%08x\"\n",
1389              spaces + BUF_SIZE - _depth,
1390              (int) cinfo->call);
1391
1392     while (NULL != ced) {
1393       ir_graph *callEd_graph = ced->callEd;
1394
1395       fprintf (stdout, "%sCall Target \"%s.%s\"\n",
1396                spaces + BUF_SIZE - _depth,
1397                get_type_name (get_entity_owner (get_irg_entity (callEd_graph))),
1398                get_entity_name (get_irg_entity (callEd_graph)));
1399
1400       ecg_ecg_count (callEd_graph);
1401
1402       ced = ced->prev;
1403     } /* done all calEds(call) */
1404     cinfo = cinfo->prev;
1405   } /* done all calls(graph) */
1406
1407   ginfo->ecg_seen = 0;
1408   _depth --;
1409 }
1410
1411 /* ====================
1412   Public Interface
1413   ==================== */
1414
1415 /**
1416    Initialise our data structures.
1417 */
1418 void ecg_init (int typalise)
1419 {
1420   do_typalise = typalise;
1421
1422   graph_infos = pmap_create ();
1423
1424   ecg_fill_calls ();
1425 }
1426
1427 /**
1428    Clean up our mess.
1429 */
1430 void ecg_cleanup ()
1431 {
1432   int i;
1433
1434   for (i = 0; i < get_irp_n_irgs (); i++) {
1435     ir_graph *graph = get_irp_irg (i);
1436
1437     graph_info_t *info = pmap_get (graph_infos, graph);
1438     call_info_t *cinfo = info->calls;
1439
1440     while (NULL != cinfo) {
1441       free (cinfo->callEds);
1442       cinfo->call = NULL;
1443
1444       callEd_info_t *ced = cinfo->callEds;
1445
1446       while (NULL != ced) {
1447         callEd_info_t *nced = ced->prev;
1448         free (ced);
1449         ced->prev = NULL;
1450         ced->callEd = NULL;
1451         ced = nced;
1452       }
1453
1454       /* Todo: delete callEds */
1455       cinfo->callEds = NULL;
1456
1457       free (cinfo);
1458       cinfo = cinfo->prev;
1459     }
1460
1461     free (info);
1462     pmap_insert (graph_infos, graph, NULL);
1463   }
1464
1465
1466   pmap_destroy (graph_infos);
1467
1468   /* BEGIN mild paranoia mode */
1469   graph_infos = NULL;
1470   /* END mild paranoia mode */
1471 }
1472
1473 /**
1474    Show what we have found.
1475 */
1476 void ecg_report ()
1477 {
1478   int i;
1479
1480   FILE *dot = fopen ("calls.dot", "w");
1481
1482   fprintf (dot, "digraph \"calls\" {\n");
1483   fprintf (dot, "\tnode [shape = \"record\", style = \"filled\"];\n");
1484   fprintf (dot, "\tedge [color = \"black\"];\n");
1485   fprintf (dot, "\n");
1486   fprintf (dot, "\tsize = \"11, 7\";\n");
1487   fprintf (dot, "\trotate = \"90\";\n");
1488   fprintf (dot, "\tratio = \"fill\";\n");
1489   fprintf (dot, "\trankdir = \"LR\";\n");
1490   fprintf (dot, "\n");
1491
1492   for (i = 0; i < get_irp_n_irgs (); i++) {
1493     ir_graph *graph = get_irp_irg (i);
1494     graph_info_t *info = (graph_info_t*) pmap_get (graph_infos, graph);
1495
1496     const char *name = get_irg_entity (graph) ?
1497       get_entity_name (get_irg_entity (graph)) : "noEntity";
1498
1499     const char *oname = get_type_name
1500       (get_entity_owner (get_irg_entity (graph)));
1501
1502     const char *color =
1503       (get_entity_stickyness
1504        (get_irg_entity (graph)) == stickyness_sticky) ?
1505       "red3" : "lightyellow";
1506
1507     fprintf (dot, "\t/* graph_0x%08x (\"%s\") */\n", (int) graph, name);
1508     fprintf (dot,
1509              "\tgraph_0x%08x [label=\"%s\\l%s\", color=\"%s\"];\n",
1510              (int) graph, oname, name, color);
1511     fprintf (dot, "\n");
1512
1513     call_info_t *cinfo = info->calls;
1514     if (cinfo) {
1515       fprintf (dot, "\t/* now the calls */\n");
1516     } else {
1517       fprintf (dot, "\t/* no calls */\n");
1518     }
1519
1520     while (NULL != cinfo) {
1521       ir_node *call = cinfo->call;
1522
1523       fprintf (dot, "\t/* call_0x%08x */\n", (int) call);
1524       fprintf (dot, "\tcall_0x%08x [label=\"call\\l0x%08x\"];\n",
1525                (int) call, (int) call);
1526       fprintf (dot, "\tgraph_0x%08x -> call_0x%08x;\n",
1527                (int) graph, (int) call);
1528
1529       callEd_info_t *ced = cinfo->callEds;
1530       while (NULL != ced) {
1531         fprintf (dot, "\tcall_0x%08x -> graph_0x%08x;\n",
1532                  (int) call, (int) ced->callEd);
1533         ced = ced->prev;
1534       }
1535       fprintf (dot, "\n");
1536
1537       cinfo = cinfo->prev;
1538     }
1539     fprintf (dot, "\n");
1540
1541     alloc_info_t *ainfo = info->allocs;
1542     if (ainfo) {
1543       fprintf (dot, "\t/* now the allocs */\n");
1544     } else {
1545       fprintf (dot, "\t/* no allocs */\n");
1546     }
1547
1548
1549     while (NULL != ainfo) {
1550       ir_node *alloc = ainfo->alloc;
1551       const char *name = get_type_name (ainfo->tp);
1552       const char *color = "red1";
1553
1554       fprintf (dot, "\talloc_0x%08x [label=\"%s\", color=\"%s\"]\n",
1555                (int) alloc, name, color);
1556       fprintf (dot, "\tgraph_0x%08x -> alloc_0x%08x\n",
1557                (int) graph, (int) alloc);
1558
1559       ainfo = ainfo->prev;
1560     }
1561   }
1562   fprintf (dot, "}\n");
1563
1564   /*
1565     fprintf (stdout, " max_callEds: %i\n", _max_callEds);
1566     fprintf (stdout, " max_callEds_callR: \"%s\"\n",
1567     get_entity_name (_max_callEds_callR));
1568   */
1569   fclose (dot);
1570
1571   ecg_ecg ();
1572 }
1573
1574 /**
1575    Experimental:  Print the ecg
1576 */
1577 void ecg_ecg ()
1578 {
1579   _graphs = 0;
1580   _calls  = 0;
1581
1582   ir_graph *main_graph = get_irp_main_irg ();
1583
1584   /*
1585   memset (spaces, '.', BUF_SIZE);
1586   spaces [BUF_SIZE-1] = '\0';
1587
1588   ecg_ecg_count (main_graph);
1589   fprintf (stdout, "n_graphs: %i\n", _graphs);
1590   fprintf (stdout, "max_depth = %i\n", _max_depth);
1591   */
1592
1593   /* return; */
1594
1595   _graphs = 0;
1596   _calls  = 0;
1597
1598   FILE *dot = fopen ("ecg.dot", "w");
1599
1600   fprintf (dot, "digraph \"ecg\" {\n");
1601   fprintf (dot, "\tnode [shape = \"record\", style = \"filled\"];\n");
1602   fprintf (dot, "\tedge [color = \"black\"];\n");
1603   fprintf (dot, "\n");
1604   fprintf (dot, "\tsize = \"11, 7\";\n");
1605   fprintf (dot, "\trotate = \"90\";\n");
1606   fprintf (dot, "\tratio = \"fill\";\n");
1607   fprintf (dot, "\trankdir = \"LR\";\n");
1608   fprintf (dot, "\n");
1609
1610   /* ir_graph *main_graph = get_irp_main_irg (); */
1611   ecg_ecg_graph (dot, main_graph);
1612
1613   fprintf (dot, "\t/* Grand Total: */\n");
1614   fprintf (dot, "\t/* calls:  %i */\n", (int) _calls);
1615   fprintf (dot, "\t/* graphs: %i */\n", (int) _graphs);
1616   fprintf (dot, "\t/* allocs: %i */\n", (int) _allocs);
1617   fprintf (dot, "\t/* (sales tax not included) */\n");
1618
1619   fprintf (dot, "}\n");
1620
1621   fclose (dot);
1622 }
1623
1624 \f
1625
1626 /*
1627   $Log$
1628   Revision 1.6  2004/10/18 12:47:19  liekweg
1629   minor fix
1630
1631   Revision 1.5  2004/10/14 11:31:28  liekweg
1632   SHUTUP_GCC
1633
1634   Revision 1.4  2004/10/12 11:02:01  liekweg
1635   wtf?
1636
1637 */