From 0b70567432ab630c2d71a70ebdf3792fbdbf5768 Mon Sep 17 00:00:00 2001
From: Kleis Auke Wolthuizen <github@kleisauke.nl>
Date: Mon, 1 Nov 2021 23:34:00 +0100
Subject: [PATCH] tsc_timer.h: sync with nanobenchmark.cc from Highway (#797)

This file was originally originated from nanobenchmark.cc on the
Highway project. Re-syncing this file ensures that the fixes from
commit google/highway@3769cb2, google/highway@1df8ed4 and
google/highway@f2efb75 are incorporated.

Including these commits also resolves the compilation with
llvm-mingw targeting i686 when build with support for profiling.
---
 AUTHORS                  |  1 +
 lib/profiler/tsc_timer.h | 81 ++++++++++++++++++++++++++--------------
 2 files changed, 55 insertions(+), 27 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 210a22704..dc396a784 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -20,6 +20,7 @@ Alexander Sago <cagelight@gmail.com>
 Dirk Lemstra <dirk@lemstra.org>
 Jon Sneyers <jon@cloudinary.com>
 Lovell Fuller
+Kleis Auke Wolthuizen <github@kleisauke.nl>
 Marcin Konicki <ahwayakchih@gmail.com>
 Petr Diblík
 Pieter Wuille
diff --git a/lib/profiler/tsc_timer.h b/lib/profiler/tsc_timer.h
index 27db276f7..d3c1bee45 100644
--- a/lib/profiler/tsc_timer.h
+++ b/lib/profiler/tsc_timer.h
@@ -10,20 +10,43 @@
 // ensure exactly the desired regions are measured.
 
 #include <stdint.h>
+#include <time.h>  // clock_gettime
+
+#if defined(_WIN32) || defined(_WIN64)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif  // WIN32_LEAN_AND_MEAN
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif  // NOMINMAX
+#include <windows.h>
+// Undef macros to avoid collisions
+#undef LoadFence
+#undef StoreFence
+#endif
+
+#if defined(__MACH__)
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#endif
+
+#if defined(__HAIKU__)
+#include <OS.h>
+#endif
 
 #include <ctime>
 #include <hwy/base.h>
 #include <hwy/cache_control.h>  // LoadFence
 
-#if HWY_COMPILER_MSVC
-#include <chrono>
-#endif  // HWY_COMPILER_MSVC
-
 namespace profiler {
 
+// Ticks := platform-specific timer values (CPU cycles on x86). Must be
+// unsigned to guarantee wraparound on overflow.
+using Ticks = uint64_t;
+
 // TicksBefore/After return absolute timestamps and must be placed immediately
-// before and after the region to measure. The functions are distinct because
-// they use different fences.
+// before and after the region to measure. We provide separate Before/After
+// functions because they use different fences.
 //
 // Background: RDTSC is not 'serializing'; earlier instructions may complete
 // after it, and/or later instructions may complete before it. 'Fences' ensure
@@ -59,7 +82,7 @@ namespace profiler {
 // Using Before+Before leads to higher variance and overhead than After+After.
 // However, After+After includes an LFENCE in the region measurements, which
 // adds a delay dependent on earlier loads. The combination of Before+After
-// is faster than Before+Before and more consistent than Stop+Stop because
+// is faster than Before+Before and more consistent than After+After because
 // the first LFENCE already delayed subsequent loads before the measured
 // region. This combination seems not to have been considered in prior work:
 // http://akaros.cs.berkeley.edu/lxr/akaros/kern/arch/x86/rdtsc_test.c
@@ -71,19 +94,18 @@ namespace profiler {
 // by several under/over-count errata, so we use the TSC instead.
 
 // Returns a 64-bit timestamp in unit of 'ticks'; to convert to seconds,
-// divide by InvariantTicksPerSecond. Although 32-bit ticks are faster to read,
-// they overflow too quickly to measure long regions.
-static HWY_INLINE HWY_MAYBE_UNUSED uint64_t TicksBefore() {
-  uint64_t t;
-#if HWY_ARCH_PPC
+// divide by InvariantTicksPerSecond.
+static HWY_INLINE HWY_MAYBE_UNUSED Ticks TicksBefore() {
+  Ticks t;
+#if HWY_ARCH_PPC && defined(__GLIBC__)
   asm volatile("mfspr %0, %1" : "=r"(t) : "i"(268));
-#elif HWY_ARCH_X86_64 && HWY_COMPILER_MSVC
+#elif HWY_ARCH_X86 && HWY_COMPILER_MSVC
   hwy::LoadFence();
   HWY_FENCE;
   t = __rdtsc();
   hwy::LoadFence();
   HWY_FENCE;
-#elif HWY_ARCH_X86_64 && (HWY_COMPILER_CLANG || HWY_COMPILER_GCC)
+#elif HWY_ARCH_X86_64
   asm volatile(
       "lfence\n\t"
       "rdtsc\n\t"
@@ -95,30 +117,35 @@ static HWY_INLINE HWY_MAYBE_UNUSED uint64_t TicksBefore() {
       // "memory" avoids reordering. rdx = TSC >> 32.
       // "cc" = flags modified by SHL.
       : "rdx", "memory", "cc");
-#elif HWY_COMPILER_MSVC
-  // Use std::chrono in MSVC 32-bit.
-  t = std::chrono::time_point_cast<std::chrono::nanoseconds>(
-          std::chrono::steady_clock::now())
-          .time_since_epoch()
-          .count();
-#else
-  // Fall back to OS - unsure how to reliably query cntvct_el0 frequency.
+#elif HWY_ARCH_RVV
+  asm volatile("rdcycle %0" : "=r"(t));
+#elif defined(_WIN32) || defined(_WIN64)
+  LARGE_INTEGER counter;
+  (void)QueryPerformanceCounter(&counter);
+  t = counter.QuadPart;
+#elif defined(__MACH__)
+  t = mach_absolute_time();
+#elif defined(__HAIKU__)
+  t = system_time_nsecs();  // since boot
+#else  // POSIX
   timespec ts;
   clock_gettime(CLOCK_MONOTONIC, &ts);
-  t = ts.tv_sec * 1000000000LL + ts.tv_nsec;
+  t = static_cast<Ticks>(ts.tv_sec * 1000000000LL + ts.tv_nsec);
 #endif
   return t;
 }
 
-static HWY_INLINE HWY_MAYBE_UNUSED uint64_t TicksAfter() {
-  uint64_t t;
-#if HWY_ARCH_X86_64 && HWY_COMPILER_MSVC
+static HWY_INLINE HWY_MAYBE_UNUSED Ticks TicksAfter() {
+  Ticks t;
+#if HWY_ARCH_PPC && defined(__GLIBC__)
+  asm volatile("mfspr %0, %1" : "=r"(t) : "i"(268));
+#elif HWY_ARCH_X86 && HWY_COMPILER_MSVC
   HWY_FENCE;
   unsigned aux;
   t = __rdtscp(&aux);
   hwy::LoadFence();
   HWY_FENCE;
-#elif HWY_ARCH_X86_64 && (HWY_COMPILER_CLANG || HWY_COMPILER_GCC)
+#elif HWY_ARCH_X86_64
   // Use inline asm because __rdtscp generates code to store TSC_AUX (ecx).
   asm volatile(
       "rdtscp\n\t"
