#!/bin/sh

# compile a C source program in $1.c to native binary for target machine

if [ $2 = object ]; then
  USE_SHMALL_OBJECT=yes
else # vm
  USE_SHMALL_OBJECT=no
fi

STACK_SIZE=$3

MACHINE=`uname -m`
if [ $MACHINE = i686 -o $MACHINE = i586 ]; then
  OS=`uname -o 2>/dev/null`
  if [ $? != 0 ]; then # -o option unrecognized
    OS=unknown
  fi
  if [ ! $OS = Cygwin ]; then
    OS=`uname -s`
  fi 
else
  OS=`uname -s`
fi

echo $MACHINE $OS

if [ $OS = OpenBSD -o $OS = FreeBSD ]; then
  REVERSE="tail -r"
else
  REVERSE=tac
fi

cp $1.c x.c

if [ $MACHINE = i686 -o $MACHINE = i586 -o $MACHINE = i386 -o $MACHINE = x86_64 -o $MACHINE = amd64 ]; then
  if [ $USE_SHMALL_OBJECT = yes ]; then
    if [ -f bin/shmall ]; then
      cat x.c | bin/shmall >xx.scm
      #filter out forward function definitions
      cat xx.scm | $REVERSE | sed -e '/^__forward/{N;N;N;d;}' | \
        $REVERSE >xxxxx.scm
      mv xxxxx.scm xx.scm
      echo x86 >compilation-mode.txt
      if [ -f bin/schant-int ]; then
        cat bin/schimper.txt compilation-mode.txt xx.scm \
          | bin/schant-int \
          >core.asm
      else
        cat bin/schant-int.vm.txt bin/schimper.txt compilation-mode.txt xx.scm \
          | bin/simpvir \
          >core.asm
      fi
      rm xx.scm
    else
      echo "shmall binary does not exist"
      exit 0
    fi
  else
    cat bin/shmall-vm.txt x.c | \
      bin/simpvir >xx.scm
    #filter out forward function definitions
    cat xx.scm | $REVERSE | sed -e '/^__forward/{N;N;N;d;}' | \
      $REVERSE >xxxxx.scm
    mv xxxxx.scm xx.scm
    echo x86 >compilation-mode.txt
    cat bin/schant-int.vm.txt bin/schimper.txt compilation-mode.txt xx.scm \
      | bin/simpvir \
      >core.asm
    rm xx.scm
  fi
  rm x.c

  # substitute fast versions of memcmp32, memset32 and memcpy32 if they exist

  grep -n __memcmp32 core.asm | cut -f1 -d: >grep.out
  if [ -s grep.out ]; then
    line1=$(head -1 grep.out)
    line2=$(tail -1 grep.out)
    cat core.asm | sed "$line1","$line2"d >xx.asm
    cat xx.asm rt/rt-x86-src/memcmp32.asm >core.asm
    echo "memcmp32 compiled source code replaced by fast assembly" $line1 $line2
  fi

  grep -n __memcpy32 core.asm | cut -f1 -d: >grep.out
  if [ -s grep.out ]; then
    line1=$(head -1 grep.out)
    line2=$(tail -1 grep.out)
    cat core.asm | sed "$line1","$line2"d >xx.asm
    cat xx.asm rt/rt-x86-src/memcpy32.asm >core.asm
    echo "memcpy32 compiled source code replaced by fast assembly" $line1 $line2
  fi

  grep -n __memset32 core.asm | cut -f1 -d: >grep.out
  if [ -s grep.out ]; then
    line1=$(head -1 grep.out)
    line2=$(tail -1 grep.out)
    cat core.asm | sed "$line1","$line2"d >xx.asm
    cat xx.asm rt/rt-x86-src/memset32.asm >core.asm
    echo "memset32 compiled source code replaced by fast assembly" $line1 $line2
  fi

  rm -f xx.asm grep.out

  # set stack size. the default size is 48Mb for Schant compilation. Override it

  sed -e "s/STACK_SIZE 21000000/STACK_SIZE $STACK_SIZE/g" \
    rt/rt-x86-src/rt.asm >rt.asm

  if [ $OS = FreeBSD ]; then
    RT_OS=rt/rt-x86-src/rt-freebsd64.asm
  elif [ $OS = OpenBSD ]; then
    RT_OS=rt/rt-x86-src/rt-openbsd32.asm
  elif [ $OS = Cygwin ]; then # windows
    RT_OS=rt/rt-x86-src/rt-win.asm
  elif [ $OS = Darwin ]; then # Mac OS X
    RT_OS=rt/rt-x86-src/rt-osx32.asm
  else # GNU/Linux
    RT_OS=rt/rt-x86-src/rt-linux.asm
  fi
  cat rt/rt-x86-src/rt-comment.asm \
      $RT_OS \
      rt.asm \
      core.asm \
      >$1.asm

  if [ $MACHINE = i686 -o $MACHINE = i586 -o $MACHINE = i386 ]; then # 32 bit
    sed -e 's/rax/eax/g' \
        -e 's/rsp/esp/g' \
      <$1.asm >x.asm
    mv x.asm $1.asm
    nasm -f elf32 -o $1.o $1.asm
    if [ $OS = Cygwin ]; then # windows
      gcc -o $1 $1.o
    elif [ $OS = OpenBSD ]; then
      ld.bfd -nopie -static -o $1 $1.o
    else # GNU/Linux or FreeBSD
      ld -s -o $1 $1.o
    fi
  else # x86_64
    if [ $OS = Darwin ]; then
      sed -e 's/rax/eax/g' \
          -e 's/rsp/esp/g' \
        <$1.asm >x.asm
      mv x.asm $1.asm
      ~/Downloads/nasm-2.11.08/nasm -f macho $1.asm -o $1.o
      ld -o $1 $1.o
    else
      sed -e 's/push  eax/push  rax/g' \
          -e 's/pop   eax/pop   rax/g' \
          -e 's/push  ebx/push  rbx/g' \
          -e 's/pop   ebx/pop   rbx/g' \
          -e 's/push  ecx/push  rcx/g' \
          -e 's/pop   ecx/pop   rcx/g' \
          -e 's/push  edx/push  rdx/g' \
          -e 's/pop   edx/pop   rdx/g' \
          -e 's/push  edi/push  rdi/g' \
          -e 's/pop   edi/pop   rdi/g' \
          -e 's/push  esi/push  rsi/g' \
          -e 's/pop   esi/pop   rsi/g' \
          -e 's/rsp + 16/rsp + 32/g' \
          -e 's/rsp + 12/rsp + 24/g' \
          -e 's/rsp + 8/rsp + 16/g' \
          -e 's/rsp + 4/rsp + 8/g' \
          -e 's/rsp, 4/rsp, 8/g' \
          -e 's/rsp, 20/rsp, 40/g' \
        <$1.asm >x.asm
      mv x.asm $1.asm
      nasm -f elf64 -o $1.o $1.asm
      if [ $OS = FreeBSD ]; then
        cc -o $1 $1.o # resolve calls to libc
      else
        ld -s -o $1 $1.o
      fi
    fi
  fi

  rm rt.asm core.asm $1.asm $1.o
elif [ $MACHINE = armv6l ] || [ $MACHINE = armv7l ]; then

  # set stack size. the default size is 48Mb for Schant compilation. Override it

  FOUR_TIMES_STACK_SIZE=`expr 4 \* $STACK_SIZE`
  sed -e "s/84000000/$FOUR_TIMES_STACK_SIZE/g" \
      -e "s/21000000/$STACK_SIZE/g" \
    rt/rt-arm-src/data.s >data.s

  if [ $MACHINE = armv6l ]; then
    ARM_MODE=arm6
    VERSION=v6
  else
    ARM_MODE=arm7
    VERSION=v7
  fi

  if [ $USE_SHMALL_OBJECT = yes ]; then
    if [ -f bin/shmall ]; then
      cat x.c | \
        bin/shmall >xx.scm
      #filter out forward function definitions
      cat xx.scm | $REVERSE | sed -e '/^__forward/{N;N;N;d;}' | \
        $REVERSE >xxxxx.scm
      mv xxxxx.scm xx.scm
      if [ $1 = shmall ]; then # TODO make poolgap an arg to the shell
        POOLGAP=250 # TODO probably only needed when v6
      else
        POOLGAP=350
      fi
      echo "arm $VERSION $POOLGAP" >compilation-mode.txt
      if [ -f bin/schant-int ]; then
        cat bin/schimper.txt compilation-mode.txt xx.scm \
          | bin/schant-int >xxx.s
      else
        cat bin/schant-int.vm.txt bin/schimper.txt compilation-mode.txt xx.scm \
          | bin/simpvir \
          >xxx.s
      fi
      rm xx.scm
    else
      echo "shmall binary does not exist"
      exit 0
    fi
  else
    cat bin/shmall-vm.txt x.c | \
      bin/simpvir >xx.scm
    #filter out forward function definitions
    cat xx.scm | $REVERSE | sed -e '/^__forward/{N;N;N;d;}' | \
      $REVERSE >xxxxx.scm
    mv xxxxx.scm xx.scm
    if [ $1 = shmall ]; then # TODO make poolgap an arg to the shell
      POOLGAP=250 # TODO probably only needed when v6
    else
      POOLGAP=350
    fi
    echo "arm $VERSION $POOLGAP" >compilation-mode.txt
    cat bin/schant-int.vm.txt bin/schimper.txt compilation-mode.txt xx.scm \
      | bin/simpvir \
      >xxx.s
    rm xx.scm
  fi

  # substitute fast versions of memcmp32, memset32 and memcpy32 if they exist

  grep -n __memcmp32 xxx.s | cut -f1 -d: >grep.out
  if [ -s grep.out ]; then
    line1=$(head -1 grep.out)
    line2=$(tail -1 grep.out)
    cat xxx.s | sed "$line1","$line2"d >xx.s
    cat xx.s rt/rt-arm-src/memcmp32.s >xxx.s
    echo "memcmp32 compiled source code replaced by fast assembly" $line1 $line2
  fi

  grep -n __memcpy32 xxx.s | cut -f1 -d: >grep.out
  if [ -s grep.out ]; then
    line1=$(head -1 grep.out)
    line2=$(tail -1 grep.out)
    cat xxx.s | sed "$line1","$line2"d >xx.s
    cat xx.s rt/rt-arm-src/memcpy32.s >xxx.s
    echo "memcpy32 compiled source code replaced by fast assembly" $line1 $line2
  fi

  grep -n __memset32 xxx.s | cut -f1 -d: >grep.out
  if [ -s grep.out ]; then
    line1=$(head -1 grep.out)
    line2=$(tail -1 grep.out)
    cat xxx.s | sed "$line1","$line2"d >xx.s
    cat xx.s rt/rt-arm-src/memset32.s >xxx.s
    echo "memset32 compiled source code replaced by fast assembly" $line1 $line2
  fi

  rm -f xx.s grep.out

  cat xxx.s | sed -e 's/_-/_/g' >yyy.s # absolute value of -maxint is somehow neg
  rm xxx.s
  grep "@X@" yyy.s | sed -e 's/@X@//g' >global-defines.s

  # include runtime

  cat data.s \
    global-defines.s \
    rt/rt-arm-src/text.s \
    rt/rt-arm-src/rt-linux.s \
    rt/rt-arm-src/rt.s \
    rt/rt-arm-src/start.s \
    rt/rt-arm-src/init.s \
    yyy.s \
    rt/rt-arm-src/cleanup.s >zzz.s

  # assemble and link arm assembly

  if [ $MACHINE = armv6l ]; then
    as -march=armv6j -mfpu=vfp -mfloat-abi=hard --fatal-warnings \
      -o zzz.o zzz.s
  else # arm7l
    as -o zzz.o zzz.s
  fi
  ld zzz.o -o $1

  rm -f rt.s yyy.s data.s global-defines.s zzz.o
elif [ $MACHINE = riscv64 ]; then

  # set stack size. the default size is 48Mb for Schant compilation. Override it

  FOUR_TIMES_STACK_SIZE=`expr 4 \* $STACK_SIZE`
  sed -e "s/84000000/$FOUR_TIMES_STACK_SIZE/g" \
      -e "s/21000000/$STACK_SIZE/g" \
    rt/rt-riscv-src/data.s >data.s

  # TODO following update is temporary

  sed -e "s/84000000/$FOUR_TIMES_STACK_SIZE/g" \
    rt/rt-riscv-src/start.s >start.s

  if [ $USE_SHMALL_OBJECT = yes ]; then
    if [ -f bin/shmall ]; then
      cat x.c | bin/shmall >xx.scm
      #filter out forward function definitions
      cat xx.scm | $REVERSE | sed -e '/^__forward/{N;N;N;d;}' | \
        $REVERSE >xxxxx.scm
      mv xxxxx.scm xx.scm
      echo riscv >compilation-mode.txt
      if [ -f bin/schant-int ]; then
        cat bin/schimper.txt compilation-mode.txt xx.scm \
          | bin/schant-int \
          >xxx.s
      else
        cat bin/schant-int.vm.txt bin/schimper.txt compilation-mode.txt xx.scm \
          | bin/simpvir \
          >xxx.s
      fi
      rm xx.scm
    else
      echo "shmall binary does not exist"
      exit 0
    fi
  else
    cat bin/shmall-vm.txt x.c | \
      bin/simpvir >xx.scm
    #filter out forward function definitions
    cat xx.scm | $REVERSE | sed -e '/^__forward/{N;N;N;d;}' | \
      $REVERSE >xxxxx.scm
    mv xxxxx.scm xx.scm
    echo riscv >compilation-mode.txt
    cat bin/schant-int.vm.txt bin/schimper.txt compilation-mode.txt xx.scm \
      | bin/simpvir \
      >xxx.s
    rm xx.scm
  fi

  # TODO do the following
  # substitute fast versions of memcmp32, memset32 and memcpy32 if they exist

  # TODO do we need rt/rt-arm-src/init.s
  cat data.s \
    rt/rt-riscv-src/text.s \
    rt/rt-riscv-src/rt-linux.s \
    rt/rt-riscv-src/rt.s \
    start.s \
    xxx.s \
    rt/rt-riscv-src/cleanup.s >zzz.s

    as -o zzz.o zzz.s
  # ld zzz.o -o $1
    ld zzz.o -o $1 -lc # TODO temporarily use C malloc from -lc

    rm -f xxx.s data.s start.s zzz.o
else
  echo "unsupported machine type:" $MACHINE
fi
