#!/usr/bin/perl -w


# Need perl > 5.10 to use logic-defined or
use 5.006; use v5.10.1;
use File::Basename;
use File::Temp qw/ :mktemp  /;
use Cwd;
use Cwd 'abs_path';

# HIP compiler driver
# Will call NVCC or HCC (depending on target) and pass the appropriate include and library options for
# the target compiler and HIP infrastructure.

# Will pass-through options to the target compiler.  The tools calling HIPCC must ensure the compiler
# options are appropriate for the target compiler.

# Environment variable HIP_PLATFORM control compilation path:
# HIP_PLATFORM='nvcc' or HIP_PLATFORM='hcc'.
# If HIP_PLATFORM is not set hipcc will attempt auto-detect based on if nvcc is found.
#
# Other environment variable controls:
# HIP_PATH  : Path to HIP directory, default is one dir level above location of this script
# CUDA_PATH : Path to CUDA SDK (default /usr/local/cuda). Used on NVIDIA platforms only.
# HCC_HOME  : Path to HCC SDK (defaults to ../../hcc relative to this
#             script's abs_path). Used on AMD platforms only.
# HSA_PATH  : Path to HSA dir (defaults to ../../hsa relative to abs_path
#             of this script). Used on AMD platforms only.
# HIP_VDI_HOME : Path to HIP/VDI directory. Used on AMD platforms only.

if(scalar @ARGV == 0){
    print "No Arguments passed, exiting ...\n";
    exit(-1);
}

#---
# Function to parse config file
sub parse_config_file {
    my ($file, $config) = @_;
    if (open (CONFIG, "$file")) {
        while (<CONFIG>) {
            my $config_line=$_;
            chop ($config_line);
            $config_line =~ s/^\s*//;
            $config_line =~ s/\s*$//;
            if (($config_line !~ /^#/) && ($config_line ne "")) {
                my ($name, $value) = split (/=/, $config_line);
                $$config{$name} = $value;
            }
        }
        close(CONFIG);
    }
}

$verbose = $ENV{'HIPCC_VERBOSE'} // 0;
# Verbose: 0x1=commands, 0x2=paths, 0x4=hipcc args

$isWindows = $^O eq 'MSWin32';

$HIPCC_COMPILE_FLAGS_APPEND=$ENV{'HIPCC_COMPILE_FLAGS_APPEND'};
$HIPCC_LINK_FLAGS_APPEND=$ENV{'HIPCC_LINK_FLAGS_APPEND'};

# Known HIP target names.
@knownTargets = ('gfx701', 'gfx801', 'gfx802', 'gfx803', 'gfx900', 'gfx906', 'gfx908', 'gfx1010', 'gfx1011', 'gfx1012');

#
# TODO: Fix rpath LDFLAGS settings
#
# Since this hipcc script gets installed at two uneven hierarchical levels,
# linked by symlink, the absolute path of this script should be used to
# derive HIP_PATH, as dirname $0 could be /opt/rocm/bin or /opt/rocm/hip/bin
# depending on how it gets invoked.
# ROCM_PATH which points to <rocm_install_dir> is determined based on whether
# we find .info/version in the parent of HIP_PATH or not. If it is found,
# ROCM_PATH is defined relative to HIP_PATH else it is hardcoded to /opt/rocm.
#
$HIP_PATH=$ENV{'HIP_PATH'} // dirname(Cwd::abs_path("$0/../")); # use parent directory of hipcc
if (-e "$HIP_PATH/../.info/version") {
    $ROCM_PATH=$ENV{'ROCM_PATH'} // dirname("$HIP_PATH"); # use parent directory of HIP_PATH
} else {
    $ROCM_PATH=$ENV{'ROCM_PATH'} // "/opt/rocm";
}
$HIP_VDI_HOME=$ENV{'HIP_VDI_HOME'};
$HIP_LIB_PATH=$ENV{'HIP_LIB_PATH'};
$HIP_CLANG_PATH=$ENV{'HIP_CLANG_PATH'};
$DEVICE_LIB_PATH=$ENV{'DEVICE_LIB_PATH'};
$HIP_CLANG_HCC_COMPAT_MODE=$ENV{'HIP_CLANG_HCC_COMPAT_MODE'}; # HCC compatibility mode

if (defined $HIP_VDI_HOME) {
    $HIP_INFO_PATH= "$HIP_VDI_HOME/lib/.hipInfo";
} else {
    $HIP_INFO_PATH= "$HIP_PATH/lib/.hipInfo"; # use actual file
}

#---
# Read .hipInfo
my %hipConfig = ();
parse_config_file("$HIP_INFO_PATH", \%hipConfig);

#---
# Temporary directories
my @tmpDirs = ();

#---
# Create a new temporary directory and return it
sub get_temp_dir {
    my $tmpdir = mkdtemp("/tmp/hipccXXXXXXXX");
    push (@tmpDirs, $tmpdir);
    return $tmpdir;
}

#---
# Delete all created temporary directories
sub delete_temp_dirs {
    if (@tmpDirs) {
        system ('rm -rf ' . join (' ', @tmpDirs));
    }
    return 0;
}

#---
#HIP_PLATFORM controls whether to use NVCC or HCC for compilation:
$HIP_PLATFORM= `$HIP_PATH/bin/hipconfig --platform` // "hcc";
$HIP_VERSION= `$HIP_PATH/bin/hipconfig --version`;
$HIP_COMPILER= $hipConfig{'HIP_COMPILER'};
$HIP_RUNTIME= $hipConfig{'HIP_RUNTIME'};

# If using VDI runtime, need to find HIP_VDI_HOME
if (defined $HIP_RUNTIME and $HIP_RUNTIME eq "VDI" and !defined $HIP_VDI_HOME) {
    my $hipcc_dir = dirname($0);
    if (-e "$hipcc_dir/../lib/bitcode") {
        $HIP_VDI_HOME = abs_path($hipcc_dir . "/..");
    } else {
        $HIP_VDI_HOME = $HIP_PATH; # use HIP_PATH
    }
}

if (defined $HIP_VDI_HOME) {
    if (!defined $HIP_CLANG_PATH and (-e "$HIP_VDI_HOME/bin/clang" or -e "$HIP_VDI_HOME/bin/clang.exe")) {
        $HIP_CLANG_PATH = "$HIP_VDI_HOME/bin";
    }
    if (!defined $DEVICE_LIB_PATH and -e "$HIP_VDI_HOME/lib/bitcode") {
        $DEVICE_LIB_PATH = "$HIP_VDI_HOME/lib/bitcode";
    }
    $HIP_INCLUDE_PATH = "$HIP_VDI_HOME/include";
    if (!defined $HIP_LIB_PATH) {
        $HIP_LIB_PATH = "$HIP_VDI_HOME/lib";
    }
}

if (defined $HIP_COMPILER and $HIP_COMPILER eq "clang") {
  $HIP_PLATFORM = "clang";
  if (!defined $HIP_CLANG_PATH) {
    $HIP_CLANG_PATH = "$ROCM_PATH/llvm/bin";
  }
  if (!defined $DEVICE_LIB_PATH) {
    $DEVICE_LIB_PATH = "$ROCM_PATH/lib";
  }
}

if ($verbose & 0x2) {
    print ("HIP_PATH=$HIP_PATH\n");
    print ("HIP_PLATFORM=$HIP_PLATFORM\n");
}

# set if user explicitly requests -stdlib=libc++. (else we default to libstdc++ for better interop with g++):
$setStdLib = 0;  # TODO - set to 0

$default_amdgpu_target = 1;

if ($HIP_PLATFORM eq "clang") {
    $HIPCC="$HIP_CLANG_PATH/clang++";

    # If $HIPCC clang++ is not compiled, use clang instead
    if ( ! -e $HIPCC ) {
        $HIPCC="$HIP_CLANG_PATH/clang";
        $HIPLDFLAGS = "--driver-mode=g++";
    }

    $HIP_CLANG_VERSION = `$HIPCC --version`;
    $HIP_CLANG_VERSION=~/.*clang version ([^ ]+).*/;
    $HIP_CLANG_VERSION=$1;

    if (! defined $HIP_CLANG_INCLUDE_PATH) {
        $HIP_CLANG_INCLUDE_PATH = abs_path("$HIP_CLANG_PATH/../lib/clang/$HIP_CLANG_VERSION/include");
    }
    if (! defined $HIP_INCLUDE_PATH) {
        $HIP_INCLUDE_PATH = "$HIP_PATH/include";
    }
    if (! defined $HIP_LIB_PATH) {
        $HIP_LIB_PATH = "$HIP_PATH/lib";
    }
    if ($verbose & 0x2) {
        if (defined $HIP_VDI_HOME) {
            print ("HIP_VDI_HOME=$HIP_VDI_HOME\n");
        }
        print ("HIP_CLANG_PATH=$HIP_CLANG_PATH\n");
        print ("HIP_CLANG_INCLUDE_PATH=$HIP_CLANG_INCLUDE_PATH\n");
        print ("HIP_INCLUDE_PATH=$HIP_INCLUDE_PATH\n");
        print ("HIP_LIB_PATH=$HIP_LIB_PATH\n");
        print ("DEVICE_LIB_PATH=$DEVICE_LIB_PATH\n");
    }

    if ($isWindows) {
      $HIPCXXFLAGS .= " -std=c++14 -fms-extensions -fms-compatibility";
    } else {
      $HIPCXXFLAGS .= " -std=c++11";
    }
    $HIPCXXFLAGS .= " -isystem $HIP_CLANG_INCLUDE_PATH";
    $HIPLDFLAGS .= " -L$HIP_LIB_PATH";
    if (not $isWindows) {
      $HIPLDFLAGS .= " -Wl,--rpath-link=$HIP_LIB_PATH";
      $HIPLDFLAGS .= " -lhip_hcc";
    } else {
      $HIPLDFLAGS .= " -lamdhip64";
    }
    if ($HIP_CLANG_HCC_COMPAT_MODE) {
        ## Allow __fp16 as function parameter and return type.
        $HIPCXXFLAGS .= " -Xclang -fallow-half-arguments-and-returns -D__HIP_HCC_COMPAT_MODE__=1";
    }

    $HSA_PATH=$ENV{'HSA_PATH'} // "$ROCM_PATH/hsa";
    $HIPCXXFLAGS .= " -isystem $HSA_PATH/include";
    if (!($HIP_RUNTIME eq "HCC")) {
      $HIPCXXFLAGS .= " -D__HIP_VDI__ -fhip-new-launch-api";
    }

} elsif ($HIP_PLATFORM eq "hcc") {
    $HIP_INCLUDE_PATH = "$HIP_PATH/include";
    if (! defined $HIP_LIB_PATH) {
        $HIP_LIB_PATH = "$HIP_PATH/lib";
    }
    $HSA_PATH=$ENV{'HSA_PATH'} // "$ROCM_PATH/hsa";

    $HCC_HOME=$ENV{'HCC_HOME'} // $hipConfig{'HCC_HOME'} // "$ROCM_PATH/hcc";

    $HCC_VERSION=`${HCC_HOME}/bin/hcc --version`;
    $HCC_VERSION=~/.*based on HCC ([^ ]+).*/;
    $HCC_VERSION=$1;
    $HCC_VERSION_MAJOR=$HCC_VERSION;
    $HCC_VERSION_MAJOR=~s/\..*//;

    $HIP_ATP_MARKER=$ENV{'HIP_ATP_MARKER'} // 1;
    $marker_path = "$ROCM_PATH/profiler/CXLActivityLogger";

    # HCC* may be used to compile src/hip_hcc.o (and also feed the HIPCXXFLAGS below)
    $HCC = "$HCC_HOME/bin/hcc";
    $HCCFLAGS = "-hc  -D__HIPCC__ -isystem $HCC_HOME/include ";

    $HIPCC=$HCC;
    $HIPCXXFLAGS = $HCCFLAGS;

    $HIPLDFLAGS = `${HCC_HOME}/bin/hcc-config --ldflags`;

    #### GCC system includes workaround ####
    $HCC_WA_FLAGS = " ";
    $HOST_OSNAME= `cat /etc/os-release | grep "^ID\=" | cut -d= -f2 | tr -d '\n'`;
    if ($HCC_VERSION_MAJOR eq 1) {
        my $GCC_CUR_VER = `gcc -dumpversion`;
        my $GPP_CUR_VER = `g++ -dumpversion`;
        $GCC_CUR_VER =~ s/\R//g;
        $GPP_CUR_VER =~ s/\R//g;

        my @GPP_VER_FIELDS = split('\.', $GPP_CUR_VER);

        # Only include the libstdc++ headers and libraries flags explicitly if the g++ is older than version 5.
        # That's because HCC already uses libstdc++ by default if a newer g++/libstdc++ is available
        # Cent OS 7 and RHEL 7.4 cannot use libstdc++ for compilation, default to libc++
        if (${GCC_CUR_VER} eq ${GPP_CUR_VER} and $GPP_VER_FIELDS[0] < 5 and ($HOST_OSNAME ne "\"centos\"") and ($HOST_OSNAME ne "\"rhel\"")) {
            $HCC_WA_FLAGS .= " -stdlib=libstdc++  -isystem /usr/include/x86_64-linux-gnu -isystem /usr/include/x86_64-linux-gnu/c++/${GCC_CUR_VER} -isystem /usr/include/c++/${GCC_CUR_VER} ";
            # Add C++ libs for GCC.
            $HIPLDFLAGS .= " -lstdc++";
        }
    }

    # Force -stdlib=libc++ on UB14.04
    $HOST_OSVER= `cat /etc/os-release | grep "^VERSION_ID\=" | cut -d= -f2 | tr -d '\n'`;
    if ($HOST_OSNAME eq "ubuntu" and $HOST_OSVER eq "\"14.04\"") {
        $HIPCXXFLAGS .= " -stdlib=libc++";
        $setStdLib = 1;
    }

    $HIPCXXFLAGS .= " -isystem $HIP_PATH/include/hip/hcc_detail/cuda";
    $HIPCXXFLAGS .= " -isystem $HSA_PATH/include";
    $HIPCXXFLAGS .= " -Wno-deprecated-register";

    $HIPLDFLAGS .= " -L$HSA_PATH/lib -L$ROCM_PATH/lib -lhsa-runtime64 -lhc_am ";
#    $HIPLDFLAGS .= " -L$HCC_HOME/compiler/lib -lLLVMAMDGPUDesc -lLLVMAMDGPUUtils -lLLVMMC -lLLVMCore -lLLVMSupport ";

    # Add trace marker library:
    # TODO - once we cleanly separate the HIP API headers from HIP library headers this logic should move to CMakebuild option - apps do not need to see the marker library.
    if ($HIP_ATP_MARKER) {
        $marker_inc_path = "$marker_path/include";
        if (-e $marker_inc_path) {
            $HIPCXXFLAGS .= " -isystem $marker_inc_path";
        }
    }

    $marker_lib_path = "$marker_path/bin/x86_64";
    if (-e $marker_lib_path) {
        $HIPLDFLAGS .= " -L$marker_lib_path -lCXLActivityLogger -Wl,--rpath=$marker_lib_path";
    }

    if (not $isWindows) {
        $HIPLDFLAGS .= " -lm";
    }

    if ($verbose & 0x2) {
        print ("HSA_PATH=$HSA_PATH\n");
        print ("HCC_HOME=$HCC_HOME\n");
    }

} elsif ($HIP_PLATFORM eq "nvcc") {
    $CUDA_PATH=$ENV{'CUDA_PATH'} // '/usr/local/cuda';
    $HIP_INCLUDE_PATH = "$HIP_PATH/include";
    if ($verbose & 0x2) {
        print ("CUDA_PATH=$CUDA_PATH\n");
    }

    $HIPCC="$CUDA_PATH/bin/nvcc";
    $HIPCXXFLAGS .= " -Wno-deprecated-gpu-targets ";
    $HIPCXXFLAGS .= " -isystem $CUDA_PATH/include";

    $HIPLDFLAGS = " -Wno-deprecated-gpu-targets -lcuda -lcudart -L$CUDA_PATH/lib64";
} else {
    printf ("error: unknown HIP_PLATFORM = '$HIP_PLATFORM'");
    exit (-1);
}

# Add paths to common HIP includes:
$HIPCXXFLAGS .= " -isystem $HIP_INCLUDE_PATH" ;

my $compileOnly = 0;
my $needCXXFLAGS = 0;  # need to add CXX flags to compile step
my $needLDFLAGS = 1;   # need to add LDFLAGS to compile step.
my $hasC = 0;          # options contain a c-style file (NVCC must force recognition as GPU file)
my $hasCU = 0;         # options contain a cu-style file (HCC must force recognition as GPU file)
my $needHipHcc = ($HIP_PLATFORM eq 'hcc');      # set if we need to link hip_hcc.o from src tree. (some builds, ie cmake, provide their own)
my $printHipVersion = 0;    # print HIP version
my $runCmd = 1;
my $buildDeps = 0;
my $linkType = 1;
my $setLinkType = 0;
my $coFormatv3 = 1;

my @options = ();
my @inputs  = ();

if ($verbose & 0x4) {
    print "hipcc-args: ", join (" ", @ARGV), "\n";
}

# Handle code object generation
my $ISACMD="";
if($HIP_PLATFORM eq "hcc"){
    $ISACMD .= "$HIP_PATH/bin/lpl ";
    if($ARGV[0] eq "--genco"){
        foreach $isaarg (@ARGV[1..$#ARGV]){
            $ISACMD .= " ";
            $ISACMD .= $isaarg;
        }
        if ($verbose & 0x1) {
            print "hipcc-cmd: ", $ISACMD, "\n";
        }
        system($ISACMD) and die();
        exit(0);
    }
}

if(($HIP_PLATFORM eq "hcc")){
    $ENV{HCC_EXTRA_LIBRARIES}="\n";
}

if($HIP_PLATFORM eq "nvcc"){
    $ISACMD .= "$HIP_PATH/bin/hipcc -ptx ";
    if($ARGV[0] eq "--genco"){
        foreach $isaarg (@ARGV[1..$#ARGV]){
            $ISACMD .= " ";
            $ISACMD .= $isaarg;
        }
        if ($verbose & 0x1) {
            print "hipcc-cmd: ", $ISACMD, "\n";
        }
        system($ISACMD) and die();
        exit(0);
    }
}

my $toolArgs = "";  # arguments to pass to the hcc or nvcc tool
my $optArg = ""; # -O args
my $targetOpt = '--amdgpu-target=';
my $targetsStr = "";

foreach $arg (@ARGV)
{
    $trimarg = $arg;
    $trimarg =~ s/^\s+|\s+$//g;  # Remive whitespace
    my $swallowArg = 0;
    if ($arg eq '-c' or $arg eq '--genco') {
        $compileOnly = 1;
        $needCXXFLAGS = 1;
        $needLDFLAGS  = 0;
    }
    if ($arg eq '-o') {
        $needLDFLAGS = 1;
    }

    if(($trimarg eq '-stdlib=libc++') and ($setStdLib eq 0))
    {
        $HIPCXXFLAGS .= " -stdlib=libc++";
        $setStdLib = 1;
    }

    # Check target selection option: --amdgpu-target=...
    if (substr($arg, 0, length($targetOpt)) eq $targetOpt) {
         # If targets string is not empty, add a comma before adding new target option value.
         $targetsStr .= ($targetsStr ? ',' : '');
         $targetsStr .=  substr($arg, length($targetOpt));
         $default_amdgpu_target = 0;
         # hip-clang does not accept --amdgpu-target= options.
         if ($HIP_PLATFORM eq 'clang') {
             $swallowArg = 1;
         }
    }

    # code object format parsing
    if ($trimarg eq '-mcode-object-v3') {
        $coFormatv3 = 1;
    }
    if ($trimarg eq '-mno-code-object-v3') {
        $coFormatv3 = 0;
    }

    if (($arg =~ /--genco/) and $HIP_PLATFORM eq 'clang' ) {
        $arg = "--cuda-device-only";
    }

    if(($trimarg eq '-stdlib=libstdc++') and ($setStdLib eq 0))
    {
        $HIPCXXFLAGS .= $HCC_WA_FLAGS;
        $setStdLib = 1;
    }
    if($trimarg eq '--version') {
        $printHipVersion = 1;
    }
    if($trimarg eq '--short-version') {
        $printHipVersion = 1;
        $runCmd = 0;
    }
    if($trimarg eq '-M') {
        $compileOnly = 1;
        $buildDeps = 1;
    }
    if($trimarg eq '-use_fast_math') {
        $HIPCXXFLAGS .= " -DHIP_FAST_MATH ";
    }
    if(($trimarg eq '-use-staticlib') and ($setLinkType eq 0))
    {
        $linkType = 0;
        $setLinkType = 1;
    }
    if(($trimarg eq '-use-sharedlib') and ($setLinkType eq 0))
    {
        $linkType = 1;
        $setLinkType = 1;
    }
    if($arg =~ m/^-O/)
    {
        $optArg = $arg;
    }

    ## This is a temporary workaround for CMake detection of OpenMP support.
    ## It should be removed when the OpenMP detection c++ test in CMake is updated
    ## and corrected CMake version is available.
    if((defined $HIP_COMPILER) and ($HIP_COMPILER eq "clang") and ($arg eq '-fopenmp')) {
        $HIPCXXFLAGS .= " -D_OPENMP "
    }

    ## process linker response file for hip-clang
    ## extract object files from static library and pass them directly to
    ## hip-clang in command line.
    ## ToDo: Remove this after hip-clang switch to lto and lld is able to
    ## handle clang-offload-bundler bundles.
    if ($arg =~ m/^-Wl,@/ and $HIP_PLATFORM eq 'clang') {
        my $file = substr $arg, 5;
        open my $in, "<:encoding(utf8)", $file or die "$file: $!";
        my $new_arg = "";
        my $tmpdir = get_temp_dir ();
        my $new_file = "$tmpdir/response_file";
        open my $out, ">", $new_file or die "$new_file: $!";
        while (my $line = <$in>) {
            chomp $line;
            if ($line =~ m/\.a$/ || $line =~ m/\.lo$/) {
                my $libFile = $line;
                my $path = abs_path($line);
                my @objs = split ('\n', `cd $tmpdir; ar xv $path`);
                ## Check if all files in .a are object files.
                my $allIsObj = 1;
                my $realObjs = "";
                foreach my $obj (@objs) {
                    chomp $obj;
                    $obj =~ s/^x - //;
                    $obj = "$tmpdir/$obj";
                    my $fileType = `file $obj`;
                    my $isObj = ($fileType =~ m/ELF/ or $fileType =~ m/COFF/);
                    $allIsObj = ($allIsObj and $isObj);
                    if ($isObj) {
                        $realObjs = ($realObjs . " " . $obj);
                    } else {
                        push (@inputs, $obj);
                        $new_arg = "$new_arg $obj";
                    }
                }
                chomp $realObjs;
                if ($allIsObj) {
                    print $out "$line\n";
                } elsif ($realObjs) {
                    my($libBaseName, $libDir, $libExt) = fileparse($libFile);
                    $libBaseName = mktemp($libBaseName . "XXXX") . $libExt;
                    system("cd $tmpdir; ar c $libBaseName $realObjs");
                    print $out "$tmpdir/$libBaseName\n";
                }
            } elsif ($line =~ m/\.o$/) {
                my $fileType = `file $line`;
                my $isObj = ($fileType =~ m/ELF/ or $fileType =~ m/COFF/);
                if ($isObj) {
                    print $out "$line\n";
                } else {
                    push (@inputs, $line);
                    $new_arg = "$new_arg $line";
                }
            } else {
                print $out "$line\n";
            }
        }
        close $in;
        close $out;
        $arg = "$new_arg -Wl,\@$new_file";
    } elsif (($arg =~ m/\.a$/ || $arg =~ m/\.lo$/) &&
             $HIP_PLATFORM eq 'clang') {
        ## process static library for hip-clang
        ## extract object files from static library and pass them directly to
        ## hip-clang.
        ## ToDo: Remove this after hip-clang switch to lto and lld is able to
        ## handle clang-offload-bundler bundles.
        
        my $new_arg = "";
        my $tmpdir = get_temp_dir ();
        my $libFile = $arg;
        my $path = abs_path($arg);
        my @objs = split ('\n', `cd $tmpdir; ar xv $path`);
        ## Check if all files in .a are object files.
        my $allIsObj = 1;
        my $realObjs = "";
        foreach my $obj (@objs) {
            chomp $obj;
            $obj =~ s/^x - //;
            $obj = "$tmpdir/$obj";
            my $fileType = `file $obj`;
            my $isObj = ($fileType =~ m/ELF/ or $fileType =~ m/COFF/);
            if ($fileType =~ m/ELF/) {
                my $sections = `readelf -e -W $obj`;
                $isObj = !($sections =~ m/__CLANG_OFFLOAD_BUNDLE__/);
            }
            $allIsObj = ($allIsObj and $isObj);
            if ($isObj) {
                $realObjs = ($realObjs . " " . $obj);
            } else {
                push (@inputs, $obj);
                if ($new_arg ne "") {
                    $new_arg .= " ";
                }
                $new_arg .= "$obj";
            }
        }
        chomp $realObjs;
        if ($allIsObj) {
            $new_arg = $arg;
        } elsif ($realObjs) {
            my($libBaseName, $libDir, $libExt) = fileparse($libFile);
            $libBaseName = mktemp($libBaseName . "XXXX") . $libExt;
            system("cd $tmpdir; ar c $libBaseName $realObjs");
            $new_arg .= " $tmpdir/$libBaseName";
        }
        $arg = "$new_arg";
        if ($toolArgs =~ m/-Xlinker$/) {
            $toolArgs = substr $toolArgs, 0, -8;
            chomp $toolArgs;
        }
    } elsif ($arg =~ m/^-/) {
        # options start with -

        # Process HIPCC options here:
        if ($arg =~ m/^--hipcc/) {
            $swallowArg = 1;
            #if $arg eq "--hipcc_profile") {  # Example argument here, hipcc
            #
            #}
        } else {
            push (@options, $arg);
        }
        #print "O: <$arg>\n";
    } else {
        # input files and libraries
        if (($arg =~ /\.cpp$/) or ($arg =~ /\.cxx$/) or ($arg =~ /\.c$/) or ($arg =~ /\.cc$/) ) {
            $hasC = 1;
            $needCXXFLAGS = 1;
            if ($HIP_PLATFORM eq 'clang') {
                $toolArgs .= " -x hip"
            }
        }
        if (($arg =~ /\.cu$/) or ($arg =~ /\.cuh$/) or ($arg =~ /\.hip$/)) {
            $hasCU = 1;
            $needCXXFLAGS = 1;
            if ($HIP_PLATFORM eq 'clang') {
                $toolArgs .= " -x hip"
            }
        }

        push (@inputs, $arg);
        #print "I: <$arg>\n";
    }
    $toolArgs .= " $arg" unless $swallowArg;
}

if($HIP_PLATFORM eq "hcc" or $HIP_PLATFORM eq "clang"){
    # No AMDGPU target specified at commandline. So look for HCC_AMDGPU_TARGET
    if($default_amdgpu_target eq 1) {
        if (defined $ENV{HCC_AMDGPU_TARGET}) {
            $targetsStr = $ENV{HCC_AMDGPU_TARGET};
        } else {
            # Else try using rocm_agent_enumerator
            $ROCM_AGENT_ENUM = "${ROCM_PATH}/bin/rocm_agent_enumerator";
            $targetsStr = `${ROCM_AGENT_ENUM} -t GPU`;
            $targetsStr =~ s/\n/,/g;
        }
        $default_amdgpu_target = 0;
    }

    # Parse the targets collected in targetStr and set corresponding compiler options.
    my @targets = split(',', $targetsStr);

    if($HIP_PLATFORM eq "hcc") {
        $GPU_ARCH_OPT = " --amdgpu-target=";
    } else {
        $GPU_ARCH_OPT = " --cuda-gpu-arch=";
    }

    foreach my $val (@targets) {
        # Ignore 'gfx000' target reported by rocm_agent_enumerator.
        if ($val ne 'gfx000') {
            # Construct an arch macro to be passed to the compiler.
            # Example: gfx900 --> -D__HIP_ARCH_GFX900__=1
            my $archMacro = ' -D__HIP_ARCH_' . uc($val) . '__=1 ';
            # Add the arch option and macro to the compiler options.
            $GPU_ARCH_ARG = $GPU_ARCH_OPT . $val;
            $HIPLDFLAGS .= $GPU_ARCH_ARG;
            $HIPCXXFLAGS .= $archMacro;
            if ($HIP_PLATFORM eq 'clang') {
                $HIPCXXFLAGS .= $GPU_ARCH_ARG;
            }

            # If the specified target is not in the list of known target names, emit a warning.
            if (grep(/$val/, @knownTargets) eq 0) {
                print "Warning: The specified HIP target: $val is unknown. Correct compilation is not guaranteed.\n";
            }
        }
    }

    # rocm_agent_enumerator failed! Throw an error and die if linking is required
    if ($default_amdgpu_target eq 1 and $compileOnly eq 0) {
        print "No valid AMD GPU target was either specified or found. Please specify a valid target using --amdgpu-target=" and die();
    }

    $ENV{HCC_EXTRA_LIBRARIES}="\n";
}

# hcc defaults to v2, so we need to convert to the appropriate flag
# hip-clang defaults to v3, so we don't need to do anything
if ($coFormatv3 and $HIP_PLATFORM eq 'hcc') {
    $HIPLDFLAGS .= " -mcode-object-v3";
    $HIPCXXFLAGS .= " -mcode-object-v3";
}

if ($hasC and $HIP_PLATFORM eq 'nvcc') {
    $HIPCXXFLAGS .= " -x cu";
}
if ($hasCU and $HIP_PLATFORM eq 'hcc') {
    $HIPCXXFLAGS .= " -x c++";
}

if ($buildDeps and $HIP_PLATFORM eq 'nvcc') {
    $HIPCXXFLAGS .= " -M -D__CUDACC__";
}

if ($buildDeps and $HIP_PLATFORM eq 'clang') {
    $HIPCXXFLAGS .= " --cuda-host-only";
}

# Add --hip-link only if there are no source files.
if (!$needCXXFLAGS and $HIP_PLATFORM eq 'clang') {
    $HIPLDFLAGS .= " --hip-link";
}

if ($setStdLib eq 0 and $HIP_PLATFORM eq 'hcc')
{
    $HIPCXXFLAGS .= $HCC_WA_FLAGS;
}

if ($needHipHcc) {
    if ($linkType eq 0) {
        substr($HIPLDFLAGS,0,0) = "  $HIP_LIB_PATH/libhip_hcc_static.a " ;
    } else {
        substr($HIPLDFLAGS,0,0) = "  -Wl,--enable-new-dtags -Wl,--rpath=$HIP_LIB_PATH:$ROCM_PATH/lib $HIP_LIB_PATH/libhip_hcc.so ";
    }
}

# hipcc currrently requires separate compilation of source files, ie it is not possible to pass
# CPP files combined with .O files
# Reason is that NVCC uses the file extension to determine whether to compile in CUDA mode or
# pass-through CPP mode.

if ($HIP_PLATFORM eq "clang") {
    # Set default optimization level to -O3 for hip-clang.
    if ($optArg eq "") {
        $HIPCXXFLAGS .= " -O3";
        $HIPLDFLAGS .= " -O3";
    }
    # Do not pass -mllvm on Windows since there is a clang bug causing duplicate -mllvm options in clang -cc1 on Windows.
    # ToDo : remove restriction for Windows after clang bug is fixed.
    if ($optArg ne "-O0" and not $isWindows) {
        $HIPCXXFLAGS .= " -mllvm -amdgpu-early-inline-all=true -mllvm -amdgpu-function-calls=false";
        if ($needLDFLAGS and not $needCXXFLAGS) {
            $HIPLDFLAGS .= " -mllvm -amdgpu-early-inline-all=true -mllvm -amdgpu-function-calls=false";
        }
    }
    $HIP_DEVLIB_FLAGS = " --hip-device-lib-path=$DEVICE_LIB_PATH";
    $HIPCXXFLAGS .= " $HIP_DEVLIB_FLAGS";
    if (not $isWindows) {
        $HIPLDFLAGS .= " -lgcc_s -lgcc -lpthread -lm";
    }
}


if ($HIPCC_COMPILE_FLAGS_APPEND) {
    $HIPCXXFLAGS .= " $HIPCC_COMPILE_FLAGS_APPEND";
}
if ($HIPCC_LINK_FLAGS_APPEND) {
    $HIPLDFLAGS .= " $HIPCC_LINK_FLAGS_APPEND";
}

my $CMD="$HIPCC";
if ($needCXXFLAGS) {
    $CMD .= " $HIPCXXFLAGS";
}
if ($needLDFLAGS and not $compileOnly) {
    $CMD .= " $HIPLDFLAGS";
}
$CMD .= " $toolArgs";

if ($verbose & 0x1) {
    print "hipcc-cmd: ", $CMD, "\n";
}

if ($printHipVersion) {
    if ($runCmd) {
        print "HIP version: "
    }
    print $HIP_VERSION, "\n";
}
if ($runCmd) {
    if ($HIP_PLATFORM eq "hcc" and exists($hipConfig{'HCC_VERSION'}) and $HCC_VERSION ne $hipConfig{'HCC_VERSION'}) {
        print ("HIP ($HIP_PATH) was built using hcc $hipConfig{'HCC_VERSION'}, but you are using $HCC_HOME/hcc with version $HCC_VERSION from hipcc. Please rebuild HIP including cmake or update HCC_HOME variable.\n") ;
        die unless $ENV{'HIP_IGNORE_HCC_VERSION'};
    }
    system ("$CMD");
    if ($? == -1) {
        print "failed to execute: $!\n";
        exit($?);
    }
    elsif ($? & 127) {
        printf "child died with signal %d, %s coredump\n",
        ($? & 127),  ($? & 128) ? 'with' : 'without';
        exit($?);
    }
    else {
         $CMD_EXIT_CODE = $? >> 8;
    }
    $? or delete_temp_dirs ();
    exit($CMD_EXIT_CODE);
}

# vim: ts=4:sw=4:expandtab:smartindent
