Merge "Change bool, and string properties to *bool, and *string for cc"
diff --git a/benchmarks/tests/interface_test.cpp b/benchmarks/tests/interface_test.cpp
index b8279b6..6987320 100644
--- a/benchmarks/tests/interface_test.cpp
+++ b/benchmarks/tests/interface_test.cpp
@@ -71,7 +71,8 @@
   char path[PATH_MAX];
   ssize_t path_len = readlink("/proc/self/exe", path, sizeof(path));
   ASSERT_TRUE(path_len >= 0);
-  *exe_name = std::string(std::regex_replace(path, std::regex("-tests"), ""));
+  *exe_name = std::string(std::regex_replace(path, std::regex("nativetest"), "benchmarktest"));
+  *exe_name = std::regex_replace(*exe_name, std::regex("-tests"), "");
 }
 
 void SystemTests::Exec(std::vector<const char*> args) {
@@ -289,6 +290,10 @@
     "BM_stdio_fopen_fgetc_fclose_no_locking/1024/iterations:1\n"
     "BM_stdio_fopen_getline_fclose_locking/iterations:1\n"
     "BM_stdio_fopen_getline_fclose_no_locking/iterations:1\n"
+    "BM_stdio_printf_literal/iterations:1\n"
+    "BM_stdio_printf_s/iterations:1\n"
+    "BM_stdio_printf_d/iterations:1\n"
+    "BM_stdio_printf_1$s/iterations:1\n"
     "BM_string_memcmp/8/0/0/iterations:1\n"
     "BM_string_memcmp/64/0/0/iterations:1\n"
     "BM_string_memcmp/512/0/0/iterations:1\n"
diff --git a/libc/NOTICE b/libc/NOTICE
index cbcafc8..db4b5a5 100644
--- a/libc/NOTICE
+++ b/libc/NOTICE
@@ -5643,6 +5643,34 @@
 
 -------------------------------------------------------------------
 
+Copyright (c) 2017 ARM Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. The name of the company may not be used to endorse or promote
+   products derived from this software without specific prior written
+   permission.
+
+THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------
+
 Copyright (c) 2017 Imagination Technologies.
 
 All rights reserved.
diff --git a/libc/arch-arm64/generic/bionic/memcmp.S b/libc/arch-arm64/generic/bionic/memcmp.S
index 3d08ecd..3a138bf 100644
--- a/libc/arch-arm64/generic/bionic/memcmp.S
+++ b/libc/arch-arm64/generic/bionic/memcmp.S
@@ -1,33 +1,34 @@
-/* Copyright (c) 2014, Linaro Limited
-   All rights reserved.
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions are met:
-       * Redistributions of source code must retain the above copyright
-         notice, this list of conditions and the following disclaimer.
-       * Redistributions in binary form must reproduce the above copyright
-         notice, this list of conditions and the following disclaimer in the
-         documentation and/or other materials provided with the distribution.
-       * Neither the name of the Linaro nor the
-         names of its contributors may be used to endorse or promote products
-         derived from this software without specific prior written permission.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-   HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
+/*
+ * Copyright (c) 2017 ARM Ltd
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the company may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
 
 /* Assumptions:
  *
- * ARMv8-a, AArch64
+ * ARMv8-a, AArch64, unaligned accesses.
  */
 
 #include <private/bionic_asm.h>
@@ -36,120 +37,93 @@
 #define src1		x0
 #define src2		x1
 #define limit		x2
-#define result		x0
+#define result		w0
 
 /* Internal variables.  */
 #define data1		x3
 #define data1w		w3
 #define data2		x4
 #define data2w		w4
-#define has_nul		x5
-#define diff		x6
-#define endloop		x7
-#define tmp1		x8
-#define tmp2		x9
-#define tmp3		x10
-#define pos		x11
-#define limit_wd	x12
-#define mask		x13
+#define tmp1		x5
 
+/* Small inputs of less than 8 bytes are handled separately.  This allows the
+   main code to be sped up using unaligned loads since there are now at least
+   8 bytes to be compared.  If the first 8 bytes are equal, align src1.
+   This ensures each iteration does at most one unaligned access even if both
+   src1 and src2 are unaligned, and mutually aligned inputs behave as if
+   aligned.  After the main loop, process the last 8 bytes using unaligned
+   accesses.  */
+
+.p2align 6
 ENTRY(memcmp)
-	cbz	limit, .Lret0
-	eor	tmp1, src1, src2
-	tst	tmp1, #7
-	b.ne	.Lmisaligned8
-	ands	tmp1, src1, #7
-	b.ne	.Lmutual_align
-	add	limit_wd, limit, #7
-	lsr	limit_wd, limit_wd, #3
-	/* Start of performance-critical section  -- one 64B cache line.  */
-.Lloop_aligned:
-	ldr	data1, [src1], #8
-	ldr	data2, [src2], #8
-.Lstart_realigned:
-	subs	limit_wd, limit_wd, #1
-	eor	diff, data1, data2	/* Non-zero if differences found.  */
-	csinv	endloop, diff, xzr, ne	/* Last Dword or differences.  */
-	cbz	endloop, .Lloop_aligned
-	/* End of performance-critical section  -- one 64B cache line.  */
+	subs	limit, limit, 8
+	b.lo	.Lless8
 
-	/* Not reached the limit, must have found a diff.  */
-	cbnz	limit_wd, .Lnot_limit
+	/* Limit >= 8, so check first 8 bytes using unaligned loads.  */
+	ldr	data1, [src1], 8
+	ldr	data2, [src2], 8
+	and	tmp1, src1, 7
+	add	limit, limit, tmp1
+	cmp	data1, data2
+	bne	.Lreturn
 
-	/* Limit % 8 == 0 => all bytes significant.  */
-	ands	limit, limit, #7
-	b.eq	.Lnot_limit
+	/* Align src1 and adjust src2 with bytes not yet done.  */
+	sub	src1, src1, tmp1
+	sub	src2, src2, tmp1
 
-	lsl	limit, limit, #3	/* Bits -> bytes.  */
-	mov	mask, #~0
-#ifdef __AARCH64EB__
-	lsr	mask, mask, limit
-#else
-	lsl	mask, mask, limit
-#endif
-	bic	data1, data1, mask
-	bic	data2, data2, mask
+	subs	limit, limit, 8
+	b.ls	.Llast_bytes
 
-	orr	diff, diff, mask
-.Lnot_limit:
+	/* Loop performing 8 bytes per iteration using aligned src1.
+	   Limit is pre-decremented by 8 and must be larger than zero.
+	   Exit if <= 8 bytes left to do or if the data is not equal.  */
+	.p2align 4
+.Lloop8:
+	ldr	data1, [src1], 8
+	ldr	data2, [src2], 8
+	subs	limit, limit, 8
+	ccmp	data1, data2, 0, hi  /* NZCV = 0b0000.  */
+	b.eq	.Lloop8
 
-#ifndef	__AARCH64EB__
-	rev	diff, diff
+	cmp	data1, data2
+	bne	.Lreturn
+
+	/* Compare last 1-8 bytes using unaligned access.  */
+.Llast_bytes:
+	ldr	data1, [src1, limit]
+	ldr	data2, [src2, limit]
+
+	/* Compare data bytes and set return value to 0, -1 or 1.  */
+.Lreturn:
+#ifndef __AARCH64EB__
 	rev	data1, data1
 	rev	data2, data2
 #endif
-	/* The MS-non-zero bit of DIFF marks either the first bit
-	   that is different, or the end of the significant data.
-	   Shifting left now will bring the critical information into the
-	   top bits.  */
-	clz	pos, diff
-	lsl	data1, data1, pos
-	lsl	data2, data2, pos
-	/* But we need to zero-extend (char is unsigned) the value and then
-	   perform a signed 32-bit subtraction.  */
-	lsr	data1, data1, #56
-	sub	result, data1, data2, lsr #56
-	ret
+	cmp     data1, data2
+.Lret_eq:
+	cset	result, ne
+	cneg	result, result, lo
+        ret
 
-.Lmutual_align:
-	/* Sources are mutually aligned, but are not currently at an
-	   alignment boundary.  Round down the addresses and then mask off
-	   the bytes that precede the start point.  */
-	bic	src1, src1, #7
-	bic	src2, src2, #7
-	add	limit, limit, tmp1	/* Adjust the limit for the extra.  */
-	lsl	tmp1, tmp1, #3		/* Bytes beyond alignment -> bits.  */
-	ldr	data1, [src1], #8
-	neg	tmp1, tmp1		/* Bits to alignment -64.  */
-	ldr	data2, [src2], #8
-	mov	tmp2, #~0
-#ifdef __AARCH64EB__
-	/* Big-endian.  Early bytes are at MSB.  */
-	lsl	tmp2, tmp2, tmp1	/* Shift (tmp1 & 63).  */
-#else
-	/* Little-endian.  Early bytes are at LSB.  */
-	lsr	tmp2, tmp2, tmp1	/* Shift (tmp1 & 63).  */
-#endif
-	add	limit_wd, limit, #7
-	orr	data1, data1, tmp2
-	orr	data2, data2, tmp2
-	lsr	limit_wd, limit_wd, #3
-	b	.Lstart_realigned
-
-.Lret0:
-	mov	result, #0
-	ret
-
-	.p2align 6
-.Lmisaligned8:
-	sub	limit, limit, #1
-1:
-	/* Perhaps we can do better than this.  */
-	ldrb	data1w, [src1], #1
-	ldrb	data2w, [src2], #1
-	subs	limit, limit, #1
-	ccmp	data1w, data2w, #0, cs	/* NZCV = 0b0000.  */
-	b.eq	1b
-	sub	result, data1, data2
+	.p2align 4
+	/* Compare up to 8 bytes.  Limit is [-8..-1].  */
+.Lless8:
+	adds	limit, limit, 4
+	b.lo	.Lless4
+	ldr	data1w, [src1], 4
+	ldr	data2w, [src2], 4
+	cmp	data1w, data2w
+	b.ne	.Lreturn
+	sub	limit, limit, 4
+.Lless4:
+	adds	limit, limit, 4
+	beq	.Lret_eq
+.Lbyte_loop:
+	ldrb	data1w, [src1], 1
+	ldrb	data2w, [src2], 1
+	subs	limit, limit, 1
+	ccmp	data1w, data2w, 0, ne	/* NZCV = 0b0000.  */
+	b.eq	.Lbyte_loop
+	sub	result, data1w, data2w
 	ret
 END(memcmp)
diff --git a/libc/include/android/legacy_fenv_inlines_arm.h b/libc/include/android/legacy_fenv_inlines_arm.h
index 58c49c2..de024cf 100644
--- a/libc/include/android/legacy_fenv_inlines_arm.h
+++ b/libc/include/android/legacy_fenv_inlines_arm.h
@@ -35,9 +35,6 @@
 
 __BEGIN_DECLS
 
-#define FPSCR_ENABLE_SHIFT 8
-#define FPSCR_ENABLE_MASK  (FE_ALL_EXCEPT << FPSCR_ENABLE_SHIFT)
-
 #define FPSCR_RMODE_SHIFT 22
 
 static __inline int fegetenv(fenv_t* __envp) {
@@ -108,7 +105,7 @@
   fenv_t __env;
   fegetenv(&__env);
   *__envp = __env;
-  __env &= ~(FE_ALL_EXCEPT | FPSCR_ENABLE_MASK);
+  __env &= ~FE_ALL_EXCEPT;
   fesetenv(&__env);
   return 0;
 }
@@ -121,30 +118,18 @@
   return 0;
 }
 
-static __inline int feenableexcept(int __mask) {
-  fenv_t __old_fpscr, __new_fpscr;
-  fegetenv(&__old_fpscr);
-  __new_fpscr = __old_fpscr | (__mask & FE_ALL_EXCEPT) << FPSCR_ENABLE_SHIFT;
-  fesetenv(&__new_fpscr);
-  return ((__old_fpscr >> FPSCR_ENABLE_SHIFT) & FE_ALL_EXCEPT);
+static __inline int feenableexcept(int __mask __unused) {
+  return -1;
 }
 
-static __inline int fedisableexcept(int __mask) {
-  fenv_t __old_fpscr, __new_fpscr;
-  fegetenv(&__old_fpscr);
-  __new_fpscr = __old_fpscr & ~((__mask & FE_ALL_EXCEPT) << FPSCR_ENABLE_SHIFT);
-  fesetenv(&__new_fpscr);
-  return ((__old_fpscr >> FPSCR_ENABLE_SHIFT) & FE_ALL_EXCEPT);
+static __inline int fedisableexcept(int __mask __unused) {
+  return 0;
 }
 
 static __inline int fegetexcept(void) {
-  fenv_t __fpscr;
-  fegetenv(&__fpscr);
-  return ((__fpscr & FPSCR_ENABLE_MASK) >> FPSCR_ENABLE_SHIFT);
+  return 0;
 }
 
-#undef FPSCR_ENABLE_SHIFT
-#undef FPSCR_ENABLE_MASK
 #undef FPSCR_RMODE_SHIFT
 
 __END_DECLS
diff --git a/libc/include/bits/fenv_arm.h b/libc/include/bits/fenv_arm.h
index 542ddbe..042fec3 100644
--- a/libc/include/bits/fenv_arm.h
+++ b/libc/include/bits/fenv_arm.h
@@ -26,13 +26,6 @@
  * $FreeBSD: src/lib/msun/arm/fenv.h,v 1.5 2005/03/16 19:03:45 das Exp $
  */
 
-/*
- * Rewritten for Android.
- *
- * The ARM FPSCR is described here:
- * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344b/Chdfafia.html
- */
-
 #ifndef _BITS_FENV_ARM_H_
 #define _BITS_FENV_ARM_H_
 
@@ -40,7 +33,28 @@
 
 __BEGIN_DECLS
 
+/*
+ * The ARM Cortex-A75 registers are described here:
+ *
+ * AArch64:
+ *  FPCR: http://infocenter.arm.com/help/topic/com.arm.doc.100403_0200_00_en/lau1442502503726.html
+ *  FPSR: http://infocenter.arm.com/help/topic/com.arm.doc.100403_0200_00_en/lau1442502526288.html
+ * AArch32:
+ *  FPSCR: http://infocenter.arm.com/help/topic/com.arm.doc.100403_0200_00_en/lau1442504290459.html
+ */
+
+#if defined(__LP64__)
+typedef struct {
+  /* FPCR, Floating-point Control Register. */
+  __uint32_t __control;
+  /* FPSR, Floating-point Status Register. */
+  __uint32_t __status;
+} fenv_t;
+
+#else
 typedef __uint32_t fenv_t;
+#endif
+
 typedef __uint32_t fexcept_t;
 
 /* Exception flags. */
@@ -49,8 +63,8 @@
 #define FE_OVERFLOW   0x04
 #define FE_UNDERFLOW  0x08
 #define FE_INEXACT    0x10
-#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \
-                       FE_OVERFLOW | FE_UNDERFLOW)
+#define FE_DENORMAL   0x80
+#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW | FE_DENORMAL)
 
 /* Rounding modes. */
 #define FE_TONEAREST  0x0
diff --git a/libc/include/bits/fenv_arm64.h b/libc/include/bits/fenv_arm64.h
deleted file mode 100644
index 67f0fb4..0000000
--- a/libc/include/bits/fenv_arm64.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*-
- * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $FreeBSD: src/lib/msun/arm/fenv.h,v 1.5 2005/03/16 19:03:45 das Exp $
- */
-
-/*
- * In ARMv8, AArch64 state, floating-point operation is controlled by:
- *
- *  * FPCR - 32Bit Floating-Point Control Register:
- *      * [31:27] - Reserved, Res0;
- *      * [26]    - AHP, Alternative half-precision control bit;
- *      * [25]    - DN, Default NaN mode control bit;
- *      * [24]    - FZ, Flush-to-zero mode control bit;
- *      * [23:22] - RMode, Rounding Mode control field:
- *            * 00  - Round to Nearest (RN) mode;
- *            * 01  - Round towards Plus Infinity (RP) mode;
- *            * 10  - Round towards Minus Infinity (RM) mode;
- *            * 11  - Round towards Zero (RZ) mode.
- *      * [21:20] - Stride, ignored during AArch64 execution;
- *      * [19]    - Reserved, Res0;
- *      * [18:16] - Len, ignored during AArch64 execution;
- *      * [15]    - IDE, Input Denormal exception trap;
- *      * [14:13] - Reserved, Res0;
- *      * [12]    - IXE, Inexact exception trap;
- *      * [11]    - UFE, Underflow exception trap;
- *      * [10]    - OFE, Overflow exception trap;
- *      * [9]     - DZE, Division by Zero exception;
- *      * [8]     - IOE, Invalid Operation exception;
- *      * [7:0]   - Reserved, Res0.
- *
- *  * FPSR - 32Bit Floating-Point Status Register:
- *      * [31]    - N, Negative condition flag for AArch32 (AArch64 sets PSTATE.N);
- *      * [30]    - Z, Zero condition flag for AArch32 (AArch64 sets PSTATE.Z);
- *      * [29]    - C, Carry conditon flag for AArch32 (AArch64 sets PSTATE.C);
- *      * [28]    - V, Overflow conditon flag for AArch32 (AArch64 sets PSTATE.V);
- *      * [27]    - QC, Cumulative saturation bit, Advanced SIMD only;
- *      * [26:8]  - Reserved, Res0;
- *      * [7]     - IDC, Input Denormal cumulative exception;
- *      * [6:5]   - Reserved, Res0;
- *      * [4]     - IXC, Inexact cumulative exception;
- *      * [3]     - UFC, Underflow cumulative exception;
- *      * [2]     - OFC, Overflow cumulative exception;
- *      * [1]     - DZC, Division by Zero cumulative exception;
- *      * [0]     - IOC, Invalid Operation cumulative exception.
- */
-
-#ifndef _BITS_FENV_ARM64_H_
-#define _BITS_FENV_ARM64_H_
-
-#include <sys/types.h>
-
-__BEGIN_DECLS
-
-typedef struct {
-  __uint32_t __control;     /* FPCR, Floating-point Control Register */
-  __uint32_t __status;      /* FPSR, Floating-point Status Register */
-} fenv_t;
-
-typedef __uint32_t fexcept_t;
-
-/* Exception flags. */
-#define FE_INVALID    0x01
-#define FE_DIVBYZERO  0x02
-#define FE_OVERFLOW   0x04
-#define FE_UNDERFLOW  0x08
-#define FE_INEXACT    0x10
-#define FE_DENORMAL   0x80
-#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \
-                       FE_OVERFLOW | FE_UNDERFLOW | FE_DENORMAL)
-
-/* Rounding modes. */
-#define FE_TONEAREST  0x0
-#define FE_UPWARD     0x1
-#define FE_DOWNWARD   0x2
-#define FE_TOWARDZERO 0x3
-
-__END_DECLS
-
-#endif
diff --git a/libc/include/fenv.h b/libc/include/fenv.h
index 4276e87..2b607f5 100644
--- a/libc/include/fenv.h
+++ b/libc/include/fenv.h
@@ -32,9 +32,7 @@
 
 #include <sys/cdefs.h>
 
-#if defined(__aarch64__)
-#include <bits/fenv_arm64.h>
-#elif defined(__arm__)
+#if defined(__aarch64__) || defined(__arm__)
 #include <bits/fenv_arm.h>
 #elif defined(__i386__)
 #include <bits/fenv_x86.h>
diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp
index 46d717a..b4b2a36 100644
--- a/libc/stdio/stdio.cpp
+++ b/libc/stdio/stdio.cpp
@@ -581,7 +581,7 @@
 
 int fseeko64(FILE* fp, off64_t offset, int whence) {
   CHECK_FP(fp);
-  return __fseeko64(fp, offset, whence, 8*sizeof(off_t));
+  return __fseeko64(fp, offset, whence, 8*sizeof(off64_t));
 }
 
 int fsetpos(FILE* fp, const fpos_t* pos) {
diff --git a/libc/stdio/vfprintf.cpp b/libc/stdio/vfprintf.cpp
index 366b196..fe2e338 100644
--- a/libc/stdio/vfprintf.cpp
+++ b/libc/stdio/vfprintf.cpp
@@ -32,6 +32,7 @@
  */
 
 #define CHAR_TYPE char
+#define FUNCTION_NAME __vfprintf
 
 #include <sys/mman.h>
 #include <sys/types.h>
@@ -108,8 +109,7 @@
  * temporary buffer.  We only work on write-only files; this avoids
  * worries about ungetc buffers and so forth.
  */
-static int __sbprintf(FILE* fp, const char* fmt, va_list ap) {
-  int ret;
+static int __sbprintf(FILE* fp, const CHAR_TYPE* fmt, va_list ap) {
   FILE fake;
   struct __sfileext fakeext;
   unsigned char buf[BUFSIZ];
@@ -127,7 +127,7 @@
   fake._lbfsize = 0; /* not actually used, but Just In Case */
 
   /* do the work, then copy any error status */
-  ret = __vfprintf(&fake, fmt, ap);
+  int ret = FUNCTION_NAME(&fake, fmt, ap);
   if (ret >= 0 && __sflush(&fake)) ret = EOF;
   if (fake._flags & __SERR) fp->_flags |= __SERR;
   return (ret);
@@ -250,17 +250,16 @@
 #define CHARINT 0x0800  /* 8 bit integer */
 #define MAXINT 0x1000   /* largest integer size (intmax_t) */
 
-int __vfprintf(FILE* fp, const char* fmt0, __va_list ap) {
+int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, __va_list ap) {
   int ch;              /* character from fmt */
   int n, n2;           /* handy integers (short term usage) */
-  char* cp;            /* handy char pointer (short term usage) */
+  CHAR_TYPE* cp;            /* handy char pointer (short term usage) */
   struct __siov* iovp; /* for PRINT macro */
+  CHAR_TYPE sign;           /* sign prefix (' ', '+', '-', or \0) */
   int flags;           /* flags as above */
   int ret;             /* return value accumulator */
   int width;           /* width from format (%8d), or 0 */
   int prec;            /* precision from format; <0 for N/A */
-  char sign;           /* sign prefix (' ', '+', '-', or \0) */
-  mbstate_t ps;
   /*
    * We can decompose the printed representation of floating
    * point numbers into several parts, some of which may be empty:
@@ -287,7 +286,7 @@
   int expsize;                /* character count for expstr */
   int lead;                   /* sig figs before decimal or group sep */
   int ndig;                   /* actual number of digits returned by dtoa */
-  char expstr[MAXEXPDIG + 2]; /* buffer for exponent string: e+ZZZ */
+  CHAR_TYPE expstr[MAXEXPDIG + 2]; /* buffer for exponent string: e+ZZZ */
   char* dtoaresult = NULL;
 
   uintmax_t _umax;             /* integer arguments %[diouxX] */
@@ -299,14 +298,14 @@
 #define NIOV 8
   struct __suio uio;       /* output information: summary */
   struct __siov iov[NIOV]; /* ... and individual io vectors */
-  char buf[BUF];           /* buffer with space for digits of uintmax_t */
-  char ox[2];              /* space for 0x; ox[1] is either x, X, or \0 */
+  CHAR_TYPE buf[BUF];           /* buffer with space for digits of uintmax_t */
+  CHAR_TYPE ox[2];              /* space for 0x; ox[1] is either x, X, or \0 */
   union arg* argtable;     /* args, built due to positional arg */
   union arg statargtable[STATIC_ARG_TBL_SIZE];
   size_t argtablesiz;
   int nextarg;   /* 1-based argument index */
   va_list orgap; /* original argument pointer */
-  char* convbuf; /* buffer for wide to multi-byte conversion */
+  CHAR_TYPE* convbuf; /* buffer for wide/multibyte conversion */
 
   /*
    * Choose PADSIZE to trade efficiency vs. size.  If larger printf
@@ -466,7 +465,6 @@
   ret = 0;
   convbuf = NULL;
 
-  memset(&ps, 0, sizeof(ps));
   /*
    * Scan the format for conversions (`%' character).
    */
@@ -1275,10 +1273,7 @@
         flags |= LONGINT;
         /*FALLTHROUGH*/
       case 's':
-        if (flags & LONGINT)
-          ADDTYPE(TP_WCHAR);
-        else
-          ADDTYPE(TP_CHAR);
+        ADDTYPE((flags & LONGINT) ? TP_WCHAR : TP_CHAR);
         break;
       case 'U':
         flags |= LONGINT;
diff --git a/libc/stdio/vfwprintf.cpp b/libc/stdio/vfwprintf.cpp
index 7235969..e8f1056 100644
--- a/libc/stdio/vfwprintf.cpp
+++ b/libc/stdio/vfwprintf.cpp
@@ -32,6 +32,7 @@
  */
 
 #define CHAR_TYPE wchar_t
+#define FUNCTION_NAME __vfwprintf
 
 #include <sys/mman.h>
 #include <sys/types.h>
@@ -91,8 +92,7 @@
  * temporary buffer.  We only work on write-only files; this avoids
  * worries about ungetc buffers and so forth.
  */
-static int __sbprintf(FILE* fp, const wchar_t* fmt, va_list ap) {
-  int ret;
+static int __sbprintf(FILE* fp, const CHAR_TYPE* fmt, va_list ap) {
   FILE fake;
   struct __sfileext fakeext;
   unsigned char buf[BUFSIZ];
@@ -110,7 +110,7 @@
   fake._lbfsize = 0; /* not actually used, but Just In Case */
 
   /* do the work, then copy any error status */
-  ret = __vfwprintf(&fake, fmt, ap);
+  int ret = FUNCTION_NAME(&fake, fmt, ap);
   if (ret >= 0 && __sflush(&fake)) ret = EOF;
   if (fake._flags & __SERR) fp->_flags |= __SERR;
   return (ret);
@@ -276,15 +276,15 @@
 #define CHARINT 0x0800  /* 8 bit integer */
 #define MAXINT 0x1000   /* largest integer size (intmax_t) */
 
-int __vfwprintf(FILE* __restrict fp, const wchar_t* __restrict fmt0, __va_list ap) {
+int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, __va_list ap) {
   wchar_t ch;    /* character from fmt */
   int n, n2, n3; /* handy integers (short term usage) */
-  wchar_t* cp;   /* handy char pointer (short term usage) */
+  CHAR_TYPE* cp;   /* handy char pointer (short term usage) */
+  CHAR_TYPE sign;  /* sign prefix (' ', '+', '-', or \0) */
   int flags;     /* flags as above */
   int ret;       /* return value accumulator */
   int width;     /* width from format (%8d), or 0 */
   int prec;      /* precision from format; <0 for N/A */
-  wchar_t sign;  /* sign prefix (' ', '+', '-', or \0) */
   /*
    * We can decompose the printed representation of floating
    * point numbers into several parts, some of which may be empty:
@@ -311,7 +311,7 @@
   int expsize;                   /* character count for expstr */
   int lead;                      /* sig figs before decimal or group sep */
   int ndig;                      /* actual number of digits returned by dtoa */
-  wchar_t expstr[MAXEXPDIG + 2]; /* buffer for exponent string: e+ZZZ */
+  CHAR_TYPE expstr[MAXEXPDIG + 2]; /* buffer for exponent string: e+ZZZ */
   char* dtoaresult = NULL;
 
   uintmax_t _umax;             /* integer arguments %[diouxX] */
@@ -320,14 +320,14 @@
   int realsz;                  /* field size expanded by dprec */
   int size;                    /* size of converted field or string */
   const char* xdigs;           /* digits for %[xX] conversion */
-  wchar_t buf[BUF];            /* buffer with space for digits of uintmax_t */
-  wchar_t ox[2];               /* space for 0x; ox[1] is either x, X, or \0 */
+  CHAR_TYPE buf[BUF];            /* buffer with space for digits of uintmax_t */
+  CHAR_TYPE ox[2];               /* space for 0x; ox[1] is either x, X, or \0 */
   union arg* argtable;         /* args, built due to positional arg */
   union arg statargtable[STATIC_ARG_TBL_SIZE];
   size_t argtablesiz;
   int nextarg;      /* 1-based argument index */
   va_list orgap;    /* original argument pointer */
-  wchar_t* convbuf; /* buffer for multibyte to wide conversion */
+  CHAR_TYPE* convbuf; /* buffer for wide/multibyte conversion */
 
   /*
    * Choose PADSIZE to trade efficiency vs. size.  If larger printf
@@ -1271,10 +1271,7 @@
         flags |= LONGINT;
         /*FALLTHROUGH*/
       case 's':
-        if (flags & LONGINT)
-          ADDTYPE(TP_CHAR);
-        else
-          ADDTYPE(TP_WCHAR);
+        ADDTYPE((flags & LONGINT) ? TP_WCHAR : TP_CHAR);
         break;
       case 'U':
         flags |= LONGINT;
diff --git a/libm/arm/fenv.c b/libm/arm/fenv.c
index 2124730..c988e4f 100644
--- a/libm/arm/fenv.c
+++ b/libm/arm/fenv.c
@@ -28,9 +28,6 @@
 
 #include <fenv.h>
 
-#define FPSCR_ENABLE_SHIFT 8
-#define FPSCR_ENABLE_MASK  (FE_ALL_EXCEPT << FPSCR_ENABLE_SHIFT)
-
 #define FPSCR_RMODE_SHIFT 22
 
 const fenv_t __fe_dfl_env = 0;
@@ -103,7 +100,7 @@
   fenv_t __env;
   fegetenv(&__env);
   *__envp = __env;
-  __env &= ~(FE_ALL_EXCEPT | FPSCR_ENABLE_MASK);
+  __env &= ~FE_ALL_EXCEPT;
   fesetenv(&__env);
   return 0;
 }
@@ -116,24 +113,14 @@
   return 0;
 }
 
-int feenableexcept(int __mask) {
-  fenv_t __old_fpscr, __new_fpscr;
-  fegetenv(&__old_fpscr);
-  __new_fpscr = __old_fpscr | (__mask & FE_ALL_EXCEPT) << FPSCR_ENABLE_SHIFT;
-  fesetenv(&__new_fpscr);
-  return ((__old_fpscr >> FPSCR_ENABLE_SHIFT) & FE_ALL_EXCEPT);
+int feenableexcept(int __mask __unused) {
+  return -1;
 }
 
-int fedisableexcept(int __mask) {
-  fenv_t __old_fpscr, __new_fpscr;
-  fegetenv(&__old_fpscr);
-  __new_fpscr = __old_fpscr & ~((__mask & FE_ALL_EXCEPT) << FPSCR_ENABLE_SHIFT);
-  fesetenv(&__new_fpscr);
-  return ((__old_fpscr >> FPSCR_ENABLE_SHIFT) & FE_ALL_EXCEPT);
+int fedisableexcept(int __mask __unused) {
+  return 0;
 }
 
 int fegetexcept(void) {
-  fenv_t __fpscr;
-  fegetenv(&__fpscr);
-  return ((__fpscr & FPSCR_ENABLE_MASK) >> FPSCR_ENABLE_SHIFT);
+  return 0;
 }
diff --git a/libm/arm64/fenv.c b/libm/arm64/fenv.c
index 19a2393..a99288b 100644
--- a/libm/arm64/fenv.c
+++ b/libm/arm64/fenv.c
@@ -29,9 +29,6 @@
 #include <stdint.h>
 #include <fenv.h>
 
-#define FPCR_EXCEPT_SHIFT 8
-#define FPCR_EXCEPT_MASK  (FE_ALL_EXCEPT << FPCR_EXCEPT_SHIFT)
-
 #define FPCR_RMODE_SHIFT 22
 
 const fenv_t __fe_dfl_env = { 0 /* control */, 0 /* status */};
@@ -137,22 +134,13 @@
 }
 
 int feholdexcept(fenv_t* envp) {
-  fenv_t env;
   fpu_status_t fpsr;
-  fpu_control_t fpcr, new_fpcr;
-
   __get_fpsr(fpsr);
+  fpu_control_t fpcr;
   __get_fpcr(fpcr);
-  env.__status = fpsr;
-  env.__control = fpcr;
+  fenv_t env = { .__status = fpsr, .__control = fpcr };
   *envp = env;
 
-  // Set exceptions to untrapped.
-  new_fpcr = fpcr & ~(FE_ALL_EXCEPT << FPCR_EXCEPT_SHIFT);
-  if (new_fpcr != fpcr) {
-    __set_fpcr(new_fpcr);
-  }
-
   // Clear all exceptions.
   fpsr &= ~FE_ALL_EXCEPT;
   __set_fpsr(fpsr);
@@ -176,31 +164,14 @@
   return 0;
 }
 
-int feenableexcept(int mask) {
-  fpu_control_t old_fpcr, new_fpcr;
-
-  __get_fpcr(old_fpcr);
-  new_fpcr = old_fpcr | ((mask & FE_ALL_EXCEPT) << FPCR_EXCEPT_SHIFT);
-  if (new_fpcr != old_fpcr) {
-    __set_fpcr(new_fpcr);
-  }
-  return ((old_fpcr >> FPCR_EXCEPT_SHIFT) & FE_ALL_EXCEPT);
+int feenableexcept(int mask __unused) {
+  return -1;
 }
 
-int fedisableexcept(int mask) {
-  fpu_control_t old_fpcr, new_fpcr;
-
-  __get_fpcr(old_fpcr);
-  new_fpcr = old_fpcr & ~((mask & FE_ALL_EXCEPT) << FPCR_EXCEPT_SHIFT);
-  if (new_fpcr != old_fpcr) {
-    __set_fpcr(new_fpcr);
-  }
-  return ((old_fpcr >> FPCR_EXCEPT_SHIFT) & FE_ALL_EXCEPT);
+int fedisableexcept(int mask __unused) {
+  return 0;
 }
 
 int fegetexcept(void) {
-  fpu_control_t fpcr;
-
-  __get_fpcr(fpcr);
-  return ((fpcr & FPCR_EXCEPT_MASK) >> FPCR_EXCEPT_SHIFT);
+  return 0;
 }
diff --git a/tests/fenv_test.cpp b/tests/fenv_test.cpp
index c914692..9fc7d96 100644
--- a/tests/fenv_test.cpp
+++ b/tests/fenv_test.cpp
@@ -182,12 +182,20 @@
 
 TEST(fenv, feenableexcept_fegetexcept) {
 #if defined(__aarch64__) || defined(__arm__)
-  // Unsupported.
-  // arm:
-  // http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100403_0200_00_en/lau1442504290459.html
-  // aarch64:
-  // http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488h/way1382990760439.html
-  GTEST_LOG_(INFO) << "arm and arm64 don't support feenableexcept";
+  // ARM doesn't support this. They used to if you go back far enough, but it was removed in
+  // the Cortex-A8 between r3p1 and r3p2.
+  ASSERT_EQ(-1, feenableexcept(FE_INVALID));
+  ASSERT_EQ(0, fegetexcept());
+  ASSERT_EQ(-1, feenableexcept(FE_DIVBYZERO));
+  ASSERT_EQ(0, fegetexcept());
+  ASSERT_EQ(-1, feenableexcept(FE_OVERFLOW));
+  ASSERT_EQ(0, fegetexcept());
+  ASSERT_EQ(-1, feenableexcept(FE_UNDERFLOW));
+  ASSERT_EQ(0, fegetexcept());
+  ASSERT_EQ(-1, feenableexcept(FE_INEXACT));
+  ASSERT_EQ(0, fegetexcept());
+  ASSERT_EQ(-1, feenableexcept(FE_DENORMAL));
+  ASSERT_EQ(0, fegetexcept());
 #else
   // We can't recover from SIGFPE, so sacrifice a child...
   pid_t pid = fork();
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 87a0eae..05a19d1 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -2148,3 +2148,37 @@
 
   ASSERT_EQ(0, fclose(fp));
 }
+
+TEST(STDIO_TEST, fseek_64bit) {
+  TemporaryFile tf;
+  FILE* fp = fopen64(tf.filename, "w+");
+  ASSERT_TRUE(fp != nullptr);
+  ASSERT_EQ(0, fseeko64(fp, 0x2'0000'0000, SEEK_SET));
+  ASSERT_EQ(0x2'0000'0000, ftello64(fp));
+  ASSERT_EQ(0, fseeko64(fp, 0x1'0000'0000, SEEK_CUR));
+  ASSERT_EQ(0x3'0000'0000, ftello64(fp));
+  ASSERT_EQ(0, fclose(fp));
+}
+
+// POSIX requires that fseek/fseeko fail with EOVERFLOW if the new file offset
+// isn't representable in long/off_t.
+TEST(STDIO_TEST, fseek_overflow_32bit) {
+  TemporaryFile tf;
+  FILE* fp = fopen64(tf.filename, "w+");
+  ASSERT_EQ(0, ftruncate64(fileno(fp), 0x2'0000'0000));
+
+  // Bionic implements overflow checking for SEEK_CUR, but glibc doesn't.
+#if defined(__BIONIC__) && !defined(__LP64__)
+  ASSERT_EQ(0, fseek(fp, 0x7fff'ffff, SEEK_SET));
+  ASSERT_EQ(-1, fseek(fp, 1, SEEK_CUR));
+  ASSERT_EQ(EOVERFLOW, errno);
+#endif
+
+  // Neither Bionic nor glibc implement the overflow checking for SEEK_END.
+  // (Aside: FreeBSD's libc is an example of a libc that checks both SEEK_CUR
+  // and SEEK_END -- many C libraries check neither.)
+  ASSERT_EQ(0, fseek(fp, 0, SEEK_END));
+  ASSERT_EQ(0x2'0000'0000, ftello64(fp));
+
+  fclose(fp);
+}