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 @@ -66,11 +66,11 @@ /// Bundle Entry ID (or, Offload Target String) has following components: /// * Offload Kind - Host, OpenMP, or HIP /// * Triple - Standard LLVM Triple -/// * GPUArch (Optional) - Processor name, like gfx906 or sm_30 +/// * TargetID (Optional) - target ID, like gfx906:xnack+ or sm_30 struct OffloadTargetInfo { llvm::StringRef OffloadKind; llvm::Triple Triple; - llvm::StringRef GPUArch; + llvm::StringRef TargetID; const OffloadBundlerConfig &BundlerConfig; @@ -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 @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #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" @@ -73,12 +74,12 @@ auto KindTriple = TripleOrGPU.first.split('-'); this->OffloadKind = KindTriple.first; this->Triple = llvm::Triple(KindTriple.second); - this->GPUArch = Target.substr(Target.find(TripleOrGPU.second)); + this->TargetID = Target.substr(Target.find(TripleOrGPU.second)); } else { auto KindTriple = TargetFeatures.first.split('-'); this->OffloadKind = KindTriple.first; this->Triple = llvm::Triple(KindTriple.second); - this->GPUArch = ""; + this->TargetID = ""; } } @@ -113,12 +114,11 @@ bool OffloadTargetInfo::operator==(const OffloadTargetInfo &Target) const { return OffloadKind == Target.OffloadKind && - Triple.isCompatibleWith(Target.Triple) && - GPUArch == Target.GPUArch; + Triple.isCompatibleWith(Target.Triple) && TargetID == Target.TargetID; } -std::string OffloadTargetInfo::str() { - return Twine(OffloadKind + "-" + Triple.str() + "-" + GPUArch).str(); +std::string OffloadTargetInfo::str() const { + return Twine(OffloadKind + "-" + Triple.str() + "-" + TargetID).str(); } static StringRef getDeviceFileExtension(StringRef Device, @@ -141,6 +141,51 @@ 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 target IDs are incompatible. + if (!clang::isCompatibleTargetID(CodeObjectInfo.TargetID, + TargetInfo.TargetID)) { + DEBUG_WITH_TYPE( + "CodeObjectCompatibility", + dbgs() << "Incompatible: target IDs are incompatible \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 @@ -1211,7 +1218,7 @@ Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" + CodeObject + getDeviceLibraryFileName(BundledObjectFileName, - CodeObjectInfo.GPUArch)) + CodeObjectInfo.TargetID)) .str(); // Replace ':' in optional target feature list with '_' to ensure // cross-platform validity. 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 // @@ -432,6 +432,41 @@ // NOHOST-NOT: host- // NOHOST-DAG: hip-amdgcn-amd-amdhsa--gfx900 // NOHOST-DAG: hip-amdgcn-amd-amdhsa--gfx906 + +// +// Check bundling ID compatibility for HIP. +// +// RUN: clang-offload-bundler -type=bc -targets=hip-amdgcn-amd-amdhsa--gfx906:xnack- \ +// RUN: -targets=hip-amdgcn-amd-amdhsa--gfx906:xnack+ \ +// RUN: -input=%t.tgt1 -input=%t.tgt2 -output=%t.hip.bundle.bc +// RUN: not clang-offload-bundler -type=bc -targets=hip-amdgcn-amd-amdhsa--gfx906 \ +// RUN: -targets=hip-amdgcn-amd-amdhsa--gfx906:xnack+ \ +// RUN: -input=%t.tgt1 -input=%t.tgt2 -output=%t.hip.bundle.bc 2>&1 \ +// RUN: | FileCheck %s -check-prefix=CONFLICT-TID +// CONFLICT-TID: error: Cannot bundle inputs with conflicting targets: 'hip-amdgcn-amd-amdhsa--gfx906' and 'hip-amdgcn-amd-amdhsa--gfx906:xnack+' + +// +// 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 @@ -442,9 +477,13 @@ // 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}} +// RUN: not clang-offload-bundler -type=o -targets=host-%itanium_abi_triple,openmp-amdgcn-amd-amdhsa-gfx906,openmp-amdgcn-amd-amdhsa-gfx906:sramecc+ -input=%t.o -input=%t.tgt1 -input=%t.tgt2 -output=%t.bad.bundle 2>&1 | FileCheck %s -check-prefix=BADTARGETS +// BADTARGETS: error: Cannot bundle inputs with conflicting targets: 'openmp-amdgcn-amd-amdhsa-gfx906' and 'openmp-amdgcn-amd-amdhsa-gfx906:sramecc+' // Check for error if no compatible code object is found in the heterogeneous archive library // RUN: not clang-offload-bundler -unbundle -type=a -targets=openmp-amdgcn-amd-amdhsa-gfx803 -input=%t.input-archive.a -output=%t-archive-gfx803-incompatible.a 2>&1 | FileCheck %s -check-prefix=INCOMPATIBLEARCHIVE Index: clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp =================================================================== --- clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp +++ clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #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" @@ -46,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -309,6 +311,8 @@ unsigned HostTargetNum = 0u; bool HIPOnly = true; llvm::DenseSet ParsedTargets; + // Map {offload-kind}-{triple} to target IDs. + std::map> TargetIDs; for (StringRef Target : TargetNames) { if (ParsedTargets.contains(Target)) { reportError(createStringError(errc::invalid_argument, @@ -331,6 +335,8 @@ reportError(createStringError(errc::invalid_argument, Msg.str())); } + TargetIDs[OffloadInfo.OffloadKind.str() + "-" + OffloadInfo.Triple.str()] + .insert(OffloadInfo.TargetID); if (KindIsValid && OffloadInfo.hasHostKind()) { ++HostTargetNum; // Save the index of the input that refers to the host. @@ -342,6 +348,17 @@ ++Index; } + for (const auto &TargetID : TargetIDs) { + if (auto ConflictingTID = + clang::getConflictTargetIDCombination(TargetID.second)) { + SmallVector Buf; + raw_svector_ostream Msg(Buf); + Msg << "Cannot bundle inputs with conflicting targets: '" + << TargetID.first + "-" + ConflictingTID->first << "' and '" + << TargetID.first + "-" + ConflictingTID->second << "'"; + reportError(createStringError(errc::invalid_argument, Msg.str())); + } + } // HIP uses clang-offload-bundler to bundle device-only compilation results // for multiple GPU archs, therefore allow no host target if all entries