ipa-utils.h (polymorphic_call_context): Add metdhos dump, debug and clear_outer_type.

* ipa-utils.h (polymorphic_call_context): Add
	metdhos dump, debug and clear_outer_type.
	(ipa_polymorphic_call_context::ipa_polymorphic_call_context): Simplify.
	(ipa_polymorphic_call_context::clear_outer_type): New method.
	* ipa-prop.c (ipa_analyze_call_uses): Do not overwrite offset.
	* ipa-devirt.c (types_odr_comparable): New function.
	(types_must_be_same_for_odr): New function.
	(odr_subtypes_equivalent_p): Simplify.
	(possible_placement_new): Break out from ...
	(ipa_polymorphic_call_context::restrict_to_inner_type): ... here;
	be more cuatious about returning false in cases the context may be
	valid in derived type or via placement new.
	(contains_type_p): Clear maybe_derived_type
	(ipa_polymorphic_call_context::dump): New method.
	(ipa_polymorphic_call_context::debug): New method.
	(ipa_polymorphic_call_context::set_by_decl): Cleanup comment.
	(ipa_polymorphic_call_context::set_by_invariant): Simplify.
	(ipa_polymorphic_call_context::ipa_polymorphic_call_context): Simplify.
	(possible_polymorphic_call_targets): Trust context.restrict_to_inner_class
	to suceed on all valid cases; remove confused sanity check.
	(dump_possible_polymorphic_call_targets): Simplify.

From-SVN: r215569
This commit is contained in:
Jan Hubicka 2014-09-24 22:30:21 +02:00 committed by Jan Hubicka
parent c6195f588b
commit a198145843
4 changed files with 312 additions and 122 deletions

View File

@ -1,3 +1,27 @@
2014-09-24 Jan Hubicka <hubicka@ucw.cz>
* ipa-utils.h (polymorphic_call_context): Add
metdhos dump, debug and clear_outer_type.
(ipa_polymorphic_call_context::ipa_polymorphic_call_context): Simplify.
(ipa_polymorphic_call_context::clear_outer_type): New method.
* ipa-prop.c (ipa_analyze_call_uses): Do not overwrite offset.
* ipa-devirt.c (types_odr_comparable): New function.
(types_must_be_same_for_odr): New function.
(odr_subtypes_equivalent_p): Simplify.
(possible_placement_new): Break out from ...
(ipa_polymorphic_call_context::restrict_to_inner_type): ... here;
be more cuatious about returning false in cases the context may be
valid in derived type or via placement new.
(contains_type_p): Clear maybe_derived_type
(ipa_polymorphic_call_context::dump): New method.
(ipa_polymorphic_call_context::debug): New method.
(ipa_polymorphic_call_context::set_by_decl): Cleanup comment.
(ipa_polymorphic_call_context::set_by_invariant): Simplify.
(ipa_polymorphic_call_context::ipa_polymorphic_call_context): Simplify.
(possible_polymorphic_call_targets): Trust context.restrict_to_inner_class
to suceed on all valid cases; remove confused sanity check.
(dump_possible_polymorphic_call_targets): Simplify.
2014-09-24 Aldy Hernandez <aldyh@redhat.com>
* cgraph.h, dbxout.c, dwarfout2.c, gimple-fold.c,

View File

@ -452,6 +452,34 @@ types_same_for_odr (const_tree type1, const_tree type2)
== DECL_ASSEMBLER_NAME (TYPE_NAME (type2)));
}
/* Return true if we can decide on ODR equivalency.
In non-LTO it is always decide, in LTO however it depends in the type has
ODR info attached. */
static bool
types_odr_comparable (tree t1, tree t2)
{
return (!in_lto_p
|| main_odr_variant (t1) == main_odr_variant (t2)
|| (odr_type_p (t1) && odr_type_p (t2))
|| (TREE_CODE (t1) == RECORD_TYPE && TREE_CODE (t2) == RECORD_TYPE
&& TYPE_BINFO (t1) && TYPE_BINFO (t2)
&& polymorphic_type_binfo_p (TYPE_BINFO (t1))
&& polymorphic_type_binfo_p (TYPE_BINFO (t2))));
}
/* Return true if T1 and T2 are ODR equivalent. If ODR equivalency is not
known, be conservative and return false. */
static bool
types_must_be_same_for_odr (tree t1, tree t2)
{
if (types_odr_comparable (t1, t2))
return types_same_for_odr (t1, t2);
else
return main_odr_variant (t1) == main_odr_variant (t2);
}
/* Compare types T1 and T2 and return true if they are
equivalent. */
@ -528,11 +556,7 @@ odr_subtypes_equivalent_p (tree t1, tree t2, hash_set<type_pair,pair_traits> *vi
/* For ODR types be sure to compare their names.
To support -wno-odr-type-merging we allow one type to be non-ODR
and other ODR even though it is a violation. */
if ((odr_type_p (t1) && odr_type_p (t2))
|| (TREE_CODE (t1) == RECORD_TYPE && TREE_CODE (t2) == RECORD_TYPE
&& TYPE_BINFO (t1) && TYPE_BINFO (t2)
&& polymorphic_type_binfo_p (TYPE_BINFO (t1))
&& polymorphic_type_binfo_p (TYPE_BINFO (t2))))
if (types_odr_comparable (t1, t2))
{
if (!types_same_for_odr (t1, t2))
return false;
@ -1325,8 +1349,8 @@ get_odr_type (tree type, bool insert)
type = main_odr_variant (type);
hash = hash_type_name (type);
slot
= odr_hash->find_slot_with_hash (type, hash, insert ? INSERT : NO_INSERT);
slot = odr_hash->find_slot_with_hash (type, hash,
insert ? INSERT : NO_INSERT);
if (!slot)
return NULL;
@ -1979,12 +2003,37 @@ contains_polymorphic_type_p (const_tree type)
return false;
}
/* Return true if it seems valid to use placement new to build EXPECTED_TYPE
at possition CUR_OFFSET within TYPE.
POD can be changed to an instance of a polymorphic type by
placement new. Here we play safe and assume that any
non-polymorphic type is POD. */
bool
possible_placement_new (tree type, tree expected_type,
HOST_WIDE_INT cur_offset)
{
return ((TREE_CODE (type) != RECORD_TYPE
|| !TYPE_BINFO (type)
|| cur_offset >= BITS_PER_WORD
|| !polymorphic_type_binfo_p (TYPE_BINFO (type)))
&& (!TYPE_SIZE (type)
|| !tree_fits_shwi_p (TYPE_SIZE (type))
|| (cur_offset
+ (expected_type ? tree_to_uhwi (TYPE_SIZE (expected_type))
: 1)
<= tree_to_uhwi (TYPE_SIZE (type)))));
}
/* THIS->OUTER_TYPE is a type of memory object where object of EXPECTED_TYPE
is contained at THIS->OFFSET. Walk the memory representation of
THIS->OUTER_TYPE and find the outermost class type that match
EXPECTED_TYPE or contain EXPECTED_TYPE as a base. Update THIS
to represent it.
If EXPECTED_TYPE is NULL, just find outermost polymorphic type with
virtual table present at possition OFFSET.
For example when THIS represents type
class A
{
@ -2005,69 +2054,111 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_type)
tree type = outer_type;
HOST_WIDE_INT cur_offset = offset;
bool speculative = false;
bool speculation_valid = false;
bool valid = false;
/* Update OUTER_TYPE to match EXPECTED_TYPE if it is not set. */
if (!outer_type)
{
type = outer_type = expected_type;
offset = cur_offset = 0;
clear_outer_type (expected_type);
type = expected_type;
cur_offset = 0;
}
/* See if OFFSET points inside OUTER_TYPE. If it does not, we know
that the context is either invalid, or the instance type must be
derived from OUTER_TYPE.
if (speculative_outer_type == outer_type
&& (!maybe_derived_type
|| speculative_maybe_derived_type))
Because the instance type may contain field whose type is of OUTER_TYPE,
we can not derive any effective information about it.
TODO: In the case we know all derrived types, we can definitely do better
here. */
else if (TYPE_SIZE (outer_type)
&& tree_fits_shwi_p (TYPE_SIZE (outer_type))
&& tree_to_shwi (TYPE_SIZE (outer_type)) >= 0
&& tree_to_shwi (TYPE_SIZE (outer_type)) <= offset)
{
speculative_outer_type = NULL;
speculative_offset = 0;
speculative_maybe_derived_type = false;
clear_outer_type (expected_type);
type = expected_type;
cur_offset = 0;
/* If derived type is not allowed, we know that the context is invalid. */
if (!maybe_derived_type)
{
clear_speculation ();
invalid = true;
return false;
}
}
/* See if speculative type seem to be derrived from outer_type.
Then speculation is valid only if it really is a derivate and derived types
are allowed.
if (speculative_outer_type)
{
/* Short cirucit the busy work bellow and give up on case when speculation
is obviously the same as outer_type. */
if ((!maybe_derived_type
|| speculative_maybe_derived_type)
&& types_must_be_same_for_odr (speculative_outer_type, outer_type))
clear_speculation ();
The test does not really look for derivate, but also accepts the case where
outer_type is a field of speculative_outer_type. In this case eiter
MAYBE_DERIVED_TYPE is false and we have full non-speculative information or
the loop bellow will correctly update SPECULATIVE_OUTER_TYPE
and SPECULATIVE_MAYBE_DERIVED_TYPE. */
if (speculative_outer_type
&& speculative_offset >= offset
&& contains_type_p (speculative_outer_type,
offset - speculative_offset,
outer_type))
speculation_valid = maybe_derived_type;
/* See if SPECULATIVE_OUTER_TYPE is contained in or derived from OUTER_TYPE.
In this case speculation is valid only if derived types are allowed.
The test does not really look for derivate, but also accepts the case where
outer_type is a field of speculative_outer_type. In this case eiter
MAYBE_DERIVED_TYPE is false and we have full non-speculative information or
the loop bellow will correctly update SPECULATIVE_OUTER_TYPE
and SPECULATIVE_MAYBE_DERIVED_TYPE. */
else if (speculative_offset < offset
|| !contains_type_p (speculative_outer_type,
speculative_offset - offset,
outer_type)
|| !maybe_derived_type)
clear_speculation ();
}
else
/* Regularize things little bit and clear all the fields when no useful
speculatin is known. */
clear_speculation ();
if (!type)
goto no_useful_type_info;
/* Find the sub-object the constant actually refers to and mark whether it is
an artificial one (as opposed to a user-defined one).
This loop is performed twice; first time for outer_type and second time
for speculative_outer_type. The second iteration has SPECULATIVE set. */
for speculative_outer_type. The second run has SPECULATIVE set. */
while (true)
{
HOST_WIDE_INT pos, size;
tree fld;
bool size_unknown;
/* If we do not know size of TYPE, we need to be more conservative
about accepting cases where we can not find EXPECTED_TYPE.
Generally the types that do matter here are of constant size.
Size_unknown case should be very rare. */
if (TYPE_SIZE (type)
&& tree_fits_shwi_p (TYPE_SIZE (type))
&& tree_to_shwi (TYPE_SIZE (type)) >= 0)
size_unknown = false;
else
size_unknown = true;
/* On a match, just return what we found. */
if (TREE_CODE (type) == TREE_CODE (expected_type)
&& (!in_lto_p
|| (TREE_CODE (type) == RECORD_TYPE
&& TYPE_BINFO (type)
&& polymorphic_type_binfo_p (TYPE_BINFO (type))))
&& types_same_for_odr (type, expected_type))
if ((types_odr_comparable (type, expected_type)
&& types_same_for_odr (type, expected_type))
|| (!expected_type
&& TREE_CODE (type) == RECORD_TYPE
&& TYPE_BINFO (type)
&& polymorphic_type_binfo_p (TYPE_BINFO (type))))
{
if (speculative)
{
gcc_assert (speculation_valid);
gcc_assert (valid);
/* If we did not match the offset, just give up on speculation. */
if (cur_offset != 0
|| (types_same_for_odr (speculative_outer_type,
outer_type)
/* Also check if speculation did not end up being same as
non-speculation. */
|| (types_must_be_same_for_odr (speculative_outer_type,
outer_type)
&& (maybe_derived_type
== speculative_maybe_derived_type)))
clear_speculation ();
@ -2075,18 +2166,24 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_type)
}
else
{
/* If type is known to be final, do not worry about derived
types. Testing it here may help us to avoid speculation. */
if (type_all_derivations_known_p (outer_type)
&& (TYPE_FINAL_P (outer_type)
|| (odr_hash
&& !get_odr_type (outer_type, true)->derived_types.length())))
maybe_derived_type = false;
/* Type can not contain itself on an non-zero offset. In that case
just give up. */
just give up. Still accept the case where size is now known.
Either the second copy may appear past the end of type or within
the non-POD buffer located inside the variably sized type
itself. */
if (cur_offset != 0)
{
valid = false;
goto give_up;
}
valid = true;
/* If speculation is not valid or we determined type precisely,
we are done. */
if (!speculation_valid
|| !maybe_derived_type)
goto no_useful_type_info;
/* If we determined type precisely or we have no clue on
speuclation, we are done. */
if (!maybe_derived_type || !speculative_outer_type)
{
clear_speculation ();
return true;
@ -2117,7 +2214,7 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_type)
}
if (!fld)
goto give_up;
goto no_useful_type_info;
type = TYPE_MAIN_VARIANT (TREE_TYPE (fld));
cur_offset -= pos;
@ -2149,8 +2246,20 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_type)
|| !tree_fits_shwi_p (TYPE_SIZE (subtype))
|| tree_to_shwi (TYPE_SIZE (subtype)) <= 0
|| !contains_polymorphic_type_p (subtype))
goto give_up;
cur_offset = cur_offset % tree_to_shwi (TYPE_SIZE (subtype));
goto no_useful_type_info;
HOST_WIDE_INT new_offset = cur_offset % tree_to_shwi (TYPE_SIZE (subtype));
/* We may see buffer for placement new. In this case the expected type
can be bigger than the subtype. */
if (TYPE_SIZE (subtype)
&& (cur_offset
+ (expected_type ? tree_to_uhwi (TYPE_SIZE (expected_type))
: 0)
> tree_to_uhwi (TYPE_SIZE (type))))
goto no_useful_type_info;
cur_offset = new_offset;
type = subtype;
if (!speculative)
{
@ -2167,31 +2276,48 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_type)
}
/* Give up on anything else. */
else
goto give_up;
{
no_useful_type_info:
/* We found no way to embedd EXPECTED_TYPE in TYPE.
We still permit two special cases - placement new and
the case of variadic types containing themselves. */
if (!speculative
&& (size_unknown || !type
|| possible_placement_new (type, expected_type, cur_offset)))
{
/* In these weird cases we want to accept the context.
In non-speculative run we have no useful outer_type info
(TODO: we may eventually want to record upper bound on the
type size that can be used to prune the walk),
but we still want to consider speculation that may
give useful info. */
if (!speculative)
{
clear_outer_type (expected_type);
if (speculative_outer_type)
{
speculative = true;
type = speculative_outer_type;
cur_offset = speculative_offset;
}
else
return true;
}
else
clear_speculation ();
return true;
}
else
{
clear_speculation ();
if (speculative)
return true;
clear_outer_type (expected_type);
invalid = true;
return false;
}
}
}
/* If we failed to find subtype we look for, give up and fall back to the
most generic query. */
give_up:
clear_speculation ();
if (valid)
return true;
outer_type = expected_type;
offset = 0;
maybe_derived_type = true;
maybe_in_construction = true;
/* POD can be changed to an instance of a polymorphic type by
placement new. Here we play safe and assume that any
non-polymorphic type is POD. */
if ((TREE_CODE (type) != RECORD_TYPE
|| !TYPE_BINFO (type)
|| !polymorphic_type_binfo_p (TYPE_BINFO (type)))
&& (!TYPE_SIZE (type)
|| TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
|| (cur_offset + tree_to_uhwi (TYPE_SIZE (expected_type)) <=
tree_to_uhwi (TYPE_SIZE (type)))))
return true;
return false;
}
/* Return true if OUTER_TYPE contains OTR_TYPE at OFFSET. */
@ -2203,6 +2329,7 @@ contains_type_p (tree outer_type, HOST_WIDE_INT offset,
ipa_polymorphic_call_context context;
context.offset = offset;
context.outer_type = TYPE_MAIN_VARIANT (outer_type);
context.maybe_derived_type = false;
return context.restrict_to_inner_class (otr_type);
}
@ -2399,6 +2526,50 @@ decl_maybe_in_construction_p (tree base, tree outer_type,
return false;
}
/* Dump human readable context to F. */
void
ipa_polymorphic_call_context::dump (FILE *f) const
{
fprintf (f, " ");
if (invalid)
fprintf (f, "Call is known to be undefined\n");
else
{
if (!outer_type && !offset && !speculative_outer_type)
fprintf (f, "Empty context\n");
if (outer_type || offset)
{
fprintf (f, "Outer type:");
print_generic_expr (f, outer_type, TDF_SLIM);
if (maybe_derived_type)
fprintf (f, " (or a derived type)");
if (maybe_in_construction)
fprintf (f, " (maybe in construction)");
fprintf (f, " offset "HOST_WIDE_INT_PRINT_DEC,
offset);
}
if (speculative_outer_type)
{
fprintf (f, " speculative outer type:");
print_generic_expr (f, speculative_outer_type, TDF_SLIM);
if (speculative_maybe_derived_type)
fprintf (f, " (or a derived type)");
fprintf (f, " at offset "HOST_WIDE_INT_PRINT_DEC,
speculative_offset);
}
}
fprintf(f, "\n");
}
/* Print context to stderr. */
void
ipa_polymorphic_call_context::debug () const
{
dump (stderr);
}
/* Proudce polymorphic call context for call method of instance
that is located within BASE (that is assumed to be a decl) at offset OFF. */
@ -2412,8 +2583,9 @@ ipa_polymorphic_call_context::set_by_decl (tree base, HOST_WIDE_INT off)
clear_speculation ();
/* Make very conservative assumption that all objects
may be in construction.
TODO: ipa-prop already contains code to tell better.
merge it later. */
It is up to caller to revisit this via
get_dynamic_type or decl_maybe_in_construction_p. */
maybe_in_construction = true;
maybe_derived_type = false;
}
@ -2433,9 +2605,7 @@ ipa_polymorphic_call_context::set_by_invariant (tree cst,
invalid = false;
off = 0;
outer_type = NULL;
maybe_in_construction = true;
maybe_derived_type = true;
clear_outer_type (otr_type);
if (TREE_CODE (cst) != ADDR_EXPR)
return false;
@ -2448,10 +2618,7 @@ ipa_polymorphic_call_context::set_by_invariant (tree cst,
/* Only type inconsistent programs can have otr_type that is
not part of outer type. */
if (otr_type && !contains_type_p (TREE_TYPE (base), off, otr_type))
{
invalid = true;
return false;
}
return false;
set_by_decl (base, off);
return true;
@ -2512,10 +2679,7 @@ ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree fndecl,
/* Set up basic info in case we find nothing interesting in the analysis. */
clear_speculation ();
outer_type = TYPE_MAIN_VARIANT (otr_type);
offset = 0;
maybe_derived_type = true;
maybe_in_construction = true;
clear_outer_type (otr_type);
invalid = false;
/* Walk SSA for outer object. */
@ -3489,18 +3653,21 @@ possible_polymorphic_call_targets (tree otr_type,
gcc_assert (!context.outer_type
|| TYPE_MAIN_VARIANT (context.outer_type) == context.outer_type);
/* Lookup the outer class type we want to walk. */
/* Lookup the outer class type we want to walk.
If we fail to do so, the context is invalid. */
if ((context.outer_type || context.speculative_outer_type)
&& !context.restrict_to_inner_class (otr_type))
{
fprintf (stderr, "Invalid\n");
if (completep)
*completep = false;
*completep = true;
if (cache_token)
*cache_token = NULL;
if (speculative_targetsp)
*speculative_targetsp = 0;
return nodes;
}
gcc_assert (!context.invalid);
/* Check that restrict_to_inner_class kept the main variant. */
gcc_assert (!context.outer_type
@ -3652,10 +3819,7 @@ possible_polymorphic_call_targets (tree otr_type,
if (type_possibly_instantiated_p (outer_type->type))
maybe_record_node (nodes, target, &inserted, can_refer, &complete);
else
{
skipped = true;
gcc_assert (in_lto_p || context.maybe_derived_type);
}
skipped = true;
if (binfo)
matched_vtables.add (BINFO_VTABLE (binfo));
@ -3789,20 +3953,8 @@ dump_possible_polymorphic_call_targets (FILE *f,
fprintf (f, " Targets of polymorphic call of type %i:", type->id);
print_generic_expr (f, type->type, TDF_SLIM);
fprintf (f, " token %i\n", (int)otr_token);
if (ctx.outer_type || ctx.offset)
{
fprintf (f, " Contained in type:");
print_generic_expr (f, ctx.outer_type, TDF_SLIM);
fprintf (f, " at offset "HOST_WIDE_INT_PRINT_DEC"\n",
ctx.offset);
}
if (ctx.speculative_outer_type)
{
fprintf (f, " Speculatively contained in type:");
print_generic_expr (f, ctx.speculative_outer_type, TDF_SLIM);
fprintf (f, " at offset "HOST_WIDE_INT_PRINT_DEC"\n",
ctx.speculative_offset);
}
ctx.dump (f);
fprintf (f, " %s%s%s%s\n ",
final ? "This is a complete list." :

View File

@ -2357,7 +2357,8 @@ ipa_analyze_call_uses (struct func_body_info *fbi, gimple call)
if (context.get_dynamic_type (instance,
OBJ_TYPE_REF_OBJECT (target),
otr_type, call))
otr_type, call)
&& context.offset == cs->indirect_info->offset)
{
gcc_assert (TREE_CODE (otr_type) == RECORD_TYPE);
cs->indirect_info->polymorphic = true;

View File

@ -83,9 +83,14 @@ public:
containing EXPECTED_TYPE as base class. */
bool restrict_to_inner_class (tree expected_type);
/* Dump human readable context to F. */
void dump (FILE *f) const;
void DEBUG_FUNCTION debug () const;
private:
void set_by_decl (tree, HOST_WIDE_INT);
bool set_by_invariant (tree, tree, HOST_WIDE_INT);
void clear_outer_type (tree otr_type = NULL);
};
/* Build polymorphic call context for indirect call E. */
@ -110,13 +115,8 @@ ipa_polymorphic_call_context::ipa_polymorphic_call_context (cgraph_edge *e)
inline
ipa_polymorphic_call_context::ipa_polymorphic_call_context ()
{
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;
clear_speculation ();
clear_outer_type ();
invalid = false;
}
@ -130,6 +130,19 @@ ipa_polymorphic_call_context::clear_speculation ()
speculative_maybe_derived_type = false;
}
/* Produce context specifying all derrived types of OTR_TYPE.
If OTR_TYPE is NULL or type of the OBJ_TYPE_REF, the context is set
to dummy "I know nothing" setting. */
inline void
ipa_polymorphic_call_context::clear_outer_type (tree otr_type)
{
outer_type = otr_type ? TYPE_MAIN_VARIANT (otr_type) : NULL;
offset = 0;
maybe_derived_type = true;
maybe_in_construction = true;
}
/* In ipa-utils.c */
void ipa_print_order (FILE*, const char *, struct cgraph_node**, int);
int ipa_reduced_postorder (struct cgraph_node **, bool, bool,