* ipa-polymorphic-call.c
authorJan Hubicka <hubicka@ucw.cz>
Sun, 16 Nov 2014 21:01:45 +0000 (22:01 +0100)
committerJan Hubicka <hubicka@gcc.gnu.org>
Sun, 16 Nov 2014 21:01:45 +0000 (21:01 +0000)
(ipa_polymorphic_call_context::speculation_consistent_p): Constify.
(ipa_polymorphic_call_context::meet_speculation_with): New function.
(ipa_polymorphic_call_context::combine_with): Handle types in construction
better.
(ipa_polymorphic_call_context::equal_to): Do not bother about useless
speculation.
(ipa_polymorphic_call_context::meet_with): New function.
* cgraph.h (class ipa_polymorphic_call_context): Add
meet_width, meet_speculation_with; constify speculation_consistent_p.
* ipa-cp.c (ipa_context_from_jfunc): Handle speculation; combine with incomming
context.
(propagate_context_accross_jump_function): Likewise; be more cureful.
about set_contains_variable.
(ipa_get_indirect_edge_target_1): Fix handling of dynamic type changes.
(find_more_scalar_values_for_callers_subset): Fix.
(find_more_contexts_for_caller_subset): Perform meet operation.

From-SVN: r217634

gcc/ChangeLog
gcc/cgraph.h
gcc/ipa-cp.c
gcc/ipa-polymorphic-call.c

index d035fe15e2012cce44eaac6afd2c4c22da5017d9..d385e334bf7a19446a15ca9009450c1d2b38a2c1 100644 (file)
@@ -1,3 +1,23 @@
+2014-11-16  Jan Hubicka  <hubicka@ucw.cz>
+
+       * ipa-polymorphic-call.c
+       (ipa_polymorphic_call_context::speculation_consistent_p): Constify.
+       (ipa_polymorphic_call_context::meet_speculation_with): New function.
+       (ipa_polymorphic_call_context::combine_with): Handle types in construction
+       better.
+       (ipa_polymorphic_call_context::equal_to): Do not bother about useless
+       speculation.
+       (ipa_polymorphic_call_context::meet_with): New function.
+       * cgraph.h (class ipa_polymorphic_call_context): Add
+       meet_width, meet_speculation_with; constify speculation_consistent_p.
+       * ipa-cp.c (ipa_context_from_jfunc): Handle speculation; combine with incomming
+       context.
+       (propagate_context_accross_jump_function): Likewise; be more cureful.
+       about set_contains_variable.
+       (ipa_get_indirect_edge_target_1): Fix handling of dynamic type changes.
+       (find_more_scalar_values_for_callers_subset): Fix.
+       (find_more_contexts_for_caller_subset): Perform meet operation.
+
 2014-11-16  Jan Hubicka  <hubicka@ucw.cz>
 
        * passes.c (execute_one_pass): Do not apply all transforms prior
index 77c2a6058e768ff1c782ec532ac3507d509b1770..fc5fe82d1bec97f103561eac7ae96c5329062ac2 100644 (file)
@@ -1389,6 +1389,7 @@ public:
      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);
+  bool meet_with (ipa_polymorphic_call_context, tree otr_type = NULL);
 
   /* Return TRUE if context is fully useless.  */
   bool useless_p () const;
@@ -1406,9 +1407,10 @@ public:
 
 private:
   bool combine_speculation_with (tree, HOST_WIDE_INT, bool, tree);
+  bool meet_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);
-  bool speculation_consistent_p (tree, HOST_WIDE_INT, bool, tree);
+  bool speculation_consistent_p (tree, HOST_WIDE_INT, bool, tree) const;
   void make_speculative (tree otr_type = NULL);
 };
 
index 0529f174615e3404e0f14ac49de636d2a077ad09..375b49e5f918ae52c69129f5815944f5569643d0 100644 (file)
@@ -946,17 +946,17 @@ ipa_context_from_jfunc (ipa_node_params *info, cgraph_edge *cs, int csidx,
     {
       ipa_polymorphic_call_context srcctx;
       int srcidx;
+      bool type_preserved = true;
       if (jfunc->type == IPA_JF_PASS_THROUGH)
        {
-         if (ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR
-             || !ipa_get_jf_pass_through_type_preserved (jfunc))
+         if (ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR)
            return ctx;
+         type_preserved = ipa_get_jf_pass_through_type_preserved (jfunc);
          srcidx = ipa_get_jf_pass_through_formal_id (jfunc);
        }
       else
        {
-         if (!ipa_get_jf_ancestor_type_preserved (jfunc))
-           return ctx;
+         type_preserved = ipa_get_jf_ancestor_type_preserved (jfunc);
          srcidx = ipa_get_jf_ancestor_formal_id (jfunc);
        }
       if (info->ipcp_orig_node)
@@ -981,7 +981,10 @@ ipa_context_from_jfunc (ipa_node_params *info, cgraph_edge *cs, int csidx,
        return ctx;
       if (jfunc->type == IPA_JF_ANCESTOR)
        srcctx.offset_by (ipa_get_jf_ancestor_offset (jfunc));
-      ctx.combine_with (srcctx);
+      if (!type_preserved)
+       srcctx.possible_dynamic_type_change (cs->in_polymorphic_cdtor);
+      srcctx.combine_with (ctx);
+      return srcctx;
     }
 
   return ctx;
@@ -1298,15 +1301,13 @@ propagate_context_accross_jump_function (cgraph_edge *cs,
     return false;
   bool ret = false;
   bool added_sth = false;
+  bool type_preserved = true;
 
   ipa_polymorphic_call_context edge_ctx, *edge_ctx_ptr
     = ipa_get_ith_polymorhic_call_context (args, idx);
 
   if (edge_ctx_ptr)
-    {
-      edge_ctx = *edge_ctx_ptr;
-      edge_ctx.clear_speculation ();
-    }
+    edge_ctx = *edge_ctx_ptr;
 
   if (jfunc->type == IPA_JF_PASS_THROUGH
       || jfunc->type == IPA_JF_ANCESTOR)
@@ -1320,15 +1321,14 @@ propagate_context_accross_jump_function (cgraph_edge *cs,
         not set instead of punting.  */
       if (jfunc->type == IPA_JF_PASS_THROUGH)
        {
-         if (ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR
-             || !ipa_get_jf_pass_through_type_preserved (jfunc))
+         if (ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR)
            goto prop_fail;
+         type_preserved = ipa_get_jf_pass_through_type_preserved (jfunc);
          src_idx = ipa_get_jf_pass_through_formal_id (jfunc);
        }
       else
        {
-         if (!ipa_get_jf_ancestor_type_preserved (jfunc))
-           goto prop_fail;
+         type_preserved = ipa_get_jf_ancestor_type_preserved (jfunc);
          src_idx = ipa_get_jf_ancestor_formal_id (jfunc);
        }
 
@@ -1338,21 +1338,24 @@ propagate_context_accross_jump_function (cgraph_edge *cs,
          && (src_lat->contains_variable
              || (src_lat->values_count > 1)))
        goto prop_fail;
-      if (src_lat->contains_variable)
-         ret |= dest_lat->set_contains_variable ();
 
       ipcp_value<ipa_polymorphic_call_context> *src_val;
       for (src_val = src_lat->values; src_val; src_val = src_val->next)
        {
          ipa_polymorphic_call_context cur = src_val->value;
+
+         if (!type_preserved)
+           cur.possible_dynamic_type_change (cs->in_polymorphic_cdtor);
          if (jfunc->type == IPA_JF_ANCESTOR)
            cur.offset_by (ipa_get_jf_ancestor_offset (jfunc));
-         /* TODO: Perhaps attempt to look up some used OTR type? */
-         cur.clear_speculation ();
-         if (!edge_ctx.useless_p ())
-           cur.combine_with (edge_ctx);
+         /* TODO: In cases we know how the context is going to be used,
+            we can improve the result by passing proper OTR_TYPE.  */
+         cur.combine_with (edge_ctx);
          if (!cur.useless_p ())
            {
+             if (src_lat->contains_variable
+                 && !edge_ctx.equal_to (cur))
+               ret |= dest_lat->set_contains_variable ();
              ret |= dest_lat->add_value (cur, cs, src_val, src_idx);
              added_sth = true;
            }
@@ -1848,6 +1851,10 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
   if (known_contexts.length () > (unsigned int) param_index)
     {
       context = known_contexts[param_index];
+      context.offset_by (anc_offset);
+      if (ie->indirect_info->vptr_changed)
+       context.possible_dynamic_type_change (ie->in_polymorphic_cdtor,
+                                             ie->indirect_info->otr_type);
       if (t)
        {
          ipa_polymorphic_call_context ctx2 = ipa_polymorphic_call_context
@@ -3160,6 +3167,7 @@ find_more_scalar_values_for_callers_subset (struct cgraph_node *node,
       struct cgraph_edge *cs;
       tree newval = NULL_TREE;
       int j;
+      bool first = true;
 
       if (ipa_get_scalar_lat (info, i)->bottom || known_csts[i])
        continue;
@@ -3178,13 +3186,15 @@ find_more_scalar_values_for_callers_subset (struct cgraph_node *node,
          t = ipa_value_from_jfunc (IPA_NODE_REF (cs->caller), jump_func);
          if (!t
              || (newval
-                 && !values_equal_for_ipcp_p (t, newval)))
+                 && !values_equal_for_ipcp_p (t, newval))
+             || (!first && !newval))
            {
              newval = NULL_TREE;
              break;
            }
          else
            newval = t;
+         first = false;
        }
 
       if (newval)
@@ -3226,7 +3236,7 @@ find_more_contexts_for_caller_subset (cgraph_node *node,
        continue;
 
       ipa_polymorphic_call_context newval;
-      bool found = false;
+      bool first = true;
       int j;
 
       FOR_EACH_VEC_ELT (callers, j, cs)
@@ -3238,21 +3248,18 @@ find_more_contexts_for_caller_subset (cgraph_node *node,
          ipa_polymorphic_call_context ctx;
          ctx = ipa_context_from_jfunc (IPA_NODE_REF (cs->caller), cs, i,
                                        jfunc);
-         ctx.clear_speculation ();
-         if (ctx.useless_p ()
-             || (found && !values_equal_for_ipcp_p (newval, ctx)))
-           {
-             found = false;
-             break;
-           }
-         else if (!found)
+         if (first)
            {
-             found = true;
              newval = ctx;
+             first = false;
            }
+         else
+           newval.meet_with (ctx);
+         if (newval.useless_p ())
+           break;
        }
 
-      if (found)
+      if (!newval.useless_p ())
        {
          if (dump_file && (dump_flags & TDF_DETAILS))
            {
index f1905f1609aec2cf6ad99e8deed15536b38ce43c..452f2d26a5a50e9c0774a23892991363733c1826 100644 (file)
@@ -1727,7 +1727,7 @@ 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)
+                                                       tree otr_type) const
 {
   if (!flag_devirtualize_speculatively)
     return false;
@@ -1873,6 +1873,102 @@ ipa_polymorphic_call_context::combine_speculation_with
   return false;
 }
 
+/* Make speculation less specific so
+   NEW_OUTER_TYPE, NEW_OFFSET, NEW_MAYBE_DERIVED_TYPE is also included.
+   If OTR_TYPE is set, assume the context is used with OTR_TYPE.  */
+
+bool
+ipa_polymorphic_call_context::meet_speculation_with
+   (tree new_outer_type, HOST_WIDE_INT new_offset, bool new_maybe_derived_type,
+    tree otr_type)
+{
+  if (!new_outer_type && speculative_outer_type)
+    {
+      clear_speculation ();
+      return true;
+    }
+
+  /* restrict_to_inner_class may eliminate wrong speculation making our job
+     easeier.  */
+  if (otr_type)
+    restrict_to_inner_class (otr_type);
+
+  if (!speculative_outer_type
+      || !speculation_consistent_p (speculative_outer_type,
+                                   speculative_offset,
+                                   speculative_maybe_derived_type,
+                                   otr_type))
+    return false;
+
+  if (!speculation_consistent_p (new_outer_type, new_offset,
+                                new_maybe_derived_type, otr_type))
+    {
+      clear_speculation ();
+      return true;
+    }
+
+  else if (types_must_be_same_for_odr (speculative_outer_type,
+                                      new_outer_type))
+    {
+      if (speculative_offset != new_offset)
+       {
+         clear_speculation ();
+         return true;
+       }
+      else
+       {
+         if (!speculative_maybe_derived_type && new_maybe_derived_type)
+           {
+             speculative_maybe_derived_type = true;
+             return true;
+           }
+         else
+           return false;
+       }
+    }
+  /* See if one type contains the other as a field (not base).  */
+  else if (contains_type_p (new_outer_type, new_offset - speculative_offset,
+                           speculative_outer_type, false, false))
+    return false;
+  else if (contains_type_p (speculative_outer_type,
+                           speculative_offset - new_offset,
+                           new_outer_type, false, false))
+    {
+      speculative_outer_type = new_outer_type;
+      speculative_offset = new_offset;
+      speculative_maybe_derived_type = new_maybe_derived_type;
+      return true;
+    }
+  /* See if OUTER_TYPE is base of CTX.OUTER_TYPE.  */
+  else if (contains_type_p (new_outer_type,
+                           new_offset - speculative_offset,
+                           speculative_outer_type, false, true))
+    {
+      if (!speculative_maybe_derived_type)
+       {
+         speculative_maybe_derived_type = true;
+         return true;
+       }
+      return false;
+    }
+  /* See if CTX.OUTER_TYPE is base of OUTER_TYPE.  */
+  else if (contains_type_p (speculative_outer_type,
+                           speculative_offset - new_offset, new_outer_type, false, true))
+    {
+      speculative_outer_type = new_outer_type;
+      speculative_offset = new_offset;
+      speculative_maybe_derived_type = true;
+      return true;
+    }
+  else
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+        fprintf (dump_file, "Giving up on speculative meet\n");
+      clear_speculation ();
+      return true;
+    }
+}
+
 /* 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
@@ -2065,6 +2161,16 @@ ipa_polymorphic_call_context::combine_with (ipa_polymorphic_call_context ctx,
                fprintf (dump_file, "First context does not permit base -> invalid\n");
              goto invalidate;
            }
+         /* Pick the base type.  */
+         else if (maybe_in_construction)
+           {
+             outer_type = ctx.outer_type;
+             maybe_in_construction = ctx.maybe_in_construction;
+             maybe_derived_type = ctx.maybe_derived_type;
+             offset = ctx.offset;
+             dynamic = ctx.dynamic;
+             updated = true;
+           }
        }
     }
   /* TODO handle merging using hiearchy. */
@@ -2160,19 +2266,220 @@ ipa_polymorphic_call_context::equal_to
   else if (x.outer_type)
     return false;
 
-  if (speculative_outer_type)
+
+  if (speculative_outer_type
+      && speculation_consistent_p (speculative_outer_type, speculative_offset,
+                                  speculative_maybe_derived_type, NULL_TREE))
     {
-      if (!x.speculative_outer_type
-         || !types_odr_comparable (speculative_outer_type,
-                                   x.speculative_outer_type)
+      if (!x.speculative_outer_type)
+       return false;
+
+      if (!types_odr_comparable (speculative_outer_type,
+                                x.speculative_outer_type)
          || !types_same_for_odr  (speculative_outer_type,
-                                   x.speculative_outer_type)
+                                  x.speculative_outer_type)
          || speculative_offset != x.speculative_offset
          || speculative_maybe_derived_type != x.speculative_maybe_derived_type)
        return false;
     }
-  else if (x.speculative_outer_type)
+  else if (x.speculative_outer_type
+          && x.speculation_consistent_p (x.speculative_outer_type,
+                                         x.speculative_offset,
+                                         x.speculative_maybe_derived_type,
+                                         NULL))
     return false;
 
   return true;
 }
+
+/* Modify context to be strictly less restrictive than CTX.  */
+
+bool
+ipa_polymorphic_call_context::meet_with (ipa_polymorphic_call_context ctx,
+                                        tree otr_type)
+{
+  bool updated = false;
+
+  if (useless_p () || ctx.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 && !useless_p () && !ctx.useless_p ())
+    {
+      restrict_to_inner_class (otr_type);
+      ctx.restrict_to_inner_class (otr_type);
+      if(invalid)
+        return false;
+    }
+
+  if (equal_to (ctx))
+    return false;
+
+  if (ctx.useless_p () || invalid)
+    {
+      *this = ctx;
+      return true;
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "Polymorphic call context meet:");
+      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 (!dynamic && ctx.dynamic)
+    {
+      dynamic = true;
+      updated = true;
+    }
+
+  /* If call is known to be invalid, we are done.  */
+  if (!outer_type)
+    ;
+  else if (!ctx.outer_type)
+    {
+      clear_outer_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 -> clearing\n");
+         clear_outer_type ();
+         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 = true;
+       }
+      if (!maybe_derived_type && ctx.maybe_derived_type)
+       {
+         updated = true;
+         maybe_derived_type = true;
+       }
+      if (!dynamic && ctx.dynamic)
+       {
+         updated = true;
+         dynamic = true;
+       }
+    }
+  /* See if one type contains the other as a field (not base).  */
+  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");
+
+      /* The second type is more specified, so we keep the first.
+         We need to set DYNAMIC flag to avoid declaring context INVALID
+        of OFFSET ends up being out of range.  */
+      if (!dynamic
+         && (ctx.dynamic
+             || (!otr_type
+                 && (!TYPE_SIZE (ctx.outer_type)
+                     || !TYPE_SIZE (outer_type)
+                     || !operand_equal_p (TYPE_SIZE (ctx.outer_type),
+                                          TYPE_SIZE (outer_type), 0)))))
+       {
+         dynamic = true;
+         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 (!dynamic
+         && (ctx.dynamic
+             || (!otr_type
+                 && (!TYPE_SIZE (ctx.outer_type)
+                     || !TYPE_SIZE (outer_type)
+                     || !operand_equal_p (TYPE_SIZE (ctx.outer_type),
+                                          TYPE_SIZE (outer_type), 0)))))
+       dynamic = true;
+      outer_type = ctx.outer_type;
+      offset = ctx.offset;
+      dynamic = ctx.dynamic;
+      maybe_in_construction = ctx.maybe_in_construction;
+      maybe_derived_type = ctx.maybe_derived_type;
+      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)
+       {
+         maybe_derived_type = true;
+         updated = true;
+       }
+      if (!maybe_in_construction && ctx.maybe_in_construction)
+       {
+         maybe_in_construction = true;
+         updated = true;
+       }
+      if (!dynamic && ctx.dynamic)
+       {
+         dynamic = true;
+         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");
+      outer_type = ctx.outer_type;
+      offset = ctx.offset;
+      updated = true;
+      if (!maybe_derived_type)
+       maybe_derived_type = true;
+      if (!maybe_in_construction && ctx.maybe_in_construction)
+       maybe_in_construction = true;
+      if (!dynamic && ctx.dynamic)
+       dynamic = true;
+    }
+  /* TODO handle merging using hiearchy. */
+  else
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+        fprintf (dump_file, "Giving up on meet\n");
+      clear_outer_type ();
+      updated = true;
+    }
+
+  updated |= meet_speculation_with (ctx.speculative_outer_type,
+                                   ctx.speculative_offset,
+                                   ctx.speculative_maybe_derived_type,
+                                   otr_type);
+
+  if (updated && dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "Updated as:                      ");
+      dump (dump_file);
+      fprintf (dump_file, "\n");
+    }
+  return updated;
+}