mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-03 12:35:02 +00:00
Fix conflicts.
This commit is contained in:
parent
1318f6d724
commit
507f261243
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=60970
@ -190,6 +190,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 *));
|
||||
@ -337,6 +339,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. */
|
||||
|
||||
@ -6285,6 +6296,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);
|
||||
@ -6512,6 +6524,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);
|
||||
@ -6851,6 +6864,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. */
|
||||
|
||||
@ -11388,6 +11411,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)
|
||||
@ -12082,6 +12109,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;
|
||||
@ -12089,7 +12119,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))
|
||||
@ -12119,7 +12154,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,
|
||||
@ -12128,6 +12164,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
|
||||
@ -12925,6 +12970,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))
|
||||
@ -13930,6 +13985,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.
|
||||
@ -13958,7 +14320,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;
|
||||
@ -14020,191 +14381,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
|
||||
@ -14234,67 +14411,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. */
|
||||
|
@ -27,6 +27,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
|
||||
|
||||
@ -191,7 +192,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
|
||||
|
||||
@ -1456,7 +1459,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,
|
||||
@ -1840,7 +1843,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
|
||||
|
||||
|
@ -6704,7 +6704,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
|
||||
@ -6721,7 +6724,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 ();
|
||||
|
||||
@ -6734,7 +6737,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);
|
||||
@ -6866,8 +6869,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
|
||||
|
@ -1190,7 +1190,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
|
||||
@ -1198,13 +1198,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
|
||||
|
Loading…
Reference in New Issue
Block a user