1 /* -------------------------------------------------------------------
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.
8 * Erstellt: Hubert Schmid, 09.06.2002
9 * ---------------------------------------------------------------- */
24 /* Eindeutige Adresse zur Markierung von besuchten Knoten und zur
25 * Darstellung der unbekannten Methode. */
26 static void * MARK = &MARK;
30 /* --- sel methods ---------------------------------------------------------- */
33 static eset * entities = NULL;
36 /* Bestimmt die eindeutige Methode, die die Methode für den
37 * übergebenene (dynamischen) Typ überschreibt. */
38 static entity * get_implementation(type * class, entity * method) {
40 if (get_entity_peculiarity(method) != description &&
41 get_entity_owner(method) == class) {
44 for (i = get_entity_n_overwrittenby(method) - 1; i >= 0; --i) {
45 entity * e = get_entity_overwrittenby(method, i);
46 if (get_entity_peculiarity(e) != description && get_entity_owner(e) == class) {
50 for (i = get_class_n_supertype(class) - 1; i >= 0; --i) {
51 entity * e = get_implementation(get_class_supertype(class, i), method);
56 assert(0 && "implemenation not found");
59 /* Returns the entity that contains the implementation of the inherited
60 entity if available, else returns the entity passed. */
61 entity *get_inherited_methods_implementation(entity *inh_meth) {
62 entity *impl_meth = NULL;
63 ir_node *addr = get_atomic_ent_value(inh_meth);
64 assert(addr && "constant entity without value");
66 if (get_irn_op(addr) == op_Const) {
67 impl_meth = get_tv_entity(get_Const_tarval(addr));
70 assert(!impl_meth || get_entity_peculiarity(impl_meth) == existent);
71 return impl_meth? impl_meth : inh_meth;
74 /* A recursive descend in the overwritten relation.
75 Cycle-free, therefore must terminate. */
76 void collect_impls(entity *method, eset *set, int *size, bool *open) {
78 if (get_entity_peculiarity(method) == existent) {
79 if (get_entity_visibility(method) == external_allocated) {
80 assert(get_entity_irg(method) == NULL);
83 assert(get_entity_irg(method) != NULL);
84 eset_insert(set, method);
88 if (get_entity_peculiarity(method) == inherited) {
89 entity *impl_ent = get_inherited_methods_implementation(method);
90 assert(impl_ent && "no implementation for inherited entity");
91 if (get_entity_visibility(impl_ent) == external_allocated) {
92 assert(get_entity_irg(impl_ent) == NULL);
95 assert(get_entity_irg(impl_ent) != NULL);
96 if (!eset_contains(set, impl_ent)) {
97 eset_insert(set, impl_ent);
102 /** recursive descend **/
103 for (i = get_entity_n_overwrittenby(method) - 1; i >= 0; --i)
104 collect_impls(get_entity_overwrittenby(method, i) ,set, size, open);
108 /* Alle Methoden bestimmen, die die übergebene Methode überschreiben
109 * (und implementieren). In der zurückgegebenen Reihung kommt jede
110 * Methode nur einmal vor. Der Wert 'NULL' steht für unbekannte
111 * (externe) Methoden. Die zurückgegebene Reihung muß vom Aufrufer
112 * wieder freigegeben werden (siehe "DEL_ARR_F"). Gibt es überhaupt
113 * keine Methoden, die die "method" überschreiben, so gibt die Methode
115 static entity ** get_impl_methods(entity * method) {
116 eset * set = eset_create();
122 /** Collect all method entities that can be called here **/
123 collect_impls(method, set, &size, &open);
125 /** Gefunden Entitaeten in ein Feld kopieren, ev. Unbekannten
126 Vorgaenger einfuegen. **/
127 if (size == 0 && !open) {
128 /* keine implementierte überschriebene Methode */
132 arr = NEW_ARR_F(entity *, size + 1);
133 arr[0] = NULL; /* Represents open method */
134 for (ent = eset_first(set); size > 0; ent = eset_next(set), --size)
138 arr = NEW_ARR_F(entity *, size);
139 for (size -= 1, ent = eset_first(set); size >= 0; ent = eset_next(set), --size)
147 static void sel_methods_walker(ir_node * node, pmap * ldname_map) {
148 if (get_irn_op(node) == op_SymConst) {
149 /* Wenn möglich SymConst-Operation durch Const-Operation
151 if (get_SymConst_kind(node) == linkage_ptr_info) {
152 pmap_entry * entry = pmap_find(ldname_map, (void *) get_SymConst_ptrinfo(node));
153 if (entry != NULL) { /* Method is declared in the compiled code */
154 entity * ent = entry->value;
155 if (get_entity_visibility(ent) != external_allocated) { /* Meth. is defined */
156 assert(get_entity_irg(ent));
157 set_irg_current_block(current_ir_graph, get_nodes_Block(node));
158 exchange(node, new_d_Const(get_irn_dbg_info(node),
159 mode_p, tarval_p_from_entity(ent)));
163 } else if (get_irn_op(node) == op_Sel &&
164 is_method_type(get_entity_type(get_Sel_entity(node)))) {
165 entity * ent = get_Sel_entity(node);
166 if (get_irn_op(skip_Proj(get_Sel_ptr(node))) == op_Alloc) {
167 /* We know which method will be called, no dispatch necessary. */
168 assert(get_entity_peculiarity(ent) != description);
169 set_irg_current_block(current_ir_graph, get_nodes_Block(node));
170 exchange (node, copy_const_value(get_atomic_ent_value(ent)));
172 assert(get_entity_peculiarity(ent) != inherited);
173 if (!eset_contains(entities, ent)) {
174 /* Entity noch nicht behandelt. Alle (intern oder extern)
175 * implementierten Methoden suchen, die diese Entity
176 * überschreiben. Die Menge an entity.link speichern. */
177 set_entity_link(ent, get_impl_methods(ent));
178 eset_insert(entities, ent);
180 if (get_entity_link(ent) == NULL) {
181 /* Die Sel-Operation kann nie einen Zeiger auf eine aufrufbare
182 * Methode zurückgeben. Damit ist sie insbesondere nicht
183 * ausführbar und nicht erreichbar. */
184 /* Gib eine Warnung aus wenn die Entitaet eine Beschreibung ist
185 fuer die es keine Implementierung gibt. */
186 if (get_entity_peculiarity(ent) == description) {
187 /* @@@ GL Methode um Fehler anzuzeigen aufrufen! */
188 xprintf("WARNING: Calling method description %I in method %I which has "
189 "no implementation!\n", get_entity_ident(ent),
190 get_entity_ident(get_irg_ent(current_ir_graph)));
192 exchange(node, new_Bad());
195 entity ** arr = get_entity_link(ent);
199 printf("\nCall site "); DDMN(node);
200 printf(" in "); DDME(get_irg_ent(current_ir_graph));
201 printf(" can call:\n");
202 for (i = 0; i < ARR_LEN(arr); i++) {
203 printf(" - "); DDME(arr[i]);
204 printf(" with owner "); DDMT(get_entity_owner(arr[i]));
209 if (ARR_LEN(arr) == 1 && arr[0] != NULL) {
210 /* Die Sel-Operation kann immer nur einen Wert auf eine
211 * interne Methode zurückgeben. Wir können daher die
212 * Sel-Operation durch eine Const- bzw. SymConst-Operation
214 set_irg_current_block(current_ir_graph, get_nodes_Block(node));
215 exchange (node, copy_const_value(get_atomic_ent_value(arr[0])));
223 /* Datenstruktur initialisieren. Zusätzlich werden alle
224 * SymConst-Operationen, die auf interne Methoden verweisen, durch
225 * Const-Operationen ersetzt. */
226 static void sel_methods_init(void) {
228 pmap * ldname_map = pmap_create();
229 assert(entities == NULL);
230 entities = eset_create();
231 for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
232 entity * ent = get_irg_ent(get_irp_irg(i));
233 /* Nur extern sichtbare Methode können überhaupt mit SymConst
234 * aufgerufen werden. */
235 if (get_entity_visibility(ent) != local) {
236 pmap_insert(ldname_map, (void *) get_entity_ld_ident(ent), ent);
239 all_irg_walk((irg_walk_func) sel_methods_walker, NULL, ldname_map);
240 pmap_destroy(ldname_map);
243 /*****************************************************************************/
245 /* Datenstruktur freigeben. */
246 static void sel_methods_dispose(void) {
249 for (ent = eset_first(entities); ent; ent = eset_next(entities)) {
250 entity ** arr = get_entity_link(ent);
254 set_entity_link(ent, NULL);
256 eset_destroy(entities);
261 /* Gibt die Menge aller Methoden zurück, die an diesem Sel-Knoten
262 * zurückgegeben werden können. Die Liste enthält keine doppelten
264 static entity ** get_Sel_arr(ir_node * sel) {
265 static entity ** NULL_ARRAY = NULL;
268 assert(sel && get_irn_op(sel) == op_Sel);
269 ent = get_Sel_entity(sel);
270 assert(is_method_type(get_entity_type(ent))); /* what else? */
271 arr = get_entity_link(ent);
275 /* "NULL" zeigt an, dass keine Implementierung existiert. Dies
276 * kann für polymorphe (abstrakte) Methoden passieren. */
278 NULL_ARRAY = NEW_ARR_F(entity *, 0);
285 static int get_Sel_n_methods(ir_node * sel) {
286 return ARR_LEN(get_Sel_arr(sel));
290 static entity * get_Sel_method(ir_node * sel, int pos) {
291 entity ** arr = get_Sel_arr(sel);
292 assert(pos >= 0 && pos < ARR_LEN(arr));
298 /* --- callee analysis ------------------------------------------------------ */
301 static void callee_ana_node(ir_node * node, eset * methods);
304 static void callee_ana_proj(ir_node * node, long n, eset * methods) {
305 assert(get_irn_mode(node) == mode_T);
306 if (get_irn_link(node) == MARK) {
307 /* already visited */
310 set_irn_link(node, MARK);
312 switch (get_irn_opcode(node)) {
314 /* proj_proj: in einem "sinnvollen" Graphen kommt jetzt ein
315 * op_Tuple oder ein Knoten, der eine "freie Methode"
317 ir_node * pred = get_Proj_pred(node);
318 if (get_irn_link(pred) != MARK) {
319 if (get_irn_op(pred) == op_Tuple) {
320 callee_ana_proj(get_Tuple_pred(pred, get_Proj_proj(node)), n, methods);
322 eset_insert(methods, MARK); /* free method -> unknown */
329 callee_ana_node(get_Tuple_pred(node, n), methods);
333 callee_ana_proj(get_Id_pred(node), n, methods);
337 eset_insert(methods, MARK); /* free method -> unknown */
341 set_irn_link(node, NULL);
345 static void callee_ana_node(ir_node * node, eset * methods) {
348 assert((get_irn_mode(node) == mode_p) || is_Bad(node));
349 /* rekursion verhindern */
350 if (get_irn_link(node) == MARK) {
351 /* already visited */
354 set_irn_link(node, MARK);
356 switch (get_irn_opcode(node)) {
358 /* externe Methode (wegen fix_symconst!) */
359 eset_insert(methods, MARK); /* free method -> unknown */
363 /* interne Methode */
364 entity * ent = get_Const_tarval(node)->u.p.ent;
365 assert(ent && is_method_type(get_entity_type(ent)));
366 if (get_entity_visibility(ent) != external_allocated) {
367 assert(get_entity_irg(ent));
368 eset_insert(methods, ent);
370 eset_insert(methods, MARK); /* free method -> unknown */
376 /* polymorphe Methode */
377 for (i = get_Sel_n_methods(node) - 1; i >= 0; --i) {
378 entity * ent = get_Sel_method(node, i);
380 eset_insert(methods, ent);
382 eset_insert(methods, MARK);
391 case iro_Phi: /* Vereinigung */
392 for (i = get_Phi_n_preds(node) - 1; i >= 0; --i) {
393 callee_ana_node(get_Phi_pred(node, i), methods);
398 callee_ana_node(get_Id_pred(node), methods);
402 callee_ana_proj(get_Proj_pred(node), get_Proj_proj(node), methods);
409 eset_insert(methods, MARK); /* free method -> unknown */
413 assert(0 && "invalid opcode or opcode not implemented");
417 set_irn_link(node, NULL);
421 static void callee_walker(ir_node * call, void * env) {
422 if (get_irn_op(call) == op_Call) {
423 eset * methods = eset_create();
425 entity ** arr = NEW_ARR_F(entity *, 0);
426 callee_ana_node(skip_nop(get_Call_ptr(call)), methods);
427 if (eset_contains(methods, MARK)) { /* unknown method */
428 ARR_APP1(entity *, arr, NULL);
430 for (ent = eset_first(methods); ent; ent = eset_next(methods)) {
432 ARR_APP1(entity *, arr, ent);
435 if (ARR_LEN(arr) == 0) {
436 /* Kann vorkommen, wenn der Vorgänger beispielsweise eine
437 * Sel-Operation war, die keine Methoden zurückgeben
438 * konnte. Wir ersetzen die Call-Operation ebenfalls durch
439 * eine Bad-Operation. Die Verlinkung muss wiederhergestellt
441 exchange(call, new_Bad());
443 set_Call_callee_arr(call, ARR_LEN(arr), arr);
446 eset_destroy(methods);
451 /* Bestimmt für jede Call-Operation die Menge der aufrufbaren Methode
452 * und speichert das Ergebnis in der Call-Operation. (siehe
453 * "set_Call_callee"). "sel_methods" wird für den Aufbau benötigt und
454 * muss bereits aufgebaut sein. */
455 static void callee_ana(void) {
457 /* Alle Graphen analysieren. */
458 for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
459 irg_walk_graph(get_irp_irg(i), callee_walker, NULL, NULL);
465 /* --- free method analysis ------------------------------------------------- */
468 static void free_mark(ir_node * node, eset * set);
470 static void free_mark_proj(ir_node * node, long n, eset * set) {
471 assert(get_irn_mode(node) == mode_T);
472 if (get_irn_link(node) == MARK) {
473 /* already visited */
476 set_irn_link(node, MARK);
477 switch (get_irn_opcode(node)) {
479 /* proj_proj: in einem "sinnvollen" Graphen kommt jetzt ein
480 * op_Tuple oder ein Knoten, der in "free_ana_walker" behandelt
482 ir_node * pred = get_Proj_pred(node);
483 if (get_irn_link(pred) != MARK && get_irn_op(pred) == op_Tuple) {
484 free_mark_proj(get_Tuple_pred(pred, get_Proj_proj(node)), n, set);
486 /* nothing: da in "free_ana_walker" behandelt. */
492 free_mark(get_Tuple_pred(node, n), set);
496 free_mark_proj(get_Id_pred(node), n, set);
502 /* nothing: Die Operationen werden in "free_ana_walker" selbst
507 assert(0 && "unexpected opcode or opcode not implemented");
510 set_irn_link(node, NULL);
514 static void free_mark(ir_node * node, eset * set) {
516 assert(get_irn_mode(node) == mode_p);
517 if (get_irn_link(node) == MARK) {
518 return; /* already visited */
520 set_irn_link(node, MARK);
521 switch (get_irn_opcode(node)) {
523 entity * ent = get_Sel_entity(node);
524 if (is_method_type(get_entity_type(ent))) {
525 for (i = get_Sel_n_methods(node) - 1; i >= 0; --i) {
526 eset_insert(set, get_Sel_method(node, i));
532 /* nothing: SymConst points to extern method */
535 tarval * val = get_Const_tarval(node);
536 entity * ent = val->u.p.ent;
537 if (ent != NULL && is_method_type(get_entity_type(ent))) {
538 eset_insert(set, ent);
543 for (i = get_Phi_n_preds(node) - 1; i >= 0; --i) {
544 free_mark(get_Phi_pred(node, i), set);
548 free_mark(get_Id_pred(node), set);
551 free_mark_proj(get_Proj_pred(node), get_Proj_proj(node), set);
554 /* nothing: Wird unten behandelt! */
557 set_irn_link(node, NULL);
561 static void free_ana_walker(ir_node * node, eset * set) {
563 if (get_irn_link(node) == MARK) {
564 /* bereits in einem Zyklus besucht. */
567 switch (get_irn_opcode(node)) {
578 /* Sonderbehandlung, da der Funktionszeigereingang natürlich kein
581 set_irn_link(node, MARK);
582 for (i = get_Call_arity(node) - 1; i >= 0; --i) {
583 ir_node * pred = get_Call_param(node, i);
584 if (get_irn_mode(pred) == mode_p) {
585 free_mark(pred, set);
589 /* other nodes: Alle anderen Knoten nehmen wir als Verräter an, bis
590 * jemand das Gegenteil implementiert. */
592 set_irn_link(node, MARK);
593 for (i = get_irn_arity(node) - 1; i >= 0; --i) {
594 ir_node * pred = get_irn_n(node, i);
595 if (get_irn_mode(pred) == mode_p) {
596 free_mark(pred, set);
601 set_irn_link(node, NULL);
605 /* Die Datenstrukturen für sel-Methoden (sel_methods) muß vor dem
606 * Aufruf von "get_free_methods" aufgebaut sein. Die (internen)
607 * SymConst-Operationen müssen in passende Const-Operationen
608 * umgewandelt worden sein, d.h. SymConst-Operationen verweisen immer
609 * auf eine echt externe Methode. */
610 static entity ** get_free_methods(void) {
611 eset * set = eset_create();
613 entity ** arr = NEW_ARR_F(entity *, 0);
615 for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
616 ir_graph * irg = get_irp_irg(i);
617 entity * ent = get_irg_ent(irg);
618 /* insert "external visible" methods. */
619 if (get_entity_visibility(ent) != local) {
620 eset_insert(set, ent);
622 irg_walk_graph(irg, NULL, (irg_walk_func) free_ana_walker, set);
624 /* Hauptprogramm ist auch frei, auch wenn es nicht "external
626 eset_insert(set, get_irg_ent(get_irp_main_irg()));
627 for (ent = eset_first(set); ent; ent = eset_next(set)) {
628 ARR_APP1(entity *, arr, ent);
635 void cgana(int *length, entity ***free_methods) {
636 entity ** free_meths;
640 free_meths = get_free_methods();
642 sel_methods_dispose();
644 /* Convert the flexible array to an array that can be handled
646 *length = ARR_LEN(free_meths);
647 *free_methods = (entity **)malloc(sizeof(entity *) * (*length));
648 for (i = 0; i < (*length); i++) (*free_methods)[i] = free_meths[i];
649 DEL_ARR_F(free_meths);