diff --git a/compiler-rt/include/profile/InstrProfData.inc b/compiler-rt/include/profile/InstrProfData.inc --- a/compiler-rt/include/profile/InstrProfData.inc +++ b/compiler-rt/include/profile/InstrProfData.inc @@ -657,6 +657,8 @@ * generated profile, and 0 if this is a Clang FE generated profile. * 1 in bit 57 indicates there are context-sensitive records in the profile. * The 59th bit indicates whether to use debug info to correlate profiles. + * The 60th bit indicates if counters are single bytes representing function + * entry coverage. */ #define VARIANT_MASKS_ALL 0xff00000000000000ULL #define GET_VERSION(V) ((V) & ~VARIANT_MASKS_ALL) @@ -664,6 +666,7 @@ #define VARIANT_MASK_CSIR_PROF (0x1ULL << 57) #define VARIANT_MASK_INSTR_ENTRY (0x1ULL << 58) #define VARIANT_MASK_DBG_CORRELATE (0x1ULL << 59) +#define VARIANT_MASK_BYTE_ENTRY_COVERAGE (0x1ULL << 60) #define INSTR_PROF_RAW_VERSION_VAR __llvm_profile_raw_version #define INSTR_PROF_PROFILE_RUNTIME_VAR __llvm_profile_runtime #define INSTR_PROF_PROFILE_COUNTER_BIAS_VAR __llvm_profile_counter_bias diff --git a/compiler-rt/lib/profile/InstrProfilingBuffer.c b/compiler-rt/lib/profile/InstrProfilingBuffer.c --- a/compiler-rt/lib/profile/InstrProfilingBuffer.c +++ b/compiler-rt/lib/profile/InstrProfilingBuffer.c @@ -65,6 +65,8 @@ } COMPILER_RT_VISIBILITY size_t __llvm_profile_counter_entry_size(void) { + if (__llvm_profile_get_version() & VARIANT_MASK_BYTE_ENTRY_COVERAGE) + return sizeof(uint8_t); return sizeof(uint64_t); } diff --git a/compiler-rt/lib/profile/InstrProfilingMerge.c b/compiler-rt/lib/profile/InstrProfilingMerge.c --- a/compiler-rt/lib/profile/InstrProfilingMerge.c +++ b/compiler-rt/lib/profile/InstrProfilingMerge.c @@ -155,8 +155,14 @@ if (SrcCounters < SrcCountersStart || SrcCounters >= SrcNameStart || (SrcCounters + __llvm_profile_counter_entry_size() * NC) > SrcNameStart) return 1; - for (unsigned I = 0; I < NC; I++) - ((uint64_t *)DstCounters)[I] += ((uint64_t *)SrcCounters)[I]; + for (unsigned I = 0; I < NC; I++) { + if (__llvm_profile_get_version() & VARIANT_MASK_BYTE_ENTRY_COVERAGE) { + // A value of zero signifies the function is covered. + DstCounters[I] &= SrcCounters[I]; + } else { + ((uint64_t *)DstCounters)[I] += ((uint64_t *)SrcCounters)[I]; + } + } /* Now merge value profile data. */ if (!VPMergeHook) diff --git a/compiler-rt/test/profile/Darwin/instrprof-debug-info-correlate.c b/compiler-rt/test/profile/Darwin/instrprof-debug-info-correlate.c --- a/compiler-rt/test/profile/Darwin/instrprof-debug-info-correlate.c +++ b/compiler-rt/test/profile/Darwin/instrprof-debug-info-correlate.c @@ -10,3 +10,13 @@ // RUN: llvm-profdata merge -o %t.normal.profdata %t.profraw // RUN: diff %t.normal.profdata %t.profdata + +// RUN: %clang_pgogen -o %t.cov -g -mllvm --debug-info-correlate -mllvm -pgo-coverage-instrumentation -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 +// RUN: llvm-profdata merge -o %t.cov.profdata --debug-info=%t.cov.dSYM %t.cov.proflite + +// RUN: %clang_pgogen -o %t.cov.normal -mllvm --pgo-coverage-instrumentation -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.profraw %run %t.cov.normal +// RUN: llvm-profdata merge -o %t.cov.normal.profdata %t.cov.profraw + +// RUN: diff %t.cov.normal.profdata %t.cov.profdata diff --git a/compiler-rt/test/profile/Inputs/instrprof-debug-info-correlate-bar.h b/compiler-rt/test/profile/Inputs/instrprof-debug-info-correlate-bar.h --- a/compiler-rt/test/profile/Inputs/instrprof-debug-info-correlate-bar.h +++ b/compiler-rt/test/profile/Inputs/instrprof-debug-info-correlate-bar.h @@ -1,4 +1,5 @@ int foo(int); +int unused(int); inline int bar(int a) { while (a > 100) diff --git a/compiler-rt/test/profile/Inputs/instrprof-debug-info-correlate-foo.cpp b/compiler-rt/test/profile/Inputs/instrprof-debug-info-correlate-foo.cpp --- a/compiler-rt/test/profile/Inputs/instrprof-debug-info-correlate-foo.cpp +++ b/compiler-rt/test/profile/Inputs/instrprof-debug-info-correlate-foo.cpp @@ -5,3 +5,5 @@ return 4 * a + 1; return bar(a); } + +int unused(int a) { return a * a; } diff --git a/compiler-rt/test/profile/Inputs/instrprof-debug-info-correlate-main.cpp b/compiler-rt/test/profile/Inputs/instrprof-debug-info-correlate-main.cpp --- a/compiler-rt/test/profile/Inputs/instrprof-debug-info-correlate-main.cpp +++ b/compiler-rt/test/profile/Inputs/instrprof-debug-info-correlate-main.cpp @@ -1,7 +1,7 @@ #include "instrprof-debug-info-correlate-bar.h" typedef int (*FP)(int); -FP Fps[2] = {foo, bar}; +FP Fps[3] = {foo, bar, unused}; int main() { for (int i = 0; i < 5; i++) diff --git a/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c b/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c --- a/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c +++ b/compiler-rt/test/profile/Linux/instrprof-debug-info-correlate.c @@ -10,3 +10,13 @@ // RUN: llvm-profdata merge -o %t.normal.profdata %t.profraw // RUN: diff %t.normal.profdata %t.profdata + +// RUN: %clang_pgogen -o %t.cov -g -mllvm --debug-info-correlate -mllvm -pgo-coverage-instrumentation -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 +// RUN: llvm-profdata merge -o %t.cov.profdata --debug-info=%t.cov %t.cov.proflite + +// RUN: %clang_pgogen -o %t.cov.normal -mllvm --pgo-coverage-instrumentation -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.profraw %run %t.cov.normal +// RUN: llvm-profdata merge -o %t.cov.normal.profdata %t.cov.profraw + +// RUN: diff %t.cov.normal.profdata %t.cov.profdata diff --git a/compiler-rt/test/profile/instrprof-coverage.c b/compiler-rt/test/profile/instrprof-coverage.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/profile/instrprof-coverage.c @@ -0,0 +1,18 @@ +// RUN: %clang_pgogen -mllvm -pgo-coverage-instrumentation %s -o %t.out +// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t.out +// RUN: llvm-profdata merge -o %t.profdata %t.profraw +// RUN: llvm-profdata show --covered %t.profdata | FileCheck %s --check-prefix CHECK --implicit-check-not goo + +int foo(int i) { return 4 * i + 1; } +int bar(int i) { return 4 * i + 2; } +int goo(int i) { return 4 * i + 3; } + +int main(int argc, char *argv[]) { + foo(5); + argc ? bar(6) : goo(7); + return 0; +} + +// CHECK: main +// CHECK: foo +// CHECK: bar diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -13129,6 +13129,32 @@ """""""""" See description of '``llvm.instrprof.increment``' intrinsic. +'``llvm.instrprof.cover``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare void @llvm.instrprof.cover(i8* , i64 ) + +Overview: +""""""""" + +The '``llvm.instrprof.cover``' intrinsic is used to implement function entry +coverage instrumentation. + +Arguments: +"""""""""" +The arguments are the same as the first two arguments of +'``llvm.instrprof.increment``'. + +Semantics: +"""""""""" +Similar to the '``llvm.instrprof.increment``' intrinsic, but it stores zero to +the profiling variable to signify that the function has been covered. We store +zero because this is more efficient on some targets. '``llvm.instrprof.value.profile``' Intrinsic ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/llvm/include/llvm/IR/IntrinsicInst.h b/llvm/include/llvm/IR/IntrinsicInst.h --- a/llvm/include/llvm/IR/IntrinsicInst.h +++ b/llvm/include/llvm/IR/IntrinsicInst.h @@ -1126,36 +1126,48 @@ Value *getSrc() const { return const_cast(getArgOperand(1)); } }; -/// This represents the llvm.instrprof_increment intrinsic. -class InstrProfIncrementInst : public IntrinsicInst { +/// A base class for all instrprof intrinsics. +class InstrProfBase : public IntrinsicInst { public: - static bool classof(const IntrinsicInst *I) { - return I->getIntrinsicID() == Intrinsic::instrprof_increment; - } - static bool classof(const Value *V) { - return isa(V) && classof(cast(V)); - } - + // The name of the instrumented function. GlobalVariable *getName() const { return cast( const_cast(getArgOperand(0))->stripPointerCasts()); } - + // The hash of the CFG for the instrumented function. ConstantInt *getHash() const { return cast(const_cast(getArgOperand(1))); } + // The number of counters for the instrumented function. + ConstantInt *getNumCounters() const; + // The index of the counter that this instruction acts on. + Value *getIndex() const; +}; - ConstantInt *getNumCounters() const { - return cast(const_cast(getArgOperand(2))); +/// This represents the llvm.instrprof.cover intrinsic. +class InstrProfCoverInst : public InstrProfBase { +public: + static bool classof(const IntrinsicInst *I) { + return I->getIntrinsicID() == Intrinsic::instrprof_cover; } - - ConstantInt *getIndex() const { - return cast(const_cast(getArgOperand(3))); + static bool classof(const Value *V) { + return isa(V) && classof(cast(V)); } +}; +/// This represents the llvm.instrprof.increment intrinsic. +class InstrProfIncrementInst : public InstrProfBase { +public: + static bool classof(const IntrinsicInst *I) { + return I->getIntrinsicID() == Intrinsic::instrprof_increment; + } + static bool classof(const Value *V) { + return isa(V) && classof(cast(V)); + } Value *getStep() const; }; +/// This represents the llvm.instrprof.increment.step intrinsic. class InstrProfIncrementInstStep : public InstrProfIncrementInst { public: static bool classof(const IntrinsicInst *I) { @@ -1166,8 +1178,8 @@ } }; -/// This represents the llvm.instrprof_value_profile intrinsic. -class InstrProfValueProfileInst : public IntrinsicInst { +/// This represents the llvm.instrprof.value.profile intrinsic. +class InstrProfValueProfileInst : public InstrProfBase { public: static bool classof(const IntrinsicInst *I) { return I->getIntrinsicID() == Intrinsic::instrprof_value_profile; @@ -1176,15 +1188,6 @@ return isa(V) && classof(cast(V)); } - GlobalVariable *getName() const { - return cast( - const_cast(getArgOperand(0))->stripPointerCasts()); - } - - ConstantInt *getHash() const { - return cast(const_cast(getArgOperand(1))); - } - Value *getTargetValue() const { return cast(const_cast(getArgOperand(2))); } diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -582,6 +582,9 @@ def int_stackprotector : DefaultAttrsIntrinsic<[], [llvm_ptr_ty, llvm_ptrptr_ty], []>; def int_stackguard : DefaultAttrsIntrinsic<[llvm_ptr_ty], [], []>; +// A cover for instrumentation based profiling. +def int_instrprof_cover : Intrinsic<[], [llvm_ptr_ty, llvm_i64_ty]>; + // A counter increment for instrumentation based profiling. def int_instrprof_increment : Intrinsic<[], [llvm_ptr_ty, llvm_i64_ty, diff --git a/llvm/include/llvm/ProfileData/InstrProf.h b/llvm/include/llvm/ProfileData/InstrProf.h --- a/llvm/include/llvm/ProfileData/InstrProf.h +++ b/llvm/include/llvm/ProfileData/InstrProf.h @@ -1154,7 +1154,8 @@ // aware this is an ir_level profile so it can set the version flag. GlobalVariable *createIRLevelProfileFlagVar(Module &M, bool IsCS, bool InstrEntryBBEnabled, - bool DebugInfoCorrelate); + bool DebugInfoCorrelate, + bool CoverageInstrumentation); // Create the variable for the profile file name. void createProfileFileNameVar(Module &M, StringRef InstrProfileOutput); diff --git a/llvm/include/llvm/ProfileData/InstrProfData.inc b/llvm/include/llvm/ProfileData/InstrProfData.inc --- a/llvm/include/llvm/ProfileData/InstrProfData.inc +++ b/llvm/include/llvm/ProfileData/InstrProfData.inc @@ -657,6 +657,8 @@ * generated profile, and 0 if this is a Clang FE generated profile. * 1 in bit 57 indicates there are context-sensitive records in the profile. * The 59th bit indicates whether to use debug info to correlate profiles. + * The 60th bit indicates if counters are single bytes representing function + * entry coverage. */ #define VARIANT_MASKS_ALL 0xff00000000000000ULL #define GET_VERSION(V) ((V) & ~VARIANT_MASKS_ALL) @@ -664,6 +666,7 @@ #define VARIANT_MASK_CSIR_PROF (0x1ULL << 57) #define VARIANT_MASK_INSTR_ENTRY (0x1ULL << 58) #define VARIANT_MASK_DBG_CORRELATE (0x1ULL << 59) +#define VARIANT_MASK_BYTE_ENTRY_COVERAGE (0x1ULL << 60) #define INSTR_PROF_RAW_VERSION_VAR __llvm_profile_raw_version #define INSTR_PROF_PROFILE_RUNTIME_VAR __llvm_profile_runtime #define INSTR_PROF_PROFILE_COUNTER_BIAS_VAR __llvm_profile_counter_bias diff --git a/llvm/include/llvm/ProfileData/InstrProfReader.h b/llvm/include/llvm/ProfileData/InstrProfReader.h --- a/llvm/include/llvm/ProfileData/InstrProfReader.h +++ b/llvm/include/llvm/ProfileData/InstrProfReader.h @@ -100,6 +100,10 @@ /// Return true if we must provide debug info to create PGO profiles. virtual bool useDebugInfoCorrelate() const { return false; } + /// Return true if the profile has single byte counters representing function + /// entry coverage. + virtual bool useSingleByteEntryCoverage() const = 0; + /// Return the PGO symtab. There are three different readers: /// Raw, Text, and Indexed profile readers. The first two types /// of readers are used only by llvm-profdata tool, while the indexed @@ -179,6 +183,7 @@ bool IsIRLevelProfile = false; bool HasCSIRLevelProfile = false; bool InstrEntryBBEnabled = false; + bool UseSingleByteEntryCoverage = false; Error readValueProfileData(InstrProfRecord &Record); @@ -197,6 +202,10 @@ bool instrEntryBBEnabled() const override { return InstrEntryBBEnabled; } + bool useSingleByteEntryCoverage() const override { + return UseSingleByteEntryCoverage; + } + /// Read the header. Error readHeader() override; @@ -276,6 +285,10 @@ return (Version & VARIANT_MASK_DBG_CORRELATE) != 0; } + bool useSingleByteEntryCoverage() const override { + return (Version & VARIANT_MASK_BYTE_ENTRY_COVERAGE) != 0; + } + InstrProfSymtab &getSymtab() override { assert(Symtab.get()); return *Symtab.get(); @@ -333,7 +346,9 @@ return Symtab->getFuncName(swap(NameRef)); } - int getCounterTypeSize() const { return sizeof(uint64_t); } + int getCounterTypeSize() const { + return useSingleByteEntryCoverage() ? sizeof(uint8_t) : sizeof(uint64_t); + } }; using RawInstrProfReader32 = RawInstrProfReader; @@ -413,6 +428,7 @@ virtual bool isIRLevelProfile() const = 0; virtual bool hasCSIRLevelProfile() const = 0; virtual bool instrEntryBBEnabled() const = 0; + virtual bool useSingleByteEntryCoverage() const = 0; virtual Error populateSymtab(InstrProfSymtab &) = 0; }; @@ -465,6 +481,10 @@ return (FormatVersion & VARIANT_MASK_INSTR_ENTRY) != 0; } + bool useSingleByteEntryCoverage() const override { + return (FormatVersion & VARIANT_MASK_BYTE_ENTRY_COVERAGE) != 0; + } + Error populateSymtab(InstrProfSymtab &Symtab) override { return Symtab.create(HashTable->keys()); } @@ -523,6 +543,10 @@ return Index->instrEntryBBEnabled(); } + bool useSingleByteEntryCoverage() const override { + return Index->useSingleByteEntryCoverage(); + } + /// Return true if the given buffer is in an indexed instrprof format. static bool hasFormat(const MemoryBuffer &DataBuffer); diff --git a/llvm/include/llvm/ProfileData/InstrProfWriter.h b/llvm/include/llvm/ProfileData/InstrProfWriter.h --- a/llvm/include/llvm/ProfileData/InstrProfWriter.h +++ b/llvm/include/llvm/ProfileData/InstrProfWriter.h @@ -41,11 +41,13 @@ StringMap FunctionData; ProfKind ProfileKind = PF_Unknown; bool InstrEntryBBEnabled; + bool UseSingleByteEntryCoverage; // Use raw pointer here for the incomplete type object. InstrProfRecordWriterTrait *InfoObj; public: - InstrProfWriter(bool Sparse = false, bool InstrEntryBBEnabled = false); + InstrProfWriter(bool Sparse = false, bool InstrEntryBBEnabled = false, + bool UseSingleByteEntryCoverage = false); ~InstrProfWriter(); StringMap &getProfileData() { return FunctionData; } @@ -103,6 +105,9 @@ } void setInstrEntryBBEnabled(bool Enabled) { InstrEntryBBEnabled = Enabled; } + void setUseSingleByteEntryCoverage(bool Enabled) { + UseSingleByteEntryCoverage = Enabled; + } // Internal interface for testing purpose only. void setValueProfDataEndianness(support::endianness Endianness); void setOutputSparse(bool Sparse); diff --git a/llvm/include/llvm/Transforms/Instrumentation/InstrProfiling.h b/llvm/include/llvm/Transforms/Instrumentation/InstrProfiling.h --- a/llvm/include/llvm/Transforms/Instrumentation/InstrProfiling.h +++ b/llvm/include/llvm/Transforms/Instrumentation/InstrProfiling.h @@ -15,6 +15,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/IR/IRBuilder.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/PassManager.h" #include "llvm/ProfileData/InstrProf.h" @@ -87,20 +88,27 @@ /// Count the number of instrumented value sites for the function. void computeNumValueSiteCounts(InstrProfValueProfileInst *Ins); - /// Replace instrprof_value_profile with a call to runtime library. + /// Replace instrprof.value.profile with a call to runtime library. void lowerValueProfileInst(InstrProfValueProfileInst *Ins); - /// Replace instrprof_increment with an increment of the appropriate value. + /// Replace instrprof.cover with a store instruction to the coverage byte. + void lowerCover(InstrProfCoverInst *Inc); + + /// Replace instrprof.increment with an increment of the appropriate value. void lowerIncrement(InstrProfIncrementInst *Inc); /// Force emitting of name vars for unused functions. void lowerCoverageData(GlobalVariable *CoverageNamesVar); + /// Compute the address of the counter value that this profiling instruction + /// acts on. + Value *getCounterAddress(InstrProfBase *I); + /// Get the region counters for an increment, creating them if necessary. /// /// If the counter array doesn't yet exist, the profile data variables /// referring to them will also be created. - GlobalVariable *getOrCreateRegionCounters(InstrProfIncrementInst *Inc); + GlobalVariable *getOrCreateRegionCounters(InstrProfBase *Inc); /// Emit the section with compressed function names. void emitNameData(); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6871,6 +6871,8 @@ case Intrinsic::experimental_gc_relocate: visitGCRelocate(cast(I)); return; + case Intrinsic::instrprof_cover: + llvm_unreachable("instrprof failed to lower a cover"); case Intrinsic::instrprof_increment: llvm_unreachable("instrprof failed to lower an increment"); case Intrinsic::instrprof_value_profile: diff --git a/llvm/lib/IR/IntrinsicInst.cpp b/llvm/lib/IR/IntrinsicInst.cpp --- a/llvm/lib/IR/IntrinsicInst.cpp +++ b/llvm/lib/IR/IntrinsicInst.cpp @@ -178,6 +178,26 @@ return -1; } +ConstantInt *InstrProfBase::getNumCounters() const { + if (InstrProfCoverInst::classof(this)) { + const auto *M = getModule(); + return ConstantInt::get(Type::getInt64Ty(M->getContext()), 1); + } else if (InstrProfValueProfileInst::classof(this)) { + llvm_unreachable("InstrProfValueProfileInst does not have counters!"); + } + return cast(const_cast(getArgOperand(2))); +} + +Value *InstrProfBase::getIndex() const { + if (InstrProfCoverInst::classof(this)) { + const auto *M = getModule(); + return ConstantInt::get(Type::getInt64Ty(M->getContext()), 0); + } else if (InstrProfValueProfileInst::classof(this)) { + llvm_unreachable("Please use InstrProfValueProfileInst::getIndex()"); + } + return const_cast(getArgOperand(3)); +} + Value *InstrProfIncrementInst::getStep() const { if (InstrProfIncrementInstStep::classof(this)) { return const_cast(getArgOperand(4)); diff --git a/llvm/lib/ProfileData/InstrProf.cpp b/llvm/lib/ProfileData/InstrProf.cpp --- a/llvm/lib/ProfileData/InstrProf.cpp +++ b/llvm/lib/ProfileData/InstrProf.cpp @@ -1188,7 +1188,8 @@ // aware this is an ir_level profile so it can set the version flag. GlobalVariable *createIRLevelProfileFlagVar(Module &M, bool IsCS, bool InstrEntryBBEnabled, - bool DebugInfoCorrelate) { + bool DebugInfoCorrelate, + bool CoverageInstrumentation) { const StringRef VarName(INSTR_PROF_QUOTE(INSTR_PROF_RAW_VERSION_VAR)); Type *IntTy64 = Type::getInt64Ty(M.getContext()); uint64_t ProfileVersion = (INSTR_PROF_RAW_VERSION | VARIANT_MASK_IR_PROF); @@ -1198,6 +1199,8 @@ ProfileVersion |= VARIANT_MASK_INSTR_ENTRY; if (DebugInfoCorrelate) ProfileVersion |= VARIANT_MASK_DBG_CORRELATE; + if (CoverageInstrumentation) + ProfileVersion |= VARIANT_MASK_BYTE_ENTRY_COVERAGE; auto IRLevelVersionVariable = new GlobalVariable( M, IntTy64, true, GlobalValue::WeakAnyLinkage, Constant::getIntegerValue(IntTy64, APInt(64, ProfileVersion)), VarName); diff --git a/llvm/lib/ProfileData/InstrProfReader.cpp b/llvm/lib/ProfileData/InstrProfReader.cpp --- a/llvm/lib/ProfileData/InstrProfReader.cpp +++ b/llvm/lib/ProfileData/InstrProfReader.cpp @@ -485,9 +485,15 @@ Record.Counts.clear(); Record.Counts.reserve(NumCounters); for (uint32_t I = 0; I < NumCounters; I++) { - const auto *CounterValue = reinterpret_cast( - CountersStart + CounterBaseOffset + I * getCounterTypeSize()); - Record.Counts.push_back(swap(*CounterValue)); + const char *Ptr = + CountersStart + CounterBaseOffset + I * getCounterTypeSize(); + if (useSingleByteEntryCoverage()) { + // A value of zero signifies the block is covered. + Record.Counts.push_back(*Ptr == 0 ? 1 : 0); + } else { + const auto *CounterValue = reinterpret_cast(Ptr); + Record.Counts.push_back(swap(*CounterValue)); + } } return success(); diff --git a/llvm/lib/ProfileData/InstrProfWriter.cpp b/llvm/lib/ProfileData/InstrProfWriter.cpp --- a/llvm/lib/ProfileData/InstrProfWriter.cpp +++ b/llvm/lib/ProfileData/InstrProfWriter.cpp @@ -166,8 +166,10 @@ } // end namespace llvm -InstrProfWriter::InstrProfWriter(bool Sparse, bool InstrEntryBBEnabled) +InstrProfWriter::InstrProfWriter(bool Sparse, bool InstrEntryBBEnabled, + bool UseSingleByteEntryCoverage) : Sparse(Sparse), InstrEntryBBEnabled(InstrEntryBBEnabled), + UseSingleByteEntryCoverage(UseSingleByteEntryCoverage), InfoObj(new InstrProfRecordWriterTrait()) {} InstrProfWriter::~InstrProfWriter() { delete InfoObj; } @@ -311,6 +313,8 @@ } if (InstrEntryBBEnabled) Header.Version |= VARIANT_MASK_INSTR_ENTRY; + if (UseSingleByteEntryCoverage) + Header.Version |= VARIANT_MASK_BYTE_ENTRY_COVERAGE; Header.Unused = 0; Header.HashType = static_cast(IndexedInstrProf::HashType); diff --git a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp --- a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp +++ b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp @@ -33,7 +33,6 @@ #include "llvm/IR/Function.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" @@ -446,24 +445,22 @@ return new InstrProfilingLegacyPass(Options, IsCS); } -static InstrProfIncrementInst *castToIncrementInst(Instruction *Instr) { - InstrProfIncrementInst *Inc = dyn_cast(Instr); - if (Inc) - return Inc; - return dyn_cast(Instr); -} - bool InstrProfiling::lowerIntrinsics(Function *F) { bool MadeChange = false; PromotionCandidates.clear(); for (BasicBlock &BB : *F) { for (Instruction &Instr : llvm::make_early_inc_range(BB)) { - InstrProfIncrementInst *Inc = castToIncrementInst(&Instr); - if (Inc) { - lowerIncrement(Inc); + if (auto *IPC = dyn_cast(&Instr)) { + lowerCover(IPC); + MadeChange = true; + } else if (auto *IPI = dyn_cast(&Instr)) { + lowerIncrement(IPI); + MadeChange = true; + } else if (auto *IPIS = dyn_cast(&Instr)) { + lowerIncrement(IPIS); MadeChange = true; - } else if (auto *Ind = dyn_cast(&Instr)) { - lowerValueProfileInst(Ind); + } else if (auto *IPVP = dyn_cast(&Instr)) { + lowerValueProfileInst(IPVP); MadeChange = true; } } @@ -540,19 +537,15 @@ /// Check if the module contains uses of any profiling intrinsics. static bool containsProfilingIntrinsics(Module &M) { - if (auto *F = M.getFunction( - Intrinsic::getName(llvm::Intrinsic::instrprof_increment))) - if (!F->use_empty()) - return true; - if (auto *F = M.getFunction( - Intrinsic::getName(llvm::Intrinsic::instrprof_increment_step))) - if (!F->use_empty()) - return true; - if (auto *F = M.getFunction( - Intrinsic::getName(llvm::Intrinsic::instrprof_value_profile))) - if (!F->use_empty()) - return true; - return false; + auto containsIntrinsic = [&](int ID) { + if (auto *F = M.getFunction(Intrinsic::getName(ID))) + return !F->use_empty(); + return false; + }; + return containsIntrinsic(llvm::Intrinsic::instrprof_cover) || + containsIntrinsic(llvm::Intrinsic::instrprof_increment) || + containsIntrinsic(llvm::Intrinsic::instrprof_increment_step) || + containsIntrinsic(llvm::Intrinsic::instrprof_value_profile); } bool InstrProfiling::run( @@ -700,47 +693,66 @@ Ind->eraseFromParent(); } -void InstrProfiling::lowerIncrement(InstrProfIncrementInst *Inc) { - GlobalVariable *Counters = getOrCreateRegionCounters(Inc); +Value *InstrProfiling::getCounterAddress(InstrProfBase *I) { + auto *Counters = getOrCreateRegionCounters(I); + IRBuilder<> Builder(I); - IRBuilder<> Builder(Inc); - uint64_t Index = Inc->getIndex()->getZExtValue(); - Value *Addr = Builder.CreateConstInBoundsGEP2_32(Counters->getValueType(), - Counters, 0, Index); - - if (isRuntimeCounterRelocationEnabled()) { - Type *Int64Ty = Type::getInt64Ty(M->getContext()); - Type *Int64PtrTy = Type::getInt64PtrTy(M->getContext()); - Function *Fn = Inc->getParent()->getParent(); - Instruction &I = Fn->getEntryBlock().front(); - LoadInst *LI = dyn_cast(&I); - if (!LI) { - IRBuilder<> Builder(&I); - GlobalVariable *Bias = - M->getGlobalVariable(getInstrProfCounterBiasVarName()); - if (!Bias) { - // Compiler must define this variable when runtime counter relocation - // is being used. Runtime has a weak external reference that is used - // to check whether that's the case or not. - Bias = new GlobalVariable( - *M, Int64Ty, false, GlobalValue::LinkOnceODRLinkage, - Constant::getNullValue(Int64Ty), getInstrProfCounterBiasVarName()); - Bias->setVisibility(GlobalVariable::HiddenVisibility); - // A definition that's weak (linkonce_odr) without being in a COMDAT - // section wouldn't lead to link errors, but it would lead to a dead - // data word from every TU but one. Putting it in COMDAT ensures there - // will be exactly one data slot in the link. - if (TT.supportsCOMDAT()) - Bias->setComdat(M->getOrInsertComdat(Bias->getName())); - } - LI = Builder.CreateLoad(Int64Ty, Bias); + Value *Addr; + if (auto *ConstIndex = dyn_cast(I->getIndex())) { + uint32_t IndexVal = ConstIndex->getZExtValue(); + Addr = Builder.CreateConstInBoundsGEP2_32(Counters->getValueType(), + Counters, 0, IndexVal); + } else { + Addr = Builder.CreateInBoundsGEP(Counters->getValueType(), Counters, + {Builder.getInt32(0), I->getIndex()}); + } + + if (!isRuntimeCounterRelocationEnabled()) + return Addr; + + Type *Int64Ty = Type::getInt64Ty(M->getContext()); + Function *Fn = I->getParent()->getParent(); + Instruction &EntryI = Fn->getEntryBlock().front(); + LoadInst *LI = dyn_cast(&EntryI); + if (!LI) { + IRBuilder<> EntryBuilder(&EntryI); + auto *Bias = M->getGlobalVariable(getInstrProfCounterBiasVarName()); + if (!Bias) { + // Compiler must define this variable when runtime counter relocation + // is being used. Runtime has a weak external reference that is used + // to check whether that's the case or not. + Bias = new GlobalVariable( + *M, Int64Ty, false, GlobalValue::LinkOnceODRLinkage, + Constant::getNullValue(Int64Ty), getInstrProfCounterBiasVarName()); + Bias->setVisibility(GlobalVariable::HiddenVisibility); + // A definition that's weak (linkonce_odr) without being in a COMDAT + // section wouldn't lead to link errors, but it would lead to a dead + // data word from every TU but one. Putting it in COMDAT ensures there + // will be exactly one data slot in the link. + if (TT.supportsCOMDAT()) + Bias->setComdat(M->getOrInsertComdat(Bias->getName())); } - auto *Add = Builder.CreateAdd(Builder.CreatePtrToInt(Addr, Int64Ty), LI); - Addr = Builder.CreateIntToPtr(Add, Int64PtrTy); + LI = EntryBuilder.CreateLoad(Int64Ty, Bias); } + auto *Add = Builder.CreateAdd(Builder.CreatePtrToInt(Addr, Int64Ty), LI); + return Builder.CreateIntToPtr(Add, Addr->getType()); +} + +void InstrProfiling::lowerCover(InstrProfCoverInst *CoverInstruction) { + auto *Addr = getCounterAddress(CoverInstruction); + // We store zero to represent that this block is covered. + IRBuilder<> Builder(CoverInstruction); + Builder.CreateStore(Builder.getInt8(0), Addr); + CoverInstruction->eraseFromParent(); +} + +void InstrProfiling::lowerIncrement(InstrProfIncrementInst *Inc) { + auto *Addr = getCounterAddress(Inc); + IRBuilder<> Builder(Inc); + auto Index = cast(Inc->getIndex()); if (Options.Atomic || AtomicCounterUpdateAll || - (Index == 0 && AtomicFirstCounter)) { + (Index->isZeroValue() && AtomicFirstCounter)) { Builder.CreateAtomicRMW(AtomicRMWInst::Add, Addr, Inc->getStep(), MaybeAlign(), AtomicOrdering::Monotonic); } else { @@ -771,7 +783,7 @@ } /// Get the name of a profiling variable for a particular function. -static std::string getVarName(InstrProfIncrementInst *Inc, StringRef Prefix, +static std::string getVarName(InstrProfBase *Inc, StringRef Prefix, bool &Renamed) { StringRef NamePrefix = getInstrProfNameVarPrefix(); StringRef Name = Inc->getName()->getName().substr(NamePrefix.size()); @@ -859,8 +871,7 @@ return true; } -GlobalVariable * -InstrProfiling::getOrCreateRegionCounters(InstrProfIncrementInst *Inc) { +GlobalVariable *InstrProfiling::getOrCreateRegionCounters(InstrProfBase *Inc) { GlobalVariable *NamePtr = Inc->getName(); auto &PD = ProfileDataMap[NamePtr]; if (PD.RegionCounters) @@ -925,16 +936,29 @@ uint64_t NumCounters = Inc->getNumCounters()->getZExtValue(); LLVMContext &Ctx = M->getContext(); - ArrayType *CounterTy = ArrayType::get(Type::getInt64Ty(Ctx), NumCounters); // Create the counters variable. - auto *CounterPtr = - new GlobalVariable(*M, CounterTy, false, Linkage, - Constant::getNullValue(CounterTy), CntsVarName); + GlobalVariable *CounterPtr; + if (isa(Inc)) { + auto *CounterTy = Type::getInt8Ty(Ctx); + auto *CounterArrTy = ArrayType::get(CounterTy, NumCounters); + // Initialize to all ones to signify that all blocks are uncovered. + std::vector InitialValues(NumCounters, + Constant::getAllOnesValue(CounterTy)); + CounterPtr = new GlobalVariable( + *M, CounterArrTy, false, Linkage, + ConstantArray::get(CounterArrTy, InitialValues), CntsVarName); + CounterPtr->setAlignment(Align(1)); + } else { + auto *CounterTy = ArrayType::get(Type::getInt64Ty(Ctx), NumCounters); + CounterPtr = + new GlobalVariable(*M, CounterTy, false, Linkage, + Constant::getNullValue(CounterTy), CntsVarName); + CounterPtr->setAlignment(Align(8)); + } CounterPtr->setVisibility(Visibility); CounterPtr->setSection( getInstrProfSectionName(IPSK_cnts, TT.getObjectFormat())); - CounterPtr->setAlignment(Align(8)); MaybeSetComdat(CounterPtr); CounterPtr->setLinkage(Linkage); PD.RegionCounters = CounterPtr; @@ -997,8 +1021,14 @@ ConstantExpr::getBitCast(ValuesVar, Type::getInt8PtrTy(Ctx)); } - if (DebugInfoCorrelate) + if (DebugInfoCorrelate) { + if (isa(Inc)) { + // Mark this global as used so optimizations don't remove it since it is + // never read. + CompilerUsedVars.push_back(PD.RegionCounters); + } return PD.RegionCounters; + } // Create data variable. auto *IntPtrTy = M->getDataLayout().getIntPtrType(M->getContext()); diff --git a/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp b/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp --- a/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp +++ b/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp @@ -255,6 +255,10 @@ "pgo-instrument-entry", cl::init(false), cl::Hidden, cl::desc("Force to instrument function entry basicblock.")); +static cl::opt CoverageInstrumentation( + "pgo-coverage-instrumentation", cl::init(false), cl::Hidden, cl::ZeroOrMore, + cl::desc("Use this option to enable function coverage instrumentation.")); + static cl::opt PGOFixEntryCount("pgo-fix-entry-count", cl::init(true), cl::Hidden, cl::desc("Fix function entry count in profile use.")); @@ -469,9 +473,9 @@ createProfileFileNameVar(M, InstrProfileOutput); // The variable in a comdat may be discarded by LTO. Ensure the // declaration will be retained. - appendToCompilerUsed(M, createIRLevelProfileFlagVar(M, /*IsCS=*/true, - PGOInstrumentEntry, - DebugInfoCorrelate)); + appendToCompilerUsed(M, createIRLevelProfileFlagVar( + M, /*IsCS=*/true, PGOInstrumentEntry, + DebugInfoCorrelate, CoverageInstrumentation)); return false; } std::string InstrProfileOutput; @@ -911,22 +915,35 @@ FuncPGOInstrumentation FuncInfo( F, TLI, ComdatMembers, true, BPI, BFI, IsCS, PGOInstrumentEntry); + + Type *I8PtrTy = Type::getInt8PtrTy(M->getContext()); + auto Name = ConstantExpr::getBitCast(FuncInfo.FuncNameVar, I8PtrTy); + auto CFGHash = ConstantInt::get(Type::getInt64Ty(M->getContext()), + FuncInfo.FunctionHash); + if (CoverageInstrumentation) { + auto &EntryBB = F.getEntryBlock(); + IRBuilder<> Builder(&EntryBB, EntryBB.getFirstInsertionPt()); + Builder.CreateCall(Intrinsic::getDeclaration(M, Intrinsic::instrprof_cover), + {Name, CFGHash}); + return; + } + std::vector InstrumentBBs; FuncInfo.getInstrumentBBs(InstrumentBBs); unsigned NumCounters = InstrumentBBs.size() + FuncInfo.SIVisitor.getNumOfSelectInsts(); uint32_t I = 0; - Type *I8PtrTy = Type::getInt8PtrTy(M->getContext()); for (auto *InstrBB : InstrumentBBs) { IRBuilder<> Builder(InstrBB, InstrBB->getFirstInsertionPt()); assert(Builder.GetInsertPoint() != InstrBB->end() && "Cannot get the Instrumentation point"); + auto Index = Builder.getInt32(I++); + // llvm.instrprof.increment(i8* , i64 , i32 , + // i32 ) Builder.CreateCall( Intrinsic::getDeclaration(M, Intrinsic::instrprof_increment), - {ConstantExpr::getBitCast(FuncInfo.FuncNameVar, I8PtrTy), - Builder.getInt64(FuncInfo.FunctionHash), Builder.getInt32(NumCounters), - Builder.getInt32(I++)}); + {Name, CFGHash, Builder.getInt32(NumCounters), Index}); } // Now instrument select instructions: @@ -1499,6 +1516,8 @@ } void SelectInstVisitor::instrumentOneSelectInst(SelectInst &SI) { + if (CoverageInstrumentation) + return; Module *M = F.getParent(); IRBuilder<> Builder(&SI); Type *Int64Ty = Builder.getInt64Ty(); @@ -1620,7 +1639,7 @@ // (before LTO/ThinLTO linking) to create these variables. if (!IsCS) createIRLevelProfileFlagVar(M, /*IsCS=*/false, PGOInstrumentEntry, - DebugInfoCorrelate); + DebugInfoCorrelate, CoverageInstrumentation); std::unordered_multimap ComdatMembers; collectComdatMembers(M, ComdatMembers); @@ -1642,9 +1661,9 @@ createProfileFileNameVar(M, CSInstrName); // The variable in a comdat may be discarded by LTO. Ensure the declaration // will be retained. - appendToCompilerUsed(M, createIRLevelProfileFlagVar(M, /*IsCS=*/true, - PGOInstrumentEntry, - DebugInfoCorrelate)); + appendToCompilerUsed(M, createIRLevelProfileFlagVar( + M, /*IsCS=*/true, PGOInstrumentEntry, + DebugInfoCorrelate, CoverageInstrumentation)); return PreservedAnalyses::all(); } @@ -1841,6 +1860,12 @@ ProfileFileName.data(), "Not an IR level instrumentation profile")); return false; } + if (PGOReader->useSingleByteEntryCoverage()) { + Ctx.diagnose(DiagnosticInfoPGOProfile( + ProfileFileName.data(), + "Cannot use function entry coverage profiles for optimization")); + return false; + } // Add the profile summary (read from the header of the indexed summary) here // so that we can use it below when reading counters (which checks if the diff --git a/llvm/test/Instrumentation/InstrProfiling/coverage.ll b/llvm/test/Instrumentation/InstrProfiling/coverage.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/InstrProfiling/coverage.ll @@ -0,0 +1,23 @@ +; RUN: opt < %s -instrprof -S | FileCheck %s + +target triple = "aarch64-unknown-linux-gnu" + +@__profn_foo = private constant [3 x i8] c"foo" +; CHECK: @__profc_foo = private global [1 x i8] c"\FF", section "__llvm_prf_cnts", comdat, align 1 +@__profn_bar = private constant [3 x i8] c"bar" +; CHECK: @__profc_bar = private global [1 x i8] c"\FF", section "__llvm_prf_cnts", comdat, align 1 + +define void @_Z3foov() { + call void @llvm.instrprof.cover(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @__profn_foo, i32 0, i32 0), i64 12345678) + ; CHECK: store i8 0, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @__profc_foo, i32 0, i32 0), align 1 + ret void +} + +%class.A = type { i32 (...)** } +define dso_local void @_Z3barv(%class.A* nocapture nonnull align 8 %0) unnamed_addr #0 align 2 { + call void @llvm.instrprof.cover(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @__profn_bar, i32 0, i32 0), i64 87654321) + ; CHECK: store i8 0, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @__profc_bar, i32 0, i32 0), align 1 + ret void +} + +declare void @llvm.instrprof.cover(i8*, i64) diff --git a/llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-coverage.ll b/llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-coverage.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/InstrProfiling/debug-info-correlate-coverage.ll @@ -0,0 +1,34 @@ +; RUN: opt < %s -instrprof -debug-info-correlate -S | opt --O2 -S | FileCheck %s + +@__profn_foo = private constant [3 x i8] c"foo" +; CHECK: @__profc_foo + +define void @_Z3foov() !dbg !12 { + call void @llvm.instrprof.cover(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @__profn_foo, i32 0, i32 0), i64 12345678) + ; CHECK: store i8 0, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @__profc_foo + ret void +} + +declare void @llvm.instrprof.cover(i8*, i64) + +!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-coverage.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 1, !"branch-target-enforcement", i32 0} +!6 = !{i32 1, !"sign-return-address", i32 0} +!7 = !{i32 1, !"sign-return-address-all", i32 0} +!8 = !{i32 1, !"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-coverage.cpp", directory: "") +!14 = !DISubroutineType(types: !15) +!15 = !{null} +!16 = !{} diff --git a/llvm/test/Transforms/PGOProfile/coverage.ll b/llvm/test/Transforms/PGOProfile/coverage.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/PGOProfile/coverage.ll @@ -0,0 +1,26 @@ +; RUN: opt < %s -passes=pgo-instr-gen -pgo-coverage-instrumentation -S | FileCheck %s +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define i32 @foo(i32 %i) { +entry: + ; CHECK: call void @llvm.instrprof.cover({{.*}}) + %cmp = icmp sgt i32 %i, 0 + br i1 %cmp, label %if.then, label %if.else + +if.then: + ; CHECK-NOT: llvm.instrprof.cover( + %add = add nsw i32 %i, 2 + %s = select i1 %cmp, i32 %add, i32 0 + br label %if.end + +if.else: + %sub = sub nsw i32 %i, 2 + br label %if.end + +if.end: + %retv = phi i32 [ %add, %if.then ], [ %sub, %if.else ] + ret i32 %retv +} + +; CHECK: declare void @llvm.instrprof.cover( diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -267,6 +267,8 @@ return; } WC->Writer.setInstrEntryBBEnabled(Reader->instrEntryBBEnabled()); + WC->Writer.setUseSingleByteEntryCoverage( + Reader->useSingleByteEntryCoverage()); for (auto &I : *Reader) { if (Remapper) @@ -2095,7 +2097,8 @@ bool ShowAllFunctions, bool ShowCS, uint64_t ValueCutoff, bool OnlyListBelow, const std::string &ShowFunction, bool TextFormat, - bool ShowBinaryIds, raw_fd_ostream &OS) { + bool ShowBinaryIds, bool ShowCovered, + raw_fd_ostream &OS) { auto ReaderOrErr = InstrProfReader::create(Filename); std::vector Cutoffs = std::move(DetailedSummaryCutoffs); if (ShowDetailedSummary && Cutoffs.empty()) { @@ -2152,6 +2155,11 @@ assert(Func.Counts.size() > 0 && "function missing entry counter"); Builder.addRecord(Func); + if (ShowCovered) + if (std::any_of(Func.Counts.begin(), Func.Counts.end(), + [](uint64_t C) { return C; })) + OS << " " << Func.Name << "\n"; + uint64_t FuncMax = 0; uint64_t FuncSum = 0; for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) { @@ -2550,6 +2558,9 @@ "extbinary format")); cl::opt ShowBinaryIds("binary-ids", cl::init(false), cl::desc("Show binary ids in the profile. ")); + cl::opt ShowCovered( + "covered", cl::init(false), + cl::desc("Show only the functions that have been executed.")); cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); @@ -2572,7 +2583,7 @@ Filename, ShowCounts, TopNFunctions, ShowIndirectCallTargets, ShowMemOPSizes, ShowDetailedSummary, DetailedSummaryCutoffs, ShowAllFunctions, ShowCS, ValueCutoff, OnlyListBelow, ShowFunction, - TextFormat, ShowBinaryIds, OS); + TextFormat, ShowBinaryIds, ShowCovered, OS); if (ProfileKind == sample) return showSampleProfile(Filename, ShowCounts, TopNFunctions, ShowAllFunctions, ShowDetailedSummary,