#!/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

cp $1.c x.c

if grep -q " dbl_init" x.c ; then
  USE_DOUBLE_INTRINSICS=yes
else
  USE_DOUBLE_INTRINSICS=no
fi

if [ $MACHINE = i686 -o $MACHINE = i586 -o $MACHINE = i386 -o $MACHINE = x86_64 -o $MACHINE = amd64 ]; then
  if [ $MACHINE = i686 -o $MACHINE = i586 -o $MACHINE = i386 ]; then
    VERSION=32
  else
    VERSION=64
  fi
  echo x86 $VERSION >compilation-mode.txt
  if [ $USE_SHMALL_OBJECT = yes ]; then
    if [ -f bin/shmall.txt ]; then
      cat bin/shmall.txt x.c | bin/schant-int >xx.scm
      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/schant-int.vm.txt bin/shmall.txt x.c | \
      bin/simpvir >xx.scm
    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

  ./replacer.sh asm rt-x86-src core

  if [ $OS = FreeBSD ]; then
    if [ $MACHINE = i686 -o $MACHINE = i586 -o $MACHINE = i386 ]; then # 32 bit
      RT_OS=rt/rt-x86-src/rt-freebsd32.asm
      RT_ASM=rt32.asm
      RT_ENTRY=rt/rt-x86-src/rt-entry32.asm
      CORE=src/asm/simpvir-x86-32-core.asm
      if [ $USE_DOUBLE_INTRINSICS = yes ]; then
        RT_DOUBLE=rt/rt-x86-src/rt-double32.asm
      else
        RT_DOUBLE=rt/rt-null.asm
      fi
    else # 64 bit
      RT_OS=rt/rt-x86-src/rt-linux64.asm
      RT_ASM=rt64.asm
      RT_ENTRY=rt/rt-x86-src/rt-entry64.asm
      if [ $USE_DOUBLE_INTRINSICS = yes ]; then
        RT_DOUBLE=rt/rt-x86-src/rt-double64.asm
      else
        RT_DOUBLE=rt/rt-null.asm
      fi

    fi
  elif [ $OS = OpenBSD ]; then
    if [ $MACHINE = i686 -o $MACHINE = i586 -o $MACHINE = i386 ]; then # 32 bit
      RT_OS=rt/rt-x86-src/rt-openbsd32.asm
      RT_ASM=rt32.asm
      RT_ENTRY=rt/rt-x86-src/rt-entry32.asm
      if [ $USE_DOUBLE_INTRINSICS = yes ]; then
        HAS_SSE=`sysctl -n machdep.sse`
        if [ $HAS_SSE = 1 ]; then
          RT_DOUBLE=rt/rt-x86-src/rt-double32.asm
        else
          echo "processor does not have sse"
          exit 0
        fi
      else
        RT_DOUBLE=rt/rt-null.asm
      fi
    else # 64 bit
      RT_OS=rt/rt-x86-src/rt-openbsd64.asm
      RT_ASM=rt64.asm
      RT_ENTRY=rt/rt-x86-src/rt-entry64.asm
      if [ $USE_DOUBLE_INTRINSICS = yes ]; then
        RT_DOUBLE=rt/rt-x86-src/rt-double64.asm
      else
        RT_DOUBLE=rt/rt-null.asm
      fi
    fi
  elif [ $OS = Cygwin ]; then # windows
    if [ $MACHINE = i686 -o $MACHINE = i586 -o $MACHINE = i386 ]; then # 32 bit
      RT_OS=rt/rt-x86-src/rt-win.asm
      RT_ASM=rt32.asm
    else
      echo "unsupported 64 bit"
      exit 0
    fi
  elif [ $OS = Darwin ]; then # Mac OS X
    if [ $MACHINE = i686 -o $MACHINE = i586 -o $MACHINE = i386 ]; then # 32 bit
      echo "unsupported 32 bit"
      exit 0
    else # 64 bit
      RT_OS=rt/rt-x86-src/rt-osx64.asm
      RT_ASM=rt/rt-null.asm
    fi
  else # GNU/Linux
    if [ $MACHINE = i686 -o $MACHINE = i586 -o $MACHINE = i386 ]; then # 32 bit
      RT_OS=rt/rt-x86-src/rt-linux32.asm
      RT_ASM=rt32.asm
      RT_ENTRY=rt/rt-x86-src/rt-entry32.asm
      if [ $USE_DOUBLE_INTRINSICS = yes ]; then
        RT_DOUBLE=rt/rt-x86-src/rt-double32.asm
      else
        RT_DOUBLE=rt/rt-null.asm
      fi
    else # 64 bit
      RT_OS=rt/rt-x86-src/rt-linux64.asm
      RT_ASM=rt64.asm
      RT_ENTRY=rt/rt-x86-src/rt-entry64.asm
      if [ $USE_DOUBLE_INTRINSICS = yes ]; then
        RT_DOUBLE=rt/rt-x86-src/rt-double64.asm
      else
        RT_DOUBLE=rt/rt-null.asm
      fi
    fi
  fi

  # 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

  cat rt/rt-x86-src/rt-comment.asm \
      $RT_OS \
      rt.asm \
      $RT_DOUBLE \
      $RT_ENTRY \
      core.asm \
      >$1.asm

  if [ $MACHINE = i686 -o $MACHINE = i586 -o $MACHINE = i386 ]; then # 32 bit
    if [ $OS = Cygwin ]; then # windows
      ENTRY_NAME=_main
    else
      ENTRY_NAME=_start
    fi
    sed -e "s/@SUBSTITUTE_ENTRY_NAME/$ENTRY_NAME/g" \
      <$1.asm >x.asm
    mv x.asm $1.asm
    sed -e 's/rax/eax/g' \
        -e 's/rbx/ebx/g' \
        -e 's/rcx/ecx/g' \
        -e 's/rdx/edx/g' \
        -e 's/rbp/ebp/g' \
        -e 's/rdi/edi/g' \
        -e 's/rsi/esi/g' \
        -e 's/rsp/esp/g' \
      <$1.asm >x.asm
    mv x.asm $1.asm

    if [ -f bin/peep ]; then

      # peephole optimizations

      cat compilation-mode.txt $1.asm | bin/peep >x.asm
      mv x.asm $1.asm
    fi

    as -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
      cc $1.c -O3 -o $1
    else # GNU/Linux or FreeBSD
      ld -s -o $1 $1.o
    fi
  else # x86_64 or amd64
    ENTRY_NAME=main
    sed -e "s/@SUBSTITUTE_ENTRY_NAME/$ENTRY_NAME/g" \
      <$1.asm >x.asm
    mv x.asm $1.asm
    if [ $OS = Darwin ]; then
      # in darwin 16 byte alignment is a power of 2
      # and the effective address formed by adding a
      # label offset has to be indirect thru a 32 bit register
      sed -e 's/align 16/align 4/g' \
    -e 's/(%rax), %rax/(%eax), %rax/g' \
        <$1.asm >x.asm
      mv x.asm $1.asm
    fi

    if [ -f bin/peep ]; then

      # peephole optimizations

      cat compilation-mode.txt $1.asm | bin/peep >x.asm
      mv x.asm $1.asm
    fi

    as -o $1.o $1.asm # TODO used to have -s. doesnt work on osx
    if [ $OS = FreeBSD ]; then
      cc -o $1 $1.o # resolve calls to libc
    elif [ $OS = OpenBSD ]; then
      cc -nopie -o $1 $1.o # resolve calls to libc
    elif [ $OS = Darwin ]; then
      ld $1.o -e _main -o $1
    else
      gcc -no-pie -z noexecstack -o $1 $1.o # resolve calls to libc
    fi
  fi

  rm -f $1.asm $1.o
  rm -f rt.asm core.asm
elif [ $MACHINE = armv6l -o $MACHINE = armv7l -o $MACHINE = evbarm -o $MACHINE = aarch64 ]; then

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

  if [ $MACHINE = armv6l -o $MACHINE = evbarm ]; then
    ARM_MODE=arm6
    VERSION=v6
    RT_SRC=rt-arm-src
  elif [ $MACHINE = armv7l ]; then
    ARM_MODE=arm7
    VERSION=v7
    RT_SRC=rt-arm-src
  else
    ARM_MODE=aarch64
    VERSION=aarch64
    RT_SRC=rt-aarch64-src
  fi

  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_SRC/data.s >data.s

  if [ $USE_SHMALL_OBJECT = yes ]; then
    if [ -f bin/shmall.txt ]; then
      cat bin/shmall.txt x.c | bin/schant-int >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/schant-int.vm.txt bin/shmall.txt x.c | \
      bin/simpvir >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

  ./replacer.sh s $RT_SRC xxx

  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

  if [ $OS = NetBSD ]; then
    RT_OS=rt/$RT_SRC/rt-netbsd.s
    TEXT=rt/$RT_SRC/text-netbsd.s
    RT=rt/$RT_SRC/rt-netbsd1.s
    START=rt/$RT_SRC/start-netbsd.s
  else
    RT_OS=rt/$RT_SRC/rt-linux.s
    TEXT=rt/$RT_SRC/text.s
    RT=rt/$RT_SRC/rt.s
    if [ $USE_DOUBLE_INTRINSICS = yes ]; then
      RT_DOUBLE=rt/$RT_SRC/rt-double.s
    else
      RT_DOUBLE=rt/rt-null.asm
    fi
    START=rt/$RT_SRC/start.s
  fi

  cat data.s \
    global-defines.s \
    $TEXT \
    $RT_OS \
    $RT \
    $RT_DOUBLE \
    $START \
    rt/$RT_SRC/init.s \
    yyy.s \
    rt/$RT_SRC/cleanup.s >zzz.s

  if [ -f bin/peep ]; then

    # peephole optimizations

    cat compilation-mode.txt zzz.s | bin/peep >zzzz
    mv zzzz zzz.s
  fi

  # assemble and link arm assembly

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

  if [ $OS = NetBSD ]; then
    cc zzz.o -o $1 -lc
  else
    ld zzz.o -o $1
  fi

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

  RT_SRC=rt-riscv-src

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

  sed -e "s/STACK_SIZE = 22000000/STACK_SIZE = $STACK_SIZE/g" \
    rt/rt-riscv-src/data.s >data.s

  if [ $USE_SHMALL_OBJECT = yes ]; then
    if [ -f bin/shmall.txt ]; then
      cat bin/shmall.txt x.c | bin/schant-int >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/schant-int.vm.txt bin/shmall.txt x.c | \
      bin/simpvir >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
  # ./replacer.sh

  if [ $USE_DOUBLE_INTRINSICS = yes ]; then
    RT_DOUBLE=rt/$RT_SRC/rt-double64.s
  else
    RT_DOUBLE=rt/rt-null.asm
  fi

  cat data.s \
    rt/rt-riscv-src/text.s \
    rt/rt-riscv-src/rt-linux.s \
    rt/rt-riscv-src/rt.s \
    $RT_DOUBLE \
    rt/rt-riscv-src/start.s \
    xxx.s \
    rt/rt-riscv-src/cleanup.s >zzz.s

  if [ -f bin/peep ]; then

    # peephole optimizations

    cat compilation-mode.txt zzz.s | bin/peep >zzzz
    mv zzzz zzz.s
  fi

  as -o zzz.o zzz.s
  ld zzz.o -o $1

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