Speed up use-after-scope (v2): rewrite into SSA
authorMartin Liska <mliska@suse.cz>
Mon, 23 Jan 2017 12:02:13 +0000 (13:02 +0100)
committerMartin Liska <marxin@gcc.gnu.org>
Mon, 23 Jan 2017 12:02:13 +0000 (12:02 +0000)
2017-01-23  Martin Liska  <mliska@suse.cz>

* asan.c (create_asan_shadow_var): New function.
(asan_expand_poison_ifn): Likewise.
* asan.h (asan_expand_poison_ifn): New declaration.
* internal-fn.c (expand_ASAN_POISON): Likewise.
* internal-fn.def (ASAN_POISON): New builtin.
* sanopt.c (pass_sanopt::execute): Expand
asan_expand_poison_ifn.
* tree-inline.c (copy_decl_for_dup_finish): Make function
external.
* tree-inline.h (copy_decl_for_dup_finish): Likewise.
* tree-ssa.c (is_asan_mark_p): New function.
(execute_update_addresses_taken): Rewrite local variables
(identified just by use-after-scope as addressable) into SSA.
2017-01-23  Martin Liska  <mliska@suse.cz>

* gcc.dg/asan/use-after-scope-3.c: Add additional flags.
* gcc.dg/asan/use-after-scope-9.c: Likewise and grep for
sanopt optimization for ASAN_POISON.

From-SVN: r244791

12 files changed:
gcc/ChangeLog
gcc/asan.c
gcc/asan.h
gcc/internal-fn.c
gcc/internal-fn.def
gcc/sanopt.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
gcc/testsuite/gcc.dg/asan/use-after-scope-9.c
gcc/tree-inline.c
gcc/tree-inline.h
gcc/tree-ssa.c

index de154fdc28efd045c8ac94e4fabd078398765885..34728a9e52fd73a8f28d8c3b531343d20abe8a95 100644 (file)
@@ -1,3 +1,19 @@
+2017-01-23  Martin Liska  <mliska@suse.cz>
+
+       * asan.c (create_asan_shadow_var): New function.
+       (asan_expand_poison_ifn): Likewise.
+       * asan.h (asan_expand_poison_ifn): New declaration.
+       * internal-fn.c (expand_ASAN_POISON): Likewise.
+       * internal-fn.def (ASAN_POISON): New builtin.
+       * sanopt.c (pass_sanopt::execute): Expand
+       asan_expand_poison_ifn.
+       * tree-inline.c (copy_decl_for_dup_finish): Make function
+       external.
+       * tree-inline.h (copy_decl_for_dup_finish): Likewise.
+       * tree-ssa.c (is_asan_mark_p): New function.
+       (execute_update_addresses_taken): Rewrite local variables
+       (identified just by use-after-scope as addressable) into SSA.
+
 2017-01-22  Gerald Pfeifer  <gerald@pfeifer.com>
 
        * doc/install.texi (Specific): opensource.apple.com uses https
index 74500448a1246a59a3ff8029d6441d21b429e18c..fe117a6951a7dd8632d0453d3dcae4d6e6720a7a 100644 (file)
@@ -32,8 +32,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pass.h"
 #include "memmodel.h"
 #include "tm_p.h"
+#include "ssa.h"
 #include "stringpool.h"
-#include "tree-vrp.h"
 #include "tree-ssanames.h"
 #include "optabs.h"
 #include "emit-rtl.h"
@@ -59,6 +59,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "params.h"
 #include "builtins.h"
 #include "fnmatch.h"
+#include "tree-inline.h"
 
 /* AddressSanitizer finds out-of-bounds and use-after-free bugs
    with <2x slowdown on average.
@@ -3064,6 +3065,112 @@ asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
   return true;
 }
 
+/* Create ASAN shadow variable for a VAR_DECL which has been rewritten
+   into SSA.  Already seen VAR_DECLs are stored in SHADOW_VARS_MAPPING.  */
+
+static tree
+create_asan_shadow_var (tree var_decl,
+                       hash_map<tree, tree> &shadow_vars_mapping)
+{
+  tree *slot = shadow_vars_mapping.get (var_decl);
+  if (slot == NULL)
+    {
+      tree shadow_var = copy_node (var_decl);
+
+      copy_body_data id;
+      memset (&id, 0, sizeof (copy_body_data));
+      id.src_fn = id.dst_fn = current_function_decl;
+      copy_decl_for_dup_finish (&id, var_decl, shadow_var);
+
+      DECL_ARTIFICIAL (shadow_var) = 1;
+      DECL_IGNORED_P (shadow_var) = 1;
+      DECL_SEEN_IN_BIND_EXPR_P (shadow_var) = 0;
+      gimple_add_tmp_var (shadow_var);
+
+      shadow_vars_mapping.put (var_decl, shadow_var);
+      return shadow_var;
+    }
+  else
+    return *slot;
+}
+
+bool
+asan_expand_poison_ifn (gimple_stmt_iterator *iter,
+                       bool *need_commit_edge_insert,
+                       hash_map<tree, tree> &shadow_vars_mapping)
+{
+  gimple *g = gsi_stmt (*iter);
+  tree poisoned_var = gimple_call_lhs (g);
+  if (!poisoned_var)
+    {
+      gsi_remove (iter, true);
+      return true;
+    }
+
+  tree shadow_var  = create_asan_shadow_var (SSA_NAME_VAR (poisoned_var),
+                                            shadow_vars_mapping);
+
+  bool recover_p;
+  if (flag_sanitize & SANITIZE_USER_ADDRESS)
+    recover_p = (flag_sanitize_recover & SANITIZE_USER_ADDRESS) != 0;
+  else
+    recover_p = (flag_sanitize_recover & SANITIZE_KERNEL_ADDRESS) != 0;
+  tree size = DECL_SIZE_UNIT (shadow_var);
+  gimple *poison_call
+    = gimple_build_call_internal (IFN_ASAN_MARK, 3,
+                                 build_int_cst (integer_type_node,
+                                                ASAN_MARK_POISON),
+                                 build_fold_addr_expr (shadow_var), size);
+
+  use_operand_p use_p;
+  imm_use_iterator imm_iter;
+  FOR_EACH_IMM_USE_FAST (use_p, imm_iter, poisoned_var)
+    {
+      gimple *use = USE_STMT (use_p);
+      if (is_gimple_debug (use))
+       continue;
+
+      int nargs;
+      tree fun = report_error_func (false, recover_p, tree_to_uhwi (size),
+                                   &nargs);
+
+      gcall *call = gimple_build_call (fun, 1,
+                                      build_fold_addr_expr (shadow_var));
+      gimple_set_location (call, gimple_location (use));
+      gimple *call_to_insert = call;
+
+      /* The USE can be a gimple PHI node.  If so, insert the call on
+        all edges leading to the PHI node.  */
+      if (is_a <gphi *> (use))
+       {
+         gphi *phi = dyn_cast<gphi *> (use);
+         for (unsigned i = 0; i < gimple_phi_num_args (phi); ++i)
+           if (gimple_phi_arg_def (phi, i) == poisoned_var)
+             {
+               edge e = gimple_phi_arg_edge (phi, i);
+
+               if (call_to_insert == NULL)
+                 call_to_insert = gimple_copy (call);
+
+               gsi_insert_seq_on_edge (e, call_to_insert);
+               *need_commit_edge_insert = true;
+               call_to_insert = NULL;
+             }
+       }
+      else
+       {
+         gimple_stmt_iterator gsi = gsi_for_stmt (use);
+         gsi_insert_before (&gsi, call, GSI_NEW_STMT);
+       }
+    }
+
+  SSA_NAME_IS_DEFAULT_DEF (poisoned_var) = true;
+  SSA_NAME_DEF_STMT (poisoned_var) = gimple_build_nop ();
+  gsi_replace (iter, poison_call, false);
+
+  return true;
+}
+
 /* Instrument the current function.  */
 
 static unsigned int
index 2f1f2eeaba78e3c226d7116568e8c4cd97a1bd74..2895bdee645115b45b1d7834fea0966f4b22068e 100644 (file)
@@ -30,6 +30,8 @@ extern void initialize_sanitizer_builtins (void);
 extern tree asan_dynamic_init_call (bool);
 extern bool asan_expand_check_ifn (gimple_stmt_iterator *, bool);
 extern bool asan_expand_mark_ifn (gimple_stmt_iterator *);
+extern bool asan_expand_poison_ifn (gimple_stmt_iterator *, bool *,
+                                   hash_map<tree, tree> &);
 
 extern gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
index 5d71cb2e08dedce82b4f27efff0b66f774e3a793..45e4ce05b86e349fc6304d0f81c28a0a4b989c37 100644 (file)
@@ -380,6 +380,13 @@ expand_ASAN_MARK (internal_fn, gcall *)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_ASAN_POISON (internal_fn, gcall *)
+{
+  gcc_unreachable ();
+}
 
 /* This should get expanded in the tsan pass.  */
 
index 4bf8383a77e9a9f8de4d4d5f8049fee98bf3ff83..7b28b6722ff657b1544c576f69f13adf6ac84233 100644 (file)
@@ -167,6 +167,7 @@ DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
 DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R...")
 DEF_INTERNAL_FN (ASAN_MARK, ECF_LEAF | ECF_NOTHROW, ".R..")
+DEF_INTERNAL_FN (ASAN_POISON, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL)
 DEF_INTERNAL_FN (ADD_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (SUB_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (MUL_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
index 55e07c0c64622d4578280ecbc4a5f5704a148442..70b7aeb80d3182afc63a8703b4e8d3dd7a660655 100644 (file)
@@ -894,6 +894,8 @@ pass_sanopt::execute (function *fun)
   bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
     && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
 
+  hash_map<tree, tree> shadow_vars_mapping;
+  bool need_commit_edge_insert = false;
   FOR_EACH_BB_FN (bb, fun)
     {
       gimple_stmt_iterator gsi;
@@ -931,6 +933,11 @@ pass_sanopt::execute (function *fun)
                case IFN_ASAN_MARK:
                  no_next = asan_expand_mark_ifn (&gsi);
                  break;
+               case IFN_ASAN_POISON:
+                 no_next = asan_expand_poison_ifn (&gsi,
+                                                   &need_commit_edge_insert,
+                                                   shadow_vars_mapping);
+                 break;
                default:
                  break;
                }
@@ -962,6 +969,10 @@ pass_sanopt::execute (function *fun)
            gsi_next (&gsi);
        }
     }
+
+  if (need_commit_edge_insert)
+    gsi_commit_edge_inserts ();
+
   return 0;
 }
 
index 7b9ac5d7e63fad5727cb1eeaa8d486a6cd062e73..bb60f696e0338801155bdad32693a31dbacba60a 100644 (file)
@@ -1,3 +1,9 @@
+2017-01-23  Martin Liska  <mliska@suse.cz>
+
+       * gcc.dg/asan/use-after-scope-3.c: Add additional flags.
+       * gcc.dg/asan/use-after-scope-9.c: Likewise and grep for
+       sanopt optimization for ASAN_POISON.
+
 2016-01-23  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
 
        * gcc.dg/lto/pr69188_0.c: Require profiling support for testcase.
index 9aeed51a770d0908f5ce56c5f65d7cfbfd969670..8b11bea99408027a005f9bb56e6a4b69396bda32 100644 (file)
@@ -1,5 +1,6 @@
 // { dg-do run }
 // { dg-shouldfail "asan" }
+// { dg-additional-options "-O0" }
 
 int
 main (void)
index 2e30deffa1899e530542ea779c123d9fc4dcc2fc..5d069dd18ea3a4a1c1ac511c9569225a3241e9e9 100644 (file)
@@ -1,5 +1,6 @@
 // { dg-do run }
 // { dg-shouldfail "asan" }
+// { dg-additional-options "-O2 -fdump-tree-asan1" }
 
 int
 main (int argc, char **argv)
@@ -15,6 +16,7 @@ main (int argc, char **argv)
   return *ptr;
 }
 
+// { dg-final { scan-tree-dump-times "= ASAN_POISON \\(\\)" 1 "asan1" } }
 // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
 // { dg-output "READ of size .*" }
 // { dg-output ".*'a' <== Memory access at offset \[0-9\]* is inside this variable.*" }
index 42055bd83186cd5e44c5538b7ad923e77ef157da..d63c70f2a1285becd2e8e22e5762c94a34bc8a8c 100644 (file)
@@ -5449,7 +5449,7 @@ declare_inline_vars (tree block, tree vars)
    but now it will be in the TO_FN.  PARM_TO_VAR means enable PARM_DECL to
    VAR_DECL translation.  */
 
-static tree
+tree
 copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy)
 {
   /* Don't generate debug information for the copy if we wouldn't have
index ecfae6b048e1327ad042804ccaec28c7fa28b205..41402a315ec4c3776548b4a1eb4abec6fe1efa07 100644 (file)
@@ -218,6 +218,7 @@ extern gimple_seq copy_gimple_seq_and_replace_locals (gimple_seq seq);
 extern bool debug_find_tree (tree, tree);
 extern tree copy_fn (tree, tree&, tree&);
 extern const char *copy_forbidden (struct function *fun);
+extern tree copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy);
 
 /* This is in tree-inline.c since the routine uses
    data structures from the inliner.  */
index 067143f49b82fc8ad23c8fa47ba539b668f03180..f1826b2c9c45f118d5212db7b0271050c5c21c2d 100644 (file)
@@ -41,6 +41,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfgexpand.h"
 #include "tree-cfg.h"
 #include "tree-dfa.h"
+#include "asan.h"
 
 /* Pointer map of variable mappings, keyed by edge.  */
 static hash_map<edge, auto_vec<edge_var_map> > *edge_var_maps;
@@ -1575,6 +1576,30 @@ maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
     }
 }
 
+/* Return true when STMT is ASAN mark where second argument is an address
+   of a local variable.  */
+
+static bool
+is_asan_mark_p (gimple *stmt)
+{
+  if (!gimple_call_internal_p (stmt, IFN_ASAN_MARK))
+    return false;
+
+  tree addr = get_base_address (gimple_call_arg (stmt, 1));
+  if (TREE_CODE (addr) == ADDR_EXPR
+      && VAR_P (TREE_OPERAND (addr, 0)))
+    {
+      tree var = TREE_OPERAND (addr, 0);
+      unsigned addressable = TREE_ADDRESSABLE (var);
+      TREE_ADDRESSABLE (var) = 0;
+      bool r = is_gimple_reg (var);
+      TREE_ADDRESSABLE (var) = addressable;
+      return r;
+    }
+
+  return false;
+}
+
 /* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.  */
 
 void
@@ -1600,17 +1625,23 @@ execute_update_addresses_taken (void)
          enum gimple_code code = gimple_code (stmt);
          tree decl;
 
-         if (code == GIMPLE_CALL
-             && optimize_atomic_compare_exchange_p (stmt))
+         if (code == GIMPLE_CALL)
            {
-             /* For __atomic_compare_exchange_N if the second argument
-                is &var, don't mark var addressable;
-                if it becomes non-addressable, we'll rewrite it into
-                ATOMIC_COMPARE_EXCHANGE call.  */
-             tree arg = gimple_call_arg (stmt, 1);
-             gimple_call_set_arg (stmt, 1, null_pointer_node);
-             gimple_ior_addresses_taken (addresses_taken, stmt);
-             gimple_call_set_arg (stmt, 1, arg);
+             if (optimize_atomic_compare_exchange_p (stmt))
+               {
+                 /* For __atomic_compare_exchange_N if the second argument
+                    is &var, don't mark var addressable;
+                    if it becomes non-addressable, we'll rewrite it into
+                    ATOMIC_COMPARE_EXCHANGE call.  */
+                 tree arg = gimple_call_arg (stmt, 1);
+                 gimple_call_set_arg (stmt, 1, null_pointer_node);
+                 gimple_ior_addresses_taken (addresses_taken, stmt);
+                 gimple_call_set_arg (stmt, 1, arg);
+               }
+             else if (is_asan_mark_p (stmt))
+               ;
+             else
+               gimple_ior_addresses_taken (addresses_taken, stmt);
            }
          else
            /* Note all addresses taken by the stmt.  */
@@ -1866,6 +1897,24 @@ execute_update_addresses_taken (void)
                        continue;
                      }
                  }
+               else if (is_asan_mark_p (stmt))
+                 {
+                   tree var = TREE_OPERAND (gimple_call_arg (stmt, 1), 0);
+                   if (bitmap_bit_p (suitable_for_renaming, DECL_UID (var)))
+                     {
+                       unlink_stmt_vdef (stmt);
+                       if (asan_mark_p (stmt, ASAN_MARK_POISON))
+                         {
+                           gcall *call
+                             = gimple_build_call_internal (IFN_ASAN_POISON, 0);
+                           gimple_call_set_lhs (call, var);
+                           gsi_replace (&gsi, call, GSI_SAME_STMT);
+                         }
+                       else
+                         gsi_remove (&gsi, true);
+                       continue;
+                     }
+                 }
                for (i = 0; i < gimple_call_num_args (stmt); ++i)
                  {
                    tree *argp = gimple_call_arg_ptr (stmt, i);