]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
ipa-prop.h (enum ipa_lattice_type): Changed comments.
authorMartin Jambor <mjambor@suse.cz>
Thu, 5 Aug 2010 13:23:07 +0000 (15:23 +0200)
committerMartin Jambor <jamborm@gcc.gnu.org>
Thu, 5 Aug 2010 13:23:07 +0000 (15:23 +0200)
2010-08-05  Martin Jambor  <mjambor@suse.cz>

* ipa-prop.h (enum ipa_lattice_type): Changed comments.
(struct ipa_param_descriptor): New fields types and
cannot_devirtualize.
(ipa_param_cannot_devirtualize_p): New function.
(ipa_param_types_vec_empty): Likewise.
(ipa_make_edge_direct_to_target): Declare.
* ipa-cp.c: Fixed first stage driver name in initial comment,
described devirtualization there too.
(ipcp_analyze_node): Call ipa_analyze_params_uses.
(ipcp_print_all_lattices): Print devirtualization info.
(ipa_set_param_cannot_devirtualize): New function.
(ipcp_initialize_node_lattices): Set cannot_devirtualize when setting
lattice to BOTTOM.
(ipcp_init_stage): Merged into...
(ipcp_generate_summary): ...its caller.
(ipcp_change_tops_to_bottom): Also process type lists.
(ipcp_add_param_type): New function.
(ipcp_copy_types): Likewise.
(ipcp_propagate_types): Likewise.
(ipcp_propagate_stage): Also propagate types.
(ipcp_need_redirect_p): Variable jump_func moved to its scope block.
Also return true if propagated types require it.
(ipcp_update_callgraph): Dump redirection info.
(ipcp_process_devirtualization_opportunities): New function.
(ipcp_const_param_count): Include known type information.
(ipcp_insert_stage): Call ipcp_process_devirtualization_opportunities
on new node.  Fixed formatting.
* ipa-prop.c (make_edge_direct_to_target): Renamed to
ipa_make_edge_direct_to_target and changed all callers.  Made
externally visible.
(ipa_node_duplication_hook): Duplicate types vector.
* cgraphunit.c (cgraph_redirect_edge_call_stmt_to_callee): Also try to
redirect outgoing calls for which we can't get a decl from the
statement.  Check that we can get a decl from the call statement.
* ipa-inline.c (inline_indirect_intraprocedural_analysis): Call
ipa_analyze_params_uses only when ipa-cp is disabled.
* tree-inline.c (get_indirect_callee_fndecl): Removed.
(expand_call_inline): Do not call get_indirect_callee_fndecl.
* params.def (PARAM_DEVIRT_TYPE_LIST_SIZE): New parameter.
* Makefile.in (ipa-cp.o): Add gimple.h to dependencies.

* testsuite/g++.dg/ipa/devirt-1.C: New test.
* testsuite/g++.dg/ipa/devirt-2.C: Likewise.
* testsuite/g++.dg/ipa/devirt-3.C: Likewise.
* testsuite/g++.dg/ipa/devirt-4.C: Likewise.
* testsuite/g++.dg/ipa/devirt-5.C: Likewise.
* testsuite/gcc.dg/ipa/iinline-3.c: Likewise.

From-SVN: r162911

16 files changed:
gcc/ChangeLog
gcc/Makefile.in
gcc/cgraphunit.c
gcc/doc/invoke.texi
gcc/ipa-cp.c
gcc/ipa-prop.c
gcc/ipa-prop.h
gcc/params.def
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ipa/devirt-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ipa/devirt-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ipa/devirt-3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ipa/devirt-4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ipa/devirt-5.C [new file with mode: 0644]
gcc/testsuite/gcc.dg/ipa/iinline-3.c [new file with mode: 0644]
gcc/tree-inline.c

index 0b0d41810576d70b2c5bf8d7e40fca55ddccdd4b..a3ea02be2f02c0ee43be98a53104bb21debed412 100644 (file)
@@ -1,3 +1,46 @@
+2010-08-05  Martin Jambor  <mjambor@suse.cz>
+
+       * ipa-prop.h (enum ipa_lattice_type): Changed comments.
+       (struct ipa_param_descriptor): New fields types and
+       cannot_devirtualize.
+       (ipa_param_cannot_devirtualize_p): New function.
+       (ipa_param_types_vec_empty): Likewise.
+       (ipa_make_edge_direct_to_target): Declare.
+       * ipa-cp.c: Fixed first stage driver name in initial comment,
+       described devirtualization there too.
+       (ipcp_analyze_node): Call ipa_analyze_params_uses.
+       (ipcp_print_all_lattices): Print devirtualization info.
+       (ipa_set_param_cannot_devirtualize): New function.
+       (ipcp_initialize_node_lattices): Set cannot_devirtualize when setting
+       lattice to BOTTOM.
+       (ipcp_init_stage): Merged into...
+       (ipcp_generate_summary): ...its caller.
+       (ipcp_change_tops_to_bottom): Also process type lists.
+       (ipcp_add_param_type): New function.
+       (ipcp_copy_types): Likewise.
+       (ipcp_propagate_types): Likewise.
+       (ipcp_propagate_stage): Also propagate types.
+       (ipcp_need_redirect_p): Variable jump_func moved to its scope block.
+       Also return true if propagated types require it.
+       (ipcp_update_callgraph): Dump redirection info.
+       (ipcp_process_devirtualization_opportunities): New function.
+       (ipcp_const_param_count): Include known type information.
+       (ipcp_insert_stage): Call ipcp_process_devirtualization_opportunities
+       on new node.  Fixed formatting.
+       * ipa-prop.c (make_edge_direct_to_target): Renamed to
+       ipa_make_edge_direct_to_target and changed all callers.  Made
+       externally visible.
+       (ipa_node_duplication_hook): Duplicate types vector.
+       * cgraphunit.c (cgraph_redirect_edge_call_stmt_to_callee): Also try to
+       redirect outgoing calls for which we can't get a decl from the
+       statement.  Check that we can get a decl from the call statement.
+       * ipa-inline.c (inline_indirect_intraprocedural_analysis): Call
+       ipa_analyze_params_uses only when ipa-cp is disabled.
+       * tree-inline.c (get_indirect_callee_fndecl): Removed.
+       (expand_call_inline): Do not call get_indirect_callee_fndecl.
+       * params.def (PARAM_DEVIRT_TYPE_LIST_SIZE): New parameter.
+       * Makefile.in (ipa-cp.o): Add gimple.h to dependencies.
+
 2010-08-05  Uros Bizjak  <ubizjak@gmail.com>
 
        * expmed.c (expand_mult_const) <case alg_shift>: Expand shift into
index 030b9c06dcc3abad3213d5a047945cf7f14e2cce..d87d4e2f66dbe027ad451d7bf129761b5d322db5 100644 (file)
@@ -3013,7 +3013,7 @@ ipa-ref.o : ipa-ref.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
    langhooks.h $(GGC_H) $(TARGET_H) $(CGRAPH_H)  $(TREE_H) $(TARGET_H) \
    $(TREE_FLOW_H) $(TM_H) $(TREE_PASS_H) $(FLAGS_H) $(TREE_H) $(GGC_H) 
 ipa-cp.o : ipa-cp.c $(CONFIG_H) $(SYSTEM_H) coretypes.h  \
-   $(TREE_H) $(TARGET_H) $(CGRAPH_H) $(IPA_PROP_H) $(TREE_FLOW_H) \
+   $(TREE_H) $(TARGET_H) $(GIMPLE_H) $(CGRAPH_H) $(IPA_PROP_H) $(TREE_FLOW_H) \
    $(TREE_PASS_H) $(FLAGS_H) $(TIMEVAR_H) $(DIAGNOSTIC_H) $(TREE_DUMP_H) \
    $(TREE_INLINE_H) $(FIBHEAP_H) $(PARAMS_H) tree-pretty-print.h
 ipa-split.o : ipa-split.c $(CONFIG_H) $(SYSTEM_H) coretypes.h  \
index 0f3a6e23d00586e0c36ed76e7147cbacb2621447..4d02057420ae9e09f85c59586f7c26e38c97342a 100644 (file)
@@ -2362,14 +2362,18 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e)
   struct cgraph_node *node;
 #endif
 
-  if (!decl || decl == e->callee->decl
+  if (e->indirect_unknown_callee
+      || decl == e->callee->decl
       /* Don't update call from same body alias to the real function.  */
-      || cgraph_get_node (decl) == cgraph_get_node (e->callee->decl))
+      || (decl && cgraph_get_node (decl) == cgraph_get_node (e->callee->decl)))
     return e->call_stmt;
 
 #ifdef ENABLE_CHECKING
-  node = cgraph_get_node (decl);
-  gcc_assert (!node || !node->clone.combined_args_to_skip);
+  if (decl)
+    {
+      node = cgraph_get_node (decl);
+      gcc_assert (!node || !node->clone.combined_args_to_skip);
+    }
 #endif
 
   if (cgraph_dump_file)
index bd317aa6c3e1c8c37ede587002a12946f0ca22b7..fb33b728e93d8ab11ab133b36f0b637a6f90495e 100644 (file)
@@ -8731,6 +8731,12 @@ loop in the loop nest by a given number of iterations.  The strip
 length can be changed using the @option{loop-block-tile-size}
 parameter.  The default value is 51 iterations.
 
+@item devirt-type-list-size
+IPA-CP attempts to track all possible types passed to a function's
+parameter in order to perform devirtualization.
+@option{devirt-type-list-size} is the maximum number of types it
+stores per a single formal parameter of a function.
+
 @end table
 @end table
 
index 6918273cba6a5a776687a44d68a1a120e0647804..354a404d36f23212598cf24f11d4bfc6a49c3795 100644 (file)
@@ -70,7 +70,7 @@ along with GCC; see the file COPYING3.  If not see
    modified_flags are defined in ipa_node_params structure
    (defined in ipa_prop.h and pointed to by cgraph_edge->aux).
 
-   -ipcp_init_stage() is the first stage driver.
+   -ipcp_generate_summary() is the first stage driver.
 
    Second stage - interprocedural analysis
    ========================================
@@ -117,6 +117,17 @@ along with GCC; see the file COPYING3.  If not see
 
    -ipcp_insert_stage() is the third phase driver.
 
+
+   This pass also performs devirtualization - turns virtual calls into direct
+   ones if it can prove that all invocations of the function call the same
+   callee.  This is achieved by building a list of all base types (actually,
+   their BINFOs) that individual parameters can have in an iterative matter
+   just like propagating scalar constants and then examining whether virtual
+   calls which take a parameter as their object fold to the same target for all
+   these types.  If we cannot enumerate all types or there is a type which does
+   not have any BINFO associated with it, cannot_devirtualize of the associated
+   parameter descriptor is set which is an equivalent of BOTTOM lattice value
+   in standard IPA constant propagation.
 */
 
 #include "config.h"
@@ -124,6 +135,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "tree.h"
 #include "target.h"
+#include "gimple.h"
 #include "cgraph.h"
 #include "ipa-prop.h"
 #include "tree-flow.h"
@@ -393,12 +405,17 @@ ipcp_print_all_lattices (FILE * f)
                  print_generic_expr (f, DECL_INITIAL (TREE_OPERAND (cst, 0)),
                                                       0);
                }
-             fprintf (f, "\n");
            }
          else if (lat->type == IPA_TOP)
-           fprintf (f, "type is TOP\n");
+           fprintf (f, "type is TOP");
+         else
+           fprintf (f, "type is BOTTOM");
+         if (ipa_param_cannot_devirtualize_p (info, i))
+           fprintf (f, " - cannot_devirtualize set\n");
+         else if (ipa_param_types_vec_empty (info, i))
+           fprintf (f, " - type list empty\n");
          else
-           fprintf (f, "type is BOTTOM\n");
+           fprintf (f, "\n");
        }
     }
 }
@@ -523,6 +540,19 @@ ipcp_cloning_candidate_p (struct cgraph_node *node)
   return true;
 }
 
+/* Mark parameter with index I of function described by INFO as unsuitable for
+   devirtualization.  Return true if it has already been marked so.  */
+
+static bool
+ipa_set_param_cannot_devirtualize (struct ipa_node_params *info, int i)
+{
+  bool ret = info->params[i].cannot_devirtualize;
+  info->params[i].cannot_devirtualize = true;
+  if (info->params[i].types)
+    VEC_free (tree, heap, info->params[i].types);
+  return ret;
+}
+
 /* Initialize ipcp_lattices array.  The lattices corresponding to supported
    types (integers, real types and Fortran constants defined as const_decls)
    are initialized to IPA_TOP, the rest of them to IPA_BOTTOM.  */
@@ -545,7 +575,11 @@ ipcp_initialize_node_lattices (struct cgraph_node *node)
     type = IPA_BOTTOM;
 
   for (i = 0; i < ipa_get_param_count (info) ; i++)
-    ipcp_get_lattice (info, i)->type = type;
+    {
+      ipcp_get_lattice (info, i)->type = type;
+      if (type == IPA_BOTTOM)
+       ipa_set_param_cannot_devirtualize (info, i);
+    }
 }
 
 /* build INTEGER_CST tree with type TREE_TYPE and value according to LAT.
@@ -599,26 +633,6 @@ ipcp_compute_node_scale (struct cgraph_node *node)
     ipcp_set_node_scale (node, sum * REG_BR_PROB_BASE / node->count);
 }
 
-/* Initialization and computation of IPCP data structures.  This is the initial
-   intraprocedural analysis of functions, which gathers information to be
-   propagated later on.  */
-
-static void
-ipcp_init_stage (void)
-{
-  struct cgraph_node *node;
-
-  for (node = cgraph_nodes; node; node = node->next)
-    if (node->analyzed)
-      {
-       /* Unreachable nodes should have been eliminated before ipcp.  */
-       gcc_assert (node->needed || node->reachable);
-
-       node->local.versionable = tree_versionable_function_p (node->decl);
-       ipa_analyze_node (node);
-      }
-}
-
 /* Return true if there are some formal parameters whose value is IPA_TOP (in
    the whole compilation unit).  Change their values to IPA_BOTTOM, since they
    most probably get their values from outside of this compilation unit.  */
@@ -649,11 +663,148 @@ ipcp_change_tops_to_bottom (void)
                }
              lat->type = IPA_BOTTOM;
            }
+         if (!ipa_param_cannot_devirtualize_p (info, i)
+             && ipa_param_types_vec_empty (info, i))
+           {
+             prop_again = true;
+             ipa_set_param_cannot_devirtualize (info, i);
+             if (dump_file)
+               {
+                 fprintf (dump_file, "Marking param ");
+                 print_generic_expr (dump_file, ipa_get_param (info, i), 0);
+                 fprintf (dump_file, " of node %s as unusable for "
+                          "devirtualization.\n",
+                          cgraph_node_name (node));
+               }
+           }
        }
     }
   return prop_again;
 }
 
+/* Insert BINFO to the list of known types of parameter number I of the
+   function described by CALLEE_INFO.  Return true iff the type information
+   associated with the callee parameter changed in any way.  */
+
+static bool
+ipcp_add_param_type (struct ipa_node_params *callee_info, int i, tree binfo)
+{
+  int j, count;
+
+  if (ipa_param_cannot_devirtualize_p (callee_info, i))
+    return false;
+
+  if (callee_info->params[i].types)
+    {
+      count = VEC_length (tree, callee_info->params[i].types);
+      for (j = 0; j < count; j++)
+       if (VEC_index (tree, callee_info->params[i].types, j) == binfo)
+         return false;
+    }
+
+  if (VEC_length (tree, callee_info->params[i].types)
+      == (unsigned) PARAM_VALUE (PARAM_DEVIRT_TYPE_LIST_SIZE))
+    return !ipa_set_param_cannot_devirtualize (callee_info, i);
+
+  VEC_safe_push (tree, heap, callee_info->params[i].types, binfo);
+  return true;
+}
+
+/* Copy known types information for parameter number CALLEE_IDX of CALLEE_INFO
+   from a parameter of CALLER_INFO as described by JF.  Return true iff the
+   type information changed in any way.  JF must be a pass-through or an
+   ancestor jump function.  */
+
+static bool
+ipcp_copy_types (struct ipa_node_params *caller_info,
+                struct ipa_node_params *callee_info,
+                int callee_idx, struct ipa_jump_func *jf)
+{
+  int caller_idx, j, count;
+  bool res;
+
+  if (ipa_param_cannot_devirtualize_p (callee_info, callee_idx))
+    return false;
+
+  if (jf->type == IPA_JF_PASS_THROUGH)
+    {
+      if (jf->value.pass_through.operation != NOP_EXPR)
+       {
+         ipa_set_param_cannot_devirtualize (callee_info, callee_idx);
+         return true;
+       }
+      caller_idx = jf->value.pass_through.formal_id;
+    }
+  else
+    caller_idx = jf->value.ancestor.formal_id;
+
+  if (ipa_param_cannot_devirtualize_p (caller_info, caller_idx))
+    {
+      ipa_set_param_cannot_devirtualize (callee_info, callee_idx);
+      return true;
+    }
+
+  if (!caller_info->params[caller_idx].types)
+    return false;
+
+  res = false;
+  count = VEC_length (tree, caller_info->params[caller_idx].types);
+  for (j = 0; j < count; j++)
+    {
+      tree binfo = VEC_index (tree, caller_info->params[caller_idx].types, j);
+      if (jf->type == IPA_JF_ANCESTOR)
+       {
+         binfo = get_binfo_at_offset (binfo, jf->value.ancestor.offset,
+                                      jf->value.ancestor.type);
+         if (!binfo)
+           {
+             ipa_set_param_cannot_devirtualize (callee_info, callee_idx);
+             return true;
+           }
+       }
+      res |= ipcp_add_param_type (callee_info, callee_idx, binfo);
+    }
+  return res;
+}
+
+/* Propagate type information for parameter of CALLEE_INFO number I as
+   described by JF.  CALLER_INFO describes the caller.  Return true iff the
+   type information changed in any way.  */
+
+static bool
+ipcp_propagate_types (struct ipa_node_params *caller_info,
+                     struct ipa_node_params *callee_info,
+                     struct ipa_jump_func *jf, int i)
+{
+  tree cst, binfo;
+
+  switch (jf->type)
+    {
+    case IPA_JF_UNKNOWN:
+    case IPA_JF_CONST_MEMBER_PTR:
+      break;
+
+    case IPA_JF_KNOWN_TYPE:
+      return ipcp_add_param_type (callee_info, i, jf->value.base_binfo);
+
+    case IPA_JF_CONST:
+      cst = jf->value.constant;
+      if (TREE_CODE (cst) != ADDR_EXPR)
+       break;
+      binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (cst, 0), NULL_TREE);
+      if (!binfo)
+       break;
+      return ipcp_add_param_type (callee_info, i, binfo);
+
+    case IPA_JF_PASS_THROUGH:
+    case IPA_JF_ANCESTOR:
+      return ipcp_copy_types (caller_info, callee_info, i, jf);
+    }
+
+  /* If we reach this we cannot use this parameter for devirtualization.  */
+  return !ipa_set_param_cannot_devirtualize (callee_info, i);
+}
+
 /* Interprocedural analysis. The algorithm propagates constants from the
    caller's parameters to the callee's arguments.  */
 static void
@@ -701,6 +852,9 @@ ipcp_propagate_stage (void)
                  dest_lat->constant = new_lat.constant;
                  ipa_push_func_to_list (&wl, cs->callee);
                }
+
+             if (ipcp_propagate_types (info, callee_info, jump_func, i))
+               ipa_push_func_to_list (&wl, cs->callee);
            }
        }
     }
@@ -852,7 +1006,6 @@ ipcp_need_redirect_p (struct cgraph_edge *cs)
 {
   struct ipa_node_params *orig_callee_info;
   int i, count;
-  struct ipa_jump_func *jump_func;
   struct cgraph_node *node = cs->callee, *orig;
 
   if (!n_cloning_candidates)
@@ -868,12 +1021,16 @@ ipcp_need_redirect_p (struct cgraph_edge *cs)
   for (i = 0; i < count; i++)
     {
       struct ipcp_lattice *lat = ipcp_get_lattice (orig_callee_info, i);
-      if (ipcp_lat_is_const (lat))
-       {
-         jump_func = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), i);
-         if (jump_func->type != IPA_JF_CONST)
-           return true;
-       }
+      struct ipa_jump_func *jump_func;
+
+      jump_func = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), i);
+      if ((ipcp_lat_is_const (lat)
+          && jump_func->type != IPA_JF_CONST)
+         || (!ipa_param_cannot_devirtualize_p (orig_callee_info, i)
+             && !ipa_param_types_vec_empty (orig_callee_info, i)
+             && jump_func->type != IPA_JF_CONST
+             && jump_func->type != IPA_JF_KNOWN_TYPE))
+       return true;
     }
 
   return false;
@@ -912,7 +1069,15 @@ ipcp_update_callgraph (void)
          {
            next = cs->next_caller;
            if (!ipcp_node_is_clone (cs->caller) && ipcp_need_redirect_p (cs))
-             cgraph_redirect_edge_callee (cs, orig_node);
+             {
+               if (dump_file)
+                 fprintf (dump_file, "Redirecting edge %s/%i -> %s/%i "
+                          "back to %s/%i.",
+                          cgraph_node_name (cs->caller), cs->caller->uid,
+                          cgraph_node_name (cs->callee), cs->callee->uid,
+                          cgraph_node_name (orig_node), orig_node->uid);
+               cgraph_redirect_edge_callee (cs, orig_node);
+             }
          }
       }
 }
@@ -1031,6 +1196,57 @@ ipcp_estimate_cloning_cost (struct cgraph_node *node)
   return cost + 1;
 }
 
+/* Walk indirect calls of NODE and if any polymorphic can be turned into a
+   direct one now, do so.  */
+
+static void
+ipcp_process_devirtualization_opportunities (struct cgraph_node *node)
+{
+  struct ipa_node_params *info = IPA_NODE_REF (node);
+  struct cgraph_edge *ie, *next_ie;
+
+  for (ie = node->indirect_calls; ie; ie = next_ie)
+    {
+      int param_index, types_count, j;
+      HOST_WIDE_INT token;
+      tree target;
+
+      next_ie = ie->next_callee;
+      if (!ie->indirect_info->polymorphic)
+       continue;
+      param_index = ie->indirect_info->param_index;
+      if (param_index == -1
+         || ipa_param_cannot_devirtualize_p (info, param_index)
+         || ipa_param_types_vec_empty (info, param_index))
+       continue;
+
+      token = ie->indirect_info->otr_token;
+      target = NULL_TREE;
+      types_count = VEC_length (tree, info->params[param_index].types);
+      for (j = 0; j < types_count; j++)
+       {
+         tree binfo = VEC_index (tree, info->params[param_index].types, j);
+         tree t = gimple_fold_obj_type_ref_known_binfo (token, binfo);
+
+         if (!t)
+           {
+             target = NULL_TREE;
+             break;
+           }
+         else if (!target)
+           target = t;
+         else if (target != t)
+           {
+             target = NULL_TREE;
+             break;
+           }
+       }
+
+      if (target)
+       ipa_make_edge_direct_to_target (ie, target);
+    }
+}
+
 /* Return number of live constant parameters.  */
 static int
 ipcp_const_param_count (struct cgraph_node *node)
@@ -1043,9 +1259,11 @@ ipcp_const_param_count (struct cgraph_node *node)
   for (i = 0; i < count; i++)
     {
       struct ipcp_lattice *lat = ipcp_get_lattice (info, i);
-      if (ipcp_lat_is_insertable (lat)
+      if ((ipcp_lat_is_insertable (lat)
          /* Do not count obviously unused arguments.  */
-          && ipa_is_param_used (info, i))
+          && ipa_is_param_used (info, i))
+         || (!ipa_param_cannot_devirtualize_p (info, i)
+             && !ipa_param_types_vec_empty (info, i)))
        const_param++;
     }
   return const_param;
@@ -1087,7 +1305,8 @@ ipcp_insert_stage (void)
     max_new_size = PARAM_VALUE (PARAM_LARGE_UNIT_INSNS);
   max_new_size = max_new_size * PARAM_VALUE (PARAM_IPCP_UNIT_GROWTH) / 100 + 1;
 
-  /* First collect all functions we proved to have constant arguments to heap.  */
+  /* First collect all functions we proved to have constant arguments to
+     heap.  */
   heap = fibheap_new ();
   for (node = cgraph_nodes; node; node = node->next)
     {
@@ -1099,7 +1318,8 @@ ipcp_insert_stage (void)
       if (ipa_is_called_with_var_arguments (info))
        continue;
       if (ipcp_const_param_count (node))
-       node->aux = fibheap_insert (heap, ipcp_estimate_cloning_cost (node), node);
+       node->aux = fibheap_insert (heap, ipcp_estimate_cloning_cost (node),
+                                   node);
      }
 
   /* Now clone in priority order until code size growth limits are met or
@@ -1183,6 +1403,8 @@ ipcp_insert_stage (void)
 
       if (node1 == NULL)
        continue;
+      ipcp_process_devirtualization_opportunities (node1);
+
       if (dump_file)
        fprintf (dump_file, "versioned function %s with growth %i, overall %i\n",
                 cgraph_node_name (node), (int)growth, (int)new_size);
@@ -1245,18 +1467,30 @@ ipcp_driver (void)
   return 0;
 }
 
-/* Note function body size.  */
+/* Initialization and computation of IPCP data structures.  This is the initial
+   intraprocedural analysis of functions, which gathers information to be
+   propagated later on.  */
+
 static void
 ipcp_generate_summary (void)
 {
+  struct cgraph_node *node;
+
   if (dump_file)
     fprintf (dump_file, "\nIPA constant propagation start:\n");
   ipa_check_create_node_params ();
   ipa_check_create_edge_args ();
   ipa_register_cgraph_hooks ();
-  /* 1. Call the init stage to initialize
-     the ipa_node_params and ipa_edge_args structures.  */
-  ipcp_init_stage ();
+
+  for (node = cgraph_nodes; node; node = node->next)
+    if (node->analyzed)
+      {
+       /* Unreachable nodes should have been eliminated before ipcp.  */
+       gcc_assert (node->needed || node->reachable);
+
+       node->local.versionable = tree_versionable_function_p (node->decl);
+       ipa_analyze_node (node);
+      }
 }
 
 /* Write ipcp summary for nodes in SET.  */
index c5da8f84fdbb516528e6a205d3dae0b3cbf6b846..9d7df0752929e0205995b6d29ede811aac47678b 100644 (file)
@@ -1430,8 +1430,8 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs,
 /* If TARGET is an addr_expr of a function declaration, make it the destination
    of an indirect edge IE and return the edge.  Otherwise, return NULL.  */
 
-static struct cgraph_edge *
-make_edge_direct_to_target (struct cgraph_edge *ie, tree target)
+struct cgraph_edge *
+ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target)
 {
   struct cgraph_node *callee;
 
@@ -1484,7 +1484,7 @@ try_make_edge_direct_simple_call (struct cgraph_edge *ie,
   else
     return NULL;
 
-  return make_edge_direct_to_target (ie, target);
+  return ipa_make_edge_direct_to_target (ie, target);
 }
 
 /* Try to find a destination for indirect edge IE that corresponds to a
@@ -1525,7 +1525,7 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
     return NULL;
 
   if (target)
-    return make_edge_direct_to_target (ie, target);
+    return ipa_make_edge_direct_to_target (ie, target);
   else
     return NULL;
 }
@@ -1794,7 +1794,7 @@ ipa_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst,
                           __attribute__((unused)) void *data)
 {
   struct ipa_node_params *old_info, *new_info;
-  int param_count;
+  int param_count, i;
 
   ipa_check_create_node_params ();
   old_info = IPA_NODE_REF (src);
@@ -1805,8 +1805,15 @@ ipa_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst,
   new_info->params = (struct ipa_param_descriptor *)
     duplicate_array (old_info->params,
                     sizeof (struct ipa_param_descriptor) * param_count);
+  for (i = 0; i < param_count; i++)
+    new_info->params[i].types = VEC_copy (tree, heap,
+                                         old_info->params[i].types);
   new_info->ipcp_orig_node = old_info->ipcp_orig_node;
   new_info->count_scale = old_info->count_scale;
+
+  new_info->called_with_var_arguments = old_info->called_with_var_arguments;
+  new_info->uses_analysis_done = old_info->uses_analysis_done;
+  new_info->node_enqueued = old_info->node_enqueued;
 }
 
 /* Register our cgraph hooks if they are not already there.  */
index 2a28aa3ce3864c8121cb7d9daafbca2b52f637a0..8244a7f79e32054faaeadc28976d6e54777aae0f 100644 (file)
@@ -133,11 +133,12 @@ struct GTY (()) ipa_jump_func
    computed by the interprocedural stage of IPCP.
    There are three main values of the lattice:
    IPA_TOP - unknown,
-   IPA_BOTTOM - non constant,
+   IPA_BOTTOM - variable,
    IPA_CONST_VALUE - simple scalar constant,
-   Cval of formal f will have a constant value if all callsites to this
-   function have the same constant value passed to f.
-   Integer and real constants are represented as IPA_CONST_VALUE.  */
+
+   We also use this type to propagate types accross the call graph for the
+   purpose of devirtualization.  In that case, IPA_CONST_VALUE denotes a known
+   type, rather than a constant.  */
 enum ipa_lattice_type
 {
   IPA_BOTTOM,
@@ -161,8 +162,14 @@ struct ipa_param_descriptor
   struct ipcp_lattice ipcp_lattice;
   /* PARAM_DECL of this parameter.  */
   tree decl;
+  /* Vector of BINFOs of types that this argument might encounter.  NULL
+     basically means a top value, bottom is marked by the cannot_devirtualize
+     flag below.*/
+  VEC (tree, heap) *types;
   /* The parameter is used.  */
   unsigned used : 1;
+  /* Set when parameter type cannot be used for devirtualization.  */
+  unsigned cannot_devirtualize : 1;
 };
 
 /* ipa_node_params stores information related to formal parameters of functions
@@ -232,6 +239,25 @@ ipa_is_param_used (struct ipa_node_params *info, int i)
   return info->params[i].used;
 }
 
+/* Return the cannot_devirtualize flag corresponding to the Ith formal
+   parameter of the function associated with INFO.  The corresponding function
+   to set the flag is ipa_set_param_cannot_devirtualize.  */
+
+static inline bool
+ipa_param_cannot_devirtualize_p (struct ipa_node_params *info, int i)
+{
+  return info->params[i].cannot_devirtualize;
+}
+
+/* Return true iff the vector of possible types of the Ith formal parameter of
+   the function associated with INFO is empty.  */
+
+static inline bool
+ipa_param_types_vec_empty (struct ipa_node_params *info, int i)
+{
+  return info->params[i].types == NULL;
+}
+
 /* Flag this node as having callers with variable number of arguments.  */
 
 static inline void
@@ -402,6 +428,10 @@ void ipa_initialize_node_params (struct cgraph_node *node);
 bool ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
                                        VEC (cgraph_edge_p, heap) **new_edges);
 
+/* Indirect edge and binfo processing.  */
+struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree);
+
+
 /* Debugging interface.  */
 void ipa_print_node_params (FILE *, struct cgraph_node *node);
 void ipa_print_all_params (FILE *);
index 1d4e68722592290b94e85cd822553ad7affe2546..d470539c9c424645a8701aaf8d6ebf5d939c0a03 100644 (file)
@@ -832,6 +832,12 @@ DEFPARAM (PARAM_IPA_SRA_PTR_GROWTH_FACTOR,
          "a pointer to an aggregate with",
          2, 0, 0)
 
+DEFPARAM (PARAM_DEVIRT_TYPE_LIST_SIZE,
+         "devirt-type-list-size",
+         "Maximum size of a type list associated with each parameter for "
+         "devirtualization",
+         8, 0, 0)
+
 /*
 Local variables:
 mode:c
index 6c37a88dd06476ec102e7a60d1ce061262644dba..59bde3f29dfc9baf62deed7459dc52dda24a2646 100644 (file)
@@ -1,3 +1,12 @@
+2010-08-05  Martin Jambor  <mjambor@suse.cz>
+
+       * g++.dg/ipa/devirt-1.C: New test.
+       * g++.dg/ipa/devirt-2.C: Likewise.
+       * g++.dg/ipa/devirt-3.C: Likewise.
+       * g++.dg/ipa/devirt-4.C: Likewise.
+       * g++.dg/ipa/devirt-5.C: Likewise.
+       * gcc.dg/ipa/iinline-3.c: Likewise.
+
 2010-08-05  Jie Zhang  <jie@codesourcery.com>
 
        PR tree-optimization/45144
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-1.C b/gcc/testsuite/g++.dg/ipa/devirt-1.C
new file mode 100644 (file)
index 0000000..de0f665
--- /dev/null
@@ -0,0 +1,62 @@
+/* Verify that simple virtual calls are converted to direct calls by ipa-cp.  */
+/* { dg-do run } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized"  } */
+
+extern "C" void abort (void);
+
+class A
+{
+public:
+  int data;
+  virtual int foo (int i);
+};
+
+class B : public A
+{
+public:
+  virtual int foo (int i);
+};
+
+class C : public A
+{
+public:
+  virtual int foo (int i);
+};
+
+int A::foo (int i)
+{
+  return i + 1;
+}
+
+int B::foo (int i)
+{
+  return i + 2;
+}
+
+int C::foo (int i)
+{
+  return i + 3;
+}
+
+static int middleman (class A *obj, int i)
+{
+  return obj->foo (i);
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+  return 1;
+}
+
+int main (int argc, char *argv[])
+{
+  class B b;
+  if (middleman (&b, get_input ()) != 3)
+    abort ();
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo"  "cp"  } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-2.C b/gcc/testsuite/g++.dg/ipa/devirt-2.C
new file mode 100644 (file)
index 0000000..d3181e4
--- /dev/null
@@ -0,0 +1,62 @@
+/* Verify that simple virtual calls using this pointer are converted
+   to direct calls by ipa-cp.  */
+/* { dg-do run } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp"  } */
+
+extern "C" void abort (void);
+
+class A
+{
+public:
+  int data;
+  virtual int foo (int i);
+  int middleman (int i)
+  {
+    return foo (i);
+  }
+};
+
+class B : public A
+{
+public:
+  virtual int foo (int i);
+};
+
+class C : public A
+{
+public:
+  virtual int foo (int i);
+};
+
+int A::foo (int i)
+{
+  return i + 1;
+}
+
+int B::foo (int i)
+{
+  return i + 2;
+}
+
+int C::foo (int i)
+{
+  return i + 3;
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+  return 1;
+}
+
+int main (int argc, char *argv[])
+{
+  class B b;
+  int i;
+  for (i = 0; i < get_input(); i++)
+    if (b.middleman (get_input ()) != 3)
+      abort ();
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo"  "cp"  } } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-3.C b/gcc/testsuite/g++.dg/ipa/devirt-3.C
new file mode 100644 (file)
index 0000000..2d7bb0a
--- /dev/null
@@ -0,0 +1,63 @@
+/* Verify that simple virtual calls on an object refrence are
+   converted to simple calls by ipa-cp.  */
+/* { dg-do run } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized"  } */
+
+extern "C" void abort (void);
+
+class A
+{
+public:
+  int data;
+  virtual int foo (int i);
+};
+
+class B : public A
+{
+public:
+  virtual int foo (int i);
+};
+
+class C : public A
+{
+public:
+  virtual int foo (int i);
+};
+
+int A::foo (int i)
+{
+  return i + 1;
+}
+
+int B::foo (int i)
+{
+  return i + 2;
+}
+
+int C::foo (int i)
+{
+  return i + 3;
+}
+
+static int middleman (class A &obj, int i)
+{
+  return obj.foo (i);
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+  return 1;
+}
+
+int main (int argc, char *argv[])
+{
+  class B b;
+  if (middleman (b, get_input ()) != 3)
+    abort ();
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo"  "cp"  } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-4.C b/gcc/testsuite/g++.dg/ipa/devirt-4.C
new file mode 100644 (file)
index 0000000..72c883b
--- /dev/null
@@ -0,0 +1,68 @@
+/* Verify that ipa-co can convert virtual calls to direct ones even
+   when a typecast to an ancestor is involved along the way.  */
+/* { dg-do run } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized"  } */
+
+extern "C" void abort (void);
+
+class A
+{
+public:
+  int data;
+  virtual int foo (int i);
+};
+
+class B : public A
+{
+public:
+  virtual int foo (int i);
+};
+
+class C : public A
+{
+public:
+  virtual int foo (int i);
+};
+
+int A::foo (int i)
+{
+  return i + 1;
+}
+
+int B::foo (int i)
+{
+  return i + 2;
+}
+
+int C::foo (int i)
+{
+  return i + 3;
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+  return 1;
+}
+
+static int middleman_1 (class A *obj, int i)
+{
+  return obj->foo (i);
+}
+
+static int middleman_2 (class B *obj, int i)
+{
+  return middleman_1 (obj, i);
+}
+
+int main (int argc, char *argv[])
+{
+  class B b;
+  if (middleman_2 (&b, get_input ()) != 3)
+    abort ();
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo"  "cp"  } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-5.C b/gcc/testsuite/g++.dg/ipa/devirt-5.C
new file mode 100644 (file)
index 0000000..575bc02
--- /dev/null
@@ -0,0 +1,79 @@
+/* Verify that ipa-cp can convert simple virtual calls to a direct
+   ones even when a typecast to an ancestor is involved along the way
+   and that ancestor is not the first one with virtual functions.  */
+/* { dg-do run } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized"  } */
+
+extern "C" void abort (void);
+
+class Distraction
+{
+public:
+  float f;
+  double d;
+  Distraction ()
+  {
+    f = 8.3;
+    d = 10.2;
+  }
+  virtual float bar (float z);
+};
+
+class A
+{
+public:
+  int data;
+  virtual int foo (int i);
+};
+
+
+class B : public Distraction, public A
+{
+public:
+  virtual int foo (int i);
+};
+
+float Distraction::bar (float z)
+{
+  f += z;
+  return f/2;
+}
+
+int A::foo (int i)
+{
+  return i + 1;
+}
+
+int B::foo (int i)
+{
+  return i + 2;
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+  return 1;
+}
+
+static int middleman_1 (class A *obj, int i)
+{
+  return obj->foo (i);
+}
+
+static int middleman_2 (class B *obj, int i)
+{
+  return middleman_1 (obj, i);
+}
+
+int main (int argc, char *argv[])
+{
+  class B b;
+
+  if (middleman_2 (&b, get_input ()) != 3)
+    abort ();
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo"  "cp"  } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-3.c b/gcc/testsuite/gcc.dg/ipa/iinline-3.c
new file mode 100644 (file)
index 0000000..b399571
--- /dev/null
@@ -0,0 +1,33 @@
+/* Verify that call declarations are not redirected according to indirect
+   inlining edges too early.  */
+/* { dg-do run } */
+/* { dg-options "-O3 -fno-early-inlining"  } */
+
+extern void abort (void);
+
+int bar (int k)
+{
+  return k+2;
+}
+
+int baz (int k)
+{
+  return k+1;
+}
+
+static int foo (int (*p)(int), int i)
+{
+  return p (i+1);
+}
+
+int (*g)(int) = baz;
+
+int main (int argc, char *argv[])
+{
+  if (foo (bar, 0) != 3)
+    abort ();
+  if (foo (g, 1) != 3)
+    abort ();
+
+  return 0;
+}
index 81d6cfb4c508f79294cf2f5ce0e809899c2a9b5d..a4785d800ff96b40662cdeb58d7522ca1c325840 100644 (file)
@@ -3707,20 +3707,6 @@ add_local_variables (struct function *callee, struct function *caller,
       }
 }
 
-/* Fetch callee declaration from the call graph edge going from NODE and
-   associated with STMR call statement.  Return NULL_TREE if not found.  */
-static tree
-get_indirect_callee_fndecl (struct cgraph_node *node, gimple stmt)
-{
-  struct cgraph_edge *cs;
-
-  cs = cgraph_edge (node, stmt);
-  if (cs && !cs->indirect_unknown_callee)
-    return cs->callee->decl;
-
-  return NULL_TREE;
-}
-
 /* If STMT is a GIMPLE_CALL, replace it with its inline expansion.  */
 
 static bool
@@ -3754,11 +3740,7 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id)
      If we cannot, then there is no hope of inlining the function.  */
   fn = gimple_call_fndecl (stmt);
   if (!fn)
-    {
-      fn = get_indirect_callee_fndecl (id->dst_node, stmt);
-      if (!fn)
-       goto egress;
-    }
+    goto egress;
 
   /* Turn forward declarations into real ones.  */
   fn = cgraph_node (fn)->decl;