[clang] 32db121 - [Coverage] Allow Clang coverage to be used with debug info correlation.

Zequan Wu via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 15 10:47:30 PDT 2023


Author: Zequan Wu
Date: 2023-09-15T13:47:23-04:00
New Revision: 32db121b29f78e4c41116b2a8f1c730f9522b202

URL: https://github.com/llvm/llvm-project/commit/32db121b29f78e4c41116b2a8f1c730f9522b202
DIFF: https://github.com/llvm/llvm-project/commit/32db121b29f78e4c41116b2a8f1c730f9522b202.diff

LOG: [Coverage] Allow Clang coverage to be used with debug info correlation.

Debug info correlation is an option in InstrProfiling pass, which is used by
both IR instrumentation and front-end instrumentation. So, Clang coverage can
also benefits the binary size saving from it.

Reviewed By: ellis

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

Added: 
    clang/test/CodeGen/coverage-profile-raw-version.c
    compiler-rt/test/profile/Darwin/coverage-debug-info-correlate.cpp
    compiler-rt/test/profile/Linux/coverage-debug-info-correlate.cpp
    llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-byte-coverage.ll
    llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-clang-coverage.ll

Modified: 
    clang/lib/CodeGen/CoverageMappingGen.cpp
    llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h
    llvm/include/llvm/ProfileData/InstrProfCorrelator.h
    llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
    llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
    llvm/lib/ProfileData/InstrProfCorrelator.cpp
    llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp

Removed: 
    llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-coverage.ll


################################################################################
diff  --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp
index dd81be6d96c6ee9..7cc2e0630f65cd7 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -31,6 +31,10 @@
 // is textually included.
 #define COVMAP_V3
 
+namespace llvm {
+extern cl::opt<bool> DebugInfoCorrelate;
+} // namespace llvm
+
 static llvm::cl::opt<bool> EmptyLineCommentCoverage(
     "emptyline-comment-coverage",
     llvm::cl::desc("Emit emptylines and comment lines as skipped regions (only "
@@ -1821,6 +1825,22 @@ void CoverageMappingModuleGen::emit() {
                              llvm::GlobalValue::InternalLinkage, NamesArrVal,
                              llvm::getCoverageUnusedNamesVarName());
   }
+  const StringRef VarName(INSTR_PROF_QUOTE(INSTR_PROF_RAW_VERSION_VAR));
+  llvm::Type *IntTy64 = llvm::Type::getInt64Ty(Ctx);
+  uint64_t ProfileVersion = INSTR_PROF_RAW_VERSION;
+  if (llvm::DebugInfoCorrelate)
+    ProfileVersion |= VARIANT_MASK_DBG_CORRELATE;
+  auto *VersionVariable = new llvm::GlobalVariable(
+      CGM.getModule(), llvm::Type::getInt64Ty(Ctx), true,
+      llvm::GlobalValue::WeakAnyLinkage,
+      llvm::Constant::getIntegerValue(IntTy64, llvm::APInt(64, ProfileVersion)),
+      VarName);
+  VersionVariable->setVisibility(llvm::GlobalValue::HiddenVisibility);
+  llvm::Triple TT(CGM.getModule().getTargetTriple());
+  if (TT.supportsCOMDAT()) {
+    VersionVariable->setLinkage(llvm::GlobalValue::ExternalLinkage);
+    VersionVariable->setComdat(CGM.getModule().getOrInsertComdat(VarName));
+  }
 }
 
 unsigned CoverageMappingModuleGen::getFileID(FileEntryRef File) {

diff  --git a/clang/test/CodeGen/coverage-profile-raw-version.c b/clang/test/CodeGen/coverage-profile-raw-version.c
new file mode 100644
index 000000000000000..48b05722967ca41
--- /dev/null
+++ b/clang/test/CodeGen/coverage-profile-raw-version.c
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -debug-info-kind=standalone -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -debug-info-kind=standalone -mllvm -debug-info-correlate -fprofile-instrument=clang -fcoverage-mapping -emit-llvm -o - %s | FileCheck %s --check-prefix=DEBUG_INFO
+
+// CHECK: @__llvm_profile_raw_version = hidden constant i64 8, comdat
+// DEBUG_INFO: @__llvm_profile_raw_version = hidden constant i64 576460752303423496, comdat
+
+int main() {
+    return 0;
+}

diff  --git a/compiler-rt/test/profile/Darwin/coverage-debug-info-correlate.cpp b/compiler-rt/test/profile/Darwin/coverage-debug-info-correlate.cpp
new file mode 100644
index 000000000000000..5555f745dceae05
--- /dev/null
+++ b/compiler-rt/test/profile/Darwin/coverage-debug-info-correlate.cpp
@@ -0,0 +1,78 @@
+// Test debug info correlate with clang coverage.
+
+// Test the case when there is no __llvm_prf_names in the binary.
+// RUN: %clang_profgen -o %t.normal -fcoverage-mapping %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
+
+// RUN: %clang_profgen -o %t -g -mllvm --debug-info-correlate -fcoverage-mapping %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.dSYM %t.proflite
+
+// RUN: 
diff  <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
+
+// RUN: llvm-cov report --instr-profile=%t.profdata %t | FileCheck %s -check-prefix=NONAME
+
+// Test debug info correlate with clang coverage (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: 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.dSYM %t.profdir
+
+// RUN: 
diff  <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
+
+// RUN: llvm-cov report --instr-profile=%t.profdata %t | FileCheck %s -check-prefix=NONAME
+// RUN: llvm-cov report --instr-profile=%t.normal.profdata %t | FileCheck %s -check-prefix=NONAME
+
+// NONAME:      Filename                                    Regions    Missed Regions     Cover   Functions  Missed Functions  Executed       Lines      Missed Lines     Cover    Branches   Missed Branches     Cover
+// NONAME-NEXT: --
+// NONAME-NEXT: instrprof-debug-info-correlate-bar.h              3                 1    66.67%           1                 0   100.00%           5                 1    80.00%           2                 1    50.00%
+// NONAME-NEXT: instrprof-debug-info-correlate-foo.cpp            5                 2    60.00%           2                 1    50.00%           6                 2    66.67%           2                 1    50.00%
+// NONAME-NEXT: instrprof-debug-info-correlate-main.cpp           4                 0   100.00%           1                 0   100.00%           5                 0   100.00%           2                 0   100.00%
+// NONAME-NEXT: --
+// NONAME-NEXT: TOTAL                                            12                 3    75.00%           4                 1    75.00%          16                 3    81.25%           6                 2    66.67%
+
+// Test the case when there is __llvm_prf_names in the binary (those are names of uninstrumented functions).
+// RUN: %clang_profgen -o %t.normal -fcoverage-mapping -mllvm -enable-name-compression=false %s
+// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t.normal
+// RUN: llvm-profdata merge -o %t.normal.profdata %t.profraw
+
+// RUN: %clang_profgen -o %t -g -mllvm --debug-info-correlate -fcoverage-mapping -mllvm -enable-name-compression=false %s
+// RUN: env LLVM_PROFILE_FILE=%t.proflite %run %t
+// RUN: llvm-profdata merge -o %t.profdata --debug-info=%t.dSYM %t.proflite
+
+// RUN: 
diff  <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
+
+// RUN: llvm-cov export --format=lcov --instr-profile=%t.profdata %t | FileCheck %s -check-prefix=NAME
+
+// Test debug info correlate with clang coverage (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: 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.dSYM %t.profdir
+
+// RUN: 
diff  <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
+
+// RUN: llvm-cov export --format=lcov --instr-profile=%t.profdata %t | FileCheck %s -check-prefix=NAME
+// NAME: _Z9used_funcv
+// NAME: main
+// NAME: _ZN1A11unused_funcEv
+
+struct A {
+  void unused_func() {}
+};
+void used_func() {}
+int main() {
+  used_func();
+  return 0;
+}

diff  --git a/compiler-rt/test/profile/Linux/coverage-debug-info-correlate.cpp b/compiler-rt/test/profile/Linux/coverage-debug-info-correlate.cpp
new file mode 100644
index 000000000000000..06901de4a24291a
--- /dev/null
+++ b/compiler-rt/test/profile/Linux/coverage-debug-info-correlate.cpp
@@ -0,0 +1,78 @@
+// Test debug info correlate with clang coverage.
+
+// Test the case when there is no __llvm_prf_names in the binary.
+// RUN: %clang_profgen -o %t.normal -fcoverage-mapping %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
+
+// RUN: %clang_profgen -o %t -g -mllvm --debug-info-correlate -fcoverage-mapping %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-cov report --instr-profile=%t.profdata %t | FileCheck %s -check-prefix=NONAME
+
+// Test debug info correlate with clang coverage (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: 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: 
diff  <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
+
+// RUN: llvm-cov report --instr-profile=%t.profdata %t | FileCheck %s -check-prefix=NONAME
+// RUN: llvm-cov report --instr-profile=%t.normal.profdata %t | FileCheck %s -check-prefix=NONAME
+
+// NONAME:      Filename                                    Regions    Missed Regions     Cover   Functions  Missed Functions  Executed       Lines      Missed Lines     Cover    Branches   Missed Branches     Cover
+// NONAME-NEXT: --
+// NONAME-NEXT: instrprof-debug-info-correlate-bar.h              3                 1    66.67%           1                 0   100.00%           5                 1    80.00%           2                 1    50.00%
+// NONAME-NEXT: instrprof-debug-info-correlate-foo.cpp            5                 2    60.00%           2                 1    50.00%           6                 2    66.67%           2                 1    50.00%
+// NONAME-NEXT: instrprof-debug-info-correlate-main.cpp           4                 0   100.00%           1                 0   100.00%           5                 0   100.00%           2                 0   100.00%
+// NONAME-NEXT: --
+// NONAME-NEXT: TOTAL                                            12                 3    75.00%           4                 1    75.00%          16                 3    81.25%           6                 2    66.67%
+
+// Test the case when there is __llvm_prf_names in the binary (those are names of uninstrumented functions).
+// RUN: %clang_profgen -o %t.normal -fcoverage-mapping -mllvm -enable-name-compression=false %s
+// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t.normal
+// RUN: llvm-profdata merge -o %t.normal.profdata %t.profraw
+
+// RUN: %clang_profgen -o %t -g -mllvm --debug-info-correlate -fcoverage-mapping -mllvm -enable-name-compression=false %s
+// 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-cov export --format=lcov --instr-profile=%t.profdata %t | FileCheck %s -check-prefix=NAME
+
+// Test debug info correlate with clang coverage (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: 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: 
diff  <(llvm-profdata show --all-functions --counts %t.normal.profdata) <(llvm-profdata show --all-functions --counts %t.profdata)
+
+// RUN: llvm-cov export --format=lcov --instr-profile=%t.profdata %t | FileCheck %s -check-prefix=NAME
+// NAME: _Z9used_funcv
+// NAME: main
+// NAME: _ZN1A11unused_funcEv
+
+struct A {
+  void unused_func() {}
+};
+void used_func() {}
+int main() {
+  used_func();
+  return 0;
+}

diff  --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h
index 326c1b0d33384e3..75cb44a47a81868 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMappingReader.h
@@ -203,7 +203,8 @@ class BinaryCoverageReader : public CoverageMappingReader {
   BinaryCoverageReader &operator=(const BinaryCoverageReader &) = delete;
 
   static Expected<std::vector<std::unique_ptr<BinaryCoverageReader>>>
-  create(MemoryBufferRef ObjectBuffer, StringRef Arch,
+  create(MemoryBufferRef ObjectBuffer, IndexedInstrProfReader &ProfileReader,
+         StringRef Arch,
          SmallVectorImpl<std::unique_ptr<MemoryBuffer>> &ObjectFileBuffers,
          StringRef CompilationDir = "",
          SmallVectorImpl<object::BuildIDRef> *BinaryIDs = nullptr);

diff  --git a/llvm/include/llvm/ProfileData/InstrProfCorrelator.h b/llvm/include/llvm/ProfileData/InstrProfCorrelator.h
index 4b6953996887bd3..9050173269283bb 100644
--- a/llvm/include/llvm/ProfileData/InstrProfCorrelator.h
+++ b/llvm/include/llvm/ProfileData/InstrProfCorrelator.h
@@ -39,6 +39,8 @@ class InstrProfCorrelator {
   /// \param MaxWarnings the maximum number of warnings to emit (0 = no limit)
   virtual Error correlateProfileData(int MaxWarnings) = 0;
 
+  virtual Error correlateCovUnusedFuncNames(int MaxWarnings) = 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;
@@ -52,6 +54,12 @@ class InstrProfCorrelator {
   /// Return the number of bytes in the names string.
   size_t getNamesSize() const { return Names.size(); }
 
+  const char *getCovUnusedFuncNamesPointer() const {
+    return CovUnusedFuncNames.c_str();
+  }
+
+  size_t getCovUnusedFuncNamesSize() const { return CovUnusedFuncNames.size(); }
+
   /// Return the size of the counters section in bytes.
   uint64_t getCountersSectionSize() const {
     return Ctx->CountersSectionEnd - Ctx->CountersSectionStart;
@@ -60,6 +68,7 @@ class InstrProfCorrelator {
   static const char *FunctionNameAttributeName;
   static const char *CFGHashAttributeName;
   static const char *NumCountersAttributeName;
+  static const char *CovFunctionNameAttributeName;
 
   enum InstrProfCorrelatorKind { CK_32Bit, CK_64Bit };
   InstrProfCorrelatorKind getKind() const { return Kind; }
@@ -83,6 +92,7 @@ class InstrProfCorrelator {
 
   std::string Names;
   std::vector<std::string> NamesVec;
+  std::string CovUnusedFuncNames;
 
   struct Probe {
     std::string FunctionName;
@@ -205,6 +215,8 @@ class DwarfInstrProfCorrelator : public InstrProfCorrelatorImpl<IntPtrT> {
   void correlateProfileDataImpl(
       int MaxWarnings,
       InstrProfCorrelator::CorrelationData *Data = nullptr) override;
+
+  Error correlateCovUnusedFuncNames(int MaxWarnings) override;
 };
 
 } // end namespace llvm

diff  --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index ac83b21968ba870..f879c73e1e4d6f6 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -361,7 +361,7 @@ Error CoverageMapping::loadFromFile(
 
   SmallVector<object::BuildIDRef> BinaryIDs;
   auto CoverageReadersOrErr = BinaryCoverageReader::create(
-      CovMappingBufRef, Arch, Buffers, CompilationDir,
+      CovMappingBufRef, ProfileReader, Arch, Buffers, CompilationDir,
       FoundBinaryIDs ? &BinaryIDs : nullptr);
   if (Error E = CoverageReadersOrErr.takeError()) {
     E = handleMaybeNoDataFoundError(std::move(E));

diff  --git a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
index e468fbf7184f8f9..40ce0ebd25e2bac 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
@@ -25,6 +25,7 @@
 #include "llvm/Object/MachOUniversal.h"
 #include "llvm/Object/ObjectFile.h"
 #include "llvm/ProfileData/InstrProf.h"
+#include "llvm/ProfileData/InstrProfReader.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Compression.h"
 #include "llvm/Support/Debug.h"
@@ -1021,8 +1022,23 @@ static Expected<std::vector<SectionRef>> lookupSections(ObjectFile &OF,
   return Sections;
 }
 
+static Error getProfileNamesFromDebugInfo(StringRef FileName,
+                                          InstrProfSymtab &ProfileNames) {
+  std::unique_ptr<InstrProfCorrelator> Correlator;
+  if (auto E = InstrProfCorrelator::get(FileName).moveInto(Correlator))
+    return E;
+  if (auto E = Correlator->correlateCovUnusedFuncNames(0))
+    return E;
+  if (auto E = ProfileNames.create(
+          StringRef(Correlator->getCovUnusedFuncNamesPointer(),
+                    Correlator->getCovUnusedFuncNamesSize())))
+    return E;
+  return Error::success();
+}
+
 static Expected<std::unique_ptr<BinaryCoverageReader>>
-loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch,
+loadBinaryFormat(std::unique_ptr<Binary> Bin,
+                 IndexedInstrProfReader &ProfileReader, StringRef Arch,
                  StringRef CompilationDir = "",
                  object::BuildIDRef *BinaryID = nullptr) {
   std::unique_ptr<ObjectFile> OF;
@@ -1052,11 +1068,24 @@ loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch,
 
   // Look for the sections that we are interested in.
   auto ObjFormat = OF->getTripleObjectFormat();
+  InstrProfSymtab ProfileNames = ProfileReader.getSymtab();
   auto NamesSection =
       lookupSections(*OF, getInstrProfSectionName(IPSK_name, ObjFormat,
-                                                 /*AddSegmentInfo=*/false));
-  if (auto E = NamesSection.takeError())
-    return std::move(E);
+                                                  /*AddSegmentInfo=*/false));
+  if (auto E = NamesSection.takeError()) {
+    if (OF->hasDebugInfo()) {
+      if (auto E =
+              getProfileNamesFromDebugInfo(OF->getFileName(), ProfileNames))
+        return make_error<CoverageMapError>(coveragemap_error::malformed);
+    }
+    consumeError(std::move(E));
+  } else {
+    std::vector<SectionRef> NamesSectionRefs = *NamesSection;
+    if (NamesSectionRefs.size() != 1)
+      return make_error<CoverageMapError>(coveragemap_error::malformed);
+    if (Error E = ProfileNames.create(NamesSectionRefs.back()))
+      return std::move(E);
+  }
   auto CoverageSection =
       lookupSections(*OF, getInstrProfSectionName(IPSK_covmap, ObjFormat,
                                                   /*AddSegmentInfo=*/false));
@@ -1071,15 +1100,6 @@ loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch,
     return CoverageMappingOrErr.takeError();
   StringRef CoverageMapping = CoverageMappingOrErr.get();
 
-  InstrProfSymtab ProfileNames;
-  std::vector<SectionRef> NamesSectionRefs = *NamesSection;
-  if (NamesSectionRefs.size() != 1)
-    return make_error<CoverageMapError>(
-        coveragemap_error::malformed,
-        "the size of coverage mapping section is not one");
-  if (Error E = ProfileNames.create(NamesSectionRefs.back()))
-    return std::move(E);
-
   // Look for the coverage records section (Version4 only).
   auto CoverageRecordsSections =
       lookupSections(*OF, getInstrProfSectionName(IPSK_covfun, ObjFormat,
@@ -1149,7 +1169,8 @@ static bool isArchSpecifierInvalidOrMissing(Binary *Bin, StringRef Arch) {
 
 Expected<std::vector<std::unique_ptr<BinaryCoverageReader>>>
 BinaryCoverageReader::create(
-    MemoryBufferRef ObjectBuffer, StringRef Arch,
+    MemoryBufferRef ObjectBuffer, IndexedInstrProfReader &ProfileReader,
+    StringRef Arch,
     SmallVectorImpl<std::unique_ptr<MemoryBuffer>> &ObjectFileBuffers,
     StringRef CompilationDir, SmallVectorImpl<object::BuildIDRef> *BinaryIDs) {
   std::vector<std::unique_ptr<BinaryCoverageReader>> Readers;
@@ -1195,8 +1216,8 @@ BinaryCoverageReader::create(
       }
 
       return BinaryCoverageReader::create(
-          ArchiveOrErr.get()->getMemoryBufferRef(), Arch, ObjectFileBuffers,
-          CompilationDir, BinaryIDs);
+          ArchiveOrErr.get()->getMemoryBufferRef(), ProfileReader, Arch,
+          ObjectFileBuffers, CompilationDir, BinaryIDs);
     }
   }
 
@@ -1209,8 +1230,8 @@ BinaryCoverageReader::create(
         return ChildBufOrErr.takeError();
 
       auto ChildReadersOrErr = BinaryCoverageReader::create(
-          ChildBufOrErr.get(), Arch, ObjectFileBuffers, CompilationDir,
-          BinaryIDs);
+          ChildBufOrErr.get(), ProfileReader, Arch, ObjectFileBuffers,
+          CompilationDir, BinaryIDs);
       if (!ChildReadersOrErr)
         return ChildReadersOrErr.takeError();
       for (auto &Reader : ChildReadersOrErr.get())
@@ -1230,8 +1251,9 @@ BinaryCoverageReader::create(
   }
 
   object::BuildIDRef BinaryID;
-  auto ReaderOrErr = loadBinaryFormat(std::move(Bin), Arch, CompilationDir,
-                                      BinaryIDs ? &BinaryID : nullptr);
+  auto ReaderOrErr =
+      loadBinaryFormat(std::move(Bin), ProfileReader, Arch, CompilationDir,
+                       BinaryIDs ? &BinaryID : nullptr);
   if (!ReaderOrErr)
     return ReaderOrErr.takeError();
   Readers.push_back(std::move(ReaderOrErr.get()));

diff  --git a/llvm/lib/ProfileData/InstrProfCorrelator.cpp b/llvm/lib/ProfileData/InstrProfCorrelator.cpp
index 71787c9bd8577b4..a31b2d9cbfca1a1 100644
--- a/llvm/lib/ProfileData/InstrProfCorrelator.cpp
+++ b/llvm/lib/ProfileData/InstrProfCorrelator.cpp
@@ -38,6 +38,8 @@ Expected<object::SectionRef> getCountersSection(const object::ObjectFile &Obj) {
 const char *InstrProfCorrelator::FunctionNameAttributeName = "Function Name";
 const char *InstrProfCorrelator::CFGHashAttributeName = "CFG Hash";
 const char *InstrProfCorrelator::NumCountersAttributeName = "Num Counters";
+const char *InstrProfCorrelator::CovFunctionNameAttributeName =
+    "Cov Function Name";
 
 llvm::Expected<std::unique_ptr<InstrProfCorrelator::Context>>
 InstrProfCorrelator::Context::get(std::unique_ptr<MemoryBuffer> Buffer,
@@ -361,3 +363,76 @@ void DwarfInstrProfCorrelator<IntPtrT>::correlateProfileDataImpl(
     WithColor::warning() << format("Suppressed %d additional warnings\n",
                                    NumSuppressedWarnings);
 }
+
+template <class IntPtrT>
+Error DwarfInstrProfCorrelator<IntPtrT>::correlateCovUnusedFuncNames(
+    int MaxWarnings) {
+  bool UnlimitedWarnings = (MaxWarnings == 0);
+  // -N suppressed warnings means we can emit up to N (unsuppressed) warnings
+  int NumSuppressedWarnings = -MaxWarnings;
+  std::vector<std::string> UnusedFuncNames;
+  auto IsDIEOfCovName = [](const DWARFDie &Die) {
+    const auto &ParentDie = Die.getParent();
+    if (!Die.isValid() || !ParentDie.isValid() || Die.isNULL())
+      return false;
+    if (Die.getTag() != dwarf::DW_TAG_variable)
+      return false;
+    if (ParentDie.getParent().isValid())
+      return false;
+    if (!Die.hasChildren())
+      return false;
+    if (const char *Name = Die.getName(DINameKind::ShortName))
+      return StringRef(Name).startswith(getCoverageUnusedNamesVarName());
+    return false;
+  };
+  auto MaybeAddCovFuncName = [&](DWARFDie Die) {
+    if (!IsDIEOfCovName(Die))
+      return;
+    for (const DWARFDie &Child : Die.children()) {
+      if (Child.getTag() != dwarf::DW_TAG_LLVM_annotation)
+        continue;
+      auto AnnotationFormName = Child.find(dwarf::DW_AT_name);
+      auto AnnotationFormValue = Child.find(dwarf::DW_AT_const_value);
+      if (!AnnotationFormName || !AnnotationFormValue)
+        continue;
+      auto AnnotationNameOrErr = AnnotationFormName->getAsCString();
+      if (auto Err = AnnotationNameOrErr.takeError()) {
+        consumeError(std::move(Err));
+        continue;
+      }
+      std::optional<const char *> FunctionName;
+      StringRef AnnotationName = *AnnotationNameOrErr;
+      if (AnnotationName.compare(
+              InstrProfCorrelator::CovFunctionNameAttributeName) == 0) {
+        if (auto EC =
+                AnnotationFormValue->getAsCString().moveInto(FunctionName))
+          consumeError(std::move(EC));
+      }
+      if (!FunctionName) {
+        if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) {
+          WithColor::warning() << format(
+              "Missing coverage function name value at DIE 0x%08" PRIx64,
+              Child.getOffset());
+        }
+        return;
+      }
+      UnusedFuncNames.push_back(*FunctionName);
+    }
+  };
+  for (auto &CU : DICtx->normal_units())
+    for (const auto &Entry : CU->dies())
+      MaybeAddCovFuncName(DWARFDie(CU.get(), &Entry));
+  for (auto &CU : DICtx->dwo_units())
+    for (const auto &Entry : CU->dies())
+      MaybeAddCovFuncName(DWARFDie(CU.get(), &Entry));
+
+  if (!UnlimitedWarnings && NumSuppressedWarnings > 0)
+    WithColor::warning() << format("Suppressed %d additional warnings\n",
+                                   NumSuppressedWarnings);
+  if (!UnusedFuncNames.empty()) {
+    auto Result = collectPGOFuncNameStrings(
+        UnusedFuncNames, /*doCompression=*/false, this->CovUnusedFuncNames);
+    return Result;
+  }
+  return Error::success();
+}

diff  --git a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp
index 8173e777b189926..c90692980d86ac5 100644
--- a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp
+++ b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp
@@ -766,6 +766,34 @@ void InstrProfiling::lowerCoverageData(GlobalVariable *CoverageNamesVar) {
     if (isa<ConstantExpr>(NC))
       NC->dropAllReferences();
   }
+  if (DebugInfoCorrelate && !ReferencedNames.empty()) {
+    MDNode *Node = *M->debug_compile_units_begin();
+    DICompileUnit *CU = cast<DICompileUnit>(Node);
+    DIBuilder DB(*M, true, CU);
+    LLVMContext &Ctx = M->getContext();
+    SmallVector<llvm::Metadata *> Annots;
+    for (auto *NameVar: ReferencedNames) {
+      Metadata *CovFunctionNameAnnotation[] = {
+          MDString::get(Ctx, InstrProfCorrelator::CovFunctionNameAttributeName),
+          MDString::get(Ctx,
+                        std::string(getPGOFuncNameVarInitializer(NameVar))),
+      };
+      Annots.push_back(MDNode::get(Ctx, CovFunctionNameAnnotation));
+    }
+    auto Annotations = DB.getOrCreateArray(Annots);
+    auto *DICovName = DB.createGlobalVariableExpression(
+        CU, CoverageNamesVar->getName(), /*LinkageName=*/StringRef(),
+        CU->getFile(),
+        /*LineNo=*/0, DB.createUnspecifiedType("Coverage Type"),
+        /*IsLocalToUnit=*/true, /*IsDefined=*/true, /*Expr=*/nullptr,
+        /*Decl=*/nullptr, /*TemplateParams=*/nullptr, /*AlignInBits=*/0,
+        Annotations);
+    CoverageNamesVar->addDebugInfo(DICovName);
+    DB.finalize();
+    ReferencedNames.clear();
+    return;
+  }
+
   CoverageNamesVar->eraseFromParent();
 }
 

diff  --git a/llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-coverage.ll b/llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-byte-coverage.ll
similarity index 100%
rename from llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-coverage.ll
rename to llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-byte-coverage.ll

diff  --git a/llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-clang-coverage.ll b/llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-clang-coverage.ll
new file mode 100644
index 000000000000000..07af56e2c1bdde5
--- /dev/null
+++ b/llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-clang-coverage.ll
@@ -0,0 +1,64 @@
+; Test if unused function names are correctly emitted as dwarf variable debug info under DW_TAG_compile_unit
+
+; RUN: opt < %s -passes=instrprof -debug-info-correlate -S > %t.ll
+; RUN: FileCheck < %t.ll --implicit-check-not "{{__llvm_prf_data|__llvm_prf_names}}" %s
+; RUN: %llc_dwarf -O0 -filetype=obj < %t.ll | llvm-dwarfdump - | FileCheck %s --check-prefix=CHECK-DWARF
+
+; REQUIRES: system-linux, object-emission
+
+ at __profn_foo = private constant [3 x i8] c"foo"
+ at __profn_bar = private constant [3 x i8] c"bar"
+ at __profn_baz = private constant [3 x i8] c"baz"
+ at __llvm_coverage_names = internal constant [2 x ptr] [ptr @__profn_bar, ptr @__profn_baz]
+; CHECK: @__llvm_coverage_names = internal constant [2 x ptr] [ptr @__profn_bar, ptr @__profn_baz], !dbg ![[DBG:[0-9]+]]
+; CHECK: ![[DBG]] = !DIGlobalVariableExpression(var: ![[VAR:[0-9]+]], expr: !DIExpression())
+; CHECK: ![[VAR]] = {{.*}} !DIGlobalVariable(name: "__llvm_coverage_names"
+; CHECK-SAME: scope: ![[SCOPE:[0-9]+]]
+; CHECK-SAME: annotations: ![[ANNOTATIONS:[0-9]+]]
+; CHECK: ![[SCOPE]] = {{.*}} !DICompileUnit(
+; CHECK: ![[ANNOTATIONS]] = !{![[FUNC_NAME1:[0-9]+]], ![[FUNC_NAME2:[0-9]+]]}
+; CHECK: ![[FUNC_NAME1]] = !{!"Cov Function Name", !"bar"}
+; CHECK: ![[FUNC_NAME2]] = !{!"Cov Function Name", !"baz"}
+
+define void @_Z3foov() !dbg !12 {
+  call void @llvm.instrprof.increment(ptr @__profn_foo, i64 12345678, i32 2, i32 0)
+  ret void
+}
+
+declare void @llvm.instrprof.increment(ptr, i64, i32, i32)
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8, !9, !10}
+!llvm.ident = !{!11}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-info-correlate.cpp", directory: "")
+!2 = !{i32 7, !"Dwarf Version", i32 4}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 8, !"branch-target-enforcement", i32 0}
+!6 = !{i32 8, !"sign-return-address", i32 0}
+!7 = !{i32 8, !"sign-return-address-all", i32 0}
+!8 = !{i32 8, !"sign-return-address-with-bkey", i32 0}
+!9 = !{i32 7, !"uwtable", i32 1}
+!10 = !{i32 7, !"frame-pointer", i32 1}
+!11 = !{!"clang version 14.0.0"}
+!12 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !13, file: !13, line: 1, type: !14, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !16)
+!13 = !DIFile(filename: "debug-info-correlate.cpp", directory: "")
+!14 = !DISubroutineType(types: !15)
+!15 = !{null}
+!16 = !{}
+
+; CHECK-DWARF: DW_TAG_compile_unit
+; CHECK-DWARF:   DW_TAG_variable
+; CHECK-DWARF:     DW_AT_name	("__llvm_coverage_names")
+; CHECK-DWARF:     DW_AT_type	({{.*}} "Coverage Type")
+; CHECK-DWARF:     DW_TAG_LLVM_annotation
+; CHECK-DWARF:       DW_AT_name	("Cov Function Name")
+; CHECK-DWARF:       DW_AT_const_value	("bar")
+; CHECK-DWARF:     DW_TAG_LLVM_annotation
+; CHECK-DWARF:       DW_AT_name	("Cov Function Name")
+; CHECK-DWARF:       DW_AT_const_value	("baz")
+; CHECK-DWARF:   DW_TAG_unspecified_type
+; CHECK-DWARF:     DW_AT_name ("Coverage Type")
+; CHECK-DWARF:   NULL


        


More information about the cfe-commits mailing list