#!/bin/sh

# "bench", a shell script to benchmark Scheme implementations
#
# Please report any errors or extensions to the author:
#
#   Marc Feeley (feeley@iro.umontreal.ca)
#
# The following have contributed to this benchmark suite:
#
#   Harvey Stein (abel@netvision.net.il)

# -----------------------------------------------------------------------------

error ()
{
  echo $1
  echo '
Usage: bench [-r runs] [-c clean] [-i iterfile] <scheme-systems> <benchmarks>

<scheme-systems> is the abbreviated name of one or more
language implementations to use, i.e.:

  bigloo           for Bigloo.
  bigloo-int       for Bigloo (interpreted).
  chez             for Chez Scheme.
  petite-chez      for Petite Chez Scheme.
  elk              for Elk.
  gambit           for Gambit-C.
  gambit-int       for Gambit-C (interpreted).
  guile            for Guile.
  hobbit           for '"SCM's"' hobbit compiler.
  mit              for MIT-Scheme.
  psi              for PSI.
  scheme2c         for scheme2c.
  scm              for SCM.
  scsh             for scsh.
  stalin           for Stalin.
  stk              for stk (actually, snow = STk No Windows).
  rs               for rscheme.
  rs-int           for rscheme (interpreted).
  umb-scheme       for umb-scheme.
  awk              for AWK.
  cc               for '"cc"' C compiler.
  gcc              for '"gcc"' C compiler.
  java             for Java.
  all              for all of the above.
  all-interpreters for the above interpreters.
  all-compilers    for the above compilers.

<benchmarks> is the name of one or more benchmarks
to use, i.e.:

  all         for all the benchmarks
  fib         for the fib benchmark
  "fib boyer" for fib & boyer.

runs is the number of times to run each benchmark (default is 1).

clean is whether or not to clean out the build directory.
true = clean.  Useful for testing or inspection.

iterfile is the file which specifies the number of iterations in
each benchmark.  If not supplied, we use num-iters.scm for compilers
and num-iters-int.scm for interpreters.  For testing, you might
want to use one-iter.scm, which runs each test once.'
  exit
}

# -----------------------------------------------------------------------------

cleanup ()
{
  if [ "$clean" = "true" ] ; then
     # It's true that technically speaking, we should be in the build
     # directory when this fcn is called.  Thus, we should be able to
     # just do rm *.  However, that's kind of dangerous, so instead,
     # we delete files newer than the mark file that evaluate () makes.

     for x in * ; do
        if [ $x -nt clean_newer_than_me ] ; then
          rm $x
        fi
     done
  fi
  rm clean_newer_than_me
}

evaluate ()
{
  echo > clean_newer_than_me
  sleep 1
  {
  echo
  echo Testing $1 under $NAME
  echo Compiling...
  make_src_code $1
  $COMP $1
  i=0
  while [ "$i" -lt "$NB_RUNS" ]
  do
    echo Running...
    $EXEC $1
    i=`expr $i + 1`
  done
  cleanup
  } 2>&1 | tee -a ../../results.${NAME}

}

make_src_code ()
{
  case "$extension" in
     ".awk")
       cat ../../src/$1.awk > $1.awk
       chmod +x $1.awk ;;
     ".c")
       cat ../../src/$1.c > $1.c ;;
     ".java")
       cat ../../src/$1.java > $1.java ;;
     ".scm")
        cat ../../src/prefix-${system}.scm ../../src/${iterfile} ../../src/$1.scm ../../src/suffix-${system}.scm > $1.scm ;;
  esac
}

# -----------------------------------------------------------------------------
# Definitions specific to Gambit-C compiler

gambit_comp ()
{
  {
     echo LD_LIBRARY_PATH=../../../lib GAMBCDIR=../../../lib ../../../gsc/gsc $1.scm
     echo gcc -I../../../include -fomit-frame-pointer -O1 -D___SINGLE_HOST -o $1 $1.c $1_.c ../../../lib/libgambc.a -lm -ldl -lncurses -lutil
  } | time sh
  ls -l $1
}

gambit_exec ()
{
   LD_LIBRARY_PATH=../../../lib time ./$1 -:m10000
}

# -----------------------------------------------------------------------------
# Definitions specific to Gambit-C interpreter

gambit_int_comp ()
{
  :
}

gambit_int_exec ()
{
   LD_LIBRARY_PATH=../../../lib time ../../../gsi/gsi -:m10000 ./$1.scm
#   LD_LIBRARY_PATH=../../../lib time ../../../../bin/i486-linux/gsi -:m10000 ./$1.scm
#   LD_LIBRARY_PATH=../../../lib time ../../../../gambc30/gsi/gsi -:d2 ./$1.scm
}

# -----------------------------------------------------------------------------
# Definitions specific to Chez Scheme

chez_comp ()
{
  :
}

chez_exec ()
{
  echo "(load \"$1.scm\")" | time scheme
}

# -----------------------------------------------------------------------------
# Definitions specific to Petite Chez

petite_chez_comp ()
{
  :
}

petite_chez_exec ()
{
  echo "(load \"$1.scm\")" | time ../../../../csv6.0a/bin/i3le/petite
}

# -----------------------------------------------------------------------------
# Definitions specific to bigloo compiler

bigloo_comp ()
{
  case $1 in
     ctak|maze|puzzle) usecc="-call/cc" ;;
     *)                usecc=""         ;;
  esac
  time bigloo -O6 -farithmetic -unsafeatrsv $usecc -copt -O2 $1.scm -o $1
  ls -l $1
}

bigloo_exec ()
{
  time ./$1
}

# -----------------------------------------------------------------------------
# Definitions specific to bigloo interpreter

bigloo_int_comp ()
{
  :
}

bigloo_int_exec ()
{
  echo "(load \"$1.scm\")" | time bigloo
}

# -----------------------------------------------------------------------------
# Definitions specific to stalin

stalin_comp ()
{
  mv $1.scm $1.sc
  time stalin -copt -O2 -Ob -Om -On -Or -Ot -s $1
}

stalin_exec ()
{
  time ./$1
}

# -----------------------------------------------------------------------------
# Definitions specific to scm

scm_comp ()
{
  :
}

scm_exec ()
{
  time scm -f $1.scm
}

# -----------------------------------------------------------------------------
# Definitions specific to STk

stk_comp ()
{
  :
}

stk_exec ()
{
  ### Need to send in /dev/null so that it doesn't hang when there's
  ### an error...
  time snow -f $1.scm < /dev/null
}

# -----------------------------------------------------------------------------
# Definitions specific to AWK

awk_comp ()
{
  :
}

awk_exec ()
{
   time ./$1.awk
}

# -----------------------------------------------------------------------------
# Definitions specific to CC

cc_comp ()
{
  {
     echo cc -O4 -o $1 $1.c -lm -lpthread
  } | time sh
  ls -l $1
}

cc_exec ()
{
   time ./$1
}

# -----------------------------------------------------------------------------
# Definitions specific to GCC

gcc_comp ()
{
  {
     echo gcc -O4 -o $1 $1.c -lm -lpthread
  } | time sh
  ls -l $1
}

gcc_exec ()
{
   time ./$1
}

# -----------------------------------------------------------------------------
# Definitions specific to Java

java_comp ()
{
  {
     echo javac $1.java
  } | time sh
  ls -l $1.class
}

java_exec ()
{
   time java $1
}

# -----------------------------------------------------------------------------

BOYER_BENCHMARKS="boyer browse cpstak ctak dderiv deriv destruc diviter divrec fft puzzle tak takl trav1 trav2 triangl"

KVW_BENCHMARKS="ack array1 cat string sum1 sumloop tail wc"

OTHER_BENCHMARKS="conform earley fib fibfp maze mazefun mbrot nucleic paraffins peval pi pnpoly ray scheme simplex slatex sum sumfp tfib"

AWK_BENCHMARKS="$KVW_BENCHMARKS"

C_BENCHMARKS="fft fib fibfp mbrot nucleic pnpoly sum sumfp tak tfib"

JAVA_BENCHMARKS="tfib"

ALL_BENCHMARKS="succeed fail crash $BOYER_BENCHMARKS $KVW_BENCHMARKS $OTHER_BENCHMARKS"

ALL_INTERPRETERS='bigloo-int petite-chez gambit-int scm stk'
ALL_COMPILERS='bigloo chez gambit stalin'
ALL_NON_SCHEME='awk cc gcc java'
ALL_SYSTEMS="$ALL_COMPILERS $ALL_INTERPRETERS $ALL_NON_SCHEME"

## Arg processing...
if [ "$#" -lt 2 ]; then
  error '>>> At least two command line arguments are needed'
fi


cmdline="$0"
flagsdone=0

NB_RUNS=1
clean=true

while [ $# -gt 2 ] ; do
   arg="$1"
   shift
   case $arg in
      -r) NB_RUNS=$1    ; shift ;;
      -c) clean=$1      ; shift ;;
      -i) forceiters=$1 ; shift ;;
       *) error ">>> Unknown argument of $arg given." ;;
   esac
done

if [ "$#" -ne 2 ]; then
  error '>>> Last two arguments must be <systems> and <benchmarks>'
fi

case "$1" in
   all)              systems="$ALL_SYSTEMS" ;;
   all-interpreters) systems="$ALL_INTERPRETERS" ;;
   all-compilers)    systems="$ALL_COMPILERS" ;;
   *)                systems="$1" ;;
esac

case "$2" in
   all)   benchmarks="$ALL_BENCHMARKS" ;;
   boyer) benchmarks="$BOYER_BENCHMARKS" ;;
   kvw)   benchmarks="$KVW_BENCHMARKS" ;;
   other) benchmarks="$OTHER_BENCHMARKS" ;;
   awk)   benchmarks="$AWK_BENCHMARKS" ;;
   c)     benchmarks="$C_BENCHMARKS" ;;
   java)  benchmarks="$JAVA_BENCHMARKS" ;;
   *)     benchmarks="$2" ;;
esac

## Run each benchmark under each system...
for system in $systems ; do

   case "$system" in

    bigloo) NAME='Bigloo'
            COMP=bigloo_comp
            EXEC=bigloo_exec
            iterfile=num-iters.scm
            extension=".scm"
            ;;

bigloo-int) NAME='Bigloo-int'
            COMP=bigloo_int_comp
            EXEC=bigloo_int_exec
            iterfile=num-iters-int.scm
            extension=".scm"
            ;;

      chez) NAME='Chez-Scheme'
            COMP=chez_comp
            EXEC=chez_exec
            iterfile=num-iters-int.scm
            extension=".scm"
            ;;

petite-chez) NAME='Petite-Chez-Scheme'
             COMP=petite_chez_comp
             EXEC=petite_chez_exec
             iterfile=num-iters-int.scm
             extension=".scm"
             ;;

    gambit) NAME='Gambit-C'
            COMP=gambit_comp
            EXEC=gambit_exec
            iterfile=num-iters.scm
            extension=".scm"
            ;;

gambit-int) NAME='Gambit-C-int'
            COMP=gambit_int_comp
            EXEC=gambit_int_exec
            iterfile=num-iters-int.scm
            extension=".scm"
            ;;

       mit) NAME='MIT-Scheme'
            COMP=mit_comp
            EXEC=mit_exec
            iterfile=num-iters.scm
            extension=".scm"
            ;;

   mit-int) NAME='MIT-Scheme-int'
            COMP=mit_int_comp
            EXEC=mit_int_exec
            iterfile=num-iters-int.scm
            extension=".scm"
            ;;

       scm) NAME='SCM'
            COMP=scm_comp
            EXEC=scm_exec
            iterfile=num-iters-int.scm
            extension=".scm"
            ;;

    stalin) NAME='Stalin'
            COMP=stalin_comp
            EXEC=stalin_exec
            iterfile=num-iters.scm
            extension=".scm"
            ;;

       stk) NAME='STk'
            COMP=stk_comp
            EXEC=stk_exec
            iterfile=num-iters-int.scm
            extension=".scm"
            ;;

       awk) NAME='AWK'
            COMP=awk_comp
            EXEC=awk_exec
            iterfile=
            extension=".awk"
            ;;

        cc) NAME='CC'
            COMP=cc_comp
            EXEC=cc_exec
            iterfile=
            extension=".c"
            ;;

       gcc) NAME='GCC'
            COMP=gcc_comp
            EXEC=gcc_exec
            iterfile=
            extension=".c"
            ;;

      java) NAME='Java'
            COMP=java_comp
            EXEC=java_exec
            iterfile=
            extension=".java"
            ;;

         *) error '>>> Unknown system'
            ;;
   esac

   if [ -n "$forceiters" ] ; then iterfile="$forceiters" ; fi

   cd sys/$system
   if [ $? != 0 ] ; then
      echo "ERROR: Can't change to directory sys/$system."
      exit 1
   fi

   {
      echo
      echo '****************************'
      echo Benchmarking $NAME on `date` under `uname -a`
   } >> ../../results.${NAME}

   for program in $benchmarks ; do
      evaluate $program
   done
   cd ../..
   if [ $? != 0 ] ; then
      echo "ERROR: Can't change back to benchmark directory."
      exit 1
   fi
done
