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 * ---------------------------------------------------------------- */
25 /* Eindeutige Adresse zur Markierung von besuchten Knoten und zur
26 * Darstellung der unbekannten Methode. */
27 static void * MARK = &MARK;
31 /* --- sel methods ---------------------------------------------------------- */
34 static eset * entities = NULL;
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) {
41 if (get_entity_peculiarity(method) != description &&
42 get_entity_owner(method) == class) {
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) {
51 for (i = get_class_n_supertype(class) - 1; i >= 0; --i) {
52 entity * e = get_implementation(get_class_supertype(class, i), method);
57 assert(0 && "implemenation not found");
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");
67 if (get_irn_op(addr) == op_Const) {
68 impl_meth = get_tv_entity(get_Const_tarval(addr));
71 assert(!impl_meth || get_entity_peculiarity(impl_meth) == existent);
72 return impl_meth? impl_meth : inh_meth;
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) {
80 if (get_entity_peculiarity(method) == existent) {
81 if (get_entity_visibility(method) == external_allocated) {
82 assert(get_entity_irg(method) == NULL);
85 assert(get_entity_irg(method) != NULL);
86 if (!eset_contains(set, method)) {
87 eset_insert(set, method);
89 //printf("Adding existent method %d ", *size); DDME(method);
90 //printf(" with owner "); DDMT(get_entity_owner(method));
94 if (get_entity_peculiarity(method) == inherited) {
95 entity *impl_ent = get_inherited_methods_implementation(method);
96 assert(impl_ent && "no implementation for inherited entity");
97 if (get_entity_visibility(impl_ent) == external_allocated) {
98 assert(get_entity_irg(impl_ent) == NULL);
101 assert(get_entity_irg(impl_ent) != NULL);
102 if (!eset_contains(set, impl_ent)) {
103 eset_insert(set, impl_ent);
105 // printf("Adding inherited method %d ", *size); DDME(impl_ent);
106 //printf(" with owner "); DDMT(get_entity_owner(impl_ent));
110 /** recursive descend **/
111 for (i = get_entity_n_overwrittenby(method) - 1; i >= 0; --i)
112 collect_impls(get_entity_overwrittenby(method, i) ,set, size, open);
116 /* Alle Methoden bestimmen, die die übergebene Methode überschreiben
117 * (und implementieren). In der zurückgegebenen Reihung kommt jede
118 * Methode nur einmal vor. Der Wert 'NULL' steht für unbekannte
119 * (externe) Methoden. Die zurückgegebene Reihung muß vom Aufrufer
120 * wieder freigegeben werden (siehe "DEL_ARR_F"). Gibt es überhaupt
121 * keine Methoden, die die "method" überschreiben, so gibt die Methode
123 static entity ** get_impl_methods(entity * method) {
124 eset * set = eset_create();
129 /** Collect all method entities that can be called here **/
130 collect_impls(method, set, &size, &open);
133 //printf("Size is %d \n\n", size);
135 /** Gefunden Entitaeten in ein Feld kopieren, ev. Unbekannten
136 Vorgaenger einfuegen. **/
137 if (size == 0 && !open) {
138 /* keine implementierte überschriebene Methode */
142 arr = NEW_ARR_F(entity *, size + 1);
143 arr[0] = NULL; /* Represents open method */
144 for (ent = eset_first(set); size > 0; ent = eset_next(set), --size)
148 arr = NEW_ARR_F(entity *, size);
149 for (size -= 1, ent = eset_first(set); size >= 0; ent = eset_next(set), --size)
157 static void sel_methods_walker(ir_node * node, pmap * ldname_map) {
158 if (get_irn_op(node) == op_SymConst) {
159 /* Wenn möglich SymConst-Operation durch Const-Operation
161 if (get_SymConst_kind(node) == linkage_ptr_info) {
162 pmap_entry * entry = pmap_find(ldname_map, (void *) get_SymConst_ptrinfo(node));
163 if (entry != NULL) { /* Method is declared in the compiled code */
164 entity * ent = entry->value;
165 if (get_entity_visibility(ent) != external_allocated) { /* Meth. is defined */
166 assert(get_entity_irg(ent));
167 set_irg_current_block(current_ir_graph, get_nodes_Block(node));
168 exchange(node, new_d_Const(get_irn_dbg_info(node),
169 mode_p, tarval_p_from_entity(ent)));
173 } else if (get_irn_op(node) == op_Sel &&
174 is_method_type(get_entity_type(get_Sel_entity(node)))) {
175 entity * ent = get_Sel_entity(node);
176 if (get_irn_op(skip_Proj(get_Sel_ptr(node))) == op_Alloc) {
177 /* We know which method will be called, no dispatch necessary. */
178 assert(get_entity_peculiarity(ent) != description);
179 set_irg_current_block(current_ir_graph, get_nodes_Block(node));
180 exchange (node, copy_const_value(get_atomic_ent_value(ent)));
182 assert(get_entity_peculiarity(ent) != inherited);
183 if (!eset_contains(entities, ent)) {
184 /* Entity noch nicht behandelt. Alle (intern oder extern)
185 * implementierten Methoden suchen, die diese Entity
186 * überschreiben. Die Menge an entity.link speichern. */
187 set_entity_link(ent, get_impl_methods(ent));
188 eset_insert(entities, ent);
190 if (get_entity_link(ent) == NULL) {
191 /* Die Sel-Operation kann nie einen Zeiger auf eine aufrufbare
192 * Methode zurückgeben. Damit ist sie insbesondere nicht
193 * ausführbar und nicht erreichbar. */
194 /* Gib eine Warnung aus wenn die Entitaet eine Beschreibung ist
195 fuer die es keine Implementierung gibt. */
196 if (get_entity_peculiarity(ent) == description) {
197 /* @@@ GL Methode um Fehler anzuzeigen aufrufen! */
198 xprintf("WARNING: Calling method description %I in method %I which has "
199 "no implementation!\n", get_entity_ident(ent),
200 get_entity_ident(get_irg_ent(current_ir_graph)));
202 exchange(node, new_Bad());
205 entity ** arr = get_entity_link(ent);
209 printf("\nCall site "); DDMN(node);
210 printf(" in "); DDME(get_irg_ent(current_ir_graph));
211 printf(" can call:\n");
212 for (i = 0; i < ARR_LEN(arr); i++) {
213 printf(" - "); DDME(arr[i]);
214 printf(" with owner "); DDMT(get_entity_owner(arr[i]));
219 if (ARR_LEN(arr) == 1 && arr[0] != NULL) {
220 /* Die Sel-Operation kann immer nur einen Wert auf eine
221 * interne Methode zurückgeben. Wir können daher die
222 * Sel-Operation durch eine Const- bzw. SymConst-Operation
224 set_irg_current_block(current_ir_graph, get_nodes_Block(node));
225 exchange (node, copy_const_value(get_atomic_ent_value(arr[0])));
233 /* Datenstruktur initialisieren. Zusätzlich werden alle
234 * SymConst-Operationen, die auf interne Methoden verweisen, durch
235 * Const-Operationen ersetzt. */
236 static void sel_methods_init(void) {
238 pmap * ldname_map = pmap_create();
239 assert(entities == NULL);
240 entities = eset_create();
241 for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
242 entity * ent = get_irg_ent(get_irp_irg(i));
243 /* Nur extern sichtbare Methode können überhaupt mit SymConst
244 * aufgerufen werden. */
245 if (get_entity_visibility(ent) != local) {
246 pmap_insert(ldname_map, (void *) get_entity_ld_ident(ent), ent);
249 all_irg_walk((irg_walk_func) sel_methods_walker, NULL, ldname_map);
250 pmap_destroy(ldname_map);
253 /*****************************************************************************/
255 /* Datenstruktur freigeben. */
256 static void sel_methods_dispose(void) {
259 for (ent = eset_first(entities); ent; ent = eset_next(entities)) {
260 entity ** arr = get_entity_link(ent);
264 set_entity_link(ent, NULL);
266 eset_destroy(entities);
271 /* Gibt die Menge aller Methoden zurück, die an diesem Sel-Knoten
272 * zurückgegeben werden können. Die Liste enthält keine doppelten
274 static entity ** get_Sel_arr(ir_node * sel) {
275 static entity ** NULL_ARRAY = NULL;
278 assert(sel && get_irn_op(sel) == op_Sel);
279 ent = get_Sel_entity(sel);
280 assert(is_method_type(get_entity_type(ent))); /* what else? */
281 arr = get_entity_link(ent);
285 /* "NULL" zeigt an, dass keine Implementierung existiert. Dies
286 * kann für polymorphe (abstrakte) Methoden passieren. */
288 NULL_ARRAY = NEW_ARR_F(entity *, 0);
295 static int get_Sel_n_methods(ir_node * sel) {
296 return ARR_LEN(get_Sel_arr(sel));
300 static entity * get_Sel_method(ir_node * sel, int pos) {
301 entity ** arr = get_Sel_arr(sel);
302 assert(pos >= 0 && pos < ARR_LEN(arr));
308 /* --- callee analysis ------------------------------------------------------ */
311 static void callee_ana_node(ir_node * node, eset * methods);
314 static void callee_ana_proj(ir_node * node, long n, eset * methods) {
315 assert(get_irn_mode(node) == mode_T);
316 if (get_irn_link(node) == MARK) {
317 /* already visited */
320 set_irn_link(node, MARK);
322 switch (get_irn_opcode(node)) {
324 /* proj_proj: in einem "sinnvollen" Graphen kommt jetzt ein
325 * op_Tuple oder ein Knoten, der eine "freie Methode"
327 ir_node * pred = get_Proj_pred(node);
328 if (get_irn_link(pred) != MARK) {
329 if (get_irn_op(pred) == op_Tuple) {
330 callee_ana_proj(get_Tuple_pred(pred, get_Proj_proj(node)), n, methods);
332 eset_insert(methods, MARK); /* free method -> unknown */
339 callee_ana_node(get_Tuple_pred(node, n), methods);
343 callee_ana_proj(get_Id_pred(node), n, methods);
347 eset_insert(methods, MARK); /* free method -> unknown */
351 set_irn_link(node, NULL);
355 static void callee_ana_node(ir_node * node, eset * methods) {
358 assert((get_irn_mode(node) == mode_p) || is_Bad(node));
359 /* rekursion verhindern */
360 if (get_irn_link(node) == MARK) {
361 /* already visited */
364 set_irn_link(node, MARK);
366 switch (get_irn_opcode(node)) {
368 /* externe Methode (wegen fix_symconst!) */
369 eset_insert(methods, MARK); /* free method -> unknown */
373 /* interne Methode */
374 entity * ent = get_Const_tarval(node)->u.p.ent;
375 assert(ent && is_method_type(get_entity_type(ent)));
376 if (get_entity_visibility(ent) != external_allocated) {
377 assert(get_entity_irg(ent));
378 eset_insert(methods, ent);
380 eset_insert(methods, MARK); /* free method -> unknown */
386 /* polymorphe Methode */
387 for (i = get_Sel_n_methods(node) - 1; i >= 0; --i) {
388 entity * ent = get_Sel_method(node, i);
390 eset_insert(methods, ent);
392 eset_insert(methods, MARK);
401 case iro_Phi: /* Vereinigung */
402 for (i = get_Phi_n_preds(node) - 1; i >= 0; --i) {
403 callee_ana_node(get_Phi_pred(node, i), methods);
408 callee_ana_node(get_Id_pred(node), methods);
412 callee_ana_proj(get_Proj_pred(node), get_Proj_proj(node), methods);
419 eset_insert(methods, MARK); /* free method -> unknown */
423 assert(0 && "invalid opcode or opcode not implemented");
427 set_irn_link(node, NULL);
431 static void callee_walker(ir_node * call, void * env) {
432 if (get_irn_op(call) == op_Call) {
433 eset * methods = eset_create();
435 entity ** arr = NEW_ARR_F(entity *, 0);
436 callee_ana_node(skip_nop(get_Call_ptr(call)), methods);
437 if (eset_contains(methods, MARK)) { /* unknown method */
438 ARR_APP1(entity *, arr, NULL);
440 for (ent = eset_first(methods); ent; ent = eset_next(methods)) {
442 ARR_APP1(entity *, arr, ent);
445 if (ARR_LEN(arr) == 0) {
446 /* Kann vorkommen, wenn der Vorgänger beispielsweise eine
447 * Sel-Operation war, die keine Methoden zurückgeben
448 * konnte. Wir ersetzen die Call-Operation ebenfalls durch
449 * eine Bad-Operation. Die Verlinkung muss wiederhergestellt
451 exchange(call, new_Bad());
453 set_Call_callee_arr(call, ARR_LEN(arr), arr);
456 eset_destroy(methods);
461 /* Bestimmt für jede Call-Operation die Menge der aufrufbaren Methode
462 * und speichert das Ergebnis in der Call-Operation. (siehe
463 * "set_Call_callee"). "sel_methods" wird für den Aufbau benötigt und
464 * muss bereits aufgebaut sein. */
465 static void callee_ana(void) {
467 /* Alle Graphen analysieren. */
468 for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
469 irg_walk_graph(get_irp_irg(i), callee_walker, NULL, NULL);
475 /* --- free method analysis ------------------------------------------------- */
478 static void free_mark(ir_node * node, eset * set);
480 static void free_mark_proj(ir_node * node, long n, eset * set) {
481 assert(get_irn_mode(node) == mode_T);
482 if (get_irn_link(node) == MARK) {
483 /* already visited */
486 set_irn_link(node, MARK);
487 switch (get_irn_opcode(node)) {
489 /* proj_proj: in einem "sinnvollen" Graphen kommt jetzt ein
490 * op_Tuple oder ein Knoten, der in "free_ana_walker" behandelt
492 ir_node * pred = get_Proj_pred(node);
493 if (get_irn_link(pred) != MARK && get_irn_op(pred) == op_Tuple) {
494 free_mark_proj(get_Tuple_pred(pred, get_Proj_proj(node)), n, set);
496 /* nothing: da in "free_ana_walker" behandelt. */
502 free_mark(get_Tuple_pred(node, n), set);
506 free_mark_proj(get_Id_pred(node), n, set);
512 /* nothing: Die Operationen werden in "free_ana_walker" selbst
517 assert(0 && "unexpected opcode or opcode not implemented");
520 set_irn_link(node, NULL);
524 static void free_mark(ir_node * node, eset * set) {
526 assert(get_irn_mode(node) == mode_p);
527 if (get_irn_link(node) == MARK) {
528 return; /* already visited */
530 set_irn_link(node, MARK);
531 switch (get_irn_opcode(node)) {
533 entity * ent = get_Sel_entity(node);
534 if (is_method_type(get_entity_type(ent))) {
535 for (i = get_Sel_n_methods(node) - 1; i >= 0; --i) {
536 eset_insert(set, get_Sel_method(node, i));
542 /* nothing: SymConst points to extern method */
545 tarval * val = get_Const_tarval(node);
546 entity * ent = val->u.p.ent;
547 if (ent != NULL && is_method_type(get_entity_type(ent))) {
548 eset_insert(set, ent);
553 for (i = get_Phi_n_preds(node) - 1; i >= 0; --i) {
554 free_mark(get_Phi_pred(node, i), set);
558 free_mark(get_Id_pred(node), set);
561 free_mark_proj(get_Proj_pred(node), get_Proj_proj(node), set);
564 /* nothing: Wird unten behandelt! */
567 set_irn_link(node, NULL);
571 static void free_ana_walker(ir_node * node, eset * set) {
573 if (get_irn_link(node) == MARK) {
574 /* bereits in einem Zyklus besucht. */
577 switch (get_irn_opcode(node)) {
588 /* Sonderbehandlung, da der Funktionszeigereingang natürlich kein
591 set_irn_link(node, MARK);
592 for (i = get_Call_arity(node) - 1; i >= 0; --i) {
593 ir_node * pred = get_Call_param(node, i);
594 if (get_irn_mode(pred) == mode_p) {
595 free_mark(pred, set);
599 /* other nodes: Alle anderen Knoten nehmen wir als Verräter an, bis
600 * jemand das Gegenteil implementiert. */
602 set_irn_link(node, MARK);
603 for (i = get_irn_arity(node) - 1; i >= 0; --i) {
604 ir_node * pred = get_irn_n(node, i);
605 if (get_irn_mode(pred) == mode_p) {
606 free_mark(pred, set);
611 set_irn_link(node, NULL);
615 /* Die Datenstrukturen für sel-Methoden (sel_methods) muß vor dem
616 * Aufruf von "get_free_methods" aufgebaut sein. Die (internen)
617 * SymConst-Operationen müssen in passende Const-Operationen
618 * umgewandelt worden sein, d.h. SymConst-Operationen verweisen immer
619 * auf eine echt externe Methode. */
620 static entity ** get_free_methods(void) {
621 eset * set = eset_create();
623 entity ** arr = NEW_ARR_F(entity *, 0);
625 for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
626 ir_graph * irg = get_irp_irg(i);
627 entity * ent = get_irg_ent(irg);
628 /* insert "external visible" methods. */
629 if (get_entity_visibility(ent) != local) {
630 eset_insert(set, ent);
632 irg_walk_graph(irg, NULL, (irg_walk_func) free_ana_walker, set);
634 /* Hauptprogramm ist auch frei, auch wenn es nicht "external
636 eset_insert(set, get_irg_ent(get_irp_main_irg()));
637 for (ent = eset_first(set); ent; ent = eset_next(set)) {
638 ARR_APP1(entity *, arr, ent);
645 void cgana(int *length, entity ***free_methods) {
646 entity ** free_meths;
650 free_meths = get_free_methods();
652 sel_methods_dispose();
654 /* Convert the flexible array to an array that can be handled
656 *length = ARR_LEN(free_meths);
657 *free_methods = (entity **)malloc(sizeof(entity *) * (*length));
658 for (i = 0; i < (*length); i++) (*free_methods)[i] = free_meths[i];
659 DEL_ARR_F(free_meths);