|
4 | 4 | * portable high-precision interval timing |
5 | 5 | * |
6 | 6 | * This file provides an abstraction layer to hide portability issues in |
7 | | - * interval timing. On x86 we use the RDTSC/RDTSCP instruction directly in |
8 | | - * certain cases, or alternatively clock_gettime() on Unix-like systems and |
| 7 | + * interval timing. On x86 we use the RDTSC/RDTSCP instruction, and on |
| 8 | + * AArch64 the CNTVCT_EL0 generic timer, directly in certain cases, or |
| 9 | + * alternatively clock_gettime() on Unix-like systems and |
9 | 10 | * QueryPerformanceCounter() on Windows. These macros also give some breathing |
10 | 11 | * room to use other high-precision-timing APIs. |
11 | 12 | * |
@@ -95,7 +96,7 @@ typedef struct instr_time |
95 | 96 | * PG_INSTR_TSC_CLOCK controls whether the TSC clock source is compiled in, and |
96 | 97 | * potentially used based on timing_tsc_enabled. |
97 | 98 | */ |
98 | | -#if defined(__x86_64__) || defined(_M_X64) |
| 99 | +#if defined(__x86_64__) || defined(_M_X64) || (defined(__aarch64__) && !defined(_MSC_VER)) |
99 | 100 | #define PG_INSTR_TICKS_TO_NS 1 |
100 | 101 | #define PG_INSTR_TSC_CLOCK 1 |
101 | 102 | #elif defined(WIN32) |
@@ -333,6 +334,8 @@ pg_ns_to_ticks(int64 ns) |
333 | 334 |
|
334 | 335 | #if PG_INSTR_TSC_CLOCK |
335 | 336 |
|
| 337 | +#if defined(__x86_64__) || defined(_M_X64) |
| 338 | + |
336 | 339 | #define PG_INSTR_TSC_CLOCK_NAME_FAST "RDTSC" |
337 | 340 | #define PG_INSTR_TSC_CLOCK_NAME "RDTSCP" |
338 | 341 |
|
@@ -382,21 +385,52 @@ pg_get_ticks(void) |
382 | 385 | return pg_get_ticks_system(); |
383 | 386 | } |
384 | 387 |
|
| 388 | +#elif defined(__aarch64__) && !defined(_MSC_VER) |
| 389 | + |
| 390 | +#define PG_INSTR_TSC_CLOCK_NAME_FAST "CNTVCT_EL0" |
| 391 | +#define PG_INSTR_TSC_CLOCK_NAME "CNTVCT_EL0 (ISB)" |
| 392 | + |
| 393 | +/* |
| 394 | + * Read the ARM generic timer counter (CNTVCT_EL0). |
| 395 | + * |
| 396 | + * The "fast" variant reads the counter without a barrier, analogous to RDTSC |
| 397 | + * on x86. The regular variant issues an ISB (Instruction Synchronization |
| 398 | + * Barrier) first, which acts as a serializing instruction analogous to RDTSCP, |
| 399 | + * ensuring all preceding instructions have completed before reading the |
| 400 | + * counter. |
| 401 | + */ |
385 | 402 | static pg_attribute_always_inline instr_time |
386 | 403 | pg_get_ticks_fast(void) |
387 | 404 | { |
388 | 405 | if (likely(timing_tsc_enabled)) |
389 | 406 | { |
390 | 407 | instr_time now; |
391 | 408 |
|
392 | | - now.ticks = pg_rdtsc(); |
| 409 | + now.ticks = __builtin_arm_rsr64("cntvct_el0"); |
393 | 410 | return now; |
394 | 411 | } |
395 | 412 |
|
396 | 413 | return pg_get_ticks_system(); |
397 | 414 | } |
398 | 415 |
|
399 | | -#else |
| 416 | +static pg_attribute_always_inline instr_time |
| 417 | +pg_get_ticks(void) |
| 418 | +{ |
| 419 | + if (likely(timing_tsc_enabled)) |
| 420 | + { |
| 421 | + instr_time now; |
| 422 | + |
| 423 | + __builtin_arm_isb(0xf); |
| 424 | + now.ticks = __builtin_arm_rsr64("cntvct_el0"); |
| 425 | + return now; |
| 426 | + } |
| 427 | + |
| 428 | + return pg_get_ticks_system(); |
| 429 | +} |
| 430 | + |
| 431 | +#endif /* defined(__aarch64__) */ |
| 432 | + |
| 433 | +#else /* !PG_INSTR_TSC_CLOCK */ |
400 | 434 |
|
401 | 435 | static pg_attribute_always_inline instr_time |
402 | 436 | pg_get_ticks(void) |
|
0 commit comments