Repository: firedancer-io/firedancer (C)

Issue:
FD_HAS_AVX includes SHA-NI
**Problem**

The FD_HAS_AVX feature, which is enabled by default (linux_gcc_x86_64) includes SHA-NI extensions. When attempting to run a Firedancer build on older CPUs that have AVX2 but no SHA-NI, this typically results in SIGILL:

```
Illegal instruction
```

This issue has been reported by a few internal and external contributors now. It is starting to become a blocker to adoption.

Notably, this is also broken in our CI environment.

**Suggested Fixes**

Couple options to fix:

1. Disable SHA-NI by default (easiest). We can always re-enable it for builds targeting high performance
2. Add to a native build profile and default to that. Let the native profile detect available CPU features and generate a set of compiler flags tuned to that host. This can be done in the Makefile itself (with obvious limitations) or in a `./configure` style script. 

Related PRs: #174 



Fix (patch):
diff --git a/config/linux_clang_icelake.mk b/config/linux_clang_icelake.mk
new file mode 100644
index 00000000000..4e7c80318fc
--- /dev/null
+++ b/config/linux_clang_icelake.mk
@@ -0,0 +1,28 @@
+BUILDDIR:=linux/clang/icelake
+
+include config/base.mk
+include config/with-clang.mk
+include config/with-debug.mk
+include config/with-brutality.mk
+include config/with-optimization.mk
+include config/with-threads.mk
+
+# Clang sadly doesn't support important optimizations.  This practically
+# limits clang usage to code hygenine usage for the time being.  Here,
+# ideally would do:
+#
+# -falign-functions=32 -falign-jumps=32 -falign-labels=32 -falign-loops=32
+# -mbranch-cost=5
+
+CPPFLAGS+=-fomit-frame-pointer -march=icelake-server -mtune=icelake-server -mfpmath=sse \
+	  -DFD_HAS_INT128=1 -DFD_HAS_DOUBLE=1 -DFD_HAS_ALLOCA=1 -DFD_HAS_X86=1 -DFD_HAS_SSE=1 -DFD_HAS_AVX=1 \
+		-DFD_HAS_SHANI=1 -DFD_HAS_GFNI=1
+
+FD_HAS_INT128:=1
+FD_HAS_DOUBLE:=1
+FD_HAS_ALLOCA:=1
+FD_HAS_X86:=1
+FD_HAS_SSE:=1
+FD_HAS_AVX:=1
+FD_HAS_SHANI:=1
+FD_HAS_GFNI:=1
diff --git a/config/linux_gcc_icelake.mk b/config/linux_gcc_icelake.mk
new file mode 100644
index 00000000000..234337ef30e
--- /dev/null
+++ b/config/linux_gcc_icelake.mk
@@ -0,0 +1,22 @@
+BUILDDIR:=linux/gcc/icelake
+
+include config/base.mk
+include config/with-gcc.mk
+include config/with-debug.mk
+include config/with-brutality.mk
+include config/with-optimization.mk
+include config/with-threads.mk
+
+CPPFLAGS+=-fomit-frame-pointer -falign-functions=32 -falign-jumps=32 -falign-labels=32 -falign-loops=32 \
+          -march=icelake-server -mtune=icelake-server -mfpmath=sse -mbranch-cost=5 \
+	  -DFD_HAS_INT128=1 -DFD_HAS_DOUBLE=1 -DFD_HAS_ALLOCA=1 -DFD_HAS_X86=1 -DFD_HAS_SSE=1 -DFD_HAS_AVX=1 \
+		-DFD_HAS_SHANI=1 -DFD_HAS_GFNI=1
+
+FD_HAS_INT128:=1
+FD_HAS_DOUBLE:=1
+FD_HAS_ALLOCA:=1
+FD_HAS_X86:=1
+FD_HAS_SSE:=1
+FD_HAS_AVX:=1
+FD_HAS_SHANI:=1
+FD_HAS_GFNI:=1
diff --git a/src/ballet/sha256/Local.mk b/src/ballet/sha256/Local.mk
index 72a4fdbf2d7..27211196a88 100644
--- a/src/ballet/sha256/Local.mk
+++ b/src/ballet/sha256/Local.mk
@@ -1,7 +1,9 @@
 $(call add-hdrs,fd_sha256.h)
 $(call add-objs,fd_sha256,fd_ballet)
-ifdef FD_HAS_AVX
+ifdef FD_HAS_SHANI
 $(call add-asms,fd_sha256_core_shaext,fd_ballet)
+endif
+ifdef FD_HAS_AVX
 $(call add-objs,fd_sha256_batch_avx,fd_ballet)
 endif
 
diff --git a/src/ballet/sha256/fd_sha256.c b/src/ballet/sha256/fd_sha256.c
index 155019513d4..5dfd7402655 100644
--- a/src/ballet/sha256/fd_sha256.c
+++ b/src/ballet/sha256/fd_sha256.c
@@ -97,7 +97,7 @@ fd_sha256_delete( void * shsha ) {
 }
 
 #ifndef FD_SHA256_CORE_IMPL
-#if FD_HAS_AVX
+#if FD_HAS_SHANI
 #define FD_SHA256_CORE_IMPL 1
 #else
 #define FD_SHA256_CORE_IMPL 0
diff --git a/src/ballet/sha256/fd_sha256_batch_avx.c b/src/ballet/sha256/fd_sha256_batch_avx.c
index 7cf2a2004da..23ffb2a982d 100644
--- a/src/ballet/sha256/fd_sha256_batch_avx.c
+++ b/src/ballet/sha256/fd_sha256_batch_avx.c
@@ -7,13 +7,22 @@ fd_sha256_private_batch_avx( ulong          batch_cnt,
                              void const *   _batch_data,
                              ulong const *  batch_sz,
                              void * const * _batch_hash ) {
-
-  if( FD_UNLIKELY( batch_cnt<6UL ) ) { /* FIXME: should be ~6 if fd_sha256_hash is using SHA-NI extensions, ~2 o.w. */
+  /* If the batch is too small, it's faster to run each part of the
+     batch sequentially.  When we have SHA-NI instructions, the
+     sequential implementation is faster, so we need a larger batch size
+     to justify using the batched implementation. */
+#if FD_HAS_SHANI
+# define MIN_BATCH_CNT (6UL)
+#else
+# define MIN_BATCH_CNT (2UL)
+#endif
+  if( FD_UNLIKELY( batch_cnt<MIN_BATCH_CNT ) ) {
     void const * const * batch_data = (void const * const *)_batch_data;
     for( ulong batch_idx=0UL; batch_idx<batch_cnt; batch_idx++ )
       fd_sha256_hash( batch_data[ batch_idx ], batch_sz[ batch_idx ], _batch_hash[ batch_idx ] );
     return;
   }
+#undef MIN_BATCH_CNT
 
   /* SHA appends to the end of each message 9 bytes of additional data
      (a messaging terminator byte and the big endian ulong with the
diff --git a/src/util/fd_util_base.h b/src/util/fd_util_base.h
index 39910f2372f..1338a1ae1d9 100644
--- a/src/util/fd_util_base.h
+++ b/src/util/fd_util_base.h
@@ -144,17 +144,31 @@
    (basically do the 256-bit wide parts of "x86intrin.h" work).
    Recommend using the simd/fd_avx.h APIs instead of raw Intel
    intrinsics for readability and to facilitate portability to non-x86
-   platforms.  Implies FD_HAS_SSE.
-
-   Note that the introduction of AVX2 circa 2013 was also around the
-   time SHA extensions were added.  Currently FD_HAS_AVX thus also
-   implies the availability of SHA extensions but we might be more
-   precise in the future. */
+   platforms.  Implies FD_HAS_SSE. */
 
 #ifndef FD_HAS_AVX
 #define FD_HAS_AVX 0
 #endif
 
+/* FD_HAS_SHANI indicates that the target supports Intel SHA extensions
+   which accelerate SHA-1 and SHA-256 computation.  This extension is
+   also called SHA-NI or SHA_NI (Secure Hash Algorithm New
+   Instructiosn).  Although proposed in 2013, they're only supported on
+   Intel Ice Lake and AMD Zen CPUs and newer.  Implies FD_HAS_AVX. */
+
+#ifndef FD_HAS_SHANI
+#define FD_HAS_SHANI 0
+#endif
+
+/* FD_HAS_GFNI indicates that the target supports Intel Galois Field
+ * extensions, which accelerate operations over binary extension fields,
+ * especially GF(2^8).  These instructions are supported on Intel Ice
+ * Lake and newer and AMD Zen4 and newer CPUs.  Implies FD_HAS_AVX. */
+
+#ifndef FD_HAS_GFNI
+#define FD_HAS_GFNI 0
+#endif
+
 /* Base development environment ***************************************/
 
 /* The functionality provided by these vanilla headers are always