Path: blob/main/contrib/llvm-project/clang/lib/Driver/ToolChains/HIPUtility.cpp
35294 views
//===--- HIPUtility.cpp - Common HIP Tool Chain Utilities -------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "HIPUtility.h"9#include "Clang.h"10#include "CommonArgs.h"11#include "clang/Driver/Compilation.h"12#include "clang/Driver/Options.h"13#include "llvm/ADT/StringExtras.h"14#include "llvm/ADT/StringRef.h"15#include "llvm/Object/Archive.h"16#include "llvm/Object/ObjectFile.h"17#include "llvm/Support/MD5.h"18#include "llvm/Support/MemoryBuffer.h"19#include "llvm/Support/Path.h"20#include "llvm/Support/raw_ostream.h"21#include "llvm/TargetParser/Triple.h"22#include <deque>23#include <set>2425using namespace clang;26using namespace clang::driver;27using namespace clang::driver::tools;28using namespace llvm::opt;29using llvm::dyn_cast;3031#if defined(_WIN32) || defined(_WIN64)32#define NULL_FILE "nul"33#else34#define NULL_FILE "/dev/null"35#endif3637namespace {38const unsigned HIPCodeObjectAlign = 4096;39} // namespace4041// Constructs a triple string for clang offload bundler.42static std::string normalizeForBundler(const llvm::Triple &T,43bool HasTargetID) {44return HasTargetID ? (T.getArchName() + "-" + T.getVendorName() + "-" +45T.getOSName() + "-" + T.getEnvironmentName())46.str()47: T.normalize();48}4950// Collect undefined __hip_fatbin* and __hip_gpubin_handle* symbols from all51// input object or archive files.52class HIPUndefinedFatBinSymbols {53public:54HIPUndefinedFatBinSymbols(const Compilation &C)55: C(C), DiagID(C.getDriver().getDiags().getCustomDiagID(56DiagnosticsEngine::Error,57"Error collecting HIP undefined fatbin symbols: %0")),58Quiet(C.getArgs().hasArg(options::OPT__HASH_HASH_HASH)),59Verbose(C.getArgs().hasArg(options::OPT_v)) {60populateSymbols();61if (Verbose) {62for (const auto &Name : FatBinSymbols)63llvm::errs() << "Found undefined HIP fatbin symbol: " << Name << "\n";64for (const auto &Name : GPUBinHandleSymbols)65llvm::errs() << "Found undefined HIP gpubin handle symbol: " << Name66<< "\n";67}68}6970const std::set<std::string> &getFatBinSymbols() const {71return FatBinSymbols;72}7374const std::set<std::string> &getGPUBinHandleSymbols() const {75return GPUBinHandleSymbols;76}7778private:79const Compilation &C;80unsigned DiagID;81bool Quiet;82bool Verbose;83std::set<std::string> FatBinSymbols;84std::set<std::string> GPUBinHandleSymbols;85std::set<std::string> DefinedFatBinSymbols;86std::set<std::string> DefinedGPUBinHandleSymbols;87const std::string FatBinPrefix = "__hip_fatbin";88const std::string GPUBinHandlePrefix = "__hip_gpubin_handle";8990void populateSymbols() {91std::deque<const Action *> WorkList;92std::set<const Action *> Visited;9394for (const auto &Action : C.getActions())95WorkList.push_back(Action);9697while (!WorkList.empty()) {98const Action *CurrentAction = WorkList.front();99WorkList.pop_front();100101if (!CurrentAction || !Visited.insert(CurrentAction).second)102continue;103104if (const auto *IA = dyn_cast<InputAction>(CurrentAction)) {105std::string ID = IA->getId().str();106if (!ID.empty()) {107ID = llvm::utohexstr(llvm::MD5Hash(ID), /*LowerCase=*/true);108FatBinSymbols.insert((FatBinPrefix + Twine('_') + ID).str());109GPUBinHandleSymbols.insert(110(GPUBinHandlePrefix + Twine('_') + ID).str());111continue;112}113if (IA->getInputArg().getNumValues() == 0)114continue;115const char *Filename = IA->getInputArg().getValue();116if (!Filename)117continue;118auto BufferOrErr = llvm::MemoryBuffer::getFile(Filename);119// Input action could be options to linker, therefore, ignore it120// if cannot read it. If it turns out to be a file that cannot be read,121// the error will be caught by the linker.122if (!BufferOrErr)123continue;124125processInput(BufferOrErr.get()->getMemBufferRef());126} else127WorkList.insert(WorkList.end(), CurrentAction->getInputs().begin(),128CurrentAction->getInputs().end());129}130}131132void processInput(const llvm::MemoryBufferRef &Buffer) {133// Try processing as object file first.134auto ObjFileOrErr = llvm::object::ObjectFile::createObjectFile(Buffer);135if (ObjFileOrErr) {136processSymbols(**ObjFileOrErr);137return;138}139140// Then try processing as archive files.141llvm::consumeError(ObjFileOrErr.takeError());142auto ArchiveOrErr = llvm::object::Archive::create(Buffer);143if (ArchiveOrErr) {144llvm::Error Err = llvm::Error::success();145llvm::object::Archive &Archive = *ArchiveOrErr.get();146for (auto &Child : Archive.children(Err)) {147auto ChildBufOrErr = Child.getMemoryBufferRef();148if (ChildBufOrErr)149processInput(*ChildBufOrErr);150else151errorHandler(ChildBufOrErr.takeError());152}153154if (Err)155errorHandler(std::move(Err));156return;157}158159// Ignore other files.160llvm::consumeError(ArchiveOrErr.takeError());161}162163void processSymbols(const llvm::object::ObjectFile &Obj) {164for (const auto &Symbol : Obj.symbols()) {165auto FlagOrErr = Symbol.getFlags();166if (!FlagOrErr) {167errorHandler(FlagOrErr.takeError());168continue;169}170171auto NameOrErr = Symbol.getName();172if (!NameOrErr) {173errorHandler(NameOrErr.takeError());174continue;175}176llvm::StringRef Name = *NameOrErr;177178bool isUndefined =179FlagOrErr.get() & llvm::object::SymbolRef::SF_Undefined;180bool isFatBinSymbol = Name.starts_with(FatBinPrefix);181bool isGPUBinHandleSymbol = Name.starts_with(GPUBinHandlePrefix);182183// Handling for defined symbols184if (!isUndefined) {185if (isFatBinSymbol) {186DefinedFatBinSymbols.insert(Name.str());187FatBinSymbols.erase(Name.str());188} else if (isGPUBinHandleSymbol) {189DefinedGPUBinHandleSymbols.insert(Name.str());190GPUBinHandleSymbols.erase(Name.str());191}192continue;193}194195// Add undefined symbols if they are not in the defined sets196if (isFatBinSymbol &&197DefinedFatBinSymbols.find(Name.str()) == DefinedFatBinSymbols.end())198FatBinSymbols.insert(Name.str());199else if (isGPUBinHandleSymbol &&200DefinedGPUBinHandleSymbols.find(Name.str()) ==201DefinedGPUBinHandleSymbols.end())202GPUBinHandleSymbols.insert(Name.str());203}204}205206void errorHandler(llvm::Error Err) {207if (Quiet)208return;209C.getDriver().Diag(DiagID) << llvm::toString(std::move(Err));210}211};212213// Construct a clang-offload-bundler command to bundle code objects for214// different devices into a HIP fat binary.215void HIP::constructHIPFatbinCommand(Compilation &C, const JobAction &JA,216llvm::StringRef OutputFileName,217const InputInfoList &Inputs,218const llvm::opt::ArgList &Args,219const Tool &T) {220// Construct clang-offload-bundler command to bundle object files for221// for different GPU archs.222ArgStringList BundlerArgs;223BundlerArgs.push_back(Args.MakeArgString("-type=o"));224BundlerArgs.push_back(225Args.MakeArgString("-bundle-align=" + Twine(HIPCodeObjectAlign)));226227// ToDo: Remove the dummy host binary entry which is required by228// clang-offload-bundler.229std::string BundlerTargetArg = "-targets=host-x86_64-unknown-linux";230// AMDGCN:231// For code object version 2 and 3, the offload kind in bundle ID is 'hip'232// for backward compatibility. For code object version 4 and greater, the233// offload kind in bundle ID is 'hipv4'.234std::string OffloadKind = "hip";235auto &TT = T.getToolChain().getTriple();236if (TT.isAMDGCN() && getAMDGPUCodeObjectVersion(C.getDriver(), Args) >= 4)237OffloadKind = OffloadKind + "v4";238for (const auto &II : Inputs) {239const auto *A = II.getAction();240auto ArchStr = llvm::StringRef(A->getOffloadingArch());241BundlerTargetArg +=242"," + OffloadKind + "-" + normalizeForBundler(TT, !ArchStr.empty());243if (!ArchStr.empty())244BundlerTargetArg += "-" + ArchStr.str();245}246BundlerArgs.push_back(Args.MakeArgString(BundlerTargetArg));247248// Use a NULL file as input for the dummy host binary entry249std::string BundlerInputArg = "-input=" NULL_FILE;250BundlerArgs.push_back(Args.MakeArgString(BundlerInputArg));251for (const auto &II : Inputs) {252BundlerInputArg = std::string("-input=") + II.getFilename();253BundlerArgs.push_back(Args.MakeArgString(BundlerInputArg));254}255256std::string Output = std::string(OutputFileName);257auto *BundlerOutputArg =258Args.MakeArgString(std::string("-output=").append(Output));259BundlerArgs.push_back(BundlerOutputArg);260261addOffloadCompressArgs(Args, BundlerArgs);262263const char *Bundler = Args.MakeArgString(264T.getToolChain().GetProgramPath("clang-offload-bundler"));265C.addCommand(std::make_unique<Command>(266JA, T, ResponseFileSupport::None(), Bundler, BundlerArgs, Inputs,267InputInfo(&JA, Args.MakeArgString(Output))));268}269270/// Add Generated HIP Object File which has device images embedded into the271/// host to the argument list for linking. Using MC directives, embed the272/// device code and also define symbols required by the code generation so that273/// the image can be retrieved at runtime.274void HIP::constructGenerateObjFileFromHIPFatBinary(275Compilation &C, const InputInfo &Output, const InputInfoList &Inputs,276const ArgList &Args, const JobAction &JA, const Tool &T) {277const ToolChain &TC = T.getToolChain();278std::string Name = std::string(llvm::sys::path::stem(Output.getFilename()));279280// Create Temp Object File Generator,281// Offload Bundled file and Bundled Object file.282// Keep them if save-temps is enabled.283const char *McinFile;284const char *BundleFile;285if (C.getDriver().isSaveTempsEnabled()) {286McinFile = C.getArgs().MakeArgString(Name + ".mcin");287BundleFile = C.getArgs().MakeArgString(Name + ".hipfb");288} else {289auto TmpNameMcin = C.getDriver().GetTemporaryPath(Name, "mcin");290McinFile = C.addTempFile(C.getArgs().MakeArgString(TmpNameMcin));291auto TmpNameFb = C.getDriver().GetTemporaryPath(Name, "hipfb");292BundleFile = C.addTempFile(C.getArgs().MakeArgString(TmpNameFb));293}294HIP::constructHIPFatbinCommand(C, JA, BundleFile, Inputs, Args, T);295296// Create a buffer to write the contents of the temp obj generator.297std::string ObjBuffer;298llvm::raw_string_ostream ObjStream(ObjBuffer);299300auto HostTriple =301C.getSingleOffloadToolChain<Action::OFK_Host>()->getTriple();302303HIPUndefinedFatBinSymbols Symbols(C);304305std::string PrimaryHipFatbinSymbol;306std::string PrimaryGpuBinHandleSymbol;307bool FoundPrimaryHipFatbinSymbol = false;308bool FoundPrimaryGpuBinHandleSymbol = false;309310std::vector<std::string> AliasHipFatbinSymbols;311std::vector<std::string> AliasGpuBinHandleSymbols;312313// Iterate through symbols to find the primary ones and collect others for314// aliasing315for (const auto &Symbol : Symbols.getFatBinSymbols()) {316if (!FoundPrimaryHipFatbinSymbol) {317PrimaryHipFatbinSymbol = Symbol;318FoundPrimaryHipFatbinSymbol = true;319} else320AliasHipFatbinSymbols.push_back(Symbol);321}322323for (const auto &Symbol : Symbols.getGPUBinHandleSymbols()) {324if (!FoundPrimaryGpuBinHandleSymbol) {325PrimaryGpuBinHandleSymbol = Symbol;326FoundPrimaryGpuBinHandleSymbol = true;327} else328AliasGpuBinHandleSymbols.push_back(Symbol);329}330331// Add MC directives to embed target binaries. We ensure that each332// section and image is 16-byte aligned. This is not mandatory, but333// increases the likelihood of data to be aligned with a cache block334// in several main host machines.335ObjStream << "# HIP Object Generator\n";336ObjStream << "# *** Automatically generated by Clang ***\n";337if (FoundPrimaryGpuBinHandleSymbol) {338// Define the first gpubin handle symbol339if (HostTriple.isWindowsMSVCEnvironment())340ObjStream << " .section .hip_gpubin_handle,\"dw\"\n";341else {342ObjStream << " .protected " << PrimaryGpuBinHandleSymbol << "\n";343ObjStream << " .type " << PrimaryGpuBinHandleSymbol << ",@object\n";344ObjStream << " .section .hip_gpubin_handle,\"aw\"\n";345}346ObjStream << " .globl " << PrimaryGpuBinHandleSymbol << "\n";347ObjStream << " .p2align 3\n"; // Align 8348ObjStream << PrimaryGpuBinHandleSymbol << ":\n";349ObjStream << " .zero 8\n"; // Size 8350351// Generate alias directives for other gpubin handle symbols352for (const auto &AliasSymbol : AliasGpuBinHandleSymbols) {353ObjStream << " .globl " << AliasSymbol << "\n";354ObjStream << " .set " << AliasSymbol << "," << PrimaryGpuBinHandleSymbol355<< "\n";356}357}358if (FoundPrimaryHipFatbinSymbol) {359// Define the first fatbin symbol360if (HostTriple.isWindowsMSVCEnvironment())361ObjStream << " .section .hip_fatbin,\"dw\"\n";362else {363ObjStream << " .protected " << PrimaryHipFatbinSymbol << "\n";364ObjStream << " .type " << PrimaryHipFatbinSymbol << ",@object\n";365ObjStream << " .section .hip_fatbin,\"a\",@progbits\n";366}367ObjStream << " .globl " << PrimaryHipFatbinSymbol << "\n";368ObjStream << " .p2align " << llvm::Log2(llvm::Align(HIPCodeObjectAlign))369<< "\n";370// Generate alias directives for other fatbin symbols371for (const auto &AliasSymbol : AliasHipFatbinSymbols) {372ObjStream << " .globl " << AliasSymbol << "\n";373ObjStream << " .set " << AliasSymbol << "," << PrimaryHipFatbinSymbol374<< "\n";375}376ObjStream << PrimaryHipFatbinSymbol << ":\n";377ObjStream << " .incbin ";378llvm::sys::printArg(ObjStream, BundleFile, /*Quote=*/true);379ObjStream << "\n";380}381if (HostTriple.isOSLinux() && HostTriple.isOSBinFormatELF())382ObjStream << " .section .note.GNU-stack, \"\", @progbits\n";383ObjStream.flush();384385// Dump the contents of the temp object file gen if the user requested that.386// We support this option to enable testing of behavior with -###.387if (C.getArgs().hasArg(options::OPT_fhip_dump_offload_linker_script))388llvm::errs() << ObjBuffer;389390// Open script file and write the contents.391std::error_code EC;392llvm::raw_fd_ostream Objf(McinFile, EC, llvm::sys::fs::OF_None);393394if (EC) {395C.getDriver().Diag(clang::diag::err_unable_to_make_temp) << EC.message();396return;397}398399Objf << ObjBuffer;400401ArgStringList McArgs{"-triple", Args.MakeArgString(HostTriple.normalize()),402"-o", Output.getFilename(),403McinFile, "--filetype=obj"};404const char *Mc = Args.MakeArgString(TC.GetProgramPath("llvm-mc"));405C.addCommand(std::make_unique<Command>(JA, T, ResponseFileSupport::None(), Mc,406McArgs, Inputs, Output));407}408409410