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]

C++ PATCH: multiple inheritance optimization



This patch adds vtable entries for virtual functions overridden from
non-primary bases.  This is part of the new ABI.  For example,
consider this program hierarchy:

  struct S
  {
    int i;
    virtual void f ();
  };

  struct T
  {
    int j;
    virtual void g ();
  };

  struct U : public S, public T
  {
    int k;
    virtual void f ();
    virtual void g ();
  };

In this case `S' is the primary base.  In U's vtable, under the old
ABI, there would be entries for the functions overridden from `S', and
any functions newly defined in U that don't override anything.  (In
this case there are none of the latter.)

So, when a virtual call is made to `g' (through a `U*'), the `U*' is
first converted to a `T*'.  Then, the vtable entry is looked up for
`g' in the `U-as-T' vtable.  This has to be a thunk because, in the
end, `g' expects a `U*' as the `this' pointer.  So, we do pointer
arithemtic twice to get back where we started.

In the new ABI, we add an entry for `g' to U's vtable, even though
there is also one in the `U-as-T' vtable.  So, a call to `g' through a
`U*' requires no conversions.

Attached is sample code for:

  U* u;
  u->g();

under the old and new ABIs, compiled with -O2.  As mentioned above,
the improvement is even better than this looks, since we also don't
have to use a thunk for the call.

--
Mark Mitchell                   mark@codesourcery.com
CodeSourcery, LLC               http://www.codesourcery.com

========== OLD ABI =========

	xorl	%eax, %eax
	testl	%ecx, %ecx
	leal	8(%ecx), %edx
	sete	%al
	decl	%eax
	andl	%edx, %eax
	movl	4(%eax), %ebx
	xorl	%eax, %eax
	testl	%ecx, %ecx
	sete	%al
	decl	%eax
	andl	%eax, %edx
	pushl	%edx
	movl	8(%ebx), %ebx
.LCFI6:
	call	*%ebx

=========== NEW ABI ========

	movl	(%eax), %edx
	pushl	%eax
	movl	12(%edx), %edx
.LCFI5:
	call	*%edx

2000-01-17  Mark Mitchell  <mark@codesourcery.com>

	* cp-tree.h (build_shared_int_cst): New function.
	* call.c (build_over_call): Use DECL_VIRTUAL_CONTEXT, for clarity.
	* class.c (modify_vtable_entry): Likewise.
	(add_virtual_function): Split out code to generated shared
	INTEGER_CSTs to build_share_int_cst.
	(modify_all_vtables): Handle all the overridden functions here.
	Add overridden functions from non-primary virtual bases to the
	primary vtable.
	(finish_struct_1): Adjust call to modify_all_vtables.  Add
	overridden functions from non-primary bases to the vtable.
	* tree.c (build_shared_int_cst): New function.

Index: call.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/call.c,v
retrieving revision 1.188
diff -c -p -r1.188 call.c
*** call.c	2000/01/17 20:18:38	1.188
--- call.c	2000/01/17 22:39:54
*************** build_over_call (cand, args, flags)
*** 4126,4132 ****
      {
        tree t, *p = &TREE_VALUE (converted_args);
        tree binfo = get_binfo
! 	(DECL_CONTEXT (fn), TREE_TYPE (TREE_TYPE (*p)), 0);
        *p = convert_pointer_to_real (binfo, *p);
        if (TREE_SIDE_EFFECTS (*p))
  	*p = save_expr (*p);
--- 4126,4132 ----
      {
        tree t, *p = &TREE_VALUE (converted_args);
        tree binfo = get_binfo
! 	(DECL_VIRTUAL_CONTEXT (fn), TREE_TYPE (TREE_TYPE (*p)), 0);
        *p = convert_pointer_to_real (binfo, *p);
        if (TREE_SIDE_EFFECTS (*p))
  	*p = save_expr (*p);
Index: class.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/class.c,v
retrieving revision 1.237
diff -c -p -r1.237 class.c
*** class.c	2000/01/17 20:18:38	1.237
--- class.c	2000/01/17 22:39:57
*************** static tree dfs_get_class_offset PROTO((
*** 103,109 ****
  static tree get_class_offset PROTO((tree, tree, tree, tree));
  static void modify_one_vtable PROTO((tree, tree, tree));
  static tree dfs_modify_vtables PROTO((tree, void *));
! static void modify_all_vtables PROTO((tree, tree));
  static void determine_primary_base PROTO((tree, int *));
  static void finish_struct_methods PROTO((tree));
  static void maybe_warn_about_overly_private_class PROTO ((tree));
--- 103,109 ----
  static tree get_class_offset PROTO((tree, tree, tree, tree));
  static void modify_one_vtable PROTO((tree, tree, tree));
  static tree dfs_modify_vtables PROTO((tree, void *));
! static tree modify_all_vtables PROTO((tree, int *, tree));
  static void determine_primary_base PROTO((tree, int *));
  static void finish_struct_methods PROTO((tree));
  static void maybe_warn_about_overly_private_class PROTO ((tree));
*************** modify_vtable_entry (old_entry_in_list, 
*** 1107,1113 ****
    if (TREE_CODE (DECL_VINDEX (fndecl)) != INTEGER_CST)
      {
        DECL_VINDEX (fndecl) = DECL_VINDEX (base_fndecl);
!       DECL_CONTEXT (fndecl) = DECL_CONTEXT (base_fndecl);
      }
  }
  
--- 1107,1113 ----
    if (TREE_CODE (DECL_VINDEX (fndecl)) != INTEGER_CST)
      {
        DECL_VINDEX (fndecl) = DECL_VINDEX (base_fndecl);
!       DECL_VIRTUAL_CONTEXT (fndecl) = DECL_VIRTUAL_CONTEXT (base_fndecl);
      }
  }
  
*************** add_virtual_function (new_virtuals_p, ov
*** 1166,1173 ****
       tree fndecl;
       tree t; /* Structure type.  */
  {
-   my_friendly_assert (DECL_CONTEXT (fndecl) == t, 20000116);
- 
    /* If this function doesn't override anything from a base class, we
       can just assign it a new DECL_VINDEX now.  Otherwise, if it does
       override something, we keep it around and assign its DECL_VINDEX
--- 1166,1171 ----
*************** add_virtual_function (new_virtuals_p, ov
*** 1184,1209 ****
        CLASSTYPE_RTTI (t) = t;
  
        start_vtable (t, has_virtual);
- 
-       /* Build a new INT_CST for this DECL_VINDEX.  */
-       {
- 	static tree index_table[256];
- 	tree idx;
- 	/* We skip a slot for the offset/tdesc entry.  */
- 	int i = (*has_virtual)++;
  
! 	if (i >= 256 || index_table[i] == 0)
! 	  {
! 	    idx = build_int_2 (i, 0);
! 	    if (i < 256)
! 	      index_table[i] = idx;
! 	  }
! 	else
! 	  idx = index_table[i];
  
- 	/* Now assign virtual dispatch information.  */
- 	DECL_VINDEX (fndecl) = idx;
-       }
        /* Save the state we've computed on the NEW_VIRTUALS list.  */
        *new_virtuals_p = tree_cons (integer_zero_node,
  				   fndecl,
--- 1182,1192 ----
        CLASSTYPE_RTTI (t) = t;
  
        start_vtable (t, has_virtual);
  
!       /* Now assign virtual dispatch information.  */
!       DECL_VINDEX (fndecl) = build_shared_int_cst ((*has_virtual)++);
!       DECL_VIRTUAL_CONTEXT (fndecl) = t;
  
        /* Save the state we've computed on the NEW_VIRTUALS list.  */
        *new_virtuals_p = tree_cons (integer_zero_node,
  				   fndecl,
*************** dfs_modify_vtables (binfo, data)
*** 2752,2768 ****
    return NULL_TREE;
  }
  
! static void
! modify_all_vtables (t, fndecl)
       tree t;
!      tree fndecl;
  {
!   tree list;
  
!   list = build_tree_list (t, fndecl);
!   dfs_walk (TYPE_BINFO (t), dfs_modify_vtables, 
! 	    dfs_unmarked_real_bases_queue_p, list);
!   dfs_walk (TYPE_BINFO (t), dfs_unmark, dfs_marked_real_bases_queue_p, t);
  }
  
  /* Fixup all the delta entries in this one vtable that need updating.  */
--- 2735,2815 ----
    return NULL_TREE;
  }
  
! /* Update all of the primary and secondary vtables for T.  Create new
!    vtables as required, and initialize their RTTI information.  Each
!    of the functions in OVERRIDDEN_VIRTUALS overrides a virtual
!    function from a base class; find and modify the appropriate entries
!    to point to the overriding functions.  Returns a list, in
!    declaration order, of the functions that are overridden in this
!    class, but do not appear in the primary base class vtable, and
!    which should therefore be appended to the end of the vtable for T.  */
! 
! static tree
! modify_all_vtables (t, has_virtual_p, overridden_virtuals)
       tree t;
!      int *has_virtual_p;
!      tree overridden_virtuals;
  {
!   tree fns;
!   tree binfo;
  
!   binfo = TYPE_BINFO (t);
! 
!   /* Even if there are no overridden virtuals, we want to go through
!      the hierarchy updating RTTI information.  */
!   if (!overridden_virtuals && TYPE_CONTAINS_VPTR_P (t) && flag_rtti)
!     overridden_virtuals = build_tree_list (NULL_TREE, NULL_TREE);
! 
!   /* Iterate through each of the overriding functions, updating the
!      base vtables.  */
!   for (fns = overridden_virtuals; fns; fns = TREE_CHAIN (fns))
!     {
!       tree list;
!       list = build_tree_list (t, TREE_VALUE (fns));
!       dfs_walk (binfo, dfs_modify_vtables, 
! 		dfs_unmarked_real_bases_queue_p, list);
!       dfs_walk (binfo, dfs_unmark, dfs_marked_real_bases_queue_p, t);
!     }
! 
!   /* If we should include overriding functions for secondary vtables
!      in our primary vtable, add them now.  */
!   if (all_overridden_vfuns_in_vtables_p ())
!     {
!       tree *fnsp = &overridden_virtuals;
! 
!       while (*fnsp)
! 	{
! 	  tree fn = TREE_VALUE (*fnsp);
! 
! 	  if (BINFO_VIRTUALS (binfo)
! 	      && !value_member (fn, BINFO_VIRTUALS (binfo)))
! 	    {
! 	      /* We know we need a vtable for this class now.  */
! 	      start_vtable (t, has_virtual_p);
! 	      /* Set the vtable index.  */
! 	      DECL_VINDEX (fn) 
! 		= build_shared_int_cst ((*has_virtual_p)++);
! 	      /* We don't need to convert to a base class when calling
! 		 this function.  */
! 	      DECL_VIRTUAL_CONTEXT (fn) = t;
! 	      /* We don't need to adjust the `this' pointer when
! 		 calling this function.  */
! 	      TREE_PURPOSE (*fnsp) = integer_zero_node;
! 
! 	      /* This is an overridden function not already in our
! 		 vtable.  Keep it.  */
! 	      fnsp = &TREE_CHAIN (*fnsp);
! 	    }
! 	  else
! 	    /* We've already got an entry for this function.  Skip
! 	       it.  */
! 	    *fnsp = TREE_CHAIN (*fnsp);
! 	}
!     }
!   else
!     overridden_virtuals = NULL_TREE;
! 
!   return overridden_virtuals;
  }
  
  /* Fixup all the delta entries in this one vtable that need updating.  */
*************** finish_struct_1 (t)
*** 4818,4839 ****
        TYPE_VFIELD (t) = vfield;
      }
  
!   if (flag_rtti && TYPE_CONTAINS_VPTR_P (t) && !overridden_virtuals)
!     modify_all_vtables (t, NULL_TREE);
  
-   for (overridden_virtuals = nreverse (overridden_virtuals);
-        overridden_virtuals;
-        overridden_virtuals = TREE_CHAIN (overridden_virtuals))
-     modify_all_vtables (t, TREE_VALUE (overridden_virtuals));
-   
    if (TYPE_USES_VIRTUAL_BASECLASSES (t))
      {
        tree vbases;
        /* Now fixup any virtual function entries from virtual bases
  	 that have different deltas.  This has to come after we do the
! 	 pending hard virtuals, as we might have a function that comes
! 	 from multiple virtual base instances that is only overridden
! 	 by a hard virtual above.  */
        vbases = CLASSTYPE_VBASECLASSES (t);
        while (vbases)
  	{
--- 4865,4879 ----
        TYPE_VFIELD (t) = vfield;
      }
  
!   overridden_virtuals 
!     = modify_all_vtables (t, &has_virtual, nreverse (overridden_virtuals));
  
    if (TYPE_USES_VIRTUAL_BASECLASSES (t))
      {
        tree vbases;
        /* Now fixup any virtual function entries from virtual bases
  	 that have different deltas.  This has to come after we do the
! 	 overridden virtuals.  */
        vbases = CLASSTYPE_VBASECLASSES (t);
        while (vbases)
  	{
*************** finish_struct_1 (t)
*** 4851,4856 ****
--- 4891,4897 ----
  
    /* If necessary, create the vtable for this class.  */
    if (new_virtuals
+       || overridden_virtuals
        || (TYPE_CONTAINS_VPTR_P (t) && vptrs_present_everywhere_p ()))
      {
        new_virtuals = nreverse (new_virtuals);
*************** finish_struct_1 (t)
*** 4859,4865 ****
  	{
  	  if (! CLASSTYPE_COM_INTERFACE (t))
  	    {
! 	      /* The second slot is for the tdesc pointer when thunks are used.  */
  	      if (flag_vtable_thunks)
  		new_virtuals = tree_cons (NULL_TREE, NULL_TREE, new_virtuals);
  
--- 4900,4907 ----
  	{
  	  if (! CLASSTYPE_COM_INTERFACE (t))
  	    {
! 	      /* The second slot is for the tdesc pointer when thunks
! 		 are used.  */
  	      if (flag_vtable_thunks)
  		new_virtuals = tree_cons (NULL_TREE, NULL_TREE, new_virtuals);
  
*************** finish_struct_1 (t)
*** 4918,4923 ****
--- 4960,4969 ----
  	 followed by entries for new functions unique to this class.  */
        TYPE_BINFO_VIRTUALS (t) 
  	= chainon (TYPE_BINFO_VIRTUALS (t), new_virtuals);
+       /* Finally, add entries for functions that override virtuals
+ 	 from non-primary bases.  */
+       TYPE_BINFO_VIRTUALS (t) 
+ 	= chainon (TYPE_BINFO_VIRTUALS (t), overridden_virtuals);
      }
  
    /* Now lay out the virtual function table.  */
Index: cp-tree.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/cp-tree.h,v
retrieving revision 1.386
diff -c -p -r1.386 cp-tree.h
*** cp-tree.h	2000/01/17 20:18:39	1.386
--- cp-tree.h	2000/01/17 22:40:01
*************** extern tree cp_build_qualified_type_real
*** 4199,4204 ****
--- 4199,4205 ----
  extern void remap_save_expr                     PROTO((tree *, splay_tree, tree, int *));
  #define cp_build_qualified_type(TYPE, QUALS) \
    cp_build_qualified_type_real ((TYPE), (QUALS), /*complain=*/1)
+ extern tree build_shared_int_cst                PROTO((int));
  
  /* in typeck.c */
  extern int string_conv_p			PROTO((tree, tree, int));
Index: tree.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/tree.c,v
retrieving revision 1.176
diff -c -p -r1.176 tree.c
*** tree.c	2000/01/17 04:08:01	1.176
--- tree.c	2000/01/17 22:40:02
***************
*** 1,5 ****
  /* Language-dependent node constructors for parse phase of GNU compiler.
!    Copyright (C) 1987, 88, 92-98, 1999 Free Software Foundation, Inc.
     Hacked by Michael Tiemann (tiemann@cygnus.com)
  
  This file is part of GNU CC.
--- 1,5 ----
  /* Language-dependent node constructors for parse phase of GNU compiler.
!    Copyright (C) 1987, 88, 92-98, 1999, 2000 Free Software Foundation, Inc.
     Hacked by Michael Tiemann (tiemann@cygnus.com)
  
  This file is part of GNU CC.
*************** build_min VPROTO((enum tree_code code, t
*** 1708,1713 ****
--- 1708,1733 ----
  
    va_end (p);
    return t;
+ }
+ 
+ /* Returns an INTEGER_CST (of type `int') corresponding to I.
+    Multiple calls with the same value of I may or may not yield the
+    same node; therefore, callers should never modify the node
+    returned.  */
+ 
+ tree
+ build_shared_int_cst (i)
+      int i;
+ {
+   static tree cache[256];
+ 
+   if (i >= 256)
+     return build_int_2 (i, 0);
+   
+   if (!cache[i])
+     cache[i] = build_int_2 (i, 0);
+   
+   return cache[i];
  }
  
  tree

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