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 && get_entity_owner(method) == class) {
43 for (i = get_entity_n_overwrittenby(method) - 1; i >= 0; --i) {
44 entity * e = get_entity_overwrittenby(method, i);
45 if (get_entity_peculiarity(e) != description && get_entity_owner(e) == class) {
49 for (i = get_class_n_supertype(class) - 1; i >= 0; --i) {
50 entity * e = get_implementation(get_class_supertype(class, i), method);
55 assert(0 && "implemenation not found");
59 /* Alle Methoden bestimmen, die die übergebene Methode überschreiben
60 * (und implementieren). In der zurückgegebenen Reihung kommt jede
61 * Methode nur einmal vor. Der Wert 'NULL' steht für unbekannte
62 * (externe) Methoden. Die zurückgegebene Reihung muß vom Aufrufer
63 * wieder freigegeben werden (siehe "DEL_ARR_F"). Gibt es überhaupt
64 * keine Methoden, die die "method" überschreiben, so gibt die Methode
66 static entity ** get_impl_methods(entity * method) {
67 eset * set = eset_create();
72 if (get_entity_peculiarity(method) == existent) {
73 if (get_entity_visibility(method) == external_allocated) {
74 assert(get_entity_irg(method) == NULL);
77 assert(get_entity_irg(method) != NULL);
78 eset_insert(set, method);
82 for (i = get_entity_n_overwrittenby(method) - 1; i >= 0; --i) {
83 entity * ent = get_entity_overwrittenby(method, i);
84 if (get_entity_peculiarity(ent) == existent) {
85 if (get_entity_visibility(ent) == external_allocated) {
86 assert(get_entity_irg(ent) == NULL);
89 assert(get_entity_irg(ent) != NULL);
90 if (!eset_contains(set, ent)) {
91 eset_insert(set, ent);
97 if (size == 0 && !open) {
98 /* keine implementierte überschriebene Methode */
102 arr = NEW_ARR_F(entity *, size + 1);
104 for (ent = eset_first(set); size > 0; ent = eset_next(set), --size) arr[size] = ent;
107 arr = NEW_ARR_F(entity *, size);
108 for (size -= 1, ent = eset_first(set); size >= 0; ent = eset_next(set), --size) arr[size] = ent;
115 static void sel_methods_walker(ir_node * node, pmap * ldname_map) {
116 if (get_irn_op(node) == op_SymConst) {
117 /* Wenn möglich SymConst-Operation durch Const-Operation
119 if (get_SymConst_kind(node) == linkage_ptr_info) {
120 pmap_entry * entry = pmap_find(ldname_map, (void *) get_SymConst_ptrinfo(node));
122 entity * ent = entry->value;
123 if (get_entity_visibility(ent) != external_allocated) {
124 assert(get_entity_irg(ent));
125 set_irg_current_block(current_ir_graph, get_nodes_Block(node));
126 exchange(node, new_Const(mode_p, tarval_p_from_entity(ent)));
130 } else if (get_irn_op(node) == op_Sel && is_method_type(get_entity_type(get_Sel_entity(node)))) {
131 entity * ent = get_Sel_entity(node);
132 if (get_irn_op(skip_Proj(get_Sel_ptr(node))) == op_Alloc) {
133 ent = get_implementation(get_Alloc_type(skip_Proj(get_Sel_ptr(node))), ent);
134 if (get_entity_visibility(ent) == external_allocated) {
135 exchange(node, new_SymConst((type_or_id_p) get_entity_ld_ident(ent), linkage_ptr_info));
137 exchange(node, new_Const(mode_p, tarval_p_from_entity(ent)));
140 if (!eset_contains(entities, ent)) {
141 /* Entity noch nicht behandelt. Alle (intern oder extern)
142 * implementierten Methoden suchen, die diese Entity
143 * überschreiben. Die Menge an entity.link speichern. */
144 set_entity_link(ent, get_impl_methods(ent));
145 eset_insert(entities, ent);
147 if (get_entity_link(ent) == NULL) {
148 /* Die Sel-Operation kann nie einen Zeiger auf eine aufrufbare
149 * Methode zurückgeben. Damit ist sie insbesondere nicht
150 * ausführbar und nicht erreichbar. */
151 exchange(node, new_Bad());
153 entity ** arr = get_entity_link(ent);
154 if (ARR_LEN(arr) == 1 && arr[0] != NULL) {
155 /* Die Sel-Operation kann immer nur einen Wert auf eine
156 * interne Methode zurückgeben. Wir können daher die
157 * Sel-Operation durch eine Const- bzw. SymConst-Operation
159 if (get_entity_visibility(arr[0]) == external_allocated) {
160 exchange(node, new_SymConst((type_or_id_p) get_entity_ld_ident(arr[0]), linkage_ptr_info));
162 exchange(node, new_Const(mode_p, tarval_p_from_entity(arr[0])));
171 /* Datenstruktur initialisieren. Zusätzlich werden alle
172 * SymConst-Operationen, die auf interne Methode verweisen, durch
173 * Const-Operationen ersetzt. */
174 static void sel_methods_init(void) {
176 pmap * ldname_map = pmap_create();
177 assert(entities == NULL);
178 entities = eset_create();
179 for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
180 entity * ent = get_irg_ent(get_irp_irg(i));
181 /* Nur extern sichtbare Methode können überhaupt mit SymConst
182 * aufgerufen werden. */
183 if (get_entity_visibility(ent) != local) {
184 pmap_insert(ldname_map, (void *) get_entity_ld_ident(ent), ent);
187 all_irg_walk((irg_walk_func) sel_methods_walker, NULL, ldname_map);
188 pmap_destroy(ldname_map);
192 /* Datenstruktur freigeben. */
193 static void sel_methods_dispose(void) {
196 for (ent = eset_first(entities); ent; ent = eset_next(entities)) {
197 entity ** arr = get_entity_link(ent);
201 set_entity_link(ent, NULL);
203 eset_destroy(entities);
208 /* Gibt die Menge aller Methoden zurück, die an diesem Sel-Knoten
209 * zurückgegeben werden können. Die Liste enthält keine doppelten
211 static entity ** get_Sel_arr(ir_node * sel) {
212 static entity ** NULL_ARRAY = NULL;
215 assert(sel && get_irn_op(sel) == op_Sel);
216 ent = get_Sel_entity(sel);
217 assert(is_method_type(get_entity_type(ent))); /* what else? */
218 arr = get_entity_link(ent);
222 /* "NULL" zeigt an, dass keine Implementierung existiert. Dies
223 * kann für polymorphe (abstrakte) Methoden passieren. */
225 NULL_ARRAY = NEW_ARR_F(entity *, 0);
232 static int get_Sel_n_methods(ir_node * sel) {
233 return ARR_LEN(get_Sel_arr(sel));
237 static entity * get_Sel_method(ir_node * sel, int pos) {
238 entity ** arr = get_Sel_arr(sel);
239 assert(pos >= 0 && pos < ARR_LEN(arr));
245 /* --- callee analysis ------------------------------------------------------ */
248 static void callee_ana_node(ir_node * node, eset * methods);
251 static void callee_ana_proj(ir_node * node, long n, eset * methods) {
252 assert(get_irn_mode(node) == mode_T);
253 if (get_irn_link(node) == MARK) {
254 /* already visited */
257 set_irn_link(node, MARK);
259 switch (get_irn_opcode(node)) {
261 /* proj_proj: in einem "sinnvollen" Graphen kommt jetzt ein
262 * op_Tuple oder ein Knoten, der eine "freie Methode"
264 ir_node * pred = get_Proj_pred(node);
265 if (get_irn_link(pred) != MARK) {
266 if (get_irn_op(pred) == op_Tuple) {
267 callee_ana_proj(get_Tuple_pred(pred, get_Proj_proj(node)), n, methods);
269 eset_insert(methods, MARK); /* free method -> unknown */
276 callee_ana_node(get_Tuple_pred(node, n), methods);
280 callee_ana_proj(get_Id_pred(node), n, methods);
284 eset_insert(methods, MARK); /* free method -> unknown */
288 set_irn_link(node, NULL);
292 static void callee_ana_node(ir_node * node, eset * methods) {
295 assert(get_irn_mode(node) == mode_p);
296 /* rekursion verhindern */
297 if (get_irn_link(node) == MARK) {
298 /* already visited */
301 set_irn_link(node, MARK);
303 switch (get_irn_opcode(node)) {
305 /* externe Methode (wegen fix_symconst!) */
306 eset_insert(methods, MARK); /* free method -> unknown */
310 /* interne Methode */
311 entity * ent = get_Const_tarval(node)->u.p.ent;
312 assert(ent && is_method_type(get_entity_type(ent)));
313 if (get_entity_visibility(ent) != external_allocated) {
314 assert(get_entity_irg(ent));
315 eset_insert(methods, ent);
317 eset_insert(methods, MARK); /* free method -> unknown */
323 /* polymorphe Methode */
324 for (i = get_Sel_n_methods(node) - 1; i >= 0; --i) {
325 entity * ent = get_Sel_method(node, i);
327 eset_insert(methods, ent);
329 eset_insert(methods, MARK);
338 case iro_Phi: /* Vereinigung */
339 for (i = get_Phi_n_preds(node) - 1; i >= 0; --i) {
340 callee_ana_node(get_Phi_pred(node, i), methods);
345 callee_ana_node(get_Id_pred(node), methods);
349 callee_ana_proj(get_Proj_pred(node), get_Proj_proj(node), methods);
356 eset_insert(methods, MARK); /* free method -> unknown */
360 assert(0 && "invalid opcode or opcode not implemented");
364 set_irn_link(node, NULL);
368 static void callee_walker(ir_node * call, void * env) {
369 if (get_irn_op(call) == op_Call) {
370 eset * methods = eset_create();
372 entity ** arr = NEW_ARR_F(entity *, 0);
373 callee_ana_node(skip_nop(get_Call_ptr(call)), methods);
374 if (eset_contains(methods, MARK)) { /* unknown method */
375 ARR_APP1(entity *, arr, NULL);
377 for (ent = eset_first(methods); ent; ent = eset_next(methods)) {
379 ARR_APP1(entity *, arr, ent);
382 if (ARR_LEN(arr) == 0) {
383 /* Kann vorkommen, wenn der Vorgänger beispielsweise eine
384 * Sel-Operation war, die keine Methoden zurückgeben
385 * konnte. Wir ersetzen die Call-Operation ebenfalls durch
386 * eine Bad-Operation. Die Verlinkung muss wiederhergestellt
388 exchange(call, new_Bad());
390 set_Call_callee_arr(call, ARR_LEN(arr), arr);
393 eset_destroy(methods);
398 /* Bestimmt für jede Call-Operation die Menge der aufrufbaren Methode
399 * und speichert das Ergebnis in der Call-Operation. (siehe
400 * "set_Call_callee"). "sel_methods" wird für den Aufbau benötigt und
401 * muss bereits aufgebaut sein. */
402 static void callee_ana(void) {
404 /* Alle Graphen analysieren. */
405 for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
406 irg_walk_graph(get_irp_irg(i), callee_walker, NULL, NULL);
412 /* --- free method analysis ------------------------------------------------- */
415 static void free_mark(ir_node * node, eset * set);
417 static void free_mark_proj(ir_node * node, long n, eset * set) {
418 assert(get_irn_mode(node) == mode_T);
419 if (get_irn_link(node) == MARK) {
420 /* already visited */
423 set_irn_link(node, MARK);
424 switch (get_irn_opcode(node)) {
426 /* proj_proj: in einem "sinnvollen" Graphen kommt jetzt ein
427 * op_Tuple oder ein Knoten, der in "free_ana_walker" behandelt
429 ir_node * pred = get_Proj_pred(node);
430 if (get_irn_link(pred) != MARK && get_irn_op(pred) == op_Tuple) {
431 free_mark_proj(get_Tuple_pred(pred, get_Proj_proj(node)), n, set);
433 /* nothing: da in "free_ana_walker" behandelt. */
439 free_mark(get_Tuple_pred(node, n), set);
443 free_mark_proj(get_Id_pred(node), n, set);
449 /* nothing: Die Operationen werden in "free_ana_walker" selbst
454 assert(0 && "unexpected opcode or opcode not implemented");
457 set_irn_link(node, NULL);
461 static void free_mark(ir_node * node, eset * set) {
463 assert(get_irn_mode(node) == mode_p);
464 if (get_irn_link(node) == MARK) {
465 return; /* already visited */
467 set_irn_link(node, MARK);
468 switch (get_irn_opcode(node)) {
470 entity * ent = get_Sel_entity(node);
471 if (is_method_type(get_entity_type(ent))) {
472 for (i = get_Sel_n_methods(node) - 1; i >= 0; --i) {
473 eset_insert(set, get_Sel_method(node, i));
479 /* nothing: SymConst points to extern method */
482 tarval * val = get_Const_tarval(node);
483 entity * ent = val->u.p.ent;
484 if (ent != NULL && is_method_type(get_entity_type(ent))) {
485 eset_insert(set, ent);
490 for (i = get_Phi_n_preds(node) - 1; i >= 0; --i) {
491 free_mark(get_Phi_pred(node, i), set);
495 free_mark(get_Id_pred(node), set);
498 free_mark_proj(get_Proj_pred(node), get_Proj_proj(node), set);
501 /* nothing: Wird unten behandelt! */
504 set_irn_link(node, NULL);
508 static void free_ana_walker(ir_node * node, eset * set) {
510 if (get_irn_link(node) == MARK) {
511 /* bereits in einem Zyklus besucht. */
514 switch (get_irn_opcode(node)) {
525 /* Sonderbehandlung, da der Funktionszeigereingang natürlich kein
528 set_irn_link(node, MARK);
529 for (i = get_Call_arity(node) - 1; i >= 0; --i) {
530 ir_node * pred = get_Call_param(node, i);
531 if (get_irn_mode(pred) == mode_p) {
532 free_mark(pred, set);
536 /* other nodes: Alle anderen Knoten nehmen wir als Verräter an, bis
537 * jemand das Gegenteil implementiert. */
539 set_irn_link(node, MARK);
540 for (i = get_irn_arity(node) - 1; i >= 0; --i) {
541 ir_node * pred = get_irn_n(node, i);
542 if (get_irn_mode(pred) == mode_p) {
543 free_mark(pred, set);
548 set_irn_link(node, NULL);
552 /* Die Datenstrukturen für sel-Methoden (sel_methods) muß vor dem
553 * Aufruf von "get_free_methods" aufgebaut sein. Die (internen)
554 * SymConst-Operationen müssen in passende Const-Operationen
555 * umgewandelt worden sein, d.h. SymConst-Operationen verweisen immer
556 * auf eine echt externe Methode. */
557 static entity ** get_free_methods(void) {
558 eset * set = eset_create();
560 entity ** arr = NEW_ARR_F(entity *, 0);
562 for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
563 ir_graph * irg = get_irp_irg(i);
564 entity * ent = get_irg_ent(irg);
565 /* insert "external visible" methods. */
566 if (get_entity_visibility(ent) != local) {
567 eset_insert(set, ent);
569 irg_walk_graph(irg, NULL, (irg_walk_func) free_ana_walker, set);
571 /* Hauptprogramm ist auch frei, auch wenn es nicht "external
573 eset_insert(set, get_irg_ent(get_irp_main_irg()));
574 for (ent = eset_first(set); ent; ent = eset_next(set)) {
575 ARR_APP1(entity *, arr, ent);
582 void cgana(int *length, entity ***free_methods) {
583 entity ** free_meths;
587 free_meths = get_free_methods();
589 sel_methods_dispose();
591 /* Convert the flexible array to an array that can be handled
593 *length = ARR_LEN(free_meths);
594 *free_methods = (entity **)malloc(sizeof(entity *) * (*length));
595 for (i = 0; i < (*length); i++) (*free_methods)[i] = free_meths[i];
596 DEL_ARR_F(free_meths);