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 // __ppc_get_timebase_freq -#elif __FreeBSD__ +#elif defined(__FreeBSD__) || defined(__APPLE__) // clang-format off #include #include /* 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 #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()(std::forward(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()(std::forward(args)...); @@ -96,11 +97,10 @@ if (supported & HH_TARGET_SSE41) { Func()(std::forward(args)...); } -#elif HH_ARCH_PPC +#elif HH_ARCH_PPC && !defined(__APPLE__) if (supported & HH_TARGET_VSX) { Func()(std::forward(args)...); } - #elif HH_ARCH_NEON if (supported & HH_TARGET_NEON) { Func()(std::forward(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 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(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 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(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