+2020-04-08 Martin Liska <mliska@suse.cz>
+
+ 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 <dennis.zhang@arm.com>
Matthew Malcomson <matthew.malcomson@arm.com>
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");
+2020-04-08 Martin Liska <mliska@suse.cz>
+
+ 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 <ppalka@redhat.com>
Core issues 1001 and 1322
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);
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:
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)
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,
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)
{
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;
}
}
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;
}
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);
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
+2020-04-08 Martin Liska <mliska@suse.cz>
+
+ PR c++/94314
+ * lto-common.c (compare_tree_sccs_1): Compare also
+ DECL_IS_REPLACEABLE_OPERATOR.
+
2020-03-25 Jakub Jelinek <jakub@redhat.com>
PR c++/94223
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);
+2020-04-08 Martin Liska <mliska@suse.cz>
+
+ 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 <dennis.zhang@arm.com>
* gcc.target/arm/acle/cde_v_1.c: New test.
--- /dev/null
+/* PR c++/94314. */
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-cddce-details" } */
+/* { dg-additional-options "-fdelete-null-pointer-checks" } */
+
+#include <stdio.h>
+
+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"} } */
--- /dev/null
+/* 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 <stdio.h>
+
+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"} } */
--- /dev/null
+/* PR c++/94314. */
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-cddce-details" } */
+/* { dg-additional-options "-fdelete-null-pointer-checks" } */
+
+#include <stdio.h>
+
+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"} } */
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 {
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;
}
processing the argument. */
bool is_delete_operator
= (is_gimple_call (stmt)
- && gimple_call_operator_delete_p (as_a <gcall *> (stmt)));
+ && gimple_call_replaceable_operator_delete_p (as_a <gcall *> (stmt)));
if (is_delete_operator
|| gimple_call_builtin_p (stmt, BUILT_IN_FREE))
{
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
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 <gcall *> (stmt)))))
+ && gimple_call_replaceable_operator_delete_p (as_a <gcall *> (stmt)))))
{
tree ptr = gimple_call_arg (stmt, 0);
if (TREE_CODE (ptr) == SSA_NAME)
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)
{
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);
}
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. */
(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)
#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)