/*
 * Copyright (C) 2002 David Defour, Catherine Daramy, and Florent de Dinechin
 *
 * This file is part of scslib, the Software Carry-Save multiple-precision
 * library, which has been developed by the Arénaire project at École normale
 * supérieure de Lyon.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */


#ifndef TBX_TIMING_H
#define TBX_TIMING_H

#include <sys/types.h>
#include <sys/time.h>
#if defined(CRLIBM_TYPECPU_ITANIUM) && defined(__INTEL_COMPILER)
#include<ia64intrin.h>
#endif

/*
 * For the user interested in High resolution timer, to my knowledge 
 * the best library is "RTAI", unfortunately it works only on Linux OS
 */


typedef union u_tbx_tick
{
  unsigned long long tick;

  struct
  {
    unsigned long low;
    unsigned long high;
  } sub;

  struct timeval timev;
} tbx_tick_t, *p_tbx_tick_t;


#if defined(CRLIBM_TYPECPU_ITANIUM) && defined(__GNUC__) && !defined(__INTEL_COMPILER)
 
#define TBX_GET_TICK(t) \
   __asm__ __volatile__("mov %0=ar.itc" : "=r"((t).tick) :: "memory")
#define TBX_TICK_RAW_DIFF(t1, t2)    ((t2).tick - (t1).tick)


#elif defined(CRLIBM_TYPECPU_ITANIUM) && defined(__HPCC__)
/* Never tested, currently nobody defines __HPCC__.
  It should work on HPUX machines using HP compiler */
#include<machine/sys/inline.h>/* to move up of course */
#define TBX_GET_TICK(t) \
   t.tick=_Asm_mov_from_ar(_AREG_ITC)
#define TBX_TICK_RAW_DIFF(t1, t2)    ((t2).tick - (t1).tick )


#elif defined(CRLIBM_TYPECPU_ITANIUM) && defined(__INTEL_COMPILER)
#define TBX_GET_TICK(t)    t.tick=__getReg(_IA64_REG_AR_ITC)   
#define TBX_TICK_RAW_DIFF(t1, t2)    ((t2).tick - (t1).tick)



#elif (defined(CRLIBM_TYPECPU_AMD64) || defined(CRLIBM_TYPECPU_X86)) \
    && defined(__GNUC__) && !defined(__INTEL_COMPILER)
#define TBX_GET_TICK(time)                \
        __asm__ __volatile__(             \
                "xorl %%eax,%%eax\n\t"    \
                "cpuid\n\t"               \
                "rdtsc\n\t"               \
                "movl %%eax,(%0)\n\t"     \
                "movl %%edx,4(%0)\n\t"    \
                "xorl %%eax,%%eax\n\t"    \
                "cpuid\n\t"               \
                : /* no output */         \
                : "S"(&time.tick)         \
                : "eax", "ebx", "ecx", "edx", "memory")
#define TBX_TICK_RAW_DIFF(t1, t2)    ((t2).tick - (t1).tick)


#elif defined(CRLIBM_TYPECPU_ALPHA)  && defined(__GNUC__)
#define TBX_GET_TICK(t) \
   __asm__ volatile("rpcc %0\n\t" : "=r"((t).tick))
#define TBX_TICK_RAW_DIFF(t1, t2) \
   (((t2).tick & 0xFFFFFFFF) - ((t1).tick & 0xFFFFFFFF))


#elif defined(CRLIBM_TYPECPU_SPARC)  && defined(__GNUC__)
#define TBX_GET_TICK(t) \
    (t).tick = gethrtime()
#define TBX_TICK_RAW_DIFF(t1, t2) \
   ((t2).tick  - (t1).tick)



#else  /* generic case */

#define TIMING_USES_GETTIMEOFDAY 1  /* inform the test functions that timings are inaccurate */
#define TBX_GET_TICK(t) \
   gettimeofday(&(t).timev, 0)
#define TBX_TICK_RAW_DIFF(t1, t2) \
   ((t2.timev.tv_sec * 1000000L + t2.timev.tv_usec) - \
    (t1.timev.tv_sec * 1000000L + t1.timev.tv_usec))

#endif

#define TBX_TICK_DIFF(t1, t2) (TBX_TICK_RAW_DIFF(t1, t2) - tbx_residual + 1)
#define TBX_TIMING_DELAY(t1, t2) tbx_tick2usec(TBX_TICK_DIFF(t1, t2))

extern unsigned long long tbx_residual;
extern tbx_tick_t         tbx_new_event;
extern tbx_tick_t         tbx_last_event;

#endif /* TBX_TIMING_H */
