Commit c9f09085 authored by Matthias Braun's avatar Matthias Braun
Browse files

fix corner cases when determining called entities/irgs

Most firm analyses behaved incorrectlye when: A called SymConst did not
have a method entity, assumed the code reachable by get_entity_irg is
the code called. These assumptions are wrong when doing crazy casts in C
or when dealing with weak symbols. This is fixed by introducing some
convenience functions: get_Call_callee(), get_entity_linktime_irg()
parent 096c9237
......@@ -373,6 +373,22 @@ FIRM_API void set_SymConst_symbol(ir_node *node, union symconst_symbol sym);
/** @} */
/**
* @addtogroup Call
* @{
*/
/**
* Convenience function: Return method that will be called by a call.
*
* This matches for an address of entity SymConst at the Call ptr input, return
* the referenced entity if it has a method type.
*/
FIRM_API ir_entity *get_Call_callee(const ir_node *call);
/** @} */
/** Returns a human readable string for the ir_builtin_kind. */
FIRM_API const char *get_builtin_kind_name(ir_builtin_kind kind);
......
......@@ -378,11 +378,21 @@ FIRM_API void *get_entity_link(const ir_entity *ent);
FIRM_API void set_entity_link(ir_entity *ent, void *l);
/**
* The entity knows the corresponding irg if the entity is a method.
* This allows to get from a Call to the called irg.
* Return the method graph of a method entity.
* @warning If it is a weak symbol, then this is not necessarily the final code
* bound to the entity. If you are writing an analysis use
* get_entity_linktime_irg()!
*/
FIRM_API ir_graph *get_entity_irg(const ir_entity *ent);
/**
* Return the method graph the method entity points to after linking.
* This is different to get_entity_irg() in case of weak symbols where this
* function returns NULL because the code may be replaced by a non-weak symbol
* after linking.
*/
FIRM_API ir_graph *get_entity_linktime_irg(const ir_entity *ent);
/** A reserved value for "not yet set". */
#define IR_VTABLE_NUM_NOT_SET ((unsigned)(-1))
......
......@@ -51,21 +51,19 @@ static ptr_access_kind analyze_arg(ir_node *arg, ptr_access_kind bits)
switch (get_irn_opcode(succ)) {
case iro_Call: {
ir_node *ptr = get_Call_ptr(succ);
ir_node *ptr = get_Call_ptr(succ);
if (ptr == arg) {
/* Hmm: not sure what this is, most likely a read */
bits |= ptr_access_read;
} else {
ir_entity *meth_ent;
if (is_SymConst_addr_ent(ptr)) {
meth_ent = get_SymConst_entity(ptr);
ir_entity *callee = get_Call_callee(succ);
if (callee != NULL) {
for (int p = get_Call_n_params(succ); p-- > 0; ) {
if (get_Call_param(succ, p) == arg) {
/* an arg can be used more than once ! */
bits |= get_method_param_access(meth_ent, p);
bits |= get_method_param_access(callee, p);
}
}
} else if (is_Sel(ptr) && get_irp_callee_info_state() == irg_callee_info_consistent) {
......@@ -74,7 +72,7 @@ static ptr_access_kind analyze_arg(ir_node *arg, ptr_access_kind bits)
/* simply look into ALL possible callees */
for (int c = cg_get_call_n_callees(succ); c-- > 0; ) {
meth_ent = cg_get_call_callee(succ, c);
ir_entity *meth_ent = cg_get_call_callee(succ, c);
/* unknown_entity is used to signal that we don't know what is called */
if (is_unknown_entity(meth_ent)) {
......
......@@ -171,7 +171,7 @@ static void ana_Call(ir_node *n, void *env)
for (size_t i = 0, n_callees = cg_get_call_n_callees(n); i < n_callees;
++i) {
ir_entity *callee_e = cg_get_call_callee(n, i);
ir_graph *callee = get_entity_irg(callee_e);
ir_graph *callee = get_entity_linktime_irg(callee_e);
if (callee) {
cg_callee_entry buf;
......
......@@ -564,9 +564,11 @@ static void callee_ana_node(ir_node *node, pset *methods)
break;
case iro_SymConst: {
if (!is_SymConst_addr_ent(node))
break;
ir_entity *ent = get_SymConst_entity(node);
assert(ent && is_method_entity(ent));
pset_insert_ptr(methods, ent);
if (is_method_entity(ent))
pset_insert_ptr(methods, ent);
break;
}
......
......@@ -396,14 +396,10 @@ static int is_malloc_Result(const ir_node *node)
node = get_Proj_pred(node);
if (!is_Call(node))
return 0;
node = get_Call_ptr(node);
if (is_SymConst_addr_ent(node)) {
ir_entity *ent = get_SymConst_entity(node);
if (get_entity_additional_properties(ent) & mtp_property_malloc)
return 1;
return 0;
}
ir_entity *callee = get_Call_callee(node);
if (callee != NULL
&& get_entity_additional_properties(callee) & mtp_property_malloc)
return 1;
return 0;
}
......@@ -1260,22 +1256,22 @@ static ir_type *clone_type_and_cache(ir_type *tp)
*/
static void update_calls_to_private(ir_node *call, void *env)
{
(void) env;
if (is_Call(call)) {
ir_node *ptr = get_Call_ptr(call);
if (is_SymConst(ptr)) {
ir_entity *ent = get_SymConst_entity(ptr);
ir_type *ctp = get_Call_type(call);
if ((get_entity_additional_properties(ent) & mtp_property_private)
&& ((get_method_additional_properties(ctp) & mtp_property_private) == 0)) {
ctp = clone_type_and_cache(ctp);
add_method_additional_properties(ctp, mtp_property_private);
set_Call_type(call, ctp);
DB((dbgcall, LEVEL_1, "changed call to private method %+F using cloned type %+F\n", ent, ctp));
}
}
(void)env;
if (!is_Call(call))
return;
ir_entity *callee = get_Call_callee(call);
if (callee == NULL)
return;
ir_type *ctp = get_Call_type(call);
if ((get_entity_additional_properties(callee) & mtp_property_private)
&& ((get_method_additional_properties(ctp) & mtp_property_private) == 0)) {
ctp = clone_type_and_cache(ctp);
add_method_additional_properties(ctp, mtp_property_private);
set_Call_type(call, ctp);
DB((dbgcall, LEVEL_1,
"changed call to private method %+F using cloned type %+F\n",
callee, ctp));
}
}
......
......@@ -736,6 +736,20 @@ const char *get_builtin_kind_name(ir_builtin_kind kind)
#undef X
}
ir_entity *get_Call_callee(const ir_node *node)
{
ir_node *ptr = get_Call_ptr(node);
if (!is_SymConst_addr_ent(ptr))
return NULL;
ir_entity *entity = get_SymConst_entity(ptr);
/* some (corner case/pointless) graphs can have non-method entities as
* call pointers */
ir_type *type = get_entity_type(entity);
if (!is_Method_type(type))
return NULL;
return entity;
}
int (is_binop)(const ir_node *node)
{
return is_binop_(node);
......
......@@ -287,11 +287,9 @@ static void check_ptr(ir_node *ptr, wlk_env *env)
*/
static bool is_self_recursive_Call(const ir_node *call)
{
const ir_node *callee = get_Call_ptr(call);
if (is_SymConst_addr_ent(callee)) {
const ir_entity *ent = get_SymConst_entity(callee);
const ir_graph *irg = get_entity_irg(ent);
const ir_entity *callee = get_Call_callee(call);
if (callee != NULL) {
const ir_graph *irg = get_entity_linktime_irg(callee);
if (irg == get_irn_irg(call))
return true;
}
......
......@@ -154,29 +154,25 @@ static int can_escape(ir_node *n)
return 1;
case iro_Call: { /* most complicated case */
ir_node *ptr = get_Call_ptr(succ);
ir_entity *ent;
if (is_SymConst_addr_ent(ptr)) {
size_t j;
ent = get_SymConst_entity(ptr);
ir_entity *callee = get_Call_callee(succ);
if (callee != NULL) {
/* we know the called entity */
for (j = get_Call_n_params(succ); j > 0;) {
for (size_t j = get_Call_n_params(succ); j > 0;) {
if (get_Call_param(succ, --j) == n) {
/* n is the j'th param of the call */
if (get_method_param_access(ent, j) & ptr_access_store)
if (get_method_param_access(callee, j) & ptr_access_store)
/* n is store in ent */
return 1;
}
}
} else if (is_Sel(ptr)) {
} else if (is_Sel(get_Call_ptr(succ))) {
size_t k;
/* go through all possible callees */
for (k = cg_get_call_n_callees(succ); k > 0;) {
size_t j;
ent = cg_get_call_callee(succ, --k);
ir_entity *ent = cg_get_call_callee(succ, --k);
if (is_unknown_entity(ent)) {
/* we don't know what will be called, a possible escape */
......
......@@ -71,12 +71,10 @@ static void collect_const_and_pure_calls(ir_node *node, void *env)
if (is_Call(node)) {
ir_type *type = get_Call_type(node);
unsigned prop = get_method_additional_properties(type);
ir_node *ptr = get_Call_ptr(node);
if (is_SymConst_addr_ent(ptr)) {
ir_entity *ent = get_SymConst_entity(ptr);
prop |= get_entity_additional_properties(ent);
}
unsigned prop = get_method_additional_properties(type);
ir_entity *callee = get_Call_callee(node);
if (callee != NULL)
prop |= get_entity_additional_properties(callee);
/* stop on aggregates (see comment in check_const_or_pure_function()) */
if ((prop & mtp_property_const) != 0
&& method_type_contains_aggregate(type)) {
......@@ -201,18 +199,14 @@ static void collect_nothrow_calls(ir_node *node, void *env)
env_t *ctx = (env_t*)env;
if (is_Call(node)) {
ir_node *call = node;
ir_node *ptr = get_Call_ptr(call);
if (!is_SymConst_addr_ent(ptr))
ir_entity *callee = get_Call_callee(node);
if (callee == NULL)
return;
ir_entity *ent = get_SymConst_entity(ptr);
unsigned prop = get_entity_additional_properties(ent);
unsigned prop = get_entity_additional_properties(callee);
if ((prop & mtp_property_nothrow) == 0)
return;
/* ok, if we get here we found a call to a nothrow function */
ARR_APP1(ir_node*, ctx->nothrow_call_list, call);
ARR_APP1(ir_node*, ctx->nothrow_call_list, node);
} else if (is_Proj(node)) {
/*
* Collect all memory and exception Proj's from
......@@ -357,20 +351,20 @@ static mtp_additional_properties follow_mem_(ir_node *node)
case iro_Call: {
/* A call is only tolerable if its either constant or pure. */
ir_node *ptr = get_Call_ptr(node);
if (!is_SymConst_addr_ent(ptr))
ir_entity *callee = get_Call_callee(node);
if (callee == NULL)
return mtp_no_property;
ir_entity *ent = get_SymConst_entity(ptr);
ir_graph *irg = get_entity_irg(ent);
mtp_additional_properties m;
ir_graph *irg = get_entity_linktime_irg(callee);
if (irg == NULL) {
m = get_entity_additional_properties(ent) & (mtp_property_const|mtp_property_pure);
mtp_additional_properties m
= get_entity_additional_properties(callee)
& (mtp_property_const|mtp_property_pure);
mode = max_property(mode, m);
} else {
/* we have a graph, analyze it. */
m = check_const_or_pure_function(irg, false);
mtp_additional_properties m
= check_const_or_pure_function(irg, false);
mode = max_property(mode, m);
}
node = get_Call_mem(node);
......@@ -589,8 +583,6 @@ static mtp_additional_properties update_property(mtp_additional_properties orig_
*/
static bool is_stored(const ir_node *n)
{
const ir_node *ptr;
foreach_out_edge(n, edge) {
const ir_node *succ = get_edge_src_irn(edge);
......@@ -610,26 +602,23 @@ static bool is_stored(const ir_node *n)
if (is_stored(succ))
return true;
break;
case iro_Call:
ptr = get_Call_ptr(succ);
if (is_SymConst_addr_ent(ptr)) {
ir_entity *ent = get_SymConst_entity(ptr);
/* we know the called entity */
for (size_t i = get_Call_n_params(succ); i > 0;) {
if (get_Call_param(succ, --i) == n) {
/* n is the i'th param of the call */
if (get_method_param_access(ent, i) & ptr_access_store) {
/* n is store in ent */
return true;
}
case iro_Call: {
ir_entity *callee = get_Call_callee(succ);
/* unknown call address */
if (callee == NULL)
return true;
/* we know the called entity */
for (size_t i = get_Call_n_params(succ); i > 0;) {
if (get_Call_param(succ, --i) == n) {
/* n is the i'th param of the call */
if (get_method_param_access(callee, i) & ptr_access_store) {
/* n is store in ent */
return true;
}
}
} else {
/* unknown call address */
return true;
}
break;
}
default:
/* bad, potential alias */
return true;
......@@ -715,23 +704,20 @@ static mtp_additional_properties check_nothrow_or_malloc(ir_graph *irg, bool top
if (is_malloc_call_result(res)) {
/* ok, this is a malloc */
} else if (is_Call(res)) {
ir_node *ptr = get_Call_ptr(res);
if (is_SymConst_addr_ent(ptr)) {
ir_entity *callee = get_Call_callee(res);
if (callee != NULL) {
/* a direct call */
ir_entity *ent = get_SymConst_entity(ptr);
ir_graph *callee = get_entity_irg(ent);
if (callee == irg) {
ir_graph *callee_irg = get_entity_irg(callee);
if (callee_irg == irg) {
/* A self-recursive call. The property did not
* depend on this call. */
} else if (callee != NULL) {
} else if (callee_irg != NULL) {
mtp_additional_properties prop
= check_nothrow_or_malloc(callee, false);
= check_nothrow_or_malloc(callee_irg, false);
curr_prop = update_property(curr_prop, prop);
} else {
mtp_additional_properties prop
= get_entity_additional_properties(ent);
= get_entity_additional_properties(callee);
curr_prop = update_property(curr_prop, prop);
}
} else {
......@@ -749,25 +735,23 @@ static mtp_additional_properties check_nothrow_or_malloc(ir_graph *irg, bool top
pred = skip_Proj(pred);
if (is_Call(pred)) {
ir_node *ptr = get_Call_ptr(pred);
if (is_SymConst_addr_ent(ptr)) {
ir_entity *callee = get_Call_callee(pred);
if (callee != NULL) {
/* a direct call */
ir_entity *ent = get_SymConst_entity(ptr);
ir_graph *callee = get_entity_irg(ent);
if (callee == irg) {
ir_graph *called_irg = get_entity_linktime_irg(callee);
if (called_irg == irg) {
/* A self-recursive call. The property did not depend
* on this call. */
} else if (callee != NULL) {
} else if (called_irg != NULL) {
/* Note: we check here for nothrow only, so do NOT
* reset the malloc property */
mtp_additional_properties prop
= check_nothrow_or_malloc(callee, false)
= check_nothrow_or_malloc(called_irg, false)
| mtp_property_malloc;
curr_prop = update_property(curr_prop, prop);
} else {
if ((get_entity_additional_properties(ent) & mtp_property_nothrow) == 0)
if ((get_entity_additional_properties(callee)
& mtp_property_nothrow) == 0)
curr_prop &= ~mtp_property_nothrow;
}
} else {
......
......@@ -417,12 +417,9 @@ static unsigned is_Call_pure(ir_node *call)
/* check first the call type */
if ((prop & (mtp_property_const|mtp_property_pure)) == 0) {
/* try the called entity */
ir_node *ptr = get_Call_ptr(call);
if (is_SymConst_addr_ent(ptr)) {
ir_entity *ent = get_SymConst_entity(ptr);
prop = get_entity_additional_properties(ent);
ir_entity *callee = get_Call_callee(call);
if (callee != NULL) {
prop = get_entity_additional_properties(callee);
}
}
return (prop & (mtp_property_const|mtp_property_pure)) != 0;
......
......@@ -673,29 +673,6 @@ typedef struct call_entry {
unsigned all_const:1; /**< Set if this call has only constant parameters. */
} call_entry;
/**
* Returns the irg called from a Call node. If the irg is not
* known, NULL is returned.
*
* @param call the call node
*/
static ir_graph *get_call_called_irg(ir_node *call)
{
ir_node *addr;
addr = get_Call_ptr(call);
if (is_SymConst_addr_ent(addr)) {
ir_entity *ent = get_SymConst_entity(addr);
/* we don't know which function gets finally bound to a weak symbol */
if (get_entity_linkage(ent) & IR_LINKAGE_WEAK)
return NULL;
return get_entity_irg(ent);
}
return NULL;
}
/**
* Environment for inlining irgs.
*/
......@@ -747,7 +724,6 @@ static void collect_calls2(ir_node *call, void *ctx)
wenv_t *env = (wenv_t*)ctx;
inline_irg_env *x = env->x;
unsigned code = get_irn_opcode(call);
ir_graph *callee;
call_entry *entry;
/* count meaningful nodes in irg */
......@@ -766,7 +742,10 @@ static void collect_calls2(ir_node *call, void *ctx)
++x->n_call_nodes;
++x->n_call_nodes_orig;
callee = get_call_called_irg(call);
ir_entity *callee_ent = get_Call_callee(call);
if (callee_ent == NULL)
return;
ir_graph *callee = get_entity_linktime_irg(callee_ent);
if (callee != NULL) {
if (! env->ignore_callers) {
inline_irg_env *callee_env = (inline_irg_env*)get_irg_link(callee);
......
......@@ -499,13 +499,9 @@ static unsigned get_Call_memory_properties(ir_node *call)
/* check first the call type */
if ((prop & (mtp_property_const|mtp_property_pure)) == 0) {
/* try the called entity */
ir_node *ptr = get_Call_ptr(call);
if (is_SymConst_addr_ent(ptr)) {
ir_entity *ent = get_SymConst_entity(ptr);
prop = get_entity_additional_properties(ent);
}
ir_entity *callee = get_Call_callee(call);
if (callee != NULL)
prop = get_entity_additional_properties(callee);
}
return prop & (mtp_property_const|mtp_property_pure);
}
......
......@@ -156,28 +156,18 @@ static void process_call(ir_node *call, ir_entity *callee, q_set *hmap)
static void collect_irg_calls(ir_node *call, void *env)
{
q_set *hmap = (q_set*)env;
ir_node *call_ptr;
ir_entity *callee;
/* We collect just "Call" nodes */
if (is_Call(call)) {
call_ptr = get_Call_ptr(call);
if (! is_SymConst_addr_ent(call_ptr))
return;
callee = get_SymConst_entity(call_ptr);
/* we don't know which function gets finally bound to a weak symbol */
if (get_entity_linkage(callee) & IR_LINKAGE_WEAK)
return;
/* we can only clone calls to existing entities */
if (get_entity_irg(callee) == NULL)
return;
if (!is_Call(call))
return;
ir_entity *callee = get_Call_callee(call);
if (callee == NULL)
return;
ir_graph *callee_irg = get_entity_linktime_irg(callee);
if (callee_irg == NULL)
return;
process_call(call, callee, hmap);
}
process_call(call, callee, hmap);
}
/**
......@@ -319,7 +309,7 @@ static void create_clone_proc_irg(ir_entity *ent, const quadruple_t *q)
ir_graph *method_irg, *clone_irg;
ir_node *arg, *const_arg;
method_irg = get_entity_irg(ent);
method_irg = get_entity_linktime_irg(ent);
/* We create the skeleton of the clone irg.*/
clone_irg = new_ir_graph(ent, 0);
......@@ -502,15 +492,13 @@ restart:
len = ARR_LEN(entry->q.calls);
for (i = 0; i < len; ++i) {
ir_node *ptr, *call = entry->q.calls[i];
ir_node *call = entry->q.calls[i];
/* might be exchanged, so skip Id nodes here. */
call = skip_Id(call);
/* we know, that a SymConst is here */
ptr = get_Call_ptr(call);
ir_entity *const callee = get_SymConst_entity(ptr);
ir_entity *const callee = get_Call_callee(call);
if (callee != entry->q.ent) {
/*
* This call is already changed because of a previous
......
......@@ -574,7 +574,7 @@ void opt_tail_rec_irg(ir_graph *irg)
for (i = get_Block_n_cfgpreds(end_block) - 1; i >= 0; --i) {
ir_node *ret = get_Block_cfgpred(end_block, i);
ir_node *call, *call_ptr;
ir_node *call;
int j;
ir_node **ress;
......@@ -592,13 +592,8 @@ void opt_tail_rec_irg(ir_graph *irg)
continue;
/* check if it's a recursive call */
call_ptr = get_Call_ptr(call);
if (! is_SymConst_addr_ent(call_ptr))
continue;
ent = get_SymConst_entity(call_ptr);
if (!ent || get_entity_irg(ent) != irg)
ir_entity *callee = get_Call_callee(call);
if (callee == NULL || get_entity_linktime_irg(callee) != irg)
continue;
/*
......
......@@ -763,6 +763,11 @@ ir_graph *(get_entity_irg)(const ir_entity *ent)
return _get_entity_irg(ent);
}
ir_graph *(get_entity_linktime_irg)(const ir_entity *ent)
{
return _get_entity_linktime_irg(ent);
}
void set_entity_irg(ir_entity *ent, ir_graph *irg)
{
assert(is_method_entity(ent));
......
......@@ -47,6 +47,7 @@
#define get_entity_link(ent) _get_entity_link(ent)
#define set_entity_link(ent, l) _set_entity_link(ent, l)
#define get_entity_irg(ent)