common.opt (fdevirtualize-speculatively): New function.
authorJan Hubicka <jh@suse.cz>
Sun, 1 Sep 2013 15:14:24 +0000 (17:14 +0200)
committerJan Hubicka <hubicka@gcc.gnu.org>
Sun, 1 Sep 2013 15:14:24 +0000 (15:14 +0000)
* common.opt (fdevirtualize-speculatively): New function.
* invoke.texi (fdevirtualize-speculatively): Document.
* ipa-devirt.c: Include ipa-inline.h
(likely_target_p): New function.
(ipa_devirt): New function.
(gate_ipa_devirt): New function.
(pass_data_ipa_devirt): New static var.
(pass_ipa_devirt): Likewise.
(make_pass_ipa_devirt): New function.
* opts.c (default_options): Add OPT_fdevirtualize_speculatively.
(common_handle_option): Disable devirtualization when
value range profiling is available.
* passes.def (pass_ipa_devirt): Add.
* timever.def (TV_IPA_DEVIRT): New timevar.
* tree-pass.h (make_pass_ipa_devirt):

From-SVN: r202145

gcc/ChangeLog
gcc/common.opt
gcc/doc/invoke.texi
gcc/ipa-devirt.c
gcc/opts.c
gcc/passes.def
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ipa/devirt-11.C
gcc/testsuite/g++.dg/tree-ssa/pr45453.C
gcc/timevar.def
gcc/tree-pass.h

index 34e5d33369d0c76e0b608d6cab6c55e8e66cfb1d..c4cc9b7839282587e6235f0abba51fe80a528328 100644 (file)
@@ -1,3 +1,21 @@
+2013-09-01  Jan Hubicka  <jh@suse.cz>
+
+       * common.opt (fdevirtualize-speculatively): New function.
+       * invoke.texi (fdevirtualize-speculatively): Document.
+       * ipa-devirt.c: Include ipa-inline.h
+       (likely_target_p): New function.
+       (ipa_devirt): New function.
+       (gate_ipa_devirt): New function.
+       (pass_data_ipa_devirt): New static var.
+       (pass_ipa_devirt): Likewise.
+       (make_pass_ipa_devirt): New function.
+       * opts.c (default_options): Add OPT_fdevirtualize_speculatively.
+       (common_handle_option): Disable devirtualization when
+       value range profiling is available.
+       * passes.def (pass_ipa_devirt): Add.
+       * timever.def (TV_IPA_DEVIRT): New timevar.
+       * tree-pass.h (make_pass_ipa_devirt): 
+
 2013-09-01  Iain Sandoe  <iain@codesourcery.com>
 
        * config/darwin.h (LINK_COMMAND_SPEC_A): Revise sanitizer specs to
index caf624f51d4ae8e52452d187e507b2a72dd26200..a18a42b410a13034275d6b4be974c1e8c84e41d3 100644 (file)
@@ -1007,6 +1007,10 @@ fdevirtualize
 Common Report Var(flag_devirtualize) Optimization
 Try to convert virtual calls to direct ones.
 
+fdevirtualize-speculatively
+Common Report Var(flag_devirtualize_speculatively) Optimization
+Perform speculative devirtualization
+
 fdiagnostics-show-location=
 Common Joined RejectNegative Enum(diagnostic_prefixing_rule)
 -fdiagnostics-show-location=[once|every-line]  How often to emit source location at the beginning of line-wrapped diagnostics
@@ -1366,7 +1370,7 @@ Common RejectNegative Joined
 
 fipa-cp
 Common Report Var(flag_ipa_cp) Optimization
-Perform Interprocedural constant propagation
+Perform interprocedural constant propagation
 
 fipa-cp-clone
 Common Report Var(flag_ipa_cp_clone) Optimization
index 1365f657cac5a68153e94139371400833761be03..9dfb4d790192c6cbe5359cbd1618181529460725 100644 (file)
@@ -365,7 +365,7 @@ Objective-C and Objective-C++ Dialects}.
 -fcse-follow-jumps -fcse-skip-blocks -fcx-fortran-rules @gol
 -fcx-limited-range @gol
 -fdata-sections -fdce -fdelayed-branch @gol
--fdelete-null-pointer-checks -fdevirtualize -fdse @gol
+-fdelete-null-pointer-checks -fdevirtualize -fdevirtualize-speculatively -fdse @gol
 -fearly-inlining -fipa-sra -fexpensive-optimizations -ffat-lto-objects @gol
 -ffast-math -ffinite-math-only -ffloat-store -fexcess-precision=@var{style} @gol
 -fforward-propagate -ffp-contract=@var{style} -ffunction-sections @gol
@@ -6712,7 +6712,7 @@ also turns on the following optimization flags:
 -fcrossjumping @gol
 -fcse-follow-jumps  -fcse-skip-blocks @gol
 -fdelete-null-pointer-checks @gol
--fdevirtualize @gol
+-fdevirtualize -fdevirtualize-speculatively @gol
 -fexpensive-optimizations @gol
 -fgcse  -fgcse-lm  @gol
 -fhoist-adjacent-loads @gol
@@ -7257,6 +7257,15 @@ indirect inlining (@code{-findirect-inlining}) and interprocedural constant
 propagation (@option{-fipa-cp}).
 Enabled at levels @option{-O2}, @option{-O3}, @option{-Os}.
 
+@item -fdevirtualize-speculatively
+@opindex fdevirtualize-speculatively
+Attempt to convert calls to virtual functions to speculative direct calls.
+Based on the analysis of the type inheritance graph, determine for a given call
+the set of likely targets. If the set is small, preferably of size 1, change
+the call into an conditional deciding on direct and indirect call.  The
+speculative calls enable more optimizations, such as inlining.  When they seem
+useless after further optimization, they are converted back into original form.
+
 @item -fexpensive-optimizations
 @opindex fexpensive-optimizations
 Perform a number of minor optimizations that are relatively expensive.
index 0b678bd750d2344dfd027730c43edb11b76b3085..cab583eb1917dac18339b1c0083a3e997ccda438 100644 (file)
@@ -101,6 +101,8 @@ along with GCC; see the file COPYING3.  If not see
 
   possible_polymorphic_call_targets returns, given an parameters found in
   indirect polymorphic edge all possible polymorphic call targets of the call.
+
+  pass_ipa_devirt performs simple speculative devirtualization.
 */
 
 #include "config.h"
@@ -116,6 +118,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pretty-print.h"
 #include "ipa-utils.h"
 #include "gimple.h"
+#include "ipa-inline.h"
 
 /* Pointer set of all call targets appearing in the cache.  */
 static pointer_set_t *cached_polymorphic_call_targets;
@@ -728,4 +731,267 @@ update_type_inheritance_graph (void)
       get_odr_type (method_class_type (TREE_TYPE (n->symbol.decl)), true);
   timevar_pop (TV_IPA_INHERITANCE);
 }
+
+
+/* Return true if N looks like likely target of a polymorphic call.
+   Rule out cxa_pure_virtual, noreturns, function declared cold and
+   other obvious cases.  */
+
+bool
+likely_target_p (struct cgraph_node *n)
+{
+  int flags;
+  /* cxa_pure_virtual and similar things are not likely.  */
+  if (TREE_CODE (TREE_TYPE (n->symbol.decl)) != METHOD_TYPE)
+    return false;
+  flags = flags_from_decl_or_type (n->symbol.decl);
+  if (flags & ECF_NORETURN)
+    return false;
+  if (lookup_attribute ("cold",
+                       DECL_ATTRIBUTES (n->symbol.decl)))
+    return false;
+  if (n->frequency < NODE_FREQUENCY_NORMAL)
+    return false;
+  return true;
+}
+
+/* The ipa-devirt pass.
+   This performs very trivial devirtualization:
+     1) when polymorphic call is known to have precisely one target,
+        turn it into direct call
+     2) when polymorphic call has only one likely target in the unit,
+        turn it into speculative call.  */
+
+static unsigned int
+ipa_devirt (void)
+{
+  struct cgraph_node *n;
+  struct pointer_set_t *bad_call_targets = pointer_set_create ();
+  struct cgraph_edge *e;
+
+  int npolymorphic = 0, nspeculated = 0, nconverted = 0, ncold = 0;
+  int nmultiple = 0, noverwritable = 0, ndevirtualized = 0, nnotdefined = 0;
+  int nwrong = 0, nok = 0, nexternal = 0;;
+
+  FOR_EACH_DEFINED_FUNCTION (n)
+    {  
+      bool update = false;
+      if (dump_file && n->indirect_calls)
+       fprintf (dump_file, "\n\nProcesing function %s/%i\n",
+                cgraph_node_name (n), n->symbol.order);
+      for (e = n->indirect_calls; e; e = e->next_callee)
+       if (e->indirect_info->polymorphic)
+         {
+           struct cgraph_node *likely_target = NULL;
+           void *cache_token;
+           bool final;
+           vec <cgraph_node *>targets
+              = possible_polymorphic_call_targets
+                   (e, &final, &cache_token);
+           unsigned int i;
+
+           if (dump_file)
+             dump_possible_polymorphic_call_targets 
+               (dump_file, e);
+           npolymorphic++;
+
+           if (final)
+             {
+               gcc_assert (targets.length());
+               if (targets.length() == 1)
+                 {
+                   if (dump_file)
+                     fprintf (dump_file,
+                              "Devirtualizing call in %s/%i to %s/%i\n",
+                              cgraph_node_name (n), n->symbol.order,
+                              cgraph_node_name (targets[0]), targets[0]->symbol.order);
+                   cgraph_make_edge_direct (e, targets[0]);
+                   ndevirtualized++;
+                   update = true;
+                   continue;
+                 }
+             }
+           if (!flag_devirtualize_speculatively)
+             continue;
+           if (!cgraph_maybe_hot_edge_p (e))
+             {
+               if (dump_file)
+                 fprintf (dump_file, "Call is cold\n");
+               ncold++;
+               continue;
+             }
+           if (e->speculative)
+             {
+               if (dump_file)
+                 fprintf (dump_file, "Call is aready speculated\n");
+               nspeculated++;
+
+               /* When dumping see if we agree with speculation.  */
+               if (!dump_file)
+                 continue;
+             }
+           if (pointer_set_contains (bad_call_targets,
+                                     cache_token))
+             {
+               if (dump_file)
+                 fprintf (dump_file, "Target list is known to be useless\n");
+               nmultiple++;
+               continue;
+             }
+           for (i = 0; i < targets.length(); i++)
+             if (likely_target_p (targets[i]))
+               {
+                 if (likely_target)
+                   {
+                     likely_target = NULL;
+                     if (dump_file)
+                       fprintf (dump_file, "More than one likely target\n");
+                     nmultiple++;
+                     break;
+                   }
+                 likely_target = targets[i];
+               }
+           if (!likely_target)
+             {
+               pointer_set_insert (bad_call_targets, cache_token);
+               continue;
+             }
+           /* This is reached only when dumping; check if we agree or disagree
+              with the speculation.  */
+           if (e->speculative)
+             {
+               struct cgraph_edge *e2;
+               struct ipa_ref *ref;
+               cgraph_speculative_call_info (e, e2, e, ref);
+               if (cgraph_function_or_thunk_node (e2->callee, NULL)
+                   == cgraph_function_or_thunk_node (likely_target, NULL))
+                 {
+                   fprintf (dump_file, "We agree with speculation\n");
+                   nok++;
+                 }
+               else
+                 {
+                   fprintf (dump_file, "We disagree with speculation\n");
+                   nwrong++;
+                 }
+               continue;
+             }
+           if (!likely_target->symbol.definition)
+             {
+               if (dump_file)
+                 fprintf (dump_file, "Target is not an definition\n");
+               nnotdefined++;
+               continue;
+             }
+           /* Do not introduce new references to external symbols.  While we
+              can handle these just well, it is common for programs to
+              incorrectly with headers defining methods they are linked
+              with.  */
+           if (DECL_EXTERNAL (likely_target->symbol.decl))
+             {
+               if (dump_file)
+                 fprintf (dump_file, "Target is external\n");
+               nexternal++;
+               continue;
+             }
+           if (cgraph_function_body_availability (likely_target)
+               <= AVAIL_OVERWRITABLE
+               && symtab_can_be_discarded ((symtab_node) likely_target))
+             {
+               if (dump_file)
+                 fprintf (dump_file, "Target is overwritable\n");
+               noverwritable++;
+               continue;
+             }
+           else
+             {
+               if (dump_file)
+                 fprintf (dump_file,
+                          "Speculatively devirtualizing call in %s/%i to %s/%i\n",
+                          cgraph_node_name (n), n->symbol.order,
+                          cgraph_node_name (likely_target),
+                          likely_target->symbol.order);
+               if (!symtab_can_be_discarded ((symtab_node) likely_target))
+                 likely_target = cgraph (symtab_nonoverwritable_alias ((symtab_node)likely_target));
+               nconverted++;
+               update = true;
+               cgraph_turn_edge_to_speculative
+                 (e, likely_target, e->count * 8 / 10, e->frequency * 8 / 10);
+             }
+         }
+      if (update)
+       inline_update_overall_summary (n);
+    }
+  pointer_set_destroy (bad_call_targets);
+
+  if (dump_file)
+    fprintf (dump_file,
+            "%i polymorphic calls, %i devirtualized,"
+            " %i speculatively devirtualized, %i cold\n"
+            "%i have multiple targets, %i overwritable,"
+            " %i already speculated (%i agree, %i disagree),"
+            " %i external, %i not defined\n",
+            npolymorphic, ndevirtualized, nconverted, ncold,
+            nmultiple, noverwritable, nspeculated, nok, nwrong,
+            nexternal, nnotdefined);
+  return ndevirtualized ? TODO_remove_functions : 0;
+}
+
+/* Gate for IPCP optimization.  */
+
+static bool
+gate_ipa_devirt (void)
+{
+  /* FIXME: We should remove the optimize check after we ensure we never run
+     IPA passes when not optimizing.  */
+  return (flag_devirtualize || flag_devirtualize_speculatively) && !in_lto_p;
+}
+
+namespace {
+
+const pass_data pass_data_ipa_devirt =
+{
+  IPA_PASS, /* type */
+  "devirt", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  true, /* has_gate */
+  true, /* has_execute */
+  TV_IPA_DEVIRT, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  ( TODO_dump_symtab ), /* todo_flags_finish */
+};
+
+class pass_ipa_devirt : public ipa_opt_pass_d
+{
+public:
+  pass_ipa_devirt(gcc::context *ctxt)
+    : ipa_opt_pass_d(pass_data_ipa_devirt, ctxt,
+                    NULL, /* generate_summary */
+                    NULL, /* write_summary */
+                    NULL, /* read_summary */
+                    NULL, /* write_optimization_summary */
+                    NULL, /* read_optimization_summary */
+                    NULL, /* stmt_fixup */
+                    0, /* function_transform_todo_flags_start */
+                    NULL, /* function_transform */
+                    NULL) /* variable_transform */
+  {}
+
+  /* opt_pass methods: */
+  bool gate () { return gate_ipa_devirt (); }
+  unsigned int execute () { return ipa_devirt (); }
+
+}; // class pass_ipa_devirt
+
+} // anon namespace
+
+ipa_opt_pass_d *
+make_pass_ipa_devirt (gcc::context *ctxt)
+{
+  return new pass_ipa_devirt (ctxt);
+}
+
 #include "gt-ipa-devirt.h"
index 133fe0f717dd7aa27f8b17185a85225a88c8d74a..6b6652d89ca4997ef536193355e56da6d45fd5b2 100644 (file)
@@ -479,6 +479,7 @@ static const struct default_options default_options_table[] =
     { OPT_LEVELS_2_PLUS, OPT_ftree_switch_conversion, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fipa_cp, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fdevirtualize, NULL, 1 },
+    { OPT_LEVELS_2_PLUS, OPT_fdevirtualize_speculatively, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_fipa_sra, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_falign_loops, NULL, 1 },
     { OPT_LEVELS_2_PLUS, OPT_falign_jumps, NULL, 1 },
@@ -1665,6 +1666,11 @@ common_handle_option (struct gcc_options *opts,
        opts->x_flag_vect_cost_model = value;
       if (!opts_set->x_flag_tree_loop_distribute_patterns)
        opts->x_flag_tree_loop_distribute_patterns = value;
+      /* Indirect call profiling should do all useful transformations
+        speculative devirutalization does.  */
+      if (!opts_set->x_flag_devirtualize_speculatively
+         && opts->x_flag_value_profile_transformations)
+       opts->x_flag_devirtualize_speculatively = false;
       break;
 
     case OPT_fprofile_generate_:
index aa45d51994b20034513c5caccdbd06c0564bfc33..736a28b64dc92ca0fd9643d36242a4476f05a55d 100644 (file)
@@ -104,6 +104,7 @@ along with GCC; see the file COPYING3.  If not see
   INSERT_PASSES_AFTER (all_regular_ipa_passes)
   NEXT_PASS (pass_ipa_whole_program_visibility);
   NEXT_PASS (pass_ipa_profile);
+  NEXT_PASS (pass_ipa_devirt);
   NEXT_PASS (pass_ipa_cp);
   NEXT_PASS (pass_ipa_cdtor_merge);
   NEXT_PASS (pass_ipa_inline);
index 9cbc4276f568c4f9301201c162eeeb7f54ab7e6a..d36b9f5b32cab40031131a3f5f7a85eab6a44097 100644 (file)
@@ -1,3 +1,8 @@
+2013-08-31  Jan Hubicka  <jh@suse.cz>
+
+       * g++.dg/ipa/devirt-11.C: Use -fno-devirtualize-speuclatively
+       * g++.dg/tree-ssa/pr45453.C: Likewise.
+
 2013-08-31  Jan Hubicka  <jh@suse.cz>
 
        * gcc.dg/fork-instrumentation.c: New testcase.
index c139f8f9633337e9c9e222e4370af74ec8186962..53f95bbcd3a05a10003d8f89398b757ddc58bab1 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-ipa-inline" } */
+/* { dg-options "-O2 -fdump-ipa-inline -fno-devirtualize-speuclatively" } */
 int baz ();
 struct A
 {
index 78c6460f7273b4d292a3fa771ec30f55f678ac28..c8ef8955f0ecbfabef06c47c888ac317082ed32d 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fdump-tree-optimized -fno-devirtualize-speculatively" } */
 struct S
 {
   S();
index bd1ee7612dc58cae00a58621ee3adea50c3b39f6..5fe9095323578d361fa176f550b424da9949ad79 100644 (file)
@@ -66,6 +66,7 @@ DEFTIMEVAR (TV_CGRAPH                , "callgraph construction")
 DEFTIMEVAR (TV_CGRAPHOPT             , "callgraph optimization")
 DEFTIMEVAR (TV_IPA_INHERITANCE       , "ipa inheritance graph")
 DEFTIMEVAR (TV_IPA_VIRTUAL_CALL      , "ipa virtual call target")
+DEFTIMEVAR (TV_IPA_DEVIRT           , "ipa devirtualization")
 DEFTIMEVAR (TV_IPA_CONSTANT_PROP     , "ipa cp")
 DEFTIMEVAR (TV_IPA_INLINING          , "ipa inlining heuristics")
 DEFTIMEVAR (TV_IPA_FNSPLIT           , "ipa function splitting")
index a6d8a8311a66290aa2e7f8e4f34bbaff8b82ba72..ea1a62f4368e7fd504ae3c1f16239386a4c1430f 100644 (file)
@@ -468,6 +468,7 @@ extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context *ctxt);
 extern simple_ipa_opt_pass *make_pass_ipa_free_inline_summary (gcc::context
                                                               *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt);
+extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);
 extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt);
 extern simple_ipa_opt_pass *make_pass_ipa_pta (gcc::context *ctxt);