From: Richard Biener Date: Thu, 1 Oct 2020 08:44:27 +0000 (+0200) Subject: make use of CALL_FROM_NEW_OR_DELETE_P X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=0b945f959f03a6185a3130f30bfd524d01d4142c;p=gcc.git make use of CALL_FROM_NEW_OR_DELETE_P This fixes points-to analysis and DCE to only consider new/delete operator calls from new or delete expressions and not direct calls. 2020-10-01 Richard Biener * gimple.h (GF_CALL_FROM_NEW_OR_DELETE): New call flag. (gimple_call_set_from_new_or_delete): New. (gimple_call_from_new_or_delete): Likewise. * gimple.c (gimple_build_call_from_tree): Set GF_CALL_FROM_NEW_OR_DELETE appropriately. * ipa-icf-gimple.c (func_checker::compare_gimple_call): Compare gimple_call_from_new_or_delete. * tree-ssa-dce.c (mark_all_reaching_defs_necessary_1): Make sure to only consider new/delete calls from new or delete expressions. (propagate_necessity): Likewise. (eliminate_unnecessary_stmts): Likewise. * tree-ssa-structalias.c (find_func_aliases_for_call): Likewise. * g++.dg/tree-ssa/pta-delete-1.C: New testcase. --- diff --git a/gcc/gimple.c b/gcc/gimple.c index fd4e0fac0d4..f07ddab7953 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -387,6 +387,10 @@ gimple_build_call_from_tree (tree t, tree fnptrtype) && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL) && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (fndecl))) gimple_call_set_alloca_for_var (call, CALL_ALLOCA_FOR_VAR_P (t)); + else if (fndecl + && (DECL_IS_OPERATOR_NEW_P (fndecl) + || DECL_IS_OPERATOR_DELETE_P (fndecl))) + gimple_call_set_from_new_or_delete (call, CALL_FROM_NEW_OR_DELETE_P (t)); else gimple_call_set_from_thunk (call, CALL_FROM_THUNK_P (t)); gimple_call_set_va_arg_pack (call, CALL_EXPR_VA_ARG_PACK (t)); diff --git a/gcc/gimple.h b/gcc/gimple.h index 6cc7e66059d..108ae846849 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -149,6 +149,7 @@ enum gf_mask { GF_CALL_MUST_TAIL_CALL = 1 << 9, GF_CALL_BY_DESCRIPTOR = 1 << 10, GF_CALL_NOCF_CHECK = 1 << 11, + GF_CALL_FROM_NEW_OR_DELETE = 1 << 12, GF_OMP_PARALLEL_COMBINED = 1 << 0, GF_OMP_TASK_TASKLOOP = 1 << 0, GF_OMP_TASK_TASKWAIT = 1 << 1, @@ -3387,6 +3388,29 @@ gimple_call_from_thunk_p (gcall *s) } +/* If FROM_NEW_OR_DELETE_P is true, mark GIMPLE_CALL S as being a call + to operator new or delete created from a new or delete expression. */ + +static inline void +gimple_call_set_from_new_or_delete (gcall *s, bool from_new_or_delete_p) +{ + if (from_new_or_delete_p) + s->subcode |= GF_CALL_FROM_NEW_OR_DELETE; + else + s->subcode &= ~GF_CALL_FROM_NEW_OR_DELETE; +} + + +/* Return true if GIMPLE_CALL S is a call to operator new or delete from + from a new or delete expression. */ + +static inline bool +gimple_call_from_new_or_delete (gcall *s) +{ + return (s->subcode & GF_CALL_FROM_NEW_OR_DELETE) != 0; +} + + /* If PASS_ARG_PACK_P is true, GIMPLE_CALL S is a stdarg call that needs the argument pack in its argument list. */ diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c index 1cd5872c03d..d5423a7e9b2 100644 --- a/gcc/ipa-icf-gimple.c +++ b/gcc/ipa-icf-gimple.c @@ -556,6 +556,7 @@ func_checker::compare_gimple_call (gcall *s1, gcall *s2) || gimple_call_tail_p (s1) != gimple_call_tail_p (s2) || gimple_call_return_slot_opt_p (s1) != gimple_call_return_slot_opt_p (s2) || gimple_call_from_thunk_p (s1) != gimple_call_from_thunk_p (s2) + || gimple_call_from_new_or_delete (s1) != gimple_call_from_new_or_delete (s2) || gimple_call_va_arg_pack_p (s1) != gimple_call_va_arg_pack_p (s2) || gimple_call_alloca_for_var_p (s1) != gimple_call_alloca_for_var_p (s2)) return false; diff --git a/gcc/testsuite/g++.dg/tree-ssa/pta-delete-1.C b/gcc/testsuite/g++.dg/tree-ssa/pta-delete-1.C new file mode 100644 index 00000000000..5e1e322344a --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/pta-delete-1.C @@ -0,0 +1,24 @@ +// { dg-do run } +// { dg-options "-O2" } + +struct X { + static struct X saved; + int *p; + X() { __builtin_memcpy (this, &saved, sizeof (X)); } +}; +X X::saved; +void __attribute__((noinline)) operator delete (void *p) +{ + __builtin_memcpy (&X::saved, p, sizeof (X)); +} +int main() +{ + int y = 1; + X *p = new X; + p->p = &y; + ::operator delete (p); + X *q = new X; + *(q->p) = 2; + if (y != 2) + __builtin_abort (); +} diff --git a/gcc/tree-ssa-dce.c b/gcc/tree-ssa-dce.c index fae5ae72340..c9e0c8fd116 100644 --- a/gcc/tree-ssa-dce.c +++ b/gcc/tree-ssa-dce.c @@ -593,9 +593,9 @@ mark_all_reaching_defs_necessary_1 (ao_ref *ref ATTRIBUTE_UNUSED, /* We want to skip statments that do not constitute stores but have a virtual definition. */ - if (is_gimple_call (def_stmt)) + if (gcall *call = dyn_cast (def_stmt)) { - tree callee = gimple_call_fndecl (def_stmt); + tree callee = gimple_call_fndecl (call); if (callee != NULL_TREE && fndecl_built_in_p (callee, BUILT_IN_NORMAL)) switch (DECL_FUNCTION_CODE (callee)) @@ -612,7 +612,8 @@ mark_all_reaching_defs_necessary_1 (ao_ref *ref ATTRIBUTE_UNUSED, if (callee != NULL_TREE && (DECL_IS_REPLACEABLE_OPERATOR_NEW_P (callee) - || DECL_IS_REPLACEABLE_OPERATOR_DELETE_P (callee))) + || DECL_IS_REPLACEABLE_OPERATOR_DELETE_P (callee)) + && gimple_call_from_new_or_delete (call)) return false; } @@ -875,23 +876,25 @@ propagate_necessity (bool aggressive) processing the argument. */ bool is_delete_operator = (is_gimple_call (stmt) + && gimple_call_from_new_or_delete (as_a (stmt)) && gimple_call_replaceable_operator_delete_p (as_a (stmt))); if (is_delete_operator || gimple_call_builtin_p (stmt, BUILT_IN_FREE)) { tree ptr = gimple_call_arg (stmt, 0); - gimple *def_stmt; + gcall *def_stmt; tree def_callee; /* If the pointer we free is defined by an allocation function do not add the call to the worklist. */ if (TREE_CODE (ptr) == SSA_NAME - && is_gimple_call (def_stmt = SSA_NAME_DEF_STMT (ptr)) + && (def_stmt = dyn_cast (SSA_NAME_DEF_STMT (ptr))) && (def_callee = gimple_call_fndecl (def_stmt)) && ((DECL_BUILT_IN_CLASS (def_callee) == BUILT_IN_NORMAL && (DECL_FUNCTION_CODE (def_callee) == BUILT_IN_ALIGNED_ALLOC || DECL_FUNCTION_CODE (def_callee) == BUILT_IN_MALLOC || DECL_FUNCTION_CODE (def_callee) == BUILT_IN_CALLOC)) - || DECL_IS_REPLACEABLE_OPERATOR_NEW_P (def_callee))) + || (DECL_IS_REPLACEABLE_OPERATOR_NEW_P (def_callee) + && gimple_call_from_new_or_delete (def_stmt)))) { if (is_delete_operator) { @@ -947,9 +950,9 @@ propagate_necessity (bool aggressive) in 1). By keeping a global visited bitmap for references we walk for 2) we avoid quadratic behavior for those. */ - if (is_gimple_call (stmt)) + if (gcall *call = dyn_cast (stmt)) { - tree callee = gimple_call_fndecl (stmt); + tree callee = gimple_call_fndecl (call); unsigned i; /* Calls to functions that are merely acting as barriers @@ -972,22 +975,23 @@ propagate_necessity (bool aggressive) if (callee != NULL_TREE && (DECL_IS_REPLACEABLE_OPERATOR_NEW_P (callee) - || DECL_IS_REPLACEABLE_OPERATOR_DELETE_P (callee))) + || DECL_IS_REPLACEABLE_OPERATOR_DELETE_P (callee)) + && gimple_call_from_new_or_delete (call)) continue; /* Calls implicitly load from memory, their arguments in addition may explicitly perform memory loads. */ - mark_all_reaching_defs_necessary (stmt); - for (i = 0; i < gimple_call_num_args (stmt); ++i) + mark_all_reaching_defs_necessary (call); + for (i = 0; i < gimple_call_num_args (call); ++i) { - tree arg = gimple_call_arg (stmt, i); + tree arg = gimple_call_arg (call, i); if (TREE_CODE (arg) == SSA_NAME || is_gimple_min_invariant (arg)) continue; if (TREE_CODE (arg) == WITH_SIZE_EXPR) arg = TREE_OPERAND (arg, 0); if (!ref_may_be_aliased (arg)) - mark_aliased_reaching_defs_necessary (stmt, arg); + mark_aliased_reaching_defs_necessary (call, arg); } } else if (gimple_assign_single_p (stmt)) @@ -1397,6 +1401,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_from_new_or_delete (as_a (stmt)) && gimple_call_replaceable_operator_delete_p (as_a (stmt))))) { tree ptr = gimple_call_arg (stmt, 0); diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c index f676bf91e95..69de932b14c 100644 --- a/gcc/tree-ssa-structalias.c +++ b/gcc/tree-ssa-structalias.c @@ -4857,7 +4857,13 @@ find_func_aliases_for_call (struct function *fn, gcall *t) point for reachable memory of their arguments. */ else if (flags & (ECF_PURE|ECF_LOOPING_CONST_OR_PURE)) handle_pure_call (t, &rhsc); - else if (fndecl && DECL_IS_REPLACEABLE_OPERATOR_DELETE_P (fndecl)) + /* If the call is to a replaceable operator delete and results + from a delete expression as opposed to a direct call to + such operator, then the effects for PTA (in particular + the escaping of the pointer) can be ignored. */ + else if (fndecl + && DECL_IS_REPLACEABLE_OPERATOR_DELETE_P (fndecl) + && gimple_call_from_new_or_delete (t)) ; else handle_rhs_call (t, &rhsc);