From 67a1b94ce37b06256c3e5f1c482dc362bfb96aca Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Thu, 2 Oct 2014 04:45:44 +0000 Subject: [PATCH] * ipa-polymorphic-call.c (ipa_polymorphic_call_context::restrict_to_inner_class): Rename EXPECTED_TYPE to OTR_TYPE; Validate speculation late; use speculation_consistent_p to do so; Add CONSDER_BASES and CONSIDER_PLACEMENT_NEW parameters. (contains_type_p): Add CONSDER_PLACEMENT_NEW and CONSIDER_BASES; short circuit obvious cases. (ipa_polymorphic_call_context::dump): Improve formatting. (ipa_polymorphic_call_context::ipa_polymorphic_call_context): Use combine_speculation_with to record speculations; Do not ICE when object is located in pointer type decl; do not ICE for methods of UNION_TYPE; do not record nonpolymorphic types. (ipa_polymorphic_call_context::speculation_consistent_p): New method. (ipa_polymorphic_call_context::combine_speculation_with): New method. (ipa_polymorphic_call_context::combine_with): New method. (ipa_polymorphic_call_context::make_speculative): Move here; use combine speculation. * cgraph.h (ipa_polymorphic_call_context): Update restrict_to_inner_class prototype; add offset_by, make_speculative, combine_with, useless_p, combine_speculation_with and speculation_consistent_p methods. (ipa_polymorphic_call_context::offset_by): New method. (ipa_polymorphic_call_context::useless_p): New method. From-SVN: r215790 --- gcc/ChangeLog | 26 ++ gcc/cgraph.h | 52 +++- gcc/ipa-polymorphic-call.c | 623 ++++++++++++++++++++++++++++++++----- 3 files changed, 617 insertions(+), 84 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index aafbd2e04868..3d6542307774 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,29 @@ +2014-10-01 Jan HUbicka + + * ipa-polymorphic-call.c + (ipa_polymorphic_call_context::restrict_to_inner_class): + Rename EXPECTED_TYPE to OTR_TYPE; Validate speculation late; + use speculation_consistent_p to do so; Add CONSDER_BASES + and CONSIDER_PLACEMENT_NEW parameters. + (contains_type_p): Add CONSDER_PLACEMENT_NEW and CONSIDER_BASES; + short circuit obvious cases. + (ipa_polymorphic_call_context::dump): Improve formatting. + (ipa_polymorphic_call_context::ipa_polymorphic_call_context): Use + combine_speculation_with to record speculations; Do not ICE when + object is located in pointer type decl; do not ICE for methods + of UNION_TYPE; do not record nonpolymorphic types. + (ipa_polymorphic_call_context::speculation_consistent_p): New method. + (ipa_polymorphic_call_context::combine_speculation_with): New method. + (ipa_polymorphic_call_context::combine_with): New method. + (ipa_polymorphic_call_context::make_speculative): Move here; use + combine speculation. + * cgraph.h (ipa_polymorphic_call_context): Update + restrict_to_inner_class prototype; add offset_by, make_speculative, + combine_with, useless_p, combine_speculation_with and + speculation_consistent_p methods. + (ipa_polymorphic_call_context::offset_by): New method. + (ipa_polymorphic_call_context::useless_p): New method. + 2014-10-01 Segher Boessenkool PR rtl-optimization/62151 diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 4fd58a551d50..fb41b01cf349 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -1314,8 +1314,35 @@ public: void clear_speculation (); /* Walk container types and modify context to point to actual class - containing EXPECTED_TYPE as base class. */ - bool restrict_to_inner_class (tree expected_type); + containing OTR_TYPE (if non-NULL) as base class. + Return true if resulting context is valid. + + When CONSIDER_PLACEMENT_NEW is false, reject contexts that may be made + valid only via alocation of new polymorphic type inside by means + of placement new. + + When CONSIDER_BASES is false, only look for actual fields, not base types + of TYPE. */ + bool restrict_to_inner_class (tree otr_type, + bool consider_placement_new = true, + bool consider_bases = true); + + /* Adjust all offsets in contexts by given number of bits. */ + void offset_by (HOST_WIDE_INT); + /* Take non-speculative info, merge it with speculative and clear speculatoin. + Used when we no longer manage to keep track of actual outer type, but we + think it is still there. + If OTR_TYPE is set, the transformation can be done more effectively assuming + that context is going to be used only that way. */ + void make_speculative (tree otr_type = NULL); + /* Assume that both THIS and a given context is valid and strenghten THIS + if possible. Return true if any strenghtening was made. + If actual type the context is being used in is known, OTR_TYPE should be + set accordingly. This improves quality of combined result. */ + bool combine_with (ipa_polymorphic_call_context, tree otr_type = NULL); + + /* Return TRUE if context is fully useless. */ + bool useless_p () const; /* Dump human readable context to F. */ void dump (FILE *f) const; @@ -1326,9 +1353,11 @@ public: void stream_in (struct lto_input_block *, struct data_in *data_in); private: + bool combine_speculation_with (tree, HOST_WIDE_INT, bool, tree); 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); + bool speculation_consistent_p (tree, HOST_WIDE_INT, bool, tree); }; /* Structure containing additional information about an indirect call. */ @@ -2634,4 +2663,23 @@ ipa_polymorphic_call_context::clear_outer_type (tree otr_type) maybe_derived_type = true; maybe_in_construction = true; } + +/* Adjust all offsets in contexts by OFF bits. */ + +inline void +ipa_polymorphic_call_context::offset_by (HOST_WIDE_INT off) +{ + if (outer_type) + offset += off; + if (speculative_outer_type) + speculative_offset += off; +} + +/* Return TRUE if context is fully useless. */ + +inline bool +ipa_polymorphic_call_context::useless_p () const +{ + return (!outer_type && !speculative_outer_type); +} #endif /* GCC_CGRAPH_H */ diff --git a/gcc/ipa-polymorphic-call.c b/gcc/ipa-polymorphic-call.c index e682b2b45ddd..e71d3ba66618 100644 --- a/gcc/ipa-polymorphic-call.c +++ b/gcc/ipa-polymorphic-call.c @@ -53,7 +53,9 @@ along with GCC; see the file COPYING3. If not see /* Return true when TYPE contains an polymorphic type and thus is interesting for devirtualization machinery. */ -static bool contains_type_p (tree, HOST_WIDE_INT, tree); +static bool contains_type_p (tree, HOST_WIDE_INT, tree, + bool consider_placement_new = true, + bool consider_bases = true); bool contains_polymorphic_type_p (const_tree type) @@ -99,13 +101,13 @@ possible_placement_new (tree type, tree expected_type, <= tree_to_uhwi (TYPE_SIZE (type))))); } -/* THIS->OUTER_TYPE is a type of memory object where object of EXPECTED_TYPE +/* THIS->OUTER_TYPE is a type of memory object where object of OTR_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 + OTR_TYPE or contain OTR_TYPE as a base. Update THIS to represent it. - If EXPECTED_TYPE is NULL, just find outermost polymorphic type with + If OTR_TYPE is NULL, just find outermost polymorphic type with virtual table present at possition OFFSET. For example when THIS represents type @@ -119,22 +121,32 @@ possible_placement_new (tree type, tree expected_type, sizeof(int). If we can not find corresponding class, give up by setting - THIS->OUTER_TYPE to EXPECTED_TYPE and THIS->OFFSET to NULL. - Return true when lookup was sucesful. */ + THIS->OUTER_TYPE to OTR_TYPE and THIS->OFFSET to NULL. + Return true when lookup was sucesful. + + When CONSIDER_PLACEMENT_NEW is false, reject contexts that may be made + valid only via alocation of new polymorphic type inside by means + of placement new. + + When CONSIDER_BASES is false, only look for actual fields, not base types + of TYPE. */ bool -ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_type) +ipa_polymorphic_call_context::restrict_to_inner_class (tree otr_type, + bool consider_placement_new, + bool consider_bases) { tree type = outer_type; HOST_WIDE_INT cur_offset = offset; bool speculative = false; bool size_unknown = false; + unsigned HOST_WIDE_INT otr_type_size = GET_MODE_BITSIZE (Pmode); /* Update OUTER_TYPE to match EXPECTED_TYPE if it is not set. */ if (!outer_type) { - clear_outer_type (expected_type); - type = expected_type; + clear_outer_type (otr_type); + type = otr_type; cur_offset = 0; } /* See if OFFSET points inside OUTER_TYPE. If it does not, we know @@ -151,8 +163,8 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_type) && tree_to_shwi (TYPE_SIZE (outer_type)) >= 0 && tree_to_shwi (TYPE_SIZE (outer_type)) <= offset) { - clear_outer_type (expected_type); - type = expected_type; + clear_outer_type (otr_type); + type = otr_type; cur_offset = 0; /* If derived type is not allowed, we know that the context is invalid. */ @@ -164,36 +176,11 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_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 (); + if (otr_type && TYPE_SIZE (otr_type) + && tree_fits_shwi_p (TYPE_SIZE (otr_type))) + otr_type_size = tree_to_uhwi (TYPE_SIZE (otr_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) + if (!type || offset < 0) goto no_useful_type_info; /* Find the sub-object the constant actually refers to and mark whether it is @@ -203,7 +190,7 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_type) for speculative_outer_type. The second run has SPECULATIVE set. */ while (true) { - HOST_WIDE_INT pos, size; + unsigned HOST_WIDE_INT pos, size; tree fld; /* If we do not know size of TYPE, we need to be more conservative @@ -218,9 +205,10 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_type) size_unknown = true; /* On a match, just return what we found. */ - if ((types_odr_comparable (type, expected_type) - && types_same_for_odr (type, expected_type)) - || (!expected_type + if ((otr_type + && types_odr_comparable (type, otr_type) + && types_same_for_odr (type, otr_type)) + || (!otr_type && TREE_CODE (type) == RECORD_TYPE && TYPE_BINFO (type) && polymorphic_type_binfo_p (TYPE_BINFO (type)))) @@ -242,7 +230,9 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_type) { /* If type is known to be final, do not worry about derived types. Testing it here may help us to avoid speculation. */ - if (type_known_to_have_no_deriavations_p (outer_type)) + if (otr_type && TREE_CODE (outer_type) == RECORD_TYPE + && (!in_lto_p || odr_type_p (outer_type)) + && type_known_to_have_no_deriavations_p (outer_type)) maybe_derived_type = false; /* Type can not contain itself on an non-zero offset. In that case @@ -254,7 +244,11 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_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) + if (!maybe_derived_type || !speculative_outer_type + || !speculation_consistent_p (speculative_outer_type, + speculative_offset, + speculative_maybe_derived_type, + otr_type)) { clear_speculation (); return true; @@ -279,8 +273,29 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_type) continue; pos = int_bit_position (fld); + if (pos > (unsigned HOST_WIDE_INT)cur_offset) + continue; + if (!DECL_SIZE (fld) || !tree_fits_uhwi_p (DECL_SIZE (fld))) + goto no_useful_type_info; size = tree_to_uhwi (DECL_SIZE (fld)); - if (pos <= cur_offset && (pos + size) > cur_offset) + + /* We can always skip types smaller than pointer size: + those can not contain a virtual table pointer. + + Disqualifying fields that are too small to fit OTR_TYPE + saves work needed to walk them for no benefit. + Because of the way the bases are packed into a class, the + field's size may be smaller than type size, so it needs + to be done with a care. */ + + if (pos <= (unsigned HOST_WIDE_INT)cur_offset + && (pos + size) >= (unsigned HOST_WIDE_INT)cur_offset + + GET_MODE_BITSIZE (Pmode) + && (!otr_type + || !TYPE_SIZE (TREE_TYPE (fld)) + || !tree_fits_shwi_p (TYPE_SIZE (TREE_TYPE (fld))) + || (pos + tree_to_uhwi (TYPE_SIZE (TREE_TYPE (fld)))) + >= cur_offset + otr_type_size)) break; } @@ -307,12 +322,16 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_type) speculative_maybe_derived_type = false; } } + else if (!consider_bases) + goto no_useful_type_info; } else if (TREE_CODE (type) == ARRAY_TYPE) { tree subtype = TYPE_MAIN_VARIANT (TREE_TYPE (type)); - /* Give up if we don't know array size. */ + /* Give up if we don't know array field size. + Also give up on non-polymorphic types as they are used + as buffers for placement new. */ if (!TYPE_SIZE (subtype) || !tree_fits_shwi_p (TYPE_SIZE (subtype)) || tree_to_shwi (TYPE_SIZE (subtype)) <= 0 @@ -324,9 +343,7 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_type) /* 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) + && (cur_offset + otr_type_size > tree_to_uhwi (TYPE_SIZE (subtype)))) goto no_useful_type_info; @@ -349,12 +366,36 @@ ipa_polymorphic_call_context::restrict_to_inner_class (tree expected_type) else { no_useful_type_info: + if (maybe_derived_type && !speculative + && TREE_CODE (outer_type) == RECORD_TYPE + && TREE_CODE (otr_type) == RECORD_TYPE + && TYPE_BINFO (otr_type) + && !offset + && get_binfo_at_offset (TYPE_BINFO (otr_type), 0, outer_type)) + { + clear_outer_type (otr_type); + if (!speculative_outer_type + || !speculation_consistent_p (speculative_outer_type, + speculative_offset, + speculative_maybe_derived_type, + otr_type)) + clear_speculation (); + if (speculative_outer_type) + { + speculative = true; + type = speculative_outer_type; + cur_offset = speculative_offset; + } + else + return true; + } /* 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))) + && consider_placement_new + && (size_unknown || !type || maybe_derived_type + || possible_placement_new (type, otr_type, cur_offset))) { /* In these weird cases we want to accept the context. In non-speculative run we have no useful outer_type info @@ -364,7 +405,13 @@ no_useful_type_info: give useful info. */ if (!speculative) { - clear_outer_type (expected_type); + clear_outer_type (otr_type); + if (!speculative_outer_type + || !speculation_consistent_p (speculative_outer_type, + speculative_offset, + speculative_maybe_derived_type, + otr_type)) + clear_speculation (); if (speculative_outer_type) { speculative = true; @@ -383,7 +430,7 @@ no_useful_type_info: clear_speculation (); if (speculative) return true; - clear_outer_type (expected_type); + clear_outer_type (otr_type); invalid = true; return false; } @@ -391,17 +438,33 @@ no_useful_type_info: } } -/* Return true if OUTER_TYPE contains OTR_TYPE at OFFSET. */ +/* Return true if OUTER_TYPE contains OTR_TYPE at OFFSET. + CONSIDER_PLACEMENT_NEW makes function to accept cases where OTR_TYPE can + be built within OUTER_TYPE by means of placement new. CONSIDER_BASES makes + function to accept cases where OTR_TYPE appears as base of OUTER_TYPE or as + base of one of fields of OUTER_TYPE. */ static bool contains_type_p (tree outer_type, HOST_WIDE_INT offset, - tree otr_type) + tree otr_type, + bool consider_placement_new, + bool consider_bases) { ipa_polymorphic_call_context context; + + /* Check that type is within range. */ + if (offset < 0) + return false; + if (TYPE_SIZE (outer_type) && TYPE_SIZE (otr_type) + && TREE_CODE (outer_type) == INTEGER_CST + && TREE_CODE (otr_type) == INTEGER_CST + && wi::ltu_p (wi::to_offset (outer_type), (wi::to_offset (otr_type) + offset))) + return false; + 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 context.restrict_to_inner_class (otr_type, consider_placement_new, consider_bases); } @@ -508,8 +571,8 @@ ipa_polymorphic_call_context::dump (FILE *f) const fprintf (f, "Call is known to be undefined\n"); else { - if (!outer_type && !offset && !speculative_outer_type) - fprintf (f, "Empty context\n"); + if (useless_p ()) + fprintf (f, "nothing known"); if (outer_type || offset) { fprintf (f, "Outer type:"); @@ -523,7 +586,9 @@ ipa_polymorphic_call_context::dump (FILE *f) const } if (speculative_outer_type) { - fprintf (f, " speculative outer type:"); + if (outer_type || offset) + fprintf (f, " "); + 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)"); @@ -730,6 +795,13 @@ ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree fndecl, tree base = get_ref_base_and_extent (TREE_OPERAND (base_pointer, 0), &offset2, &size, &max_size); + if (max_size != -1 && max_size == size) + combine_speculation_with (TYPE_MAIN_VARIANT + (TREE_TYPE (TREE_TYPE (base_pointer))), + offset + offset2, + true, + NULL /* Do not change outer type. */); + /* If this is a varying address, punt. */ if ((TREE_CODE (base) == MEM_REF || DECL_P (base)) && max_size != -1 @@ -748,8 +820,6 @@ ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree fndecl, is known. */ else if (DECL_P (base)) { - gcc_assert (!POINTER_TYPE_P (TREE_TYPE (base))); - /* Only type inconsistent programs can have otr_type that is not part of outer type. */ if (otr_type @@ -801,15 +871,17 @@ ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree fndecl, { outer_type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (base_pointer))); - gcc_assert (TREE_CODE (outer_type) == RECORD_TYPE); + gcc_assert (TREE_CODE (outer_type) == RECORD_TYPE + || TREE_CODE (outer_type) == UNION_TYPE); /* Dynamic casting has possibly upcasted the type in the hiearchy. In this case outer type is less informative than inner type and we should forget about it. */ - if (otr_type - && !contains_type_p (outer_type, offset, - otr_type)) + if ((otr_type + && !contains_type_p (outer_type, offset, + otr_type)) + || !contains_polymorphic_type_p (outer_type)) { outer_type = NULL; if (instance) @@ -842,17 +914,24 @@ ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree fndecl, { outer_type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (base_pointer))); - gcc_assert (!POINTER_TYPE_P (outer_type)); /* Only type inconsistent programs can have otr_type that is not part of outer type. */ - if (!contains_type_p (outer_type, offset, - otr_type)) + if (otr_type && !contains_type_p (outer_type, offset, + otr_type)) { invalid = true; if (instance) *instance = base_pointer; return; } + /* Non-polymorphic types have no interest for us. */ + else if (!otr_type && !contains_polymorphic_type_p (outer_type)) + { + outer_type = NULL; + if (instance) + *instance = base_pointer; + return; + } maybe_derived_type = false; maybe_in_construction = false; if (instance) @@ -878,17 +957,10 @@ ipa_polymorphic_call_context::ipa_polymorphic_call_context (tree fndecl, base_type = TREE_TYPE (gimple_assign_rhs1 (SSA_NAME_DEF_STMT (base_pointer))); - if (POINTER_TYPE_P (base_type) - && (otr_type - || !contains_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (base_type)), - offset, - otr_type))) - { - speculative_outer_type = TYPE_MAIN_VARIANT - (TREE_TYPE (base_type)); - speculative_offset = offset; - speculative_maybe_derived_type = true; - } + if (POINTER_TYPE_P (base_type)) + combine_speculation_with (TYPE_MAIN_VARIANT (TREE_TYPE (base_type)), + offset, + true, NULL /* Do not change type here */); /* TODO: There are multiple ways to derive a type. For instance if BASE_POINTER is passed to an constructor call prior our refernece. We do not make this type of flow sensitive analysis yet. */ @@ -1080,7 +1152,7 @@ extr_type_from_vtbl_ptr_store (gimple stmt, struct type_change_info *tci, { if (dump_file) fprintf (dump_file, " Construction vtable used\n"); - /* FIXME: We should suport construction contextes. */ + /* FIXME: We should suport construction contexts. */ return NULL; } @@ -1516,3 +1588,390 @@ ipa_polymorphic_call_context::get_dynamic_type (tree instance, return true; } +/* See if speculation given by SPEC_OUTER_TYPE, SPEC_OFFSET and SPEC_MAYBE_DERIVED_TYPE + seems consistent (and useful) with what we already have in the non-speculative context. */ + +bool +ipa_polymorphic_call_context::speculation_consistent_p (tree spec_outer_type, + HOST_WIDE_INT spec_offset, + bool spec_maybe_derived_type, + tree otr_type) +{ + if (!flag_devirtualize_speculatively) + return false; + /* If we know nothing, speculation is always good. */ + if (!outer_type) + return true; + + /* Speculation is only useful to avoid derived types. + This is not 100% true for placement new, where the outer context may + turn out to be useless, but ignore these for now. */ + if (!maybe_derived_type) + return false; + + /* If types agrees, speculation is consistent, but it makes sense only + when it says something new. */ + if (types_must_be_same_for_odr (spec_outer_type, outer_type)) + return maybe_derived_type && !spec_maybe_derived_type; + + /* Non-polymorphic types are useless for deriving likely polymorphic + call targets. */ + if (!contains_polymorphic_type_p (spec_outer_type)) + return false; + + /* If speculation does not contain the type in question, ignore it. */ + if (otr_type + && !contains_type_p (spec_outer_type, spec_offset, otr_type, false, true)) + return false; + + /* If outer type already contains speculation as a filed, + it is useless. We already know from OUTER_TYPE + SPEC_TYPE and that it is not in the construction. */ + if (contains_type_p (outer_type, offset - spec_offset, + spec_outer_type, false, false)) + return false; + + /* If speculative outer type is not more specified than outer + type, just give up. + We can only decide this safely if we can compare types with OUTER_TYPE. + */ + if ((!in_lto_p || odr_type_p (outer_type)) + && !contains_type_p (spec_outer_type, + spec_offset - offset, + outer_type, false)) + return false; + return true; +} + +/* Improve THIS with speculation described by NEW_OUTER_TYPE, NEW_OFFSET, + NEW_MAYBE_DERIVED_TYPE + If OTR_TYPE is set, assume the context is used with OTR_TYPE. */ + +bool +ipa_polymorphic_call_context::combine_speculation_with + (tree new_outer_type, HOST_WIDE_INT new_offset, bool new_maybe_derived_type, + tree otr_type) +{ + if (!new_outer_type) + return false; + + /* restrict_to_inner_class may eliminate wrong speculation making our job + easeier. */ + if (otr_type) + restrict_to_inner_class (otr_type); + + if (!speculation_consistent_p (new_outer_type, new_offset, + new_maybe_derived_type, otr_type)) + return false; + + /* New speculation is a win in case we have no speculation or new + speculation does not consider derivations. */ + if (!speculative_outer_type + || (speculative_maybe_derived_type + && !new_maybe_derived_type)) + { + speculative_outer_type = new_outer_type; + speculative_offset = new_offset; + speculative_maybe_derived_type = new_maybe_derived_type; + return true; + } + else if (types_must_be_same_for_odr (speculative_outer_type, + new_outer_type)) + { + if (speculative_offset != new_offset) + { + /* OK we have two contexts that seems valid but they disagree, + just give up. + + This is not a lattice operation, so we may want to drop it later. */ + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "Speculative outer types match, " + "offset mismatch -> invalid speculation\n"); + clear_speculation (); + return true; + } + else + { + if (speculative_maybe_derived_type && !new_maybe_derived_type) + { + speculative_maybe_derived_type = false; + return true; + } + else + return false; + } + } + /* Choose type that contains the other. This one either contains the outer + as a field (thus giving exactly one target) or is deeper in the type + hiearchy. */ + else if (speculative_outer_type + && speculative_maybe_derived_type + && (new_offset > speculative_offset + || (new_offset == speculative_offset + && contains_type_p (new_outer_type, + 0, speculative_outer_type, false)))) + { + tree old_outer_type = speculative_outer_type; + HOST_WIDE_INT old_offset = speculative_offset; + bool old_maybe_derived_type = speculative_maybe_derived_type; + + speculative_outer_type = new_outer_type; + speculative_offset = new_offset; + speculative_maybe_derived_type = new_maybe_derived_type; + + if (otr_type) + restrict_to_inner_class (otr_type); + + /* If the speculation turned out to make no sense, revert to sensible + one. */ + if (!speculative_outer_type) + { + speculative_outer_type = old_outer_type; + speculative_offset = old_offset; + speculative_maybe_derived_type = old_maybe_derived_type; + return false; + } + return (old_offset != speculative_offset + || old_maybe_derived_type != speculative_maybe_derived_type + || types_must_be_same_for_odr (speculative_outer_type, + new_outer_type)); + } + return false; +} + +/* Assume that both THIS and a given context is valid and strenghten THIS + if possible. Return true if any strenghtening was made. + If actual type the context is being used in is known, OTR_TYPE should be + set accordingly. This improves quality of combined result. */ + +bool +ipa_polymorphic_call_context::combine_with (ipa_polymorphic_call_context ctx, + tree otr_type) +{ + bool updated = false; + + if (ctx.useless_p () || invalid) + return false; + + /* Restricting context to inner type makes merging easier, however do not + do that unless we know how the context is used (OTR_TYPE is non-NULL) */ + if (otr_type && !invalid && !ctx.invalid) + { + restrict_to_inner_class (otr_type); + ctx.restrict_to_inner_class (otr_type); + if(invalid) + return false; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Polymorphic call context combine:"); + dump (dump_file); + fprintf (dump_file, "With context: "); + ctx.dump (dump_file); + if (otr_type) + { + fprintf (dump_file, "To be used with type: "); + print_generic_expr (dump_file, otr_type, TDF_SLIM); + fprintf (dump_file, "\n"); + } + } + + /* If call is known to be invalid, we are done. */ + if (ctx.invalid) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "-> Invalid context\n"); + goto invalidate; + } + + if (!ctx.outer_type) + ; + else if (!outer_type) + { + outer_type = ctx.outer_type; + offset = ctx.offset; + maybe_in_construction = ctx.maybe_in_construction; + maybe_derived_type = ctx.maybe_derived_type; + updated = true; + } + /* If types are known to be same, merging is quite easy. */ + else if (types_must_be_same_for_odr (outer_type, ctx.outer_type)) + { + if (offset != ctx.offset + && TYPE_SIZE (outer_type) + && TREE_CODE (TYPE_SIZE (outer_type)) == INTEGER_CST) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Outer types match, offset mismatch -> invalid\n"); + clear_speculation (); + clear_outer_type (); + invalid = true; + return true; + } + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Outer types match, merging flags\n"); + if (maybe_in_construction && !ctx.maybe_in_construction) + { + updated = true; + maybe_in_construction = false; + } + if (maybe_derived_type && !ctx.maybe_derived_type) + { + updated = true; + maybe_derived_type = false; + } + } + /* If we know the type precisely, there is not much to improve. */ + else if (!maybe_derived_type && !maybe_in_construction + && !ctx.maybe_derived_type && !ctx.maybe_in_construction) + { + /* It may be easy to check if second context permits the first + and set INVALID otherwise. This is not easy to do in general; + contains_type_p may return false negatives for non-comparable + types. + + If OTR_TYPE is known, we however can expect that + restrict_to_inner_class should have discovered the same base + type. */ + if (otr_type && !ctx.maybe_in_construction && !ctx.maybe_derived_type) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Contextes disagree -> invalid\n"); + goto invalidate; + } + } + /* See if one type contains the other as a field (not base). + In this case we want to choose the wider type, because it contains + more information. */ + else if (contains_type_p (ctx.outer_type, ctx.offset - offset, + outer_type, false, false)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Second type contain the first as a field\n"); + + if (maybe_derived_type) + { + outer_type = ctx.outer_type; + maybe_derived_type = ctx.maybe_derived_type; + offset = ctx.offset; + updated = true; + } + + /* If we do not know how the context is being used, we can + not clear MAYBE_IN_CONSTRUCTION because it may be offseted + to other component of OUTER_TYPE later and we know nothing + about it. */ + if (otr_type && maybe_in_construction + && !ctx.maybe_in_construction) + { + maybe_in_construction = false; + updated = true; + } + } + else if (contains_type_p (outer_type, offset - ctx.offset, + ctx.outer_type, false, false)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "First type contain the second as a field\n"); + + if (otr_type && maybe_in_construction + && !ctx.maybe_in_construction) + { + maybe_in_construction = false; + updated = true; + } + } + /* See if OUTER_TYPE is base of CTX.OUTER_TYPE. */ + else if (contains_type_p (ctx.outer_type, + ctx.offset - offset, outer_type, false, true)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "First type is base of second\n"); + if (!maybe_derived_type) + { + if (!ctx.maybe_in_construction + && types_odr_comparable (outer_type, ctx.outer_type)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Second context does not permit base -> invalid\n"); + goto invalidate; + } + } + /* Pick variant deeper in the hiearchy. */ + else + { + outer_type = ctx.outer_type; + maybe_in_construction = ctx.maybe_in_construction; + maybe_derived_type = ctx.maybe_derived_type; + offset = ctx.offset; + updated = true; + } + } + /* See if CTX.OUTER_TYPE is base of OUTER_TYPE. */ + else if (contains_type_p (outer_type, + offset - ctx.offset, ctx.outer_type, false, true)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Second type is base of first\n"); + if (!ctx.maybe_derived_type) + { + if (!maybe_in_construction + && types_odr_comparable (outer_type, ctx.outer_type)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "First context does not permit base -> invalid\n"); + goto invalidate; + } + } + } + /* TODO handle merging using hiearchy. */ + else if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Giving up on merge\n"); + + updated |= combine_speculation_with (ctx.speculative_outer_type, + ctx.speculative_offset, + ctx.maybe_in_construction, + otr_type); + + if (updated && dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Updated as: "); + dump (dump_file); + fprintf (dump_file, "\n"); + } + return updated; + +invalidate: + invalid = true; + clear_speculation (); + clear_outer_type (); + return true; +} + +/* Take non-speculative info, merge it with speculative and clear speculation. + Used when we no longer manage to keep track of actual outer type, but we + think it is still there. */ + +void +ipa_polymorphic_call_context::make_speculative (tree otr_type) +{ + tree spec_outer_type = outer_type; + HOST_WIDE_INT spec_offset = offset; + bool spec_maybe_derived_type = maybe_derived_type; + + if (invalid) + { + invalid = false; + clear_outer_type (); + clear_speculation (); + return; + } + if (!outer_type) + return; + clear_outer_type (); + combine_speculation_with (spec_outer_type, spec_offset, + spec_maybe_derived_type, + otr_type); +}