about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMaciej W. Rozycki <macro@codesourcery.com>2013-02-27 23:45:07 +0000
committerMaciej W. Rozycki <macro@codesourcery.com>2013-02-27 23:45:07 +0000
commit43301bd3c281036ba97eef384c9340cc7b6130d3 (patch)
tree263f0cc7e01c33c72e626480a52b2bfc1dce78f0
parent85bd816a603a437aedeb688a60a3e0dba4439c50 (diff)
downloadglibc-43301bd3c281036ba97eef384c9340cc7b6130d3.tar.gz
glibc-43301bd3c281036ba97eef384c9340cc7b6130d3.tar.xz
glibc-43301bd3c281036ba97eef384c9340cc7b6130d3.zip
Add support for building as MIPS16 code.
-rw-r--r--ports/ChangeLog.mips106
-rw-r--r--ports/sysdeps/mips/__longjmp.c6
-rw-r--r--ports/sysdeps/mips/abort-instr.h6
-rw-r--r--ports/sysdeps/mips/bits/atomic.h45
-rw-r--r--ports/sysdeps/mips/bsd-_setjmp.S2
-rw-r--r--ports/sysdeps/mips/bsd-setjmp.S2
-rw-r--r--ports/sysdeps/mips/dl-machine.h102
-rw-r--r--ports/sysdeps/mips/dl-trampoline.c130
-rw-r--r--ports/sysdeps/mips/fpu/e_sqrt.c2
-rw-r--r--ports/sysdeps/mips/fpu/e_sqrtf.c2
-rw-r--r--ports/sysdeps/mips/fpu_control.h11
-rw-r--r--ports/sysdeps/mips/machine-gmon.h14
-rw-r--r--ports/sysdeps/mips/memset.S1
-rw-r--r--ports/sysdeps/mips/mips32/crti.S2
-rw-r--r--ports/sysdeps/mips/mips32/crtn.S2
-rw-r--r--ports/sysdeps/mips/mips32/fpu/Versions5
-rw-r--r--ports/sysdeps/mips/mips32/fpu/fpu_control.c34
-rw-r--r--ports/sysdeps/mips/mips32/mips16/add_n.c1
-rw-r--r--ports/sysdeps/mips/mips32/mips16/addmul_1.c1
-rw-r--r--ports/sysdeps/mips/mips32/mips16/fpu/Makefile5
-rw-r--r--ports/sysdeps/mips/mips32/mips16/lshift.c1
-rw-r--r--ports/sysdeps/mips/mips32/mips16/mul_1.c1
-rw-r--r--ports/sysdeps/mips/mips32/mips16/rshift.c1
-rw-r--r--ports/sysdeps/mips/mips32/mips16/sub_n.c1
-rw-r--r--ports/sysdeps/mips/mips32/mips16/submul_1.c1
-rw-r--r--ports/sysdeps/mips/mips64/n32/crti.S2
-rw-r--r--ports/sysdeps/mips/mips64/n32/crtn.S2
-rw-r--r--ports/sysdeps/mips/mips64/n64/crti.S2
-rw-r--r--ports/sysdeps/mips/mips64/n64/crtn.S2
-rw-r--r--ports/sysdeps/mips/nptl/tls.h5
-rw-r--r--ports/sysdeps/mips/preconfigure7
-rw-r--r--ports/sysdeps/mips/setjmp.S2
-rw-r--r--ports/sysdeps/mips/setjmp_aux.c2
-rw-r--r--ports/sysdeps/mips/start.S80
-rw-r--r--ports/sysdeps/mips/sys/tas.h5
-rw-r--r--ports/sysdeps/mips/tls-macros.h63
-rw-r--r--ports/sysdeps/unix/mips/mips32/sysdep.h2
-rw-r--r--ports/sysdeps/unix/mips/mips64/n32/sysdep.h2
-rw-r--r--ports/sysdeps/unix/mips/mips64/n64/sysdep.h2
-rw-r--r--ports/sysdeps/unix/mips/sysdep.S2
-rw-r--r--ports/sysdeps/unix/mips/sysdep.h2
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/brk.c13
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/clone.S1
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/getcontext.S1
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/makecontext.S1
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/Makefile13
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/Versions6
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall.h89
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall0.c30
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall1.c32
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall2.c32
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall3.c32
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall4.c32
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall5.c33
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall6.c33
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall7.c33
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/mips32/nptl/libc.abilist2
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/mips32/sysdep.h35
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/nptl/sysdep-cancel.h1
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/setcontext.S1
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/swapcontext.S1
-rw-r--r--ports/sysdeps/unix/sysv/linux/mips/vfork.S1
62 files changed, 1041 insertions, 47 deletions
diff --git a/ports/ChangeLog.mips b/ports/ChangeLog.mips
index f06c986333..251806572d 100644
--- a/ports/ChangeLog.mips
+++ b/ports/ChangeLog.mips
@@ -1,3 +1,109 @@
+2013-02-27  Chung-Lin Tang  <cltang@codesourcery.com>
+            Maciej W. Rozycki  <macro@codesourcery.com>
+            Maxim Kuvyrkov  <maxim@codesourcery.com>
+
+	* sysdeps/mips/abort-instr.h (ABORT_INSTRUCTION) [__mips16]:
+	New macro.
+	* sysdeps/mips/dl-machine.h (elf_machine_load_address): Add
+	MIPS16 version of assembly code.
+	(RTLD_START) [__mips16]: New macro.
+	* sysdeps/mips/fpu_control.h (__mips_fpu_getcw): New prototype.
+	(__mips_fpu_setcw): Likewise.
+	(_FPU_GETCW) [__mips16]: New macro.
+	(_FPU_SETCW) [__mips16]: Likewise.
+	* sysdeps/mips/machine-gmon.h (MCOUNT): Add `.set nomips16'.
+	* sysdeps/mips/tls-macros.h (LOAD_GP) [__mips16]: New macro.
+	(TLS_GD, TLS_LD, TLS_IE, TLS_LE) [__mips16]: Likewise.
+	* sysdeps/mips/bits/atomic.h: Also use __atomic_* builtins with
+	GCC 4.7 in MIPS16 code.
+	(atomic_compare_and_exchange_val_acq) [__mips16]: New macro.
+	(atomic_compare_and_exchange_bool_acq) [__mips16]: Likewise.
+	(atomic_exchange_acq) [__mips16]: Likewise.
+	(atomic_exchange_and_add) [__mips16]: Likewise.
+	(atomic_bit_test_set) [__mips16]: Likewise.
+	(atomic_and, atomic_and_val) [__mips16]: Likewise.
+	(atomic_or, atomic_or_val) [__mips16]: Likewise.
+	(atomic_full_barrier) [__mips16]: Likewise.
+	* sysdeps/mips/nptl/tls.h (READ_THREAD_POINTER) [__mips16]:
+	Likewise.
+	* sysdeps/mips/sys/tas.h (_test_and_set): Add `__nomips16__'
+	attribute.
+	* sysdeps/unix/mips/sysdep.h (PSEUDO_NOERRNO): Add
+	`.set nomips16'.
+	(PSEUDO_ERRVAL): Likewise.
+	* sysdeps/unix/mips/mips32/sysdep.h (PSEUDO): Likewise.
+	* sysdeps/unix/mips/mips64/n32/sysdep.h (PSEUDO): Likewise.
+	* sysdeps/unix/mips/mips64/n64/sysdep.h (PSEUDO): Likewise.
+	* sysdeps/unix/sysv/linux/mips/mips32/sysdep.h
+	(INTERNAL_SYSCALL, INTERNAL_SYSCALL_NCS) [__mips16]: New macros.
+	(INTERNAL_SYSCALL_MIPS16) [__mips16]: Likewise.
+	* sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall.h:
+	New file.
+	* sysdeps/unix/sysv/linux/mips/nptl/sysdep-cancel.h (PSEUDO):
+	Add `.set nomips16'.
+	* sysdeps/mips/bsd-_setjmp.S (_setjmp): Likewise.
+	* sysdeps/mips/bsd-setjmp.S (setjmp): Likewise.
+	* sysdeps/mips/memset.S (memset): Likewise.
+	* sysdeps/mips/setjmp.S (__sigsetjmp): Likewise.
+	* sysdeps/mips/start.S (ENTRY_POINT) [__mips16]: New function.
+	* sysdeps/mips/mips32/crti.S: Add `.set nomips16'.
+	* sysdeps/mips/mips32/crtn.S: Likewise.
+	* sysdeps/mips/mips64/n32/crti.S: Likewise.
+	* sysdeps/mips/mips64/n32/crtn.S: Likewise.
+	* sysdeps/mips/mips64/n64/crti.S: Likewise.
+	* sysdeps/mips/mips64/n64/crtn.S: Likewise.
+	* sysdeps/unix/mips/sysdep.S: Likewise.
+	* sysdeps/unix/sysv/linux/mips/clone.S: Likewise.
+	* sysdeps/unix/sysv/linux/mips/getcontext.S: Likewise.
+	* sysdeps/unix/sysv/linux/mips/makecontext.S: Likewise.
+	* sysdeps/unix/sysv/linux/mips/setcontext.S: Likewise.
+	* sysdeps/unix/sysv/linux/mips/swapcontext.S: Likewise.
+	* sysdeps/unix/sysv/linux/mips/vfork.S: Likewise.
+	* sysdeps/mips/__longjmp.c (__longjmp): Rename function to...
+	(____longjmp): ... this.  Make static and add `nomips16'
+	attribute.
+	(__longjmp): New alias.
+	* sysdeps/mips/dl-trampoline.c (_dl_runtime_resolve) [__mips16]:
+	New function.
+	(_dl_runtime_pltresolve): Likewise.
+	* sysdeps/mips/setjmp_aux.c (__sigsetjmp_aux): Add `nomips16'
+	attribute.
+	* sysdeps/mips/fpu/e_sqrt.c (__ieee754_sqrt): Likewise.
+	* sysdeps/mips/fpu/e_sqrtf.c (__ieee754_sqrtf): Likewise.
+	* sysdeps/unix/sysv/linux/mips/brk.c (__brk): Rewrite in terms
+	of INTERNAL_SYSCALL.
+	* sysdeps/mips/mips32/fpu/fpu_control.c: New file.
+	* sysdeps/mips/mips32/mips16/add_n.c: New file.
+	* sysdeps/mips/mips32/mips16/addmul_1.c: New file.
+	* sysdeps/mips/mips32/mips16/lshift.c: New file.
+	* sysdeps/mips/mips32/mips16/mul_1.c: New file.
+	* sysdeps/mips/mips32/mips16/rshift.c: New file.
+	* sysdeps/mips/mips32/mips16/sub_n.c: New file.
+	* sysdeps/mips/mips32/mips16/submul_1.c: New file.
+	* sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall0.c:
+	New file.
+	* sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall1.c:
+	New file.
+	* sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall2.c:
+	New file.
+	* sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall3.c:
+	New file.
+	* sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall4.c:
+	New file.
+	* sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall5.c:
+	New file.
+	* sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall6.c:
+	New file.
+	* sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall7.c:
+	New file.
+	* sysdeps/mips/mips32/fpu/Versions: New file.
+	* sysdeps/unix/sysv/linux/mips/mips32/mips16/Versions: New file.
+	* sysdeps/unix/sysv/linux/mips/mips32/nptl/libc.abilist: New
+	file.
+	* sysdeps/mips/mips32/mips16/fpu/Makefile: New file.
+	* sysdeps/unix/sysv/linux/mips/mips32/mips16/Makefile: New file.
+	* sysdeps/mips/preconfigure: Handle o32 MIPS16 compilation.
+
 2013-02-20  Thomas Schwinge  <thomas@codesourcery.com>
 
 	* sysdeps/mips/bits/nan.h [!__GNUC__] (__nan_union): Change
diff --git a/ports/sysdeps/mips/__longjmp.c b/ports/sysdeps/mips/__longjmp.c
index a9efb0dac4..67bdb86cf8 100644
--- a/ports/sysdeps/mips/__longjmp.c
+++ b/ports/sysdeps/mips/__longjmp.c
@@ -23,8 +23,8 @@
   #error This file uses GNU C extensions; you must compile with GCC.
 #endif
 
-void
-__longjmp (env_arg, val_arg)
+static void __attribute__ ((nomips16))
+____longjmp (env_arg, val_arg)
      __jmp_buf env_arg;
      int val_arg;
 {
@@ -86,3 +86,5 @@ __longjmp (env_arg, val_arg)
   /* Avoid `volatile function does return' warnings.  */
   for (;;);
 }
+
+strong_alias (____longjmp, __longjmp);
diff --git a/ports/sysdeps/mips/abort-instr.h b/ports/sysdeps/mips/abort-instr.h
index d7d8d501b3..7ccae5736b 100644
--- a/ports/sysdeps/mips/abort-instr.h
+++ b/ports/sysdeps/mips/abort-instr.h
@@ -1,2 +1,6 @@
 /* An instruction which should crash any program is a breakpoint.  */
-#define ABORT_INSTRUCTION asm ("break 255")
+#ifdef __mips16
+# define ABORT_INSTRUCTION asm ("break 63")
+#else
+# define ABORT_INSTRUCTION asm ("break 255")
+#endif
diff --git a/ports/sysdeps/mips/bits/atomic.h b/ports/sysdeps/mips/bits/atomic.h
index 566b3dd2b8..bdc3acef7b 100644
--- a/ports/sysdeps/mips/bits/atomic.h
+++ b/ports/sysdeps/mips/bits/atomic.h
@@ -78,9 +78,12 @@ typedef uintmax_t uatomic_max_t;
 #define MIPS_SYNC_STR_1(X) MIPS_SYNC_STR_2(X)
 #define MIPS_SYNC_STR MIPS_SYNC_STR_1(MIPS_SYNC)
 
-#if __GNUC_PREREQ (4, 8)
+#if __GNUC_PREREQ (4, 8) || (defined __mips16 && __GNUC_PREREQ (4, 7))
 /* The __atomic_* builtins are available in GCC 4.7 and later, but MIPS
-   support for their efficient implementation was added only in GCC 4.8.  */
+   support for their efficient implementation was added only in GCC 4.8.
+   We still want to use them even with GCC 4.7 for MIPS16 code where we
+   have no assembly alternative available and want to avoid the __sync_*
+   if at all possible.  */
 
 /* Compare and exchange.
    For all "bool" routines, we return FALSE if exchange succesful.  */
@@ -200,7 +203,33 @@ typedef uintmax_t uatomic_max_t;
 # define atomic_exchange_and_add_rel(mem, value)			\
   __atomic_val_bysize (__arch_exchange_and_add, int, mem, value,	\
 		       __ATOMIC_RELEASE)
-#else /* !__GNUC_PREREQ (4, 8) */
+
+#elif defined __mips16 /* !__GNUC_PREREQ (4, 7) */
+/* This implementation using __sync* builtins will be removed once glibc
+   requires GCC 4.7 or later to build.  */
+
+# define atomic_compare_and_exchange_val_acq(mem, newval, oldval)	\
+  __sync_val_compare_and_swap ((mem), (oldval), (newval))
+# define atomic_compare_and_exchange_bool_acq(mem, newval, oldval)	\
+  (!__sync_bool_compare_and_swap ((mem), (oldval), (newval)))
+
+# define atomic_exchange_acq(mem, newval)				\
+  __sync_lock_test_and_set ((mem), (newval))
+
+# define atomic_exchange_and_add(mem, val)				\
+  __sync_fetch_and_add ((mem), (val))
+
+# define atomic_bit_test_set(mem, bit)					\
+  ({ __typeof (bit) __bit = (bit);					\
+     (__sync_fetch_and_or ((mem), 1 << (__bit)) & (1 << (__bit))); })
+
+# define atomic_and(mem, mask) (void) __sync_fetch_and_and ((mem), (mask))
+# define atomic_and_val(mem, mask) __sync_fetch_and_and ((mem), (mask))
+
+# define atomic_or(mem, mask) (void) __sync_fetch_and_or ((mem), (mask))
+# define atomic_or_val(mem, mask) __sync_fetch_and_or ((mem), (mask))
+
+#else /* !__mips16 && !__GNUC_PREREQ (4, 8) */
 /* This implementation using inline assembly will be removed once glibc
    requires GCC 4.8 or later to build.  */
 
@@ -443,15 +472,21 @@ typedef uintmax_t uatomic_max_t;
 # define atomic_exchange_and_add_rel(mem, value)			\
   __atomic_val_bysize (__arch_exchange_and_add, int, mem, value,	\
 		       MIPS_SYNC_STR, "")
-#endif /* __GNUC_PREREQ (4, 8) */
+
+#endif /* !__mips16 && !__GNUC_PREREQ (4, 8) */
 
 /* TODO: More atomic operations could be implemented efficiently; only the
    basic requirements are done.  */
 
-#define atomic_full_barrier() \
+#ifdef __mips16
+# define atomic_full_barrier() __sync_synchronize ()
+
+#else /* !__mips16 */
+# define atomic_full_barrier() \
   __asm__ __volatile__ (".set push\n\t"					      \
 			MIPS_PUSH_MIPS2					      \
 			MIPS_SYNC_STR "\n\t"				      \
 			".set pop" : : : "memory")
+#endif /* !__mips16 */
 
 #endif /* bits/atomic.h */
diff --git a/ports/sysdeps/mips/bsd-_setjmp.S b/ports/sysdeps/mips/bsd-_setjmp.S
index 1974e4b393..50cce7e725 100644
--- a/ports/sysdeps/mips/bsd-_setjmp.S
+++ b/ports/sysdeps/mips/bsd-_setjmp.S
@@ -22,6 +22,8 @@
 
 #include <sysdep.h>
 
+	.set	nomips16
+
 #ifdef __PIC__
 	.option pic2
 #endif
diff --git a/ports/sysdeps/mips/bsd-setjmp.S b/ports/sysdeps/mips/bsd-setjmp.S
index 5eedcddd78..18e8b0f908 100644
--- a/ports/sysdeps/mips/bsd-setjmp.S
+++ b/ports/sysdeps/mips/bsd-setjmp.S
@@ -22,6 +22,8 @@
 
 #include <sysdep.h>
 
+	.set	nomips16
+
 #ifdef __PIC__
 	.option pic2
 #endif
diff --git a/ports/sysdeps/mips/dl-machine.h b/ports/sysdeps/mips/dl-machine.h
index 91f7a7bb5e..a7c784fec5 100644
--- a/ports/sysdeps/mips/dl-machine.h
+++ b/ports/sysdeps/mips/dl-machine.h
@@ -119,6 +119,7 @@ static inline ElfW(Addr)
 elf_machine_load_address (void)
 {
   ElfW(Addr) addr;
+#ifndef __mips16
   asm ("	.set noreorder\n"
        "	" STRINGXP (PTR_LA) " %0, 0f\n"
        "	bltzal $0, 0f\n"
@@ -128,6 +129,19 @@ elf_machine_load_address (void)
        :	"=r" (addr)
        :	/* No inputs */
        :	"$31");
+#else
+  ElfW(Addr) tmp;
+  asm ("	.set noreorder\n"
+       "	move %1,$gp\n"
+       "	lw %1,%%got(0f)(%1)\n"
+       "0:	.fill 0\n"		/* Clear the ISA bit on 0:.  */
+       "	la %0,0b\n"
+       "	addiu %1,%%lo(0b)\n"
+       "	subu %0,%1\n"
+       "	.set reorder\n"
+       :	"=d" (addr), "=d" (tmp)
+       :	/* No inputs */);
+#endif
   return addr;
 }
 
@@ -210,7 +224,8 @@ do {									\
    2) That under Unix the entry is named __start
       and not just plain _start.  */
 
-#define RTLD_START asm (\
+#ifndef __mips16
+# define RTLD_START asm (\
 	".text\n\
 	" _RTLD_PROLOGUE(ENTRY_POINT) "\
 	" STRINGXV(SETUP_GPX($25)) "\n\
@@ -283,6 +298,91 @@ do {									\
 	".previous"\
 );
 
+#else /* __mips16 */
+/* MIPS16 version.  We currently only support O32 under MIPS16; the proper
+   assembly preprocessor abstractions will need to be added if other ABIs
+   are to be supported.  */
+
+# define RTLD_START asm (\
+	".text\n\
+	.set mips16\n\
+	" _RTLD_PROLOGUE (ENTRY_POINT) "\
+	# Construct GP value in $3.\n\
+	li $3, %hi(_gp_disp)\n\
+	addiu $4, $pc, %lo(_gp_disp)\n\
+	sll $3, 16\n\
+	addu $3, $4\n\
+	move $28, $3\n\
+	lw $4, %got(_DYNAMIC)($3)\n\
+	sw $4, -0x7ff0($3)\n\
+	move $4, $sp\n\
+	addiu $sp, -16\n\
+	# _dl_start() is sufficiently near to use pc-relative\n\
+	# load address.\n\
+	la $3, _dl_start\n\
+	move $25, $3\n\
+	jalr $3\n\
+	addiu $sp, 16\n\
+	" _RTLD_EPILOGUE (ENTRY_POINT) "\
+	\n\
+	\n\
+	" _RTLD_PROLOGUE (_dl_start_user) "\
+	li $16, %hi(_gp_disp)\n\
+	addiu $4, $pc, %lo(_gp_disp)\n\
+	sll $16, 16\n\
+	addu $16, $4\n\
+	move $17, $2\n\
+	move $28, $16\n\
+	lw $4, %got(_dl_skip_args)($16)\n\
+	lw $4, 0($4)\n\
+	beqz $4, 1f\n\
+	# Load the original argument count.\n\
+	lw $5, 0($sp)\n\
+	# Subtract _dl_skip_args from it.\n\
+	subu $5, $4\n\
+	# Adjust the stack pointer to skip _dl_skip_args words.\n\
+	sll $4, " STRINGXP (PTRLOG) "\n\
+	move $6, $sp\n\
+	addu $6, $4\n\
+	move $sp, $6\n\
+	# Save back the modified argument count.\n\
+	sw $5, 0($sp)\n\
+1:	# Call _dl_init (struct link_map *main_map, int argc, char **argv, char **env) \n\
+	lw $4, %got(_rtld_local)($16)\n\
+	lw $4, 0($4)\n\
+	lw $5, 0($sp)\n\
+	addiu $6, $sp, " STRINGXP (PTRSIZE) "\n\
+	sll $7, $5, " STRINGXP (PTRLOG) "\n\
+	addu $7, $6\n\
+	addu $7, " STRINGXP (PTRSIZE) "\n\
+	# Make sure the stack pointer is aligned for _dl_init_internal.\n\
+	li $2, 2 * " STRINGXP (SZREG) "\n\
+	neg $2, $2\n\
+	move $3, $sp\n\
+	and $2, $3\n\
+	sw $3, -" STRINGXP (SZREG) "($2)\n\
+	addiu $2, -32\n\
+	move $sp, $2\n\
+	sw $16, 16($sp)\n\
+	# Call the function to run the initializers.\n\
+	lw $2, %call16(_dl_init_internal)($16)\n\
+	move $25, $2\n\
+	jalr $2\n\
+	# Restore the stack pointer for _start.\n\
+	lw $2, 32-" STRINGXP (SZREG) "($sp)\n\
+	move $sp, $2\n\
+	move $28, $16\n\
+	# Pass our finalizer function to the user in $2 as per ELF ABI.\n\
+	lw $2, %call16(_dl_fini)($16)\n\
+	# Jump to the user entry point.\n\
+	move $25, $17\n\
+	jr $17\n\t"\
+	_RTLD_EPILOGUE (_dl_start_user)\
+	".previous"\
+);
+
+#endif /* __mips16 */
+
 /* Names of the architecture-specific auditing callback functions.  */
 # if _MIPS_SIM == _ABIO32
 #  define ARCH_LA_PLTENTER mips_o32_gnu_pltenter
diff --git a/ports/sysdeps/mips/dl-trampoline.c b/ports/sysdeps/mips/dl-trampoline.c
index 57fb05b86d..605e44e181 100644
--- a/ports/sysdeps/mips/dl-trampoline.c
+++ b/ports/sysdeps/mips/dl-trampoline.c
@@ -292,9 +292,11 @@ __dl_runtime_resolve (ElfW(Word) sym_index,
 
 #endif
 
+#ifndef __mips16
 asm ("\n\
 	.text\n\
 	.align	2\n\
+	.set	nomips16\n\
 	.globl	_dl_runtime_resolve\n\
 	.type	_dl_runtime_resolve,@function\n\
 	.ent	_dl_runtime_resolve\n\
@@ -351,6 +353,7 @@ _dl_runtime_resolve:\n\
 asm ("\n\
 	.text\n\
 	.align	2\n\
+	.set	nomips16\n\
 	.globl	_dl_runtime_pltresolve\n\
 	.type	_dl_runtime_pltresolve,@function\n\
 	.ent	_dl_runtime_pltresolve\n\
@@ -381,3 +384,130 @@ _dl_runtime_pltresolve:\n\
 	.previous\n\
 ");
 
+#elif _MIPS_SIM == _ABIO32 /* __mips16 */
+/* MIPS16 version, O32 only.  */
+asm ("\n\
+	.text\n\
+	.align	2\n\
+	.set	mips16\n\
+	.globl	_dl_runtime_resolve\n\
+	.type	_dl_runtime_resolve,@function\n\
+	.ent	_dl_runtime_resolve\n\
+_dl_runtime_resolve:\n\
+	.frame	$29, " STRINGXP (ELF_DL_FRAME_SIZE) ", $31\n\
+	# Save arguments and sp value in stack.\n\t"
+# if _MIPS_ISA >= _MIPS_ISA_MIPS32
+	"save	" STRINGXP (ELF_DL_FRAME_SIZE) ", $4-$7, $ra\n\t"
+# else
+	"addiu	$sp, -" STRINGXP (ELF_DL_FRAME_SIZE) "\n\
+	sw	$7, 32($sp)\n\
+	sw	$6, 28($sp)\n\
+	sw	$5, 24($sp)\n\
+	sw	$4, 20($sp)\n\t"
+# endif
+	"# Preserve caller's $ra, for RESTORE instruction below.\n\
+	move	$5, $15\n\
+	sw	$5, 36($sp)\n\
+	# Compute GP into $2.\n\
+	li	$2, %hi(_gp_disp)\n\
+	addiu	$3, $pc, %lo(_gp_disp)\n\
+	sll	$2, 16\n\
+	addu	$2, $3\n\
+	lw	$3, %got(__dl_runtime_resolve)($2)\n\
+	move	$4, $24\n\
+	addiu	$3, %lo(__dl_runtime_resolve)\n\
+	move	$7, $ra\n\
+	move	$6, $28\n\
+	move	$25, $3\n\
+	jalr	$3\n\t"
+# if _MIPS_ISA >= _MIPS_ISA_MIPS32
+	"restore " STRINGXP(ELF_DL_FRAME_SIZE) ", $4-$7, $ra\n\t"
+# else
+	"# Restore $ra, move placed further down to hide latency.\n\
+	lw	$4, 36($sp)\n\
+	lw	$5, 24($sp)\n\
+	lw	$6, 28($sp)\n\
+	lw	$7, 32($sp)\n\
+	move	$ra, $4\n\
+	lw	$4, 20($sp)\n\
+	addiu	$sp, " STRINGXP(ELF_DL_FRAME_SIZE) "\n\t"
+# endif
+	"move	$25, $2\n\
+	jr	$2\n\
+	.end	_dl_runtime_resolve\n\
+	.previous\n\
+");
+
+asm ("\n\
+	.text\n\
+	.align	2\n\
+	.set	mips16\n\
+	.globl	_dl_runtime_pltresolve\n\
+	.type	_dl_runtime_pltresolve,@function\n\
+	.ent	_dl_runtime_pltresolve\n\
+_dl_runtime_pltresolve:\n\
+	.frame	$29, " STRINGXP(ELF_DL_PLT_FRAME_SIZE) ", $31\n\
+	# Save arguments and sp value in stack.\n\t"
+# if _MIPS_ISA >= _MIPS_ISA_MIPS32
+	"save	" STRINGXP(ELF_DL_PLT_FRAME_SIZE) ", $4-$7, $ra\n\t"
+# else
+	"addiu	$sp, -" STRINGXP(ELF_DL_PLT_FRAME_SIZE) "\n\
+	sw	$7, 40($sp)\n\
+	sw	$6, 36($sp)\n\
+	sw	$5, 32($sp)\n\
+	sw	$4, 28($sp)\n\t"
+# endif
+	"# Preserve MIPS16 stub function arguments.\n\
+	sw	$3, 20($sp)\n\
+	sw	$2, 16($sp)\n\
+	# Preserve caller's $ra, for RESTORE instruction below.\n\
+	move	$3, $15\n\
+	sw	$3, 44($sp)\n\
+	# Compute GP into $2.\n\
+	li	$2, %hi(_gp_disp)\n\
+	addiu	$3, $pc, %lo(_gp_disp)\n\
+	sll	$2, 16\n\
+	addu	$2, $3\n\
+	# Save GP value in slot.\n\
+	sw	$2, 24($sp)\n\
+	# Load _dl_fixup address.\n\
+	lw	$6, %call16(_dl_fixup)($2)\n\
+	# Load link map address.\n\
+	move	$3, $28\n\
+	lw	$4, " STRINGXP (PTRSIZE) "($3)\n\
+	move	$5, $24\n\
+	sll	$5, " STRINGXP (PTRLOG) " + 1\n\
+	# Call _dl_fixup.\n\
+	move	$25, $6\n\
+	jalr	$6\n\
+	move	$25, $2\n\
+	# Reload GP value into $28.\n\
+	lw	$3, 24($sp)\n\
+	move	$28, $3\n\
+	lw	$3, 16($sp)\n\
+	move	$15, $3\n\
+	lw	$3, 20($sp)\n\t"
+# if _MIPS_ISA >= _MIPS_ISA_MIPS32
+	"restore " STRINGXP (ELF_DL_PLT_FRAME_SIZE) ", $4-$7, $ra\n\t"
+# else
+	"# Restore $ra, move placed further down to hide latency.\n\
+	lw	$4, 44($sp)\n\
+	lw	$5, 32($sp)\n\
+	lw	$6, 36($sp)\n\
+	lw	$7, 40($sp)\n\
+	move	$ra, $4\n\
+	lw	$4, 28($sp)\n\
+	addiu	$sp, " STRINGXP (ELF_DL_PLT_FRAME_SIZE) "\n\t"
+# endif
+	".set	noreorder\n\
+	jr	$2\n\
+	 move	$2, $15\n\
+	.set	reorder\n\
+	.end	_dl_runtime_pltresolve\n\
+	.previous\n\
+");
+
+#else /* __mips16 && _MIPS_SIM != _ABIO32 */
+# error "MIPS16 support for N32/N64 not implemented"
+
+#endif /* __mips16 */
diff --git a/ports/sysdeps/mips/fpu/e_sqrt.c b/ports/sysdeps/mips/fpu/e_sqrt.c
index cff9cec6ec..26314b0109 100644
--- a/ports/sysdeps/mips/fpu/e_sqrt.c
+++ b/ports/sysdeps/mips/fpu/e_sqrt.c
@@ -22,7 +22,7 @@
 
 #if (_MIPS_ISA >= _MIPS_ISA_MIPS2)
 
-double
+double __attribute__ ((nomips16))
 __ieee754_sqrt (double x)
 {
   double z;
diff --git a/ports/sysdeps/mips/fpu/e_sqrtf.c b/ports/sysdeps/mips/fpu/e_sqrtf.c
index 87d242d82c..0f7bfd94ba 100644
--- a/ports/sysdeps/mips/fpu/e_sqrtf.c
+++ b/ports/sysdeps/mips/fpu/e_sqrtf.c
@@ -22,7 +22,7 @@
 
 #if (_MIPS_ISA >= _MIPS_ISA_MIPS2)
 
-float
+float __attribute__ ((nomips16))
 __ieee754_sqrtf (float x)
 {
   float z;
diff --git a/ports/sysdeps/mips/fpu_control.h b/ports/sysdeps/mips/fpu_control.h
index 30e54f9c3a..6aecb3bc8b 100644
--- a/ports/sysdeps/mips/fpu_control.h
+++ b/ports/sysdeps/mips/fpu_control.h
@@ -99,8 +99,15 @@ extern fpu_control_t __fpu_control;
 typedef unsigned int fpu_control_t __attribute__ ((__mode__ (__SI__)));
 
 /* Macros for accessing the hardware control word.  */
-#define _FPU_GETCW(cw) __asm__ volatile ("cfc1 %0,$31" : "=r" (cw))
-#define _FPU_SETCW(cw) __asm__ volatile ("ctc1 %0,$31" : : "r" (cw))
+extern fpu_control_t __mips_fpu_getcw (void) __THROW;
+extern void __mips_fpu_setcw (fpu_control_t) __THROW;
+#ifdef __mips16
+# define _FPU_GETCW(cw) do { (cw) = __mips_fpu_getcw (); } while (0)
+# define _FPU_SETCW(cw) __mips_fpu_setcw (cw)
+#else
+# define _FPU_GETCW(cw) __asm__ volatile ("cfc1 %0,$31" : "=r" (cw))
+# define _FPU_SETCW(cw) __asm__ volatile ("ctc1 %0,$31" : : "r" (cw))
+#endif
 
 /* Default control word set at startup.  */
 extern fpu_control_t __fpu_control;
diff --git a/ports/sysdeps/mips/machine-gmon.h b/ports/sysdeps/mips/machine-gmon.h
index 8c62d84a93..144c044026 100644
--- a/ports/sysdeps/mips/machine-gmon.h
+++ b/ports/sysdeps/mips/machine-gmon.h
@@ -37,6 +37,8 @@ static void __attribute_used__ __mcount (u_long frompc, u_long selfpc)
 #define MCOUNT asm(\
 	".globl _mcount;\n\t" \
 	".align 2;\n\t" \
+	".set push;\n\t" \
+	".set nomips16;\n\t" \
 	".type _mcount,@function;\n\t" \
 	".ent _mcount\n\t" \
         "_mcount:\n\t" \
@@ -67,9 +69,8 @@ static void __attribute_used__ __mcount (u_long frompc, u_long selfpc)
         "addu $29,$29,56;\n\t" \
         "j $31;\n\t" \
         "move $31,$1;\n\t" \
-        ".set reorder;\n\t" \
-        ".set at\n\t" \
-        ".end _mcount");
+	".end _mcount;\n\t" \
+	".set pop");
 
 #else
 
@@ -94,6 +95,8 @@ static void __attribute_used__ __mcount (u_long frompc, u_long selfpc)
 #define MCOUNT asm(\
 	".globl _mcount;\n\t" \
 	".align 3;\n\t" \
+	".set push;\n\t" \
+	".set nomips16;\n\t" \
 	".type _mcount,@function;\n\t" \
 	".ent _mcount\n\t" \
         "_mcount:\n\t" \
@@ -132,8 +135,7 @@ static void __attribute_used__ __mcount (u_long frompc, u_long selfpc)
         PTR_ADDU_STRING " $29,$29,96;\n\t" \
         "j $31;\n\t" \
         "move $31,$1;\n\t" \
-        ".set reorder;\n\t" \
-        ".set at\n\t" \
-        ".end _mcount");
+	".end _mcount;\n\t" \
+	".set pop");
 
 #endif
diff --git a/ports/sysdeps/mips/memset.S b/ports/sysdeps/mips/memset.S
index 8c8606c834..78dab481dd 100644
--- a/ports/sysdeps/mips/memset.S
+++ b/ports/sysdeps/mips/memset.S
@@ -18,6 +18,7 @@
 
 #include <sysdep.h>
 
+	.set	nomips16
 
 /* void *memset(void *s, int c, size_t n).  */
 
diff --git a/ports/sysdeps/mips/mips32/crti.S b/ports/sysdeps/mips/mips32/crti.S
index 5b46279c1e..5f3e9ba43d 100644
--- a/ports/sysdeps/mips/mips32/crti.S
+++ b/ports/sysdeps/mips/mips32/crti.S
@@ -54,6 +54,8 @@
 	.hidden PREINIT_FUNCTION
 #endif
 
+	.set nomips16
+
 	.section .init,"ax",@progbits
 	.p2align 2
 	.globl _init
diff --git a/ports/sysdeps/mips/mips32/crtn.S b/ports/sysdeps/mips/mips32/crtn.S
index 44cdff0a1a..42381c5129 100644
--- a/ports/sysdeps/mips/mips32/crtn.S
+++ b/ports/sysdeps/mips/mips32/crtn.S
@@ -36,6 +36,8 @@
 /* crtn.S puts function epilogues in the .init and .fini sections
    corresponding to the prologues in crti.S. */
 
+	.set nomips16
+
 	.section .init,"ax",@progbits
 	lw $31,28($sp)
 	.set noreorder
diff --git a/ports/sysdeps/mips/mips32/fpu/Versions b/ports/sysdeps/mips/mips32/fpu/Versions
new file mode 100644
index 0000000000..91bbf564b3
--- /dev/null
+++ b/ports/sysdeps/mips/mips32/fpu/Versions
@@ -0,0 +1,5 @@
+libc {
+  GLIBC_2.18 {
+    __mips_fpu_getcw; __mips_fpu_setcw;
+  }
+}
diff --git a/ports/sysdeps/mips/mips32/fpu/fpu_control.c b/ports/sysdeps/mips/mips32/fpu/fpu_control.c
new file mode 100644
index 0000000000..cd107c533a
--- /dev/null
+++ b/ports/sysdeps/mips/mips32/fpu/fpu_control.c
@@ -0,0 +1,34 @@
+/* FPU control word handling, MIPS version, needed by MIPS16 callers.
+   Copyright (C) 1996-2013 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <math/fpu_control.c>
+
+fpu_control_t
+__mips_fpu_getcw (void)
+{
+  fpu_control_t cw;
+
+  _FPU_GETCW (cw);
+  return cw;
+}
+
+void
+__mips_fpu_setcw (fpu_control_t cw)
+{
+  _FPU_SETCW (cw);
+}
diff --git a/ports/sysdeps/mips/mips32/mips16/add_n.c b/ports/sysdeps/mips/mips32/mips16/add_n.c
new file mode 100644
index 0000000000..fbb4120418
--- /dev/null
+++ b/ports/sysdeps/mips/mips32/mips16/add_n.c
@@ -0,0 +1 @@
+#include <stdlib/add_n.c>
diff --git a/ports/sysdeps/mips/mips32/mips16/addmul_1.c b/ports/sysdeps/mips/mips32/mips16/addmul_1.c
new file mode 100644
index 0000000000..c0e4a0bd7e
--- /dev/null
+++ b/ports/sysdeps/mips/mips32/mips16/addmul_1.c
@@ -0,0 +1 @@
+#include <stdlib/addmul_1.c>
diff --git a/ports/sysdeps/mips/mips32/mips16/fpu/Makefile b/ports/sysdeps/mips/mips32/mips16/fpu/Makefile
new file mode 100644
index 0000000000..b58c4eeb0c
--- /dev/null
+++ b/ports/sysdeps/mips/mips32/mips16/fpu/Makefile
@@ -0,0 +1,5 @@
+# Building hard-float libm as MIPS16 actually produces larger code size,
+# so avoid doing so.
+ifeq ($(subdir),math)
+sysdep-CFLAGS += -mno-mips16
+endif
diff --git a/ports/sysdeps/mips/mips32/mips16/lshift.c b/ports/sysdeps/mips/mips32/mips16/lshift.c
new file mode 100644
index 0000000000..2f945d2c59
--- /dev/null
+++ b/ports/sysdeps/mips/mips32/mips16/lshift.c
@@ -0,0 +1 @@
+#include <stdlib/lshift.c>
diff --git a/ports/sysdeps/mips/mips32/mips16/mul_1.c b/ports/sysdeps/mips/mips32/mips16/mul_1.c
new file mode 100644
index 0000000000..8e758d6039
--- /dev/null
+++ b/ports/sysdeps/mips/mips32/mips16/mul_1.c
@@ -0,0 +1 @@
+#include <stdlib/mul_1.c>
diff --git a/ports/sysdeps/mips/mips32/mips16/rshift.c b/ports/sysdeps/mips/mips32/mips16/rshift.c
new file mode 100644
index 0000000000..4e350a0dcb
--- /dev/null
+++ b/ports/sysdeps/mips/mips32/mips16/rshift.c
@@ -0,0 +1 @@
+#include <stdlib/rshift.c>
diff --git a/ports/sysdeps/mips/mips32/mips16/sub_n.c b/ports/sysdeps/mips/mips32/mips16/sub_n.c
new file mode 100644
index 0000000000..d8b54925b1
--- /dev/null
+++ b/ports/sysdeps/mips/mips32/mips16/sub_n.c
@@ -0,0 +1 @@
+#include <stdlib/sub_n.c>
diff --git a/ports/sysdeps/mips/mips32/mips16/submul_1.c b/ports/sysdeps/mips/mips32/mips16/submul_1.c
new file mode 100644
index 0000000000..44cadf5cc0
--- /dev/null
+++ b/ports/sysdeps/mips/mips32/mips16/submul_1.c
@@ -0,0 +1 @@
+#include <stdlib/submul_1.c>
diff --git a/ports/sysdeps/mips/mips64/n32/crti.S b/ports/sysdeps/mips/mips64/n32/crti.S
index d9066b2d64..ddcc9cc7f5 100644
--- a/ports/sysdeps/mips/mips64/n32/crti.S
+++ b/ports/sysdeps/mips/mips64/n32/crti.S
@@ -54,6 +54,8 @@
 	.hidden PREINIT_FUNCTION
 #endif
 
+	.set nomips16
+
 	.section .init,"ax",@progbits
 	.p2align 2
 	.globl _init
diff --git a/ports/sysdeps/mips/mips64/n32/crtn.S b/ports/sysdeps/mips/mips64/n32/crtn.S
index daf7d4256d..5eb2b4f489 100644
--- a/ports/sysdeps/mips/mips64/n32/crtn.S
+++ b/ports/sysdeps/mips/mips64/n32/crtn.S
@@ -36,6 +36,8 @@
 /* crtn.S puts function epilogues in the .init and .fini sections
    corresponding to the prologues in crti.S. */
 
+	.set nomips16
+
 	.section .init,"ax",@progbits
 	ld $31,8($sp)
 	ld $28,0($sp)
diff --git a/ports/sysdeps/mips/mips64/n64/crti.S b/ports/sysdeps/mips/mips64/n64/crti.S
index 2111ba5394..0c66d0de6f 100644
--- a/ports/sysdeps/mips/mips64/n64/crti.S
+++ b/ports/sysdeps/mips/mips64/n64/crti.S
@@ -54,6 +54,8 @@
 	.hidden PREINIT_FUNCTION
 #endif
 
+	.set nomips16
+
 	.section .init,"ax",@progbits
 	.p2align 2
 	.globl _init
diff --git a/ports/sysdeps/mips/mips64/n64/crtn.S b/ports/sysdeps/mips/mips64/n64/crtn.S
index c66a2e5550..4c014b711b 100644
--- a/ports/sysdeps/mips/mips64/n64/crtn.S
+++ b/ports/sysdeps/mips/mips64/n64/crtn.S
@@ -36,6 +36,8 @@
 /* crtn.S puts function epilogues in the .init and .fini sections
    corresponding to the prologues in crti.S. */
 
+	.set nomips16
+
 	.section .init,"ax",@progbits
 	ld $31,8($sp)
 	ld $28,0($sp)
diff --git a/ports/sysdeps/mips/nptl/tls.h b/ports/sysdeps/mips/nptl/tls.h
index 0c91995342..2529408f2f 100644
--- a/ports/sysdeps/mips/nptl/tls.h
+++ b/ports/sysdeps/mips/nptl/tls.h
@@ -37,12 +37,17 @@ typedef union dtv
   } pointer;
 } dtv_t;
 
+#ifdef __mips16
+/* MIPS16 uses GCC builtin to access the TP.  */
+# define READ_THREAD_POINTER() (__builtin_thread_pointer ())
+#else
 /* Note: rd must be $v1 to be ABI-conformant.  */
 # define READ_THREAD_POINTER() \
     ({ void *__result;							      \
        asm volatile (".set\tpush\n\t.set\tmips32r2\n\t"			      \
 		     "rdhwr\t%0, $29\n\t.set\tpop" : "=v" (__result));	      \
        __result; })
+#endif
 
 #else /* __ASSEMBLER__ */
 # include <tcb-offsets.h>
diff --git a/ports/sysdeps/mips/preconfigure b/ports/sysdeps/mips/preconfigure
index 9190eee87a..b215eb2c17 100644
--- a/ports/sysdeps/mips/preconfigure
+++ b/ports/sysdeps/mips/preconfigure
@@ -25,5 +25,10 @@ mips64*)	base_machine=mips64
 		  CPPFLAGS="$CPPFLAGS -mabi=$mips_config_abi"
 		fi
 		;;
-mips*)		base_machine=mips machine=mips/mips32/$machine ;;
+mips*)		base_machine=mips
+		case "$CC $CFLAGS $CPPFLAGS " in
+		*" -mips16 "*) machine=mips/mips32/mips16/$machine ;;
+		*) machine=mips/mips32/$machine ;;
+		esac
+		;;
 esac
diff --git a/ports/sysdeps/mips/setjmp.S b/ports/sysdeps/mips/setjmp.S
index 3f923fb52b..f014b73b3f 100644
--- a/ports/sysdeps/mips/setjmp.S
+++ b/ports/sysdeps/mips/setjmp.S
@@ -17,6 +17,8 @@
 
 #include <sysdep.h>
 
+	.set	nomips16
+
 /* The function __sigsetjmp_aux saves all the registers, but it can't
    reliably access the stack or frame pointers, so we pass them in as
    extra arguments.  */
diff --git a/ports/sysdeps/mips/setjmp_aux.c b/ports/sysdeps/mips/setjmp_aux.c
index a955a4ffa1..cb9ea245bb 100644
--- a/ports/sysdeps/mips/setjmp_aux.c
+++ b/ports/sysdeps/mips/setjmp_aux.c
@@ -23,7 +23,7 @@
    pointer.  We do things this way because it's difficult to reliably
    access them in C.  */
 
-int
+int __attribute__ ((nomips16))
 __sigsetjmp_aux (jmp_buf env, int savemask, int sp, int fp)
 {
 #ifdef __mips_hard_float
diff --git a/ports/sysdeps/mips/start.S b/ports/sysdeps/mips/start.S
index 82b7a229f3..83a68959a3 100644
--- a/ports/sysdeps/mips/start.S
+++ b/ports/sysdeps/mips/start.S
@@ -74,14 +74,15 @@
 	.text
 	.globl ENTRY_POINT
 	.type ENTRY_POINT,@function
+#ifndef __mips16
 ENTRY_POINT:
-#ifdef __PIC__
+# ifdef __PIC__
 	SETUP_GPX($0)
 	SETUP_GPX64($25,$0)
-#else
+# else
 	PTR_LA $28, _gp		/* Setup GP correctly if we're non-PIC.  */
 	move $31, $0
-#endif
+# endif
 
 	PTR_LA $4, main		/* main */
 	PTR_L $5, 0($29)		/* argc */
@@ -92,22 +93,85 @@ ENTRY_POINT:
 	   on o32 and quad words (16 bytes) on n32 and n64.  */
 	
 	and $29, -2 * SZREG
-#if _MIPS_SIM == _ABIO32
+# if _MIPS_SIM == _ABIO32
 	PTR_SUBIU $29, 32
-#endif
+# endif
 	PTR_LA $7, __libc_csu_init		/* init */
 	PTR_LA $8, __libc_csu_fini
-#if _MIPS_SIM == _ABIO32
+# if _MIPS_SIM == _ABIO32
 	PTR_S $8, 16($29)		/* fini */
 	PTR_S $2, 20($29)		/* rtld_fini */
 	PTR_S $29, 24($29)		/* stack_end */
-#else
+# else
 	move $9, $2		/* rtld_fini */
 	move $10, $29		/* stack_end */
-#endif
+# endif
 	jal __libc_start_main
 hlt:	b hlt			/* Crash if somehow it does return.  */
 
+#elif _MIPS_SIM == _ABIO32 /* __mips16 */
+	/* MIPS16 entry point.  */
+	.set	mips16
+ENTRY_POINT:
+# ifdef __PIC__
+	li	$3, %hi(_gp_disp)
+	addiu	$4, $pc, %lo(_gp_disp)
+	sll	$3, 16
+	addu	$3, $4
+	move	$gp, $3
+# else
+	li	$3, %hi(_gp)
+	sll	$3, 16
+	addiu	$3, %lo(_gp)
+	move	$gp, $3
+# endif
+	/* Tie end of stack frames.  */
+	li	$4, 0
+	move	$31, $4
+	/* Create new SP value in $7, including alignment.  */
+	li	$4, 2 * SZREG
+	neg	$4, $4
+	move	$7, $sp
+	and	$7, $4
+	addiu	$7, -32
+	/* Load arguments with original SP.  */
+	lw	$5, 0($sp)
+	addiu	$6, $sp, PTRSIZE
+	/* Update SP.  */
+	move	$sp, $7
+	/* Lay out last arguments, and call __libc_start_main().  */
+# ifdef __PIC__
+	sw	$7, 24($sp)			/* stack_end */
+	lw	$4, %got(__libc_csu_fini)($3)
+	lw	$7, %got(__libc_csu_init)($3)	/* init */
+	sw	$4, 16($sp)			/* fini */
+	lw	$4, %got(main)($3)		/* main */
+	lw	$3, %call16(__libc_start_main)($3)
+	sw	$2, 20($sp)			/* rtld_fini */
+	move	$25, $3
+	jalr	$3
+# else
+	lw	$4, 1f
+	sw	$7, 24($sp)			/* stack_end */
+	lw	$7, 2f				/* init */
+	sw	$4, 16($sp)			/* fini */
+	lw	$4, 3f				/* main */
+	sw	$2, 20($sp)			/* rtld_fini */
+	jal	__libc_start_main
+# endif
+hlt:	b	hlt		/* Crash if somehow it does return.  */
+# ifndef __PIC__
+	.align	2
+1:	.word	__libc_csu_fini
+2:	.word	__libc_csu_init
+3:	.word	main
+# endif
+
+#else /* __mips16 && _MIPS_SIM != _ABIO32 */
+# error "MIPS16 support for N32/N64 not implemented"
+
+#endif /* __mips16 */
+
 /* Define a symbol for the first piece of initialized data.  */
 	.data
 	.globl __data_start
diff --git a/ports/sysdeps/mips/sys/tas.h b/ports/sysdeps/mips/sys/tas.h
index 603346ac3b..871818565e 100644
--- a/ports/sysdeps/mips/sys/tas.h
+++ b/ports/sysdeps/mips/sys/tas.h
@@ -24,7 +24,8 @@
 
 __BEGIN_DECLS
 
-extern int _test_and_set (int *__p, int __v) __THROW;
+extern int _test_and_set (int *__p, int __v)
+     __THROW __attribute__ ((__nomips16__));
 
 #ifdef __USE_EXTERN_INLINES
 
@@ -32,7 +33,7 @@ extern int _test_and_set (int *__p, int __v) __THROW;
 #  define _EXTERN_INLINE __extern_inline
 # endif
 
-_EXTERN_INLINE int
+_EXTERN_INLINE int __attribute__ ((__nomips16__))
 __NTH (_test_and_set (int *__p, int __v))
 {
   int __r, __t;
diff --git a/ports/sysdeps/mips/tls-macros.h b/ports/sysdeps/mips/tls-macros.h
index 8fe2e4a150..3e87e42ea1 100644
--- a/ports/sysdeps/mips/tls-macros.h
+++ b/ports/sysdeps/mips/tls-macros.h
@@ -12,16 +12,33 @@
    (abicalls pic0) function.  */
 #ifndef __PIC__
 # if _MIPS_SIM != _ABI64
-#  define LOAD_GP "move %[tmp], $28\n\tla $28, __gnu_local_gp\n\t"
+#  ifndef __mips16
+#   define LOAD_GP "move %[tmp], $28\n\tla $28, __gnu_local_gp\n\t"
+#  else
+#   define LOAD_GP					\
+           "li %[tmp], %%hi(__gnu_local_gp)\n\t"	\
+           "sll %[tmp], 16\n\t"				\
+           "addiu %[tmp], %%lo(__gnu_local_gp)\n\t"
+#  endif
 # else
 #  define LOAD_GP "move %[tmp], $28\n\tdla $28, __gnu_local_gp\n\t"
 # endif
 # define UNLOAD_GP "\n\tmove $28, %[tmp]"
 #else
-# define LOAD_GP
+/* MIPS16 (re)creates the GP value using PC-relative instructions.  */
+# ifdef __mips16
+#  define LOAD_GP					\
+           "li %[tmp], %%hi(_gp_disp)\n\t"		\
+           "addiu %0, $pc, %%lo(_gp_disp)\n\t"		\
+           "sll %[tmp], 16\n\t"				\
+           "addu %[tmp], %0\n\t"
+# else
+#  define LOAD_GP
+# endif
 # define UNLOAD_GP
 #endif
 
+#ifndef __mips16
 # define TLS_GD(x)					\
   ({ void *__result, *__tmp;				\
      extern void *__tls_get_addr (void *);		\
@@ -62,3 +79,45 @@
 	  ADDU " %0,%0,$3"				\
 	  : "+r" (__result) : : "$3");			\
      __result; })
+
+#else /* __mips16 */
+/* MIPS16 version.  */
+# define TLS_GD(x)					\
+  ({ void *__result, *__tmp;				\
+     extern void *__tls_get_addr (void *);		\
+     asm (LOAD_GP ADDIU " %1, %%tlsgd(" #x ")"		\
+	  "\n\tmove %0, %1"				\
+	  : "=d" (__result), [tmp] "=&d" (__tmp));	\
+     (int *) __tls_get_addr (__result); })
+# define TLS_LD(x)					\
+  ({ void *__result, *__tmp;				\
+     extern void *__tls_get_addr (void *);		\
+     asm (LOAD_GP ADDIU " %1, %%tlsldm(" #x ")"		\
+	  "\n\tmove %0, %1"				\
+	  : "=d" (__result), [tmp] "=&d" (__tmp));	\
+     __result = __tls_get_addr (__result);		\
+     asm ("li $3,%%dtprel_hi(" #x ")\n\t"		\
+	  "sll $3,16\n\t"				\
+	  "addiu $3,%%dtprel_lo(" #x ")\n\t"		\
+	  ADDU " %0,%0,$3"				\
+	  : "+d" (__result) : : "$3");			\
+     __result; })
+# define TLS_IE(x)					\
+  ({ void *__result, *__tmp, *__tp;			\
+     __tp = __builtin_thread_pointer ();		\
+     asm (LOAD_GP LW " $3,%%gottprel(" #x ")(%1)\n\t"	\
+	  ADDU " %0,%[tp],$3"				\
+	  : "=&d" (__result), [tmp] "=&d" (__tmp)	\
+	  : [tp] "d" (__tp) : "$3");			\
+     __result; })
+# define TLS_LE(x)					\
+  ({ void *__result, *__tp;				\
+     __tp = __builtin_thread_pointer ();		\
+     asm ("li $3,%%tprel_hi(" #x ")\n\t"		\
+	  "sll $3,16\n\t"				\
+	  "addiu $3,%%tprel_lo(" #x ")\n\t"		\
+	  ADDU " %0,%[tp],$3"				\
+	  : "=d" (__result) : [tp] "d" (__tp) : "$3");	\
+     __result; })
+
+#endif /* __mips16 */
diff --git a/ports/sysdeps/unix/mips/mips32/sysdep.h b/ports/sysdeps/unix/mips/mips32/sysdep.h
index 5c5d7695d0..5d96d05c65 100644
--- a/ports/sysdeps/unix/mips/mips32/sysdep.h
+++ b/ports/sysdeps/unix/mips/mips32/sysdep.h
@@ -24,6 +24,7 @@
 #ifdef __PIC__
 #define PSEUDO(name, syscall_name, args) \
   .align 2;								      \
+  .set nomips16;							      \
   cfi_startproc;							      \
   99: la t9,__syscall_error;						      \
   jr t9;								      \
@@ -39,6 +40,7 @@ L(syse1):
 #else
 #define PSEUDO(name, syscall_name, args) \
   .set noreorder;							      \
+  .set nomips16;							      \
   .align 2;								      \
   cfi_startproc;							      \
   99: j __syscall_error;						      \
diff --git a/ports/sysdeps/unix/mips/mips64/n32/sysdep.h b/ports/sysdeps/unix/mips/mips64/n32/sysdep.h
index 49ae76991e..c55b95cc2a 100644
--- a/ports/sysdeps/unix/mips/mips64/n32/sysdep.h
+++ b/ports/sysdeps/unix/mips/mips64/n32/sysdep.h
@@ -26,6 +26,7 @@
 #ifdef __PIC__
 #define PSEUDO(name, syscall_name, args) \
   .align 2;								      \
+  .set nomips16;							      \
   cfi_startproc;							      \
   99:;									      \
   .set noat;								      \
@@ -46,6 +47,7 @@ L(syse1):
 #define PSEUDO(name, syscall_name, args) \
   .set noreorder;							      \
   .align 2;								      \
+  .set nomips16;							      \
   cfi_startproc;							      \
   99: j __syscall_error;						      \
   nop;                                                                        \
diff --git a/ports/sysdeps/unix/mips/mips64/n64/sysdep.h b/ports/sysdeps/unix/mips/mips64/n64/sysdep.h
index 2670c68b7d..445e6ff738 100644
--- a/ports/sysdeps/unix/mips/mips64/n64/sysdep.h
+++ b/ports/sysdeps/unix/mips/mips64/n64/sysdep.h
@@ -26,6 +26,7 @@
 #ifdef __PIC__
 #define PSEUDO(name, syscall_name, args) \
   .align 2;								      \
+  .set nomips16;							      \
   cfi_startproc;							      \
   99:;									      \
   .set noat;								      \
@@ -46,6 +47,7 @@ L(syse1):
 #define PSEUDO(name, syscall_name, args) \
   .set noreorder;							      \
   .align 2;								      \
+  .set nomips16;							      \
   cfi_startproc;							      \
   99: j __syscall_error;						      \
   nop;                                                                        \
diff --git a/ports/sysdeps/unix/mips/sysdep.S b/ports/sysdeps/unix/mips/sysdep.S
index e778918573..e27aada1db 100644
--- a/ports/sysdeps/unix/mips/sysdep.S
+++ b/ports/sysdeps/unix/mips/sysdep.S
@@ -21,6 +21,8 @@
 #include <bits/errno.h>
 #include <sys/asm.h>
 
+	.set	nomips16
+
 #ifdef _LIBC_REENTRANT
 
 LOCALSZ= 3
diff --git a/ports/sysdeps/unix/mips/sysdep.h b/ports/sysdeps/unix/mips/sysdep.h
index ef5c0893f3..4d7667ea9c 100644
--- a/ports/sysdeps/unix/mips/sysdep.h
+++ b/ports/sysdeps/unix/mips/sysdep.h
@@ -44,6 +44,7 @@
 #define PSEUDO_NOERRNO(name, syscall_name, args)	\
   .align 2;						\
   ENTRY(name)						\
+  .set nomips16;					\
   .set noreorder;					\
   li v0, SYS_ify(syscall_name);				\
   syscall
@@ -56,6 +57,7 @@
 #define PSEUDO_ERRVAL(name, syscall_name, args)	\
   .align 2;						\
   ENTRY(name)						\
+  .set nomips16;					\
   .set noreorder;					\
   li v0, SYS_ify(syscall_name);				\
   syscall
diff --git a/ports/sysdeps/unix/sysv/linux/mips/brk.c b/ports/sysdeps/unix/sysv/linux/mips/brk.c
index 9bd6d94bd1..07c8a9649f 100644
--- a/ports/sysdeps/unix/sysv/linux/mips/brk.c
+++ b/ports/sysdeps/unix/sysv/linux/mips/brk.c
@@ -30,19 +30,10 @@ weak_alias (__curbrk, ___brk_addr)
 int
 __brk (void *addr)
 {
+  INTERNAL_SYSCALL_DECL (err);
   void *newbrk;
 
-  {
-    register long int res __asm__ ("$2");
-
-    asm ("move\t$4,%2\n\t"
-	 "li\t%0,%1\n\t"
-	 "syscall"		/* Perform the system call.  */
-	 : "=r" (res)
-	 : "I" (SYS_ify (brk)), "r" (addr)
-	 : "$4", "$7", __SYSCALL_CLOBBERS);
-    newbrk = (void *) res;
-  }
+  newbrk = (void *) INTERNAL_SYSCALL (brk, err, 1, addr);
   __curbrk = newbrk;
 
   if (newbrk < addr)
diff --git a/ports/sysdeps/unix/sysv/linux/mips/clone.S b/ports/sysdeps/unix/sysv/linux/mips/clone.S
index ef801882a9..f6f2f05659 100644
--- a/ports/sysdeps/unix/sysv/linux/mips/clone.S
+++ b/ports/sysdeps/unix/sysv/linux/mips/clone.S
@@ -34,6 +34,7 @@
 	     void *parent_tidptr, void *tls, void *child_tidptr) */
 
 	.text
+	.set		nomips16
 #if _MIPS_SIM == _ABIO32
 # define EXTRA_LOCALS 1
 #else
diff --git a/ports/sysdeps/unix/sysv/linux/mips/getcontext.S b/ports/sysdeps/unix/sysv/linux/mips/getcontext.S
index 7af6825a4a..268098c0b1 100644
--- a/ports/sysdeps/unix/sysv/linux/mips/getcontext.S
+++ b/ports/sysdeps/unix/sysv/linux/mips/getcontext.S
@@ -27,6 +27,7 @@
 /* int getcontext (ucontext_t *ucp) */
 
 	.text
+	.set	nomips16
 LOCALSZ = 0
 MASK = 0x00000000
 #ifdef __PIC__
diff --git a/ports/sysdeps/unix/sysv/linux/mips/makecontext.S b/ports/sysdeps/unix/sysv/linux/mips/makecontext.S
index d89db01677..a8bbebbcc0 100644
--- a/ports/sysdeps/unix/sysv/linux/mips/makecontext.S
+++ b/ports/sysdeps/unix/sysv/linux/mips/makecontext.S
@@ -27,6 +27,7 @@
 /* int makecontext (ucontext_t *ucp, (void *func) (), int argc, ...) */
 
 	.text
+	.set	nomips16
 LOCALSZ = 0
 ARGSZ = 0
 MASK = 0x00000000
diff --git a/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/Makefile b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/Makefile
new file mode 100644
index 0000000000..fa9fcb7e6f
--- /dev/null
+++ b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/Makefile
@@ -0,0 +1,13 @@
+ifeq ($(subdir),misc)
+sysdep_routines += mips16-syscall0 mips16-syscall1 mips16-syscall2
+sysdep_routines += mips16-syscall3 mips16-syscall4 mips16-syscall5
+sysdep_routines += mips16-syscall6 mips16-syscall7
+CFLAGS-mips16-syscall0.c += -fexceptions
+CFLAGS-mips16-syscall1.c += -fexceptions
+CFLAGS-mips16-syscall2.c += -fexceptions
+CFLAGS-mips16-syscall3.c += -fexceptions
+CFLAGS-mips16-syscall4.c += -fexceptions
+CFLAGS-mips16-syscall5.c += -fexceptions
+CFLAGS-mips16-syscall6.c += -fexceptions
+CFLAGS-mips16-syscall7.c += -fexceptions
+endif
diff --git a/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/Versions b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/Versions
new file mode 100644
index 0000000000..73bcfb566c
--- /dev/null
+++ b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/Versions
@@ -0,0 +1,6 @@
+libc {
+  GLIBC_PRIVATE {
+    __mips16_syscall0; __mips16_syscall1; __mips16_syscall2; __mips16_syscall3;
+    __mips16_syscall4; __mips16_syscall5; __mips16_syscall6; __mips16_syscall7;
+  }
+}
diff --git a/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall.h b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall.h
new file mode 100644
index 0000000000..8449836d32
--- /dev/null
+++ b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall.h
@@ -0,0 +1,89 @@
+/* MIPS16 syscall wrappers.
+   Copyright (C) 2013 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef MIPS16_SYSCALL_H
+#define MIPS16_SYSCALL_H 1
+
+#define __nomips16 __attribute__ ((nomips16))
+
+union __mips16_syscall_return
+  {
+    long long val;
+    struct
+      {
+	long v0;
+	long v1;
+      }
+    reg;
+  };
+
+long long __nomips16 __mips16_syscall0 (long number);
+#define __mips16_syscall0(dummy, number)				\
+	__mips16_syscall0 ((long) (number))
+
+long long __nomips16 __mips16_syscall1 (long a0,
+					long number);
+#define __mips16_syscall1(a0, number)					\
+	__mips16_syscall1 ((long) (a0),					\
+			   (long) (number))
+
+long long __nomips16 __mips16_syscall2 (long a0, long a1,
+					long number);
+#define __mips16_syscall2(a0, a1, number)				\
+	__mips16_syscall2 ((long) (a0), (long) (a1),			\
+			   (long) (number))
+
+long long __nomips16 __mips16_syscall3 (long a0, long a1, long a2,
+					long number);
+#define __mips16_syscall3(a0, a1, a2, number)				\
+	__mips16_syscall3 ((long) (a0), (long) (a1), (long) (a2),	\
+			   (long) (number))
+
+long long __nomips16 __mips16_syscall4 (long a0, long a1, long a2, long a3,
+					long number);
+#define __mips16_syscall4(a0, a1, a2, a3, number)			\
+	__mips16_syscall4 ((long) (a0), (long) (a1), (long) (a2),	\
+			   (long) (a3),					\
+			   (long) (number))
+
+long long __nomips16 __mips16_syscall5 (long a0, long a1, long a2, long a3,
+					long a4,
+					long number);
+#define __mips16_syscall5(a0, a1, a2, a3, a4, number)			\
+	__mips16_syscall5 ((long) (a0), (long) (a1), (long) (a2),	\
+			   (long) (a3), (long) (a4),			\
+			   (long) (number))
+
+long long __nomips16 __mips16_syscall6 (long a0, long a1, long a2, long a3,
+					long a4, long a5,
+					long number);
+#define __mips16_syscall6(a0, a1, a2, a3, a4, a5, number)		\
+	__mips16_syscall6 ((long) (a0), (long) (a1), (long) (a2),	\
+			   (long) (a3), (long) (a4), (long) (a5),	\
+			   (long) (number))
+
+long long __nomips16 __mips16_syscall7 (long a0, long a1, long a2, long a3,
+					long a4, long a5, long a6,
+					long number);
+#define __mips16_syscall7(a0, a1, a2, a3, a4, a5, a6, number)		\
+	__mips16_syscall7 ((long) (a0), (long) (a1), (long) (a2),	\
+			   (long) (a3), (long) (a4), (long) (a5),	\
+			   (long) (a6),					\
+			   (long) (number))
+
+#endif
diff --git a/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall0.c b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall0.c
new file mode 100644
index 0000000000..0cdf94c22c
--- /dev/null
+++ b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall0.c
@@ -0,0 +1,30 @@
+/* MIPS16 syscall wrappers.
+   Copyright (C) 2013 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+#include <mips16-syscall.h>
+
+#undef __mips16_syscall0
+
+long long __nomips16
+__mips16_syscall0 (long number)
+{
+  union __mips16_syscall_return ret;
+  ret.reg.v0 = INTERNAL_SYSCALL_MIPS16 (number, ret.reg.v1, 0);
+  return ret.val;
+}
diff --git a/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall1.c b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall1.c
new file mode 100644
index 0000000000..5e60a59d56
--- /dev/null
+++ b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall1.c
@@ -0,0 +1,32 @@
+/* MIPS16 syscall wrappers.
+   Copyright (C) 2013 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+#include <mips16-syscall.h>
+
+#undef __mips16_syscall1
+
+long long __nomips16
+__mips16_syscall1 (long a0,
+		   long number)
+{
+  union __mips16_syscall_return ret;
+  ret.reg.v0 = INTERNAL_SYSCALL_MIPS16 (number, ret.reg.v1, 1,
+					a0);
+  return ret.val;
+}
diff --git a/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall2.c b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall2.c
new file mode 100644
index 0000000000..7750c519ce
--- /dev/null
+++ b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall2.c
@@ -0,0 +1,32 @@
+/* MIPS16 syscall wrappers.
+   Copyright (C) 2013 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+#include <mips16-syscall.h>
+
+#undef __mips16_syscall2
+
+long long __nomips16
+__mips16_syscall2 (long a0, long a1,
+		   long number)
+{
+  union __mips16_syscall_return ret;
+  ret.reg.v0 = INTERNAL_SYSCALL_MIPS16 (number, ret.reg.v1, 2,
+					a0, a1);
+  return ret.val;
+}
diff --git a/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall3.c b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall3.c
new file mode 100644
index 0000000000..7fc1c4e435
--- /dev/null
+++ b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall3.c
@@ -0,0 +1,32 @@
+/* MIPS16 syscall wrappers.
+   Copyright (C) 2013 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+#include <mips16-syscall.h>
+
+#undef __mips16_syscall3
+
+long long __nomips16
+__mips16_syscall3 (long a0, long a1, long a2,
+		   long number)
+{
+  union __mips16_syscall_return ret;
+  ret.reg.v0 = INTERNAL_SYSCALL_MIPS16 (number, ret.reg.v1, 3,
+					a0, a1, a2);
+  return ret.val;
+}
diff --git a/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall4.c b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall4.c
new file mode 100644
index 0000000000..b8b4198c78
--- /dev/null
+++ b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall4.c
@@ -0,0 +1,32 @@
+/* MIPS16 syscall wrappers.
+   Copyright (C) 2013 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+#include <mips16-syscall.h>
+
+#undef __mips16_syscall4
+
+long long __nomips16
+__mips16_syscall4 (long a0, long a1, long a2, long a3,
+		   long number)
+{
+  union __mips16_syscall_return ret;
+  ret.reg.v0 = INTERNAL_SYSCALL_MIPS16 (number, ret.reg.v1, 4,
+					a0, a1, a2, a3);
+  return ret.val;
+}
diff --git a/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall5.c b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall5.c
new file mode 100644
index 0000000000..e1322d107f
--- /dev/null
+++ b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall5.c
@@ -0,0 +1,33 @@
+/* MIPS16 syscall wrappers.
+   Copyright (C) 2013 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+#include <mips16-syscall.h>
+
+#undef __mips16_syscall5
+
+long long __nomips16
+__mips16_syscall5 (long a0, long a1, long a2, long a3,
+		   long a4,
+		   long number)
+{
+  union __mips16_syscall_return ret;
+  ret.reg.v0 = INTERNAL_SYSCALL_MIPS16 (number, ret.reg.v1, 5,
+					a0, a1, a2, a3, a4);
+  return ret.val;
+}
diff --git a/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall6.c b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall6.c
new file mode 100644
index 0000000000..a9e5cd9dfe
--- /dev/null
+++ b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall6.c
@@ -0,0 +1,33 @@
+/* MIPS16 syscall wrappers.
+   Copyright (C) 2013 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+#include <mips16-syscall.h>
+
+#undef __mips16_syscall6
+
+long long __nomips16
+__mips16_syscall6 (long a0, long a1, long a2, long a3,
+		   long a4, long a5,
+		   long number)
+{
+  union __mips16_syscall_return ret;
+  ret.reg.v0 = INTERNAL_SYSCALL_MIPS16 (number, ret.reg.v1, 6,
+					a0, a1, a2, a3, a4, a5);
+  return ret.val;
+}
diff --git a/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall7.c b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall7.c
new file mode 100644
index 0000000000..d87b5ba5a8
--- /dev/null
+++ b/ports/sysdeps/unix/sysv/linux/mips/mips32/mips16/mips16-syscall7.c
@@ -0,0 +1,33 @@
+/* MIPS16 syscall wrappers.
+   Copyright (C) 2013 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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+#include <mips16-syscall.h>
+
+#undef __mips16_syscall7
+
+long long __nomips16
+__mips16_syscall7 (long a0, long a1, long a2, long a3,
+		   long a4, long a5, long a6,
+		   long number)
+{
+  union __mips16_syscall_return ret;
+  ret.reg.v0 = INTERNAL_SYSCALL_MIPS16 (number, ret.reg.v1, 7,
+					a0, a1, a2, a3, a4, a5, a6);
+  return ret.val;
+}
diff --git a/ports/sysdeps/unix/sysv/linux/mips/mips32/nptl/libc.abilist b/ports/sysdeps/unix/sysv/linux/mips/mips32/nptl/libc.abilist
index 2c2cd54868..f01278e4a4 100644
--- a/ports/sysdeps/unix/sysv/linux/mips/mips32/nptl/libc.abilist
+++ b/ports/sysdeps/unix/sysv/linux/mips/mips32/nptl/libc.abilist
@@ -1401,6 +1401,8 @@ GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+ __mips_fpu_getcw F
+ __mips_fpu_setcw F
 GLIBC_2.2
  GLIBC_2.2 A
  _Exit F
diff --git a/ports/sysdeps/unix/sysv/linux/mips/mips32/sysdep.h b/ports/sysdeps/unix/sysv/linux/mips/mips32/sysdep.h
index c8bfe5dc8e..0faf68c4fc 100644
--- a/ports/sysdeps/unix/sysv/linux/mips/mips32/sysdep.h
+++ b/ports/sysdeps/unix/sysv/linux/mips/mips32/sysdep.h
@@ -95,17 +95,46 @@
 #endif
 
 #undef INTERNAL_SYSCALL
-#define INTERNAL_SYSCALL(name, err, nr, args...)			\
+#undef INTERNAL_SYSCALL_NCS
+
+#ifdef __mips16
+/* There's no MIPS16 syscall instruction, so we go through out-of-line
+   standard MIPS wrappers.  These do use inline snippets below though,
+   through INTERNAL_SYSCALL_MIPS16.  Spilling the syscall number to
+   memory gives the best code in that case, avoiding the need to save
+   and restore a static register.  */
+
+# include <mips16-syscall.h>
+
+# define INTERNAL_SYSCALL(name, err, nr, args...)			\
+	INTERNAL_SYSCALL_NCS (SYS_ify (name), err, nr, args)
+
+# define INTERNAL_SYSCALL_NCS(number, err, nr, args...)			\
+({									\
+	union __mips16_syscall_return ret;				\
+	ret.val = __mips16_syscall##nr (args, number);			\
+	err = ret.reg.v1;						\
+	ret.reg.v0;							\
+})
+
+# define INTERNAL_SYSCALL_MIPS16(number, err, nr, args...)		\
+	internal_syscall##nr ("lw\t%0, %2\n\t",				\
+			      "R" (number),				\
+			      0, err, args)
+
+#else /* !__mips16 */
+# define INTERNAL_SYSCALL(name, err, nr, args...)			\
 	internal_syscall##nr ("li\t%0, %2\t\t\t# " #name "\n\t",	\
 			      "IK" (SYS_ify (name)),			\
 			      0, err, args)
 
-#undef INTERNAL_SYSCALL_NCS
-#define INTERNAL_SYSCALL_NCS(number, err, nr, args...)			\
+# define INTERNAL_SYSCALL_NCS(number, err, nr, args...)			\
 	internal_syscall##nr (MOVE32 "\t%0, %2\n\t",			\
 			      "r" (__s0),				\
 			      number, err, args)
 
+#endif /* !__mips16 */
+
 #define internal_syscall0(v0_init, input, number, err, dummy...)	\
 ({									\
 	long _sys_result;						\
diff --git a/ports/sysdeps/unix/sysv/linux/mips/nptl/sysdep-cancel.h b/ports/sysdeps/unix/sysv/linux/mips/nptl/sysdep-cancel.h
index 1e0bfedecf..3b0eccc77f 100644
--- a/ports/sysdeps/unix/sysv/linux/mips/nptl/sysdep-cancel.h
+++ b/ports/sysdeps/unix/sysv/linux/mips/nptl/sysdep-cancel.h
@@ -39,6 +39,7 @@
 # undef PSEUDO
 # define PSEUDO(name, syscall_name, args)				      \
       .align 2;								      \
+      .set nomips16;							      \
   L(pseudo_start):							      \
       cfi_startproc;							      \
   99: PSEUDO_ERRJMP							      \
diff --git a/ports/sysdeps/unix/sysv/linux/mips/setcontext.S b/ports/sysdeps/unix/sysv/linux/mips/setcontext.S
index 6ed355c97f..2d5aee013b 100644
--- a/ports/sysdeps/unix/sysv/linux/mips/setcontext.S
+++ b/ports/sysdeps/unix/sysv/linux/mips/setcontext.S
@@ -27,6 +27,7 @@
 /* int setcontext (const ucontext_t *ucp) */
 
 	.text
+	.set	nomips16
 LOCALSZ = 0
 ARGSZ = 0
 MASK = 0x00000000
diff --git a/ports/sysdeps/unix/sysv/linux/mips/swapcontext.S b/ports/sysdeps/unix/sysv/linux/mips/swapcontext.S
index ddd3b28fde..ec271b0584 100644
--- a/ports/sysdeps/unix/sysv/linux/mips/swapcontext.S
+++ b/ports/sysdeps/unix/sysv/linux/mips/swapcontext.S
@@ -27,6 +27,7 @@
 /* int swapcontext (ucontext_t *oucp, const ucontext_t *ucp) */
 
 	.text
+	.set	nomips16
 LOCALSZ = 0
 ARGSZ = 0
 MASK = 0x00000000
diff --git a/ports/sysdeps/unix/sysv/linux/mips/vfork.S b/ports/sysdeps/unix/sysv/linux/mips/vfork.S
index d627923528..ae76a91d38 100644
--- a/ports/sysdeps/unix/sysv/linux/mips/vfork.S
+++ b/ports/sysdeps/unix/sysv/linux/mips/vfork.S
@@ -34,6 +34,7 @@
 /* int vfork() */
 
 	.text
+	.set		nomips16
 LOCALSZ= 1
 FRAMESZ= (((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK
 GPOFF= FRAMESZ-(1*SZREG)