/* Compile me with -O0 or else you'll get none. */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/time.h>

#include "do_loops.h"

volatile int buf[CACHE_FLUSH_BUFFER_SIZE_INTS];
volatile int buf_dummy = 0;
volatile int *flush = NULL;
volatile int flush_dummy = 0;
volatile double a = 0.5, b = 2.2;

void
do_reads( int n )
{
	int i, retval;
	static int fd = -1;
	char buf;

	if ( fd == -1 ) {
		fd = open( "/dev/zero", O_RDONLY );
		if ( fd == -1 ) {
			perror( "open(/dev/zero)" );
			exit( 1 );
		}
	}

	for ( i = 0; i < n; i++ ) {
		retval = ( int ) read( fd, &buf, sizeof ( buf ) );
		if ( retval != sizeof ( buf ) ) {
			if ( retval < 0 )
				perror( "/dev/zero cannot be read" );
			else
				fprintf( stderr,
						 "/dev/zero cannot be read: only got %d bytes.\n",
						 retval );
			exit( 1 );
		}
	}
}

void
fdo_reads( int *n )
{
	do_reads( *n );
}

void
fdo_reads_( int *n )
{
	do_reads( *n );
}

void
fdo_reads__( int *n )
{
	do_reads( *n );
}

void
FDO_READS( int *n )
{
	do_reads( *n );
}

void
_FDO_READS( int *n )
{
	do_reads( *n );
}

void
do_flops( int n )
{
	int i;
	double c = 0.11;

	for ( i = 0; i < n; i++ ) {
		c += a * b;
	}
	dummy( ( void * ) &c );
}

void
fdo_flops( int *n )
{
	do_flops( *n );
}

void
fdo_flops_( int *n )
{
	do_flops( *n );
}

void
fdo_flops__( int *n )
{
	do_flops( *n );
}

void
FDO_FLOPS( int *n )
{
	do_flops( *n );
}

void
_FDO_FLOPS( int *n )
{
	do_flops( *n );
}

void
do_misses( int n, int bytes )
{
	register int i, j, tmp = buf_dummy, len = bytes / ( int ) sizeof ( int );
	dummy( ( void * ) buf );
	dummy( ( void * ) &buf_dummy );
	assert( len <= CACHE_FLUSH_BUFFER_SIZE_INTS );
	for ( j = 0; j < n; j++ ) {
		for ( i = 0; i < len; i++ ) {
			/* We need to read, modify, write here to look
			   out for the write allocate policies. */
			buf[i] += tmp;
			/* Fake out some naive prefetchers */
			buf[len - 1 - i] -= tmp;
		}
		tmp += len;
	}
	buf_dummy = tmp;
	dummy( ( void * ) buf );
	dummy( ( void * ) &buf_dummy );
}

void
fdo_misses( int *n, int *size )
{
	do_misses( *n, *size );
}

void
fdo_misses_( int *n, int *size )
{
	do_misses( *n, *size );
}

void
fdo_misses__( int *n, int *size )
{
	do_misses( *n, *size );
}

void
FDO_MISSES( int *n, int *size )
{
	do_misses( *n, *size );
}

void
_FDO_MISSES( int *n, int *size )
{
	do_misses( *n, *size );
}

void
do_flush( void )
{
	register int i;
	if ( flush == NULL )
		flush = ( int * ) malloc( ( 1024 * 1024 * 16 ) * sizeof ( int ) );
	if ( !flush )
		return;

	dummy( ( void * ) flush );
	for ( i = 0; i < ( 1024 * 1024 * 16 ); i++ ) {
		flush[i] += flush_dummy;
	}
	flush_dummy++;
	dummy( ( void * ) flush );
	dummy( ( void * ) &flush_dummy );
}

void
fdo_flush( void )
{
	do_flush(  );
}

void
fdo_flush_( void )
{
	do_flush(  );
}

void
fdo_flush__( void )
{
	do_flush(  );
}

void
FDO_FLUSH( void )
{
	do_flush(  );
}

void
_FDO_FLUSH( void )
{
	do_flush(  );
}

void
do_l1misses( int n )
{
	do_misses( n, L1_MISS_BUFFER_SIZE_INTS );
}

void
fdo_l1misses( int *n )
{
	do_l1misses( *n );
}

void
fdo_l1misses_( int *n )
{
	do_l1misses( *n );
}

void
fdo_l1misses__( int *n )
{
	do_l1misses( *n );
}

void
FDO_L1MISSES( int *n )
{
	do_l1misses( *n );
}

void
_FDO_L1MISSES( int *n )
{
	do_l1misses( *n );
}

void
do_stuff( void )
{
	static int loops = 0;

	if ( loops == 0 ) {
		struct timeval now, then;
		gettimeofday( &then, NULL );
		do {
			do_flops( NUM_FLOPS );
			do_reads( NUM_READS );
			do_misses( 1, 1024 * 1024 );
			gettimeofday( &now, NULL );
			loops++;
		} while ( now.tv_sec - then.tv_sec < NUM_WORK_SECONDS );
	} else {
		int i = 0;
		do {
			do_flops( NUM_FLOPS );
			do_reads( NUM_READS );
			do_misses( 1, 1024 * 1024 );
			i++;
		} while ( i < loops );
	}
}

void
do_stuff_( void )
{
	do_stuff(  );
}

void
do_stuff__( void )
{
	do_stuff(  );
}

void
DO_STUFF( void )
{
	do_stuff(  );
}

void
_DO_STUFF( void )
{
	do_stuff(  );
}

void
dummy( void *array )
{
/* Confuse the compiler so as not to optimize
   away the flops in the calling routine    */
/* Cast the array as a void to eliminate unused argument warning */
	( void ) array;
}

void
dummy_( void *array )
{
	( void ) array;
}

void
dummy__( void *array )
{
	( void ) array;
}

void
DUMMY( void *array )
{
	( void ) array;
}

void
_DUMMY( void *array )
{
	( void ) array;
}

/* We have to actually touch the memory to confuse some
 * systems, so they actually allocate the memory.
 * -KSL
 */
void
touch_dummy( double *array, int size )
{
	int i;
	double *tmp = array;
	for ( i = 0; i < size; i++, tmp++ )
		*tmp = ( double ) rand(  );
}
