IA MCU psABI support: changes to libraries
[gcc.git] / gcc / ipa-split.c
index 924c6b1e7cde4c5b41c5261a38e31682db4635ac..13d9a6447e5248a03bdb2b8ea0faeafbf94dd918 100644 (file)
@@ -77,22 +77,14 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "hash-set.h"
-#include "machmode.h"
-#include "vec.h"
-#include "double-int.h"
-#include "input.h"
 #include "alias.h"
 #include "symtab.h"
 #include "options.h"
-#include "wide-int.h"
-#include "inchash.h"
 #include "tree.h"
 #include "fold-const.h"
 #include "predict.h"
 #include "tm.h"
 #include "hard-reg-set.h"
-#include "input.h"
 #include "function.h"
 #include "dominance.h"
 #include "cfg.h"
@@ -101,19 +93,24 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-ssa-alias.h"
 #include "internal-fn.h"
 #include "gimple-expr.h"
-#include "is-a.h"
 #include "gimple.h"
 #include "stringpool.h"
-#include "expr.h"
+#include "rtl.h"
+#include "flags.h"
+#include "insn-config.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
 #include "calls.h"
+#include "emit-rtl.h"
+#include "varasm.h"
+#include "stmt.h"
+#include "expr.h"
 #include "gimplify.h"
 #include "gimple-iterator.h"
 #include "gimplify-me.h"
 #include "gimple-walk.h"
 #include "target.h"
-#include "hash-map.h"
-#include "plugin-api.h"
-#include "ipa-ref.h"
 #include "cgraph.h"
 #include "alloc-pool.h"
 #include "symbol-summary.h"
@@ -122,12 +119,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "tree-phinodes.h"
 #include "ssa-iterators.h"
-#include "stringpool.h"
 #include "tree-ssanames.h"
 #include "tree-into-ssa.h"
 #include "tree-dfa.h"
 #include "tree-pass.h"
-#include "flags.h"
 #include "diagnostic.h"
 #include "tree-dump.h"
 #include "tree-inline.h"
@@ -588,6 +583,31 @@ consider_split (struct split_point *current, bitmap non_ssa_vars,
       return;
     }
 
+  /* Splitting functions brings the target out of comdat group; this will
+     lead to code duplication if the function is reused by other unit.
+     Limit this duplication.  This is consistent with limit in tree-sra.c  
+     FIXME: with LTO we ought to be able to do better!  */
+  if (DECL_ONE_ONLY (current_function_decl)
+      && current->split_size >= (unsigned int) MAX_INLINE_INSNS_AUTO)
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       fprintf (dump_file,
+                "  Refused: function is COMDAT and tail is too large\n");
+      return;
+    }
+  /* For comdat functions also reject very small tails; those will likely get
+     inlined back and we do not want to risk the duplication overhead.
+     FIXME: with LTO we ought to be able to do better!  */
+  if (DECL_ONE_ONLY (current_function_decl)
+      && current->split_size
+        <= (unsigned int) PARAM_VALUE (PARAM_EARLY_INLINING_INSNS) / 2)
+    {
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       fprintf (dump_file,
+                "  Refused: function is COMDAT and tail is too small\n");
+      return;
+    }
+
   /* FIXME: we currently can pass only SSA function parameters to the split
      arguments.  Once parm_adjustment infrastructure is supported by cloning,
      we can pass more than that.  */
@@ -734,7 +754,8 @@ consider_split (struct split_point *current, bitmap non_ssa_vars,
    of the form:
    <retval> = tmp_var;
    return <retval>
-   but return_bb can not be more complex than this.
+   but return_bb can not be more complex than this (except for
+   -fsanitize=thread we allow TSAN_FUNC_EXIT () internal call in there).
    If nothing is found, return the exit block.
 
    When there are multiple RETURN statement, chose one with return value,
@@ -779,6 +800,13 @@ find_return_bb (void)
          found_return = true;
          retval = gimple_return_retval (return_stmt);
        }
+      /* For -fsanitize=thread, allow also TSAN_FUNC_EXIT () in the return
+        bb.  */
+      else if ((flag_sanitize & SANITIZE_THREAD)
+              && is_gimple_call (stmt)
+              && gimple_call_internal_p (stmt)
+              && gimple_call_internal_fn (stmt) == IFN_TSAN_FUNC_EXIT)
+       ;
       else
        break;
     }
@@ -1039,12 +1067,11 @@ typedef struct
    the component used by consider_split.  */
 
 static void
-find_split_points (int overall_time, int overall_size)
+find_split_points (basic_block return_bb, int overall_time, int overall_size)
 {
   stack_entry first;
   vec<stack_entry> stack = vNULL;
   basic_block bb;
-  basic_block return_bb = find_return_bb ();
   struct split_point current;
 
   current.header_time = overall_time;
@@ -1188,32 +1215,19 @@ find_split_points (int overall_time, int overall_size)
   BITMAP_FREE (current.ssa_names_to_pass);
 }
 
-/* Build and insert initialization of returned bounds RETBND
-   for returned value RETVAL.  Statements are inserted after
-   a statement pointed by GSI and GSI is modified to point to
-   the last inserted statement.  */
-
-static void
-insert_bndret_call_after (tree retbnd, tree retval, gimple_stmt_iterator *gsi)
-{
-  tree fndecl = targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDRET);
-  gimple bndret = gimple_build_call (fndecl, 1, retval);
-  gimple_call_set_lhs (bndret, retbnd);
-  gsi_insert_after (gsi, bndret, GSI_CONTINUE_LINKING);
-}
 /* Split function at SPLIT_POINT.  */
 
 static void
-split_function (struct split_point *split_point)
+split_function (basic_block return_bb, struct split_point *split_point,
+               bool add_tsan_func_exit)
 {
   vec<tree> args_to_pass = vNULL;
   bitmap args_to_skip;
   tree parm;
   int num = 0;
   cgraph_node *node, *cur_node = cgraph_node::get (current_function_decl);
-  basic_block return_bb = find_return_bb ();
   basic_block call_bb;
-  gcall *call;
+  gcall *call, *tsan_func_exit_call = NULL;
   edge e;
   edge_iterator ei;
   tree retval = NULL, real_retval = NULL, retbnd = NULL;
@@ -1359,6 +1373,8 @@ split_function (struct split_point *split_point)
     (vNULL, NULL, args_to_skip, !split_part_return_p, split_point->split_bbs,
      split_point->entry_bb, "part");
 
+  node->split_part = true;
+
   /* Let's take a time profile for splitted function.  */
   node->tp_first_run = cur_node->tp_first_run + 1;
 
@@ -1499,11 +1515,18 @@ split_function (struct split_point *split_point)
          || DECL_BY_REFERENCE (DECL_RESULT (current_function_decl))))
     gimple_call_set_return_slot_opt (call, true);
 
+  if (add_tsan_func_exit)
+    tsan_func_exit_call = gimple_build_call_internal (IFN_TSAN_FUNC_EXIT, 0);
+
   /* Update return value.  This is bit tricky.  When we do not return,
      do nothing.  When we return we might need to update return_bb
      or produce a new return statement.  */
   if (!split_part_return_p)
-    gsi_insert_after (&gsi, call, GSI_NEW_STMT);
+    {
+      gsi_insert_after (&gsi, call, GSI_NEW_STMT);
+      if (tsan_func_exit_call)
+       gsi_insert_after (&gsi, tsan_func_exit_call, GSI_NEW_STMT);
+    }
   else
     {
       e = make_edge (call_bb, return_bb,
@@ -1600,13 +1623,15 @@ split_function (struct split_point *split_point)
                    }
                  /* Build bndret call to obtain returned bounds.  */
                  if (retbnd)
-                   insert_bndret_call_after (retbnd, retval, &gsi);
+                   chkp_insert_retbnd_call (retbnd, retval, &gsi);
                  gimple_call_set_lhs (call, retval);
                  update_stmt (call);
                }
            }
          else
            gsi_insert_after (&gsi, call, GSI_NEW_STMT);
+         if (tsan_func_exit_call)
+           gsi_insert_after (&gsi, tsan_func_exit_call, GSI_NEW_STMT);
        }
       /* We don't use return block (there is either no return in function or
         multiple of them).  So create new basic block with return statement.
@@ -1648,7 +1673,9 @@ split_function (struct split_point *split_point)
           gsi_insert_after (&gsi, call, GSI_NEW_STMT);
          /* Build bndret call to obtain returned bounds.  */
          if (retbnd)
-           insert_bndret_call_after (retbnd, retval, &gsi);
+           chkp_insert_retbnd_call (retbnd, retval, &gsi);
+         if (tsan_func_exit_call)
+           gsi_insert_after (&gsi, tsan_func_exit_call, GSI_NEW_STMT);
          ret = gimple_build_return (retval);
          gsi_insert_after (&gsi, ret, GSI_NEW_STMT);
        }
@@ -1726,6 +1753,7 @@ execute_split_functions (void)
        /* Local functions called once will be completely inlined most of time.  */
        || (!node->callers->next_caller && node->local.local))
       && !node->address_taken
+      && !node->has_aliases_p ()
       && (!flag_lto || !node->externally_visible))
     {
       if (dump_file)
@@ -1756,6 +1784,8 @@ execute_split_functions (void)
   /* Compute local info about basic blocks and determine function size/time.  */
   bb_info_vec.safe_grow_cleared (last_basic_block_for_fn (cfun) + 1);
   memset (&best_split_point, 0, sizeof (best_split_point));
+  basic_block return_bb = find_return_bb ();
+  int tsan_exit_found = -1;
   FOR_EACH_BB_FN (bb, cfun)
     {
       int time = 0;
@@ -1782,16 +1812,37 @@ execute_split_functions (void)
                       freq, this_size, this_time);
              print_gimple_stmt (dump_file, stmt, 0, 0);
            }
+
+         if ((flag_sanitize & SANITIZE_THREAD)
+             && is_gimple_call (stmt)
+             && gimple_call_internal_p (stmt)
+             && gimple_call_internal_fn (stmt) == IFN_TSAN_FUNC_EXIT)
+           {
+             /* We handle TSAN_FUNC_EXIT for splitting either in the
+                return_bb, or in its immediate predecessors.  */
+             if ((bb != return_bb && !find_edge (bb, return_bb))
+                 || (tsan_exit_found != -1
+                     && tsan_exit_found != (bb != return_bb)))
+               {
+                 if (dump_file)
+                   fprintf (dump_file, "Not splitting: TSAN_FUNC_EXIT"
+                            " in unexpected basic block.\n");
+                 BITMAP_FREE (forbidden_dominators);
+                 bb_info_vec.release ();
+                 return 0;
+               }
+             tsan_exit_found = bb != return_bb;
+           }
        }
       overall_time += time;
       overall_size += size;
       bb_info_vec[bb->index].time = time;
       bb_info_vec[bb->index].size = size;
     }
-  find_split_points (overall_time, overall_size);
+  find_split_points (return_bb, overall_time, overall_size);
   if (best_split_point.split_bbs)
     {
-      split_function (&best_split_point);
+      split_function (return_bb, &best_split_point, tsan_exit_found == 1);
       BITMAP_FREE (best_split_point.ssa_names_to_pass);
       BITMAP_FREE (best_split_point.split_bbs);
       todo = TODO_update_ssa | TODO_cleanup_cfg;