== 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. */
/* 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;
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;
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
{
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;
}
-
- if (speculative_outer_type == outer_type
- && (!maybe_derived_type
- || speculative_maybe_derived_type))
+ /* 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.
+
+ 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.
-
- 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;
+ 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 ();
+
+ /* 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 ();
}
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;
}
if (!fld)
- goto give_up;
+ goto no_useful_type_info;
type = TYPE_MAIN_VARIANT (TREE_TYPE (fld));
cur_offset -= pos;
|| !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)
{
}
/* 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. */
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);
}
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. */
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;
}
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;
/* 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;
/* 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. */
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
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));
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." :