Commit 6f8091fc by Jan Hubicka Committed by Jan Hubicka

ipa-utils.h (ipa_polymorphic_call_context): Turn into class; add ctors.


	* ipa-utils.h (ipa_polymorphic_call_context): Turn into class; add ctors.
	(possible_polymorphic_call_targets, dump_possible_polymorphic_call_targets,
	possible_polymorphic_call_target_p, possible_polymorphic_call_target_p): Simplify.
	(get_dynamic_type): Remove.
	* ipa-devirt.c (ipa_dummy_polymorphic_call_context): Remove.
	(clear_speculation): Bring to ipa-deivrt.h
	(get_class_context): Rename to ...
	(ipa_polymorphic_call_context::restrict_to_inner_class): ... this one.
	(contains_type_p): Update.
	(get_dynamic_type): Rename to ...
	ipa_polymorphic_call_context::get_dynamic_type(): ... this one.
	(possible_polymorphic_call_targets): UPdate.
	* tree-ssa-pre.c (eliminate_dom_walker::before_dom_children): Update.
	* ipa-prop.c (ipa_analyze_call_uses): Update.

From-SVN: r215418
parent 8e1ba78f
2014-09-19 Jan Hubicka <hubicka@ucw.cz> 2014-09-19 Jan Hubicka <hubicka@ucw.cz>
* ipa-utils.h (ipa_polymorphic_call_context): Turn into class; add ctors.
(possible_polymorphic_call_targets, dump_possible_polymorphic_call_targets,
possible_polymorphic_call_target_p, possible_polymorphic_call_target_p): Simplify.
(get_dynamic_type): Remove.
* ipa-devirt.c (ipa_dummy_polymorphic_call_context): Remove.
(clear_speculation): Bring to ipa-deivrt.h
(get_class_context): Rename to ...
(ipa_polymorphic_call_context::restrict_to_inner_class): ... this one.
(contains_type_p): Update.
(get_dynamic_type): Rename to ...
ipa_polymorphic_call_context::get_dynamic_type(): ... this one.
(possible_polymorphic_call_targets): UPdate.
* tree-ssa-pre.c (eliminate_dom_walker::before_dom_children): Update.
* ipa-prop.c (ipa_analyze_call_uses): Update.
2014-09-19 Jan Hubicka <hubicka@ucw.cz>
* ipa-visibility.c (varpool_node::externally_visible_p): Do not * ipa-visibility.c (varpool_node::externally_visible_p): Do not
privatize dynamic TLS variables. privatize dynamic TLS variables.
...@@ -884,21 +884,15 @@ cgraph_node::create_indirect_edge (gimple call_stmt, int ecf_flags, ...@@ -884,21 +884,15 @@ cgraph_node::create_indirect_edge (gimple call_stmt, int ecf_flags,
&& (target = gimple_call_fn (call_stmt)) && (target = gimple_call_fn (call_stmt))
&& virtual_method_call_p (target)) && virtual_method_call_p (target))
{ {
tree otr_type; ipa_polymorphic_call_context context (decl, target, call_stmt);
HOST_WIDE_INT otr_token;
ipa_polymorphic_call_context context;
get_polymorphic_call_info (decl,
target,
&otr_type, &otr_token,
&context, call_stmt);
/* Only record types can have virtual calls. */ /* Only record types can have virtual calls. */
gcc_assert (TREE_CODE (otr_type) == RECORD_TYPE);
edge->indirect_info->polymorphic = true; edge->indirect_info->polymorphic = true;
edge->indirect_info->param_index = -1; edge->indirect_info->param_index = -1;
edge->indirect_info->otr_token = otr_token; edge->indirect_info->otr_token
edge->indirect_info->otr_type = otr_type; = tree_to_uhwi (OBJ_TYPE_REF_TOKEN (target));
edge->indirect_info->otr_type = obj_type_ref_class (target);
gcc_assert (TREE_CODE (edge->indirect_info->otr_type) == RECORD_TYPE);
edge->indirect_info->outer_type = context.outer_type; edge->indirect_info->outer_type = context.outer_type;
edge->indirect_info->speculative_outer_type edge->indirect_info->speculative_outer_type
= context.speculative_outer_type; = context.speculative_outer_type;
......
...@@ -2563,8 +2563,8 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace) ...@@ -2563,8 +2563,8 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
{ {
if (dump_file && virtual_method_call_p (callee) if (dump_file && virtual_method_call_p (callee)
&& !possible_polymorphic_call_target_p && !possible_polymorphic_call_target_p
(callee, cgraph_node::get (gimple_call_addr_fndecl (callee, stmt, cgraph_node::get (gimple_call_addr_fndecl
(OBJ_TYPE_REF_EXPR (callee))))) (OBJ_TYPE_REF_EXPR (callee)))))
{ {
fprintf (dump_file, fprintf (dump_file,
"Type inheritance inconsistent devirtualization of "); "Type inheritance inconsistent devirtualization of ");
......
...@@ -1618,14 +1618,11 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, ...@@ -1618,14 +1618,11 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
if (TREE_CODE (t) != TREE_BINFO) if (TREE_CODE (t) != TREE_BINFO)
{ {
ipa_polymorphic_call_context context; ipa_polymorphic_call_context context (t, ie->indirect_info->otr_type,
anc_offset);
vec <cgraph_node *>targets; vec <cgraph_node *>targets;
bool final; bool final;
if (!get_polymorphic_call_info_from_invariant
(&context, t, ie->indirect_info->otr_type,
anc_offset))
return NULL_TREE;
targets = possible_polymorphic_call_targets targets = possible_polymorphic_call_targets
(ie->indirect_info->otr_type, (ie->indirect_info->otr_type,
ie->indirect_info->otr_token, ie->indirect_info->otr_token,
......
...@@ -2400,41 +2400,43 @@ decl_maybe_in_construction_p (tree base, tree outer_type, ...@@ -2400,41 +2400,43 @@ decl_maybe_in_construction_p (tree base, tree outer_type,
} }
/* Proudce polymorphic call context for call method of instance /* Proudce polymorphic call context for call method of instance
that is located within BASE (that is assumed to be a decl) at OFFSET. */ that is located within BASE (that is assumed to be a decl) at offset OFF. */
static void void
get_polymorphic_call_info_for_decl (ipa_polymorphic_call_context *context, ipa_polymorphic_call_context::set_by_decl (tree base, HOST_WIDE_INT off)
tree base, HOST_WIDE_INT offset)
{ {
gcc_assert (DECL_P (base)); gcc_assert (DECL_P (base));
context->outer_type = TYPE_MAIN_VARIANT (TREE_TYPE (base)); outer_type = TYPE_MAIN_VARIANT (TREE_TYPE (base));
context->offset = offset; offset = off;
context->speculative_outer_type = NULL; clear_speculation ();
context->speculative_offset = 0;
context->speculative_maybe_derived_type = true;
/* Make very conservative assumption that all objects /* Make very conservative assumption that all objects
may be in construction. may be in construction.
TODO: ipa-prop already contains code to tell better. TODO: ipa-prop already contains code to tell better.
merge it later. */ merge it later. */
context->maybe_in_construction = true; maybe_in_construction = true;
context->maybe_derived_type = false; maybe_derived_type = false;
} }
/* CST is an invariant (address of decl), try to get meaningful /* CST is an invariant (address of decl), try to get meaningful
polymorphic call context for polymorphic call of method polymorphic call context for polymorphic call of method
if instance of OTR_TYPE that is located at OFFSET of this invariant. if instance of OTR_TYPE that is located at offset OFF of this invariant.
Return FALSE if nothing meaningful can be found. */ Return FALSE if nothing meaningful can be found. */
bool bool
get_polymorphic_call_info_from_invariant (ipa_polymorphic_call_context *context, ipa_polymorphic_call_context::set_by_invariant (tree cst,
tree cst, tree otr_type,
tree otr_type, HOST_WIDE_INT off)
HOST_WIDE_INT offset)
{ {
HOST_WIDE_INT offset2, size, max_size; HOST_WIDE_INT offset2, size, max_size;
tree base; tree base;
invalid = false;
off = 0;
outer_type = NULL;
maybe_in_construction = true;
maybe_derived_type = true;
if (TREE_CODE (cst) != ADDR_EXPR) if (TREE_CODE (cst) != ADDR_EXPR)
return false; return false;
...@@ -2445,10 +2447,13 @@ get_polymorphic_call_info_from_invariant (ipa_polymorphic_call_context *context, ...@@ -2445,10 +2447,13 @@ get_polymorphic_call_info_from_invariant (ipa_polymorphic_call_context *context,
/* Only type inconsistent programs can have otr_type that is /* Only type inconsistent programs can have otr_type that is
not part of outer type. */ not part of outer type. */
if (!contains_type_p (TREE_TYPE (base), offset, otr_type)) if (otr_type && !contains_type_p (TREE_TYPE (base), off, otr_type))
return false; {
invalid = true;
return false;
}
get_polymorphic_call_info_for_decl (context, base, offset); set_by_decl (base, off);
return true; return true;
} }
...@@ -2472,34 +2477,46 @@ walk_ssa_copies (tree op) ...@@ -2472,34 +2477,46 @@ walk_ssa_copies (tree op)
return op; return op;
} }
/* Given REF call in FNDECL, determine class of the polymorphic /* Create polymorphic call context from IP invariant CST.
call (OTR_TYPE), its token (OTR_TOKEN) and CONTEXT. This is typically &global_var.
CALL is optional argument giving the actual statement (usually call) where OTR_TYPE specify type of polymorphic call or NULL if unknown, OFF
the context is used. is offset of call. */
Return pointer to object described by the context or an declaration if
we found the instance to be stored in the static storage. */
tree ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree cst,
get_polymorphic_call_info (tree fndecl, tree otr_type,
tree ref, HOST_WIDE_INT off)
tree *otr_type, {
HOST_WIDE_INT *otr_token, clear_speculation ();
ipa_polymorphic_call_context *context, set_by_invariant (cst, otr_type, off);
gimple call) }
/* Build context for pointer REF contained in FNDECL at statement STMT.
if INSTANCE is non-NULL, return pointer to the object described by
the context or DECL where context is contained in. */
ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree fndecl,
tree ref,
gimple stmt,
tree *instance)
{ {
tree otr_type = NULL;
tree base_pointer; tree base_pointer;
*otr_type = obj_type_ref_class (ref);
*otr_token = tree_to_uhwi (OBJ_TYPE_REF_TOKEN (ref)); if (TREE_CODE (ref) == OBJ_TYPE_REF)
{
otr_type = obj_type_ref_class (ref);
base_pointer = OBJ_TYPE_REF_OBJECT (ref);
}
else
base_pointer = ref;
/* Set up basic info in case we find nothing interesting in the analysis. */ /* Set up basic info in case we find nothing interesting in the analysis. */
context->speculative_outer_type = NULL; clear_speculation ();
context->speculative_offset = 0; outer_type = TYPE_MAIN_VARIANT (otr_type);
context->speculative_maybe_derived_type = true; offset = 0;
context->outer_type = TYPE_MAIN_VARIANT (*otr_type); maybe_derived_type = true;
context->offset = 0; maybe_in_construction = true;
base_pointer = OBJ_TYPE_REF_OBJECT (ref); invalid = false;
context->maybe_derived_type = true;
context->maybe_in_construction = true;
/* Walk SSA for outer object. */ /* Walk SSA for outer object. */
do do
...@@ -2522,9 +2539,9 @@ get_polymorphic_call_info (tree fndecl, ...@@ -2522,9 +2539,9 @@ get_polymorphic_call_info (tree fndecl,
if (TREE_CODE (base) == MEM_REF) if (TREE_CODE (base) == MEM_REF)
{ {
base_pointer = TREE_OPERAND (base, 0); base_pointer = TREE_OPERAND (base, 0);
context->offset offset
+= offset2 + mem_ref_offset (base).to_short_addr () * BITS_PER_UNIT; += offset2 + mem_ref_offset (base).to_short_addr () * BITS_PER_UNIT;
context->outer_type = NULL; outer_type = NULL;
} }
/* We found base object. In this case the outer_type /* We found base object. In this case the outer_type
is known. */ is known. */
...@@ -2534,24 +2551,25 @@ get_polymorphic_call_info (tree fndecl, ...@@ -2534,24 +2551,25 @@ get_polymorphic_call_info (tree fndecl,
/* Only type inconsistent programs can have otr_type that is /* Only type inconsistent programs can have otr_type that is
not part of outer type. */ not part of outer type. */
if (!contains_type_p (TREE_TYPE (base), if (otr_type
context->offset + offset2, *otr_type)) && !contains_type_p (TREE_TYPE (base),
offset + offset2, otr_type))
{ {
/* Use OTR_TOKEN = INT_MAX as a marker of probably type inconsistent invalid = true;
code sequences; we arrange the calls to be builtin_unreachable if (instance)
later. */ *instance = base_pointer;
*otr_token = INT_MAX; return;
return base_pointer;
} }
get_polymorphic_call_info_for_decl (context, base, set_by_decl (base, offset + offset2);
context->offset + offset2); if (maybe_in_construction && stmt)
if (context->maybe_in_construction && call) maybe_in_construction
context->maybe_in_construction
= decl_maybe_in_construction_p (base, = decl_maybe_in_construction_p (base,
context->outer_type, outer_type,
call, stmt,
fndecl); fndecl);
return base; if (instance)
*instance = base;
return;
} }
else else
break; break;
...@@ -2562,7 +2580,7 @@ get_polymorphic_call_info (tree fndecl, ...@@ -2562,7 +2580,7 @@ get_polymorphic_call_info (tree fndecl,
else if (TREE_CODE (base_pointer) == POINTER_PLUS_EXPR else if (TREE_CODE (base_pointer) == POINTER_PLUS_EXPR
&& tree_fits_uhwi_p (TREE_OPERAND (base_pointer, 1))) && tree_fits_uhwi_p (TREE_OPERAND (base_pointer, 1)))
{ {
context->offset += tree_to_shwi (TREE_OPERAND (base_pointer, 1)) offset += tree_to_shwi (TREE_OPERAND (base_pointer, 1))
* BITS_PER_UNIT; * BITS_PER_UNIT;
base_pointer = TREE_OPERAND (base_pointer, 0); base_pointer = TREE_OPERAND (base_pointer, 0);
} }
...@@ -2580,19 +2598,22 @@ get_polymorphic_call_info (tree fndecl, ...@@ -2580,19 +2598,22 @@ get_polymorphic_call_info (tree fndecl,
if (TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE if (TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE
&& SSA_NAME_VAR (base_pointer) == DECL_ARGUMENTS (fndecl)) && SSA_NAME_VAR (base_pointer) == DECL_ARGUMENTS (fndecl))
{ {
context->outer_type outer_type
= TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (base_pointer))); = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (base_pointer)));
gcc_assert (TREE_CODE (context->outer_type) == RECORD_TYPE); gcc_assert (TREE_CODE (outer_type) == RECORD_TYPE);
/* Dynamic casting has possibly upcasted the type /* Dynamic casting has possibly upcasted the type
in the hiearchy. In this case outer type is less in the hiearchy. In this case outer type is less
informative than inner type and we should forget informative than inner type and we should forget
about it. */ about it. */
if (!contains_type_p (context->outer_type, context->offset, if (otr_type
*otr_type)) && !contains_type_p (outer_type, offset,
otr_type))
{ {
context->outer_type = NULL; outer_type = NULL;
return base_pointer; if (instance)
*instance = base_pointer;
return;
} }
/* If the function is constructor or destructor, then /* If the function is constructor or destructor, then
...@@ -2601,38 +2622,41 @@ get_polymorphic_call_info (tree fndecl, ...@@ -2601,38 +2622,41 @@ get_polymorphic_call_info (tree fndecl,
if (DECL_CXX_CONSTRUCTOR_P (fndecl) if (DECL_CXX_CONSTRUCTOR_P (fndecl)
|| DECL_CXX_DESTRUCTOR_P (fndecl)) || DECL_CXX_DESTRUCTOR_P (fndecl))
{ {
context->maybe_in_construction = true; maybe_in_construction = true;
context->maybe_derived_type = false; maybe_derived_type = false;
} }
else else
{ {
context->maybe_derived_type = true; maybe_derived_type = true;
context->maybe_in_construction = false; maybe_in_construction = false;
} }
return base_pointer; if (instance)
*instance = base_pointer;
return;
} }
/* Non-PODs passed by value are really passed by invisible /* Non-PODs passed by value are really passed by invisible
reference. In this case we also know the type of the reference. In this case we also know the type of the
object. */ object. */
if (DECL_BY_REFERENCE (SSA_NAME_VAR (base_pointer))) if (DECL_BY_REFERENCE (SSA_NAME_VAR (base_pointer)))
{ {
context->outer_type outer_type
= TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (base_pointer))); = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (base_pointer)));
gcc_assert (!POINTER_TYPE_P (context->outer_type)); gcc_assert (!POINTER_TYPE_P (outer_type));
/* Only type inconsistent programs can have otr_type that is /* Only type inconsistent programs can have otr_type that is
not part of outer type. */ not part of outer type. */
if (!contains_type_p (context->outer_type, context->offset, if (!contains_type_p (outer_type, offset,
*otr_type)) otr_type))
{ {
/* Use OTR_TOKEN = INT_MAX as a marker of probably type inconsistent invalid = true;
code sequences; we arrange the calls to be builtin_unreachable if (instance)
later. */ *instance = base_pointer;
*otr_token = INT_MAX; return;
return base_pointer;
} }
context->maybe_derived_type = false; maybe_derived_type = false;
context->maybe_in_construction = false; maybe_in_construction = false;
return base_pointer; if (instance)
*instance = base_pointer;
return;
} }
} }
...@@ -2642,11 +2666,10 @@ get_polymorphic_call_info (tree fndecl, ...@@ -2642,11 +2666,10 @@ get_polymorphic_call_info (tree fndecl,
&& SSA_NAME_IS_DEFAULT_DEF (base_pointer) && SSA_NAME_IS_DEFAULT_DEF (base_pointer)
&& TREE_CODE (SSA_NAME_VAR (base_pointer)) != PARM_DECL) && TREE_CODE (SSA_NAME_VAR (base_pointer)) != PARM_DECL)
{ {
/* Use OTR_TOKEN = INT_MAX as a marker of probably type inconsistent invalid = true;
code sequences; we arrange the calls to be builtin_unreachable if (instance)
later. */ *instance = base_pointer;
*otr_token = INT_MAX; return;
return base_pointer;
} }
if (TREE_CODE (base_pointer) == SSA_NAME if (TREE_CODE (base_pointer) == SSA_NAME
&& SSA_NAME_DEF_STMT (base_pointer) && SSA_NAME_DEF_STMT (base_pointer)
...@@ -2655,19 +2678,22 @@ get_polymorphic_call_info (tree fndecl, ...@@ -2655,19 +2678,22 @@ get_polymorphic_call_info (tree fndecl,
(SSA_NAME_DEF_STMT (base_pointer))); (SSA_NAME_DEF_STMT (base_pointer)));
if (POINTER_TYPE_P (base_type) if (POINTER_TYPE_P (base_type)
&& contains_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (base_type)), && (otr_type
context->offset, || !contains_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (base_type)),
*otr_type)) offset,
otr_type)))
{ {
context->speculative_outer_type = TYPE_MAIN_VARIANT speculative_outer_type = TYPE_MAIN_VARIANT
(TREE_TYPE (base_type)); (TREE_TYPE (base_type));
context->speculative_offset = context->offset; speculative_offset = offset;
context->speculative_maybe_derived_type = true; speculative_maybe_derived_type = true;
} }
/* TODO: There are multiple ways to derive a type. For instance /* TODO: There are multiple ways to derive a type. For instance
if BASE_POINTER is passed to an constructor call prior our refernece. if BASE_POINTER is passed to an constructor call prior our refernece.
We do not make this type of flow sensitive analysis yet. */ We do not make this type of flow sensitive analysis yet. */
return base_pointer; if (instance)
*instance = base_pointer;
return;
} }
/* Structure to be passed in between detect_type_change and /* Structure to be passed in between detect_type_change and
...@@ -3404,9 +3430,6 @@ struct final_warning_record *final_warning_records; ...@@ -3404,9 +3430,6 @@ struct final_warning_record *final_warning_records;
temporarily change to one of base types. INCLUDE_DERIVER_TYPES make temporarily change to one of base types. INCLUDE_DERIVER_TYPES make
us to walk the inheritance graph for all derivations. us to walk the inheritance graph for all derivations.
OTR_TOKEN == INT_MAX is used to mark calls that are provably
undefined and should be redirected to unreachable.
If COMPLETEP is non-NULL, store true if the list is complete. If COMPLETEP is non-NULL, store true if the list is complete.
CACHE_TOKEN (if non-NULL) will get stored to an unique ID of entry CACHE_TOKEN (if non-NULL) will get stored to an unique ID of entry
in the target cache. If user needs to visit every target list in the target cache. If user needs to visit every target list
...@@ -3443,23 +3466,12 @@ possible_polymorphic_call_targets (tree otr_type, ...@@ -3443,23 +3466,12 @@ possible_polymorphic_call_targets (tree otr_type,
otr_type = TYPE_MAIN_VARIANT (otr_type); otr_type = TYPE_MAIN_VARIANT (otr_type);
/* If ODR is not initialized, return empty incomplete list. */ /* If ODR is not initialized or the constext is invalid, return empty
if (!odr_hash) incomplete list. */
if (!odr_hash || context.invalid)
{ {
if (completep) if (completep)
*completep = false; *completep = context.invalid;
if (cache_token)
*cache_token = NULL;
if (speculative_targetsp)
*speculative_targetsp = 0;
return nodes;
}
/* If we hit type inconsistency, just return empty list of targets. */
if (otr_token == INT_MAX)
{
if (completep)
*completep = true;
if (cache_token) if (cache_token)
*cache_token = NULL; *cache_token = NULL;
if (speculative_targetsp) if (speculative_targetsp)
...@@ -3853,6 +3865,26 @@ possible_polymorphic_call_target_p (tree otr_type, ...@@ -3853,6 +3865,26 @@ possible_polymorphic_call_target_p (tree otr_type,
} }
/* Return true if N can be possibly target of a polymorphic call of
OBJ_TYPE_REF expression REF in STMT. */
bool
possible_polymorphic_call_target_p (tree ref,
gimple stmt,
struct cgraph_node *n)
{
ipa_polymorphic_call_context context (current_function_decl, ref, stmt);
tree call_fn = gimple_call_fn (stmt);
return possible_polymorphic_call_target_p (obj_type_ref_class (call_fn),
tree_to_uhwi
(OBJ_TYPE_REF_TOKEN (call_fn)),
context,
n);
}
/* After callgraph construction new external nodes may appear. /* After callgraph construction new external nodes may appear.
Add them into the graph. */ Add them into the graph. */
......
...@@ -2347,14 +2347,13 @@ ipa_analyze_call_uses (struct func_body_info *fbi, gimple call) ...@@ -2347,14 +2347,13 @@ ipa_analyze_call_uses (struct func_body_info *fbi, gimple call)
{ {
tree otr_type; tree otr_type;
HOST_WIDE_INT otr_token; HOST_WIDE_INT otr_token;
ipa_polymorphic_call_context context;
tree instance; tree instance;
tree target = gimple_call_fn (call); tree target = gimple_call_fn (call);
ipa_polymorphic_call_context context (current_function_decl,
target, call, &instance);
instance = get_polymorphic_call_info (current_function_decl, otr_type = obj_type_ref_class (target);
target, otr_token = tree_to_uhwi (OBJ_TYPE_REF_TOKEN (target));
&otr_type, &otr_token,
&context, call);
if (context.get_dynamic_type (instance, if (context.get_dynamic_type (instance,
OBJ_TYPE_REF_OBJECT (target), OBJ_TYPE_REF_OBJECT (target),
...@@ -2609,7 +2608,7 @@ ipa_intraprocedural_devirtualization (gimple call) ...@@ -2609,7 +2608,7 @@ ipa_intraprocedural_devirtualization (gimple call)
#ifdef ENABLE_CHECKING #ifdef ENABLE_CHECKING
if (fndecl) if (fndecl)
gcc_assert (possible_polymorphic_call_target_p gcc_assert (possible_polymorphic_call_target_p
(otr, cgraph_node::get (fndecl))); (otr, call, cgraph_node::get (fndecl)));
#endif #endif
return fndecl; return fndecl;
} }
...@@ -3121,14 +3120,12 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie, ...@@ -3121,14 +3120,12 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
if (TREE_CODE (binfo) != TREE_BINFO) if (TREE_CODE (binfo) != TREE_BINFO)
{ {
ipa_polymorphic_call_context context; ipa_polymorphic_call_context context (binfo,
ie->indirect_info->otr_type,
ie->indirect_info->offset);
vec <cgraph_node *>targets; vec <cgraph_node *>targets;
bool final; bool final;
if (!get_polymorphic_call_info_from_invariant
(&context, binfo, ie->indirect_info->otr_type,
ie->indirect_info->offset))
return NULL;
targets = possible_polymorphic_call_targets targets = possible_polymorphic_call_targets
(ie->indirect_info->otr_type, (ie->indirect_info->otr_type,
ie->indirect_info->otr_token, ie->indirect_info->otr_token,
......
...@@ -53,12 +53,28 @@ public: ...@@ -53,12 +53,28 @@ public:
/* True if speculative outer object may be of derived type. We always /* True if speculative outer object may be of derived type. We always
speculate that construction does not happen. */ speculate that construction does not happen. */
bool speculative_maybe_derived_type; bool speculative_maybe_derived_type;
/* True if the context is invalid and all calls should be redirected
to BUILTIN_UNREACHABLE. */
bool invalid;
/* Build empty "I know nothing" context. */ /* Build empty "I know nothing" context. */
ipa_polymorphic_call_context (); ipa_polymorphic_call_context ();
/* Build polymorphic call context for indirect call E. */ /* Build polymorphic call context for indirect call E. */
ipa_polymorphic_call_context (cgraph_edge *e); ipa_polymorphic_call_context (cgraph_edge *e);
/* Build polymorphic call context for IP invariant CST.
If specified, OTR_TYPE specify the type of polymorphic call
that takes CST+OFFSET as a prameter. */
ipa_polymorphic_call_context (tree cst, tree otr_type = NULL,
HOST_WIDE_INT offset = 0);
/* Build context for pointer REF contained in FNDECL at statement STMT.
if INSTANCE is non-NULL, return pointer to the object described by
the context. */
ipa_polymorphic_call_context (tree fndecl, tree ref, gimple stmt,
tree *instance = NULL);
/* Look for vtable stores or constructor calls to work out dynamic type
of memory location. */
bool get_dynamic_type (tree, tree, tree, gimple);
/* Make context non-speculative. */ /* Make context non-speculative. */
void clear_speculation (); void clear_speculation ();
...@@ -67,9 +83,9 @@ public: ...@@ -67,9 +83,9 @@ public:
containing EXPECTED_TYPE as base class. */ containing EXPECTED_TYPE as base class. */
bool restrict_to_inner_class (tree expected_type); bool restrict_to_inner_class (tree expected_type);
/* Look for vtable stores or constructor calls to work out dynamic type private:
of memory location. */ void set_by_decl (tree, HOST_WIDE_INT);
bool get_dynamic_type (tree, tree, tree, gimple); bool set_by_invariant (tree, tree, HOST_WIDE_INT);
}; };
/* Build polymorphic call context for indirect call E. */ /* Build polymorphic call context for indirect call E. */
...@@ -77,6 +93,8 @@ public: ...@@ -77,6 +93,8 @@ public:
inline inline
ipa_polymorphic_call_context::ipa_polymorphic_call_context (cgraph_edge *e) ipa_polymorphic_call_context::ipa_polymorphic_call_context (cgraph_edge *e)
{ {
gcc_checking_assert (e->indirect_info->polymorphic);
offset = e->indirect_info->offset; offset = e->indirect_info->offset;
speculative_offset = e->indirect_info->speculative_offset; speculative_offset = e->indirect_info->speculative_offset;
outer_type = e->indirect_info->outer_type; outer_type = e->indirect_info->outer_type;
...@@ -84,16 +102,22 @@ ipa_polymorphic_call_context::ipa_polymorphic_call_context (cgraph_edge *e) ...@@ -84,16 +102,22 @@ ipa_polymorphic_call_context::ipa_polymorphic_call_context (cgraph_edge *e)
maybe_in_construction = e->indirect_info->maybe_in_construction; maybe_in_construction = e->indirect_info->maybe_in_construction;
maybe_derived_type = e->indirect_info->maybe_derived_type; maybe_derived_type = e->indirect_info->maybe_derived_type;
speculative_maybe_derived_type = e->indirect_info->speculative_maybe_derived_type; speculative_maybe_derived_type = e->indirect_info->speculative_maybe_derived_type;
invalid = false;
} }
/* Build empty "I know nothing" context. */ /* Build empty "I know nothing" context. */
inline inline
ipa_polymorphic_call_context::ipa_polymorphic_call_context () ipa_polymorphic_call_context::ipa_polymorphic_call_context ()
: offset(0), speculative_offset(0), outer_type(NULL),
speculative_outer_type(NULL), maybe_in_construction(false),
maybe_derived_type(false), speculative_maybe_derived_type(false)
{ {
offset = 0;
speculative_offset = 0;
outer_type = NULL;
speculative_outer_type = NULL;
maybe_in_construction = true;
maybe_derived_type = true;
speculative_maybe_derived_type = false;
invalid = false;
} }
/* Make context non-speculative. */ /* Make context non-speculative. */
...@@ -131,22 +155,17 @@ void update_type_inheritance_graph (void); ...@@ -131,22 +155,17 @@ void update_type_inheritance_graph (void);
vec <cgraph_node *> vec <cgraph_node *>
possible_polymorphic_call_targets (tree, HOST_WIDE_INT, possible_polymorphic_call_targets (tree, HOST_WIDE_INT,
ipa_polymorphic_call_context, ipa_polymorphic_call_context,
bool *final = NULL, bool *copletep = NULL,
void **cache_token = NULL, void **cache_token = NULL,
int *nonconstruction_targets = NULL); int *nonconstruction_targets = NULL);
odr_type get_odr_type (tree, bool insert = false); odr_type get_odr_type (tree, bool insert = false);
bool possible_polymorphic_call_target_p (tree ref, gimple stmt, struct cgraph_node *n);
void dump_possible_polymorphic_call_targets (FILE *, tree, HOST_WIDE_INT, void dump_possible_polymorphic_call_targets (FILE *, tree, HOST_WIDE_INT,
const ipa_polymorphic_call_context &); const ipa_polymorphic_call_context &);
bool possible_polymorphic_call_target_p (tree, HOST_WIDE_INT, bool possible_polymorphic_call_target_p (tree, HOST_WIDE_INT,
const ipa_polymorphic_call_context &, const ipa_polymorphic_call_context &,
struct cgraph_node *); struct cgraph_node *);
tree method_class_type (const_tree); tree method_class_type (const_tree);
tree get_polymorphic_call_info (tree, tree, tree *,
HOST_WIDE_INT *,
ipa_polymorphic_call_context *,
gimple call = NULL);
bool get_polymorphic_call_info_from_invariant (ipa_polymorphic_call_context *,
tree, tree, HOST_WIDE_INT);
bool decl_maybe_in_construction_p (tree, tree, gimple, tree); bool decl_maybe_in_construction_p (tree, tree, gimple, tree);
tree vtable_pointer_value_to_binfo (const_tree); tree vtable_pointer_value_to_binfo (const_tree);
bool vtable_pointer_value_to_vtable (const_tree, tree *, unsigned HOST_WIDE_INT *); bool vtable_pointer_value_to_vtable (const_tree, tree *, unsigned HOST_WIDE_INT *);
...@@ -155,7 +174,7 @@ bool contains_polymorphic_type_p (const_tree); ...@@ -155,7 +174,7 @@ bool contains_polymorphic_type_p (const_tree);
void register_odr_type (tree); void register_odr_type (tree);
/* Return vector containing possible targets of polymorphic call E. /* Return vector containing possible targets of polymorphic call E.
If FINALP is non-NULL, store true if the list is complette. If COMPLETEP is non-NULL, store true if the list is complette.
CACHE_TOKEN (if non-NULL) will get stored to an unique ID of entry CACHE_TOKEN (if non-NULL) will get stored to an unique ID of entry
in the target cache. If user needs to visit every target list in the target cache. If user needs to visit every target list
just once, it can memoize them. just once, it can memoize them.
...@@ -166,16 +185,16 @@ void register_odr_type (tree); ...@@ -166,16 +185,16 @@ void register_odr_type (tree);
inline vec <cgraph_node *> inline vec <cgraph_node *>
possible_polymorphic_call_targets (struct cgraph_edge *e, possible_polymorphic_call_targets (struct cgraph_edge *e,
bool *final = NULL, bool *completep = NULL,
void **cache_token = NULL, void **cache_token = NULL,
int *nonconstruction_targets = NULL) int *nonconstruction_targets = NULL)
{ {
gcc_checking_assert (e->indirect_info->polymorphic);
ipa_polymorphic_call_context context(e); ipa_polymorphic_call_context context(e);
return possible_polymorphic_call_targets (e->indirect_info->otr_type, return possible_polymorphic_call_targets (e->indirect_info->otr_type,
e->indirect_info->otr_token, e->indirect_info->otr_token,
context, context,
final, cache_token, completep, cache_token,
nonconstruction_targets); nonconstruction_targets);
} }
...@@ -184,21 +203,16 @@ possible_polymorphic_call_targets (struct cgraph_edge *e, ...@@ -184,21 +203,16 @@ possible_polymorphic_call_targets (struct cgraph_edge *e,
inline vec <cgraph_node *> inline vec <cgraph_node *>
possible_polymorphic_call_targets (tree ref, possible_polymorphic_call_targets (tree ref,
gimple call, gimple call,
bool *final = NULL, bool *completep = NULL,
void **cache_token = NULL) void **cache_token = NULL)
{ {
tree otr_type; ipa_polymorphic_call_context context (current_function_decl, ref, call);
HOST_WIDE_INT otr_token;
ipa_polymorphic_call_context context;
get_polymorphic_call_info (current_function_decl,
ref,
&otr_type, &otr_token, &context, call);
return possible_polymorphic_call_targets (obj_type_ref_class (ref), return possible_polymorphic_call_targets (obj_type_ref_class (ref),
tree_to_uhwi tree_to_uhwi
(OBJ_TYPE_REF_TOKEN (ref)), (OBJ_TYPE_REF_TOKEN (ref)),
context, context,
final, cache_token); completep, cache_token);
} }
/* Dump possible targets of a polymorphic call E into F. */ /* Dump possible targets of a polymorphic call E into F. */
...@@ -206,8 +220,8 @@ possible_polymorphic_call_targets (tree ref, ...@@ -206,8 +220,8 @@ possible_polymorphic_call_targets (tree ref,
inline void inline void
dump_possible_polymorphic_call_targets (FILE *f, struct cgraph_edge *e) dump_possible_polymorphic_call_targets (FILE *f, struct cgraph_edge *e)
{ {
gcc_checking_assert (e->indirect_info->polymorphic);
ipa_polymorphic_call_context context(e); ipa_polymorphic_call_context context(e);
dump_possible_polymorphic_call_targets (f, e->indirect_info->otr_type, dump_possible_polymorphic_call_targets (f, e->indirect_info->otr_type,
e->indirect_info->otr_token, e->indirect_info->otr_token,
context); context);
...@@ -221,26 +235,12 @@ possible_polymorphic_call_target_p (struct cgraph_edge *e, ...@@ -221,26 +235,12 @@ possible_polymorphic_call_target_p (struct cgraph_edge *e,
struct cgraph_node *n) struct cgraph_node *n)
{ {
ipa_polymorphic_call_context context(e); ipa_polymorphic_call_context context(e);
return possible_polymorphic_call_target_p (e->indirect_info->otr_type, return possible_polymorphic_call_target_p (e->indirect_info->otr_type,
e->indirect_info->otr_token, e->indirect_info->otr_token,
context, n); context, n);
} }
/* Return true if N can be possibly target of a polymorphic call of
OBJ_TYPE_REF expression CALL. */
inline bool
possible_polymorphic_call_target_p (tree call,
struct cgraph_node *n)
{
ipa_polymorphic_call_context context;
return possible_polymorphic_call_target_p (obj_type_ref_class (call),
tree_to_uhwi
(OBJ_TYPE_REF_TOKEN (call)),
context,
n);
}
/* Return true of T is type with One Definition Rule info attached. /* Return true of T is type with One Definition Rule info attached.
It means that either it is anonymous type or it has assembler name It means that either it is anonymous type or it has assembler name
set. */ set. */
......
...@@ -4277,16 +4277,11 @@ eliminate_dom_walker::before_dom_children (basic_block b) ...@@ -4277,16 +4277,11 @@ eliminate_dom_walker::before_dom_children (basic_block b)
&& flag_devirtualize && flag_devirtualize
&& virtual_method_call_p (fn)) && virtual_method_call_p (fn))
{ {
tree otr_type; tree otr_type = obj_type_ref_class (fn);
HOST_WIDE_INT otr_token;
ipa_polymorphic_call_context context;
tree instance; tree instance;
ipa_polymorphic_call_context context (current_function_decl, fn, stmt, &instance);
bool final; bool final;
instance = get_polymorphic_call_info (current_function_decl,
fn,
&otr_type, &otr_token, &context, stmt);
context.get_dynamic_type (instance, OBJ_TYPE_REF_OBJECT (fn), otr_type, stmt); context.get_dynamic_type (instance, OBJ_TYPE_REF_OBJECT (fn), otr_type, stmt);
vec <cgraph_node *>targets vec <cgraph_node *>targets
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment