]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
ipa-devirt.c: Include gimple-pretty-print.h
authorJan Hubicka <hubicka@ucw.cz>
Thu, 7 Aug 2014 20:58:17 +0000 (22:58 +0200)
committerJan Hubicka <hubicka@gcc.gnu.org>
Thu, 7 Aug 2014 20:58:17 +0000 (20:58 +0000)
* ipa-devirt.c: Include gimple-pretty-print.h
(referenced_from_vtable_p): Exclude DECL_EXTERNAL from
further tests.
(decl_maybe_in_construction_p): Fix conditional on cdtor check
(get_polymorphic_call_info): Fix return value
(type_change_info): New sturcture based on ipa-prop
variant.
(noncall_stmt_may_be_vtbl_ptr_store): New predicate
based on ipa-prop variant.
(extr_type_from_vtbl_ptr_store): New function
based on ipa-prop variant.
(record_known_type): New function.
(check_stmt_for_type_change): New function.
(get_dynamic_type): New function.
* ipa-prop.c (ipa_analyze_call_uses): Use get_dynamic_type.
* tree-ssa-pre.c: ipa-utils.h
(eliminate_dom_walker::before_dom_children): Use ipa-devirt
machinery; sanity check with ipa-prop devirtualization.
* trans-mem.c (ipa_tm_insert_gettmclone_call): Clear
polymorphic flag.

* g++.dg/ipa/devirt-35.C: New testcase.
* g++.dg/ipa/devirt-36.C: New testcase.

From-SVN: r213739

gcc/ChangeLog
gcc/ipa-devirt.c
gcc/ipa-prop.c
gcc/ipa-utils.h
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ipa/devirt-35.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ipa/devirt-36.C [new file with mode: 0644]
gcc/trans-mem.c
gcc/tree-ssa-pre.c

index 8eba08d457f4e00ca8c22d9e778080f5421e7c70..beee78f21afe058ebaac513a29ef1f9e6edf2104 100644 (file)
@@ -1,3 +1,26 @@
+2014-08-07  Jan Hubicka  <hubicka@ucw.cz>
+
+       * ipa-devirt.c: Include gimple-pretty-print.h
+       (referenced_from_vtable_p): Exclude DECL_EXTERNAL from
+       further tests.
+       (decl_maybe_in_construction_p): Fix conditional on cdtor check
+       (get_polymorphic_call_info): Fix return value
+       (type_change_info): New sturcture based on ipa-prop
+       variant.
+       (noncall_stmt_may_be_vtbl_ptr_store): New predicate
+       based on ipa-prop variant.
+       (extr_type_from_vtbl_ptr_store): New function
+       based on ipa-prop variant.
+       (record_known_type): New function.
+       (check_stmt_for_type_change): New function.
+       (get_dynamic_type): New function.
+       * ipa-prop.c (ipa_analyze_call_uses): Use get_dynamic_type.
+       * tree-ssa-pre.c: ipa-utils.h
+       (eliminate_dom_walker::before_dom_children): Use ipa-devirt
+       machinery; sanity check with ipa-prop devirtualization.
+       * trans-mem.c (ipa_tm_insert_gettmclone_call): Clear
+       polymorphic flag.
+
 2014-08-07  Trevor Saunders  <tsaunders@mozilla.com>
 
        * Makefile.in: Remove references to pointer-set.c and pointer-set.h.
index 56eeaf5c9c2d05aa671754e780cd5e803b6171c4..8827d0ecb523db8b585a2074896cac75829dd8c9 100644 (file)
@@ -131,6 +131,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-dfa.h"
 #include "demangle.h"
 #include "dbgcnt.h"
+#include "gimple-pretty-print.h"
 #include "stor-layout.h"
 #include "intl.h"
 #include "hash-map.h"
@@ -1323,6 +1324,7 @@ referenced_from_vtable_p (struct cgraph_node *node)
   bool found = false;
 
   if (node->externally_visible
+      || DECL_EXTERNAL (node->decl)
       || node->used_from_other_partition)
     return true;
 
@@ -2113,7 +2115,7 @@ decl_maybe_in_construction_p (tree base, tree outer_type,
 
        if (TREE_CODE (TREE_TYPE (fn)) != METHOD_TYPE
            || (!DECL_CXX_CONSTRUCTOR_P (fn)
-               || !DECL_CXX_DESTRUCTOR_P (fn)))
+               && !DECL_CXX_DESTRUCTOR_P (fn)))
          {
            /* Watch for clones where we constant propagated the first
               argument (pointer to the instance).  */
@@ -2122,7 +2124,7 @@ decl_maybe_in_construction_p (tree base, tree outer_type,
                || !is_global_var (base)
                || TREE_CODE (TREE_TYPE (fn)) != METHOD_TYPE
                || (!DECL_CXX_CONSTRUCTOR_P (fn)
-                   || !DECL_CXX_DESTRUCTOR_P (fn)))
+                   && !DECL_CXX_DESTRUCTOR_P (fn)))
              continue;
          }
        if (flags_from_decl_or_type (fn) & (ECF_PURE | ECF_CONST))
@@ -2142,7 +2144,7 @@ decl_maybe_in_construction_p (tree base, tree outer_type,
     {
       if (TREE_CODE (TREE_TYPE (function)) != METHOD_TYPE
          || (!DECL_CXX_CONSTRUCTOR_P (function)
-             || !DECL_CXX_DESTRUCTOR_P (function)))
+             && !DECL_CXX_DESTRUCTOR_P (function)))
        {
          if (!DECL_ABSTRACT_ORIGIN (function))
            return false;
@@ -2152,7 +2154,7 @@ decl_maybe_in_construction_p (tree base, tree outer_type,
          if (!function
              || TREE_CODE (TREE_TYPE (function)) != METHOD_TYPE
              || (!DECL_CXX_CONSTRUCTOR_P (function)
-                 || !DECL_CXX_DESTRUCTOR_P (function)))
+                 && !DECL_CXX_DESTRUCTOR_P (function)))
            return false;
        }
       /* FIXME: this can go away once we have ODR types equivalency on
@@ -2243,7 +2245,8 @@ walk_ssa_copies (tree op)
    call (OTR_TYPE), its token (OTR_TOKEN) and CONTEXT.
    CALL is optional argument giving the actual statement (usually call) where
    the context is used.
-   Return pointer to object described by the context  */
+   Return pointer to object described by the context or an declaration if
+   we found the instance to be stored in the static storage.  */
 
 tree
 get_polymorphic_call_info (tree fndecl,
@@ -2317,7 +2320,7 @@ get_polymorphic_call_info (tree fndecl,
                                                     context->outer_type,
                                                     call,
                                                     current_function_decl);
-                 return base_pointer;
+                 return base;
                }
              else
                break;
@@ -2436,6 +2439,515 @@ get_polymorphic_call_info (tree fndecl,
   return base_pointer;
 }
 
+/* Structure to be passed in between detect_type_change and
+   check_stmt_for_type_change.  */
+
+struct type_change_info
+{
+  /* Offset into the object where there is the virtual method pointer we are
+     looking for.  */
+  HOST_WIDE_INT offset;
+  /* The declaration or SSA_NAME pointer of the base that we are checking for
+     type change.  */
+  tree instance;
+  /* The reference to virtual table pointer used.  */
+  tree vtbl_ptr_ref;
+  tree otr_type;
+  /* If we actually can tell the type that the object has changed to, it is
+     stored in this field.  Otherwise it remains NULL_TREE.  */
+  tree known_current_type;
+  HOST_WIDE_INT known_current_offset;
+
+  /* Set to true if dynamic type change has been detected.  */
+  bool type_maybe_changed;
+  /* Set to true if multiple types have been encountered.  known_current_type
+     must be disregarded in that case.  */
+  bool multiple_types_encountered;
+  /* Set to true if we possibly missed some dynamic type changes and we should
+     consider the set to be speculative.  */
+  bool speculative;
+  bool seen_unanalyzed_store;
+};
+
+/* Return true if STMT is not call and can modify a virtual method table pointer.
+   We take advantage of fact that vtable stores must appear within constructor
+   and destructor functions.  */
+
+bool
+noncall_stmt_may_be_vtbl_ptr_store (gimple stmt)
+{
+  if (is_gimple_assign (stmt))
+    {
+      tree lhs = gimple_assign_lhs (stmt);
+
+      if (gimple_clobber_p (stmt))
+       return false;
+      if (!AGGREGATE_TYPE_P (TREE_TYPE (lhs)))
+       {
+         if (flag_strict_aliasing
+             && !POINTER_TYPE_P (TREE_TYPE (lhs)))
+           return false;
+
+         if (TREE_CODE (lhs) == COMPONENT_REF
+             && !DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1)))
+           return false;
+         /* In the future we might want to use get_base_ref_and_offset to find
+            if there is a field corresponding to the offset and if so, proceed
+            almost like if it was a component ref.  */
+       }
+    }
+
+  /* Code unification may mess with inline stacks.  */
+  if (cfun->after_inlining)
+    return true;
+
+  /* Walk the inline stack and watch out for ctors/dtors.
+     TODO: Maybe we can require the store to appear in toplevel
+     block of CTOR/DTOR.  */
+  for (tree block = gimple_block (stmt); block && TREE_CODE (block) == BLOCK;
+       block = BLOCK_SUPERCONTEXT (block))
+    if (BLOCK_ABSTRACT_ORIGIN (block)
+       && TREE_CODE (BLOCK_ABSTRACT_ORIGIN (block)) == FUNCTION_DECL)
+      {
+       tree fn = BLOCK_ABSTRACT_ORIGIN (block);
+
+       if (flags_from_decl_or_type (fn) & (ECF_PURE | ECF_CONST))
+         return false;
+       return (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE
+               && (DECL_CXX_CONSTRUCTOR_P (fn)
+                   || DECL_CXX_DESTRUCTOR_P (fn)));
+      }
+  return (TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE
+         && (DECL_CXX_CONSTRUCTOR_P (current_function_decl)
+             || DECL_CXX_DESTRUCTOR_P (current_function_decl)));
+}
+
+/* If STMT can be proved to be an assignment to the virtual method table
+   pointer of ANALYZED_OBJ and the type associated with the new table
+   identified, return the type.  Otherwise return NULL_TREE.  */
+
+static tree
+extr_type_from_vtbl_ptr_store (gimple stmt, struct type_change_info *tci,
+                              HOST_WIDE_INT *type_offset)
+{
+  HOST_WIDE_INT offset, size, max_size;
+  tree lhs, rhs, base, binfo;
+
+  if (!gimple_assign_single_p (stmt))
+    return NULL_TREE;
+
+  lhs = gimple_assign_lhs (stmt);
+  rhs = gimple_assign_rhs1 (stmt);
+  if (TREE_CODE (lhs) != COMPONENT_REF
+      || !DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1)))
+    return NULL_TREE;
+
+  if (tci->vtbl_ptr_ref && operand_equal_p (lhs, tci->vtbl_ptr_ref, 0))
+    ;
+  else
+    {
+      base = get_ref_base_and_extent (lhs, &offset, &size, &max_size);
+      if (offset != tci->offset
+         || size != POINTER_SIZE
+         || max_size != POINTER_SIZE)
+       return NULL_TREE;
+      if (DECL_P (tci->instance))
+       {
+         if (base != tci->instance)
+           return NULL_TREE;
+       }
+      else if (TREE_CODE (base) == MEM_REF)
+       {
+         if (!operand_equal_p (tci->instance, TREE_OPERAND (base, 0), 0)
+             || !integer_zerop (TREE_OPERAND (base, 1)))
+           return NULL_TREE;
+       }
+      else if (!operand_equal_p (tci->instance, base, 0)
+              || tci->offset)
+       return NULL_TREE;
+    }
+
+  binfo = vtable_pointer_value_to_binfo (rhs);
+
+  if (!binfo)
+    return NULL;
+  *type_offset = tree_to_shwi (BINFO_OFFSET (binfo)) * BITS_PER_UNIT;
+  if (TYPE_BINFO (BINFO_TYPE (binfo)) == binfo)
+    return BINFO_TYPE (binfo);
+
+  /* TODO: Figure out the type containing BINFO.  */
+  return NULL;
+}
+
+/* Record dynamic type change of TCI to TYPE.  */
+
+void
+record_known_type (struct type_change_info *tci, tree type, HOST_WIDE_INT offset)
+{
+  if (dump_file)
+    {
+      if (type)
+       {
+          fprintf (dump_file, "  Recording type: ");
+         print_generic_expr (dump_file, type, TDF_SLIM);
+          fprintf (dump_file, " at offset %i\n", (int)offset);
+       }
+     else
+       fprintf (dump_file, "  Recording unknown type\n");
+    }
+  if (tci->type_maybe_changed
+      && (type != tci->known_current_type
+         || offset != tci->known_current_offset))
+    tci->multiple_types_encountered = true;
+  tci->known_current_type = type;
+  tci->known_current_offset = offset;
+  tci->type_maybe_changed = true;
+}
+
+/* Callback of walk_aliased_vdefs and a helper function for
+   detect_type_change to check whether a particular statement may modify
+   the virtual table pointer, and if possible also determine the new type of
+   the (sub-)object.  It stores its result into DATA, which points to a
+   type_change_info structure.  */
+
+static bool
+check_stmt_for_type_change (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef, void *data)
+{
+  gimple stmt = SSA_NAME_DEF_STMT (vdef);
+  struct type_change_info *tci = (struct type_change_info *) data;
+  tree fn;
+
+  /* If we already gave up, just terminate the rest of walk.  */
+  if (tci->multiple_types_encountered)
+    return true;
+
+  if (is_gimple_call (stmt))
+    {
+      if (gimple_call_flags (stmt) & (ECF_CONST | ECF_PURE))
+       return false;
+
+      /* Check for a constructor call.  */
+      if ((fn = gimple_call_fndecl (stmt)) != NULL_TREE
+         && DECL_CXX_CONSTRUCTOR_P (fn)
+         && TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE
+         && gimple_call_num_args (stmt))
+      {
+       tree op = walk_ssa_copies (gimple_call_arg (stmt, 0));
+       tree type = method_class_type (TREE_TYPE (fn));
+       HOST_WIDE_INT offset = 0, size, max_size;
+
+       if (dump_file)
+         {
+           fprintf (dump_file, "  Checking constructor call: ");
+           print_gimple_stmt (dump_file, stmt, 0, 0);
+         }
+
+       /* See if THIS parameter seems like instance pointer.  */
+       if (TREE_CODE (op) == ADDR_EXPR)
+         {
+           op = get_ref_base_and_extent (TREE_OPERAND (op, 0),
+                                         &offset, &size, &max_size);
+           if (size != max_size || max_size == -1)
+             {
+                tci->speculative = true;
+               return false;
+             }
+           if (op && TREE_CODE (op) == MEM_REF)
+             {
+               if (!tree_fits_shwi_p (TREE_OPERAND (op, 1)))
+                 {
+                    tci->speculative = true;
+                   return false;
+                 }
+               offset += tree_to_shwi (TREE_OPERAND (op, 1))
+                         * BITS_PER_UNIT;
+               op = TREE_OPERAND (op, 0);
+             }
+           else
+             {
+                tci->speculative = true;
+               return false;
+             }
+           op = walk_ssa_copies (op);
+         }
+       if (operand_equal_p (op, tci->instance, 0)
+           && TYPE_SIZE (type)
+           && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
+           && tree_fits_shwi_p (TYPE_SIZE (type))
+           && tree_to_shwi (TYPE_SIZE (type)) + offset > tci->offset)
+         {
+           record_known_type (tci, type, tci->offset - offset);
+           return true;
+         }
+      }
+     /* Calls may possibly change dynamic type by placement new. Assume
+        it will not happen, but make result speculative only.  */
+     if (dump_file)
+       {
+          fprintf (dump_file, "  Function call may change dynamic type:");
+         print_gimple_stmt (dump_file, stmt, 0, 0);
+       }
+     tci->speculative = true;
+     return false;
+   }
+  /* Check for inlined virtual table store.  */
+  else if (noncall_stmt_may_be_vtbl_ptr_store (stmt))
+    {
+      tree type;
+      HOST_WIDE_INT offset = 0;
+      if (dump_file)
+       {
+         fprintf (dump_file, "  Checking vtbl store: ");
+         print_gimple_stmt (dump_file, stmt, 0, 0);
+       }
+
+      type = extr_type_from_vtbl_ptr_store (stmt, tci, &offset);
+      gcc_assert (!type || TYPE_MAIN_VARIANT (type) == type);
+      if (!type)
+       {
+         if (dump_file)
+           fprintf (dump_file, "  Unanalyzed store may change type.\n");
+         tci->seen_unanalyzed_store = true;
+         tci->speculative = true;
+       }
+      else
+        record_known_type (tci, type, offset);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* CONTEXT is polymorphic call context obtained from get_polymorphic_context.
+   OTR_OBJECT is pointer to the instance returned by OBJ_TYPE_REF_OBJECT.
+   INSTANCE is pointer to the outer instance as returned by
+   get_polymorphic_context.  To avoid creation of temporary expressions,
+   INSTANCE may also be an declaration of get_polymorphic_context found the
+   value to be in static storage.
+
+   If the type of instance is not fully determined
+   (either OUTER_TYPE is unknown or MAYBE_IN_CONSTRUCTION/INCLUDE_DERIVED_TYPES
+   is set), try to walk memory writes and find the actual construction of the
+   instance.
+
+   We do not include this analysis in the context analysis itself, because
+   it needs memory SSA to be fully built and the walk may be expensive.
+   So it is not suitable for use withing fold_stmt and similar uses.  */
+
+bool
+get_dynamic_type (tree instance,
+                 ipa_polymorphic_call_context *context,
+                 tree otr_object,
+                 tree otr_type,
+                 gimple call)
+{
+  struct type_change_info tci;
+  ao_ref ao;
+  bool function_entry_reached = false;
+  tree instance_ref = NULL;
+  gimple stmt = call;
+
+  if (!context->maybe_in_construction && !context->maybe_derived_type)
+    return false;
+
+  /* We need to obtain refernce to virtual table pointer.  It is better
+     to look it up in the code rather than build our own.  This require bit
+     of pattern matching, but we end up verifying that what we found is
+     correct. 
+
+     What we pattern match is:
+
+       tmp = instance->_vptr.A;   // vtbl ptr load
+       tmp2 = tmp[otr_token];    // vtable lookup
+       OBJ_TYPE_REF(tmp2;instance->0) (instance);
+     We want to start alias oracle walk from vtbl pointer load,
+     but we may not be able to identify it, for example, when PRE moved the
+     load around.  */
+
+  if (gimple_code (call) == GIMPLE_CALL)
+    {
+      tree ref = gimple_call_fn (call);
+      HOST_WIDE_INT offset2, size, max_size;
+
+      if (TREE_CODE (ref) == OBJ_TYPE_REF)
+       {
+         ref = OBJ_TYPE_REF_EXPR (ref);
+         ref = walk_ssa_copies (ref);
+
+         /* Check if definition looks like vtable lookup.  */
+         if (TREE_CODE (ref) == SSA_NAME
+             && !SSA_NAME_IS_DEFAULT_DEF (ref)
+             && gimple_assign_load_p (SSA_NAME_DEF_STMT (ref))
+             && TREE_CODE (gimple_assign_rhs1
+                            (SSA_NAME_DEF_STMT (ref))) == MEM_REF)
+           {
+             ref = get_base_address
+                    (TREE_OPERAND (gimple_assign_rhs1
+                                    (SSA_NAME_DEF_STMT (ref)), 0));
+             ref = walk_ssa_copies (ref);
+             /* Find base address of the lookup and see if it looks like
+                vptr load.  */
+             if (TREE_CODE (ref) == SSA_NAME
+                 && !SSA_NAME_IS_DEFAULT_DEF (ref)
+                 && gimple_assign_load_p (SSA_NAME_DEF_STMT (ref)))
+               {
+                 tree ref_exp = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (ref));
+                 tree base_ref = get_ref_base_and_extent
+                                  (ref_exp, &offset2, &size, &max_size);
+
+                 /* Finally verify that what we found looks like read from OTR_OBJECT
+                    or from INSTANCE with offset OFFSET.  */
+                 if (base_ref
+                     && TREE_CODE (base_ref) == MEM_REF
+                     && ((offset2 == context->offset
+                          && TREE_OPERAND (base_ref, 0) == instance)
+                         || (!offset2 && TREE_OPERAND (base_ref, 0) == otr_object)))
+                   {
+                     stmt = SSA_NAME_DEF_STMT (ref);
+                     instance_ref = ref_exp;
+                   }
+               }
+           }
+       }
+    }
+  /* If we failed to look up the refernece in code, build our own.  */
+  if (!instance_ref)
+    {
+      /* If the statement in question does not use memory, we can't tell
+        anything.  */
+      if (!gimple_vuse (stmt))
+       return false;
+      ao_ref_init_from_ptr_and_size (&ao, otr_object, NULL);
+    }
+  else
+  /* Otherwise use the real reference.  */
+    ao_ref_init (&ao, instance_ref);
+
+  /* We look for vtbl pointer read.  */
+  ao.size = POINTER_SIZE;
+  ao.max_size = ao.size;
+  ao.ref_alias_set
+    = get_deref_alias_set (TREE_TYPE (BINFO_VTABLE (TYPE_BINFO (otr_type))));
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "Determining dynamic type for call: ");
+      print_gimple_stmt (dump_file, call, 0, 0);
+      fprintf (dump_file, "  Starting walk at: ");
+      print_gimple_stmt (dump_file, stmt, 0, 0);
+      fprintf (dump_file, "  instance pointer: ");
+      print_generic_expr (dump_file, otr_object, TDF_SLIM);
+      fprintf (dump_file, "  Outer instance pointer: ");
+      print_generic_expr (dump_file, instance, TDF_SLIM);
+      fprintf (dump_file, " offset: %i (bits)", (int)context->offset);
+      fprintf (dump_file, " vtbl reference: ");
+      print_generic_expr (dump_file, instance_ref, TDF_SLIM);
+      fprintf (dump_file, "\n");
+    }
+
+  tci.offset = context->offset;
+  tci.instance = instance;
+  tci.vtbl_ptr_ref = instance_ref;
+  gcc_assert (TREE_CODE (instance) != MEM_REF);
+  tci.known_current_type = NULL_TREE;
+  tci.known_current_offset = 0;
+  tci.otr_type = otr_type;
+  tci.type_maybe_changed = false;
+  tci.multiple_types_encountered = false;
+  tci.speculative = false;
+  tci.seen_unanalyzed_store = false;
+
+  walk_aliased_vdefs (&ao, gimple_vuse (stmt), check_stmt_for_type_change,
+                     &tci, NULL, &function_entry_reached);
+
+  /* If we did not find any type changing statements, we may still drop
+     maybe_in_construction flag if the context already have outer type. 
+
+     Here we make special assumptions about both constructors and
+     destructors which are all the functions that are allowed to alter the
+     VMT pointers.  It assumes that destructors begin with assignment into
+     all VMT pointers and that constructors essentially look in the
+     following way:
+
+     1) The very first thing they do is that they call constructors of
+     ancestor sub-objects that have them.
+
+     2) Then VMT pointers of this and all its ancestors is set to new
+     values corresponding to the type corresponding to the constructor.
+
+     3) Only afterwards, other stuff such as constructor of member
+     sub-objects and the code written by the user is run.  Only this may
+     include calling virtual functions, directly or indirectly.
+
+     4) placement new can not be used to change type of non-POD statically
+     allocated variables.
+
+     There is no way to call a constructor of an ancestor sub-object in any
+     other way.
+
+     This means that we do not have to care whether constructors get the
+     correct type information because they will always change it (in fact,
+     if we define the type to be given by the VMT pointer, it is undefined).
+
+     The most important fact to derive from the above is that if, for some
+     statement in the section 3, we try to detect whether the dynamic type
+     has changed, we can safely ignore all calls as we examine the function
+     body backwards until we reach statements in section 2 because these
+     calls cannot be ancestor constructors or destructors (if the input is
+     not bogus) and so do not change the dynamic type (this holds true only
+     for automatically allocated objects but at the moment we devirtualize
+     only these).  We then must detect that statements in section 2 change
+     the dynamic type and can try to derive the new type.  That is enough
+     and we can stop, we will never see the calls into constructors of
+     sub-objects in this code. 
+
+     Therefore if the static outer type was found (context->outer_type)
+     we can safely ignore tci.speculative that is set on calls and give up
+     only if there was dyanmic type store that may affect given variable
+     (seen_unanalyzed_store)  */
+
+  if (!tci.type_maybe_changed)
+    {
+      if (!context->outer_type || tci.seen_unanalyzed_store)
+       return false;
+      if (context->maybe_in_construction)
+        context->maybe_in_construction = false;
+      if (dump_file)
+       fprintf (dump_file, "  No dynamic type change found.\n");
+      return true;
+    }
+
+  if (tci.known_current_type
+      && !function_entry_reached
+      && !tci.multiple_types_encountered)
+    {
+      if (!tci.speculative)
+       {
+         context->outer_type = tci.known_current_type;
+         context->offset = tci.known_current_offset;
+         context->maybe_in_construction = false;
+         context->maybe_derived_type = false;
+         if (dump_file)
+           fprintf (dump_file, "  Determined dynamic type.\n");
+       }
+      else if (!context->speculative_outer_type
+              || context->speculative_maybe_derived_type)
+       {
+         context->speculative_outer_type = tci.known_current_type;
+         context->speculative_offset = tci.known_current_offset;
+         context->speculative_maybe_derived_type = false;
+         if (dump_file)
+           fprintf (dump_file, "  Determined speculative dynamic type.\n");
+       }
+    }
+  else if (dump_file)
+    fprintf (dump_file, "  Found multiple types.\n");
+
+  return true;
+}
+
 /* Walk bases of OUTER_TYPE that contain OTR_TYPE at OFFSET.
    Lookup their respecitve virtual methods for OTR_TOKEN and OTR_TYPE
    and insert them to NODES.
@@ -2516,6 +3028,7 @@ devirt_variable_node_removal_hook (varpool_node *n,
 }
 
 /* Record about how many calls would benefit from given type to be final.  */
+
 struct odr_type_warn_count
 {
   tree type;
@@ -2524,6 +3037,7 @@ struct odr_type_warn_count
 };
 
 /* Record about how many calls would benefit from given method to be final.  */
+
 struct decl_warn_count
 {
   tree decl;
@@ -2532,6 +3046,7 @@ struct decl_warn_count
 };
 
 /* Information about type and decl warnings.  */
+
 struct final_warning_record
 {
   gcov_type dyn_count;
index 4b309b97978fc4c1683398e21d828c18212493a3..3e975d6d497cc94eda979ee4424983b95fa797ee 100644 (file)
@@ -2337,11 +2337,46 @@ ipa_analyze_call_uses (struct func_body_info *fbi, gimple call)
           && !virtual_method_call_p (target)))
     return;
 
+  struct cgraph_edge *cs = fbi->node->get_edge (call);
   /* If we previously turned the call into a direct call, there is
      no need to analyze.  */
-  struct cgraph_edge *cs = fbi->node->get_edge (call);
   if (cs && !cs->indirect_unknown_callee)
     return;
+
+  if (cs->indirect_info->polymorphic)
+    {
+      tree otr_type;
+      HOST_WIDE_INT otr_token;
+      ipa_polymorphic_call_context context;
+      tree instance;
+      tree target = gimple_call_fn (call);
+
+      instance = get_polymorphic_call_info (current_function_decl,
+                                           target,
+                                           &otr_type, &otr_token,
+                                           &context, call);
+
+      if (get_dynamic_type (instance, &context,
+                           OBJ_TYPE_REF_OBJECT (target),
+                           otr_type, call))
+       {
+         gcc_assert (TREE_CODE (otr_type) == RECORD_TYPE);
+         cs->indirect_info->polymorphic = true;
+         cs->indirect_info->param_index = -1;
+         cs->indirect_info->otr_token = otr_token;
+         cs->indirect_info->otr_type = otr_type;
+         cs->indirect_info->outer_type = context.outer_type;
+         cs->indirect_info->speculative_outer_type = context.speculative_outer_type;
+         cs->indirect_info->offset = context.offset;
+         cs->indirect_info->speculative_offset = context.speculative_offset;
+         cs->indirect_info->maybe_in_construction
+            = context.maybe_in_construction;
+         cs->indirect_info->maybe_derived_type = context.maybe_derived_type;
+         cs->indirect_info->speculative_maybe_derived_type
+            = context.speculative_maybe_derived_type;
+       }
+    }
+
   if (TREE_CODE (target) == SSA_NAME)
     ipa_analyze_indirect_call_uses (fbi, call, target);
   else if (virtual_method_call_p (target))
index 12543048289a295c142a507ba519b38f0f79f59d..3801525ae7d28532d811cb0177c48932ad5d3ba2 100644 (file)
@@ -95,7 +95,7 @@ tree get_polymorphic_call_info (tree, tree, tree *,
                                HOST_WIDE_INT *,
                                ipa_polymorphic_call_context *,
                                gimple call = NULL);
-bool get_dynamic_type (tree, ipa_polymorphic_call_context *, tree, gimple);
+bool get_dynamic_type (tree, ipa_polymorphic_call_context *, tree, tree, gimple);
 bool get_polymorphic_call_info_from_invariant (ipa_polymorphic_call_context *,
                                               tree, tree, HOST_WIDE_INT);
 bool decl_maybe_in_construction_p (tree, tree, gimple, tree);
index 16956b781abb4afe9055bc643ec24f87ab0f1ce4..c62648022351946344d33cd3e2f4d1f1eb4c2940 100644 (file)
@@ -1,3 +1,8 @@
+2014-08-07  Jan Hubicka  <hubicka@ucw.cz>
+
+       * g++.dg/ipa/devirt-35.C: New testcase.
+       * g++.dg/ipa/devirt-36.C: New testcase.
+
 2014-08-07  Paolo Carlini  <paolo.carlini@oracle.com>
 
        PR c++/51312
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-35.C b/gcc/testsuite/g++.dg/ipa/devirt-35.C
new file mode 100644 (file)
index 0000000..6c30430
--- /dev/null
@@ -0,0 +1,23 @@
+/* { dg-options "-O2 -fdump-ipa-devirt-details -fdump-tree-fre1-details"  } */
+struct A {virtual int t(void) {return 1;}};
+struct B:A {B(); virtual int t(void) {return 2;}};
+void test2(struct A *);
+int
+m(struct B *b)
+{
+  struct A *a = new (B);
+  a->t(); // This call should be devirtualized by 
+          // FRE because we know type from ctor call
+  ((struct B *)a)->B::t(); // Make devirt possible 
+                           // C++ FE won't produce inline body without this
+  test2(a);
+  return a->t();  // This call should be devirtualized speculatively because
+                  //  test2 may change the type of A by placement new.
+                  // C++ standard is bit imprecise about this.
+}
+/* { dg-final { scan-ipa-dump "converting indirect call to function virtual int B::t"  "fre1"  } } */
+/* { dg-final { scan-ipa-dump "to virtual int B::t"  "devirt"  } } */
+/* { dg-final { scan-ipa-dump "1 speculatively devirtualized"  "devirt"  } } */
+/* { dg-final { cleanup-ipa-dump "devirt" } } */
+/* { dg-final { cleanup-tree-dump "fre" } } */
+
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-36.C b/gcc/testsuite/g++.dg/ipa/devirt-36.C
new file mode 100644 (file)
index 0000000..c32531d
--- /dev/null
@@ -0,0 +1,25 @@
+/* { dg-options "-O2 -fdump-ipa-devirt-details -fdump-tree-fre1-details"  } */
+struct A {virtual int t(void) {return 1;}};
+struct B:A {B(); virtual int t(void) {return 2;}};
+struct C {int a; struct B b;};
+void test2(struct A *);
+int
+m(struct B *b)
+{
+  struct C *c = new (C);
+  struct A *a = &c->b;
+  a->t(); // This call should be devirtualized by 
+          // FRE because we know type from ctor call
+  ((struct B *)a)->B::t(); // Make devirt possible 
+                           // C++ FE won't produce inline body without this
+  test2(a);
+  return a->t();  // This call should be devirtualized speculatively because
+                  //  test2 may change the type of A by placement new.
+                  // C++ standard is bit imprecise about this.
+}
+/* { dg-final { scan-ipa-dump "converting indirect call to function virtual int B::t"  "fre1"  } } */
+/* { dg-final { scan-ipa-dump "to virtual int B::t"  "devirt"  } } */
+/* { dg-final { scan-ipa-dump "1 speculatively devirtualized"  "devirt"  } } */
+/* { dg-final { cleanup-ipa-dump "devirt" } } */
+/* { dg-final { cleanup-tree-dump "fre" } } */
+
index 5633398e96b134dddc259ad15dfcabc70f8cebe0..36412319aa95281900d789d53a228b6a632a4ada 100644 (file)
@@ -5042,6 +5042,9 @@ ipa_tm_insert_gettmclone_call (struct cgraph_node *node,
   }
 
   update_stmt (stmt);
+  cgraph_edge *e = cgraph_node::get (current_function_decl)->get_edge (stmt);
+  if (e && e->indirect_info)
+    e->indirect_info->polymorphic = false;
 
   return true;
 }
index 8b4d2badb6f20c98a0bb61d81f72934985ae1301..0e968d5e22ce2dbbb72771fdd4881e40b40caa37 100644 (file)
@@ -64,6 +64,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "domwalk.h"
 #include "ipa-prop.h"
 #include "tree-ssa-propagate.h"
+#include "ipa-utils.h"
 
 /* TODO:
 
@@ -4360,12 +4361,41 @@ eliminate_dom_walker::before_dom_children (basic_block b)
        {
          tree fn = gimple_call_fn (stmt);
          if (fn
-             && TREE_CODE (fn) == OBJ_TYPE_REF
-             && TREE_CODE (OBJ_TYPE_REF_EXPR (fn)) == SSA_NAME)
+             && flag_devirtualize
+             && virtual_method_call_p (fn))
            {
-             fn = ipa_intraprocedural_devirtualization (stmt);
-             if (fn && dbg_cnt (devirt))
+             tree otr_type;
+             HOST_WIDE_INT otr_token;
+             ipa_polymorphic_call_context context;
+             tree instance;
+             bool final;
+
+             instance = get_polymorphic_call_info (current_function_decl,
+                                                   fn,
+                                                   &otr_type, &otr_token, &context, stmt);
+
+             get_dynamic_type (instance, &context,
+                               OBJ_TYPE_REF_OBJECT (fn), otr_type, stmt);
+
+             vec <cgraph_node *>targets
+               = possible_polymorphic_call_targets (obj_type_ref_class (fn),
+                                                    tree_to_uhwi
+                                                      (OBJ_TYPE_REF_TOKEN (fn)),
+                                                    context,
+                                                    &final);
+             if (dump_enabled_p ())
+               dump_possible_polymorphic_call_targets (dump_file, 
+                                                       obj_type_ref_class (fn),
+                                                       tree_to_uhwi
+                                                         (OBJ_TYPE_REF_TOKEN (fn)),
+                                                       context);
+             if (final && targets.length () <= 1 && dbg_cnt (devirt))
                {
+                 tree fn;
+                 if (targets.length () == 1)
+                   fn = targets[0]->decl;
+                 else
+                   fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE);
                  if (dump_enabled_p ())
                    {
                      location_t loc = gimple_location_safe (stmt);
@@ -4377,6 +4407,8 @@ eliminate_dom_walker::before_dom_children (basic_block b)
                  gimple_call_set_fndecl (stmt, fn);
                  gimple_set_modified (stmt, true);
                }
+             else
+               gcc_assert (!ipa_intraprocedural_devirtualization (stmt));
            }
        }