x86-64: Optimize access to globals in PIE with copy reloc
authorSriraman Tallam <tmsriram@google.com>
Thu, 4 Dec 2014 19:40:50 +0000 (19:40 +0000)
committerH.J. Lu <hjl@gcc.gnu.org>
Thu, 4 Dec 2014 19:40:50 +0000 (11:40 -0800)
Normally, with -fPIE/-fpie, GCC accesses globals that are extern to the
module using the GOT.  This is two instructions, one to get the address
of the global from the GOT and the other to get the value.  If it turns
out that the global gets defined in the executable at link-time, it still
needs to go through the GOT as it is too late then to generate a direct
 access.

Examples:

foo.cc
------
int a_glob;
int main () {
  return a_glob; // defined in this file
}

With -O2 -fpie -pie, the generated code directly accesses the global via
PC-relative insn:

5e0   <main>:
   mov    0x165a(%rip),%eax        # 1c40 <a_glob>

foo.cc
------

extern int a_glob;
int main () {
  return a_glob; // defined in this file
}

With -O2 -fpie -pie, the generated code accesses global via GOT using
two memory loads:

6f0  <main>:
   mov    0x1609(%rip),%rax   # 1d00 <_DYNAMIC+0x230>
   mov    (%rax),%eax

This is true even if in the latter case the global was defined in the
executable through a different file.

Some experiments on google benchmarks shows that the extra memory loads
affects performance by 1% to 5%.

Solution - Copy Relocations:

When the linker supports copy relocations, GCC can always assume that
the global will be defined in the executable.  For globals that are truly
extern (come from shared objects), the linker will create copy relocations
and have them defined in the executable. Result is that no global access
needs to go through the GOT and hence improves performance.

This optimization only applies to undefined, non-weak global data.
Undefined, weak global data access still must go through the GOT.

This patch checks if linker supports PIE with copy reloc, which is
enabled in gold and bfd linker in bininutils 2.25, at configure time
and enables this optimization if the linker support is available.

gcc/

* configure.ac (HAVE_LD_PIE_COPYRELOC): Defined to 1 if
Linux/x86-64 linker supports PIE with copy reloc.
* config.in: Regenerated.
* configure: Likewise.

* config/i386/i386.c (legitimate_pic_address_disp_p): Allow
pc-relative address for undefined, non-weak, non-function
symbol reference in 64-bit PIE if linker supports PIE with
copy reloc.

* doc/sourcebuild.texi: Document pie_copyreloc target.

gcc/testsuite/

* gcc.target/i386/pie-copyrelocs-1.c: New test.
* gcc.target/i386/pie-copyrelocs-2.c: Likewise.
* gcc.target/i386/pie-copyrelocs-3.c: Likewise.
* gcc.target/i386/pie-copyrelocs-4.c: Likewise.

* lib/target-supports.exp (check_effective_target_pie_copyreloc):
New procedure.

Co-Authored-By: H.J. Lu <hongjiu.lu@intel.com>
From-SVN: r218397

12 files changed:
gcc/ChangeLog
gcc/config.in
gcc/config/i386/i386.c
gcc/configure
gcc/configure.ac
gcc/doc/sourcebuild.texi
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/i386/pie-copyrelocs-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/pie-copyrelocs-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/pie-copyrelocs-3.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/pie-copyrelocs-4.c [new file with mode: 0644]
gcc/testsuite/lib/target-supports.exp

index 2d41d93b171d5379571a70809dbd3dd754315e9c..18b481e11a0dbc69ec430c8f4f6bdf67f7ae3550 100644 (file)
@@ -1,3 +1,18 @@
+2014-12-04  Sriraman Tallam  <tmsriram@google.com>
+           H.J. Lu  <hongjiu.lu@intel.com>
+
+       * configure.ac (HAVE_LD_PIE_COPYRELOC): Defined to 1 if
+       Linux/x86-64 linker supports PIE with copy reloc.
+       * config.in: Regenerated.
+       * configure: Likewise.
+
+       * config/i386/i386.c (legitimate_pic_address_disp_p): Allow
+       pc-relative address for undefined, non-weak, non-function
+       symbol reference in 64-bit PIE if linker supports PIE with
+       copy reloc.
+
+       * doc/sourcebuild.texi: Document pie_copyreloc target.
+
 2014-12-04  Marek Polacek  <polacek@redhat.com>
 
        PR middle-end/56917
index 65d5e421a9038a027eb140208e10060d9ed1dc41..f34adb57e6ddd9a71f4ed53bafb7c1f080e17d18 100644 (file)
 #endif
 
 
+/* Define 0/1 if your linker supports -pie option with copy reloc. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_LD_PIE_COPYRELOC
+#endif
+
+
 /* Define if your linker links a mix of read-only and read-write sections into
    a read-write section. */
 #ifndef USED_FOR_TARGET
index 211c9e6a65ecccd956daf3b60cdc1d8a4b914f7b..4f1a18b993a7ca5ea6c3cb0593794a20f456c090 100644 (file)
@@ -13113,7 +13113,11 @@ legitimate_pic_address_disp_p (rtx disp)
                return true;
            }
          else if (!SYMBOL_REF_FAR_ADDR_P (op0)
-                  && SYMBOL_REF_LOCAL_P (op0)
+                  && (SYMBOL_REF_LOCAL_P (op0)
+                      || (HAVE_LD_PIE_COPYRELOC
+                          && flag_pie
+                          && !SYMBOL_REF_WEAK (op0)
+                          && !SYMBOL_REF_FUNCTION_P (op0)))
                   && ix86_cmodel != CM_LARGE_PIC)
            return true;
          break;
index 6b46bbb9e4e48e756f0198bebe6f0e6751f30b10..811f05dc78a583f2c8adc001834c476ab0edd4d7 100755 (executable)
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_ld_pie" >&5
 $as_echo "$gcc_cv_ld_pie" >&6; }
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking linker PIE support with copy reloc" >&5
+$as_echo_n "checking linker PIE support with copy reloc... " >&6; }
+gcc_cv_ld_pie_copyreloc=no
+if test $gcc_cv_ld_pie = yes ; then
+  if test $in_tree_ld = yes ; then
+    if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 25 -o "$gcc_cv_gld_major_version" -gt 2; then
+      gcc_cv_ld_pie_copyreloc=yes
+    fi
+  elif test x$gcc_cv_as != x -a x$gcc_cv_ld != x ; then
+    # Check if linker supports -pie option with copy reloc
+    case "$target" in
+    i?86-*-linux* | x86_64-*-linux*)
+      cat > conftest1.s <<EOF
+       .globl  a_glob
+       .data
+       .type   a_glob, @object
+       .size   a_glob, 4
+a_glob:
+       .long   2
+EOF
+      cat > conftest2.s <<EOF
+       .text
+       .globl  main
+       .type   main, @function
+main:
+       movl    %eax, a_glob(%rip)
+       .size   main, .-main
+EOF
+      if $gcc_cv_as --64 -o conftest1.o conftest1.s > /dev/null 2>&1 \
+         && $gcc_cv_ld -shared -melf_x86_64 -o conftest1.so conftest1.o > /dev/null 2>&1 \
+         && $gcc_cv_as --64 -o conftest2.o conftest2.s > /dev/null 2>&1 \
+         && $gcc_cv_ld -pie -melf_x86_64 -o conftest conftest2.o conftest1.so > /dev/null 2>&1; then
+        gcc_cv_ld_pie_copyreloc=yes
+      fi
+      rm -f conftest conftest1.so conftest1.o conftest2.o conftest1.s conftest2.s
+      ;;
+    esac
+  fi
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LD_PIE_COPYRELOC `if test x"$gcc_cv_ld_pie_copyreloc" = xyes; then echo 1; else echo 0; fi`
+_ACEOF
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_ld_pie_copyreloc" >&5
+$as_echo "$gcc_cv_ld_pie_copyreloc" >&6; }
+
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking linker EH-compatible garbage collection of sections" >&5
 $as_echo_n "checking linker EH-compatible garbage collection of sections... " >&6; }
 gcc_cv_ld_eh_gc_sections=no
index 48c8000b24973635bebac852a20390c95058baea..a33f3a57bb4d87b0e81e63ab5948d7f8b86c7b2c 100644 (file)
@@ -4693,6 +4693,49 @@ if test x"$gcc_cv_ld_pie" = xyes; then
 fi
 AC_MSG_RESULT($gcc_cv_ld_pie)
 
+AC_MSG_CHECKING(linker PIE support with copy reloc)
+gcc_cv_ld_pie_copyreloc=no
+if test $gcc_cv_ld_pie = yes ; then
+  if test $in_tree_ld = yes ; then
+    if test "$gcc_cv_gld_major_version" -eq 2 -a "$gcc_cv_gld_minor_version" -ge 25 -o "$gcc_cv_gld_major_version" -gt 2; then
+      gcc_cv_ld_pie_copyreloc=yes
+    fi
+  elif test x$gcc_cv_as != x -a x$gcc_cv_ld != x ; then
+    # Check if linker supports -pie option with copy reloc
+    case "$target" in
+    i?86-*-linux* | x86_64-*-linux*)
+      cat > conftest1.s <<EOF
+       .globl  a_glob
+       .data
+       .type   a_glob, @object
+       .size   a_glob, 4
+a_glob:
+       .long   2
+EOF
+      cat > conftest2.s <<EOF
+       .text
+       .globl  main
+       .type   main, @function
+main:
+       movl    %eax, a_glob(%rip)
+       .size   main, .-main
+EOF
+      if $gcc_cv_as --64 -o conftest1.o conftest1.s > /dev/null 2>&1 \
+         && $gcc_cv_ld -shared -melf_x86_64 -o conftest1.so conftest1.o > /dev/null 2>&1 \
+         && $gcc_cv_as --64 -o conftest2.o conftest2.s > /dev/null 2>&1 \
+         && $gcc_cv_ld -pie -melf_x86_64 -o conftest conftest2.o conftest1.so > /dev/null 2>&1; then
+        gcc_cv_ld_pie_copyreloc=yes
+      fi
+      rm -f conftest conftest1.so conftest1.o conftest2.o conftest1.s conftest2.s
+      ;;
+    esac
+  fi
+  AC_DEFINE_UNQUOTED(HAVE_LD_PIE_COPYRELOC,
+    [`if test x"$gcc_cv_ld_pie_copyreloc" = xyes; then echo 1; else echo 0; fi`],
+    [Define 0/1 if your linker supports -pie option with copy reloc.])
+fi
+AC_MSG_RESULT($gcc_cv_ld_pie_copyreloc)
+
 AC_MSG_CHECKING(linker EH-compatible garbage collection of sections)
 gcc_cv_ld_eh_gc_sections=no
 if test $in_tree_ld = yes ; then
index 20a206dbf321df79dd9f46e4e63763c3c747a447..98ba1a67c2a7921184542a5ce00639bda827922d 100644 (file)
@@ -1717,6 +1717,9 @@ or @code{EM_SPARCV9} executables.
 
 @item vect_cmdline_needed
 Target requires a command line argument to enable a SIMD instruction set.
+
+@item pie_copyreloc
+The x86-64 target linker supports PIE with copy reloc.
 @end table
 
 @subsubsection Environment attributes
index 0b3a9d682a92593ca9f2ad884ed6365556a514be..295a8103accf465eb17630243fde228a1eef70d8 100644 (file)
@@ -1,3 +1,14 @@
+2014-12-04  Sriraman Tallam  <tmsriram@google.com>
+           H.J. Lu  <hongjiu.lu@intel.com>
+
+       * gcc.target/i386/pie-copyrelocs-1.c: New test.
+       * gcc.target/i386/pie-copyrelocs-2.c: Likewise.
+       * gcc.target/i386/pie-copyrelocs-3.c: Likewise.
+       * gcc.target/i386/pie-copyrelocs-4.c: Likewise.
+
+       * lib/target-supports.exp (check_effective_target_pie_copyreloc):
+       New procedure.
+
 2014-12-04  Marek Polacek  <polacek@redhat.com>
 
        PR middle-end/56917
diff --git a/gcc/testsuite/gcc.target/i386/pie-copyrelocs-1.c b/gcc/testsuite/gcc.target/i386/pie-copyrelocs-1.c
new file mode 100644 (file)
index 0000000..7af851b
--- /dev/null
@@ -0,0 +1,14 @@
+/* Check that GOTPCREL isn't used to access glob_a.  */
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-require-effective-target pie_copyreloc } */
+/* { dg-options "-O2 -fpie" } */
+
+extern int glob_a;
+
+int foo ()
+{
+  return glob_a;
+}
+
+/* glob_a should never be accessed with a GOTPCREL.  */
+/* { dg-final { scan-assembler-not "glob_a@GOTPCREL" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pie-copyrelocs-2.c b/gcc/testsuite/gcc.target/i386/pie-copyrelocs-2.c
new file mode 100644 (file)
index 0000000..19cb97e
--- /dev/null
@@ -0,0 +1,14 @@
+/* Check that GOTPCREL isn't used to access glob_a.  */
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-require-effective-target pie_copyreloc } */
+/* { dg-options "-O2 -fpie" } */
+
+int glob_a;
+
+int foo ()
+{
+  return glob_a;
+}
+
+/* glob_a should never be accessed with a GOTPCREL.  */
+/* { dg-final { scan-assembler-not "glob_a@GOTPCREL" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pie-copyrelocs-3.c b/gcc/testsuite/gcc.target/i386/pie-copyrelocs-3.c
new file mode 100644 (file)
index 0000000..c2fa896
--- /dev/null
@@ -0,0 +1,14 @@
+/* Check that PLT is used to access glob_a.  */
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-require-effective-target pie_copyreloc } */
+/* { dg-options "-O2 -fpie" } */
+
+extern int glob_a (void);
+
+int foo ()
+{
+  return glob_a ();
+}
+
+/* glob_a should be accessed with a PLT.  */
+/* { dg-final { scan-assembler "glob_a@PLT" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pie-copyrelocs-4.c b/gcc/testsuite/gcc.target/i386/pie-copyrelocs-4.c
new file mode 100644 (file)
index 0000000..413cdf3
--- /dev/null
@@ -0,0 +1,17 @@
+/* Check that GOTPCREL is used to access glob_a.  */
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-require-effective-target pie_copyreloc } */
+/* { dg-options "-O2 -fpie" } */
+
+extern int glob_a  __attribute__((weak));
+
+int foo ()
+{
+  if (&glob_a != 0)
+    return glob_a;
+  else
+    return 0;
+}
+
+/* weak glob_a should be accessed with a GOTPCREL.  */
+/* { dg-final { scan-assembler "glob_a@GOTPCREL" { target { ! ia32 } } } } */
index e960e12cd8081082905657367130fa3ce400965b..4846724378b70c64916e4010c194d21ae5258882 100644 (file)
@@ -6098,3 +6098,54 @@ proc force_conventional_output_for { test } {
     }
 }
 
+# Return 1 if the x86-64 target supports PIE with copy reloc, 0
+# otherwise.  Cache the result.
+
+proc check_effective_target_pie_copyreloc { } {
+    global pie_copyreloc_available_saved
+    global tool
+    global GCC_UNDER_TEST
+
+    if { !([istarget x86_64-*-*] || [istarget i?86-*-*]) } {
+       return 0
+    }
+
+    # Need auto-host.h to check linker support.
+    if { ![file exists ../../auto-host.h ] } {
+       return 0
+    }
+
+    if [info exists pie_copyreloc_available_saved] {
+       verbose "check_effective_target_pie_copyreloc returning saved $pie_copyreloc_available_saved" 2
+    } else {
+       # Set up and compile to see if linker supports PIE with copy
+       # reloc.  Include the current process ID in the file names to
+       # prevent conflicts with invocations for multiple testsuites.
+
+       set src pie[pid].c
+       set obj pie[pid].o
+
+       set f [open $src "w"]
+       puts $f "#include \"../../auto-host.h\""
+       puts $f "#if HAVE_LD_PIE_COPYRELOC == 0"
+       puts $f "# error Linker does not support PIE with copy reloc."
+       puts $f "#endif"
+       close $f
+
+       verbose "check_effective_target_pie_copyreloc compiling testfile $src" 2
+       set lines [${tool}_target_compile $src $obj object ""]
+
+       file delete $src
+       file delete $obj
+
+       if [string match "" $lines] then {
+           verbose "check_effective_target_pie_copyreloc testfile compilation passed" 2
+           set pie_copyreloc_available_saved 1
+       } else {
+           verbose "check_effective_target_pie_copyreloc testfile compilation failed" 2
+           set pie_copyreloc_available_saved 0
+       }
+    }
+
+    return $pie_copyreloc_available_saved
+}