]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Enable musttail tail conversion even when not optimizing
authorAndi Kleen <ak@linux.intel.com>
Thu, 16 May 2024 02:57:22 +0000 (19:57 -0700)
committerAndi Kleen <ak@gcc.gnu.org>
Thu, 18 Jul 2024 04:25:27 +0000 (21:25 -0700)
Enable the tailcall optimization for non optimizing builds,
but in this case only checks calls that have the musttail attribute set.
This makes musttail work without optimization.

This is done with a new late musttail pass that is only active when
not optimizing. The new pass relies on tree-cfg to discover musttails.
This avoids a ~0.8% compiler run time penalty at -O0.

gcc/ChangeLog:

PR c/83324
* function.h (struct function): Add has_musttail.
* lto-streamer-in.cc (input_struct_function_base): Stream
has_musttail.
* lto-streamer-out.cc (output_struct_function_base): Dito.
* passes.def (pass_musttail): Add.
* tree-cfg.cc (notice_special_calls): Record has_musttail.
(clear_special_calls): Clear has_musttail.
* tree-pass.h (make_pass_musttail): Add.
* tree-tailcall.cc (find_tail_calls): Handle only_musttail
argument.
(tree_optimize_tail_calls_1): Pass on only_musttail.
(execute_tail_calls): Pass only_musttail as false.
(class pass_musttail): Add.
(make_pass_musttail): Add.

gcc/function.h
gcc/lto-streamer-in.cc
gcc/lto-streamer-out.cc
gcc/passes.def
gcc/tree-cfg.cc
gcc/tree-pass.h
gcc/tree-tailcall.cc

index c0ba6cc1531a8713b6d491807c38f324300a1c98..fbeadeaf410423ef2f40f0dd4d19d3b9536eef18 100644 (file)
@@ -430,6 +430,9 @@ struct GTY(()) function {
   /* Nonzero when the tail call has been identified.  */
   unsigned int tail_call_marked : 1;
 
+  /* Has musttail marked calls.  */
+  unsigned int has_musttail : 1;
+
   /* Nonzero if the current function contains a #pragma GCC unroll.  */
   unsigned int has_unroll : 1;
 
index ad0ca24007a08f0dc7192e8232aa9fe5259b29eb..2e592be80823685b961a21fac2c5f23efbe06074 100644 (file)
@@ -1325,6 +1325,7 @@ input_struct_function_base (struct function *fn, class data_in *data_in,
   fn->calls_eh_return = bp_unpack_value (&bp, 1);
   fn->has_force_vectorize_loops = bp_unpack_value (&bp, 1);
   fn->has_simduid_loops = bp_unpack_value (&bp, 1);
+  fn->has_musttail = bp_unpack_value (&bp, 1);
   fn->assume_function = bp_unpack_value (&bp, 1);
   fn->va_list_fpr_size = bp_unpack_value (&bp, 8);
   fn->va_list_gpr_size = bp_unpack_value (&bp, 8);
index 8b4bf9659cb30c4f39c6b2476a4bbd19910d13fb..c329ac8af958dc89601cb6f589d6f708b808185f 100644 (file)
@@ -2282,6 +2282,7 @@ output_struct_function_base (struct output_block *ob, struct function *fn)
   bp_pack_value (&bp, fn->calls_eh_return, 1);
   bp_pack_value (&bp, fn->has_force_vectorize_loops, 1);
   bp_pack_value (&bp, fn->has_simduid_loops, 1);
+  bp_pack_value (&bp, fn->has_musttail, 1);
   bp_pack_value (&bp, fn->assume_function, 1);
   bp_pack_value (&bp, fn->va_list_fpr_size, 8);
   bp_pack_value (&bp, fn->va_list_gpr_size, 8);
index 94386ba545775b9973881e074f387abc5dc4e0b1..5e7f9395d84fbe43f134261cefbe25a7f79a7e47 100644 (file)
@@ -444,6 +444,7 @@ along with GCC; see the file COPYING3.  If not see
   NEXT_PASS (pass_tsan_O0);
   NEXT_PASS (pass_sanopt);
   NEXT_PASS (pass_cleanup_eh);
+  NEXT_PASS (pass_musttail);
   NEXT_PASS (pass_lower_resx);
   NEXT_PASS (pass_nrv);
   NEXT_PASS (pass_gimple_isel);
index 7fb7b92966be0e54824079b193d53a92c3fb28c6..e6fd1294b958bc78d3baefffa159faf10f759d5c 100644 (file)
@@ -2290,6 +2290,8 @@ notice_special_calls (gcall *call)
     cfun->calls_alloca = true;
   if (flags & ECF_RETURNS_TWICE)
     cfun->calls_setjmp = true;
+  if (gimple_call_must_tail_p (call))
+    cfun->has_musttail = true;
 }
 
 
@@ -2301,6 +2303,7 @@ clear_special_calls (void)
 {
   cfun->calls_alloca = false;
   cfun->calls_setjmp = false;
+  cfun->has_musttail = false;
 }
 
 /* Remove PHI nodes associated with basic block BB and all edges out of BB.  */
index 84bc91b51e9d9f50d84e02ff78589d26f84a45d7..3a0cf13089e2f15d7263112366e9ad380cf68604 100644 (file)
@@ -368,6 +368,7 @@ extern gimple_opt_pass *make_pass_sra (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_sra_early (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tail_recursion (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tail_calls (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_musttail (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_fix_loops (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tree_loop (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tree_no_loop (gcc::context *ctxt);
index e9f7f8a12b3a0cc274be40d98e5785b43f7901da..43e8c25215cbf6369ec86c0d0d0c82e9461b993a 100644 (file)
@@ -408,10 +408,10 @@ static live_vars_map *live_vars;
 static vec<bitmap_head> live_vars_vec;
 
 /* Finds tailcalls falling into basic block BB. The list of found tailcalls is
-   added to the start of RET.  */
+   added to the start of RET. When ONLY_MUSTTAIL is set only handle musttail.  */
 
 static void
-find_tail_calls (basic_block bb, struct tailcall **ret)
+find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail)
 {
   tree ass_var = NULL_TREE, ret_var, func, param;
   gimple *stmt;
@@ -445,6 +445,9 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
       if (is_gimple_call (stmt))
        {
          call = as_a <gcall *> (stmt);
+         /* Handle only musttail calls when not optimizing.  */
+         if (only_musttail && !gimple_call_must_tail_p (call))
+           return;
          ass_var = gimple_call_lhs (call);
          break;
        }
@@ -467,7 +470,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
       edge_iterator ei;
       /* Recurse to the predecessors.  */
       FOR_EACH_EDGE (e, ei, bb->preds)
-       find_tail_calls (e->src, ret);
+       find_tail_calls (e->src, ret, only_musttail);
 
       return;
     }
@@ -528,7 +531,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
   func = gimple_call_fndecl (call);
   if (func
       && !fndecl_built_in_p (func)
-      && recursive_call_p (current_function_decl, func))
+      && recursive_call_p (current_function_decl, func)
+      && !only_musttail)
     {
       tree arg;
 
@@ -1094,10 +1098,11 @@ create_tailcall_accumulator (const char *label, basic_block bb, tree init)
 }
 
 /* Optimizes tail calls in the function, turning the tail recursion
-   into iteration.  */
+   into iteration. When ONLY_MUSTCALL is true only optimize mustcall
+   marked calls.  */
 
 static unsigned int
-tree_optimize_tail_calls_1 (bool opt_tailcalls)
+tree_optimize_tail_calls_1 (bool opt_tailcalls, bool only_mustcall)
 {
   edge e;
   bool phis_constructed = false;
@@ -1117,7 +1122,7 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls)
       /* Only traverse the normal exits, i.e. those that end with return
         statement.  */
       if (safe_is_a <greturn *> (*gsi_last_bb (e->src)))
-       find_tail_calls (e->src, &tailcalls);
+       find_tail_calls (e->src, &tailcalls, only_mustcall);
     }
 
   if (live_vars)
@@ -1228,7 +1233,7 @@ gate_tail_calls (void)
 static unsigned int
 execute_tail_calls (void)
 {
-  return tree_optimize_tail_calls_1 (true);
+  return tree_optimize_tail_calls_1 (true, false);
 }
 
 namespace {
@@ -1261,7 +1266,7 @@ public:
   bool gate (function *) final override { return gate_tail_calls (); }
   unsigned int execute (function *) final override
     {
-      return tree_optimize_tail_calls_1 (false);
+      return tree_optimize_tail_calls_1 (false, false);
     }
 
 }; // class pass_tail_recursion
@@ -1312,3 +1317,48 @@ make_pass_tail_calls (gcc::context *ctxt)
 {
   return new pass_tail_calls (ctxt);
 }
+
+namespace {
+
+const pass_data pass_data_musttail =
+{
+  GIMPLE_PASS, /* type */
+  "musttail", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_cfg | PROP_ssa ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_musttail : public gimple_opt_pass
+{
+public:
+  pass_musttail (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_musttail, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  /* This pass is only used when the other tail call pass
+     doesn't run to make [[musttail]] still work. But only
+     run it when there is actually a musttail in the function.  */
+  bool gate (function *f) final override
+  {
+    return !flag_optimize_sibling_calls && f->has_musttail;
+  }
+  unsigned int execute (function *) final override
+  {
+    return tree_optimize_tail_calls_1 (true, true);
+  }
+
+}; // class pass_musttail
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_musttail (gcc::context *ctxt)
+{
+  return new pass_musttail (ctxt);
+}