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. */