C++ PATCH: Key methods for ARM EABI

Mark Mitchell mark@codesourcery.com
Tue Aug 31 21:34:00 GMT 2004


The ARM EABI has an optimization to the standard Itanium C++ ABI key
method rules: an inline function is never chosen as the key method.
In the Itanium C++ ABI, the key method is the first virtual, non-pure
function that is not inline *at the point of class definition*.  The
difference comes up for classes like:

  class C { virtual void f(); };
  inline void C::f() {} // Itanium key method, but not ARM key method

The Itanium C++ ABI committee was concerned that C::f would be defined
in some translation units, but not others, with the result that there
would be no definition emitted for C's vtable.  But, technically, the
standard prevents that ("An inline function shall be defined in every
translation unit in which it is used" and "A virtual member function
is used if it is not pure") so ARM is optimizing by not emitting the
vtable in so many places.

Tested on i686-pc-linux-gnu and arm-none-eabi, applied on the mainline
and on the csl-arm-branch.

--
Mark Mitchell
CodeSourcery, LLC
mark@codesourcery.com

2004-08-31  Mark Mitchell  <mark@codesourcery.com>

	* hooks.c (hook_bool_void_true): New function.
	* hooks.h (hook_bool_void_true): Declare.
	* target-def.h (TARGET_CXX): Add
	TARGET_CXX_KEY_METHOD_MAY_BE_INLINE.
	* target.h (struct cxx): Add key_method_may_be_inline.
	* config/arm/arm.c (arm_cxx_key_method_may_be_inline): New
	function.
	(TARGET_CXX_KEY_METHOD_MAY_BE_INLINE): New macro.
	* config/arm/bpabi.h: Use __THUMB_INTERWORK__ instead of
	__THUMB_INTERWORK.
	
2004-08-31  Mark Mitchell  <mark@codesourcery.com>

 	* class.c (key_method): Rename to ...
	(determine_key_method): ... this.
	(finish_struct_1): Adjust accordingly.
	* cp-tree.h (key_method): Declare.
	* decl2.c (import_export_vtable): Do not set the linkage for a
	vtable before end-of-file on targets where an inline method cannot
	be the key method.
	(maybe_emit_vtables): Determine the key method here if it has not
	already been done.

2004-08-31  Mark Mitchell  <mark@codesourcery.com>

	* g++.dg/abi/key1.C: New test.

Index: hooks.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/hooks.c,v
retrieving revision 1.22.4.1
diff -c -5 -p -r1.22.4.1 hooks.c
*** hooks.c	3 Mar 2004 16:02:40 -0000	1.22.4.1
--- hooks.c	31 Aug 2004 16:06:48 -0000
*************** bool
*** 39,49 ****
  hook_bool_void_false (void)
  {
    return false;
  }
  
! /* The same, but formally returning NO_REGS.  */
  int
  hook_int_void_no_regs (void)
  {
    return NO_REGS;
  }
--- 39,56 ----
  hook_bool_void_false (void)
  {
    return false;
  }
  
! /* Generic hook that takes no arguments and returns true.  */
! bool
! hook_bool_void_true (void)
! {
!   return true;
! }
! 
! /* Generic hook that takes no arguments and returns NO_REGS. */
  int
  hook_int_void_no_regs (void)
  {
    return NO_REGS;
  }
Index: hooks.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/hooks.h,v
retrieving revision 1.23.4.1
diff -c -5 -p -r1.23.4.1 hooks.h
*** hooks.h	3 Mar 2004 16:02:40 -0000	1.23.4.1
--- hooks.h	31 Aug 2004 16:06:48 -0000
*************** Foundation, 59 Temple Place - Suite 330,
*** 21,30 ****
--- 21,31 ----
  
  #ifndef GCC_HOOKS_H
  #define GCC_HOOKS_H
  
  extern bool hook_bool_void_false (void);
+ extern bool hook_bool_void_true (void);
  extern bool hook_bool_bool_false (bool);
  extern bool hook_bool_tree_false (tree);
  extern bool hook_bool_tree_true (tree);
  extern bool hook_bool_tree_hwi_hwi_tree_false (tree, HOST_WIDE_INT, HOST_WIDE_INT,
  					tree);
Index: target-def.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/target-def.h,v
retrieving revision 1.59.2.8
diff -c -5 -p -r1.59.2.8 target-def.h
*** target-def.h	5 Aug 2004 16:16:05 -0000	1.59.2.8
--- target-def.h	31 Aug 2004 16:06:49 -0000
*************** Foundation, 59 Temple Place - Suite 330,
*** 380,396 ****
  
  #ifndef TARGET_CXX_CDTOR_RETURNS_THIS
  #define TARGET_CXX_CDTOR_RETURNS_THIS hook_bool_void_false
  #endif
  
  #define TARGET_CXX			\
    {					\
      TARGET_CXX_GUARD_TYPE,		\
      TARGET_CXX_GUARD_MASK_BIT,		\
      TARGET_CXX_GET_COOKIE_SIZE,		\
      TARGET_CXX_COOKIE_HAS_SIZE,		\
!     TARGET_CXX_CDTOR_RETURNS_THIS	\
    }
  
  /* The whole shebang.  */
  #define TARGET_INITIALIZER			\
  {						\
--- 380,401 ----
  
  #ifndef TARGET_CXX_CDTOR_RETURNS_THIS
  #define TARGET_CXX_CDTOR_RETURNS_THIS hook_bool_void_false
  #endif
  
+ #ifndef TARGET_CXX_KEY_METHOD_MAY_BE_INLINE
+ #define TARGET_CXX_KEY_METHOD_MAY_BE_INLINE hook_bool_void_true
+ #endif
+ 
  #define TARGET_CXX			\
    {					\
      TARGET_CXX_GUARD_TYPE,		\
      TARGET_CXX_GUARD_MASK_BIT,		\
      TARGET_CXX_GET_COOKIE_SIZE,		\
      TARGET_CXX_COOKIE_HAS_SIZE,		\
!     TARGET_CXX_CDTOR_RETURNS_THIS,	\
!     TARGET_CXX_KEY_METHOD_MAY_BE_INLINE \
    }
  
  /* The whole shebang.  */
  #define TARGET_INITIALIZER			\
  {						\
Index: target.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/target.h,v
retrieving revision 1.67.2.9
diff -c -5 -p -r1.67.2.9 target.h
*** target.h	5 Aug 2004 16:16:05 -0000	1.67.2.9
--- target.h	31 Aug 2004 16:06:49 -0000
*************** struct gcc_target
*** 450,459 ****
--- 450,464 ----
      /* Returns true if the element size should be stored in the
         array cookie.  */
      bool (*cookie_has_size) (void);
      /* Returns true if constructors and destructors return "this".  */
      bool (*cdtor_returns_this) (void);
+     /* Returns true if the key method for a class can be an inline
+        function, so long as it is not declared inline in the class
+        itself.  Returning true is the behavior required by the Itanium
+        C++ ABI.  */
+     bool (*key_method_may_be_inline) (void);
    } cxx;
  
    /* Leave the boolean fields at the end.  */
  
    /* True if arbitrary sections are supported.  */
Index: config/arm/arm.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/arm/arm.c,v
retrieving revision 1.303.2.48
diff -c -5 -p -r1.303.2.48 arm.c
*** config/arm/arm.c	18 Aug 2004 03:57:43 -0000	1.303.2.48
--- config/arm/arm.c	31 Aug 2004 16:06:50 -0000
*************** static bool arm_promote_prototypes (tree
*** 163,172 ****
--- 163,173 ----
  static tree arm_cxx_guard_type (void);
  static bool arm_cxx_guard_mask_bit (void);
  static tree arm_get_cookie_size (tree);
  static bool arm_cookie_has_size (void);
  static bool arm_cxx_cdtor_returns_this (void);
+ static bool arm_cxx_key_method_may_be_inline (void);
  static void arm_init_libfuncs (void);
  
  
  /* Initialize the GCC target structure.  */
  #if TARGET_DLLIMPORT_DECL_ATTRIBUTES
*************** static void arm_init_libfuncs (void);
*** 276,285 ****
--- 277,289 ----
  #define TARGET_CXX_COOKIE_HAS_SIZE arm_cookie_has_size
  
  #undef TARGET_CXX_CDTOR_RETURNS_THIS
  #define TARGET_CXX_CDTOR_RETURNS_THIS arm_cxx_cdtor_returns_this
  
+ #undef TARGET_CXX_KEY_METHOD_MAY_BE_INLINE
+ #define TARGET_CXX_KEY_METHOD_MAY_BE_INLINE arm_cxx_key_method_may_be_inline
+ 
  struct gcc_target targetm = TARGET_INITIALIZER;
  
  /* Obstack for minipool constant handling.  */
  static struct obstack minipool_obstack;
  static char *         minipool_startobj;
*************** static bool
*** 14599,14608 ****
--- 14603,14620 ----
  arm_cxx_cdtor_returns_this (void)
  {
    return TARGET_AAPCS_BASED;
  }
  
+ /* The EABI says that an inline function may never be the key
+    method.  */
+ 
+ static bool
+ arm_cxx_key_method_may_be_inline (void)
+ {
+   return !TARGET_AAPCS_BASED;
+ }
  
  void
  arm_set_return_address (rtx source, rtx scratch)
  {
    arm_stack_offsets *offsets;
Index: config/arm/bpabi.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/arm/bpabi.h,v
retrieving revision 1.1.4.4
diff -c -5 -p -r1.1.4.4 bpabi.h
*** config/arm/bpabi.h	20 Aug 2004 20:10:30 -0000	1.1.4.4
--- config/arm/bpabi.h	31 Aug 2004 16:06:50 -0000
***************
*** 44,54 ****
  #undef LINK_SPEC
  #define LINK_SPEC "%{mbig-endian:-EB} %{mlittle-endian:-EL} "		\
    "%{static:-Bstatic} %{shared:-shared} %{symbolic:-Bsymbolic} "	\
    "-X"
  
! #if defined (__thumb__) && !defined (__THUMB_INTERWORK) 
  #define RENAME_LIBRARY_SET ".thumb_set"
  #else
  #define RENAME_LIBRARY_SET ".set"
  #endif
  
--- 44,54 ----
  #undef LINK_SPEC
  #define LINK_SPEC "%{mbig-endian:-EB} %{mlittle-endian:-EL} "		\
    "%{static:-Bstatic} %{shared:-shared} %{symbolic:-Bsymbolic} "	\
    "-X"
  
! #if defined (__thumb__) && !defined (__THUMB_INTERWORK__) 
  #define RENAME_LIBRARY_SET ".thumb_set"
  #else
  #define RENAME_LIBRARY_SET ".set"
  #endif
  
Index: cp/class.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/class.c,v
retrieving revision 1.581.2.7
diff -c -5 -p -r1.581.2.7 class.c
*** cp/class.c	17 Aug 2004 18:30:45 -0000	1.581.2.7
--- cp/class.c	31 Aug 2004 16:06:51 -0000
*************** layout_class_type (tree t, tree *virtual
*** 4937,4968 ****
  
    /* Clean up.  */
    splay_tree_delete (empty_base_offsets);
  }
  
! /* Returns the virtual function with which the vtable for TYPE is
!    emitted, or NULL_TREE if that heuristic is not applicable to TYPE.  */
  
! static tree
! key_method (tree type)
  {
    tree method;
  
    if (TYPE_FOR_JAVA (type)
        || processing_template_decl
        || CLASSTYPE_TEMPLATE_INSTANTIATION (type)
        || CLASSTYPE_INTERFACE_KNOWN (type))
!     return NULL_TREE;
  
    for (method = TYPE_METHODS (type); method != NULL_TREE;
         method = TREE_CHAIN (method))
      if (DECL_VINDEX (method) != NULL_TREE
  	&& ! DECL_DECLARED_INLINE_P (method)
  	&& ! DECL_PURE_VIRTUAL_P (method))
!       return method;
  
!   return NULL_TREE;
  }
  
  /* Perform processing required when the definition of T (a class type)
     is complete.  */
  
--- 4937,4975 ----
  
    /* Clean up.  */
    splay_tree_delete (empty_base_offsets);
  }
  
! /* Determine the "key method" for the class type indicated by TYPE,
!    and set CLASSTYPE_KEY_METHOD accordingly.  */
  
! void
! determine_key_method (tree type)
  {
    tree method;
  
    if (TYPE_FOR_JAVA (type)
        || processing_template_decl
        || CLASSTYPE_TEMPLATE_INSTANTIATION (type)
        || CLASSTYPE_INTERFACE_KNOWN (type))
!     return;
  
+   /* The key method is the first non-pure virtual function that is not
+      inline at the point of class definition.  On some targets the
+      key function may not be inline; those targets should not call
+      this function until the end of the translation unit.  */
    for (method = TYPE_METHODS (type); method != NULL_TREE;
         method = TREE_CHAIN (method))
      if (DECL_VINDEX (method) != NULL_TREE
  	&& ! DECL_DECLARED_INLINE_P (method)
  	&& ! DECL_PURE_VIRTUAL_P (method))
!       {
! 	CLASSTYPE_KEY_METHOD (type) = method;
! 	break;
!       }
  
!   return;
  }
  
  /* Perform processing required when the definition of T (a class type)
     is complete.  */
  
*************** finish_struct_1 (tree t)
*** 5003,5013 ****
    check_bases_and_members (t);
  
    /* Find the key method.  */
    if (TYPE_CONTAINS_VPTR_P (t))
      {
!       CLASSTYPE_KEY_METHOD (t) = key_method (t);
  
        /* If a polymorphic class has no key method, we may emit the vtable
  	 in every translation unit where the class definition appears.  */
        if (CLASSTYPE_KEY_METHOD (t) == NULL_TREE)
  	keyed_classes = tree_cons (NULL_TREE, t, keyed_classes);
--- 5010,5029 ----
    check_bases_and_members (t);
  
    /* Find the key method.  */
    if (TYPE_CONTAINS_VPTR_P (t))
      {
!       /* The Itanium C++ ABI permits the key method to be chosen when
! 	 the class is defined -- even though the key method so
! 	 selected may later turn out to be an inline function.  On
! 	 some systems (such as ARM Symbian OS) the key method cannot
! 	 be determined until the end of the translation unit.  On such
! 	 systems, we leave CLASSTYPE_KEY_METHOD set to NULL, which
! 	 will cause the class to be added to KEYED_CLASSES.  Then, in
! 	 finish_file we will determine the key method.  */
!       if (targetm.cxx.key_method_may_be_inline ())
! 	determine_key_method (t);
  
        /* If a polymorphic class has no key method, we may emit the vtable
  	 in every translation unit where the class definition appears.  */
        if (CLASSTYPE_KEY_METHOD (t) == NULL_TREE)
  	keyed_classes = tree_cons (NULL_TREE, t, keyed_classes);
Index: cp/cp-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-tree.h,v
retrieving revision 1.930.2.7
diff -c -5 -p -r1.930.2.7 cp-tree.h
*** cp/cp-tree.h	17 Aug 2004 18:30:47 -0000	1.930.2.7
--- cp/cp-tree.h	31 Aug 2004 16:06:51 -0000
*************** extern void note_name_declared_in_class 
*** 3618,3627 ****
--- 3618,3628 ----
  extern tree get_vtbl_decl_for_binfo             (tree);
  extern tree get_vtt_name                        (tree);
  extern tree get_primary_binfo                   (tree);
  extern void debug_class				(tree);
  extern void debug_thunks 			(tree);
+ extern void determine_key_method                (tree);
  
  /* in cvt.c */
  extern tree convert_to_reference (tree, tree, int, int, tree);
  extern tree convert_from_reference (tree);
  extern tree convert_lvalue (tree, tree);
Index: cp/decl2.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/decl2.c,v
retrieving revision 1.685.4.7
diff -c -5 -p -r1.685.4.7 decl2.c
*** cp/decl2.c	17 Aug 2004 19:39:20 -0000	1.685.4.7
--- cp/decl2.c	31 Aug 2004 16:06:51 -0000
*************** import_export_vtable (tree decl, tree ty
*** 1457,1467 ****
      {
        /* We can only wait to decide if we have real non-inline virtual
  	 functions in our class, or if we come from a template.  */
  
        int found = (CLASSTYPE_TEMPLATE_INSTANTIATION (type)
! 		   || CLASSTYPE_KEY_METHOD (type) != NULL_TREE);
  
        if (final || ! found)
  	{
  	  comdat_linkage (decl);
  	  DECL_EXTERNAL (decl) = 0;
--- 1457,1472 ----
      {
        /* We can only wait to decide if we have real non-inline virtual
  	 functions in our class, or if we come from a template.  */
  
        int found = (CLASSTYPE_TEMPLATE_INSTANTIATION (type)
! 		   || CLASSTYPE_KEY_METHOD (type) != NULL_TREE
! 		   /* On targets where the key function is not
! 		      permitted to be inline, we cannot determine
! 		      whether or not the vtable should be emitted
! 		      until end-of-file.  */
! 		   || !targetm.cxx.key_method_may_be_inline ());
  
        if (final || ! found)
  	{
  	  comdat_linkage (decl);
  	  DECL_EXTERNAL (decl) = 0;
*************** maybe_emit_vtables (tree ctype)
*** 1563,1572 ****
--- 1568,1583 ----
      return false;
    /* Ignore dummy vtables made by get_vtable_decl.  */
    if (TREE_TYPE (primary_vtbl) == void_type_node)
      return false;
  
+   /* On some targets, we cannot determine the key method until the end
+      of the translation unit -- which is when this function is
+      called.  */
+   if (!targetm.cxx.key_method_may_be_inline ())
+     determine_key_method (ctype);
+ 
    import_export_class (ctype);
  
    /* See if any of the vtables are needed.  */
    for (vtbl = CLASSTYPE_VTABLES (ctype); vtbl; vtbl = TREE_CHAIN (vtbl))
      {
Index: testsuite/g++.dg/abi/key1.C
===================================================================
RCS file: testsuite/g++.dg/abi/key1.C
diff -N testsuite/g++.dg/abi/key1.C
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- testsuite/g++.dg/abi/key1.C	31 Aug 2004 16:06:54 -0000
***************
*** 0 ****
--- 1,26 ----
+ // On ARM EABI platforms, key methods may never be inline.
+ // { dg-do compile { target arm*-*-eabi* arm*-*-symbianelf* } }
+ // { dg-final { scan-assembler-not _ZTV1S } }
+ // { dg-final { scan-assembler-not _ZTV1T } }
+ // { dg-final { scan-assembler _ZTV1U } }
+ 
+ struct S {
+   virtual void f();
+ };
+ 
+ inline void S::f() {}
+ 
+ struct T {
+   virtual void g();
+   virtual void h();
+ };
+ 
+ inline void T::g() {}
+ 
+ struct U {
+   virtual void i();
+   virtual void j();
+ };
+ 
+ inline void U::i() {}
+ void U::j () {}
Index: tm.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/tm.texi,v
retrieving revision 1.264.2.15
diff -c -5 -p -r1.264.2.15 tm.texi
*** tm.texi	19 Aug 2004 22:04:33 -0000	1.264.2.15
--- tm.texi	31 Aug 2004 17:08:24 -0000
*************** array cookies.  The default is to return
*** 8549,8558 ****
--- 8549,8568 ----
  This hook should return @code{true} if constructors and destructors return
  the address of the object created/destroyed.  The default is to return
  @code{false}.
  @end deftypefn
  
+ @deftypefn {Target Hook} bool TARGET_CXX_KEY_METHOD_MAY_BE_INLINE (void)
+ This hook returns true if the key method for a class (i.e., the method
+ which, if defined in the current translation unit, causes the virtual
+ table to be emitted) may be an inline function.  Under the standard
+ Itanium C++ ABI the key method may be an inline function so long as
+ the function is not declared inline in the class definition.  Under
+ some variants of the ABI, an inline function can never be the key
+ method.  The default is to return @code{true}.
+ @end deftypefn
+ 
  @node Misc
  @section Miscellaneous Parameters
  @cindex parameters, miscellaneous
  
  @c prevent bad page break with this line



More information about the Gcc-patches mailing list