This is the mail archive of the libc-ports@sources.redhat.com mailing list for the libc-ports project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 1a/9] Tilera (and Linux asm-generic) support for glibc


(Resending PATCH 1/9 in three pieces to accommodate mailing-list limits.)

This patch is the basic tile architecture support, without the disassembly
support for backtracing (patch 1b) or Linux support (patch 1c).

2011-11-03  Chris Metcalf  <cmetcalf@tilera.com>

  * sysdeps/tile/Implies: New file.
  * sysdeps/tile/Makefile: New file.
  * sysdeps/tile/Versions: New file.
  * sysdeps/tile/__longjmp.S: New file.
  * sysdeps/tile/__tls_get_addr.S: New file.
  * sysdeps/tile/_mcount.S: New file.
  * sysdeps/tile/abort-instr.h: New file.
  * sysdeps/tile/backtrace.c: New file.
  * sysdeps/tile/bits/atomic.h: New file.
  * sysdeps/tile/bits/byteswap.h: New file.
  * sysdeps/tile/bits/endian.h: New file.
  * sysdeps/tile/bits/fenv.h: New file.
  * sysdeps/tile/bits/link.h: New file.
  * sysdeps/tile/bits/mathdef.h: New file.
  * sysdeps/tile/bits/mathinline.h: New file.
  * sysdeps/tile/bits/setjmp.h: New file.
  * sysdeps/tile/bsd-_setjmp.S: New file.
  * sysdeps/tile/bsd-setjmp.S: New file.
  * sysdeps/tile/bzero.S: New file.
  * sysdeps/tile/dl-lookupcfg.h: New file.
  * sysdeps/tile/dl-machine.h: New file.
  * sysdeps/tile/dl-runtime.c: New file.
  * sysdeps/tile/dl-start.S: New file.
  * sysdeps/tile/dl-tls.c: New file.
  * sysdeps/tile/dl-tls.h: New file.
  * sysdeps/tile/dl-trampoline.S: New file.
  * sysdeps/tile/elf/start.S: New file.
  * sysdeps/tile/fegetenv.c: New file.
  * sysdeps/tile/fegetround.c: New file.
  * sysdeps/tile/feholdexcpt.c: New file.
  * sysdeps/tile/fesetenv.c: New file.
  * sysdeps/tile/fesetround.c: New file.
  * sysdeps/tile/feupdateenv.c: New file.
  * sysdeps/tile/ffs.c: New file.
  * sysdeps/tile/ffsll.c: New file.
  * sysdeps/tile/gccframe.h: New file.
  * sysdeps/tile/jmpbuf-offsets.h: New file.
  * sysdeps/tile/jmpbuf-unwind.h: New file.
  * sysdeps/tile/ldsodefs.h: New file.
  * sysdeps/tile/libc-tls.c: New file.
  * sysdeps/tile/machine-gmon.h: New file.
  * sysdeps/tile/nptl/Makefile: New file.
  * sysdeps/tile/nptl/pthread_spin_lock.c: New file.
  * sysdeps/tile/nptl/pthread_spin_trylock.c: New file.
  * sysdeps/tile/nptl/pthreaddef.h: New file.
  * sysdeps/tile/nptl/tcb-offsets.sym: New file.
  * sysdeps/tile/nptl/tls.h: New file.
  * sysdeps/tile/preconfigure: New file.
  * sysdeps/tile/s_fma.c: New file.
  * sysdeps/tile/s_fmaf.c: New file.
  * sysdeps/tile/setjmp.S: New file.
  * sysdeps/tile/stackinfo.h: New file.
  * sysdeps/tile/sysdep.h: New file.
  * sysdeps/tile/tilegx/Makefile: New file.
  * sysdeps/tile/tilegx/bits/atomic.h: New file.
  * sysdeps/tile/tilegx/bits/wordsize.h: New file.
  * sysdeps/tile/tilegx/memchr.c: New file.
  * sysdeps/tile/tilegx/memcpy.c: New file.
  * sysdeps/tile/tilegx/memset.c: New file.
  * sysdeps/tile/tilegx/memusage.h: New file.
  * sysdeps/tile/tilegx/rawmemchr.c: New file.
  * sysdeps/tile/tilegx/strchr.c: New file.
  * sysdeps/tile/tilegx/strchrnul.c: New file.
  * sysdeps/tile/tilegx/string-endian.h: New file.
  * sysdeps/tile/tilegx/strlen.c: New file.
  * sysdeps/tile/tilegx/strrchr.c: New file.
  * sysdeps/tile/tilegx/tilegx32/Implies: New file.
  * sysdeps/tile/tilegx/tilegx64/Implies: New file.
  * sysdeps/tile/tilepro/Implies: New file.
  * sysdeps/tile/tilepro/Makefile: New file.
  * sysdeps/tile/tilepro/bits/atomic.h: New file.
  * sysdeps/tile/tilepro/bits/wordsize.h: New file.
  * sysdeps/tile/tilepro/memchr.c: New file.
  * sysdeps/tile/tilepro/memcpy.S: New file.
  * sysdeps/tile/tilepro/memset.c: New file.
  * sysdeps/tile/tilepro/memusage.h: New file.
  * sysdeps/tile/tilepro/rawmemchr.c: New file.
  * sysdeps/tile/tilepro/strchr.c: New file.
  * sysdeps/tile/tilepro/strchrnul.c: New file.
  * sysdeps/tile/tilepro/strlen.c: New file.
  * sysdeps/tile/tilepro/strrchr.c: New file.
  * sysdeps/tile/tls-macros.h: New file.
  * sysdeps/tile/tst-audit.h: New file.

diff --git a/sysdeps/tile/Implies b/sysdeps/tile/Implies
new file mode 100644
index 0000000..5b29b26
--- /dev/null
+++ b/sysdeps/tile/Implies
@@ -0,0 +1,2 @@
+ieee754/dbl-64
+ieee754/flt-32
diff --git a/sysdeps/tile/Makefile b/sysdeps/tile/Makefile
new file mode 100644
index 0000000..09a9e36
--- /dev/null
+++ b/sysdeps/tile/Makefile
@@ -0,0 +1,13 @@
+# We don't support long doubles as a distinct type.  We don't need to set
+# this variable; it's here mostly for documentational purposes.
+
+long-double-fcts = no
+
+ifeq ($(subdir),gmon)
+sysdep_routines += _mcount
+endif
+
+ifeq ($(subdir),elf)
+# extra shared linker files to link only into dl-allobjs.so
+sysdep-rtld-routines += dl-start __tls_get_addr
+endif
diff --git a/sysdeps/tile/Versions b/sysdeps/tile/Versions
new file mode 100644
index 0000000..b275d7f
--- /dev/null
+++ b/sysdeps/tile/Versions
@@ -0,0 +1,6 @@
+libc {
+  GLIBC_2.12 {
+    # name requested by gcc community.
+    __mcount;
+  }
+}
diff --git a/sysdeps/tile/__longjmp.S b/sysdeps/tile/__longjmp.S
new file mode 100644
index 0000000..60fc919
--- /dev/null
+++ b/sysdeps/tile/__longjmp.S
@@ -0,0 +1,58 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <sysdep.h>
+#include <jmpbuf-offsets.h>
+#include <asm-syntax.h>
+#include <feedback-asm.h>
+#include <arch/spr_def.h>
+
+/* PL to return to via iret in longjmp */
+#define RETURN_PL 0
+
+	.text
+ENTRY (__longjmp)
+
+#define RESTORE(r) { LD r, r0 ; ADDI_PTR r0, r0, REGSIZE }
+	FOR_EACH_CALLEE_SAVED_REG(RESTORE)
+
+	/* Make longjmp(buf, 0) return "1" instead.
+	   At the same time, construct our iret context; we set ICS so
+	   we can validly load EX_CONTEXT for iret without being
+	   interrupted halfway through.  */
+	{
+	 LD r2, r0   /* retrieve ICS bit from jmp_buf */
+	 movei r3, 1
+	 CMPEQI r0, r1, 0
+	}
+	{
+	 mtspr INTERRUPT_CRITICAL_SECTION, r3
+	 shli r2, r2, SPR_EX_CONTEXT_0_1__ICS_SHIFT
+	}
+	{
+	 mtspr EX_CONTEXT_0_0, lr
+	 ori r2, r2, RETURN_PL
+	}
+	{
+	 or r0, r1, r0
+	 mtspr EX_CONTEXT_0_1, r2
+	}
+	iret
+	jrp lr   /* Keep the backtracer happy. */
+END (__longjmp)
diff --git a/sysdeps/tile/__tls_get_addr.S b/sysdeps/tile/__tls_get_addr.S
new file mode 100644
index 0000000..ca491c5
--- /dev/null
+++ b/sysdeps/tile/__tls_get_addr.S
@@ -0,0 +1,150 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <sysdep.h>
+#include <tls.h>
+
+#ifdef _LP64
+#define LOG_SIZEOF_DTV_T 4
+#else
+#define LOG_SIZEOF_DTV_T 3
+#endif
+
+/* On entry, r0 points to two words, the module and the offset.
+   On return, r0 holds the pointer to the relevant TLS memory.
+   Only registers r25..r29 are clobbered by the call.  */
+
+	.text
+ENTRY (__tls_get_addr)
+	{
+	 lnk r25
+	 ADDI_PTR r27, tp, DTV_OFFSET
+	}
+.Llnk:
+#ifdef __tilegx__
+	{
+	 LD_PTR r27, r27	/* r27 = THREAD_DTV() */
+	 moveli r26, hw1_last(_rtld_local + TLS_GENERATION_OFFSET - .Llnk)
+	}
+	shl16insli r26, r26, hw0(_rtld_local + TLS_GENERATION_OFFSET - .Llnk)
+	{
+	 ADD_PTR r25, r25, r26
+	 LD_PTR r26, r0		/* r26 = ti_module */
+	}
+#else
+	{
+	 LD_PTR r27, r27	/* r27 = THREAD_DTV() */
+	 addli r25, r25, lo16(_rtld_local + TLS_GENERATION_OFFSET - .Llnk)
+	}
+	{
+	 auli r25, r25, ha16(_rtld_local + TLS_GENERATION_OFFSET - .Llnk)
+	 LD_PTR r26, r0		/* r26 = ti_module */
+	}
+#endif
+	LD_PTR r25, r25		/* r25 = DL(dl_tls_generation) */
+	{
+	 LD_PTR r28, r27	/* r28 = THREAD_DTV()->counter */
+	 ADDI_PTR r29, r0, __SIZEOF_POINTER__
+	}
+	{
+	 LD_PTR r29, r29	/* r29 = ti_offset */
+	 CMPEQ r25, r28, r25	/* r25 nonzero if generation OK */
+	 shli r28, r26, LOG_SIZEOF_DTV_T  /* byte index into dtv array */
+	}
+	{
+	 BEQZ r25, .Lslowpath
+	 CMPEQI r25, r26, -1	/* r25 nonzero if ti_module invalid */
+	}
+	{
+	 BNEZ r25, .Lslowpath
+	 ADD_PTR r28, r28, r27	/* pointer into module array */
+	}
+	LD_PTR r26, r28		/* r26 = module TLS pointer */
+	{
+	 ADD_PTR r0, r26, r29
+	 jrp lr
+	}
+
+.Lslowpath:
+	{
+	 ST sp, lr
+	 ADDLI_PTR r29, sp, - (25 * REGSIZE)
+	}
+	cfi_offset (lr, 0)
+	{
+	 ST r29, sp
+	 ADDLI_PTR sp, sp, - (26 * REGSIZE)
+	}
+	cfi_def_cfa_offset (26 * REGSIZE)
+	ADDI_PTR r29, sp, (2 * REGSIZE)
+	{ ST r29, r1;  ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r2;  ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r3;  ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r4;  ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r5;  ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r6;  ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r7;  ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r8;  ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r9;  ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r10; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r11; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r12; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r13; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r14; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r15; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r16; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r17; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r18; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r19; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r20; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r21; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r22; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r23; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r24; ADDI_PTR r29, r29, REGSIZE }
+	.hidden __tls_get_addr_slow
+	jal __tls_get_addr_slow
+	ADDI_PTR r29, sp, (2 * REGSIZE)
+	{ LD r1,  r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r2,  r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r3,  r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r4,  r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r5,  r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r6,  r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r7,  r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r8,  r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r9,  r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r10, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r11, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r12, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r13, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r14, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r15, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r16, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r17, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r18, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r19, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r20, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r21, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r22, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r23, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r24, r29; ADDLI_PTR sp, sp, (26 * REGSIZE) }
+	cfi_def_cfa_offset (0)
+	LD lr, sp
+	jrp lr
+END (__tls_get_addr)
diff --git a/sysdeps/tile/_mcount.S b/sysdeps/tile/_mcount.S
new file mode 100644
index 0000000..63708a1
--- /dev/null
+++ b/sysdeps/tile/_mcount.S
@@ -0,0 +1,88 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+   Based on work contributed by David Mosberger (davidm@cs.arizona.edu).
+
+   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.  */
+
+/* Assembly stub to invoke __mcount_internal().  Compiler-generated
+   code calls mcount after executing a function's prologue, placing
+   the "lr" register in "r10" for the call.  As a result "lr" is the
+   function that invoked mcount, and "r10" is mcount's caller's
+   caller.  However, we have to save all the parameter registers here
+   before invoking _mcount_internal.  Callee-save and temporary
+   registers need no special attention.  We save r10 and restore it to
+   lr on the way out, to properly handle the case of ENTRY() in
+   assembly code, before lr is saved.  We use the name __mcount since
+   the gcc community prefers using the reserved namespace.  */
+
+#include <sysdep.h>
+
+	.text
+ENTRY(__mcount)
+	{
+	 ST sp, lr
+	 ADDI_PTR r29, sp, - (12 * REGSIZE)
+	}
+	cfi_offset (lr, 0)
+	{
+	 ADDI_PTR sp, sp, - (13 * REGSIZE)
+	 ST r29, sp
+	 ADDI_PTR r29, r29, REGSIZE
+	}
+	cfi_def_cfa_offset (13 * REGSIZE)
+	{ ST r29, r0; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r1; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r2; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r3; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r4; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r5; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r6; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r7; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r8; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r9; ADDI_PTR r29, r29, REGSIZE }
+	{ ST r29, r10; ADDI_PTR r29, r29, REGSIZE; move r0, r10 }
+	{
+	 move r1, lr
+	 jal __mcount_internal
+	}
+	{
+	 ADDI_PTR r29, sp, (2 * REGSIZE)
+	}
+	{ LD r0, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r1, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r2, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r3, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r4, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r5, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r6, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r7, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r8, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r9, r29; ADDI_PTR r29, r29, REGSIZE }
+	{ LD r10, r29; ADDI_PTR sp, sp, (13 * REGSIZE) }
+	cfi_def_cfa_offset (0)
+	{
+	 LD lr, sp
+	}
+	{
+	 move lr, r10
+	 jrp lr
+	}
+END(__mcount)
+
+#undef mcount
+weak_alias (__mcount, _mcount)   /* exported in gmon/Versions */
+weak_alias (__mcount, mcount)    /* exported in stdlib/Versions */
diff --git a/sysdeps/tile/abort-instr.h b/sysdeps/tile/abort-instr.h
new file mode 100644
index 0000000..0f57da5
--- /dev/null
+++ b/sysdeps/tile/abort-instr.h
@@ -0,0 +1,2 @@
+/* An instruction which should crash any program is `hlt'.  */
+#define ABORT_INSTRUCTION asm ("ill")
diff --git a/sysdeps/tile/backtrace.c b/sysdeps/tile/backtrace.c
new file mode 100644
index 0000000..32ad175
--- /dev/null
+++ b/sysdeps/tile/backtrace.c
@@ -0,0 +1,973 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <execinfo.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <sys/ucontext.h>
+#include <sys/syscall.h>
+#include <arch/abi.h>
+
+#include <jmpbuf-offsets.h>
+#include <tile-desc.h>
+
+/* Reads 'size' bytes from 'address' and writes the data to 'result'.
+ * Returns true if successful, else false (e.g. memory not readable).
+ */
+typedef bool (*BacktraceMemoryReader) (void *result,
+				       unsigned long address,
+				       unsigned int size, void *extra);
+
+typedef struct
+{
+  /* Current PC. */
+  unsigned long pc;
+
+  /* Current stack pointer value. */
+  unsigned long sp;
+
+  /* Current frame pointer value (i.e. caller's stack pointer) */
+  unsigned long fp;
+
+  /* Internal use only: caller's PC for first frame. */
+  unsigned long initial_frame_caller_pc;
+
+  /* Internal use only: callback to read memory. */
+  BacktraceMemoryReader read_memory_func;
+
+  /* Internal use only: arbitrary argument to read_memory_func. */
+  void *read_memory_func_extra;
+
+} BacktraceIterator;
+
+
+typedef enum
+{
+
+  /* We have no idea what the caller's pc is. */
+  PC_LOC_UNKNOWN,
+
+  /* The caller's pc is currently in lr. */
+  PC_LOC_IN_LR,
+
+  /* The caller's pc can be found by dereferencing the caller's sp. */
+  PC_LOC_ON_STACK
+} CallerPCLocation;
+
+
+typedef enum
+{
+
+  /* We have no idea what the caller's sp is. */
+  SP_LOC_UNKNOWN,
+
+  /* The caller's sp is currently in r52. */
+  SP_LOC_IN_R52,
+
+  /* The caller's sp can be found by adding a certain constant
+   * to the current value of sp.
+   */
+  SP_LOC_OFFSET
+} CallerSPLocation;
+
+
+/* Bit values ORed into CALLER_* values for info ops. */
+enum
+{
+  /* Setting the low bit on any of these values means the info op
+   * applies only to one bundle ago.
+   */
+  ONE_BUNDLE_AGO_FLAG = 1,
+
+  /* Setting this bit on a CALLER_SP_* value means the PC is in LR.
+   * If not set, PC is on the stack.
+   */
+  PC_IN_LR_FLAG = 2,
+
+  /* This many of the low bits of a CALLER_SP_* value are for the
+   * flag bits above.
+   */
+  NUM_INFO_OP_FLAGS = 2,
+
+  /* We cannot have one in the memory pipe so this is the maximum. */
+  MAX_INFO_OPS_PER_BUNDLE = 2
+};
+
+
+/* Internal constants used to define 'info' operands. */
+enum
+{
+  /* 0 and 1 are reserved, as are all negative numbers. */
+
+  CALLER_UNKNOWN_BASE = 2,
+
+  CALLER_SP_IN_R52_BASE = 4,
+
+  CALLER_SP_OFFSET_BASE = 8,
+};
+
+
+/* Current backtracer state describing where it thinks the caller is. */
+typedef struct
+{
+  /*
+   * Public fields
+   */
+
+  /* How do we find the caller's PC? */
+  CallerPCLocation pc_location:8;
+
+  /* How do we find the caller's SP? */
+  CallerSPLocation sp_location:8;
+
+  /* If sp_location == SP_LOC_OFFSET, then caller_sp == sp +
+   * loc->sp_offset. Else this field is undefined.
+   */
+  uint16_t sp_offset;
+
+  /* In the most recently visited bundle a terminating bundle? */
+  bool at_terminating_bundle;
+
+  /*
+   * Private fields
+   */
+
+  /* Will the forward scanner see someone clobbering sp
+   * (i.e. changing it with something other than addi sp, sp, N?)
+   */
+  bool sp_clobber_follows;
+
+  /* Operand to next "visible" info op (no more than one bundle past
+   * the next terminating bundle), or -32768 if none.
+   */
+  int16_t next_info_operand;
+
+  /* Is the info of in next_info_op in the very next bundle? */
+  bool is_next_info_operand_adjacent;
+
+} CallerLocation;
+
+#ifdef __tilegx__
+#define TILE_MAX_INSTRUCTIONS_PER_BUNDLE TILEGX_MAX_INSTRUCTIONS_PER_BUNDLE
+#define tile_decoded_instruction tilegx_decoded_instruction
+#define tile_mnemonic tilegx_mnemonic
+#define parse_insn_tile parse_insn_tilegx
+#define TILE_OPC_IRET TILEGX_OPC_IRET
+#define TILE_OPC_ADDI TILEGX_OPC_ADDI
+#define TILE_OPC_ADDLI TILEGX_OPC_ADDLI
+#define TILE_OPC_INFO TILEGX_OPC_INFO
+#define TILE_OPC_INFOL TILEGX_OPC_INFOL
+#define TILE_OPC_JRP TILEGX_OPC_JRP
+#define TILE_OPC_MOVE TILEGX_OPC_MOVE
+#define OPCODE_STORE TILEGX_OPC_ST
+typedef long long bt_int_reg_t;
+#else
+#define TILE_MAX_INSTRUCTIONS_PER_BUNDLE TILEPRO_MAX_INSTRUCTIONS_PER_BUNDLE
+#define tile_decoded_instruction tilepro_decoded_instruction
+#define tile_mnemonic tilepro_mnemonic
+#define parse_insn_tile parse_insn_tilepro
+#define TILE_OPC_IRET TILEPRO_OPC_IRET
+#define TILE_OPC_ADDI TILEPRO_OPC_ADDI
+#define TILE_OPC_ADDLI TILEPRO_OPC_ADDLI
+#define TILE_OPC_INFO TILEPRO_OPC_INFO
+#define TILE_OPC_INFOL TILEPRO_OPC_INFOL
+#define TILE_OPC_JRP TILEPRO_OPC_JRP
+#define TILE_OPC_MOVE TILEPRO_OPC_MOVE
+#define OPCODE_STORE TILEPRO_OPC_SW
+typedef int bt_int_reg_t;
+#endif
+
+/* A decoded bundle used for backtracer analysis. */
+struct BacktraceBundle
+{
+  tile_bundle_bits bits;
+  int num_insns;
+  struct tile_decoded_instruction insns[TILE_MAX_INSTRUCTIONS_PER_BUNDLE];
+};
+
+
+/* Locates an instruction inside the given bundle that
+ * has the specified mnemonic, and whose first 'num_operands_to_match'
+ * operands exactly match those in 'operand_values'.
+ */
+static const struct tile_decoded_instruction *
+find_matching_insn (const struct BacktraceBundle *bundle,
+		    tile_mnemonic mnemonic,
+		    const int *operand_values, int num_operands_to_match)
+{
+  int i, j;
+  bool match;
+
+  for (i = 0; i < bundle->num_insns; i++)
+    {
+      const struct tile_decoded_instruction *insn = &bundle->insns[i];
+
+      if (insn->opcode->mnemonic != mnemonic)
+	continue;
+
+      match = true;
+      for (j = 0; j < num_operands_to_match; j++)
+	{
+	  if (operand_values[j] != insn->operand_values[j])
+	    {
+	      match = false;
+	      break;
+	    }
+	}
+
+      if (match)
+	return insn;
+    }
+
+  return NULL;
+}
+
+/* Does this bundle contain an 'iret' instruction? */
+static inline bool
+bt_has_iret (const struct BacktraceBundle *bundle)
+{
+  return find_matching_insn (bundle, TILE_OPC_IRET, NULL, 0) != NULL;
+}
+
+/* Does this bundle contain an 'addi sp, sp, OFFSET' or
+ * 'addli sp, sp, OFFSET' instruction, and if so, what is OFFSET?
+ */
+static bool
+bt_has_addi_sp (const struct BacktraceBundle *bundle, int *adjust)
+{
+  static const int vals[2] = { TREG_SP, TREG_SP };
+
+  const struct tile_decoded_instruction *insn =
+    find_matching_insn (bundle, TILE_OPC_ADDI, vals, 2);
+  if (insn == NULL)
+    insn = find_matching_insn (bundle, TILE_OPC_ADDLI, vals, 2);
+#ifdef __tilegx__
+  if (insn == NULL)
+    insn = find_matching_insn (bundle, TILEGX_OPC_ADDXLI, vals, 2);
+  if (insn == NULL)
+    insn = find_matching_insn (bundle, TILEGX_OPC_ADDXI, vals, 2);
+#endif
+  if (insn == NULL)
+    return false;
+
+  *adjust = insn->operand_values[2];
+  return true;
+}
+
+/* Does this bundle contain any 'info OP' or 'infol OP'
+ * instruction, and if so, what are their OP?  Note that OP is interpreted
+ * as an unsigned value by this code since that's what the caller wants.
+ * Returns the number of info ops found.
+ */
+static int
+bt_get_info_ops (const struct BacktraceBundle *bundle,
+		 int operands[MAX_INFO_OPS_PER_BUNDLE])
+{
+  int num_ops = 0;
+  int i;
+
+  for (i = 0; i < bundle->num_insns; i++)
+    {
+      const struct tile_decoded_instruction *insn = &bundle->insns[i];
+
+      if (insn->opcode->mnemonic == TILE_OPC_INFO ||
+	  insn->opcode->mnemonic == TILE_OPC_INFOL)
+	{
+	  operands[num_ops++] = insn->operand_values[0];
+	}
+    }
+
+  return num_ops;
+}
+
+/* Does this bundle contain a jrp instruction, and if so, to which
+ * register is it jumping?
+ */
+static bool
+bt_has_jrp (const struct BacktraceBundle *bundle, int *target_reg)
+{
+  const struct tile_decoded_instruction *insn =
+    find_matching_insn (bundle, TILE_OPC_JRP, NULL, 0);
+  if (insn == NULL)
+    return false;
+
+  *target_reg = insn->operand_values[0];
+  return true;
+}
+
+/* Does this bundle modify the specified register in any way? */
+static bool
+bt_modifies_reg (const struct BacktraceBundle *bundle, int reg)
+{
+  int i, j;
+  for (i = 0; i < bundle->num_insns; i++)
+    {
+      const struct tile_decoded_instruction *insn = &bundle->insns[i];
+
+      if (insn->opcode->implicitly_written_register == reg)
+	return true;
+
+      for (j = 0; j < insn->opcode->num_operands; j++)
+	if (insn->operands[j]->is_dest_reg && insn->operand_values[j] == reg)
+	  return true;
+    }
+
+  return false;
+}
+
+/* Does this bundle modify sp? */
+static inline bool
+bt_modifies_sp (const struct BacktraceBundle *bundle)
+{
+  return bt_modifies_reg (bundle, TREG_SP);
+}
+
+/* Does this bundle modify lr? */
+static inline bool
+bt_modifies_lr (const struct BacktraceBundle *bundle)
+{
+  return bt_modifies_reg (bundle, TREG_LR);
+}
+
+/* Does this bundle contain the instruction 'move fp, sp'? */
+static inline bool
+bt_has_move_r52_sp (const struct BacktraceBundle *bundle)
+{
+  static const int vals[2] = { 52, TREG_SP };
+  return find_matching_insn (bundle, TILE_OPC_MOVE, vals, 2) != NULL;
+}
+
+/* Does this bundle contain a store of lr to sp? */
+static inline bool
+bt_has_sw_sp_lr (const struct BacktraceBundle *bundle)
+{
+  static const int vals[2] = { TREG_SP, TREG_LR };
+  return find_matching_insn (bundle, OPCODE_STORE, vals, 2) != NULL;
+}
+
+#ifdef __tilegx__
+/* Track moveli values placed into registers. */
+static inline void
+bt_update_moveli (const struct BacktraceBundle *bundle, int moveli_args[])
+{
+  int i;
+  for (i = 0; i < bundle->num_insns; i++)
+    {
+      const struct tile_decoded_instruction *insn = &bundle->insns[i];
+
+      if (insn->opcode->mnemonic == TILEGX_OPC_MOVELI)
+	{
+	  int reg = insn->operand_values[0];
+	  moveli_args[reg] = insn->operand_values[1];
+	}
+    }
+}
+
+/* Does this bundle contain an 'add sp, sp, reg' instruction
+ * from a register that we saw a moveli into, and if so, what
+ * is the value in the register?
+ */
+static bool
+bt_has_add_sp (const struct BacktraceBundle *bundle, int *adjust,
+	       int moveli_args[])
+{
+  static const int vals[2] = { TREG_SP, TREG_SP };
+
+  const struct tile_decoded_instruction *insn =
+    find_matching_insn (bundle, TILEGX_OPC_ADDX, vals, 2);
+  if (insn)
+    {
+      int reg = insn->operand_values[2];
+      if (moveli_args[reg])
+	{
+	  *adjust = moveli_args[reg];
+	  return true;
+	}
+    }
+  return false;
+}
+#endif
+
+/* Locates the caller's PC and SP for a program starting at the
+ * given address.
+ */
+static void
+find_caller_pc_and_caller_sp (CallerLocation * location,
+			      const unsigned long start_pc,
+			      BacktraceMemoryReader read_memory_func,
+			      void *read_memory_func_extra)
+{
+  /* Have we explicitly decided what the sp is,
+   * rather than just the default?
+   */
+  bool sp_determined = false;
+
+  /* Has any bundle seen so far modified lr? */
+  bool lr_modified = false;
+
+  /* Have we seen a move from sp to fp? */
+  bool sp_moved_to_r52 = false;
+
+  /* Have we seen a terminating bundle? */
+  bool seen_terminating_bundle = false;
+
+  /* Cut down on round-trip reading overhead by reading several
+   * bundles at a time.
+   */
+  tile_bundle_bits prefetched_bundles[32];
+  int num_bundles_prefetched = 0;
+  int next_bundle = 0;
+  unsigned long pc;
+
+#ifdef __tilegx__
+  /* Naively try to track moveli values to support addx for -m32. */
+  int moveli_args[TILEGX_NUM_REGISTERS] = { 0 };
+#endif
+
+  /* Default to assuming that the caller's sp is the current sp.
+   * This is necessary to handle the case where we start backtracing
+   * right at the end of the epilog.
+   */
+  location->sp_location = SP_LOC_OFFSET;
+  location->sp_offset = 0;
+
+  /* Default to having no idea where the caller PC is. */
+  location->pc_location = PC_LOC_UNKNOWN;
+
+  /* Don't even try if the PC is not aligned. */
+  if (start_pc % TILE_BUNDLE_ALIGNMENT_IN_BYTES != 0)
+    return;
+
+  for (pc = start_pc;; pc += sizeof (tile_bundle_bits))
+    {
+
+      struct BacktraceBundle bundle;
+      int num_info_ops, info_operands[MAX_INFO_OPS_PER_BUNDLE];
+      int one_ago, jrp_reg;
+      bool has_jrp;
+
+      if (next_bundle >= num_bundles_prefetched)
+	{
+	  /* Prefetch some bytes, but don't cross a page
+	   * boundary since that might cause a read failure we
+	   * don't care about if we only need the first few
+	   * bytes. Note: we don't care what the actual page
+	   * size is; using the minimum possible page size will
+	   * prevent any problems.
+	   */
+	  unsigned int bytes_to_prefetch = 4096 - (pc & 4095);
+	  if (bytes_to_prefetch > sizeof prefetched_bundles)
+	    bytes_to_prefetch = sizeof prefetched_bundles;
+
+	  if (!read_memory_func (prefetched_bundles, pc,
+				 bytes_to_prefetch, read_memory_func_extra))
+	    {
+	      if (pc == start_pc)
+		{
+		  /* The program probably called a bad
+		   * address, such as a NULL pointer.
+		   * So treat this as if we are at the
+		   * start of the function prolog so the
+		   * backtrace will show how we got here.
+		   */
+		  location->pc_location = PC_LOC_IN_LR;
+		  return;
+		}
+
+	      /* Unreadable address. Give up. */
+	      break;
+	    }
+
+	  next_bundle = 0;
+	  num_bundles_prefetched =
+	    bytes_to_prefetch / sizeof (tile_bundle_bits);
+	}
+
+      /* Decode the next bundle. */
+      bundle.bits = prefetched_bundles[next_bundle++];
+#ifdef __BIG_ENDIAN__
+      /*
+       * TILE executes the same bundles in little-endian and big-
+       * endian, also the layout of executables in memory are the
+       * same. But in big-endian, if we load the instructions from
+       * memory by hand (for example, in this case) we get
+       * byte-swapped bundles which TILE will not recognize.
+       * This source file is shared by linux kernel, gdb and glibc,
+       * so we are not able to use le64toh (glibc) or le64_to_cpu
+       * (kernel).
+       */
+      bundle.bits = __builtin_bswap64 (bundle.bits);
+#endif
+      bundle.num_insns = parse_insn_tile (bundle.bits, pc, bundle.insns);
+      num_info_ops = bt_get_info_ops (&bundle, info_operands);
+
+      /* First look at any one_ago info ops if they are interesting,
+       * since they should shadow any non-one-ago info ops.
+       */
+      for (one_ago = (pc != start_pc) ? 1 : 0; one_ago >= 0; one_ago--)
+	{
+	  int i;
+	  for (i = 0; i < num_info_ops; i++)
+	    {
+	      int info_operand = info_operands[i];
+	      if (info_operand < CALLER_UNKNOWN_BASE)
+		{
+		  /* Weird; reserved value, ignore it. */
+		  continue;
+		}
+
+	      /* Skip info ops which are not in the
+	       * "one_ago" mode we want right now.
+	       */
+	      if (((info_operand & ONE_BUNDLE_AGO_FLAG) != 0)
+		  != (one_ago != 0))
+		continue;
+
+	      /* Clear the flag to make later checking
+	       * easier. */
+	      info_operand &= ~ONE_BUNDLE_AGO_FLAG;
+
+	      /* Default to looking at PC_IN_LR_FLAG. */
+	      if (info_operand & PC_IN_LR_FLAG)
+		location->pc_location = PC_LOC_IN_LR;
+	      else
+		location->pc_location = PC_LOC_ON_STACK;
+
+	      switch (info_operand)
+		{
+		case CALLER_UNKNOWN_BASE:
+		  location->pc_location = PC_LOC_UNKNOWN;
+		  location->sp_location = SP_LOC_UNKNOWN;
+		  return;
+
+		case CALLER_SP_IN_R52_BASE:
+		case CALLER_SP_IN_R52_BASE | PC_IN_LR_FLAG:
+		  location->sp_location = SP_LOC_IN_R52;
+		  return;
+
+		default:
+		  {
+		    const unsigned int val = info_operand
+		      - CALLER_SP_OFFSET_BASE;
+		    const unsigned int sp_offset =
+		      (val >> NUM_INFO_OP_FLAGS) * 8;
+		    if (sp_offset < 32768)
+		      {
+			/* This is a properly encoded
+			 * SP offset. */
+			location->sp_location = SP_LOC_OFFSET;
+			location->sp_offset = sp_offset;
+			return;
+		      }
+		    else
+		      {
+			/* This looked like an SP
+			 * offset, but it's outside
+			 * the legal range, so this
+			 * must be an unrecognized
+			 * info operand.  Ignore it.
+			 */
+		      }
+		  }
+		  break;
+		}
+	    }
+	}
+
+      if (seen_terminating_bundle)
+	{
+	  /* We saw a terminating bundle during the previous
+	   * iteration, so we were only looking for an info op.
+	   */
+	  break;
+	}
+
+      if (bundle.bits == 0)
+	{
+	  /* Wacky terminating bundle. Stop looping, and hope
+	   * we've already seen enough to find the caller.
+	   */
+	  break;
+	}
+
+      /*
+       * Try to determine caller's SP.
+       */
+
+      if (!sp_determined)
+	{
+	  int adjust;
+	  if (bt_has_addi_sp (&bundle, &adjust)
+#ifdef __tilegx__
+	      || bt_has_add_sp (&bundle, &adjust, moveli_args)
+#endif
+	    )
+	    {
+	      location->sp_location = SP_LOC_OFFSET;
+
+	      if (adjust <= 0)
+		{
+		  /* We are in prolog about to adjust
+		   * SP. */
+		  location->sp_offset = 0;
+		}
+	      else
+		{
+		  /* We are in epilog restoring SP. */
+		  location->sp_offset = adjust;
+		}
+
+	      sp_determined = true;
+	    }
+	  else
+	    {
+	      if (bt_has_move_r52_sp (&bundle))
+		{
+		  /* Maybe in prolog, creating an
+		   * alloca-style frame.  But maybe in
+		   * the middle of a fixed-size frame
+		   * clobbering r52 with SP.
+		   */
+		  sp_moved_to_r52 = true;
+		}
+
+	      if (bt_modifies_sp (&bundle))
+		{
+		  if (sp_moved_to_r52)
+		    {
+		      /* We saw SP get saved into
+		       * r52 earlier (or now), which
+		       * must have been in the
+		       * prolog, so we now know that
+		       * SP is still holding the
+		       * caller's sp value.
+		       */
+		      location->sp_location = SP_LOC_OFFSET;
+		      location->sp_offset = 0;
+		    }
+		  else
+		    {
+		      /* Someone must have saved
+		       * aside the caller's SP value
+		       * into r52, so r52 holds the
+		       * current value.
+		       */
+		      location->sp_location = SP_LOC_IN_R52;
+		    }
+		  sp_determined = true;
+		}
+	    }
+
+#ifdef __tilegx__
+	  /* Track moveli arguments for -m32 mode. */
+	  bt_update_moveli (&bundle, moveli_args);
+#endif
+	}
+
+      if (bt_has_iret (&bundle))
+	{
+	  /* This is a terminating bundle. */
+	  seen_terminating_bundle = true;
+	  continue;
+	}
+
+      /*
+       * Try to determine caller's PC.
+       */
+
+      jrp_reg = -1;
+      has_jrp = bt_has_jrp (&bundle, &jrp_reg);
+      if (has_jrp)
+	seen_terminating_bundle = true;
+
+      if (location->pc_location == PC_LOC_UNKNOWN)
+	{
+	  if (has_jrp)
+	    {
+	      if (jrp_reg == TREG_LR && !lr_modified)
+		{
+		  /* Looks like a leaf function, or else
+		   * lr is already restored. */
+		  location->pc_location = PC_LOC_IN_LR;
+		}
+	      else
+		{
+		  location->pc_location = PC_LOC_ON_STACK;
+		}
+	    }
+	  else if (bt_has_sw_sp_lr (&bundle))
+	    {
+	      /* In prolog, spilling initial lr to stack. */
+	      location->pc_location = PC_LOC_IN_LR;
+	    }
+	  else if (bt_modifies_lr (&bundle))
+	    {
+	      lr_modified = true;
+	    }
+	}
+    }
+}
+
+/* Initializes a backtracer to start from the given location.
+ *
+ * If the frame pointer cannot be determined it is set to -1.
+ *
+ * state: The state to be filled in.
+ * read_memory_func: A callback that reads memory.
+ * read_memory_func_extra: An arbitrary argument to read_memory_func.
+ * pc: The current PC.
+ * lr: The current value of the 'lr' register.
+ * sp: The current value of the 'sp' register.
+ * r52: The current value of the 'r52' register.
+ */
+static void
+backtrace_init (BacktraceIterator * state,
+		BacktraceMemoryReader read_memory_func,
+		void *read_memory_func_extra,
+		unsigned long pc, unsigned long lr,
+		unsigned long sp, unsigned long r52)
+{
+  CallerLocation location;
+  unsigned long fp, initial_frame_caller_pc;
+
+  /* Find out where we are in the initial frame. */
+  find_caller_pc_and_caller_sp (&location, pc,
+				read_memory_func, read_memory_func_extra);
+
+  switch (location.sp_location)
+    {
+    case SP_LOC_UNKNOWN:
+      /* Give up. */
+      fp = -1;
+      break;
+
+    case SP_LOC_IN_R52:
+      fp = r52;
+      break;
+
+    case SP_LOC_OFFSET:
+      fp = sp + location.sp_offset;
+      break;
+
+    default:
+      /* Give up. */
+      fp = -1;
+      break;
+    }
+
+  /* If the frame pointer is not aligned to the basic word size
+   * something terrible happened and we should mark it as invalid.
+   */
+  if (fp % sizeof (bt_int_reg_t) != 0)
+    fp = -1;
+
+  /* -1 means "don't know initial_frame_caller_pc". */
+  initial_frame_caller_pc = -1;
+
+  switch (location.pc_location)
+    {
+    case PC_LOC_UNKNOWN:
+      /* Give up. */
+      fp = -1;
+      break;
+
+    case PC_LOC_IN_LR:
+      if (lr == 0 || lr % TILE_BUNDLE_ALIGNMENT_IN_BYTES != 0)
+	{
+	  /* Give up. */
+	  fp = -1;
+	}
+      else
+	{
+	  initial_frame_caller_pc = lr;
+	}
+      break;
+
+    case PC_LOC_ON_STACK:
+      /* Leave initial_frame_caller_pc as -1,
+       * meaning check the stack.
+       */
+      break;
+
+    default:
+      /* Give up. */
+      fp = -1;
+      break;
+    }
+
+  state->pc = pc;
+  state->sp = sp;
+  state->fp = fp;
+  state->initial_frame_caller_pc = initial_frame_caller_pc;
+  state->read_memory_func = read_memory_func;
+  state->read_memory_func_extra = read_memory_func_extra;
+}
+
+/* Handle the case where the register holds more bits than the VA. */
+static bool
+valid_addr_reg (bt_int_reg_t reg)
+{
+  return ((unsigned long) reg == reg);
+}
+
+/* Advances the backtracing state to the calling frame, returning
+ * true iff successful.
+ */
+static bool
+backtrace_next (BacktraceIterator * state)
+{
+  unsigned long next_fp, next_pc;
+  bt_int_reg_t next_frame[2];
+
+  if (state->fp == -1)
+    {
+      /* No parent frame. */
+      return false;
+    }
+
+  /* Try to read the frame linkage data chaining to the next function. */
+  if (!state->read_memory_func (&next_frame, state->fp, sizeof next_frame,
+				state->read_memory_func_extra))
+    {
+      return false;
+    }
+
+  next_fp = next_frame[1];
+  if (!valid_addr_reg (next_frame[1]) || next_fp % sizeof (bt_int_reg_t) != 0)
+    {
+      /* Caller's frame pointer is suspect, so give up. */
+      return false;
+    }
+
+  if (state->initial_frame_caller_pc != -1)
+    {
+      /* We must be in the initial stack frame and already know the
+       * caller PC.
+       */
+      next_pc = state->initial_frame_caller_pc;
+
+      /* Force reading stack next time, in case we were in the
+       * initial frame.  We don't do this above just to paranoidly
+       * avoid changing the struct at all when we return false.
+       */
+      state->initial_frame_caller_pc = -1;
+    }
+  else
+    {
+      /* Get the caller PC from the frame linkage area. */
+      next_pc = next_frame[0];
+      if (!valid_addr_reg (next_frame[0]) || next_pc == 0 ||
+	  next_pc % TILE_BUNDLE_ALIGNMENT_IN_BYTES != 0)
+	{
+	  /* The PC is suspect, so give up. */
+	  return false;
+	}
+    }
+
+  /* Update state to become the caller's stack frame. */
+  state->pc = next_pc;
+  state->sp = state->fp;
+  state->fp = next_fp;
+
+  return true;
+}
+
+static bool
+bt_read_memory (void *result, unsigned long addr,
+		unsigned int size, void *extra)
+{
+  if ((unsigned long) addr < __getpagesize ())
+    return false;		/* Page at zero is never mapped */
+  memcpy (result, (const void *) addr, size);
+  return true;
+}
+
+/* Macro to define a copy of the kernel's __rt_sigreturn function
+   (in arch/tile/kernel/entry.S).  If that function is changed,
+   this one needs to be changed to match it.  */
+#define _sigreturn_asm(REG, NR) asm(                    \
+    ".pushsection .text.__rt_sigreturn,\"a\"\n"         \
+    ".global __rt_sigreturn\n"                          \
+    ".type __rt_sigreturn,@function\n"                  \
+    "__rt_sigreturn:\n"                                 \
+    "moveli " #REG ", " #NR "\n"                        \
+    "swint1\n"                                          \
+    ".size __rt_sigreturn, . - __rt_sigreturn\n"        \
+    ".popsection")
+#define sigreturn_asm(REG, NR) _sigreturn_asm(REG, NR)
+#define SIGRETURN_LEN 16
+sigreturn_asm (TREG_SYSCALL_NR_NAME, __NR_rt_sigreturn);
+extern char __rt_sigreturn[];
+
+/* Store up to SIZE return addresses of the current program state in
+   ARRAY and return the exact number of values stored.  */
+int
+__backtrace (void **array, int size)
+{
+  jmp_buf jmpbuf;
+  BacktraceIterator it;
+  int i = 0;
+
+  /* See longjmp.h and bits/setjmp.h for the list of saved registers.
+     JB_PC holds the PC of the call site of setjmp(), i.e. right here.
+     We don't have LR, but we have SP/FP, which the backtrace code
+     can use to locate LR on our stack.  */
+  setjmp (jmpbuf);
+  backtrace_init (&it, bt_read_memory, NULL,
+		  jmpbuf->__jmpbuf[JB_PC], 0,
+		  jmpbuf->__jmpbuf[JB_SP], jmpbuf->__jmpbuf[JB_FP]);
+
+  for (;;)
+    {
+
+      /* Loop going up the stack looking for normal caller frames. */
+      while (i < size && backtrace_next (&it))
+	array[i++] = (void *) it.pc;
+
+      /* We're done, unless this was a signal handler. */
+      if (i >= size || i == 0 ||
+	  memcmp ((void *) it.pc, __rt_sigreturn, SIGRETURN_LEN) != 0)
+	break;
+
+      /* It was a signal handler; update the reported PC to point to
+         our copy, since that will be findable with dladdr() and therefore
+         somewhat easier to help understand what actually happened. */
+      array[i - 1] = __rt_sigreturn;
+
+      /* Restart using the registers saved by the signal. */
+      struct ucontext *uc = (struct ucontext *)
+	(it.sp + C_ABI_SAVE_AREA_SIZE + sizeof (struct siginfo));
+
+      backtrace_init (&it, bt_read_memory, NULL,
+		      uc->uc_mcontext.pc,
+		      uc->uc_mcontext.lr,
+		      uc->uc_mcontext.sp, uc->uc_mcontext.gregs[TREG_FP]);
+      array[i++] = (void *) (unsigned long) uc->uc_mcontext.pc;
+    }
+
+  return i;
+}
+
+weak_alias (__backtrace, backtrace)
+libc_hidden_def (__backtrace)
diff --git a/sysdeps/tile/bits/atomic.h b/sysdeps/tile/bits/atomic.h
new file mode 100644
index 0000000..f347a8e
--- /dev/null
+++ b/sysdeps/tile/bits/atomic.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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.  */
+
+/* The sub-architecture headers provide definitions for these macros
+   that work for "int" and "long" size values only:
+
+   atomic_compare_and_exchange_val_acq()
+   atomic_exchange_acq()
+   atomic_exchange_and_add()
+   atomic_and_val()
+   atomic_or_val()
+   atomic_decrement_if_positive() [tilegx only]
+
+   Here we provide generic definitions true for all Tilera chips.  */
+
+#include <stdint.h>
+#include <features.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;
+
+/* Barrier macro. */
+#define atomic_full_barrier() __insn_mf ()
+
+/* APIs with "release" semantics. */
+#define atomic_compare_and_exchange_val_rel(mem, n, o)          \
+  ({                                                            \
+    atomic_full_barrier ();                                     \
+    atomic_compare_and_exchange_val_acq ((mem), (n), (o));      \
+  })
+#define atomic_compare_and_exchange_bool_rel(mem, n, o)         \
+  ({                                                            \
+    atomic_full_barrier ();                                     \
+    atomic_compare_and_exchange_bool_acq ((mem), (n), (o));     \
+  })
+#define atomic_exchange_rel(mem, n)                             \
+  ({                                                            \
+    atomic_full_barrier ();                                     \
+    atomic_exchange_acq ((mem), (n));                           \
+  })
+
+/* Various macros that should just be synonyms. */
+#define catomic_exchange_and_add atomic_exchange_and_add
+#define atomic_and(mem, mask) ((void) atomic_and_val ((mem), (mask)))
+#define catomic_and atomic_and
+#define atomic_or(mem, mask) ((void) atomic_or_val ((mem), (mask)))
+#define catomic_or atomic_or
+
+/* atomic_bit_test_set in terms of atomic_or_val. */
+#define atomic_bit_test_set(mem, bit)                                   \
+  ({ __typeof (*(mem)) __att0_mask = ((__typeof (*(mem))) 1 << (bit));  \
+    atomic_or_val ((mem), __att0_mask) & __att0_mask; })
+
+/*
+ * This non-existent symbol is called for unsupporrted sizes,
+ * indicating a bug in the caller.
+ */
+extern int __atomic_error_bad_argument_size(void)
+  __attribute__ ((warning ("bad sizeof atomic argument")));
diff --git a/sysdeps/tile/bits/byteswap.h b/sysdeps/tile/bits/byteswap.h
new file mode 100644
index 0000000..6858a30
--- /dev/null
+++ b/sysdeps/tile/bits/byteswap.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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.  */
+
+#if !defined _BYTESWAP_H && !defined _NETINET_IN_H && !defined _ENDIAN_H
+# error "Never use <bits/byteswap.h> directly; include <byteswap.h> instead."
+#endif
+
+#ifndef _BITS_BYTESWAP_H
+#define _BITS_BYTESWAP_H 1
+
+/* gcc __builtin_bswap64() can constant-fold, etc, so always use it. */
+#define __bswap_16(x) ((unsigned short)(__builtin_bswap32(x) >> 16))
+#define __bswap_32(x) ((unsigned int)__builtin_bswap32(x))
+#define __bswap_64(x) ((unsigned long long)__builtin_bswap64(x))
+
+#define __bswap_constant_16(x) __bswap_16(x)
+#define __bswap_constant_32(x) __bswap_32(x)
+#define __bswap_constant_64(x) __bswap_64(x)
+
+#endif /* _BITS_BYTESWAP_H */
diff --git a/sysdeps/tile/bits/endian.h b/sysdeps/tile/bits/endian.h
new file mode 100644
index 0000000..43d94bb
--- /dev/null
+++ b/sysdeps/tile/bits/endian.h
@@ -0,0 +1,13 @@
+/* Set endianness for tile.  */
+
+#ifndef _ENDIAN_H
+# error "Never use <bits/endian.h> directly; include <endian.h> instead."
+#endif
+
+#if defined __BIG_ENDIAN__
+# define __BYTE_ORDER __BIG_ENDIAN
+#elif defined __LITTLE_ENDIAN__
+# define __BYTE_ORDER __LITTLE_ENDIAN
+#else
+# error "Endianness not declared!!"
+#endif
diff --git a/sysdeps/tile/bits/fenv.h b/sysdeps/tile/bits/fenv.h
new file mode 100644
index 0000000..feb41fa
--- /dev/null
+++ b/sysdeps/tile/bits/fenv.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 1997, 1998, 1999, 2008 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 _FENV_H
+# error "Never use <bits/fenv.h> directly; include <fenv.h> instead."
+#endif
+
+/* The TILE-Gx hardware does not provide floating-point exception
+   handling, and TILEPro does not support any floating-point operations.  */
+#define FE_ALL_EXCEPT 0
+
+/* TILE-Gx supports only round-to-nearest.  The software
+   floating-point support also acts this way.  */
+enum
+  {
+    FE_TONEAREST = 1,
+#define FE_TONEAREST	FE_TONEAREST
+  };
+
+/* Type representing exception flags (if there were any).  */
+typedef unsigned int fexcept_t;
+
+/* Type representing floating-point environment.  */
+typedef unsigned int fenv_t;
+
+/* If the default argument is used we use this value.  */
+#define FE_DFL_ENV	((__const fenv_t *) -1l)
diff --git a/sysdeps/tile/bits/link.h b/sysdeps/tile/bits/link.h
new file mode 100644
index 0000000..2d48cff
--- /dev/null
+++ b/sysdeps/tile/bits/link.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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	_LINK_H
+# error "Never include <bits/link.h> directly; use <link.h> instead."
+#endif
+
+#define __need_int_reg_t
+#include <arch/abi.h>
+
+
+/* Registers for entry into PLT.  */
+typedef struct La_tile_regs
+{
+  __uint_reg_t lr_reg[10];
+} La_tile_regs;
+
+/* Return values for calls from PLT.  */
+typedef struct La_tile_retval
+{
+  /* Up to ten registers can be used for a return value (e.g. small struct). */
+  __uint_reg_t lrv_reg[10];
+} La_tile_retval;
+
+
+__BEGIN_DECLS
+
+extern ElfW(Addr) la_tile_gnu_pltenter (ElfW(Sym) *__sym, unsigned int __ndx,
+                                        uintptr_t *__refcook,
+                                        uintptr_t *__defcook,
+                                        La_tile_regs *__regs,
+                                        unsigned int *__flags,
+                                        const char *__symname,
+                                        long int *__framesizep);
+extern ElfW(Addr) la_tile_gnu_pltexit (ElfW(Sym) *__sym, unsigned int __ndx,
+                                       uintptr_t *__refcook,
+                                       uintptr_t *__defcook,
+                                       const La_tile_regs *__inregs,
+                                       La_tile_retval *__outregs,
+                                       const char *__symname);
+
+__END_DECLS
diff --git a/sysdeps/tile/bits/mathdef.h b/sysdeps/tile/bits/mathdef.h
new file mode 100644
index 0000000..7c7ef5d
--- /dev/null
+++ b/sysdeps/tile/bits/mathdef.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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.  */
+
+#if !defined _MATH_H && !defined _COMPLEX_H
+# error "Never use <bits/mathdef.h> directly; include <math.h> instead"
+#endif
+
+#if defined __USE_ISOC99 && defined _MATH_H && !defined _MATH_H_MATHDEF
+# define _MATH_H_MATHDEF	1
+
+/* "float" and "double" expressions evaluated as "float" and "double". */
+typedef float float_t;
+typedef double double_t;
+
+/* The values returned by `ilogb' for 0 and NaN respectively.  */
+# define FP_ILOGB0	(-2147483647)
+# define FP_ILOGBNAN	(2147483647)
+
+/* The GCC 4.6 compiler will define __FP_FAST_FMA{,F,L} if the fma{,f,l}
+   builtins are supported.  */
+# if __FP_FAST_FMA
+#  define FP_FAST_FMA 1
+# endif
+
+# if __FP_FAST_FMAF
+#  define FP_FAST_FMAF 1
+# endif
+
+# if __FP_FAST_FMAL
+#  define FP_FAST_FMAL 1
+# endif
+
+#endif /* ISO C99 */
diff --git a/sysdeps/tile/bits/mathinline.h b/sysdeps/tile/bits/mathinline.h
new file mode 100644
index 0000000..db29fdb
--- /dev/null
+++ b/sysdeps/tile/bits/mathinline.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 _MATH_H
+# error "Never use <bits/mathinline.h> directly; include <math.h> instead."
+#endif
+
+#ifndef __extern_always_inline
+# define __MATH_INLINE __inline
+#else
+# define __MATH_INLINE __extern_always_inline
+#endif
+
+
+#if defined __USE_ISOC99 && defined __GNUC__
+
+/* Test for negative number.  Used in the signbit() macro.  */
+__MATH_INLINE int
+__NTH (__signbitf (float __x))
+{
+  __extension__ union { float __f; int __i; } __u = { __f: __x };
+  return __u.__i < 0;
+}
+__MATH_INLINE int
+__NTH (__signbit (double __x))
+{
+  __extension__ union { double __d; long long __i; } __u = { __d: __x };
+  return __u.__i < 0;
+}
+
+#endif
diff --git a/sysdeps/tile/bits/setjmp.h b/sysdeps/tile/bits/setjmp.h
new file mode 100644
index 0000000..4961259
--- /dev/null
+++ b/sysdeps/tile/bits/setjmp.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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.  */
+
+/* Define the machine-dependent type `jmp_buf'.  TILE version.  */
+#ifndef _BITS_SETJMP_H
+#define _BITS_SETJMP_H	1
+
+#if !defined _SETJMP_H && !defined _PTHREAD_H
+# error "Never include <bits/setjmp.h> directly; use <setjmp.h> instead."
+#endif
+
+#ifndef _ASM
+
+#define __need_int_reg_t
+#include <arch/abi.h>
+
+typedef __uint_reg_t __jmp_buf[32];
+
+#endif
+
+#endif /* bits/setjmp.h */
diff --git a/sysdeps/tile/bsd-_setjmp.S b/sysdeps/tile/bsd-_setjmp.S
new file mode 100644
index 0000000..4e6a2da
--- /dev/null
+++ b/sysdeps/tile/bsd-_setjmp.S
@@ -0,0 +1 @@
+/* _setjmp is in setjmp.S  */
diff --git a/sysdeps/tile/bsd-setjmp.S b/sysdeps/tile/bsd-setjmp.S
new file mode 100644
index 0000000..1da848d
--- /dev/null
+++ b/sysdeps/tile/bsd-setjmp.S
@@ -0,0 +1 @@
+/* setjmp is in setjmp.S  */
diff --git a/sysdeps/tile/bzero.S b/sysdeps/tile/bzero.S
new file mode 100644
index 0000000..12d8aba
--- /dev/null
+++ b/sysdeps/tile/bzero.S
@@ -0,0 +1,30 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <sysdep.h>
+
+	.text
+ENTRY(__bzero)
+	{
+	 move r2, r1
+	 move r1, zero
+	}
+	j __memset
+END(__bzero)
+weak_alias (__bzero, bzero)
diff --git a/sysdeps/tile/dl-lookupcfg.h b/sysdeps/tile/dl-lookupcfg.h
new file mode 100644
index 0000000..e1a5b26
--- /dev/null
+++ b/sysdeps/tile/dl-lookupcfg.h
@@ -0,0 +1,28 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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.  */
+
+#define DL_UNMAP_IS_SPECIAL
+
+#include_next <dl-lookupcfg.h>
+
+struct link_map;
+
+void internal_function _dl_unmap (struct link_map *map);
+
+#define DL_UNMAP(map) _dl_unmap (map)
diff --git a/sysdeps/tile/dl-machine.h b/sysdeps/tile/dl-machine.h
new file mode 100644
index 0000000..8f2ed4b
--- /dev/null
+++ b/sysdeps/tile/dl-machine.h
@@ -0,0 +1,1213 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+   Based on work contributed by by Carl Pederson & Martin Schwidefsky.
+
+   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 dl_machine_h
+#define dl_machine_h
+
+#ifdef __tilegx__
+#define ELF_MACHINE_NAME "tilegx"
+#else
+#define ELF_MACHINE_NAME "tilepro"
+#endif
+
+#include <sys/param.h>
+#include <string.h>
+#include <link.h>
+#include <bits/wordsize.h>
+#include <arch/icache.h>
+
+#include <tile-desc.h>
+
+/* Return nonzero iff ELF header is compatible with the running host.  */
+static inline int
+elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
+{
+#if defined __tilegx__
+  if (ehdr->e_machine != EM_TILEGX)
+    return 0;
+# if __WORDSIZE == 32
+  return (ehdr->e_ident[EI_CLASS] == ELFCLASS32);
+# else
+  return (ehdr->e_ident[EI_CLASS] == ELFCLASS64);
+# endif
+#elif defined __tilepro__
+  return ehdr->e_machine == EM_TILEPRO;
+#else
+# error "Unknown tile architecture."
+#endif
+}
+
+
+/* Return the link-time address of _DYNAMIC.  Conveniently, this is the
+   first element of the GOT.  This must be inlined in a function which
+   uses global data.  */
+
+static inline ElfW(Addr)
+elf_machine_dynamic (void)
+{
+  ElfW(Addr) *got;
+
+#ifdef __tilegx__
+  ElfW(Addr) tmp;
+  asm( "	{ lnk %0; moveli %1, hw2_last(_GLOBAL_OFFSET_TABLE_ - 1f) }\n"
+       "1:	shl16insli %1, %1, hw1(_GLOBAL_OFFSET_TABLE_ - 1b)\n"
+       "	shl16insli %1, %1, hw0(_GLOBAL_OFFSET_TABLE_ - 1b)\n"
+       "        add %0, %0, %1"
+       : "=r" (got), "=r" (tmp));
+#else
+  asm( "	lnk %0\n"
+       "1:	addli %0, %0, lo16(_GLOBAL_OFFSET_TABLE_ - 1b)\n"
+       "	auli %0, %0, ha16(_GLOBAL_OFFSET_TABLE_ - 1b)"
+       : "=r" (got));
+#endif
+
+  return *got;
+}
+
+
+/* Return the run-time load address of the shared object.  */
+static inline ElfW(Addr)
+elf_machine_load_address (void)
+{
+  ElfW(Addr) *got;
+  ElfW(Addr) dynamic;
+
+#ifdef __tilegx__
+  ElfW(Addr) tmp;
+  asm( "	lnk %2\n"
+       "1:	{\n"
+       "	 moveli %0, hw2_last(_GLOBAL_OFFSET_TABLE_ - 1b)\n"
+       "	 moveli %1, hw2_last(_DYNAMIC - 1b)\n"
+       "	}\n"
+       "	{\n"
+       "	 shl16insli %0, %0, hw1(_GLOBAL_OFFSET_TABLE_ - 1b)\n"
+       "	 shl16insli %1, %1, hw1(_DYNAMIC - 1b)\n"
+       "	}\n"
+       "	{\n"
+       "	 shl16insli %0, %0, hw0(_GLOBAL_OFFSET_TABLE_ - 1b)\n"
+       "	 shl16insli %1, %1, hw0(_DYNAMIC - 1b)\n"
+       "	}\n"
+       "	{\n"
+       "	 add %0, %0, %2\n"
+       "	 add %1, %1, %2\n"
+       "	}"
+       : "=r" (got), "=r" (dynamic), "=r" (tmp));
+#else
+  asm( "	lnk %0\n"
+       "1:	{\n"
+       "	 addli %0, %0, lo16(_GLOBAL_OFFSET_TABLE_ - 1b)\n"
+       "	 addli %1, %0, lo16(_DYNAMIC - 1b)\n"
+       "	}\n"
+       "	{\n"
+       "	 auli %0, %0, ha16(_GLOBAL_OFFSET_TABLE_ - 1b)\n"
+       "	 auli %1, %1, ha16(_DYNAMIC - 1b)\n"
+       "	}\n"
+       : "=r" (got), "=r" (dynamic));
+#endif
+
+  return dynamic - *got;
+}
+
+/* Flush some range of the instruction cache.  If invoked prior to
+   actually setting dl_pagesize, we conservatively use 4KB, which
+   is the smallest page size we could plausibly be running with.  */
+static inline void
+_dl_flush_icache (const void *addr, unsigned long size)
+{
+  invalidate_icache (addr, size, GLRO(dl_pagesize) ? : 4096);
+}
+
+/* Set up the loaded object described by L so its unrelocated PLT
+   entries will jump to the on-demand fixup code in dl-runtime.c.
+   We use the first few PLT slots to trampoline to the resolve function.
+
+   The TILEPro header looks like:
+  
+       {
+        moveli r28, lo16(MODULE)
+        rli r29, r29, 16
+       }
+       {
+        auli r28, r28, ha16(MODULE)
+        j _dl_runtime_{resolve,profile}
+       }
+
+    (We use rli rather than shri so that we could theoretically support
+    more than 64K PLT entries by having a fake PLT entry every 64K
+    entries that sets the low 16 bits of r29 and then jumps here.
+    With rotate, no bits are lost.)
+
+    The 32-bit TILE-Gx header looks like:
+
+       {
+        moveli r28, hw1_last(MODULE)
+        moveli r27, hw1_last(_dl_runtime_{resolve,profile})
+       }
+       {
+        shl16insli r28, r28, hw0(MODULE)
+        shl16insli r27, r27, hw0(_dl_runtime_{resolve,profile})
+       }
+       {
+        jr r27
+        info 10  ## SP not offset, return PC in LR
+       }
+
+    The 64-bit TILE-Gx header is the same but extends to 48-bit VAs:
+
+       {
+        moveli r28, hw2_last(MODULE)
+        moveli r27, hw2_last(_dl_runtime_{resolve,profile})
+       }
+       {
+        shl16insli r28, r28, hw1(MODULE)
+        shl16insli r27, r27, hw1(_dl_runtime_{resolve,profile})
+       }
+       {
+        shl16insli r28, r28, hw0(MODULE)
+        shl16insli r27, r27, hw0(_dl_runtime_{resolve,profile})
+       }
+       {
+        jr r27
+        info 10  ## SP not offset, return PC in LR
+       }
+*/
+
+static inline int __attribute__ ((unused))
+old_elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
+{
+  extern void _dl_runtime_resolve (ElfW(Word));
+  extern void _dl_runtime_profile (ElfW(Word));
+  ElfW(Addr) rfunc;
+  tile_bundle_bits *plt_base, *plt;
+  plt = plt_base = (tile_bundle_bits *) D_PTR (l, l_info[DT_PLTGOT]);
+
+  if (__builtin_expect (profile, 0))
+    {
+      rfunc = (ElfW(Addr)) &_dl_runtime_profile;
+      
+      if (GLRO(dl_profile) != NULL
+          && _dl_name_match_p (GLRO(dl_profile), l))
+        GL(dl_profile_map) = l;
+    }
+  else
+    {
+      rfunc = (ElfW(Addr)) &_dl_runtime_resolve;
+    }
+
+#if !defined __tilegx__
+  *plt++ = (0x400183aea0000fdcLL
+            | create_Imm16_X0 ((unsigned long) (l)));
+  *plt++ = (0x500000003000071cLL
+            | create_Imm16_X0 (((unsigned long) (l) + 0x8000) >> 16)
+            | create_JOffLong_X1 ((rfunc - (Elf32_Addr) &plt_base[1]) >> 3));
+#elif __WORDSIZE == 32
+  *plt++ = (0x000007ed90000fdcLL
+            | create_Imm16_X0 ((int) ((unsigned long) l >> 16))
+            | create_Imm16_X1 ((int) (rfunc >> 16)));
+  *plt++ = (0x3800036df000071cLL
+            | create_Imm16_X0 ((int) (unsigned long) l)
+            | create_Imm16_X1 ((int) rfunc));
+  *plt++ = 0x286a73604030afffLL;
+#else
+  *plt++ = (0x000007ed90000fdcLL
+            | create_Imm16_X0 ((int) ((unsigned long) l >> 32))
+            | create_Imm16_X1 ((int) (rfunc >> 32)));
+  *plt++ = (0x3800036df000071cLL
+            | create_Imm16_X0 ((int) ((unsigned long) l >> 16))
+            | create_Imm16_X1 ((int) (rfunc >> 16)));
+  *plt++ = (0x3800036df000071cLL
+            | create_Imm16_X0 ((int) (unsigned long) l)
+            | create_Imm16_X1 ((int) rfunc));
+  *plt++ = 0x286a73604030afffLL;
+#endif
+
+  _dl_flush_icache (plt_base, (char *) plt - (char *) plt_base);
+
+  return lazy;
+}
+
+/* In the old model, pltgot points to the .plt, which is initally 0,
+   then part of a trampoline after elf_machine_runtime_setup().  In
+   the new model, pltgot points to .got.plt, whose first entry is
+   initially -1, and the address of the struct link_map after
+   elf_machine_runtime_setup ().  So we can test for which model of
+   plt we're running by examining the contents of pltgot.  */
+static inline bool
+tile_old_plt_model (struct link_map *l, ElfW(Addr) *pltgot)
+{
+  return *pltgot != (ElfW(Addr)) l && *pltgot != (ElfW(Addr)) -1;
+}
+
+/* Set up the loaded object described by L so its unrelocated PLT
+   entries will jump to the on-demand fixup code in dl-runtime.c.  */
+
+static inline int __attribute__ ((unused))
+elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
+{
+  ElfW(Addr) *gotplt;
+  extern void _dl_runtime_resolve (ElfW(Word));
+  extern void _dl_runtime_profile (ElfW(Word));
+
+  if (l->l_info[DT_JMPREL] && lazy)
+    {
+      gotplt = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
+
+      if (__builtin_expect (*gotplt != (ElfW(Addr)) -1, 0))
+        return old_elf_machine_runtime_setup (l, lazy, profile);
+
+      /* The GOT entries for functions in the PLT have not yet been filled
+         in.  Their initial contents will arrange when called to put in
+         registers an offset into the .rel.plt section, and gotplt[0], then
+         jump to gotplt[1].  */
+
+      /* Identify this shared object.  */
+      gotplt[0] = (ElfW(Addr)) l;
+
+      /* The gotplt[1] entry contains the address of a function which gets
+         called to get the address of a so far unresolved function and jump
+         to it.  The profiling extension of the dynamic linker allows to
+         intercept the calls to collect information.  In this case we don't
+         store the address in the GOTPLT so that all future calls also end
+         in this function.  */
+      if (__builtin_expect (profile, 0))
+        {
+          gotplt[1] = (ElfW(Addr)) &_dl_runtime_profile;
+
+          if (GLRO(dl_profile) != NULL
+              && _dl_name_match_p (GLRO(dl_profile), l))
+            /* This is the object we are looking for.  Say that we really
+               want profiling and the timers are started.  */
+            GL(dl_profile_map) = l;
+        }
+      else
+        /* This function will get called to fix up the GOTPLT entry
+           indicated by the offset on the stack, and then jump to the
+           resolved address.  */
+        gotplt[1] = (ElfW(Addr)) &_dl_runtime_resolve;
+    }
+
+  return lazy;
+}
+
+#if __WORDSIZE == 32
+/* Mask identifying addresses reserved for the user program,
+   where the dynamic linker should not map anything.  */
+#define ELF_MACHINE_USER_ADDRESS_MASK   0xf8000000UL
+#endif
+
+/* Initial entry point code for the dynamic linker.
+   The C function `_dl_start' is the real entry point;
+   its return value is the user program's entry point.  */
+
+#define RTLD_START asm (".globl _dl_start");
+
+#ifndef RTLD_START_SPECIAL_INIT
+#define RTLD_START_SPECIAL_INIT /* nothing */
+#endif
+
+/* Wrap a generic Tilera relocation type. */
+#ifdef __tilegx__
+#define R_TILE(x) R_TILEGX_##x
+#define __R_TILE_TLS(x,c) R_TILEGX_TLS_##x##c
+#define _R_TILE_TLS(x,c) __R_TILE_TLS(x,c)
+#define R_TILE_TLS(x) _R_TILE_TLS(x,__ELF_NATIVE_CLASS)
+#else
+#define R_TILE(x) R_TILEPRO_##x
+#define R_TILE_TLS(x) R_TILEPRO_TLS_##x##32
+#endif
+
+/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry or
+   TLS variable, so undefined references should not be allowed to
+   define the value.
+   ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one
+   of the main executable's symbols, as for a COPY reloc.  */
+#define elf_machine_type_class(type) \
+  ((((type) == R_TILE(JMP_SLOT) || (type) == R_TILE_TLS(DTPMOD)		      \
+     || (type) == R_TILE_TLS(DTPOFF) || (type) == R_TILE_TLS(TPOFF))	      \
+    * ELF_RTYPE_CLASS_PLT)						      \
+   | (((type) == R_TILE(COPY)) * ELF_RTYPE_CLASS_COPY))
+
+/* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
+#define ELF_MACHINE_JMP_SLOT R_TILE(JMP_SLOT)
+
+/* TILE never uses Elf32_Rel relocations.  */
+#define ELF_MACHINE_NO_REL 1
+
+/* TILE overlaps DT_RELA and DT_PLTREL.  */
+#define ELF_MACHINE_PLTREL_OVERLAP 1
+
+/* We define an initialization functions.  This is called very early in
+   _dl_sysdep_start.  */
+#define DL_PLATFORM_INIT dl_platform_init ()
+
+static inline void __attribute__ ((unused))
+dl_platform_init (void)
+{
+  if (GLRO(dl_platform) != NULL && *GLRO(dl_platform) == '\0')
+    /* Avoid an empty string which would disturb us.  */
+    GLRO(dl_platform) = NULL;
+}
+
+static inline ElfW(Addr)
+old_elf_machine_fixup_plt (struct link_map *map, lookup_t t,
+                           const ElfW(Rela) *reloc,
+                           ElfW(Addr) *reloc_addr, ElfW(Addr) value)
+{
+#ifndef __tilegx__
+  /* Updating a PLT entry atomically on TILEPro is very tricky, since
+     bundles are 8 bytes, we can only atomically write four bytes at a
+     time, and jump instructions span both words of the bundle.
+
+     We carefully update one word and then the other in an order
+     such that the intermediate values seen by any reader are
+     all valid code sequences. It is fortuitous that such a sequence
+     exists.
+
+     Initially each PLT entry looks like this:
+
+     { auli r29, zero, MY_PLT_INDEX ; bzt zero, _PROCEDURE_LINKAGE_TABLE_ }
+
+     Our goal is to change the 'bzt' into 'j target'.
+
+     First we modify the low word to change just the part of the 'j' offset
+     that "spills over" into the low word of the bundle. This turns out
+     to only be one bit. Fortuitously this bit is the same bit that
+     distinguishes 'bzt' from 'bz'. So the above bundle either stays the
+     same or the 'bzt' changes to 'bz'. So temporarily we have one of
+     these bundles:
+
+     { auli r29, zero, MY_PLT_INDEX ; bz  zero, _PROCEDURE_LINKAGE_TABLE_ }
+     { auli r29, zero, MY_PLT_INDEX ; bzt zero, _PROCEDURE_LINKAGE_TABLE_ }
+
+     These are semantically identical, so if some other thread executes
+     this bundle partway through its being updated, nothing bad happens.
+
+     Finally we write the second word of the bundle, which includes the
+     'j' opcode and all but one of the jump offset bits. That yields
+     the final result:
+
+     { auli r29, zero, MY_PLT_INDEX ; j target }
+
+     The auli is now unnecessary but we don't bother replacing it with a fnop
+     (although we could).  */
+
+  long offset = (const char *) value - (const char *) reloc_addr;
+  tile_bundle_bits new_bundle =
+    (((0x5000000000000000LL | ((unsigned long *) reloc_addr)[0])
+      & ~create_JOffLong_X1 (-1))
+     | create_JOffLong_X1 (offset >> 3));
+
+  /* Write out the low word. The only thing we are changing here from
+     the default entry is one bit of jump offset, which might change
+     the bzt into a bz, but that's harmless.  */
+  ((volatile unsigned long *) reloc_addr)[0] = (unsigned long) new_bundle;
+
+  /* Guarantee that the first store is definitely visible before the
+     second store in case some other thread starts executing this PLT
+     entry right now. This may be unnecessary since the stores should
+     be ordered, but there's little cost to paranoia here.  */
+  __insn_mf ();
+
+  /* Now change the bzt/bz into a j, and set the remaining jump offset
+     bits (which are stored in this word). */
+  ((volatile unsigned long *) reloc_addr)[1] =
+    (unsigned long) (new_bundle >> 32);
+
+  _dl_flush_icache (reloc_addr, sizeof (tile_bundle_bits));
+#else
+  /* Updating a PLT entry on TILE-Gx is easier, since we can write an
+     8-byte instruction in a single instruction.  Initially each PLT
+     entry looks like this:
+
+     { shl16insli r29, zero, MY_PLT_INDEX ; j _PROCEDURE_LINKAGE_TABLE_ }
+     { jr r29 }
+     { jr r29 }
+     { jr r29 }
+
+     Our goal is to change this into a jump to 'target'.
+
+     If 'target' is within the relative offset of a 'j' instruction,
+     we just use that, for efficiency.  Otherwise we spend up to three
+     bundles materializing the address in r29 and then 'jr r29'.  */
+
+  /* Even in 32-bit mode the PC is actually 64 bits, so we cannot rely
+     on it "wrapping around". So we always check for jump distance
+     using the offset as 64-bit pointers.  We right shift by 3 because
+     jump offsets are always in terms of bundles. */
+  long long offset =
+    ((long long) (intptr_t) value -
+     ((long long) (intptr_t) reloc_addr)) >> 3;
+
+  /* WARNING: Other threads may be modifying or executing the code
+     we are self-modifying, so we need to be very careful about the
+     order in which we self-modify. */
+  tile_bundle_bits *bundles = (tile_bundle_bits *) reloc_addr;
+  tile_bundle_bits first_bundle;
+
+  /* Determine size of jump offset. */
+  int shift = __builtin_clzll (get_JumpOff_X1 (create_JumpOff_X1 (~0)));
+
+  /* Sign extend the jump offset we stored into the bundle, so
+     we can see if any information was lost via overflow. */
+  long long f = get_JumpOff_X1 (create_JumpOff_X1 (offset));
+  f = (f << shift) >> shift;
+
+  if (offset == f)
+    {
+      /* { j value } */
+      first_bundle = 0x2400000051483000ULL | create_JumpOff_X1 (offset);
+    }
+  else
+    {
+      ElfW(Addr) t = (unsigned long) value;
+
+#if __SIZEOF_POINTER__ == 4
+      /* { moveli r29, hw1_last(value) } */
+      first_bundle = 0x000007eed1483000ULL | create_Imm16_X1 (t >> 16);
+
+      /* { shl16insli r29, r29, hw0(value) } */
+      bundles[1] = 0x380003aed1483000ULL | create_Imm16_X1 (t);
+#else
+      /* { moveli r29, hw2_last(value) } */
+      first_bundle = 0x000007eed1483000ULL | create_Imm16_X1 (t >> 32);
+
+      /* { shl16insli r29, r29, hw1(value) } */
+      bundles[1] = 0x380003aed1483000ULL | create_Imm16_X1 (t >> 16);
+
+      /* { shl16insli r29, r29, hw0(value) } */
+      bundles[2] = 0x380003aed1483000ULL | create_Imm16_X1 (t);
+#endif
+
+      /* Make sure that every other thread can see the preceding
+         bundles before we modify the first one and make them visible. */
+      _dl_flush_icache (&bundles[1], 2 * sizeof (bundles[0]));
+    }
+
+  /* Finally, record the first bundle. */
+  bundles[0] = first_bundle;
+  _dl_flush_icache (&bundles[0], sizeof (bundles[0]));
+#endif
+
+  return value;
+}
+
+static inline ElfW(Addr)
+elf_machine_fixup_plt (struct link_map *map, lookup_t t,
+        	       const ElfW(Rela) *reloc,
+        	       ElfW(Addr) *reloc_addr, ElfW(Addr) value)
+{
+  ElfW(Addr) *gotplt = (ElfW(Addr) *) D_PTR (map, l_info[DT_PLTGOT]);
+
+  if (__builtin_expect (tile_old_plt_model (map, gotplt), 0))
+    return old_elf_machine_fixup_plt (map, t, reloc, reloc_addr, value);
+
+  return *reloc_addr = value;
+}
+
+/* Return the final value of a plt relocation.  */
+static inline ElfW(Addr)
+elf_machine_plt_value (struct link_map *map, const ElfW(Rela) *reloc,
+        	       ElfW(Addr) value)
+{
+  return value;
+}
+
+/* Support notifying the simulator about new objects. */
+void internal_function _dl_arch_map_object (struct link_map *l);
+#define _dl_arch_map_object _dl_arch_map_object
+
+/* Names of the architecture-specific auditing callback functions.  */
+#define ARCH_LA_PLTENTER tile_gnu_pltenter
+#define ARCH_LA_PLTEXIT tile_gnu_pltexit
+
+#endif /* !dl_machine_h */
+
+
+#ifdef RESOLVE_MAP
+
+struct reloc_howto
+{
+  /* Right shift operand by this number of bits. */
+  unsigned char right_shift;
+
+#ifdef __tilegx__
+  /* If nonzero, this is updating a code bundle. */
+  unsigned char is_bundle_update;
+#else
+  /* If nonzero, add 0x8000 to the value. */
+  unsigned char add_0x8000;
+#endif
+
+  /* If nonzero, subtract the containing address from the address. */
+  unsigned char is_pcrel;
+
+  /* Size in bytes, or 0 if this table entry should be ignored. */
+  unsigned char byte_size;
+};
+
+/* Relocation information. Cannot contain create_* function pointers
+   because then the table would not be position-independent. */
+static const struct reloc_howto howto[] =
+{
+#ifdef __tilegx__
+
+# if __WORDSIZE == 32
+  /* The GX -m32 loader only handles 32-bit types, so it will be confused
+     by shifts larger than that.  We convert them to just sign-extend;
+     they usually indicate a program bug or missed optimization, but we
+     have to handle them correctly anyway.  */
+#  define S32 31
+#  define S48 31
+# else
+#  define S32 32
+#  define S48 48
+# endif
+
+  /* R_TILEGX_NONE */                    {   0, 0, 0, 0 },
+  /* R_TILEGX_64 */                      {   0, 0, 0, 8 },
+  /* R_TILEGX_32 */                      {   0, 0, 0, 4 },
+  /* R_TILEGX_16 */                      {   0, 0, 0, 2 },
+  /* R_TILEGX_8 */                       {   0, 0, 0, 1 },
+  /* R_TILEGX_64_PCREL */                {   0, 0, 1, 8 },
+  /* R_TILEGX_32_PCREL */                {   0, 0, 1, 4 },
+  /* R_TILEGX_16_PCREL */                {   0, 0, 1, 2 },
+  /* R_TILEGX_8_PCREL */                 {   0, 0, 1, 1 },
+  /* R_TILEGX_HW0 */                     {   0, 0, 0, 0 },
+  /* R_TILEGX_HW1 */                     {  16, 0, 0, 0 },
+  /* R_TILEGX_HW2 */                     { S32, 0, 0, 0 },
+  /* R_TILEGX_HW3 */                     { S48, 0, 0, 0 },
+  /* R_TILEGX_HW0_LAST */                {   0, 0, 0, 0 },
+  /* R_TILEGX_HW1_LAST */                {  16, 0, 0, 0 },
+  /* R_TILEGX_HW2_LAST */                { S32, 0, 0, 0 },
+  /* R_TILEGX_COPY */                    {   0, 0, 0, 0 },
+  /* R_TILEGX_GLOB_DAT */                {   0, 0, 0, 8 },
+  /* R_TILEGX_JMP_SLOT */                {   0, 0, 0, 0 },
+  /* R_TILEGX_RELATIVE */                {   0, 0, 0, 0 },
+  /* R_TILEGX_BROFF_X1 */                {   3, 1, 1, 8 },
+  /* R_TILEGX_JUMPOFF_X1 */              {   3, 1, 1, 8 },
+  /* R_TILEGX_JUMPOFF_X1_PLT */          {   3, 1, 1, 8 },
+  /* R_TILEGX_IMM8_X0 */                 {   0, 1, 0, 8 },
+  /* R_TILEGX_IMM8_Y0 */                 {   0, 1, 0, 8 },
+  /* R_TILEGX_IMM8_X1 */                 {   0, 1, 0, 8 },
+  /* R_TILEGX_IMM8_Y1 */                 {   0, 1, 0, 8 },
+  /* R_TILEGX_DEST_IMM8_X1 */            {   0, 1, 0, 8 },
+  /* R_TILEGX_MT_IMM14_X1 */             {   0, 1, 0, 8 },
+  /* R_TILEGX_MF_IMM14_X1 */             {   0, 1, 0, 8 },
+  /* R_TILEGX_MMSTART_X0 */              {   0, 1, 0, 8 },
+  /* R_TILEGX_MMEND_X0 */                {   0, 1, 0, 8 },
+  /* R_TILEGX_SHAMT_X0 */                {   0, 1, 0, 8 },
+  /* R_TILEGX_SHAMT_X1 */                {   0, 1, 0, 8 },
+  /* R_TILEGX_SHAMT_Y0 */                {   0, 1, 0, 8 },
+  /* R_TILEGX_SHAMT_Y1 */                {   0, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW0 */            {   0, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW0 */            {   0, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW1 */            {  16, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW1 */            {  16, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW2 */            { S32, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW2 */            { S32, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW3 */            { S48, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW3 */            { S48, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW0_LAST */       {   0, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW0_LAST */       {   0, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW1_LAST */       {  16, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW1_LAST */       {  16, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW2_LAST */       { S32, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW2_LAST */       { S32, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW0_PCREL */      {   0, 1, 1, 8 },
+  /* R_TILEGX_IMM16_X1_HW0_PCREL */      {   0, 1, 1, 8 },
+  /* R_TILEGX_IMM16_X0_HW1_PCREL */      {  16, 1, 1, 8 },
+  /* R_TILEGX_IMM16_X1_HW1_PCREL */      {  16, 1, 1, 8 },
+  /* R_TILEGX_IMM16_X0_HW2_PCREL */      { S32, 1, 1, 8 },
+  /* R_TILEGX_IMM16_X1_HW2_PCREL */      { S32, 1, 1, 8 },
+  /* R_TILEGX_IMM16_X0_HW3_PCREL */      { S48, 1, 1, 8 },
+  /* R_TILEGX_IMM16_X1_HW3_PCREL */      { S48, 1, 1, 8 },
+  /* R_TILEGX_IMM16_X0_HW0_LAST_PCREL */ {   0, 1, 1, 8 },
+  /* R_TILEGX_IMM16_X1_HW0_LAST_PCREL */ {   0, 1, 1, 8 },
+  /* R_TILEGX_IMM16_X0_HW1_LAST_PCREL */ {  16, 1, 1, 8 },
+  /* R_TILEGX_IMM16_X1_HW1_LAST_PCREL */ {  16, 1, 1, 8 },
+  /* R_TILEGX_IMM16_X0_HW2_LAST_PCREL */ { S32, 1, 1, 8 },
+  /* R_TILEGX_IMM16_X1_HW2_LAST_PCREL */ { S32, 1, 1, 8 },
+  /* R_TILEGX_IMM16_X0_HW0_GOT */        {   0, 1, 0, 0 },
+  /* R_TILEGX_IMM16_X1_HW0_GOT */        {   0, 1, 0, 0 },
+  /* R_TILEGX_IMM16_X0_HW1_GOT */        {  16, 1, 0, 0 },
+  /* R_TILEGX_IMM16_X1_HW1_GOT */        {  16, 1, 0, 0 },
+  /* R_TILEGX_IMM16_X0_HW2_GOT */        { S32, 1, 0, 0 },
+  /* R_TILEGX_IMM16_X1_HW2_GOT */        { S32, 1, 0, 0 },
+  /* R_TILEGX_IMM16_X0_HW3_GOT */        { S48, 1, 0, 0 },
+  /* R_TILEGX_IMM16_X1_HW3_GOT */        { S48, 1, 0, 0 },
+  /* R_TILEGX_IMM16_X0_HW0_LAST_GOT */   {   0, 1, 0, 0 },
+  /* R_TILEGX_IMM16_X1_HW0_LAST_GOT */   {   0, 1, 0, 0 },
+  /* R_TILEGX_IMM16_X0_HW1_LAST_GOT */   {  16, 1, 0, 0 },
+  /* R_TILEGX_IMM16_X1_HW1_LAST_GOT */   {  16, 1, 0, 0 },
+  /* R_TILEGX_IMM16_X0_HW2_LAST_GOT */   { S32, 1, 0, 0 },
+  /* R_TILEGX_IMM16_X1_HW2_LAST_GOT */   { S32, 1, 0, 0 },
+  /* R_TILEGX_IMM16_X0_HW0_TLS_GD */     {   0, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW0_TLS_GD */     {   0, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW1_TLS_GD */     {  16, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW1_TLS_GD */     {  16, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW2_TLS_GD */     { S32, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW2_TLS_GD */     { S32, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW3_TLS_GD */     { S48, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW3_TLS_GD */     { S48, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD */{   0, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD */{   0, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD */{  16, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD */{  16, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW2_LAST_TLS_GD */{ S32, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW2_LAST_TLS_GD */{ S32, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW0_TLS_IE */     {   0, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW0_TLS_IE */     {   0, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW1_TLS_IE */     {  16, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW1_TLS_IE */     {  16, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW2_TLS_IE */     { S32, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW2_TLS_IE */     { S32, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW3_TLS_IE */     { S48, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW3_TLS_IE */     { S48, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE */{   0, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE */{   0, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE */{  16, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE */{  16, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X0_HW2_LAST_TLS_IE */{ S32, 1, 0, 8 },
+  /* R_TILEGX_IMM16_X1_HW2_LAST_TLS_IE */{ S32, 1, 0, 8 },
+  /* R_TILEGX_TLS_DTPMOD64 */            {   0, 0, 0, 0 },
+  /* R_TILEGX_TLS_DTPOFF64 */            {   0, 0, 0, 0 },
+  /* R_TILEGX_TLS_TPOFF64 */             {   0, 0, 0, 0 },
+  /* R_TILEGX_TLS_DTPMOD32 */            {   0, 0, 0, 0 },
+  /* R_TILEGX_TLS_DTPOFF32 */            {   0, 0, 0, 0 },
+  /* R_TILEGX_TLS_TPOFF32 */             {   0, 0, 0, 0 }
+#else
+  /* R_TILEPRO_NONE */                   { 0,  0, 0, 0 },
+  /* R_TILEPRO_32 */                     { 0,  0, 0, 4 },
+  /* R_TILEPRO_16 */                     { 0,  0, 0, 2 },
+  /* R_TILEPRO_8 */                      { 0,  0, 0, 1 },
+  /* R_TILEPRO_32_PCREL */               { 0,  0, 1, 4 },
+  /* R_TILEPRO_16_PCREL */               { 0,  0, 1, 2 },
+  /* R_TILEPRO_8_PCREL */                { 0,  0, 1, 1 },
+  /* R_TILEPRO_LO16 */                   { 0,  0, 0, 2 },
+  /* R_TILEPRO_HI16 */                   { 16, 0, 0, 2 },
+  /* R_TILEPRO_HA16 */                   { 16, 1, 0, 2 },
+  /* R_TILEPRO_COPY */                   { 0,  0, 0, 0 },
+  /* R_TILEPRO_GLOB_DAT */               { 0,  0, 0, 4 },
+  /* R_TILEPRO_JMP_SLOT */               { 0,  0, 0, 0 },
+  /* R_TILEPRO_RELATIVE */               { 0,  0, 0, 0 },
+  /* R_TILEPRO_BROFF_X1 */               { 3,  0, 1, 8 },
+  /* R_TILEPRO_JOFFLONG_X1 */            { 3,  0, 1, 8 },
+  /* R_TILEPRO_JOFFLONG_X1_PLT */        { 3,  0, 1, 8 },
+  /* R_TILEPRO_IMM8_X0 */                { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM8_Y0 */                { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM8_X1 */                { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM8_Y1 */                { 0,  0, 0, 8 },
+  /* R_TILEPRO_MT_IMM15_X1 */            { 0,  0, 0, 8 },
+  /* R_TILEPRO_MF_IMM15_X1 */            { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM16_X0 */               { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM16_X1 */               { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM16_X0_LO */            { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM16_X1_LO */            { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM16_X0_HI */            { 16, 0, 0, 8 },
+  /* R_TILEPRO_IMM16_X1_HI */            { 16, 0, 0, 8 },
+  /* R_TILEPRO_IMM16_X0_HA */            { 16, 1, 0, 8 },
+  /* R_TILEPRO_IMM16_X1_HA */            { 16, 1, 0, 8 },
+  /* R_TILEPRO_IMM16_X0_PCREL */         { 0,  0, 1, 8 },
+  /* R_TILEPRO_IMM16_X1_PCREL */         { 0,  0, 1, 8 },
+  /* R_TILEPRO_IMM16_X0_LO_PCREL */      { 0,  0, 1, 8 },
+  /* R_TILEPRO_IMM16_X1_LO_PCREL */      { 0,  0, 1, 8 },
+  /* R_TILEPRO_IMM16_X0_HI_PCREL */      { 16, 0, 1, 8 },
+  /* R_TILEPRO_IMM16_X1_HI_PCREL */      { 16, 0, 1, 8 },
+  /* R_TILEPRO_IMM16_X0_HA_PCREL */      { 16, 1, 1, 8 },
+  /* R_TILEPRO_IMM16_X1_HA_PCREL */      { 16, 1, 1, 8 },
+  /* R_TILEPRO_IMM16_X0_GOT */           { 0,  0, 0, 0 },
+  /* R_TILEPRO_IMM16_X1_GOT */           { 0,  0, 0, 0 },
+  /* R_TILEPRO_IMM16_X0_GOT_LO */        { 0,  0, 0, 0 },
+  /* R_TILEPRO_IMM16_X1_GOT_LO */        { 0,  0, 0, 0 },
+  /* R_TILEPRO_IMM16_X0_GOT_HI */        { 0,  0, 0, 0 },
+  /* R_TILEPRO_IMM16_X1_GOT_HI */        { 0,  0, 0, 0 },
+  /* R_TILEPRO_IMM16_X0_GOT_HA */        { 0,  0, 0, 0 },
+  /* R_TILEPRO_IMM16_X1_GOT_HA */        { 0,  0, 0, 0 },
+  /* R_TILEPRO_MMSTART_X0 */             { 0,  0, 0, 8 },
+  /* R_TILEPRO_MMEND_X0 */               { 0,  0, 0, 8 },
+  /* R_TILEPRO_MMSTART_X1 */             { 0,  0, 0, 8 },
+  /* R_TILEPRO_MMEND_X1 */               { 0,  0, 0, 8 },
+  /* R_TILEPRO_SHAMT_X0 */               { 0,  0, 0, 8 },
+  /* R_TILEPRO_SHAMT_X1 */               { 0,  0, 0, 8 },
+  /* R_TILEPRO_SHAMT_Y0 */               { 0,  0, 0, 8 },
+  /* R_TILEPRO_SHAMT_Y1 */               { 0,  0, 0, 8 },
+  /* R_TILEPRO_SN_BROFF */               { 0,  0, 0, 0 },
+  /* R_TILEPRO_SN_IMM8 */                { 0,  0, 0, 0 },
+  /* R_TILEPRO_SN_UIMM8 */               { 0,  0, 0, 0 },
+  /* R_TILEPRO_SN_BYTE0 */               { 0,  0, 0, 0 },
+  /* R_TILEPRO_SN_BYTE1 */               { 0,  0, 0, 0 },
+  /* R_TILEPRO_SN_BYTE2 */               { 0,  0, 0, 0 },
+  /* R_TILEPRO_SN_BYTE3 */               { 0,  0, 0, 0 },
+  /* R_TILEPRO_SN_SPCREL0 */             { 0,  0, 0, 0 },
+  /* R_TILEPRO_SN_SPCREL1 */             { 0,  0, 0, 0 },
+  /* R_TILEPRO_SN_SPCREL2 */             { 0,  0, 0, 0 },
+  /* R_TILEPRO_SN_SPCREL3 */             { 0,  0, 0, 0 },
+  /* R_TILEPRO_IMM16_X0_TLS_GD */        { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM16_X1_TLS_GD */        { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM16_X0_TLS_GD_LO */     { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM16_X1_TLS_GD_LO */     { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM16_X0_TLS_GD_HI */     { 16, 0, 0, 8 },
+  /* R_TILEPRO_IMM16_X1_TLS_GD_HI */     { 16, 0, 0, 8 },
+  /* R_TILEPRO_IMM16_X0_TLS_GD_HA */     { 16, 1, 0, 8 },
+  /* R_TILEPRO_IMM16_X1_TLS_GD_HA */     { 16, 1, 0, 8 },
+  /* R_TILEPRO_IMM16_X0_TLS_IE */        { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM16_X1_TLS_IE */        { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM16_X0_TLS_IE_LO */     { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM16_X1_TLS_IE_LO */     { 0,  0, 0, 8 },
+  /* R_TILEPRO_IMM16_X0_TLS_IE_HI */     { 16, 0, 0, 8 },
+  /* R_TILEPRO_IMM16_X1_TLS_IE_HI */     { 16, 0, 0, 8 },
+  /* R_TILEPRO_IMM16_X0_TLS_IE_HA */     { 16, 1, 0, 8 },
+  /* R_TILEPRO_IMM16_X1_TLS_IE_HA */     { 16, 1, 0, 8 },
+  /* R_TILEPRO_TLS_DTPMOD32 */           { 0,  0, 0, 0 },
+  /* R_TILEPRO_TLS_DTPOFF32 */           { 0,  0, 0, 0 },
+  /* R_TILEPRO_TLS_TPOFF32 */            { 0,  0, 0, 0 },
+#endif
+};
+
+#if __ELF_NATIVE_CLASS == 32
+#define ELFW_R_TYPE	ELF32_R_TYPE
+#define ELFW_ST_TYPE	ELF32_ST_TYPE
+#else
+#define ELFW_R_TYPE	ELF64_R_TYPE
+#define ELFW_ST_TYPE	ELF64_ST_TYPE
+#endif
+
+/* Perform the relocation specified by RELOC and SYM (which is fully resolved).
+   MAP is the object containing the reloc.  */
+
+auto inline void __attribute__ ((always_inline))
+elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
+        	  const ElfW(Sym) *sym, const struct r_found_version *version,
+        	  void *const reloc_addr_arg, int skip_ifunc)
+{
+  ElfW(Addr) *const reloc_addr = reloc_addr_arg;
+  const unsigned int r_type = ELFW_R_TYPE (reloc->r_info);
+
+#if !defined RTLD_BOOTSTRAP || !defined HAVE_Z_COMBRELOC
+  if (__builtin_expect (r_type == R_TILE(RELATIVE), 0))
+    {
+# if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC
+      /* This is defined in rtld.c, but nowhere in the static libc.a;
+         make the reference weak so static programs can still link.
+         This declaration cannot be done when compiling rtld.c
+         (i.e. #ifdef RTLD_BOOTSTRAP) because rtld.c contains the
+         common defn for _dl_rtld_map, which is incompatible with a
+         weak decl in the same file.  */
+#  ifndef SHARED
+      weak_extern (GL(dl_rtld_map));
+#  endif
+      if (map != &GL(dl_rtld_map)) /* Already done in rtld itself.  */
+# endif
+        *reloc_addr = map->l_addr + reloc->r_addend;
+      return;
+    }
+#endif
+
+  if (__builtin_expect (r_type == R_TILE(NONE), 0))
+    return;
+
+#if !defined RTLD_BOOTSTRAP && !defined RESOLVE_CONFLICT_FIND_MAP
+  const ElfW(Sym) *const refsym = sym;
+#endif
+  struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
+  ElfW(Addr) value;
+
+  if (sym == NULL)
+    value = 0;
+  else if (ELFW_ST_TYPE (sym->st_info) == STT_SECTION)
+    value = map->l_addr;  /* like a RELATIVE reloc */
+  else
+    value = sym_map->l_addr + sym->st_value;
+
+  if (sym != NULL
+      && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0)
+      && __builtin_expect (sym->st_shndx != SHN_UNDEF, 1)
+      && __builtin_expect (!skip_ifunc, 1))
+    value = ((Elf64_Addr (*) (void)) value) ();
+
+  switch (r_type)
+    {
+    case R_TILE(JMP_SLOT):
+      elf_machine_fixup_plt (map, 0, reloc, reloc_addr,
+                             value + reloc->r_addend);
+      return;
+
+#ifndef RESOLVE_CONFLICT_FIND_MAP
+    case R_TILE_TLS(DTPMOD):
+# ifdef RTLD_BOOTSTRAP
+      /* During startup the dynamic linker is always the module
+         with index 1.
+         XXX If this relocation is necessary move before RESOLVE
+         call.  */
+      *reloc_addr = 1;
+# else
+      /* Get the information from the link map returned by the
+         resolv function.  */
+      if (sym_map != NULL)
+        *reloc_addr = sym_map->l_tls_modid;
+# endif
+      return;
+    case R_TILE_TLS(DTPOFF):
+# ifndef RTLD_BOOTSTRAP
+      /* During relocation all TLS symbols are defined and used.
+         Therefore the offset is already correct.  */
+      if (sym != NULL)
+        *reloc_addr = sym->st_value + reloc->r_addend;
+# endif
+      return;
+    case R_TILE_TLS(TPOFF):
+# ifdef RTLD_BOOTSTRAP
+      *reloc_addr = sym->st_value + reloc->r_addend + map->l_tls_offset;
+# else
+      if (sym != NULL)
+        {
+          CHECK_STATIC_TLS (map, sym_map);
+          *reloc_addr = (sym->st_value + reloc->r_addend
+                         + sym_map->l_tls_offset);
+        }
+#endif
+      return;
+#endif /* use TLS */
+
+#if !defined RTLD_BOOTSTRAP && !defined RESOLVE_CONFLICT_FIND_MAP
+      /* Not needed in dl-conflict.c.  */
+    case R_TILE(COPY):
+      if (sym == NULL)
+        /* This can happen in trace mode if an object could not be found.  */
+        return;
+      if (__builtin_expect (sym->st_size > refsym->st_size, 0)
+          || (__builtin_expect (sym->st_size < refsym->st_size, 0)
+              && __builtin_expect (GLRO(dl_verbose), 0)))
+        {
+          const char *strtab;
+
+          strtab = (const char *) D_PTR (map,l_info[DT_STRTAB]);
+          _dl_error_printf ("%s: Symbol `%s' has different size in shared"
+                            " object, consider re-linking\n",
+                            rtld_progname ?: "<program name unknown>",
+                            strtab + refsym->st_name);
+        }
+      memcpy (reloc_addr_arg, (void *) value,
+              MIN (sym->st_size, refsym->st_size));
+      return;
+#endif
+    }
+
+  /* All remaining relocations must be in the lookup table. */
+  const struct reloc_howto *h = &howto[r_type];
+  if ((unsigned int) r_type >= sizeof howto / sizeof howto[0] ||
+      h->byte_size == 0)
+    {
+#if !defined RTLD_BOOTSTRAP || defined _NDEBUG
+      /* We add these checks in the version to relocate ld.so only
+         if we are still debugging.  */
+      _dl_reloc_bad_type (map, r_type, 0);
+#endif
+      return;
+    }
+
+  value += reloc->r_addend;
+
+  /* The lookup table entry knows how to perform this reloc. */
+  if (h->is_pcrel)
+    value -= (ElfW(Addr)) reloc_addr;
+
+#ifndef __tilegx__
+  if (h->add_0x8000)
+    value += 0x8000;
+#endif
+
+  value >>= h->right_shift;
+
+  switch (h->byte_size)
+    {
+    case 1:
+      *(char *) reloc_addr = value;
+      return;
+    case 2:
+      *(short *) reloc_addr = value;
+      return;
+    case 4:
+      *(int *) reloc_addr = value;
+      return;
+#ifdef __tilegx__
+    case 8:
+      if (!h->is_bundle_update)
+        {
+          *(ElfW(Addr) *) reloc_addr = value;
+          return;
+        }
+#endif
+    }
+
+  /* We are updating a bundle, so use the function pointer that
+     swizzles the operand bits into the right location. */
+
+  tile_bundle_bits *p = (tile_bundle_bits *) reloc_addr;
+  tile_bundle_bits bits = *p;
+
+#define MUNGE(func) do {                                            \
+    bits = ((bits & ~create_##func (-1)) | create_##func (value));  \
+    if (get_##func (bits) != value)                                 \
+      _dl_signal_error (0, map->l_name, NULL,                       \
+                        "relocation value too large for " #func);   \
+  } while (0)
+
+#define MUNGE_NOCHECK(func)                                     \
+  bits = ((bits & ~create_##func (-1)) | create_##func (value))
+
+  switch (r_type)
+    {
+#ifdef __tilegx__
+    case R_TILEGX_BROFF_X1:
+      MUNGE (BrOff_X1);
+      break;
+    case R_TILEGX_JUMPOFF_X1:
+    case R_TILEGX_JUMPOFF_X1_PLT:
+      MUNGE (JumpOff_X1);
+      break;
+    case R_TILEGX_IMM8_X0:
+      MUNGE (Imm8_X0);
+      break;
+    case R_TILEGX_IMM8_Y0:
+      MUNGE (Imm8_Y0);
+      break;
+    case R_TILEGX_IMM8_X1:
+      MUNGE (Imm8_X1);
+      break;
+    case R_TILEGX_IMM8_Y1:
+      MUNGE (Imm8_Y1);
+      break;
+    case R_TILEGX_MT_IMM14_X1:
+      MUNGE (MT_Imm14_X1);
+      break;
+    case R_TILEGX_MF_IMM14_X1:
+      MUNGE (MF_Imm14_X1);
+      break;
+    case R_TILEGX_IMM16_X0_HW0:
+    case R_TILEGX_IMM16_X0_HW1:
+    case R_TILEGX_IMM16_X0_HW2:
+    case R_TILEGX_IMM16_X0_HW3:
+    case R_TILEGX_IMM16_X0_HW0_PCREL:
+    case R_TILEGX_IMM16_X0_HW1_PCREL:
+    case R_TILEGX_IMM16_X0_HW2_PCREL:
+    case R_TILEGX_IMM16_X0_HW3_PCREL:
+    case R_TILEGX_IMM16_X0_HW0_TLS_GD:
+    case R_TILEGX_IMM16_X0_HW1_TLS_GD:
+    case R_TILEGX_IMM16_X0_HW2_TLS_GD:
+    case R_TILEGX_IMM16_X0_HW3_TLS_GD:
+    case R_TILEGX_IMM16_X0_HW0_TLS_IE:
+    case R_TILEGX_IMM16_X0_HW1_TLS_IE:
+    case R_TILEGX_IMM16_X0_HW2_TLS_IE:
+    case R_TILEGX_IMM16_X0_HW3_TLS_IE:
+      MUNGE_NOCHECK (Imm16_X0);
+      break;
+    case R_TILEGX_IMM16_X0_HW0_LAST:
+    case R_TILEGX_IMM16_X0_HW1_LAST:
+    case R_TILEGX_IMM16_X0_HW2_LAST:
+    case R_TILEGX_IMM16_X0_HW0_LAST_PCREL:
+    case R_TILEGX_IMM16_X0_HW1_LAST_PCREL:
+    case R_TILEGX_IMM16_X0_HW2_LAST_PCREL:
+    case R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD:
+    case R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD:
+    case R_TILEGX_IMM16_X0_HW2_LAST_TLS_GD:
+    case R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE:
+    case R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE:
+    case R_TILEGX_IMM16_X0_HW2_LAST_TLS_IE:
+      MUNGE (Imm16_X0);
+      break;
+    case R_TILEGX_IMM16_X1_HW0:
+    case R_TILEGX_IMM16_X1_HW1:
+    case R_TILEGX_IMM16_X1_HW2:
+    case R_TILEGX_IMM16_X1_HW3:
+    case R_TILEGX_IMM16_X1_HW0_PCREL:
+    case R_TILEGX_IMM16_X1_HW1_PCREL:
+    case R_TILEGX_IMM16_X1_HW2_PCREL:
+    case R_TILEGX_IMM16_X1_HW3_PCREL:
+    case R_TILEGX_IMM16_X1_HW0_TLS_GD:
+    case R_TILEGX_IMM16_X1_HW1_TLS_GD:
+    case R_TILEGX_IMM16_X1_HW2_TLS_GD:
+    case R_TILEGX_IMM16_X1_HW3_TLS_GD:
+    case R_TILEGX_IMM16_X1_HW0_TLS_IE:
+    case R_TILEGX_IMM16_X1_HW1_TLS_IE:
+    case R_TILEGX_IMM16_X1_HW2_TLS_IE:
+    case R_TILEGX_IMM16_X1_HW3_TLS_IE:
+      MUNGE_NOCHECK (Imm16_X1);
+      break;
+    case R_TILEGX_IMM16_X1_HW0_LAST:
+    case R_TILEGX_IMM16_X1_HW1_LAST:
+    case R_TILEGX_IMM16_X1_HW2_LAST:
+    case R_TILEGX_IMM16_X1_HW0_LAST_PCREL:
+    case R_TILEGX_IMM16_X1_HW1_LAST_PCREL:
+    case R_TILEGX_IMM16_X1_HW2_LAST_PCREL:
+    case R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD:
+    case R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD:
+    case R_TILEGX_IMM16_X1_HW2_LAST_TLS_GD:
+    case R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE:
+    case R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE:
+    case R_TILEGX_IMM16_X1_HW2_LAST_TLS_IE:
+      MUNGE (Imm16_X1);
+      break;
+    case R_TILEGX_MMSTART_X0:
+      MUNGE (BFStart_X0);
+      break;
+    case R_TILEGX_MMEND_X0:
+      MUNGE (BFEnd_X0);
+      break;
+    case R_TILEGX_SHAMT_X0:
+      MUNGE (ShAmt_X0);
+      break;
+    case R_TILEGX_SHAMT_X1:
+      MUNGE (ShAmt_X1);
+      break;
+    case R_TILEGX_SHAMT_Y0:
+      MUNGE (ShAmt_Y0);
+      break;
+    case R_TILEGX_SHAMT_Y1:
+      MUNGE (ShAmt_Y1);
+      break;
+#else
+    case R_TILEPRO_BROFF_X1:
+      MUNGE (BrOff_X1);
+      break;
+    case R_TILEPRO_JOFFLONG_X1:
+    case R_TILEPRO_JOFFLONG_X1_PLT:
+      MUNGE_NOCHECK (JOffLong_X1);   /* holds full 32-bit value */
+      break;
+    case R_TILEPRO_IMM8_X0:
+      MUNGE (Imm8_X0);
+      break;
+    case R_TILEPRO_IMM8_Y0:
+      MUNGE (Imm8_Y0);
+      break;
+    case R_TILEPRO_IMM8_X1:
+      MUNGE (Imm8_X1);
+      break;
+    case R_TILEPRO_IMM8_Y1:
+      MUNGE (Imm8_Y1);
+      break;
+    case R_TILEPRO_MT_IMM15_X1:
+      MUNGE (MT_Imm15_X1);
+      break;
+    case R_TILEPRO_MF_IMM15_X1:
+      MUNGE (MF_Imm15_X1);
+      break;
+    case R_TILEPRO_IMM16_X0_LO:
+    case R_TILEPRO_IMM16_X0_HI:
+    case R_TILEPRO_IMM16_X0_HA:
+    case R_TILEPRO_IMM16_X0_LO_PCREL:
+    case R_TILEPRO_IMM16_X0_HI_PCREL:
+    case R_TILEPRO_IMM16_X0_HA_PCREL:
+    case R_TILEPRO_IMM16_X0_TLS_GD_LO:
+    case R_TILEPRO_IMM16_X0_TLS_GD_HI:
+    case R_TILEPRO_IMM16_X0_TLS_GD_HA:
+    case R_TILEPRO_IMM16_X0_TLS_IE_LO:
+    case R_TILEPRO_IMM16_X0_TLS_IE_HI:
+    case R_TILEPRO_IMM16_X0_TLS_IE_HA:
+      MUNGE_NOCHECK (Imm16_X0);
+      break;
+    case R_TILEPRO_IMM16_X0:
+    case R_TILEPRO_IMM16_X0_PCREL:
+    case R_TILEPRO_IMM16_X0_TLS_GD:
+    case R_TILEPRO_IMM16_X0_TLS_IE:
+      MUNGE (Imm16_X0);
+      break;
+    case R_TILEPRO_IMM16_X1_LO:
+    case R_TILEPRO_IMM16_X1_HI:
+    case R_TILEPRO_IMM16_X1_HA:
+    case R_TILEPRO_IMM16_X1_LO_PCREL:
+    case R_TILEPRO_IMM16_X1_HI_PCREL:
+    case R_TILEPRO_IMM16_X1_HA_PCREL:
+    case R_TILEPRO_IMM16_X1_TLS_GD_LO:
+    case R_TILEPRO_IMM16_X1_TLS_GD_HI:
+    case R_TILEPRO_IMM16_X1_TLS_GD_HA:
+    case R_TILEPRO_IMM16_X1_TLS_IE_LO:
+    case R_TILEPRO_IMM16_X1_TLS_IE_HI:
+    case R_TILEPRO_IMM16_X1_TLS_IE_HA:
+      MUNGE_NOCHECK (Imm16_X1);
+      break;
+    case R_TILEPRO_IMM16_X1:
+    case R_TILEPRO_IMM16_X1_PCREL:
+    case R_TILEPRO_IMM16_X1_TLS_GD:
+    case R_TILEPRO_IMM16_X1_TLS_IE:
+      MUNGE (Imm16_X1);
+      break;
+    case R_TILEPRO_MMSTART_X0:
+      MUNGE (MMStart_X0);
+      break;
+    case R_TILEPRO_MMEND_X0:
+      MUNGE (MMEnd_X0);
+      break;
+    case R_TILEPRO_MMSTART_X1:
+      MUNGE (MMStart_X1);
+      break;
+    case R_TILEPRO_MMEND_X1:
+      MUNGE (MMEnd_X1);
+      break;
+    case R_TILEPRO_SHAMT_X0:
+      MUNGE (ShAmt_X0);
+      break;
+    case R_TILEPRO_SHAMT_X1:
+      MUNGE (ShAmt_X1);
+      break;
+    case R_TILEPRO_SHAMT_Y0:
+      MUNGE (ShAmt_Y0);
+      break;
+    case R_TILEPRO_SHAMT_Y1:
+      MUNGE (ShAmt_Y1);
+      break;
+#endif
+    }
+#undef MUNGE
+  *p = bits;
+  _dl_flush_icache (p, sizeof (*p));
+}
+
+auto inline void __attribute__ ((always_inline))
+elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
+                           void *const reloc_addr_arg)
+{
+  ElfW(Addr) *const reloc_addr = reloc_addr_arg;
+  *reloc_addr = l_addr + reloc->r_addend;
+}
+
+auto inline void __attribute__ ((always_inline))
+elf_machine_lazy_rel (struct link_map *map,
+                      ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
+                      int skip_ifunc)
+{
+  const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
+
+  /* Check for unexpected PLT reloc type.  */
+  if (__builtin_expect (r_type == R_TILE(JMP_SLOT), 1))
+    {
+      ElfW(Addr) *gotplt = (ElfW(Addr) *) D_PTR (map, l_info[DT_PLTGOT]);
+
+      if (__builtin_expect (!tile_old_plt_model (map, gotplt), 1))
+        {
+          ElfW(Addr) *const reloc_addr = (void *) (l_addr + reloc->r_offset);
+          *reloc_addr += l_addr;
+        }
+    }
+  else
+    _dl_reloc_bad_type (map, r_type, 1);
+}
+
+#endif /* RESOLVE_MAP */
diff --git a/sysdeps/tile/dl-runtime.c b/sysdeps/tile/dl-runtime.c
new file mode 100644
index 0000000..e8a8361
--- /dev/null
+++ b/sysdeps/tile/dl-runtime.c
@@ -0,0 +1,80 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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.  */
+
+/* Like x86_64, we pass the index of the relocation and not its offset.
+   In _dl_profile_fixup and _dl_call_pltexit we also use the index.
+   Therefore it is wasteful to compute the offset in the trampoline
+   just to reverse the operation immediately afterwards.  */
+#define reloc_offset reloc_arg * sizeof (PLTREL)
+#define reloc_index  reloc_arg
+
+#include <elf/dl-runtime.c>
+
+#include <sys/mman.h>
+#include <arch/sim.h>
+
+/* Support notifying the simulator about new objects. */
+void internal_function
+_dl_arch_map_object (struct link_map *l)
+{
+  int shift;
+
+#define DLPUTC(c) __insn_mtspr(SPR_SIM_CONTROL,                         \
+                               (SIM_CONTROL_DLOPEN                      \
+                                | ((c) << _SIM_CONTROL_OPERATOR_BITS)))
+
+  /* Write the library address in hex. */
+  DLPUTC ('0');
+  DLPUTC ('x');
+  for (shift = (int) sizeof (unsigned long) * 8 - 4; shift >= 0; shift -= 4)
+    DLPUTC ("0123456789abcdef"[(l->l_map_start >> shift) & 0xF]);
+  DLPUTC (':');
+
+  /* Write the library path, including the terminating '\0'. */
+  for (size_t i = 0;; i++)
+    {
+      DLPUTC (l->l_name[i]);
+      if (l->l_name[i] == '\0')
+        break;
+    }
+#undef DLPUTC
+}
+
+/* Support notifying the simulator about removed objects prior to munmap(). */
+void internal_function
+_dl_unmap (struct link_map *l)
+{
+  int shift;
+
+#define DLPUTC(c) __insn_mtspr(SPR_SIM_CONTROL,                         \
+                               (SIM_CONTROL_DLCLOSE                     \
+                                | ((c) << _SIM_CONTROL_OPERATOR_BITS)))
+
+  /* Write the library address in hex. */
+  DLPUTC ('0');
+  DLPUTC ('x');
+  for (shift = (int) sizeof (unsigned long) * 8 - 4; shift >= 0; shift -= 4)
+    DLPUTC ("0123456789abcdef"[(l->l_map_start >> shift) & 0xF]);
+  DLPUTC ('\0');
+#undef DLPUTC
+
+  __munmap ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
+}
+
+#define DL_UNMAP(map) _dl_unmap (map)
diff --git a/sysdeps/tile/dl-start.S b/sysdeps/tile/dl-start.S
new file mode 100644
index 0000000..2991374
--- /dev/null
+++ b/sysdeps/tile/dl-start.S
@@ -0,0 +1,114 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <sysdep.h>
+
+	/* Get address of "sym" in "reg" assuming r51 holds ".Llink". */
+	.macro pic_addr reg, sym
+#ifdef __tilegx__
+	moveli \reg, hw1_last(\sym - .Llink)
+	shl16insli \reg, \reg, hw0(\sym - .Llink)
+	ADD_PTR \reg, r51, \reg
+#else
+	ADDLI_PTR \reg, r51, lo16(\sym - .Llink)
+	auli \reg, \reg, ha16(\sym - .Llink)
+#endif
+	.endm
+
+	.text
+ENTRY (_start)
+	/* Linux starts us with sp pointing at the conventional Elf layout,
+	   but we need to allow two 'caller' words for our ABI convention.  */
+	{
+	 move r52, sp
+	 andi sp, sp, -8
+	}
+	cfi_def_cfa_register (r52)
+	{
+	 /* Point sp at base of ABI area; point r4 to the caller-sp word. */
+	 ADDI_PTR sp, sp, -(2 * REGSIZE)
+	 ADDI_PTR r4, sp, -REGSIZE
+	}
+	{
+	 /* Save zero for caller sp in our 'caller' save area, and make
+	    sure lr has a zero value, to limit backtraces.  */
+	 move lr, zero
+	 ST r4, zero
+	}
+	{
+	 move r0, r52
+	 jal _dl_start
+	}
+	/* Save returned start of user program address for later. */
+	move r50, r0
+
+	/* See if we were invoked explicitly with the dynamic loader,
+	   in which case we have to adjust the argument vector.  */
+	lnk r51; .Llink:
+	pic_addr r4, _dl_skip_args
+	LD4U r4, r4
+	BEQZT r4, .Lno_skip
+
+	/* Load the argc word at the initial sp and adjust it.
+	   We basically jump "sp" up over the first few argv entries
+	   and write "argc" a little higher up in memory, to be the
+	   base of the new kernel-initialized stack area.  */
+	LD_PTR r0, r52
+	{
+	 sub r0, r0, r4
+	 SHL_PTR_ADD r52, r4, r52
+	}
+	{
+	 ST_PTR r52, r0
+	 SHL_PTR_ADD sp, r4, sp
+	}
+
+.Lno_skip:
+	/* Call_dl_init (_dl_loaded, argc, argv, envp).  See elf/start.s
+	   for the layout of memory here; r52 is pointing to "+0".  */
+	pic_addr r0, _rtld_local
+	{
+	 LD_PTR r1, r52  /* load argc in r1 */
+	 ADDLI_PTR r2, r52, __SIZEOF_POINTER__  /* point r2 at argv */
+	}
+	{
+	 LD_PTR r0, r0    /* yields _rtld_global._ns_loaded */
+	 addi r3, r1, 1
+	 move lr, zero
+	}
+	{
+	 SHL_PTR_ADD r3, r3, r2  /* point r3 at envp */
+	 jal _dl_init_internal
+	}
+
+	/* Call user program whose address we saved in r50.
+	   We invoke it just like a static binary, but with _dl_fini
+	   in r0 so we can distinguish.  */
+
+	pic_addr r0, _dl_fini
+	move lr, zero
+	{
+	 move sp, r52
+	 jr r50
+	}
+
+	/* Tell backtracer to give up (_start has no caller). */
+	info 2  /* INFO_OP_CANNOT_BACKTRACE */
+
+END (_start)
diff --git a/sysdeps/tile/dl-tls.c b/sysdeps/tile/dl-tls.c
new file mode 100644
index 0000000..096539b
--- /dev/null
+++ b/sysdeps/tile/dl-tls.c
@@ -0,0 +1,28 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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.  */
+
+#ifdef SHARED
+/* We provide a fast-path version of __tls_get_addr to allow for
+   the normal case to be fast, both by coding the function tightly,
+   and more importantly by fixing its register clobber API so the
+   compiler can avoid having to set up frames, etc., unnecessarily.  */
+#define __tls_get_addr __tls_get_addr_slow
+#endif
+
+#include <elf/dl-tls.c>
diff --git a/sysdeps/tile/dl-tls.h b/sysdeps/tile/dl-tls.h
new file mode 100644
index 0000000..baa0c12
--- /dev/null
+++ b/sysdeps/tile/dl-tls.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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;
+
+/* Fast-path function to get a TLS pointer.  */
+extern void *__tls_get_addr (tls_index *ti);
+
+/* The thread pointer points to the first static TLS block.  */
+#define TLS_TP_OFFSET		0
+
+/* Dynamic thread vector pointers at the start of each TLS block.  */
+#define TLS_DTV_OFFSET		0
+
+/* 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)
+
+/* Value used for dtv entries for which the allocation is delayed.  */
+#define TLS_DTV_UNALLOCATED    ((void *) -1l)
diff --git a/sysdeps/tile/dl-trampoline.S b/sysdeps/tile/dl-trampoline.S
new file mode 100644
index 0000000..486b958
--- /dev/null
+++ b/sysdeps/tile/dl-trampoline.S
@@ -0,0 +1,194 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <sysdep.h>
+#include <arch/abi.h>
+
+/* This function is called via the PLT header, which is called
+   from an individual PLT entry.
+
+   At this point we have several values passed in:
+
+   lr:  return address to original user code
+   r28: the tpnt value to pass to _dl_runtime_resolver
+   r29: the PLT index of the invoked jump table entry.
+
+   We set up a frame entry that looks like this (in int_reg_t units):
+
+   +57: r25 return values from function...
+   +56: r24
+   [...]
+   +33: r1
+   +32: r0
+   +31: PLT index
+   +30: tpnt
+   +29: stackframe
+   +28: caller lr
+   +27: r25 arguments to function...
+   +26: r24
+   [...]
+   +3:  r1
+   +2:  r0
+   +1:  standard ABI slot (sp)
+   +0:  standard ABI slot (callee lr)
+
+   The entries from "stackframe" up are only used in _dl_profile_resolve.
+   We save and restore r0 through r25, rather than the strictly
+   architected r0 through r9, to support unusual calling conventions;
+   for example, __tls_get_addr takes r0 and returns r0, but promises
+   not to clobber r1 through r24 to support its usual fast path.  */
+
+#define FRAME_SP		(1 * REGSIZE)
+#define FRAME_REGS		(2 * REGSIZE)
+#define FRAME_LR		(28 * REGSIZE)  /* Must follow FRAME_REGS */
+#define FRAME_STACKFRAME	(29 * REGSIZE)
+#define FRAME_TPNT		(30 * REGSIZE)
+#define FRAME_INDEX		(31 * REGSIZE)
+#define FRAME_RETVAL		(32 * REGSIZE)
+
+#define FRAME_SIZE_SMALL	(30 * REGSIZE)
+#define FRAME_SIZE_LARGE	(58 * REGSIZE)
+
+#define FOR_EACH_REG(f) \
+	f(r0);  f(r1);  f(r2);  f(r3);  \
+	f(r4);  f(r5);  f(r6);  f(r7);  \
+	f(r8);  f(r9);  f(r10); f(r11); \
+	f(r12); f(r13); f(r14); f(r15); \
+	f(r16); f(r17); f(r18); f(r19); \
+	f(r20); f(r21); f(r22); f(r23); \
+	f(r24); f(r25)
+
+#define SAVE(REG) { ST r27, REG; ADDI_PTR r27, r27, REGSIZE }
+#define RESTORE(REG) { LD REG, r27; ADDI_PTR r27, r27, REGSIZE }
+
+	.macro dl_resolve, name, profile, framesize
+.text
+.global \name
+.hidden \name
+/* Note that cpp expands ENTRY(\name) incorrectly. */
+.type \name,@function
+.align 8
+\name:
+	cfi_startproc
+	{
+	 ST sp, lr
+	 move r26, sp
+	}
+	{
+	 ADDLI_PTR sp, sp, -\framesize
+	 ADDLI_PTR r27, sp, FRAME_SP - \framesize
+	}
+	cfi_def_cfa_offset (\framesize)
+	{
+	 ST r27, r26
+	 ADDI_PTR r27, r27, FRAME_REGS - FRAME_SP
+	}
+	FOR_EACH_REG(SAVE)
+	{
+	 ST r27, lr
+	 ADDLI_PTR r27, sp, FRAME_TPNT
+	}
+	cfi_offset (lr, FRAME_LR - \framesize)
+	.if \profile
+	{
+	 move r0, r28  /* tpnt value */
+	 ST r27, r28
+	 ADDI_PTR r27, r27, FRAME_INDEX - FRAME_TPNT
+	}
+	{
+	 move r1, r29  /* PLT index */
+	 ST r27, r29
+	}
+	{
+	 move r2, lr  /* retaddr */
+	 ADDI_PTR r3, sp, FRAME_REGS  /* La_tile_regs pointer */
+	}
+	{
+	 ADDLI_PTR r4, sp, FRAME_STACKFRAME  /* framesize pointer */
+	 jal _dl_profile_fixup
+	}
+	ADDLI_PTR r28, sp, FRAME_STACKFRAME
+	LD_PTR r28, r28
+	BGTZ r28, 1f
+	.else
+	{
+	 move r0, r28  /* tpnt value 1 */
+	 move r1, r29  /* PLT index 2 */
+	}
+	jal _dl_fixup
+	.endif
+	{
+	 /* Copy aside the return value so we can restore r0 below. */
+	 move r29, r0
+	 /* Set up r27 to let us start restoring registers. */
+	 ADDLI_PTR r27, sp, FRAME_REGS
+	}
+	FOR_EACH_REG(RESTORE)
+	.if \profile
+	ADDLI_PTR r28, sp, FRAME_STACKFRAME
+	LD r28, r28
+	BGTZ r28, 1f
+	.endif
+	{
+	 /* Restore original user return address. */
+	 LD lr, r27
+	 /* Pop off our stack frame. */
+	 ADDLI_PTR sp, sp, \framesize
+	}
+	cfi_def_cfa_offset (0)
+	jr r29  /* Transfer control to freshly loaded code. */
+	jrp lr  /* Keep backtracer happy. */
+
+	.if \profile
+1:	jalr r29   /* Call resolved function. */
+	{
+	 ADDLI_PTR r28, sp, FRAME_TPNT
+	 ADDLI_PTR r27, sp, FRAME_RETVAL
+	}
+	FOR_EACH_REG(SAVE)
+	{
+	 LD r0, r28
+	 ADDI_PTR r28, r28, FRAME_INDEX - FRAME_TPNT
+	}
+        {
+	 LD r1, r28
+	 ADDLI_PTR r2, sp, FRAME_REGS
+	}
+	{
+	 ADDLI_PTR r3, sp, FRAME_RETVAL
+	 jal _dl_call_pltexit
+	}
+	{
+	 ADDLI_PTR lr, sp, FRAME_LR
+	 ADDLI_PTR r27, sp, FRAME_RETVAL
+        }
+	FOR_EACH_REG(RESTORE)
+	{
+	 LD lr, lr
+	 ADDLI_PTR sp, sp, \framesize
+	}
+	jrp lr
+	.endif
+END (\name)
+	.endm
+
+	dl_resolve _dl_runtime_resolve, 0, FRAME_SIZE_SMALL
+#ifndef PROF
+	dl_resolve _dl_runtime_profile, 1, FRAME_SIZE_LARGE
+#endif
diff --git a/sysdeps/tile/elf/start.S b/sysdeps/tile/elf/start.S
new file mode 100644
index 0000000..71a2c8b
--- /dev/null
+++ b/sysdeps/tile/elf/start.S
@@ -0,0 +1,184 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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.
+
+   In addition to the permissions in the GNU Lesser General Public
+   License, the Free Software Foundation gives you unlimited
+   permission to link the compiled version of this file with other
+   programs, and to distribute those programs without any restriction
+   coming from the use of this file. (The GNU Lesser General Public
+   License restrictions do apply in other respects; for example, they
+   cover modification of the file, and distribution when not linked
+   into another program.)
+
+   Note that people who make modified versions of this file are not
+   obligated to grant this special exception for their modified
+   versions; it is their choice whether to do so. The GNU Lesser
+   General Public License gives permission to release a modified
+   version without this exception; this exception also makes it
+   possible to release a modified version which carries forward this
+   exception.
+
+   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.  */
+
+/* This is the canonical entry point, usually the first thing in the text
+   segment.  The ELF standard tells us that the stack is set up like this on
+   entry (the left side is the offset from "sp"), in units of
+   __SIZEOF_POINTER__ entries:
+
+		+0		argc
+		+1		argv[0]
+		...
+		+(argc+1)	NULL
+		+(argc+2)	envp[0]
+		...
+				NULL
+		...		ElfInfo
+
+   The ElfInfo is pairs of key/value long words following the envp
+   pointers and terminated by a zero-valued key.
+
+   Although not mandated by the standard, it happens to be the case
+   that we store the actual argv and envp strings immediately after
+   the ElfInfo data on the stack.
+
+   On entry r0 points to the shared library termination function, or 0
+   if there isn't one.
+*/
+
+#include <features.h>
+#include <sysdep.h>
+#include <arch/abi.h>
+
+	.text
+	.global _start
+	.type   _start,@function
+	.align 8
+_start:
+	/* Linux starts us with sp pointing at the conventional Elf layout,
+	   but we need to allow two "caller" words for our ABI convention.  */
+	{
+	 /* Load argc (stored as a "long", equivalent to a pointer type). */
+	 LD_PTR r1, sp
+
+	 /* Save incoming 'sp', which points to the Elf argument block. */
+	 move r52, sp
+	}
+
+	{
+	 /* Allocate stack frame callee space for __libc_start_main. */
+	 ADDI_PTR r12, sp, -(2 * REGSIZE)
+	}
+
+	{
+	 /* Get our PC. */
+	 lnk r13
+
+	 /* sp is not necessarily properly aligned on startup because
+	    of the way ld.so pops off leading argv elements. So align it. */
+	 andi sp, r12, -8
+	}
+.Lmy_pc:
+
+	{
+	 /* Pass the address of the shared library termination function. */
+	 move r5, r0
+
+	 /* Compute location where __libc_start_main's caller is supposed to
+	    store its frame pointer. */
+	 ADDI_PTR r12, sp, REGSIZE
+
+	 /* Zero out callee space for return address. Unnecessary but free.
+	    This is just paranoia to help backtracing not go awry. */
+	 ST sp, zero
+	}
+	{
+	 /* Zero out our frame pointer for __libc_start_main. */
+	 ST r12, zero
+
+	 /* Zero out lr to make __libc_start_main the end of backtrace.  */
+	 move lr, zero
+
+	 /* Compute a pointer to argv. envp will be determined
+	    later in __libc_start_main.  We set up the first argument
+	    (the address of main) below. */
+	 ADDI_PTR r2, r52, __SIZEOF_POINTER__
+	}
+	{
+	 /* Pass the highest stack address to user code. */
+	 ADDI_PTR r6, sp, (2 * REGSIZE)
+
+	 /* Pass address of main() in r0, and of our own entry
+	    points to .fini and .init in r3 and r4.  */
+#ifdef __tilegx__
+	 moveli r0, hw2_last(main - .Lmy_pc)
+	}
+	{
+	 moveli r3, hw2_last(__libc_csu_init - .Lmy_pc)
+	 shl16insli r0, r0, hw1(main - .Lmy_pc)
+	}
+	{
+	 shl16insli r3, r3, hw1(__libc_csu_init - .Lmy_pc)
+	 shl16insli r0, r0, hw0(main - .Lmy_pc)
+	}
+	{
+	 shl16insli r3, r3, hw0(__libc_csu_init - .Lmy_pc)
+	 moveli r4, hw2_last(__libc_csu_fini - .Lmy_pc)
+	}
+	{
+	 ADD_PTR r0, r0, r13
+	 shl16insli r4, r4, hw1(__libc_csu_fini - .Lmy_pc)
+	}
+	{
+	 ADD_PTR r3, r3, r13
+	 shl16insli r4, r4, hw0(__libc_csu_fini - .Lmy_pc)
+	}
+	{
+	 ADD_PTR r4, r4, r13
+#else
+	 addli r0, r13, lo16(main - .Lmy_pc)
+	}
+	{
+	 auli r0, r0, ha16(main - .Lmy_pc)
+	 addli r3, r13, lo16(__libc_csu_init - .Lmy_pc)
+	}
+	{
+	 auli r3, r3, ha16(__libc_csu_init - .Lmy_pc)
+	 addli r4, r13, lo16(__libc_csu_fini - .Lmy_pc)
+	}
+	{
+	 auli r4, r4, ha16(__libc_csu_fini - .Lmy_pc)
+
+#endif
+
+	 /* Call the user's main function, and exit with its value.
+	    But let the libc call main. */
+	 j plt(__libc_start_main)
+	}
+	{
+	 /* Tell backtracer to give up (_start has no caller). */
+	 info INFO_OP_CANNOT_BACKTRACE
+	}
+.size _start, .-_start
+
+/* Define a symbol for the first piece of initialized data.  */
+	.data
+	.global __data_start
+	.align 8
+__data_start:
+	.long 0
+	.weak data_start
+	data_start = __data_start
diff --git a/sysdeps/tile/fegetenv.c b/sysdeps/tile/fegetenv.c
new file mode 100644
index 0000000..ff84609
--- /dev/null
+++ b/sysdeps/tile/fegetenv.c
@@ -0,0 +1,29 @@
+/* Store current floating-point environment.
+   Copyright (C) 1997, 1999, 2000 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
+
+   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 <fenv.h>
+
+int
+__fegetenv (fenv_t *envp)
+{
+  /* As a no-op, this always succeeds. */
+  return 0;
+}
+libm_hidden_ver (__fegetenv, fegetenv)
diff --git a/sysdeps/tile/fegetround.c b/sysdeps/tile/fegetround.c
new file mode 100644
index 0000000..837fd59
--- /dev/null
+++ b/sysdeps/tile/fegetround.c
@@ -0,0 +1,27 @@
+/* Return current rounding direction.
+   Copyright (C) 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
+
+   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 <fenv.h>
+
+int
+fegetround (void)
+{
+  return FE_TONEAREST;
+}
diff --git a/sysdeps/tile/feholdexcpt.c b/sysdeps/tile/feholdexcpt.c
new file mode 100644
index 0000000..384b696
--- /dev/null
+++ b/sysdeps/tile/feholdexcpt.c
@@ -0,0 +1,28 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <fenv.h>
+
+/* Tile has no exception flags, so this routine can be a no-op.  */
+int
+feholdexcept (fenv_t *envp)
+{
+  return 0;
+}
+libm_hidden_def (feholdexcept)
diff --git a/sysdeps/tile/fesetenv.c b/sysdeps/tile/fesetenv.c
new file mode 100644
index 0000000..ceb45e4
--- /dev/null
+++ b/sysdeps/tile/fesetenv.c
@@ -0,0 +1,32 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <fenv.h>
+#include <shlib-compat.h>
+
+/* The only way to get an initialized fenv_t on Tile is with feholdexcept()
+   or via FE_DFL_ENV, either of which restores the environment to its
+   normal state, i.e. FE_DFL_ENV.  */
+int
+__fesetenv (const fenv_t *envp)
+{
+  return 0;
+}
+libm_hidden_ver (__fesetenv, fesetenv)
+versioned_symbol (libm, __fesetenv, fesetenv, GLIBC_2_2);
diff --git a/sysdeps/tile/fesetround.c b/sysdeps/tile/fesetround.c
new file mode 100644
index 0000000..8954a65
--- /dev/null
+++ b/sysdeps/tile/fesetround.c
@@ -0,0 +1,28 @@
+/* Set current rounding direction.
+   Copyright (C) 1997, 2005 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
+
+   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 <fenv.h>
+
+int
+fesetround (int round)
+{
+  return (round == FE_TONEAREST) ? 0 : 1;
+}
+libm_hidden_def (fesetround)
diff --git a/sysdeps/tile/feupdateenv.c b/sysdeps/tile/feupdateenv.c
new file mode 100644
index 0000000..65cb318
--- /dev/null
+++ b/sysdeps/tile/feupdateenv.c
@@ -0,0 +1,28 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <fenv.h>
+
+/* Tile has no exception flags, so this routine can be a no-op.  */
+int
+__feupdateenv (const fenv_t *envp)
+{
+  return 0;
+}
+weak_alias (__feupdateenv, feupdateenv)
diff --git a/sysdeps/tile/ffs.c b/sysdeps/tile/ffs.c
new file mode 100644
index 0000000..498559c
--- /dev/null
+++ b/sysdeps/tile/ffs.c
@@ -0,0 +1,45 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <limits.h>
+#define ffsl __something_else
+#include <string.h>
+
+#undef ffs
+int
+__ffs (int x)
+{
+  return __builtin_ffs (x);
+}
+weak_alias (__ffs, ffs)
+libc_hidden_builtin_def (ffs)
+
+#undef ffsll
+int
+ffsll (long long x)
+{
+  return __builtin_ffsll (x);
+}
+
+#undef ffsl
+#if ULONG_MAX == UINT_MAX
+weak_alias (__ffs, ffsl)
+#else
+weak_alias (ffsll, ffsl)
+#endif
diff --git a/sysdeps/tile/ffsll.c b/sysdeps/tile/ffsll.c
new file mode 100644
index 0000000..180eaa8
--- /dev/null
+++ b/sysdeps/tile/ffsll.c
@@ -0,0 +1 @@
+/* This function is defined in ffs.c.  */
diff --git a/sysdeps/tile/gccframe.h b/sysdeps/tile/gccframe.h
new file mode 100644
index 0000000..041d3d8
--- /dev/null
+++ b/sysdeps/tile/gccframe.h
@@ -0,0 +1,22 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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.  */
+
+#define FIRST_PSEUDO_REGISTER 64
+
+#include <sysdeps/generic/gccframe.h>
diff --git a/sysdeps/tile/jmpbuf-offsets.h b/sysdeps/tile/jmpbuf-offsets.h
new file mode 100644
index 0000000..ce4f1b2
--- /dev/null
+++ b/sysdeps/tile/jmpbuf-offsets.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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.  */
+
+/* We don't use most of these symbols; they are here for documentation. */
+#define JB_R30  0
+#define JB_R31  1
+#define JB_R32  2
+#define JB_R33  3
+#define JB_R34  4
+#define JB_R35  5
+#define JB_R36  6
+#define JB_R37  7
+#define JB_R38  8
+#define JB_R39  9
+#define JB_R40  10
+#define JB_R41  11
+#define JB_R42  12
+#define JB_R43  13
+#define JB_R44  14
+#define JB_R45  15
+#define JB_R46  16
+#define JB_R47  17
+#define JB_R48  18
+#define JB_R49  19
+#define JB_R50  20
+#define JB_R51  21
+#define JB_FP   22  /* r52 */
+#define JB_TP   23  /* r53 */
+#define JB_SP   24  /* r54 */
+#define JB_PC   25  /* normally LR, r55 */
+#define JB_ICS  26  /* interrupt critical section bit */
+
+/* We save space for some extra state to accomodate future changes.  */
+#define JB_LEN  32  /* number of words */
+
+#define JB_SIZE (JB_LEN * REGSIZE)
+
+/* Helper macro used by all the setjmp/longjmp assembly code. */
+#define FOR_EACH_CALLEE_SAVED_REG(f)                              \
+  .no_require_canonical_reg_names;                f(r30); f(r31); \
+  f(r32); f(r33); f(r34); f(r35); f(r36); f(r37); f(r38); f(r39); \
+  f(r40); f(r41); f(r42); f(r43); f(r44); f(r45); f(r46); f(r47); \
+  f(r48); f(r49); f(r50); f(r51); f(r52); f(r53); f(r54); f(r55)
+
+/* Helper for generic ____longjmp_chk(). */
+#define JB_FRAME_ADDRESS(buf) \
+  ((void *) (unsigned long) (buf[JB_SP]))
diff --git a/sysdeps/tile/jmpbuf-unwind.h b/sysdeps/tile/jmpbuf-unwind.h
new file mode 100644
index 0000000..624a014
--- /dev/null
+++ b/sysdeps/tile/jmpbuf-unwind.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+   Based on work contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
+
+   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 <setjmp.h>
+#include <jmpbuf-offsets.h>
+#include <stdint.h>
+#include <unwind.h>
+#include <sysdep.h>
+
+/* Test if longjmp to JMPBUF would unwind the frame
+   containing a local variable at ADDRESS.  */
+#define _JMPBUF_UNWINDS(jmpbuf, address, demangle) \
+  ((void *) (address) < (void *) demangle ((jmpbuf)[JB_SP]))
+
+#define _JMPBUF_CFA_UNWINDS_ADJ(_jmpbuf, _context, _adj) \
+  _JMPBUF_UNWINDS_ADJ (_jmpbuf, (void *) _Unwind_GetCFA (_context), _adj)
+
+static inline uintptr_t __attribute__ ((unused))
+_jmpbuf_sp (__jmp_buf regs)
+{
+  uintptr_t sp = regs[JB_SP];
+#ifdef PTR_DEMANGLE
+  PTR_DEMANGLE (sp);
+#endif
+  return sp;
+}
+
+#define _JMPBUF_UNWINDS_ADJ(_jmpbuf, _address, _adj) \
+  ((uintptr_t) (_address) - (_adj) < _jmpbuf_sp (_jmpbuf) - (_adj))
+
+/* We use the normal longjmp for unwinding.  */
+#define __libc_unwind_longjmp(buf, val) __libc_longjmp (buf, val)
diff --git a/sysdeps/tile/ldsodefs.h b/sysdeps/tile/ldsodefs.h
new file mode 100644
index 0000000..e90de56
--- /dev/null
+++ b/sysdeps/tile/ldsodefs.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 _TILE_LDSODEFS_H
+#define _TILE_LDSODEFS_H 1
+
+#include <elf.h>
+
+struct La_tile_regs;
+struct La_tile_retval;
+
+#define ARCH_PLTENTER_MEMBERS                                           \
+  ElfW(Addr) (*tile_gnu_pltenter) (ElfW(Sym) *, unsigned int, uintptr_t *, \
+                                   uintptr_t *, struct La_tile_regs *,  \
+                                   unsigned int *, const char *,        \
+                                   long int *)
+
+#define ARCH_PLTEXIT_MEMBERS \
+  ElfW(Addr) (*tile_gnu_pltexit) (ElfW(Sym) *, unsigned int, uintptr_t *, \
+                                  uintptr_t *, struct La_tile_regs *,   \
+                                  struct La_tile_retval *, const char *)
+
+#include_next <ldsodefs.h>
+
+#endif
diff --git a/sysdeps/tile/libc-tls.c b/sysdeps/tile/libc-tls.c
new file mode 100644
index 0000000..04d26a7
--- /dev/null
+++ b/sysdeps/tile/libc-tls.c
@@ -0,0 +1,68 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <csu/libc-tls.c>
+
+#define REQUEST_ASSEMBLER_MACROS
+#include <sysdep.h>
+#include <dl-tls.h>
+#include <tcb-offsets.h>
+
+#ifndef __OPTIMIZE__
+# error "Must compile with optimization to avoid frame setup."
+#endif
+
+/* On TILE, 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.  Written in assembly so that it conforms to the
+   ABI requirement that only registers r25..r29 can be clobbered.  */
+
+void *
+__tls_get_addr (tls_index *ti)
+{
+  /* dtv_t *dtv = THREAD_DTV ();
+     return (char *) dtv[1].pointer.val + ti->ti_offset;  */
+
+  void *retval;
+#define _helper(_ADDI_PTR, _ADD_PTR, _LD_PTR)                           \
+  asm("{"                                                               \
+       #_ADDI_PTR" r29, tp, %1\n"                                       \
+       #_ADDI_PTR" r0, r0, %2\n"                                        \
+      "}\n"                                                             \
+      #_LD_PTR" r29, r29\n"		/* r29 = dtv */                 \
+      "{\n"                                                             \
+      #_LD_PTR" r0, r0\n"		/* r0 = ti->ti_offset */        \
+      #_ADDI_PTR" r29, r29, %3\n"	/* r29 = &dtv[1] */             \
+      "}\n"                                                             \
+      #_LD_PTR" r29, r29\n"		/* r29 = dtv[1].pointer.val */  \
+      "{\n"                                                             \
+       #_ADD_PTR" r0, r0, r29\n"                                        \
+      " jrp lr\n"                                                       \
+      "}\n"                                                             \
+      : "=R00" (retval)                                                  \
+      : "n" (DTV_OFFSET), "n" (sizeof(void*)), "n" (sizeof(dtv_t)), "R00" (ti)\
+      : "r29")
+#define helper(_ADDI_PTR, _ADD_PTR, _LD_PTR)    \
+  _helper (_ADDI_PTR, _ADD_PTR, _LD_PTR)
+
+  helper (ADDI_PTR, ADD_PTR, LD_PTR);
+
+  return retval;
+}
diff --git a/sysdeps/tile/machine-gmon.h b/sysdeps/tile/machine-gmon.h
new file mode 100644
index 0000000..473707b
--- /dev/null
+++ b/sysdeps/tile/machine-gmon.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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.  */
+
+#define _MCOUNT_DECL(from, self) \
+ void __mcount_internal (u_long from, u_long self)
+
+/* Call __mcount_internal with our the return PC for our caller, and
+   the return PC our caller will return to.  Empty since we use an
+   assembly stub instead. */
+#define MCOUNT
diff --git a/sysdeps/tile/nptl/Makefile b/sysdeps/tile/nptl/Makefile
new file mode 100644
index 0000000..24990a2
--- /dev/null
+++ b/sysdeps/tile/nptl/Makefile
@@ -0,0 +1,21 @@
+# Copyright (C) 2002, 2003 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.
+
+ifeq ($(subdir),csu)
+gen-as-const-headers += tcb-offsets.sym
+endif
diff --git a/sysdeps/tile/nptl/pthread_spin_lock.c b/sysdeps/tile/nptl/pthread_spin_lock.c
new file mode 100644
index 0000000..ee05562
--- /dev/null
+++ b/sysdeps/tile/nptl/pthread_spin_lock.c
@@ -0,0 +1,57 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 "pthreadP.h"
+#include <arch/spr_def.h>
+#include <atomic.h>
+
+/* Bound point for bounded exponential backoff */
+#define BACKOFF_MAX 2048
+
+/* Initial cycle delay for exponential backoff */
+#define BACKOFF_START 32
+
+#ifdef __tilegx__
+/* Use cmpexch() after the initial fast-path exch to avoid
+   invalidating the cache line of the lock holder.  */
+# define TNS(p) atomic_exchange_acq((p), 1)
+# define CMPTNS(p) atomic_compare_and_exchange_val_acq((p), 1, 0)
+#else
+# define TNS(p) __insn_tns(p)
+# define CMPTNS(p) __insn_tns(p)
+# define SPR_CYCLE SPR_CYCLE_LOW   /* The low 32 bits are sufficient. */
+#endif
+
+int
+pthread_spin_lock (pthread_spinlock_t *lock)
+{
+  if (__builtin_expect (TNS (lock) != 0, 0))
+    {
+      unsigned int backoff = BACKOFF_START;
+      while (CMPTNS (lock) != 0)
+        {
+          unsigned int start = __insn_mfspr (SPR_CYCLE);
+          while (__insn_mfspr (SPR_CYCLE) - start < backoff)
+            ;
+          if (backoff < BACKOFF_MAX)
+            backoff *= 2;
+        }
+    }
+  return 0;
+}
diff --git a/sysdeps/tile/nptl/pthread_spin_trylock.c b/sysdeps/tile/nptl/pthread_spin_trylock.c
new file mode 100644
index 0000000..f111cc7
--- /dev/null
+++ b/sysdeps/tile/nptl/pthread_spin_trylock.c
@@ -0,0 +1,33 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 "pthreadP.h"
+#include <errno.h>
+
+#ifdef __tilegx__
+#define TNS(p) __insn_exch4((p), 1)
+#else
+#define TNS(p) __insn_tns(p)
+#endif
+
+int
+pthread_spin_trylock (pthread_spinlock_t *lock)
+{
+  return (TNS (lock) == 0) ? 0 : EBUSY;
+}
diff --git a/sysdeps/tile/nptl/pthreaddef.h b/sysdeps/tile/nptl/pthreaddef.h
new file mode 100644
index 0000000..4ba1adb
--- /dev/null
+++ b/sysdeps/tile/nptl/pthreaddef.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <stdlib.h>
+#include <string.h>
+
+/* Default stack size.  */
+#define ARCH_STACK_DEFAULT_SIZE	(2 * 1024 * 1024)
+
+/* Required stack pointer alignment at beginning.  */
+#define STACK_ALIGN		16
+
+/* Minimal stack size after allocating thread descriptor and guard size.  */
+#define MINIMAL_REST_STACK	2048
+
+/* Alignment requirement for TCB.  */
+#define TCB_ALIGNMENT		16
+
+
+/* Location of current stack frame.  */
+#define CURRENT_STACK_FRAME	__builtin_frame_address (0)
+
+/* XXX Until we have a better place keep the definitions here.  */
+
+#define __exit_thread_inline(val) \
+  INLINE_SYSCALL (exit, 1, (val))
diff --git a/sysdeps/tile/nptl/tcb-offsets.sym b/sysdeps/tile/nptl/tcb-offsets.sym
new file mode 100644
index 0000000..6740bc9
--- /dev/null
+++ b/sysdeps/tile/nptl/tcb-offsets.sym
@@ -0,0 +1,17 @@
+#define SHARED  /* needed to get struct rtld_global from <ldsodefs.h> */
+#include <sysdep.h>
+#include <tls.h>
+#include <ldsodefs.h>
+
+--
+
+-- Abuse tls.h macros to derive offsets relative to the thread register.
+#define thread_offsetof(mem)	(long)(offsetof(struct pthread, mem) - TLS_TCB_OFFSET - TLS_PRE_TCB_SIZE)
+
+MULTIPLE_THREADS_OFFSET		thread_offsetof (header.multiple_threads)
+PID_OFFSET			thread_offsetof (pid)
+TID_OFFSET			thread_offsetof (tid)
+POINTER_GUARD			(offsetof (tcbhead_t, pointer_guard) - TLS_TCB_OFFSET - sizeof (tcbhead_t))
+FEEDBACK_DATA_OFFSET		(offsetof (tcbhead_t, feedback_data) - TLS_TCB_OFFSET - sizeof (tcbhead_t))
+DTV_OFFSET			(offsetof (tcbhead_t, dtv) - TLS_TCB_OFFSET - sizeof (tcbhead_t))
+TLS_GENERATION_OFFSET		offsetof (struct rtld_global, _dl_tls_generation)
diff --git a/sysdeps/tile/nptl/tls.h b/sysdeps/tile/nptl/tls.h
new file mode 100644
index 0000000..106451a
--- /dev/null
+++ b/sysdeps/tile/nptl/tls.h
@@ -0,0 +1,195 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 _TLS_H
+#define _TLS_H	1
+
+# include <dl-sysdep.h>
+
+#ifndef __ASSEMBLER__
+# include <stdbool.h>
+# include <stddef.h>
+# include <stdint.h>
+
+/* Type for the dtv.  */
+typedef union dtv
+{
+  size_t counter;
+  struct
+  {
+    void *val;
+    bool is_static;
+  } pointer;
+} dtv_t;
+
+#else /* __ASSEMBLER__ */
+# include <tcb-offsets.h>
+#endif /* __ASSEMBLER__ */
+
+
+#ifndef __ASSEMBLER__
+
+/* Get system call information.  */
+# include <sysdep.h>
+
+/* The TP points to the start of the thread blocks.  */
+# define TLS_DTV_AT_TP	1
+
+/* We use the multiple_threads field in the pthread struct */
+#define TLS_MULTIPLE_THREADS_IN_TCB	1
+
+/* Get the thread descriptor definition.  */
+# include <nptl/descr.h>
+
+/* The stack_guard is accessed directly by GCC -fstack-protector code,
+   so it is a part of public ABI.  The dtv and pointer_guard fields
+   are private.  */
+typedef struct
+{
+  void *feedback_data;
+  uintptr_t pointer_guard;
+  uintptr_t stack_guard;
+  dtv_t *dtv;
+} tcbhead_t;
+
+/* This is the size of the initial TCB.  Because our TCB is before the thread
+   pointer, we don't need this.  */
+# define TLS_INIT_TCB_SIZE	0
+
+/* Alignment requirements for the initial TCB.  */
+# define TLS_INIT_TCB_ALIGN	__alignof__ (struct pthread)
+
+/* This is the size of the TCB.  Because our TCB is before the thread
+   pointer, we don't need this.  */
+# define TLS_TCB_SIZE		0
+
+/* Alignment requirements for the TCB.  */
+# define TLS_TCB_ALIGN		__alignof__ (struct pthread)
+
+/* This is the size we need before TCB - actually, it includes the TCB.  */
+# define TLS_PRE_TCB_SIZE \
+  (sizeof (struct pthread)						      \
+   + ((sizeof (tcbhead_t) + TLS_TCB_ALIGN - 1) & ~(TLS_TCB_ALIGN - 1)))
+
+/* Return the thread descriptor (tp) for the current thread.  */
+register void *__thread_pointer asm ("tp");
+
+/* The thread pointer (in hardware register tp) points to the end of
+   the TCB.  The pthread_descr structure is immediately in front of the TCB.  */
+# define TLS_TCB_OFFSET	0
+
+/* Install the dtv pointer.  The pointer passed is to the element with
+   index -1 which contain the length.  */
+# define INSTALL_DTV(tcbp, dtvp) \
+  (((tcbhead_t *) (tcbp))[-1].dtv = (dtvp) + 1)
+
+/* Install new dtv for current thread.  */
+# define INSTALL_NEW_DTV(dtv) (THREAD_DTV() = (dtv))
+
+/* Return dtv of given thread descriptor.  */
+# define GET_DTV(tcbp)	(((tcbhead_t *) (tcbp))[-1].dtv)
+
+/* Code to initially initialize the thread pointer (tp).  */
+# define TLS_INIT_TP(tcbp, secondcall) \
+    (__thread_pointer = (char *)(tcbp) + TLS_TCB_OFFSET, NULL)
+
+/* Return the address of the dtv for the current thread.  */
+# define THREAD_DTV() \
+    (((tcbhead_t *) (__thread_pointer - TLS_TCB_OFFSET))[-1].dtv)
+
+/* Return the thread descriptor for the current thread.  */
+# define THREAD_SELF \
+    ((struct pthread *) (__thread_pointer \
+			 - TLS_TCB_OFFSET - TLS_PRE_TCB_SIZE))
+
+/* Magic for libthread_db to know how to do THREAD_SELF.  */
+#ifdef __tilegx__
+# define DB_THREAD_SELF \
+  REGISTER (64, 64, REG_TP * 8, - TLS_TCB_OFFSET - TLS_PRE_TCB_SIZE)
+#else
+# define DB_THREAD_SELF \
+  REGISTER (32, 32, REG_TP * 4, - TLS_TCB_OFFSET - TLS_PRE_TCB_SIZE)
+#endif
+
+/* Read member of the thread descriptor directly.  */
+# define THREAD_GETMEM(descr, member) (descr->member)
+
+/* Same as THREAD_GETMEM, but the member offset can be non-constant.  */
+# define THREAD_GETMEM_NC(descr, member, idx) \
+    (descr->member[idx])
+
+/* Set member of the thread descriptor directly.  */
+# define THREAD_SETMEM(descr, member, value) \
+    (descr->member = (value))
+
+/* Same as THREAD_SETMEM, but the member offset can be non-constant.  */
+# define THREAD_SETMEM_NC(descr, member, idx, value) \
+    (descr->member[idx] = (value))
+
+/* Set the stack guard field in TCB head.  */
+# define THREAD_SET_STACK_GUARD(value) \
+    (((tcbhead_t *) ((char *) __thread_pointer				      \
+		     - TLS_TCB_OFFSET))[-1].stack_guard = (value))
+# define THREAD_COPY_STACK_GUARD(descr) \
+    (((tcbhead_t *) ((char *) (descr)					      \
+		     + TLS_PRE_TCB_SIZE))[-1].stack_guard		      \
+     = ((tcbhead_t *) ((char *) __thread_pointer			      \
+		       - TLS_TCB_OFFSET))[-1].stack_guard)
+
+/* Set the pointer guard field in TCB head.  */
+# define THREAD_GET_POINTER_GUARD() \
+    (((tcbhead_t *) ((char *) __thread_pointer				      \
+		     - TLS_TCB_OFFSET))[-1].pointer_guard)
+# define THREAD_SET_POINTER_GUARD(value) \
+    (THREAD_GET_POINTER_GUARD () = (value))
+# define THREAD_COPY_POINTER_GUARD(descr) \
+    (((tcbhead_t *) ((char *) (descr)					      \
+		     + TLS_PRE_TCB_SIZE))[-1].pointer_guard		      \
+     = THREAD_GET_POINTER_GUARD())
+
+/* l_tls_offset == 0 is perfectly valid on Tile, so we have to use some
+   different value to mean unset l_tls_offset.  */
+# define NO_TLS_OFFSET		-1
+
+/* Get and set the global scope generation counter in struct pthread.  */
+#define THREAD_GSCOPE_FLAG_UNUSED 0
+#define THREAD_GSCOPE_FLAG_USED   1
+#define THREAD_GSCOPE_FLAG_WAIT   2
+#define THREAD_GSCOPE_RESET_FLAG() \
+  do									     \
+    { int __res								     \
+	= atomic_exchange_rel (&THREAD_SELF->header.gscope_flag,	     \
+			       THREAD_GSCOPE_FLAG_UNUSED);		     \
+      if (__res == THREAD_GSCOPE_FLAG_WAIT)				     \
+	lll_futex_wake (&THREAD_SELF->header.gscope_flag, 1, LLL_PRIVATE);   \
+    }									     \
+  while (0)
+#define THREAD_GSCOPE_SET_FLAG() \
+  do									     \
+    {									     \
+      THREAD_SELF->header.gscope_flag = THREAD_GSCOPE_FLAG_USED;	     \
+      atomic_write_barrier ();						     \
+    }									     \
+  while (0)
+#define THREAD_GSCOPE_WAIT() \
+  GL(dl_wait_lookup_done) ()
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* tls.h */
diff --git a/sysdeps/tile/preconfigure b/sysdeps/tile/preconfigure
new file mode 100644
index 0000000..61e1f96
--- /dev/null
+++ b/sysdeps/tile/preconfigure
@@ -0,0 +1,14 @@
+# This is a -*- sh -*-
+case "$machine" in
+    tilepro)
+	base_machine=tile machine=tile/tilepro ;;
+    tilegx)
+	base_machine=tile machine=tile/tilegx/tilegx64 ;;
+    tilegx32)
+	base_machine=tile machine=tile/tilegx/tilegx32 ;;
+esac
+case "$machine" in
+    tile*)
+	libc_commonpagesize=0x10000
+	libc_relro_required=yes ;;
+esac
diff --git a/sysdeps/tile/s_fma.c b/sysdeps/tile/s_fma.c
new file mode 100644
index 0000000..355b4ed
--- /dev/null
+++ b/sysdeps/tile/s_fma.c
@@ -0,0 +1,3 @@
+/* Although tile uses ieee754/dbl-64, it does not support the
+   rounding modes required to use the standard dbl-64 s_fma.c.  */
+#include <math/s_fma.c>
diff --git a/sysdeps/tile/s_fmaf.c b/sysdeps/tile/s_fmaf.c
new file mode 100644
index 0000000..caa8e40
--- /dev/null
+++ b/sysdeps/tile/s_fmaf.c
@@ -0,0 +1,3 @@
+/* Although tile uses ieee754/dbl-64, it does not support the
+   rounding modes required to use the standard dbl-64 s_fmaf.c.  */
+#include <math/s_fmaf.c>
diff --git a/sysdeps/tile/setjmp.S b/sysdeps/tile/setjmp.S
new file mode 100644
index 0000000..950e4ca
--- /dev/null
+++ b/sysdeps/tile/setjmp.S
@@ -0,0 +1,47 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <sysdep.h>
+#include <jmpbuf-offsets.h>
+#include <feedback-asm.h>
+
+	.text
+
+	/* Keep traditional entry points in with sigsetjmp(). */
+ENTRY(setjmp)
+	{ movei r1, 1; j 1f }
+END(setjmp)
+
+ENTRY(_setjmp)
+	{ movei r1, 0; j 1f }
+END(_setjmp)
+libc_hidden_def (_setjmp)
+
+ENTRY(__sigsetjmp)
+1:
+	move r2, r0
+
+#define SAVE(r) { ST r2, r ; ADDI_PTR r2, r2, REGSIZE }
+	FOR_EACH_CALLEE_SAVED_REG(SAVE)
+
+	mfspr r3, INTERRUPT_CRITICAL_SECTION
+	ST r2, r3
+	j plt(__sigjmp_save)
+	jrp lr   /* Keep the backtracer happy. */
+END(__sigsetjmp)
diff --git a/sysdeps/tile/stackinfo.h b/sysdeps/tile/stackinfo.h
new file mode 100644
index 0000000..e3233ac
--- /dev/null
+++ b/sysdeps/tile/stackinfo.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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.  */
+
+/* This file contains a bit of information about the stack allocation
+   of the processor.  */
+
+#ifndef _STACKINFO_H
+#define _STACKINFO_H	1
+
+#include <elf.h>
+
+/* On tile the stack grows down.  */
+#define _STACK_GROWS_DOWN	1
+
+/* Default to a non-executable stack. */
+#define DEFAULT_STACK_PERMS (PF_R|PF_W)
+
+/* Access to the stack pointer.  The macros are used in alloca_account
+   for which they need to act as barriers as well, hence the additional
+   (unnecessary) parameters.  */
+#define stackinfo_get_sp() \
+  ({ void *p__; asm volatile ("move %0, sp" : "=r" (p__)); p__; })
+#if defined __tilegx__ && __WORDSIZE == 32
+#define __stackinfo_sub "subx"
+#else
+#define __stackinfo_sub "sub"
+#endif
+#define stackinfo_sub_sp(ptr)                                  \
+  ({ ptrdiff_t d__;                                             \
+    asm volatile (__stackinfo_sub " %0, %0, sp" : "=r" (d__) : "0" (ptr));  \
+    d__; })
+
+#endif /* stackinfo.h */
diff --git a/sysdeps/tile/sysdep.h b/sysdeps/tile/sysdep.h
new file mode 100644
index 0000000..57d8260
--- /dev/null
+++ b/sysdeps/tile/sysdep.h
@@ -0,0 +1,118 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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/sysdep.h>
+#include <bits/wordsize.h>
+#include <arch/abi.h>
+
+#ifndef HAVE_ELF
+# error "ELF is assumed."
+#endif
+
+#ifndef NO_UNDERSCORES
+# error "User-label prefix (underscore) assumed absent."
+#endif
+
+#if defined __ASSEMBLER__ || defined REQUEST_ASSEMBLER_MACROS
+
+/* Make use of .type and .size directives.  */
+#define ASM_TYPE_DIRECTIVE(name,typearg) .type name,typearg;
+#define ASM_SIZE_DIRECTIVE(name) .size name,.-name;
+
+/* Define an entry point visible from C.  */
+#define	ENTRY(name)							      \
+  ASM_GLOBAL_DIRECTIVE C_SYMBOL_NAME(name);				      \
+  ASM_TYPE_DIRECTIVE (C_SYMBOL_NAME(name),@function)			      \
+  .align 8;								      \
+  C_LABEL(name)								      \
+  cfi_startproc;							      \
+  CALL_MCOUNT
+
+#undef	END
+#define END(name)							      \
+  cfi_endproc;								      \
+  ASM_SIZE_DIRECTIVE(name)
+
+/* Since C identifiers are not normally prefixed with an underscore
+   on this system, the asm identifier `syscall_error' intrudes on the
+   C name space.  Make sure we use an innocuous name.  */
+#define	syscall_error	__syscall_error
+#define mcount		_mcount
+
+/* If compiled for profiling, call `mcount' at the start of each function.
+   The mcount code requires the caller PC in r10.  The `mcount' function
+   sets lr back to the value r10 had on entry when it returns.  */
+#ifdef	PROF
+#define CALL_MCOUNT { move r10, lr; jal mcount }
+#else
+#define CALL_MCOUNT             /* Do nothing.  */
+#endif
+
+/* Local label name for asm code. */
+#define L(name)		.L##name
+
+/* Specify the size in bytes of a machine register.  */
+#ifdef __tilegx__
+#define REGSIZE		8
+#else
+#define REGSIZE		4
+#endif
+
+/* Support a limited form of shared assembly between tile and tilegx.
+   The presumption is that LD/ST are used for manipulating registers.
+   Since opcode parsing is case-insensitive, we don't need to provide
+   definitions for these on tilegx.  */
+#ifndef __tilegx__
+#define LD		lw
+#define LD4U		lw
+#define ST		sw
+#define ST4		sw
+#define BNEZ		bnz
+#define BEQZ		bz
+#define BEQZT		bzt
+#define BGTZ		bgz
+#define CMPEQI		seqi
+#define CMPEQ		seq
+#define CMOVEQZ		mvz
+#define CMOVNEZ		mvnz
+#endif
+
+/* Provide "pointer-oriented" instruction variants.  These differ not
+   just for tile vs tilegx, but also for tilegx -m64 vs -m32.  */
+#if defined __tilegx__ && __WORDSIZE == 32
+#define ADD_PTR		addx
+#define ADDI_PTR	addxi
+#define ADDLI_PTR	addxli
+#define LD_PTR		ld4s
+#define ST_PTR		st4
+#define SHL_PTR_ADD	shl2add
+#else
+#define ADD_PTR		add
+#define ADDI_PTR	addi
+#define ADDLI_PTR	addli
+#define LD_PTR		LD
+#define ST_PTR		ST
+#ifdef __tilegx__
+#define SHL_PTR_ADD	shl3add
+#else
+#define SHL_PTR_ADD	s2a
+#endif
+#endif
+
+#endif /* __ASSEMBLER__ */
diff --git a/sysdeps/tile/tilegx/Makefile b/sysdeps/tile/tilegx/Makefile
new file mode 100644
index 0000000..49517fa
--- /dev/null
+++ b/sysdeps/tile/tilegx/Makefile
@@ -0,0 +1,3 @@
+ifeq ($(subdir),debug)
+sysdep_routines += tile-desc
+endif
diff --git a/sysdeps/tile/tilegx/bits/atomic.h b/sysdeps/tile/tilegx/bits/atomic.h
new file mode 100644
index 0000000..1cb4e2b
--- /dev/null
+++ b/sysdeps/tile/tilegx/bits/atomic.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 _BITS_ATOMIC_H
+#define _BITS_ATOMIC_H	1
+
+#include <arch/spr_def.h>
+
+/* Pick appropriate 8- or 4-byte instruction. */
+#define __atomic_update(mem, v, op)                                     \
+  ((__typeof (*(mem))) (__typeof (*(mem) - *(mem)))                     \
+   ((sizeof (*(mem)) == 8) ?                                            \
+    __insn_##op ((void *) (mem), (int64_t) (__typeof((v) - (v))) (v)) : \
+    (sizeof (*(mem)) == 4) ?                                            \
+    __insn_##op##4 ((void *) (mem), (int32_t) (__typeof ((v) - (v))) (v)) : \
+    __atomic_error_bad_argument_size()))
+
+#define atomic_compare_and_exchange_val_acq(mem, n, o)                  \
+  ({ __insn_mtspr (SPR_CMPEXCH_VALUE, (int64_t) (__typeof ((o) - (o))) (o)); \
+     __atomic_update (mem, n, cmpexch); })
+#define atomic_exchange_acq(mem, newvalue) \
+  __atomic_update (mem, newvalue, exch)
+#define atomic_exchange_and_add(mem, value) \
+  __atomic_update (mem, value, fetchadd)
+#define atomic_and_val(mem, mask) \
+  __atomic_update (mem, mask, fetchand)
+#define atomic_or_val(mem, mask) \
+  __atomic_update (mem, mask, fetchor)
+#define atomic_decrement_if_positive(mem) \
+  __atomic_update (mem, -1, fetchaddgez)
+
+#include <sysdeps/tile/bits/atomic.h>
+
+#endif /* bits/atomic.h */
diff --git a/sysdeps/tile/tilegx/bits/wordsize.h b/sysdeps/tile/tilegx/bits/wordsize.h
new file mode 100644
index 0000000..78f3481
--- /dev/null
+++ b/sysdeps/tile/tilegx/bits/wordsize.h
@@ -0,0 +1,8 @@
+/* Determine the wordsize from the preprocessor defines.  */
+
+#ifdef __LP64__
+# define __WORDSIZE	64
+# define __WORDSIZE_COMPAT32	1
+#else
+# define __WORDSIZE	32
+#endif
diff --git a/sysdeps/tile/tilegx/memchr.c b/sysdeps/tile/tilegx/memchr.c
new file mode 100644
index 0000000..52a20b6
--- /dev/null
+++ b/sysdeps/tile/tilegx/memchr.c
@@ -0,0 +1,74 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <string.h>
+#include <stdint.h>
+#include "string-endian.h"
+
+void *
+__memchr (const void *s, int c, size_t n)
+{
+  const uint64_t *last_word_ptr;
+  const uint64_t *p;
+  const char *last_byte_ptr;
+  uintptr_t s_int;
+  uint64_t goal, before_mask, v, bits;
+  char *ret;
+
+  if (__builtin_expect (n == 0, 0))
+    {
+      /* Don't dereference any memory if the array is empty. */
+      return NULL;
+    }
+
+  /* Get an aligned pointer. */
+  s_int = (uintptr_t) s;
+  p = (const uint64_t *) (s_int & -8);
+
+  /* Create eight copies of the byte for which we are looking. */
+  goal = 0x0101010101010101ULL * (uint8_t) c;
+
+  /* Read the first word, but munge it so that bytes before the array
+     will not match goal.  */
+  before_mask = MASK (s_int);
+  v = (*p | before_mask) ^ (goal & before_mask);
+
+  /* Compute the address of the last byte. */
+  last_byte_ptr = (const char *) s + n - 1;
+
+  /* Compute the address of the word containing the last byte. */
+  last_word_ptr = (const uint64_t *) ((uintptr_t) last_byte_ptr & -8);
+
+  while ((bits = __insn_v1cmpeq (v, goal)) == 0)
+    {
+      if (__builtin_expect (p == last_word_ptr, 0))
+        {
+          /* We already read the last word in the array, so give up.  */
+          return NULL;
+        }
+      v = *++p;
+    }
+
+  /* We found a match, but it might be in a byte past the end
+     of the array.  */
+  ret = ((char *) p) + (CFZ (bits) >> 3);
+  return (ret <= last_byte_ptr) ? ret : NULL;
+}
+weak_alias (__memchr, memchr)
+libc_hidden_builtin_def (memchr)
diff --git a/sysdeps/tile/tilegx/memcpy.c b/sysdeps/tile/tilegx/memcpy.c
new file mode 100644
index 0000000..35495be
--- /dev/null
+++ b/sysdeps/tile/tilegx/memcpy.c
@@ -0,0 +1,196 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <arch/chip.h>
+
+/* Must be 8 bytes in size. */
+#define word_t uint64_t
+
+/* How many cache lines ahead should we prefetch? */
+#define PREFETCH_LINES_AHEAD 3
+
+void *
+__memcpy (void *__restrict dstv, const void *__restrict srcv, size_t n)
+{
+  char *__restrict dst1 = (char *) dstv;
+  const char *__restrict src1 = (const char *) srcv;
+  const char *__restrict src1_end;
+  const char *__restrict prefetch;
+  word_t *__restrict dst8; /* 8-byte pointer to destination memory. */
+  word_t final; /* Final bytes to write to trailing word, if any */
+  long i;
+
+  if (n < 16)
+    {
+      for (; n; n--)
+        *dst1++ = *src1++;
+      return dstv;
+    }
+
+  /* Locate the end of source memory we will copy.  Don't prefetch
+     past this.  */
+  src1_end = src1 + n - 1;
+
+  /* Prefetch ahead a few cache lines, but not past the end. */
+  prefetch = src1;
+  for (i = 0; i < PREFETCH_LINES_AHEAD; i++)
+    {
+      __insn_prefetch (prefetch);
+      prefetch += CHIP_L2_LINE_SIZE ();
+      prefetch = (prefetch > src1_end) ? prefetch : src1;
+    }
+
+  /* Copy bytes until dst is word-aligned. */
+  for (; (uintptr_t) dst1 & (sizeof (word_t) - 1); n--)
+    *dst1++ = *src1++;
+
+  /* 8-byte pointer to destination memory. */
+  dst8 = (word_t *) dst1;
+
+  if (__builtin_expect ((uintptr_t) src1 & (sizeof (word_t) - 1), 0))
+    {
+      /* Misaligned copy.  Copy 8 bytes at a time, but don't bother
+         with other fanciness.
+         TODO: Consider prefetching and using wh64 as well.  */
+
+      /* Create an aligned src8. */
+      const word_t *__restrict src8 =
+        (const word_t *) ((uintptr_t) src1 & -sizeof (word_t));
+      word_t b;
+
+      word_t a = *src8++;
+      for (; n >= sizeof (word_t); n -= sizeof (word_t))
+        {
+          b = *src8++;
+          a = __insn_dblalign (a, b, src1);
+          *dst8++ = a;
+          a = b;
+        }
+
+      if (n == 0)
+        return dstv;
+
+      b = ((const char *) src8 <= src1_end) ? *src8 : 0;
+
+      /* Final source bytes to write to trailing partial word, if any. */
+      final = __insn_dblalign (a, b, src1);
+    }
+  else
+    {
+      /* Aligned copy. */
+
+      const word_t *__restrict src8 = (const word_t *) src1;
+
+      /* src8 and dst8 are both word-aligned. */
+      if (n >= CHIP_L2_LINE_SIZE ())
+        {
+          /* Copy until 'dst' is cache-line-aligned. */
+          for (; (uintptr_t) dst8 & (CHIP_L2_LINE_SIZE () - 1);
+               n -= sizeof (word_t))
+            *dst8++ = *src8++;
+
+          for (; n >= CHIP_L2_LINE_SIZE ();)
+            {
+              __insn_wh64 (dst8);
+
+              /* Prefetch and advance to next line to prefetch, but
+                 don't go past the end.  */
+              __insn_prefetch (prefetch);
+              prefetch += CHIP_L2_LINE_SIZE ();
+              prefetch = (prefetch > src1_end) ? prefetch :
+                (const char *) src8;
+
+              /* Copy an entire cache line.  Manually unrolled to
+                 avoid idiosyncracies of compiler unrolling.  */
+#define COPY_WORD(offset) ({ dst8[offset] = src8[offset]; n -= 8; })
+              COPY_WORD (0);
+              COPY_WORD (1);
+              COPY_WORD (2);
+              COPY_WORD (3);
+              COPY_WORD (4);
+              COPY_WORD (5);
+              COPY_WORD (6);
+              COPY_WORD (7);
+#if CHIP_L2_LINE_SIZE() != 64
+# error "Fix code that assumes particular L2 cache line size."
+#endif
+
+              dst8 += CHIP_L2_LINE_SIZE () / sizeof (word_t);
+              src8 += CHIP_L2_LINE_SIZE () / sizeof (word_t);
+            }
+        }
+
+      for (; n >= sizeof (word_t); n -= sizeof (word_t))
+        *dst8++ = *src8++;
+
+      if (__builtin_expect (n == 0, 1))
+        return dstv;
+
+      final = *src8;
+    }
+
+  /* n != 0 if we get here.  Write out any trailing bytes. */
+  dst1 = (char *) dst8;
+#ifndef __BIG_ENDIAN__
+  if (n & 4)
+    {
+      *(uint32_t *) dst1 = final;
+      dst1 += 4;
+      final >>= 32;
+      n &= 3;
+    }
+  if (n & 2)
+    {
+      *(uint16_t *) dst1 = final;
+      dst1 += 2;
+      final >>= 16;
+      n &= 1;
+    }
+  if (n)
+    *(uint8_t *) dst1 = final;
+#else
+  if (n & 4)
+    {
+      *(uint32_t *) dst1 = final >> 32;
+      dst1 += 4;
+    }
+  else
+    {
+      final >>= 32;
+    }
+  if (n & 2)
+    {
+      *(uint16_t *) dst1 = final >> 16;
+      dst1 += 2;
+    }
+  else
+    {
+      final >>= 16;
+    }
+  if (n & 1)
+    *(uint8_t *) dst1 = final >> 8;
+#endif
+
+  return dstv;
+}
+weak_alias (__memcpy, memcpy)
+libc_hidden_builtin_def (memcpy)
diff --git a/sysdeps/tile/tilegx/memset.c b/sysdeps/tile/tilegx/memset.c
new file mode 100644
index 0000000..ddde05f
--- /dev/null
+++ b/sysdeps/tile/tilegx/memset.c
@@ -0,0 +1,151 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <arch/chip.h>
+#include <string.h>
+#include <stdint.h>
+
+void *
+__memset (void *s, int c, size_t n)
+{
+  uint64_t *out64;
+  int n64, to_align64;
+  uint64_t v64;
+  uint8_t *out8 = s;
+
+  /* Experimentation shows that a trivial tight loop is a win up until
+     around a size of 20, where writing a word at a time starts to win.  */
+#define BYTE_CUTOFF 20
+
+#if BYTE_CUTOFF < 7
+  /* This must be at least at least this big, or some code later
+     on doesn't work.  */
+# error "BYTE_CUTOFF is too small."
+#endif
+
+  if (n < BYTE_CUTOFF)
+    {
+      /* Strangely, this turns out to be the tightest way to write
+         this loop.  */
+      if (n != 0)
+        {
+          do
+            {
+              /* Strangely, combining these into one line performs worse.  */
+              *out8 = c;
+              out8++;
+            }
+          while (--n != 0);
+        }
+
+      return s;
+    }
+
+  /* Align 'out8'. We know n >= 7 so this won't write past the end.  */
+  while (((uintptr_t) out8 & 7) != 0)
+    {
+      *out8++ = c;
+      --n;
+    }
+
+  /* Align 'n'. */
+  while (n & 7)
+    out8[--n] = c;
+
+  out64 = (uint64_t *) out8;
+  n64 = n >> 3;
+
+  /* Tile input byte out to 64 bits. */
+  v64 = 0x0101010101010101ULL * (uint8_t) c;
+
+  /* This must be at least 8 or the following loop doesn't work. */
+#define CACHE_LINE_SIZE_IN_DOUBLEWORDS (CHIP_L2_LINE_SIZE() / 8)
+
+  /* Determine how many words we need to emit before the 'out32'
+     pointer becomes aligned modulo the cache line size.  */
+  to_align64 = (-((uintptr_t) out64 >> 3)) &
+    (CACHE_LINE_SIZE_IN_DOUBLEWORDS - 1);
+
+  /* Only bother aligning and using wh64 if there is at least
+     one full cache line to process.  This check also prevents
+     overrunning the end of the buffer with alignment words.  */
+  if (to_align64 <= n64 - CACHE_LINE_SIZE_IN_DOUBLEWORDS)
+    {
+      int lines_left;
+
+      /* Align out64 mod the cache line size so we can use wh64. */
+      n64 -= to_align64;
+      for (; to_align64 != 0; to_align64--)
+        {
+          *out64 = v64;
+          out64++;
+        }
+
+      /* Use unsigned divide to turn this into a right shift. */
+      lines_left = (unsigned) n64 / CACHE_LINE_SIZE_IN_DOUBLEWORDS;
+
+      do
+        {
+          /* Only wh64 a few lines at a time, so we don't exceed the
+             maximum number of victim lines.  */
+          int x = ((lines_left < CHIP_MAX_OUTSTANDING_VICTIMS ()) ? lines_left
+                   : CHIP_MAX_OUTSTANDING_VICTIMS ());
+          uint64_t *wh = out64;
+          int i = x;
+          int j;
+
+          lines_left -= x;
+
+          do
+            {
+              __insn_wh64 (wh);
+              wh += CACHE_LINE_SIZE_IN_DOUBLEWORDS;
+            }
+          while (--i);
+
+          for (j = x * (CACHE_LINE_SIZE_IN_DOUBLEWORDS / 4); j != 0; j--)
+            {
+              *out64++ = v64;
+              *out64++ = v64;
+              *out64++ = v64;
+              *out64++ = v64;
+            }
+        }
+      while (lines_left != 0);
+
+      /* We processed all full lines above, so only this many
+         words remain to be processed.  */
+      n64 &= CACHE_LINE_SIZE_IN_DOUBLEWORDS - 1;
+    }
+
+  /* Now handle any leftover values. */
+  if (n64 != 0)
+    {
+      do
+        {
+          *out64 = v64;
+          out64++;
+        }
+      while (--n64 != 0);
+    }
+
+  return s;
+}
+weak_alias (__memset, memset)
+libc_hidden_builtin_def (memset)
diff --git a/sysdeps/tile/tilegx/memusage.h b/sysdeps/tile/tilegx/memusage.h
new file mode 100644
index 0000000..7afb50a
--- /dev/null
+++ b/sysdeps/tile/tilegx/memusage.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <arch/spr_def.h>
+
+#define GETSP() ({ register uintptr_t stack_ptr asm ("sp"); stack_ptr; })
+
+#define GETTIME(low,high)                       \
+  {                                             \
+    uint64_t cycles = __insn_mfspr (SPR_CYCLE); \
+    low = cycles & 0xffffffff;                  \
+    high = cycles >> 32;                        \
+  }
+
+#include <sysdeps/generic/memusage.h>
diff --git a/sysdeps/tile/tilegx/rawmemchr.c b/sysdeps/tile/tilegx/rawmemchr.c
new file mode 100644
index 0000000..cd0f2fd
--- /dev/null
+++ b/sysdeps/tile/tilegx/rawmemchr.c
@@ -0,0 +1,46 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <string.h>
+#include <stdint.h>
+#include "string-endian.h"
+
+void *
+__rawmemchr (const void *s, int c)
+{
+  /* Get an aligned pointer. */
+  const uintptr_t s_int = (uintptr_t) s;
+  const uint64_t *p = (const uint64_t *) (s_int & -8);
+
+  /* Create eight copies of the byte for which we are looking. */
+  const uint64_t goal = 0x0101010101010101ULL * (uint8_t) c;
+
+  /* Read the first word, but munge it so that bytes before the array
+     will not match goal.  */
+  const uint64_t before_mask = MASK (s_int);
+  uint64_t v = (*p | before_mask) ^ (goal & before_mask);
+
+  uint64_t bits;
+  while ((bits = __insn_v1cmpeq (v, goal)) == 0)
+    v = *++p;
+
+  return ((char *) p) + (CFZ (bits) >> 3);
+}
+libc_hidden_def (__rawmemchr)
+weak_alias (__rawmemchr, rawmemchr)
diff --git a/sysdeps/tile/tilegx/strchr.c b/sysdeps/tile/tilegx/strchr.c
new file mode 100644
index 0000000..2dc7506
--- /dev/null
+++ b/sysdeps/tile/tilegx/strchr.c
@@ -0,0 +1,68 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <string.h>
+#include <stdint.h>
+#include "string-endian.h"
+
+#undef strchr
+
+char *
+strchr (const char *s, int c)
+{
+  int z, g;
+
+  /* Get an aligned pointer. */
+  const uintptr_t s_int = (uintptr_t) s;
+  const uint64_t *p = (const uint64_t *) (s_int & -8);
+
+  /* Create eight copies of the byte for which we are looking. */
+  const uint64_t goal = 0x0101010101010101ULL * (uint8_t) c;
+
+  /* Read the first aligned word, but force bytes before the string to
+     match neither zero nor goal (we make sure the high bit of each byte
+     is 1, and the low 7 bits are all the opposite of the goal byte).  */
+  const uint64_t before_mask = MASK (s_int);
+  uint64_t v = (*p | before_mask) ^ (goal & __insn_v1shrui (before_mask, 1));
+
+  uint64_t zero_matches, goal_matches;
+  while (1)
+    {
+      /* Look for a terminating '\0'. */
+      zero_matches = __insn_v1cmpeqi (v, 0);
+
+      /* Look for the goal byte. */
+      goal_matches = __insn_v1cmpeq (v, goal);
+
+      if (__builtin_expect ((zero_matches | goal_matches) != 0, 0))
+        break;
+
+      v = *++p;
+    }
+
+  z = CFZ (zero_matches);
+  g = CFZ (goal_matches);
+
+  /* If we found c before '\0' we got a match. Note that if c == '\0'
+     then g == z, and we correctly return the address of the '\0'
+     rather than NULL.  */
+  return (g <= z) ? ((char *) p) + (g >> 3) : NULL;
+}
+weak_alias (strchr, index)
+libc_hidden_builtin_def (strchr)
diff --git a/sysdeps/tile/tilegx/strchrnul.c b/sysdeps/tile/tilegx/strchrnul.c
new file mode 100644
index 0000000..bb165b5
--- /dev/null
+++ b/sysdeps/tile/tilegx/strchrnul.c
@@ -0,0 +1,65 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <string.h>
+#include <stdint.h>
+#include "string-endian.h"
+
+char *
+__strchrnul (const char *s, int c)
+{
+  int z, g;
+
+  /* Get an aligned pointer. */
+  const uintptr_t s_int = (uintptr_t) s;
+  const uint64_t *p = (const uint64_t *) (s_int & -8);
+
+  /* Create eight copies of the byte for which we are looking. */
+  const uint64_t goal = 0x0101010101010101ULL * (uint8_t) c;
+
+  /* Read the first aligned word, but force bytes before the string to
+     match neither zero nor goal (we make sure the high bit of each byte
+     is 1, and the low 7 bits are all the opposite of the goal byte).  */
+  const uint64_t before_mask = MASK (s_int);
+  uint64_t v = (*p | before_mask) ^ (goal & __insn_v1shrui (before_mask, 1));
+
+  uint64_t zero_matches, goal_matches;
+  while (1)
+    {
+      /* Look for a terminating '\0'. */
+      zero_matches = __insn_v1cmpeqi (v, 0);
+
+      /* Look for the goal byte. */
+      goal_matches = __insn_v1cmpeq (v, goal);
+
+      if (__builtin_expect ((zero_matches | goal_matches) != 0, 0))
+        break;
+
+      v = *++p;
+    }
+
+  z = CFZ (zero_matches);
+  g = CFZ (goal_matches);
+
+  /* Return a pointer to the NUL or goal, whichever is first. */
+  if (z < g)
+    g = z;
+  return ((char *) p) + (g >> 3);
+}
+weak_alias (__strchrnul, strchrnul)
diff --git a/sysdeps/tile/tilegx/string-endian.h b/sysdeps/tile/tilegx/string-endian.h
new file mode 100644
index 0000000..29ca793
--- /dev/null
+++ b/sysdeps/tile/tilegx/string-endian.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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.  */
+
+/* Provide a mask based on the pointer alignment that
+   sets up non-zero bytes before the beginning of the string.
+   The MASK expression works because shift counts are taken mod 64.
+   Also, specify how to count "first" and "last" bits
+   when the bits have been read as a word.  */
+
+#ifndef __BIG_ENDIAN__
+#define MASK(x) (__insn_shl(1ULL, (x << 3)) - 1)
+#define NULMASK(x) ((2ULL << x) - 1)
+#define CFZ(x) __insn_ctz(x)
+#define REVCZ(x) __insn_clz(x)
+#else
+#define MASK(x) (__insn_shl(-2LL, ((-x << 3) - 1)))
+#define NULMASK(x) (-2LL << (63 - x))
+#define CFZ(x) __insn_clz(x)
+#define REVCZ(x) __insn_ctz(x)
+#endif
diff --git a/sysdeps/tile/tilegx/strlen.c b/sysdeps/tile/tilegx/strlen.c
new file mode 100644
index 0000000..a729e99
--- /dev/null
+++ b/sysdeps/tile/tilegx/strlen.c
@@ -0,0 +1,40 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <string.h>
+#include <stdint.h>
+#include "string-endian.h"
+
+size_t
+strlen (const char *s)
+{
+  /* Get an aligned pointer. */
+  const uintptr_t s_int = (uintptr_t) s;
+  const uint64_t *p = (const uint64_t *) (s_int & -8);
+
+  /* Read and MASK the first word. */
+  uint64_t v = *p | MASK (s_int);
+
+  uint64_t bits;
+  while ((bits = __insn_v1cmpeqi (v, 0)) == 0)
+    v = *++p;
+
+  return ((const char *) p) + (CFZ (bits) >> 3) - s;
+}
+libc_hidden_builtin_def (strlen)
diff --git a/sysdeps/tile/tilegx/strrchr.c b/sysdeps/tile/tilegx/strrchr.c
new file mode 100644
index 0000000..9724d02
--- /dev/null
+++ b/sysdeps/tile/tilegx/strrchr.c
@@ -0,0 +1,69 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <string.h>
+#include <stdint.h>
+#include "string-endian.h"
+
+char *
+strrchr (const char *s, int c)
+{
+  /* Get an aligned pointer. */
+  const uintptr_t s_int = (uintptr_t) s;
+  const uint64_t *p = (const uint64_t *) (s_int & -8);
+
+  /* Create eight copies of the byte for which we are looking. */
+  const uint64_t goal = 0x0101010101010101ULL * (uint8_t) c;
+
+  /* Read the first aligned word, but force bytes before the string to
+     match neither zero nor goal (we make sure the high bit of each byte
+     is 1, and the low 7 bits are all the opposite of the goal byte).  */
+  const uint64_t before_mask = MASK (s_int);
+  uint64_t v = (*p | before_mask) ^ (goal & __insn_v1shrui (before_mask, 1));
+  const char *found = NULL;
+  uint64_t zero_matches, goal_matches;
+  while (1)
+    {
+      /* Look for a terminating '\0'. */
+      zero_matches = __insn_v1cmpeqi (v, 0);
+
+      /* Look for the goal byte. */
+      goal_matches = __insn_v1cmpeq (v, goal);
+
+      /* If we found the goal, record the last offset. */
+      if (__builtin_expect (goal_matches != 0, 0))
+        {
+          if (__builtin_expect (zero_matches != 0, 0))
+            {
+              /* Clear any goal after the first zero. */
+              int first_nul = CFZ (zero_matches);
+              goal_matches &= NULMASK (first_nul);
+            }
+          if (__builtin_expect (goal_matches != 0, 1))
+            found = ((char *) p) + 7 - (REVCZ (goal_matches) >> 3);
+        }
+
+      if (__builtin_expect (zero_matches != 0, 0))
+        return (char *) found;
+
+      v = *++p;
+    }
+}
+weak_alias (strrchr, rindex)
+libc_hidden_builtin_def (strrchr)
diff --git a/sysdeps/tile/tilegx/tilegx32/Implies b/sysdeps/tile/tilegx/tilegx32/Implies
new file mode 100644
index 0000000..993b7f4
--- /dev/null
+++ b/sysdeps/tile/tilegx/tilegx32/Implies
@@ -0,0 +1,3 @@
+tile/tilegx
+tile
+wordsize-32
diff --git a/sysdeps/tile/tilegx/tilegx64/Implies b/sysdeps/tile/tilegx/tilegx64/Implies
new file mode 100644
index 0000000..eb0686e
--- /dev/null
+++ b/sysdeps/tile/tilegx/tilegx64/Implies
@@ -0,0 +1,3 @@
+tile/tilegx
+tile
+wordsize-64
diff --git a/sysdeps/tile/tilepro/Implies b/sysdeps/tile/tilepro/Implies
new file mode 100644
index 0000000..709e1dc
--- /dev/null
+++ b/sysdeps/tile/tilepro/Implies
@@ -0,0 +1,2 @@
+tile
+wordsize-32
diff --git a/sysdeps/tile/tilepro/Makefile b/sysdeps/tile/tilepro/Makefile
new file mode 100644
index 0000000..49517fa
--- /dev/null
+++ b/sysdeps/tile/tilepro/Makefile
@@ -0,0 +1,3 @@
+ifeq ($(subdir),debug)
+sysdep_routines += tile-desc
+endif
diff --git a/sysdeps/tile/tilepro/bits/atomic.h b/sysdeps/tile/tilepro/bits/atomic.h
new file mode 100644
index 0000000..9d996cb
--- /dev/null
+++ b/sysdeps/tile/tilepro/bits/atomic.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 _BITS_ATOMIC_H
+#define _BITS_ATOMIC_H	1
+
+#include <asm/unistd.h>
+
+/* 32-bit integer compare-and-exchange. */
+static __inline __attribute__ ((always_inline))
+int __atomic_cmpxchg_32 (volatile int *mem, int newval, int oldval)
+{
+  int result;
+  __asm__ __volatile__ ("swint1"
+                        : "=R00" (result), "=m" (*mem)
+                        : "R10" (__NR_FAST_cmpxchg), "R00" (mem),
+                          "R01" (oldval), "R02" (newval), "m" (*mem)
+                        : "r20", "r21", "r22", "r23", "r24",
+                          "r25", "r26", "r27", "r28", "r29", "memory");
+  return result;
+}
+
+#define atomic_compare_and_exchange_val_acq(mem, n, o)                  \
+  ((__typeof (*(mem)))                                                  \
+   ((sizeof (*(mem)) == 4) ?                                            \
+    __atomic_cmpxchg_32 ((int *) (mem), (int) (n), (int) (o)) :         \
+    __atomic_error_bad_argument_size()))
+
+/* Atomically compute:
+     int old = *ptr;
+     *ptr = (old & mask) + addend;
+     return old;  */
+
+static __inline __attribute__ ((always_inline))
+int __atomic_update_32 (volatile int *mem, int mask, int addend)
+{
+  int result;
+  __asm__ __volatile__ ("swint1"
+                        : "=R00" (result), "=m" (*mem)
+                        : "R10" (__NR_FAST_atomic_update), "R00" (mem),
+                          "R01" (mask), "R02" (addend), "m" (*mem)
+                        : "r20", "r21", "r22", "r23", "r24",
+                          "r25", "r26", "r27", "r28", "r29", "memory");
+  return result;
+}
+
+/* Size-checked verson of __atomic_update_32. */
+#define __atomic_update(mem, mask, addend)                              \
+  ((__typeof (*(mem)))                                                  \
+   ((sizeof (*(mem)) == 4) ?                                            \
+    __atomic_update_32 ((int *) (mem), (int) (mask), (int) (addend)) :  \
+    __atomic_error_bad_argument_size ()))
+
+#define atomic_exchange_acq(mem, newvalue)              \
+  __atomic_update ((mem), 0, (newvalue))
+#define atomic_exchange_and_add(mem, value)             \
+  __atomic_update ((mem), -1, (value))
+#define atomic_and_val(mem, mask)                       \
+  __atomic_update ((mem), (mask), 0)
+#define atomic_or_val(mem, mask)                        \
+  ({ __typeof (mask) __att1_v = (mask);                 \
+    __atomic_update ((mem), ~__att1_v, __att1_v); })
+
+#include <sysdeps/tile/bits/atomic.h>
+
+#endif /* bits/atomic.h */
diff --git a/sysdeps/tile/tilepro/bits/wordsize.h b/sysdeps/tile/tilepro/bits/wordsize.h
new file mode 100644
index 0000000..da587a2
--- /dev/null
+++ b/sysdeps/tile/tilepro/bits/wordsize.h
@@ -0,0 +1,3 @@
+/* Determine the wordsize from the preprocessor defines.  */
+
+#define __WORDSIZE	32
diff --git a/sysdeps/tile/tilepro/memchr.c b/sysdeps/tile/tilepro/memchr.c
new file mode 100644
index 0000000..125fd0d
--- /dev/null
+++ b/sysdeps/tile/tilepro/memchr.c
@@ -0,0 +1,73 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <string.h>
+#include <stdint.h>
+
+void *
+__memchr (const void *s, int c, size_t n)
+{
+  const uint32_t *last_word_ptr;
+  const uint32_t *p;
+  const char *last_byte_ptr;
+  uintptr_t s_int;
+  uint32_t goal, before_mask, v, bits;
+  char *ret;
+
+  if (__builtin_expect (n == 0, 0))
+    {
+      /* Don't dereference any memory if the array is empty. */
+      return NULL;
+    }
+
+  /* Get an aligned pointer. */
+  s_int = (uintptr_t) s;
+  p = (const uint32_t *) (s_int & -4);
+
+  /* Create four copies of the byte for which we are looking. */
+  goal = 0x01010101 * (uint8_t) c;
+
+  /* Read the first word, but munge it so that bytes before the array
+     will not match goal.  Note that this shift count expression works
+     because we know shift counts are taken mod 32.  */
+  before_mask = (1 << (s_int << 3)) - 1;
+  v = (*p | before_mask) ^ (goal & before_mask);
+
+  /* Compute the address of the last byte. */
+  last_byte_ptr = (const char *) s + n - 1;
+
+  /* Compute the address of the word containing the last byte. */
+  last_word_ptr = (const uint32_t *) ((uintptr_t) last_byte_ptr & -4);
+
+  while ((bits = __insn_seqb (v, goal)) == 0)
+    {
+      if (__builtin_expect (p == last_word_ptr, 0))
+        {
+          /* We already read the last word in the array, so give up.  */
+          return NULL;
+        }
+      v = *++p;
+    }
+
+  /* We found a match, but it might be in a byte past the end of the array.  */
+  ret = ((char *) p) + (__insn_ctz (bits) >> 3);
+  return (ret <= last_byte_ptr) ? ret : NULL;
+}
+weak_alias (__memchr, memchr)
+libc_hidden_builtin_def (memchr)
diff --git a/sysdeps/tile/tilepro/memcpy.S b/sysdeps/tile/tilepro/memcpy.S
new file mode 100644
index 0000000..f481103
--- /dev/null
+++ b/sysdeps/tile/tilepro/memcpy.S
@@ -0,0 +1,399 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <arch/chip.h>
+#include <feedback-asm.h>
+#include <sysdep.h>
+
+	.text
+ENTRY (__memcpy)
+	FEEDBACK_ENTER(__memcpy)
+
+	/* r0 is the dest, r1 is the source, r2 is the size. */
+
+	/* Save aside original dest so we can return it at the end. */
+	{ sw sp, lr; move r23, r0; or r4, r0, r1 }
+	cfi_offset (lr, 0)
+
+	/* Check for an empty size. */
+	{ bz r2, .Ldone; andi r4, r4, 3 }
+
+	/* Check for an unaligned source or dest. */
+	{ bnz r4, .Lcopy_unaligned_maybe_many; addli r4, r2, -256 }
+
+.Lcheck_aligned_copy_size:
+	/* If we are copying < 256 bytes, branch to simple case. */
+	{ blzt r4, .Lcopy_8_check; slti_u r8, r2, 8 }
+
+	/* Copying >= 256 bytes, so jump to complex prefetching loop. */
+	{ andi r6, r1, 63; j .Lcopy_many }
+
+/* Aligned 4 byte at a time copy loop.  */
+
+.Lcopy_8_loop:
+	/* Copy two words at a time to hide load latency. */
+	{ lw r3, r1; addi r1, r1, 4; slti_u r8, r2, 16 }
+	{ lw r4, r1; addi r1, r1, 4 }
+	{ sw r0, r3; addi r0, r0, 4; addi r2, r2, -4 }
+	{ sw r0, r4; addi r0, r0, 4; addi r2, r2, -4 }
+.Lcopy_8_check:
+	{ bzt r8, .Lcopy_8_loop; slti_u r4, r2, 4 }
+
+	/* Copy odd leftover word, if any. */
+	{ bnzt r4, .Lcheck_odd_stragglers }
+	{ lw r3, r1; addi r1, r1, 4 }
+	{ sw r0, r3; addi r0, r0, 4; addi r2, r2, -4 }
+
+.Lcheck_odd_stragglers:
+	{ bnz r2, .Lcopy_unaligned_few }
+
+.Ldone:
+	{ move r0, r23; jrp lr }
+
+/* Prefetching multiple cache line copy handler (for large transfers).  */
+
+	/* Copy words until r1 is cache-line-aligned. */
+.Lalign_loop:
+	{ lw r3, r1; addi r1, r1, 4 }
+	{ andi r6, r1, 63 }
+	{ sw r0, r3; addi r0, r0, 4; addi r2, r2, -4 }
+.Lcopy_many:
+	{ bnzt r6, .Lalign_loop; addi r9, r0, 63 }
+
+	{ addi r3, r1, 60; andi r9, r9, -64 }
+
+	/* No need to prefetch dst, we'll just do the wh64
+           right before we copy a line.  */
+	{ lw r5, r3; addi r3, r3, 64; movei r4, 1 }
+	/* Intentionally stall for a few cycles to leave L2 cache alone. */
+	{ bnzt zero, .; move r27, lr }
+	{ lw r6, r3; addi r3, r3, 64 }
+	/* Intentionally stall for a few cycles to leave L2 cache alone. */
+	{ bnzt zero, . }
+	{ lw r7, r3; addi r3, r3, 64 }
+	/* Intentionally stall for a few cycles to leave L2 cache alone. */
+	{ bz zero, .Lbig_loop2 }
+
+	/* On entry to this loop:
+	   - r0 points to the start of dst line 0
+	   - r1 points to start of src line 0
+	   - r2 >= (256 - 60), only the first time the loop trips.
+	   - r3 contains r1 + 128 + 60    [pointer to end of source line 2]
+	     This is our prefetch address. When we get near the end
+	     rather than prefetching off the end this is changed to point
+	     to some "safe" recently loaded address.
+	   - r5 contains *(r1 + 60)       [i.e. last word of source line 0]
+	   - r6 contains *(r1 + 64 + 60)  [i.e. last word of source line 1]
+	   - r9 contains ((r0 + 63) & -64)
+	       [start of next dst cache line.]  */
+
+.Lbig_loop:
+	{ jal .Lcopy_line2; add r15, r1, r2 }
+
+.Lbig_loop2:
+	/* Copy line 0, first stalling until r5 is ready. */
+	{ move r12, r5; lw r16, r1 }
+	{ bz r4, .Lcopy_8_check; slti_u r8, r2, 8 }
+	/* Prefetch several lines ahead. */
+	{ lw r5, r3; addi r3, r3, 64 }
+	{ jal .Lcopy_line }
+
+	/* Copy line 1, first stalling until r6 is ready. */
+	{ move r12, r6; lw r16, r1 }
+	{ bz r4, .Lcopy_8_check; slti_u r8, r2, 8 }
+	/* Prefetch several lines ahead. */
+	{ lw r6, r3; addi r3, r3, 64 }
+	{ jal .Lcopy_line }
+
+	/* Copy line 2, first stalling until r7 is ready. */
+	{ move r12, r7; lw r16, r1 }
+	{ bz r4, .Lcopy_8_check; slti_u r8, r2, 8 }
+	/* Prefetch several lines ahead. */
+	{ lw r7, r3; addi r3, r3, 64 }
+	/* Use up a caches-busy cycle by jumping back to the top of the
+	   loop. Might as well get it out of the way now.  */
+	{ j .Lbig_loop }
+
+
+	/* On entry:
+	   - r0 points to the destination line.
+	   - r1 points to the source line.
+	   - r3 is the next prefetch address.
+	   - r9 holds the last address used for wh64.
+	   - r12 = WORD_15
+	   - r16 = WORD_0.
+	   - r17 == r1 + 16.
+	   - r27 holds saved lr to restore.
+	  
+	   On exit:
+	   - r0 is incremented by 64.
+	   - r1 is incremented by 64, unless that would point to a word
+	     beyond the end of the source array, in which case it is redirected
+	     to point to an arbitrary word already in the cache.
+	   - r2 is decremented by 64.
+	   - r3 is unchanged, unless it points to a word beyond the
+	     end of the source array, in which case it is redirected
+	     to point to an arbitrary word already in the cache.
+	     Redirecting is OK since if we are that close to the end
+	     of the array we will not come back to this subroutine
+	     and use the contents of the prefetched address.
+	   - r4 is nonzero iff r2 >= 64.
+	   - r9 is incremented by 64, unless it points beyond the
+	     end of the last full destination cache line, in which
+	     case it is redirected to a "safe address" that can be
+	     clobbered (sp - 64)
+	   - lr contains the value in r27.  */
+
+/* r26 unused */
+
+.Lcopy_line:
+	/* TODO: when r3 goes past the end, we would like to redirect it
+	   to prefetch the last partial cache line (if any) just once, for the
+	   benefit of the final cleanup loop. But we don't want to
+	   prefetch that line more than once, or subsequent prefetches
+	   will go into the RTF. But then .Lbig_loop should unconditionally
+	   branch to top of loop to execute final prefetch, and its
+	   nop should become a conditional branch.  */
+
+	/* We need two non-memory cycles here to cover the resources
+	   used by the loads initiated by the caller.  */
+	{ add r15, r1, r2 }
+.Lcopy_line2:
+	{ slt_u r13, r3, r15; addi r17, r1, 16 }
+
+	/* NOTE: this will stall for one cycle as L1 is busy. */
+
+	/* Fill second L1D line. */
+	{ lw r17, r17; addi r1, r1, 48; mvz r3, r13, r1 } /* r17 = WORD_4 */
+
+	/* Prepare destination line for writing. */
+	{ wh64 r9; addi r9, r9, 64 }
+
+	/* Load seven words that are L1D hits to cover wh64 L2 usage. */
+
+	/* Load the three remaining words from the last L1D line, which
+	   we know has already filled the L1D.  */
+	{ lw r4, r1;  addi r1, r1, 4;   addi r20, r1, 16 }   /* r4 = WORD_12 */
+	{ lw r8, r1;  addi r1, r1, 4;   slt_u r13, r20, r15 }/* r8 = WORD_13 */
+	{ lw r11, r1; addi r1, r1, -52; mvz r20, r13, r1 }  /* r11 = WORD_14 */
+
+	/* Load the three remaining words from the first L1D line, first
+	   stalling until it has filled by "looking at" r16.  */
+	{ lw r13, r1; addi r1, r1, 4; move zero, r16 }   /* r13 = WORD_1 */
+	{ lw r14, r1; addi r1, r1, 4 }                   /* r14 = WORD_2 */
+	{ lw r15, r1; addi r1, r1, 8; addi r10, r0, 60 } /* r15 = WORD_3 */
+
+	/* Load second word from the second L1D line, first
+	   stalling until it has filled by "looking at" r17.  */
+	{ lw r19, r1; addi r1, r1, 4; move zero, r17 }  /* r19 = WORD_5 */
+
+	/* Store last word to the destination line, potentially dirtying it
+	   for the first time, which keeps the L2 busy for two cycles.  */
+	{ sw r10, r12 }                                 /* store(WORD_15) */
+
+	/* Use two L1D hits to cover the sw L2 access above. */
+	{ lw r10, r1; addi r1, r1, 4 }                  /* r10 = WORD_6 */
+	{ lw r12, r1; addi r1, r1, 4 }                  /* r12 = WORD_7 */
+
+	/* Fill third L1D line. */
+	{ lw r18, r1; addi r1, r1, 4 }                  /* r18 = WORD_8 */
+
+	/* Store first L1D line. */
+	{ sw r0, r16; addi r0, r0, 4; add r16, r0, r2 } /* store(WORD_0) */
+	{ sw r0, r13; addi r0, r0, 4; andi r16, r16, -64 } /* store(WORD_1) */
+	{ sw r0, r14; addi r0, r0, 4; slt_u r16, r9, r16 } /* store(WORD_2) */
+	{ sw r0, r15; addi r0, r0, 4; addi r13, sp, -64 } /* store(WORD_3) */
+
+	/* Store second L1D line. */
+	{ sw r0, r17; addi r0, r0, 4; mvz r9, r16, r13 }/* store(WORD_4) */
+	{ sw r0, r19; addi r0, r0, 4 }                  /* store(WORD_5) */
+	{ sw r0, r10; addi r0, r0, 4 }                  /* store(WORD_6) */
+	{ sw r0, r12; addi r0, r0, 4 }                  /* store(WORD_7) */
+
+	{ lw r13, r1; addi r1, r1, 4; move zero, r18 }  /* r13 = WORD_9 */
+	{ lw r14, r1; addi r1, r1, 4 }                  /* r14 = WORD_10 */
+	{ lw r15, r1; move r1, r20   }                  /* r15 = WORD_11 */
+
+	/* Store third L1D line. */
+	{ sw r0, r18; addi r0, r0, 4 }                  /* store(WORD_8) */
+	{ sw r0, r13; addi r0, r0, 4 }                  /* store(WORD_9) */
+	{ sw r0, r14; addi r0, r0, 4 }                  /* store(WORD_10) */
+	{ sw r0, r15; addi r0, r0, 4 }                  /* store(WORD_11) */
+
+	/* Store rest of fourth L1D line. */
+	{ sw r0, r4;  addi r0, r0, 4 }                  /* store(WORD_12) */
+	{
+	sw r0, r8                                       /* store(WORD_13) */
+	addi r0, r0, 4
+	/* Will r2 be > 64 after we subtract 64 below? */
+	shri r4, r2, 7
+	}
+	{
+	sw r0, r11                                      /* store(WORD_14) */
+	addi r0, r0, 8
+	/* Record 64 bytes successfully copied. */
+	addi r2, r2, -64
+	}
+
+	{ jrp lr; move lr, r27 }
+
+	/* Convey to the backtrace library that the stack frame is
+	   size zero, and the real return address is on the stack
+	   rather than in 'lr'.  */
+	{ info 8 }
+
+	.align 64
+.Lcopy_unaligned_maybe_many:
+	/* Skip the setup overhead if we aren't copying many bytes. */
+	{ slti_u r8, r2, 20; sub r4, zero, r0 }
+	{ bnzt r8, .Lcopy_unaligned_few; andi r4, r4, 3 }
+	{ bz r4, .Ldest_is_word_aligned; add r18, r1, r2 }
+
+/* Unaligned 4 byte at a time copy handler.  */
+
+	/* Copy single bytes until r0 == 0 mod 4, so we can store words. */
+.Lalign_dest_loop:
+	{ lb_u r3, r1; addi r1, r1, 1; addi r4, r4, -1 }
+	{ sb r0, r3;   addi r0, r0, 1; addi r2, r2, -1 }
+	{ bnzt r4, .Lalign_dest_loop; andi r3, r1, 3 }
+
+	/* If source and dest are now *both* aligned, do an aligned copy. */
+	{ bz r3, .Lcheck_aligned_copy_size; addli r4, r2, -256 }
+
+.Ldest_is_word_aligned:
+
+	{ andi r8, r0, 63; lwadd_na r6, r1, 4}
+	{ slti_u r9, r2, 64; bz r8, .Ldest_is_L2_line_aligned }
+
+	/* This copies unaligned words until either there are fewer
+	   than 4 bytes left to copy, or until the destination pointer
+	   is cache-aligned, whichever comes first.
+	  
+	   On entry:
+	   - r0 is the next store address.
+	   - r1 points 4 bytes past the load address corresponding to r0.
+	   - r2 >= 4
+	   - r6 is the next aligned word loaded.  */
+.Lcopy_unaligned_src_words:
+	{ lwadd_na r7, r1, 4; slti_u r8, r2, 4 + 4 }
+	/* stall */
+	{ dword_align r6, r7, r1; slti_u r9, r2, 64 + 4 }
+	{ swadd r0, r6, 4; addi r2, r2, -4 }
+	{ bnz r8, .Lcleanup_unaligned_words; andi r8, r0, 63 }
+	{ bnzt r8, .Lcopy_unaligned_src_words; move r6, r7 }
+
+	/* On entry:
+	   - r0 is the next store address.
+	   - r1 points 4 bytes past the load address corresponding to r0.
+	   - r2 >= 4 (# of bytes left to store).
+	   - r6 is the next aligned src word value.
+	   - r9 = (r2 < 64U).
+	   - r18 points one byte past the end of source memory.  */
+.Ldest_is_L2_line_aligned:
+
+	{
+	/* Not a full cache line remains. */
+	bnz r9, .Lcleanup_unaligned_words
+	move r7, r6
+	}
+
+	/* r2 >= 64 */
+
+	/* Kick off two prefetches, but don't go past the end. */
+	{ addi r3, r1, 63 - 4; addi r8, r1, 64 + 63 - 4 }
+	{ prefetch r3; move r3, r8; slt_u r8, r8, r18 }
+	{ mvz r3, r8, r1; addi r8, r3, 64 }
+	{ prefetch r3; move r3, r8; slt_u r8, r8, r18 }
+	{ mvz r3, r8, r1; movei r17, 0 }
+
+.Lcopy_unaligned_line:
+	/* Prefetch another line. */
+	{ prefetch r3; addi r15, r1, 60; addi r3, r3, 64 }
+	/* Fire off a load of the last word we are about to copy. */
+	{ lw_na r15, r15; slt_u r8, r3, r18 }
+
+	{ mvz r3, r8, r1; wh64 r0 }
+
+	/* This loop runs twice.
+	  
+	   On entry:
+	   - r17 is even before the first iteration, and odd before
+	     the second.  It is incremented inside the loop.  Encountering
+	     an even value at the end of the loop makes it stop.  */
+.Lcopy_half_an_unaligned_line:
+	{
+	/* Stall until the last byte is ready. In the steady state this
+	   guarantees all words to load below will be in the L2 cache, which
+	   avoids shunting the loads to the RTF.  */
+	move zero, r15
+	lwadd_na r7, r1, 16
+	}
+	{ lwadd_na r11, r1, 12 }
+	{ lwadd_na r14, r1, -24 }
+	{ lwadd_na r8, r1, 4 }
+	{ lwadd_na r9, r1, 4 }
+	{
+	lwadd_na r10, r1, 8
+	/* r16 = (r2 < 64), after we subtract 32 from r2 below. */
+	slti_u r16, r2, 64 + 32
+	}
+	{ lwadd_na r12, r1, 4; addi r17, r17, 1 }
+	{ lwadd_na r13, r1, 8; dword_align r6, r7, r1 }
+	{ swadd r0, r6,  4; dword_align r7,  r8,  r1 }
+	{ swadd r0, r7,  4; dword_align r8,  r9,  r1 }
+	{ swadd r0, r8,  4; dword_align r9,  r10, r1 }
+	{ swadd r0, r9,  4; dword_align r10, r11, r1 }
+	{ swadd r0, r10, 4; dword_align r11, r12, r1 }
+	{ swadd r0, r11, 4; dword_align r12, r13, r1 }
+	{ swadd r0, r12, 4; dword_align r13, r14, r1 }
+	{ swadd r0, r13, 4; addi r2, r2, -32 }
+	{ move r6, r14; bbst r17, .Lcopy_half_an_unaligned_line }
+
+	{ bzt r16, .Lcopy_unaligned_line; move r7, r6 }
+
+	/* On entry:
+	   - r0 is the next store address.
+	   - r1 points 4 bytes past the load address corresponding to r0.
+	   - r2 >= 0 (# of bytes left to store).
+	   - r7 is the next aligned src word value.  */
+.Lcleanup_unaligned_words:
+	/* Handle any trailing bytes. */
+	{ bz r2, .Lcopy_unaligned_done; slti_u r8, r2, 4 }
+	{ bzt r8, .Lcopy_unaligned_src_words; move r6, r7 }
+
+	/* Move r1 back to the point where it corresponds to r0. */
+	{ addi r1, r1, -4 }
+
+	/* Fall through */
+
+/* 1 byte at a time copy handler.  */
+
+.Lcopy_unaligned_few:
+	{ lb_u r3, r1; addi r1, r1, 1 }
+	{ sb r0, r3;   addi r0, r0, 1; addi r2, r2, -1 }
+	{ bnzt r2, .Lcopy_unaligned_few }
+
+.Lcopy_unaligned_done:
+
+	{ move r0, r23; jrp lr }
+
+END (__memcpy)
+
+weak_alias (__memcpy, memcpy)
+libc_hidden_builtin_def (memcpy)
diff --git a/sysdeps/tile/tilepro/memset.c b/sysdeps/tile/tilepro/memset.c
new file mode 100644
index 0000000..bfdc3d4
--- /dev/null
+++ b/sysdeps/tile/tilepro/memset.c
@@ -0,0 +1,152 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <string.h>
+#include <stdint.h>
+#include <arch/chip.h>
+
+void *
+__memset (void *s, int c, size_t n)
+{
+  uint32_t *out32;
+  int n32;
+  uint32_t v16, v32;
+  uint8_t *out8 = s;
+  int to_align32;
+
+  /* Experimentation shows that a trivial tight loop is a win up until
+     around a size of 20, where writing a word at a time starts to win.  */
+#define BYTE_CUTOFF 20
+
+#if BYTE_CUTOFF < 3
+  /* This must be at least at least this big, or some code later
+     on doesn't work.  */
+# error "BYTE_CUTOFF is too small."
+#endif
+
+  if (n < BYTE_CUTOFF)
+    {
+      /* Strangely, this turns out to be the tightest way to write
+         this loop.  */
+      if (n != 0)
+        {
+          do
+            {
+              /* Strangely, combining these into one line performs worse.  */
+              *out8 = c;
+              out8++;
+            }
+          while (--n != 0);
+        }
+
+      return s;
+    }
+
+  /* Align 'out8'. We know n >= 3 so this won't write past the end. */
+  while (((uintptr_t) out8 & 3) != 0)
+    {
+      *out8++ = c;
+      --n;
+    }
+
+  /* Align 'n'. */
+  while (n & 3)
+    out8[--n] = c;
+
+  out32 = (uint32_t *) out8;
+  n32 = n >> 2;
+
+  /* Tile input byte out to 32 bits. */
+  v16 = __insn_intlb (c, c);
+  v32 = __insn_intlh (v16, v16);
+
+  /* This must be at least 8 or the following loop doesn't work. */
+#define CACHE_LINE_SIZE_IN_WORDS (CHIP_L2_LINE_SIZE() / 4)
+
+  /* Determine how many words we need to emit before the 'out32'
+     pointer becomes aligned modulo the cache line size.  */
+  to_align32 = (-((uintptr_t) out32 >> 2)) & (CACHE_LINE_SIZE_IN_WORDS - 1);
+
+  /* Only bother aligning and using wh64 if there is at least one full
+     cache line to process.  This check also prevents overrunning the
+     end of the buffer with alignment words.  */
+  if (to_align32 <= n32 - CACHE_LINE_SIZE_IN_WORDS)
+    {
+      int lines_left;
+
+      /* Align out32 mod the cache line size so we can use wh64. */
+      n32 -= to_align32;
+      for (; to_align32 != 0; to_align32--)
+        {
+          *out32 = v32;
+          out32++;
+        }
+
+      /* Use unsigned divide to turn this into a right shift. */
+      lines_left = (unsigned) n32 / CACHE_LINE_SIZE_IN_WORDS;
+
+      do
+        {
+          /* Only wh64 a few lines at a time, so we don't exceed the
+             maximum number of victim lines.  */
+          int x = ((lines_left < CHIP_MAX_OUTSTANDING_VICTIMS ())? lines_left
+                   : CHIP_MAX_OUTSTANDING_VICTIMS ());
+          uint32_t *wh = out32;
+          int i = x;
+          int j;
+
+          lines_left -= x;
+
+          do
+            {
+              __insn_wh64 (wh);
+              wh += CACHE_LINE_SIZE_IN_WORDS;
+            }
+          while (--i);
+
+          for (j = x * (CACHE_LINE_SIZE_IN_WORDS / 4); j != 0; j--)
+            {
+              *out32++ = v32;
+              *out32++ = v32;
+              *out32++ = v32;
+              *out32++ = v32;
+            }
+        }
+      while (lines_left != 0);
+
+      /* We processed all full lines above, so only this many words
+         remain to be processed.  */
+      n32 &= CACHE_LINE_SIZE_IN_WORDS - 1;
+    }
+
+  /* Now handle any leftover values. */
+  if (n32 != 0)
+    {
+      do
+        {
+          *out32 = v32;
+          out32++;
+        }
+      while (--n32 != 0);
+    }
+
+  return s;
+}
+weak_alias (__memset, memset)
+libc_hidden_builtin_def (memset)
diff --git a/sysdeps/tile/tilepro/memusage.h b/sysdeps/tile/tilepro/memusage.h
new file mode 100644
index 0000000..700c08b
--- /dev/null
+++ b/sysdeps/tile/tilepro/memusage.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <arch/spr_def.h>
+
+#define GETSP() ({ register uintptr_t stack_ptr asm ("sp"); stack_ptr; })
+
+#define GETTIME(low,high)                       \
+  {                                             \
+    low = __insn_mfspr (SPR_CYCLE_LOW);         \
+    high = __insn_mfspr (SPR_CYCLE_HIGH);       \
+  }
+
+#include <sysdeps/generic/memusage.h>
diff --git a/sysdeps/tile/tilepro/rawmemchr.c b/sysdeps/tile/tilepro/rawmemchr.c
new file mode 100644
index 0000000..c1a58ec
--- /dev/null
+++ b/sysdeps/tile/tilepro/rawmemchr.c
@@ -0,0 +1,46 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <string.h>
+#include <stdint.h>
+
+void *
+__rawmemchr (const void *s, int c)
+{
+  /* Get an aligned pointer. */
+  const uintptr_t s_int = (uintptr_t) s;
+  const uint32_t *p = (const uint32_t *) (s_int & -4);
+
+  /* Create four copies of the byte for which we are looking. */
+  const uint32_t goal = 0x01010101 * (uint8_t) c;
+
+  /* Read the first word, but munge it so that bytes before the array
+     will not match goal.  Note that this shift count expression works
+     because we know shift counts are taken mod 32.  */
+  const uint32_t before_mask = (1 << (s_int << 3)) - 1;
+  uint32_t v = (*p | before_mask) ^ (goal & before_mask);
+
+  uint32_t bits;
+  while ((bits = __insn_seqb (v, goal)) == 0)
+    v = *++p;
+
+  return ((char *) p) + (__insn_ctz (bits) >> 3);
+}
+libc_hidden_def (__rawmemchr)
+weak_alias (__rawmemchr, rawmemchr)
diff --git a/sysdeps/tile/tilepro/strchr.c b/sysdeps/tile/tilepro/strchr.c
new file mode 100644
index 0000000..e116ea9
--- /dev/null
+++ b/sysdeps/tile/tilepro/strchr.c
@@ -0,0 +1,69 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <string.h>
+#include <stdint.h>
+
+#undef strchr
+
+char *
+strchr (const char *s, int c)
+{
+  int z, g;
+
+  /* Get an aligned pointer. */
+  const uintptr_t s_int = (uintptr_t) s;
+  const uint32_t *p = (const uint32_t *) (s_int & -4);
+
+  /* Create four copies of the byte for which we are looking. */
+  const uint32_t goal = 0x01010101 * (uint8_t) c;
+
+  /* Read the first aligned word, but force bytes before the string to
+     match neither zero nor goal (we make sure the high bit of each
+     byte is 1, and the low 7 bits are all the opposite of the goal
+     byte).  Note that this shift count expression works because we
+     know shift counts are taken mod 32.  */
+  const uint32_t before_mask = (1 << (s_int << 3)) - 1;
+  uint32_t v = (*p | before_mask) ^ (goal & __insn_shrib (before_mask, 1));
+
+  uint32_t zero_matches, goal_matches;
+  while (1)
+    {
+      /* Look for a terminating '\0'. */
+      zero_matches = __insn_seqb (v, 0);
+
+      /* Look for the goal byte. */
+      goal_matches = __insn_seqb (v, goal);
+
+      if (__builtin_expect ((zero_matches | goal_matches) != 0, 0))
+        break;
+
+      v = *++p;
+    }
+
+  z = __insn_ctz (zero_matches);
+  g = __insn_ctz (goal_matches);
+
+  /* If we found c before '\0' we got a match. Note that if c == '\0'
+     then g == z, and we correctly return the address of the '\0'
+     rather than NULL.  */
+  return (g <= z) ? ((char *) p) + (g >> 3) : NULL;
+}
+weak_alias (strchr, index)
+libc_hidden_builtin_def (strchr)
diff --git a/sysdeps/tile/tilepro/strchrnul.c b/sysdeps/tile/tilepro/strchrnul.c
new file mode 100644
index 0000000..6e7bba9
--- /dev/null
+++ b/sysdeps/tile/tilepro/strchrnul.c
@@ -0,0 +1,66 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <string.h>
+#include <stdint.h>
+
+char *
+__strchrnul (const char *s, int c)
+{
+  int z, g;
+
+  /* Get an aligned pointer. */
+  const uintptr_t s_int = (uintptr_t) s;
+  const uint32_t *p = (const uint32_t *) (s_int & -4);
+
+  /* Create four copies of the byte for which we are looking. */
+  const uint32_t goal = 0x01010101 * (uint8_t) c;
+
+  /* Read the first aligned word, but force bytes before the string to
+     match neither zero nor goal (we make sure the high bit of each
+     byte is 1, and the low 7 bits are all the opposite of the goal
+     byte).  Note that this shift count expression works because we
+     know shift counts are taken mod 32.  */
+  const uint32_t before_mask = (1 << (s_int << 3)) - 1;
+  uint32_t v = (*p | before_mask) ^ (goal & __insn_shrib (before_mask, 1));
+
+  uint32_t zero_matches, goal_matches;
+  while (1)
+    {
+      /* Look for a terminating '\0'. */
+      zero_matches = __insn_seqb (v, 0);
+
+      /* Look for the goal byte. */
+      goal_matches = __insn_seqb (v, goal);
+
+      if (__builtin_expect ((zero_matches | goal_matches) != 0, 0))
+        break;
+
+      v = *++p;
+    }
+
+  z = __insn_ctz (zero_matches);
+  g = __insn_ctz (goal_matches);
+
+  /* Return a pointer to the NUL or goal, whichever is first. */
+  if (z < g)
+    g = z;
+  return ((char *) p) + (g >> 3);
+}
+weak_alias (__strchrnul, strchrnul)
diff --git a/sysdeps/tile/tilepro/strlen.c b/sysdeps/tile/tilepro/strlen.c
new file mode 100644
index 0000000..2612068
--- /dev/null
+++ b/sysdeps/tile/tilepro/strlen.c
@@ -0,0 +1,40 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <string.h>
+#include <stdint.h>
+
+size_t
+strlen (const char *s)
+{
+  /* Get an aligned pointer. */
+  const uintptr_t s_int = (uintptr_t) s;
+  const uint32_t *p = (const uint32_t *) (s_int & -4);
+
+  /* Read the first word, but force bytes before the string to be nonzero.
+     This expression works because we know shift counts are taken mod 32.  */
+  uint32_t v = *p | ((1 << (s_int << 3)) - 1);
+
+  uint32_t bits;
+  while ((bits = __insn_seqb (v, 0)) == 0)
+    v = *++p;
+
+  return ((const char *) p) + (__insn_ctz (bits) >> 3) - s;
+}
+libc_hidden_builtin_def (strlen)
diff --git a/sysdeps/tile/tilepro/strrchr.c b/sysdeps/tile/tilepro/strrchr.c
new file mode 100644
index 0000000..b91b02b
--- /dev/null
+++ b/sysdeps/tile/tilepro/strrchr.c
@@ -0,0 +1,74 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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 <string.h>
+#include <stdint.h>
+
+char *
+strrchr (const char *s, int c)
+{
+  /* Get an aligned pointer. */
+  const uintptr_t s_int = (uintptr_t) s;
+  const uint32_t *p = (const uint32_t *) (s_int & -4);
+
+  /* Create four copies of the byte for which we are looking. */
+  const uint32_t goal = 0x01010101 * (uint8_t) c;
+
+  /* Read the first aligned word, but force bytes before the string to
+     match neither zero nor goal (we make sure the high bit of each
+     byte is 1, and the low 7 bits are all the opposite of the goal
+     byte).  Note that this shift count expression works because we
+     know shift counts are taken mod 32.  */
+  const uint32_t before_mask = (1 << (s_int << 3)) - 1;
+  uint32_t v = (*p | before_mask) ^ (goal & __insn_shrib (before_mask, 1));
+  const char *found = NULL;
+  uint32_t zero_matches, goal_matches;
+  while (1)
+    {
+      /* Look for a terminating '\0'. */
+      zero_matches = __insn_seqb (v, 0);
+
+      /* Look for the goal byte. */
+      goal_matches = __insn_seqb (v, goal);
+
+      /* If we found the goal, record the last offset. */
+      if (__builtin_expect (goal_matches != 0, 0))
+        {
+          if (__builtin_expect (zero_matches != 0, 0))
+            {
+              /* Clear any goal after the first zero. */
+              int first_nul = __insn_ctz (zero_matches);
+              /* The number of top bits we need to clear is
+                 32 - (first_nul + 8).  */
+              int shift_amt = (24 - first_nul);
+              goal_matches <<= shift_amt;
+              goal_matches >>= shift_amt;
+            }
+          if (__builtin_expect (goal_matches != 0, 1))
+            found = ((char *) p) + 3 - (__insn_clz (goal_matches) >> 3);
+        }
+
+      if (__builtin_expect (zero_matches != 0, 0))
+        return (char *) found;
+
+      v = *++p;
+    }
+}
+weak_alias (strrchr, rindex)
+libc_hidden_builtin_def (strrchr)
diff --git a/sysdeps/tile/tls-macros.h b/sysdeps/tile/tls-macros.h
new file mode 100644
index 0000000..88023bc
--- /dev/null
+++ b/sysdeps/tile/tls-macros.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2011 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Chris Metcalf <cmetcalf@tilera.com>, 2011.
+
+   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.  */
+
+#ifdef __tilegx__
+#define TLS_GD_REL "hw0_last_tls_gd"
+#define TLS_IE_REL "hw0_last_tls_ie"
+#else
+#define TLS_GD_REL "tls_gd_lo16"
+#define TLS_IE_REL "tls_ie_lo16"
+#endif
+
+#define TLS_GD(x)							\
+  ({									\
+    int __offset;                                                       \
+    extern char _GLOBAL_OFFSET_TABLE_[];                                \
+    extern void *__tls_get_addr (void *);				\
+									\
+    asm ("moveli %0, " TLS_GD_REL "(" #x ")" : "=&r" (__offset));       \
+    (int *) __tls_get_addr (_GLOBAL_OFFSET_TABLE_ + __offset); })
+
+/* No special support for LD mode. */
+#define TLS_LD TLS_GD
+
+#define TLS_IE(x)							\
+  ({									\
+    register char *__tp asm("tp");                                      \
+    extern char _GLOBAL_OFFSET_TABLE_[];                                \
+    int __offset;							\
+									\
+    asm ("moveli %0, " TLS_IE_REL "(" #x ")" : "=&r" (__offset));       \
+    (int *) (__tp + *(int *) (_GLOBAL_OFFSET_TABLE_ + __offset)); })
+
+/* No special support for LE mode. */
+#define TLS_LE TLS_IE
diff --git a/sysdeps/tile/tst-audit.h b/sysdeps/tile/tst-audit.h
new file mode 100644
index 0000000..d0e24f2
--- /dev/null
+++ b/sysdeps/tile/tst-audit.h
@@ -0,0 +1,26 @@
+/* Definitions for testing PLT entry/exit auditing.  ARM 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.  */
+
+#define pltenter la_tile_gnu_pltenter
+#define pltexit la_tile_gnu_pltexit
+#define La_regs La_tile_regs
+#define La_retval La_tile_retval
+#define int_retval lrv_reg[0]


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]