#  /usr/local/bin/bash  
#!  /bin/bash  
## -*- sh -*-
## cloog2test for Cloog2Test
##
## Made by Louis-Noel Pouchet
## Contact: <louis-noel.pouchet@inria.fr>
##
## Started on  Tue May  2 14:30:19 2006 Louis-Noel Pouchet



## Set this to bash if you experience some troubles on old systems.
##
#SH_BINARY=/usr/local/bin/bash
SH_BINARY=/bin/bash




## Print the usage, and exit.
##
function usage()
{
    echo "Usage: cloog2test [options] filename";
    echo;
    echo "Filename must follow format XXX.cloog or XXX.c if -n option\
 is specified.";
    echo "A file named XXX.program must be located at the same place.";
    echo "Define CLOOG environment variable to set the path to CLooG.";
    echo "Options are:
-s	Add FIFO schedule (Linux only)
-c	Add cycle count (Linux only)
-t	Add time count
-n	No CLooG call
-k	Keep the Sed script
-v	Verbose output
-h	Print this help
-p ARG	Set input program file to ARG (no naming convention needed)
";
   exit 1;
}

## Read a string, and set program options.
##
function set_options()
{
    val="$1";
    count=1;
    opt=${val:$count:1}
    while [ "$opt" != "" ]; do
	case "$opt" in
	    s) FIFO_SCHEDULE="Y";;
	    c) CYCLE_COUNT="Y";;
	    t) TIME_COUNT="Y";;
	    n) NO_CLOOG="Y";;
	    k) KEEP_SED="Y";;
	    h) PRINT_HELP="Y";;
	    v) VERBOSE_OUTPUT="Y";;
	    p) INPUT_FILE="Y"; WITH_ARG="INPUT_FILE";
		count=$(($count + 1));
		opt=${val:$count:1}
		if ! [ -z "$opt" ]; then
		    usage;
		fi
		;;
	    *) echo "Unrecognized option: -$opt."; usage; exit 1;;
	esac;
	count=$(($count + 1));
	opt=${val:$count:1}
    done;
}


## Read the command line.
##
function read_parameters()
{
while [ $# -ne 0 ]; do
    case "$1" in
	-*) set_options "$1"
	    if ! [ -z "$WITH_ARG" ];
		then
		shift 1;
		eval $WITH_ARG="$1";
		WITH_ARG="";
	    fi
	    ;;
	 *) if ! [ -z "$FILENAME" ]; then
		usage;
	    fi
	    FILENAME="$1";
	    FILENAME_FULL="$1";
	    ;;
    esac;
    shift 1;
done;
}


## Remove the sed script if needed.
##
function rm_sed()
{
    if [ -z "$KEEP_SED" ]; then
	rm -f "$FILENAME.sed";
    fi
}


## Sed script generator. Reads the .program content, and create a sed
## script named FILE.sed.
##
function generate_parval()
{
echo "int PARVAL1=atoi(argv[1]);\\"
}

# ## Generate parameters.


function the_sed_script_generator()
{
TheSedScriptGenerator='
FILENAME="$1";

## Generate statements.
count=1
var=S$count;
value=`eval echo -e "$""$var"`;
value=`echo "$value" | sed -e "s/\\\\\//g"`;

while ! [ -z "$value" ]; do
    echo \
"/#define S$count(/ {
 c\\
$value
}" >> "$FILENAME.sed";
    count=$(($count + 1));
    var=S$count;
    value=`eval echo -e "$""$var"`;
    value=`echo "$value" | sed -e "s/\\\\\//g"`;
done;


## Generate remainder.
echo "
/#include <stdio.h>/ {
  i\\
#include <sys/time.h>\\
#include <sys/resource.h>\\
#include <time.h>\\
#ifdef LETSEE_MIPS_TARGET \\
# if defined(__mips) && defined(__GNUC__) \\
\\
# define PERFMON_TIMER_T unsigned long long \\
# define BILLION  1000000000L \\
\\
PERFMON_TIMER_T read_cycle_counter() \\
{ \\
    struct timespec s; \\
    clock_gettime(CLOCK_REALTIME, &s); \\
    return (PERFMON_TIMER_T)\\
    ((PERFMON_TIMER_T)((PERFMON_TIMER_T)(s.tv_sec)*BILLION)+s.tv_nsec); \\
} \\
# endif \\
#endif \\
#include <sched.h>\\
  `if [ -z "$INCLUDES" ]; \
	then echo "\\\\\";  \
	else echo -n "$INCLUDES"  | sed -e "s/\(.*\)/\1\\\\\/g"; \
   fi`

}

/#define PARVAL*/ {
d
}

/main(*/ {
  c\\
int main(int argc, char** argv) {\\
  `count=1;\
var=PARVAL$count;\
value=\`eval echo "$""$var"\`;\
while ! [ -z "$value" ]; do\
     echo "\\\\\";\
     count=$(($count + 1));\
     var=PARVAL$count;\
     value=\`eval echo "$""$var"\`;\
done;\
  if [ -z "$PROLOGUE" ]; \
	then echo "\\\\\";  \
	else echo -n "$PROLOGUE"  | sed -e "s/\(.*\)/\1\\\\\/g"; \
   fi`
'

if [ "$1" = "xY" ]; then
TheSedScriptGenerator="$TheSedScriptGenerator"'\\
#ifndef LETSEE_MIPS_TARGET \\
  /* Use FIFO scheduler to limit OS interference. */\\
  struct sched_param schedParam;\\
  schedParam.sched_priority = 99;\\
  sched_setscheduler(0, SCHED_FIFO, &schedParam);\\
#endif\\
'
fi

if [ "$3" = "xY" ]; then
TheSedScriptGenerator="$TheSedScriptGenerator"'\\
#ifndef LETSEE_MIPS_TARGET \\
  /* Initialize timer */\\
  double		time_start, time_stop;\\
  struct rusage	rusage;\\
\\
  getrusage(RUSAGE_SELF, &rusage);\\
  time_start = (double)(rusage.ru_utime.tv_sec);\\
  time_start = time_start + (double)(rusage.ru_utime.tv_usec) * 1.0e-06;\\
#endif\\
'
fi

if [ "$2" = "xY" ]; then
TheSedScriptGenerator="$TheSedScriptGenerator"'\\
#ifdef LETSEE_MIPS_TARGET \\
    PERFMON_TIMER_T cycles_start, cycles_stop; \\
    // Start cycle count. \\
      cycles_start = read_cycle_counter(); \\
#endif \\
#ifdef LETSEE_TRAVIATA \\
  struct timeval tv; \\
  struct timezone tz; \\
  gettimeofday (&tv, &tz); \\
  double time_start = (double) tv.tv_sec; \\
  time_start += (double) tv.tv_usec * 1.0e-6; \\
#else \\
# ifndef LETSEE_MIPS_TARGET \\
  /* Initialize the hardware clock cycle counter. */\\
  unsigned long long int cycle_start, cycle_stop;\\
  __asm__ volatile (\"RDTSC\" : \"=A\" (cycle_start));\\
# endif \\
#endif \\
  `if [ -z "$PREKERNEL" ]; \
	then echo "\\\\\";  \
	else echo -n "$PREKERNEL"  | sed -e "s/\(.*\)/\1\\\\\/g"; \
   fi`
'
fi

# if ! [ -z "$PREKERNEL" ]; then
#     tmp=`echo -n "$PREKERNEL"  | sed -e "s/\(.*\)/\1\\/g";`
# TheSedScriptGenerator="$TheSedScriptGenerator"'\\
# $tmp\\
# '    
# fi


TheSedScriptGenerator="$TheSedScriptGenerator"'\\

}
" >> "$FILENAME.sed";


echo "
/printf/ {
  d
}

/return/ {
  i\\
'
# if ! [ -z "$POSTKERNEL" ]; then
#     tmp=`echo -n "$POSTKERNEL"  | sed -e "s/\(.*\)/\1\\/g";`
# TheSedScriptGenerator="$TheSedScriptGenerator"'\\
# $tmp\\
# '    
# fi

if [ "$2" = "xY" ]; then
TheSedScriptGenerator="$TheSedScriptGenerator"'\\
  `if [ -z "$POSTKERNEL" ]; \
	then echo "\\\\\";  \
	else echo -n "$POSTKERNEL"  | sed -e "s/\(.*\)/\1\\\\\/g"; \
   fi`
#ifdef LETSEE_MIPS_TARGET \\
   cycles_stop = read_cycle_counter(); \\
  /* stop the hardware clock cycle counter. */\\
#endif \\
#ifdef LETSEE_TRAVIATA \\
   gettimeofday (&tv, &tz); \\
   double time_stop = (double) tv.tv_sec; \\
   time_stop += (double) tv.tv_usec * 1.0e-6; \\
   printf (\"Cycles: %d\\\\n\", (int)((time_stop - time_start) * 1.0e+06)); \\
#else \\
# ifndef LETSEE_MIPS_TARGET \\
   __asm__ volatile (\"RDTSC\" : \"=A\" (cycle_stop));\\
# endif \\
  /* Print the cycle count of the kernel. */\\
  printf(\"Cycles: %llu\\\\n\", cycle_stop - cycle_start);\\
#endif \\
'
fi

if [ "$3" = "xY" ]; then
TheSedScriptGenerator="$TheSedScriptGenerator"'\\
#ifndef LETSEE_MIPS_TARGET \\
  getrusage(RUSAGE_SELF, &rusage);\\
  time_stop = (double)(rusage.ru_utime.tv_sec);\\
  time_stop = time_stop + (double)(rusage.ru_utime.tv_usec) * 1.0e-06;\\
\\
  /* Print the time taken by the kernel. */\\
  printf(\"Time:   %fs\\\\n\", time_stop - time_start);\\
#endif \\
'
fi

TheSedScriptGenerator="$TheSedScriptGenerator"'\\
  `if [ -z "$EPILOGUE" ]; \
	then echo "\\\\\";  \
	else echo -n "$EPILOGUE"  | sed -e "s/\(.*\)/\1\\\\\/g" | grep -v "^n"; \
   fi`

}" >> "$FILENAME.sed";
'
}


function main()
{
    read_parameters "$@";
    if [ -z "$FILENAME" ] || ! [ -z "$PRINT_HELP" ]; then usage; fi;

    ## Set appropriate filename.
    if [ -z "$NO_CLOOG" ]; then
	FILENAME=${FILENAME%.cloog};
    else
	FILENAME=${FILENAME%.c};
    fi

    ## Exit if filename extension is invalid.
    if [ "$FILENAME" = "$FILENAME_FULL" ]; then
	usage;
    fi

    ## Check if a file named FILE.program exists.
    ##
    if ! [ -f "$FILENAME.program" ] && [ -z "$INPUT_FILE" ]; then
      echo -e "\033[31mA file named \"$FILENAME.program\" is expected.\033[0m";
      exit 1;
    fi

    ## Set CLooG binary name.
    if [ -z "$CLOOG" ]; then
	CLOOG=cloog;
    fi

    ## Generate the sed script.
    the_sed_script_generator "x$FIFO_SCHEDULE" "x$CYCLE_COUNT" "x$TIME_COUNT";
    if [ -z "$INPUT_FILE" ]; then
	head=`cat "$FILENAME.program"`;
    else
	head=`cat "$INPUT_FILE"`;
    fi
    echo "$head\n$TheSedScriptGenerator" | "$SH_BINARY" -s "$FILENAME";

    if ! [ -z "$VERBOSE_OUTPUT" ]; then
	echo -e "\033[36mProcessing file $FILENAME_FULL.\033[0m";
    fi

    ## Run CLooG.
   if [ -z "$NO_CLOOG" ]; then
	if ! [ -z "$VERBOSE_OUTPUT" ]; then
 	    $CLOOG "$FILENAME_FULL" -o "$FILENAME.c";
 	else
 	    $CLOOG "$FILENAME_FULL" -o "$FILENAME.c" &>/dev/null;
 	fi
 	if [ $? -ne 0 ]; then
 	    $CLOOG  "$FILENAME_FULL" -o "$FILENAME.c";
 	    echo -e "\033[31mFile $FILENAME_FULL not processed by CLooG\
  (an error occured).\033[0m";
 	    rm_sed;
 	    exit 1;
 	fi
	if ! [ -z "$VERBOSE_OUTPUT" ]; then
	    echo -e "\033[32mFile $FILENAME.c generated by CLooG.\033[0m";
	fi
   fi;

    ## Run the sed script.
    tmp_file=`mktemp /tmp/cloog2test.XXXXXX`
    if [ $? -ne 0 ]; then echo "mktemp error."; exit 1; fi
    sed -f "$FILENAME.sed" "$FILENAME.c" > "$tmp_file";
    if [ $? -ne 0 ]; then
	echo -e "\033[31mFile $FILENAME_FULL not modified (an error\
 occured).\033[0m";
	rm_sed;
	rm -f "$tmp_file";
	exit 1;
    fi
    mv "$tmp_file" "$FILENAME.c";
    if ! [ -z "$VERBOSE_OUTPUT" ]; then
    echo -e "\033[32mFile $FILENAME.c modified with $FILENAME.program.\033[0m";
    fi
    ## Be clean.
    rm_sed;
    rm -f "$tmp_file";
}


## Call the main.
main "$@"
