[PATCH] Use aliases instead of separate copies for !DECL_ONE_ONLY C[12] ctors or D[12] dtors with identical bodies (PR c++/3187)

Jakub Jelinek jakub@redhat.com
Wed Nov 11 14:20:00 GMT 2009


Hi!

When the C++ FE knows the base and complete ctors or dtors of some class
have identical bodies, it is a waste to not use aliases.

This patch attempts to deal with !DECL_ONE_ONLY ctors or dtors.
In the first version of the patch I was calling assemble_alias from
maybe_clone_body in cp/optimize.c, unfortunately that is way too early, it
meant e.g. that the function couldn't be properly inlined etc.
So this patch moves this stuff to the cgraph level, after all functions are
processed and cgraph decided which functions should be emitted and which
should not.  The C++ FE tells cgraph about functions which have the same
body (and where it is possible for them to be aliases, as when they are
aliases the function pointers are equal; one can't take address of a ctor or
dtor, so it is fine though) and cgraph, when it is about to emit two or more
such functions with the same body, if they are !DECL_ONE_ONLY just emits one
of them and for the rest emits aliases if possible.

I'm not doing anything for comdat ctors/dtors at this point, that's
something for further discussions whether just do a gcc change, where we
could worst case risk growing of the library size if mixing ctors or dtors
from different compilers, or if we should also add some linker extension.

Bootstrapped/regtested on x86_64-linux (libstdc++.so.6 .text section shrunk
by ~ 20KB or 3.1%) and on i686-linux (libstdc++.so.6 .text section shrunk by
~ 25KB or 4.1%).  Ok for trunk?

2009-11-11  Jakub Jelinek  <jakub@redhat.com>

	PR c++/3187
	* cgraph.h (struct cgraph_node): Add same_body and same_body_alias
	fields.
	* cgraphunit.c (cgraph_expand_function): Handle same_body aliases.
	* cgraph.c (cgraph_remove_node): Unlink node from same_body circular
	list.

	* cp-tree.h (language_function): Add in_charge_parm_used field.
	(current_function_uses_in_charge_parm): Define.
	* call.c (build_special_member_call): Set
	current_function_uses_in_charge_parm if needed.
	* init.c (expand_virtual_init, push_base_cleanups): Likewise.
	* class.c (build_base_path): Likewise.
	* optimize.c: Include cgraph.h.
	(maybe_clone_body): If in_charge_parm_used is not set and both base
	and complete clones are created, tell cgraph they have the same
	body.
	* Make-lang.in (cp/optimize.o): Depend on $(CGRAPH_H).

--- gcc/cgraph.h.jj	2009-10-09 12:19:05.000000000 +0200
+++ gcc/cgraph.h	2009-11-10 18:01:24.000000000 +0100
@@ -184,6 +184,9 @@ struct GTY((chain_next ("%h.next"), chai
   struct cgraph_node *prev_sibling_clone;
   struct cgraph_node *clones;
   struct cgraph_node *clone_of;
+  /* Circular list of functions that have the same body and can be emitted
+     as aliases.  */
+  struct cgraph_node *same_body;
   /* For functions with many calls sites it holds map from call expression
      to the edge to speed up cgraph_edge function.  */
   htab_t GTY((param_is (struct cgraph_edge))) call_site_hash;
@@ -228,6 +231,9 @@ struct GTY((chain_next ("%h.next"), chai
   unsigned alias : 1;
   /* Set for nodes that was constructed and finalized by frontend.  */
   unsigned finalized_by_frontend : 1;
+  /* Set for nodes that should be output as aliases because they
+     have the same body as another node.  */
+  unsigned same_body_alias : 1;
 };
 
 typedef struct cgraph_node *cgraph_node_ptr;
--- gcc/cgraphunit.c.jj	2009-10-28 15:33:02.000000000 +0100
+++ gcc/cgraphunit.c	2009-11-11 10:38:09.000000000 +0100
@@ -1141,6 +1141,26 @@ cgraph_mark_functions_to_output (void)
     }
 }
 
+/* Remove all nodes inlined into INLINED_TO, as for same_body aliases
+   ipa transforms aren't performed.  */
+
+static void
+cgraph_remove_inline_callees (struct cgraph_node *node,
+			      struct cgraph_node *inlined_to)
+{
+  struct cgraph_edge *e, *next;
+
+  for (e = node->callees; e; e = next)
+    {
+      next = e->next_callee;
+      if (e->callee->global.inlined_to == inlined_to)
+	{
+	  cgraph_remove_inline_callees (e->callee, inlined_to);
+	  cgraph_remove_node (e->callee);
+	}
+    }
+}
+
 /* Expand function specified by NODE.  */
 
 static void
@@ -1156,8 +1176,49 @@ cgraph_expand_function (struct cgraph_no
 
   gcc_assert (node->lowered);
 
-  /* Generate RTL for the body of DECL.  */
-  tree_rest_of_compilation (decl);
+  if (node->same_body_alias)
+    {
+      assemble_alias (decl, DECL_ASSEMBLER_NAME (node->same_body->decl));
+      cgraph_remove_inline_callees (node, node);
+      /* We might need the body of this function so that we can expand
+	 it inline somewhere else.  */
+      if (cgraph_preserve_function_body_p (node->decl))
+	{
+	  bitmap_obstack_initialize (NULL);
+	  current_function_decl = decl;
+	  init_function_start (decl);
+	  cfun->dont_save_pending_sizes_p = 1;
+	  save_inline_function_body (node);
+	  bitmap_obstack_release (NULL);
+	  set_cfun (NULL);
+	}
+      node->same_body = NULL;
+    }
+  else
+    {
+      if (node->same_body)
+	{
+	  struct cgraph_node *n, *next;
+
+	  for (n = node->same_body; n != node; n = next)
+	    {
+	      next = n->same_body;
+#ifdef ASM_OUTPUT_DEF
+	      if (n->process && !DECL_ONE_ONLY (n->decl))
+		{
+		  n->same_body_alias = 1;
+		  n->same_body = node;
+		}
+	      else
+#endif
+		n->same_body = NULL;
+	    }
+	  node->same_body = NULL;
+	}
+
+      /* Generate RTL for the body of DECL.  */
+      tree_rest_of_compilation (decl);
+    }
 
   /* Make sure that BE didn't give up on compiling.  */
   gcc_assert (TREE_ASM_WRITTEN (decl));
--- gcc/cgraph.c.jj	2009-10-09 17:37:37.000000000 +0200
+++ gcc/cgraph.c	2009-11-10 18:01:24.000000000 +0100
@@ -1163,6 +1163,16 @@ cgraph_remove_node (struct cgraph_node *
   /* Incremental inlining access removed nodes stored in the postorder list.
      */
   node->needed = node->reachable = false;
+  if (node->same_body && !node->same_body_alias)
+    {
+      for (n = node->same_body; n->same_body != node; n = n->same_body)
+	;
+      if (n == node->same_body)
+	n->same_body = NULL;
+      else
+	n->same_body = node->same_body;
+      node->same_body = NULL;
+    }
   for (n = node->nested; n; n = n->next_nested)
     n->origin = NULL;
   node->nested = NULL;
--- gcc/cp/cp-tree.h.jj	2009-11-08 14:25:10.000000000 +0100
+++ gcc/cp/cp-tree.h	2009-11-10 18:01:24.000000000 +0100
@@ -923,6 +923,7 @@ struct GTY(()) language_function {
   BOOL_BITFIELD returns_abnormally : 1;
   BOOL_BITFIELD in_function_try_handler : 1;
   BOOL_BITFIELD in_base_initializer : 1;
+  BOOL_BITFIELD in_charge_parm_used : 1;
 
   /* True if this function can throw an exception.  */
   BOOL_BITFIELD can_throw : 1;
@@ -989,6 +990,11 @@ struct GTY(()) language_function {
 
 #define in_function_try_handler cp_function_chain->in_function_try_handler
 
+/* True if current_in_charge_parm is ever used in the current function,
+   except for checking whether to call delete operator at the end of dtor.  */
+#define current_function_uses_in_charge_parm \
+  cp_function_chain->in_charge_parm_used
+
 /* Expression always returned from function, or error_mark_node
    otherwise, for use by the automatic named return value optimization.  */
 
--- gcc/cp/Make-lang.in.jj	2009-09-15 16:10:52.000000000 +0200
+++ gcc/cp/Make-lang.in	2009-11-10 18:01:24.000000000 +0100
@@ -303,7 +303,7 @@ cp/semantics.o: cp/semantics.c $(CXX_TRE
 cp/dump.o: cp/dump.c $(CXX_TREE_H) $(TM_H) $(TREE_DUMP_H)
 cp/optimize.o: cp/optimize.c $(CXX_TREE_H) $(TM_H) rtl.h $(INTEGRATE_H) \
   insn-config.h input.h $(PARAMS_H) debug.h $(TREE_INLINE_H) $(GIMPLE_H) \
-  $(TARGET_H) tree-iterator.h
+  $(TARGET_H) tree-iterator.h $(CGRAPH_H)
 cp/mangle.o: cp/mangle.c $(CXX_TREE_H) $(TM_H) toplev.h $(REAL_H) \
   gt-cp-mangle.h $(TARGET_H) $(TM_P_H)
 cp/parser.o: cp/parser.c $(CXX_TREE_H) $(TM_H) $(DIAGNOSTIC_H) gt-cp-parser.h \
--- gcc/cp/call.c.jj	2009-11-07 18:40:22.000000000 +0100
+++ gcc/cp/call.c	2009-11-10 18:01:24.000000000 +0100
@@ -6040,6 +6040,7 @@ build_special_member_call (tree instance
 			    current_in_charge_parm, integer_zero_node),
 		    current_vtt_parm,
 		    vtt);
+      current_function_uses_in_charge_parm = true;
       gcc_assert (BINFO_SUBVTT_INDEX (binfo));
       sub_vtt = build2 (POINTER_PLUS_EXPR, TREE_TYPE (vtt), vtt,
 			BINFO_SUBVTT_INDEX (binfo));
--- gcc/cp/init.c.jj	2009-09-01 12:17:23.000000000 +0200
+++ gcc/cp/init.c	2009-11-10 18:01:24.000000000 +0100
@@ -930,6 +930,7 @@ expand_virtual_init (tree binfo, tree de
 			     current_in_charge_parm, integer_zero_node),
 		     vtbl2,
 		     vtbl);
+      current_function_uses_in_charge_parm = true;
     }
 
   /* Compute the location of the vtpr.  */
@@ -3212,6 +3213,7 @@ push_base_cleanups (void)
 	      expr = build3 (COND_EXPR, void_type_node, cond,
 			     expr, void_zero_node);
 	      finish_decl_cleanup (NULL_TREE, expr);
+	      current_function_uses_in_charge_parm = true;
 	    }
 	}
     }
--- gcc/cp/class.c.jj	2009-11-07 18:40:22.000000000 +0100
+++ gcc/cp/class.c	2009-11-10 18:01:24.000000000 +0100
@@ -392,15 +392,18 @@ build_base_path (enum tree_code code,
 	v_offset = build2 (code, ptrdiff_type_node, v_offset, offset);
 
       if (fixed_type_p < 0)
-	/* Negative fixed_type_p means this is a constructor or destructor;
-	   virtual base layout is fixed in in-charge [cd]tors, but not in
-	   base [cd]tors.  */
-	offset = build3 (COND_EXPR, ptrdiff_type_node,
-			 build2 (EQ_EXPR, boolean_type_node,
-				 current_in_charge_parm, integer_zero_node),
-			 v_offset,
-			 convert_to_integer (ptrdiff_type_node,
-					     BINFO_OFFSET (binfo)));
+	{
+	  /* Negative fixed_type_p means this is a constructor or destructor;
+	     virtual base layout is fixed in in-charge [cd]tors, but not in
+	     base [cd]tors.  */
+	  offset = build3 (COND_EXPR, ptrdiff_type_node,
+			   build2 (EQ_EXPR, boolean_type_node,
+				   current_in_charge_parm, integer_zero_node),
+			   v_offset,
+			   convert_to_integer (ptrdiff_type_node,
+					       BINFO_OFFSET (binfo)));
+	  current_function_uses_in_charge_parm = true;
+	}
       else
 	offset = v_offset;
     }
--- gcc/cp/optimize.c.jj	2009-11-07 18:40:22.000000000 +0100
+++ gcc/cp/optimize.c	2009-11-10 18:01:24.000000000 +0100
@@ -42,6 +42,7 @@ along with GCC; see the file COPYING3.  
 #include "tree-dump.h"
 #include "gimple.h"
 #include "tree-iterator.h"
+#include "cgraph.h"
 
 /* Prototypes.  */
 
@@ -149,8 +150,9 @@ bool
 maybe_clone_body (tree fn)
 {
   tree clone;
-  tree complete_dtor = NULL_TREE;
+  tree fns[2];
   bool first = true;
+  bool in_charge_parm_used;
 
   /* We only clone constructors and destructors.  */
   if (!DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (fn)
@@ -160,14 +162,20 @@ maybe_clone_body (tree fn)
   /* Emit the DWARF1 abstract instance.  */
   (*debug_hooks->deferred_inline_function) (fn);
 
+  in_charge_parm_used
+    = DECL_STRUCT_FUNCTION (fn)->language->in_charge_parm_used;
+  fns[0] = NULL_TREE;
+  fns[1] = NULL_TREE;
+
   /* Look for the complete destructor which may be used to build the
      delete destructor.  */
   FOR_EACH_CLONE (clone, fn)
-    if (DECL_NAME (clone) == complete_dtor_identifier)
-      {
-        complete_dtor = clone;
-        break;
-      }
+    if (DECL_NAME (clone) == complete_dtor_identifier
+	|| DECL_NAME (clone) == complete_ctor_identifier)
+      fns[0] = clone;
+    else if (DECL_NAME (clone) == base_dtor_identifier
+	     || DECL_NAME (clone) == base_ctor_identifier)
+      fns[1] = clone;
 
   /* We know that any clones immediately follow FN in the TYPE_METHODS
      list.  */
@@ -226,7 +234,7 @@ maybe_clone_body (tree fn)
       /* Build the delete destructor by calling complete destructor
          and delete function.  */
       if (DECL_NAME (clone) == deleting_dtor_identifier)
-        build_delete_destructor_body (clone, complete_dtor);
+	build_delete_destructor_body (clone, fns[0]);
       else
         {
           /* Remap the parameters.  */
@@ -296,6 +304,24 @@ maybe_clone_body (tree fn)
     }
   pop_from_top_level ();
 
+  /* Tell cgraph if both ctors or both dtors are known to have
+     the same body.  */
+  if (!in_charge_parm_used && fns[0] && fns[1])
+    {
+      struct cgraph_node *base_node, *complete_node;
+
+      base_node = cgraph_node (fns[1]);
+      complete_node = cgraph_node (fns[0]);
+      gcc_assert (base_node->same_body == NULL);
+      gcc_assert (complete_node->same_body == NULL);
+      gcc_assert ((DECL_NAME (fns[0]) == complete_ctor_identifier
+		   && DECL_NAME (fns[1]) == base_ctor_identifier)
+		  || (DECL_NAME (fns[0]) == complete_dtor_identifier
+		      && DECL_NAME (fns[1]) == base_dtor_identifier));
+      base_node->same_body = complete_node;
+      complete_node->same_body = base_node;
+    }
+
   /* We don't need to process the original function any further.  */
   return 1;
 }

	Jakub



More information about the Gcc-patches mailing list