#!/bin/bash
#
#  aompcc: Wrapper (driver) for the AOMP compiler. 
#          The goal of this wrapper is to have a simplified interface for 
#          compiling for openmp target offload.
#
#  Written by Greg Rodgers  Gregory.Rodgers@amd.com
#
#
# Copyright (c) 2020 ADVANCED MICRO DEVICES, INC.  
# 
# AMD is granting you permission to use this software and documentation (if any) (collectively, the 
# Materials) pursuant to the terms and conditions of the Software License Agreement included with the 
# Materials.  If you do not have a copy of the Software License Agreement, contact your AMD 
# representative for a copy.
# 
# You agree that you will not reverse engineer or decompile the Materials, in whole or in part, except for 
# example code which is provided in source code form and as allowed by applicable law.
# 
# WARRANTY DISCLAIMER: THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
# KIND.  AMD DISCLAIMS ALL WARRANTIES, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING BUT NOT 
# LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
# PURPOSE, TITLE, NON-INFRINGEMENT, THAT THE SOFTWARE WILL RUN UNINTERRUPTED OR ERROR-
# FREE OR WARRANTIES ARISING FROM CUSTOM OF TRADE OR COURSE OF USAGE.  THE ENTIRE RISK 
# ASSOCIATED WITH THE USE OF THE SOFTWARE IS ASSUMED BY YOU.  Some jurisdictions do not 
# allow the exclusion of implied warranties, so the above exclusion may not apply to You. 
# 
# LIMITATION OF LIABILITY AND INDEMNIFICATION:  AMD AND ITS LICENSORS WILL NOT, 
# UNDER ANY CIRCUMSTANCES BE LIABLE TO YOU FOR ANY PUNITIVE, DIRECT, INCIDENTAL, 
# INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM USE OF THE SOFTWARE OR THIS 
# AGREEMENT EVEN IF AMD AND ITS LICENSORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH 
# DAMAGES.  In no event shall AMD's total liability to You for all damages, losses, and 
# causes of action (whether in contract, tort (including negligence) or otherwise) 
# exceed the amount of $100 USD.  You agree to defend, indemnify and hold harmless 
# AMD and its licensors, and any of their directors, officers, employees, affiliates or 
# agents from and against any and all loss, damage, liability and other expenses 
# (including reasonable attorneys' fees), resulting from Your use of the Software or 
# violation of the terms and conditions of this Agreement.  
# 
# U.S. GOVERNMENT RESTRICTED RIGHTS: The Materials are provided with "RESTRICTED RIGHTS." 
# Use, duplication, or disclosure by the Government is subject to the restrictions as set 
# forth in FAR 52.227-14 and DFAR252.227-7013, et seq., or its successor.  Use of the 
# Materials by the Government constitutes acknowledgement of AMD's proprietary rights in them.
# 
# EXPORT RESTRICTIONS: The Materials may be subject to export restrictions as stated in the 
# Software License Agreement.
# 
function usage(){
/bin/cat 2>&1 <<"EOF" 

   aompcc: Wrapper (driver) for the AOMP compiler. 
           The goal of this wrapper is to have a simplified interface
           for compiling openmp target offload.
   Usage:  aompcc [ options ] input-files

   Options without values:
    -g        Generate debug information
    -v        Verbose, just print commands
    -vv       Very verbose, pass -v to commands
    -n        Dryrun, do nothing, show commands that would execute
    -h        Print this help message
    -k        Keep temporary files
    -c        Compile to object code only

   Options with values:         
    -aomp      <path>           $AOMP or /src/out/ubuntu-18.04/18.04/openmp-extras
    -I         <include dir>    Provide one directory per -I option
    -O         <LLVM opt>       LLVM optimization level
    -o         <outfilename>    Default=a.out
    -t         <tdir>           Temporary directory or intermediate files
                                Default=/tmp/aompcc-tmp-$$
    --offload-arch  <cputype>  Default= value returned by mygpu utility

   Examples:
    aompcc my.c              /* Compiles with OpenMP to create a.out */

   Note: Instead of providing these command line options:
     -aomp, --offload-arch,
     you may set these environment variables, respectively:
     AOMP, AOMP_GPU

   Command line options take precedence over environment variables. 

   Copyright (c) 2020 ADVANCED MICRO DEVICES, INC.

EOF
   exit 0 
}

DEADRC=12

#  Utility Functions
function do_err(){
   if [ $NEWTMPDIR ] ; then 
      if [ $KEEPTDIR ] ; then 
         cp -rp $TMPDIR $OUTDIR
         [ $VERBOSE ] && echo "#Info:  Temp files copied to $OUTDIR/$TMPNAME"
      fi
      rm -rf $TMPDIR
   else 
      if [ $KEEPTDIR ] ; then 
         [ $VERBOSE ] && echo "#Info:  Temp files kept in $TMPDIR"
      fi 
   fi
   [ $VV ] && echo "#Info:  Done"
   exit $1
}

function runcmd(){
   THISCMD=$1
   if [ $DRYRUN ] ; then
      echo "$THISCMD"
   else 
      [ $VERBOSE ] && echo "$THISCMD"
      $THISCMD
      rc=$?
      if [ $rc != 0 ] ; then 
         echo "ERROR:  The following command failed with return code $rc."
         echo "        $THISCMD"
         do_err $rc
      fi
   fi
}

function getdname(){
   local __DIRN=`dirname "$1"`
   if [ "$__DIRN" = "." ] ; then 
      __DIRN=$PWD; 
   else
      if [ ${__DIRN:0:1} != "/" ] ; then 
         if [ ${__DIRN:0:2} == ".." ] ; then 
               __DIRN=`dirname $PWD`/${__DIRN:3}
         else
            if [ ${__DIRN:0:1} = "." ] ; then 
               __DIRN=$PWD/${__DIRN:2}
            else
               __DIRN=$PWD/$__DIRN
            fi
         fi
      fi
   fi
   echo $__DIRN
}

#  --------  The main code starts here -----
INCLUDES=""
PASSTHRUARGS=""
INPUTFILES=""
#  Argument processing
while [ $# -gt 0 ] ; do 
   case "$1" in 
      -q)               QUIET=true;;
      --quiet)          QUIET=true;;
      -k) 		KEEPTDIR=true;; 
      -n) 		DRYRUN=true;; 
      -c)               GEN_OBJECT_ONLY=true;;
      -g)               GEN_DEBUG=true;;
      -noqp) 		NOQP=true;;
      -noshared) 	NOSHARED=true;;
      -I) 		INCLUDES="$INCLUDES -I $2"; shift ;; 
      -O) 		LLVMOPT=$2; shift ;; 
      -O3) 		LLVMOPT=3 ;; 
      -O2) 		LLVMOPT=2 ;; 
      -O1) 		LLVMOPT=1 ;; 
      -O0) 		LLVMOPT=0 ;; 
      -o) 		OUTFILE=$2; shift ;; 
      -t)		TMPDIR=$2; shift ;; 
      --offload-arch)   AOMP_GPU=$2; shift ;;
      -aomp)            AOMP=$2; shift ;;
      -triple)          TARGET_TRIPLE=$2; shift ;;
      -cuda-path)       CUDA_PATH=$2; shift ;;
      -h) 	        usage ;;
      -help) 	        usage ;;
      --help) 	        usage ;;
      -v) 		VERBOSE=true;;
      -vv) 		VV=true;;
      --) 		shift ;;
      *)
        dash=${1:0:1}
	if [ $dash == "-" ] ; then
	   PASSTHRUARGS+=" $1"
        else
	   INPUTFILES+=" $1"
        fi
   esac
   shift
done

fcount=0
for __input_file in `echo $INPUTFILES` ; do
   fcount=$(( fcount + 1 ))
   if [ $fcount == 1 ] ; then
      FIRST_INPUT_FILE_NAME=$__input_file
   fi
   if [ ! -e "$__input_file" ] ; then
      echo "ERROR:  The file $__input_file does not exist."
      exit $DEADRC
   fi
done
if [ -z "$FIRST_INPUT_FILE_NAME" ]  ; then
   echo "ERROR:  No File specified."
   exit $DEADRC
fi

cdir=$(getdname $0)
[ ! -L "$cdir/aompcc" ] || cdir=$(getdname `readlink -f "$cdir/aompcc"`)
HOW_CALLED=${0##*/}

AOMP=${AOMP:-/src/out/ubuntu-18.04/18.04/openmp-extras}
if [ ! -d $AOMP ] ; then
   AOMP="/tmp/grodgers/rocm/aomp_11.5-0"
fi
if [ ! -d $AOMP ] ; then
   echo "ERROR: AOMP not found at $AOMP"
   echo "       Please install AOMP or set environment variable AOMP"
   exit 1
fi

TARGET_TRIPLE=${TARGET_TRIPLE:-amdgcn-amd-amdhsa}

# Determine which gfx processor to use, default to Vega (gfx900)
if [ ! $AOMP_GPU ] ; then 
   # Use the mygpu in pair with this script, no the pre-installed one.
   AOMP_GPU=`$cdir/mygpu -d gfx900`
   if [ "$AOMP_GPU" == "" ] ; then 
      AOMP_GPU="gfx900"
   fi
fi

LLVMOPT=${LLVMOPT:-3}

if [ $VV ]  ; then 
   VERBOSE=true
fi

RUNDATE=`date`

# Parse FIRST_INPUT_FILE_NAME for filetype, directory, and filename
INPUT_FTYPE=${FIRST_INPUT_FILE_NAME##*\.}
INDIR=$(getdname $FIRST_INPUT_FILE_NAME)
FILENAME=${FIRST_INPUT_FILE_NAME##*/}
# FNAME has the filetype extension removed, used for naming intermediate filenames
FNAME=${FILENAME%.*}

if [ -z $OUTFILE ] ; then 
#  Output file not specified so use input directory
   OUTDIR=$INDIR
   if [ $GEN_OBJECT_ONLY ] ; then
      OUTFILE=${FNAME}.o
   else
      OUTFILE="a.out"
   fi
else 
#  Use the specified OUTFILE
   OUTDIR=$(getdname $OUTFILE)
   OUTFILE=${OUTFILE##*/}
fi 

sdir=$(getdname $0)
[ ! -L "$sdir/aompcc" ] || sdir=$(getdname `readlink "$sdir/aompcc"`)
ROCC_DIR=$sdir

TMPNAME="aompcc-tmp-$$"
TMPDIR=${TMPDIR:-/tmp/$TMPNAME}
if [ -d $TMPDIR ] ; then 
   KEEPTDIR=true
else 
   if [ $DRYRUN ] ; then
      echo "mkdir -p $TMPDIR"
   else
      mkdir -p $TMPDIR
      NEWTMPDIR=true
   fi
fi

# Be sure not to delete the output directory
if [ $TMPDIR == $OUTDIR ] ; then 
   KEEPTDIR=true
fi
if [ ! -d $TMPDIR ] && [ ! $DRYRUN ] ; then 
   echo "ERROR:  Directory $TMPDIR does not exist or could not be created"
   exit $DEADRC
fi 
if [ ! -d $OUTDIR ] && [ ! $DRYRUN ]  ; then 
   echo "ERROR:  The output directory $OUTDIR does not exist"
   exit $DEADRC
fi 

#  Print Header block
if [ $VERBOSE ] ; then 
   echo "#   "
   echo "#Info:  AOMP Path:	$AOMP/bin"
   echo "#Info:  How called:	$HOW_CALLED"
   echo "#Info:  Run date:	$RUNDATE"
   echo "#Info:  Input files:	$INPUTFILES"
   echo "#Info:  Code object:	$OUTDIR/$OUTFILE"
   [ $KEEPTDIR ] &&  echo "#Info:  Temp dir:	$TMPDIR" 
   echo "#   "
fi 

UNAMEP=`uname -p`
HOST_TARGET="$UNAMEP-pc-linux-gnu"
if [ "$UNAMEP" == "ppc64le" ] ; then 
  HOST_TARGET="ppc64le-linux-gnu"
fi

#  Pick the compiler based on filetype 
COMPILER_BIN_DIR="$AOMP/bin"

if [ "$INPUT_FTYPE" == "cpp" ]  || [ "$INPUT_FTYPE" == "cxx" ] ; then
   # OpenMP c++
   CLANG_ARGS="$CLANG_ARGS -O$LLVMOPT -target $HOST_TARGET -fopenmp -fopenmp-targets=$TARGET_TRIPLE -Xopenmp-target=$TARGET_TRIPLE -march=$AOMP_GPU"
   CLANG_CMD="clang++"

elif [ "$INPUT_FTYPE" == "c" ] ; then 
   # OpenMP c
   CLANG_ARGS="$CLANG_ARGS -O$LLVMOPT -target $HOST_TARGET -fopenmp -fopenmp-targets=$TARGET_TRIPLE -Xopenmp-target=$TARGET_TRIPLE -march=$AOMP_GPU"
   CLANG_CMD="clang"

elif [ "$INPUT_FTYPE" == "f" ] ; then 
   # OpenMP FORTRAN
   CLANG_ARGS="$CLANG_ARGS -O$LLVMOPT -target $HOST_TARGET -fopenmp -fopenmp-targets=$TARGET_TRIPLE -Xopenmp-target=$TARGET_TRIPLE -march=$AOMP_GPU"
   CLANG_CMD="flang"
else
   echo "ERROR:  PRIMARY FILE TYPE $INPUT_FTYPE NOT SUPPORTED"
   exit $DEADRC
fi

if [ $GEN_DEBUG ]  ; then
   CLANG_ARGS=" -g $CLANG_ARGS"
fi
if [ $VV ]  ; then
   CLANG_ARGS=" -v $CLANG_ARGS"
fi
if [ $GEN_OBJECT_ONLY ]  ; then
   CLANG_ARGS=" -c $CLANG_ARGS"
fi

__INPUTS=""
for __input_file in `echo $INPUTFILES` ; do
  __INPUTS+=" $__input_file"
done

rc=0
runcmd "$COMPILER_BIN_DIR/$CLANG_CMD $CLANG_ARGS $PASSTHRUARGS $__INPUTS -o $OUTDIR/$OUTFILE"

# cleanup
do_err 0
exit 0
