[clang] [llvm] [compiler-rt] [clang-tools-extra] [Profile] Allow profile merging with multiple correlate files. (PR #75957)

Zequan Wu via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 20 10:20:25 PST 2023


https://github.com/ZequanWu updated https://github.com/llvm/llvm-project/pull/75957

>From d72f0e1ad7759bad81767418604d27f11d74d6de Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Tue, 19 Dec 2023 12:32:15 -0500
Subject: [PATCH 1/3] [Profile] Allow profile merging with multiple correlate
 files.

---
 .../Linux/instrprof-correlation-mixed.test    |  33 ++++
 .../Linux/instrprof-debug-info-correlate.c    |  19 ++
 .../llvm/ProfileData/InstrProfCorrelator.h    | 124 ++++++++++---
 .../llvm/ProfileData/InstrProfReader.h        |  17 +-
 llvm/lib/Object/BuildID.cpp                   |  18 ++
 llvm/lib/ProfileData/InstrProfCorrelator.cpp  | 164 +++++++++++++-----
 llvm/lib/ProfileData/InstrProfReader.cpp      |  26 ++-
 llvm/tools/llvm-profdata/llvm-profdata.cpp    |  84 +++++----
 8 files changed, 358 insertions(+), 127 deletions(-)
 create mode 100644 compiler-rt/test/profile/Linux/instrprof-correlation-mixed.test

diff --git a/compiler-rt/test/profile/Linux/instrprof-correlation-mixed.test b/compiler-rt/test/profile/Linux/instrprof-correlation-mixed.test
new file mode 100644
index 00000000000000..8cc4611eec4f0b
--- /dev/null
+++ b/compiler-rt/test/profile/Linux/instrprof-correlation-mixed.test
@@ -0,0 +1,33 @@
+// REQUIRES: lld-available
+// Test llvm-profdata merging with multiple correlation files mixing different correlation modes.
+
+// RUN: %clang_pgogen -o %t.normal -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp %S/../Inputs/instrprof-debug-info-correlate-foo.cpp
+// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t.normal
+// RUN: llvm-profdata merge -o %t.normal.profdata %t.profraw
+
+// Compiling with differnt configs.
+// RUN: %clang_pgogen -o %t -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp -c -o %t-main.o
+// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp -c -o %t-main.debug.o
+// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-foo.cpp -fpic -shared -Wl,--build-id -o %t-libfoo.debug.so
+// RUN: %clang_pgogen -o %t -mllvm -profile-correlate=binary -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-foo.cpp -fpic -shared -Wl,--build-id -o %t-libfoo.binary.so
+
+// Test mixing default raw profile and lightweight raw profile generated with debug info correlate.
+// The raw profiles are mixed in %t.proflite.
+// RUN: %clang_pgogen -o %t %t-main.o %t-libfoo.debug.so -Wl,--build-id -o %t
+// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t
+// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t-libfoo.debug.so %t.proflite
+// RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
+// Two separate raw profiles.
+// RUN: rm -rf %t.dir && mkdir %t.dir
+// RUN: env LLVM_PROFILE_FILE=%t.dir/raw%m.proflite %run %t
+// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t-libfoo.debug.so %t.dir
+// RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
+
+// Test lightweight raw profiles generated with debug info correlate and binary correlate.
+// Note we can not mix different correlation modes in static linking because when merging, the same correlate file can not be used for more than one correaltion mode.
+// Two separate lightweight raw profiles.
+// RUN: %clang_pgogen -o %t -g %t-main.debug.o %t-libfoo.binary.so -Wl,--build-id -o %t
+// RUN: rm -rf %t.dir && mkdir %t.dir
+// RUN: env LLVM_PROFILE_FILE=%t.dir/raw%m.proflite %run %t
+// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t --binary-file=%t-libfoo.binary.so %t.dir
+// RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
diff --git a/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c b/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c
index a918d7b6299005..47dbf3c87e68f1 100644
--- a/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c
+++ b/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c
@@ -25,6 +25,25 @@
 
 // RUN: diff <(llvm-profdata show --all-functions --counts %t.cov.normal.profdata) <(llvm-profdata show --all-functions --counts %t.cov.profdata)
 
+// Test debug info correlate with build id.
+
+// Both binaries are built with build id.
+// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-foo.cpp -c -fpic -shared -Wl,--build-id -o %t-libfoo.so
+// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp %t-libfoo.so -Wl,--build-id -o %t
+// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t
+// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t,%t-libfoo.o %t.proflite
+// RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
+
+// One binary is built without build id.
+// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp %t-libfoo.so -o %t
+// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t
+// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t,%t-libfoo.o %t.proflite
+// RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
+
+// Warning about multiple correlate files have the same build id.
+// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t,%t,%t-libfoo.o %t.proflite 2>&1 >/dev/null | FileCheck %s --check-prefix=WARN
+// WARN: Duplicate build id ({{.*}}) found for {{.*}} and {{.*}}
+
 // Test debug info correlate with online merging.
 
 // RUN: env LLVM_PROFILE_FILE=%t-1.profraw %run %t.normal
diff --git a/llvm/include/llvm/ProfileData/InstrProfCorrelator.h b/llvm/include/llvm/ProfileData/InstrProfCorrelator.h
index c07c67d287e2ce..1b579e0ac2fd24 100644
--- a/llvm/include/llvm/ProfileData/InstrProfCorrelator.h
+++ b/llvm/include/llvm/ProfileData/InstrProfCorrelator.h
@@ -13,6 +13,7 @@
 #define LLVM_PROFILEDATA_INSTRPROFCORRELATOR_H
 
 #include "llvm/ADT/DenseSet.h"
+#include "llvm/Object/BuildID.h"
 #include "llvm/ProfileData/InstrProf.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -35,20 +36,33 @@ class InstrProfCorrelator {
   /// correlate.
   enum ProfCorrelatorKind { NONE, DEBUG_INFO, BINARY };
 
+  struct AtomicWarningCounter {
+    AtomicWarningCounter(uint64_t MaxWarnings)
+        : MaxWarnings(MaxWarnings), WarningCount(0){};
+    bool shouldEmitWarning();
+    ~AtomicWarningCounter();
+
+  private:
+    const uint64_t MaxWarnings;
+    std::atomic<uint64_t> WarningCount;
+  };
+
   static llvm::Expected<std::unique_ptr<InstrProfCorrelator>>
-  get(StringRef Filename, ProfCorrelatorKind FileKind);
+  get(StringRef Filename, ProfCorrelatorKind FileKind,
+      std::mutex &CorrelateLock, std::mutex &WarnLock,
+      AtomicWarningCounter *WarnCounter);
 
   /// Construct a ProfileData vector used to correlate raw instrumentation data
   /// to their functions.
-  /// \param MaxWarnings the maximum number of warnings to emit (0 = no limit)
-  virtual Error correlateProfileData(int MaxWarnings) = 0;
+  virtual Error correlateProfileData() = 0;
 
   /// Process debug info and dump the correlation data.
-  /// \param MaxWarnings the maximum number of warnings to emit (0 = no limit)
-  virtual Error dumpYaml(int MaxWarnings, raw_ostream &OS) = 0;
+  virtual Error dumpYaml(raw_ostream &OS) = 0;
+
+  virtual const char *getDataPointer() const = 0;
 
   /// Return the number of ProfileData elements.
-  std::optional<size_t> getDataSize() const;
+  size_t getDataSize() const;
 
   /// Return a pointer to the names string that this class constructs.
   const char *getNamesPointer() const { return Names.c_str(); }
@@ -61,6 +75,8 @@ class InstrProfCorrelator {
     return Ctx->CountersSectionEnd - Ctx->CountersSectionStart;
   }
 
+  object::BuildIDRef getBuildID() const { return Ctx->BuildID; }
+
   static const char *FunctionNameAttributeName;
   static const char *CFGHashAttributeName;
   static const char *NumCountersAttributeName;
@@ -84,16 +100,25 @@ class InstrProfCorrelator {
     const char *DataEnd;
     const char *NameStart;
     size_t NameSize;
+    object::BuildIDRef BuildID;
     /// True if target and host have different endian orders.
     bool ShouldSwapBytes;
   };
   const std::unique_ptr<Context> Ctx;
 
-  InstrProfCorrelator(InstrProfCorrelatorKind K, std::unique_ptr<Context> Ctx)
-      : Ctx(std::move(Ctx)), Kind(K) {}
+  InstrProfCorrelator(InstrProfCorrelatorKind K, std::unique_ptr<Context> Ctx,
+                      std::mutex &CorrelateLock, std::mutex &WarnLock,
+                      AtomicWarningCounter *WarnCounter)
+      : Ctx(std::move(Ctx)), IsCorrelated(false), CorrelateLock(CorrelateLock),
+        WarnLock(WarnLock), WarnCounter(WarnCounter), Kind(K) {}
 
   std::string Names;
-  std::vector<std::string> NamesVec;
+  /// True if correlation is already done.
+  bool IsCorrelated;
+  std::mutex &CorrelateLock;
+  std::mutex &WarnLock;
+
+  bool shouldEmitWarning();
 
   struct Probe {
     std::string FunctionName;
@@ -115,8 +140,11 @@ class InstrProfCorrelator {
 
 private:
   static llvm::Expected<std::unique_ptr<InstrProfCorrelator>>
-  get(std::unique_ptr<MemoryBuffer> Buffer, ProfCorrelatorKind FileKind);
+  get(std::unique_ptr<MemoryBuffer> Buffer, ProfCorrelatorKind FileKind,
+      std::mutex &CorrelateLock, std::mutex &WarnLock,
+      AtomicWarningCounter *WarnCounter);
 
+  AtomicWarningCounter *WarnCounter;
   const InstrProfCorrelatorKind Kind;
 };
 
@@ -125,13 +153,15 @@ class InstrProfCorrelator {
 template <class IntPtrT>
 class InstrProfCorrelatorImpl : public InstrProfCorrelator {
 public:
-  InstrProfCorrelatorImpl(std::unique_ptr<InstrProfCorrelator::Context> Ctx);
+  InstrProfCorrelatorImpl(std::unique_ptr<InstrProfCorrelator::Context> Ctx,
+                          std::mutex &CorrelateLock, std::mutex &WarnLock,
+                          AtomicWarningCounter *WarnCounter);
   static bool classof(const InstrProfCorrelator *C);
 
   /// Return a pointer to the underlying ProfileData vector that this class
   /// constructs.
-  const RawInstrProf::ProfileData<IntPtrT> *getDataPointer() const {
-    return Data.empty() ? nullptr : Data.data();
+  const char *getDataPointer() const override {
+    return Data.empty() ? nullptr : (const char *)Data.data();
   }
 
   /// Return the number of ProfileData elements.
@@ -139,19 +169,20 @@ class InstrProfCorrelatorImpl : public InstrProfCorrelator {
 
   static llvm::Expected<std::unique_ptr<InstrProfCorrelatorImpl<IntPtrT>>>
   get(std::unique_ptr<InstrProfCorrelator::Context> Ctx,
-      const object::ObjectFile &Obj, ProfCorrelatorKind FileKind);
+      const object::ObjectFile &Obj, ProfCorrelatorKind FileKind,
+      std::mutex &CorrelateLock, std::mutex &WarnLock,
+      AtomicWarningCounter *WarnCounter);
 
 protected:
   std::vector<RawInstrProf::ProfileData<IntPtrT>> Data;
 
-  Error correlateProfileData(int MaxWarnings) override;
+  Error correlateProfileData() override;
   virtual void correlateProfileDataImpl(
-      int MaxWarnings,
       InstrProfCorrelator::CorrelationData *Data = nullptr) = 0;
 
   virtual Error correlateProfileNameImpl() = 0;
 
-  Error dumpYaml(int MaxWarnings, raw_ostream &OS) override;
+  Error dumpYaml(raw_ostream &OS) override;
 
   void addDataProbe(uint64_t FunctionName, uint64_t CFGHash,
                     IntPtrT CounterOffset, IntPtrT FunctionPtr,
@@ -164,8 +195,11 @@ class InstrProfCorrelatorImpl : public InstrProfCorrelator {
 
 private:
   InstrProfCorrelatorImpl(InstrProfCorrelatorKind Kind,
-                          std::unique_ptr<InstrProfCorrelator::Context> Ctx)
-      : InstrProfCorrelator(Kind, std::move(Ctx)){};
+                          std::unique_ptr<InstrProfCorrelator::Context> Ctx,
+                          std::mutex &CorrelateLock, std::mutex &WarnLock,
+                          AtomicWarningCounter *WarnCounter)
+      : InstrProfCorrelator(Kind, std::move(Ctx), CorrelateLock, WarnLock,
+                            WarnCounter){};
   llvm::DenseSet<IntPtrT> CounterOffsets;
 };
 
@@ -174,13 +208,18 @@ class InstrProfCorrelatorImpl : public InstrProfCorrelator {
 template <class IntPtrT>
 class DwarfInstrProfCorrelator : public InstrProfCorrelatorImpl<IntPtrT> {
 public:
-  DwarfInstrProfCorrelator(std::unique_ptr<DWARFContext> DICtx,
-                           std::unique_ptr<InstrProfCorrelator::Context> Ctx)
-      : InstrProfCorrelatorImpl<IntPtrT>(std::move(Ctx)),
+  DwarfInstrProfCorrelator(
+      std::unique_ptr<DWARFContext> DICtx,
+      std::unique_ptr<InstrProfCorrelator::Context> Ctx,
+      std::mutex &CorrelateLock, std::mutex &WarnLock,
+      InstrProfCorrelator::AtomicWarningCounter *WarnCounter)
+      : InstrProfCorrelatorImpl<IntPtrT>(std::move(Ctx), CorrelateLock,
+                                         WarnLock, WarnCounter),
         DICtx(std::move(DICtx)) {}
 
 private:
   std::unique_ptr<DWARFContext> DICtx;
+  std::vector<std::string> NamesVec;
 
   /// Return the address of the object that the provided DIE symbolizes.
   std::optional<uint64_t> getLocation(const DWARFDie &Die) const;
@@ -217,7 +256,6 @@ class DwarfInstrProfCorrelator : public InstrProfCorrelatorImpl<IntPtrT> {
   /// \param MaxWarnings the maximum number of warnings to emit (0 = no limit)
   /// \param Data if provided, populate with the correlation data found
   void correlateProfileDataImpl(
-      int MaxWarnings,
       InstrProfCorrelator::CorrelationData *Data = nullptr) override;
 
   Error correlateProfileNameImpl() override;
@@ -228,8 +266,12 @@ class DwarfInstrProfCorrelator : public InstrProfCorrelatorImpl<IntPtrT> {
 template <class IntPtrT>
 class BinaryInstrProfCorrelator : public InstrProfCorrelatorImpl<IntPtrT> {
 public:
-  BinaryInstrProfCorrelator(std::unique_ptr<InstrProfCorrelator::Context> Ctx)
-      : InstrProfCorrelatorImpl<IntPtrT>(std::move(Ctx)) {}
+  BinaryInstrProfCorrelator(
+      std::unique_ptr<InstrProfCorrelator::Context> Ctx,
+      std::mutex &CorrelateLock, std::mutex &WarnLock,
+      InstrProfCorrelator::AtomicWarningCounter *WarnCounter)
+      : InstrProfCorrelatorImpl<IntPtrT>(std::move(Ctx), CorrelateLock,
+                                         WarnLock, WarnCounter) {}
 
   /// Return a pointer to the names string that this class constructs.
   const char *getNamesPointer() const { return this->Ctx.NameStart; }
@@ -239,12 +281,42 @@ class BinaryInstrProfCorrelator : public InstrProfCorrelatorImpl<IntPtrT> {
 
 private:
   void correlateProfileDataImpl(
-      int MaxWarnings,
       InstrProfCorrelator::CorrelationData *Data = nullptr) override;
 
   Error correlateProfileNameImpl() override;
 };
 
+// InstrProfCorrelators contains a map from BuildID to InstrProfCorrelator and
+// correlate profile on-demand when users call getCorrelator.
+class InstrProfCorrelators {
+public:
+  static llvm::Expected<std::unique_ptr<InstrProfCorrelators>>
+  get(ArrayRef<std::pair<StringRef, InstrProfCorrelator::ProfCorrelatorKind>>
+          CorrelateInputs,
+      uint32_t MaxWarnings);
+
+  InstrProfCorrelators(
+      StringMap<std::unique_ptr<InstrProfCorrelator>> &&CorrelatorMap,
+      std::unique_ptr<std::mutex> CorrelateLock,
+      std::unique_ptr<std::mutex> WarnLock,
+      std::unique_ptr<InstrProfCorrelator::AtomicWarningCounter> WarnCounter)
+      : CorrelatorMap(std::move(CorrelatorMap)),
+        CorrelateLock(std::move(CorrelateLock)), WarnLock(std::move(WarnLock)),
+        WarnCounter(std::move(WarnCounter)) {}
+
+  llvm::Expected<const InstrProfCorrelator *>
+  getCorrelator(object::BuildIDRef BuildID) const;
+
+  bool empty() const { return CorrelatorMap.empty(); }
+  Error dumpYaml(raw_ostream &OS);
+
+private:
+  // A map from BuildID to correlator.
+  const StringMap<std::unique_ptr<InstrProfCorrelator>> CorrelatorMap;
+  std::unique_ptr<std::mutex> CorrelateLock;
+  std::unique_ptr<std::mutex> WarnLock;
+  std::unique_ptr<InstrProfCorrelator::AtomicWarningCounter> WarnCounter;
+};
 } // end namespace llvm
 
 #endif // LLVM_PROFILEDATA_INSTRPROFCORRELATOR_H
diff --git a/llvm/include/llvm/ProfileData/InstrProfReader.h b/llvm/include/llvm/ProfileData/InstrProfReader.h
index ff50dfde0e7938..c7810c8b1af50c 100644
--- a/llvm/include/llvm/ProfileData/InstrProfReader.h
+++ b/llvm/include/llvm/ProfileData/InstrProfReader.h
@@ -199,12 +199,12 @@ class InstrProfReader {
   /// instrprof file.
   static Expected<std::unique_ptr<InstrProfReader>>
   create(const Twine &Path, vfs::FileSystem &FS,
-         const InstrProfCorrelator *Correlator = nullptr,
+         const InstrProfCorrelators *Correlators = nullptr,
          std::function<void(Error)> Warn = nullptr);
 
   static Expected<std::unique_ptr<InstrProfReader>>
   create(std::unique_ptr<MemoryBuffer> Buffer,
-         const InstrProfCorrelator *Correlator = nullptr,
+         const InstrProfCorrelators *Correlators = nullptr,
          std::function<void(Error)> Warn = nullptr);
 
   /// \param Weight for raw profiles use this as the temporal profile trace
@@ -313,7 +313,8 @@ class RawInstrProfReader : public InstrProfReader {
   std::unique_ptr<MemoryBuffer> DataBuffer;
   /// If available, this hold the ProfileData array used to correlate raw
   /// instrumentation data to their functions.
-  const InstrProfCorrelatorImpl<IntPtrT> *Correlator;
+  const InstrProfCorrelators *Correlators;
+  bool IsCorrelatorUsed;
   /// A list of timestamps paired with a function name reference.
   std::vector<std::pair<uint64_t, uint64_t>> TemporalProfTimestamps;
   bool ShouldSwapBytes;
@@ -346,11 +347,9 @@ class RawInstrProfReader : public InstrProfReader {
 
 public:
   RawInstrProfReader(std::unique_ptr<MemoryBuffer> DataBuffer,
-                     const InstrProfCorrelator *Correlator,
+                     const InstrProfCorrelators *Correlator,
                      std::function<void(Error)> Warn)
-      : DataBuffer(std::move(DataBuffer)),
-        Correlator(dyn_cast_or_null<const InstrProfCorrelatorImpl<IntPtrT>>(
-            Correlator)),
+      : DataBuffer(std::move(DataBuffer)), Correlators((Correlator)),
         Warn(Warn) {}
   RawInstrProfReader(const RawInstrProfReader &) = delete;
   RawInstrProfReader &operator=(const RawInstrProfReader &) = delete;
@@ -434,8 +433,8 @@ class RawInstrProfReader : public InstrProfReader {
   bool atEnd() const { return Data == DataEnd; }
 
   void advanceData() {
-    // `CountersDelta` is a constant zero when using debug info correlation.
-    if (!Correlator) {
+    // `CountersDelta` is a constant zero when using correlation.
+    if (!IsCorrelatorUsed) {
       // The initial CountersDelta is the in-memory address difference between
       // the data and counts sections:
       // start(__llvm_prf_cnts) - start(__llvm_prf_data)
diff --git a/llvm/lib/Object/BuildID.cpp b/llvm/lib/Object/BuildID.cpp
index ef21458060abd6..363d2d379e9aa3 100644
--- a/llvm/lib/Object/BuildID.cpp
+++ b/llvm/lib/Object/BuildID.cpp
@@ -14,6 +14,7 @@
 
 #include "llvm/Object/BuildID.h"
 
+#include "llvm/Object/COFF.h"
 #include "llvm/Object/ELFObjectFile.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
@@ -42,6 +43,21 @@ template <typename ELFT> BuildIDRef getBuildID(const ELFFile<ELFT> &Obj) {
   return {};
 }
 
+BuildIDRef getBuildID(const COFFObjectFile *Obj) {
+  for (const debug_directory &D : Obj->debug_directories()) {
+    if (D.AddressOfRawData == 0 || D.Type != COFF::IMAGE_DEBUG_TYPE_CODEVIEW)
+      continue;
+    const codeview::DebugInfo *DebugInfo;
+    StringRef PDBFileName;
+    if (Error E = Obj->getDebugPDBInfo(&D, DebugInfo, PDBFileName))
+      consumeError(std::move(E));
+    if (DebugInfo->Signature.CVSignature == OMF::Signature::PDB70)
+      return ArrayRef(DebugInfo->PDB70.Signature,
+                      sizeof(DebugInfo->PDB70.Signature));
+  }
+  return {};
+}
+
 } // namespace
 
 BuildID llvm::object::parseBuildID(StringRef Str) {
@@ -62,6 +78,8 @@ BuildIDRef llvm::object::getBuildID(const ObjectFile *Obj) {
     return ::getBuildID(O->getELFFile());
   if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(Obj))
     return ::getBuildID(O->getELFFile());
+  if (auto *O = dyn_cast<COFFObjectFile>(Obj))
+    return ::getBuildID(O);
   return std::nullopt;
 }
 
diff --git a/llvm/lib/ProfileData/InstrProfCorrelator.cpp b/llvm/lib/ProfileData/InstrProfCorrelator.cpp
index cf80a58f43bd90..edd38fdcf9c446 100644
--- a/llvm/lib/ProfileData/InstrProfCorrelator.cpp
+++ b/llvm/lib/ProfileData/InstrProfCorrelator.cpp
@@ -52,6 +52,18 @@ const char *InstrProfCorrelator::FunctionNameAttributeName = "Function Name";
 const char *InstrProfCorrelator::CFGHashAttributeName = "CFG Hash";
 const char *InstrProfCorrelator::NumCountersAttributeName = "Num Counters";
 
+bool InstrProfCorrelator::AtomicWarningCounter::shouldEmitWarning() {
+  return MaxWarnings == 0 ||
+         WarningCount.fetch_add(1, std::memory_order_relaxed) < MaxWarnings;
+}
+
+InstrProfCorrelator::AtomicWarningCounter::~AtomicWarningCounter() {
+  if (MaxWarnings > 0 && WarningCount > MaxWarnings) {
+    WithColor::warning() << format("Suppressed %d additional warnings\n",
+                                   WarningCount - MaxWarnings);
+  }
+}
+
 llvm::Expected<std::unique_ptr<InstrProfCorrelator::Context>>
 InstrProfCorrelator::Context::get(std::unique_ptr<MemoryBuffer> Buffer,
                                   const object::ObjectFile &Obj,
@@ -87,11 +99,14 @@ InstrProfCorrelator::Context::get(std::unique_ptr<MemoryBuffer> Buffer,
     ++C->CountersSectionStart;
 
   C->ShouldSwapBytes = Obj.isLittleEndian() != sys::IsLittleEndianHost;
+  C->BuildID = object::getBuildID(&Obj);
   return Expected<std::unique_ptr<Context>>(std::move(C));
 }
 
 llvm::Expected<std::unique_ptr<InstrProfCorrelator>>
-InstrProfCorrelator::get(StringRef Filename, ProfCorrelatorKind FileKind) {
+InstrProfCorrelator::get(StringRef Filename, ProfCorrelatorKind FileKind,
+                         std::mutex &CorrelateLock, std::mutex &WarnLock,
+                         AtomicWarningCounter *WarnCounter) {
   if (FileKind == DEBUG_INFO) {
     auto DsymObjectsOrErr =
         object::MachOObjectFile::findDsymObjectMembers(Filename);
@@ -110,14 +125,16 @@ InstrProfCorrelator::get(StringRef Filename, ProfCorrelatorKind FileKind) {
     if (auto Err = BufferOrErr.takeError())
       return std::move(Err);
 
-    return get(std::move(*BufferOrErr), FileKind);
+    return get(std::move(*BufferOrErr), FileKind, CorrelateLock, WarnLock,
+               WarnCounter);
   }
   if (FileKind == BINARY) {
     auto BufferOrErr = errorOrToExpected(MemoryBuffer::getFile(Filename));
     if (auto Err = BufferOrErr.takeError())
       return std::move(Err);
 
-    return get(std::move(*BufferOrErr), FileKind);
+    return get(std::move(*BufferOrErr), FileKind, CorrelateLock, WarnLock,
+               WarnCounter);
   }
   return make_error<InstrProfError>(
       instrprof_error::unable_to_correlate_profile,
@@ -127,7 +144,9 @@ InstrProfCorrelator::get(StringRef Filename, ProfCorrelatorKind FileKind) {
 
 llvm::Expected<std::unique_ptr<InstrProfCorrelator>>
 InstrProfCorrelator::get(std::unique_ptr<MemoryBuffer> Buffer,
-                         ProfCorrelatorKind FileKind) {
+                         ProfCorrelatorKind FileKind, std::mutex &CorrelateLock,
+                         std::mutex &WarnLock,
+                         AtomicWarningCounter *WarnCounter) {
   auto BinOrErr = object::createBinary(*Buffer);
   if (auto Err = BinOrErr.takeError())
     return std::move(Err);
@@ -139,36 +158,46 @@ InstrProfCorrelator::get(std::unique_ptr<MemoryBuffer> Buffer,
     auto T = Obj->makeTriple();
     if (T.isArch64Bit())
       return InstrProfCorrelatorImpl<uint64_t>::get(std::move(*CtxOrErr), *Obj,
-                                                    FileKind);
+                                                    FileKind, CorrelateLock,
+                                                    WarnLock, WarnCounter);
     if (T.isArch32Bit())
       return InstrProfCorrelatorImpl<uint32_t>::get(std::move(*CtxOrErr), *Obj,
-                                                    FileKind);
+                                                    FileKind, CorrelateLock,
+                                                    WarnLock, WarnCounter);
   }
   return make_error<InstrProfError>(
       instrprof_error::unable_to_correlate_profile, "not an object file");
 }
 
-std::optional<size_t> InstrProfCorrelator::getDataSize() const {
+size_t InstrProfCorrelator::getDataSize() const {
   if (auto *C = dyn_cast<InstrProfCorrelatorImpl<uint32_t>>(this)) {
     return C->getDataSize();
   } else if (auto *C = dyn_cast<InstrProfCorrelatorImpl<uint64_t>>(this)) {
     return C->getDataSize();
   }
-  return {};
+  return 0;
+}
+
+bool InstrProfCorrelator::shouldEmitWarning() {
+  return WarnCounter && WarnCounter->shouldEmitWarning();
 }
 
 namespace llvm {
 
 template <>
 InstrProfCorrelatorImpl<uint32_t>::InstrProfCorrelatorImpl(
-    std::unique_ptr<InstrProfCorrelator::Context> Ctx)
-    : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_32Bit,
-                              std::move(Ctx)) {}
+    std::unique_ptr<InstrProfCorrelator::Context> Ctx,
+    std::mutex &CorrelateLock, std::mutex &WarnLock,
+    AtomicWarningCounter *WarnCounter)
+    : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_32Bit, std::move(Ctx),
+                              CorrelateLock, WarnLock, WarnCounter) {}
 template <>
 InstrProfCorrelatorImpl<uint64_t>::InstrProfCorrelatorImpl(
-    std::unique_ptr<InstrProfCorrelator::Context> Ctx)
-    : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_64Bit,
-                              std::move(Ctx)) {}
+    std::unique_ptr<InstrProfCorrelator::Context> Ctx,
+    std::mutex &CorrelateLock, std::mutex &WarnLock,
+    AtomicWarningCounter *WarnCounter)
+    : InstrProfCorrelatorImpl(InstrProfCorrelatorKind::CK_64Bit, std::move(Ctx),
+                              CorrelateLock, WarnLock, WarnCounter) {}
 template <>
 bool InstrProfCorrelatorImpl<uint32_t>::classof(const InstrProfCorrelator *C) {
   return C->getKind() == InstrProfCorrelatorKind::CK_32Bit;
@@ -184,35 +213,42 @@ template <class IntPtrT>
 llvm::Expected<std::unique_ptr<InstrProfCorrelatorImpl<IntPtrT>>>
 InstrProfCorrelatorImpl<IntPtrT>::get(
     std::unique_ptr<InstrProfCorrelator::Context> Ctx,
-    const object::ObjectFile &Obj, ProfCorrelatorKind FileKind) {
+    const object::ObjectFile &Obj, ProfCorrelatorKind FileKind,
+    std::mutex &CorrelateLock, std::mutex &WarnLock,
+    AtomicWarningCounter *WarnCounter) {
   if (FileKind == DEBUG_INFO) {
     if (Obj.isELF() || Obj.isMachO()) {
       auto DICtx = DWARFContext::create(Obj);
       return std::make_unique<DwarfInstrProfCorrelator<IntPtrT>>(
-          std::move(DICtx), std::move(Ctx));
+          std::move(DICtx), std::move(Ctx), CorrelateLock, WarnLock,
+          WarnCounter);
     }
     return make_error<InstrProfError>(
         instrprof_error::unable_to_correlate_profile,
         "unsupported debug info format (only DWARF is supported)");
   }
   if (Obj.isELF() || Obj.isCOFF())
-    return std::make_unique<BinaryInstrProfCorrelator<IntPtrT>>(std::move(Ctx));
+    return std::make_unique<BinaryInstrProfCorrelator<IntPtrT>>(
+        std::move(Ctx), CorrelateLock, WarnLock, WarnCounter);
   return make_error<InstrProfError>(
       instrprof_error::unable_to_correlate_profile,
       "unsupported binary format (only ELF and COFF are supported)");
 }
 
 template <class IntPtrT>
-Error InstrProfCorrelatorImpl<IntPtrT>::correlateProfileData(int MaxWarnings) {
-  assert(Data.empty() && Names.empty() && NamesVec.empty());
-  correlateProfileDataImpl(MaxWarnings);
+Error InstrProfCorrelatorImpl<IntPtrT>::correlateProfileData() {
+  std::lock_guard<std::mutex> Guard(this->CorrelateLock);
+  if (IsCorrelated)
+    return Error::success();
+  assert(Data.empty() && Names.empty());
+  correlateProfileDataImpl();
   if (this->Data.empty())
     return make_error<InstrProfError>(
         instrprof_error::unable_to_correlate_profile,
         "could not find any profile data metadata in correlated file");
   Error Result = correlateProfileNameImpl();
-  this->CounterOffsets.clear();
-  this->NamesVec.clear();
+  CounterOffsets.clear();
+  IsCorrelated = true;
   return Result;
 }
 
@@ -240,10 +276,9 @@ template <> struct yaml::SequenceElementTraits<InstrProfCorrelator::Probe> {
 };
 
 template <class IntPtrT>
-Error InstrProfCorrelatorImpl<IntPtrT>::dumpYaml(int MaxWarnings,
-                                                 raw_ostream &OS) {
+Error InstrProfCorrelatorImpl<IntPtrT>::dumpYaml(raw_ostream &OS) {
   InstrProfCorrelator::CorrelationData Data;
-  correlateProfileDataImpl(MaxWarnings, &Data);
+  correlateProfileDataImpl(&Data);
   if (Data.Probes.empty())
     return make_error<InstrProfError>(
         instrprof_error::unable_to_correlate_profile,
@@ -324,10 +359,7 @@ bool DwarfInstrProfCorrelator<IntPtrT>::isDIEOfProbe(const DWARFDie &Die) {
 
 template <class IntPtrT>
 void DwarfInstrProfCorrelator<IntPtrT>::correlateProfileDataImpl(
-    int MaxWarnings, InstrProfCorrelator::CorrelationData *Data) {
-  bool UnlimitedWarnings = (MaxWarnings == 0);
-  // -N suppressed warnings means we can emit up to N (unsuppressed) warnings
-  int NumSuppressedWarnings = -MaxWarnings;
+    InstrProfCorrelator::CorrelationData *Data) {
   auto maybeAddProbe = [&](DWARFDie Die) {
     if (!isDIEOfProbe(Die))
       return;
@@ -364,7 +396,8 @@ void DwarfInstrProfCorrelator<IntPtrT>::correlateProfileDataImpl(
       }
     }
     if (!FunctionName || !CFGHash || !CounterPtr || !NumCounters) {
-      if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) {
+      if (this->shouldEmitWarning()) {
+        std::lock_guard<std::mutex> Guard(this->WarnLock);
         WithColor::warning()
             << "Incomplete DIE for function " << FunctionName
             << ": CFGHash=" << CFGHash << "  CounterPtr=" << CounterPtr
@@ -376,7 +409,8 @@ void DwarfInstrProfCorrelator<IntPtrT>::correlateProfileDataImpl(
     uint64_t CountersStart = this->Ctx->CountersSectionStart;
     uint64_t CountersEnd = this->Ctx->CountersSectionEnd;
     if (*CounterPtr < CountersStart || *CounterPtr >= CountersEnd) {
-      if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) {
+      if (this->shouldEmitWarning()) {
+        std::lock_guard<std::mutex> Guard(this->WarnLock);
         WithColor::warning()
             << format("CounterPtr out of range for function %s: Actual=0x%x "
                       "Expected=[0x%x, 0x%x)\n",
@@ -385,7 +419,8 @@ void DwarfInstrProfCorrelator<IntPtrT>::correlateProfileDataImpl(
       }
       return;
     }
-    if (!FunctionPtr && (UnlimitedWarnings || ++NumSuppressedWarnings < 1)) {
+    if (!FunctionPtr && this->shouldEmitWarning()) {
+      std::lock_guard<std::mutex> Guard(this->WarnLock);
       WithColor::warning() << format("Could not find address of function %s\n",
                                      *FunctionName);
       LLVM_DEBUG(Die.dump(dbgs()));
@@ -411,7 +446,7 @@ void DwarfInstrProfCorrelator<IntPtrT>::correlateProfileDataImpl(
     } else {
       this->addDataProbe(IndexedInstrProf::ComputeHash(*FunctionName), *CFGHash,
                          CounterOffset, FunctionPtr.value_or(0), *NumCounters);
-      this->NamesVec.push_back(*FunctionName);
+      NamesVec.push_back(*FunctionName);
     }
   };
   for (auto &CU : DICtx->normal_units())
@@ -420,33 +455,25 @@ void DwarfInstrProfCorrelator<IntPtrT>::correlateProfileDataImpl(
   for (auto &CU : DICtx->dwo_units())
     for (const auto &Entry : CU->dies())
       maybeAddProbe(DWARFDie(CU.get(), &Entry));
-
-  if (!UnlimitedWarnings && NumSuppressedWarnings > 0)
-    WithColor::warning() << format("Suppressed %d additional warnings\n",
-                                   NumSuppressedWarnings);
 }
 
 template <class IntPtrT>
 Error DwarfInstrProfCorrelator<IntPtrT>::correlateProfileNameImpl() {
-  if (this->NamesVec.empty()) {
+  if (NamesVec.empty()) {
     return make_error<InstrProfError>(
         instrprof_error::unable_to_correlate_profile,
         "could not find any profile name metadata in debug info");
   }
   auto Result =
-      collectGlobalObjectNameStrings(this->NamesVec,
+      collectGlobalObjectNameStrings(NamesVec,
                                      /*doCompression=*/false, this->Names);
   return Result;
 }
 
 template <class IntPtrT>
 void BinaryInstrProfCorrelator<IntPtrT>::correlateProfileDataImpl(
-    int MaxWarnings, InstrProfCorrelator::CorrelationData *CorrelateData) {
+    InstrProfCorrelator::CorrelationData *CorrelateData) {
   using RawProfData = RawInstrProf::ProfileData<IntPtrT>;
-  bool UnlimitedWarnings = (MaxWarnings == 0);
-  // -N suppressed warnings means we can emit up to N (unsuppressed) warnings
-  int NumSuppressedWarnings = -MaxWarnings;
-
   const RawProfData *DataStart = (const RawProfData *)this->Ctx->DataStart;
   const RawProfData *DataEnd = (const RawProfData *)this->Ctx->DataEnd;
   // We need to use < here because the last data record may have no padding.
@@ -455,7 +482,8 @@ void BinaryInstrProfCorrelator<IntPtrT>::correlateProfileDataImpl(
     uint64_t CountersStart = this->Ctx->CountersSectionStart;
     uint64_t CountersEnd = this->Ctx->CountersSectionEnd;
     if (CounterPtr < CountersStart || CounterPtr >= CountersEnd) {
-      if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) {
+      if (this->shouldEmitWarning()) {
+        std::lock_guard<std::mutex> Guard(this->WarnLock);
         WithColor::warning()
             << format("CounterPtr out of range for function: Actual=0x%x "
                       "Expected=[0x%x, 0x%x) at data offset=0x%x\n",
@@ -481,3 +509,49 @@ Error BinaryInstrProfCorrelator<IntPtrT>::correlateProfileNameImpl() {
   this->Names.append(this->Ctx->NameStart, this->Ctx->NameSize);
   return Error::success();
 }
+
+llvm::Expected<std::unique_ptr<InstrProfCorrelators>> InstrProfCorrelators::get(
+    ArrayRef<std::pair<StringRef, InstrProfCorrelator::ProfCorrelatorKind>>
+        CorrelateInputs,
+    uint32_t MaxWarnings) {
+  StringMap<std::unique_ptr<InstrProfCorrelator>> CorrelatorMap;
+  StringMap<StringRef> FileMap;
+  auto WarnCounter =
+      std::make_unique<InstrProfCorrelator::AtomicWarningCounter>(MaxWarnings);
+  std::unique_ptr<std::mutex> CorrelateLock = std::make_unique<std::mutex>();
+  std::unique_ptr<std::mutex> WarnLock = std::make_unique<std::mutex>();
+  for (const auto &Input : CorrelateInputs) {
+    std::unique_ptr<InstrProfCorrelator> Correlator;
+    if (auto Err = InstrProfCorrelator::get(Input.first, Input.second,
+                                            *CorrelateLock.get(),
+                                            *WarnLock.get(), WarnCounter.get())
+                       .moveInto(Correlator))
+      return Err;
+    std::string BuildID = toHex(Correlator->getBuildID());
+    FileMap.try_emplace(BuildID, Input.first);
+    bool Inserted =
+        CorrelatorMap.try_emplace(BuildID, std::move(Correlator)).second;
+    if (!Inserted && WarnCounter->shouldEmitWarning()) {
+      std::lock_guard<std::mutex> Guard(*WarnLock);
+      WithColor::warning() << format(
+          "Duplicate build id (%s) found for %s and %s\n", BuildID.c_str(),
+          FileMap[BuildID].str().c_str(), Input.first.str().c_str());
+    }
+  }
+  return std::make_unique<InstrProfCorrelators>(
+      std::move(CorrelatorMap), std::move(CorrelateLock), std::move(WarnLock),
+      std::move(WarnCounter));
+}
+
+llvm::Expected<const InstrProfCorrelator *>
+InstrProfCorrelators::getCorrelator(object::BuildIDRef BuildID) const {
+  std::string BuildIDStr = toHex(BuildID);
+  auto I = CorrelatorMap.find(BuildIDStr);
+  if (I == CorrelatorMap.end())
+    return make_error<InstrProfError>(
+        instrprof_error::unable_to_correlate_profile,
+        "missing correlator file with build id " + BuildIDStr + "\n");
+  if (auto Err = I->getValue()->correlateProfileData())
+    return Err;
+  return I->getValue().get();
+}
diff --git a/llvm/lib/ProfileData/InstrProfReader.cpp b/llvm/lib/ProfileData/InstrProfReader.cpp
index 068922d421f8b9..c135710961cd43 100644
--- a/llvm/lib/ProfileData/InstrProfReader.cpp
+++ b/llvm/lib/ProfileData/InstrProfReader.cpp
@@ -155,19 +155,19 @@ printBinaryIdsInternal(raw_ostream &OS,
 
 Expected<std::unique_ptr<InstrProfReader>>
 InstrProfReader::create(const Twine &Path, vfs::FileSystem &FS,
-                        const InstrProfCorrelator *Correlator,
+                        const InstrProfCorrelators *Correlators,
                         std::function<void(Error)> Warn) {
   // Set up the buffer to read.
   auto BufferOrError = setupMemoryBuffer(Path, FS);
   if (Error E = BufferOrError.takeError())
     return std::move(E);
-  return InstrProfReader::create(std::move(BufferOrError.get()), Correlator,
+  return InstrProfReader::create(std::move(BufferOrError.get()), Correlators,
                                  Warn);
 }
 
 Expected<std::unique_ptr<InstrProfReader>>
 InstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer,
-                        const InstrProfCorrelator *Correlator,
+                        const InstrProfCorrelators *Correlators,
                         std::function<void(Error)> Warn) {
   if (Buffer->getBufferSize() == 0)
     return make_error<InstrProfError>(instrprof_error::empty_raw_profile);
@@ -177,9 +177,9 @@ InstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer,
   if (IndexedInstrProfReader::hasFormat(*Buffer))
     Result.reset(new IndexedInstrProfReader(std::move(Buffer)));
   else if (RawInstrProfReader64::hasFormat(*Buffer))
-    Result.reset(new RawInstrProfReader64(std::move(Buffer), Correlator, Warn));
+    Result.reset(new RawInstrProfReader64(std::move(Buffer), Correlators, Warn));
   else if (RawInstrProfReader32::hasFormat(*Buffer))
-    Result.reset(new RawInstrProfReader32(std::move(Buffer), Correlator, Warn));
+    Result.reset(new RawInstrProfReader32(std::move(Buffer), Correlators, Warn));
   else if (TextInstrProfReader::hasFormat(*Buffer))
     Result.reset(new TextInstrProfReader(std::move(Buffer)));
   else
@@ -559,6 +559,7 @@ Error RawInstrProfReader<IntPtrT>::readHeader(
 
   uint64_t BinaryIdSize = swap(Header.BinaryIdsSize);
   // Binary id start just after the header if exists.
+  object::BuildIDRef BinaryId;
   const uint8_t *BinaryIdStart =
       reinterpret_cast<const uint8_t *>(&Header) + sizeof(RawInstrProf::Header);
   const uint8_t *BinaryIdEnd = BinaryIdStart + BinaryIdSize;
@@ -570,6 +571,7 @@ Error RawInstrProfReader<IntPtrT>::readHeader(
             readBinaryIdsInternal(*DataBuffer, BinaryIdSize, BinaryIdStart,
                                   BinaryIds, getDataEndianness()))
       return Err;
+    BinaryId = BinaryIds.back();
   }
 
   CountersDelta = swap(Header.CountersDelta);
@@ -600,22 +602,28 @@ Error RawInstrProfReader<IntPtrT>::readHeader(
   if (Start + ValueDataOffset > DataBuffer->getBufferEnd())
     return error(instrprof_error::bad_header);
 
-  if (Correlator) {
+  if (DataSize == 0 && NamesSize == 0 && Correlators && !Correlators->empty()) {
     // These sizes in the raw file are zero because we constructed them in the
     // Correlator.
-    if (!(DataSize == 0 && NamesSize == 0 && CountersDelta == 0 &&
-          NamesDelta == 0))
+    if (!(CountersDelta == 0 && NamesDelta == 0))
       return error(instrprof_error::unexpected_correlation_info);
-    Data = Correlator->getDataPointer();
+    auto CorrelatorOrErr = Correlators->getCorrelator(BinaryId);
+    if (!CorrelatorOrErr)
+      return CorrelatorOrErr.takeError();
+    auto *Correlator = CorrelatorOrErr.get();
+    Data = reinterpret_cast<const RawInstrProf::ProfileData<IntPtrT> *>(
+        Correlator->getDataPointer());
     DataEnd = Data + Correlator->getDataSize();
     NamesStart = Correlator->getNamesPointer();
     NamesEnd = NamesStart + Correlator->getNamesSize();
+    IsCorrelatorUsed = true;
   } else {
     Data = reinterpret_cast<const RawInstrProf::ProfileData<IntPtrT> *>(
         Start + DataOffset);
     DataEnd = Data + NumData;
     NamesStart = Start + NamesOffset;
     NamesEnd = NamesStart + NamesSize;
+    IsCorrelatorUsed = false;
   }
 
   CountersStart = Start + CountersOffset;
diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp
index 322b7da2678f4f..05a7f79bd6d72e 100644
--- a/llvm/tools/llvm-profdata/llvm-profdata.cpp
+++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp
@@ -118,18 +118,18 @@ cl::opt<std::string> ProfiledBinary(
     "profiled-binary", cl::init(""),
     cl::desc("Path to binary from which the profile was collected."),
     cl::sub(ShowSubcommand), cl::sub(MergeSubcommand));
-cl::opt<std::string> DebugInfoFilename(
-    "debug-info", cl::init(""),
+cl::list<std::string> DebugInfoFilenames(
+    "debug-info",
     cl::desc(
         "For show, read and extract profile metadata from debug info and show "
         "the functions it found. For merge, use the provided debug info to "
         "correlate the raw profile."),
     cl::sub(ShowSubcommand), cl::sub(MergeSubcommand));
-cl::opt<std::string>
-    BinaryFilename("binary-file", cl::init(""),
-                   cl::desc("For merge, use the provided unstripped bianry to "
-                            "correlate the raw profile."),
-                   cl::sub(MergeSubcommand));
+cl::list<std::string>
+    BinaryFilenames("binary-file",
+                    cl::desc("For merge, use the provided unstripped bianry to "
+                             "correlate the raw profile."),
+                    cl::sub(MergeSubcommand));
 cl::opt<std::string> FuncNameFilter(
     "function",
     cl::desc("Details for matching functions. For overlapping CSSPGO, this "
@@ -607,7 +607,7 @@ static void overlapInput(const std::string &BaseFilename,
 
 /// Load an input into a writer context.
 static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,
-                      const InstrProfCorrelator *Correlator,
+                      const InstrProfCorrelators *Correlators,
                       const StringRef ProfiledBinary, WriterContext *WC) {
   std::unique_lock<std::mutex> CtxGuard{WC->Lock};
 
@@ -675,7 +675,7 @@ static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,
     ReaderWarning = {make_error<InstrProfError>(ErrCode, Msg), Filename};
   };
   auto ReaderOrErr =
-      InstrProfReader::create(Input.Filename, *FS, Correlator, Warn);
+      InstrProfReader::create(Input.Filename, *FS, Correlators, Warn);
   if (Error E = ReaderOrErr.takeError()) {
     // Skip the empty profiles by returning silently.
     auto [ErrCode, Msg] = InstrProfError::take(std::move(E));
@@ -793,28 +793,27 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
       OutputFormat != PF_Text)
     exitWithError("unknown format is specified");
 
-  // TODO: Maybe we should support correlation with mixture of different
-  // correlation modes(w/wo debug-info/object correlation).
-  if (!DebugInfoFilename.empty() && !BinaryFilename.empty())
-    exitWithError("Expected only one of -debug-info, -binary-file");
-  std::string CorrelateFilename;
-  ProfCorrelatorKind CorrelateKind = ProfCorrelatorKind::NONE;
-  if (!DebugInfoFilename.empty()) {
-    CorrelateFilename = DebugInfoFilename;
-    CorrelateKind = ProfCorrelatorKind::DEBUG_INFO;
-  } else if (!BinaryFilename.empty()) {
-    CorrelateFilename = BinaryFilename;
-    CorrelateKind = ProfCorrelatorKind::BINARY;
-  }
+  std::unique_ptr<InstrProfCorrelators> Correlators;
+  SmallVector<std::pair<StringRef, InstrProfCorrelator::ProfCorrelatorKind>>
+      CorrelateInputs;
 
-  std::unique_ptr<InstrProfCorrelator> Correlator;
-  if (CorrelateKind != InstrProfCorrelator::NONE) {
-    if (auto Err = InstrProfCorrelator::get(CorrelateFilename, CorrelateKind)
-                       .moveInto(Correlator))
-      exitWithError(std::move(Err), CorrelateFilename);
-    if (auto Err = Correlator->correlateProfileData(MaxDbgCorrelationWarnings))
-      exitWithError(std::move(Err), CorrelateFilename);
+  for (StringRef Filenames : DebugInfoFilenames) {
+    SmallVector<StringRef, 1> FilenameVec;
+    Filenames.split(FilenameVec, ',');
+    for (StringRef Filename : FilenameVec)
+      CorrelateInputs.push_back({Filename, InstrProfCorrelator::DEBUG_INFO});
   }
+  for (StringRef Filenames : BinaryFilenames) {
+    SmallVector<StringRef, 1> FilenameVec;
+    Filenames.split(FilenameVec, ',');
+    for (StringRef Filename : FilenameVec)
+      CorrelateInputs.push_back({Filename, InstrProfCorrelator::BINARY});
+  }
+  if (!CorrelateInputs.empty())
+    if (auto Err = InstrProfCorrelators::get(CorrelateInputs,
+                                             MaxDbgCorrelationWarnings)
+                       .moveInto(Correlators))
+      exitWithError(std::move(Err));
 
   std::mutex ErrorLock;
   SmallSet<instrprof_error, 4> WriterErrorCodes;
@@ -833,7 +832,7 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
 
   if (NumThreads == 1) {
     for (const auto &Input : Inputs)
-      loadInput(Input, Remapper, Correlator.get(), ProfiledBinary,
+      loadInput(Input, Remapper, Correlators.get(), ProfiledBinary,
                 Contexts[0].get());
   } else {
     ThreadPool Pool(hardware_concurrency(NumThreads));
@@ -841,7 +840,7 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
     // Load the inputs in parallel (N/NumThreads serial steps).
     unsigned Ctx = 0;
     for (const auto &Input : Inputs) {
-      Pool.async(loadInput, Input, Remapper, Correlator.get(), ProfiledBinary,
+      Pool.async(loadInput, Input, Remapper, Correlators.get(), ProfiledBinary,
                  Contexts[Ctx].get());
       Ctx = (Ctx + 1) % NumThreads;
     }
@@ -3077,17 +3076,22 @@ static int showDebugInfoCorrelation(const std::string &Filename,
   if (SFormat == ShowFormat::Json)
     exitWithError("JSON output is not supported for debug info correlation");
   std::unique_ptr<InstrProfCorrelator> Correlator;
+  InstrProfCorrelator::AtomicWarningCounter WarnCounter(
+      MaxDbgCorrelationWarnings);
+  std::mutex CorrelateLock;
+  std::mutex WarnLock;
   if (auto Err =
-          InstrProfCorrelator::get(Filename, InstrProfCorrelator::DEBUG_INFO)
+          InstrProfCorrelator::get(Filename, InstrProfCorrelator::DEBUG_INFO,
+                                   CorrelateLock, WarnLock, &WarnCounter)
               .moveInto(Correlator))
     exitWithError(std::move(Err), Filename);
   if (SFormat == ShowFormat::Yaml) {
-    if (auto Err = Correlator->dumpYaml(MaxDbgCorrelationWarnings, OS))
+    if (auto Err = Correlator->dumpYaml(OS))
       exitWithError(std::move(Err), Filename);
     return 0;
   }
 
-  if (auto Err = Correlator->correlateProfileData(MaxDbgCorrelationWarnings))
+  if (auto Err = Correlator->correlateProfileData())
     exitWithError(std::move(Err), Filename);
 
   InstrProfSymtab Symtab;
@@ -3108,10 +3112,10 @@ static int showDebugInfoCorrelation(const std::string &Filename,
 }
 
 static int show_main(int argc, const char *argv[]) {
-  if (Filename.empty() && DebugInfoFilename.empty())
+  if (Filename.empty() && DebugInfoFilenames.empty())
     exitWithError(
         "the positional argument '<profdata-file>' is required unless '--" +
-        DebugInfoFilename.ArgStr + "' is provided");
+        DebugInfoFilenames.ArgStr + "' is provided");
 
   if (Filename == OutputFilename) {
     errs() << sys::path::filename(argv[0]) << " " << argv[1]
@@ -3129,8 +3133,12 @@ static int show_main(int argc, const char *argv[]) {
   if (ShowAllFunctions && !FuncNameFilter.empty())
     WithColor::warning() << "-function argument ignored: showing all functions\n";
 
-  if (!DebugInfoFilename.empty())
-    return showDebugInfoCorrelation(DebugInfoFilename, SFormat, OS);
+  if (!DebugInfoFilenames.empty()) {
+    if (DebugInfoFilenames.size() > 1)
+      exitWithError("'--" + DebugInfoFilenames.ArgStr +
+                    "' only accept one argument in show subcommand");
+    return showDebugInfoCorrelation(DebugInfoFilenames[0], SFormat, OS);
+  }
 
   if (ShowProfileKind == instr)
     return showInstrProfile(SFormat, OS);

>From 3481cf6ddd3b7a9759c2a7b37cf288cc36e25847 Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Tue, 19 Dec 2023 12:36:32 -0500
Subject: [PATCH 2/3] fixup! [Profile] Allow profile merging with multiple
 correlate files.

---
 .../test/profile/Linux/instrprof-debug-info-correlate.c       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c b/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c
index 47dbf3c87e68f1..af19101f91a2f1 100644
--- a/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c
+++ b/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c
@@ -31,13 +31,13 @@
 // RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-foo.cpp -c -fpic -shared -Wl,--build-id -o %t-libfoo.so
 // RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp %t-libfoo.so -Wl,--build-id -o %t
 // RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t
-// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t,%t-libfoo.o %t.proflite
+// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t,%t-libfoo.so %t.proflite
 // RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
 
 // One binary is built without build id.
 // RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp %t-libfoo.so -o %t
 // RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t
-// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t,%t-libfoo.o %t.proflite
+// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t,%t-libfoo.so %t.proflite
 // RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
 
 // Warning about multiple correlate files have the same build id.

>From 5f90bc2f97df90766483657cfc7634cffc67caeb Mon Sep 17 00:00:00 2001
From: Zequan Wu <zequanwu at google.com>
Date: Wed, 20 Dec 2023 13:20:10 -0500
Subject: [PATCH 3/3] add windows binary id test and clean up.

---
 .../Linux/instrprof-correlation-mixed.test    |  5 +-
 .../Linux/instrprof-debug-info-correlate.c    | 62 +++++++++++--------
 .../Windows/instrprof-binary-correlate.c      | 41 ++++++++++++
 .../test/profile/instrprof-binary-correlate.c |  2 -
 llvm/lib/ProfileData/InstrProfCorrelator.cpp  |  2 +-
 llvm/lib/ProfileData/InstrProfReader.cpp      |  6 +-
 6 files changed, 85 insertions(+), 33 deletions(-)
 create mode 100644 compiler-rt/test/profile/Windows/instrprof-binary-correlate.c

diff --git a/compiler-rt/test/profile/Linux/instrprof-correlation-mixed.test b/compiler-rt/test/profile/Linux/instrprof-correlation-mixed.test
index 8cc4611eec4f0b..60f167dee4a49c 100644
--- a/compiler-rt/test/profile/Linux/instrprof-correlation-mixed.test
+++ b/compiler-rt/test/profile/Linux/instrprof-correlation-mixed.test
@@ -7,8 +7,9 @@
 
 // Compiling with differnt configs.
 // RUN: %clang_pgogen -o %t -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp -c -o %t-main.o
-// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp -c -o %t-main.debug.o
-// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-foo.cpp -fpic -shared -Wl,--build-id -o %t-libfoo.debug.so
+// RUN: %clang_pgogen -o %t -g -mllvm -profile-correlate=debug-info -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp -c -o %t-main.debug.o
+// RUN: %clang_pgogen -o %t -mllvm -profile-correlate=binary -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-foo.cpp -c -o %t-foo.binary.o
+// RUN: %clang_pgogen -o %t -g -mllvm -profile-correlate=debug-info -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-foo.cpp -fpic -shared -Wl,--build-id -o %t-libfoo.debug.so
 // RUN: %clang_pgogen -o %t -mllvm -profile-correlate=binary -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-foo.cpp -fpic -shared -Wl,--build-id -o %t-libfoo.binary.so
 
 // Test mixing default raw profile and lightweight raw profile generated with debug info correlate.
diff --git a/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c b/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c
index af19101f91a2f1..fa2ab6fb2cd27d 100644
--- a/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c
+++ b/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c
@@ -7,13 +7,16 @@
 // RUN: env LLVM_PROFILE_FILE=%t.d4.proflite %run %t.d4
 // RUN: llvm-profdata merge -o %t.d4.profdata --debug-info=%t.d4 %t.d4.proflite
 
-// RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.d4.profdata)
+// RUN: llvm-profdata show --all-functions --counts %t.normal.profdata > %t.normal.profdata.show
+// RUN: llvm-profdata show --all-functions --counts %t.d4.profdata > %t.d4.profdata.show
+// RUN: diff %t.normal.profdata.show %t.d4.profdata.show
 
 // RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp %S/../Inputs/instrprof-debug-info-correlate-foo.cpp
 // RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t
 // RUN: llvm-profdata merge -o %t.profdata --debug-info=%t %t.proflite
 
-// RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
+// RUN: llvm-profdata show --all-functions --counts %t.profdata > %t.profdata.show
+// RUN: diff %t.normal.profdata.show %t.profdata.show
 
 // RUN: %clang_pgogen -o %t.cov -g -mllvm --debug-info-correlate -mllvm -pgo-function-entry-coverage -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp %S/../Inputs/instrprof-debug-info-correlate-foo.cpp
 // RUN: env LLVM_PROFILE_FILE=%t.cov.proflite %run %t.cov
@@ -23,43 +26,50 @@
 // RUN: env LLVM_PROFILE_FILE=%t.cov.profraw %run %t.cov.normal
 // RUN: llvm-profdata merge -o %t.cov.normal.profdata %t.cov.profraw
 
-// RUN: diff <(llvm-profdata show --all-functions --counts %t.cov.normal.profdata) <(llvm-profdata show --all-functions --counts %t.cov.profdata)
-
-// Test debug info correlate with build id.
-
-// Both binaries are built with build id.
-// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-foo.cpp -c -fpic -shared -Wl,--build-id -o %t-libfoo.so
-// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp %t-libfoo.so -Wl,--build-id -o %t
-// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t
-// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t,%t-libfoo.so %t.proflite
-// RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
-
-// One binary is built without build id.
-// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp %t-libfoo.so -o %t
-// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t
-// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t,%t-libfoo.so %t.proflite
-// RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
-
-// Warning about multiple correlate files have the same build id.
-// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t,%t,%t-libfoo.o %t.proflite 2>&1 >/dev/null | FileCheck %s --check-prefix=WARN
-// WARN: Duplicate build id ({{.*}}) found for {{.*}} and {{.*}}
+// RUN: llvm-profdata show --all-functions --counts %t.cov.normal.profdata > %t.cov.normal.profdata.show
+// RUN: llvm-profdata show --all-functions --counts %t.cov.profdata > %t.cov.profdata.show
+// RUN: diff %t.cov.normal.profdata.show  %t.cov.profdata.show
 
 // Test debug info correlate with online merging.
 
 // RUN: env LLVM_PROFILE_FILE=%t-1.profraw %run %t.normal
 // RUN: env LLVM_PROFILE_FILE=%t-2.profraw %run %t.normal
-// RUN: llvm-profdata merge -o %t.normal.profdata %t-1.profraw %t-2.profraw
+// RUN: llvm-profdata merge -o %t.merged.normal.profdata %t-1.profraw %t-2.profraw
 
 // RUN: rm -rf %t.profdir && mkdir %t.profdir
 // RUN: env LLVM_PROFILE_FILE=%t.profdir/%m.proflite %run %t
 // RUN: env LLVM_PROFILE_FILE=%t.profdir/%m.proflite %run %t
-// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t %t.profdir/
+// RUN: llvm-profdata merge -o %t.merged.profdata --debug-info=%t %t.profdir/
 
-// RUN: diff <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
+// RUN: llvm-profdata show --all-functions --counts %t.merged.normal.profdata > %t.merged.normal.profdata.show
+// RUN: llvm-profdata show --all-functions --counts %t.merged.profdata > %t.merged.profdata.show
+// RUN: diff %t.merged.normal.profdata.show %t.merged.profdata.show
 
 // RUN: rm -rf %t.profdir && mkdir %t.profdir
 // RUN: env LLVM_PROFILE_FILE=%t.profdir/%m.cov.proflite %run %t.cov
 // RUN: env LLVM_PROFILE_FILE=%t.profdir/%m.cov.proflite %run %t.cov
 // RUN: llvm-profdata merge -o %t.cov.profdata --debug-info=%t.cov %t.profdir/
 
-// RUN: diff <(llvm-profdata show --all-functions --counts %t.cov.normal.profdata) <(llvm-profdata show --all-functions --counts %t.cov.profdata)
+// RUN: llvm-profdata show --all-functions --counts %t.cov.profdata > %t.cov.profdata.show
+// RUN: diff %t.cov.normal.profdata.show %t.cov.profdata.show
+
+// Test debug info correlate with build id.
+
+// Both binaries are built with build id.
+// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-foo.cpp -fpic -shared -Wl,--build-id -o %t-libfoo.so
+// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp %t-libfoo.so -Wl,--build-id -o %t
+// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t
+// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t,%t-libfoo.so %t.proflite
+// RUN: llvm-profdata show --all-functions --counts %t.profdata > %t.profdata.show
+// RUN: diff %t.normal.profdata.show %t.profdata.show
+
+// One binary is built without build id.
+// RUN: %clang_pgogen -o %t -g -mllvm --debug-info-correlate -mllvm --disable-vp=true %S/../Inputs/instrprof-debug-info-correlate-main.cpp %t-libfoo.so -o %t
+// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t
+// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t,%t-libfoo.so %t.proflite
+// RUN: llvm-profdata show --all-functions --counts %t.profdata > %t.profdata.show
+// RUN: diff %t.normal.profdata.show %t.profdata.show
+
+// Warning about multiple correlate files have the same build id.
+// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t,%t,%t-libfoo.so %t.proflite 2>&1 >/dev/null | FileCheck %s --check-prefix=WARN
+// WARN: Duplicate build id ("") found for {{.*}} and {{.*}}
diff --git a/compiler-rt/test/profile/Windows/instrprof-binary-correlate.c b/compiler-rt/test/profile/Windows/instrprof-binary-correlate.c
new file mode 100644
index 00000000000000..f01fa2b85ee1f3
--- /dev/null
+++ b/compiler-rt/test/profile/Windows/instrprof-binary-correlate.c
@@ -0,0 +1,41 @@
+// REQUIRES: target={{.*windows-msvc.*}}
+// REQUIRES: lld-available
+
+// Test binary correlate with build id.
+
+// Three binaries are built with build id.
+// RUN: rm -rf %t.dir && split-file %s %t.dir
+// RUN: %clang_profgen %t.dir/main.c %t.dir/foo.c %t.dir/bar.c -o %t.dir/main.exe
+// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t.dir/main.exe
+// RUN: llvm-profdata merge -o %t.normal.profdata %t.profraw
+// RUN: llvm-profdata show --all-functions --counts %t.normal.profdata > %t.normal.profdata.show
+
+// RUN: %clang_profgen -mllvm -profile-correlate=binary %t.dir/foo.c -fuse-ld=lld -Wl,-build-id -Wl,-dll -o %t.dir/foo.dll
+// RUN: %clang_profgen -mllvm -profile-correlate=binary %t.dir/bar.c -fuse-ld=lld -Wl,-build-id -Wl,-dll -o %t.dir/bar.dll
+// RUN: %clang_profgen -mllvm -profile-correlate=binary %t.dir/main.c -fuse-ld=lld -Wl,-build-id %t.dir/foo.lib %t.dir/bar.lib -o %t.dir/main.exe
+// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t.dir/main.exe
+// RUN: llvm-profdata merge -o %t.profdata %t.proflite --binary-file=%t.dir/foo.dll,%t.dir/bar.dll,%t.dir/main.exe
+// RUN: llvm-profdata show --all-functions --counts %t.profdata > %t.profdata.show
+// RUN: diff %t.normal.profdata.show %t.profdata.show
+
+// One binary is built without build id.
+// RUN: %clang_profgen -mllvm -profile-correlate=binary %t.dir/main.c -fuse-ld=lld %t.dir/foo.lib %t.dir/bar.lib -o %t.dir/main.exe
+// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t.dir/main.exe
+// RUN: llvm-profdata merge -o %t.profdata %t.proflite --binary-file=%t.dir/foo.dll,%t.dir/bar.dll,%t.dir/main.exe
+// RUN: llvm-profdata show --all-functions --counts %t.profdata > %t.profdata.show
+// RUN: diff %t.normal.profdata.show %t.profdata.show
+
+//--- foo.c
+__declspec(dllexport) void foo() {}
+
+//--- bar.c
+__declspec(dllexport) void bar() {}
+
+//--- main.c
+__declspec(dllimport) void foo();
+__declspec(dllimport) void bar();
+int main() {
+  foo();
+  bar();
+  return 0;
+}
diff --git a/compiler-rt/test/profile/instrprof-binary-correlate.c b/compiler-rt/test/profile/instrprof-binary-correlate.c
index 0e96745a9b58f4..2e9b636cb712a2 100644
--- a/compiler-rt/test/profile/instrprof-binary-correlate.c
+++ b/compiler-rt/test/profile/instrprof-binary-correlate.c
@@ -47,5 +47,3 @@
 // RUN: diff %t.normal.merged.profdata.show %t-4.profdata.show
 // RUN: diff %t.normal.merged.report %t-4.report
 // RUN: diff %t.normal.merged.show %t-4.show
-
-// TODO: After adding support for binary ID, test binaries with different binary IDs.
diff --git a/llvm/lib/ProfileData/InstrProfCorrelator.cpp b/llvm/lib/ProfileData/InstrProfCorrelator.cpp
index edd38fdcf9c446..ff2488654b70a0 100644
--- a/llvm/lib/ProfileData/InstrProfCorrelator.cpp
+++ b/llvm/lib/ProfileData/InstrProfCorrelator.cpp
@@ -534,7 +534,7 @@ llvm::Expected<std::unique_ptr<InstrProfCorrelators>> InstrProfCorrelators::get(
     if (!Inserted && WarnCounter->shouldEmitWarning()) {
       std::lock_guard<std::mutex> Guard(*WarnLock);
       WithColor::warning() << format(
-          "Duplicate build id (%s) found for %s and %s\n", BuildID.c_str(),
+          "Duplicate build id (\"%s\") found for %s and %s\n", BuildID.c_str(),
           FileMap[BuildID].str().c_str(), Input.first.str().c_str());
     }
   }
diff --git a/llvm/lib/ProfileData/InstrProfReader.cpp b/llvm/lib/ProfileData/InstrProfReader.cpp
index ba3f8696a3fd45..8f7e694eba9691 100644
--- a/llvm/lib/ProfileData/InstrProfReader.cpp
+++ b/llvm/lib/ProfileData/InstrProfReader.cpp
@@ -177,9 +177,11 @@ InstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer,
   if (IndexedInstrProfReader::hasFormat(*Buffer))
     Result.reset(new IndexedInstrProfReader(std::move(Buffer)));
   else if (RawInstrProfReader64::hasFormat(*Buffer))
-    Result.reset(new RawInstrProfReader64(std::move(Buffer), Correlators, Warn));
+    Result.reset(
+        new RawInstrProfReader64(std::move(Buffer), Correlators, Warn));
   else if (RawInstrProfReader32::hasFormat(*Buffer))
-    Result.reset(new RawInstrProfReader32(std::move(Buffer), Correlators, Warn));
+    Result.reset(
+        new RawInstrProfReader32(std::move(Buffer), Correlators, Warn));
   else if (TextInstrProfReader::hasFormat(*Buffer))
     Result.reset(new TextInstrProfReader(std::move(Buffer)));
   else



More information about the cfe-commits mailing list