+/* -*-c-*-
+ *
+ * CPU-specific dispatch
+ *
+ * (c) 2015 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * Catacomb 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef CATACOMB_DISPATCH_H
+#define CATACOMB_DISPATCH_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/macros.h>
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- Atomic data access machinery --- *
+ *
+ * If they're available, use GCC's `__atomic_*' intrinsics. If that doesn't
+ * work and we're using one of a small number of processors I'm sure won't
+ * mind, then just stick with simple memory access. Otherwise turn
+ * dispatching off, because it probably isn't thread-safe.
+ */
+
+#if GCC_VERSION_P(4, 7)
+# define CPU_DISPATCH_P 1
+# define DISPATCH_LOAD(g, v) \
+ ((v) = __atomic_load_n(&(g), __ATOMIC_RELAXED))
+# define DISPATCH_STORE(g, v) \
+ (__atomic_store_n(&(g), (v), __ATOMIC_RELAXED))
+#elif defined(__i386__) || defined(__amd64__) || \
+ defined(__arm__) || defined(__aarch64__) || \
+ defined(__mips__)
+# define CPU_DISPATCH_P 1
+# define DISPATCH_LOAD(g, v) ((v) = (g))
+# define DISPATCH_STORE(g, v) ((g) = (v))
+#endif
+
+/* --- A simple hack --- */
+
+#ifndef EMPTY
+# define EMPTY
+#endif
+
+/* --- @CPU_DISPATCH@ --- *
+ *
+ * Arguments: @stcls@ = storage class for the main @ext@ function
+ * (typically either @static@ or @EMPTY@)
+ * @rtn@ = prefix for tail-calling a function of the appropriate
+ * type (either @(void)@ or @return@)
+ * @ret@ = return type for the function
+ * @ext@ = name for the main function (other named are derived
+ * from this)
+ * @argdecls@ = parenthesis-enclosed list of argument types
+ * @args@ = parenthesis-enclosed list of argument names only
+ * @pick@ = function to select appropriate implementation
+ * @dflt@ = fallback implementation
+ *
+ * Use: Main machinery for CPU-specfic dispatching.
+ *
+ * The macro defines a function
+ *
+ * @stcls ret ext argdcls@
+ *
+ * The first time @ext@ is called, it will invoke @pick@ to
+ * select and a return a pointer to an appropriate
+ * implementation for the runtime environment. Subsequent calls
+ * to @ext@ will (usually) call this preferred implementation
+ * directly.
+ *
+ * Some target platforms may not be able to establish the
+ * necessary function pointer in a threadsafe way. On such
+ * platforms, the dispatch machinery is disabled and @ext@ will
+ * simply call @dflt@.
+ *
+ * Some additional declarations are made. As a convenience,
+ * @ext__functype@ is the function type of @ext@. Declarations
+ * are made for @pick@ and @dflt@, as @static@ functions.
+ */
+
+#ifdef CPU_DISPATCH_P
+
+#define CPU_DISPATCH(stcls, rtn, ret, ext, argdecls, args, pick, dflt) \
+ \
+typedef ret ext##__functype argdecls; \
+static ret dflt argdecls; \
+static ret ext##__dispatch argdecls; \
+static ext##__functype *pick(void); \
+static ext##__functype *ext##__ptr = ext##__dispatch; \
+ \
+static ret ext##__dispatch argdecls \
+{ \
+ ext##__functype *f = pick(); \
+ DISPATCH_STORE(ext##__ptr, f); \
+ rtn f args; \
+} \
+ \
+stcls ret ext argdecls \
+{ \
+ ext##__functype *f; \
+ DISPATCH_LOAD(ext##__ptr, f); \
+ rtn f args; \
+}
+
+#else
+
+#define CPU_DISPATCH(stcls, rtn, ret, ext, argdecls, args, pick, dflt) \
+ \
+typedef ret ext##__functype argdecls; \
+static ret dflt argdecls; \
+static ext##__functype *pick(void) IGNORABLE; \
+ \
+stcls ret ext argdecls { rtn dflt args; }
+
+#endif
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @cpu_feature_p@ --- *
+ *
+ * Arguments: @unsigned feat@ = a @CPUFEAT_...@ code
+ *
+ * Returns: Nonzero if the feature is available.
+ */
+
+enum {
+ CPUFEAT_X86_SSE2 /* Streaming SIMD Extensions 2 */
+};
+
+extern int cpu_feature_p(int /*feat*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif