New files for MIPS TLS support.
authorAndreas Jaeger <aj@suse.de>
Mon, 28 Mar 2005 09:14:59 +0000 (09:14 +0000)
committerAndreas Jaeger <aj@suse.de>
Mon, 28 Mar 2005 09:14:59 +0000 (09:14 +0000)
sysdeps/mips/bits/atomic.h [new file with mode: 0644]
sysdeps/mips/dl-tls.h [new file with mode: 0644]
sysdeps/mips/elf/configure [new file with mode: 0644]
sysdeps/mips/elf/configure.in [new file with mode: 0644]
sysdeps/mips/libc-tls.c [new file with mode: 0644]
sysdeps/mips/tls-macros.h [new file with mode: 0644]

diff --git a/sysdeps/mips/bits/atomic.h b/sysdeps/mips/bits/atomic.h
new file mode 100644 (file)
index 0000000..167d9a5
--- /dev/null
@@ -0,0 +1,303 @@
+/* Low-level functions for atomic operations. Mips version.
+   Copyright (C) 2005 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _MIPS_BITS_ATOMIC_H
+#define _MIPS_BITS_ATOMIC_H 1
+
+#include <inttypes.h>
+#include <sgidefs.h>
+
+typedef int32_t atomic32_t;
+typedef uint32_t uatomic32_t;
+typedef int_fast32_t atomic_fast32_t;
+typedef uint_fast32_t uatomic_fast32_t;
+
+typedef int64_t atomic64_t;
+typedef uint64_t uatomic64_t;
+typedef int_fast64_t atomic_fast64_t;
+typedef uint_fast64_t uatomic_fast64_t;
+
+typedef intptr_t atomicptr_t;
+typedef uintptr_t uatomicptr_t;
+typedef intmax_t atomic_max_t;
+typedef uintmax_t uatomic_max_t;
+
+#if _MIPS_SIM == _ABIO32
+#define MIPS_PUSH_MIPS2 ".set  mips2\n\t"
+#else
+#define MIPS_PUSH_MIPS2
+#endif
+
+/* See the comments in <sys/asm.h> about the use of the sync instruction.  */
+#ifndef MIPS_SYNC
+# define MIPS_SYNC     sync
+#endif
+
+#define MIPS_SYNC_STR_2(X) #X
+#define MIPS_SYNC_STR_1(X) MIPS_SYNC_STR_2(X)
+#define MIPS_SYNC_STR MIPS_SYNC_STR_1(MIPS_SYNC)
+
+/* Compare and exchange.  For all of the "xxx" routines, we expect a
+   "__prev" and a "__cmp" variable to be provided by the enclosing scope,
+   in which values are returned.  */
+
+#define __arch_compare_and_exchange_xxx_8_int(mem, newval, oldval, rel, acq) \
+  (abort (), __prev = __cmp = 0)
+
+#define __arch_compare_and_exchange_xxx_16_int(mem, newval, oldval, rel, acq) \
+  (abort (), __prev = __cmp = 0)
+
+#define __arch_compare_and_exchange_xxx_32_int(mem, newval, oldval, rel, acq) \
+     __asm__ __volatile__ (                                                  \
+     ".set     push\n\t"                                                     \
+     MIPS_PUSH_MIPS2                                                         \
+     rel       "\n"                                                          \
+     "1:\t"                                                                  \
+     "ll       %0,%4\n\t"                                                    \
+     "move     %1,$0\n\t"                                                    \
+     "bne      %0,%2,2f\n\t"                                                 \
+     "move     %1,%3\n\t"                                                    \
+     "sc       %1,%4\n\t"                                                    \
+     "beqz     %1,1b\n"                                                      \
+     acq       "\n\t"                                                        \
+     ".set     pop\n"                                                        \
+     "2:\n\t"                                                                \
+             : "=&r" (__prev), "=&r" (__cmp)                                 \
+             : "r" (oldval), "r" (newval), "m" (*mem)                        \
+             : "memory")
+
+#if _MIPS_SIM == _ABIO32
+/* We can't do an atomic 64-bit operation in O32.  */
+#define __arch_compare_and_exchange_xxx_64_int(mem, newval, oldval, rel, acq) \
+  (abort (), __prev = __cmp = 0)
+#else
+#define __arch_compare_and_exchange_xxx_64_int(mem, newval, oldval, rel, acq) \
+     __asm__ __volatile__ ("\n"                                                      \
+     ".set     push\n\t"                                                     \
+     MIPS_PUSH_MIPS2                                                         \
+     rel       "\n"                                                          \
+     "1:\t"                                                                  \
+     "lld      %0,%4\n\t"                                                    \
+     "move     %1,$0\n\t"                                                    \
+     "bne      %0,%2,2f\n\t"                                                 \
+     "move     %1,%3\n\t"                                                    \
+     "scd      %1,%4\n\t"                                                    \
+     "beqz     %1,1b\n"                                                      \
+     acq       "\n\t"                                                        \
+     ".set     pop\n"                                                        \
+     "2:\n\t"                                                                \
+             : "=&r" (__prev), "=&r" (__cmp)                                 \
+             : "r" (oldval), "r" (newval), "m" (*mem)                        \
+             : "memory")
+#endif
+
+/* For all "bool" routines, we return FALSE if exchange succesful.  */
+
+#define __arch_compare_and_exchange_bool_8_int(mem, new, old, rel, acq)        \
+({ typeof (*mem) __prev; int __cmp;                                    \
+   __arch_compare_and_exchange_xxx_8_int(mem, new, old, rel, acq);     \
+   !__cmp; })
+
+#define __arch_compare_and_exchange_bool_16_int(mem, new, old, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;                                    \
+   __arch_compare_and_exchange_xxx_16_int(mem, new, old, rel, acq);    \
+   !__cmp; })
+
+#define __arch_compare_and_exchange_bool_32_int(mem, new, old, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;                                    \
+   __arch_compare_and_exchange_xxx_32_int(mem, new, old, rel, acq);    \
+   !__cmp; })
+
+#define __arch_compare_and_exchange_bool_64_int(mem, new, old, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;                                    \
+   __arch_compare_and_exchange_xxx_64_int(mem, new, old, rel, acq);    \
+   !__cmp; })
+
+/* For all "val" routines, return the old value whether exchange
+   successful or not.  */
+
+#define __arch_compare_and_exchange_val_8_int(mem, new, old, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;                                    \
+   __arch_compare_and_exchange_xxx_8_int(mem, new, old, rel, acq);     \
+   (typeof (*mem))__prev; })
+
+#define __arch_compare_and_exchange_val_16_int(mem, new, old, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;                                    \
+   __arch_compare_and_exchange_xxx_16_int(mem, new, old, rel, acq);    \
+   (typeof (*mem))__prev; })
+
+#define __arch_compare_and_exchange_val_32_int(mem, new, old, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;                                    \
+   __arch_compare_and_exchange_xxx_32_int(mem, new, old, rel, acq);    \
+   (typeof (*mem))__prev; })
+
+#define __arch_compare_and_exchange_val_64_int(mem, new, old, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;                                    \
+   __arch_compare_and_exchange_xxx_64_int(mem, new, old, rel, acq);    \
+   (typeof (*mem))__prev; })
+
+/* Compare and exchange with "acquire" semantics, ie barrier after.  */
+
+#define atomic_compare_and_exchange_bool_acq(mem, new, old)    \
+  __atomic_bool_bysize (__arch_compare_and_exchange_bool, int, \
+                       mem, new, old, "", MIPS_SYNC_STR)
+
+#define atomic_compare_and_exchange_val_acq(mem, new, old)     \
+  __atomic_val_bysize (__arch_compare_and_exchange_val, int,   \
+                      mem, new, old, "", MIPS_SYNC_STR)
+
+/* Compare and exchange with "release" semantics, ie barrier before.  */
+
+#define atomic_compare_and_exchange_bool_rel(mem, new, old)    \
+  __atomic_bool_bysize (__arch_compare_and_exchange_bool, int, \
+                       mem, new, old, MIPS_SYNC_STR, "")
+
+#define atomic_compare_and_exchange_val_rel(mem, new, old)     \
+  __atomic_val_bysize (__arch_compare_and_exchange_val, int,   \
+                      mem, new, old, MIPS_SYNC_STR, "")
+
+
+
+/* Atomic exchange (without compare).  */
+
+#define __arch_exchange_xxx_8_int(mem, newval, rel, acq) \
+  (abort (), 0)
+
+#define __arch_exchange_xxx_16_int(mem, newval, rel, acq) \
+  (abort (), 0)
+
+#define __arch_exchange_xxx_32_int(mem, newval, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;                                          \
+     __asm__ __volatile__ ("\n"                                                      \
+     ".set     push\n\t"                                                     \
+     MIPS_PUSH_MIPS2                                                         \
+     rel       "\n"                                                          \
+     "1:\t"                                                                  \
+     "ll       %0,%3\n\t"                                                    \
+     "move     %1,%2\n\t"                                                    \
+     "sc       %1,%3\n\t"                                                    \
+     "beqz     %1,1b\n"                                                      \
+     acq       "\n\t"                                                        \
+     ".set     pop\n"                                                        \
+     "2:\n\t"                                                                \
+             : "=&r" (__prev), "=&r" (__cmp)                                 \
+             : "r" (newval), "m" (*mem)                                      \
+             : "memory");                                                    \
+  __prev; })
+
+#if _MIPS_SIM == _ABIO32
+/* We can't do an atomic 64-bit operation in O32.  */
+#define __arch_exchange_xxx_64_int(mem, newval, rel, acq) \
+  (abort (), 0)
+#else
+#define __arch_exchange_xxx_64_int(mem, newval, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;                                          \
+     __asm__ __volatile__ ("\n"                                                      \
+     ".set     push\n\t"                                                     \
+     MIPS_PUSH_MIPS2                                                         \
+     rel       "\n"                                                          \
+     "1:\n"                                                                  \
+     "lld      %0,%3\n\t"                                                    \
+     "move     %1,%2\n\t"                                                    \
+     "scd      %1,%3\n\t"                                                    \
+     "beqz     %1,1b\n"                                                      \
+     acq       "\n\t"                                                        \
+     ".set     pop\n"                                                        \
+     "2:\n\t"                                                                \
+             : "=&r" (__prev), "=&r" (__cmp)                                 \
+             : "r" (newval), "m" (*mem)                                      \
+             : "memory");                                                    \
+  __prev; })
+#endif
+
+#define atomic_exchange_acq(mem, value) \
+  __atomic_val_bysize (__arch_exchange_xxx, int, mem, value, "", MIPS_SYNC_STR)
+
+#define atomic_exchange_rel(mem, value) \
+  __atomic_val_bysize (__arch_exchange_xxx, int, mem, value, MIPS_SYNC_STR, "")
+
+
+/* Atomically add value and return the previous (unincremented) value.  */
+
+#define __arch_exchange_and_add_8_int(mem, newval, rel, acq) \
+  (abort (), (typeof(*mem)) 0)
+
+#define __arch_exchange_and_add_16_int(mem, newval, rel, acq) \
+  (abort (), (typeof(*mem)) 0)
+
+#define __arch_exchange_and_add_32_int(mem, value, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;                                          \
+     __asm__ __volatile__ ("\n"                                                      \
+     ".set     push\n\t"                                                     \
+     MIPS_PUSH_MIPS2                                                         \
+     rel       "\n"                                                          \
+     "1:\t"                                                                  \
+     "ll       %0,%3\n\t"                                                    \
+     "addu     %1,%0,%2\n\t"                                                 \
+     "sc       %1,%3\n\t"                                                    \
+     "beqz     %1,1b\n"                                                      \
+     acq       "\n\t"                                                        \
+     ".set     pop\n"                                                        \
+     "2:\n\t"                                                                \
+             : "=&r" (__prev), "=&r" (__cmp)                                 \
+             : "r" (value), "m" (*mem)                                       \
+             : "memory");                                                    \
+  __prev; })
+
+#if _MIPS_SIM == _ABIO32
+/* We can't do an atomic 64-bit operation in O32.  */
+#define __arch_exchange_and_add_64_int(mem, value, rel, acq) \
+  (abort (), (typeof(*mem)) 0)
+#else
+#define __arch_exchange_and_add_64_int(mem, value, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;                                          \
+     __asm__ __volatile__ (                                                  \
+     ".set     push\n\t"                                                     \
+     MIPS_PUSH_MIPS2                                                         \
+     rel       "\n"                                                          \
+     "1:\t"                                                                  \
+     "lld      %0,%3\n\t"                                                    \
+     "daddu    %1,%0,%2\n\t"                                                 \
+     "scd      %1,%3\n\t"                                                    \
+     "beqz     %1,1b\n"                                                      \
+     acq       "\n\t"                                                        \
+     ".set     pop\n"                                                        \
+     "2:\n\t"                                                                \
+             : "=&r" (__prev), "=&r" (__cmp)                                 \
+             : "r" (value), "m" (*mem)                                       \
+             : "memory");                                                    \
+  __prev; })
+#endif
+
+/* ??? Barrier semantics for atomic_exchange_and_add appear to be 
+   undefined.  Use full barrier for now, as that's safe.  */
+#define atomic_exchange_and_add(mem, value) \
+  __atomic_val_bysize (__arch_exchange_and_add, int, mem, value,             \
+                      MIPS_SYNC_STR, MIPS_SYNC_STR)
+
+/* TODO: More atomic operations could be implemented efficiently; only the
+   basic requirements are done.  */
+
+#define atomic_full_barrier() \
+  __asm__ __volatile__ (".set push\n\t"                                              \
+                       MIPS_PUSH_MIPS2                                       \
+                       MIPS_SYNC_STR "\n\t"                                  \
+                       ".set pop" : : : "memory")
+
+#endif /* bits/atomic.h */
diff --git a/sysdeps/mips/dl-tls.h b/sysdeps/mips/dl-tls.h
new file mode 100644 (file)
index 0000000..6d3ed6f
--- /dev/null
@@ -0,0 +1,46 @@
+/* Thread-local storage handling in the ELF dynamic linker.  MIPS version.
+   Copyright (C) 2005 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+
+/* Type used for the representation of TLS information in the GOT.  */
+typedef struct
+{
+  unsigned long int ti_module;
+  unsigned long int ti_offset;
+} tls_index;
+
+/* The thread pointer points 0x7000 past the first static TLS block.  */
+#define TLS_TP_OFFSET          0x7000
+
+/* Dynamic thread vector pointers point 0x8000 past the start of each
+   TLS block.  */
+#define TLS_DTV_OFFSET         0x8000
+
+/* Compute the value for a GOTTPREL reloc.  */
+#define TLS_TPREL_VALUE(sym_map, sym) \
+  ((sym_map)->l_tls_offset + (sym)->st_value - TLS_TP_OFFSET)
+
+/* Compute the value for a DTPREL reloc.  */
+#define TLS_DTPREL_VALUE(sym) \
+  ((sym)->st_value - TLS_DTV_OFFSET)
+
+extern void *__tls_get_addr (tls_index *ti);
+
+# define GET_ADDR_OFFSET       (ti->ti_offset + TLS_DTV_OFFSET)
+# define __TLS_GET_ADDR(__ti)  (__tls_get_addr (__ti) - TLS_DTV_OFFSET)
diff --git a/sysdeps/mips/elf/configure b/sysdeps/mips/elf/configure
new file mode 100644 (file)
index 0000000..3d90a1e
--- /dev/null
@@ -0,0 +1,46 @@
+# This file is generated from configure.in by Autoconf.  DO NOT EDIT!
+ # Local configure fragment for sysdeps/mips/elf.
+
+if test "$usetls" != no; then
+# Check for support of thread-local storage handling in assembler and
+# linker.
+echo "$as_me:$LINENO: checking for MIPS TLS support" >&5
+echo $ECHO_N "checking for MIPS TLS support... $ECHO_C" >&6
+if test "${libc_cv_mips_tls+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat > conftest.s <<\EOF
+       .section ".tdata", "awT", %progbits
+       .globl foo
+foo:   .long   1
+       .section ".tbss", "awT", %nobits
+       .globl bar
+bar:   .skip   4
+       .text
+
+       lw      $25, %call16(__tls_get_addr)($28)
+       jalr    $25
+       addiu   $4, $28, %tlsgd(x)
+EOF
+if { ac_try='${CC-cc} -c $CFLAGS conftest.s 1>&5'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  libc_cv_mips_tls=yes
+else
+  libc_cv_mips_tls=no
+fi
+rm -f conftest*
+fi
+echo "$as_me:$LINENO: result: $libc_cv_mips_tls" >&5
+echo "${ECHO_T}$libc_cv_mips_tls" >&6
+if test $libc_cv_mips_tls = yes; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_TLS_SUPPORT 1
+_ACEOF
+
+fi
+fi
+
diff --git a/sysdeps/mips/elf/configure.in b/sysdeps/mips/elf/configure.in
new file mode 100644 (file)
index 0000000..ecb9108
--- /dev/null
@@ -0,0 +1,35 @@
+GLIBC_PROVIDES dnl See aclocal.m4 in the top level source directory.
+# Local configure fragment for sysdeps/mips/elf.
+
+if test "$usetls" != no; then
+# Check for support of thread-local storage handling in assembler and
+# linker.
+AC_CACHE_CHECK(for MIPS TLS support, libc_cv_mips_tls, [dnl
+cat > conftest.s <<\EOF
+       .section ".tdata", "awT", %progbits
+       .globl foo
+foo:   .long   1
+       .section ".tbss", "awT", %nobits
+       .globl bar
+bar:   .skip   4
+       .text
+
+       lw      $25, %call16(__tls_get_addr)($28)
+       jalr    $25
+       addiu   $4, $28, %tlsgd(x) 
+EOF
+dnl
+if AC_TRY_COMMAND(${CC-cc} -c $CFLAGS conftest.s 1>&AS_MESSAGE_LOG_FD); then
+  libc_cv_mips_tls=yes
+else
+  libc_cv_mips_tls=no
+fi
+rm -f conftest*])
+if test $libc_cv_mips_tls = yes; then
+  AC_DEFINE(HAVE_TLS_SUPPORT)
+fi
+fi
+
+dnl No MIPS GCC supports accessing static and hidden symbols in an
+dnl position independent way.
+dnl AC_DEFINE(PI_STATIC_AND_HIDDEN)
diff --git a/sysdeps/mips/libc-tls.c b/sysdeps/mips/libc-tls.c
new file mode 100644 (file)
index 0000000..157ba33
--- /dev/null
@@ -0,0 +1,37 @@
+/* Thread-local storage handling in the ELF dynamic linker.  MIPS version.
+   Copyright (C) 2005 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <sysdeps/generic/libc-tls.c>
+#include <dl-tls.h>
+
+#if USE_TLS
+
+/* On MIPS, linker optimizations are not required, so __tls_get_addr
+   can be called even in statically linked binaries.  In this case module
+   must be always 1 and PT_TLS segment exist in the binary, otherwise it
+   would not link.  */
+
+void *
+__tls_get_addr (tls_index *ti)
+{
+  dtv_t *dtv = THREAD_DTV ();
+  return (char *) dtv[1].pointer.val + GET_ADDR_OFFSET;
+}
+
+#endif
diff --git a/sysdeps/mips/tls-macros.h b/sysdeps/mips/tls-macros.h
new file mode 100644 (file)
index 0000000..2d0516b
--- /dev/null
@@ -0,0 +1,88 @@
+/* Macros to support TLS testing in times of missing compiler support.  */
+
+#if _MIPS_SIM != _ABI64
+
+/* These versions are for o32 and n32.  */
+
+# define TLS_GD(x)                                     \
+  ({ void *__result;                                   \
+     extern void *__tls_get_addr (void *);             \
+     asm ("addiu %0, $28, %%tlsgd(" #x ")"             \
+         : "=r" (__result));                           \
+     (int *)__tls_get_addr (__result); })
+#else
+# define TLS_GD(x)                                     \
+  ({ void *__result;                                   \
+     extern void *__tls_get_addr (void *);             \
+     asm ("daddiu %0, $28, %%tlsgd(" #x ")"            \
+         : "=r" (__result));                           \
+     (int *)__tls_get_addr (__result); })
+#endif
+
+#if _MIPS_SIM != _ABI64
+# define TLS_LD(x)                                     \
+  ({ void *__result;                                   \
+     extern void *__tls_get_addr (void *);             \
+     asm ("addiu %0, $28, %%tlsldm(" #x ")"            \
+         : "=r" (__result));                           \
+     __result = __tls_get_addr (__result);             \
+     asm ("lui $3,%%dtprel_hi(" #x ")\n\t"             \
+         "addiu $3,$3,%%dtprel_lo(" #x ")\n\t"         \
+         "addu %0,%0,$3"                               \
+         : "+r" (__result) : : "$3");                  \
+     __result; })
+# define TLS_IE(x)                                     \
+  ({ void *__result;                                   \
+     asm (".set push\n\t.set mips32r2\n\t"             \
+         "rdhwr\t%0,$29\n\t.set pop"                   \
+         : "=v" (__result));                           \
+     asm ("lw $3,%%gottprel(" #x ")($28)\n\t"          \
+         "addu %0,%0,$3"                               \
+         : "+r" (__result) : : "$3");                  \
+     __result; })
+# define TLS_LE(x)                                     \
+  ({ void *__result;                                   \
+     asm (".set push\n\t.set mips32r2\n\t"             \
+         "rdhwr\t%0,$29\n\t.set pop"                   \
+         : "=v" (__result));                           \
+     asm ("lui $3,%%tprel_hi(" #x ")\n\t"              \
+         "addiu $3,$3,%%tprel_lo(" #x ")\n\t"          \
+         "addu %0,%0,$3"                               \
+         : "+r" (__result) : : "$3");                  \
+     __result; })
+
+#else
+
+/* These versions are for n64.  */
+
+# define TLS_LD(x)                                     \
+  ({ void *__result;                                   \
+     extern void *__tls_get_addr (void *);             \
+     asm ("daddiu %0, $28, %%tlsldm(" #x ")"           \
+         : "=r" (__result));                           \
+     __result = __tls_get_addr (__result);             \
+     asm ("lui $3,%%dtprel_hi(" #x ")\n\t"             \
+         "daddiu $3,$3,%%dtprel_lo(" #x ")\n\t"        \
+         "daddu %0,%0,$3"                              \
+         : "+r" (__result) : : "$3");                  \
+     __result; })
+# define TLS_IE(x)                                     \
+  ({ void *__result;                                   \
+     asm (".set push\n\t.set mips32r2\n\t"             \
+         "rdhwr\t%0,$29\n\t.set pop"                   \
+         : "=v" (__result));                           \
+     asm ("ld $3,%%gottprel(" #x ")($28)\n\t"          \
+         "daddu %0,%0,$3"                              \
+         : "+r" (__result) : : "$3");                  \
+     __result; })
+# define TLS_LE(x)                                     \
+  ({ void *__result;                                   \
+     asm (".set push\n\t.set mips32r2\n\t"             \
+         "rdhwr\t%0,$29\n\t.set pop"                   \
+         : "=v" (__result));                           \
+     asm ("lui $3,%%tprel_hi(" #x ")\n\t"              \
+         "daddiu $3,$3,%%tprel_lo(" #x ")\n\t"         \
+         "daddu %0,%0,$3"                              \
+         : "+r" (__result) : : "$3");                  \
+     __result; })
+#endif