#!/usr/bin/bash
#
# mymcpu:  Print the mcpu value for the current machine
# mygpu:   Print a gpu value acceptable as gpu-arch for cuda clang
#
# Written by Greg Rodgers Gregory.Rodgers@amd.com

PROGVERSION=11.5-0

# Copyright (c) 2018 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" 

   mymcpu: Print out a codename for the gpu of the current system 
           or for the gpu specified with -getcodename option. 

   mygpu:  Print out the real gpu name for the current system 
           or for the codename specified with -getgpuname option.
           mygpu will only print values accepted by cuda clang in 
           the clang argument --cuda-gpu-arch

   Usage: mygpu <options>

   Options without values:
    -h        Print this help message
    -d        If GPU is unknown, set to this default device
    -version  Print the version of mymcpu and mygpu

   Options with values:
    -getgpuname   <codename>  Convert <codename> to gpu name. 
    -getcodename  <gpuname>   Convert <gpuname> to a code name 

   Copyright (c) 2018 ADVANCED MICRO DEVICES, INC.

EOF
   exit 1 
}

function version(){ 
   echo $PROGVERSION
   exit 0
}

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
}

function code2gpu(){ 
   case "$1" in 
      "tahiti")  gpuname="gfx600"
      ;;
      "gfx600")  gpuname="gfx600"
      ;;
      "oland")  gpuname="gfx601"
      ;;
      "gfx601")  gpuname="gfx601"
      ;;
      "gfx700")  gpuname="gfx700"
      ;;
      "kaveri")  gpuname="gfx700"
      ;;
      "gfx701")  gpuname="gfx701"
      ;;
      "hawaii")  gpuname="gfx701"
      ;;
      "gfx703")  gpuname="gfx703"
      ;;
      "kabini")  gpuname="gfx703"
      ;;
      "gfx801")  gpuname="gfx801"
      ;;
      "carrizo") gpuname="gfx801"
      ;;
      "gfx802")  gpuname="gfx802"
      ;;
      "tonga")   gpuname="gfx802"
      ;;
      "gfx803")  gpuname="gfx803"
      ;;
      "fiji")    gpuname="gfx803"
      ;;
      "vega")    gpuname="gfx900"
      ;;
      "RavenRidge")  gpuname="gfx902"
      ;;
      "vega20")   gpuname="gfx906"
      ;;
      "NRAXJ")   gpuname="gfx908"
      ;;
      "sm_30")   gpuname="sm_30"
      ;;
      "k4000")   gpuname="sm_30"
      ;;
      "k4200")   gpuname="sm_30"
      ;;
      "gtx960m")   gpuname="sm_50"
      ;;
      "gtx980")   gpuname="sm_35"
      ;;
      "gtx1050")   gpuname="sm_61"
      ;;
      "gtx1060")   gpuname="sm_61"
      ;;
      "gtx1080")   gpuname="sm_61"
      ;;
      "gt730")   gpuname="sm_35"
      ;;
      "p100")   gpuname="sm_60"
      ;;
      "gv100")   gpuname="sm_70"
      ;;
      "v100")   gpuname="sm_70"
      ;;
      "")  gpuname="sm_35"
      ;;
      *) gpuname=$unknown_value
      ;;
   esac
   echo $gpuname
}

# This routine can echo the gpu name as the codename.
# HELP! TODO: Lots more options to add for both amd and nvidia
function gpu2code(){ 
   case "$1" in 
      "gfx600") codename="tahiti"
      ;;
      "gfx601") codename="oland"
      ;;
      "gfx700") codename="kaveri"
      ;;
      "gfx701") codename="hawaii"
      ;;
      "gfx703") codename="kabini"
      ;;
      "gfx801") codename="carrizo"
      ;;
      "gfx802") codename="tonga"
      ;;
      "gfx803") codename="fiji"
      ;;
      "gfx900") codename="vega"
      ;;
      "gfx906") codename="vega20"
      ;;
      "gfx908") codename="NRAXJ"
      ;;
# sm_ numbers do not map to specific gpus so just return the sm number
      *) codename="$1"
      ;;
   esac
   echo $codename
}

function getmycodename()
{
thisdir=$(getdname $0)
if [ -L "$thisdir/mymcpu" ] ; then
   linkfile=`readlink "$thisdir/mymcpu"`
   if [ "${linkfile%%/*}" == ".." ] ; then
      linkfile=$thisdir/$linkfile
   fi
   thisdir=$(getdname $linkfile)
fi
GPUTABLE_FILE=${GPUTABLE_FILE:-$thisdir/gputable.txt}
local _found=0
if [ -f $GPUTABLE_FILE ] ; then
   for sysfsname in `find -L /sys/bus/pci/devices -maxdepth 2 -name uevent | xargs grep -l -E 'DRIVER=amdgpu|DRIVER=nvidia'` ; do
      gpuid=`cat $sysfsname | grep 'PCI_ID=' | cut -d"=" -f2 | tr '[:upper:]' '[:lower:]'`
      if [ $_found == 0  ] ; then
         entry=`grep -m1 "^$gpuid" $GPUTABLE_FILE`
         if [ $? == 0 ] ; then
            codename=`echo $entry | awk '{print $4}'`
            _found=1
         fi
      fi
   done
fi
if [ $_found == 1  ] ; then
   echo $codename
else
   echo "unknown"
fi
}

#  ===========  Main code for mymcpu and mygpu starts here ========== 
TYPERUN=${0##*\/}
MODE=

unknown_value="unknown"
#  process args
while [ $# -gt 0 ] ; do 
   case "$1" in 
      -getgpuname)      INPUT_CODENAME=$2; shift ;;
      -getcodename)     INPUT_GPUNAME=$2; shift ;;
      -n) 	        MODE=num; shift ;;
      -h) 	        usage ;; 
      -d) 	        unknown_value=$2; shift ;;
      -help) 	        usage ;; 
      --help) 	        usage ;; 
      -version) 	version ;; 
      --version) 	version ;; 
      --) 		shift ; break;;
      *) 		break;echo $1 ignored;
   esac
   shift
done

if [ $INPUT_GPUNAME ] && [ $INPUT_CODENAME ] ; then 
   echo "ERROR: Please specify only -convert or -codename option"
   exit 1
fi

rc=0
if [ "$TYPERUN" == "mygpu" ] ; then 
   if [ $INPUT_GPUNAME ] ; then 
      # nothing to do stupid
      gpuname=$INPUT_GPUNAME
   else
      if [ $INPUT_CODENAME ] ; then 
         codename=`echo $INPUT_CODENAME | tr '[:upper:]' '[:lower:]'`
      else
         codename=$(getmycodename)
      fi
      gpuname=$(code2gpu $codename)
   fi
   [ "$gpuname" == "unknown" ] && rc=1
   if [ -z "$MODE" ]; then
     echo $gpuname
   else
     echo `echo $gpuname | sed 's/gfx//' | sed 's/sm_//'`
   fi
else 
   # This is mymcpu so print the codename
   if [ $INPUT_CODENAME ] ; then 
      # nothing to do stupid
      codename=$INPUT_CODENAME
   else 
      if [ $INPUT_GPUNAME ] ; then 
         gpuname=`echo $INPUT_GPUNAME | tr '[:upper:]' '[:lower:]'`
         codename=$(gpu2code $gpuname)
      else
         codename=$(getmycodename)
      fi
   fi
   [ "$codename" == "unknown" ] && rc=1
   echo $codename
fi

exit $rc
