Patchwork PowerPC32 .gnu.attributes

login
register
mail settings
Submitter Alan Modra
Date Feb. 23, 2011, 11:07 a.m.
Message ID <20110223110721.GA5959@bubble.grove.modra.org>
Download mbox | patch
Permalink /patch/84126/
State New
Headers show

Comments

Alan Modra - Feb. 23, 2011, 11:07 a.m.
Since gcc-4.3.0, powerpc32 gcc has emitted object files with a
.gnu.attributes section that specifies whether hard or soft-float
function calling conventions are used, and whether the Alitivec ABI
was in force.  Later versions of gcc extended these attributes, and
added another to specify the struct return ABI.  GNU ld warns if you
try to link object files with incompatible attributes.  This all
sounds good, but the current implementation has a problem:  The
attributes are always emitted based on compiler flags, even if the
potential ABI incompatibilities do not occur.  In particular, glibc
startup files crt1.o, crti.o and crtn.o are marked.  crti.o and crtn.o
are used when gcc builds soft-float multilibs resulting in linker
warnings and the wrong attributes on the .so files.

This patch teaches gcc to notice when potential ABI incompatibilities
occur using some of the backend function calling hooks, and moves
emitting of the attributes to the end of the assembly file.  Some
changes to init_cumulative_args were needed: I want the return MODE
from INIT_CUMULATIVE_LIBCALL_ARGS to catch libcalls that return a
float, and FNDECL from INIT_CUMULATIVE_ARGS to do the same for normal
calls.

I try to limit the set of functions for which we notice ABI
incompatibilities to those that may actually be called.  For example, a
static function that does not have its address taken should not cause
an ABI incompatibility in an object file.  See call_ABI_of_interest,
code mostly stolen shamelessly from cgraphunit.c, but with the
addition of TREE_ADDRESSABLE.  I think I need that flag for cases like

static double f (double x)
{
  return x * 2.0;
}

double (*funp) (double) = f;

However, there is a problem with using that flag since it gets set on
all called functions.  c-typeck.c:build_function_call_vec calls
function_to_pointer_conversion, calls build_unary_op(.., ADDR_EXP,..),
calls c_mark_addressable which sets TREE_ADDRESSABLE.  That
unfortunately results in us considering g() in the following as
interesting when it is not.

static double g (int x)
{
  return (double) x;
}

int f (int x)
{
  return g (x) / 2.0;
}

Can someone help me out here, to make this patch closer to the ideal?


	* config/rs6000/linux.h (TARGET_ASM_FILE_END): Don't define.
	* config/rs6000/linux64.h (TARGET_ASM_FILE_END): Don't define.
	* config/rs6000/sysv4.h (TARGET_ASM_FILE_END): Define.
	* config/rs6000/rs6000-protos.h (init_cumulative_args): Add fndecl and
	return_mode args.
	* config/rs6000/rs6000.h (CUMULATIVE_ARGS): Add "escapes".
	(INIT_CUMULATIVE_ARGS): Pass FNDECL, VOIDmode.
	(INIT_CUMULATIVE_INCOMING_ARGS): Pass current_function_decl, VOIDmode.
	(INIT_CUMULATIVE_LIBCALL_ARGS): Pass NULL_TREE, MODE.
	* config/rs6000/rs6000.c
	(rs6000_elf_end_indicate_exec_stack): Rename to..
	(rs6000_elf_file_end): ..this.  Only call file_end_indicate_exec_stack
	for POWERPC_LINUX.  Move code emitting .gnu_attribute to here, from..
	(rs6000_file_start): ..here.
	(rs6000_passes_float, rs6000_passes_vector, rs6000_returns_struct): New
	file scope variables.
	(call_ABI_of_interest): New function.
	(init_cumulative_args): Set above vars when function return value
	is a float, vector, or small struct.
	(rs6000_function_arg_advance_1): Likewise for function args.
	(rs6000_va_start): Set rs6000_passes_float if variable arg function
	references float args.

Patch

Index: gcc/config/rs6000/linux.h
===================================================================
--- gcc/config/rs6000/linux.h	(revision 170373)
+++ gcc/config/rs6000/linux.h	(working copy)
@@ -114,8 +114,6 @@ 
 #define RELOCATABLE_NEEDS_FIXUP \
   (target_flags & target_flags_explicit & MASK_RELOCATABLE)
 
-#define TARGET_ASM_FILE_END file_end_indicate_exec_stack
-
 #define TARGET_POSIX_IO
 
 #define MD_UNWIND_SUPPORT "config/rs6000/linux-unwind.h"
Index: gcc/config/rs6000/linux64.h
===================================================================
--- gcc/config/rs6000/linux64.h	(revision 170373)
+++ gcc/config/rs6000/linux64.h	(working copy)
@@ -537,8 +546,6 @@  extern int dot_symbols;
 #undef DRAFT_V4_STRUCT_RET
 #define DRAFT_V4_STRUCT_RET (!TARGET_64BIT)
 
-#define TARGET_ASM_FILE_END rs6000_elf_end_indicate_exec_stack
-
 #define TARGET_POSIX_IO
 
 #define LINK_GCC_C_SEQUENCE_SPEC \
Index: gcc/config/rs6000/sysv4.h
===================================================================
--- gcc/config/rs6000/sysv4.h	(revision 170373)
+++ gcc/config/rs6000/sysv4.h	(working copy)
@@ -1031,6 +1031,8 @@  ncrtn.o%s"
 /* Generate entries in .fixup for relocatable addresses.  */
 #define RELOCATABLE_NEEDS_FIXUP 1
 
+#define TARGET_ASM_FILE_END rs6000_elf_file_end
+
 /* This target uses the sysv4.opt file.  */
 #define TARGET_USES_SYSV4_OPT 1
 
Index: gcc/config/rs6000/rs6000.h
===================================================================
--- gcc/config/rs6000/rs6000.h	(revision 170373)
+++ gcc/config/rs6000/rs6000.h	(working copy)
@@ -1570,25 +1598,29 @@  typedef struct rs6000_args
   int floats_in_gpr;		/* count of SFmode floats taking up
 				   GPR space (darwin64) */
   int named;			/* false for varargs params */
+  int escapes;			/* if function visible outside tu */
 } CUMULATIVE_ARGS;
 
 /* Initialize a variable CUM of type CUMULATIVE_ARGS
    for a call to a function whose data type is FNTYPE.
    For a library call, FNTYPE is 0.  */
 
-#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \
-  init_cumulative_args (&CUM, FNTYPE, LIBNAME, FALSE, FALSE, N_NAMED_ARGS)
+#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, FNDECL, N_NAMED_ARGS) \
+  init_cumulative_args (&CUM, FNTYPE, LIBNAME, FALSE, FALSE, \
+			N_NAMED_ARGS, FNDECL, VOIDmode)
 
 /* Similar, but when scanning the definition of a procedure.  We always
    set NARGS_PROTOTYPE large so we never return an EXPR_LIST.  */
 
 #define INIT_CUMULATIVE_INCOMING_ARGS(CUM, FNTYPE, LIBNAME) \
-  init_cumulative_args (&CUM, FNTYPE, LIBNAME, TRUE, FALSE, 1000)
+  init_cumulative_args (&CUM, FNTYPE, LIBNAME, TRUE, FALSE, \
+			1000, current_function_decl, VOIDmode)
 
 /* Like INIT_CUMULATIVE_ARGS' but only used for outgoing libcalls.  */
 
 #define INIT_CUMULATIVE_LIBCALL_ARGS(CUM, MODE, LIBNAME) \
-  init_cumulative_args (&CUM, NULL_TREE, LIBNAME, FALSE, TRUE, 0)
+  init_cumulative_args (&CUM, NULL_TREE, LIBNAME, FALSE, TRUE, \
+			0, NULL_TREE, MODE)
 
 /* If defined, a C expression which determines whether, and in which
    direction, to pad out an argument with extra space.  The value
Index: gcc/config/rs6000/rs6000-protos.h
===================================================================
--- gcc/config/rs6000/rs6000-protos.h	(revision 170373)
+++ gcc/config/rs6000/rs6000-protos.h	(working copy)
@@ -28,7 +28,8 @@ 
 #ifdef RTX_CODE
 
 #ifdef TREE_CODE
-extern void init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx, int, int, int);
+extern void init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx, int, int, int,
+				  tree, enum machine_mode);
 #endif /* TREE_CODE */
 
 extern bool easy_altivec_constant (rtx, enum machine_mode);
Index: gcc/config/rs6000/rs6000.c
===================================================================
--- gcc/config/rs6000/rs6000.c	(revision 170373)
+++ gcc/config/rs6000/rs6000.c	(working copy)
@@ -184,6 +184,14 @@  unsigned rs6000_pmode;
 /* Width in bits of a pointer.  */
 unsigned rs6000_pointer_size;
 
+#ifdef HAVE_AS_GNU_ATTRIBUTE
+/* Flag whether floating point values have been passed/returned.  */
+static bool rs6000_passes_float;
+/* Flag whether vector values have been passed/returned.  */
+static bool rs6000_passes_vector;
+/* Flag whether small (<= 8 byte) structures have been returned.  */
+static bool rs6000_returns_struct;
+#endif
 
 /* Value is TRUE if register/mode pair is acceptable.  */
 bool rs6000_hard_regno_mode_ok_p[NUM_MACHINE_MODES][FIRST_PSEUDO_REGISTER];
@@ -940,7 +948,7 @@  static void rs6000_file_start (void);
 static int rs6000_elf_reloc_rw_mask (void);
 static void rs6000_elf_asm_out_constructor (rtx, int) ATTRIBUTE_UNUSED;
 static void rs6000_elf_asm_out_destructor (rtx, int) ATTRIBUTE_UNUSED;
-static void rs6000_elf_end_indicate_exec_stack (void) ATTRIBUTE_UNUSED;
+static void rs6000_elf_file_end (void) ATTRIBUTE_UNUSED;
 static void rs6000_elf_asm_init_sections (void);
 static section *rs6000_elf_select_rtx_section (enum machine_mode, rtx,
 					       unsigned HOST_WIDE_INT);
@@ -4695,23 +4703,6 @@  rs6000_file_start (void)
 	putc ('\n', file);
     }
 
-#ifdef HAVE_AS_GNU_ATTRIBUTE
-  if (TARGET_32BIT && DEFAULT_ABI == ABI_V4)
-    {
-      fprintf (file, "\t.gnu_attribute 4, %d\n",
-	       ((TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT) ? 1 
-	        : (TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT) ? 3 
-	        : 2));
-      fprintf (file, "\t.gnu_attribute 8, %d\n",
-	       (TARGET_ALTIVEC_ABI ? 2
-		: TARGET_SPE_ABI ? 3
-		: 1));
-      fprintf (file, "\t.gnu_attribute 12, %d\n",
-	       aix_struct_return ? 2 : 1);
-
-    }
-#endif
-
   if (DEFAULT_ABI == ABI_AIX || (TARGET_ELF && flag_pic == 2))
     {
       switch_to_section (toc_section);
@@ -7859,9 +7849,37 @@  rs6000_return_in_memory (const_tree type
   return false;
 }
 
+#ifdef HAVE_AS_GNU_ATTRIBUTE
+/* Return TRUE if a call to function FNDECL may be one that
+   potentially affects the function calling ABI of the object file.  */
+
+static bool
+call_ABI_of_interest (const_tree fndecl)
+{
+  /* Libcalls are always interesting.  */
+  if (fndecl == NULL_TREE)
+    return true;
+
+  /* Any call to an external function is interesting.  */
+  if (DECL_EXTERNAL (fndecl))
+    return true;
+
+  /* Interesting functions that we are emitting in this object file.  */
+  if (TREE_PUBLIC (fndecl)
+      || TREE_ADDRESSABLE (fndecl)
+      || DECL_STATIC_CONSTRUCTOR (fndecl)
+      || DECL_STATIC_DESTRUCTOR (fndecl)
+      /* COMDAT virtual functions may be referenced by vtable from
+	 other compilation unit.  */
+      || (DECL_VIRTUAL_P (fndecl) && DECL_COMDAT (fndecl)))
+    return true;
+  return false;
+}
+#endif
+
 /* Initialize a variable CUM of type CUMULATIVE_ARGS
    for a call to a function whose data type is FNTYPE.
-   For a library call, FNTYPE is 0.
+   For a library call, FNTYPE is 0 and RETURN_MODE the return value mode.
 
    For incoming args we set the number of arguments in the prototype large
    so we never return a PARALLEL.  */
@@ -7869,7 +7887,9 @@  rs6000_return_in_memory (const_tree type
 void
 init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
 		      rtx libname ATTRIBUTE_UNUSED, int incoming,
-		      int libcall, int n_named_args)
+		      int libcall, int n_named_args,
+		      tree fndecl ATTRIBUTE_UNUSED,
+		      enum machine_mode return_mode ATTRIBUTE_UNUSED)
 {
   static CUMULATIVE_ARGS zero_cumulative;
 
@@ -7911,6 +7931,45 @@  init_cumulative_args (CUMULATIVE_ARGS *c
 	       cum->prototype, cum->nargs_prototype);
     }
 
+#ifdef HAVE_AS_GNU_ATTRIBUTE
+  if (DEFAULT_ABI == ABI_V4)
+    {
+      cum->escapes = call_ABI_of_interest (fndecl);
+      if (cum->escapes)
+	{
+	  tree return_type;
+
+	  if (fntype)
+	    {
+	      return_type = TREE_TYPE (fntype);
+	      return_mode = TYPE_MODE (return_type);
+	    }
+	  else
+	    return_type = lang_hooks.types.type_for_mode (return_mode, 0);
+
+	  if (return_type != NULL)
+	    {
+	      if (TREE_CODE (return_type) == RECORD_TYPE
+		  && TYPE_TRANSPARENT_AGGR (return_type))
+		{
+		  return_type = TREE_TYPE (first_field (return_type));
+		  return_mode = TYPE_MODE (return_type);
+		}
+	      if (AGGREGATE_TYPE_P (return_type)
+		  && ((unsigned HOST_WIDE_INT) int_size_in_bytes (return_type)
+		      <= 8))
+		rs6000_returns_struct = true;
+	    }
+	  if (SCALAR_FLOAT_MODE_P (return_mode))
+	    rs6000_passes_float = true;
+	  else if (ALTIVEC_VECTOR_MODE (return_mode)
+		   || VSX_VECTOR_MODE (return_mode)
+		   || SPE_VECTOR_MODE (return_mode))
+	    rs6000_passes_vector = true;
+	}
+    }
+#endif
+
   if (fntype
       && !TARGET_ALTIVEC
       && TARGET_ALTIVEC_ABI
@@ -8229,11 +8288,25 @@  static void
 rs6000_function_arg_advance_1 (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 			       const_tree type, bool named, int depth)
 {
-
   /* Only tick off an argument if we're not recursing.  */
   if (depth == 0)
     cum->nargs_prototype--;
 
+#ifdef HAVE_AS_GNU_ATTRIBUTE
+  if (DEFAULT_ABI == ABI_V4
+      && cum->escapes)
+    {
+      if (SCALAR_FLOAT_MODE_P (mode))
+	rs6000_passes_float = true;
+      else if (named && (ALTIVEC_VECTOR_MODE (mode) || VSX_VECTOR_MODE (mode)))
+	rs6000_passes_vector = true;
+      else if (SPE_VECTOR_MODE (mode)
+	       && !cum->stdarg
+	       && cum->sysv_gregno <= GP_ARG_MAX_REG)
+	rs6000_passes_vector = true;
+    }
+#endif
+
   if (TARGET_ALTIVEC_ABI
       && (ALTIVEC_VECTOR_MODE (mode)
 	  || VSX_VECTOR_MODE (mode)
@@ -9498,6 +9571,11 @@  rs6000_va_start (tree valist, rtx nextar
 		  build_int_cst (NULL_TREE, n_fpr));
       TREE_SIDE_EFFECTS (t) = 1;
       expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+#ifdef HAVE_AS_GNU_ATTRIBUTE
+      if (call_ABI_of_interest (cfun->decl))
+	rs6000_passes_float = true;
+#endif
     }
 
   /* Find the overflow area.  */
@@ -25780,10 +25869,30 @@  rs6000_elf_declare_function_name (FILE *
 }
 
 static void
-rs6000_elf_end_indicate_exec_stack (void)
+rs6000_elf_file_end (void)
 {
+#ifdef HAVE_AS_GNU_ATTRIBUTE
+  if (TARGET_32BIT && DEFAULT_ABI == ABI_V4)
+    {
+      if (rs6000_passes_float)
+	fprintf (asm_out_file, "\t.gnu_attribute 4, %d\n",
+		 ((TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT) ? 1 
+		  : (TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT) ? 3 
+		  : 2));
+      if (rs6000_passes_vector)
+	fprintf (asm_out_file, "\t.gnu_attribute 8, %d\n",
+		 (TARGET_ALTIVEC_ABI ? 2
+		  : TARGET_SPE_ABI ? 3
+		  : 1));
+      if (rs6000_returns_struct)
+	fprintf (asm_out_file, "\t.gnu_attribute 12, %d\n",
+		 aix_struct_return ? 2 : 1);
+    }
+#endif
+#ifdef POWERPC_LINUX
   if (TARGET_32BIT)
     file_end_indicate_exec_stack ();
+#endif
 }
 #endif