This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

PowerPC [target/17828] -O2 -fPIC doesn't work with switches in linkonce functions and new binutils


This fixes a powerpc-linux problem with the .got2 section.  When gcc
uses linkonce sections (to handle C++ code duplication) or code is
compiled with -ffunction-sections in combination with -gc-sections, then
it is possible to have references in .got2 to discarded sections.  Newer
binutils gives an error on encountering such references.  Apart from
simply ignoring the linker error, two other solutions spring to mind:
We can teach ld how to edit the .got2 section to remove the bad entries
similarly to the way debug and eh_frame info is edited, or we can use
separate .got2 sections.  The trouble with editing .got2 is that ld
doesn't have enough information to do so easily.  No relocs are emitted
on instructions that access .got2, because a function sets up a .got2
base pointer in the prologue then accesses relative to that.  I suppose
you could emit dummy relocs on all these instructions, but that doesn't
help unless all code on your system using .got2 is recompiled.  Also,
compacting .got2 is problematic because ld -r can result in different
.got2 base offsets being used.

So I decided that the most reasonable solution is to generate
per-function .got2 sections when necessary, and allow the linker comdat
group support to handle the business of removing all of a discarded
function's sections.  This means that constants in .got2 are not shared
across functions when using -ffunction-sections -fdata-sections, an
improvement if the per-translation unit .got2 was overflowing, but
otherwise resulting in bloat.  For example, libstdc++.so .got2 increases
from 0xfa0 to 0x2fe4 bytes, which I'm not really happy about.  I fixed
ld --gc-sections to work on shared libs, but that doesn't help very
much.  Is there a good reason why libstdc++ is compiled with
-ffunction-sections -fdata-sections?  It seems useless when not using
--gc-sections.

Bootstrapped and regression tested powerpc-linux, both with and without
ld support http://sources.redhat.com/ml/binutils/2005-07/msg00445.html.

:ADDPATCH target (powerpc):

	PR target/17828
	* config/rs6000/rs6000.c (struct machine_function): Add toc_label_name.
	(struct toc_hash_struct): Add toc_labelno.
	(constant_pool_expr_1): Use cfun->machine->toc_label_name.
	(rs6000_elf_declare_function_name): Likewise.
	(rs6000_toc_sym): New function.
	(rs6000_emit_load_toc_table): Use rs6000_toc_sym.
	(create_TOC_reference): Init cfun->machine->toc_label_name.
	(toc_hash_function): XOR toc_labelno.
	(toc_hash_eq): Compare toc_labelno.
	(output_toc): Set toc_labelno.
	* config/rs6000/rs6000.h (TOC_PER_FUNCTION): Define as 0.
	* config/rs6000/sysv4.h (TOC_PER_FUNCTION): Define.
	(toc_section): Handle TOC_PER_FUNCTION.
	* varasm.c (assemble_start_function): Call resolve_unique_section 
	before output_constant_pool.
	* configure.ac (HAVE_LD_MULT_GOT2): Add test.
	* configure: Regenerate.
	* config.in: Regenerate.

diff -urp -xCVS -x'*~' -x'.#*' gcc-virgin/gcc/config/rs6000/rs6000.c gcc-current/gcc/config/rs6000/rs6000.c
--- gcc-virgin/gcc/config/rs6000/rs6000.c	2005-07-16 10:36:10.000000000 +0930
+++ gcc-current/gcc/config/rs6000/rs6000.c	2005-07-22 14:55:28.000000000 +0930
@@ -120,6 +120,8 @@ typedef struct machine_function GTY(())
   int ra_needs_full_frame;
   /* Some local-dynamic symbol.  */
   const char *some_ld_name;
+  /* Label 32k into toc section.  */
+  const char *toc_label_name;
   /* Whether the instruction chain has been scanned already.  */
   int insn_chain_scanned_p;
   /* Flags if __builtin_return_address (0) was used.  */
@@ -757,6 +759,7 @@ struct toc_hash_struct GTY(())
      ASM_OUTPUT_SPECIAL_POOL_ENTRY_P.  */
   rtx key;
   enum machine_mode key_mode;
+  int toc_labelno;
   int labelno;
 };
 
@@ -2417,7 +2420,8 @@ constant_pool_expr_1 (rtx op, int *have_
 	  else
 	    return 0;
 	}
-      else if (! strcmp (XSTR (op, 0), toc_label_name))
+      else if (cfun->machine->toc_label_name != NULL
+	       && strcmp (XSTR (op, 0), cfun->machine->toc_label_name) == 0)
 	{
 	  *have_toc = 1;
 	  return 1;
@@ -12868,6 +12886,17 @@ rs6000_maybe_dead (rtx insn)
 					REG_NOTES (insn));
 }
 
+/* Return the toc base symbol.  */
+
+static rtx
+rs6000_toc_sym (void)
+{
+  const char *toc_sym = cfun->machine->toc_label_name;
+  if (toc_sym == NULL)
+    toc_sym = toc_label_name;
+  return gen_rtx_SYMBOL_REF (Pmode, toc_sym);
+}
+
 /* Emit instructions needed to load the TOC register.
    This is only needed when TARGET_TOC, TARGET_MINIMAL_TOC, and there is
    a constant pool; or for SVR4 -fpic.  */
@@ -12886,7 +12915,7 @@ rs6000_emit_load_toc_table (int fromprol
       ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno);
       lab = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
       if (flag_pic == 2)
-	got = gen_rtx_SYMBOL_REF (Pmode, toc_label_name);
+	got = rs6000_toc_sym ();
       else
 	got = rs6000_got_sym ();
       tmp1 = tmp2 = dest;
@@ -12955,7 +12984,7 @@ rs6000_emit_load_toc_table (int fromprol
 	{
 	  rtx tocsym;
 
-	  tocsym = gen_rtx_SYMBOL_REF (Pmode, toc_label_name);
+	  tocsym = rs6000_toc_sym ();
 	  emit_insn (gen_load_toc_v4_PIC_1b (tempLR, tocsym));
 	  emit_move_insn (dest, tempLR);
 	  emit_move_insn (temp0, gen_rtx_MEM (Pmode, dest));
@@ -12967,10 +12996,7 @@ rs6000_emit_load_toc_table (int fromprol
   else if (TARGET_ELF && !TARGET_AIX && flag_pic == 0 && TARGET_MINIMAL_TOC)
     {
       /* This is for AIX code running in non-PIC ELF32.  */
-      char buf[30];
-      rtx realsym;
-      ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 1);
-      realsym = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
+      rtx realsym = rs6000_toc_sym ();
 
       insn = emit_insn (gen_elf_high (dest, realsym));
       if (fromprolog)
@@ -13073,11 +13099,24 @@ uses_TOC (void)
 rtx
 create_TOC_reference (rtx symbol)
 {
-  return gen_rtx_PLUS (Pmode,
-	   gen_rtx_REG (Pmode, TOC_REGISTER),
-	     gen_rtx_CONST (Pmode,
-	       gen_rtx_MINUS (Pmode, symbol,
-		 gen_rtx_SYMBOL_REF (Pmode, toc_label_name))));
+  rtx toc, off;
+
+  if (cfun->machine->toc_label_name == NULL)
+    {
+      if (TOC_PER_FUNCTION)
+	{
+	  char buf[32];
+	  int toc_label = current_function_funcdef_no + 2;
+
+	  ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", toc_label);
+	  cfun->machine->toc_label_name = ggc_strdup (buf);
+	}
+      else
+	cfun->machine->toc_label_name = toc_label_name;
+    }
+  toc = gen_rtx_SYMBOL_REF (Pmode, cfun->machine->toc_label_name);
+  off = gen_rtx_CONST (Pmode, gen_rtx_MINUS (Pmode, symbol, toc));
+  return gen_rtx_PLUS (Pmode, gen_rtx_REG (Pmode, TOC_REGISTER), off);
 }
 
 /* If _Unwind_* has been called from within the same module,
@@ -15160,7 +15199,7 @@ toc_hash_function (const void *hash_entr
 {
   const struct toc_hash_struct *thc =
     (const struct toc_hash_struct *) hash_entry;
-  return rs6000_hash_constant (thc->key) ^ thc->key_mode;
+  return rs6000_hash_constant (thc->key) ^ thc->key_mode ^ thc->toc_labelno;
 }
 
 /* Compare H1 and H2 for equivalence.  */
@@ -15168,14 +15207,14 @@ toc_hash_function (const void *hash_entr
 static int
 toc_hash_eq (const void *h1, const void *h2)
 {
-  rtx r1 = ((const struct toc_hash_struct *) h1)->key;
-  rtx r2 = ((const struct toc_hash_struct *) h2)->key;
+  const struct toc_hash_struct *th1 = (const struct toc_hash_struct *) h1;
+  const struct toc_hash_struct *th2 = (const struct toc_hash_struct *) h2;
 
-  if (((const struct toc_hash_struct *) h1)->key_mode
-      != ((const struct toc_hash_struct *) h2)->key_mode)
+  if (th1->key_mode != th2->key_mode
+      || th1->toc_labelno != th2->toc_labelno)
     return 0;
 
-  return rtx_equal_p (r1, r2);
+  return rtx_equal_p (th1->key, th2->key);
 }
 
 /* These are the names given by the C++ front-end to vtables, and
@@ -15241,6 +15280,9 @@ output_toc (FILE *file, rtx x, int label
       h = ggc_alloc (sizeof (*h));
       h->key = x;
       h->key_mode = mode;
+      h->toc_labelno = 1;
+      if (TOC_PER_FUNCTION)
+	h->toc_labelno = current_function_funcdef_no + 2;
       h->labelno = labelno;
 
       found = htab_find_slot (toc_hash_table, h, 1);
@@ -17591,13 +17633,12 @@ rs6000_elf_declare_function_name (FILE *
       && (get_pool_size () != 0 || current_function_profile)
       && uses_TOC ())
     {
-      char buf[256];
+      char buf[32];
 
       (*targetm.asm_out.internal_label) (file, "LCL", rs6000_pic_labelno);
 
-      ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 1);
       fprintf (file, "\t.long ");
-      assemble_name (file, buf);
+      assemble_name (file, cfun->machine->toc_label_name);
       putc ('-', file);
       ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno);
       assemble_name (file, buf);
diff -urp -xCVS -x'*~' -x'.#*' gcc-virgin/gcc/config/rs6000/rs6000.h gcc-current/gcc/config/rs6000/rs6000.h
--- gcc-virgin/gcc/config/rs6000/rs6000.h	2005-07-13 09:57:27.000000000 +0930
+++ gcc-current/gcc/config/rs6000/rs6000.h	2005-07-22 13:22:11.000000000 +0930
@@ -1960,6 +1960,9 @@ extern int rs6000_compare_fp_p;
 /* Flag to say the TOC is initialized */
 extern int toc_initialized;
 
+/* TOC is usually per translation unit.  */
+#define TOC_PER_FUNCTION 0
+
 /* Macro to output a special constant pool entry.  Go to WIN if we output
    it.  Otherwise, it is written the usual way.
 
diff -urp -xCVS -x'*~' -x'.#*' gcc-virgin/gcc/config/rs6000/sysv4.h gcc-current/gcc/config/rs6000/sysv4.h
--- gcc-virgin/gcc/config/rs6000/sysv4.h	2005-07-13 09:57:27.000000000 +0930
+++ gcc-current/gcc/config/rs6000/sysv4.h	2005-07-25 11:43:44.000000000 +0930
@@ -355,6 +355,20 @@ do {									\
   (TARGET_RELOCATABLE || (flag_pic && DEFAULT_ABI != ABI_AIX)		\
    ? "\t.section\t\".got2\",\"aw\"" : "\t.section\t\".got1\",\"aw\"")
 
+#if HAVE_LD_MULT_GOT2 && HAVE_COMDAT_GROUP
+/* If function text will be placed in separate sections, then place
+   toc entries for the function in separate sections too.  */
+#undef TOC_PER_FUNCTION
+#define TOC_PER_FUNCTION \
+  (TARGET_ELF								\
+   && DEFAULT_ABI == ABI_V4						\
+   && flag_pic == 2							\
+   && cfun != NULL							\
+   && cfun->decl != NULL						\
+   && (DECL_ONE_ONLY (cfun->decl)					\
+       || (flag_function_sections && flag_data_sections)))
+#endif
+
 #define	SDATA_SECTION_ASM_OP "\t.section\t\".sdata\",\"aw\""
 #define	SDATA2_SECTION_ASM_OP "\t.section\t\".sdata2\",\"a\""
 #define	SBSS_SECTION_ASM_OP "\t.section\t\".sbss\",\"aw\",@nobits"
@@ -378,7 +392,38 @@ do {									\
 void									\
 toc_section (void)							\
 {									\
-  if (in_section != in_toc)						\
+  if (TOC_PER_FUNCTION)							\
+    {									\
+      if (in_section != in_toc						\
+	  || toc_initialized != current_function_funcdef_no + 2)	\
+	{								\
+	  const char *s;						\
+									\
+	  in_section = in_toc;						\
+	  s = TREE_STRING_POINTER (DECL_SECTION_NAME (cfun->decl));	\
+	  s = strchr (s + 1, '.');					\
+	  if (s == NULL)						\
+	    s = "";							\
+									\
+	  fprintf (asm_out_file, "\t.section\t\".got2%s\",\"aw", s);	\
+	  if (DECL_ONE_ONLY (cfun->decl))				\
+	    fprintf (asm_out_file, "G\",%s,comdat\n",			\
+		     lang_hooks.decls.comdat_group (cfun->decl));	\
+	  else								\
+	    fprintf (asm_out_file, "\"\n");				\
+	  if (toc_initialized != current_function_funcdef_no + 2)	\
+	    {								\
+	      toc_initialized = current_function_funcdef_no + 2;	\
+	      ASM_OUTPUT_INTERNAL_LABEL_PREFIX (asm_out_file, "LCTOC");	\
+	      fprintf (asm_out_file, "%d", current_function_funcdef_no + 2); \
+	      if (*s == 0)						\
+		fprintf (asm_out_file, " = LCTOC1\n");			\
+	      else							\
+		fprintf (asm_out_file, " = .+32768\n");			\
+	    }								\
+	}								\
+    }									\
+  else if (in_section != in_toc)					\
     {									\
       in_section = in_toc;						\
       if (DEFAULT_ABI == ABI_AIX					\
diff -urp -xCVS -x'*~' -x'.#*' gcc-virgin/gcc/varasm.c gcc-current/gcc/varasm.c
--- gcc-virgin/gcc/varasm.c	2005-07-20 13:07:30.000000000 +0930
+++ gcc-current/gcc/varasm.c	2005-07-20 18:50:20.000000000 +0930
@@ -1256,11 +1256,11 @@ assemble_start_function (tree decl, cons
 
   app_disable ();
 
+  resolve_unique_section (decl, 0, flag_function_sections);
+
   if (CONSTANT_POOL_BEFORE_FUNCTION)
     output_constant_pool (fnname, decl);
 
-  resolve_unique_section (decl, 0, flag_function_sections);
-
   /* Make sure the not and cold text (code) sections are properly
      aligned.  This is necessary here in the case where the function
      has both hot and cold sections, because we don't want to re-set
diff -urp -xCVS -x'*~' -x'.#*' gcc-virgin/gcc/configure.ac gcc-current/gcc/configure.ac
--- gcc-virgin/gcc/configure.ac	2005-07-13 09:56:52.000000000 +0930
+++ gcc-current/gcc/configure.ac	2005-07-25 15:49:08.000000000 +0930
@@ -2939,6 +2939,34 @@ if test x"$gcc_cv_ld_as_needed" = xyes; 
 fi
 
 case "$target" in
+  powerpc*-*-aix* | powerpc*-*-darwin*) ;;
+  powerpc*-*-*)
+    AC_CACHE_CHECK(linker support for multiple got2 sections,
+    gcc_cv_ld_mult_got2,
+    [gcc_cv_ld_mult_got2=no
+    if test x$gcc_cv_as != x -a x$gcc_cv_ld != x -a x$gcc_cv_objdump != x ; then
+      cat > conftest.s <<EOF
+	.section ".got2.first", "aw"
+	.long 1
+	.section ".got2.second", "aw"
+	.long 2
+EOF
+      if $gcc_cv_as -a32 -o conftest.o conftest.s > /dev/null 2>&1 \
+	 && $gcc_cv_ld -melf32ppc -o conftest conftest.o > /dev/null 2>&1 \
+	 && $gcc_cv_objdump -h conftest | grep '\.got2 ' > /dev/null; then
+        gcc_cv_ld_mult_got2=yes
+      fi
+      rm -f conftest conftest.o conftest.s
+    fi
+    ])
+    if test x"$gcc_cv_ld_mult_got2" = xyes; then
+      AC_DEFINE(HAVE_LD_MULT_GOT2, 1,
+    [Define if your PowerPC linker supports multiple .got2 sections.])
+    fi
+    ;;
+esac
+
+case "$target" in
   powerpc64*-*-linux*)
     AC_CACHE_CHECK(linker support for omitting dot symbols,
     gcc_cv_ld_no_dot_syms,

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]