This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [PATCH] Fix missed DSE opportunity with operator delete.
- From: Mikhail Maltsev <maltsevm at gmail dot com>
- To: Richard Biener <richard dot guenther at gmail dot com>
- Cc: gcc-patches mailing list <gcc-patches at gcc dot gnu dot org>, Marc Glisse <marc dot glisse at inria dot fr>
- Date: Tue, 19 Apr 2016 23:48:11 +0300
- Subject: Re: [PATCH] Fix missed DSE opportunity with operator delete.
- Authentication-results: sourceware.org; auth=none
- References: <5712AF84 dot 5080002 at gmail dot com> <CAFiYyc2wz_a-92BbvMGa5mtUtWTgk2wafxWmmzum1r_t-_-vnw at mail dot gmail dot com>
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. */