Fixes for powerpc: no VSX on Darwin (incompatible ISA), use the timebase
register (mftb) for tick counts and hw.tbfrequency to convert them to
seconds. On 32-bit the timebase is read via the mftbu/mftb loop to avoid
tearing. Based on the original patch by Sergey Fedorov.

--- auxil/highwayhash/highwayhash/arch_specific.cc	2025-08-19 02:02:15.000000000 +0800
+++ auxil/highwayhash/highwayhash/arch_specific.cc	2026-08-20 00:00:00.000000000 +0800
@@ -23,7 +23,7 @@
 #if HH_ARCH_PPC
 #if __GLIBC__
 #include <sys/platform/ppc.h>  // __ppc_get_timebase_freq
-#elif __FreeBSD__
+#elif defined(__FreeBSD__) || defined(__APPLE__)
 // clang-format off
 #include <sys/types.h>
 #include <sys/sysctl.h>                 /* must come after sys/types.h */
@@ -155,6 +155,14 @@
   sysctlbyname("dev.cpu.0.freq", &freq, &length, NULL, 0);
   freq *= 1E6;
   return freq;
+#elif defined(__APPLE__)
+  uint64_t freq_hz = 0;
+  size_t length = sizeof(freq_hz);
+  int rc = sysctlbyname("hw.cpufrequency", &freq_hz, &length, NULL, 0);
+  if (rc == 0 && freq_hz > 0)
+    return (double)freq_hz;
+  else
+    return 0.0;
 #endif
 #endif
 
@@ -173,18 +181,34 @@
 #if HH_ARCH_PPC
 #if __GLIBC__
   static const double cycles_per_second = __ppc_get_timebase_freq();
+  return cycles_per_second;
 #elif __FreeBSD__
   double cycles_per_second = 0;
   size_t length = sizeof(cycles_per_second);
   sysctlbyname("kern.timecounter.tc.timebase.frequency", &cycles_per_second,
                &length, NULL, 0);
+  return cycles_per_second;
 #elif __OpenBSD__
   /* There is currently no method of retrieving this via userland.
    * This value is correct for Power8 and Power9.
    */
   static const double cycles_per_second = 512000000;
-#endif
   return cycles_per_second;
+#elif defined(__APPLE__)
+  /* The tick source used on Darwin PPC is the timebase register (mftb),
+   * which runs at hw.tbfrequency, not at the CPU clock (hw.cpufrequency). */
+  uint64_t freq_hz = 0;
+  size_t length = sizeof(freq_hz);
+  int rc = sysctlbyname("hw.tbfrequency", &freq_hz, &length, NULL, 0);
+  if (rc == 0 && length == sizeof(uint32_t))
+    freq_hz >>= 32; /* big-endian: a 4-byte value lands in the high word */
+  if (rc == 0 && freq_hz > 0)
+    return (double)freq_hz;
+  else
+    return 0.0;
+#endif
+  /* fallback */
+  return DetectNominalClockRate();
 #else
   return NominalClockRate();
 #endif
--- auxil/highwayhash/highwayhash/arch_specific.h	2025-08-19 02:02:15.000000000 +0800
+++ auxil/highwayhash/highwayhash/arch_specific.h	2026-08-20 00:00:00.000000000 +0800
@@ -70,7 +70,7 @@
 #define HH_ARCH_NEON 0
 #endif
 
-#if defined(__powerpc64__) || defined(_M_PPC)
+#if defined(__powerpc__) || defined(__powerpc64__) || defined(__POWERPC__) || defined(_M_PPC)
 #define HH_ARCH_PPC 1
 #else
 #define HH_ARCH_PPC 0
@@ -95,7 +95,7 @@
 // https://stackoverflow.com/questions/18563978/detect-the-availability-of-sse-sse2-instruction-set-in-visual-studio
 #elif defined(__SSE4_1__) || (HH_MSC_VERSION != 0 && defined(__AVX__))
 #define HH_TARGET_NAME SSE41
-#elif defined(__VSX__)
+#elif defined(__VSX__) && !defined(__APPLE__)
 #define HH_TARGET_NAME VSX
 #elif HH_ARCH_NEON
 #define HH_TARGET_NAME NEON
--- auxil/highwayhash/highwayhash/hh_vsx.cc	2025-08-19 02:02:15.000000000 +0800
+++ auxil/highwayhash/highwayhash/hh_vsx.cc	2026-08-20 00:00:00.000000000 +0800
@@ -17,6 +17,6 @@
 
 #define HH_TARGET_NAME VSX
 
-#ifdef __VSX__
+#if defined(__VSX__) && !defined(__APPLE__)
 #include "highwayhash/highwayhash_target.cc"
 #endif
--- auxil/highwayhash/highwayhash/hh_vsx.h	2025-08-19 02:02:15.000000000 +0800
+++ auxil/highwayhash/highwayhash/hh_vsx.h	2026-08-20 00:00:00.000000000 +0800
@@ -27,7 +27,7 @@
 
 // For auto-dependency generation, we need to include all headers but not their
 // contents
-#ifndef HH_DISABLE_TARGET_SPECIFIC
+#if !defined(HH_DISABLE_TARGET_SPECIFIC) && !defined(__APPLE__)
 
 #include <altivec.h>
 #undef vector
--- auxil/highwayhash/highwayhash/instruction_sets.h	2025-08-19 02:02:15.000000000 +0800
+++ auxil/highwayhash/highwayhash/instruction_sets.h	2026-08-20 00:00:00.000000000 +0800
@@ -36,7 +36,8 @@
 // The HH_TARGET_Portable bit is guaranteed to be set.
 #if HH_ARCH_X64
   static TargetBits Supported();
-#elif HH_ARCH_PPC
+// No VSX support on Darwin, incompatible ISA. Notice, !defined(__VSX__) does not work.
+#elif HH_ARCH_PPC && !defined(__APPLE__)
   static HH_INLINE TargetBits Supported() {
     return HH_TARGET_VSX | HH_TARGET_Portable;
   }
@@ -64,7 +65,7 @@
       Func<HH_TARGET_SSE41>()(std::forward<Args>(args)...);
       return HH_TARGET_SSE41;
     }
-#elif HH_ARCH_PPC
+#elif HH_ARCH_PPC && !defined(__APPLE__)
     const TargetBits supported = Supported();
     if (supported & HH_TARGET_VSX) {
       Func<HH_TARGET_VSX>()(std::forward<Args>(args)...);
@@ -96,11 +97,10 @@
     if (supported & HH_TARGET_SSE41) {
       Func<HH_TARGET_SSE41>()(std::forward<Args>(args)...);
     }
-#elif HH_ARCH_PPC
+#elif HH_ARCH_PPC && !defined(__APPLE__)
     if (supported & HH_TARGET_VSX) {
       Func<HH_TARGET_VSX>()(std::forward<Args>(args)...);
     }
-
 #elif HH_ARCH_NEON
     if (supported & HH_TARGET_NEON) {
       Func<HH_TARGET_NEON>()(std::forward<Args>(args)...);
--- auxil/highwayhash/highwayhash/tsc_timer.h	2025-08-19 02:02:15.000000000 +0800
+++ auxil/highwayhash/highwayhash/tsc_timer.h	2026-08-20 00:00:00.000000000 +0800
@@ -94,7 +94,21 @@
 inline uint64_t Start<uint64_t>() {
   uint64_t t;
 #if HH_ARCH_PPC
-  asm volatile("mfspr %0, %1" : "=r"(t) : "i"(268));
+  #if defined(__APPLE__) && !defined(__ppc64__)
+    /* 32-bit: a 64-bit "=r" operand names only the high register of the
+     * pair, and the 64-bit timebase must be read without tearing. */
+    uint32_t hi, lo, hi2;
+    do {
+      asm volatile("mftbu %0" : "=r"(hi));
+      asm volatile("mftb %0" : "=r"(lo));
+      asm volatile("mftbu %0" : "=r"(hi2));
+    } while (hi != hi2);
+    t = (static_cast<uint64_t>(hi) << 32) | lo;
+  #elif defined(__APPLE__)
+    asm volatile("mftb %0" : "=r"(t));
+  #else
+    asm volatile("mfspr %0, %1" : "=r"(t) : "i"(268));
+  #endif
 #elif HH_ARCH_AARCH64
   asm volatile("mrs %0, cntvct_el0" : "=r"(t));
 #elif HH_ARCH_X64 && HH_MSC_VERSION
@@ -125,7 +139,21 @@
 inline uint64_t Stop<uint64_t>() {
   uint64_t t;
 #if HH_ARCH_PPC
-  asm volatile("mfspr %0, %1" : "=r"(t) : "i"(268));
+  #if defined(__APPLE__) && !defined(__ppc64__)
+    /* 32-bit: a 64-bit "=r" operand names only the high register of the
+     * pair, and the 64-bit timebase must be read without tearing. */
+    uint32_t hi, lo, hi2;
+    do {
+      asm volatile("mftbu %0" : "=r"(hi));
+      asm volatile("mftb %0" : "=r"(lo));
+      asm volatile("mftbu %0" : "=r"(hi2));
+    } while (hi != hi2);
+    t = (static_cast<uint64_t>(hi) << 32) | lo;
+  #elif defined(__APPLE__)
+    asm volatile("mftb %0" : "=r"(t));
+  #else
+    asm volatile("mfspr %0, %1" : "=r"(t) : "i"(268));
+  #endif
 #elif HH_ARCH_AARCH64
   asm volatile("mrs %0, cntvct_el0" : "=r"(t));
 #elif HH_ARCH_X64 && HH_MSC_VERSION
