This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
C++ PATCH: multiple inheritance optimization
- To: gcc-patches at gcc dot gnu dot org
- Subject: C++ PATCH: multiple inheritance optimization
- From: Mark Mitchell <mark at codesourcery dot com>
- Date: Mon, 17 Jan 2000 14:55:15 -0800
- Organization: CodeSourcery, LLC
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