Index: clang/include/clang/Basic/TargetID.h =================================================================== --- clang/include/clang/Basic/TargetID.h +++ clang/include/clang/Basic/TargetID.h @@ -51,6 +51,10 @@ /// returns None. llvm::Optional> getConflictTargetIDCombination(const std::set &TargetIDs); + +/// Check whether the provided target ID is compatible with the requested +/// target ID. +bool isCompatibleTargetID(llvm::StringRef Provided, llvm::StringRef Requested); } // namespace clang #endif Index: clang/include/clang/Driver/OffloadBundler.h =================================================================== --- clang/include/clang/Driver/OffloadBundler.h +++ clang/include/clang/Driver/OffloadBundler.h @@ -81,7 +81,7 @@ bool isOffloadKindCompatible(const llvm::StringRef TargetOffloadKind) const; bool isTripleValid() const; bool operator==(const OffloadTargetInfo &Target) const; - std::string str(); + std::string str() const; }; } // namespace clang Index: clang/lib/Basic/TargetID.cpp =================================================================== --- clang/lib/Basic/TargetID.cpp +++ clang/lib/Basic/TargetID.cpp @@ -164,4 +164,25 @@ return llvm::None; } +bool isCompatibleTargetID(llvm::StringRef Provided, llvm::StringRef Requested) { + llvm::StringMap ProvidedFeatures, RequestedFeatures; + llvm::StringRef ProvidedProc = + *parseTargetIDWithFormatCheckingOnly(Provided, &ProvidedFeatures); + llvm::StringRef RequestedProc = + *parseTargetIDWithFormatCheckingOnly(Requested, &RequestedFeatures); + if (ProvidedProc != RequestedProc) + return false; + for (const auto &F : ProvidedFeatures) { + auto Loc = RequestedFeatures.find(F.first()); + // The default (unspecified) value of a feature is 'All', which can match + // either 'On' or 'Off'. + if (Loc == RequestedFeatures.end()) + return false; + // If a feature is specified, it must have exact match. + if (Loc->second != F.second) + return false; + } + return true; +} + } // namespace clang Index: clang/lib/Driver/OffloadBundler.cpp =================================================================== --- clang/lib/Driver/OffloadBundler.cpp +++ clang/lib/Driver/OffloadBundler.cpp @@ -14,9 +14,10 @@ /// //===----------------------------------------------------------------------===// +#include "clang/Driver/OffloadBundler.h" #include "clang/Basic/Cuda.h" +#include "clang/Basic/TargetID.h" #include "clang/Basic/Version.h" -#include "clang/Driver/OffloadBundler.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" @@ -117,7 +118,7 @@ GPUArch == Target.GPUArch; } -std::string OffloadTargetInfo::str() { +std::string OffloadTargetInfo::str() const { return Twine(OffloadKind + "-" + Triple.str() + "-" + GPUArch).str(); } @@ -141,6 +142,50 @@ return Result; } +/// @brief Checks if a code object \p CodeObjectInfo is compatible with a given +/// target \p TargetInfo. +/// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id +bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo, + const OffloadTargetInfo &TargetInfo) { + + // Compatible in case of exact match. + if (CodeObjectInfo == TargetInfo) { + DEBUG_WITH_TYPE("CodeObjectCompatibility", + dbgs() << "Compatible: Exact match: \t[CodeObject: " + << CodeObjectInfo.str() + << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); + return true; + } + + // Incompatible if Kinds or Triples mismatch. + if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) || + !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) { + DEBUG_WITH_TYPE( + "CodeObjectCompatibility", + dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: " + << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() + << "]\n"); + return false; + } + + // Incompatible if GPUArch mismatch. + if (!clang::isCompatibleTargetID(CodeObjectInfo.GPUArch, + TargetInfo.GPUArch)) { + DEBUG_WITH_TYPE("CodeObjectCompatibility", + dbgs() << "Incompatible: GPU Arch mismatch \t[CodeObject: " + << CodeObjectInfo.str() + << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); + return false; + } + + DEBUG_WITH_TYPE( + "CodeObjectCompatibility", + dbgs() << "Compatible: Code Objects are compatible \t[CodeObject: " + << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() + << "]\n"); + return true; +} + /// Generic file handler interface. class FileHandler { public: @@ -959,17 +1004,22 @@ StringRef CurTriple = **CurTripleOrErr; assert(!CurTriple.empty()); - auto Output = Worklist.find(CurTriple); - // The file may have more bundles for other targets, that we don't care - // about. Therefore, move on to the next triple + auto Output = Worklist.begin(); + for (auto E = Worklist.end(); Output != E; Output++) { + if (isCodeObjectCompatible( + OffloadTargetInfo(CurTriple, BundlerConfig), + OffloadTargetInfo((*Output).first(), BundlerConfig))) { + break; + } + } + if (Output == Worklist.end()) continue; - // Check if the output file can be opened and copy the bundle to it. std::error_code EC; - raw_fd_ostream OutputFile(Output->second, EC, sys::fs::OF_None); + raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None); if (EC) - return createFileError(Output->second, EC); + return createFileError((*Output).second, EC); if (Error Err = FH->ReadBundle(OutputFile, Input)) return Err; if (Error Err = FH->ReadBundleEnd(Input)) @@ -1040,49 +1090,6 @@ : Archive::K_GNU; } -/// @brief Checks if a code object \p CodeObjectInfo is compatible with a given -/// target \p TargetInfo. -/// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id -bool isCodeObjectCompatible(OffloadTargetInfo &CodeObjectInfo, - OffloadTargetInfo &TargetInfo) { - - // Compatible in case of exact match. - if (CodeObjectInfo == TargetInfo) { - DEBUG_WITH_TYPE("CodeObjectCompatibility", - dbgs() << "Compatible: Exact match: \t[CodeObject: " - << CodeObjectInfo.str() - << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); - return true; - } - - // Incompatible if Kinds or Triples mismatch. - if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) || - !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) { - DEBUG_WITH_TYPE( - "CodeObjectCompatibility", - dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: " - << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() - << "]\n"); - return false; - } - - // Incompatible if GPUArch mismatch. - if (CodeObjectInfo.GPUArch != TargetInfo.GPUArch) { - DEBUG_WITH_TYPE("CodeObjectCompatibility", - dbgs() << "Incompatible: GPU Arch mismatch \t[CodeObject: " - << CodeObjectInfo.str() - << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); - return false; - } - - DEBUG_WITH_TYPE( - "CodeObjectCompatibility", - dbgs() << "Compatible: Code Objects are compatible \t[CodeObject: " - << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() - << "]\n"); - return true; -} - /// @brief Computes a list of targets among all given targets which are /// compatible with this code object /// @param [in] CodeObjectInfo Code Object Index: clang/test/Driver/clang-offload-bundler.c =================================================================== --- clang/test/Driver/clang-offload-bundler.c +++ clang/test/Driver/clang-offload-bundler.c @@ -224,7 +224,7 @@ // RUN: diff %t.empty %t.res.tgt2 // Check that bindler prints an error if given host bundle does not exist in the fat binary. -// RUN: not clang-offload-bundler -type=s -targets=host-x86_64-xxx-linux-gnu,openmp-powerpc64le-ibm-linux-gnu -output=%t.res.s -output=%t.res.tgt1 -input=%t.bundle3.s -unbundle -allow-missing-bundles 2>&1 | FileCheck %s --check-prefix CK-NO-HOST-BUNDLE +// RUN: not clang-offload-bundler -type=s -targets=host-amdgcn-xxx-linux-gnu,openmp-powerpc64le-ibm-linux-gnu -output=%t.res.s -output=%t.res.tgt1 -input=%t.bundle3.s -unbundle -allow-missing-bundles 2>&1 | FileCheck %s --check-prefix CK-NO-HOST-BUNDLE // CK-NO-HOST-BUNDLE: error: Can't find bundle for the host target // @@ -421,6 +421,29 @@ // NOHOST-NOT: host- // NOHOST-DAG: hip-amdgcn-amd-amdhsa--gfx900 // NOHOST-DAG: hip-amdgcn-amd-amdhsa--gfx906 + +// +// Check extracting bundle entry with compatible target ID for HIP. +// +// RUN: clang-offload-bundler -type=bc -targets=hip-amdgcn-amd-amdhsa--gfx906 \ +// RUN: -input=%t.tgt1 -output=%t.hip.bundle.bc +// RUN: clang-offload-bundler -type=bc -targets=hip-amdgcn-amd-amdhsa--gfx906:xnack- \ +// RUN: -output=%t.res.tgt1 -input=%t.hip.bundle.bc -unbundle +// RUN: diff %t.tgt1 %t.res.tgt1 +// RUN: clang-offload-bundler -type=bc -targets=hip-amdgcn-amd-amdhsa--gfx906:xnack+ \ +// RUN: -output=%t.res.tgt1 -input=%t.hip.bundle.bc -unbundle +// RUN: diff %t.tgt1 %t.res.tgt1 + +// RUN: clang-offload-bundler -type=bc -targets=hip-amdgcn-amd-amdhsa--gfx906:xnack+ \ +// RUN: -input=%t.tgt1 -output=%t.hip.bundle.bc +// RUN: not clang-offload-bundler -type=bc -targets=hip-amdgcn-amd-amdhsa--gfx906:xnack- \ +// RUN: -output=%t.res.tgt1 -input=%t.hip.bundle.bc -unbundle 2>&1 | FileCheck %s -check-prefix=NOXNACK +// NOXNACK: error: Can't find bundles for hip-amdgcn-amd-amdhsa--gfx906:xnack- +// RUN: not clang-offload-bundler -type=bc -targets=hip-amdgcn-amd-amdhsa--gfx906 \ +// RUN: -output=%t.res.tgt1 -input=%t.hip.bundle.bc -unbundle 2>&1 | FileCheck %s -check-prefix=NOGFX906 +// NOGFX906: error: Can't find bundles for hip-amdgcn-amd-amdhsa--gfx906 + +// // Check archive unbundling // // Create few code object bundles and archive them to create an input archive @@ -431,6 +454,8 @@ // RUN: clang-offload-bundler -unbundle -type=a -targets=openmp-amdgcn-amd-amdhsa-gfx906,openmp-amdgcn-amd-amdhsa-gfx908 -input=%t.input-archive.a -output=%t-archive-gfx906-simple.a -output=%t-archive-gfx908-simple.a // RUN: llvm-ar t %t-archive-gfx906-simple.a | FileCheck %s -check-prefix=GFX906 +// RUN: clang-offload-bundler -unbundle -type=a -targets=openmp-amdgcn-amd-amdhsa-gfx906:xnack+ -input=%t.input-archive.a -output=%t-archive-gfx906-simple.a +// RUN: llvm-ar t %t-archive-gfx906-simple.a | FileCheck %s -check-prefix=GFX906 // GFX906: simple-openmp-amdgcn-amd-amdhsa-gfx906 // RUN: llvm-ar t %t-archive-gfx908-simple.a | FileCheck %s -check-prefix=GFX908 // GFX908-NOT: {{gfx906}}