[PATCH] compiler-gcc: Workaround MIPS GCC reordering bug

From: Paul Burton
Date: Mon Feb 15 2016 - 12:59:49 EST


GCC for MIPS currently has a bug which leads to it reordering
instructions into branch delay slots incorrectly when a call to
__builtin_unreachable is the only content of the default case of a
switch statement. See this thread for details:

https://gcc.gnu.org/ml/gcc-patches/2015-09/msg00360.html

Work around this bug by placing an empty volatile asm statement before
the __builtin_unreachable call, which prevents GCC's delay slot filler
from seeing past the asm statement (& thus the call to
__builtin_unreachable) to instructions it shouldn't reorder. It would be
good to guard this based upon GCC_VERSION once the bug is fixed in GCC,
but meanwhile this workaround has no known negative side effects.

This bug affects at least a maltasmvp_defconfig kernel built from the
v4.4 tag using GCC 4.9.2 (from a Codescape SDK 2015.06-05 toolchain),
with the result being an address exception taken after log messages
about the L1 caches (during probe of the L2 cache):

Initmem setup node 0 [mem 0x0000000080000000-0x000000009fffffff]
VPE topology {2,2} total 4
Primary instruction cache 64kB, VIPT, 4-way, linesize 32 bytes.
Primary data cache 64kB, 4-way, PIPT, no aliases, linesize 32 bytes
<AdEL exception here>

This is early enough that the kernel exception vectors are not in use,
so any further output depends upon the bootloader. This is reproducible
in QEMU where no further output occurs - ie. the system hangs here.
Given the affected v4.4 configuration this patch is marked for stable
backport to v4.4, however I cannot test every configuration so it's
entirely possible that this bug hits other platforms in earlier kernel
versions. Given the nature of the bug it may potentially be hit with
differing symptoms.

Signed-off-by: Paul Burton <paul.burton@xxxxxxxxxx>
Cc: <stable@xxxxxxxxxxxxxxx> # v4.4+
Cc: Matthew Fortune <matthew.fortune@xxxxxxxxxx>
Cc: Robert Suchanek <robert.suchanek@xxxxxxxxxx>
---

include/linux/compiler-gcc.h | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 22ab246..e7c3502 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -196,7 +196,25 @@
* this in the preprocessor, but we can live with this because they're
* unreleased. Really, we need to have autoconf for the kernel.
*/
-#define unreachable() __builtin_unreachable()
+#if defined(__mips__)
+/*
+ * GCC for MIPS currently has a bug which leads to it incorrectly
+ * reordering instructions into branch delay slots when a call to
+ * __builtin_unreachable() is the only thing in the default case of a
+ * switch statement. We avoid this bug for those versions of GCC by
+ * placing an empty volatile asm statement before the call to
+ * __builtin_unreachable, which GCC is prevented from reordering past.
+ *
+ * See this thread for details:
+ * https://gcc.gnu.org/ml/gcc-patches/2015-09/msg00360.html
+ */
+# define unreachable() do { \
+ asm volatile(""); \
+ __builtin_unreachable(); \
+} while (0)
+#else
+# define unreachable() __builtin_unreachable()
+#endif

/* Mark a function definition as prohibited from being cloned. */
#define __noclone __attribute__((__noclone__))
--
2.7.1