+/*----- Cycle counting ----------------------------------------------------*/
+
+typedef kludge64 cycles;
+static int cyclecount_active_p = 0;
+
+#if defined(__GNUC__) && (CPUFAM_X86 || CPUFAM_AMD64)
+
+static void init_cyclecount(void) { cyclecount_active_p = 1; }
+
+static cycles cyclecount(void)
+{
+ uint32 lo, hi;
+ kludge64 cy;
+
+ __asm__("rdtsc" : "=a"(lo), "=d"(hi));
+ SET64(cy, hi, lo);
+ return cy;
+}
+
+#elif defined(HAVE_LINUX_PERF_EVENT_H) && defined(HAVE_UINT64)
+
+static int perf_fd = -1;
+
+static void init_cyclecount(void)
+{
+ struct perf_event_attr attr = { 0 };
+
+ attr.type = PERF_TYPE_HARDWARE;
+ attr.size = sizeof(attr);
+ attr.config = PERF_COUNT_HW_CPU_CYCLES;
+ attr.disabled = 0;
+ attr.exclude_kernel = 1;
+ attr.exclude_hv = 1;
+
+ if ((perf_fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0)) < 0)
+ moan("failed to open perf event: %s", strerror(errno));
+ else
+ cyclecount_active_p = 1;
+}
+
+static cycles cyclecount(void)
+{
+ kludge64 cy;
+ ssize_t n;
+
+ if (!cyclecount_active_p)
+ goto fail;
+ else if ((n = read(perf_fd, &cy.i, sizeof(cy.i))) != sizeof(cy.i)) {
+ if (n < 0) moan("error reading perf event: %s", strerror(errno));
+ else moan("unexpected short read from perf event");
+ cyclecount_active_p = 0; close(perf_fd); perf_fd = -1;
+ goto fail;
+ }
+end:
+ return (cy);
+fail:
+ SET64(cy, 0, 0);
+ goto end;
+}
+
+#else
+
+static void init_cyclecount(void) { cyclecount_active_p = 0; }
+static cycles cyclecount(void) { kludge64 cy; SET64(cy, 0, 0); return (cy); }
+
+#endif
+