#!/usr/bin/perl -w # Need perl > 5.10 to use logic-defined or use 5.006; use v5.10.1; 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); } #--- # Function to parse config file sub parse_config_file { my ($file, $config) = @_; if (open (CONFIG, "$file")) { while () { 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=hippc args $HIP_PATH=$ENV{'HIP_PATH'} // dirname (dirname $0); # use parent directory of hipcc #--- # Read .hipInfo my %hipConfig = (); parse_config_file("$HIP_PATH/lib/.hipInfo", \%hipConfig); #--- #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_VERSION_MAJOR, $HIP_VERSION_MINOR, $HIP_VERSION_PATCH) = split(/\./, $HIP_VERSION); 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 if ($HIP_PLATFORM eq "hcc") { $HSA_PATH=$ENV{'HSA_PATH'} // "/opt/rocm/hsa"; $HCC_HOME=$ENV{'HCC_HOME'} // $hipConfig{'HCC_HOME'} // "/opt/rocm/hcc"; $HCC_VERSION=`${HCC_HOME}/bin/hcc --version | cut -d" " -f9 | tr -d "\n"`; $ROCM_PATH=$ENV{'ROCM_PATH'} // "/opt/rocm"; $HIP_ATP_MARKER=$ENV{'HIP_ATP_MARKER'}; $marker_path = "$ROCM_PATH/profiler/CXLActivityLogger"; $ROCM_TARGET=$ENV{'ROCM_TARGET'} // "fiji"; # 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/hip/hcc_detail/cuda"; $HIPCXXFLAGS .= " -I$HSA_PATH/include"; $HIPCXXFLAGS .= " -Wno-deprecated-register"; $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 $HOST_OSNAME= `cat /etc/os-release | grep "^ID\=" | cut -d= -f2 | tr -d '\n'`; $HOST_OSVER= `cat /etc/os-release | grep "^VERSION_ID\=" | cut -d= -f2 | tr -d '\n'`; if ($HOST_OSNAME eq "ubuntu" and $HOST_OSVER eq "\"16.04\"") { # No additional flags required } else { $HIPLDFLAGS .= " -Wl,--defsym=_binary_kernel_spir_end=1 -Wl,--defsym=_binary_kernel_spir_start=1 -Wl,--defsym=_binary_kernel_cl_start=1 -Wl,--defsym=_binary_kernel_cl_end=1"; } # Satisfy HCC dependencies $HIPLDFLAGS .= " -lc++abi -lsupc++"; $HIPLDFLAGS .= " -L$HSA_PATH/lib -L$ROCM_PATH/lib -lhsa-runtime64 -lhc_am -lhsakmt"; # Handle ROCm target platform if ($ROCM_TARGET eq "fiji") { $HIPLDFLAGS .= " -amdgpu-target=AMD:AMDGPU:8:0:3"; } if ($ROCM_TARGET eq "carrizo") { $HIPLDFLAGS .= " -amdgpu-target=AMD:AMDGPU:8:0:1"; } if ($ROCM_TARGET eq "hawaii") { $HIPLDFLAGS .= " -amdgpu-target=AMD:AMDGPU:7:0:1"; } # 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 .= " -I$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"; } # 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") { if ($verbose & 0x2) { print ("CUDA_PATH=$CUDA_PATH\n"); } $CUDA_PATH=$ENV{'CUDA_PATH'} // '/usr/local/cuda'; $HIPCC="$CUDA_PATH/bin/nvcc"; $HIPCXXFLAGS .= " -I$CUDA_PATH/include"; $HIPLDFLAGS = "-lcuda -lcudart -L$CUDA_PATH/lib64"; } else { printf ("error: unknown HIP_PLATFORM = '$HIP_PLATFORM'"); exit (-1); } # Add paths to common HIP includes: $HIPCXXFLAGS .= " -I$HIP_PATH/include -DHIP_VERSION_MAJOR=$HIP_VERSION_MAJOR -DHIP_VERSION_MINOR=$HIP_VERSION_MINOR -DHIP_VERSION_PATCH=$HIP_VERSION_PATCH" ; 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 @options = (); my @inputs = (); if ($verbose & 0x4) { print "hipcc-args: ", join (" ", @ARGV), "\n"; } # Handle code object generation my $ISACMD=""; if($HIP_PLATFORM eq "hcc"){ $ISACMD .= "set ROCM_PATH=$ROCM_PATH && set ROCM_TARGET=$ROCM_TARGET && $HIP_PATH/bin/hccgenco.sh "; 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"){ $EXPORT_LL=" "; $ENV{HCC_EXTRA_LIBRARIES}="$HIP_PATH/lib/hip_ir.ll\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 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=libc++') and ($setStdLib eq 0)) { $HIPCXXFLAGS .= " -stdlib=libc++"; $setStdLib = 1; } if(($arg eq '-stdlib=libstdc++') and ($setStdLib eq 0)) { $HIPCXXFLAGS .= " -stdlib=libstdc++"; $setStdLib = 1; } if($arg eq '--version') { $printHipVersion = 1; } if($arg eq '--short-version') { $printHipVersion = 1; $runCmd = 0; } if($arg eq '-M') { $compileOnly = 1; $buildDeps = 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 ($buildDeps and $HIP_PLATFORM eq 'nvcc') { $HIPCXXFLAGS .= " -M -D__CUDACC__"; } if ($setStdLib eq 0 and $HIP_PLATFORM eq 'hcc') { $HIPCXXFLAGS .= " -stdlib=libstdc++"; } if ($needHipHcc) { $HIP_LIB_TYPE = $hipConfig{'HIP_LIB_TYPE'} // 0; if ($HIP_LIB_TYPE eq 0) { $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/hip_ldg.cpp.o $HIP_PATH/lib/hip_fp16.cpp.o $HIP_PATH/lib/hip_context.cpp.o $HIP_PATH/lib/hip_module.cpp.o"; } elsif ($HIP_LIB_TYPE eq 1) { $HIPLDFLAGS .= " -L$HIP_PATH/lib -lhip_hcc" ; } else { $HIPLDFLAGS .= " -L$HIP_PATH/lib -Wl,--rpath=$HIP_PATH/lib -lhip_hcc"; } } # 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"; } 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 $HCC_VERSION. Please rebuild HIP including cmake.\n") && die (); } system ("$CMD") and die (); }