#!/usr/bin/perl -w

use File::Basename;

# 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 (default /opt/rocm/hcc).  Used on AMD platforms only.
# HSA_PATH  : Path to HSA dir (default /opt/rocm/hsa).  Used on AMD platforms only.

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

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

$HIP_PATH=$ENV{'HIP_PATH'};
$HIP_PATH=dirname (dirname $0) unless defined $HIP_PATH;        # use parent directory of hipcc

#print "HIP_PATH=$HIP_PATH\n";

$CUDA_PATH=$ENV{'CUDA_PATH'};
$CUDA_PATH='/usr/local/cuda' unless defined $CUDA_PATH;

$CODEXL_PATH = $ENV{'CODEXL_PATH'};
$CODEXL_PATH = "/opt/AMD/CodeXL" unless defined $CODEXL_PATH;
$marker_path = "$CODEXL_PATH/SDK/AMDTActivityLogger";

#---
#HIP_PLATFORM controls whether to use NVCC or HCC for compilation:
$HIP_PLATFORM=$ENV{'HIP_PLATFORM'};
if (not defined $HIP_PLATFORM and (-e "$CUDA_PATH/bin/nvcc")) {
    $HIP_PLATFORM="nvcc";
} 
$HIP_PLATFORM="hcc" unless defined $HIP_PLATFORM;


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

$enablestdcpplib = 0;

if ($HIP_PLATFORM eq "hcc") {
	$HSA_PATH=$ENV{'HSA_PATH'};
	$HSA_PATH="/opt/rocm/hsa" unless defined $HSA_PATH;

    $HCC_HOME=$ENV{'HCC_HOME'};
    $HCC_HOME="/opt/rocm/hcc" unless defined $HCC_HOME;

    $ROCM_PATH=$ENV{'ROCM_PATH'};
    $ROCM_PATH="/opt/rocm" unless defined $ROCM_PATH;

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

    $HIPCC=$HCC;
    $HIPCXXFLAGS = $HCCFLAGS;
    $HIPCXXFLAGS .= " -I$HIP_PATH/include/hcc_detail/cuda";
    $HIPCXXFLAGS .= " -I$HSA_PATH/include";
	$HIPLDFLAGS =  "-hc -L$HCC_HOME/lib -Wl,--rpath=$HCC_HOME/lib -lc++ -ldl -lpthread -Wl,--whole-archive -lmcwamp -Wl,--no-whole-archive";
    # Suppress linker warnings in case HCC distribution contains OpenCL/SPIR symbols
    $HIPLDFLAGS .= " -Wl,--defsym=_binary_kernel_spir_end=0 -Wl,--defsym=_binary_kernel_spir_start=0 -Wl,--defsym=_binary_kernel_cl_start=0 -Wl,--defsym=_binary_kernel_cl_end=0";
    # Satisfy HCC dependencies
    $HIPLDFLAGS .= " -lc++abi";
	$HIPLDFLAGS .= " -L$HSA_PATH/lib -L$ROCM_PATH/lib -lhsa-runtime64 -lhc_am";

    # 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.
    $marker_inc_path = "$marker_path/include";
    if (-e $marker_inc_path) {
        $HIPCXXFLAGS .= " -I$marker_inc_path";
    }

    $marker_lib_path = "$marker_path/bin/x86_64";
    if (-e $marker_lib_path) {
        $HIPLDFLAGS .= " -L$marker_lib_path -lAMDTActivityLogger";
    }

	# Add C++ libs for GCC.
	$HIPLDFLAGS .= " -lstdc++";
	$HIPLDFLAGS .= " -lm";

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

} elsif ($HIP_PLATFORM eq "nvcc") {

    $HIPCC="$CUDA_PATH/bin/nvcc";
    $HIPCXXFLAGS .= " -I$CUDA_PATH/include";

	$HIPLDFLAGS = "";
} else {
	printf ("error: unknown HIP_PLATFORM = '$HIP_PLATFORM'");
	exit (-1);
}


# Add paths to common HIP includes:
$HIPCXXFLAGS .= " -I$HIP_PATH/include" ;

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 @options = ();
my @inputs  = ();

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

my $toolArgs = "";  # arguments to pass to the hcc or nvcc tool
foreach $arg (@ARGV) 
{
    my $swallowArg = 0;
    if ($arg eq '-c') {
        $compileOnly = 1;
        $needCXXFLAGS = 1;
        $needLDFLAGS  = 0;
    }
    if ($arg eq '-o') {
        $needLDFLAGS = 1;
    }

    if($arg eq '-stdlib=libstdc++' and $enablestdcpplib eq 0)
    {
	$HIPCXXFLAGS .= " -stdlib=libstdc++";
	$enablestdcpplib = 1;
    }

    if ($arg =~ m/^-/) {
        # options start with -
        
        # Process HIPCC options here:
        if ($arg =~ m/^--hipcc/) {
            $swallowArg = 1;
            if ($arg eq "--hipcc_explicit_lib") {
                # Some environments (ie cmake tests) already link their own hip_hcc.o, so don't add here:
                $needHipHcc = 0;
            }
        } else {
            push (@options, $arg);
        }
        #print "O: <$arg>\n";
    } else  {
        # input files and libraries 
        if (($arg =~ /\.cpp$/) or ($arg =~ /\.c$/) or ($arg =~ /\.cc$/) ) {
            $hasC = 1;
            $needCXXFLAGS = 1;
        } 
        if (($arg =~ /\.cu$/) or ($arg =~ /\.cuh$/)) {
            $hasCU = 1;
            $needCXXFLAGS = 1;
        }

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

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

if ($enablestdcpplib eq 0 and $HIP_PLATFORM eq 'hcc')
{
    $HIPCXXFLAGS .= " -stdlib=libc++";
}

if ($needHipHcc) {
    $HIP_USE_SHARED_LIBRARY = $ENV{'HIP_USE_SHARED_LIBRARY'};
    $HIP_USE_SHARED_LIBRARY = 0 unless defined $HIP_USE_SHARED_LIBRARY;

    #$HIPLDFLAGS .= " -L/opt/rocm/hip/lib -lhip_hcc" ;
    if ($HIP_USE_SHARED_LIBRARY) {
        $HIPLDFLAGS .= " -L$HIP_PATH/lib -Wl,--rpath=$HIP_PATH/lib -lhip_hcc";
    } else {
        $HIPLDFLAGS .= " $HIP_PATH/lib/device_util.cpp.o $HIP_PATH/lib/hip_device.cpp.o $HIP_PATH/lib/hip_error.cpp.o $HIP_PATH/lib/hip_event.cpp.o $HIP_PATH/lib/hip_hcc.cpp.o $HIP_PATH/lib/hip_memory.cpp.o $HIP_PATH/lib/hip_peer.cpp.o $HIP_PATH/lib/hip_stream.cpp.o $HIP_PATH/lib/staging_buffer.cpp.o $HIP_PATH/lib/hip_ldg.cpp.o";
    }
}

# 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.



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

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

system ("$CMD") and die ();
