#ifndef SRDTSC_H
#define SRDTSC_H

#ifdef __cplusplus
extern "C" {
#endif

/*
 * если определены HAVE_{CLANG,GCC}_BUILTIN_RDTSC, то есть builtin
 * inline asm для clang/gcc одинаковый
 * на POWER AIX/Linux есть библиотечные функции
 * если определено _WIN32, то есть builtin
 * на Solaris нужен asm
 * если ia32/x86_64 и нет HAVE_{CLANG,GCC}_BUILTIN_RDTSC, то нужен asm
 */

#if defined HAVE_GCC_BUILTIN_RDTSC && !(defined ANDROID)
#if defined(PROCESSOR_TYPE) && (PROCESSOR_TYPE == PROC_TYPE_E2K32 || PROCESSOR_TYPE == PROC_TYPE_E2K64)
#include <x86intrin.h>
#endif
static inline unsigned long long leoh_readtsc(void)
{
    return __builtin_ia32_rdtsc();
}
#elif defined HAVE_CLANG_BUILTIN_RDTSC
#include <x86intrin.h>
static inline unsigned long long leoh_readtsc(void)
{
    return _rdtsc();
}
#elif !defined(IOS) && (defined __clang__ || defined __GNUC__) && (PROCESSOR_TYPE == PROC_TYPE_I386 || PROCESSOR_TYPE == PROC_TYPE_X64)
static inline unsigned long long leoh_readtsc(void)
{
    unsigned int tickl, tickh;
    __asm__ __volatile__("rdtsc":"=a"(tickl),"=d"(tickh));
    return ((unsigned long long)tickh << 32)|tickl;
}
#elif !defined(IOS) && (defined __clang__ || defined __GNUC__) && (PROCESSOR_TYPE == PROC_TYPE_ARM64)
// We will be reading CNTVCT_EL0 (the virtual counter), which is prepared for us by OS.
// The system counter sits outside multiprocessor in SOC and runs on a different frequency.
// Increments at a fixed frequency, typically in the range 1-50MHz.
// Applications can figure out system counter configuration via CNTFRQ_EL0.
//
// Notice:
// Reads of CNTVCT_EL0 can occur speculatively and out of order relative to other
// instructions executed on the same PE.
// For example, if a read from memory is used to obtain a signal from another agent
// that indicates that CNTVCT_EL0 must be read, an ISB is used to ensure that the
// read of CNTVCT_EL0 occurs after the signal has been read from memory
//
// More details:
// Chapter D6: The Generic Timer in AArch64 state
// ARM DDI 0487B.a, ID033117 (file: DDI0487B_a_armv8_arm.pdf)

static inline unsigned long long leoh_readtsc(void) 
{
       unsigned long long ret;  // unsigned 64-bit value
       asm volatile("isb; mrs %0, cntvct_el0" : "=r"(ret));
       return ret;
}
#elif defined (__powerpc__) || defined (__powerpc) || defined (__ppc__)
#include <sys/time.h>
#if defined AIX
/*
 * результат в наносекундах
 * http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.basetechref/doc/basetrf2/read_real_time.htm
 */
static inline unsigned long long leoh_readtsc(void)
{
    timebasestruct_t cur_time;
    const unsigned long long ns_per_sec = 1000000000ULL;
    read_real_time(&cur_time, TIMEBASE_SZ);
    return
	ns_per_sec * (unsigned long long)cur_time.tb_high + cur_time.tb_low;
}
#elif defined LINUX  /* POWER LINUX */
#include <sys/platform/ppc.h>
/*
 * https://www.gnu.org/software/libc/manual/html_node/PowerPC.html
 * возвращает uint64_t --> преобразование не нужно
 */
static inline unsigned long long leoh_readtsc(void)
{
    return __ppc_get_timebase();
}
#else
#error POWER at unkonwn OS detected, cant continue
#endif /* POWER или AIX или Linux. На 2016 год других не было */

#elif defined _WIN32
#  if (PROCESSOR_TYPE == PROC_TYPE_I386 || PROCESSOR_TYPE == PROC_TYPE_X64)
/*
 * как минимум, начиная с VS 2005 есть intrinsic __rdtsc()
 * https://msdn.microsoft.com/ru-ru/library/twchhe95%28v=vs.80%29.aspx
 */
#pragma warning (push)
#pragma warning (disable:4005)
#define _VCRUNTIME_DISABLED_WARNINGS
#include <intrin.h>
#pragma warning (pop)

#pragma intrinsic(__rdtsc)
static __inline unsigned long long leoh_readtsc(void)
{
    return (unsigned long long) __rdtsc();
}
#  elif (PROCESSOR_TYPE == PROC_TYPE_ARM64) 
//from winnt.h\wdm.h
//ReadTimeStampCounter(
// op0=2/3 encodings, use with _Read/WriteStatusReg
#define ARM64_SYSREG(op0, op1, crn, crm, op2) \
        ( ((op0 & 1) << 14) | \
          ((op1 & 7) << 11) | \
          ((crn & 15) << 7) | \
          ((crm & 15) << 3) | \
          ((op2 & 7) << 0) )
#define ARM64_PMCCNTR_EL0       ARM64_SYSREG(3,3, 9,13,0)  // Cycle Count Register [CP15_PMCCNTR]

static inline long long leoh_readtsc(void)
{
    return (long long)_ReadStatusReg(ARM64_PMCCNTR_EL0);
}
#  endif  /*(PROCESSOR_TYPE == PROC_TYPE_I386 || PROCESSOR_TYPE == PROC_TYPE_X64) || PROCESSOR_TYPE == PROC_TYPE_ARM64 */
#elif defined __SUNPRO_CC || defined __SUNPRO_C
#  if PROCESSOR_TYPE == PROC_TYPE_I386 || PROCESSOR_TYPE == PROC_TYPE_X64
static inline unsigned long long leoh_readtsc(void)
{
    unsigned int tickl, tickh;
    asm("rdtsc" : "=a" (tickl), "=d" (tickh));
    return ((unsigned long long)tickh << 32)|tickl;
}
#  else
unsigned long long leoh_readtsc(void);
#  endif	/* Solaris + SunStudio */
#elif defined(IOS)
# define NO_LEOH_READTSC 1
#elif (defined(UNIX) && defined(PROCESSOR_TYPE) && (PROCESSOR_TYPE == PROC_TYPE_ARM || PROCESSOR_TYPE == PROC_TYPE_ARM64 || PROCESSOR_TYPE == PROC_TYPE_MIPS32))
# define NO_LEOH_READTSC 1
#else

# if !defined NO_LEOH_READTSC
#   define NO_LEOH_READTSC 1
#   error "Unknown platform: Read TSC implementation not found"
# endif
#endif	/* конец перебора вариантов */

#ifdef __cplusplus
}
#endif

#endif /*SRDTSC_H*/
