#!/bin/bash

usage(){
    cat << EOF

    Usage: $0 [-c <clang path>] [-d] -s|--asm <assembly file path>
    Option:
           -c <clang path>: optional
               if not specify clang path, use default: llvm/bin/clang in repository gemmPerf.

           -s|--asm <assembly file path>: required
               assembly file path must end with .s

EOF
}

gen_amdgcn_target(){
    amdgcn_target=$(grep -nr "\.amdgcn_target" $s_abs_path | awk '{print $NF}' | awk -F "\"" '{print $2}')
    amdgcn_target=${amdgcn_target#*--}
    [[ -n ${amdgcn_target} ]] || { echo "target-id null, exit"; exit -1; }
    [[ $DEBUG_MODE = true ]] && echo -e "\n--- amdgcn_target: $amdgcn_target"
}

gen_co_from_s(){
    local co_dir=${PWD}/hsaco_kernel
    local o_dir=${PWD}/o_kernel
    [[ -d ${co_dir} ]] || mkdir -p ${co_dir}
    [[ -d ${o_dir} ]] || mkdir -p ${o_dir}

    local o_abs_dir_path
    local name_prefix
    local o_name
    local co_name
    o_abs_dir_path=$(dirname ${s_abs_path})
    name_prefix=$(basename ${s_abs_path})
    name_prefix=${name_prefix%.s}
    [[ $DEBUG_MODE = true ]] && echo -e "\n--- name prefix: $name_prefix"
    o_name="${name_prefix}.o"
    co_name="${name_prefix}.co"

    ### Command: generate .o from .s:
    #grep "$2" $s_abs_path || { echo -e "\n*** Fatal: .amdgcn_target in .s not contain specified target-id: $2\n*** .s: $(grep ".amdgcn_target" $s_abs_path)"; exit -1; }
    [[ $DEBUG_MODE = true ]] && echo -e "\n--- .s -> .o:\n     $ ${ASM_COMPILER} -x assembler -target amdgcn-amd-amdhsa -mcpu=${amdgcn_target} -c -o ${o_dir}/${o_name} ${s_abs_path}"
    ${ASM_COMPILER} -x assembler -target amdgcn-amd-amdhsa -mcpu=${amdgcn_target} -c -o ${o_dir}/${o_name} ${s_abs_path}
    [[ $? -eq 0 ]] || { echo -e "\n*** Fatal: $(basename $0) failure .s -> .o\n    $ ${ASM_COMPILER} -x assembler -target amdgcn-amd-amdhsa -mcpu=${amdgcn_target} -c -o ${o_dir}/${o_name} ${s_abs_path} "; exit -1; }
    o_abs_path=$(realpath -e ${o_dir}/${o_name})
    [[ $DEBUG_MODE = true ]] && echo -e "\n--- .o dir: ${o_dir}\n--- .o path:\n    $(realpath -e ${o_dir}/${o_name})"

    ### Command: generate .hsaco/.co from .o:
    [[ $DEBUG_MODE = true ]] && echo -e "\n--- .o -> .co:\n    $ ${ASM_COMPILER} -target amdgcn-amd-amdhsa -o ${co_dir}/${co_name} ${o_abs_path}"
    ${ASM_COMPILER} -target amdgcn-amd-amdhsa -o ${co_dir}/${co_name} ${o_abs_path}
    [[ $? -eq 0 ]] || { echo -e "\n*** Fatal: $(basename $0) failure .o -> .co\n    $ ${ASM_COMPILER} -target amdgcn-amd-amdhsa -o ${co_dir}/${co_name} ${o_abs_path}"; exit -1; }
    co_abs_path=$(realpath -e ${co_dir}/${co_name}) || exit -1
    [[ $DEBUG_MODE = true ]] && echo -e "\n--- .co dir: ${co_dir}\n--- .co path:\n    ${co_abs_path}"
    echo "${co_abs_path}"
}

check_getopt_exit_status(){
    local exit_status=$1
    if [[ $exit_status -eq 0 ]] && [[ $DEBUG_MODE = true ]]; then
        echo -e "--- Parsing Successfully."
    elif [[ $exit_status -eq 1 ]]; then
        echo -e "*** getopt(3) returns errors."
        exit -1
    elif [[ $exit_status -eq 2 ]]; then
        echo -e "*** Don't understand its own parameters"
        exit -1
    elif [[ $exit_status -eq 3 ]]; then
        echo -e "*** an internal error occurs like out-of-memory"
        exit -1
    elif [[ $exit_status -eq 4 ]] && [[ $DEBUG_MODE = true ]]; then
        echo -e "--- called with -T"
    fi
}

parse_option(){
    getopt -T
    if [[ $? -eq 4 ]]; then
        GETOPT_PARSE=$(getopt --name "${0}" --longoptions help,compiler:,asm:,debug --options hc:ds: -- "$@" 2>getopt_stderr.log)
        GETOPT_RET=$?
        if [[ -s getopt_stderr.log ]]; then
            echo -e "\n*** Fatal:"
            echo "*** $(cat getopt_stderr.log)"
            rm getopt_stderr.log
        fi
        check_getopt_exit_status $GETOPT_RET
        #if [[ $GETOPT_RET -eq 0 ]] ; then
        #    echo -e "--- option: ${GETOPT_PARSE}"
        #fi
    else
        echo -e "\n*** Fatal:"
        echo "*** getopt is an old version, Need enhanced version"
        exit -1
    fi
    eval set -- ${GETOPT_PARSE}
    while true;
    do
        case "$1" in
            -h|--help)
                usage
                exit 0
                ;;
            -c|--compiler)
                ASM_COMPILER=$(realpath -e $2) || exit -1
                shift 2
                ;;
            -s|--asm)
                ASM_FILE_PATH=$(realpath -e $2) || exit -1
                [[ $ASM_FILE_PATH =~ \.s$ ]] || { echo -e "\n*** Fatal: asm file not end with .s\n*** asm file: $ASM_FILE_PATH"; exit -1; }
                shift 2
                ;;
            -d|--debug)
                DEBUG_MODE=true
                shift
                ;;
            --)
                shift
                break
                ;;
            *)
                echo "unrecognized parameter:[$1] received, exit"
                exit -1
                ;;
        esac
    done
    if [[ -z $ASM_COMPILER ]]; then
        [[ $DEBUG_MODE = true ]] && echo -e "\n--- option no compiler option: -c/--compiler\n--- use default: /opt/rocm/llvm/bin/clang-18)"
        ASM_COMPILER=/opt/rocm/llvm/bin/clang-18 || exit -1
    fi
    if [[ -z $ASM_FILE_PATH ]]; then
        usage
        exit -1
    fi
    REMAIN_PARAMS_NUM=$#
    REMAIN_PARAMS=$@
    if [[ $DEBUG_MODE = true ]]; then
        echo "--- remaining parameter number: $#"
        echo "--- remaining parameter option: $@"
    fi
}

#####
# main
#####
# name
#    A word consisting only of alphanumeric characters and underscores,
#    and beginning with an alphabetic character or an underscore.
#    Also referred to as an identifier.
#####
ASM_COMPILER=
ASM_FILE_PATH=
REMAIN_PARAMS_NUM=
REMAIN_PARAMS=
DEBUG_MODE=false
parse_option $@
shift $(($#-$REMAIN_PARAMS_NUM))
#echo "\$@: $@"

#####
# compile .s -> .co
#####
s_abs_path=$ASM_FILE_PATH
co_abs_path=
gen_amdgcn_target
gen_co_from_s
