This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
ia64 c++ abi: descriptors in vtables
- To: gcc-patches at gcc dot gnu dot org
- Subject: ia64 c++ abi: descriptors in vtables
- From: Richard Henderson <rth at redhat dot com>
- Date: Thu, 13 Sep 2001 23:32:42 -0700
I'd appreciate it if some c++ folk would give this a once-over,
as I flailed about a bit trying to figure out how vtables for
virtual bases are constructed. In particular, look at the comment
in add_vcall_offset_vtbl_entries_1. I think I may just not
understand what "vcall" means in this context.
The following is against mainline; very minor modifications are
required for the 3.0 branch.
I've not touched Java yet, though I plan to do so.
r~
* defaults.h (TARGET_VTABLE_USES_DESCRIPTORS): New.
* tree.def (FDESC_EXPR): New.
* expr.c (expand_expr): Handle it.
* varasm.c (initializer_constant_valid_p): Likewise.
(output_constant): Likewise.
* config/ia64/ia64.h (TARGET_VTABLE_USES_DESCRIPTORS): New.
(ASM_OUTPUT_FDESC): New.
* cp/call.c (build_over_call): Use build_vfn_ref.
* cp/class.c (build_vfn_ref): New.
(set_vindex): Account for TARGET_VTABLE_USES_DESCRIPTORS.
(build_vtbl_initializer): Likewise.
* cp/cp-tree.h (build_vfn_ref): Declare.
* cp/decl2.c (mark_vtable_entries): Handle FDESC_EXPR.
* cp/typeck.c (get_member_function_from_ptrfunc): Handle
function descriptors.
* g++.old-deja/g++.abi/ptrmem.C: Update for ia64 function descriptors.
* g++.old-deja/g++.abi/vtable2.C: Likewise.
Index: gcc/defaults.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/defaults.h,v
retrieving revision 1.55
diff -c -p -d -r1.55 defaults.h
*** defaults.h 2001/08/22 14:34:56 1.55
--- defaults.h 2001/09/14 06:08:03
*************** do { \
*** 358,363 ****
--- 358,373 ----
#define PREFERRED_STACK_BOUNDARY STACK_BOUNDARY
#endif
+ /* By default, the C++ compiler will use function addresses in the
+ vtable entries. Setting this non-zero tells the compiler to use
+ function descriptors instead. The value of this macro says how
+ many words wide the descriptor is (normally 2). It is assumed
+ that the address of a function descriptor may be treated as a
+ pointer to a function. */
+ #ifndef TARGET_VTABLE_USES_DESCRIPTORS
+ #define TARGET_VTABLE_USES_DESCRIPTORS 0
+ #endif
+
/* Select a format to encode pointers in exception handling data. We
prefer those that result in fewer dynamic relocations. Assume no
special support here and encode direct references. */
Index: gcc/expr.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/expr.c,v
retrieving revision 1.351
diff -c -p -d -r1.351 expr.c
*** expr.c 2001/09/06 08:59:36 1.351
--- expr.c 2001/09/14 06:08:04
*************** expand_expr (exp, target, tmode, modifie
*** 8755,8760 ****
--- 8755,8765 ----
case EXC_PTR_EXPR:
return get_exception_pointer (cfun);
+ case FDESC_EXPR:
+ /* Function descriptors are not valid except for as
+ initialization constants, and should not be expanded. */
+ abort ();
+
default:
return (*lang_expand_expr) (exp, original_target, tmode, modifier);
}
Index: gcc/tree.def
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree.def,v
retrieving revision 1.45
diff -c -p -d -r1.45 tree.def
*** tree.def 2001/09/06 23:49:38 1.45
--- tree.def 2001/09/14 06:08:04
*************** DEFTREECODE (ADDR_EXPR, "addr_expr", 'e'
*** 716,723 ****
DEFTREECODE (REFERENCE_EXPR, "reference_expr", 'e', 1)
/* Operand is a function constant; result is a function variable value
! of typeEPmode. Used only for languages that need static chains. */
DEFTREECODE (ENTRY_VALUE_EXPR, "entry_value_expr", 'e', 1)
/* Given two real or integer operands of the same type,
returns a complex value of the corresponding complex type. */
--- 716,727 ----
DEFTREECODE (REFERENCE_EXPR, "reference_expr", 'e', 1)
/* Operand is a function constant; result is a function variable value
! of type EPmode. Used only for languages that need static chains. */
DEFTREECODE (ENTRY_VALUE_EXPR, "entry_value_expr", 'e', 1)
+
+ /* Operand0 is a function constant; result is part N of a function
+ descriptor of type ptr_mode. */
+ DEFTREECODE (FDESC_EXPR, "fdesc_expr", 'e', 2)
/* Given two real or integer operands of the same type,
returns a complex value of the corresponding complex type. */
Index: gcc/varasm.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/varasm.c,v
retrieving revision 1.207
diff -c -p -d -r1.207 varasm.c
*** varasm.c 2001/09/13 14:37:12 1.207
--- varasm.c 2001/09/14 06:08:04
*************** initializer_constant_valid_p (value, end
*** 4277,4282 ****
--- 4277,4283 ----
return null_pointer_node;
case ADDR_EXPR:
+ case FDESC_EXPR:
return staticp (TREE_OPERAND (value, 0)) ? TREE_OPERAND (value, 0) : 0;
case NON_LVALUE_EXPR:
*************** output_constant (exp, size, align)
*** 4466,4471 ****
--- 4467,4484 ----
if (TREE_CODE (exp) == CONSTRUCTOR && CONSTRUCTOR_ELTS (exp) == 0)
{
assemble_zeros (size);
+ return;
+ }
+
+ if (TREE_CODE (exp) == FDESC_EXPR)
+ {
+ HOST_WIDE_INT part = tree_low_cst (TREE_OPERAND (exp, 1), 0);
+ tree decl = TREE_OPERAND (exp, 0);
+ #ifdef ASM_OUTPUT_FDESC
+ ASM_OUTPUT_FDESC (asm_out_file, decl, part);
+ #else
+ abort ();
+ #endif
return;
}
Index: gcc/config/ia64/ia64.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/ia64/ia64.h,v
retrieving revision 1.87
diff -c -p -d -r1.87 ia64.h
*** ia64.h 2001/08/23 07:44:03 1.87
--- ia64.h 2001/09/14 06:08:04
*************** while (0)
*** 416,421 ****
--- 416,428 ----
/* A code distinguishing the floating point format of the target machine. */
#define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT
+ /* By default, the C++ compiler will use function addresses in the
+ vtable entries. Setting this non-zero tells the compiler to use
+ function descriptors instead. The value of this macro says how
+ many words wide the descriptor is (normally 2). It is assumed
+ that the address of a function descriptor may be treated as a
+ pointer to a function. */
+ #define TARGET_VTABLE_USES_DESCRIPTORS 2
/* Layout of Source Language Data Types */
*************** do { \
*** 1534,1539 ****
--- 1541,1557 ----
fprintf (FILE, "\n"); \
} while (0)
+ /* Output part N of a function descriptor for DECL. For ia64, both
+ words are emitted with a single relocation, so ignore N > 0. */
+ #define ASM_OUTPUT_FDESC(FILE, DECL, PART) \
+ do { \
+ if ((PART) == 0) \
+ { \
+ fputs ("\tdata16.ua @iplt(", FILE); \
+ assemble_name (FILE, XSTR (XEXP (DECL_RTL (DECL), 0), 0)); \
+ fputs (")\n", FILE); \
+ } \
+ } while (0)
/* Generating Code for Profiling. */
Index: gcc/cp/call.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/call.c,v
retrieving revision 1.282
diff -c -p -d -r1.282 call.c
*** call.c 2001/09/04 12:21:32 1.282
--- call.c 2001/09/14 06:08:04
*************** build_over_call (cand, args, flags)
*** 4321,4327 ****
if (DECL_CONTEXT (fn) && TYPE_JAVA_INTERFACE (DECL_CONTEXT (fn)))
fn = build_java_interface_fn_ref (fn, *p);
else
! fn = build_vtbl_ref (build_indirect_ref (*p, 0), DECL_VINDEX (fn));
TREE_TYPE (fn) = t;
}
else if (DECL_INLINE (fn))
--- 4321,4327 ----
if (DECL_CONTEXT (fn) && TYPE_JAVA_INTERFACE (DECL_CONTEXT (fn)))
fn = build_java_interface_fn_ref (fn, *p);
else
! fn = build_vfn_ref (build_indirect_ref (*p, 0), DECL_VINDEX (fn));
TREE_TYPE (fn) = t;
}
else if (DECL_INLINE (fn))
Index: gcc/cp/class.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/class.c,v
retrieving revision 1.409
diff -c -p -d -r1.409 class.c
*** class.c 2001/09/06 09:41:29 1.409
--- class.c 2001/09/14 06:08:04
*************** build_vtable_entry_ref (basetype, idx)
*** 459,467 ****
}
/* Given an object INSTANCE, return an expression which yields the
! virtual function vtable element corresponding to INDEX. There are
! many special cases for INSTANCE which we take care of here, mainly
! to avoid creating extra tree nodes when we don't have to. */
tree
build_vtbl_ref (instance, idx)
--- 459,467 ----
}
/* Given an object INSTANCE, return an expression which yields the
! vtable element corresponding to INDEX. There are many special
! cases for INSTANCE which we take care of here, mainly to avoid
! creating extra tree nodes when we don't have to. */
tree
build_vtbl_ref (instance, idx)
*************** build_vtbl_ref (instance, idx)
*** 543,548 ****
--- 543,566 ----
return aref;
}
+ /* Given an object INSTANCE, return an expression which yields a
+ function pointer corresponding to vtable element INDEX. */
+
+ tree
+ build_vfn_ref (instance, idx)
+ tree instance, idx;
+ {
+ tree aref = build_vtbl_ref (instance, idx);
+
+ /* When using function descriptors, the address of the
+ vtable entry is treated as a function pointer. */
+ if (TARGET_VTABLE_USES_DESCRIPTORS)
+ return build1 (NOP_EXPR, TREE_TYPE (aref),
+ build_unary_op (ADDR_EXPR, aref, /*noconvert=*/1));
+
+ return aref;
+ }
+
/* Return the name of the virtual function table (as an IDENTIFIER_NODE)
for the given TYPE. */
*************** set_vindex (decl, vfuns_p)
*** 823,829 ****
{
int vindex;
! vindex = (*vfuns_p)++;
DECL_VINDEX (decl) = build_shared_int_cst (vindex);
}
--- 841,849 ----
{
int vindex;
! vindex = *vfuns_p;
! *vfuns_p += (TARGET_VTABLE_USES_DESCRIPTORS
! ? TARGET_VTABLE_USES_DESCRIPTORS : 1);
DECL_VINDEX (decl) = build_shared_int_cst (vindex);
}
*************** build_vtbl_initializer (binfo, orig_binf
*** 7587,7593 ****
}
/* And add it to the chain of initializers. */
! vfun_inits = tree_cons (NULL_TREE, init, vfun_inits);
}
/* The initializers for virtual functions were built up in reverse
--- 7607,7631 ----
}
/* And add it to the chain of initializers. */
! if (TARGET_VTABLE_USES_DESCRIPTORS)
! {
! int i;
! if (init == size_zero_node)
! for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
! vfun_inits = tree_cons (NULL_TREE, init, vfun_inits);
! else
! for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i)
! {
! tree fdesc = build (FDESC_EXPR, vfunc_ptr_type_node,
! TREE_OPERAND (init, 0),
! build_int_2 (i, 0));
! TREE_CONSTANT (fdesc) = 1;
!
! vfun_inits = tree_cons (NULL_TREE, fdesc, vfun_inits);
! }
! }
! else
! vfun_inits = tree_cons (NULL_TREE, init, vfun_inits);
}
/* The initializers for virtual functions were built up in reverse
*************** add_vcall_offset_vtbl_entries_1 (binfo,
*** 7934,7939 ****
--- 7972,7978 ----
/* The next vcall offset will be found at a more negative
offset. */
+ /* ??? Check TARGET_VTABLE_USES_DESCRIPTORS. */
vid->index = size_binop (MINUS_EXPR, vid->index, ssize_int (1));
/* Keep track of this function. */
Index: gcc/cp/cp-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-tree.h,v
retrieving revision 1.640
diff -c -p -d -r1.640 cp-tree.h
*** cp-tree.h 2001/09/06 08:59:38 1.640
--- cp-tree.h 2001/09/14 06:08:04
*************** extern tree perform_implicit_conversion
*** 3527,3532 ****
--- 3527,3533 ----
/* in class.c */
extern tree build_vbase_path PARAMS ((enum tree_code, tree, tree, tree, int));
extern tree build_vtbl_ref PARAMS ((tree, tree));
+ extern tree build_vfn_ref PARAMS ((tree, tree));
extern tree get_vtable_decl PARAMS ((tree, int));
extern void add_method PARAMS ((tree, tree, int));
extern int currently_open_class PARAMS ((tree));
Index: gcc/cp/decl2.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/decl2.c,v
retrieving revision 1.491
diff -c -p -d -r1.491 decl2.c
*** decl2.c 2001/09/12 16:25:07 1.491
--- decl2.c 2001/09/14 06:08:05
*************** mark_vtable_entries (decl)
*** 2169,2175 ****
tree fnaddr = TREE_VALUE (entries);
tree fn;
! if (TREE_CODE (fnaddr) != ADDR_EXPR)
/* This entry is an offset: a virtual base class offset, a
virtual call offset, an RTTI offset, etc. */
continue;
--- 2169,2176 ----
tree fnaddr = TREE_VALUE (entries);
tree fn;
! if (TREE_CODE (fnaddr) != ADDR_EXPR
! && TREE_CODE (fnaddr) != FDESC_EXPR)
/* This entry is an offset: a virtual base class offset, a
virtual call offset, an RTTI offset, etc. */
continue;
Index: gcc/cp/typeck.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/typeck.c,v
retrieving revision 1.364
diff -c -p -d -r1.364 typeck.c
*** typeck.c 2001/08/24 12:07:46 1.364
--- typeck.c 2001/09/14 06:08:05
*************** get_member_function_from_ptrfunc (instan
*** 2910,2915 ****
--- 2910,2921 ----
vtbl = build_indirect_ref (vtbl, NULL);
e2 = build_array_ref (vtbl, idx);
+ /* When using function descriptors, the address of the
+ vtable entry is treated as a function pointer. */
+ if (TARGET_VTABLE_USES_DESCRIPTORS)
+ e2 = build1 (NOP_EXPR, TREE_TYPE (e2),
+ build_unary_op (ADDR_EXPR, e2, /*noconvert=*/1));
+
TREE_TYPE (e2) = TREE_TYPE (e3);
e1 = build_conditional_expr (e1, e2, e3);
Index: gcc/testsuite/g++.old-deja/g++.abi/ptrmem.C
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/g++.old-deja/g++.abi/ptrmem.C,v
retrieving revision 1.5
diff -c -p -d -r1.5 ptrmem.C
*** ptrmem.C 2001/06/10 21:50:47 1.5
--- ptrmem.C 2001/09/14 06:08:05
***************
*** 14,19 ****
--- 14,29 ----
#define ADJUST_DELTA(delta, virt) (delta)
#endif
+ /* IA64 uses function descriptors instead of function pointers in its
+ vtables, which means that we can't meaningfully compare them directly. */
+ #if defined __ia64__
+ #define CMP_PTRFN(A, B) (*(void **)(A) == *(void **)(B))
+ #define VPTE_SIZE (16)
+ #else
+ #define CMP_PTRFN(A, B) ((A) == (B))
+ #define VPTE_SIZE sizeof(void *)
+ #endif
+
#if defined (__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100
// Check that pointers-to-member functions are represented correctly.
*************** main ()
*** 85,96 ****
// There should be no adjustment for the `T' version, and an
// appropriate adjustment for the `S' version.
y = &T::f;
! if (yp->ptr != ADJUST_PTRFN (&_ZN1T1fEv, 0))
return 5;
if (yp->adj != ADJUST_DELTA (0, 0))
return 6;
x = (sp) y;
! if (xp->ptr != ADJUST_PTRFN (&_ZN1T1fEv, 0))
return 7;
if (xp->adj != ADJUST_DELTA (delta, 0))
return 8;
--- 95,106 ----
// There should be no adjustment for the `T' version, and an
// appropriate adjustment for the `S' version.
y = &T::f;
! if (! CMP_PTRFN (yp->ptr, ADJUST_PTRFN (&_ZN1T1fEv, 0)))
return 5;
if (yp->adj != ADJUST_DELTA (0, 0))
return 6;
x = (sp) y;
! if (! CMP_PTRFN (xp->ptr, ADJUST_PTRFN (&_ZN1T1fEv, 0)))
return 7;
if (xp->adj != ADJUST_DELTA (delta, 0))
return 8;
*************** main ()
*** 99,110 ****
// one. `T::h' is in the second slot: the vtable pointer points to
// the first virtual function.
y = &T::h;
! if (yp->ptr != ADJUST_PTRFN (sizeof (void *), 1))
return 9;
if (yp->adj != ADJUST_DELTA (0, 1))
return 10;
x = (sp) y;
! if (xp->ptr != ADJUST_PTRFN (sizeof (void *), 1))
return 11;
if (xp->adj != ADJUST_DELTA (delta, 1))
return 12;
--- 109,120 ----
// one. `T::h' is in the second slot: the vtable pointer points to
// the first virtual function.
y = &T::h;
! if (yp->ptr != ADJUST_PTRFN (VPTE_SIZE, 1))
return 9;
if (yp->adj != ADJUST_DELTA (0, 1))
return 10;
x = (sp) y;
! if (xp->ptr != ADJUST_PTRFN (VPTE_SIZE, 1))
return 11;
if (xp->adj != ADJUST_DELTA (delta, 1))
return 12;
Index: gcc/testsuite/g++.old-deja/g++.abi/vtable2.C
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/g++.old-deja/g++.abi/vtable2.C,v
retrieving revision 1.4
diff -c -p -d -r1.4 vtable2.C
*** vtable2.C 2001/05/25 01:30:56 1.4
--- vtable2.C 2001/09/14 06:08:05
*************** void _ZN2S32s3Ev ();
*** 127,132 ****
--- 127,141 ----
void _ZN2S42s1Ev ();
}
+ // IA-64 uses function descriptors not function pointers in its vtables.
+ #if defined __ia64__
+ #define CMP_VPTR(A, B) (*(void **)(A) == *(void **)(B))
+ #define INC_VPTR(A) ((A) += 2)
+ #else
+ #define CMP_VPTR(A, B) (*(A) == (ptrdiff_t)(B))
+ #define INC_VPTR(A) ((A) += 1)
+ #endif
+
int main ()
{
S4 s4;
*************** int main ()
*** 148,157 ****
return 4;
// Skip the RTTI entry.
vtbl++;
! if (*vtbl++ != (ptrdiff_t) &_ZN2S32s3Ev)
return 5;
! if (*vtbl++ != (ptrdiff_t) &_ZN2S42s1Ev)
return 6;
// The S1 vbase offset.
if (*vtbl++ != 0)
return 7;
--- 157,168 ----
return 4;
// Skip the RTTI entry.
vtbl++;
! if (! CMP_VPTR (vtbl, &_ZN2S32s3Ev))
return 5;
! INC_VPTR (vtbl);
! if (! CMP_VPTR (vtbl, &_ZN2S42s1Ev))
return 6;
+ INC_VPTR (vtbl);
// The S1 vbase offset.
if (*vtbl++ != 0)
return 7;
*************** int main ()
*** 169,176 ****
// Skip the RTTI entry.
vtbl++;
// Skip the remaining virtual functions -- they are thunks.
! vtbl++;
! vtbl++;
}
#else /* !(defined (__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100) */
--- 180,187 ----
// Skip the RTTI entry.
vtbl++;
// Skip the remaining virtual functions -- they are thunks.
! INC_VPTR (vtbl);
! INC_VPTR (vtbl);
}
#else /* !(defined (__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100) */