diff mbox

[nios2] Large -fPIC support

Message ID 53062EFE.2050105@codesourcery.com
State New
Headers show

Commit Message

Chung-Lin Tang Feb. 20, 2014, 4:36 p.m. UTC
This patch adds large GOT support for the Nios II backend. Tested by
running glibc tests with -fPIC forced on.  A few smaller libgcc fixes
are also included as well. Patch committed.

Chung-Lin

2014-02-20  Chung-Lin Tang  <cltang@codesourcery.com>
            Sandra Loosemore  <sandra@codesourcery.com>

	gcc/
        * config/nios2/nios2.md (unspec): Add UNSPEC_PIC_GOTOFF_SYM enum.
        * config/nios2/nios2.c (nios2_function_profiler):
        Add -fPIC (flag_pic == 2) support.
        (nios2_handle_custom_fpu_cfg): Fix warning parameter.
        (nios2_large_offset_p): New function.
        (nios2_unspec_reloc_p): Move up position, update to use
        nios2_large_offset_p.
        (nios2_unspec_address): Remove function.
        (nios2_unspec_offset): New function.
        (nios2_large_got_address): New function.
        (nios2_got_address): Add large offset support.
        (nios2_legitimize_tls_address): Update usage of removed and new
        functions.
        (nios2_symbol_binds_local_p): New function.
        (nios2_load_pic_address): Add -fPIC (flag_pic == 2) support.
        (nios2_legitimize_address): Update to use nios2_large_offset_p.
        (nios2_emit_move_sequence): Avoid legitimizing (const (unspec ...)).
        (nios2_print_operand): Merge H/L processing, add hiadj/lo
        processing for (const (unspec ...)).
        (nios2_unspec_reloc_name): Add UNSPEC_PIC_GOTOFF_SYM case.

	gcc/testsuite/
        * gcc.target/nios2/biggot-1.c: New.
        * gcc.target/nios2/biggot-2.c: New.

	libgcc/
        * config/nios2/t-nios2 (CRTSTUFF_T_CFLAGS): Add -mno-gpopt.
        * config/nios2/crti.S: Remove .file directive.
        * config/nios2/crtn.S: Likewise.
diff mbox

Patch

Index: gcc/config/nios2/nios2.c
===================================================================
--- gcc/config/nios2/nios2.c	(revision 207964)
+++ gcc/config/nios2/nios2.c	(working copy)
@@ -664,7 +664,7 @@  void
 nios2_function_profiler (FILE *file, int labelno ATTRIBUTE_UNUSED)
 {
   fprintf (file, "\tmov\tr8, ra\n");
-  if (flag_pic)
+  if (flag_pic == 1)
     {
       fprintf (file, "\tnextpc\tr2\n");
       fprintf (file, "\t1: movhi\tr3, %%hiadj(_gp_got - 1b)\n");
@@ -673,6 +673,18 @@  nios2_function_profiler (FILE *file, int labelno A
       fprintf (file, "\tldw\tr2, %%call(_mcount)(r2)\n");
       fprintf (file, "\tcallr\tr2\n");
     }
+  else if (flag_pic == 2)
+    {
+      fprintf (file, "\tnextpc\tr2\n");
+      fprintf (file, "\t1: movhi\tr3, %%hiadj(_gp_got - 1b)\n");
+      fprintf (file, "\taddi\tr3, r3, %%lo(_gp_got - 1b)\n");
+      fprintf (file, "\tadd\tr2, r2, r3\n");
+      fprintf (file, "\tmovhi\tr3, %%call_hiadj(_mcount)\n");
+      fprintf (file, "\taddi\tr3, %%call_lo(_mcount)\n");
+      fprintf (file, "\tadd\tr3, r2, r3\n");
+      fprintf (file, "\tldw\tr2, 0(r3)\n");
+      fprintf (file, "\tcallr\tr2\n");
+    }
   else
     fprintf (file, "\tcall\t_mcount\n");
   fprintf (file, "\tmov\tra, r8\n");
@@ -920,7 +932,7 @@  nios2_handle_custom_fpu_cfg (const char *cfgname,
     }
   else
     warning (0, "ignoring unrecognized switch %<-mcustom-fpu-cfg%> "
-	     "value %<%s%>", cfg);    
+	     "value %<%s%>", cfgname);
 
   /* Guard against errors in the standard configurations.  */
   nios2_custom_check_insns ();
@@ -1116,20 +1128,64 @@  nios2_call_tls_get_addr (rtx ti)
   return ret;
 }
 
+/* Return true for large offsets requiring hiadj/lo relocation pairs.  */
+static bool
+nios2_large_offset_p (int unspec)
+{
+  gcc_assert (nios2_unspec_reloc_name (unspec) != NULL);
+
+  if (flag_pic == 2
+      /* FIXME: TLS GOT offset relocations will eventually also get this
+	 treatment, after binutils support for those are also completed.  */
+      && (unspec == UNSPEC_PIC_SYM || unspec == UNSPEC_PIC_CALL_SYM))
+    return true;
+
+  /* 'gotoff' offsets are always hiadj/lo.  */
+  if (unspec == UNSPEC_PIC_GOTOFF_SYM)
+    return true;
+
+  return false;
+}
+
+/* Return true for conforming unspec relocations.  Also used in
+   constraints.md and predicates.md.  */
+bool
+nios2_unspec_reloc_p (rtx op)
+{
+  return (GET_CODE (op) == CONST
+	  && GET_CODE (XEXP (op, 0)) == UNSPEC
+	  && ! nios2_large_offset_p (XINT (XEXP (op, 0), 1)));
+}
+
+/* Helper to generate unspec constant.  */
 static rtx
-nios2_unspec_address (rtx loc, rtx base_reg, int unspec)
+nios2_unspec_offset (rtx loc, int unspec)
 {
-  rtx unspec_offset =
-    gen_rtx_CONST (Pmode, gen_rtx_UNSPEC (Pmode, gen_rtvec (1, loc),
-					  unspec));
-  return gen_rtx_PLUS (Pmode, base_reg, unspec_offset);
+  return gen_rtx_CONST (Pmode, gen_rtx_UNSPEC (Pmode, gen_rtvec (1, loc),
+					       unspec));
 }
 
+/* Generate GOT pointer based address with large offset.  */
 static rtx
+nios2_large_got_address (rtx sym, rtx offset)
+{
+  rtx addr = gen_reg_rtx (Pmode);
+  emit_insn (gen_add3_insn (addr, pic_offset_table_rtx,
+			    force_reg (Pmode, offset)));
+  return addr;
+}
+
+/* Generate a GOT pointer based address.  */
+static rtx
 nios2_got_address (rtx loc, int unspec)
 {
+  rtx offset = nios2_unspec_offset (loc, unspec);
   crtl->uses_pic_offset_table = 1;
-  return nios2_unspec_address (loc, pic_offset_table_rtx, unspec);
+
+  if (nios2_large_offset_p (unspec))
+    return nios2_large_got_address (loc, offset);
+
+  return gen_rtx_PLUS (Pmode, pic_offset_table_rtx, offset);
 }
 
 /* Generate the code to access LOC, a thread local SYMBOL_REF.  The
@@ -1151,8 +1207,8 @@  nios2_legitimize_tls_address (rtx loc)
     case TLS_MODEL_LOCAL_DYNAMIC:
       tmp = gen_reg_rtx (Pmode);
       emit_move_insn (tmp, nios2_got_address (loc, UNSPEC_ADD_TLS_LDM));
-      return nios2_unspec_address (loc, nios2_call_tls_get_addr (tmp),
-				   UNSPEC_ADD_TLS_LDO);
+      return gen_rtx_PLUS (Pmode, nios2_call_tls_get_addr (tmp),
+			   nios2_unspec_offset (loc, UNSPEC_ADD_TLS_LDO));
 
     case TLS_MODEL_INITIAL_EXEC:
       tmp = gen_reg_rtx (Pmode);
@@ -1163,8 +1219,8 @@  nios2_legitimize_tls_address (rtx loc)
 
     case TLS_MODEL_LOCAL_EXEC:
       tp = gen_rtx_REG (Pmode, TP_REGNO);
-      return nios2_unspec_address (loc, tp, UNSPEC_ADD_TLS_LE);
-
+      return gen_rtx_PLUS (Pmode, tp,
+			   nios2_unspec_offset (loc, UNSPEC_ADD_TLS_LE));
     default:
       gcc_unreachable ();
     }
@@ -1599,7 +1655,16 @@  nios2_section_type_flags (tree decl, const char *n
   return flags;
 }
 
+/* Return true if SYMBOL_REF X binds locally.  */
 
+static bool
+nios2_symbol_binds_local_p (const_rtx x)
+{
+  return (SYMBOL_REF_DECL (x)
+	  ? targetm.binds_local_p (SYMBOL_REF_DECL (x))
+	  : SYMBOL_REF_LOCAL_P (x));
+}
+
 /* Position independent code related.  */
 
 /* Emit code to load the PIC register.  */
@@ -1616,8 +1681,13 @@  nios2_load_pic_register (void)
 static rtx
 nios2_load_pic_address (rtx sym, int unspec)
 {
-  rtx gotaddr = nios2_got_address (sym, unspec);
-  return gen_const_mem (Pmode, gotaddr);
+  if (flag_pic == 2
+      && GET_CODE (sym) == SYMBOL_REF
+      && nios2_symbol_binds_local_p (sym))
+    /* Under -fPIC, generate a GOTOFF address for local symbols.  */
+    return nios2_got_address (sym, UNSPEC_PIC_GOTOFF_SYM);
+
+  return gen_const_mem (Pmode, nios2_got_address (sym, unspec));
 }
 
 /* Nonzero if the constant value X is a legitimate general operand
@@ -1626,6 +1696,11 @@  nios2_load_pic_address (rtx sym, int unspec)
 bool
 nios2_legitimate_pic_operand_p (rtx x)
 {
+  if (GET_CODE (x) == CONST
+      && GET_CODE (XEXP (x, 0)) == UNSPEC
+      && nios2_large_offset_p (XINT (XEXP (x, 0), 1)))
+    return true;
+
   return ! (GET_CODE (x) == SYMBOL_REF
 	    || GET_CODE (x) == LABEL_REF || GET_CODE (x) == CONST);
 }
@@ -1701,7 +1776,7 @@  nios2_legitimize_address (rtx x, rtx oldx ATTRIBUT
       rtx unspec, offset, reg = XEXP (x, 0);
       split_const (XEXP (x, 1), &unspec, &offset);
       if (GET_CODE (unspec) == UNSPEC
-	  && nios2_unspec_reloc_name (XINT (unspec, 1)) != NULL
+	  && !nios2_large_offset_p (XINT (unspec, 1))
 	  && offset != const0_rtx)
 	{
 	  unspec = copy_rtx (unspec);
@@ -1728,7 +1803,8 @@  nios2_emit_move_sequence (rtx *operands, enum mach
     }
 
   if (GET_CODE (from) == SYMBOL_REF || GET_CODE (from) == LABEL_REF
-      || GET_CODE (from) == CONST)
+      || (GET_CODE (from) == CONST
+	  && GET_CODE (XEXP (from, 0)) != UNSPEC))
     from = nios2_legitimize_constant_address (from);
 
   operands[0] = to;
@@ -1845,20 +1921,23 @@  nios2_print_operand (FILE *file, rtx op, int lette
           output_addr_const (file, op);
           return;
         }
-      else if (letter == 'H')
-        {
-          fprintf (file, "%%hiadj(");
+      else if (letter == 'H' || letter == 'L')
+	{
+	  fprintf (file, "%%");
+	  if (GET_CODE (op) == CONST
+	      && GET_CODE (XEXP (op, 0)) == UNSPEC)
+	    {
+	      rtx unspec = XEXP (op, 0);
+	      int unspec_reloc = XINT (unspec, 1);
+	      gcc_assert (nios2_large_offset_p (unspec_reloc));
+	      fprintf (file, "%s_", nios2_unspec_reloc_name (unspec_reloc));
+	      op = XVECEXP (unspec, 0, 0);
+	    }
+          fprintf (file, letter == 'H' ? "hiadj(" : "lo(");
           output_addr_const (file, op);
           fprintf (file, ")");
           return;
-        }
-      else if (letter == 'L')
-        {
-          fprintf (file, "%%lo(");
-          output_addr_const (file, op);
-          fprintf (file, ")");
-          return;
-        }
+	}
       break;
 
     case SUBREG:
@@ -1910,6 +1989,8 @@  nios2_unspec_reloc_name (int unspec)
       return "got";
     case UNSPEC_PIC_CALL_SYM:
       return "call";
+    case UNSPEC_PIC_GOTOFF_SYM:
+      return "gotoff";
     case UNSPEC_LOAD_TLS_IE:
       return "tls_ie";
     case UNSPEC_ADD_TLS_LE:
@@ -1925,16 +2006,6 @@  nios2_unspec_reloc_name (int unspec)
     }
 }
 
-/* Return true for conforming unspec relocations.  Also used in
-   constraints.md and predicates.md.  */
-bool
-nios2_unspec_reloc_p (rtx op)
-{
-  return (GET_CODE (op) == CONST
-	  && GET_CODE (XEXP (op, 0)) == UNSPEC
-	  && nios2_unspec_reloc_name (XINT (XEXP (op, 0), 1)) != NULL);
-}
-
 /* Implement TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA.  */
 static bool
 nios2_output_addr_const_extra (FILE *file, rtx op)
Index: gcc/config/nios2/nios2.md
===================================================================
--- gcc/config/nios2/nios2.md	(revision 207964)
+++ gcc/config/nios2/nios2.md	(working copy)
@@ -73,6 +73,7 @@ 
   UNSPEC_LOAD_GOT_REGISTER
   UNSPEC_PIC_SYM
   UNSPEC_PIC_CALL_SYM
+  UNSPEC_PIC_GOTOFF_SYM
   UNSPEC_TLS
   UNSPEC_TLS_LDM
   UNSPEC_LOAD_TLS_IE
Index: gcc/testsuite/gcc.target/nios2/biggot-1.c
===================================================================
--- gcc/testsuite/gcc.target/nios2/biggot-1.c	(revision 0)
+++ gcc/testsuite/gcc.target/nios2/biggot-1.c	(revision 0)
@@ -0,0 +1,67 @@ 
+/* Check that the GOT pointer is being initialized correctly to allow
+   access to the full 64K maximum GOT size for -fpic, rather than only 32K
+   (which would happen if the GOT pointer points to the base of the GOT,
+   as the GOT16 and CALL16 relocations are signed).  */
+
+/* { dg-options "-fpic" } */
+/* { dg-do run { target nios2-*-linux-gnu } } */
+
+extern void abort (void);
+
+static int n = 0;
+
+void
+doit (int m)
+{
+  if (m != n)
+    abort ();
+  n++;
+}
+
+#define X(N) \
+  void f_##N (void) { doit (0x##N); }
+
+#define F(N) f_##N ();
+
+#define A(N) \
+  X(N##0) X(N##1) X(N##2) X(N##3) X(N##4) X(N##5) X(N##6) X(N##7) \
+  X(N##8) X(N##9) X(N##a) X(N##b) X(N##c) X(N##d) X(N##e) X(N##f) \
+  void f_##N (void) { \
+    F(N##0) F(N##1) F(N##2) F(N##3) F(N##4) F(N##5) F(N##6) F(N##7) \
+    F(N##8) F(N##9) F(N##a) F(N##b) F(N##c) F(N##d) F(N##e) F(N##f) \
+    }
+
+#define B(N) \
+  A(N##0) A(N##1) A(N##2) A(N##3) A(N##4) A(N##5) A(N##6) A(N##7) \
+  A(N##8) A(N##9) A(N##a) A(N##b) A(N##c) A(N##d) A(N##e) A(N##f) \
+  void f_##N (void) { \
+    F(N##0) F(N##1) F(N##2) F(N##3) F(N##4) F(N##5) F(N##6) F(N##7) \
+    F(N##8) F(N##9) F(N##a) F(N##b) F(N##c) F(N##d) F(N##e) F(N##f) \
+    }
+
+#define C(N) \
+  B(N##0) B(N##1) B(N##2) B(N##3) B(N##4) B(N##5) B(N##6) B(N##7) \
+  B(N##8) B(N##9) B(N##a) B(N##b) B(N##c) B(N##d) B(N##e) B(N##f) \
+  void f_##N (void) { \
+    F(N##0) F(N##1) F(N##2) F(N##3) F(N##4) F(N##5) F(N##6) F(N##7) \
+    F(N##8) F(N##9) F(N##a) F(N##b) F(N##c) F(N##d) F(N##e) F(N##f) \
+    }
+
+#define D(N) \
+  C(N##0) C(N##1) C(N##2) \
+  void f_##N (void) { \
+    F(N##0) F(N##1) F(N##2) \
+    }
+
+/* This defines 16x16x16x3 leaf functions, requiring something over
+   48K of GOT space overall.  */
+D(0)
+
+int
+main (void)
+{
+  f_0 ();
+  if (n != 16*16*16*3)
+    abort ();
+  return 0;
+}
Index: gcc/testsuite/gcc.target/nios2/biggot-2.c
===================================================================
--- gcc/testsuite/gcc.target/nios2/biggot-2.c	(revision 0)
+++ gcc/testsuite/gcc.target/nios2/biggot-2.c	(revision 0)
@@ -0,0 +1,68 @@ 
+/* Check that a program that requires large-GOT support builds and
+   executes without error.  This program defines a very large number
+   of leaf functions; compiled with -fPIC, they all require GOT
+   entries, which will overflow the range addressible by 16-bit -fpic
+   offsets by about a factor of 2.  */
+
+/* { dg-options "-fPIC" } */
+/* { dg-do run { target nios2-*-linux-gnu } } */
+
+extern void abort (void);
+
+static int n = 0;
+
+void
+doit (int m)
+{
+  if (m != n)
+    abort ();
+  n++;
+}
+
+#define X(N) \
+  void f_##N (void) { doit (0x##N); }
+
+#define F(N) f_##N ();
+
+#define A(N) \
+  X(N##0) X(N##1) X(N##2) X(N##3) X(N##4) X(N##5) X(N##6) X(N##7) \
+  X(N##8) X(N##9) X(N##a) X(N##b) X(N##c) X(N##d) X(N##e) X(N##f) \
+  void f_##N (void) { \
+    F(N##0) F(N##1) F(N##2) F(N##3) F(N##4) F(N##5) F(N##6) F(N##7) \
+    F(N##8) F(N##9) F(N##a) F(N##b) F(N##c) F(N##d) F(N##e) F(N##f) \
+    }
+
+#define B(N) \
+  A(N##0) A(N##1) A(N##2) A(N##3) A(N##4) A(N##5) A(N##6) A(N##7) \
+  A(N##8) A(N##9) A(N##a) A(N##b) A(N##c) A(N##d) A(N##e) A(N##f) \
+  void f_##N (void) { \
+    F(N##0) F(N##1) F(N##2) F(N##3) F(N##4) F(N##5) F(N##6) F(N##7) \
+    F(N##8) F(N##9) F(N##a) F(N##b) F(N##c) F(N##d) F(N##e) F(N##f) \
+    }
+
+#define C(N) \
+  B(N##0) B(N##1) B(N##2) B(N##3) B(N##4) B(N##5) B(N##6) B(N##7) \
+  B(N##8) B(N##9) B(N##a) B(N##b) B(N##c) B(N##d) B(N##e) B(N##f) \
+  void f_##N (void) { \
+    F(N##0) F(N##1) F(N##2) F(N##3) F(N##4) F(N##5) F(N##6) F(N##7) \
+    F(N##8) F(N##9) F(N##a) F(N##b) F(N##c) F(N##d) F(N##e) F(N##f) \
+    }
+
+#define D(N) \
+  C(N##0) C(N##1) C(N##2) C(N##3) C(N##4) C(N##5) C(N##6) C(N##7) \
+  void f_##N (void) { \
+    F(N##0) F(N##1) F(N##2) F(N##3) F(N##4) F(N##5) F(N##6) F(N##7) \
+    }
+
+/* This defines 16x16x16x8 leaf functions, requiring something over
+   128K of GOT space overall.  */
+D(0)
+
+int
+main (void)
+{
+  f_0 ();
+  if (n != 16*16*16*8)
+    abort ();
+  return 0;
+}
Index: libgcc/config/nios2/crti.S
===================================================================
--- libgcc/config/nios2/crti.S	(revision 207964)
+++ libgcc/config/nios2/crti.S	(working copy)
@@ -36,8 +36,6 @@  see the files COPYING3 and COPYING.RUNTIME respect
 
    See crt0.s for the code that calls init and fini.  */
 
-	.file	"crti.asm"
-
 	.section	".init"
 	.align 2
 	.global	_init
Index: libgcc/config/nios2/crtn.S
===================================================================
--- libgcc/config/nios2/crtn.S	(revision 207964)
+++ libgcc/config/nios2/crtn.S	(working copy)
@@ -23,13 +23,9 @@  see the files COPYING3 and COPYING.RUNTIME respect
 
 
 /* This file just makes sure that the .fini and .init sections do in
-fact return.  Users may put any desired instructions in those sections.
-This file is the last thing linked into any executable.
-*/	
-	.file	"crtn.asm"
+   fact return.  Users may put any desired instructions in those sections.
+   This file is the last thing linked into any executable.  */	
 
-
-
 	.section	".init"
 	ldw	ra, 44(sp)
 	ldw	r23, 40(sp)
Index: libgcc/config/nios2/t-nios2
===================================================================
--- libgcc/config/nios2/t-nios2	(revision 207964)
+++ libgcc/config/nios2/t-nios2	(working copy)
@@ -3,3 +3,6 @@  LIB2ADD += $(srcdir)/config/nios2/lib2-divmod.c \
 	   $(srcdir)/config/nios2/lib2-divtable.c \
 	   $(srcdir)/config/nios2/lib2-mul.c \
 	   $(srcdir)/config/nios2/tramp.c
+
+# Disable use of GP-relative addressing in startup code.
+CRTSTUFF_T_CFLAGS += -mno-gpopt