Bring in bug fixes from the GCC anoncvs server's "gcc-2_95-branch"
branch as of May 26th, 2000. [these are changes March 31 - May 24th]
This commit is contained in:
parent
89d6e979f6
commit
890b30850f
@ -1,3 +1,60 @@
|
||||
Fri May 26 10:30:02 2000 Richard Earnshaw (rearnsha@arm.com)
|
||||
|
||||
* arm.md (negdi2): Add clobber of condition code register.
|
||||
|
||||
Wed May 24 00:25:45 2000 Alexandre Oliva <oliva@lsd.ic.unicamp.br>
|
||||
|
||||
2000-05-22 Richard Henderson <rth@cygnus.com>
|
||||
* function.c (thread_prologue_and_epilogue_insns): Make sure
|
||||
existing line number notes appear after the prologue.
|
||||
|
||||
2000-05-16 Franz Sirl <Franz.Sirl-kernel@lauterbach.com>
|
||||
|
||||
* sysv4.h (CPP_OS_LINUX_SPEC): Define __unix and __linux according
|
||||
to given options.
|
||||
|
||||
2000-05-14 Franz Sirl <Franz.Sirl-kernel@lauterbach.com>
|
||||
|
||||
* rs6000/rs6000.md (nonlocal_goto_receiver): Add length attribute.
|
||||
|
||||
Wed Apr 12 00:42:19 2000 Jeffrey A Law (law@cygnus.com)
|
||||
|
||||
2000-02-19 Richard Henderson <rth@cygnus.com>
|
||||
* c-typeck.c (add_pending_init): Don't abort for multiple
|
||||
fields at the same offset.
|
||||
(pending_init_member): Test the correct member.
|
||||
|
||||
Wed Apr 12 00:44:31 2000 Jeffrey A Law (law@cygnus.com)
|
||||
* config/i386/freebsd-elf.h (DEFAULT_VTABLE_THUNKS): Changed to 2.
|
||||
|
||||
Tue Nov 9 17:14:40 1999 H.J. Lu <hjl@gnu.org>
|
||||
* invoke.texi (-fvtable-thunks): Document that Linux/GNU uses
|
||||
version 2 as default.
|
||||
|
||||
Tue Nov 9 10:16:55 1999 H.J. Lu <hjl@gnu.org>
|
||||
* alpha/linux-elf.h (DEFAULT_VTABLE_THUNKS): Changed to 2.
|
||||
* arm/linux-elf.h: Likewise.
|
||||
* rs6000/linux.h: Likewise.
|
||||
* sparc/linux.h: Likewise.
|
||||
|
||||
Mon Nov 8 14:41:34 1999 H.J. Lu <hjl@gnu.org>
|
||||
* config/linux.h (DEFAULT_VTABLE_THUNKS): Changed to 2.
|
||||
|
||||
Sun May 2 08:49:27 1999 Martin von Löwis <loewis@informatik.hu-berlin.de>
|
||||
* invoke.texi (-fvtable-thunks): Document numeric argument.
|
||||
|
||||
2000-04-11 Alexandre Oliva <oliva@lsd.ic.unicamp.br>
|
||||
|
||||
* config/elfos.h (SELECT_SECTION): Decide whether to use a data or
|
||||
const section to output a CONSTRUCTOR based on the same conditions
|
||||
used for VAR_DECLs.
|
||||
|
||||
Mon Apr 3 02:31:32 2000 Jeffrey A Law (law@cygnus.com)
|
||||
|
||||
2000-01-20 Michael Hayes <m.hayes@elec.canterbury.ac.nz>
|
||||
* unroll.c (loop_iterations): Don't abort if REG_USERVAR_P set
|
||||
on iteration_var.
|
||||
|
||||
Wed Mar 22 13:11:54 2000 David Edelsohn <edelsohn@gnu.org>
|
||||
|
||||
* rs6000.c (reg_or_u_cint_operand): New function.
|
||||
|
@ -5846,7 +5846,7 @@ add_pending_init (purpose, value)
|
||||
p = *q;
|
||||
if (tree_int_cst_lt (purpose, p->purpose))
|
||||
q = &p->left;
|
||||
else if (tree_int_cst_lt (p->purpose, purpose))
|
||||
else if (p->purpose != purpose)
|
||||
q = &p->right;
|
||||
else
|
||||
abort ();
|
||||
@ -5860,8 +5860,7 @@ add_pending_init (purpose, value)
|
||||
if (tree_int_cst_lt (DECL_FIELD_BITPOS (purpose),
|
||||
DECL_FIELD_BITPOS (p->purpose)))
|
||||
q = &p->left;
|
||||
else if (tree_int_cst_lt (DECL_FIELD_BITPOS (p->purpose),
|
||||
DECL_FIELD_BITPOS (purpose)))
|
||||
else if (p->purpose != purpose)
|
||||
q = &p->right;
|
||||
else
|
||||
abort ();
|
||||
@ -6046,7 +6045,7 @@ pending_init_member (field)
|
||||
{
|
||||
while (p)
|
||||
{
|
||||
if (tree_int_cst_equal (field, p->purpose))
|
||||
if (field == p->purpose)
|
||||
return 1;
|
||||
else if (tree_int_cst_lt (field, p->purpose))
|
||||
p = p->left;
|
||||
|
@ -38,7 +38,7 @@ Boston, MA 02111-1307, USA. */
|
||||
|
||||
#ifndef USE_GNULIBC_1
|
||||
#undef DEFAULT_VTABLE_THUNKS
|
||||
#define DEFAULT_VTABLE_THUNKS 1
|
||||
#define DEFAULT_VTABLE_THUNKS 2
|
||||
#endif
|
||||
|
||||
#ifndef USE_GNULIBC_1
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* elfos.h -- operating system specific defines to be used when
|
||||
targeting GCC for some generic ELF system
|
||||
Copyright (C) 1991, 1994, 1995, 1999 Free Software Foundation, Inc.
|
||||
Copyright (C) 1991, 1994, 1995, 1999, 2000 Free Software Foundation, Inc.
|
||||
Based on svr4.h contributed by Ron Guilmette (rfg@netcom.com).
|
||||
|
||||
This file is part of GNU CC.
|
||||
@ -426,7 +426,8 @@ do { \
|
||||
else \
|
||||
data_section (); \
|
||||
} \
|
||||
else if (TREE_CODE (DECL) == VAR_DECL) \
|
||||
else if (TREE_CODE (DECL) == VAR_DECL \
|
||||
|| TREE_CODE (DECL) == CONSTRUCTOR) \
|
||||
{ \
|
||||
if ((flag_pic && RELOC) \
|
||||
|| !TREE_READONLY (DECL) || TREE_SIDE_EFFECTS (DECL) \
|
||||
|
@ -37,7 +37,7 @@ Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* Use more efficient ``thunks'' to implement C++ vtables. */
|
||||
#undef DEFAULT_VTABLE_THUNKS
|
||||
#define DEFAULT_VTABLE_THUNKS 1
|
||||
#define DEFAULT_VTABLE_THUNKS 2
|
||||
|
||||
/* Override the default comment-starter of "/". */
|
||||
#undef ASM_COMMENT_START
|
||||
|
@ -37,7 +37,7 @@ Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* Use more efficient ``thunks'' to implement C++ vtables. */
|
||||
#undef DEFAULT_VTABLE_THUNKS
|
||||
#define DEFAULT_VTABLE_THUNKS 1
|
||||
#define DEFAULT_VTABLE_THUNKS 2
|
||||
|
||||
/* Override the default comment-starter of "/". */
|
||||
#undef ASM_COMMENT_START
|
||||
|
@ -89,7 +89,7 @@ Boston, MA 02111-1307, USA. */
|
||||
|
||||
#ifndef USE_GNULIBC_1
|
||||
#undef DEFAULT_VTABLE_THUNKS
|
||||
#define DEFAULT_VTABLE_THUNKS 1
|
||||
#define DEFAULT_VTABLE_THUNKS 2
|
||||
#endif
|
||||
|
||||
#undef LIB_SPEC
|
||||
|
@ -37,7 +37,7 @@ Boston, MA 02111-1307, USA. */
|
||||
|
||||
#ifndef USE_GNULIBC_1
|
||||
#undef DEFAULT_VTABLE_THUNKS
|
||||
#define DEFAULT_VTABLE_THUNKS 1
|
||||
#define DEFAULT_VTABLE_THUNKS 2
|
||||
#endif
|
||||
|
||||
/* Use stabs instead of DWARF debug format. */
|
||||
|
@ -1,3 +1,117 @@
|
||||
2000-03-08 Nathan Sidwell <nathan@codesourcery.com>
|
||||
|
||||
* exception.cc (__cp_pop_exception): Cleanup the original object.
|
||||
|
||||
1999-11-13 Jason Merrill <jason@yorick.cygnus.com>
|
||||
|
||||
* rtti.c (get_tinfo_fn_unused): Split out from get_tinfo_fn.
|
||||
* class.c (set_rtti_entry): Use it.
|
||||
|
||||
Wed Apr 12 00:45:49 2000 Jeffrey A Law (law@cygnus.com)
|
||||
|
||||
2000-02-03 <loewis@informatik.hu-berlin.de>
|
||||
* call.c (add_function_candidate): Do not add vlist ctor into
|
||||
candidates in compatibility mode.
|
||||
(build_user_type_conversion_1): Add LOOKUP_HAS_VLIST when adding
|
||||
vlist.
|
||||
(convert_like): Likewise.
|
||||
(build_over_call): Skip vlist only if it is mentioned in flags.
|
||||
(build_new_method_call): Do not add vlist in compatibility mode,
|
||||
except for dtors.
|
||||
* cp-tree.h (flag_vtable_thunks): Has now four possible values.
|
||||
(DECL_VLIST_CTOR_WRAPPER_P, DECL_VLIST_CTOR_WRAPPED): New macros.
|
||||
(in_charge_identifier): Declare external.
|
||||
* decl.c (xref_basetypes): Check for vtable_thunks >=2.
|
||||
(finish_dtor): Use bit 2 of in_chrg.
|
||||
(finish_function): Do not finish vlist ctor wrappers.
|
||||
* decl2.c (flag_vtable_thunks_compat): New variable.
|
||||
(lang_decode_option): Set it accordingly.
|
||||
(maybe_retrofit_in_chrg): Call make_vlist_ctor_wrapper.
|
||||
* init.c (build_base_dtor_call): Pass 4 in in_chrg.
|
||||
(no_vlist_base_init): New function.
|
||||
(expand_default_init): Do not pass vlist in compatibility mode.
|
||||
Try to call old base ctor if new one was not generated.
|
||||
(build_new_1): Do not pass vlist in compatibility mode.
|
||||
* method.c (get_id_2): Do not put _Vlist into dtor name in
|
||||
compatibility mode.
|
||||
(make_vlist_ctor_wrapper, emit_vlist_ctor_wrapper): New functions.
|
||||
(synthesize_method): Call emit_vlist_ctor_wrapper.
|
||||
* pt.c (instantiate_class_template): Check for vtable_thunks >=2.
|
||||
|
||||
Sat Nov 13 15:48:59 1999 H.J. Lu <hjl@gnu.org>
|
||||
* init.c (finish_dtor): Call mark_all_temps_used () before
|
||||
declaring vlist.
|
||||
|
||||
Tue Nov 9 15:01:57 1999 H.J. Lu <hjl@gnu.org>
|
||||
* init.c (construct_virtual_bases): Update.
|
||||
(expand_cleanup_for_base): Update.
|
||||
|
||||
Tue Nov 9 08:25:04 1999 H.J. Lu <hjl@gnu.org>
|
||||
* init.c (construct_virtual_bases): Update.
|
||||
(expand_cleanup_for_base): Take vlist parameter.
|
||||
(emit_base_init): Pass vlist to expand_cleanup_for_base.
|
||||
(construct_virtual_bases): Likewise.
|
||||
|
||||
1999-05-02 Martin von Löwis <loewis@informatik.hu-berlin.de>
|
||||
* class.c (prepare_ctor_vtable, finish_one_ctor_vtable,
|
||||
prepend_ctor_vfields_for_vbase, finish_ctor_vtables_for_vbases,
|
||||
finish_ctor_vtables_1, prepend_vbase_vfields,
|
||||
finish_ctor_vtables): New functions.
|
||||
(finish_struct_1): Call finish_ctor_vtables.
|
||||
* cp-tree.h (TYPE_USES_PVBASES): New macro.
|
||||
(constructor_for_vbase_attr): Widen to two bits.
|
||||
(CONSTRUCTOR_FOR_VBASE, CONSTRUCTOR_FOR_PVBASE,
|
||||
DESTRUCTOR_FOR_PVBASE): New macros.
|
||||
(DECL_CONSTRUCTOR_FOR_VBASE_P): Adopt to new enumeration.
|
||||
(DECL_CONSTRUCTOR_FOR_VBASE): New macro.
|
||||
(DECL_CONSTRUCTOR_FOR_PVBASE_P, DECL_DESTRUCTOR_FOR_PVBASE_P): New
|
||||
macros.
|
||||
(vlist_identifier, vlist_type_node, vlist_zero_node): Declare.
|
||||
(VCTABLE_NAME, VLIST_NAME_FORMAT, VLIST_NAME, VLIST1_NAME,
|
||||
VLIST_TYPE_NAME): New macros.
|
||||
(LOOKUP_HAS_VLIST): New macro.
|
||||
(build_base_dtor_call, init_vlist): Declare.
|
||||
(build_destructor_name): Add int argument.
|
||||
* decl.c (vlist_identifier, vlist_type_node, vlist_zero_node):
|
||||
Define.
|
||||
(init_decl_processing): Initialize them.
|
||||
(grokdeclarator): Handle vlist argument.
|
||||
(copy_args_p): Likewise.
|
||||
(grok_ctor_properties): Don't try to skip initial int for
|
||||
templates. Skip vlist if available.
|
||||
(xref_basetypes): Set TYPE_USES_PVBASES.
|
||||
(finish_dtor, finish_ctor): New functions, moved out of ...
|
||||
(finish_function): ... here.
|
||||
* decl2.c (lang_decode_option): Set flag_vtable_thunks explicitly.
|
||||
(maybe_retrofit_in_chrg): Retrofit __vlist parameter.
|
||||
(grokclassfn): Pass pvbase flag into mangled name.
|
||||
* init.c (build_base_dtor_call): New function.
|
||||
(build_partial_cleanup_for): Call it.
|
||||
(pvbasecount, init_vlist, ): New functions.
|
||||
(emit_base_init): Process vlist argument.
|
||||
(expand_aggr_vbase_init_1): Likewise.
|
||||
(expand_aggr_vbase_init): Likewise.
|
||||
(expand_default_init): Likewise.
|
||||
(build_new_1): Pass null vlist argument.
|
||||
(build_delete): Likewise. Call build_base_dtor_call.
|
||||
* method.c (process_overload_item): Mangle _Vlist specially.
|
||||
(build_base_path, get_vlist_vtable_id): New functions.
|
||||
(build_destructor_name): Potentially mangle _Vlist into it.
|
||||
(do_build_copy_constructor): Skip vlist argument.
|
||||
(synthesize_method): Likewise.
|
||||
* pt.c (has_pvbases_p): New function.
|
||||
(instantiate_class_template): Call it.
|
||||
(tsubst_decl): Retrofit before mangling. Pass pvbase_p to
|
||||
destructor mangling.
|
||||
* search.c (expand_direct_vtbls_init_thunks): New function.
|
||||
(expand_indirect_vtbls_init_thunks): New function.
|
||||
(expand_indirect_vtbls_init): Call it.
|
||||
* call.c (add_function_candidate): Process vlist argument.
|
||||
(build_user_type_conversion_1): Add vlist argument.
|
||||
(convert_like): Likewise.
|
||||
(build_over_call): Likewise.
|
||||
(build_new_method_call): Likewise.
|
||||
|
||||
2000-02-18 Martin von Loewis <loewis@informatik.hu-berlin.de>
|
||||
|
||||
* typeck2.c (my_friendly_abort): Use GCCBUGURL.
|
||||
|
@ -1065,8 +1065,8 @@ add_function_candidate (candidates, fn, arglist, flags)
|
||||
tree parmnode, argnode;
|
||||
int viable = 1;
|
||||
|
||||
/* The `this' and `in_chrg' arguments to constructors are not considered
|
||||
in overload resolution. */
|
||||
/* The `this', `in_chrg', and `vlist' arguments to constructors are
|
||||
not considered in overload resolution. */
|
||||
if (DECL_CONSTRUCTOR_P (fn))
|
||||
{
|
||||
parmlist = TREE_CHAIN (parmlist);
|
||||
@ -1076,6 +1076,22 @@ add_function_candidate (candidates, fn, arglist, flags)
|
||||
parmlist = TREE_CHAIN (parmlist);
|
||||
arglist = TREE_CHAIN (arglist);
|
||||
}
|
||||
if ((flags & LOOKUP_HAS_VLIST)
|
||||
&& DECL_CONSTRUCTOR_FOR_PVBASE_P (fn))
|
||||
{
|
||||
parmlist = TREE_CHAIN (parmlist);
|
||||
arglist = TREE_CHAIN (arglist);
|
||||
}
|
||||
else if (!(flags & LOOKUP_HAS_VLIST)
|
||||
&& !DECL_CONSTRUCTOR_FOR_PVBASE_P (fn))
|
||||
/* Ok */;
|
||||
else
|
||||
{
|
||||
/* The ctor expects a vlist and the arguments don't have
|
||||
one, or vice versa, so fn is not even a candidate, since
|
||||
the corresponding ctor would be the candidate. */
|
||||
return candidates;
|
||||
}
|
||||
}
|
||||
|
||||
len = list_length (arglist);
|
||||
@ -2071,6 +2087,11 @@ build_user_type_conversion_1 (totype, expr, flags)
|
||||
tree t = build_int_2 (0, 0);
|
||||
TREE_TYPE (t) = build_pointer_type (totype);
|
||||
args = build_scratch_list (NULL_TREE, expr);
|
||||
if (TYPE_USES_PVBASES (totype) && !flag_vtable_thunks_compat)
|
||||
{
|
||||
args = scratch_tree_cons (NULL_TREE, vlist_zero_node, args);
|
||||
flags |= LOOKUP_HAS_VLIST;
|
||||
}
|
||||
if (TYPE_USES_VIRTUAL_BASECLASSES (totype))
|
||||
args = scratch_tree_cons (NULL_TREE, integer_one_node, args);
|
||||
args = scratch_tree_cons (NULL_TREE, t, args);
|
||||
@ -3038,6 +3059,7 @@ convert_like (convs, expr)
|
||||
= WRAPPER_PTR (TREE_OPERAND (convs, 1));
|
||||
tree fn = cand->fn;
|
||||
tree args;
|
||||
int flags = LOOKUP_NORMAL;
|
||||
|
||||
if (DECL_CONSTRUCTOR_P (fn))
|
||||
{
|
||||
@ -3045,13 +3067,19 @@ convert_like (convs, expr)
|
||||
TREE_TYPE (t) = build_pointer_type (DECL_CONTEXT (fn));
|
||||
|
||||
args = build_scratch_list (NULL_TREE, expr);
|
||||
if (TYPE_USES_PVBASES (DECL_CONTEXT (fn))
|
||||
&& !flag_vtable_thunks_compat)
|
||||
{
|
||||
args = scratch_tree_cons (NULL_TREE, vlist_zero_node, args);
|
||||
flags != LOOKUP_HAS_VLIST;
|
||||
}
|
||||
if (TYPE_USES_VIRTUAL_BASECLASSES (DECL_CONTEXT (fn)))
|
||||
args = scratch_tree_cons (NULL_TREE, integer_one_node, args);
|
||||
args = scratch_tree_cons (NULL_TREE, t, args);
|
||||
}
|
||||
else
|
||||
args = build_this (expr);
|
||||
expr = build_over_call (cand, args, LOOKUP_NORMAL);
|
||||
expr = build_over_call (cand, args, flags);
|
||||
|
||||
/* If this is a constructor or a function returning an aggr type,
|
||||
we need to build up a TARGET_EXPR. */
|
||||
@ -3260,6 +3288,13 @@ build_over_call (cand, args, flags)
|
||||
arg = TREE_CHAIN (arg);
|
||||
parm = TREE_CHAIN (parm);
|
||||
}
|
||||
if (flags & LOOKUP_HAS_VLIST)
|
||||
{
|
||||
converted_args = expr_tree_cons
|
||||
(NULL_TREE, TREE_VALUE (arg), converted_args);
|
||||
arg = TREE_CHAIN (arg);
|
||||
parm = TREE_CHAIN (parm);
|
||||
}
|
||||
}
|
||||
/* Bypass access control for 'this' parameter. */
|
||||
else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)
|
||||
@ -3369,6 +3404,8 @@ build_over_call (cand, args, flags)
|
||||
arg = TREE_CHAIN (converted_args);
|
||||
if (TYPE_USES_VIRTUAL_BASECLASSES (DECL_CONTEXT (fn)))
|
||||
arg = TREE_CHAIN (arg);
|
||||
if (flags & LOOKUP_HAS_VLIST)
|
||||
arg = TREE_CHAIN (arg);
|
||||
arg = TREE_VALUE (arg);
|
||||
|
||||
/* Pull out the real argument, disregarding const-correctness. */
|
||||
@ -3544,6 +3581,8 @@ build_new_method_call (instance, name, args, basetype_path, flags)
|
||||
remove it for error reporting. */
|
||||
if (flags & LOOKUP_HAS_IN_CHARGE)
|
||||
user_args = TREE_CHAIN (args);
|
||||
if (flags & LOOKUP_HAS_VLIST)
|
||||
user_args = TREE_CHAIN (user_args);
|
||||
|
||||
args = resolve_args (args);
|
||||
|
||||
@ -3616,6 +3655,12 @@ build_new_method_call (instance, name, args, basetype_path, flags)
|
||||
if (name == ctor_identifier && TYPE_USES_VIRTUAL_BASECLASSES (basetype)
|
||||
&& ! (flags & LOOKUP_HAS_IN_CHARGE))
|
||||
{
|
||||
if (TYPE_USES_PVBASES(basetype)
|
||||
&& (!flag_vtable_thunks_compat || (name == dtor_identifier)))
|
||||
{
|
||||
args = scratch_tree_cons (NULL_TREE, vlist_zero_node, args);
|
||||
flags |= LOOKUP_HAS_VLIST;
|
||||
}
|
||||
flags |= LOOKUP_HAS_IN_CHARGE;
|
||||
args = scratch_tree_cons (NULL_TREE, integer_one_node, args);
|
||||
}
|
||||
|
@ -102,8 +102,15 @@ 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_ctor_vtable PROTO((tree, tree, tree));
|
||||
static void fixup_vtable_deltas1 PROTO((tree, tree));
|
||||
static void fixup_vtable_deltas PROTO((tree, int, tree));
|
||||
static tree finish_one_ctor_vtable PROTO((tree, tree, tree, tree));
|
||||
static tree prepend_ctor_vfields_for_vbase PROTO((tree, tree, tree, tree, int, tree));
|
||||
static tree finish_ctor_vtables_for_vbases PROTO((tree, tree, tree));
|
||||
static tree finish_ctor_vtables_1 PROTO((tree, tree));
|
||||
static tree prepend_vbase_vfields 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));
|
||||
@ -673,7 +680,7 @@ set_rtti_entry (virtuals, offset, type)
|
||||
return;
|
||||
|
||||
if (flag_rtti)
|
||||
vfn = build1 (ADDR_EXPR, vfunc_ptr_type_node, get_tinfo_fn (type));
|
||||
vfn = build1 (ADDR_EXPR, vfunc_ptr_type_node, get_tinfo_fn_unused (type));
|
||||
else
|
||||
vfn = build1 (NOP_EXPR, vfunc_ptr_type_node, size_zero_node);
|
||||
TREE_CONSTANT (vfn) = 1;
|
||||
@ -922,6 +929,43 @@ prepare_fresh_vtable (binfo, for_type)
|
||||
SET_BINFO_NEW_VTABLE_MARKED (binfo);
|
||||
}
|
||||
|
||||
/* Return a new vtable for use in initialization of the BASE subobject
|
||||
of COMPLETE_TYPE. The vtable there goes into the vfield of the
|
||||
VBASEBASE virtual subobject. */
|
||||
|
||||
static tree
|
||||
prepare_ctor_vtable (complete_type, base, vbasebase)
|
||||
tree complete_type, base, vbasebase;
|
||||
{
|
||||
tree orig_decl = BINFO_VTABLE (vbasebase);
|
||||
tree name = get_vlist_vtable_id (base, vbasebase);
|
||||
tree new_decl;
|
||||
|
||||
new_decl = build_lang_decl (VAR_DECL, name, TREE_TYPE (orig_decl));
|
||||
/* Remember which class this vtable is really for. */
|
||||
DECL_CONTEXT (new_decl) = complete_type;
|
||||
|
||||
DECL_ARTIFICIAL (new_decl) = 1;
|
||||
TREE_STATIC (new_decl) = 1;
|
||||
new_decl = pushdecl_top_level (new_decl);
|
||||
DECL_VIRTUAL_P (new_decl) = 1;
|
||||
#ifndef WRITABLE_VTABLES
|
||||
/* Make them READONLY by default. (mrs) */
|
||||
TREE_READONLY (new_decl) = 1;
|
||||
#endif
|
||||
DECL_ALIGN (new_decl) = DECL_ALIGN (orig_decl);
|
||||
|
||||
#ifdef GATHER_STATISTICS
|
||||
n_vtables += 1;
|
||||
n_vtable_elems += list_length (BINFO_VIRTUALS (binfo));
|
||||
#endif
|
||||
|
||||
/* Set TREE_PUBLIC and TREE_EXTERN as appropriate. */
|
||||
import_export_vtable (new_decl, complete_type, 0);
|
||||
|
||||
return new_decl;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Access the virtual function table entry that logically
|
||||
contains BASE_FNDECL. VIRTUALS is the virtual function table's
|
||||
@ -1798,6 +1842,7 @@ finish_struct_bits (t, max_has_virtual)
|
||||
TYPE_USES_COMPLEX_INHERITANCE (variants) = TYPE_USES_COMPLEX_INHERITANCE (t);
|
||||
TYPE_VIRTUAL_P (variants) = TYPE_VIRTUAL_P (t);
|
||||
TYPE_USES_VIRTUAL_BASECLASSES (variants) = TYPE_USES_VIRTUAL_BASECLASSES (t);
|
||||
TYPE_USES_PVBASES (variants) = TYPE_USES_PVBASES (t);
|
||||
/* Copy whatever these are holding today. */
|
||||
TYPE_MIN_VALUE (variants) = TYPE_MIN_VALUE (t);
|
||||
TYPE_MAX_VALUE (variants) = TYPE_MAX_VALUE (t);
|
||||
@ -2933,6 +2978,309 @@ warn_hidden (t)
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate one vtable for use in constructors or destructors of BASE
|
||||
subobjects of COMPLETE_TYPE objects. The vtable belongs to the
|
||||
vfield of the VBASEVASE subobject of the VBASE virtual base of
|
||||
COMPLETE_TYPE (and BASE). */
|
||||
|
||||
static tree
|
||||
finish_one_ctor_vtable (complete_type, base, vbase, vbasebase)
|
||||
tree complete_type, base, vbase, vbasebase;
|
||||
{
|
||||
tree virtuals;
|
||||
tree newtable;
|
||||
tree newvirtuals;
|
||||
tree offset;
|
||||
tree newvbase = binfo_member (BINFO_TYPE (vbase),
|
||||
CLASSTYPE_VBASECLASSES (complete_type));
|
||||
|
||||
newtable = prepare_ctor_vtable (complete_type, base, vbasebase);
|
||||
newvirtuals = copy_list (BINFO_VIRTUALS (vbasebase));
|
||||
|
||||
virtuals = newvirtuals;
|
||||
/* Change the offset entry. First, delta between base an vbase. */
|
||||
offset = ssize_binop (MINUS_EXPR, BINFO_OFFSET (newvbase),
|
||||
BINFO_OFFSET (base));
|
||||
/* Add delta between vbase and vbasebase. */
|
||||
offset = ssize_binop (PLUS_EXPR, offset, BINFO_OFFSET (vbasebase));
|
||||
offset = ssize_binop (MINUS_EXPR, offset, BINFO_OFFSET (vbase));
|
||||
/* Finally, negate. */
|
||||
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 delta, newdelta, binfo_context;
|
||||
tree context = DECL_CLASS_CONTEXT (fndecl);
|
||||
|
||||
/* If this method is implemented in a base of the vbase, the
|
||||
thunk we have is correct. */
|
||||
if (DERIVED_FROM_P (context, vbase))
|
||||
continue;
|
||||
|
||||
binfo_context = binfo_value (context, base);
|
||||
if (TREE_VIA_VIRTUAL (binfo_context))
|
||||
binfo_context = binfo_member
|
||||
(context, CLASSTYPE_VBASECLASSES (complete_type));
|
||||
/* 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. BASE already has the offset to
|
||||
the complete type. */
|
||||
delta = BINFO_OFFSET (binfo_context);
|
||||
/* This is the delta from the A to the complete C. */
|
||||
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) = NULL_TREE;
|
||||
cp_finish_decl (newtable, DECL_INITIAL (newtable), NULL_TREE, 0, 0);
|
||||
DECL_CONTEXT (newtable) = complete_type;
|
||||
return newtable;
|
||||
}
|
||||
|
||||
/* Add all vtables into LIST for the VBASEBASE subobject and its bases
|
||||
of VBASE virtual BASE of COMPLETE_TYPE for use in BASE
|
||||
constructors. DO_SELF indicates whether this is the VBASEBASE that
|
||||
has 'primary' vfield. Return the new LIST. */
|
||||
|
||||
static tree
|
||||
prepend_ctor_vfields_for_vbase (complete_type, base, vbase, vbasebase,
|
||||
do_self, list)
|
||||
tree complete_type, base, vbase, vbasebase;
|
||||
int do_self;
|
||||
tree list;
|
||||
{
|
||||
int i;
|
||||
tree vtbl;
|
||||
tree bases = BINFO_BASETYPES (vbasebase);
|
||||
int vfp = CLASSTYPE_VFIELD_PARENT (BINFO_TYPE (vbasebase));
|
||||
|
||||
if (do_self && CLASSTYPE_VFIELDS (BINFO_TYPE (vbasebase)))
|
||||
{
|
||||
vtbl = finish_one_ctor_vtable (complete_type, base, vbase, vbasebase);
|
||||
vtbl = build1 (ADDR_EXPR, vtbl_ptr_type_node, vtbl);
|
||||
TREE_READONLY (vtbl) = 1;
|
||||
TREE_CONSTANT (vtbl) = 1;
|
||||
list = tree_cons (NULL_TREE, vtbl, list);
|
||||
}
|
||||
|
||||
if (!bases)
|
||||
return list;
|
||||
|
||||
for (i = 0; i < TREE_VEC_LENGTH (bases); i++)
|
||||
{
|
||||
tree vbasebase = TREE_VEC_ELT (bases, i);
|
||||
if (TREE_VIA_VIRTUAL (vbasebase))
|
||||
continue;
|
||||
list = prepend_ctor_vfields_for_vbase
|
||||
(complete_type, base, vbase, vbasebase, (i != vfp), list);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Iterate over all virtual bases of the BASE subobject of
|
||||
COMPLETE_TYPE. This list is given in VBASES. Return the list of
|
||||
vtables generated in the process. */
|
||||
|
||||
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))
|
||||
result = prepend_ctor_vfields_for_vbase
|
||||
(complete_type, base, vbases, vbases, 1, 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;}
|
||||
|
||||
BINFO is a base subject (e.g. B) of COMPLETE_TYPE. Returns the list
|
||||
of virtual tables. */
|
||||
|
||||
static tree
|
||||
finish_ctor_vtables_1 (binfo, complete_type)
|
||||
tree binfo;
|
||||
tree complete_type;
|
||||
{
|
||||
int i;
|
||||
tree binfos;
|
||||
tree result = NULL_TREE;
|
||||
|
||||
binfos = BINFO_BASETYPES (binfo);
|
||||
if (!binfos)
|
||||
return result;
|
||||
|
||||
/* Iterate over all bases (i.e. B). */
|
||||
for (i = 0; i < TREE_VEC_LENGTH (binfos); 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 (!TYPE_USES_PVBASES (BINFO_TYPE (base)))
|
||||
/* Class has no polymorphic vbases. */
|
||||
continue;
|
||||
/* Prepend vtable list for base class. */
|
||||
result = chainon (finish_ctor_vtables_1 (base, complete_type),
|
||||
result);
|
||||
/* Prepend our own vtable list. */
|
||||
result = chainon
|
||||
(finish_ctor_vtables_for_vbases (vbases, base, complete_type),
|
||||
result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Add the vtables of a virtual base BINFO in front of LIST, returning
|
||||
the new list. DO_SELF indicates whether we have to return the
|
||||
vtable of a vfield borrowed in a derived class. */
|
||||
|
||||
static tree
|
||||
prepend_vbase_vfields (binfo, do_self, list)
|
||||
tree binfo;
|
||||
int do_self;
|
||||
tree list;
|
||||
{
|
||||
int i;
|
||||
tree vtbl;
|
||||
tree bases = BINFO_BASETYPES (binfo);
|
||||
int vfp = CLASSTYPE_VFIELD_PARENT (BINFO_TYPE (binfo));
|
||||
|
||||
if (do_self && CLASSTYPE_VFIELDS (BINFO_TYPE (binfo)))
|
||||
{
|
||||
vtbl = BINFO_VTABLE (binfo);
|
||||
vtbl = build1 (ADDR_EXPR, vtbl_ptr_type_node, vtbl);
|
||||
TREE_READONLY (vtbl) = 1;
|
||||
TREE_CONSTANT (vtbl) = 1;
|
||||
list = tree_cons (NULL_TREE, vtbl, list);
|
||||
}
|
||||
|
||||
if (!bases)
|
||||
return list;
|
||||
|
||||
for (i = 0; i < TREE_VEC_LENGTH (bases); i++)
|
||||
{
|
||||
tree base = TREE_VEC_ELT (bases, i);
|
||||
if (TREE_VIA_VIRTUAL (base))
|
||||
continue;
|
||||
list = prepend_vbase_vfields (base, (i != vfp), list);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Wrapper around finish_ctor_vtables_1. Compute the vtable list for
|
||||
type T. */
|
||||
|
||||
static void
|
||||
finish_ctor_vtables (t)
|
||||
tree t;
|
||||
{
|
||||
tree veclist = NULL_TREE;
|
||||
tree decl, type;
|
||||
char *name;
|
||||
tree vbase;
|
||||
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))
|
||||
veclist = prepend_vbase_vfields (vbase, 1, veclist);
|
||||
|
||||
/* Compute the list of vtables for the bases. */
|
||||
veclist = chainon (veclist, finish_ctor_vtables_1 (TYPE_BINFO (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 = chainon (veclist,
|
||||
finish_ctor_vtables_1 (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_lang_decl (VAR_DECL, get_identifier (name), type);
|
||||
TREE_STATIC (decl) = 1;
|
||||
TREE_READONLY (decl) = 1;
|
||||
decl = pushdecl_top_level (decl);
|
||||
import_export_vtable (decl, t, 0);
|
||||
DECL_INITIAL (decl) = build_nt (CONSTRUCTOR, NULL_TREE, veclist);
|
||||
|
||||
DECL_ARTIFICIAL (decl) = 1;
|
||||
/* This tells finish_file et.al. that this is related to virtual
|
||||
tables. There is currently no way to distinguish between vtables
|
||||
and vlists, other than the name of the decl. */
|
||||
DECL_VIRTUAL_P (decl) = 1;
|
||||
|
||||
/* Output the array. */
|
||||
cp_finish_decl (decl, DECL_INITIAL (decl), NULL_TREE, 0, 0);
|
||||
|
||||
/* Set the class context after finishing, so that finish thinks this
|
||||
is an unrelated global, and then finish_vtable_vardecl knows what
|
||||
class this is related to. */
|
||||
DECL_CONTEXT (decl) = t;
|
||||
}
|
||||
|
||||
/* Check for things that are invalid. There are probably plenty of other
|
||||
things we should check for also. */
|
||||
|
||||
@ -4069,6 +4417,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_PVBASES (t))
|
||||
finish_ctor_vtables (t);
|
||||
hack_incomplete_structures (t);
|
||||
|
||||
#if 0
|
||||
|
@ -52,7 +52,7 @@ Boston, MA 02111-1307, USA. */
|
||||
4: BINFO_NEW_VTABLE_MARKED.
|
||||
TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
|
||||
or FIELD_DECL).
|
||||
5: Not used.
|
||||
5: TYPE_USES_PVBASES (in a class TYPE).
|
||||
6: Not used.
|
||||
|
||||
Usage of TYPE_LANG_FLAG_?:
|
||||
@ -503,8 +503,9 @@ extern int write_virtuals;
|
||||
|
||||
/* True for more efficient but incompatible (not fully tested)
|
||||
vtable implementation (using thunks).
|
||||
0 is old behavior; 1 is new behavior. */
|
||||
extern int flag_vtable_thunks;
|
||||
0 is old behavior; 1 is new behavior; 3 adds vlist arguments;
|
||||
2 is 3 plus backwards-compatibility to 1. */
|
||||
extern int flag_vtable_thunks, flag_vtable_thunks_compat;
|
||||
|
||||
/* INTERFACE_ONLY nonzero means that we are in an "interface"
|
||||
section of the compiler. INTERFACE_UNKNOWN nonzero means
|
||||
@ -889,6 +890,10 @@ struct lang_type
|
||||
hierarchy, then we can use more efficient search techniques. */
|
||||
#define TYPE_USES_VIRTUAL_BASECLASSES(NODE) (TREE_LANG_FLAG_3(NODE))
|
||||
|
||||
/* Nonzero means that this _CLASSTYPE uses polymorphic virtual bases.
|
||||
This flag is set only when we use vtable thunks. */
|
||||
#define TYPE_USES_PVBASES(NODE) (TREE_LANG_FLAG_5(NODE))
|
||||
|
||||
/* Vector member functions defined in this class. Each element is
|
||||
either a FUNCTION_DECL, a TEMPLATE_DECL, or an OVERLOAD. All
|
||||
functions with the same name end up in the same slot. The first
|
||||
@ -965,7 +970,7 @@ struct lang_type
|
||||
/* The number of virtual functions defined for this
|
||||
_CLASSTYPE node. */
|
||||
#define CLASSTYPE_VSIZE(NODE) (TYPE_LANG_SPECIFIC(NODE)->vsize)
|
||||
/* The virtual base classes that this type uses. */
|
||||
/* The list of binfos of virtual base classes that this type uses. */
|
||||
#define CLASSTYPE_VBASECLASSES(NODE) (TYPE_LANG_SPECIFIC(NODE)->vbases)
|
||||
/* The virtual function pointer fields that this type contains. */
|
||||
#define CLASSTYPE_VFIELDS(NODE) (TYPE_LANG_SPECIFIC(NODE)->vfields)
|
||||
@ -1165,22 +1170,21 @@ struct lang_decl_flags
|
||||
unsigned static_function : 1;
|
||||
unsigned const_memfunc : 1;
|
||||
unsigned volatile_memfunc : 1;
|
||||
|
||||
unsigned abstract_virtual : 1;
|
||||
unsigned permanent_attr : 1 ;
|
||||
unsigned constructor_for_vbase_attr : 1;
|
||||
|
||||
unsigned mutable_flag : 1;
|
||||
unsigned is_default_implementation : 1;
|
||||
unsigned saved_inline : 1;
|
||||
unsigned use_template : 2;
|
||||
|
||||
unsigned nonconverting : 1;
|
||||
unsigned declared_inline : 1;
|
||||
unsigned not_really_extern : 1;
|
||||
unsigned needs_final_overrider : 1;
|
||||
unsigned bitfield : 1;
|
||||
unsigned defined_in_class : 1;
|
||||
unsigned dummy : 4;
|
||||
unsigned constructor_for_vbase_attr : 2;
|
||||
unsigned dummy : 3;
|
||||
|
||||
tree access;
|
||||
tree context;
|
||||
@ -1226,9 +1230,35 @@ struct lang_decl
|
||||
#define DECL_CONV_FN_P(NODE) \
|
||||
(IDENTIFIER_TYPENAME_P (DECL_NAME (NODE)) && TREE_TYPE (DECL_NAME (NODE)))
|
||||
|
||||
/* For FUNCTION_DECLs: nonzero means that this function is a constructor
|
||||
#define CONSTRUCTOR_FOR_VBASE 1
|
||||
#define CONSTRUCTOR_FOR_PVBASE 2
|
||||
#define DESTRUCTOR_FOR_PVBASE 3
|
||||
|
||||
/* For FUNCTION_DECLs: nonzero means that this function is a con/destructor
|
||||
for an object with virtual baseclasses. */
|
||||
#define DECL_CONSTRUCTOR_FOR_VBASE_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.constructor_for_vbase_attr)
|
||||
#define DECL_CONSTRUCTOR_FOR_VBASE(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.constructor_for_vbase_attr)
|
||||
|
||||
/* Nonzero means that this function is a constructor for an object
|
||||
with virtual baseclasses. */
|
||||
#define DECL_CONSTRUCTOR_FOR_VBASE_P(NODE) \
|
||||
(DECL_CONSTRUCTOR_FOR_VBASE (NODE) == CONSTRUCTOR_FOR_VBASE)
|
||||
|
||||
/* Nonzero means that this function is a constructor for an object
|
||||
with virtual baseclasses which have virtual functions. */
|
||||
#define DECL_CONSTRUCTOR_FOR_PVBASE_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.constructor_for_vbase_attr == CONSTRUCTOR_FOR_PVBASE)
|
||||
|
||||
/* Nonzero means that this function is a destructor for an object
|
||||
with virtual baseclasses which have virtual functions. */
|
||||
#define DECL_DESTRUCTOR_FOR_PVBASE_P(NODE) (DECL_LANG_SPECIFIC(NODE)->decl_flags.constructor_for_vbase_attr == DESTRUCTOR_FOR_PVBASE)
|
||||
|
||||
/* Nonzero means that this function is a wrapper around a PVBASE ctor. */
|
||||
#define DECL_VLIST_CTOR_WRAPPER_P(NODE) \
|
||||
(DECL_CONSTRUCTOR_FOR_VBASE_P (NODE) && DECL_VLIST_CTOR_WRAPPED (NODE)\
|
||||
&& TREE_CODE (DECL_VLIST_CTOR_WRAPPED (NODE)) == FUNCTION_DECL \
|
||||
&& DECL_CONSTRUCTOR_FOR_PVBASE_P (DECL_VLIST_CTOR_WRAPPED (NODE)))
|
||||
|
||||
/* Refers to original function that NODE wraps. */
|
||||
#define DECL_VLIST_CTOR_WRAPPED(NODE) DECL_MEMFUNC_POINTER_TO (NODE)
|
||||
|
||||
/* Non-zero for a FUNCTION_DECL that declares a type-info function. */
|
||||
#define DECL_TINFO_FN_P(NODE) \
|
||||
@ -2168,6 +2198,7 @@ extern tree delta2_identifier;
|
||||
extern tree pfn_or_delta2_identifier;
|
||||
extern tree tag_identifier;
|
||||
extern tree vt_off_identifier;
|
||||
extern tree in_charge_identifier;
|
||||
|
||||
/* A node that is a list (length 1) of error_mark_nodes. */
|
||||
extern tree error_mark_list;
|
||||
@ -2177,6 +2208,8 @@ extern tree class_type_node, record_type_node, union_type_node, enum_type_node;
|
||||
extern tree unknown_type_node;
|
||||
extern tree opaque_type_node, signature_type_node;
|
||||
|
||||
extern tree vlist_identifier, vlist_type_node, vlist_zero_node;
|
||||
|
||||
/* Node for "pointer to (virtual) function".
|
||||
This may be distinct from ptr_type_node so gdb can distinguish them. */
|
||||
#define vfunc_ptr_type_node \
|
||||
@ -2292,6 +2325,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 "__vc$"
|
||||
#define VLIST_NAME_FORMAT "__vl$%s"
|
||||
#define VFIELD_BASE "$vf"
|
||||
#define VFIELD_NAME "_vptr$"
|
||||
#define VFIELD_NAME_FORMAT "_vptr$%s"
|
||||
@ -2314,6 +2349,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 "__vc."
|
||||
#define VLIST_NAME_FORMAT "__vl.%s"
|
||||
#define VFIELD_BASE ".vf"
|
||||
#define VFIELD_NAME "_vptr."
|
||||
#define VFIELD_NAME_FORMAT "_vptr.%s"
|
||||
@ -2346,6 +2383,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 "__vc_"
|
||||
#define VLIST_NAME_FORMAT "__vl_%s"
|
||||
#define VFIELD_BASE "__vfb"
|
||||
#define VFIELD_NAME "__vptr_"
|
||||
#define VFIELD_NAME_P(ID_NODE) \
|
||||
@ -2379,6 +2418,9 @@ extern int current_function_parms_stored;
|
||||
#define DTOR_NAME "__dt"
|
||||
|
||||
#define IN_CHARGE_NAME "__in_chrg"
|
||||
#define VLIST_NAME "__vlist"
|
||||
#define VLIST1_NAME "__vlist1"
|
||||
#define VLIST_TYPE_NAME "6_Vlist"
|
||||
|
||||
#define VTBL_PTR_TYPE "__vtbl_ptr_type"
|
||||
#define VTABLE_DELTA_NAME "__delta"
|
||||
@ -2556,6 +2598,8 @@ extern tree current_class_name; /* IDENTIFIER_NODE: name of current class */
|
||||
as well as the space of member functions.
|
||||
LOOKUP_HAS_IN_CHARGE means that the "in charge" variable is already
|
||||
in the parameter list.
|
||||
LOOKUP_HAS_VLIST means that the "vlist" variable is already in
|
||||
the parameter list.
|
||||
LOOKUP_ONLYCONVERTING means that non-conversion constructors are not tried.
|
||||
DIRECT_BIND means that if a temporary is created, it should be created so
|
||||
that it lives as long as the current variable bindings; otherwise it
|
||||
@ -2594,6 +2638,7 @@ extern tree current_class_name; /* IDENTIFIER_NODE: name of current class */
|
||||
#define LOOKUP_PREFER_NAMESPACES (4096)
|
||||
#define LOOKUP_PREFER_BOTH (6144)
|
||||
#define LOOKUP_TEMPLATES_EXPECTED (8192)
|
||||
#define LOOKUP_HAS_VLIST (16384)
|
||||
|
||||
#define LOOKUP_NAMESPACES_ONLY(f) \
|
||||
(((f) & LOOKUP_PREFER_NAMESPACES) && !((f) & LOOKUP_PREFER_TYPES))
|
||||
@ -3051,6 +3096,8 @@ extern tree build_x_delete PROTO((tree, int, tree));
|
||||
extern tree build_delete PROTO((tree, tree, tree, int, int));
|
||||
extern tree build_vbase_delete PROTO((tree, tree));
|
||||
extern tree build_vec_delete PROTO((tree, tree, tree, tree, int));
|
||||
extern tree build_base_dtor_call PROTO((tree, tree, tree));
|
||||
extern void init_vlist PROTO((tree));
|
||||
|
||||
/* in input.c */
|
||||
|
||||
@ -3117,13 +3164,15 @@ extern tree build_decl_overload_real PROTO((tree, tree, tree, tree,
|
||||
extern void set_mangled_name_for_decl PROTO((tree));
|
||||
extern tree build_typename_overload PROTO((tree));
|
||||
extern tree build_overload_with_type PROTO((tree, tree));
|
||||
extern tree build_destructor_name PROTO((tree));
|
||||
extern tree build_destructor_name PROTO((tree, int));
|
||||
extern tree build_opfncall PROTO((enum tree_code, int, tree, tree, tree));
|
||||
extern tree hack_identifier PROTO((tree, tree));
|
||||
extern tree make_thunk PROTO((tree, int));
|
||||
extern void emit_thunk PROTO((tree));
|
||||
extern void synthesize_method PROTO((tree));
|
||||
extern tree get_id_2 PROTO((char *, tree));
|
||||
extern tree get_vlist_vtable_id PROTO((tree, tree));
|
||||
|
||||
|
||||
/* in pt.c */
|
||||
extern void check_template_shadow PROTO ((tree));
|
||||
@ -3200,6 +3249,7 @@ extern tree get_tinfo_fn_dynamic PROTO((tree));
|
||||
extern tree build_typeid PROTO((tree));
|
||||
extern tree build_x_typeid PROTO((tree));
|
||||
extern tree get_tinfo_fn PROTO((tree));
|
||||
extern tree get_tinfo_fn_unused PROTO((tree));
|
||||
extern tree get_typeid PROTO((tree));
|
||||
extern tree get_typeid_1 PROTO((tree));
|
||||
extern tree build_dynamic_cast PROTO((tree, tree));
|
||||
|
@ -188,6 +188,8 @@ static tree record_builtin_java_type PROTO((const char *, int));
|
||||
static const char *tag_name PROTO((enum tag_types code));
|
||||
static void find_class_binding_level PROTO((void));
|
||||
static struct binding_level *innermost_nonclass_level PROTO((void));
|
||||
static void finish_dtor PROTO((void));
|
||||
static void finish_ctor PROTO((int));
|
||||
static tree poplevel_class PROTO((void));
|
||||
static void warn_about_implicit_typename_lookup PROTO((tree, tree));
|
||||
static int walk_namespaces_r PROTO((tree, walk_namespaces_fn, void *));
|
||||
@ -335,6 +337,15 @@ tree __ptr_desc_array_type, __attr_dec_array_type, __func_desc_array_type;
|
||||
tree __ptmf_desc_array_type, __ptmd_desc_array_type;
|
||||
#endif
|
||||
|
||||
/* This is the identifier __vlist. */
|
||||
tree vlist_identifier;
|
||||
|
||||
/* This is the type _Vlist = vtable_entry_type**. */
|
||||
tree vlist_type_node;
|
||||
|
||||
/* A null pointer of type _Vlist. */
|
||||
tree vlist_zero_node;
|
||||
|
||||
/* Indicates that there is a type value in some namespace, although
|
||||
that is not necessarily in scope at the moment. */
|
||||
|
||||
@ -6283,6 +6294,7 @@ init_decl_processing ()
|
||||
|
||||
this_identifier = get_identifier (THIS_NAME);
|
||||
in_charge_identifier = get_identifier (IN_CHARGE_NAME);
|
||||
vlist_identifier = get_identifier (VLIST_NAME);
|
||||
ctor_identifier = get_identifier (CTOR_NAME);
|
||||
dtor_identifier = get_identifier (DTOR_NAME);
|
||||
pfn_identifier = get_identifier (VTABLE_PFN_NAME);
|
||||
@ -6510,6 +6522,7 @@ init_decl_processing ()
|
||||
#if 0
|
||||
record_builtin_type (RID_MAX, NULL_PTR, ptr_type_node);
|
||||
#endif
|
||||
|
||||
endlink = void_list_node;
|
||||
int_endlink = tree_cons (NULL_TREE, integer_type_node, endlink);
|
||||
double_endlink = tree_cons (NULL_TREE, double_type_node, endlink);
|
||||
@ -6849,6 +6862,16 @@ init_decl_processing ()
|
||||
layout_type (vtbl_ptr_type_node);
|
||||
record_builtin_type (RID_MAX, NULL_PTR, vtbl_ptr_type_node);
|
||||
|
||||
if (flag_vtable_thunks)
|
||||
{
|
||||
/* We need vlists only when using thunks; otherwise leave them
|
||||
as NULL_TREE. That way, it doesn't get into the way of the
|
||||
mangling. */
|
||||
vlist_type_node = build_pointer_type (vtbl_ptr_type_node);
|
||||
vlist_zero_node = build_int_2 (0, 0);
|
||||
TREE_TYPE (vlist_zero_node) = vlist_type_node;
|
||||
}
|
||||
|
||||
/* Simplify life by making a "sigtable_entry_type". Give its
|
||||
fields names so that the debugger can use them. */
|
||||
|
||||
@ -11386,6 +11409,10 @@ grokdeclarator (declarator, declspecs, decl_context, initialized, attrlist)
|
||||
if (TYPE_USES_VIRTUAL_BASECLASSES (DECL_CONTEXT (decl)))
|
||||
arg_types = TREE_CHAIN (arg_types);
|
||||
|
||||
/* And the `vlist' argument. */
|
||||
if (TYPE_USES_PVBASES (DECL_CONTEXT (decl)))
|
||||
arg_types = TREE_CHAIN (arg_types);
|
||||
|
||||
if (arg_types == void_list_node
|
||||
|| (arg_types
|
||||
&& TREE_CHAIN (arg_types)
|
||||
@ -12080,6 +12107,9 @@ replace_defarg (arg, init)
|
||||
TREE_PURPOSE (arg) = init;
|
||||
}
|
||||
|
||||
/* Return 1 if D copies its arguments. This is used to test for copy
|
||||
constructors and copy assignment operators. */
|
||||
|
||||
int
|
||||
copy_args_p (d)
|
||||
tree d;
|
||||
@ -12087,7 +12117,12 @@ copy_args_p (d)
|
||||
tree t = FUNCTION_ARG_CHAIN (d);
|
||||
if (DECL_CONSTRUCTOR_P (d)
|
||||
&& TYPE_USES_VIRTUAL_BASECLASSES (DECL_CONTEXT (d)))
|
||||
t = TREE_CHAIN (t);
|
||||
{
|
||||
t = TREE_CHAIN (t);
|
||||
if (TYPE_USES_PVBASES (DECL_CONTEXT (d)))
|
||||
t = TREE_CHAIN (t);
|
||||
}
|
||||
|
||||
if (t && TREE_CODE (TREE_VALUE (t)) == REFERENCE_TYPE
|
||||
&& (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (t)))
|
||||
== DECL_CLASS_CONTEXT (d))
|
||||
@ -12117,7 +12152,8 @@ grok_ctor_properties (ctype, decl)
|
||||
added to any ctor so we can tell if the class has been initialized
|
||||
yet. This could screw things up in this function, so we deliberately
|
||||
ignore the leading int if we're in that situation. */
|
||||
if (TYPE_USES_VIRTUAL_BASECLASSES (ctype))
|
||||
if (TYPE_USES_VIRTUAL_BASECLASSES (ctype)
|
||||
&& !CLASSTYPE_IS_TEMPLATE (ctype))
|
||||
{
|
||||
my_friendly_assert (parmtypes
|
||||
&& TREE_VALUE (parmtypes) == integer_type_node,
|
||||
@ -12126,6 +12162,15 @@ grok_ctor_properties (ctype, decl)
|
||||
parmtype = TREE_VALUE (parmtypes);
|
||||
}
|
||||
|
||||
if (TYPE_USES_PVBASES (ctype))
|
||||
{
|
||||
my_friendly_assert (parmtypes
|
||||
&& TREE_VALUE (parmtypes) == vlist_type_node,
|
||||
980529);
|
||||
parmtypes = TREE_CHAIN (parmtypes);
|
||||
parmtype = TREE_VALUE (parmtypes);
|
||||
}
|
||||
|
||||
/* [class.copy]
|
||||
|
||||
A non-template constructor for class X is a copy constructor if
|
||||
@ -12923,6 +12968,16 @@ xref_basetypes (code_type_node, name, ref, binfo)
|
||||
{
|
||||
TYPE_USES_VIRTUAL_BASECLASSES (ref) = 1;
|
||||
TYPE_USES_COMPLEX_INHERITANCE (ref) = 1;
|
||||
/* The PVBASES flag is never set for templates; we know
|
||||
only for instantiations whether the virtual bases are
|
||||
polymorphic. */
|
||||
if (flag_vtable_thunks >= 2 && !CLASSTYPE_IS_TEMPLATE (ref))
|
||||
{
|
||||
if (via_virtual && TYPE_VIRTUAL_P (basetype))
|
||||
TYPE_USES_PVBASES (ref) = 1;
|
||||
else if (TYPE_USES_PVBASES (basetype))
|
||||
TYPE_USES_PVBASES (ref) = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (CLASS_TYPE_P (basetype))
|
||||
@ -13928,6 +13983,313 @@ store_return_init (return_id, init)
|
||||
}
|
||||
|
||||
|
||||
/* Emit implicit code for a destructor. This is a subroutine of
|
||||
finish_function. */
|
||||
|
||||
static void
|
||||
finish_dtor ()
|
||||
{
|
||||
tree binfo = TYPE_BINFO (current_class_type);
|
||||
tree cond = integer_one_node;
|
||||
tree exprstmt;
|
||||
tree in_charge_node = lookup_name (in_charge_identifier, 0);
|
||||
tree virtual_size;
|
||||
int ok_to_optimize_dtor = 0;
|
||||
int empty_dtor = get_last_insn () == last_dtor_insn;
|
||||
rtx insns, last_parm_insn;
|
||||
|
||||
if (current_function_assigns_this)
|
||||
cond = build (NE_EXPR, boolean_type_node,
|
||||
current_class_ptr, integer_zero_node);
|
||||
else
|
||||
{
|
||||
int n_baseclasses = CLASSTYPE_N_BASECLASSES (current_class_type);
|
||||
|
||||
/* If this destructor is empty, then we don't need to check
|
||||
whether `this' is NULL in some cases. */
|
||||
if ((flag_this_is_variable & 1) == 0)
|
||||
ok_to_optimize_dtor = 1;
|
||||
else if (empty_dtor)
|
||||
ok_to_optimize_dtor
|
||||
= (n_baseclasses == 0
|
||||
|| (n_baseclasses == 1
|
||||
&& TYPE_HAS_DESTRUCTOR (TYPE_BINFO_BASETYPE (current_class_type, 0))));
|
||||
}
|
||||
|
||||
/* If this has a vlist1 parameter, allocate the corresponding vlist
|
||||
parameter. */
|
||||
if (DECL_DESTRUCTOR_FOR_PVBASE_P (current_function_decl))
|
||||
{
|
||||
/* _Vlist __vlist; */
|
||||
tree vlist;
|
||||
|
||||
mark_all_temps_used();
|
||||
vlist = pushdecl (build_decl (VAR_DECL, vlist_identifier,
|
||||
vlist_type_node));
|
||||
TREE_USED (vlist) = 1;
|
||||
DECL_ARTIFICIAL (vlist) = 1;
|
||||
expand_decl (vlist);
|
||||
expand_decl_init (vlist);
|
||||
}
|
||||
|
||||
/* These initializations might go inline. Protect
|
||||
the binding level of the parms. */
|
||||
pushlevel (0);
|
||||
expand_start_bindings (0);
|
||||
|
||||
if (current_function_assigns_this)
|
||||
{
|
||||
current_function_assigns_this = 0;
|
||||
current_function_just_assigned_this = 0;
|
||||
}
|
||||
|
||||
/* Generate the code to call destructor on base class.
|
||||
If this destructor belongs to a class with virtual
|
||||
functions, then set the virtual function table
|
||||
pointer to represent the type of our base class. */
|
||||
|
||||
/* This side-effect makes call to `build_delete' generate the
|
||||
code we have to have at the end of this destructor.
|
||||
`build_delete' will set the flag again. */
|
||||
TYPE_HAS_DESTRUCTOR (current_class_type) = 0;
|
||||
|
||||
/* These are two cases where we cannot delegate deletion. */
|
||||
if (TYPE_USES_VIRTUAL_BASECLASSES (current_class_type)
|
||||
|| TYPE_GETS_REG_DELETE (current_class_type))
|
||||
exprstmt = build_delete
|
||||
(current_class_type, current_class_ref, integer_zero_node,
|
||||
LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR|LOOKUP_NORMAL, 0);
|
||||
else
|
||||
exprstmt = build_delete
|
||||
(current_class_type, current_class_ref, in_charge_node,
|
||||
LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR|LOOKUP_NORMAL, 0);
|
||||
|
||||
/* If we did not assign to this, then `this' is non-zero at
|
||||
the end of a destructor. As a special optimization, don't
|
||||
emit test if this is an empty destructor. If it does nothing,
|
||||
it does nothing. If it calls a base destructor, the base
|
||||
destructor will perform the test. */
|
||||
|
||||
if (exprstmt != error_mark_node
|
||||
&& (TREE_CODE (exprstmt) != NOP_EXPR
|
||||
|| TREE_OPERAND (exprstmt, 0) != integer_zero_node
|
||||
|| TYPE_USES_VIRTUAL_BASECLASSES (current_class_type)))
|
||||
{
|
||||
expand_label (dtor_label);
|
||||
if (cond != integer_one_node)
|
||||
expand_start_cond (cond, 0);
|
||||
if (exprstmt != void_zero_node)
|
||||
/* Don't call `expand_expr_stmt' if we're not going to do
|
||||
anything, since -Wall will give a diagnostic. */
|
||||
expand_expr_stmt (exprstmt);
|
||||
|
||||
/* Run destructor on all virtual baseclasses. */
|
||||
if (TYPE_USES_VIRTUAL_BASECLASSES (current_class_type))
|
||||
{
|
||||
tree vbases = nreverse
|
||||
(copy_list (CLASSTYPE_VBASECLASSES (current_class_type)));
|
||||
expand_start_cond (build (BIT_AND_EXPR, integer_type_node,
|
||||
in_charge_node, integer_two_node), 0);
|
||||
while (vbases)
|
||||
{
|
||||
if (TYPE_NEEDS_DESTRUCTOR (BINFO_TYPE (vbases)))
|
||||
{
|
||||
tree vb = get_vbase
|
||||
(BINFO_TYPE (vbases),
|
||||
TYPE_BINFO (current_class_type));
|
||||
|
||||
expand_expr_stmt
|
||||
(build_base_dtor_call (current_class_ref,
|
||||
vb, integer_zero_node));
|
||||
}
|
||||
vbases = TREE_CHAIN (vbases);
|
||||
}
|
||||
expand_end_cond ();
|
||||
}
|
||||
|
||||
do_pending_stack_adjust ();
|
||||
if (cond != integer_one_node)
|
||||
expand_end_cond ();
|
||||
}
|
||||
|
||||
virtual_size = c_sizeof (current_class_type);
|
||||
|
||||
/* At the end, call delete if that's what's requested. */
|
||||
|
||||
/* FDIS sez: At the point of definition of a virtual destructor
|
||||
(including an implicit definition), non-placement operator
|
||||
delete shall be looked up in the scope of the destructor's
|
||||
class and if found shall be accessible and unambiguous.
|
||||
|
||||
This is somewhat unclear, but I take it to mean that if the
|
||||
class only defines placement deletes we don't do anything here.
|
||||
So we pass LOOKUP_SPECULATIVELY; delete_sanity will complain
|
||||
for us if they ever try to delete one of these. */
|
||||
|
||||
if (TYPE_GETS_REG_DELETE (current_class_type)
|
||||
|| TYPE_USES_VIRTUAL_BASECLASSES (current_class_type))
|
||||
exprstmt = build_op_delete_call
|
||||
(DELETE_EXPR, current_class_ptr, virtual_size,
|
||||
LOOKUP_NORMAL | LOOKUP_SPECULATIVELY, NULL_TREE);
|
||||
else
|
||||
exprstmt = NULL_TREE;
|
||||
|
||||
if (exprstmt)
|
||||
{
|
||||
cond = build (BIT_AND_EXPR, integer_type_node,
|
||||
in_charge_node, integer_one_node);
|
||||
expand_start_cond (cond, 0);
|
||||
expand_expr_stmt (exprstmt);
|
||||
expand_end_cond ();
|
||||
}
|
||||
|
||||
/* End of destructor. */
|
||||
expand_end_bindings (NULL_TREE, getdecls () != NULL_TREE, 0);
|
||||
poplevel (getdecls () != NULL_TREE, 0, 0);
|
||||
|
||||
/* Back to the top of destructor. */
|
||||
/* Don't execute destructor code if `this' is NULL. */
|
||||
|
||||
start_sequence ();
|
||||
|
||||
/* If we need thunk-style vlists, initialize them if the caller did
|
||||
not pass them. This requires a new temporary. The generated code
|
||||
looks like
|
||||
if (!(__in_charge & 4))
|
||||
__vlist = __vl.<type> + sizeof(__vl.<type>);
|
||||
else
|
||||
__vlist = __vlist1;
|
||||
*/
|
||||
if (TYPE_USES_PVBASES (current_class_type))
|
||||
{
|
||||
tree vlist = lookup_name (vlist_identifier, 0);
|
||||
tree vlist1 = lookup_name (get_identifier (VLIST1_NAME), 0);
|
||||
cond = build (BIT_AND_EXPR, integer_type_node,
|
||||
in_charge_node, build_int_2 (4, 0));
|
||||
cond = build1 (TRUTH_NOT_EXPR, boolean_type_node, cond);
|
||||
expand_start_cond (cond, 0);
|
||||
init_vlist (current_class_type);
|
||||
expand_start_else ();
|
||||
expand_expr_stmt (build_modify_expr (vlist, NOP_EXPR, vlist1));
|
||||
expand_end_cond ();
|
||||
}
|
||||
|
||||
/* If the dtor is empty, and we know there is not possible way we
|
||||
could use any vtable entries, before they are possibly set by
|
||||
a base class dtor, we don't have to setup the vtables, as we
|
||||
know that any base class dtoring will set up any vtables it
|
||||
needs. We avoid MI, because one base class dtor can do a
|
||||
virtual dispatch to an overridden function that would need to
|
||||
have a non-related vtable set up, we cannot avoid setting up
|
||||
vtables in that case. We could change this to see if there is
|
||||
just one vtable. */
|
||||
if (! empty_dtor || TYPE_USES_COMPLEX_INHERITANCE (current_class_type))
|
||||
{
|
||||
/* Make all virtual function table pointers in non-virtual base
|
||||
classes point to CURRENT_CLASS_TYPE's virtual function
|
||||
tables. */
|
||||
expand_direct_vtbls_init (binfo, binfo, 1, 0, current_class_ptr);
|
||||
|
||||
if (TYPE_USES_VIRTUAL_BASECLASSES (current_class_type))
|
||||
expand_indirect_vtbls_init (binfo, current_class_ref, current_class_ptr);
|
||||
}
|
||||
|
||||
if (! ok_to_optimize_dtor)
|
||||
{
|
||||
cond = build_binary_op (NE_EXPR,
|
||||
current_class_ptr, integer_zero_node);
|
||||
expand_start_cond (cond, 0);
|
||||
}
|
||||
|
||||
insns = get_insns ();
|
||||
end_sequence ();
|
||||
|
||||
last_parm_insn = get_first_nonparm_insn ();
|
||||
if (last_parm_insn == NULL_RTX)
|
||||
last_parm_insn = get_last_insn ();
|
||||
else
|
||||
last_parm_insn = previous_insn (last_parm_insn);
|
||||
|
||||
emit_insns_after (insns, last_parm_insn);
|
||||
|
||||
if (! ok_to_optimize_dtor)
|
||||
expand_end_cond ();
|
||||
}
|
||||
|
||||
/* Emit implicit code for a constructor. This is a subroutine of
|
||||
finish_function. CALL_POPLEVEL is the same variable in
|
||||
finish_function. */
|
||||
|
||||
static void
|
||||
finish_ctor (call_poplevel)
|
||||
int call_poplevel;
|
||||
{
|
||||
register tree fndecl = current_function_decl;
|
||||
tree cond = NULL_TREE, thenclause = NULL_TREE;
|
||||
rtx insns;
|
||||
tree decls;
|
||||
|
||||
/* Allow constructor for a type to get a new instance of the object
|
||||
using `build_new'. */
|
||||
tree abstract_virtuals = CLASSTYPE_ABSTRACT_VIRTUALS (current_class_type);
|
||||
CLASSTYPE_ABSTRACT_VIRTUALS (current_class_type) = NULL_TREE;
|
||||
|
||||
if (flag_this_is_variable > 0)
|
||||
{
|
||||
cond = build_binary_op (EQ_EXPR, current_class_ptr, integer_zero_node);
|
||||
thenclause =
|
||||
build_modify_expr (current_class_ptr, NOP_EXPR,
|
||||
build_new (NULL_TREE, current_class_type,
|
||||
void_type_node, 0));
|
||||
}
|
||||
|
||||
CLASSTYPE_ABSTRACT_VIRTUALS (current_class_type) = abstract_virtuals;
|
||||
|
||||
start_sequence ();
|
||||
|
||||
if (flag_this_is_variable > 0)
|
||||
{
|
||||
expand_start_cond (cond, 0);
|
||||
expand_expr_stmt (thenclause);
|
||||
expand_end_cond ();
|
||||
}
|
||||
|
||||
/* Emit insns from `emit_base_init' which sets up virtual
|
||||
function table pointer(s). */
|
||||
if (base_init_expr)
|
||||
{
|
||||
expand_expr_stmt (base_init_expr);
|
||||
base_init_expr = NULL_TREE;
|
||||
}
|
||||
|
||||
insns = get_insns ();
|
||||
end_sequence ();
|
||||
|
||||
/* This is where the body of the constructor begins. */
|
||||
|
||||
emit_insns_after (insns, last_parm_cleanup_insn);
|
||||
|
||||
end_protect_partials ();
|
||||
|
||||
/* This is where the body of the constructor ends. */
|
||||
expand_label (ctor_label);
|
||||
ctor_label = NULL_TREE;
|
||||
|
||||
if (call_poplevel)
|
||||
{
|
||||
decls = getdecls ();
|
||||
expand_end_bindings (decls, decls != NULL_TREE, 0);
|
||||
poplevel (decls != NULL_TREE, 1, 0);
|
||||
}
|
||||
|
||||
/* c_expand_return knows to return 'this' from a constructor. */
|
||||
c_expand_return (NULL_TREE);
|
||||
|
||||
current_function_assigns_this = 0;
|
||||
current_function_just_assigned_this = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Finish up a function declaration and compile that function
|
||||
all the way to assembler language output. The free the storage
|
||||
for the function definition.
|
||||
@ -13956,7 +14318,6 @@ finish_function (lineno, flags, nested)
|
||||
{
|
||||
register tree fndecl = current_function_decl;
|
||||
tree fntype, ctype = NULL_TREE;
|
||||
rtx last_parm_insn, insns;
|
||||
/* Label to use if this function is supposed to return a value. */
|
||||
tree no_return_label = NULL_TREE;
|
||||
tree decls = NULL_TREE;
|
||||
@ -14018,191 +14379,7 @@ finish_function (lineno, flags, nested)
|
||||
do_pending_stack_adjust ();
|
||||
|
||||
if (dtor_label)
|
||||
{
|
||||
tree binfo = TYPE_BINFO (current_class_type);
|
||||
tree cond = integer_one_node;
|
||||
tree exprstmt;
|
||||
tree in_charge_node = lookup_name (in_charge_identifier, 0);
|
||||
tree virtual_size;
|
||||
int ok_to_optimize_dtor = 0;
|
||||
int empty_dtor = get_last_insn () == last_dtor_insn;
|
||||
|
||||
if (current_function_assigns_this)
|
||||
cond = build (NE_EXPR, boolean_type_node,
|
||||
current_class_ptr, integer_zero_node);
|
||||
else
|
||||
{
|
||||
int n_baseclasses = CLASSTYPE_N_BASECLASSES (current_class_type);
|
||||
|
||||
/* If this destructor is empty, then we don't need to check
|
||||
whether `this' is NULL in some cases. */
|
||||
if ((flag_this_is_variable & 1) == 0)
|
||||
ok_to_optimize_dtor = 1;
|
||||
else if (empty_dtor)
|
||||
ok_to_optimize_dtor
|
||||
= (n_baseclasses == 0
|
||||
|| (n_baseclasses == 1
|
||||
&& TYPE_HAS_DESTRUCTOR (TYPE_BINFO_BASETYPE (current_class_type, 0))));
|
||||
}
|
||||
|
||||
/* These initializations might go inline. Protect
|
||||
the binding level of the parms. */
|
||||
pushlevel (0);
|
||||
expand_start_bindings (0);
|
||||
|
||||
if (current_function_assigns_this)
|
||||
{
|
||||
current_function_assigns_this = 0;
|
||||
current_function_just_assigned_this = 0;
|
||||
}
|
||||
|
||||
/* Generate the code to call destructor on base class.
|
||||
If this destructor belongs to a class with virtual
|
||||
functions, then set the virtual function table
|
||||
pointer to represent the type of our base class. */
|
||||
|
||||
/* This side-effect makes call to `build_delete' generate the
|
||||
code we have to have at the end of this destructor.
|
||||
`build_delete' will set the flag again. */
|
||||
TYPE_HAS_DESTRUCTOR (current_class_type) = 0;
|
||||
|
||||
/* These are two cases where we cannot delegate deletion. */
|
||||
if (TYPE_USES_VIRTUAL_BASECLASSES (current_class_type)
|
||||
|| TYPE_GETS_REG_DELETE (current_class_type))
|
||||
exprstmt = build_delete (current_class_type, current_class_ref, integer_zero_node,
|
||||
LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR|LOOKUP_NORMAL, 0);
|
||||
else
|
||||
exprstmt = build_delete (current_class_type, current_class_ref, in_charge_node,
|
||||
LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR|LOOKUP_NORMAL, 0);
|
||||
|
||||
/* If we did not assign to this, then `this' is non-zero at
|
||||
the end of a destructor. As a special optimization, don't
|
||||
emit test if this is an empty destructor. If it does nothing,
|
||||
it does nothing. If it calls a base destructor, the base
|
||||
destructor will perform the test. */
|
||||
|
||||
if (exprstmt != error_mark_node
|
||||
&& (TREE_CODE (exprstmt) != NOP_EXPR
|
||||
|| TREE_OPERAND (exprstmt, 0) != integer_zero_node
|
||||
|| TYPE_USES_VIRTUAL_BASECLASSES (current_class_type)))
|
||||
{
|
||||
expand_label (dtor_label);
|
||||
if (cond != integer_one_node)
|
||||
expand_start_cond (cond, 0);
|
||||
if (exprstmt != void_zero_node)
|
||||
/* Don't call `expand_expr_stmt' if we're not going to do
|
||||
anything, since -Wall will give a diagnostic. */
|
||||
expand_expr_stmt (exprstmt);
|
||||
|
||||
/* Run destructor on all virtual baseclasses. */
|
||||
if (TYPE_USES_VIRTUAL_BASECLASSES (current_class_type))
|
||||
{
|
||||
tree vbases = nreverse (copy_list (CLASSTYPE_VBASECLASSES (current_class_type)));
|
||||
expand_start_cond (build (BIT_AND_EXPR, integer_type_node,
|
||||
in_charge_node, integer_two_node), 0);
|
||||
while (vbases)
|
||||
{
|
||||
if (TYPE_NEEDS_DESTRUCTOR (BINFO_TYPE (vbases)))
|
||||
{
|
||||
tree vb = get_vbase
|
||||
(BINFO_TYPE (vbases),
|
||||
TYPE_BINFO (current_class_type));
|
||||
expand_expr_stmt
|
||||
(build_scoped_method_call
|
||||
(current_class_ref, vb, dtor_identifier,
|
||||
build_expr_list (NULL_TREE, integer_zero_node)));
|
||||
}
|
||||
vbases = TREE_CHAIN (vbases);
|
||||
}
|
||||
expand_end_cond ();
|
||||
}
|
||||
|
||||
do_pending_stack_adjust ();
|
||||
if (cond != integer_one_node)
|
||||
expand_end_cond ();
|
||||
}
|
||||
|
||||
virtual_size = c_sizeof (current_class_type);
|
||||
|
||||
/* At the end, call delete if that's what's requested. */
|
||||
|
||||
/* FDIS sez: At the point of definition of a virtual destructor
|
||||
(including an implicit definition), non-placement operator
|
||||
delete shall be looked up in the scope of the destructor's
|
||||
class and if found shall be accessible and unambiguous.
|
||||
|
||||
This is somewhat unclear, but I take it to mean that if the
|
||||
class only defines placement deletes we don't do anything here.
|
||||
So we pass LOOKUP_SPECULATIVELY; delete_sanity will complain
|
||||
for us if they ever try to delete one of these. */
|
||||
|
||||
if (TYPE_GETS_REG_DELETE (current_class_type)
|
||||
|| TYPE_USES_VIRTUAL_BASECLASSES (current_class_type))
|
||||
exprstmt = build_op_delete_call
|
||||
(DELETE_EXPR, current_class_ptr, virtual_size,
|
||||
LOOKUP_NORMAL | LOOKUP_SPECULATIVELY, NULL_TREE);
|
||||
else
|
||||
exprstmt = NULL_TREE;
|
||||
|
||||
if (exprstmt)
|
||||
{
|
||||
cond = build (BIT_AND_EXPR, integer_type_node,
|
||||
in_charge_node, integer_one_node);
|
||||
expand_start_cond (cond, 0);
|
||||
expand_expr_stmt (exprstmt);
|
||||
expand_end_cond ();
|
||||
}
|
||||
|
||||
/* End of destructor. */
|
||||
expand_end_bindings (NULL_TREE, getdecls () != NULL_TREE, 0);
|
||||
poplevel (getdecls () != NULL_TREE, 0, 0);
|
||||
|
||||
/* Back to the top of destructor. */
|
||||
/* Don't execute destructor code if `this' is NULL. */
|
||||
|
||||
start_sequence ();
|
||||
|
||||
/* If the dtor is empty, and we know there is not possible way we
|
||||
could use any vtable entries, before they are possibly set by
|
||||
a base class dtor, we don't have to setup the vtables, as we
|
||||
know that any base class dtoring will set up any vtables it
|
||||
needs. We avoid MI, because one base class dtor can do a
|
||||
virtual dispatch to an overridden function that would need to
|
||||
have a non-related vtable set up, we cannot avoid setting up
|
||||
vtables in that case. We could change this to see if there is
|
||||
just one vtable. */
|
||||
if (! empty_dtor || TYPE_USES_COMPLEX_INHERITANCE (current_class_type))
|
||||
{
|
||||
/* Make all virtual function table pointers in non-virtual base
|
||||
classes point to CURRENT_CLASS_TYPE's virtual function
|
||||
tables. */
|
||||
expand_direct_vtbls_init (binfo, binfo, 1, 0, current_class_ptr);
|
||||
|
||||
if (TYPE_USES_VIRTUAL_BASECLASSES (current_class_type))
|
||||
expand_indirect_vtbls_init (binfo, current_class_ref, current_class_ptr);
|
||||
}
|
||||
|
||||
if (! ok_to_optimize_dtor)
|
||||
{
|
||||
cond = build_binary_op (NE_EXPR,
|
||||
current_class_ptr, integer_zero_node);
|
||||
expand_start_cond (cond, 0);
|
||||
}
|
||||
|
||||
insns = get_insns ();
|
||||
end_sequence ();
|
||||
|
||||
last_parm_insn = get_first_nonparm_insn ();
|
||||
if (last_parm_insn == NULL_RTX)
|
||||
last_parm_insn = get_last_insn ();
|
||||
else
|
||||
last_parm_insn = previous_insn (last_parm_insn);
|
||||
|
||||
emit_insns_after (insns, last_parm_insn);
|
||||
|
||||
if (! ok_to_optimize_dtor)
|
||||
expand_end_cond ();
|
||||
}
|
||||
finish_dtor ();
|
||||
else if (current_function_assigns_this)
|
||||
{
|
||||
/* Does not need to call emit_base_init, because
|
||||
@ -14232,67 +14409,9 @@ finish_function (lineno, flags, nested)
|
||||
current_function_just_assigned_this = 0;
|
||||
base_init_expr = NULL_TREE;
|
||||
}
|
||||
else if (DECL_CONSTRUCTOR_P (fndecl))
|
||||
{
|
||||
tree cond = NULL_TREE, thenclause = NULL_TREE;
|
||||
/* Allow constructor for a type to get a new instance of the object
|
||||
using `build_new'. */
|
||||
tree abstract_virtuals = CLASSTYPE_ABSTRACT_VIRTUALS (current_class_type);
|
||||
CLASSTYPE_ABSTRACT_VIRTUALS (current_class_type) = NULL_TREE;
|
||||
|
||||
if (flag_this_is_variable > 0)
|
||||
{
|
||||
cond = build_binary_op (EQ_EXPR,
|
||||
current_class_ptr, integer_zero_node);
|
||||
thenclause = build_modify_expr (current_class_ptr, NOP_EXPR,
|
||||
build_new (NULL_TREE, current_class_type, void_type_node, 0));
|
||||
}
|
||||
|
||||
CLASSTYPE_ABSTRACT_VIRTUALS (current_class_type) = abstract_virtuals;
|
||||
|
||||
start_sequence ();
|
||||
|
||||
if (flag_this_is_variable > 0)
|
||||
{
|
||||
expand_start_cond (cond, 0);
|
||||
expand_expr_stmt (thenclause);
|
||||
expand_end_cond ();
|
||||
}
|
||||
|
||||
/* Emit insns from `emit_base_init' which sets up virtual
|
||||
function table pointer(s). */
|
||||
if (base_init_expr)
|
||||
{
|
||||
expand_expr_stmt (base_init_expr);
|
||||
base_init_expr = NULL_TREE;
|
||||
}
|
||||
|
||||
insns = get_insns ();
|
||||
end_sequence ();
|
||||
|
||||
/* This is where the body of the constructor begins. */
|
||||
|
||||
emit_insns_after (insns, last_parm_cleanup_insn);
|
||||
|
||||
end_protect_partials ();
|
||||
|
||||
/* This is where the body of the constructor ends. */
|
||||
expand_label (ctor_label);
|
||||
ctor_label = NULL_TREE;
|
||||
|
||||
if (call_poplevel)
|
||||
{
|
||||
decls = getdecls ();
|
||||
expand_end_bindings (decls, decls != NULL_TREE, 0);
|
||||
poplevel (decls != NULL_TREE, 1, 0);
|
||||
}
|
||||
|
||||
/* c_expand_return knows to return 'this' from a constructor. */
|
||||
c_expand_return (NULL_TREE);
|
||||
|
||||
current_function_assigns_this = 0;
|
||||
current_function_just_assigned_this = 0;
|
||||
}
|
||||
else if (DECL_CONSTRUCTOR_P (fndecl)
|
||||
&& !DECL_VLIST_CTOR_WRAPPER_P (fndecl))
|
||||
finish_ctor (call_poplevel);
|
||||
else if (DECL_MAIN_P (fndecl))
|
||||
{
|
||||
/* Make it so that `main' always returns 0 by default. */
|
||||
|
@ -224,14 +224,21 @@ int warn_long_long = 1;
|
||||
|
||||
int warn_ctor_dtor_privacy = 1;
|
||||
|
||||
/* True if we want to implement vtables using "thunks".
|
||||
The default is off. */
|
||||
/* 1 or 2 if we want to implement vtables using "thunks".
|
||||
The default is off. Version 1 indicates "old" implementation;
|
||||
Version 2 passes the __vlist argument in pvbase cases. */
|
||||
|
||||
#ifndef DEFAULT_VTABLE_THUNKS
|
||||
#define DEFAULT_VTABLE_THUNKS 0
|
||||
#endif
|
||||
int flag_vtable_thunks = DEFAULT_VTABLE_THUNKS;
|
||||
|
||||
#if DEFAULT_VTABLE_THUNKS == 2
|
||||
int flag_vtable_thunks_compat = 1;
|
||||
#else
|
||||
int flag_vtable_thunks_compat = 0;
|
||||
#endif
|
||||
|
||||
/* True if we want to deal with repository information. */
|
||||
|
||||
int flag_use_repository;
|
||||
@ -633,6 +640,31 @@ lang_decode_option (argc, argv)
|
||||
found = 1;
|
||||
cp_deprecated ("-fexternal-templates");
|
||||
}
|
||||
else if (!strncmp (p, "vtable-thunks", 13))
|
||||
{
|
||||
if (p[13] == '=')
|
||||
{
|
||||
flag_vtable_thunks =
|
||||
read_integral_parameter (p+14, p, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If the machine file has a default setting, use that
|
||||
for -fvtable-thunks. Otherwise, set it to version
|
||||
2. */
|
||||
#if DEFAULT_VTABLE_THUNKS
|
||||
flag_vtable_thunks = DEFAULT_VTABLE_THUNKS;
|
||||
#else
|
||||
flag_vtable_thunks = 1;
|
||||
#endif
|
||||
}
|
||||
if (flag_vtable_thunks == 2)
|
||||
/* v2 is a compatibility mode between v1 and v3. */
|
||||
flag_vtable_thunks_compat = 1;
|
||||
else if(flag_vtable_thunks == 3)
|
||||
flag_vtable_thunks_compat = 0;
|
||||
found = 1;
|
||||
}
|
||||
else if (!strcmp (p, "handle-signatures"))
|
||||
{
|
||||
flag_handle_signatures = 1;
|
||||
@ -644,7 +676,7 @@ lang_decode_option (argc, argv)
|
||||
flag_new_abi = 1;
|
||||
flag_do_squangling = 1;
|
||||
flag_honor_std = 1;
|
||||
flag_vtable_thunks = 1;
|
||||
flag_vtable_thunks = 2;
|
||||
}
|
||||
else if (!strcmp (p, "no-new-abi"))
|
||||
{
|
||||
@ -917,17 +949,27 @@ grok_x_components (specs)
|
||||
|
||||
This function adds the "in-charge" flag to member function FN if
|
||||
appropriate. It is called from grokclassfn and tsubst.
|
||||
FN must be either a constructor or destructor. */
|
||||
FN must be either a constructor or destructor.
|
||||
|
||||
For vtable thunks, types with polymorphic virtual bases need an
|
||||
additional "vlist" argument which is an array of virtual tables.
|
||||
In addition, if backwards-compatibility to v1 thunks is requested,
|
||||
a wrapper constructor may be needed as well. */
|
||||
|
||||
void
|
||||
maybe_retrofit_in_chrg (fn)
|
||||
tree fn;
|
||||
{
|
||||
tree basetype, arg_types, parms, parm, fntype;
|
||||
tree wrapper;
|
||||
|
||||
if (CLASSTYPE_IS_TEMPLATE (DECL_CLASS_CONTEXT (fn)))
|
||||
/* Never retrofit arguments on template methods. */
|
||||
return;
|
||||
|
||||
if (DECL_CONSTRUCTOR_P (fn)
|
||||
&& TYPE_USES_VIRTUAL_BASECLASSES (DECL_CLASS_CONTEXT (fn))
|
||||
&& ! DECL_CONSTRUCTOR_FOR_VBASE_P (fn))
|
||||
&& DECL_CONSTRUCTOR_FOR_VBASE (fn) == 0)
|
||||
/* OK */;
|
||||
else if (! DECL_CONSTRUCTOR_P (fn)
|
||||
&& TREE_CHAIN (DECL_ARGUMENTS (fn)) == NULL_TREE)
|
||||
@ -936,7 +978,37 @@ maybe_retrofit_in_chrg (fn)
|
||||
return;
|
||||
|
||||
if (DECL_CONSTRUCTOR_P (fn))
|
||||
DECL_CONSTRUCTOR_FOR_VBASE_P (fn) = 1;
|
||||
{
|
||||
if (TYPE_USES_PVBASES (DECL_CLASS_CONTEXT (fn)))
|
||||
DECL_CONSTRUCTOR_FOR_VBASE (fn) = CONSTRUCTOR_FOR_PVBASE;
|
||||
else
|
||||
DECL_CONSTRUCTOR_FOR_VBASE (fn) = CONSTRUCTOR_FOR_VBASE;
|
||||
}
|
||||
else if (TYPE_USES_PVBASES (DECL_CLASS_CONTEXT (fn)))
|
||||
DECL_CONSTRUCTOR_FOR_VBASE (fn) = DESTRUCTOR_FOR_PVBASE;
|
||||
|
||||
/* Retrieve the arguments, because it is potentially modified twice. */
|
||||
arg_types = TYPE_ARG_TYPES (TREE_TYPE (fn));
|
||||
basetype = TREE_TYPE (TREE_VALUE (arg_types));
|
||||
arg_types = TREE_CHAIN (arg_types);
|
||||
|
||||
if (DECL_CONSTRUCTOR_FOR_PVBASE_P (fn)
|
||||
|| DECL_DESTRUCTOR_FOR_PVBASE_P (fn))
|
||||
{
|
||||
/* Add the __vlist argument first. See __in_chrg below. */
|
||||
tree id = vlist_identifier;
|
||||
if (DECL_DESTRUCTOR_FOR_PVBASE_P (fn))
|
||||
id = get_identifier (VLIST1_NAME);
|
||||
parm = build_decl (PARM_DECL, id, vlist_type_node);
|
||||
SET_DECL_ARTIFICIAL (parm);
|
||||
DECL_ARG_TYPE (parm) = vlist_type_node;
|
||||
parms = DECL_ARGUMENTS (fn);
|
||||
/* Add it after 'this'. */
|
||||
TREE_CHAIN (parm) = TREE_CHAIN (parms);
|
||||
TREE_CHAIN (parms) = parm;
|
||||
|
||||
arg_types = hash_tree_chain (vlist_type_node, arg_types);
|
||||
}
|
||||
|
||||
/* First add it to DECL_ARGUMENTS... */
|
||||
parm = build_decl (PARM_DECL, in_charge_identifier, integer_type_node);
|
||||
@ -949,15 +1021,17 @@ maybe_retrofit_in_chrg (fn)
|
||||
TREE_CHAIN (parms) = parm;
|
||||
|
||||
/* ...and then to TYPE_ARG_TYPES. */
|
||||
arg_types = TYPE_ARG_TYPES (TREE_TYPE (fn));
|
||||
basetype = TREE_TYPE (TREE_VALUE (arg_types));
|
||||
arg_types = hash_tree_chain (integer_type_node, TREE_CHAIN (arg_types));
|
||||
arg_types = hash_tree_chain (integer_type_node, arg_types);
|
||||
fntype = build_cplus_method_type (basetype, TREE_TYPE (TREE_TYPE (fn)),
|
||||
arg_types);
|
||||
if (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)))
|
||||
fntype = build_exception_variant (fntype,
|
||||
TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)));
|
||||
TREE_TYPE (fn) = fntype;
|
||||
|
||||
if (flag_vtable_thunks_compat
|
||||
&& DECL_CONSTRUCTOR_FOR_PVBASE_P (fn))
|
||||
make_vlist_ctor_wrapper (fn);
|
||||
}
|
||||
|
||||
/* Classes overload their constituent function names automatically.
|
||||
@ -1042,7 +1116,8 @@ grokclassfn (ctype, function, flags, quals)
|
||||
|
||||
if (flags == DTOR_FLAG)
|
||||
{
|
||||
DECL_ASSEMBLER_NAME (function) = build_destructor_name (ctype);
|
||||
DECL_ASSEMBLER_NAME (function) =
|
||||
build_destructor_name (ctype, DECL_DESTRUCTOR_FOR_PVBASE_P (function));
|
||||
TYPE_HAS_DESTRUCTOR (ctype) = 1;
|
||||
}
|
||||
else
|
||||
|
@ -247,10 +247,10 @@ __cp_pop_exception (cp_eh_info *p)
|
||||
|
||||
if (p->cleanup)
|
||||
/* 2 is a magic value for destructors; see build_delete(). */
|
||||
p->cleanup (p->value, 2);
|
||||
p->cleanup (p->original_value, 2); // value may have been adjusted.
|
||||
|
||||
if (! __is_pointer (p->type))
|
||||
__eh_free (p->original_value); // value may have been co-erced.
|
||||
__eh_free (p->original_value); // value may have been adjusted.
|
||||
|
||||
__eh_free (p);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ Questions and comments to Benjamin Kosnik @code{<bkoz@@cygnus.com>}.
|
||||
* Exception Handling::
|
||||
* Free Store::
|
||||
* Mangling:: Function name mangling for C++ and Java
|
||||
* Vtables:: Two ways to do virtual functions
|
||||
* Concept Index::
|
||||
@end menu
|
||||
|
||||
@ -190,7 +191,9 @@ accessed by the BINFO_ accessor macros.
|
||||
The virtual function table holds information used in virtual function
|
||||
dispatching. In the compiler, they are usually referred to as vtables,
|
||||
or vtbls. The first index is not used in the normal way, I believe it
|
||||
is probably used for the virtual destructor.
|
||||
is probably used for the virtual destructor. There are two forms of
|
||||
virtual tables, one that has offsets in addition to pointers, and one
|
||||
using thunks. @xref{Vtables}.
|
||||
|
||||
@item vfield
|
||||
|
||||
@ -1455,7 +1458,7 @@ To meet the first goal, we defer emission of inlines and vtables until
|
||||
the end of the translation unit, where we can decide whether or not they
|
||||
are needed, and how to emit them if they are.
|
||||
|
||||
@node Mangling, Concept Index, Free Store, Top
|
||||
@node Mangling, Vtables, Free Store, Top
|
||||
@section Function name mangling for C++ and Java
|
||||
|
||||
Both C++ and Jave provide overloaded function and methods,
|
||||
@ -1839,7 +1842,231 @@ Used for template type parameters.
|
||||
The letters @samp{G}, @samp{M}, @samp{O}, and @samp{p}
|
||||
also seem to be used for obscure purposes ...
|
||||
|
||||
@node Concept Index, , Mangling, Top
|
||||
@node Vtables, Concept Index, Mangling, Top
|
||||
@section Virtual Tables
|
||||
|
||||
In order to invoke virtual functions, GNU C++ uses virtual tables. Each
|
||||
virtual function gets an index, and the table entry points to the
|
||||
overridden function to call. Sometimes, and adjustment to the this
|
||||
pointer has to be made before calling a virtual function:
|
||||
|
||||
@example
|
||||
struct A@{
|
||||
int i;
|
||||
virtual void foo();
|
||||
@};
|
||||
|
||||
struct B@{
|
||||
int j;
|
||||
virtual void bar();
|
||||
@};
|
||||
|
||||
struct C:A,B@{
|
||||
virtual void bar();
|
||||
@};
|
||||
|
||||
void C::bar()
|
||||
@{
|
||||
i++;
|
||||
@}
|
||||
|
||||
int main()
|
||||
@{
|
||||
C *c = new C;
|
||||
B *b = c;
|
||||
c->bar();
|
||||
@}
|
||||
@end example
|
||||
|
||||
Here, casting from @samp{c} to @samp{b} adds an offset. When @samp{bar}
|
||||
is called, this offset needs to be subtracted, so that @samp{C::bar} can
|
||||
properly access @samp{i}. One approach of achieving this is to use
|
||||
@emph{thunks}, which are small half-functions put into the virtual
|
||||
table. The modify the first argument (the @samp{this} pointer), and then
|
||||
jump into the real function.
|
||||
|
||||
The other (traditional) approach is to have an additional integer in the
|
||||
virtual table which is added to this. This is an additional overhead
|
||||
both at the function call, and in the size of virtual tables: In the
|
||||
case of single inheritance (or for the first base class), these integers
|
||||
will always be zero.
|
||||
|
||||
@subsection Virtual Base Classes with Virtual Tables
|
||||
|
||||
In case of virtual bases, the code is even more
|
||||
complicated. Constructors and destructors need to know whether they are
|
||||
"in charge" of the virtual bases, and an implicit integer
|
||||
@samp{__in_chrg} for that purpose.
|
||||
|
||||
@example
|
||||
struct A@{
|
||||
int i;
|
||||
virtual void bar();
|
||||
void call_bar()@{bar();@}
|
||||
@};
|
||||
|
||||
struct B:virtual A@{
|
||||
B();
|
||||
int j;
|
||||
virtual void bar();
|
||||
@};
|
||||
|
||||
B::B()@{
|
||||
call_bar();
|
||||
@}
|
||||
|
||||
struct C@{
|
||||
int k;
|
||||
@};
|
||||
|
||||
struct D:C,B@{
|
||||
int l;
|
||||
virtual void bar();
|
||||
@};
|
||||
|
||||
@end example
|
||||
|
||||
When constructing an instance of B, it will have the following layout:
|
||||
@samp{vbase pointer to A}, @samp{j}, @samp{A virtual table}, @samp{i}.
|
||||
On a 32-bit machine, downcasting from @samp{A*} to @samp{B*} would need
|
||||
to subtract 8, which would be the thunk executed when calling
|
||||
@samp{B::bar} inside @samp{call_bar}.
|
||||
|
||||
When constructing an instance of D, it will have a different layout:
|
||||
@samp{k}, @samp{vbase pointer to A}, @samp{j}, @samp{l}, @samp{A virtual
|
||||
table}, @samp{i}. So, when downcasting from @samp{A*} to @samp{B*} in a
|
||||
@samp{D} object, the offset would be @samp{12}.
|
||||
|
||||
This means that during construction of the @samp{B} base of a @samp{D}
|
||||
object, a virtual table is needed which has a @samp{-12} thunk to
|
||||
@samp{B::bar}. This is @emph{only} needed during construction and
|
||||
destruction, as the full object will use a @samp{-16} thunk to
|
||||
@samp{D::bar}.
|
||||
|
||||
In order to implement this, the compiler generates an implicit argument
|
||||
(in addition to @code{__in_chrg}): the virtual list argument
|
||||
@code{__vlist}. This is a list of virtual tables needed during
|
||||
construction and destruction. The virtual pointers are ordered in the
|
||||
way they are used during construction; the destructors will process the
|
||||
array in reverse order. The ordering is as follows:
|
||||
@itemize @bullet
|
||||
@item
|
||||
If the class is in charge, the vlist starts with virtual table pointers
|
||||
for the virtual bases that have virtual bases themselves. Here, only
|
||||
@emph{polymorphic} virtual bases (pvbases) are interesting: if a vbase
|
||||
has no virtual functions, it doesn't have a virtual table.
|
||||
|
||||
@item
|
||||
Next, the vlist has virtual tables for the initialization of the
|
||||
non-virtual bases. These bases are not in charge, so the layout is
|
||||
recursive, but ignores virtual bases during recursion.
|
||||
|
||||
@item
|
||||
Next, there is a number of virtual tables for each virtual base. These
|
||||
are sorted in the order in which virtual bases are constructed. Each
|
||||
virtual base may have more than one @code{vfield}, and therefore require
|
||||
more than one @code{vtable}. The order of vtables is the same as used
|
||||
when initializing vfields of non-virtual bases in a constructor.
|
||||
@end itemize
|
||||
|
||||
The compiler emits a virtual table list in a variable mangled as
|
||||
@code{__vl.classname}.
|
||||
|
||||
Class with virtual bases, but without pvbases, only have the
|
||||
@code{__in_chrg} argument to their ctors and dtors: they don't have any
|
||||
vfields in the vbases to initialize.
|
||||
|
||||
A further problem arises with virtual destructors: A destructor
|
||||
typically has only the @code{__in_chrg} argument, which also indicates
|
||||
whether the destructor should call @code{operator delete}. A dtor of a
|
||||
class with pvbases has an additional argument. Unfortunately, a caller
|
||||
of a virtual dtor might not know whether to pass that argument or not.
|
||||
Therefore, the dtor processes the @code{__vlist} argument in an
|
||||
automatic variable, which is initialized from the class' vlist if the
|
||||
__in_chrg flag has a zero value in bit 2 (bit mask 4), or from the
|
||||
argument @code{__vlist1} if bit 2 of the __in_chrg parameter is set to
|
||||
one.
|
||||
|
||||
@subsection Specification of non-thunked vtables
|
||||
|
||||
In the traditional implementation of vtables, each slot contains three
|
||||
fields: The offset to be added to the this pointer before invoking a
|
||||
virtual function, an unused field that is always zero, and the pointer
|
||||
to the virtual function. The first two fields are typically 16 bits
|
||||
wide. The unused field is called `index'; it may be non-zero in
|
||||
pointer-to-member-functions, which use the same layout.
|
||||
|
||||
The virtual table then is an array of vtable slots. The first slot is
|
||||
always the virtual type info function, the other slots are in the order
|
||||
in which the virtual functions appear in the class declaration.
|
||||
|
||||
If a class has base classes, it may inherit other bases' vfields. Each
|
||||
class may have a primary vfield; the primary vfield of the derived class
|
||||
is the primary vfield of the left-most non-virtual base class. If a
|
||||
class inherits a primary vfield, any new virtual functions in the
|
||||
derived class are appended to the virtual table of the primary
|
||||
vfield. If there are new virtual functions in the derived class, and no
|
||||
primary vfield is inherited, a new vfield is introduced which becomes
|
||||
primary. The redefined virtual functions fill the vtable slots inherited
|
||||
from the base; new virtual functions are put into the primary vtable in
|
||||
the order of declaration. If no new virtual functions are introduced, no
|
||||
primary vfield is allocated.
|
||||
|
||||
In a base class that has pvbases, virtual tables are needed which are
|
||||
used only in the constructor (see example above). At run-time, the
|
||||
virtual tables of the base class are adjusted, to reflect the new offset
|
||||
of the pvbase. The compiler knows statically what offset the pvbase has
|
||||
for a complete object. At run-time, the offset of the pvbase can be
|
||||
extracted from the vbase pointer, which is set in the constructor of the
|
||||
complete object. These two offsets result in a delta, which is used to
|
||||
adjust the deltas in the vtable (the adjustment might be different for
|
||||
different vtable slots). To adjust the vtables, the compiler emits code
|
||||
that creates a vtable on the stack. This vtable is initialized with the
|
||||
vtable for the complete base type, and then adjusted.
|
||||
|
||||
In order to call a virtual function, the compiler gets the offset field
|
||||
from the vtable entry, and adds it to the this pointer. It then
|
||||
indirectly calls the virtual function pointer, passing the adjusted this
|
||||
pointer, and any arguments the virtual function may have.
|
||||
|
||||
To implement dynamic casting, the dynamic_cast function needs typeinfos
|
||||
for the complete type, and the pointer to the complete type. The
|
||||
typeinfo pointer is obtained by calling the virtual typeinfo function
|
||||
(which doesn't take a this parameter). The pointer to the complete
|
||||
object is obtained by adding the offset of the virtual typeinfo vtable
|
||||
slot, since this virtual function is always implemented in the complete
|
||||
object.
|
||||
|
||||
@subsection Specification of thunked vtables
|
||||
|
||||
For vtable thunks, each slot only consists of a pointer to the virtual
|
||||
function, which might be a thunk function. The first slot in the vtable
|
||||
is an offset of the this pointer to the complete object, which is needed
|
||||
as a parameter to __dynamic_cast. The second slot is the virtual
|
||||
typeinfo function. All other slots are allocated with the same procedure
|
||||
as in the non-thunked case. Allocation of vfields also uses the same
|
||||
procedure as described above.
|
||||
|
||||
If the virtual function needs an adjusted this pointer, a thunk function
|
||||
is emitted. If supported by the target architecture, this is only a
|
||||
half-function. Such a thunk has no stack frame; it merely adjusts the
|
||||
first argument of the function, and then directly branches into the
|
||||
implementation of the virtual function. If the architecture does not
|
||||
support half-functions (i.e. if ASM_OUTPUT_MI_THUNK is not defined), the
|
||||
compiler emits a wrapper function, which copies all arguments, adjust
|
||||
the this pointer, and then calls the original function. Since objects of
|
||||
non-aggregate type are passed by invisible reference, this copies only
|
||||
POD arguments. The approach fails for virtual functions with a variable
|
||||
number of arguments.
|
||||
|
||||
In order to support the vtables needed in base constructors with
|
||||
pvbases, the compiler passes an implicit __vlist argument as described
|
||||
above, if the version 2 thunks are used. For version 1 thunks, the base
|
||||
class constructor will fill in the vtables for the complete base class,
|
||||
which will incorrectly adjust the this pointer, leading to a dynamic
|
||||
error.
|
||||
|
||||
@node Concept Index, , Vtables, Top
|
||||
|
||||
@section Concept Index
|
||||
|
||||
|
@ -59,7 +59,8 @@ static tree initializing_context PROTO((tree));
|
||||
static void expand_vec_init_try_block PROTO((tree));
|
||||
static void expand_vec_init_catch_clause PROTO((tree, tree, tree, tree));
|
||||
static tree build_java_class_ref PROTO((tree));
|
||||
static void expand_cleanup_for_base PROTO((tree, tree));
|
||||
static void expand_cleanup_for_base PROTO((tree, tree, tree));
|
||||
static int pvbasecount PROTO((tree, int));
|
||||
|
||||
/* Cache the identifier nodes for the magic field of a new cookie. */
|
||||
static tree nc_nelts_field_id;
|
||||
@ -478,6 +479,93 @@ sort_base_init (t, rbase_ptr, vbase_ptr)
|
||||
*vbase_ptr = vbases;
|
||||
}
|
||||
|
||||
/* Invoke a base-class destructor. REF is the object being destroyed,
|
||||
BINFO is the base class, and DTOR_ARG indicates whether the base
|
||||
class should invoke delete. */
|
||||
|
||||
tree
|
||||
build_base_dtor_call (ref, binfo, dtor_arg)
|
||||
tree ref, binfo, dtor_arg;
|
||||
{
|
||||
tree args = NULL_TREE;
|
||||
tree vlist = lookup_name (vlist_identifier, 0);
|
||||
tree call, decr;
|
||||
|
||||
if (TYPE_USES_PVBASES (BINFO_TYPE (binfo)))
|
||||
{
|
||||
args = expr_tree_cons (NULL_TREE, vlist, args);
|
||||
dtor_arg = build (BIT_IOR_EXPR, integer_type_node,
|
||||
dtor_arg, build_int_2 (4, 0));
|
||||
dtor_arg = fold (dtor_arg);
|
||||
}
|
||||
args = expr_tree_cons (NULL_TREE, dtor_arg, args);
|
||||
call = build_scoped_method_call (ref, binfo, dtor_identifier, args);
|
||||
|
||||
if (!TYPE_USES_PVBASES (BINFO_TYPE (binfo)))
|
||||
/* For plain inheritance, do not try to adjust __vlist. */
|
||||
return call;
|
||||
|
||||
/* Now decrement __vlist by the number of slots consumed by the base
|
||||
dtor. */
|
||||
decr = build_int_2 (pvbasecount (BINFO_TYPE (binfo), 0), 0);
|
||||
decr = build_binary_op (MINUS_EXPR, vlist, decr);
|
||||
decr = build_modify_expr (vlist, NOP_EXPR, decr);
|
||||
|
||||
return build (COMPOUND_EXPR, void_type_node, call, decr);
|
||||
}
|
||||
|
||||
/* Return the number of vlist entries needed to initialize TYPE,
|
||||
depending on whether it is IN_CHARGE. */
|
||||
|
||||
static int
|
||||
pvbasecount (type, in_charge)
|
||||
tree type;
|
||||
int in_charge;
|
||||
{
|
||||
int i;
|
||||
int result = 0;
|
||||
tree vbase;
|
||||
|
||||
for (vbase = (CLASSTYPE_VBASECLASSES (type)); vbase;
|
||||
vbase = TREE_CHAIN (vbase))
|
||||
{
|
||||
result += list_length (CLASSTYPE_VFIELDS (BINFO_TYPE (vbase)));
|
||||
if (in_charge)
|
||||
result += pvbasecount (BINFO_TYPE (vbase), 0);
|
||||
}
|
||||
|
||||
for (i=0; i < CLASSTYPE_N_BASECLASSES (type); i++)
|
||||
{
|
||||
tree base = TREE_VEC_ELT (TYPE_BINFO_BASETYPES (type), i);
|
||||
if (TREE_VIA_VIRTUAL (base))
|
||||
continue;
|
||||
result += pvbasecount (BINFO_TYPE (base), 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
init_vlist (t)
|
||||
tree t;
|
||||
{
|
||||
char *name;
|
||||
tree expr;
|
||||
tree vlist = lookup_name (vlist_identifier, 0);
|
||||
|
||||
name = alloca (strlen (VLIST_NAME_FORMAT)
|
||||
+ TYPE_ASSEMBLER_NAME_LENGTH (t) + 2);
|
||||
sprintf (name, VLIST_NAME_FORMAT, TYPE_ASSEMBLER_NAME_STRING (t));
|
||||
|
||||
expr = get_identifier (name);
|
||||
expr = lookup_name (expr, 0);
|
||||
expr = build1 (ADDR_EXPR, TREE_TYPE (vlist), expr);
|
||||
if (DECL_DESTRUCTOR_FOR_PVBASE_P (current_function_decl))
|
||||
/* Move to the end of the vlist. */
|
||||
expr = build_binary_op (PLUS_EXPR, expr,
|
||||
build_int_2 (pvbasecount (t, 1), 0));
|
||||
expand_expr_stmt (build_modify_expr (vlist, NOP_EXPR, expr));
|
||||
}
|
||||
|
||||
/* Perform whatever initializations have yet to be done on the base
|
||||
class of the class variable. These actions are in the global
|
||||
variable CURRENT_BASE_INIT_LIST. Such an action could be
|
||||
@ -510,6 +598,7 @@ emit_base_init (t, immediately)
|
||||
tree binfos = BINFO_BASETYPES (t_binfo);
|
||||
int i, n_baseclasses = binfos ? TREE_VEC_LENGTH (binfos) : 0;
|
||||
tree expr = NULL_TREE;
|
||||
tree vlist = lookup_name (vlist_identifier, 0);
|
||||
|
||||
if (! immediately)
|
||||
{
|
||||
@ -581,7 +670,7 @@ emit_base_init (t, immediately)
|
||||
free_temp_slots ();
|
||||
}
|
||||
|
||||
expand_cleanup_for_base (base_binfo, NULL_TREE);
|
||||
expand_cleanup_for_base (base_binfo, vlist, NULL_TREE);
|
||||
rbase_init_list = TREE_CHAIN (rbase_init_list);
|
||||
}
|
||||
|
||||
@ -750,30 +839,39 @@ expand_virtual_init (binfo, decl)
|
||||
destroyed. */
|
||||
|
||||
static void
|
||||
expand_cleanup_for_base (binfo, flag)
|
||||
expand_cleanup_for_base (binfo, vlist, flag)
|
||||
tree binfo;
|
||||
tree vlist;
|
||||
tree flag;
|
||||
{
|
||||
tree expr;
|
||||
|
||||
if (!TYPE_NEEDS_DESTRUCTOR (BINFO_TYPE (binfo)))
|
||||
return;
|
||||
if (TYPE_NEEDS_DESTRUCTOR (BINFO_TYPE (binfo)))
|
||||
{
|
||||
/* All cleanups must be on the function_obstack. */
|
||||
push_obstacks_nochange ();
|
||||
resume_temporary_allocation ();
|
||||
|
||||
/* All cleanups must be on the function_obstack. */
|
||||
push_obstacks_nochange ();
|
||||
resume_temporary_allocation ();
|
||||
/* Call the destructor. */
|
||||
expr = build_base_dtor_call (current_class_ref, binfo,
|
||||
integer_zero_node);
|
||||
if (flag)
|
||||
expr = fold (build (COND_EXPR, void_type_node,
|
||||
truthvalue_conversion (flag),
|
||||
expr, integer_zero_node));
|
||||
|
||||
/* Call the destructor. */
|
||||
expr = (build_scoped_method_call
|
||||
(current_class_ref, binfo, dtor_identifier,
|
||||
build_expr_list (NULL_TREE, integer_zero_node)));
|
||||
if (flag)
|
||||
expr = fold (build (COND_EXPR, void_type_node,
|
||||
truthvalue_conversion (flag),
|
||||
expr, integer_zero_node));
|
||||
pop_obstacks ();
|
||||
add_partial_entry (expr);
|
||||
}
|
||||
|
||||
pop_obstacks ();
|
||||
add_partial_entry (expr);
|
||||
if (TYPE_USES_PVBASES (BINFO_TYPE (binfo)))
|
||||
{
|
||||
/* Increment vlist by number of base's vbase classes. */
|
||||
expr = build_int_2 (pvbasecount (BINFO_TYPE (binfo), 0), 0);
|
||||
expr = build_binary_op (PLUS_EXPR, vlist, expr);
|
||||
expr = build_modify_expr (vlist, NOP_EXPR, expr);
|
||||
expand_expr_stmt (expr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Subroutine of `expand_aggr_vbase_init'.
|
||||
@ -813,6 +911,7 @@ construct_virtual_bases (type, this_ref, this_ptr, init_list, flag)
|
||||
{
|
||||
tree vbases;
|
||||
tree result;
|
||||
tree vlist = NULL_TREE;
|
||||
|
||||
/* If there are no virtual baseclasses, we shouldn't even be here. */
|
||||
my_friendly_assert (TYPE_USES_VIRTUAL_BASECLASSES (type), 19990621);
|
||||
@ -820,6 +919,11 @@ construct_virtual_bases (type, this_ref, this_ptr, init_list, flag)
|
||||
/* First set the pointers in our object that tell us where to find
|
||||
our virtual baseclasses. */
|
||||
expand_start_cond (flag, 0);
|
||||
if (TYPE_USES_PVBASES (type))
|
||||
{
|
||||
init_vlist (type);
|
||||
vlist = lookup_name (vlist_identifier, 0);
|
||||
}
|
||||
result = init_vbase_pointers (type, this_ptr);
|
||||
if (result)
|
||||
expand_expr_stmt (build_compound_expr (result));
|
||||
@ -851,7 +955,7 @@ construct_virtual_bases (type, this_ref, this_ptr, init_list, flag)
|
||||
init_list);
|
||||
expand_end_cond ();
|
||||
|
||||
expand_cleanup_for_base (vbases, flag);
|
||||
expand_cleanup_for_base (vbases, vlist, flag);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1146,6 +1250,44 @@ expand_aggr_init (exp, init, flags)
|
||||
TREE_THIS_VOLATILE (exp) = was_volatile;
|
||||
}
|
||||
|
||||
static tree
|
||||
no_vlist_base_init (rval, exp, init, binfo, flags)
|
||||
tree rval, exp, init, binfo;
|
||||
int flags;
|
||||
{
|
||||
tree nrval, func, parms;
|
||||
|
||||
/* Obtain the vlist-expecting ctor. */
|
||||
func = rval;
|
||||
my_friendly_assert (TREE_CODE (func) == CALL_EXPR, 20000131);
|
||||
func = TREE_OPERAND (func, 0);
|
||||
my_friendly_assert (TREE_CODE (func) == ADDR_EXPR, 20000132);
|
||||
|
||||
if (init == NULL_TREE
|
||||
|| (TREE_CODE (init) == TREE_LIST && ! TREE_TYPE (init)))
|
||||
{
|
||||
parms = init;
|
||||
if (parms)
|
||||
init = TREE_VALUE (parms);
|
||||
}
|
||||
else
|
||||
parms = build_expr_list (NULL_TREE, init);
|
||||
|
||||
flags &= ~LOOKUP_HAS_VLIST;
|
||||
|
||||
parms = expr_tree_cons (NULL_TREE, integer_zero_node, parms);
|
||||
flags |= LOOKUP_HAS_IN_CHARGE;
|
||||
|
||||
nrval = build_method_call (exp, ctor_identifier,
|
||||
parms, binfo, flags);
|
||||
|
||||
func = build (NE_EXPR, boolean_type_node,
|
||||
func, null_pointer_node);
|
||||
nrval = build (COND_EXPR, void_type_node,
|
||||
func, rval, nrval);
|
||||
return nrval;
|
||||
}
|
||||
|
||||
static void
|
||||
expand_default_init (binfo, true_exp, exp, init, flags)
|
||||
tree binfo;
|
||||
@ -1163,6 +1305,8 @@ expand_default_init (binfo, true_exp, exp, init, flags)
|
||||
out, then look hard. */
|
||||
tree rval;
|
||||
tree parms;
|
||||
tree vlist = NULL_TREE;
|
||||
tree orig_init = init;
|
||||
|
||||
if (init && TREE_CODE (init) != TREE_LIST
|
||||
&& (flags & LOOKUP_ONLYCONVERTING))
|
||||
@ -1206,6 +1350,21 @@ expand_default_init (binfo, true_exp, exp, init, flags)
|
||||
|
||||
if (TYPE_USES_VIRTUAL_BASECLASSES (type))
|
||||
{
|
||||
if (TYPE_USES_PVBASES (type))
|
||||
{
|
||||
/* In compatibility mode, when not calling a base ctor,
|
||||
we do not pass the vlist argument. */
|
||||
if (true_exp == exp)
|
||||
vlist = flag_vtable_thunks_compat? NULL_TREE : vlist_zero_node;
|
||||
else
|
||||
vlist = lookup_name (vlist_identifier, 0);
|
||||
|
||||
if (vlist)
|
||||
{
|
||||
parms = expr_tree_cons (NULL_TREE, vlist, parms);
|
||||
flags |= LOOKUP_HAS_VLIST;
|
||||
}
|
||||
}
|
||||
if (true_exp == exp)
|
||||
parms = expr_tree_cons (NULL_TREE, integer_one_node, parms);
|
||||
else
|
||||
@ -1215,6 +1374,10 @@ expand_default_init (binfo, true_exp, exp, init, flags)
|
||||
|
||||
rval = build_method_call (exp, ctor_identifier,
|
||||
parms, binfo, flags);
|
||||
if (vlist && true_exp != exp && flag_vtable_thunks_compat)
|
||||
{
|
||||
rval = no_vlist_base_init (rval, exp, orig_init, binfo, flags);
|
||||
}
|
||||
if (TREE_SIDE_EFFECTS (rval))
|
||||
expand_expr_stmt (rval);
|
||||
}
|
||||
@ -2408,6 +2571,12 @@ build_new_1 (exp)
|
||||
|
||||
if (rval && TYPE_USES_VIRTUAL_BASECLASSES (true_type))
|
||||
{
|
||||
if (TYPE_USES_PVBASES (true_type)
|
||||
&& !flag_vtable_thunks_compat)
|
||||
{
|
||||
init = expr_tree_cons (NULL_TREE, vlist_zero_node, init);
|
||||
flags |= LOOKUP_HAS_VLIST;
|
||||
}
|
||||
init = expr_tree_cons (NULL_TREE, integer_one_node, init);
|
||||
flags |= LOOKUP_HAS_IN_CHARGE;
|
||||
}
|
||||
@ -3123,9 +3292,21 @@ build_delete (type, addr, auto_delete, flags, use_global_delete)
|
||||
else
|
||||
passed_auto_delete = auto_delete;
|
||||
|
||||
expr = build_method_call
|
||||
(ref, dtor_identifier, build_expr_list (NULL_TREE, passed_auto_delete),
|
||||
NULL_TREE, flags);
|
||||
/* Maybe pass vlist pointer to destructor. */
|
||||
if (TYPE_USES_PVBASES (type))
|
||||
{
|
||||
/* Pass vlist_zero even if in backwards compatibility mode,
|
||||
as the extra argument should not hurt if it is not used. */
|
||||
expr = build_expr_list (NULL_TREE, vlist_zero_node);
|
||||
flags |= LOOKUP_HAS_VLIST;
|
||||
}
|
||||
else
|
||||
expr = NULL_TREE;
|
||||
|
||||
expr = expr_tree_cons (NULL_TREE, passed_auto_delete, expr);
|
||||
|
||||
expr = build_method_call (ref, dtor_identifier, expr,
|
||||
NULL_TREE, flags);
|
||||
|
||||
if (do_delete)
|
||||
expr = build (COMPOUND_EXPR, void_type_node, expr, do_delete);
|
||||
@ -3181,14 +3362,13 @@ build_delete (type, addr, auto_delete, flags, use_global_delete)
|
||||
{
|
||||
tree this_auto_delete;
|
||||
|
||||
/* Should the base invoke delete? */
|
||||
if (BINFO_OFFSET_ZEROP (base_binfo))
|
||||
this_auto_delete = parent_auto_delete;
|
||||
else
|
||||
this_auto_delete = integer_zero_node;
|
||||
|
||||
expr = build_scoped_method_call
|
||||
(ref, base_binfo, dtor_identifier,
|
||||
build_expr_list (NULL_TREE, this_auto_delete));
|
||||
expr = build_base_dtor_call (ref, base_binfo, this_auto_delete);
|
||||
exprstmt = expr_tree_cons (NULL_TREE, expr, exprstmt);
|
||||
}
|
||||
|
||||
@ -3200,9 +3380,7 @@ build_delete (type, addr, auto_delete, flags, use_global_delete)
|
||||
|| TREE_VIA_VIRTUAL (base_binfo))
|
||||
continue;
|
||||
|
||||
expr = build_scoped_method_call
|
||||
(ref, base_binfo, dtor_identifier,
|
||||
build_expr_list (NULL_TREE, integer_zero_node));
|
||||
expr = build_base_dtor_call (ref, base_binfo, integer_zero_node);
|
||||
|
||||
exprstmt = expr_tree_cons (NULL_TREE, expr, exprstmt);
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ Boston, MA 02111-1307, USA. */
|
||||
#include "hard-reg-set.h"
|
||||
#include "flags.h"
|
||||
#include "toplev.h"
|
||||
#include "decl.h"
|
||||
|
||||
/* TREE_LIST of the current inline functions that need to be
|
||||
processed. */
|
||||
@ -88,6 +89,8 @@ static int is_back_referenceable_type PROTO((tree));
|
||||
static int check_btype PROTO((tree));
|
||||
static void build_mangled_name_for_type PROTO((tree));
|
||||
static void build_mangled_name_for_type_with_Gcode PROTO((tree, int));
|
||||
static tree build_base_path PROTO((tree, int));
|
||||
|
||||
|
||||
# define OB_INIT() (scratch_firstobj ? (obstack_free (&scratch_obstack, scratch_firstobj), 0) : 0)
|
||||
# define OB_PUTC(C) (obstack_1grow (&scratch_obstack, (C)))
|
||||
@ -1349,6 +1352,15 @@ process_overload_item (parmtype, extra_Gcode)
|
||||
#endif
|
||||
|
||||
case POINTER_TYPE:
|
||||
/* Even though the vlist_type_node is PPPFe (i.e. `int
|
||||
(***)(...)'), it is different from the any other occurence of
|
||||
the pointer type, because the underlying function type is
|
||||
different. */
|
||||
if (parmtype == vlist_type_node)
|
||||
{
|
||||
OB_PUTS (VLIST_TYPE_NAME);
|
||||
return;
|
||||
}
|
||||
OB_PUTC ('P');
|
||||
more:
|
||||
build_mangled_name_for_type (TREE_TYPE (parmtype));
|
||||
@ -1809,14 +1821,64 @@ get_id_2 (name, name2)
|
||||
return get_identifier (obstack_base (&scratch_obstack));
|
||||
}
|
||||
|
||||
/* Returns a DECL_ASSEMBLER_NAME for the destructor of type TYPE. */
|
||||
/* Print a binfo path T, starting with the most derived class. If
|
||||
OMIT_LAST is set, drop and return the most derived class. */
|
||||
|
||||
static tree
|
||||
build_base_path (t, omit_last)
|
||||
tree t;
|
||||
int omit_last;
|
||||
{
|
||||
tree ret = NULL_TREE;
|
||||
if (BINFO_INHERITANCE_CHAIN (t))
|
||||
ret = build_base_path (BINFO_INHERITANCE_CHAIN (t), omit_last);
|
||||
else if (omit_last)
|
||||
return t;
|
||||
process_overload_item (BINFO_TYPE (t), 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Return a mangled name for a vlist vtable, using the path of both
|
||||
BASE and VBASE. */
|
||||
|
||||
tree
|
||||
build_destructor_name (type)
|
||||
tree type;
|
||||
get_vlist_vtable_id (base, vbase)
|
||||
tree base, vbase;
|
||||
{
|
||||
return build_overload_with_type (get_identifier (DESTRUCTOR_DECL_PREFIX),
|
||||
type);
|
||||
tree last;
|
||||
OB_INIT ();
|
||||
OB_PUTS (VCTABLE_NAME);
|
||||
build_base_path (base, 0);
|
||||
OB_PUTC ('_');
|
||||
/* Since the base path should end where the vbase path starts, we
|
||||
can omit the most-derived class in the vbase path. Check below
|
||||
that this really happens. */
|
||||
last = build_base_path (vbase, 1);
|
||||
my_friendly_assert (BINFO_TYPE (last) == BINFO_TYPE (base), 990402);
|
||||
OB_FINISH ();
|
||||
return get_identifier (obstack_base (&scratch_obstack));
|
||||
}
|
||||
|
||||
/* Returns a DECL_ASSEMBLER_NAME for the destructor of type TYPE. If
|
||||
HAS_VLIST is set, also add the vlist argument. */
|
||||
|
||||
tree
|
||||
build_destructor_name (type, has_vlist)
|
||||
tree type;
|
||||
int has_vlist;
|
||||
{
|
||||
OB_INIT ();
|
||||
OB_PUTS (DESTRUCTOR_DECL_PREFIX);
|
||||
start_squangling ();
|
||||
build_mangled_name_for_type (type);
|
||||
/* If we need backwards compatibility, we can get aways by
|
||||
not linking type-safely, as the dtor will check whether
|
||||
the argument was provided. */
|
||||
if (has_vlist && !flag_vtable_thunks_compat)
|
||||
OB_PUTS (VLIST_TYPE_NAME);
|
||||
OB_FINISH ();
|
||||
end_squangling ();
|
||||
return get_identifier (obstack_base (&scratch_obstack));
|
||||
}
|
||||
|
||||
/* Given a tree_code CODE, and some arguments (at least one),
|
||||
@ -2177,6 +2239,136 @@ emit_thunk (thunk_fndecl)
|
||||
|
||||
TREE_SET_CODE (thunk_fndecl, THUNK_DECL);
|
||||
}
|
||||
|
||||
void
|
||||
make_vlist_ctor_wrapper (fn)
|
||||
tree fn;
|
||||
{
|
||||
tree fntype, decl;
|
||||
tree arg_types, parms, parm, basetype, pbasetype;
|
||||
tree t, ctors;
|
||||
|
||||
arg_types = TYPE_ARG_TYPES (TREE_TYPE (fn));
|
||||
pbasetype = TREE_VALUE (arg_types);
|
||||
basetype = TREE_TYPE (pbasetype);
|
||||
parms = DECL_ARGUMENTS (fn);
|
||||
|
||||
/* Skip this, __in_chrg, and _vlist */
|
||||
arg_types = TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arg_types)));
|
||||
|
||||
|
||||
/* Add __in_charge. */
|
||||
arg_types = hash_tree_chain (integer_type_node, arg_types);
|
||||
|
||||
/* Don't add this to arg_types, as build_cplus_method_type does so. */
|
||||
|
||||
fntype = build_cplus_method_type (basetype, TREE_TYPE (TREE_TYPE (fn)),
|
||||
arg_types);
|
||||
|
||||
decl = build_lang_decl (FUNCTION_DECL, DECL_NAME (fn), fntype);
|
||||
DECL_LANG_SPECIFIC (decl)->decl_flags = DECL_LANG_SPECIFIC (fn)->decl_flags;
|
||||
DECL_EXTERNAL (decl) = 0;
|
||||
TREE_PUBLIC (decl) = 1;
|
||||
DECL_ARTIFICIAL (decl) = 1;
|
||||
DECL_CONSTRUCTOR_P (decl) = 1;
|
||||
DECL_CONSTRUCTOR_FOR_VBASE (decl) = CONSTRUCTOR_FOR_VBASE;
|
||||
/* Claim that this is never a template instantiation. */
|
||||
DECL_USE_TEMPLATE (decl) = 0;
|
||||
DECL_TEMPLATE_INFO (decl) = NULL_TREE;
|
||||
|
||||
/* Set up clone argument trees for the thunk. */
|
||||
parms = TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (parms)));
|
||||
/* Add this */
|
||||
t = build_decl (PARM_DECL, this_identifier, pbasetype);
|
||||
SET_DECL_ARTIFICIAL (t);
|
||||
DECL_ARG_TYPE (t) = pbasetype;
|
||||
DECL_REGISTER (t) = 1;
|
||||
/* Add __in_charge. */
|
||||
parm = build_decl (PARM_DECL, in_charge_identifier, integer_type_node);
|
||||
SET_DECL_ARTIFICIAL (parm);
|
||||
DECL_ARG_TYPE (parm) = integer_type_node;
|
||||
TREE_CHAIN (parm) = t;
|
||||
t = parm;
|
||||
|
||||
while (parms)
|
||||
{
|
||||
tree x = copy_node (parms);
|
||||
TREE_CHAIN (x) = t;
|
||||
DECL_CONTEXT (x) = decl;
|
||||
t = x;
|
||||
parms = TREE_CHAIN (parms);
|
||||
}
|
||||
parms = nreverse (t);
|
||||
DECL_ARGUMENTS (decl) = parms;
|
||||
|
||||
DECL_ASSEMBLER_NAME (decl)
|
||||
= build_decl_overload (DECL_NAME (decl),
|
||||
TYPE_ARG_TYPES (TREE_TYPE (decl)), 2);
|
||||
|
||||
ctors = CLASSTYPE_METHOD_VEC (basetype);
|
||||
if (ctors)
|
||||
ctors = TREE_VEC_ELT (ctors, 0);
|
||||
for ( ; ctors; ctors = OVL_NEXT (ctors))
|
||||
if (DECL_ASSEMBLER_NAME (OVL_CURRENT (ctors))
|
||||
== DECL_ASSEMBLER_NAME (decl))
|
||||
break;
|
||||
|
||||
if (!ctors)
|
||||
{
|
||||
add_method (basetype, 0, decl);
|
||||
cp_finish_decl (decl, NULL_TREE, NULL_TREE, 0, 0);
|
||||
}
|
||||
else
|
||||
decl = OVL_CURRENT (ctors);
|
||||
|
||||
/* Remember the original function. */
|
||||
DECL_VLIST_CTOR_WRAPPED (decl) = fn;
|
||||
|
||||
/* When fn is declared, DECL_INITIAL is null. When it is defined,
|
||||
DECL_INITIAL will be error_mark_node. */
|
||||
if (DECL_INITIAL (fn) == error_mark_node)
|
||||
{
|
||||
/* Record that the ctor is being defined, so we also emit the
|
||||
wrapper later. */
|
||||
TREE_USED (decl) = 1;
|
||||
DECL_NOT_REALLY_EXTERN (decl) = 1;
|
||||
DECL_INITIAL (decl) = NULL_TREE;
|
||||
mark_inline_for_output (decl);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
emit_vlist_ctor_wrapper (decl)
|
||||
tree decl;
|
||||
{
|
||||
tree t, parms, fn;
|
||||
|
||||
current_function_is_thunk = 1;
|
||||
|
||||
parms = DECL_ARGUMENTS (decl);
|
||||
fn = DECL_VLIST_CTOR_WRAPPED (decl);
|
||||
mark_used (fn);
|
||||
|
||||
/* Build up the call to the real function. */
|
||||
t = NULL_TREE;
|
||||
/* Push this, __in_charge. */
|
||||
t = expr_tree_cons (NULL_TREE, parms, t);
|
||||
parms = TREE_CHAIN (parms);
|
||||
t = expr_tree_cons (NULL_TREE, parms, t);
|
||||
parms = TREE_CHAIN (parms);
|
||||
/* Push 0 as __vlist. */
|
||||
t = expr_tree_cons (NULL_TREE, vlist_zero_node, t);
|
||||
/* Push rest of arguments. */
|
||||
while (parms)
|
||||
{
|
||||
t = expr_tree_cons (NULL_TREE, parms, t);
|
||||
parms = TREE_CHAIN (parms);
|
||||
}
|
||||
t = nreverse (t);
|
||||
t = build_call (fn, TREE_TYPE (TREE_TYPE (fn)), t);
|
||||
expand_expr_stmt (t);
|
||||
}
|
||||
|
||||
|
||||
/* Code for synthesizing methods which have default semantics defined. */
|
||||
|
||||
@ -2212,6 +2404,8 @@ do_build_copy_constructor (fndecl)
|
||||
|
||||
if (TYPE_USES_VIRTUAL_BASECLASSES (current_class_type))
|
||||
parm = TREE_CHAIN (parm);
|
||||
if (TYPE_USES_PVBASES (current_class_type))
|
||||
parm = TREE_CHAIN (parm);
|
||||
parm = convert_from_reference (parm);
|
||||
|
||||
if (TYPE_HAS_TRIVIAL_INIT_REF (current_class_type)
|
||||
@ -2408,6 +2602,16 @@ synthesize_method (fndecl)
|
||||
int nested = (current_function_decl != NULL_TREE);
|
||||
tree context = hack_decl_function_context (fndecl);
|
||||
|
||||
/* If this is a wrapper around a undefined vlist ctor, don't emit it
|
||||
even if it is used. */
|
||||
if (DECL_VLIST_CTOR_WRAPPER_P (fndecl))
|
||||
{
|
||||
tree orig_fn = DECL_VLIST_CTOR_WRAPPED (fndecl);
|
||||
mark_used (orig_fn);
|
||||
if (DECL_INITIAL (orig_fn) == NULL_TREE)
|
||||
return;
|
||||
}
|
||||
|
||||
if (at_eof)
|
||||
import_export_decl (fndecl);
|
||||
|
||||
@ -2429,7 +2633,11 @@ synthesize_method (fndecl)
|
||||
tree arg_chain = FUNCTION_ARG_CHAIN (fndecl);
|
||||
if (DECL_CONSTRUCTOR_FOR_VBASE_P (fndecl))
|
||||
arg_chain = TREE_CHAIN (arg_chain);
|
||||
if (arg_chain != void_list_node)
|
||||
else if (DECL_CONSTRUCTOR_FOR_PVBASE_P (fndecl))
|
||||
arg_chain = TREE_CHAIN (TREE_CHAIN (arg_chain));
|
||||
if (DECL_VLIST_CTOR_WRAPPER_P (fndecl))
|
||||
emit_vlist_ctor_wrapper (fndecl);
|
||||
else if (arg_chain != void_list_node)
|
||||
do_build_copy_constructor (fndecl);
|
||||
else if (TYPE_NEEDS_CONSTRUCTING (current_class_type))
|
||||
setup_vtbl_ptr ();
|
||||
@ -2443,3 +2651,6 @@ synthesize_method (fndecl)
|
||||
else if (nested)
|
||||
pop_cp_function_context (context);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -161,6 +161,7 @@ static int coerce_template_template_parms PROTO((tree, tree, int,
|
||||
static tree determine_specialization PROTO((tree, tree, tree *, int));
|
||||
static int template_args_equal PROTO((tree, tree));
|
||||
static void print_template_context PROTO((int));
|
||||
static int has_pvbases_p PROTO((tree, tree));
|
||||
|
||||
/* We use TREE_VECs to hold template arguments. If there is only one
|
||||
level of template arguments, then the TREE_VEC contains the
|
||||
@ -4685,6 +4686,23 @@ tsubst_friend_class (friend_tmpl, args)
|
||||
return friend_type;
|
||||
}
|
||||
|
||||
static int
|
||||
has_pvbases_p (t, pattern)
|
||||
tree t, pattern;
|
||||
{
|
||||
if (!TYPE_USES_VIRTUAL_BASECLASSES (t))
|
||||
return 0;
|
||||
|
||||
if (TYPE_USES_PVBASES (pattern))
|
||||
return 1;
|
||||
|
||||
for (t = CLASSTYPE_VBASECLASSES (t); t; t = TREE_CHAIN (t))
|
||||
if (TYPE_VIRTUAL_P (BINFO_TYPE (t)))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
tree
|
||||
instantiate_class_template (type)
|
||||
tree type;
|
||||
@ -5035,6 +5053,13 @@ instantiate_class_template (type)
|
||||
}
|
||||
}
|
||||
|
||||
/* After we have calculated the bases, we can now compute whether we
|
||||
have polymorphic vbases. This needs to happen before we
|
||||
instantiate the methods, because the constructors may take
|
||||
additional arguments. */
|
||||
if (flag_vtable_thunks >= 2)
|
||||
TYPE_USES_PVBASES (type) = has_pvbases_p (type, pattern);
|
||||
|
||||
/* Set up the list (TYPE_METHODS) and vector (CLASSTYPE_METHOD_VEC)
|
||||
for this instantiation. */
|
||||
for (t = TYPE_METHODS (pattern); t; t = TREE_CHAIN (t))
|
||||
@ -5716,9 +5741,17 @@ tsubst_decl (t, args, type, in_decl)
|
||||
SET_DECL_IMPLICIT_INSTANTIATION (r);
|
||||
register_specialization (r, gen_tmpl, argvec);
|
||||
|
||||
|
||||
if (DECL_CONSTRUCTOR_P (r) || DECL_DESTRUCTOR_P (r))
|
||||
{
|
||||
maybe_retrofit_in_chrg (r);
|
||||
grok_ctor_properties (ctx, r);
|
||||
}
|
||||
|
||||
/* Set the mangled name for R. */
|
||||
if (DECL_DESTRUCTOR_P (t))
|
||||
DECL_ASSEMBLER_NAME (r) = build_destructor_name (ctx);
|
||||
DECL_ASSEMBLER_NAME (r) =
|
||||
build_destructor_name (ctx, DECL_DESTRUCTOR_FOR_PVBASE_P (r));
|
||||
else
|
||||
{
|
||||
/* Instantiations of template functions must be mangled
|
||||
@ -5761,11 +5794,14 @@ tsubst_decl (t, args, type, in_decl)
|
||||
in_decl);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* This has now moved further up. */
|
||||
if (DECL_CONSTRUCTOR_P (r))
|
||||
{
|
||||
maybe_retrofit_in_chrg (r);
|
||||
grok_ctor_properties (ctx, r);
|
||||
}
|
||||
#endif
|
||||
if (IDENTIFIER_OPNAME_P (DECL_NAME (r)))
|
||||
grok_op_properties (r, DECL_VIRTUAL_P (r), DECL_FRIEND_P (r));
|
||||
}
|
||||
|
@ -363,8 +363,19 @@ get_tinfo_var (type)
|
||||
return tdecl;
|
||||
}
|
||||
|
||||
/* Returns the decl for a function which will return a type_info node for
|
||||
TYPE. This version does not mark the function used, for use in
|
||||
set_rtti_entry; for the vtable case, we'll get marked in
|
||||
finish_vtable_vardecl, when we know that we want to be emitted.
|
||||
|
||||
We do this to avoid emitting the tinfo node itself, since we don't
|
||||
currently support DECL_DEFER_OUTPUT for variables. Also, we don't
|
||||
associate constant pools with their functions properly, so we would
|
||||
emit string constants and such even though we don't emit the actual
|
||||
function. When those bugs are fixed, this function should go away. */
|
||||
|
||||
tree
|
||||
get_tinfo_fn (type)
|
||||
get_tinfo_fn_unused (type)
|
||||
tree type;
|
||||
{
|
||||
tree name;
|
||||
@ -393,13 +404,23 @@ get_tinfo_fn (type)
|
||||
|
||||
pushdecl_top_level (d);
|
||||
make_function_rtl (d);
|
||||
mark_used (d);
|
||||
mark_inline_for_output (d);
|
||||
pop_obstacks ();
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
/* Likewise, but also mark it used. Called by various EH and RTTI code. */
|
||||
|
||||
tree
|
||||
get_tinfo_fn (type)
|
||||
tree type;
|
||||
{
|
||||
tree d = get_tinfo_fn_unused (type);
|
||||
mark_used (d);
|
||||
return d;
|
||||
}
|
||||
|
||||
tree
|
||||
get_typeid_1 (type)
|
||||
tree type;
|
||||
|
@ -151,6 +151,9 @@ static int protected_accessible_p PROTO ((tree, tree, tree, tree));
|
||||
static int friend_accessible_p PROTO ((tree, tree, tree, tree));
|
||||
static void setup_class_bindings PROTO ((tree, int));
|
||||
static int template_self_reference_p PROTO ((tree, tree));
|
||||
static void expand_direct_vtbls_init_thunks PROTO((tree, tree, int));
|
||||
static void expand_indirect_vtbls_init_thunks PROTO((tree, tree, tree));
|
||||
|
||||
|
||||
/* Allocate a level of searching. */
|
||||
|
||||
@ -2626,6 +2629,117 @@ fixup_virtual_upcast_offsets (real_binfo, binfo, init_self, can_elide, addr, ori
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit initialization of vfields of BASE, where the complete object
|
||||
is pointed to by decl_ptr. DO_SELF indicates we have to do our own
|
||||
vfield, otherwise only proceed to our own direct non-virtual bases. */
|
||||
|
||||
static void
|
||||
expand_direct_vtbls_init_thunks (base, decl_ptr, do_self)
|
||||
tree base, decl_ptr;
|
||||
int do_self;
|
||||
{
|
||||
tree addr, expr;
|
||||
tree type = BINFO_TYPE (base);
|
||||
tree binfos = BINFO_BASETYPES (base);
|
||||
int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0;
|
||||
tree vlist = lookup_name (vlist_identifier, 0);
|
||||
int in_dtor = DECL_DESTRUCTOR_FOR_PVBASE_P (current_function_decl);
|
||||
|
||||
my_friendly_assert (vlist != NULL_TREE, 990320);
|
||||
|
||||
if (in_dtor)
|
||||
/* In the destructor, we find the vfield pointers for the bases in
|
||||
reverse order, before we find our own vfield pointer. */
|
||||
for (i = n_baselinks - 1; i >= 0; i--)
|
||||
{
|
||||
tree base_binfo = TREE_VEC_ELT (binfos, i);
|
||||
int is_not_base_vtable
|
||||
= i != CLASSTYPE_VFIELD_PARENT (type);
|
||||
if (! TREE_VIA_VIRTUAL (base_binfo))
|
||||
expand_direct_vtbls_init_thunks (base_binfo, decl_ptr,
|
||||
is_not_base_vtable);
|
||||
}
|
||||
|
||||
if (do_self && CLASSTYPE_VFIELDS (type))
|
||||
{
|
||||
addr = build_vbase_path (PLUS_EXPR, build_pointer_type (type),
|
||||
decl_ptr, base, 1);
|
||||
addr = build_indirect_ref (addr, "vptr");
|
||||
addr = build_vfield_ref (addr, type);
|
||||
|
||||
/* In a destructor, we decrease the vlist before we retrieve the
|
||||
value. In a constructor, we increase the vlist after we
|
||||
retrieve the value. */
|
||||
if (in_dtor)
|
||||
{
|
||||
expr = build_binary_op (MINUS_EXPR, vlist, integer_one_node);
|
||||
expr = build_modify_expr (vlist, NOP_EXPR, expr);
|
||||
expand_expr_stmt (expr);
|
||||
}
|
||||
|
||||
/* Store the next vptr into the vbase's vptr. */
|
||||
expr = build_indirect_ref (vlist, "__vlist");
|
||||
expr = convert_force (TREE_TYPE (addr), expr, 0);
|
||||
expr = build_modify_expr (addr, NOP_EXPR, expr);
|
||||
expand_expr_stmt (expr);
|
||||
|
||||
/* Advance the vlist. */
|
||||
if (!in_dtor)
|
||||
{
|
||||
expr = build_binary_op (PLUS_EXPR, vlist, integer_one_node);
|
||||
expr = build_modify_expr (vlist, NOP_EXPR, expr);
|
||||
expand_expr_stmt (expr);
|
||||
}
|
||||
}
|
||||
|
||||
if (!in_dtor)
|
||||
for (i = 0; i < n_baselinks; i++)
|
||||
{
|
||||
tree base_binfo = TREE_VEC_ELT (binfos, i);
|
||||
int is_not_base_vtable = i != CLASSTYPE_VFIELD_PARENT (type);
|
||||
if (! TREE_VIA_VIRTUAL (base_binfo))
|
||||
expand_direct_vtbls_init_thunks (base_binfo, decl_ptr,
|
||||
is_not_base_vtable);
|
||||
}
|
||||
}
|
||||
|
||||
/* Like expand_indirect_vtbls_init below, but based on the vtable list
|
||||
passed to the constructor. */
|
||||
|
||||
static void
|
||||
expand_indirect_vtbls_init_thunks (binfo, true_exp, decl_ptr)
|
||||
tree binfo;
|
||||
tree true_exp, decl_ptr;
|
||||
{
|
||||
tree type = BINFO_TYPE (binfo);
|
||||
tree vbases = CLASSTYPE_VBASECLASSES (type);
|
||||
struct vbase_info vi;
|
||||
|
||||
vi.decl_ptr = (true_exp ? build_unary_op (ADDR_EXPR, true_exp, 0)
|
||||
: decl_ptr);
|
||||
vi.vbase_types = vbases;
|
||||
|
||||
dfs_walk (binfo, dfs_find_vbases, unmarked_new_vtablep, &vi);
|
||||
|
||||
/* Initialized with vtables of type TYPE. */
|
||||
for (; vbases; vbases = TREE_CHAIN (vbases))
|
||||
{
|
||||
tree addr;
|
||||
|
||||
if (!CLASSTYPE_VFIELD (BINFO_TYPE (vbases)))
|
||||
/* This vbase doesn't have a vptr of its own. */
|
||||
/* FIXME: check */
|
||||
continue;
|
||||
|
||||
addr = convert_pointer_to_vbase (TREE_TYPE (vbases), decl_ptr);
|
||||
expand_direct_vtbls_init_thunks (TYPE_BINFO (BINFO_TYPE (vbases)),
|
||||
addr, 1);
|
||||
|
||||
}
|
||||
|
||||
dfs_walk (binfo, dfs_clear_vbase_slots, marked_new_vtablep, 0);
|
||||
}
|
||||
|
||||
/* Build a COMPOUND_EXPR which when expanded will generate the code
|
||||
needed to initialize all the virtual function table slots of all
|
||||
the virtual baseclasses. MAIN_BINFO is the binfo which determines
|
||||
@ -2657,6 +2771,12 @@ expand_indirect_vtbls_init (binfo, true_exp, decl_ptr)
|
||||
|
||||
mark_all_temps_used();
|
||||
|
||||
if (TYPE_USES_PVBASES (type))
|
||||
{
|
||||
expand_indirect_vtbls_init_thunks (binfo, true_exp, decl_ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TYPE_USES_VIRTUAL_BASECLASSES (type))
|
||||
{
|
||||
rtx fixup_insns = NULL_RTX;
|
||||
|
@ -6695,7 +6695,10 @@ void
|
||||
thread_prologue_and_epilogue_insns (f)
|
||||
rtx f ATTRIBUTE_UNUSED;
|
||||
{
|
||||
int insertted = 0;
|
||||
int inserted = 0;
|
||||
#ifdef HAVE_prologue
|
||||
rtx prologue_end = NULL_RTX;
|
||||
#endif
|
||||
|
||||
prologue = 0;
|
||||
#ifdef HAVE_prologue
|
||||
@ -6712,7 +6715,7 @@ thread_prologue_and_epilogue_insns (f)
|
||||
seq = get_insns ();
|
||||
prologue = record_insns (seq);
|
||||
|
||||
emit_note (NULL, NOTE_INSN_PROLOGUE_END);
|
||||
prologue_end = emit_note (NULL, NOTE_INSN_PROLOGUE_END);
|
||||
seq = gen_sequence ();
|
||||
end_sequence ();
|
||||
|
||||
@ -6725,7 +6728,7 @@ thread_prologue_and_epilogue_insns (f)
|
||||
abort ();
|
||||
|
||||
insert_insn_on_edge (seq, ENTRY_BLOCK_PTR->succ);
|
||||
insertted = 1;
|
||||
inserted = 1;
|
||||
}
|
||||
else
|
||||
emit_insn_after (seq, f);
|
||||
@ -6857,8 +6860,56 @@ thread_prologue_and_epilogue_insns (f)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (insertted)
|
||||
if (inserted)
|
||||
commit_edge_insertions ();
|
||||
|
||||
#ifdef HAVE_prologue
|
||||
if (prologue_end)
|
||||
{
|
||||
rtx insn, prev;
|
||||
|
||||
/* GDB handles `break f' by setting a breakpoint on the first
|
||||
line note *after* the prologue. Which means (1) that if
|
||||
there are line number notes before where we inserted the
|
||||
prologue we should move them, and (2) if there is no such
|
||||
note, then we should generate one at the prologue. */
|
||||
|
||||
for (insn = prologue_end; insn ; insn = prev)
|
||||
{
|
||||
prev = PREV_INSN (insn);
|
||||
if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
|
||||
{
|
||||
/* Note that we cannot reorder the first insn in the
|
||||
chain, since rest_of_compilation relies on that
|
||||
remaining constant. Do the next best thing. */
|
||||
if (prev == NULL)
|
||||
{
|
||||
emit_line_note_after (NOTE_SOURCE_FILE (insn),
|
||||
NOTE_LINE_NUMBER (insn),
|
||||
prologue_end);
|
||||
NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
|
||||
}
|
||||
else
|
||||
reorder_insns (insn, insn, prologue_end);
|
||||
}
|
||||
}
|
||||
|
||||
insn = NEXT_INSN (prologue_end);
|
||||
if (! insn || GET_CODE (insn) != NOTE || NOTE_LINE_NUMBER (insn) <= 0)
|
||||
{
|
||||
for (insn = next_active_insn (f); insn ; insn = PREV_INSN (insn))
|
||||
{
|
||||
if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
|
||||
{
|
||||
emit_line_note_after (NOTE_SOURCE_FILE (insn),
|
||||
NOTE_LINE_NUMBER (insn),
|
||||
prologue_end);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Reposition the prologue-end and epilogue-begin notes after instruction
|
||||
|
@ -1186,7 +1186,7 @@ anachronism. Therefore, by default it is invalid to assign to
|
||||
type @samp{X *}. However, for backwards compatibility, you can make it
|
||||
valid with @samp{-fthis-is-variable}.
|
||||
|
||||
@item -fvtable-thunks
|
||||
@item -fvtable-thunks=@var{thunks-version}
|
||||
Use @samp{thunks} to implement the virtual function dispatch table
|
||||
(@samp{vtable}). The traditional (cfront-style) approach to
|
||||
implementing vtables was to store a pointer to the function and two
|
||||
@ -1194,13 +1194,27 @@ offsets for adjusting the @samp{this} pointer at the call site. Newer
|
||||
implementations store a single pointer to a @samp{thunk} function which
|
||||
does any necessary adjustment and then calls the target function.
|
||||
|
||||
The original implementation of thunks (version 1) had a bug regarding
|
||||
virtual base classes; this bug is fixed with version 2 of the thunks
|
||||
implementation. With setting the version to 2, compatibility to the
|
||||
version 1 thunks is provided, at the cost of extra machine code. Version
|
||||
3 does not include this compatibility.
|
||||
|
||||
This option also enables a heuristic for controlling emission of
|
||||
vtables; if a class has any non-inline virtual functions, the vtable
|
||||
will be emitted in the translation unit containing the first one of
|
||||
those.
|
||||
|
||||
Like all options that change the ABI, all C++ code, @emph{including
|
||||
libgcc.a} must be built with the same setting of this option.
|
||||
libgcc.a} must be built with the same setting of this option. Since
|
||||
version 1 and version 2 are also incompatible (for classes with virtual
|
||||
bases defining virtual functions), all code must also be compiled with
|
||||
the same version.
|
||||
|
||||
On some targets (e.g. Linux/GNU), version 2 thunks are the default. On these
|
||||
targets, no option or -fvtable-thunks will produce version 2 thunks. On
|
||||
all other targets, not giving the option will use the traditional
|
||||
implementation, and -fvtable-thunks will produce version 2 thunks.
|
||||
|
||||
@item -nostdinc++
|
||||
Do not search for header files in the standard directories specific to
|
||||
|
@ -3682,10 +3682,14 @@ loop_iterations (loop_start, loop_end, loop_info)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The only new registers that care created before loop iterations are
|
||||
givs made from biv increments, so this should never occur. */
|
||||
/* The only new registers that are created before loop iterations
|
||||
are givs made from biv increments or registers created by
|
||||
load_mems. In the latter case, it is possible that try_copy_prop
|
||||
will propagate a new pseudo into the old iteration register but
|
||||
this will be marked by having the REG_USERVAR_P bit set. */
|
||||
|
||||
if ((unsigned) REGNO (iteration_var) >= reg_iv_type->num_elements)
|
||||
if ((unsigned) REGNO (iteration_var) >= reg_iv_type->num_elements
|
||||
&& ! REG_USERVAR_P (iteration_var))
|
||||
abort ();
|
||||
|
||||
iteration_info (iteration_var, &initial_value, &increment,
|
||||
|
@ -1,3 +1,8 @@
|
||||
2000-04-24 Magnus Fromreide <magfr@lysator.liu.se>
|
||||
|
||||
* sstream: New file.
|
||||
* Makefile.in (HEADERS): Add it.
|
||||
|
||||
2000-03-12 Gabriel Dos Reis <dosreis@cmla.ens-cachan.fr>
|
||||
|
||||
* std/bastring.h (basic_string<>::push_back): Define.
|
||||
|
@ -28,7 +28,8 @@ HEADERS= cassert cctype cerrno cfloat ciso646 climits clocale cmath complex \
|
||||
cwchar cwctype string stdexcept \
|
||||
algorithm deque functional hash_map hash_set iterator list map \
|
||||
memory numeric pthread_alloc queue rope set slist stack utility \
|
||||
vector fstream iomanip iostream strstream iosfwd bitset valarray
|
||||
vector fstream iomanip iostream strstream iosfwd bitset valarray \
|
||||
sstream
|
||||
|
||||
ARLIB = libstdc++.a.$(VERSION)
|
||||
ARLINK = libstdc++.a
|
||||
|
225
contrib/libstdc++/sstream
Normal file
225
contrib/libstdc++/sstream
Normal file
@ -0,0 +1,225 @@
|
||||
/* This is part of libio/iostream, providing -*- C++ -*- input/output.
|
||||
Copyright (C) 2000 Free Software Foundation
|
||||
|
||||
This file is part of the GNU IO Library. This library is free
|
||||
software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this library; see the file COPYING. If not, write to the Free
|
||||
Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if you link this library with files
|
||||
compiled with a GNU compiler to produce an executable, this does not cause
|
||||
the resulting executable to be covered by the GNU General Public License.
|
||||
This exception does not however invalidate any other reasons why
|
||||
the executable file might be covered by the GNU General Public License. */
|
||||
|
||||
/* Written by Magnus Fromreide (magfr@lysator.liu.se). */
|
||||
|
||||
#ifndef __SSTREAM__
|
||||
#define __SSTREAM__
|
||||
|
||||
#include <string>
|
||||
#include <iostream.h>
|
||||
#include <streambuf.h>
|
||||
|
||||
namespace std
|
||||
{
|
||||
class stringbuf : public streambuf
|
||||
{
|
||||
public:
|
||||
typedef char char_type;
|
||||
typedef int int_type;
|
||||
typedef streampos pos_type;
|
||||
typedef streamoff off_type;
|
||||
|
||||
explicit stringbuf(int which=ios::in|ios::out) :
|
||||
streambuf(which), buf(), mode(static_cast<ios::open_mode>(which)),
|
||||
rpos(0), bufsize(1)
|
||||
{ }
|
||||
|
||||
explicit stringbuf(const std::string &s, int which=ios::in|ios::out) :
|
||||
streambuf(which), buf(s), mode(static_cast<ios::open_mode>(which)),
|
||||
bufsize(1)
|
||||
{
|
||||
if(mode & ios::in)
|
||||
{
|
||||
setg(&defbuf, &defbuf + bufsize, &defbuf + bufsize);
|
||||
}
|
||||
if(mode & ios::out)
|
||||
{
|
||||
setp(&defbuf, &defbuf + bufsize);
|
||||
}
|
||||
rpos = (mode & ios::ate ? s.size() : 0);
|
||||
}
|
||||
|
||||
std::string str() const
|
||||
{
|
||||
const_cast<stringbuf*>(this)->sync(); // Sigh, really ugly hack
|
||||
return buf;
|
||||
};
|
||||
|
||||
void str(const std::string& s)
|
||||
{
|
||||
buf = s;
|
||||
if(mode & ios::in)
|
||||
{
|
||||
gbump(egptr() - gptr());
|
||||
}
|
||||
if(mode & ios::out)
|
||||
{
|
||||
pbump(pbase() - pptr());
|
||||
}
|
||||
rpos = (mode & ios::ate ? s.size() : 0);
|
||||
}
|
||||
|
||||
protected:
|
||||
inline virtual int sync();
|
||||
inline virtual int overflow(int = EOF);
|
||||
inline virtual int underflow();
|
||||
private:
|
||||
std::string buf;
|
||||
ios::open_mode mode;
|
||||
std::string::size_type rpos;
|
||||
streamsize bufsize;
|
||||
char defbuf;
|
||||
};
|
||||
|
||||
class stringstreambase : virtual public ios {
|
||||
protected:
|
||||
stringbuf __my_sb;
|
||||
public:
|
||||
std::string str() const
|
||||
{
|
||||
return dynamic_cast<stringbuf*>(_strbuf)->str();
|
||||
}
|
||||
void str(const std::string& s)
|
||||
{
|
||||
clear();
|
||||
dynamic_cast<stringbuf*>(_strbuf)->str(s);
|
||||
}
|
||||
|
||||
stringbuf* rdbuf()
|
||||
{
|
||||
return &__my_sb;
|
||||
}
|
||||
protected:
|
||||
stringstreambase(int which) :
|
||||
__my_sb(which)
|
||||
{
|
||||
init (&__my_sb);
|
||||
}
|
||||
|
||||
stringstreambase(const std::string& s, int which) :
|
||||
__my_sb(s, which)
|
||||
{
|
||||
init (&__my_sb);
|
||||
}
|
||||
};
|
||||
|
||||
class istringstream : public stringstreambase, public istream {
|
||||
public:
|
||||
istringstream(int which=ios::in) :
|
||||
stringstreambase(which)
|
||||
{ }
|
||||
|
||||
istringstream(const std::string& s, int which=ios::in) :
|
||||
stringstreambase(s, which)
|
||||
{ }
|
||||
};
|
||||
|
||||
class ostringstream : public stringstreambase, public ostream {
|
||||
public:
|
||||
ostringstream(int which=ios::out) :
|
||||
stringstreambase(which)
|
||||
{ }
|
||||
|
||||
ostringstream(const std::string& s, int which=ios::out) :
|
||||
stringstreambase(s, which)
|
||||
{ }
|
||||
};
|
||||
|
||||
class stringstream : public stringstreambase, public iostream {
|
||||
public:
|
||||
stringstream(int which=ios::in|ios::out) :
|
||||
stringstreambase(which)
|
||||
{ }
|
||||
|
||||
stringstream(const std::string &s, int which=ios::in|ios::out) :
|
||||
stringstreambase(s, which)
|
||||
{ }
|
||||
};
|
||||
}
|
||||
|
||||
inline int std::stringbuf::sync()
|
||||
{
|
||||
if((mode & ios::out) == 0)
|
||||
return EOF;
|
||||
|
||||
streamsize n = pptr() - pbase();
|
||||
if(n)
|
||||
{
|
||||
buf.replace(rpos, std::string::npos, pbase(), n);
|
||||
if(buf.size() - rpos != n)
|
||||
return EOF;
|
||||
rpos += n;
|
||||
pbump(-n);
|
||||
gbump(egptr() - gptr());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int std::stringbuf::overflow(int ch)
|
||||
{
|
||||
if((mode & ios::out) == 0)
|
||||
return EOF;
|
||||
|
||||
streamsize n = pptr() - pbase();
|
||||
|
||||
if(n && sync())
|
||||
return EOF;
|
||||
|
||||
if(ch != EOF)
|
||||
{
|
||||
std::string::size_type oldSize = buf.size();
|
||||
|
||||
buf.replace(rpos, std::string::npos, ch);
|
||||
if(buf.size() - oldSize != 1)
|
||||
return EOF;
|
||||
++rpos;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int std::stringbuf::underflow()
|
||||
{
|
||||
sync();
|
||||
if((mode & ios::in) == 0)
|
||||
{
|
||||
return EOF;
|
||||
}
|
||||
if(rpos >= buf.size())
|
||||
{
|
||||
return EOF;
|
||||
}
|
||||
|
||||
std::string::size_type n = egptr() - eback();
|
||||
std::string::size_type s;
|
||||
|
||||
s = buf.copy(eback(), n, rpos);
|
||||
pbump(pbase() - pptr());
|
||||
gbump(eback() - gptr());
|
||||
int res = (0377 & buf[rpos]);
|
||||
rpos += s;
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif /* not __STRSTREAM__ */
|
Loading…
Reference in New Issue
Block a user