This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

DCE enhancement to delete dead vptr assignment


This is related to PR34949. It makes use C++ language semantics.
Currently it handles empty dtors or their inline instances into
deleting dtors or inline instances into callers followed by delete
call.  It will extend automatically to handle other cases (e.g. dtor
with printf statement etc) when more precise alias/mod-ref or builtin
function side effect information is available.

bootstrap and regression on x86-64/linux

Ok for mainline?

Thanks,

David

Attachment: dce_chg.txt
Description: Text document

Index: gcc/testsuite/g++.dg/tree-ssa/vptr_init_dse3.C
===================================================================
--- gcc/testsuite/g++.dg/tree-ssa/vptr_init_dse3.C	(revision 0)
+++ gcc/testsuite/g++.dg/tree-ssa/vptr_init_dse3.C	(revision 0)
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-O2  -fdump-tree-optimized" } */
+struct BB {
+
+  BB();
+  virtual int foo(void);
+  int i;
+  ~BB() {}
+};
+struct B : virtual public BB {
+
+  B();
+  virtual int foo(void);
+  int i;
+  ~B() {}
+};
+
+struct D :  public B{
+  D(int i);
+  int foo(void);
+  virtual ~D();
+  int d;
+};
+
+D::~D()
+{
+}
+ 
+/* { dg-final { scan-tree-dump-times "vptr" 0 "optimized"} } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: gcc/testsuite/g++.dg/tree-ssa/vptr_init_dse2.C
===================================================================
--- gcc/testsuite/g++.dg/tree-ssa/vptr_init_dse2.C	(revision 0)
+++ gcc/testsuite/g++.dg/tree-ssa/vptr_init_dse2.C	(revision 0)
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-O2  -fdump-tree-optimized" } */
+struct B {
+
+  B();
+  virtual int foo(void);
+  int i;
+  ~B () __attribute__((noinline));
+};
+
+B::~B() {}
+
+struct D : public B{
+  D(int i);
+  int foo(void);
+  ~D() {}
+  int d;
+};
+
+ int _main(void)
+ {
+   D *d = new D('c');
+
+   delete d;
+   return 0;
+ }
+/* { dg-final { scan-tree-dump-times "vptr" 0 "optimized"} } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: gcc/testsuite/g++.dg/tree-ssa/vptr_init_dse4.C
===================================================================
--- gcc/testsuite/g++.dg/tree-ssa/vptr_init_dse4.C	(revision 0)
+++ gcc/testsuite/g++.dg/tree-ssa/vptr_init_dse4.C	(revision 0)
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2  -fdump-tree-optimized" } */
+#include <stdio.h>
+struct B  {
+
+  B();
+  virtual int foo(void);
+  int i;
+  virtual ~B() {}
+};
+
+struct D :  virtual public B{
+  D(int i);
+  int foo(void);
+  ~D();
+  int d;
+};
+
+D::~D()
+{
+}
+
+ 
+/* { dg-final { scan-tree-dump-times "vptr" 0 "optimized"} } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: gcc/testsuite/g++.dg/tree-ssa/vptr_init_dse.C
===================================================================
--- gcc/testsuite/g++.dg/tree-ssa/vptr_init_dse.C	(revision 0)
+++ gcc/testsuite/g++.dg/tree-ssa/vptr_init_dse.C	(revision 0)
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2  -fdump-tree-optimized" } */
+struct B {
+
+  B();
+  virtual int foo(void);
+  int i;
+  ~B() {}
+};
+
+struct D : public B{
+  D(int i);
+  int foo(void);
+  ~D() {}
+  int d;
+};
+
+ int _main(void)
+ {
+   D *d = new D('c');
+
+   delete d;
+   return 0;
+ }
+ 
+/* { dg-final { scan-tree-dump-times "vptr" 0 "optimized"} } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: gcc/cp/cp-lang.c
===================================================================
--- gcc/cp/cp-lang.c	(revision 157962)
+++ gcc/cp/cp-lang.c	(working copy)
@@ -40,6 +40,10 @@ static void cp_init_ts (void);
 static const char * cxx_dwarf_name (tree t, int verbosity);
 static enum classify_record cp_classify_record (tree type);
 static tree cp_eh_personality (void);
+static bool cp_is_decl_dtor_p (tree);
+static bool cp_is_vptr_field_p (tree);
+static bool cp_is_polymorphic_type_p (tree);
+static bool cp_is_global_delete_fndecl_p (tree);
 
 /* Lang hooks common to C++ and ObjC++ are declared in cp/cp-objcp-common.h;
    consequently, there should be very few hooks below.  */
@@ -85,6 +89,14 @@ static tree cp_eh_personality (void);
 #define LANG_HOOKS_EH_PERSONALITY cp_eh_personality
 #undef LANG_HOOKS_EH_RUNTIME_TYPE
 #define LANG_HOOKS_EH_RUNTIME_TYPE build_eh_type_type
+#undef LANG_HOOKS_IS_DECL_DTOR
+#define LANG_HOOKS_IS_DECL_DTOR cp_is_decl_dtor_p
+#undef LANG_HOOKS_IS_VPTR_FIELD
+#define LANG_HOOKS_IS_VPTR_FIELD cp_is_vptr_field_p
+#undef LANG_HOOKS_IS_TYPE_POLYMORPHIC
+#define LANG_HOOKS_IS_TYPE_POLYMORPHIC cp_is_polymorphic_type_p
+#undef LANG_HOOKS_IS_GLOBAL_DELETE_FNDECL
+#define LANG_HOOKS_IS_GLOBAL_DELETE_FNDECL cp_is_global_delete_fndecl_p
 
 /* Each front end provides its own lang hook initializer.  */
 struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
@@ -184,5 +196,39 @@ cp_eh_personality (void)
   return cp_eh_personality_decl;
 }
 
+
+static bool
+cp_is_decl_dtor_p (tree decl)
+{
+  return DECL_DESTRUCTOR_P (decl);
+}
+
+static bool
+cp_is_vptr_field_p (tree field)
+{
+  tree type;
+
+  type = DECL_FCONTEXT (field);
+  if (!type)
+    return false;
+  if (TYPE_VFIELD (type) == field)
+    return true;
+  else
+    return false;
+}
+
+static bool
+cp_is_polymorphic_type_p (tree type)
+{
+  return TYPE_POLYMORPHIC_P (type);
+}
+
+static bool
+cp_is_global_delete_fndecl_p (tree decl)
+{
+  return decl == global_delete_fndecl;
+}
+
+
 #include "gt-cp-cp-lang.h"
 #include "gtype-cp.h"
Index: gcc/langhooks.c
===================================================================
--- gcc/langhooks.c	(revision 157962)
+++ gcc/langhooks.c	(working copy)
@@ -54,6 +54,12 @@ lhd_do_nothing_t (tree ARG_UNUSED (t))
 {
 }
 
+bool
+lhd_do_nothing_t_return_false (tree ARG_UNUSED (t))
+{
+  return false;
+}
+
 /* Pass through (tree).  */
 tree
 lhd_pass_through_t (tree t)
Index: gcc/langhooks.h
===================================================================
--- gcc/langhooks.h	(revision 157962)
+++ gcc/langhooks.h	(working copy)
@@ -226,6 +226,18 @@ struct lang_hooks_for_decls
 
   /* Do language specific checking on an implicitly determined clause.  */
   void (*omp_finish_clause) (tree clause);
+
+  /* Return true if DECL is a global destructor.  */
+  bool (*is_decl_dtor) (tree decl);
+
+  /* Return true if DECL is a vptr field.  */
+  bool (*is_vptr_field) (tree decl);
+
+  /* Return true if TYPE is a polymorphic type.  */
+  bool (*is_type_polymorphic) (tree type);
+
+  /* Return true if DECL is global delete.  */
+  bool (*is_global_delete_fndecl) (tree decl);
 };
 
 /* Language hooks related to LTO serialization.  */
Index: gcc/tree-ssa-dce.c
===================================================================
--- gcc/tree-ssa-dce.c	(revision 157962)
+++ gcc/tree-ssa-dce.c	(working copy)
@@ -66,6 +66,7 @@ along with GCC; see the file COPYING3.  
 #include "flags.h"
 #include "cfgloop.h"
 #include "tree-scalar-evolution.h"
+#include "langhooks.h"
 
 static struct stmt_stats
 {
@@ -225,6 +226,95 @@ mark_stmt_necessary (gimple stmt, bool a
     SET_BIT (bb_contains_live_stmts, gimple_bb (stmt)->index);
 }
 
+/* Follow the UD chain to find the base pointer for
+   pointer PTR.  */
+
+static tree
+find_base_ptr (tree ptr)
+{
+  STRIP_NOPS (ptr);
+
+  if (TREE_CODE (ptr) != SSA_NAME)
+    return ptr;
+
+  do
+    {
+      gimple stmt = SSA_NAME_DEF_STMT (ptr);
+
+      if (gimple_code (stmt) != GIMPLE_ASSIGN)
+        return ptr;
+
+      if (gimple_assign_copy_p (stmt)
+          || gimple_assign_unary_nop_p (stmt)
+          || gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
+        {
+          tree rhs = gimple_assign_rhs1 (stmt);
+          STRIP_NOPS (rhs);
+          if (TREE_CODE (rhs) != SSA_NAME)
+            return ptr;
+          else
+            ptr = rhs;
+        }
+      else
+        return ptr;
+    } while (true);
+
+  return ptr;
+}
+
+
+/* Returns true if the STMT's def is used by a global
+   delete call and there is no real uses of the definition
+   by the intervening stmts.  */
+
+static bool
+is_killed_by_delete (gimple stmt)
+{
+  tree lhs, base, base_ptr;
+  gimple use_stmt, delete_stmt = NULL;
+  tree *vdef;
+  use_operand_p use_op;
+  imm_use_iterator iter;
+
+  lhs = gimple_assign_lhs (stmt);
+  base = get_base_address (lhs);
+  if (TREE_CODE (base) != INDIRECT_REF)
+    return false;
+  base_ptr = TREE_OPERAND (base, 0);
+
+  vdef = gimple_vdef_op (stmt);
+  FOR_EACH_IMM_USE_FAST (use_op, iter, *vdef)
+    {
+      use_stmt = USE_STMT (use_op);
+      if (is_global_delete_call (use_stmt))
+        {
+          if (find_base_ptr (base_ptr)
+              == find_base_ptr (gimple_call_arg (use_stmt, 0)))
+            {
+              delete_stmt = use_stmt;
+              break;
+            }
+        }
+    }
+
+  if (!delete_stmt)
+    return false;
+
+  if (!dominated_by_p (CDI_POST_DOMINATORS,
+                       gimple_bb (stmt), gimple_bb (delete_stmt)))
+    return false;
+
+  /* Now check if all other use stmts are dominated by DELETE_STMT.  */
+  FOR_EACH_IMM_USE_FAST (use_op, iter, *vdef)
+    {
+      use_stmt = USE_STMT (use_op);
+      if (use_stmt != delete_stmt
+          && !stmt_dominates_stmt_p (delete_stmt, use_stmt))
+        return false;
+    }
+
+  return true;
+}
 
 /* Mark the statement defining operand OP as necessary.  */
 
@@ -252,6 +342,9 @@ mark_operand_necessary (tree op)
   if (gimple_plf (stmt, STMT_NECESSARY) || gimple_nop_p (stmt))
     return;
 
+  if (is_vptr_init (stmt) && is_killed_by_delete (stmt))
+    return;
+
   if (dump_file && (dump_flags & TDF_DETAILS))
     {
       fprintf (dump_file, "marking necessary through ");
@@ -361,6 +454,33 @@ mark_stmt_if_obviously_necessary (gimple
       return;
     }
 
+  if (is_vptr_init (stmt))
+    {
+      /* Initialization of the vptr field for the same object this
+         destructor is operating on won't be live out of the function
+	 if it is not used within the function, so do not treat them
+	 as a hidden global store.  */
+      if (lang_hooks.decls.is_decl_dtor (current_function_decl))
+        {
+	  tree base, base_ptr;
+	  tree lhs = gimple_assign_lhs (stmt);
+	  base = get_base_address (lhs);
+	  if (TREE_CODE (base) == INDIRECT_REF)
+            {
+              base_ptr = TREE_OPERAND (base, 0);
+              base_ptr = find_base_ptr (base_ptr);
+
+              /* Initializing vptr field of the object being destructed.  */
+	      if ((TREE_CODE (base_ptr) == SSA_NAME)
+                  && (gimple_nop_p (SSA_NAME_DEF_STMT (base_ptr))))
+                return;
+            }
+	}
+
+      if (is_killed_by_delete (stmt))
+        return;
+    }
+
   if (is_hidden_global_store (stmt))
     {
       mark_stmt_necessary (stmt, true);
Index: gcc/tree-flow.h
===================================================================
--- gcc/tree-flow.h	(revision 157962)
+++ gcc/tree-flow.h	(working copy)
@@ -849,6 +849,13 @@ tree force_gimple_operand_gsi (gimple_st
 			       bool, enum gsi_iterator_update);
 tree gimple_fold_indirect_ref (tree);
 
+/* In gimple.c  */
+extern bool is_vptr_access (tree);
+extern bool is_vptr_init (gimple);
+extern bool is_vtbl_access (tree);
+extern bool is_global_delete (tree);
+extern bool is_global_delete_call (gimple);
+
 /* In tree-ssa-live.c */
 extern void remove_unused_locals (void);
 extern void dump_scope_blocks (FILE *, int);
Index: gcc/gimple.c
===================================================================
--- gcc/gimple.c	(revision 157962)
+++ gcc/gimple.c	(working copy)
@@ -36,6 +36,7 @@ along with GCC; see the file COPYING3.  
 #include "flags.h"
 #include "alias.h"
 #include "demangle.h"
+#include "langhooks.h"
 
 /* Global type table.  FIXME lto, it should be possible to re-use some
    of the type hashing routines in tree.c (type_hash_canon, type_hash_lookup,
@@ -4623,4 +4624,104 @@ gimple_fold_obj_type_ref (tree ref, tree
   return build_fold_addr_expr (fndecl);
 }
 
+/* Returns true if ACCESS is a memory read or write
+   of a vptr field.  */
+
+bool
+is_vptr_access (tree access)
+{
+  tree field;
+
+  if (TREE_CODE (access) != COMPONENT_REF)
+    return false;
+
+  field = TREE_OPERAND (access, 1);
+
+  return lang_hooks.decls.is_vptr_field (field);
+}
+
+/* Returns true if STMT is an assignment statement
+   to a vptr field.  */
+
+bool
+is_vptr_init (gimple stmt)
+{
+  tree lhs;
+  if (gimple_code (stmt) != GIMPLE_ASSIGN)
+    return false;
+
+  lhs = gimple_assign_lhs (stmt);
+
+  return is_vptr_access (lhs);
+}
+
+/* Returns true if CALL is a function call to operator delete.  */
+
+bool
+is_global_delete_call (gimple call)
+{
+  tree decl;
+
+  if (gimple_code (call) != GIMPLE_CALL)
+    return false;
+
+  decl = gimple_call_fndecl (call);
+  if (!decl)
+    return false;
+  return lang_hooks.decls.is_global_delete_fndecl (decl);
+}
+
+/* Follows the UD chain to find the real definition
+   of the base pointer PTR and returns true if its value
+   is definitely loaded from a vptr field.  */
+
+static bool
+points_to_vbtl (tree ptr)
+{
+  gimple root_def;
+
+  do
+    {
+      root_def = SSA_NAME_DEF_STMT (ptr);
+      if (gimple_code (root_def) != GIMPLE_ASSIGN)
+        return false;
+      if (gimple_assign_ssa_name_copy_p (root_def))
+        ptr = gimple_assign_rhs1 (root_def);
+      else if (gimple_assign_unary_nop_p (root_def))
+        ptr = gimple_assign_rhs1 (root_def);
+      else if (gimple_assign_single_p (root_def))
+        {
+          if (is_vptr_access (gimple_assign_rhs1 (root_def)))
+            return true;
+          else
+            return false;
+        }
+      else if (gimple_assign_rhs_code (root_def) == POINTER_PLUS_EXPR)
+        ptr = gimple_assign_rhs1 (root_def);
+      else
+        return false;
+
+    } while (TREE_CODE (ptr) == SSA_NAME);
+
+  return false;
+}
+
+/* Returns true of ACCESS is an indirect memory
+   reference of a virtual table.  */
+
+bool
+is_vtbl_access (tree access)
+{
+  tree base_ptr;
+
+  if (TREE_CODE (access) != INDIRECT_REF)
+    return false;
+
+  base_ptr = TREE_OPERAND (access, 0);
+  if (TREE_CODE (base_ptr) != SSA_NAME)
+    return false;
+
+  return points_to_vbtl (base_ptr);
+}
+
 #include "gt-gimple.h"
Index: gcc/langhooks-def.h
===================================================================
--- gcc/langhooks-def.h	(revision 157962)
+++ gcc/langhooks-def.h	(working copy)
@@ -40,6 +40,7 @@ extern void lhd_do_nothing (void);
 extern void lhd_do_nothing_t (tree);
 extern void lhd_do_nothing_i (int);
 extern void lhd_do_nothing_f (struct function *);
+extern bool lhd_do_nothing_t_return_false (tree);
 extern tree lhd_pass_through_t (tree);
 extern bool lhd_post_options (const char **);
 extern alias_set_type lhd_get_alias_set (tree);
@@ -210,6 +211,10 @@ extern tree lhd_make_node (enum tree_cod
 #define LANG_HOOKS_OMP_CLAUSE_ASSIGN_OP lhd_omp_assignment
 #define LANG_HOOKS_OMP_CLAUSE_DTOR hook_tree_tree_tree_null
 #define LANG_HOOKS_OMP_FINISH_CLAUSE hook_void_tree
+#define LANG_HOOKS_IS_DECL_DTOR lhd_do_nothing_t_return_false
+#define LANG_HOOKS_IS_VPTR_FIELD lhd_do_nothing_t_return_false
+#define LANG_HOOKS_IS_TYPE_POLYMORPHIC lhd_do_nothing_t_return_false
+#define LANG_HOOKS_IS_GLOBAL_DELETE_FNDECL lhd_do_nothing_t_return_false
 
 #define LANG_HOOKS_DECLS { \
   LANG_HOOKS_GLOBAL_BINDINGS_P, \
@@ -231,7 +236,11 @@ extern tree lhd_make_node (enum tree_cod
   LANG_HOOKS_OMP_CLAUSE_COPY_CTOR, \
   LANG_HOOKS_OMP_CLAUSE_ASSIGN_OP, \
   LANG_HOOKS_OMP_CLAUSE_DTOR, \
-  LANG_HOOKS_OMP_FINISH_CLAUSE \
+  LANG_HOOKS_OMP_FINISH_CLAUSE, \
+  LANG_HOOKS_IS_DECL_DTOR, \
+  LANG_HOOKS_IS_VPTR_FIELD, \
+  LANG_HOOKS_IS_TYPE_POLYMORPHIC,  \
+  LANG_HOOKS_IS_GLOBAL_DELETE_FNDECL \
 }
 
 /* LTO hooks.  */

Attachment: dce_chg_test.txt
Description: Text document


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]