Properly handle -fno-plt in ix86_expand_call
authorH.J. Lu <hongjiu.lu@intel.com>
Tue, 27 Oct 2015 14:29:31 +0000 (14:29 +0000)
committerH.J. Lu <hjl@gcc.gnu.org>
Tue, 27 Oct 2015 14:29:31 +0000 (07:29 -0700)
prepare_call_address in calls.c is the wrong place to handle -fno-plt.
We shoudn't force function address into register and hope that load
function address via GOT and indirect call via register will be folded
into indirect call via GOT, which doesn't always happen.  Also non-PIC
case can only be handled in backend.  Instead, backend should expand
external function call into indirect call via GOT for -fno-plt.

This patch reverts -fno-plt in prepare_call_address and handles it in
ix86_expand_call.  Other backends may need similar changes to support
-fno-plt.  Alternately, we can introduce a target hook to indicate
whether an external function should be called via register for -fno-plt
so that i386 backend can disable it in prepare_call_address.

gcc/

PR target/67215
* calls.c (prepare_call_address): Don't handle -fno-plt here.
* config/i386/i386.c (ix86_expand_call): Generate indirect call
via GOT for -fno-plt.  Support indirect call via GOT for x32.
* config/i386/predicates.md (sibcall_memory_operand): Allow
GOT memory operand.

gcc/testsuite/

PR target/67215
* gcc.target/i386/pr67215-1.c: New test.
* gcc.target/i386/pr67215-2.c: Likewise.
* gcc.target/i386/pr67215-3.c: Likewise.

From-SVN: r229444

gcc/ChangeLog
gcc/calls.c
gcc/config/i386/i386.c
gcc/config/i386/predicates.md
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/i386/pr67215-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/pr67215-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/pr67215-3.c [new file with mode: 0644]

index 9659978a60815f7fc11829c55ab3fa2dddb7053f..2891da7e34a29e140be70b30482f6ba189b59c1e 100644 (file)
@@ -1,3 +1,12 @@
+2015-10-27  H.J. Lu  <hongjiu.lu@intel.com>
+
+       PR target/67215
+       * calls.c (prepare_call_address): Don't handle -fno-plt here.
+       * config/i386/i386.c (ix86_expand_call): Generate indirect call
+       via GOT for -fno-plt.  Support indirect call via GOT for x32.
+       * config/i386/predicates.md (sibcall_memory_operand): Allow
+       GOT memory operand.
+
 2015-10-27  Richard Biener  <rguenther@suse.de>
 
        PR tree-optimization/68104
index 6e6f33dbd873268e207045bd599a3ee965b6bed7..68c4eb56611be5b0ba64b1c03efd879109207b24 100644 (file)
@@ -203,18 +203,6 @@ prepare_call_address (tree fndecl_or_type, rtx funexp, rtx static_chain_value,
               && targetm.small_register_classes_for_mode_p (FUNCTION_MODE))
              ? force_not_mem (memory_address (FUNCTION_MODE, funexp))
              : memory_address (FUNCTION_MODE, funexp));
-  else if (flag_pic
-          && fndecl_or_type
-          && TREE_CODE (fndecl_or_type) == FUNCTION_DECL
-          && (!flag_plt
-              || lookup_attribute ("noplt", DECL_ATTRIBUTES (fndecl_or_type)))
-          && !targetm.binds_local_p (fndecl_or_type))
-    {
-      /* This is done only for PIC code.  There is no easy interface to force the
-        function address into GOT for non-PIC case.  non-PIC case needs to be
-        handled specially by the backend.  */
-      funexp = force_reg (Pmode, funexp);
-    }
   else if (! sibcallp)
     {
       if (!NO_FUNCTION_CSE && optimize && ! flag_no_function_cse)
index 1bc6ef9caf9aa9c00dbd76b5f00cb7b9c36ab737..d05c8f8493a889e6d949a4ab50e73d188a9b253f 100644 (file)
@@ -26753,21 +26753,54 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
       /* Static functions and indirect calls don't need the pic register.  Also,
         check if PLT was explicitly avoided via no-plt or "noplt" attribute, making
         it an indirect call.  */
+      rtx addr = XEXP (fnaddr, 0);
       if (flag_pic
-         && (!TARGET_64BIT
-             || (ix86_cmodel == CM_LARGE_PIC
-                 && DEFAULT_ABI != MS_ABI))
-         && GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF
-         && !SYMBOL_REF_LOCAL_P (XEXP (fnaddr, 0))
-         && flag_plt
-         && (SYMBOL_REF_DECL ((XEXP (fnaddr, 0))) == NULL_TREE
-             || !lookup_attribute ("noplt",
-                    DECL_ATTRIBUTES (SYMBOL_REF_DECL (XEXP (fnaddr, 0))))))
+         && GET_CODE (addr) == SYMBOL_REF
+         && !SYMBOL_REF_LOCAL_P (addr))
        {
-         use_reg (&use, gen_rtx_REG (Pmode, REAL_PIC_OFFSET_TABLE_REGNUM));
-         if (ix86_use_pseudo_pic_reg ())
-           emit_move_insn (gen_rtx_REG (Pmode, REAL_PIC_OFFSET_TABLE_REGNUM),
-                           pic_offset_table_rtx);
+         if (flag_plt
+             && (SYMBOL_REF_DECL (addr) == NULL_TREE
+                 || !lookup_attribute ("noplt",
+                                       DECL_ATTRIBUTES (SYMBOL_REF_DECL (addr)))))
+           {
+             if (!TARGET_64BIT
+                 || (ix86_cmodel == CM_LARGE_PIC
+                     && DEFAULT_ABI != MS_ABI))
+               {
+                 use_reg (&use, gen_rtx_REG (Pmode,
+                                             REAL_PIC_OFFSET_TABLE_REGNUM));
+                 if (ix86_use_pseudo_pic_reg ())
+                   emit_move_insn (gen_rtx_REG (Pmode,
+                                                REAL_PIC_OFFSET_TABLE_REGNUM),
+                                   pic_offset_table_rtx);
+               }
+           }
+         else if (!TARGET_PECOFF && !TARGET_MACHO)
+           {
+             if (TARGET_64BIT)
+               {
+                 fnaddr = gen_rtx_UNSPEC (Pmode,
+                                          gen_rtvec (1, addr),
+                                          UNSPEC_GOTPCREL);
+                 fnaddr = gen_rtx_CONST (Pmode, fnaddr);
+               }
+             else
+               {
+                 fnaddr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr),
+                                          UNSPEC_GOT);
+                 fnaddr = gen_rtx_CONST (Pmode, fnaddr);
+                 fnaddr = gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
+                                        fnaddr);
+               }
+             fnaddr = gen_const_mem (Pmode, fnaddr);
+             /* Pmode may not be the same as word_mode for x32, which
+                doesn't support indirect branch via 32-bit memory slot.
+                Since x32 GOT slot is 64 bit with zero upper 32 bits,
+                indirect branch via x32 GOT slot is OK.  */
+             if (GET_MODE (fnaddr) != word_mode)
+               fnaddr = gen_rtx_ZERO_EXTEND (word_mode, fnaddr);
+             fnaddr = gen_rtx_MEM (QImode, fnaddr);
+           }
        }
     }
 
@@ -26789,9 +26822,15 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1,
       && GET_CODE (XEXP (fnaddr, 0)) == SYMBOL_REF
       && !local_symbolic_operand (XEXP (fnaddr, 0), VOIDmode))
     fnaddr = gen_rtx_MEM (QImode, construct_plt_address (XEXP (fnaddr, 0)));
-  else if (sibcall
-          ? !sibcall_insn_operand (XEXP (fnaddr, 0), word_mode)
-          : !call_insn_operand (XEXP (fnaddr, 0), word_mode))
+  /* Since x32 GOT slot is 64 bit with zero upper 32 bits, indirect
+     branch via x32 GOT slot is OK.  */
+  else if (!(TARGET_X32
+            && MEM_P (fnaddr)
+            && GET_CODE (XEXP (fnaddr, 0)) == ZERO_EXTEND
+            && GOT_memory_operand (XEXP (XEXP (fnaddr, 0), 0), Pmode))
+          && (sibcall
+              ? !sibcall_insn_operand (XEXP (fnaddr, 0), word_mode)
+              : !call_insn_operand (XEXP (fnaddr, 0), word_mode)))
     {
       fnaddr = convert_to_mode (word_mode, XEXP (fnaddr, 0), 1);
       fnaddr = gen_rtx_MEM (QImode, copy_to_mode_reg (word_mode, fnaddr));
index 042b9494a2bf46593694cb2b3c901062adb2bb4c..1595142255da8d264e1606503327cf72f5e70a8d 100644 (file)
 ;; Return true if OP is a memory operands that can be used in sibcalls.
 (define_predicate "sibcall_memory_operand"
   (and (match_operand 0 "memory_operand")
-       (match_test "CONSTANT_P (XEXP (op, 0))")))
+       (match_test "CONSTANT_P (XEXP (op, 0))
+                   || (GET_CODE (XEXP (op, 0)) == PLUS
+                       && REG_P (XEXP (XEXP (op, 0), 0))
+                       && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST
+                       && GET_CODE (XEXP (XEXP (XEXP (op, 0), 1), 0)) == UNSPEC
+                       && XINT (XEXP (XEXP (XEXP (op, 0), 1), 0), 1) == UNSPEC_GOT)")))
 
 ;; Test for a valid operand for a call instruction.
 ;; Allow constant call address operands in Pmode only.
index bcadb0c390019f7899cc5ecfe5b2e353553c4412..e338762a5e6b61e816d51e0f3f6947876a19da39 100644 (file)
@@ -1,3 +1,10 @@
+2015-10-27  H.J. Lu  <hongjiu.lu@intel.com>
+
+       PR target/67215
+       * gcc.target/i386/pr67215-1.c: New test.
+       * gcc.target/i386/pr67215-2.c: Likewise.
+       * gcc.target/i386/pr67215-3.c: Likewise.
+
 2015-10-27  Richard Biener  <rguenther@suse.de>
 
        PR tree-optimization/68104
diff --git a/gcc/testsuite/gcc.target/i386/pr67215-1.c b/gcc/testsuite/gcc.target/i386/pr67215-1.c
new file mode 100644 (file)
index 0000000..fd37f8e
--- /dev/null
@@ -0,0 +1,20 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt" } */
+
+extern char* bar (int);
+extern char* arr[32];
+
+void
+foo (void)
+{
+  int i;
+
+  for (i = 0; i < 32; i++)
+    arr[i] = bar (128);
+}
+
+/* { dg-final { scan-assembler "call\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*.bar@GOT\\(" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]*.bar@GOT\\(" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*.bar@PLT" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr67215-2.c b/gcc/testsuite/gcc.target/i386/pr67215-2.c
new file mode 100644 (file)
index 0000000..ebf2919
--- /dev/null
@@ -0,0 +1,20 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic" } */
+
+extern char* bar (int) __attribute__ ((noplt));
+extern char* arr[32];
+
+void
+foo (void)
+{
+  int i;
+
+  for (i = 0; i < 32; i++)
+    arr[i] = bar (128);
+}
+
+/* { dg-final { scan-assembler "call\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*.bar@GOT\\(" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "mov(l|q)\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "movl\[ \t\]*.bar@GOT\\(" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]*.bar@PLT" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr67215-3.c b/gcc/testsuite/gcc.target/i386/pr67215-3.c
new file mode 100644 (file)
index 0000000..eb6bb39
--- /dev/null
@@ -0,0 +1,13 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -fdump-rtl-expand" } */
+
+extern int bar (void);
+
+int
+foo (void)
+{
+  return bar ();
+}
+
+/* { dg-final { scan-rtl-dump "\\(call \\(mem:QI \\(mem/u/c:" "expand" { target { ! x32 } } } } */
+/* { dg-final { scan-rtl-dump "\\(call \\(mem:QI \\(zero_extend:DI \\(mem/u/c:" "expand" { target x32 } } } */