Patchwork Call tls_get_addr via GOT for GNU TLS if possible

login
register
mail settings
Submitter H.J. Lu
Date June 24, 2016, 3:57 p.m.
Message ID <20160624155729.GA12941@intel.com>
Download mbox | patch
Permalink /patch/13360/
State New
Headers show

Comments

H.J. Lu - June 24, 2016, 3:57 p.m.
There are extensions to x86-64 psABI:

https://groups.google.com/forum/#!topic/x86-64-abi/de5_KnLHxtI

and i386 psABI:

https://groups.google.com/forum/#!topic/ia32-abi/awsRSvJOJfs

to call tls_get_addr via GOT.  X86 assembler and linker in binutils 2.27
implemented

call *__tls_get_addr@GOTPCREL(%rip)

in 64-bit and

call *___tls_get_addr@GOT(%reg)

in 32-bit to access global and local thread loal variables in shared
library. We check if 32-bit x86 assembler and linker work with

call *___tls_get_addr@GOT(%reg)

as 32-bit and 64-bit assembler and linker are enabled togther.

In 32-bit, since any integer register except EAX, which is used to pass
parameter to ___tls_get_addr, and ESP, can be used as GOT base, a new
register class, TLS_REGS, along with a new constraint, Yb, are added.
They are used to improve register allocation for 32-bit dynamic TLS
patterns.

Tested on i686 and x86-64.  OK for trunk?

Thanks.

H.J.
---
gcc/

	* configure.ac (calling ___tls_get_addr via GOT): New
	assembler/linker check.
	(HAVE_AS_IX86_TLS_GET_ADDR_GOT): New.  Defined to 1 if 32-bit
	assembler and linker supports calling ___tls_get_addr via GOT.
	Otherise, defined to 0.
	* config.in: Regenerated.
	* configure: Likewise.
	* config/i386/constraints.md (Yb): New constraint.
	* config/i386/i386.h (reg_class): Add TLS_REGS.
	(REG_CLASS_NAMES): Add TLS_REGS.
	(REG_CLASS_CONTENTS): Likewise.
	* config/i386/i386.md (*tls_global_dynamic_32_gnu): Replace
	the b constraint with the Yb constraint.  Call ___tls_get_addr
	via GOT for GNU TLS with -fno-plt if HAVE_AS_IX86_TLS_GET_ADDR_GOT
	is 1.
	(*tls_local_dynamic_base_32_gnu): Likewise.
	(*tls_global_dynamic_64_<mode>): Call _tls_get_addr via GOT for
	GNU TLS with -fno-plt if HAVE_AS_IX86_TLS_GET_ADDR_GOT is 1.
	(*tls_local_dynamic_base_64_<mode>): Likewise.

gcc/testsuite/

	* gcc.target/i386/noplt-gd-1.c: New test.
	* gcc.target/i386/noplt-gd-2.c: Likewise.
	* gcc.target/i386/noplt-gd-3.c: Likewise.
	* gcc.target/i386/noplt-ld-1.c: Likewise.
	* gcc.target/i386/noplt-ld-2.c: Likewise.
	* gcc.target/i386/noplt-ld-3.c: Likewise.
	* lib/target-supports.exp
	(check_effective_target_tls_get_addr_via_got): New.
---
 gcc/config.in                              |  7 ++++
 gcc/config/i386/constraints.md             | 11 ++++++
 gcc/config/i386/i386.h                     |  3 ++
 gcc/config/i386/i386.md                    | 33 +++++++++++++-----
 gcc/configure                              | 54 ++++++++++++++++++++++++++++++
 gcc/configure.ac                           | 31 +++++++++++++++++
 gcc/testsuite/gcc.target/i386/noplt-gd-1.c | 27 +++++++++++++++
 gcc/testsuite/gcc.target/i386/noplt-gd-2.c | 28 ++++++++++++++++
 gcc/testsuite/gcc.target/i386/noplt-gd-3.c | 12 +++++++
 gcc/testsuite/gcc.target/i386/noplt-ld-1.c | 27 +++++++++++++++
 gcc/testsuite/gcc.target/i386/noplt-ld-2.c | 21 ++++++++++++
 gcc/testsuite/gcc.target/i386/noplt-ld-3.c | 12 +++++++
 gcc/testsuite/lib/target-supports.exp      | 51 ++++++++++++++++++++++++++++
 13 files changed, 308 insertions(+), 9 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/noplt-gd-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/noplt-gd-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/noplt-gd-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/noplt-ld-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/noplt-ld-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/noplt-ld-3.c

Patch

diff --git a/gcc/config.in b/gcc/config.in
index 5f80176..fc3321c 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -485,6 +485,13 @@ 
 #endif
 
 
+/* Define 0/1 if your assembler and linker support calling ___tls_get_addr via
+   GOT. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_AS_IX86_TLS_GET_ADDR_GOT
+#endif
+
+
 /* Define if your assembler supports the 'ud2' mnemonic. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_AS_IX86_UD2
diff --git a/gcc/config/i386/constraints.md b/gcc/config/i386/constraints.md
index a2466a9..8f17bca 100644
--- a/gcc/config/i386/constraints.md
+++ b/gcc/config/i386/constraints.md
@@ -104,6 +104,10 @@ 
 ;;  m	MMX inter-unit moves to MMX register enabled
 ;;  n	MMX inter-unit moves from MMX register enabled
 ;;  a	Integer register when zero extensions with AND are disabled
+;;  b	Any register that can be used as the GOT base when calling
+;;	___tls_get_addr: that is, any general register except EAX
+;;	and ESP, for -fno-plt if linker supports it.  Otherwise,
+;;	EBX.
 ;;  p	Integer register when TARGET_PARTIAL_REG_STALL is disabled
 ;;  f	x87 register when 80387 floating point arithmetic is enabled
 ;;  r	SSE regs not requiring REX prefix when prefixes avoidance is enabled
@@ -137,6 +141,13 @@ 
   ? NO_REGS : GENERAL_REGS"
  "@internal Any integer register when zero extensions with AND are disabled.")
 
+(define_register_constraint "Yb"
+ "(!flag_plt && HAVE_AS_IX86_TLS_GET_ADDR_GOT) ? TLS_REGS : BREG"
+ "@internal Any register that can be used as the GOT base when calling
+  ___tls_get_addr: that is, any general register except @code{a} and
+  @code{sp} registers, for -fno-plt if linker supports it.  Otherwise,
+  @code{b} register.")
+
 (define_register_constraint "Yf"
  "(ix86_fpmath & FPMATH_387) ? FLOAT_REGS : NO_REGS"
  "@internal Any x87 register when 80387 FP arithmetic is enabled.")
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index cab0a5d..acd6f90 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1347,6 +1347,7 @@  enum reg_class
   CLOBBERED_REGS,		/* call-clobbered integer registers */
   Q_REGS,			/* %eax %ebx %ecx %edx */
   NON_Q_REGS,			/* %esi %edi %ebp %esp */
+  TLS_REGS,			/* %ebx %ecx %edx %esi %edi %ebp */
   INDEX_REGS,			/* %eax %ebx %ecx %edx %esi %edi %ebp */
   LEGACY_REGS,			/* %eax %ebx %ecx %edx %esi %edi %ebp %esp */
   GENERAL_REGS,			/* %eax %ebx %ecx %edx %esi %edi %ebp %esp
@@ -1407,6 +1408,7 @@  enum reg_class
    "AD_REGS",				\
    "CLOBBERED_REGS",			\
    "Q_REGS", "NON_Q_REGS",		\
+   "TLS_REGS",				\
    "INDEX_REGS",			\
    "LEGACY_REGS",			\
    "GENERAL_REGS",			\
@@ -1447,6 +1449,7 @@  enum reg_class
       { 0x07,       0x0,    0x0 },       /* CLOBBERED_REGS */            \
       { 0x0f,       0x0,    0x0 },       /* Q_REGS */                    \
   { 0x1100f0,    0x1fe0,    0x0 },       /* NON_Q_REGS */                \
+      { 0x7e,    0x1fe0,    0x0 },       /* TLS_REGS */			 \
       { 0x7f,    0x1fe0,    0x0 },       /* INDEX_REGS */                \
   { 0x1100ff,       0x0,    0x0 },       /* LEGACY_REGS */               \
   { 0x1100ff,    0x1fe0,    0x0 },       /* GENERAL_REGS */              \
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 85dda3f..bb70102 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -13565,7 +13565,7 @@ 
 (define_insn "*tls_global_dynamic_32_gnu"
   [(set (match_operand:SI 0 "register_operand" "=a")
 	(unspec:SI
-	 [(match_operand:SI 1 "register_operand" "b")
+	 [(match_operand:SI 1 "register_operand" "Yb")
 	  (match_operand 2 "tls_symbolic_operand")
 	  (match_operand 3 "constant_call_address_operand" "Bz")
 	  (reg:SI SP_REG)]
@@ -13575,15 +13575,21 @@ 
    (clobber (reg:CC FLAGS_REG))]
   "!TARGET_64BIT && TARGET_GNU_TLS"
 {
-  output_asm_insn
-    ("lea{l}\t{%E2@tlsgd(,%1,1), %0|%0, %E2@tlsgd[%1*1]}", operands);
+  if (TARGET_SUN_TLS || flag_plt || !HAVE_AS_IX86_TLS_GET_ADDR_GOT)
+    output_asm_insn
+      ("lea{l}\t{%E2@tlsgd(,%1,1), %0|%0, %E2@tlsgd[%1*1]}", operands);
+  else
+    output_asm_insn
+      ("lea{l}\t{%E2@tlsgd(%1), %0|%0, %E2@tlsgd[%1]}", operands);
   if (TARGET_SUN_TLS)
 #ifdef HAVE_AS_IX86_TLSGDPLT
     return "call\t%a2@tlsgdplt";
 #else
     return "call\t%p3@plt";
 #endif
-  return "call\t%P3";
+  if (flag_plt || !HAVE_AS_IX86_TLS_GET_ADDR_GOT)
+    return "call\t%P3";
+  return "call\t{*%p3@GOT(%1)|[DWORD PTR %p3@GOT[%1]]}";
 }
   [(set_attr "type" "multi")
    (set_attr "length" "12")])
@@ -13616,11 +13622,16 @@ 
     fputs (ASM_BYTE "0x66\n", asm_out_file);
   output_asm_insn
     ("lea{q}\t{%E1@tlsgd(%%rip), %%rdi|rdi, %E1@tlsgd[rip]}", operands);
-  fputs (ASM_SHORT "0x6666\n", asm_out_file);
+  if (TARGET_SUN_TLS || flag_plt || !HAVE_AS_IX86_TLS_GET_ADDR_GOT)
+    fputs (ASM_SHORT "0x6666\n", asm_out_file);
+  else
+    fputs (ASM_BYTE "0x66\n", asm_out_file);
   fputs ("\trex64\n", asm_out_file);
   if (TARGET_SUN_TLS)
     return "call\t%p2@plt";
-  return "call\t%P2";
+  if (flag_plt || !HAVE_AS_IX86_TLS_GET_ADDR_GOT)
+    return "call\t%P2";
+  return "call\t{*%p2@GOTPCREL(%%rip)|[QWORD PTR %p2@GOTPCREL[rip]]}";
 }
   [(set_attr "type" "multi")
    (set (attr "length")
@@ -13664,7 +13675,7 @@ 
 (define_insn "*tls_local_dynamic_base_32_gnu"
   [(set (match_operand:SI 0 "register_operand" "=a")
 	(unspec:SI
-	 [(match_operand:SI 1 "register_operand" "b")
+	 [(match_operand:SI 1 "register_operand" "Yb")
 	  (match_operand 2 "constant_call_address_operand" "Bz")
 	  (reg:SI SP_REG)]
 	 UNSPEC_TLS_LD_BASE))
@@ -13682,7 +13693,9 @@ 
       else
 	return "call\t%p2@plt";
     }
-  return "call\t%P2";
+  if (flag_plt || !HAVE_AS_IX86_TLS_GET_ADDR_GOT)
+    return "call\t%P2";
+  return "call\t{*%p2@GOT(%1)|[DWORD PTR %p2@GOT[%1]]}";
 }
   [(set_attr "type" "multi")
    (set_attr "length" "11")])
@@ -13713,7 +13726,9 @@ 
     ("lea{q}\t{%&@tlsld(%%rip), %%rdi|rdi, %&@tlsld[rip]}", operands);
   if (TARGET_SUN_TLS)
     return "call\t%p1@plt";
-  return "call\t%P1";
+  if (flag_plt || !HAVE_AS_IX86_TLS_GET_ADDR_GOT)
+    return "call\t%P1";
+  return "call\t{*%p1@GOTPCREL(%%rip)|[QWORD PTR %p1@GOTPCREL[rip]]}";
 }
   [(set_attr "type" "multi")
    (set_attr "length" "12")])
diff --git a/gcc/configure b/gcc/configure
index 94ba9bd..09706b9 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -26010,6 +26010,60 @@  cat >>confdefs.h <<_ACEOF
 #define HAVE_AS_IX86_GOT32X `if test x"$gcc_cv_as_ix86_got32x" = xyes; then echo 1; else echo 0; fi`
 _ACEOF
 
+
+    conftest_s='
+	.section .text,"ax",@progbits
+	.globl  _start
+	.type   _start, @function
+_start:
+	leal	ld@tlsldm(%ecx), %eax
+	call	*___tls_get_addr@GOT(%ecx)
+	leal	gd@tlsgd(%ecx), %eax
+	call	*___tls_get_addr@GOT(%ecx)
+
+	.section .tdata,"aw'$tls_section_flag'",@progbits
+	.type	ld, @object
+ld:
+	.byte 0
+	.globl  gd
+	.type	gd, @object
+gd:
+	.byte 0'
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for calling ___tls_get_addr via GOT" >&5
+$as_echo_n "checking assembler for calling ___tls_get_addr via GOT... " >&6; }
+if test "${gcc_cv_as_ix86_tls_get_addr_via_got+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  gcc_cv_as_ix86_tls_get_addr_via_got=no
+  if test x$gcc_cv_as != x; then
+    $as_echo "$conftest_s" > conftest.s
+    if { ac_try='$gcc_cv_as $gcc_cv_as_flags $as_ix86_gas_32_opt -o conftest.o conftest.s >&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+    then
+	if test x$gcc_cv_ld != x \
+	    && $gcc_cv_ld $ld_ix86_gld_32_opt -o conftest conftest.o > /dev/null 2>&1; then
+	   gcc_cv_as_ix86_tls_get_addr_via_got=yes
+	 fi
+	 rm -f conftest
+    else
+      echo "configure: failed program was" >&5
+      cat conftest.s >&5
+    fi
+    rm -f conftest.o conftest.s
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_ix86_tls_get_addr_via_got" >&5
+$as_echo "$gcc_cv_as_ix86_tls_get_addr_via_got" >&6; }
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_AS_IX86_TLS_GET_ADDR_GOT `if test x"$gcc_cv_as_ix86_tls_get_addr_via_got" = xyes; then echo 1; else echo 0; fi`
+_ACEOF
+
     ;;
 
   ia64*-*-*)
diff --git a/gcc/configure.ac b/gcc/configure.ac
index b0f38d1..99e8b0c 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -4229,6 +4229,37 @@  _start:
     AC_DEFINE_UNQUOTED(HAVE_AS_IX86_GOT32X,
       [`if test x"$gcc_cv_as_ix86_got32x" = xyes; then echo 1; else echo 0; fi`],
       [Define 0/1 if your assembler and linker support @GOT.])
+
+    conftest_s='
+	.section .text,"ax",@progbits
+	.globl  _start
+	.type   _start, @function
+_start:
+	leal	ld@tlsldm(%ecx), %eax
+	call	*___tls_get_addr@GOT(%ecx)
+	leal	gd@tlsgd(%ecx), %eax
+	call	*___tls_get_addr@GOT(%ecx)
+
+	.section .tdata,"aw'$tls_section_flag'",@progbits
+	.type	ld, @object
+ld:
+	.byte 0
+	.globl  gd
+	.type	gd, @object
+gd:
+	.byte 0'
+    gcc_GAS_CHECK_FEATURE([calling ___tls_get_addr via GOT],
+        gcc_cv_as_ix86_tls_get_addr_via_got,,
+	[$as_ix86_gas_32_opt],
+	[$conftest_s],
+	[if test x$gcc_cv_ld != x \
+	    && $gcc_cv_ld $ld_ix86_gld_32_opt -o conftest conftest.o > /dev/null 2>&1; then
+	   gcc_cv_as_ix86_tls_get_addr_via_got=yes
+	 fi
+	 rm -f conftest])
+    AC_DEFINE_UNQUOTED(HAVE_AS_IX86_TLS_GET_ADDR_GOT,
+      [`if test x"$gcc_cv_as_ix86_tls_get_addr_via_got" = xyes; then echo 1; else echo 0; fi`],
+      [Define 0/1 if your assembler and linker support calling ___tls_get_addr via GOT.])
     ;;
 
   ia64*-*-*)
diff --git a/gcc/testsuite/gcc.target/i386/noplt-gd-1.c b/gcc/testsuite/gcc.target/i386/noplt-gd-1.c
new file mode 100644
index 0000000..5d5b531
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-gd-1.c
@@ -0,0 +1,27 @@ 
+/* { dg-do compile { target { *-*-linux* && tls_get_addr_via_got } } } */
+/* { dg-options "-fpic -fno-plt" } */
+
+extern __thread int gd;
+
+int *
+get_gd (void)
+{
+  return &gd;
+}
+
+void
+set_gd (int i)
+{
+  gd = i;
+}
+
+int
+test_gd (int i)
+{
+  return gd == i;
+}
+
+/* { dg-final { scan-assembler "call\[ \t\]\\*__tls_get_addr@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]\\*___tls_get_addr@GOT\\(" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]__tls_get_addr@PLT" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]___tls_get_addr@PLT" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-gd-2.c b/gcc/testsuite/gcc.target/i386/noplt-gd-2.c
new file mode 100644
index 0000000..9a6c275
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-gd-2.c
@@ -0,0 +1,28 @@ 
+/* { dg-do run { target { *-*-linux* } } } */
+/* { dg-options "-O2 -fpic -fno-plt" } */
+/* { dg-additional-sources noplt-gd-1.c } */
+
+__thread int gd = 1;
+extern void abort (void);
+extern int * get_gd (void);
+extern void set_gd (int);
+extern int test_gd (int);
+
+int
+main ()
+{
+  int *p;
+ 
+  if (gd != 1)
+    abort ();
+
+  p = get_gd ();
+  if (*p != gd)
+    abort ();
+
+  set_gd (4);
+  if (*p != 4 || !test_gd (4))
+    abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/noplt-gd-3.c b/gcc/testsuite/gcc.target/i386/noplt-gd-3.c
new file mode 100644
index 0000000..6b0e780
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-gd-3.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile { target { *-*-linux* && { ia32 && tls_get_addr_via_got } } } } */
+/* { dg-options "-fpic -fno-plt" } */
+
+extern __thread int gd;
+
+int *
+get_gd (void)
+{
+  return &gd;
+}
+
+/* { dg-final { scan-assembler-not "call\[ \t\]\\*___tls_get_addr@GOT\\(%ebx\\)" } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-ld-1.c b/gcc/testsuite/gcc.target/i386/noplt-ld-1.c
new file mode 100644
index 0000000..8769830
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-ld-1.c
@@ -0,0 +1,27 @@ 
+/* { dg-do compile { target { *-*-linux* && tls_get_addr_via_got } } } */
+/* { dg-options "-fpic -fno-plt" } */
+
+static __thread int ld;
+
+int *
+get_ld (void)
+{
+  return &ld;
+}
+
+void
+set_ld (int i)
+{
+  ld = i;
+}
+
+int
+test_ld (int i)
+{
+  return ld == i;
+}
+
+/* { dg-final { scan-assembler "call\[ \t\]\\*__tls_get_addr@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]\\*___tls_get_addr@GOT\\(" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]__tls_get_addr@PLT" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "call\[ \t\]___tls_get_addr@PLT" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/noplt-ld-2.c b/gcc/testsuite/gcc.target/i386/noplt-ld-2.c
new file mode 100644
index 0000000..86cc93e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-ld-2.c
@@ -0,0 +1,21 @@ 
+/* { dg-do run { target { *-*-linux* } } } */
+/* { dg-options "-O2 -fpic -fno-plt" } */
+/* { dg-additional-sources noplt-ld-1.c } */
+
+extern void abort (void);
+extern int * get_ld (void);
+extern void set_ld (int);
+extern int test_ld (int);
+
+int
+main ()
+{
+  int *p;
+ 
+  p = get_ld ();
+  set_ld (4);
+  if (*p != 4 || !test_ld (4))
+    abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/noplt-ld-3.c b/gcc/testsuite/gcc.target/i386/noplt-ld-3.c
new file mode 100644
index 0000000..b014d53
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/noplt-ld-3.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile { target { *-*-linux* && { ia32 && tls_get_addr_via_got } } } } */
+/* { dg-options "-fpic -fno-plt" } */
+
+static __thread int ld;
+
+int *
+get_ld (void)
+{
+  return &ld;
+}
+
+/* { dg-final { scan-assembler-not "call\[ \t\]\\*___tls_get_addr@GOT\\(%ebx\\)" } } */
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index 2b80f6e..de6c072 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -7111,6 +7111,57 @@  proc check_effective_target_got32x_reloc { } {
     return $got32x_reloc_available_saved
 }
 
+# Return 1 if the x86 target supports calling ___tls_get_addr via GOT,
+# 0 otherwise.  Cache the result.
+
+proc check_effective_target_tls_get_addr_via_got { } {
+    global tls_get_addr_via_got_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 tls_get_addr_via_got_available_saved] {
+	verbose "check_effective_target_tls_get_addr_via_got returning saved $tls_get_addr_via_got_available_saved" 2
+    } else {
+	# Include the current process ID in the file names to prevent
+	# conflicts with invocations for multiple testsuites.
+
+	set src tls_get_addr_via_got[pid].c
+	set obj tls_get_addr_via_got[pid].o
+
+	set f [open $src "w"]
+	puts $f "#include \"../../auto-host.h\""
+	puts $f "#if HAVE_AS_IX86_TLS_GET_ADDR_GOT == 0"
+	puts $f "# error Assembler/linker do not support calling ___tls_get_addr via GOT."
+	puts $f "#endif"
+	close $f
+
+	verbose "check_effective_target_tls_get_addr_via_got 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_tls_get_addr_via_got testfile compilation passed" 2
+	    set tls_get_addr_via_got_available_saved 1
+	} else {
+	    verbose "check_effective_target_tls_get_addr_via_got testfile compilation failed" 2
+	    set tls_get_addr_via_got_available_saved 0
+	}
+    }
+
+    return $tls_get_addr_via_got_available_saved
+}
+
 # Return 1 if the target uses comdat groups.
 
 proc check_effective_target_comdat_group {} {