This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] Fix missed DSE opportunity with operator delete.


On 04/18/2016 12:14 PM, Richard Biener wrote:
> 
> Enlarging tree_function_decl is bad.
Probably using 3 bits for malloc_flag, operator_new_flag and free_flag is
redundant. I packed the state into 2 bits.
> 
> Passes should get at the info via flags_from_decl_or_type () and a new
> ECF_FREE.
Fixed.

-- 
Regards,
    Mikhail Maltsev
diff --git a/gcc/ada/gcc-interface/trans.c b/gcc/ada/gcc-interface/trans.c
index 357d26f..00e4f84 100644
--- a/gcc/ada/gcc-interface/trans.c
+++ b/gcc/ada/gcc-interface/trans.c
@@ -400,7 +400,7 @@ gigi (Node_Id gnat_root,
 			   ftype,
 			   NULL_TREE, is_disabled, false, true, true, false,
 			   true, false, NULL, Empty);
-  DECL_IS_MALLOC (malloc_decl) = 1;
+  DECL_SET_MALLOC (malloc_decl);
 
   /* free is a function declaration tree for a function to free memory.  */
   free_decl
diff --git a/gcc/ada/gcc-interface/utils.c b/gcc/ada/gcc-interface/utils.c
index d568dff..5b12e3d 100644
--- a/gcc/ada/gcc-interface/utils.c
+++ b/gcc/ada/gcc-interface/utils.c
@@ -6026,7 +6026,7 @@ handle_malloc_attribute (tree *node, tree name, tree ARG_UNUSED (args),
 {
   if (TREE_CODE (*node) == FUNCTION_DECL
       && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (*node))))
-    DECL_IS_MALLOC (*node) = 1;
+    DECL_SET_MALLOC (*node);
   else
     {
       warning (OPT_Wattributes, "%qs attribute ignored",
diff --git a/gcc/builtin-attrs.def b/gcc/builtin-attrs.def
index 089817a..ddaf3e6 100644
--- a/gcc/builtin-attrs.def
+++ b/gcc/builtin-attrs.def
@@ -88,6 +88,7 @@ DEF_ATTR_IDENT (ATTR_CONST, "const")
 DEF_ATTR_IDENT (ATTR_FORMAT, "format")
 DEF_ATTR_IDENT (ATTR_FORMAT_ARG, "format_arg")
 DEF_ATTR_IDENT (ATTR_MALLOC, "malloc")
+DEF_ATTR_IDENT (ATTR_FREE, "free")
 DEF_ATTR_IDENT (ATTR_NONNULL, "nonnull")
 DEF_ATTR_IDENT (ATTR_NORETURN, "noreturn")
 DEF_ATTR_IDENT (ATTR_NOTHROW, "nothrow")
@@ -141,6 +142,10 @@ DEF_ATTR_TREE_LIST (ATTR_MALLOC_NOTHROW_LIST, ATTR_MALLOC,	\
 			ATTR_NULL, ATTR_NOTHROW_LIST)
 DEF_ATTR_TREE_LIST (ATTR_MALLOC_NOTHROW_LEAF_LIST, ATTR_MALLOC,	\
 			ATTR_NULL, ATTR_NOTHROW_LEAF_LIST)
+DEF_ATTR_TREE_LIST (ATTR_FREE_NOTHROW_LIST, ATTR_FREE,		\
+			ATTR_NULL, ATTR_NOTHROW_LIST)
+DEF_ATTR_TREE_LIST (ATTR_FREE_NOTHROW_LEAF_LIST, ATTR_FREE,	\
+			ATTR_NULL, ATTR_NOTHROW_LEAF_LIST)
 DEF_ATTR_TREE_LIST (ATTR_SENTINEL_NOTHROW_LIST, ATTR_SENTINEL,	\
 			ATTR_NULL, ATTR_NOTHROW_LIST)
 DEF_ATTR_TREE_LIST (ATTR_SENTINEL_NOTHROW_LEAF_LIST, ATTR_SENTINEL,	\
@@ -269,8 +274,10 @@ DEF_ATTR_TREE_LIST (ATTR_TM_NOTHROW_RT_LIST,
 DEF_ATTR_TREE_LIST (ATTR_TMPURE_MALLOC_NOTHROW_LIST,
 		   ATTR_TM_TMPURE, ATTR_NULL, ATTR_MALLOC_NOTHROW_LIST)
 /* Same attributes used for BUILT_IN_FREE except with TM_PURE thrown in.  */
-DEF_ATTR_TREE_LIST (ATTR_TMPURE_NOTHROW_LIST,
-		   ATTR_TM_TMPURE, ATTR_NULL, ATTR_NOTHROW_LIST)
+DEF_ATTR_TREE_LIST (ATTR_TMPURE_FREE_NOTHROW_LIST,
+		   ATTR_TM_TMPURE, ATTR_NULL, ATTR_FREE_NOTHROW_LIST)
+DEF_ATTR_TREE_LIST (ATTR_TMPURE_FREE_NOTHROW_LEAF_LIST,
+		   ATTR_TM_TMPURE, ATTR_NULL, ATTR_FREE_NOTHROW_LEAF_LIST)
 
 DEF_ATTR_TREE_LIST (ATTR_TMPURE_NOTHROW_LEAF_LIST,
 		    ATTR_TM_TMPURE, ATTR_NULL, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 2fc7f65..e3d1614 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -781,7 +781,7 @@ DEF_EXT_LIB_BUILTIN    (BUILT_IN_FFSLL, "ffsll", BT_FN_INT_LONGLONG, ATTR_CONST_
 DEF_EXT_LIB_BUILTIN        (BUILT_IN_FORK, "fork", BT_FN_PID, ATTR_NOTHROW_LIST)
 DEF_GCC_BUILTIN        (BUILT_IN_FRAME_ADDRESS, "frame_address", BT_FN_PTR_UINT, ATTR_NULL)
 /* [trans-mem]: Adjust BUILT_IN_TM_FREE if BUILT_IN_FREE is changed.  */
-DEF_LIB_BUILTIN        (BUILT_IN_FREE, "free", BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST)
+DEF_LIB_BUILTIN        (BUILT_IN_FREE, "free", BT_FN_VOID_PTR, ATTR_FREE_NOTHROW_LEAF_LIST)
 DEF_GCC_BUILTIN        (BUILT_IN_FROB_RETURN_ADDR, "frob_return_addr", BT_FN_PTR_PTR, ATTR_NULL)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_GETTEXT, "gettext", BT_FN_STRING_CONST_STRING, ATTR_FORMAT_ARG_1)
 DEF_C99_BUILTIN        (BUILT_IN_IMAXABS, "imaxabs", BT_FN_INTMAX_INTMAX, ATTR_CONST_NOTHROW_LEAF_LIST)
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index cae2faf..12d7924 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -355,6 +355,7 @@ static tree handle_tls_model_attribute (tree *, tree, tree, int,
 static tree handle_no_instrument_function_attribute (tree *, tree,
 						     tree, int, bool *);
 static tree handle_malloc_attribute (tree *, tree, tree, int, bool *);
+static tree handle_free_attribute (tree *, tree, tree, int, bool *);
 static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_limit_stack_attribute (tree *, tree, tree, int,
 					     bool *);
@@ -720,6 +721,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      false },
   { "malloc",                 0, 0, true,  false, false,
 			      handle_malloc_attribute, false },
+  { "free",                   0, 0, true,  false, false,
+			      handle_free_attribute, false },
   { "returns_twice",          0, 0, true,  false, false,
 			      handle_returns_twice_attribute, false },
   { "no_stack_limit",         0, 0, true,  false, false,
@@ -8315,7 +8318,7 @@ handle_malloc_attribute (tree *node, tree name, tree ARG_UNUSED (args),
 {
   if (TREE_CODE (*node) == FUNCTION_DECL
       && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (*node))))
-    DECL_IS_MALLOC (*node) = 1;
+    DECL_ALLOC_FN_KIND (*node) = ALLOC_FN_MALLOC;
   else
     {
       warning (OPT_Wattributes, "%qE attribute ignored", name);
@@ -8325,6 +8328,27 @@ handle_malloc_attribute (tree *node, tree name, tree ARG_UNUSED (args),
   return NULL_TREE;
 }
 
+/* Handle a "free" attribute; arguments as in struct attribute_spec.handler.  */
+
+static tree
+handle_free_attribute (tree *node, tree name, tree /*args*/, int /*flags*/,
+		       bool *no_add_attrs)
+{
+  tree decl = *node;
+  if (TREE_CODE (decl) == FUNCTION_DECL
+      && type_num_arguments (TREE_TYPE (decl)) != 0
+      && POINTER_TYPE_P (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (decl)))))
+    DECL_ALLOC_FN_KIND (decl) = ALLOC_FN_FREE;
+  else
+    {
+      warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
+		  "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
 /* Handle a "alloc_size" attribute; arguments as in
    struct attribute_spec.handler.  */
 
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index f0c677b..b1178b1 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -2476,8 +2476,8 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype)
 	  DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (newdecl)
 	    |= DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl);
 	  TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl);
-	  DECL_IS_MALLOC (newdecl) |= DECL_IS_MALLOC (olddecl);
-	  DECL_IS_OPERATOR_NEW (newdecl) |= DECL_IS_OPERATOR_NEW (olddecl);
+	  if (DECL_ALLOC_FN_KIND (olddecl) != ALLOC_FN_NONE)
+	    DECL_ALLOC_FN_KIND (newdecl) = DECL_ALLOC_FN_KIND (olddecl);
 	  TREE_READONLY (newdecl) |= TREE_READONLY (olddecl);
 	  DECL_PURE_P (newdecl) |= DECL_PURE_P (olddecl);
 	  DECL_IS_NOVOPS (newdecl) |= DECL_IS_NOVOPS (olddecl);
diff --git a/gcc/calls.c b/gcc/calls.c
index 6415e08..bf9b0c7 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -719,9 +719,11 @@ flags_from_decl_or_type (const_tree exp)
 
   if (DECL_P (exp))
     {
-      /* The function exp may have the `malloc' attribute.  */
+      /* The function exp may have the `malloc' and `free' attributes.  */
       if (DECL_IS_MALLOC (exp))
 	flags |= ECF_MALLOC;
+      if (DECL_IS_FREE (exp))
+	flags |= ECF_FREE;
 
       /* The function exp may have the `returns_twice' attribute.  */
       if (DECL_IS_RETURNS_TWICE (exp))
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 461822b..f213a9c 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -2184,8 +2184,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
 	  DECL_NO_LIMIT_STACK (newdecl) |= DECL_NO_LIMIT_STACK (olddecl);
 	  TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl);
 	  TREE_NOTHROW (newdecl) |= TREE_NOTHROW (olddecl);
-	  DECL_IS_MALLOC (newdecl) |= DECL_IS_MALLOC (olddecl);
-	  DECL_IS_OPERATOR_NEW (newdecl) |= DECL_IS_OPERATOR_NEW (olddecl);
+	  if (DECL_ALLOC_FN_KIND (olddecl) != ALLOC_FN_NONE)
+	    DECL_ALLOC_FN_KIND (newdecl) = DECL_ALLOC_FN_KIND (olddecl);
 	  DECL_PURE_P (newdecl) |= DECL_PURE_P (olddecl);
 	  TREE_READONLY (newdecl) |= TREE_READONLY (olddecl);
 	  DECL_LOOPING_CONST_OR_PURE_P (newdecl) 
@@ -4146,13 +4146,14 @@ cxx_init_decl_processing (void)
     deltype = cp_build_type_attribute_variant (void_ftype_ptr, extvisattr);
     deltype = build_exception_variant (deltype, empty_except_spec);
     tree opnew = push_cp_library_fn (NEW_EXPR, newtype, 0);
-    DECL_IS_MALLOC (opnew) = 1;
-    DECL_IS_OPERATOR_NEW (opnew) = 1;
+    DECL_ALLOC_FN_KIND (opnew) = ALLOC_FN_OPERATOR_NEW;
     opnew = push_cp_library_fn (VEC_NEW_EXPR, newtype, 0);
-    DECL_IS_MALLOC (opnew) = 1;
-    DECL_IS_OPERATOR_NEW (opnew) = 1;
-    push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW);
-    push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW);
+    DECL_ALLOC_FN_KIND (opnew) = ALLOC_FN_OPERATOR_NEW;
+
+    tree opdelete = push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW);
+    DECL_ALLOC_FN_KIND (opdelete) = ALLOC_FN_FREE;
+    opdelete = push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW);
+    DECL_ALLOC_FN_KIND (opdelete) = ALLOC_FN_FREE;
     if (flag_sized_deallocation)
       {
 	/* Also push the sized deallocation variants:
@@ -4164,8 +4165,10 @@ cxx_init_decl_processing (void)
 	deltype = cp_build_type_attribute_variant (void_ftype_ptr_size,
 						   extvisattr);
 	deltype = build_exception_variant (deltype, empty_except_spec);
-	push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW);
-	push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW);
+	opdelete = push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW);
+	DECL_ALLOC_FN_KIND (opdelete) = ALLOC_FN_FREE;
+	opdelete = push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW);
+	DECL_ALLOC_FN_KIND (opdelete) = ALLOC_FN_FREE;
       }
 
     nullptr_type_node = make_node (NULLPTR_TYPE);
@@ -12184,10 +12187,13 @@ grok_op_properties (tree decl, bool complain)
   if (operator_code == NEW_EXPR || operator_code == VEC_NEW_EXPR)
     {
       TREE_TYPE (decl) = coerce_new_type (TREE_TYPE (decl));
-      DECL_IS_OPERATOR_NEW (decl) = 1;
+      DECL_ALLOC_FN_KIND (decl) = ALLOC_FN_OPERATOR_NEW;
     }
   else if (operator_code == DELETE_EXPR || operator_code == VEC_DELETE_EXPR)
-    TREE_TYPE (decl) = coerce_delete_type (TREE_TYPE (decl));
+    {
+      TREE_TYPE (decl) = coerce_delete_type (TREE_TYPE (decl));
+      DECL_ALLOC_FN_KIND (decl) = ALLOC_FN_FREE;
+    }
   else
     {
       /* An operator function must either be a non-static member function
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index a5a8b23..fcf0f0a 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2825,6 +2825,17 @@ a pointer to uninitialized or zeroed-out storage.  However, functions
 like @code{realloc} do not have this property, as they can return a
 pointer to storage containing pointers.
 
+@item free
+@cindex @code{free} function attribute
+@cindex functions that behave like free
+This tells the compiler that a function is @code{free}-like, i.e., that it
+does not access the storage addressed by the pointer @var{P} passed to the
+function.  Moreover, accessing the storage after the function returns invokes
+undefined behavior.
+
+Using this attribute can expose more opportunities to dead store elimination
+optimization.
+
 @item no_icf
 @cindex @code{no_icf} function attribute
 This function attribute prevents a functions from being merged with another
diff --git a/gcc/fortran/f95-lang.c b/gcc/fortran/f95-lang.c
index b89a291..f138289 100644
--- a/gcc/fortran/f95-lang.c
+++ b/gcc/fortran/f95-lang.c
@@ -969,7 +969,7 @@ gfc_init_builtin_functions (void)
 				    size_type_node, NULL_TREE);
   gfc_define_builtin ("__builtin_calloc", ftype, BUILT_IN_CALLOC,
 		      "calloc", ATTR_NOTHROW_LEAF_MALLOC_LIST);
-  DECL_IS_MALLOC (builtin_decl_explicit (BUILT_IN_CALLOC)) = 1;
+  DECL_SET_MALLOC (builtin_decl_explicit (BUILT_IN_CALLOC));
 
   ftype = build_function_type_list (pvoid_type_node,
                                     size_type_node, pvoid_type_node,
diff --git a/gcc/gtm-builtins.def b/gcc/gtm-builtins.def
index 6d5cfb9..556391b 100644
--- a/gcc/gtm-builtins.def
+++ b/gcc/gtm-builtins.def
@@ -32,7 +32,7 @@ DEF_TM_BUILTIN (BUILT_IN_TM_MALLOC, "_ITM_malloc",
 DEF_TM_BUILTIN (BUILT_IN_TM_CALLOC, "_ITM_calloc",
 		BT_FN_PTR_SIZE_SIZE, ATTR_TMPURE_MALLOC_NOTHROW_LIST)
 DEF_TM_BUILTIN (BUILT_IN_TM_FREE, "_ITM_free",
-		BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+		BT_FN_VOID_PTR, ATTR_TMPURE_FREE_NOTHROW_LEAF_LIST)
 
 /* Logging builtins.  */
 DEF_TM_BUILTIN (BUILT_IN_TM_LOG_1, "_ITM_LU1",
diff --git a/gcc/java/decl.c b/gcc/java/decl.c
index 93304da..69465d0 100644
--- a/gcc/java/decl.c
+++ b/gcc/java/decl.c
@@ -969,11 +969,11 @@ java_init_decl_processing (void)
   t = build_function_type_list (ptr_type_node, class_ptr_type, NULL_TREE);
   alloc_object_node = add_builtin_function ("_Jv_AllocObject", t,
 					    0, NOT_BUILT_IN, NULL, NULL_TREE);
-  DECL_IS_MALLOC (alloc_object_node) = 1;
+  DECL_SET_MALLOC (alloc_object_node);
   alloc_no_finalizer_node =
     add_builtin_function ("_Jv_AllocObjectNoFinalizer", t,
 			  0, NOT_BUILT_IN, NULL, NULL_TREE);
-  DECL_IS_MALLOC (alloc_no_finalizer_node) = 1;
+  DECL_SET_MALLOC (alloc_no_finalizer_node);
 
   t = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
   soft_initclass_node = add_builtin_function ("_Jv_InitClass", t,
@@ -1005,7 +1005,7 @@ java_init_decl_processing (void)
   soft_newarray_node
       = add_builtin_function ("_Jv_NewPrimArray", t,
 			      0, NOT_BUILT_IN, NULL, NULL_TREE);
-  DECL_IS_MALLOC (soft_newarray_node) = 1;
+  DECL_SET_MALLOC (soft_newarray_node);
 
   t = build_function_type_list (ptr_type_node,
 				int_type_node, class_ptr_type,
@@ -1013,7 +1013,7 @@ java_init_decl_processing (void)
   soft_anewarray_node
       = add_builtin_function ("_Jv_NewObjectArray", t,
 			      0, NOT_BUILT_IN, NULL, NULL_TREE);
-  DECL_IS_MALLOC (soft_anewarray_node) = 1;
+  DECL_SET_MALLOC (soft_anewarray_node);
 
   t = build_varargs_function_type_list (ptr_type_node,
 					ptr_type_node, int_type_node,
@@ -1021,7 +1021,7 @@ java_init_decl_processing (void)
   soft_multianewarray_node
       = add_builtin_function ("_Jv_NewMultiArray", t,
 			      0, NOT_BUILT_IN, NULL, NULL_TREE);
-  DECL_IS_MALLOC (soft_multianewarray_node) = 1;
+  DECL_SET_MALLOC (soft_multianewarray_node);
 
   t = build_function_type_list (void_type_node, int_type_node, NULL_TREE);
   soft_badarrayindex_node
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index 95c446d..c696004 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -277,6 +277,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
       bp_pack_value (&bp, (flags & ECF_PURE) != 0, 1);
       bp_pack_value (&bp, (flags & ECF_NORETURN) != 0, 1);
       bp_pack_value (&bp, (flags & ECF_MALLOC) != 0, 1);
+      bp_pack_value (&bp, (flags & ECF_FREE) != 0, 1);
       bp_pack_value (&bp, (flags & ECF_NOTHROW) != 0, 1);
       bp_pack_value (&bp, (flags & ECF_RETURNS_TWICE) != 0, 1);
       /* Flags that should not appear on indirect calls.  */
@@ -1505,6 +1506,8 @@ input_edge (struct lto_input_block *ib, vec<symtab_node *> nodes,
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_MALLOC;
       if (bp_unpack_value (&bp, 1))
+	ecf_flags |= ECF_FREE;
+      if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_NOTHROW;
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_RETURNS_TWICE;
diff --git a/gcc/lto-streamer-out.c b/gcc/lto-streamer-out.c
index 6703d41..b3bc5ae 100644
--- a/gcc/lto-streamer-out.c
+++ b/gcc/lto-streamer-out.c
@@ -1120,6 +1120,7 @@ hash_tree (struct streamer_tree_cache_d *cache, hash_map<tree, hashval_t> *map,
       hstate.add_flag (DECL_IS_RETURNS_TWICE (t));
       hstate.add_flag (DECL_IS_MALLOC (t));
       hstate.add_flag (DECL_IS_OPERATOR_NEW (t));
+      hstate.add_flag (DECL_IS_FREE (t));
       hstate.add_flag (DECL_DECLARED_INLINE_P (t));
       hstate.add_flag (DECL_STATIC_CHAIN (t));
       hstate.add_flag (DECL_NO_INLINE_WARNING_P (t));
diff --git a/gcc/lto/lto-lang.c b/gcc/lto/lto-lang.c
index b5efe3a..060044b 100644
--- a/gcc/lto/lto-lang.c
+++ b/gcc/lto/lto-lang.c
@@ -288,7 +288,7 @@ handle_malloc_attribute (tree *node, tree ARG_UNUSED (name),
 {
   if (TREE_CODE (*node) == FUNCTION_DECL
       && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (*node))))
-    DECL_IS_MALLOC (*node) = 1;
+    DECL_ALLOC_FN_KIND (*node) = ALLOC_FN_MALLOC;
   else
     gcc_unreachable ();
 
diff --git a/gcc/testsuite/g++.dg/opt/op-delete-dse.C b/gcc/testsuite/g++.dg/opt/op-delete-dse.C
new file mode 100644
index 0000000..4df869f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/opt/op-delete-dse.C
@@ -0,0 +1,14 @@
+// { dg-do compile }
+// { dg-options "-O -fdump-tree-dse1-details" }
+// { dg-final { scan-tree-dump-times "Deleted dead call" 1 "dse1" } }
+
+void use (void *);
+
+void
+test_delete_pod ()
+{
+  int *data = new int;
+  use (data);
+  __builtin_memset (data, 0, sizeof (int));
+  delete data;
+}
diff --git a/gcc/testsuite/gcc.dg/attr-free.c b/gcc/testsuite/gcc.dg/attr-free.c
new file mode 100644
index 0000000..6aa9153
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-free.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O1" } */
+
+void *malloc (__SIZE_TYPE__);
+void custom_free (void *) __attribute__((free));
+
+void
+test (void)
+{
+  char *data = (char *) malloc (1);
+  data[0] = 42;
+  custom_free (data);
+}
+
+/* { dg-final { scan-assembler-not "42" } } */
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 0d48ff5..fa96d93 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -87,6 +87,9 @@ struct die_struct;
 /* Nonzero if this call is into the transaction runtime library.  */
 #define ECF_TM_BUILTIN		  (1 << 12)
 
+/* Nonzero if this is a call to free or a related function.  */
+#define ECF_FREE		  (1 << 13)
+
 /* Call argument flags.  */
 /* Nonzero if the argument is not dereferenced recursively, thus only
    directly reachable memory is read or written.  */
@@ -1647,6 +1650,18 @@ struct GTY(()) tree_decl_non_common {
   tree result;
 };
 
+/* Functions related to memory allocation.  */
+
+enum alloc_fn_kind {
+  ALLOC_FN_NONE,
+  /* malloc and alloca.  */
+  ALLOC_FN_MALLOC,
+  /* C++ operator new.  */
+  ALLOC_FN_OPERATOR_NEW,
+  /* Any deallocation function (free, operator delete). */
+  ALLOC_FN_FREE
+};
+
 /* FUNCTION_DECL inherits from DECL_NON_COMMON because of the use of the
    arguments/result/saved_tree fields by front ends.   It was either inherit
    FUNCTION_DECL from non_common, or inherit non_common from FUNCTION_DECL,
@@ -1685,8 +1700,7 @@ struct GTY(()) tree_function_decl {
   unsigned possibly_inlined : 1;
   unsigned novops_flag : 1;
   unsigned returns_twice_flag : 1;
-  unsigned malloc_flag : 1;
-  unsigned operator_new_flag : 1;
+  unsigned alloc_fn : 2;
   unsigned declared_inline_flag : 1;
   unsigned no_inline_warning_flag : 1;
 
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index 08f10e5..26a3845 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pretty-print.h"
 #include "alias.h"
 #include "fold-const.h"
+#include "calls.h"
 
 #include "langhooks.h"
 #include "dumpfile.h"
@@ -1728,6 +1729,15 @@ ref_maybe_used_by_call_p_1 (gcall *call, ao_ref *ref)
 	  /* Fallthru to general call handling.  */;
       }
 
+  /* free-like functions may not reference their first argument.  */
+  if (callee != NULL_TREE && (flags & ECF_FREE) != 0)
+    {
+      tree ptr = gimple_call_arg (call, 0);
+      tree base = ao_ref_base (ref);
+      if (base && TREE_CODE (base) == MEM_REF && TREE_OPERAND (base, 0) == ptr)
+	return false;
+    }
+
   /* Check if base is a global static variable that is not read
      by the function.  */
   if (callee != NULL_TREE
@@ -2117,6 +2127,13 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref)
 	  /* Fallthru to general call handling.  */;
       }
 
+  if (callee != NULL_TREE
+      && (flags_from_decl_or_type (callee) & ECF_FREE) != 0)
+    {
+      tree ptr = gimple_call_arg (call, 0);
+      return ptr_deref_may_alias_ref_p_1 (ptr, ref);
+    }
+
   /* Check if base is a global static variable that is not written
      by the function.  */
   if (callee != NULL_TREE
@@ -2332,16 +2349,6 @@ stmt_kills_ref_p (gimple *stmt, ao_ref *ref)
 	  && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
 	switch (DECL_FUNCTION_CODE (callee))
 	  {
-	  case BUILT_IN_FREE:
-	    {
-	      tree ptr = gimple_call_arg (stmt, 0);
-	      tree base = ao_ref_base (ref);
-	      if (base && TREE_CODE (base) == MEM_REF
-		  && TREE_OPERAND (base, 0) == ptr)
-		return true;
-	      break;
-	    }
-
 	  case BUILT_IN_MEMCPY:
 	  case BUILT_IN_MEMPCPY:
 	  case BUILT_IN_MEMMOVE:
@@ -2402,6 +2409,16 @@ stmt_kills_ref_p (gimple *stmt, ao_ref *ref)
 
 	  default:;
 	  }
+
+      if (callee != NULL_TREE
+	  && (flags_from_decl_or_type (callee) & ECF_FREE) != 0)
+	{
+	  tree ptr = gimple_call_arg (stmt, 0);
+	  tree base = ao_ref_base (ref);
+	  if (base && TREE_CODE (base) == MEM_REF
+	      && TREE_OPERAND (base, 0) == ptr)
+	    return true;
+	}
     }
   return false;
 }
diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c
index 2ad2f92..4f01eac 100644
--- a/gcc/tree-streamer-in.c
+++ b/gcc/tree-streamer-in.c
@@ -322,8 +322,7 @@ unpack_ts_function_decl_value_fields (struct bitpack_d *bp, tree expr)
   DECL_POSSIBLY_INLINED (expr) = (unsigned) bp_unpack_value (bp, 1);
   DECL_IS_NOVOPS (expr) = (unsigned) bp_unpack_value (bp, 1);
   DECL_IS_RETURNS_TWICE (expr) = (unsigned) bp_unpack_value (bp, 1);
-  DECL_IS_MALLOC (expr) = (unsigned) bp_unpack_value (bp, 1);
-  DECL_IS_OPERATOR_NEW (expr) = (unsigned) bp_unpack_value (bp, 1);
+  DECL_ALLOC_FN_KIND (expr) = (alloc_fn_kind) bp_unpack_value (bp, 2);
   DECL_DECLARED_INLINE_P (expr) = (unsigned) bp_unpack_value (bp, 1);
   DECL_STATIC_CHAIN (expr) = (unsigned) bp_unpack_value (bp, 1);
   DECL_NO_INLINE_WARNING_P (expr) = (unsigned) bp_unpack_value (bp, 1);
diff --git a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c
index c37755d..41acc1d 100644
--- a/gcc/tree-streamer-out.c
+++ b/gcc/tree-streamer-out.c
@@ -290,8 +290,7 @@ pack_ts_function_decl_value_fields (struct bitpack_d *bp, tree expr)
   bp_pack_value (bp, DECL_POSSIBLY_INLINED (expr), 1);
   bp_pack_value (bp, DECL_IS_NOVOPS (expr), 1);
   bp_pack_value (bp, DECL_IS_RETURNS_TWICE (expr), 1);
-  bp_pack_value (bp, DECL_IS_MALLOC (expr), 1);
-  bp_pack_value (bp, DECL_IS_OPERATOR_NEW (expr), 1);
+  bp_pack_value (bp, DECL_ALLOC_FN_KIND (expr), 2);
   bp_pack_value (bp, DECL_DECLARED_INLINE_P (expr), 1);
   bp_pack_value (bp, DECL_STATIC_CHAIN (expr), 1);
   bp_pack_value (bp, DECL_NO_INLINE_WARNING_P (expr), 1);
diff --git a/gcc/tree.c b/gcc/tree.c
index 6de46a8..527d0ac 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -10335,7 +10335,9 @@ set_call_expr_flags (tree decl, int flags)
   if (flags & ECF_NORETURN)
     TREE_THIS_VOLATILE (decl) = 1;
   if (flags & ECF_MALLOC)
-    DECL_IS_MALLOC (decl) = 1;
+    DECL_ALLOC_FN_KIND (decl) = ALLOC_FN_MALLOC;
+  else if (flags & ECF_FREE)
+    DECL_ALLOC_FN_KIND (decl) = ALLOC_FN_FREE;
   if (flags & ECF_RETURNS_TWICE)
     DECL_IS_RETURNS_TWICE (decl) = 1;
   if (flags & ECF_LEAF)
diff --git a/gcc/tree.h b/gcc/tree.h
index 33833a7..ebfbb53 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2789,17 +2789,34 @@ extern void decl_fini_priority_insert (tree, priority_type);
 #define DECL_SAVED_TREE(NODE) \
   (FUNCTION_DECL_CHECK (NODE)->function_decl.saved_tree)
 
+/* In a FUNCTION_DECL, non-zero means that this function is related to
+   memory allocation, one of: ALLOC_FN_MALLOC, ALLOC_FN_OPERATOR_NEW and
+   ALLOC_FN_FREE.  The semantics of each kind is described below.  */
+#define DECL_ALLOC_FN_KIND(NODE) \
+  (FUNCTION_DECL_CHECK (NODE)->function_decl.alloc_fn)
+
 /* Nonzero in a FUNCTION_DECL means this function should be treated
    as if it were a malloc, meaning it returns a pointer that is
    not an alias.  */
 #define DECL_IS_MALLOC(NODE) \
-  (FUNCTION_DECL_CHECK (NODE)->function_decl.malloc_flag)
+  (FUNCTION_DECL_CHECK (NODE)->function_decl.alloc_fn == ALLOC_FN_MALLOC \
+   || FUNCTION_DECL_CHECK (NODE)->function_decl.alloc_fn == ALLOC_FN_OPERATOR_NEW)
+
+/* Mark the function as malloc-like.  */
+#define DECL_SET_MALLOC(NODE) \
+  (FUNCTION_DECL_CHECK (NODE)->function_decl.alloc_fn = ALLOC_FN_MALLOC)
 
 /* Nonzero in a FUNCTION_DECL means this function should be treated as
    C++ operator new, meaning that it returns a pointer for which we
    should not use type based aliasing.  */
 #define DECL_IS_OPERATOR_NEW(NODE) \
-  (FUNCTION_DECL_CHECK (NODE)->function_decl.operator_new_flag)
+  (FUNCTION_DECL_CHECK (NODE)->function_decl.alloc_fn == ALLOC_FN_OPERATOR_NEW)
+
+/* Nonzero in a FUNCTION_DECL means this function should be treated as
+   if it were free or C++ operator delete (first parameter is the object being
+   freed) for the purpose of DSE.  */
+#define DECL_IS_FREE(NODE) \
+  (FUNCTION_DECL_CHECK (NODE)->function_decl.alloc_fn == ALLOC_FN_FREE)
 
 /* Nonzero in a FUNCTION_DECL means this function may return more
    than once.  */

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]