Status: vtable thunks for base classes bug.

Martin v. Loewis martin@mira.isdn.cs.tu-berlin.de
Sun Mar 7 05:05:00 GMT 1999


This patch is a first step for fixing vtable thunks. It is meant for
review only.

With this patch, the compiler will emit a table for each class with
virtual bases (called vtable list, mangled as __vl.<class>). This
array has one slot for each constructor/vbase combination encountered
during construction. The base classes are expected to pass this on to
their bases, and then consume the right amount of vtables from the
list.

This list roughly consists of three parts:
a) vtables needed while constructing vbases that have vbases 
   (i.e. during the in_chrg part)
b) vtables passed to non-virtual bases (one set for each base)
c) vtables needed for vbases at the inheritance level (one slot for
   each vbase).
The sets in part b) recursively consist of b) and c) parts.

In some cases, plain old vtables can be put into the list. In many
cases, adjustments will be different, so new vtables are generated.
These new vtables are mangled as __vc.<virtual base>.<base path>.

With this patch, the compiler passes the testsuite on
i586-pc-linux-gnu. This is not surprising, since the emitted tables
are not referenced anywhere.

In the next step, I will try to modify the constructors to pass these
tables around, and iterate through them. There will be a limited form
of backwards-compatibility: the old constructor will wrap around the
new one, so calls to the old constructor will succeed (at the cost of
additionl parameter copies). The new constructor will reference the
base class constructor weakly, and fall back to calling the old
constructor if the linker doesn't provide the new one.

I'm not sure when I will find the time to work on this next step.

Regards,
Martin

1999-03-07  Martin von Löwis  <loewis@informatik.hu-berlin.de>

	* cp-tree.h (VCTABLE_NAME_FORMAT, VLIST_NAME_FORMAT): New macros.
	* class.c (get_fresh_vtable_name): New function.
	(prepare_fresh_vtable): Use it, new parameter for_base.
	(modify_one_vtable): Change caller.
	(fixup_vtable_deltas1): Likewise.
	(override_one_vtable): Likewise.
	(finish_one_ctor_vtable, finish_ctor_vtables_for_vbases
	finish_ctor_vtables_1, finish_ctor_vtables): New functions.
	(finish_struct_1): Call finish_ctor_vtables.

Index: cp-tree.h
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/cp-tree.h,v
retrieving revision 1.203
diff -u -p -r1.203 cp-tree.h
--- cp-tree.h	1999/03/05 16:38:50	1.203
+++ cp-tree.h	1999/03/07 12:34:23
@@ -2224,6 +2225,8 @@ extern int current_function_parms_stored
 #define AUTO_TEMP_FORMAT "_$tmp_%d"
 #define VTABLE_BASE "$vb"
 #define VTABLE_NAME_FORMAT (flag_vtable_thunks ? "__vt_%s" : "_vt$%s")
+#define VCTABLE_NAME_FORMAT  "__vc$%s$%s"
+#define VLIST_NAME_FORMAT "__vl$%s"
 #define VFIELD_BASE "$vf"
 #define VFIELD_NAME "_vptr$"
 #define VFIELD_NAME_FORMAT "_vptr$%s"
@@ -2246,6 +2249,8 @@ extern int current_function_parms_stored
 #define AUTO_TEMP_FORMAT "_.tmp_%d"
 #define VTABLE_BASE ".vb"
 #define VTABLE_NAME_FORMAT (flag_vtable_thunks ? "__vt_%s" : "_vt.%s")
+#define VCTABLE_NAME_FORMAT  "__vc.%s.%s"
+#define VLIST_NAME_FORMAT "__vl.%s"
 #define VFIELD_BASE ".vf"
 #define VFIELD_NAME "_vptr."
 #define VFIELD_NAME_FORMAT "_vptr.%s"
@@ -2278,6 +2283,8 @@ extern int current_function_parms_stored
 #define VTABLE_NAME_P(ID_NODE) \
   (!strncmp (IDENTIFIER_POINTER (ID_NODE), VTABLE_NAME, \
 	     sizeof (VTABLE_NAME) - 1))
+#define VCTABLE_NAME_FORMAT  "__vc_%s_%s"
+#define VLIST_NAME_FORMAT "__vl_%s"
 #define VFIELD_BASE "__vfb"
 #define VFIELD_NAME "__vptr_"
 #define VFIELD_NAME_P(ID_NODE) \
Index: class.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/cp/class.c,v
retrieving revision 1.134
diff -u -p -r1.134 class.c
--- class.c	1999/03/03 11:24:37	1.134
+++ class.c	1999/03/07 12:34:29
@@ -99,9 +99,10 @@ static tree get_derived_offset PROTO((tr
 static tree get_basefndecls PROTO((tree, tree));
 static void set_rtti_entry PROTO((tree, tree, tree));
 static tree build_vtable PROTO((tree, tree));
-static void prepare_fresh_vtable PROTO((tree, tree));
+static tree prepare_fresh_vtable PROTO((tree, tree, tree));
 static void fixup_vtable_deltas1 PROTO((tree, tree));
 static void fixup_vtable_deltas PROTO((tree, int, tree));
+static void finish_ctor_vtables PROTO((tree));
 static void finish_vtbls PROTO((tree, int, tree));
 static void modify_vtable_entry PROTO((tree, tree, tree));
 static tree get_vtable_entry_n PROTO((tree, unsigned HOST_WIDE_INT));
@@ -619,6 +620,32 @@ get_vtable_name (type)
   return get_identifier (buf);
 }
 
+/* Return a fresh vtable name, either in the VTABLE_NAME_FORMAT
+   or in the VCTABLE_NAME_FORMAT. */
+
+static tree
+get_fresh_vtable_name (name, for_base)
+     char *name;
+     tree for_base;
+{
+  char *buf;
+  if (for_base)
+    {
+      for_base = BINFO_TYPE (for_base);
+      buf = (char *) alloca (strlen (VCTABLE_NAME_FORMAT) 
+			     + TYPE_ASSEMBLER_NAME_LENGTH (for_base)
+			     + strlen (name) + 1);
+      sprintf (buf, VCTABLE_NAME_FORMAT, 
+	       TYPE_ASSEMBLER_NAME_STRING (for_base), name);
+    }
+  else
+    {
+      buf = (char *) alloca (strlen (VTABLE_NAME_FORMAT) + strlen (name) + 1);
+      sprintf (buf, VTABLE_NAME_FORMAT, name);
+    }
+  return get_identifier (buf);
+}
+
 /* Return the offset to the main vtable for a given base BINFO.  */
 
 tree
@@ -761,23 +788,26 @@ extern tree signed_size_zero_node;
    FOR_TYPE is the derived type which caused this table to
    be needed.
 
+   If the binfo FOR_BASE is non-zero, the table is for FOR_BASE as
+   appearing inside the complete type for_type.
+
    BINFO is the type association which provided TYPE for FOR_TYPE.
 
    The order in which vtables are built (by calling this function) for
    an object must remain the same, otherwise a binary incompatibility
    can result.  */
 
-static void
-prepare_fresh_vtable (binfo, for_type)
-     tree binfo, for_type;
+static tree
+prepare_fresh_vtable (binfo, for_type, for_base)
+     tree binfo, for_type, for_base;
 {
   tree basetype;
   tree orig_decl = BINFO_VTABLE (binfo);
   tree name;
   tree new_decl;
   tree offset;
-  tree path = binfo;
-  char *buf, *buf2;
+  tree path = for_base ? for_base : binfo;
+  char *buf2;
   char joiner = '_';
   int i;
 
@@ -785,7 +815,7 @@ prepare_fresh_vtable (binfo, for_type)
   joiner = JOINER;
 #endif
 
-  basetype = TYPE_MAIN_VARIANT (BINFO_TYPE (binfo));
+  basetype = TYPE_MAIN_VARIANT (BINFO_TYPE (path));
 
   buf2 = TYPE_ASSEMBLER_NAME_STRING (basetype);
   i = TYPE_ASSEMBLER_NAME_LENGTH (basetype) + 1;
@@ -809,9 +839,7 @@ prepare_fresh_vtable (binfo, for_type)
 
       sprintf (buf1, "%s%c%s", TYPE_ASSEMBLER_NAME_STRING (for_type), joiner,
 	       buf2);
-      buf = (char *) alloca (strlen (VTABLE_NAME_FORMAT) + strlen (buf1) + 1);
-      sprintf (buf, VTABLE_NAME_FORMAT, buf1);
-      name = get_identifier (buf);
+      name = get_fresh_vtable_name (buf1, for_base ? binfo : NULL_TREE);
 
       /* If this name doesn't clash, then we can use it, otherwise
 	 we add more to the name until it is unique.  */
@@ -840,10 +868,7 @@ prepare_fresh_vtable (binfo, for_type)
 	    sprintf (buf1, "%s%c%s%c%d",
 		     TYPE_ASSEMBLER_NAME_STRING (basetype), joiner,
 		     buf2, joiner, j);
-	    buf = (char *) alloca (strlen (VTABLE_NAME_FORMAT)
-				   + strlen (buf1) + 1);
-	    sprintf (buf, VTABLE_NAME_FORMAT, buf1);
-	    name = get_identifier (buf);
+	    name = get_fresh_vtable_name (buf1, for_base ? binfo : NULL_TREE);
 
 	    /* If this name doesn't clash, then we can use it,
 	       otherwise we add something different to the name until
@@ -870,7 +895,9 @@ prepare_fresh_vtable (binfo, for_type)
 
   DECL_ARTIFICIAL (new_decl) = 1;
   TREE_STATIC (new_decl) = 1;
-  BINFO_VTABLE (binfo) = pushdecl_top_level (new_decl);
+  new_decl = pushdecl_top_level (new_decl);
+  if (!for_base)
+    BINFO_VTABLE (binfo) = new_decl;
   DECL_VIRTUAL_P (new_decl) = 1;
 #ifndef WRITABLE_VTABLES
   /* Make them READONLY by default. (mrs) */
@@ -878,10 +905,11 @@ prepare_fresh_vtable (binfo, for_type)
 #endif
   DECL_ALIGN (new_decl) = DECL_ALIGN (orig_decl);
 
-  /* Make fresh virtual list, so we can smash it later.  */
-  BINFO_VIRTUALS (binfo) = copy_list (BINFO_VIRTUALS (binfo));
+  if (!for_base)
+    /* Make fresh virtual list, so we can smash it later.  */
+    BINFO_VIRTUALS (binfo) = copy_list (BINFO_VIRTUALS (binfo));
 
-  if (TREE_VIA_VIRTUAL (binfo))
+  if (!for_base && TREE_VIA_VIRTUAL (binfo))
     {
       tree binfo1 = binfo_member (BINFO_TYPE (binfo), 
 				  CLASSTYPE_VBASECLASSES (for_type));
@@ -897,9 +925,10 @@ prepare_fresh_vtable (binfo, for_type)
   else
     offset = BINFO_OFFSET (binfo);
 
-  set_rtti_entry (BINFO_VIRTUALS (binfo),
-		  ssize_binop (MINUS_EXPR, integer_zero_node, offset),
-		  for_type);
+  if (!for_base)
+    set_rtti_entry (BINFO_VIRTUALS (binfo),
+		    ssize_binop (MINUS_EXPR, integer_zero_node, offset),
+		    for_type);
 
 #ifdef GATHER_STATISTICS
   n_vtables += 1;
@@ -909,11 +938,14 @@ prepare_fresh_vtable (binfo, for_type)
   /* Set TREE_PUBLIC and TREE_EXTERN as appropriate.  */
   import_export_vtable (new_decl, for_type, 0);
 
-  if (TREE_VIA_VIRTUAL (binfo))
+  if (!for_base && TREE_VIA_VIRTUAL (binfo))
     my_friendly_assert (binfo == binfo_member (BINFO_TYPE (binfo),
 				   CLASSTYPE_VBASECLASSES (current_class_type)),
 			170);
-  SET_BINFO_NEW_VTABLE_MARKED (binfo);
+  if (!for_base)
+    SET_BINFO_NEW_VTABLE_MARKED (binfo);
+
+  return new_decl;
 }
 
 #if 0
@@ -2372,6 +2404,7 @@ finish_vtbls (binfo, do_self, t)
 	}
       finish_vtbls (base_binfo, is_not_base_vtable, t);
     }
+
 }
 
 /* True if we should override the given BASE_FNDECL with the given
@@ -2529,7 +2562,7 @@ modify_one_vtable (binfo, t, fndecl, pfn
       else
 	{
 	  if (! BINFO_NEW_VTABLE_MARKED (binfo))
-	    prepare_fresh_vtable (binfo, t);
+	    prepare_fresh_vtable (binfo, t, 0);
 	}
     }
   if (fndecl == NULL_TREE)
@@ -2578,7 +2611,7 @@ modify_one_vtable (binfo, t, fndecl, pfn
 		 that override the virtual functions in these base classes
 		 which are not defined by the current type.  */
 	      if (! BINFO_NEW_VTABLE_MARKED (binfo))
-		prepare_fresh_vtable (binfo, t);
+		prepare_fresh_vtable (binfo, t, 0);
 	    }
 
 #ifdef NOTQUITE
@@ -2677,7 +2710,7 @@ fixup_vtable_deltas1 (binfo, t)
 		     that override the virtual functions in these base classes
 		     which are not defined by the current type.  */
 		  if (! BINFO_NEW_VTABLE_MARKED (binfo))
-		    prepare_fresh_vtable (binfo, t);
+		    prepare_fresh_vtable (binfo, t, 0);
 		}
 
 	      modify_vtable_entry (get_vtable_entry_n (BINFO_VIRTUALS (binfo), n),
@@ -2827,7 +2860,7 @@ override_one_vtable (binfo, old, t)
 	      choose = NEITHER;
 	      if (! BINFO_NEW_VTABLE_MARKED (binfo))
 		{
-		  prepare_fresh_vtable (binfo, t);
+		  prepare_fresh_vtable (binfo, t, 0);
 		  override_one_vtable (binfo, old, t);
 		  return;
 		}
@@ -2842,7 +2875,7 @@ override_one_vtable (binfo, old, t)
 	      choose = NEITHER;
 	      if (! BINFO_NEW_VTABLE_MARKED (binfo))
 		{
-		  prepare_fresh_vtable (binfo, t);
+		  prepare_fresh_vtable (binfo, t, 0);
 		  override_one_vtable (binfo, old, t);
 		  return;
 		}
@@ -2858,7 +2891,7 @@ override_one_vtable (binfo, old, t)
 	  choose = NEITHER;
 	  if (! BINFO_NEW_VTABLE_MARKED (binfo))
 	    {
-	      prepare_fresh_vtable (binfo, t);
+	      prepare_fresh_vtable (binfo, t, 0);
 	      override_one_vtable (binfo, old, t);
 	      return;
 	    }
@@ -3116,6 +3149,218 @@ warn_hidden (t)
     }
 }
 
+/* Generate one vtable for use in constructors or destructors of base
+   classes with virtual bases.  */
+
+static tree
+finish_one_ctor_vtable (complete_type, base, oldvbase, newvbase)
+     tree complete_type, base, oldvbase, newvbase;
+{
+  tree virtuals;
+  tree newtable;
+  tree newvirtuals;
+  tree offset;
+
+  newtable = prepare_fresh_vtable (newvbase, complete_type, base);
+  newvirtuals = copy_list (BINFO_VIRTUALS (oldvbase));
+
+  virtuals = newvirtuals;
+  /* Change the offset entry. */
+  offset = ssize_binop (MINUS_EXPR, BINFO_OFFSET (newvbase),
+			BINFO_OFFSET (base));
+  offset = ssize_binop (MINUS_EXPR, integer_zero_node, offset);
+  offset = build1 (NOP_EXPR, vfunc_ptr_type_node, offset);
+  TREE_CONSTANT (offset) = 1;
+  TREE_VALUE (virtuals) = build_vtable_entry (integer_zero_node, offset);
+  virtuals = TREE_CHAIN (virtuals);
+
+  /* Skip the typeinfo function. */
+  virtuals = TREE_CHAIN (virtuals);
+
+  /* Iterate over all methods of this virtual base. */
+  for (; virtuals; virtuals = TREE_CHAIN (virtuals))
+    {
+      tree fndecl = TREE_VALUE (virtuals);
+      tree pfn = FNADDR_FROM_VTABLE_ENTRY (fndecl);
+      fndecl = TREE_OPERAND (pfn, 0);
+      if (fndecl)
+	{
+	  tree context = DECL_CLASS_CONTEXT (fndecl);
+	  /* This is the delta from a complete C to a B subobject,
+	     or more generally to the base subobject that implements
+	     the virtual function for B. */
+	  tree delta = get_class_offset (context, complete_type, 
+					 newvbase, fndecl);
+	  /* This is the delta from the A to the complete C. */
+	  tree newdelta = BINFO_OFFSET (newvbase);
+	  /* This is the delta from the A to the B subobject. */
+	  newdelta = size_binop (MINUS_EXPR, newdelta, delta);
+	  newdelta = ssize_binop (MINUS_EXPR, integer_zero_node,
+				  newdelta);
+
+	  modify_vtable_entry (virtuals, 	
+			       build_vtable_entry (newdelta, pfn),
+			       fndecl);
+	}
+    }
+  DECL_INITIAL (newtable) = build_nt (CONSTRUCTOR, NULL_TREE,
+				      newvirtuals);
+  DECL_CONTEXT (newtable) = 0;
+  cp_finish_decl (newtable, DECL_INITIAL (newtable), NULL_TREE, 0, 0);
+  DECL_CONTEXT (newtable) = complete_type;
+  return newtable;
+}
+
+/* Iterate over all virtual bases of one base (i.e. A in the comment
+   for finish_ctor_vtables_1).  */
+
+static tree
+finish_ctor_vtables_for_vbases (vbases, base, complete_type) 
+     tree vbases, base, complete_type;
+{
+  tree result = NULL_TREE;
+  for (; vbases; vbases = TREE_CHAIN (vbases))
+    {
+      tree newvbase;
+      tree vtbl;
+      if (!BINFO_VIRTUALS (vbases))
+	/* Class is not polymorphic. */
+	continue;
+      newvbase = binfo_member (BINFO_TYPE (vbases), 
+			       CLASSTYPE_VBASECLASSES (complete_type));
+      vtbl = finish_one_ctor_vtable (complete_type, base, vbases, newvbase);
+      vtbl = build1 (ADDR_EXPR, vtbl_ptr_type_node, vtbl);
+      TREE_READONLY (vtbl) = 1;
+      TREE_CONSTANT (vtbl) = 1;
+      
+      result = tree_cons (NULL_TREE, vtbl, result);
+    }
+  return result;
+}
+
+/* Generate special vtables for virtual bases for use inside base
+   class ctors and dtors. Inside this function, we assume the
+   following scenario:
+   class A{virtual void foo();};
+   class B:virtual A{int member1;}
+   class C:B{int member2;}
+*/
+
+static tree
+finish_ctor_vtables_1 (t, complete_type)
+     tree t;
+     tree complete_type;
+{
+  int i;
+  tree binfos;
+  tree result = NULL_TREE;
+
+  binfos = TYPE_BINFO_BASETYPES (t);
+
+  /* Iterate over all bases (i.e. B). */
+  for (i = 0; i < CLASSTYPE_N_BASECLASSES (t); i++)
+    {
+      tree base = TREE_VEC_ELT (binfos, i);
+      tree vbases = CLASSTYPE_VBASECLASSES (BINFO_TYPE (base));
+      if (!vbases)
+	/* This base class does not have virtual bases. */
+	continue;
+      if (TREE_VIA_VIRTUAL (base))
+	/* A virtual base class is initialized on in the most-derived
+	   constructor. */
+	continue;
+      if (!BINFO_VIRTUALS (base))
+	/* Class is not polymorphic. */
+	continue;
+      /* Prepend vtable list for base class. */
+      result = chainon 
+	(finish_ctor_vtables_1 (BINFO_TYPE (base), complete_type),
+	 result);
+      /* Prepend our own vtable list. */
+      result = chainon 
+	(finish_ctor_vtables_for_vbases (vbases, base, complete_type),
+	 result);
+    }
+  return result;
+}
+
+/* Wrapper around finish_ctor_vtables_1. */
+
+static void
+finish_ctor_vtables (t)
+     tree t;
+{
+  tree veclist = NULL_TREE;
+  tree decl, type;
+  char *name;
+  tree vbase, vtbl;
+  int len;
+
+  /* This is only good for vtable thunks. */
+  my_friendly_assert (flag_vtable_thunks, 990307);
+
+  /* Start with the list of most-derived vtables. */
+
+  for (vbase = CLASSTYPE_VBASECLASSES (t); vbase;
+       vbase = TREE_CHAIN (vbase))
+    {
+      if (!BINFO_VIRTUALS (vbase))
+	/* Class is not polymorphic. */
+	continue;
+      vtbl = BINFO_VTABLE (vbase);
+      vtbl = build1 (ADDR_EXPR, vtbl_ptr_type_node, vtbl);
+      TREE_READONLY (vtbl) = 1;
+      TREE_CONSTANT (vtbl) = 1;
+      veclist = tree_cons (NULL_TREE, vtbl, veclist);
+    }
+
+  /* Compute the list of vtables for the bases. */
+  veclist = chainon (veclist, finish_ctor_vtables_1 (t, t));
+
+  /* Finally, we initialize the virtual bases first. */
+  for (vbase = CLASSTYPE_VBASECLASSES (t); vbase;
+       vbase = TREE_CHAIN (vbase))
+    {
+      tree vbases = CLASSTYPE_VBASECLASSES (BINFO_TYPE (vbase));
+      if (!vbases)
+	continue;
+      veclist = chainon (veclist,
+			 finish_ctor_vtables_for_vbases (vbases, vbase, t));
+    }
+
+  veclist = nreverse (veclist);
+
+  /* Generate the name for the vtable list. */
+  name = alloca (strlen (VLIST_NAME_FORMAT) 
+		 + TYPE_ASSEMBLER_NAME_LENGTH (t) + 2);
+  sprintf (name, VLIST_NAME_FORMAT, TYPE_ASSEMBLER_NAME_STRING (t));
+
+  /* Build the type of the list. */
+  len = list_length (veclist) - 1;
+  if (len < 0)
+    /* If this class has virtual bases without virtual methods, make a
+       single zero-entry in the array. This avoids zero-sized objects.  */
+    len++;
+  type = build_cplus_array_type (vtbl_ptr_type_node, 
+				 build_index_type (size_int (len)));
+
+
+  /* Produce a new decl holding the list. */
+  decl = build_decl (VAR_DECL, get_identifier (name), type);
+  DECL_INTERFACE_KNOWN (decl) = 1;
+  TREE_PUBLIC (decl) = 1;
+  DECL_WEAK (decl) = 1;
+  TREE_STATIC (decl) = 1;
+  TREE_READONLY (decl) = 1;
+  TREE_ADDRESSABLE (decl) = 1;
+  
+  decl = pushdecl_top_level (decl);
+  DECL_INITIAL (decl) = build_nt (CONSTRUCTOR, NULL_TREE, veclist);
+
+  /* Output the array. */
+  cp_finish_decl (decl, DECL_INITIAL (decl), NULL_TREE, 0, 0);
+} 
+
 /* Check for things that are invalid.  There are probably plenty of other
    things we should check for also.  */
 
@@ -4269,6 +4514,10 @@ finish_struct_1 (t, warn_anon)
   /* Make the rtl for any new vtables we have created, and unmark
      the base types we marked.  */
   finish_vtbls (TYPE_BINFO (t), 1, t);
+  /* If we use thunks, and have virtual bases, we might need to emit
+     additional vtables.  */
+  if (flag_vtable_thunks && TYPE_USES_VIRTUAL_BASECLASSES (t))
+    finish_ctor_vtables (t);  
   hack_incomplete_structures (t);
 
 #if 0


More information about the Gcc mailing list