Normalisierung der Zugriffsfunktionen,
[libfirm] / ir / ana / cgana.c
1 /* -------------------------------------------------------------------
2  * $Id$
3  * -------------------------------------------------------------------
4  * Intraprozedurale Analyse zur Abschätzung der Aufrulrelation. Es
5  * wird eine Menge von freien Methoden und anschließend die an den
6  * Call-Operationen aufrufbaren Methoden bestimmt.
7  *
8  * Erstellt: Hubert Schmid, 09.06.2002
9  * ---------------------------------------------------------------- */
10
11 #include "stdlib.h"
12 #include "cgana.h"
13
14
15 #include "eset.h"
16 #include "pmap.h"
17 #include "array.h"
18 #include "irprog.h"
19 #include "irgwalk.h"
20 #include "ircons.h"
21 #include "irgmod.h"
22 #include "xprintf.h"
23
24
25 /* Eindeutige Adresse zur Markierung von besuchten Knoten und zur
26  * Darstellung der unbekannten Methode. */
27 static void * MARK = &MARK;
28
29
30
31 /* --- sel methods ---------------------------------------------------------- */
32
33
34 static eset * entities = NULL;
35
36
37 /* Bestimmt die eindeutige Methode, die die Methode für den
38  * übergebenene (dynamischen) Typ überschreibt. */
39 static entity * get_implementation(type * class, entity * method) {
40   int i;
41   if (get_entity_peculiarity(method) != description &&
42       get_entity_owner(method) == class) {
43     return method;
44   }
45   for (i = get_entity_n_overwrittenby(method) - 1; i >= 0; --i) {
46     entity * e = get_entity_overwrittenby(method, i);
47     if (get_entity_peculiarity(e) != description && get_entity_owner(e) == class) {
48       return e;
49     }
50   }
51   for (i = get_class_n_supertypes(class) - 1; i >= 0; --i) {
52     entity * e = get_implementation(get_class_supertype(class, i), method);
53     if (e) {
54       return e;
55     }
56   }
57   assert(0 && "implemenation not found");
58 }
59
60 /* Returns the entity that contains the implementation of the inherited
61    entity if available, else returns the entity passed. */
62 entity *get_inherited_methods_implementation(entity *inh_meth) {
63   entity *impl_meth = NULL;
64   ir_node *addr = get_atomic_ent_value(inh_meth);
65   assert(addr && "constant entity without value");
66
67   if (get_irn_op(addr) == op_Const) {
68     impl_meth = get_tv_entity(get_Const_tarval(addr));
69   }
70
71   assert(!impl_meth || get_entity_peculiarity(impl_meth) == existent);
72   return impl_meth? impl_meth : inh_meth;
73 }
74
75
76 /* A recursive descend in the overwritten relation.
77    Cycle-free, therefore must terminate. */
78 void collect_impls(entity *method, eset *set, int *size, bool *open) {
79   int i;
80   if (get_entity_peculiarity(method) == existent) {
81     if (get_entity_visibility(method) == external_allocated) {
82       assert(get_entity_irg(method) == NULL);
83       *open = true;
84     } else {
85       assert(get_entity_irg(method) != NULL);
86       if (!eset_contains(set, method)) {
87         eset_insert(set, method);
88         ++(*size);
89       }
90     }
91   }
92   if (get_entity_peculiarity(method) == inherited) {
93     entity *impl_ent = get_inherited_methods_implementation(method);
94     assert(impl_ent && "no implementation for inherited entity");
95     if (get_entity_visibility(impl_ent) == external_allocated) {
96       assert(get_entity_irg(impl_ent) == NULL);
97       *open = true;
98     } else {
99       assert(get_entity_irg(impl_ent) != NULL);
100       if (!eset_contains(set, impl_ent)) {
101         eset_insert(set, impl_ent);
102         ++(*size);
103       }
104     }
105   }
106   /** recursive descend **/
107   for (i = get_entity_n_overwrittenby(method) - 1; i >= 0; --i)
108     collect_impls(get_entity_overwrittenby(method, i) ,set, size, open);
109 }
110
111
112 /* Alle Methoden bestimmen, die die übergebene Methode überschreiben
113  * (und implementieren). In der zurückgegebenen Reihung kommt jede
114  * Methode nur einmal vor. Der Wert 'NULL' steht für unbekannte
115  * (externe) Methoden. Die zurückgegebene Reihung muß vom Aufrufer
116  * wieder freigegeben werden (siehe "DEL_ARR_F"). Gibt es überhaupt
117  * keine Methoden, die die "method" überschreiben, so gibt die Methode
118  * "NULL" zurück. */
119 static entity ** get_impl_methods(entity * method) {
120   eset * set = eset_create();
121   int size = 0;
122   entity ** arr;
123   bool open = false;
124
125   /** Collect all method entities that can be called here **/
126   collect_impls(method, set, &size, &open);
127
128   /** Gefunden Entitaeten in ein Feld kopieren, ev. Unbekannten
129      Vorgaenger einfuegen. **/
130   if (size == 0 && !open) {
131     /* keine implementierte überschriebene Methode */
132     arr = NULL;
133   } else if (open) {
134     entity * ent;
135     arr = NEW_ARR_F(entity *, size + 1);
136     arr[0] = NULL;  /* Represents open method */
137     for (ent = eset_first(set); size > 0; ent = eset_next(set), --size)
138       arr[size] = ent;
139   } else {
140     entity * ent;
141     arr = NEW_ARR_F(entity *, size);
142     for (size -= 1, ent = eset_first(set); size >= 0; ent = eset_next(set), --size)
143       arr[size] = ent;
144   }
145   eset_destroy(set);
146   return arr;
147 }
148
149
150 static void sel_methods_walker(ir_node * node, pmap * ldname_map) {
151   if (get_irn_op(node) == op_SymConst) {
152     /* Wenn möglich SymConst-Operation durch Const-Operation
153      * ersetzen. */
154     if (get_SymConst_kind(node) == linkage_ptr_info) {
155       pmap_entry * entry = pmap_find(ldname_map, (void *) get_SymConst_ptrinfo(node));
156       if (entry != NULL) { /* Method is declared in the compiled code */
157         entity * ent = entry->value;
158         if (get_entity_visibility(ent) != external_allocated) { /* Meth. is defined */
159           assert(get_entity_irg(ent));
160           set_irg_current_block(current_ir_graph, get_nodes_Block(node));
161           exchange(node, new_d_Const(get_irn_dbg_info(node),
162                                      mode_p, tarval_p_from_entity(ent)));
163         }
164       }
165     }
166   } else if (get_irn_op(node) == op_Sel &&
167              is_method_type(get_entity_type(get_Sel_entity(node)))) {
168     entity * ent = get_Sel_entity(node);
169     if (get_irn_op(skip_Proj(get_Sel_ptr(node))) == op_Alloc) {
170       /* We know which method will be called, no dispatch necessary. */
171       assert(get_entity_peculiarity(ent) != description);
172       set_irg_current_block(current_ir_graph, get_nodes_Block(node));
173       exchange (node, copy_const_value(get_atomic_ent_value(ent)));
174     } else {
175       assert(get_entity_peculiarity(ent) != inherited);
176       if (!eset_contains(entities, ent)) {
177         /* Entity noch nicht behandelt. Alle (intern oder extern)
178          * implementierten Methoden suchen, die diese Entity
179          * überschreiben. Die Menge an entity.link speichern. */
180         set_entity_link(ent, get_impl_methods(ent));
181         eset_insert(entities, ent);
182       }
183       if (get_entity_link(ent) == NULL) {
184         /* Die Sel-Operation kann nie einen Zeiger auf eine aufrufbare
185          * Methode zurückgeben. Damit ist sie insbesondere nicht
186          * ausführbar und nicht erreichbar. */
187         /* Gib eine Warnung aus wenn die Entitaet eine Beschreibung ist
188            fuer die es keine Implementierung gibt. */
189         if (get_entity_peculiarity(ent) == description) {
190           /* @@@ GL Methode um Fehler anzuzeigen aufrufen! */
191           xprintf("WARNING: Calling method description %I in method %I which has "
192                   "no implementation!\n", get_entity_ident(ent),
193                   get_entity_ident(get_irg_ent(current_ir_graph)));
194         } else {
195           exchange(node, new_Bad());
196         }
197       } else {
198         entity ** arr = get_entity_link(ent);
199
200 #if 0
201         int i;
202         printf("\nCall site "); DDMN(node);
203         printf("  in "); DDME(get_irg_ent(current_ir_graph));
204         printf("  can call:\n");
205         for (i = 0; i < ARR_LEN(arr); i++) {
206           printf("   - "); DDME(arr[i]);
207           printf("     with owner "); DDMT(get_entity_owner(arr[i]));
208         }
209         printf("\n");
210 #endif
211
212         if (ARR_LEN(arr) == 1 && arr[0] != NULL) {
213           /* Die Sel-Operation kann immer nur einen Wert auf eine
214            * interne Methode zurückgeben. Wir können daher die
215            * Sel-Operation durch eine Const- bzw. SymConst-Operation
216            * ersetzen. */
217           set_irg_current_block(current_ir_graph, get_nodes_Block(node));
218           exchange (node, copy_const_value(get_atomic_ent_value(arr[0])));
219         }
220       }
221     }
222   }
223 }
224
225
226 /* Datenstruktur initialisieren. Zusätzlich werden alle
227  * SymConst-Operationen, die auf interne Methoden verweisen, durch
228  * Const-Operationen ersetzt. */
229 static void sel_methods_init(void) {
230   int i;
231   pmap * ldname_map = pmap_create();
232   assert(entities == NULL);
233   entities = eset_create();
234   for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
235     entity * ent = get_irg_ent(get_irp_irg(i));
236     /* Nur extern sichtbare Methode können überhaupt mit SymConst
237      * aufgerufen werden. */
238     if (get_entity_visibility(ent) != local) {
239       pmap_insert(ldname_map, (void *) get_entity_ld_ident(ent), ent);
240     }
241   }
242   all_irg_walk((irg_walk_func) sel_methods_walker, NULL, ldname_map);
243   pmap_destroy(ldname_map);
244 }
245
246 /*****************************************************************************/
247
248 /* Datenstruktur freigeben. */
249 static void sel_methods_dispose(void) {
250   entity * ent;
251   assert(entities);
252   for (ent = eset_first(entities); ent; ent = eset_next(entities)) {
253     entity ** arr = get_entity_link(ent);
254     if (arr) {
255       DEL_ARR_F(arr);
256     }
257     set_entity_link(ent, NULL);
258   }
259   eset_destroy(entities);
260   entities = NULL;
261 }
262
263
264 /* Gibt die Menge aller Methoden zurück, die an diesem Sel-Knoten
265  * zurückgegeben werden können. Die Liste enthält keine doppelten
266  * Einträge. */
267 static entity ** get_Sel_arr(ir_node * sel) {
268   static entity ** NULL_ARRAY = NULL;
269   entity * ent;
270   entity ** arr;
271   assert(sel && get_irn_op(sel) == op_Sel);
272   ent = get_Sel_entity(sel);
273   assert(is_method_type(get_entity_type(ent))); /* what else? */
274   arr = get_entity_link(ent);
275   if (arr) {
276     return arr;
277   } else {
278     /* "NULL" zeigt an, dass keine Implementierung existiert. Dies
279      * kann für polymorphe (abstrakte) Methoden passieren. */
280     if (!NULL_ARRAY) {
281       NULL_ARRAY = NEW_ARR_F(entity *, 0);
282     }
283     return NULL_ARRAY;
284   }
285 }
286
287
288 static int get_Sel_n_methods(ir_node * sel) {
289   return ARR_LEN(get_Sel_arr(sel));
290 }
291
292
293 static entity * get_Sel_method(ir_node * sel, int pos) {
294   entity ** arr = get_Sel_arr(sel);
295   assert(pos >= 0 && pos < ARR_LEN(arr));
296   return arr[pos];
297 }
298
299
300
301 /* --- callee analysis ------------------------------------------------------ */
302
303
304 static void callee_ana_node(ir_node * node, eset * methods);
305
306
307 static void callee_ana_proj(ir_node * node, long n, eset * methods) {
308   assert(get_irn_mode(node) == mode_T);
309   if (get_irn_link(node) == MARK) {
310     /* already visited */
311     return;
312   }
313   set_irn_link(node, MARK);
314
315   switch (get_irn_opcode(node)) {
316   case iro_Proj: {
317     /* proj_proj: in einem "sinnvollen" Graphen kommt jetzt ein
318      * op_Tuple oder ein Knoten, der eine "freie Methode"
319      * zurückgibt. */
320     ir_node * pred = get_Proj_pred(node);
321     if (get_irn_link(pred) != MARK) {
322       if (get_irn_op(pred) == op_Tuple) {
323         callee_ana_proj(get_Tuple_pred(pred, get_Proj_proj(node)), n, methods);
324       } else {
325         eset_insert(methods, MARK); /* free method -> unknown */
326       }
327     }
328     break;
329   }
330
331   case iro_Tuple:
332     callee_ana_node(get_Tuple_pred(node, n), methods);
333     break;
334
335   case iro_Id:
336     callee_ana_proj(get_Id_pred(node), n, methods);
337     break;
338
339   default:
340     eset_insert(methods, MARK); /* free method -> unknown */
341     break;
342   }
343
344   set_irn_link(node, NULL);
345 }
346
347
348 static void callee_ana_node(ir_node * node, eset * methods) {
349   int i;
350
351   assert((get_irn_mode(node) == mode_p) || is_Bad(node));
352   /* rekursion verhindern */
353   if (get_irn_link(node) == MARK) {
354     /* already visited */
355     return;
356   }
357   set_irn_link(node, MARK);
358
359   switch (get_irn_opcode(node)) {
360   case iro_SymConst:
361     /* externe Methode (wegen fix_symconst!) */
362     eset_insert(methods, MARK); /* free method -> unknown */
363     break;
364
365   case iro_Const: {
366     /* interne Methode */
367     entity * ent = get_Const_tarval(node)->u.p.ent;
368     assert(ent && is_method_type(get_entity_type(ent)));
369     if (get_entity_visibility(ent) != external_allocated) {
370       assert(get_entity_irg(ent));
371       eset_insert(methods, ent);
372     } else {
373       eset_insert(methods, MARK); /* free method -> unknown */
374     }
375     break;
376   }
377
378   case iro_Sel:
379     /* polymorphe Methode */
380     for (i = get_Sel_n_methods(node) - 1; i >= 0; --i) {
381       entity * ent = get_Sel_method(node, i);
382       if (ent) {
383         eset_insert(methods, ent);
384       } else {
385         eset_insert(methods, MARK);
386       }
387     }
388     break;
389
390   case iro_Bad:
391     /* nothing */
392     break;
393
394   case iro_Phi: /* Vereinigung */
395     for (i = get_Phi_n_preds(node) - 1; i >= 0; --i) {
396       callee_ana_node(get_Phi_pred(node, i), methods);
397     }
398     break;
399
400   case iro_Id:
401     callee_ana_node(get_Id_pred(node), methods);
402     break;
403
404   case iro_Proj:
405     callee_ana_proj(get_Proj_pred(node), get_Proj_proj(node), methods);
406     break;
407
408   case iro_Add:
409   case iro_Sub:
410   case iro_Conv:
411     /* extern */
412     eset_insert(methods, MARK); /* free method -> unknown */
413     break;
414
415   default:
416     assert(0 && "invalid opcode or opcode not implemented");
417     break;
418   }
419
420   set_irn_link(node, NULL);
421 }
422
423
424 static void callee_walker(ir_node * call, void * env) {
425   if (get_irn_op(call) == op_Call) {
426     eset * methods = eset_create();
427     entity * ent;
428     entity ** arr = NEW_ARR_F(entity *, 0);
429     callee_ana_node(skip_nop(get_Call_ptr(call)), methods);
430     if (eset_contains(methods, MARK)) { /* unknown method */
431       ARR_APP1(entity *, arr, NULL);
432     }
433     for (ent = eset_first(methods); ent; ent = eset_next(methods)) {
434       if (ent != MARK) {
435         ARR_APP1(entity *, arr, ent);
436       }
437     }
438     if (ARR_LEN(arr) == 0) {
439       /* Kann vorkommen, wenn der Vorgänger beispielsweise eine
440        * Sel-Operation war, die keine Methoden zurückgeben
441        * konnte. Wir ersetzen die Call-Operation ebenfalls durch
442        * eine Bad-Operation. Die Verlinkung muss wiederhergestellt
443        * werden! */
444       exchange(call, new_Bad());
445     } else {
446       set_Call_callee_arr(call, ARR_LEN(arr), arr);
447     }
448     DEL_ARR_F(arr);
449     eset_destroy(methods);
450   }
451 }
452
453
454 /* Bestimmt für jede Call-Operation die Menge der aufrufbaren Methode
455  * und speichert das Ergebnis in der Call-Operation. (siehe
456  * "set_Call_callee"). "sel_methods" wird für den Aufbau benötigt und
457  * muss bereits aufgebaut sein. */
458 static void callee_ana(void) {
459   int i;
460   /* Alle Graphen analysieren. */
461   for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
462     irg_walk_graph(get_irp_irg(i), callee_walker, NULL, NULL);
463   }
464 }
465
466
467
468 /* --- free method analysis ------------------------------------------------- */
469
470
471 static void free_mark(ir_node * node, eset * set);
472
473 static void free_mark_proj(ir_node * node, long n, eset * set) {
474   assert(get_irn_mode(node) == mode_T);
475   if (get_irn_link(node) == MARK) {
476     /* already visited */
477     return;
478   }
479   set_irn_link(node, MARK);
480   switch (get_irn_opcode(node)) {
481   case iro_Proj: {
482     /* proj_proj: in einem "sinnvollen" Graphen kommt jetzt ein
483      * op_Tuple oder ein Knoten, der in "free_ana_walker" behandelt
484      * wird. */
485     ir_node * pred = get_Proj_pred(node);
486     if (get_irn_link(pred) != MARK && get_irn_op(pred) == op_Tuple) {
487       free_mark_proj(get_Tuple_pred(pred, get_Proj_proj(node)), n, set);
488     } else {
489       /* nothing: da in "free_ana_walker" behandelt. */
490     }
491     break;
492   }
493
494   case iro_Tuple:
495     free_mark(get_Tuple_pred(node, n), set);
496     break;
497
498   case iro_Id:
499     free_mark_proj(get_Id_pred(node), n, set);
500     break;
501
502   case iro_Start:
503   case iro_Alloc:
504   case iro_Load:
505     /* nothing: Die Operationen werden in "free_ana_walker" selbst
506      * behandelt. */
507     break;
508
509   default:
510     assert(0 && "unexpected opcode or opcode not implemented");
511     break;
512   }
513   set_irn_link(node, NULL);
514 }
515
516
517 static void free_mark(ir_node * node, eset * set) {
518   int i;
519   assert(get_irn_mode(node) == mode_p);
520   if (get_irn_link(node) == MARK) {
521     return; /* already visited */
522   }
523   set_irn_link(node, MARK);
524   switch (get_irn_opcode(node)) {
525   case iro_Sel: {
526     entity * ent = get_Sel_entity(node);
527     if (is_method_type(get_entity_type(ent))) {
528       for (i = get_Sel_n_methods(node) - 1; i >= 0; --i) {
529         eset_insert(set, get_Sel_method(node, i));
530       }
531     }
532     break;
533   }
534   case iro_SymConst:
535     /* nothing: SymConst points to extern method */
536     break;
537   case iro_Const: {
538     tarval * val = get_Const_tarval(node);
539     entity * ent = val->u.p.ent;
540     if (ent != NULL && is_method_type(get_entity_type(ent))) {
541       eset_insert(set, ent);
542     }
543     break;
544   }
545   case iro_Phi:
546     for (i = get_Phi_n_preds(node) - 1; i >= 0; --i) {
547       free_mark(get_Phi_pred(node, i), set);
548     }
549     break;
550   case iro_Id:
551     free_mark(get_Id_pred(node), set);
552     break;
553   case iro_Proj:
554     free_mark_proj(get_Proj_pred(node), get_Proj_proj(node), set);
555     break;
556   default:
557     /* nothing: Wird unten behandelt! */
558     break;
559   }
560   set_irn_link(node, NULL);
561 }
562
563
564 static void free_ana_walker(ir_node * node, eset * set) {
565   int i;
566   if (get_irn_link(node) == MARK) {
567     /* bereits in einem Zyklus besucht. */
568     return;
569   }
570   switch (get_irn_opcode(node)) {
571   /* special nodes */
572   case iro_Sel:
573   case iro_SymConst:
574   case iro_Const:
575   case iro_Phi:
576   case iro_Id:
577   case iro_Proj:
578   case iro_Tuple:
579     /* nothing */
580     break;
581   /* Sonderbehandlung, da der Funktionszeigereingang natürlich kein
582    * Verräter ist. */
583   case iro_Call:
584     set_irn_link(node, MARK);
585     for (i = get_Call_arity(node) - 1; i >= 0; --i) {
586       ir_node * pred = get_Call_param(node, i);
587       if (get_irn_mode(pred) == mode_p) {
588         free_mark(pred, set);
589       }
590     }
591     break;
592   /* other nodes: Alle anderen Knoten nehmen wir als Verräter an, bis
593    * jemand das Gegenteil implementiert. */
594   default:
595     set_irn_link(node, MARK);
596     for (i = get_irn_arity(node) - 1; i >= 0; --i) {
597       ir_node * pred = get_irn_n(node, i);
598       if (get_irn_mode(pred) == mode_p) {
599         free_mark(pred, set);
600       }
601     }
602     break;
603   }
604   set_irn_link(node, NULL);
605 }
606
607
608 /* Die Datenstrukturen für sel-Methoden (sel_methods) muß vor dem
609  * Aufruf von "get_free_methods" aufgebaut sein. Die (internen)
610  * SymConst-Operationen müssen in passende Const-Operationen
611  * umgewandelt worden sein, d.h. SymConst-Operationen verweisen immer
612  * auf eine echt externe Methode.  */
613 static entity ** get_free_methods(void) {
614   eset * set = eset_create();
615   int i;
616   entity ** arr = NEW_ARR_F(entity *, 0);
617   entity * ent;
618   for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
619     ir_graph * irg = get_irp_irg(i);
620     entity * ent = get_irg_ent(irg);
621     /* insert "external visible" methods. */
622     if (get_entity_visibility(ent) != local) {
623       eset_insert(set, ent);
624     }
625     irg_walk_graph(irg, NULL, (irg_walk_func) free_ana_walker, set);
626   }
627   /* Hauptprogramm ist auch frei, auch wenn es nicht "external
628    * visible" ist. */
629   eset_insert(set, get_irg_ent(get_irp_main_irg()));
630   for (ent = eset_first(set); ent; ent = eset_next(set)) {
631     ARR_APP1(entity *, arr, ent);
632   }
633   eset_destroy(set);
634
635   return arr;
636 }
637
638 void cgana(int *length, entity ***free_methods) {
639   entity ** free_meths;
640   int i;
641
642   sel_methods_init();
643   free_meths = get_free_methods();
644   callee_ana();
645   sel_methods_dispose();
646
647   /* Convert the flexible array to an array that can be handled
648      by standard C. */
649   *length = ARR_LEN(free_meths);
650   *free_methods = (entity **)malloc(sizeof(entity *) * (*length));
651   for (i = 0; i < (*length); i++) (*free_methods)[i] = free_meths[i];
652   DEL_ARR_F(free_meths);
653 }