Emit the profile using GCC's gcov format (Not yet supported).
+ .. option:: -sparse[=true|false]
+
+ Do not emit function records with 0 execution count. Can only be used in
+ conjunction with -instr. Defaults to false, since it can inhibit compiler
+ optimization during PGO.
+
EXAMPLES
^^^^^^^^
Basic Usage
typedef SmallDenseMap<uint64_t, InstrProfRecord, 1> ProfilingData;
private:
+ bool Sparse;
StringMap<ProfilingData> FunctionData;
uint64_t MaxFunctionCount;
// Use raw pointer here for the incomplete type object.
InstrProfRecordWriterTrait *InfoObj;
public:
- InstrProfWriter();
+ InstrProfWriter(bool Sparse = false);
~InstrProfWriter();
/// Add function counts for the given function. If there are already counts
// Internal interface for testing purpose only.
void setValueProfDataEndianness(support::endianness Endianness);
+ void setOutputSparse(bool Sparse);
private:
+ bool shouldEncodeData(const ProfilingData &PD);
void writeImpl(ProfOStream &OS);
};
};
}
-InstrProfWriter::InstrProfWriter()
- : FunctionData(), MaxFunctionCount(0),
+InstrProfWriter::InstrProfWriter(bool Sparse)
+ : Sparse(Sparse), FunctionData(), MaxFunctionCount(0),
InfoObj(new InstrProfRecordWriterTrait()) {}
InstrProfWriter::~InstrProfWriter() { delete InfoObj; }
support::endianness Endianness) {
InfoObj->ValueProfDataEndianness = Endianness;
}
+void InstrProfWriter::setOutputSparse(bool Sparse) {
+ this->Sparse = Sparse;
+}
std::error_code InstrProfWriter::addRecord(InstrProfRecord &&I,
uint64_t Weight) {
return Result;
}
+bool InstrProfWriter::shouldEncodeData(const ProfilingData &PD) {
+ if (!Sparse)
+ return true;
+ for (const auto &Func : PD) {
+ const InstrProfRecord &IPR = Func.second;
+ if (std::any_of(IPR.Counts.begin(), IPR.Counts.end(),
+ [](uint64_t Count) { return Count > 0; }))
+ return true;
+ }
+ return false;
+}
+
void InstrProfWriter::writeImpl(ProfOStream &OS) {
OnDiskChainedHashTableGenerator<InstrProfRecordWriterTrait> Generator;
// Populate the hash table generator.
for (const auto &I : FunctionData)
- Generator.insert(I.getKey(), &I.getValue());
+ if (shouldEncodeData(I.getValue()))
+ Generator.insert(I.getKey(), &I.getValue());
// Write the header.
IndexedInstrProf::Header Header;
Header.Magic = IndexedInstrProf::Magic;
void InstrProfWriter::writeText(raw_fd_ostream &OS) {
InstrProfSymtab Symtab;
for (const auto &I : FunctionData)
- Symtab.addFuncName(I.getKey());
+ if (shouldEncodeData(I.getValue()))
+ Symtab.addFuncName(I.getKey());
Symtab.finalizeSymtab();
for (const auto &I : FunctionData)
- for (const auto &Func : I.getValue())
- writeRecordInText(Func.second, Symtab, OS);
+ if (shouldEncodeData(I.getValue()))
+ for (const auto &Func : I.getValue())
+ writeRecordInText(Func.second, Symtab, OS);
}
+# RUN: llvm-profdata merge -sparse=true %s -o %t.profdata
-
-# RUN: llvm-profdata merge %s -o %t.profdata
+# RUN: llvm-profdata merge -sparse=false %s -o %t.profdata.dense
# RUN: llvm-profdata show %t.profdata --function function_count_only --counts | FileCheck %s -check-prefix=FUNC_COUNT_ONLY
function_count_only
# FUNC_COUNT_ONLY-NEXT: Function count: 97531
# FUNC_COUNT_ONLY-NEXT: Block counts: []
-# RUN: llvm-profdata show %t.profdata --function "name with spaces" --counts | FileCheck %s -check-prefix=SPACES
+# RUN: llvm-profdata show %t.profdata.dense --function "name with spaces" --counts | FileCheck %s -check-prefix=SPACES
+# RUN: llvm-profdata show %t.profdata --function "name with spaces" --counts | FileCheck %s --check-prefix=SPARSE_SPACES
name with spaces
1024
2
# SPACES-NEXT: Counters: 2
# SPACES-NEXT: Function count: 0
# SPACES-NEXT: Block counts: [0]
+# SPARSE_SPACES-NOT: Function count: 0
# RUN: llvm-profdata show %t.profdata --function large_numbers --counts | FileCheck %s -check-prefix=LARGENUM
large_numbers
# LARGENUM-NEXT: Function count: 2305843009213693952
# LARGENUM-NEXT: Block counts: [1152921504606846976, 576460752303423488, 288230376151711744, 144115188075855872, 72057594037927936]
-# RUN: llvm-profdata show %t.profdata --function hex_hash | FileCheck %s -check-prefix=HEX-HASH
+# RUN: llvm-profdata show %t.profdata.dense --function hex_hash | FileCheck %s -check-prefix=HEX-HASH
hex_hash
0x1234
1
# NOSUCHFUNC: Functions shown: 0
# RUN: llvm-profdata show %t.profdata --function _ | FileCheck %s -check-prefix=SOMEFUNCS
+# RUN: llvm-profdata show %t.profdata.dense --function _ | FileCheck %s -check-prefix=SOMEFUNCS_DENSE
# SOMEFUNCS: Counters:
# SOMEFUNCS: function_count_only:
# SOMEFUNCS: large_numbers:
-# SOMEFUNCS: Functions shown: 3
+# SOMEFUNCS: Functions shown: 2
+# SOMEFUNCS_DENSE: Functions shown: 3
-# RUN: llvm-profdata show %t.profdata | FileCheck %s -check-prefix=SUMMARY
+# RUN: llvm-profdata show %t.profdata.dense | FileCheck %s -check-prefix=SUMMARY
# SUMMARY-NOT: Counters:
# SUMMARY-NOT: Functions shown:
# SUMMARY: Total functions: 4
# SUMMARY: Maximum function count: 2305843009213693952
# SUMMARY: Maximum internal block count: 1152921504606846976
-# RUN: llvm-profdata show --detailed-summary %t.profdata | FileCheck %s -check-prefix=DETAILED-SUMMARY
+# RUN: llvm-profdata show --detailed-summary %t.profdata.dense | FileCheck %s -check-prefix=DETAILED-SUMMARY
# DETAILED-SUMMARY: Detailed summary:
# DETAILED-SUMMARY: Total number of blocks: 10
# DETAILED-SUMMARY: Total count: 4539628424389557499
static void mergeInstrProfile(const WeightedFileVector &Inputs,
StringRef OutputFilename,
- ProfileFormat OutputFormat) {
+ ProfileFormat OutputFormat, bool OutputSparse) {
if (OutputFilename.compare("-") == 0)
exitWithError("Cannot write indexed profdata format to stdout.");
if (EC)
exitWithErrorCode(EC, OutputFilename);
- InstrProfWriter Writer;
+ InstrProfWriter Writer(OutputSparse);
SmallSet<std::error_code, 4> WriterErrorCodes;
for (const auto &Input : Inputs) {
auto ReaderOrErr = InstrProfReader::create(Input.Filename);
"GCC encoding (only meaningful for -sample)"),
clEnumValEnd));
+ cl::opt<bool> OutputSparse("sparse", cl::init(false),
+ cl::desc("Generate a sparse profile (only meaningful for -instr)"));
+
cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n");
if (InputFilenames.empty() && WeightedInputFilenames.empty())
WeightedInputs.push_back(parseWeightedFile(WeightedFilename));
if (ProfileKind == instr)
- mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat);
+ mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat,
+ OutputSparse);
else
mergeSampleProfile(WeightedInputs, OutputFilename, OutputFormat);
void SetUp() override {
NextFile = 0;
+ ProfileWriter.setOutputSparse(false);
}
unsigned getFile(StringRef Name) {
}
};
-TEST_F(CoverageMappingTest, basic_write_read) {
+struct MaybeSparseCoverageMappingTest
+ : public CoverageMappingTest,
+ public ::testing::WithParamInterface<bool> {
+ void SetUp() {
+ CoverageMappingTest::SetUp();
+ ProfileWriter.setOutputSparse(GetParam());
+ }
+};
+
+TEST_P(MaybeSparseCoverageMappingTest, basic_write_read) {
addCMR(Counter::getCounter(0), "foo", 1, 1, 1, 1);
addCMR(Counter::getCounter(1), "foo", 2, 1, 2, 2);
addCMR(Counter::getZero(), "foo", 3, 1, 3, 4);
}
}
-TEST_F(CoverageMappingTest, expansion_gets_first_counter) {
+TEST_P(MaybeSparseCoverageMappingTest, expansion_gets_first_counter) {
addCMR(Counter::getCounter(1), "foo", 10, 1, 10, 2);
// This starts earlier in "foo", so the expansion should get its counter.
addCMR(Counter::getCounter(2), "foo", 1, 1, 20, 1);
ASSERT_EQ(3U, OutputCMRs[2].LineStart);
}
-TEST_F(CoverageMappingTest, basic_coverage_iteration) {
+TEST_P(MaybeSparseCoverageMappingTest, basic_coverage_iteration) {
InstrProfRecord Record("func", 0x1234, {30, 20, 10, 0});
ProfileWriter.addRecord(std::move(Record));
readProfCounts();
ASSERT_EQ(CoverageSegment(11, 11, false), Segments[6]);
}
-TEST_F(CoverageMappingTest, uncovered_function) {
+TEST_P(MaybeSparseCoverageMappingTest, uncovered_function) {
readProfCounts();
addCMR(Counter::getZero(), "file1", 1, 2, 3, 4);
ASSERT_EQ(CoverageSegment(3, 4, false), Segments[1]);
}
-TEST_F(CoverageMappingTest, uncovered_function_with_mapping) {
+TEST_P(MaybeSparseCoverageMappingTest, uncovered_function_with_mapping) {
readProfCounts();
addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
ASSERT_EQ(CoverageSegment(9, 9, false), Segments[2]);
}
-TEST_F(CoverageMappingTest, combine_regions) {
+TEST_P(MaybeSparseCoverageMappingTest, combine_regions) {
InstrProfRecord Record("func", 0x1234, {10, 20, 30});
ProfileWriter.addRecord(std::move(Record));
readProfCounts();
ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]);
}
-TEST_F(CoverageMappingTest, dont_combine_expansions) {
- InstrProfRecord Record("func", 0x1234, {10, 20});
- ProfileWriter.addRecord(std::move(Record));
+TEST_P(MaybeSparseCoverageMappingTest, dont_combine_expansions) {
+ InstrProfRecord Record1("func", 0x1234, {10, 20});
+ InstrProfRecord Record2("func", 0x1234, {0, 0});
+ ProfileWriter.addRecord(std::move(Record1));
+ ProfileWriter.addRecord(std::move(Record2));
readProfCounts();
addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
ASSERT_EQ(CoverageSegment(9, 9, false), Segments[3]);
}
-TEST_F(CoverageMappingTest, strip_filename_prefix) {
- InstrProfRecord Record("file1:func", 0x1234, {10});
+TEST_P(MaybeSparseCoverageMappingTest, strip_filename_prefix) {
+ InstrProfRecord Record("file1:func", 0x1234, {0});
ProfileWriter.addRecord(std::move(Record));
readProfCounts();
ASSERT_EQ("func", Names[0]);
}
+INSTANTIATE_TEST_CASE_P(MaybeSparse, MaybeSparseCoverageMappingTest,
+ ::testing::Bool());
+
} // end anonymous namespace
InstrProfWriter Writer;
std::unique_ptr<IndexedInstrProfReader> Reader;
+ void SetUp() { Writer.setOutputSparse(false); }
+
void readProfile(std::unique_ptr<MemoryBuffer> Profile) {
auto ReaderOrErr = IndexedInstrProfReader::create(std::move(Profile));
ASSERT_TRUE(NoError(ReaderOrErr.getError()));
}
};
-TEST_F(InstrProfTest, write_and_read_empty_profile) {
+struct SparseInstrProfTest : public InstrProfTest {
+ void SetUp() { Writer.setOutputSparse(true); }
+};
+
+struct MaybeSparseInstrProfTest : public InstrProfTest,
+ public ::testing::WithParamInterface<bool> {
+ void SetUp() {
+ Writer.setOutputSparse(GetParam());
+ }
+};
+
+TEST_P(MaybeSparseInstrProfTest, write_and_read_empty_profile) {
auto Profile = Writer.writeBuffer();
readProfile(std::move(Profile));
ASSERT_TRUE(Reader->begin() == Reader->end());
}
-TEST_F(InstrProfTest, write_and_read_one_function) {
+TEST_P(MaybeSparseInstrProfTest, write_and_read_one_function) {
InstrProfRecord Record("foo", 0x1234, {1, 2, 3, 4});
Writer.addRecord(std::move(Record));
auto Profile = Writer.writeBuffer();
ASSERT_TRUE(++I == E);
}
-TEST_F(InstrProfTest, get_instr_prof_record) {
+TEST_P(MaybeSparseInstrProfTest, get_instr_prof_record) {
InstrProfRecord Record1("foo", 0x1234, {1, 2});
InstrProfRecord Record2("foo", 0x1235, {3, 4});
Writer.addRecord(std::move(Record1));
ASSERT_TRUE(ErrorEquals(instrprof_error::unknown_function, R.getError()));
}
-TEST_F(InstrProfTest, get_function_counts) {
+TEST_P(MaybeSparseInstrProfTest, get_function_counts) {
InstrProfRecord Record1("foo", 0x1234, {1, 2});
InstrProfRecord Record2("foo", 0x1235, {3, 4});
Writer.addRecord(std::move(Record1));
ASSERT_TRUE(ErrorEquals(instrprof_error::unknown_function, EC));
}
-TEST_F(InstrProfTest, get_icall_data_read_write) {
+TEST_P(MaybeSparseInstrProfTest, get_icall_data_read_write) {
InstrProfRecord Record1("caller", 0x1234, {1, 2});
InstrProfRecord Record2("callee1", 0x1235, {3, 4});
InstrProfRecord Record3("callee2", 0x1235, {3, 4});
ASSERT_EQ(StringRef((const char *)VD[2].Value, 7), StringRef("callee1"));
}
-TEST_F(InstrProfTest, get_icall_data_read_write_with_weight) {
+TEST_P(MaybeSparseInstrProfTest, get_icall_data_read_write_with_weight) {
InstrProfRecord Record1("caller", 0x1234, {1, 2});
InstrProfRecord Record2("callee1", 0x1235, {3, 4});
InstrProfRecord Record3("callee2", 0x1235, {3, 4});
ASSERT_EQ(StringRef((const char *)VD[2].Value, 7), StringRef("callee1"));
}
-TEST_F(InstrProfTest, get_icall_data_read_write_big_endian) {
+TEST_P(MaybeSparseInstrProfTest, get_icall_data_read_write_big_endian) {
InstrProfRecord Record1("caller", 0x1234, {1, 2});
InstrProfRecord Record2("callee1", 0x1235, {3, 4});
InstrProfRecord Record3("callee2", 0x1235, {3, 4});
Writer.setValueProfDataEndianness(support::little);
}
-TEST_F(InstrProfTest, get_icall_data_merge1) {
+TEST_P(MaybeSparseInstrProfTest, get_icall_data_merge1) {
static const char caller[] = "caller";
static const char callee1[] = "callee1";
static const char callee2[] = "callee2";
ASSERT_EQ(2U, VD_4[2].Count);
}
-TEST_F(InstrProfTest, get_icall_data_merge1_saturation) {
+TEST_P(MaybeSparseInstrProfTest, get_icall_data_merge1_saturation) {
static const char bar[] = "bar";
const uint64_t Max = std::numeric_limits<uint64_t>::max();
// This test tests that when there are too many values
// for a given site, the merged results are properly
// truncated.
-TEST_F(InstrProfTest, get_icall_data_merge_site_trunc) {
+TEST_P(MaybeSparseInstrProfTest, get_icall_data_merge_site_trunc) {
static const char caller[] = "caller";
InstrProfRecord Record11(caller, 0x1234, {1, 2});
nullptr};
static uint16_t NumValueSites[IPVK_Last + 1] = {5};
-TEST_F(InstrProfTest, runtime_value_prof_data_read_write) {
+TEST_P(MaybeSparseInstrProfTest, runtime_value_prof_data_read_write) {
ValueProfRuntimeRecord RTRecord;
initializeValueProfRuntimeRecord(&RTRecord, &NumValueSites[0],
&ValueProfNodes[0]);
free(VPData);
}
-TEST_F(InstrProfTest, get_max_function_count) {
+TEST_P(MaybeSparseInstrProfTest, get_max_function_count) {
InstrProfRecord Record1("foo", 0x1234, {1ULL << 31, 2});
InstrProfRecord Record2("bar", 0, {1ULL << 63});
InstrProfRecord Record3("baz", 0x5678, {0, 0, 0, 0});
ASSERT_EQ(1ULL << 63, Reader->getMaximumFunctionCount());
}
-TEST_F(InstrProfTest, get_weighted_function_counts) {
+TEST_P(MaybeSparseInstrProfTest, get_weighted_function_counts) {
InstrProfRecord Record1("foo", 0x1234, {1, 2});
InstrProfRecord Record2("foo", 0x1235, {3, 4});
Writer.addRecord(std::move(Record1), 3);
ASSERT_EQ(20U, Counts[1]);
}
-TEST_F(InstrProfTest, instr_prof_symtab_test) {
+TEST_P(MaybeSparseInstrProfTest, instr_prof_symtab_test) {
std::vector<StringRef> FuncNames;
FuncNames.push_back("func1");
FuncNames.push_back("func2");
ASSERT_EQ(StringRef("bar3"), R);
}
-TEST_F(InstrProfTest, instr_prof_symtab_module_test) {
+TEST_P(MaybeSparseInstrProfTest, instr_prof_symtab_module_test) {
LLVMContext Ctx;
std::unique_ptr<Module> M = llvm::make_unique<Module>("MyModule.cpp", Ctx);
FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx),
}
}
-TEST_F(InstrProfTest, instr_prof_symtab_compression_test) {
+TEST_P(MaybeSparseInstrProfTest, instr_prof_symtab_compression_test) {
std::vector<std::string> FuncNames1;
std::vector<std::string> FuncNames2;
for (int I = 0; I < 10 * 1024; I++) {
}
}
+TEST_F(SparseInstrProfTest, preserve_no_records) {
+ InstrProfRecord Record1("foo", 0x1234, {0});
+ InstrProfRecord Record2("bar", 0x4321, {0, 0});
+ InstrProfRecord Record3("bar", 0x4321, {0, 0, 0});
+
+ Writer.addRecord(std::move(Record1));
+ Writer.addRecord(std::move(Record2));
+ Writer.addRecord(std::move(Record3));
+ auto Profile = Writer.writeBuffer();
+ readProfile(std::move(Profile));
+
+ auto I = Reader->begin(), E = Reader->end();
+ ASSERT_TRUE(I == E);
+}
+
+INSTANTIATE_TEST_CASE_P(MaybeSparse, MaybeSparseInstrProfTest,
+ ::testing::Bool());
+
} // end anonymous namespace