kala 1.0.0

Timing with nanosecond precision on multiple architectures.
Documentation
#![cfg(target_arch = "aarch64")]

use crate::arch::TscFrequency;
use crate::time::Instant;
use core::arch::asm;

// Returns tick (TSC) frequency in Hz.
#[inline(always)]
pub fn tick_frequency() -> TscFrequency {
  // No need for LazyLock since reading this static storage "register" is fast.
  let frequency: u64;

  // SAFETY: Reading the TSC frequency register is always safe on AArch64
  unsafe {
    asm!(
        "mrs {}, cntfrq_el0",
        out(reg) frequency,
        options(nomem, nostack, preserves_flags, pure),
    );
  }
  TscFrequency::CpuMetadata(frequency)
}

pub fn is_hw_supported() -> bool {
  // Apple does not allow using the MRS instruction on iOS. Otherwise the system
  // counter is in the AArch64 baseline.
  cfg!(not(target_os = "ios"))
}

#[inline(always)]
pub fn clock_fence() {
  // SAFETY: A plain ISB is always safe and always present on AArch64.
  unsafe {
    // core::arch::aarch64::__isb(0) is not yet stable
    // Intentionally omitting `nomem` to also prevent memory access
    // reordering.
    asm!("isb", options(nostack, preserves_flags));
  }
}

#[inline(always)]
pub fn ticks() -> u64 {
  let ticks: u64;
  // SAFETY: This is a simple register read that should always be safe, provided
  // `is_hw_supported` returns true (which should be checked ahead of time).
  unsafe {
    asm!(
      "mrs {}, cntvct_el0",
      out(reg) ticks,
      // Intentionally omitting `nomem` to also prevent memory access
      // reordering.
      options(nostack, preserves_flags),
    );
  }
  ticks
}

#[inline(always)]
pub fn now() -> Instant {
  Instant::Ticks(ticks())
}