[clang] 844b84a - [clang-offload-bundler] extracting compatible bundle entry

Yaxun Liu via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 5 16:45:00 PDT 2022


Author: Yaxun (Sam) Liu
Date: 2022-10-05T19:44:38-04:00
New Revision: 844b84af20c71482093995b1d64abc190eb5a5f9

URL: https://github.com/llvm/llvm-project/commit/844b84af20c71482093995b1d64abc190eb5a5f9
DIFF: https://github.com/llvm/llvm-project/commit/844b84af20c71482093995b1d64abc190eb5a5f9.diff

LOG: [clang-offload-bundler] extracting compatible bundle entry

In HIP a library is usually compiled with default target ID e.g. gfx906 so that
it can be used in all GPU configurations. The bitcode is saved in bundled
bitcode with gfx906 in entry ID.

In runtime compilation, a HIP program is compiled with a target ID matching
the GPU configuration, e.g. gfx906:xnack-. This program needs to link with
a library bundled bitcode with target ID gfx906.

For example:

  clang --offload-arch=gfx906 -o lib.o lib.hip
  clang --offload-arch=gfx906:xnack- program.hip lib.o

This common use case requires that clang-offlod-bundler to be able to extract
entry with compatible target ID, e.g. extracting an gfx906 entry when requesting
gfx906:xnack-.

Currently clang-offload-bundler only allow extracting entry with exact match
of target ID. This patch relaxes that so that it can extract entries with compatible
target ID.

Reviewed by: Artem Belevich, Saiyedul Islam

Differential Revision: https://reviews.llvm.org/D134546

Added: 
    

Modified: 
    clang/include/clang/Basic/TargetID.h
    clang/include/clang/Driver/OffloadBundler.h
    clang/lib/Basic/TargetID.cpp
    clang/lib/Driver/OffloadBundler.cpp
    clang/test/Driver/clang-offload-bundler.c
    clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/TargetID.h b/clang/include/clang/Basic/TargetID.h
index 2579276fc0340..a34bebbae1ad4 100644
--- a/clang/include/clang/Basic/TargetID.h
+++ b/clang/include/clang/Basic/TargetID.h
@@ -51,6 +51,10 @@ std::string getCanonicalTargetID(llvm::StringRef Processor,
 /// returns None.
 llvm::Optional<std::pair<llvm::StringRef, llvm::StringRef>>
 getConflictTargetIDCombination(const std::set<llvm::StringRef> &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

diff  --git a/clang/include/clang/Driver/OffloadBundler.h b/clang/include/clang/Driver/OffloadBundler.h
index ea0631b0ddf7f..d7f927c223819 100644
--- a/clang/include/clang/Driver/OffloadBundler.h
+++ b/clang/include/clang/Driver/OffloadBundler.h
@@ -66,11 +66,11 @@ class OffloadBundler {
 /// 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 @@ struct OffloadTargetInfo {
   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

diff  --git a/clang/lib/Basic/TargetID.cpp b/clang/lib/Basic/TargetID.cpp
index abfbe49e1a91a..6f583cb0c6e72 100644
--- a/clang/lib/Basic/TargetID.cpp
+++ b/clang/lib/Basic/TargetID.cpp
@@ -164,4 +164,25 @@ getConflictTargetIDCombination(const std::set<llvm::StringRef> &TargetIDs) {
   return llvm::None;
 }
 
+bool isCompatibleTargetID(llvm::StringRef Provided, llvm::StringRef Requested) {
+  llvm::StringMap<bool> 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

diff  --git a/clang/lib/Driver/OffloadBundler.cpp b/clang/lib/Driver/OffloadBundler.cpp
index 4a87942934ff9..9e28cfbc319c3 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/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 @@ OffloadTargetInfo::OffloadTargetInfo(const StringRef Target,
     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::isTripleValid() const {
 
 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 @@ static std::string getDeviceLibraryFileName(StringRef BundleFileName,
   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 @@ Error OffloadBundler::UnbundleFiles() {
     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 @@ static Archive::Kind getDefaultArchiveKindForHost() {
                                                             : 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 @@ Error OffloadBundler::UnbundleArchive() {
               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.

diff  --git a/clang/test/Driver/clang-offload-bundler.c b/clang/test/Driver/clang-offload-bundler.c
index 965c619dd14a6..a27712351ac0e 100644
--- a/clang/test/Driver/clang-offload-bundler.c
+++ b/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

diff  --git a/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp b/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp
index 94973158b4414..a7652622cd215 100644
--- a/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp
+++ b/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 <cstddef>
 #include <cstdint>
 #include <forward_list>
+#include <map>
 #include <memory>
 #include <set>
 #include <string>
@@ -309,6 +311,8 @@ int main(int argc, const char **argv) {
   unsigned HostTargetNum = 0u;
   bool HIPOnly = true;
   llvm::DenseSet<StringRef> ParsedTargets;
+  // Map {offload-kind}-{triple} to target IDs.
+  std::map<std::string, std::set<StringRef>> TargetIDs;
   for (StringRef Target : TargetNames) {
     if (ParsedTargets.contains(Target)) {
       reportError(createStringError(errc::invalid_argument,
@@ -331,6 +335,8 @@ int main(int argc, const char **argv) {
       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 @@ int main(int argc, const char **argv) {
 
     ++Index;
   }
+  for (const auto &TargetID : TargetIDs) {
+    if (auto ConflictingTID =
+            clang::getConflictTargetIDCombination(TargetID.second)) {
+      SmallVector<char, 128u> 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


        


More information about the cfe-commits mailing list