From: Martin Liska Date: Wed, 8 Apr 2020 15:16:55 +0000 (+0200) Subject: Allow new/delete operator deletion only for replaceable. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=70df40cab6f268ba7f05c6d1421928cca0834ee3;p=gcc.git Allow new/delete operator deletion only for replaceable. PR c++/94314 * gimple.c (gimple_call_operator_delete_p): Rename to... (gimple_call_replaceable_operator_delete_p): ... this. Use DECL_IS_REPLACEABLE_OPERATOR_DELETE_P. * gimple.h (gimple_call_operator_delete_p): Rename to ... (gimple_call_replaceable_operator_delete_p): ... this. * tree-core.h (tree_function_decl): Add replaceable_operator flag. * tree-ssa-dce.c (mark_all_reaching_defs_necessary_1): Use DECL_IS_REPLACEABLE_OPERATOR_DELETE_P. (propagate_necessity): Use gimple_call_replaceable_operator_delete_p. (eliminate_unnecessary_stmts): Likewise. * tree-streamer-in.c (unpack_ts_function_decl_value_fields): Pack DECL_IS_REPLACEABLE_OPERATOR. * tree-streamer-out.c (pack_ts_function_decl_value_fields): Unpack the field here. * tree.h (DECL_IS_REPLACEABLE_OPERATOR): New. (DECL_IS_REPLACEABLE_OPERATOR_NEW_P): New. (DECL_IS_REPLACEABLE_OPERATOR_DELETE_P): New. * cgraph.c (cgraph_node::dump): Dump if an operator is replaceable. * ipa-icf.c (sem_item::compare_referenced_symbol_properties): Compare replaceable operator flags. PR c++/94314 * decl.c (duplicate_decls): Duplicate also DECL_IS_REPLACEABLE_OPERATOR. (cxx_init_decl_processing): Mark replaceable all implicitly defined operators. PR c++/94314 * lto-common.c (compare_tree_sccs_1): Compare also DECL_IS_REPLACEABLE_OPERATOR. PR c++/94314 * g++.dg/pr94314-2.C: New test. * g++.dg/pr94314-3.C: New test. * g++.dg/pr94314.C: New test. --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index ffd4679e4ed..88e79520807 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,28 @@ +2020-04-08 Martin Liska + + PR c++/94314 + * gimple.c (gimple_call_operator_delete_p): Rename to... + (gimple_call_replaceable_operator_delete_p): ... this. + Use DECL_IS_REPLACEABLE_OPERATOR_DELETE_P. + * gimple.h (gimple_call_operator_delete_p): Rename to ... + (gimple_call_replaceable_operator_delete_p): ... this. + * tree-core.h (tree_function_decl): Add replaceable_operator + flag. + * tree-ssa-dce.c (mark_all_reaching_defs_necessary_1): + Use DECL_IS_REPLACEABLE_OPERATOR_DELETE_P. + (propagate_necessity): Use gimple_call_replaceable_operator_delete_p. + (eliminate_unnecessary_stmts): Likewise. + * tree-streamer-in.c (unpack_ts_function_decl_value_fields): + Pack DECL_IS_REPLACEABLE_OPERATOR. + * tree-streamer-out.c (pack_ts_function_decl_value_fields): + Unpack the field here. + * tree.h (DECL_IS_REPLACEABLE_OPERATOR): New. + (DECL_IS_REPLACEABLE_OPERATOR_NEW_P): New. + (DECL_IS_REPLACEABLE_OPERATOR_DELETE_P): New. + * cgraph.c (cgraph_node::dump): Dump if an operator is replaceable. + * ipa-icf.c (sem_item::compare_referenced_symbol_properties): Compare + replaceable operator flags. + 2020-04-08 Dennis Zhang Matthew Malcomson diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 6b780f80eb3..ecb234d032f 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -2157,10 +2157,11 @@ cgraph_node::dump (FILE *f) if (parallelized_function) fprintf (f, " parallelized_function"); if (DECL_IS_OPERATOR_NEW_P (decl)) - fprintf (f, " operator_new"); + fprintf (f, " %soperator_new", + DECL_IS_REPLACEABLE_OPERATOR (decl) ? "replaceable_" : ""); if (DECL_IS_OPERATOR_DELETE_P (decl)) - fprintf (f, " operator_delete"); - + fprintf (f, " %soperator_delete", + DECL_IS_REPLACEABLE_OPERATOR (decl) ? "replaceable_" : ""); fprintf (f, "\n"); diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index cedd157dbf9..7c5edc266d6 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,10 @@ +2020-04-08 Martin Liska + + PR c++/94314 + * decl.c (duplicate_decls): Duplicate also DECL_IS_REPLACEABLE_OPERATOR. + (cxx_init_decl_processing): Mark replaceable all implicitly defined + operators. + 2020-04-08 Patrick Palka Core issues 1001 and 1322 diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 0ea4b320d86..a6a1340e631 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -2368,6 +2368,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) DECL_SET_IS_OPERATOR_NEW (newdecl, true); DECL_LOOPING_CONST_OR_PURE_P (newdecl) |= DECL_LOOPING_CONST_OR_PURE_P (olddecl); + DECL_IS_REPLACEABLE_OPERATOR (newdecl) + |= DECL_IS_REPLACEABLE_OPERATOR (olddecl); if (merge_attr) merge_attribute_bits (newdecl, olddecl); @@ -4438,13 +4440,17 @@ cxx_init_decl_processing (void) tree opnew = push_cp_library_fn (NEW_EXPR, newtype, 0); DECL_IS_MALLOC (opnew) = 1; DECL_SET_IS_OPERATOR_NEW (opnew, true); + DECL_IS_REPLACEABLE_OPERATOR (opnew) = 1; opnew = push_cp_library_fn (VEC_NEW_EXPR, newtype, 0); DECL_IS_MALLOC (opnew) = 1; DECL_SET_IS_OPERATOR_NEW (opnew, true); + DECL_IS_REPLACEABLE_OPERATOR (opnew) = 1; tree opdel = push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW); DECL_SET_IS_OPERATOR_DELETE (opdel, true); + DECL_IS_REPLACEABLE_OPERATOR (opdel) = 1; opdel = push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW); DECL_SET_IS_OPERATOR_DELETE (opdel, true); + DECL_IS_REPLACEABLE_OPERATOR (opdel) = 1; if (flag_sized_deallocation) { /* Also push the sized deallocation variants: @@ -4458,8 +4464,10 @@ cxx_init_decl_processing (void) deltype = build_exception_variant (deltype, empty_except_spec); opdel = push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW); DECL_SET_IS_OPERATOR_DELETE (opdel, true); + DECL_IS_REPLACEABLE_OPERATOR (opdel) = 1; opdel = push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW); DECL_SET_IS_OPERATOR_DELETE (opdel, true); + DECL_IS_REPLACEABLE_OPERATOR (opdel) = 1; } if (aligned_new_threshold) @@ -4478,9 +4486,11 @@ cxx_init_decl_processing (void) opnew = push_cp_library_fn (NEW_EXPR, newtype, 0); DECL_IS_MALLOC (opnew) = 1; DECL_SET_IS_OPERATOR_NEW (opnew, true); + DECL_IS_REPLACEABLE_OPERATOR (opnew) = 1; opnew = push_cp_library_fn (VEC_NEW_EXPR, newtype, 0); DECL_IS_MALLOC (opnew) = 1; DECL_SET_IS_OPERATOR_NEW (opnew, true); + DECL_IS_REPLACEABLE_OPERATOR (opnew) = 1; /* operator delete (void *, align_val_t); */ deltype = build_function_type_list (void_type_node, ptr_type_node, @@ -4489,8 +4499,10 @@ cxx_init_decl_processing (void) deltype = build_exception_variant (deltype, empty_except_spec); opdel = push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW); DECL_SET_IS_OPERATOR_DELETE (opdel, true); + DECL_IS_REPLACEABLE_OPERATOR (opdel) = 1; opdel = push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW); DECL_SET_IS_OPERATOR_DELETE (opdel, true); + DECL_IS_REPLACEABLE_OPERATOR (opdel) = 1; if (flag_sized_deallocation) { @@ -4502,8 +4514,10 @@ cxx_init_decl_processing (void) deltype = build_exception_variant (deltype, empty_except_spec); opdel = push_cp_library_fn (DELETE_EXPR, deltype, ECF_NOTHROW); DECL_SET_IS_OPERATOR_DELETE (opdel, true); + DECL_IS_REPLACEABLE_OPERATOR (opdel) = 1; opdel = push_cp_library_fn (VEC_DELETE_EXPR, deltype, ECF_NOTHROW); DECL_SET_IS_OPERATOR_DELETE (opdel, true); + DECL_IS_REPLACEABLE_OPERATOR (opdel) = 1; } } diff --git a/gcc/gimple.c b/gcc/gimple.c index 61a400b356f..10c562f4def 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -2730,15 +2730,15 @@ gimple_builtin_call_types_compatible_p (const gimple *stmt, tree fndecl) return true; } -/* Return true when STMT is operator delete call. */ +/* Return true when STMT is operator a replaceable delete call. */ bool -gimple_call_operator_delete_p (const gcall *stmt) +gimple_call_replaceable_operator_delete_p (const gcall *stmt) { tree fndecl; if ((fndecl = gimple_call_fndecl (stmt)) != NULL_TREE) - return DECL_IS_OPERATOR_DELETE_P (fndecl); + return DECL_IS_REPLACEABLE_OPERATOR_DELETE_P (fndecl); return false; } diff --git a/gcc/gimple.h b/gcc/gimple.h index 305d98f5438..ca7fec6247e 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -1615,7 +1615,7 @@ extern alias_set_type gimple_get_alias_set (tree); extern bool gimple_ior_addresses_taken (bitmap, gimple *); extern bool gimple_builtin_call_types_compatible_p (const gimple *, tree); extern combined_fn gimple_call_combined_fn (const gimple *); -extern bool gimple_call_operator_delete_p (const gcall *); +extern bool gimple_call_replaceable_operator_delete_p (const gcall *); extern bool gimple_call_builtin_p (const gimple *); extern bool gimple_call_builtin_p (const gimple *, enum built_in_class); extern bool gimple_call_builtin_p (const gimple *, enum built_in_function); diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c index 17a0ed9760b..069de9d82fb 100644 --- a/gcc/ipa-icf.c +++ b/gcc/ipa-icf.c @@ -347,6 +347,10 @@ sem_item::compare_referenced_symbol_properties (symtab_node *used_by, if (DECL_IS_OPERATOR_NEW_P (n1->decl) != DECL_IS_OPERATOR_NEW_P (n2->decl)) return return_false_with_msg ("operator new flags are different"); + + if (DECL_IS_REPLACEABLE_OPERATOR (n1->decl) + != DECL_IS_REPLACEABLE_OPERATOR (n2->decl)) + return return_false_with_msg ("replaceable operator flags are different"); } /* Merging two definitions with a reference to equivalent vtables, but diff --git a/gcc/lto/ChangeLog b/gcc/lto/ChangeLog index 4171a3dc4ae..829b26d4005 100644 --- a/gcc/lto/ChangeLog +++ b/gcc/lto/ChangeLog @@ -1,3 +1,9 @@ +2020-04-08 Martin Liska + + PR c++/94314 + * lto-common.c (compare_tree_sccs_1): Compare also + DECL_IS_REPLACEABLE_OPERATOR. + 2020-03-25 Jakub Jelinek PR c++/94223 diff --git a/gcc/lto/lto-common.c b/gcc/lto/lto-common.c index c95a9b00d9e..e073abce2e7 100644 --- a/gcc/lto/lto-common.c +++ b/gcc/lto/lto-common.c @@ -1236,6 +1236,7 @@ compare_tree_sccs_1 (tree t1, tree t2, tree **map) compare_values (DECL_DISREGARD_INLINE_LIMITS); compare_values (DECL_PURE_P); compare_values (DECL_LOOPING_CONST_OR_PURE_P); + compare_values (DECL_IS_REPLACEABLE_OPERATOR); compare_values (DECL_FINAL_P); compare_values (DECL_CXX_CONSTRUCTOR_P); compare_values (DECL_CXX_DESTRUCTOR_P); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 9b2d1f62721..a420deaad05 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2020-04-08 Martin Liska + + PR c++/94314 + * g++.dg/pr94314-2.C: New test. + * g++.dg/pr94314-3.C: New test. + * g++.dg/pr94314.C: New test. + 2020-04-08 Dennis Zhang * gcc.target/arm/acle/cde_v_1.c: New test. diff --git a/gcc/testsuite/g++.dg/pr94314-2.C b/gcc/testsuite/g++.dg/pr94314-2.C new file mode 100644 index 00000000000..36b93ed6d4d --- /dev/null +++ b/gcc/testsuite/g++.dg/pr94314-2.C @@ -0,0 +1,26 @@ +/* PR c++/94314. */ +/* { dg-do run } */ +/* { dg-options "-O2 -fdump-tree-cddce-details" } */ +/* { dg-additional-options "-fdelete-null-pointer-checks" } */ + +#include + +struct A +{ + __attribute__((always_inline)) A(int x) + { + if (x == 123) + throw x; + } +}; + +int +main(int argc, char **argv) +{ + A *a = new A (argc); + delete a; + + return 0; +} + +/* { dg-final { scan-tree-dump-times "Deleting : operator delete" 2 "cddce1"} } */ diff --git a/gcc/testsuite/g++.dg/pr94314-3.C b/gcc/testsuite/g++.dg/pr94314-3.C new file mode 100644 index 00000000000..a5b10139290 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr94314-3.C @@ -0,0 +1,55 @@ +/* PR c++/94314. */ +/* { dg-do run } */ +/* { dg-options "-O2 --param early-inlining-insns=100 -fdump-tree-cddce-details" } */ +/* { dg-additional-options "-fdelete-null-pointer-checks" } */ + +#include + +volatile int idx; + +struct base +{ + __attribute__ ((malloc, noinline)) static void * + operator new (unsigned long sz) + { + return ::operator new (sz); + } + + __attribute__ ((noinline)) static void operator delete (void *ptr) + { + int c = count[idx]; + count[idx] = c - 1; + ::operator delete (ptr); + } + volatile static int count[2]; +}; + +volatile int base::count[2] = {0, 0}; + +struct B : base +{ + static void *operator new (unsigned long sz) + { + int c = count[idx]; + count[idx] = c + 1; + return base::operator new (sz); + } +}; + +volatile int c = 1; + +int +main () +{ + for (int i; i < c; i++) + { + idx = 0; + delete new B; + if (B::count[0] != 0) + __builtin_abort (); + } + + return 0; +} + +/* { dg-final { scan-tree-dump-not "Deleting : operator delete" "cddce1"} } */ diff --git a/gcc/testsuite/g++.dg/pr94314.C b/gcc/testsuite/g++.dg/pr94314.C new file mode 100644 index 00000000000..a06800d2219 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr94314.C @@ -0,0 +1,85 @@ +/* PR c++/94314. */ +/* { dg-do run } */ +/* { dg-options "-O2 -fdump-tree-cddce-details" } */ +/* { dg-additional-options "-fdelete-null-pointer-checks" } */ + +#include + +struct A +{ + __attribute__((malloc,noinline)) + static void* operator new(unsigned long sz) + { + ++count; + return ::operator new(sz); + } + + static void operator delete(void* ptr) + { + --count; + ::operator delete(ptr); + } + + static int count; +}; + +int A::count = 0; + +struct B +{ + __attribute__((malloc,noinline)) + static void* operator new(unsigned long sz) + { + ++count; + return ::operator new(sz); + } + + __attribute__((noinline)) + static void operator delete(void* ptr) + { + --count; + ::operator delete(ptr); + } + + static int count; +}; + +int B::count = 0; + +struct C +{ + static void* operator new(unsigned long sz) + { + ++count; + return ::operator new(sz); + } + + static void operator delete(void* ptr) + { + --count; + ::operator delete(ptr); + } + + static int count; +}; + +int C::count = 0; + +int main(){ + delete new A; + if (A::count != 0) + __builtin_abort (); + + delete new B; + if (B::count != 0) + __builtin_abort (); + + delete new C; + if (C::count != 0) + __builtin_abort (); + + return 0; +} + +/* { dg-final { scan-tree-dump-times "Deleting : operator delete" 1 "cddce1"} } */ +/* { dg-final { scan-tree-dump-not "Deleting : B::operator delete" "cddce1"} } */ diff --git a/gcc/tree-core.h b/gcc/tree-core.h index 765ea2a9542..d84fe959acc 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -1896,8 +1896,9 @@ struct GTY(()) tree_function_decl { ENUM_BITFIELD(function_decl_type) decl_type: 2; unsigned has_debug_args_flag : 1; unsigned versioned_function : 1; + unsigned replaceable_operator : 1; - /* 12 bits left for future expansion. */ + /* 11 bits left for future expansion. */ }; struct GTY(()) tree_translation_unit_decl { diff --git a/gcc/tree-ssa-dce.c b/gcc/tree-ssa-dce.c index e4077b58890..fd5f24c746c 100644 --- a/gcc/tree-ssa-dce.c +++ b/gcc/tree-ssa-dce.c @@ -614,7 +614,7 @@ mark_all_reaching_defs_necessary_1 (ao_ref *ref ATTRIBUTE_UNUSED, if (callee != NULL_TREE && (DECL_IS_REPLACEABLE_OPERATOR_NEW_P (callee) - || DECL_IS_OPERATOR_DELETE_P (callee))) + || DECL_IS_REPLACEABLE_OPERATOR_DELETE_P (callee))) return false; } @@ -806,7 +806,7 @@ propagate_necessity (bool aggressive) processing the argument. */ bool is_delete_operator = (is_gimple_call (stmt) - && gimple_call_operator_delete_p (as_a (stmt))); + && gimple_call_replaceable_operator_delete_p (as_a (stmt))); if (is_delete_operator || gimple_call_builtin_p (stmt, BUILT_IN_FREE)) { @@ -896,7 +896,7 @@ propagate_necessity (bool aggressive) if (callee != NULL_TREE && (DECL_IS_REPLACEABLE_OPERATOR_NEW_P (callee) - || DECL_IS_OPERATOR_DELETE_P (callee))) + || DECL_IS_REPLACEABLE_OPERATOR_DELETE_P (callee))) continue; /* Calls implicitly load from memory, their arguments @@ -1321,7 +1321,7 @@ eliminate_unnecessary_stmts (void) if (gimple_plf (stmt, STMT_NECESSARY) && (gimple_call_builtin_p (stmt, BUILT_IN_FREE) || (is_gimple_call (stmt) - && gimple_call_operator_delete_p (as_a (stmt))))) + && gimple_call_replaceable_operator_delete_p (as_a (stmt))))) { tree ptr = gimple_call_arg (stmt, 0); if (TREE_CODE (ptr) == SSA_NAME) diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c index 4e035e9638f..0bfc272d076 100644 --- a/gcc/tree-streamer-in.c +++ b/gcc/tree-streamer-in.c @@ -343,6 +343,7 @@ unpack_ts_function_decl_value_fields (struct bitpack_d *bp, tree expr) DECL_DISREGARD_INLINE_LIMITS (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_PURE_P (expr) = (unsigned) bp_unpack_value (bp, 1); DECL_LOOPING_CONST_OR_PURE_P (expr) = (unsigned) bp_unpack_value (bp, 1); + DECL_IS_REPLACEABLE_OPERATOR (expr) = (unsigned) bp_unpack_value (bp, 1); unsigned int fcode = 0; if (cl != NOT_BUILT_IN) { diff --git a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c index 8e5e1355196..5bbcebba87e 100644 --- a/gcc/tree-streamer-out.c +++ b/gcc/tree-streamer-out.c @@ -305,6 +305,7 @@ pack_ts_function_decl_value_fields (struct bitpack_d *bp, tree expr) bp_pack_value (bp, DECL_DISREGARD_INLINE_LIMITS (expr), 1); bp_pack_value (bp, DECL_PURE_P (expr), 1); bp_pack_value (bp, DECL_LOOPING_CONST_OR_PURE_P (expr), 1); + bp_pack_value (bp, DECL_IS_REPLACEABLE_OPERATOR (expr), 1); if (DECL_BUILT_IN_CLASS (expr) != NOT_BUILT_IN) bp_pack_value (bp, DECL_UNCHECKED_FUNCTION_CODE (expr), 32); } diff --git a/gcc/tree.h b/gcc/tree.h index 66dfa876256..1c28785d411 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -3037,6 +3037,11 @@ set_function_decl_type (tree decl, function_decl_type t, bool set) FUNCTION_DECL_DECL_TYPE (decl) = NONE; } +/* Nonzero in a FUNCTION_DECL means this function is a replaceable + function (like replaceable operators new or delete). */ +#define DECL_IS_REPLACEABLE_OPERATOR(NODE)\ + (FUNCTION_DECL_CHECK (NODE)->function_decl.replaceable_operator) + /* 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. */ @@ -3044,7 +3049,7 @@ set_function_decl_type (tree decl, function_decl_type t, bool set) (FUNCTION_DECL_CHECK (NODE)->function_decl.decl_type == OPERATOR_NEW) #define DECL_IS_REPLACEABLE_OPERATOR_NEW_P(NODE) \ - (DECL_IS_OPERATOR_NEW_P (NODE) && DECL_IS_MALLOC (NODE)) + (DECL_IS_OPERATOR_NEW_P (NODE) && DECL_IS_REPLACEABLE_OPERATOR (NODE)) #define DECL_SET_IS_OPERATOR_NEW(NODE, VAL) \ set_function_decl_type (FUNCTION_DECL_CHECK (NODE), OPERATOR_NEW, VAL) @@ -3054,6 +3059,9 @@ set_function_decl_type (tree decl, function_decl_type t, bool set) #define DECL_IS_OPERATOR_DELETE_P(NODE) \ (FUNCTION_DECL_CHECK (NODE)->function_decl.decl_type == OPERATOR_DELETE) +#define DECL_IS_REPLACEABLE_OPERATOR_DELETE_P(NODE) \ + (DECL_IS_OPERATOR_DELETE_P (NODE) && DECL_IS_REPLACEABLE_OPERATOR (NODE)) + #define DECL_SET_IS_OPERATOR_DELETE(NODE, VAL) \ set_function_decl_type (FUNCTION_DECL_CHECK (NODE), OPERATOR_DELETE, VAL)