+ default:
+ eset_insert(methods, unknown_entity); /* free method -> unknown */
+ break;
+ }
+}
+
+/**
+ * Analyse a Call address.
+ *
+ * @param node the node representing the call address
+ * @param methods after call contains the set of all possibly called entities
+ */
+static void callee_ana_node(ir_node *node, eset *methods)
+{
+ int i;
+
+ assert(mode_is_reference(get_irn_mode(node)) || is_Bad(node));
+ /* Beware of recursion */
+ if (get_irn_link(node) == MARK) {
+ /* already visited */
+ return;
+ }
+ set_irn_link(node, MARK);
+
+ switch (get_irn_opcode(node)) {
+ case iro_Const:
+ /* A direct address call. We tread this as an external
+ call and ignore it completely. */
+ eset_insert(methods, unknown_entity); /* free method -> unknown */
+ break;
+
+ case iro_SymConst: {
+ ir_entity *ent = get_SymConst_entity(node);
+ assert(ent && is_method_entity(ent));
+ eset_insert(methods, ent);
+ break;
+ }
+
+ case iro_Sel:
+ /* polymorphic method */
+ for (i = get_Sel_n_methods(node) - 1; i >= 0; --i) {
+ ir_entity *ent = get_Sel_method(node, i);
+ if (ent != NULL) {
+ eset_insert(methods, ent);
+ } else {
+ eset_insert(methods, unknown_entity);
+ }
+ }
+ break;
+
+ case iro_Bad:
+ /* nothing */
+ break;
+
+ case iro_Phi:
+ for (i = get_Phi_n_preds(node) - 1; i >= 0; --i) {
+ callee_ana_node(get_Phi_pred(node, i), methods);
+ }
+ break;
+
+ case iro_Mux:
+ callee_ana_node(get_Mux_false(node), methods);
+ callee_ana_node(get_Mux_true(node), methods);
+ break;
+
+ case iro_Id:
+ callee_ana_node(get_Id_pred(node), methods);
+ break;
+
+ case iro_Proj:
+ callee_ana_proj(get_Proj_pred(node), get_Proj_proj(node), methods);
+ break;
+
+ case iro_Add:
+ case iro_Sub:
+ case iro_Conv:
+ /* extern */
+ eset_insert(methods, unknown_entity); /* free method -> unknown */
+ break;
+
+ default:
+ assert(0 && "invalid opcode or opcode not implemented");
+ break;
+ }
+}
+
+/**
+ * Walker: Analyses every Call node and calculates an array of possible
+ * callees for that call.
+ */
+static void callee_walker(ir_node *call, void *env)
+{
+ (void) env;
+ if (is_Call(call)) {
+ eset *methods = eset_create();
+ ir_entity *ent;
+ ir_entity **arr;
+ int i;
+
+ callee_ana_node(get_Call_ptr(call), methods);
+ arr = NEW_ARR_F(ir_entity *, eset_count(methods));
+ for (i = 0, ent = eset_first(methods); ent; ent = eset_next(methods)) {
+ arr[i] = ent;
+ /* we want the unknown_entity on the zero position for easy tests later */
+ if (ent == unknown_entity) {
+ arr[i] = arr[0];
+ arr[0] = unknown_entity;
+ }
+ ++i;
+ }
+ set_Call_callee_arr(call, ARR_LEN(arr), arr);
+ DEL_ARR_F(arr);
+ eset_destroy(methods);
+ }
+}