re PR target/65697 (__atomic memory barriers not strong enough for __sync builtins)
[gcc.git] / gcc / gimple-ssa-isolate-paths.c
index ab6185c276763246af51d88460fd1c95c01e1993..2633736bb2b4575dd222ac5aa7332c93b0ba2ca7 100644 (file)
@@ -1,7 +1,7 @@
 /* Detect paths through the CFG which can never be executed in a conforming
    program and isolate them.
 
-   Copyright (C) 2013-2014 Free Software Foundation, Inc.
+   Copyright (C) 2013-2015 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -22,13 +22,22 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "alias.h"
+#include "symtab.h"
+#include "options.h"
 #include "tree.h"
+#include "fold-const.h"
 #include "flags.h"
+#include "predict.h"
+#include "tm.h"
+#include "hard-reg-set.h"
+#include "function.h"
+#include "dominance.h"
+#include "cfg.h"
 #include "basic-block.h"
 #include "tree-ssa-alias.h"
 #include "internal-fn.h"
 #include "gimple-expr.h"
-#include "is-a.h"
 #include "gimple.h"
 #include "gimple-iterator.h"
 #include "gimple-walk.h"
@@ -42,6 +51,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "tree-pass.h"
 #include "tree-cfg.h"
+#include "diagnostic-core.h"
+#include "intl.h"
 
 
 static bool cfg_altered;
@@ -94,7 +105,7 @@ insert_trap_and_remove_trailing_statements (gimple_stmt_iterator *si_p, tree op)
       update_stmt (stmt);
     }
 
-  gimple new_stmt
+  gcall *new_stmt
     = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
   gimple_seq seq = NULL;
   gimple_seq_add_stmt (&seq, new_stmt);
@@ -132,13 +143,15 @@ insert_trap_and_remove_trailing_statements (gimple_stmt_iterator *si_p, tree op)
    Optimization is simple as well.  Replace STMT in BB' with an
    unconditional trap and remove all outgoing edges from BB'.
 
+   If RET_ZERO, do not trap, only return NULL.
+
    DUPLICATE is a pre-existing duplicate, use it as BB' if it exists.
 
    Return BB'.  */
 
 basic_block
 isolate_path (basic_block bb, basic_block duplicate,
-             edge e, gimple stmt, tree op)
+             edge e, gimple stmt, tree op, bool ret_zero)
 {
   gimple_stmt_iterator si, si2;
   edge_iterator ei;
@@ -151,8 +164,9 @@ isolate_path (basic_block bb, basic_block duplicate,
   if (!duplicate)
     {
       duplicate = duplicate_block (bb, NULL, NULL);
-      for (ei = ei_start (duplicate->succs); (e2 = ei_safe_edge (ei)); )
-       remove_edge (e2);
+      if (!ret_zero)
+       for (ei = ei_start (duplicate->succs); (e2 = ei_safe_edge (ei)); )
+         remove_edge (e2);
     }
 
   /* Complete the isolation step by redirecting E to reach DUPLICATE.  */
@@ -197,7 +211,17 @@ isolate_path (basic_block bb, basic_block duplicate,
      SI2 points to the duplicate of STMT in DUPLICATE.  Insert a trap
      before SI2 and remove SI2 and all trailing statements.  */
   if (!gsi_end_p (si2))
-    insert_trap_and_remove_trailing_statements (&si2, op);
+    {
+      if (ret_zero)
+       {
+         greturn *ret = as_a <greturn *> (gsi_stmt (si2));
+         tree zero = build_zero_cst (TREE_TYPE (gimple_return_retval (ret)));
+         gimple_return_set_retval (ret, zero);
+         update_stmt (ret);
+       }
+      else
+       insert_trap_and_remove_trailing_statements (&si2, op);
+    }
 
   return duplicate;
 }
@@ -217,7 +241,7 @@ find_implicit_erroneous_behaviour (void)
 
   FOR_EACH_BB_FN (bb, cfun)
     {
-      gimple_stmt_iterator si;
+      gphi_iterator si;
 
       /* Out of an abundance of caution, do not isolate paths to a
         block where the block has any abnormal outgoing edges.
@@ -236,7 +260,7 @@ find_implicit_erroneous_behaviour (void)
         cases.   */
       for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
        {
-         gimple phi = gsi_stmt (si);
+         gphi *phi = si.phi ();
          tree lhs = gimple_phi_result (phi);
 
          /* If the result is not a pointer, then there is no need to
@@ -255,16 +279,53 @@ find_implicit_erroneous_behaviour (void)
               i = next_i)
            {
              tree op = gimple_phi_arg_def (phi, i);
+             edge e = gimple_phi_arg_edge (phi, i);
+             imm_use_iterator iter;
+             gimple use_stmt;
 
              next_i = i + 1;
 
+             if (TREE_CODE (op) == ADDR_EXPR)
+               {
+                 tree valbase = get_base_address (TREE_OPERAND (op, 0));
+                 if ((TREE_CODE (valbase) == VAR_DECL
+                      && !is_global_var (valbase))
+                     || TREE_CODE (valbase) == PARM_DECL)
+                   {
+                     FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
+                       {
+                         greturn *return_stmt
+                           = dyn_cast <greturn *> (use_stmt);
+                         if (!return_stmt)
+                           continue;
+
+                         if (gimple_return_retval (return_stmt) != lhs)
+                           continue;
+
+                         if (warning_at (gimple_location (use_stmt),
+                                         OPT_Wreturn_local_addr,
+                                         "function may return address "
+                                         "of local variable"))
+                           inform (DECL_SOURCE_LOCATION(valbase),
+                                   "declared here");
+
+                         if (gimple_bb (use_stmt) == bb)
+                           {
+                             duplicate = isolate_path (bb, duplicate, e,
+                                                       use_stmt, lhs, true);
+
+                             /* When we remove an incoming edge, we need to
+                                reprocess the Ith element.  */
+                             next_i = i;
+                             cfg_altered = true;
+                           }
+                       }
+                   }
+               }
+
              if (!integer_zerop (op))
                continue;
 
-             edge e = gimple_phi_arg_edge (phi, i);
-             imm_use_iterator iter;
-             gimple use_stmt;
-
              /* We've got a NULL PHI argument.  Now see if the
                 PHI's result is dereferenced within BB.  */
              FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
@@ -280,8 +341,8 @@ find_implicit_erroneous_behaviour (void)
                                           flag_isolate_erroneous_paths_attribute))
 
                    {
-                     duplicate = isolate_path (bb, duplicate,
-                                               e, use_stmt, lhs);
+                     duplicate = isolate_path (bb, duplicate, e,
+                                               use_stmt, lhs, false);
 
                      /* When we remove an incoming edge, we need to
                         reprocess the Ith element.  */
@@ -347,9 +408,45 @@ find_explicit_erroneous_behaviour (void)
              cfg_altered = true;
              break;
            }
+
+         /* Detect returning the address of a local variable.  This only
+            becomes undefined behavior if the result is used, so we do not
+            insert a trap and only return NULL instead.  */
+         if (greturn *return_stmt = dyn_cast <greturn *> (stmt))
+           {
+             tree val = gimple_return_retval (return_stmt);
+             if (val && TREE_CODE (val) == ADDR_EXPR)
+               {
+                 tree valbase = get_base_address (TREE_OPERAND (val, 0));
+                 if ((TREE_CODE (valbase) == VAR_DECL
+                      && !is_global_var (valbase))
+                     || TREE_CODE (valbase) == PARM_DECL)
+                   {
+                     /* We only need it for this particular case.  */
+                     calculate_dominance_info (CDI_POST_DOMINATORS);
+                     const char* msg;
+                     bool always_executed = dominated_by_p
+                       (CDI_POST_DOMINATORS,
+                        single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)), bb);
+                     if (always_executed)
+                       msg = N_("function returns address of local variable");
+                     else
+                       msg = N_("function may return address of "
+                                "local variable");
+
+                     if (warning_at (gimple_location (stmt),
+                                     OPT_Wreturn_local_addr, msg))
+                       inform (DECL_SOURCE_LOCATION(valbase), "declared here");
+                     tree zero = build_zero_cst (TREE_TYPE (val));
+                     gimple_return_set_retval (return_stmt, zero);
+                     update_stmt (stmt);
+                   }
+               }
+           }
        }
     }
 }
+
 /* Search the function for statements which, if executed, would cause
    the program to fault such as a dereference of a NULL pointer.
 
@@ -420,13 +517,12 @@ const pass_data pass_data_isolate_erroneous_paths =
   GIMPLE_PASS, /* type */
   "isolate-paths", /* name */
   OPTGROUP_NONE, /* optinfo_flags */
-  true, /* has_execute */
   TV_ISOLATE_ERRONEOUS_PATHS, /* tv_id */
   ( PROP_cfg | PROP_ssa ), /* properties_required */
   0, /* properties_provided */
   0, /* properties_destroyed */
   0, /* todo_flags_start */
-  TODO_verify_ssa, /* todo_flags_finish */
+  0, /* todo_flags_finish */
 };
 
 class pass_isolate_erroneous_paths : public gimple_opt_pass
@@ -446,7 +542,10 @@ public:
              || flag_isolate_erroneous_paths_attribute != 0);
     }
 
-  unsigned int execute () { return gimple_ssa_isolate_erroneous_paths (); }
+  virtual unsigned int execute (function *)
+    {
+      return gimple_ssa_isolate_erroneous_paths ();
+    }
 
 }; // class pass_isolate_erroneous_paths
 }