* ipa-polymorphic-call.c
authorJan Hubicka <hubicka@ucw.cz>
Thu, 2 Oct 2014 04:45:44 +0000 (04:45 +0000)
committerJan Hubicka <hubicka@gcc.gnu.org>
Thu, 2 Oct 2014 04:45:44 +0000 (04:45 +0000)
(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
gcc/cgraph.h
gcc/ipa-polymorphic-call.c

index aafbd2e048687b66bbbd5d8efa658fc8f615540b..3d65423077746cc89f07c5c13aeb265608a18bbc 100644 (file)
@@ -1,3 +1,29 @@
+2014-10-01  Jan HUbicka  <hubicka@ucw.cz>
+
+       * 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  <segher@kernel.crashing.org>
 
        PR rtl-optimization/62151
index 4fd58a551d50f94809c18a3928257ae109036e0c..fb41b01cf349c5c58bc9e877158a75c9c16930f0 100644 (file)
@@ -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  */
index e682b2b45ddd36be9cc7ff706d6c8d70cb5a0188..e71d3ba666183ce1a5989d7bd49e16a9368f5979 100644 (file)
@@ -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 ();
-
-      /* 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 (otr_type && TYPE_SIZE (otr_type)
+      && tree_fits_shwi_p (TYPE_SIZE (otr_type)))
+    otr_type_size = tree_to_uhwi (TYPE_SIZE (otr_type));
 
-  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);
+}