base/dispatch.c, base/dispatch.h: Add proper detection for AVX2.
[catacomb] / base / dispatch.c
index 7652f32..f1859c2 100644 (file)
@@ -53,15 +53,21 @@ enum {
 #  define CPUID1C_PCLMUL (1u << 1)
 #  define CPUID1C_SSSE3 (1u << 9)
 #  define CPUID1C_AESNI (1u << 25)
+#  define CPUID1C_OSXSAVE (1u << 27)
 #  define CPUID1C_AVX (1u << 28)
 #  define CPUID1C_RDRAND (1u << 30)
 
+  CPUID_7_0_B,                         /* eax = 7, ecx = 0 => ebx&?? */
+#  define CPUID70B_AVX2 (1u << 5)
+#  define CPUID70B_RDSEED (1u << 18)
 };
 
 struct cpuid { unsigned a, b, c, d; };
+struct xcr { unsigned lo, hi; };
 extern int dispatch_x86ish_cpuid(struct cpuid *, unsigned a, unsigned c);
 extern int dispatch_x86ish_xmmregisters_p(void);
-extern int dispatch_x86ish_rdrand(unsigned *);
+extern int dispatch_x86ish_xgetbv(struct xcr *z_out, unsigned c);
+extern int dispatch_x86ish_rdrand(unsigned op, unsigned *);
 
 static void cpuid(struct cpuid *cc, unsigned a, unsigned c)
 {
@@ -99,18 +105,22 @@ static int cpuid_feature_p(unsigned leaf, unsigned bits)
       if (cpuid_maxleaf() < 1) return (0);
       cpuid(&c, 1, 0); r = c.c;
       break;
+    case CPUID_7_0_B:
+      if (cpuid_maxleaf() < 7) return (0);
+      cpuid(&c, 7, 0); r = c.b;
+      break;
     default:
       assert(!"unknown cpuid leaf");
   }
   return ((r&bits) == bits);
 }
 
-/* --- @xmm_registers_available_p@ --- *
+/* --- @{x,y}mm_registers_available_p@ --- *
  *
  * Arguments:  ---
  *
- * Returns:    Nonzero if the operating system has made the XMM registers
- *             available for use.
+ * Returns:    Nonzero if the operating system has made the XMM or YMM
+ *             registers available for use.
  */
 
 static int xmm_registers_available_p(void)
@@ -121,6 +131,22 @@ static int xmm_registers_available_p(void)
   return (f);
 }
 
+static int ymm_registers_available_p(void)
+{
+  struct xcr xcr0;
+  int f;
+
+  f = cpuid_feature_p(CPUID_1_C, CPUID1C_OSXSAVE);
+  dispatch_debug("XGETBV %savailable", f ? "" : "not ");
+  if (!f) return (0);
+
+  dispatch_x86ish_xgetbv(&xcr0, 0); f = (xcr0.lo&0x06) == 0x06;
+  dispatch_debug("YMM state %senabled", f ? "" : "not ");
+  if (!f) return (0);
+
+  return (1);
+}
+
 /* --- @rdrand_works_p@ --- *
  *
  *
@@ -130,28 +156,37 @@ static int xmm_registers_available_p(void)
  *             that it's already been verified to be safe to issue.
  */
 
-static int rdrand_works_p(void)
+enum { OP_RDRAND, OP_RDSEED };
+
+static int rdrand_works_p(unsigned op)
 {
   unsigned ref, x, i;
+  const char *what;
+
+  switch (op) {
+    case OP_RDRAND: what = "RDRAND"; break;
+    case OP_RDSEED: what = "RDSEED"; break;
+    default: assert(!"unexpected op");
+  }
 
   /* Check that it doesn't always give the same answer.  Try four times: this
    * will fail with probability %$2^{-128}$% with a truly random generator,
    * which seems fair enough.
    */
-  if (dispatch_x86ish_rdrand(&ref)) goto fail;
+  if (dispatch_x86ish_rdrand(op, &ref)) goto fail;
   for (i = 0; i < 4; i++) {
-    if (dispatch_x86ish_rdrand(&x)) goto fail;
+    if (dispatch_x86ish_rdrand(op, &x)) goto fail;
     if (x != ref) goto not_stuck;
   }
-  dispatch_debug("RDRAND always returns 0x%08x!", ref);
+  dispatch_debug("%s always returns 0x%08x!", what, ref);
   return (0);
 
 not_stuck:
-  dispatch_debug("RDRAND instruction looks plausible");
+  dispatch_debug("%s instruction looks plausible", what);
   return (1);
 
 fail:
-  dispatch_debug("RDRAND instruction fails too often");
+  dispatch_debug("%s instruction fails too often", what);
   return (0);
 }
 
@@ -398,7 +433,7 @@ static unsigned get_hwcaps(void)
   unsigned hw;
 
   DISPATCH_LOAD(hwcaps, hw);
-  if (!(hwcaps & HF_PROBED)) { probe_hwcaps(); DISPATCH_LOAD(hwcaps, hw); }
+  if (!(hw & HF_PROBED)) { probe_hwcaps(); DISPATCH_LOAD(hwcaps, hw); }
   return (hw);
 }
 
@@ -505,20 +540,27 @@ int cpu_feature_p(int feat)
                 cpuid_feature_p(CPUID_1_D, CPUID1D_SSE2) &&
                 xmm_registers_available_p());
     CASE_CPUFEAT(X86_AESNI, "x86:aesni",
-                cpuid_feature_p(CPUID_1_D, CPUID1C_AESNI) &&
+                cpuid_feature_p(CPUID_1_C, CPUID1C_AESNI) &&
                 xmm_registers_available_p());
     CASE_CPUFEAT(X86_RDRAND, "x86:rdrand",
                 cpuid_feature_p(CPUID_1_C, CPUID1C_RDRAND) &&
-                rdrand_works_p());
+                rdrand_works_p(OP_RDRAND));
     CASE_CPUFEAT(X86_AVX, "x86:avx",
                 cpuid_feature_p(CPUID_1_C, CPUID1C_AVX) &&
-                xmm_registers_available_p());
+                ymm_registers_available_p());
+    CASE_CPUFEAT(X86_AVX2, "x86:avx2",
+                cpuid_feature_p(CPUID_1_C, CPUID1C_AVX) &&
+                cpuid_feature_p(CPUID_7_0_B, CPUID70B_AVX2) &&
+                ymm_registers_available_p());
     CASE_CPUFEAT(X86_SSSE3, "x86:ssse3",
                 cpuid_feature_p(CPUID_1_C, CPUID1C_SSSE3) &&
                 xmm_registers_available_p());
     CASE_CPUFEAT(X86_PCLMUL, "x86:pclmul",
                 cpuid_feature_p(CPUID_1_C, CPUID1C_PCLMUL) &&
                 xmm_registers_available_p());
+    CASE_CPUFEAT(X86_RDSEED, "x86:rdseed",
+                cpuid_feature_p(CPUID_7_0_B, CPUID70B_RDSEED) &&
+                rdrand_works_p(OP_RDSEED));
 #endif
 #ifdef CAPMAP
 #  define FEATP__CASE(feat, tok)                                       \