[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