1*0eae32dcSDimitry Andric //===--- HIPUtility.cpp - Common HIP Tool Chain Utilities -------*- C++ -*-===// 2*0eae32dcSDimitry Andric // 3*0eae32dcSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0eae32dcSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0eae32dcSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0eae32dcSDimitry Andric // 7*0eae32dcSDimitry Andric //===----------------------------------------------------------------------===// 8*0eae32dcSDimitry Andric 9*0eae32dcSDimitry Andric #include "HIPUtility.h" 10*0eae32dcSDimitry Andric #include "CommonArgs.h" 11*0eae32dcSDimitry Andric #include "clang/Driver/Compilation.h" 12*0eae32dcSDimitry Andric #include "llvm/ADT/StringRef.h" 13*0eae32dcSDimitry Andric #include "llvm/ADT/Triple.h" 14*0eae32dcSDimitry Andric #include "llvm/Support/Path.h" 15*0eae32dcSDimitry Andric 16*0eae32dcSDimitry Andric using namespace clang::driver; 17*0eae32dcSDimitry Andric using namespace clang::driver::tools; 18*0eae32dcSDimitry Andric using namespace llvm::opt; 19*0eae32dcSDimitry Andric 20*0eae32dcSDimitry Andric #if defined(_WIN32) || defined(_WIN64) 21*0eae32dcSDimitry Andric #define NULL_FILE "nul" 22*0eae32dcSDimitry Andric #else 23*0eae32dcSDimitry Andric #define NULL_FILE "/dev/null" 24*0eae32dcSDimitry Andric #endif 25*0eae32dcSDimitry Andric 26*0eae32dcSDimitry Andric namespace { 27*0eae32dcSDimitry Andric const unsigned HIPCodeObjectAlign = 4096; 28*0eae32dcSDimitry Andric } // namespace 29*0eae32dcSDimitry Andric 30*0eae32dcSDimitry Andric // Constructs a triple string for clang offload bundler. 31*0eae32dcSDimitry Andric static std::string normalizeForBundler(const llvm::Triple &T, 32*0eae32dcSDimitry Andric bool HasTargetID) { 33*0eae32dcSDimitry Andric return HasTargetID ? (T.getArchName() + "-" + T.getVendorName() + "-" + 34*0eae32dcSDimitry Andric T.getOSName() + "-" + T.getEnvironmentName()) 35*0eae32dcSDimitry Andric .str() 36*0eae32dcSDimitry Andric : T.normalize(); 37*0eae32dcSDimitry Andric } 38*0eae32dcSDimitry Andric 39*0eae32dcSDimitry Andric // Construct a clang-offload-bundler command to bundle code objects for 40*0eae32dcSDimitry Andric // different devices into a HIP fat binary. 41*0eae32dcSDimitry Andric void HIP::constructHIPFatbinCommand(Compilation &C, const JobAction &JA, 42*0eae32dcSDimitry Andric llvm::StringRef OutputFileName, 43*0eae32dcSDimitry Andric const InputInfoList &Inputs, 44*0eae32dcSDimitry Andric const llvm::opt::ArgList &Args, 45*0eae32dcSDimitry Andric const Tool &T) { 46*0eae32dcSDimitry Andric // Construct clang-offload-bundler command to bundle object files for 47*0eae32dcSDimitry Andric // for different GPU archs. 48*0eae32dcSDimitry Andric ArgStringList BundlerArgs; 49*0eae32dcSDimitry Andric BundlerArgs.push_back(Args.MakeArgString("-type=o")); 50*0eae32dcSDimitry Andric BundlerArgs.push_back( 51*0eae32dcSDimitry Andric Args.MakeArgString("-bundle-align=" + Twine(HIPCodeObjectAlign))); 52*0eae32dcSDimitry Andric 53*0eae32dcSDimitry Andric // ToDo: Remove the dummy host binary entry which is required by 54*0eae32dcSDimitry Andric // clang-offload-bundler. 55*0eae32dcSDimitry Andric std::string BundlerTargetArg = "-targets=host-x86_64-unknown-linux"; 56*0eae32dcSDimitry Andric std::string BundlerInputArg = "-inputs=" NULL_FILE; 57*0eae32dcSDimitry Andric 58*0eae32dcSDimitry Andric // AMDGCN: 59*0eae32dcSDimitry Andric // For code object version 2 and 3, the offload kind in bundle ID is 'hip' 60*0eae32dcSDimitry Andric // for backward compatibility. For code object version 4 and greater, the 61*0eae32dcSDimitry Andric // offload kind in bundle ID is 'hipv4'. 62*0eae32dcSDimitry Andric std::string OffloadKind = "hip"; 63*0eae32dcSDimitry Andric auto &TT = T.getToolChain().getTriple(); 64*0eae32dcSDimitry Andric if (TT.isAMDGCN() && getAMDGPUCodeObjectVersion(C.getDriver(), Args) >= 4) 65*0eae32dcSDimitry Andric OffloadKind = OffloadKind + "v4"; 66*0eae32dcSDimitry Andric for (const auto &II : Inputs) { 67*0eae32dcSDimitry Andric const auto *A = II.getAction(); 68*0eae32dcSDimitry Andric auto ArchStr = llvm::StringRef(A->getOffloadingArch()); 69*0eae32dcSDimitry Andric BundlerTargetArg += 70*0eae32dcSDimitry Andric "," + OffloadKind + "-" + normalizeForBundler(TT, !ArchStr.empty()); 71*0eae32dcSDimitry Andric if (!ArchStr.empty()) 72*0eae32dcSDimitry Andric BundlerTargetArg += "-" + ArchStr.str(); 73*0eae32dcSDimitry Andric BundlerInputArg = BundlerInputArg + "," + II.getFilename(); 74*0eae32dcSDimitry Andric } 75*0eae32dcSDimitry Andric BundlerArgs.push_back(Args.MakeArgString(BundlerTargetArg)); 76*0eae32dcSDimitry Andric BundlerArgs.push_back(Args.MakeArgString(BundlerInputArg)); 77*0eae32dcSDimitry Andric 78*0eae32dcSDimitry Andric std::string Output = std::string(OutputFileName); 79*0eae32dcSDimitry Andric auto *BundlerOutputArg = 80*0eae32dcSDimitry Andric Args.MakeArgString(std::string("-outputs=").append(Output)); 81*0eae32dcSDimitry Andric BundlerArgs.push_back(BundlerOutputArg); 82*0eae32dcSDimitry Andric 83*0eae32dcSDimitry Andric const char *Bundler = Args.MakeArgString( 84*0eae32dcSDimitry Andric T.getToolChain().GetProgramPath("clang-offload-bundler")); 85*0eae32dcSDimitry Andric C.addCommand(std::make_unique<Command>( 86*0eae32dcSDimitry Andric JA, T, ResponseFileSupport::None(), Bundler, BundlerArgs, Inputs, 87*0eae32dcSDimitry Andric InputInfo(&JA, Args.MakeArgString(Output)))); 88*0eae32dcSDimitry Andric } 89*0eae32dcSDimitry Andric 90*0eae32dcSDimitry Andric /// Add Generated HIP Object File which has device images embedded into the 91*0eae32dcSDimitry Andric /// host to the argument list for linking. Using MC directives, embed the 92*0eae32dcSDimitry Andric /// device code and also define symbols required by the code generation so that 93*0eae32dcSDimitry Andric /// the image can be retrieved at runtime. 94*0eae32dcSDimitry Andric void HIP::constructGenerateObjFileFromHIPFatBinary( 95*0eae32dcSDimitry Andric Compilation &C, const InputInfo &Output, const InputInfoList &Inputs, 96*0eae32dcSDimitry Andric const ArgList &Args, const JobAction &JA, const Tool &T) { 97*0eae32dcSDimitry Andric const ToolChain &TC = T.getToolChain(); 98*0eae32dcSDimitry Andric std::string Name = std::string(llvm::sys::path::stem(Output.getFilename())); 99*0eae32dcSDimitry Andric 100*0eae32dcSDimitry Andric // Create Temp Object File Generator, 101*0eae32dcSDimitry Andric // Offload Bundled file and Bundled Object file. 102*0eae32dcSDimitry Andric // Keep them if save-temps is enabled. 103*0eae32dcSDimitry Andric const char *McinFile; 104*0eae32dcSDimitry Andric const char *BundleFile; 105*0eae32dcSDimitry Andric if (C.getDriver().isSaveTempsEnabled()) { 106*0eae32dcSDimitry Andric McinFile = C.getArgs().MakeArgString(Name + ".mcin"); 107*0eae32dcSDimitry Andric BundleFile = C.getArgs().MakeArgString(Name + ".hipfb"); 108*0eae32dcSDimitry Andric } else { 109*0eae32dcSDimitry Andric auto TmpNameMcin = C.getDriver().GetTemporaryPath(Name, "mcin"); 110*0eae32dcSDimitry Andric McinFile = C.addTempFile(C.getArgs().MakeArgString(TmpNameMcin)); 111*0eae32dcSDimitry Andric auto TmpNameFb = C.getDriver().GetTemporaryPath(Name, "hipfb"); 112*0eae32dcSDimitry Andric BundleFile = C.addTempFile(C.getArgs().MakeArgString(TmpNameFb)); 113*0eae32dcSDimitry Andric } 114*0eae32dcSDimitry Andric HIP::constructHIPFatbinCommand(C, JA, BundleFile, Inputs, Args, T); 115*0eae32dcSDimitry Andric 116*0eae32dcSDimitry Andric // Create a buffer to write the contents of the temp obj generator. 117*0eae32dcSDimitry Andric std::string ObjBuffer; 118*0eae32dcSDimitry Andric llvm::raw_string_ostream ObjStream(ObjBuffer); 119*0eae32dcSDimitry Andric 120*0eae32dcSDimitry Andric auto HostTriple = 121*0eae32dcSDimitry Andric C.getSingleOffloadToolChain<Action::OFK_Host>()->getTriple(); 122*0eae32dcSDimitry Andric 123*0eae32dcSDimitry Andric // Add MC directives to embed target binaries. We ensure that each 124*0eae32dcSDimitry Andric // section and image is 16-byte aligned. This is not mandatory, but 125*0eae32dcSDimitry Andric // increases the likelihood of data to be aligned with a cache block 126*0eae32dcSDimitry Andric // in several main host machines. 127*0eae32dcSDimitry Andric ObjStream << "# HIP Object Generator\n"; 128*0eae32dcSDimitry Andric ObjStream << "# *** Automatically generated by Clang ***\n"; 129*0eae32dcSDimitry Andric if (HostTriple.isWindowsMSVCEnvironment()) { 130*0eae32dcSDimitry Andric ObjStream << " .section .hip_fatbin, \"dw\"\n"; 131*0eae32dcSDimitry Andric } else { 132*0eae32dcSDimitry Andric ObjStream << " .protected __hip_fatbin\n"; 133*0eae32dcSDimitry Andric ObjStream << " .type __hip_fatbin,@object\n"; 134*0eae32dcSDimitry Andric ObjStream << " .section .hip_fatbin,\"a\",@progbits\n"; 135*0eae32dcSDimitry Andric } 136*0eae32dcSDimitry Andric ObjStream << " .globl __hip_fatbin\n"; 137*0eae32dcSDimitry Andric ObjStream << " .p2align " << llvm::Log2(llvm::Align(HIPCodeObjectAlign)) 138*0eae32dcSDimitry Andric << "\n"; 139*0eae32dcSDimitry Andric ObjStream << "__hip_fatbin:\n"; 140*0eae32dcSDimitry Andric ObjStream << " .incbin "; 141*0eae32dcSDimitry Andric llvm::sys::printArg(ObjStream, BundleFile, /*Quote=*/true); 142*0eae32dcSDimitry Andric ObjStream << "\n"; 143*0eae32dcSDimitry Andric ObjStream.flush(); 144*0eae32dcSDimitry Andric 145*0eae32dcSDimitry Andric // Dump the contents of the temp object file gen if the user requested that. 146*0eae32dcSDimitry Andric // We support this option to enable testing of behavior with -###. 147*0eae32dcSDimitry Andric if (C.getArgs().hasArg(options::OPT_fhip_dump_offload_linker_script)) 148*0eae32dcSDimitry Andric llvm::errs() << ObjBuffer; 149*0eae32dcSDimitry Andric 150*0eae32dcSDimitry Andric // Open script file and write the contents. 151*0eae32dcSDimitry Andric std::error_code EC; 152*0eae32dcSDimitry Andric llvm::raw_fd_ostream Objf(McinFile, EC, llvm::sys::fs::OF_None); 153*0eae32dcSDimitry Andric 154*0eae32dcSDimitry Andric if (EC) { 155*0eae32dcSDimitry Andric C.getDriver().Diag(clang::diag::err_unable_to_make_temp) << EC.message(); 156*0eae32dcSDimitry Andric return; 157*0eae32dcSDimitry Andric } 158*0eae32dcSDimitry Andric 159*0eae32dcSDimitry Andric Objf << ObjBuffer; 160*0eae32dcSDimitry Andric 161*0eae32dcSDimitry Andric ArgStringList McArgs{"-triple", Args.MakeArgString(HostTriple.normalize()), 162*0eae32dcSDimitry Andric "-o", Output.getFilename(), 163*0eae32dcSDimitry Andric McinFile, "--filetype=obj"}; 164*0eae32dcSDimitry Andric const char *Mc = Args.MakeArgString(TC.GetProgramPath("llvm-mc")); 165*0eae32dcSDimitry Andric C.addCommand(std::make_unique<Command>(JA, T, ResponseFileSupport::None(), Mc, 166*0eae32dcSDimitry Andric McArgs, Inputs, Output)); 167*0eae32dcSDimitry Andric } 168